aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.luarc.json3
-rw-r--r--main.lua13
-rw-r--r--src/ecs.lua1
-rw-r--r--src/im.lua454
-rw-r--r--src/init.lua1
-rw-r--r--src/input.lua20
-rw-r--r--src/objs/player.lua44
-rw-r--r--src/utils.lua12
8 files changed, 527 insertions, 21 deletions
diff --git a/.luarc.json b/.luarc.json
index 7a1b616..f33c270 100644
--- a/.luarc.json
+++ b/.luarc.json
@@ -4,5 +4,6 @@
"disable": [
"lowercase-global"
]
- }
+ },
+ "runtime.version": "LuaJIT"
}
diff --git a/main.lua b/main.lua
index b516216..517a948 100644
--- a/main.lua
+++ b/main.lua
@@ -11,6 +11,9 @@ function love.load()
event_bind(scn.on_draw, "Sprite", sprite_draw_sys)
event_bind(scn.on_draw, "Tilemap", tilemap_draw_sys)
+
+ event_bind(scn.on_ui, "Player", player_ui_sys)
+
set_scene(scn)
player = new_player(100, 100)
@@ -34,7 +37,7 @@ end
function love.draw(dt)
lg.origin()
- lg.setCanvas(viewport)
+ lg.setCanvas({viewport, stencil=true})
lg.clear()
local scn = get_current_scene()
@@ -45,6 +48,14 @@ function love.draw(dt)
ground2:draw()
player.box:draw()
+ im.begin_step()
+
+ fire_event(scn.on_ui)
+
+ im.draw()
+
+ im.end_step()
+
-- TODO: Take care of weird displays
lg.setCanvas()
local scr_width, scr_height = lg.getDimensions()
diff --git a/src/ecs.lua b/src/ecs.lua
index 24603c0..373d338 100644
--- a/src/ecs.lua
+++ b/src/ecs.lua
@@ -20,6 +20,7 @@ function new_scene()
-- Events
on_draw = new_event(),
on_update = new_event(),
+ on_ui = new_event(),
comp_removeq = {},
comp_entq = {},
diff --git a/src/im.lua b/src/im.lua
new file mode 100644
index 0000000..28c7064
--- /dev/null
+++ b/src/im.lua
@@ -0,0 +1,454 @@
+im = {
+ cols = {
+ title_bar = {0.15, 0.15, 0.15},
+ border = {0.1, 0.1, 0.1},
+ bg = {0.2, 0.2, 0.2},
+ hover = {0.3, 0.3, 0.3},
+ active = {0.15, 0.15, 0.15},
+ title_text = {0.5, 0.5, 0.5},
+ text = {0.8, 0.8, 0.8},
+ },
+ padding = 1,
+ scroll_speed = 5,
+ slider_handle_width = 5,
+ resize_handle_size = 5,
+}
+
+--- if true, then the focused window is already decided
+local focus_finalized = false
+local next_focused --- next focused window
+local focused_win
+local wins = {}
+
+local current_win
+
+local mouse_down = false
+local mouse_just_up = false
+
+local mx, my = 0, 0
+local mdx, mdy = 0, 0
+local scroll = 0
+
+local layout
+local layout_i = 1
+
+local max_h = 0
+local next_x = 0
+local next_y = 0
+local next_w = 0
+
+local function rect_contains(x, y, w, h, px, py)
+ return
+ px > x and
+ py > y and
+ px < x + w and
+ py < y + h
+end
+
+local function draw_text(text, font, r, g, b, x, y)
+ lg.setColor(r, g, b)
+ lg.setFont(font)
+ lg.print(text, x, y)
+end
+
+local function draw_rect(mode, x, y, w, h, r, g, b)
+ lg.setColor(r, g, b)
+ lg.rectangle(mode, x, y, w, h)
+end
+
+local function draw_img(img, x, y, quad)
+ lg.setColor(1, 1, 1)
+ if not quad then
+ lg.draw(img, x, y)
+ else
+ lg.draw(img, quad, x, y)
+ end
+end
+
+local function draw_stencil(x, y, w, h)
+ lg.stencil(function()
+ lg.rectangle("fill", x, y, w, h)
+ end, "replace", 1)
+end
+
+local function text_cmd(text, x, y, r, g, b)
+ table.insert(current_win.cmds, {
+ fn = draw_text,
+ args = {text, lg.getFont(), r, g, b, x, y},
+ })
+end
+
+local function rect_cmd(x, y, w, h, r, g, b)
+ table.insert(current_win.cmds, {
+ fn = draw_rect,
+ args = {"fill", x, y, w, h, r, g, b},
+ })
+end
+
+local function img_cmd(img, x, y, quad)
+ table.insert(current_win.cmds, {
+ fn = draw_img,
+ args = {img, x, y, quad}
+ })
+end
+
+local function stencil_cmd(x, y, w, h)
+ table.insert(current_win.cmds, {
+ fn = draw_stencil,
+ args = {x, y, w, h},
+ })
+end
+
+local function control_stencil(x, y, w, h)
+ local win = current_win
+ local tlx, tly = x, y
+ local brx, bry = x + w, y + h
+
+ tlx = clamp(tlx, win.cx, win.cx + win.cw)
+ tly = clamp(tly, win.cy, win.cy + win.ch)
+
+ brx = clamp(brx, win.cx, win.cx + win.cw)
+ bry = clamp(bry, win.cy, win.cy + win.ch)
+
+ return tlx, tly, math.max(brx - tlx, 0), math.max(bry - tly, 0)
+end
+
+local function reset_stencil()
+ stencil_cmd(current_win.cx, current_win.cy, current_win.cw, current_win.ch)
+end
+
+local function shrink(x, y, w, h, by)
+ x = x + by
+ y = y + by
+ w = w - by * 2
+ h = h - by * 2
+ return x, y, w, h
+end
+
+function im.layout(new_layout, start)
+ assert(current_win, "cannot set layout outside of a window")
+
+ new_layout = new_layout or {1}
+
+ local proc = {}
+ local last = start or 0
+
+ local win_x = current_win.x + im.padding
+ local win_w = current_win.w - im.padding * 2
+
+ for i, v in ipairs(new_layout) do
+ assert(v >= last, "layout must be sorted low to high")
+ local w = win_w * (v - last) - im.padding * 2
+ proc[i] = {x=win_x + win_w * last + im.padding, w=w}
+ last = v
+ end
+
+ layout = proc
+ layout_i = 1
+ im.next_position(0)
+end
+
+function im.next_position(h)
+ h = h or 0
+
+ local x, y, w = next_x, next_y, next_w
+
+ max_h = math.max(max_h, h)
+
+ if layout_i > #layout then -- no more columns
+ next_y = next_y + max_h + im.padding
+ current_win.content_max = math.max(
+ current_win.content_max,
+ next_y + current_win.scroll - current_win.cy)
+ layout_i = 1
+ max_h = 0
+ end
+ next_x = layout[layout_i].x
+ next_w = layout[layout_i].w
+ layout_i = layout_i + 1
+
+ return x, y, w
+end
+
+function im.image(img, quad)
+ assert(current_win, "a window must be active")
+ local w, h = img:getDimensions()
+ if quad then
+ _, _, w, h = quad:getViewport()
+ end
+ local x, y, max_w = im.next_position(h)
+
+ w = math.min(w, max_w)
+
+ stencil_cmd(control_stencil(x, y, w, h))
+ img_cmd(img, round(x), round(y), quad)
+ reset_stencil()
+end
+
+function im.separator()
+ assert(current_win, "a window must be active")
+ local x, y, w = im.next_position(1)
+ rect_cmd(x, y, w, 1, unpack(im.cols.border))
+end
+
+function im.text(text, r, g, b)
+ assert(current_win, "a window must be active")
+ r = r or im.cols.text[1]
+ g = g or im.cols.text[2]
+ b = b or im.cols.text[3]
+
+ local font = lg.getFont()
+ local h = font:getHeight()
+ local x, y, w = im.next_position(h)
+ stencil_cmd(control_stencil(x, y, w, h))
+ text_cmd(text, x, y, r, g, b)
+ reset_stencil()
+end
+
+function im.button(text, r, g, b)
+ assert(current_win, "a window must be active")
+ r = r or im.cols.text[1]
+ g = g or im.cols.text[2]
+ b = b or im.cols.text[3]
+
+ local font = lg.getFont()
+ local text_w, text_h = font:getWidth(text), font:getHeight() + im.padding
+ local w, h = text_w * 1.1, text_h * 1.1
+ local x, y = im.next_position(h)
+
+ stencil_cmd(control_stencil(x, y, w, h))
+
+ rect_cmd(x, y, w, h, unpack(im.cols.border))
+
+ x, y, w, h = shrink(x, y, w, h, 1)
+
+ local col = im.cols.bg
+ local pressed = false
+
+ if current_win == focused_win and rect_contains(x, y, w, h, mx, my) then
+ col = im.cols.hover
+ if mouse_just_up then
+ col = im.cols.active
+ pressed = true
+ end
+ end
+
+ rect_cmd(x, y, w, h, unpack(col))
+
+ local text_x, text_y = x + w / 2 - text_w / 2, y + h / 2 - text_h / 2
+ text_cmd(text, text_x, text_y, r, g, b)
+
+ reset_stencil()
+
+ local ret = pressed
+ return ret
+end
+
+function im.slider(val, min, max, step, h)
+ assert(current_win, "a window must be active")
+ assert(max > min, "min must be lesser than max")
+ step = step or 0.1
+ h = h or lg.getFont():getHeight()
+ local p = (val - min) / (max - min)
+
+ local x, y, w = im.next_position(h)
+
+ rect_cmd(x, y, w, h, unpack(im.cols.border))
+ x, y, w, h = shrink(x, y, w, h, 1)
+ rect_cmd(x, y, w, h, unpack(im.cols.bg))
+
+ local max_w = w - im.slider_handle_width
+ local handle = max_w * p
+
+ local col = im.cols.bg
+ if rect_contains(x, y, w, h, mx - mdx, my - mdy) and
+ current_win == focused_win then
+ col = im.cols.hover
+ if mouse_down then
+ col = im.cols.active
+ handle = clamp(mx - x, 0, max_w)
+ end
+ end
+
+ rect_cmd(x, y, handle, h, unpack(im.cols.hover))
+
+ x = x + handle
+ w = im.slider_handle_width
+ y = y
+ h = h
+ rect_cmd(x, y, w, h, unpack(im.cols.border))
+ x, y, w, h = shrink(x, y, w, h, 1)
+ rect_cmd(x, y, w, h, unpack(col))
+
+ p = handle / max_w
+ return clamp(p * (max - min) + min, min, max)
+end
+
+function im.begin_window(title, x, y, w, h, opts)
+ assert(not current_win, "window is already active")
+ opts = opts or {}
+
+ local win = wins[title]
+ if not win then
+ win = {
+ win = true,
+ x = x,
+ y = y,
+ w = w,
+ h = h,
+ opts = opts,
+ content_max = 0,
+ scroll = 0,
+ open = true,
+ }
+ wins[title] = win
+ table.insert(wins, win)
+ win.idx = #wins
+ end
+
+ win.cmds = {}
+
+ if rect_contains(win.x, win.y, win.w, win.h, mx, my) then
+ if (not next_focused or next_focused.idx < win.idx) and
+ not focus_finalized then
+ next_focused = win
+ end
+
+ if mouse_down and focused_win == win then
+ local last = pop(wins)
+ wins[win.idx] = last
+ last.idx = win.idx
+ table.insert(wins, win)
+ win.idx = #wins
+ end
+ end
+
+ if not win.open then
+ return false
+ end
+
+ current_win = win
+
+ local font = lg.getFont()
+ local title_x, title_y = win.x + 1, win.y + 1
+ local title_w, title_h = win.w - 2, font:getHeight() + im.padding
+ if focused_win == win and
+ rect_contains(title_x, title_y, title_w, title_h, mx - mdx, my - mdy) and
+ mouse_down then
+ next_focused = current_win
+ focus_finalized = true
+ win.x = round(win.x + mdx)
+ win.y = round(win.y + mdy)
+ title_x, title_y = win.x + 1, win.y + 1
+ end
+
+ stencil_cmd(0, 0, viewport:getDimensions())
+
+ rect_cmd(win.x, win.y, win.w, win.h, unpack(im.cols.border))
+
+ stencil_cmd(win.x, win.y, win.w, win.h)
+
+ rect_cmd(title_x, title_y, title_w, title_h, unpack(im.cols.title_bar))
+ text_cmd(title, title_x + im.padding, title_y, unpack(im.cols.title_text))
+
+ local cx, cy = win.x + 1, win.y + title_h
+ local cw, ch = win.w - 2, win.h - title_h - 1
+ win.cx, win.cy, win.cw, win.ch = cx, cy, cw, ch
+ rect_cmd(cx, cy, cw, ch, unpack(im.cols.bg))
+
+ -- resizing
+ if focused_win == win then
+ local o = im.resize_handle_size / 2
+ local hs = im.resize_handle_size
+ if rect_contains(cx + cw - o, cy + ch - o, hs, hs, mx - mdx, my - mdy) or
+ rect_contains(cx, cy + ch - o, cw, hs, mx - mdx, my - mdy) or
+ rect_contains(cx + cw - o, cy, hs, ch, mx - mdx, my - mdy) then
+ if mouse_down then
+ next_focused = current_win
+ focus_finalized = true
+
+ win.w = win.w + mdx
+ win.h = win.h + mdy
+ end
+ rect_cmd(cx + cw, cy, 1, ch + 1, unpack(im.cols.hover))
+ rect_cmd(cx, cy + ch, cw, 1, unpack(im.cols.hover))
+ end
+ end
+
+ win.w = math.max(win.w, 30)
+ win.h = math.max(win.h, title_h + 30)
+
+ stencil_cmd(cx, cy, cw, ch)
+
+ if rect_contains(cx, cy, cw, ch, mx, my) then
+ if win.content_max > win.ch then
+ win.scroll = clamp(win.scroll - scroll, 0, win.content_max - win.ch)
+ else
+ win.scroll = 0
+ end
+ win.content_max = 0
+ end
+
+ -- reset layout
+ im.layout()
+ next_x, next_y = cx + im.padding, cy - win.scroll
+
+ return true
+end
+
+function im.end_window()
+ assert(current_win, "a window must be active to end one")
+ reset_stencil()
+ current_win = nil
+end
+
+function im.begin_step()
+end
+
+function im.end_step()
+ mdx, mdy = 0, 0
+ mouse_just_up = false
+ scroll = 0
+ focused_win = next_focused
+ next_focused = nil
+ focus_finalized = false
+end
+
+function im.draw()
+ lg.setStencilTest("greater", 0)
+ for _, win in ipairs(wins) do
+ for _, cmd in ipairs(win.cmds) do
+ cmd.fn(unpack(cmd.args))
+ end
+ end
+ lg.setStencilTest()
+end
+
+function im.has_focus()
+ -- double not to ensure boolean :)
+ return not not focused_win
+end
+
+function im.mousereleased(_, _, btn)
+ if btn == 1 then
+ mouse_down = false
+ mouse_just_up = true
+ end
+end
+
+function im.mousepressed(_, _, btn)
+ if btn == 1 then
+ mouse_down = true
+ end
+end
+
+function im.mousemoved(x, y, dx, dy)
+ mx, my = x, y
+ -- accumlate for processing in step
+ mdx = mdx + dx
+ mdy = mdy + dy
+end
+
+function im.wheelmoved(_, y)
+ scroll = scroll + y * im.scroll_speed
+end
diff --git a/src/init.lua b/src/init.lua
index 37010ef..5b707a9 100644
--- a/src/init.lua
+++ b/src/init.lua
@@ -5,6 +5,7 @@ require "src.input"
require "src.textures"
require "src.phys"
require "src.sprite"
+require "src.im"
SCR_WIDTH = 320
SCR_HEIGHT = 180
diff --git a/src/input.lua b/src/input.lua
index 8029be2..c5c9012 100644
--- a/src/input.lua
+++ b/src/input.lua
@@ -55,8 +55,26 @@ function love.keypressed(key)
keyEvents[key] = true
end
-function love.mousepressed(x, y, btn)
+function love.mousepressed(_, _, btn)
mouseEvents[btn] = true
+ local sx, sy = get_mouse_pos()
+ im.mousepressed(sx, sy, btn)
+end
+
+function love.mousereleased(_, _, btn)
+ local sx, sy = get_mouse_pos()
+ im.mousereleased(sx, sy, btn)
+end
+
+function love.mousemoved(_, _, dx, dy)
+ local sx, sy = get_mouse_pos()
+ local scrw, scrh = love.graphics.getDimensions()
+ local rdx, rdy = dx / scrw * SCR_WIDTH, dy / scrh * SCR_HEIGHT
+ im.mousemoved(sx, sy, rdx, rdy)
+end
+
+function love.wheelmoved(...)
+ im.wheelmoved(...)
end
function get_mouse_pos()
diff --git a/src/objs/player.lua b/src/objs/player.lua
index bc71ea7..0903372 100644
--- a/src/objs/player.lua
+++ b/src/objs/player.lua
@@ -14,6 +14,8 @@ function body_sys(ent, dt)
ent.y = ent.box.y
end
+local tile = 1
+
function player_movement_sys(player, dt)
local inpx, inpy = input_direction("Left", "Right", "Up", "Down")
inpx, inpy = normalize(inpx, inpy)
@@ -21,24 +23,40 @@ function player_movement_sys(player, dt)
player.vy = dlerp(player.vy, inpy * PLAYER_SPEED, 25 * dt)
-- Testicle stuff, remove when testising is no longer needed
- if is_input_pressed("Right_Click") then
- local scn = get_current_scene()
- assert(scn, "no scene set.")
+ if not im.has_focus() then
+ if is_input_pressed("Right_Click") then
+ local scn = get_current_scene()
+ assert(scn, "no scene set.")
- local mx, my = get_mouse_pos()
- local tx, ty = to_tile_coords(mx, my)
- set_tile(scn.tilemap, tx, ty, 1)
- end
- if is_input_pressed("Left_Click") then
- local scn = get_current_scene()
- assert(scn, "no scene set.")
+ local mx, my = get_mouse_pos()
+ local tx, ty = to_tile_coords(mx, my)
+ set_tile(scn.tilemap, tx, ty, tile)
+ end
+ if is_input_pressed("Left_Click") then
+ local scn = get_current_scene()
+ assert(scn, "no scene set.")
- local mx, my = get_mouse_pos()
- local tx, ty = to_tile_coords(mx, my)
- remove_tile(scn.tilemap, tx, ty)
+ local mx, my = get_mouse_pos()
+ local tx, ty = to_tile_coords(mx, my)
+ remove_tile(scn.tilemap, tx, ty)
+ end
end
end
+function player_ui_sys(_)
+ im.begin_window("Room Editor", 5, 5, 50, 50, {})
+ im.layout({0.5, 0.75, 1})
+ im.text("Tile: " .. tostring(tile))
+ if im.button(" - ") then
+ tile = math.max(tile - 1, 0)
+ end
+ if im.button("+ ") then
+ tile = tile + 1
+ end
+ im.layout()
+ im.end_window()
+end
+
function new_player(x, y)
local ent = new_entity()
add_comp(ent, "Body", x, y, 16, 16, {
diff --git a/src/utils.lua b/src/utils.lua
index 29c3024..33ca7e8 100644
--- a/src/utils.lua
+++ b/src/utils.lua
@@ -18,6 +18,12 @@ function min(a, b)
return b
end
+function pop(tbl)
+ local last = tbl[#tbl]
+ tbl[#tbl] = nil
+ return last
+end
+
function normalize(x, y)
if x == 0 and y == 0 then
return 0, 0
@@ -38,11 +44,7 @@ function dist(x1, y1, x2, y2)
return (dx*dx + dy*dy)^0.5
end
-mathx = {}
-
-mathx.tau = math.pi * 2
-
-function mathx.sign(x)
+function sign(x)
if x == 0 then
return 0
end