diff options
| author | iamcheeseman <[email protected]> | 2026-03-13 15:25:03 -0400 |
|---|---|---|
| committer | iamcheeseman <[email protected]> | 2026-03-13 15:25:03 -0400 |
| commit | 332d00e92bf620b6dd11dc10096ead7a5badd7f4 (patch) | |
| tree | d0e557afe65949d0d7de2f7aa97a8327f1537360 | |
| parent | b47490aea2f63d881a2cc69a326b03abae1a46c0 (diff) | |
Add basic entity editor
| -rw-r--r-- | main.lua | 2 | ||||
| -rw-r--r-- | src/im.lua | 3 | ||||
| -rw-r--r-- | src/init.lua | 5 | ||||
| -rw-r--r-- | src/objs/muntik.lua | 5 | ||||
| -rw-r--r-- | src/objs/room_editor.lua | 65 | ||||
| -rw-r--r-- | src/room_editor.lua | 283 | ||||
| -rw-r--r-- | src/utils.lua | 2 |
7 files changed, 296 insertions, 69 deletions
@@ -14,6 +14,8 @@ function love.load() event_bind(scn.on_update, "Room_Editor", room_editor_ui_sys) event_bind(scn.on_ui, "Room_Editor", tile_place_sys) + event_bind(scn.on_draw, "Editor_Entity", draw_editor_entity_sys) + event_bind(scn.on_ui, "Editor_Entity", editor_entity_ui_sys) set_scene(scn) @@ -281,7 +281,7 @@ function im.slider(val, min, max, step, h) rect_cmd(x, y, w, h, unpack(col)) p = handle / max_w - return clamp(p * (max - min) + min, min, max) + return snap(clamp(p * (max - min) + min, min, max), step) end function im.begin_window(title, x, y, w, h, opts) @@ -422,6 +422,7 @@ function im.draw() for _, cmd in ipairs(win.cmds) do cmd.fn(unpack(cmd.args)) end + win.cmds = {} end lg.setStencilTest() end diff --git a/src/init.lua b/src/init.lua index a8d106b..246147a 100644 --- a/src/init.lua +++ b/src/init.lua @@ -6,6 +6,7 @@ require "src.textures" require "src.phys" require "src.sprite" require "src.im" +require "src.room_editor" SCR_WIDTH = 320 SCR_HEIGHT = 180 @@ -16,8 +17,8 @@ register_input("Right", {{"key", "right"}, {"key", "d"}}) register_input("Down", {{"key", "down"}, {"key", "s"}}) register_input("Up", {{"key", "up"}, {"key", "w"}}) -register_input("Right_Click", {{"mouse", 1}}) -register_input("Left_Click", {{"mouse", 2}}) +register_input("Right_Click", {{"mouse", 2}}) +register_input("Left_Click", {{"mouse", 1}}) lg = love.graphics lf = love.filesystem diff --git a/src/objs/muntik.lua b/src/objs/muntik.lua new file mode 100644 index 0000000..3db7d2b --- /dev/null +++ b/src/objs/muntik.lua @@ -0,0 +1,5 @@ +define_entity("Muntik", "res/img/muntik.ase", { + {"face_dir", 1, min=-1, max=1, step=1} +}, function() +end) + diff --git a/src/objs/room_editor.lua b/src/objs/room_editor.lua deleted file mode 100644 index 70f0a86..0000000 --- a/src/objs/room_editor.lua +++ /dev/null @@ -1,65 +0,0 @@ -register_input("Next_Tileset", {{"key", "t"}}) -register_input("Prev_Tileset", {{"key", "r"}}) - -register_comp("Room_Editor", function(ent) - ent.room_editor = { - tile = 1, - } -end) - -function tile_place_sys(ent) - local room_editor = ent.room_editor - - 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, room_editor.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) - end - end - - if is_input_just_pressed("Next_Tileset") then - room_editor.tile = math.min(room_editor.tile + 1, get_tileset_count()) - end - if is_input_just_pressed("Prev_Tileset") then - room_editor.tile = math.max(room_editor.tile - 1, 1) - end -end - -function room_editor_ui_sys(ent) - local room_editor = ent.room_editor - im.begin_window("Room Editor", 120, 5, 180, 320, {}) - im.text("Tile: " .. tostring(room_editor.tile)) - im.separator() - im.layout({0.1, 0.6, 1}) - - for tileset_id=1, get_tileset_count() do - local text = " " - if tileset_id == room_editor.tile then - text = "*" - elseif tileset_id == room_editor.tile - 1 then - text = "r" - elseif tileset_id == room_editor.tile + 1 then - text = "t" - end - im.text(text) - if im.button("select") then - room_editor.tile = tileset_id - end - im.image(TILE_TEX, get_tileset_quad(tileset_id)) - end - - im.layout() - im.end_window() -end diff --git a/src/room_editor.lua b/src/room_editor.lua new file mode 100644 index 0000000..01d5c90 --- /dev/null +++ b/src/room_editor.lua @@ -0,0 +1,283 @@ +register_input("Next_Tileset", {{"key", "v"}}) +register_input("Prev_Tileset", {{"key", "c"}}) +register_input("Tile_Brush", {{"key", "t"}}) +register_input("Entity_Brush", {{"key", "e"}}) +register_input("Delete", {{"key", "delete"}, {"key", "backspace"}}) + +local basic_entity_properties = { + {"flip_h", false}, + {"flip_v", false}, +} + +local entity_definitions = {} + +function define_entity(name, tex_name, properties, spawner) + table.insert(entity_definitions, name) + entity_definitions[name] = { + spawner = spawner, + tex_name = tex_name, + properties = properties, + idx = #entity_definitions, + } +end + +register_comp("Room_Editor", function(ent) + ent.room_editor = { + -- Valid values are Tile, Entity + -- TODO: + -- * Prop (place stuff on top of tiles automatically) + brush = "Tile", + tile = 1, + entity = "Muntik", + entity_snap = true, + selected_ent = -1, + placed_entities = {}, + } +end) + +register_comp("Editor_Entity", function(ent, room_editor, ent_type, x, y) + ent.x = x + ent.y = y + local entity_def = entity_definitions[ent_type] + local properties = {} + for _, property in ipairs(basic_entity_properties) do + local name = property[1] + local default = property[2] + properties[name] = default + end + for _, property in ipairs(entity_def.properties) do + local name = property[1] + local default = property[2] + properties[name] = default + end + ent.editor_dat = { + type = ent_type, + room_editor = room_editor, + properties = properties, + } +end) + +local function get_selected_entity(room_editor) + return room_editor.placed_entities[room_editor.selected_ent] +end + +local function new_editor_entity(room_editor, ent_type, x, y) + local ent = new_entity() + add_comp(ent, "Editor_Entity", room_editor, ent_type, x, y) + add_comp(ent, "Sprite", entity_definitions[ent_type].tex_name, {}) + return ent +end + +function draw_editor_entity_sys(ent) + local editor_dat = ent.editor_dat + local selected_ent = get_selected_entity(editor_dat.room_editor) + + if selected_ent == ent then + -- show selected mhmm + lg.rectangle("line", ent.x, ent.y, ent.sprite.width, ent.sprite.height) + end +end + +local property_ui = { + ["number"] = function(name, editor_dat, property) + im.layout({0.5, 1}) + im.text(name) + im.text(tostring(snap(editor_dat.properties[name], 0.01))) + im.layout() + editor_dat.properties[name] = im.slider( + editor_dat.properties[name], + property.min or 0, + property.max or 1, + property.step or 0.1 + ) + end, + ["boolean"] = function(name, editor_dat, property) + im.layout({0.25, 0.5, 1}) + im.text(name) + im.text(tostring(editor_dat.properties[name])) + if im.button("Toggle") then + editor_dat.properties[name] = not editor_dat.properties[name] + end + end, +} + +local function property_list(ent, properties) + local editor_dat = ent.editor_dat + for _, property in ipairs(properties) do + local name = property[1] + local prop_type = type(property[2]) + property_ui[prop_type](name, editor_dat, property) + im.layout() + end +end + +function editor_entity_ui_sys(ent) + local editor_dat = ent.editor_dat + if editor_dat.room_editor.brush ~= "Entity" then + return + end + + local selected_ent = get_selected_entity(editor_dat.room_editor) + + if selected_ent == ent then + local entity_def = entity_definitions[editor_dat.type] + im.begin_window("Entity Properties", 190, 5, 180, 340, {}) + im.text("Base Properties") + im.separator() + property_list(ent, basic_entity_properties) + im.separator() + im.text("Type Properties") + im.separator() + property_list(ent, entity_def.properties) + im.end_window() + end +end + +local function get_snapped_to_grid(room_editor, x, y) + if room_editor.entity_snap then + return snap(x, TILESIZE), snap(y, TILESIZE) + else + -- Still snap to the pixel cause yes + return round(x), round(y) + end +end + +local brushes = { + ["Tile"] = function(room_editor) + 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, room_editor.tile) + end + 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) + remove_tile(scn.tilemap, tx, ty) + end + end, + ["Entity"] = function(room_editor) + local mx, my = get_mouse_pos() + if is_input_just_pressed("Left_Click") then + -- first check if we're selecting an entity + local selected = false + for i, ent in ipairs(room_editor.placed_entities) do + if point_in_rect( + mx, my, + ent.x, ent.y, + ent.sprite.width, ent.sprite.height + ) then + room_editor.selected_ent = i + selected = true + break + end + end + + -- Place one frfr + if not selected then + mx, my = get_snapped_to_grid(room_editor, mx, my) + local ent = new_editor_entity(room_editor, room_editor.entity, mx, my) + table.insert(room_editor.placed_entities, ent) + room_editor.selected_ent = #room_editor.placed_entities + end + end + end +} + +function tile_place_sys(ent) + local room_editor = ent.room_editor + + if not im.has_focus() then + brushes[room_editor.brush](room_editor) + end + + if is_input_just_pressed("Next_Tileset") then + room_editor.tile = math.min(room_editor.tile + 1, get_tileset_count()) + end + if is_input_just_pressed("Prev_Tileset") then + room_editor.tile = math.max(room_editor.tile - 1, 1) + end + + if is_input_just_pressed("Tile_Brush") then + room_editor.brush = "Tile" + end + if is_input_just_pressed("Entity_Brush") then + room_editor.brush = "Entity" + end +end + +function room_editor_ui_sys(ent) + local room_editor = ent.room_editor + + im.begin_window("Room Editor", 5, 5, 180, 180, {}) + im.text("Brushes") + im.layout({0.1, 1}) + im.text("t") + if im.button("Tile") then + room_editor.brush = "Tile" + end + im.text("e") + if im.button("Entity") then + room_editor.brush = "Entity" + end + im.layout() + im.end_window() + + if room_editor.brush == "Tile" then + im.begin_window("Tile Editor", 5, 190, 180, 340, {}) + im.text("Tile: " .. tostring(room_editor.tile)) + im.separator() + im.layout({0.1, 0.6, 1}) + + for tileset_id=1, get_tileset_count() do + local text = " " + if tileset_id == room_editor.tile then + text = "*" + elseif tileset_id == room_editor.tile - 1 then + text = "c" + elseif tileset_id == room_editor.tile + 1 then + text = "v" + end + im.text(text) + if im.button("select") then + room_editor.tile = tileset_id + end + im.image(TILE_TEX, get_tileset_quad(tileset_id)) + end + + im.layout() + im.end_window() + elseif room_editor.brush == "Entity" then + im.begin_window("Entity Editor", 5, 190, 180, 340, {}) + im.layout({0.2, 1}) + + im.text(room_editor.entity_snap and "On" or "Off") + if im.button("Position Snapping") then + room_editor.entity_snap = not room_editor.entity_snap + end + + im.layout({0.1, 0.6, 1}) + + for _, entity_name in ipairs(entity_definitions) do + local entity_def = entity_definitions[entity_name] + + local text = " " + if entity_name == room_editor.entity then + text = "*" + end + im.text(text) + if im.button("select") then + room_editor.entity = entity_name + end + im.image(get_tex(entity_def.tex_name)) + end + + im.layout() + im.end_window() + end +end diff --git a/src/utils.lua b/src/utils.lua index ec5d16e..55a9d35 100644 --- a/src/utils.lua +++ b/src/utils.lua @@ -54,7 +54,7 @@ function clamp(a, min, max) end function snap(x, step) - return math.floor(x / step) * step + return round(x / step) * step end function frac(x) |
