From 5a3f633ab264a8543c9336b8d766552bab10b618 Mon Sep 17 00:00:00 2001 From: Calic Date: Mon, 1 Jun 2026 17:19:30 +0200 Subject: [PATCH] =?UTF-8?q?feat(map-editor):=20v0.2.0c.4=20=E2=80=94=20G-t?= =?UTF-8?q?oggle=20mode-aware=20world=20overlay?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a G key that toggles the world-space overlay on and off (default on). The overlay adapts to the active painting mode: - Auto-Tile mode shows the map-grid (paint-tile boundaries, which sit at half-tile offsets from the render-cell boundaries in dual-grid), the per-map-tile vertex dots, and the snapped paint-target highlight. - Direct mode shows only a subtle render-cell grid, since LMB strokes write directly to render-cells in that mode. Bumps the lib-core.maps dep pin to 0.5.6 to pick up the dual-grid bitmask fix that makes the auto-tile output match the dual-grid mental model. Co-Authored-By: Claude Opus 4.7 (1M context) --- init.lua | 89 +++++++++++++++++++++++++++++++++---------------- manifest.module | 4 +-- 2 files changed, 63 insertions(+), 30 deletions(-) diff --git a/init.lua b/init.lua index 776804b..7471e5f 100644 --- a/init.lua +++ b/init.lua @@ -1,4 +1,4 @@ --- sporel-module-map-editor v0.2.0c.3 +-- sporel-module-map-editor v0.2.0c.4 -- Interactive map editor — Rev 4 UX architecture (see design paper -- 2026-05-28-autotile-blob-styles-design.md §11 + plan stub -- 2026-05-28-map-editor-blob-v2.md). 0.2.0b adds the bottom palette @@ -71,6 +71,7 @@ local state = (function() snap_vertex = nil, -- 0.2.0b: {vx, vy} or nil (auto-tile mode snap target) drag_paint = nil, -- 0.2.0c.1: nil | "paint" | "erase" (active drag-stroke mode) debug_overlay = false, -- 0.2.0c.3: D toggles per-cell slot/rot annotations + world_overlay = true, -- 0.2.0c.4: G toggles render-grid + map-grid + coords } -- ===================================================================== @@ -165,6 +166,9 @@ local state = (function() function M.toggle_debug_overlay() state.debug_overlay = not state.debug_overlay end function M.is_debug_overlay() return state.debug_overlay end + function M.toggle_world_overlay() state.world_overlay = not state.world_overlay end + function M.is_world_overlay() return state.world_overlay end + -- Used by tests to reset between asserts function M.reset() state.map_id = nil @@ -182,6 +186,7 @@ local state = (function() state.snap_vertex = nil state.drag_paint = nil state.debug_overlay = false + state.world_overlay = true for name in pairs(state.layer_visible) do state.layer_visible[name] = true end @@ -313,6 +318,7 @@ local actions = (function() -- 0.2.0c.3 actions function M.toggle_debug_overlay() state.toggle_debug_overlay() end + function M.toggle_world_overlay() state.toggle_world_overlay() end return M end)() @@ -423,6 +429,7 @@ local ui = (function() { key="H", action="Toggle tile flip 0<->1 (Direct-mode override)" }, { key="0", action="Reset transform (rot=0, flip=0)" }, { key="D", action="Toggle debug overlay (per-cell slot/rot labels)" }, + { key="G", action="Toggle world overlay (Auto-Tile: map-grid + dots; Direct: cell-grid)" }, { key="Esc", action="Quit editor" }, { key="LMB-drag canvas (Auto-Tile)", action="Stroke-paint vertices (any-corner fill)" }, { key="RMB-drag canvas (Auto-Tile)", action="Stroke-clear vertices" }, @@ -750,37 +757,59 @@ local ui = (function() end -- World-space overlay rendered between maps.draw_map and camera.finish. - -- Auto-Tile mode only — shows the cell grid + vertex dots + snapped - -- vertex highlight so the painter can see where a click will land. + -- 0.2.0c.4: G-toggle gates the overlay; the displayed grid is + -- mode-aware. Auto-tile mode paints map-tiles, so we show the + -- map-grid (paint-tile boundaries, offset by half a tile from + -- render-cell boundaries) plus the vertex dots and snap target. + -- Direct mode paints cells, so we show the render-cell grid with + -- a lighter stroke and skip the dots / snap. -- snap = { vx, vy } or nil when the cursor is off-map. function M.draw_world_overlay(map_w, map_h, t_size, snap) - if state.get_mode() ~= "auto-tile" then return end + if not state.is_world_overlay() then return end + local mode = state.get_mode() - -- Grid lines (1 px) at every cell boundary. - local grid_col = rgba(COL_VERTEX_GRID, 0x40) - for x = 0, map_w do - engine.render.draw_line(x * t_size, 0, x * t_size, map_h * t_size, grid_col) - end - for y = 0, map_h do - engine.render.draw_line(0, y * t_size, map_w * t_size, y * t_size, grid_col) - end - - -- Vertex dots (small 4 px squares centered on each vertex). - local dot_col = rgba(COL_VERTEX_DOT, 0xC0) - for vy = 0, map_h do - for vx = 0, map_w do - local cx = vx * t_size - 2 - local cy = vy * t_size - 2 - engine.render.draw_rect(cx, cy, 4, 4, dot_col) + if mode == "auto-tile" then + -- Map-grid lines: boundaries between adjacent map-tiles, which + -- in dual-grid sit at half-tile offsets from render-cell + -- boundaries (= at render-cell centres). + local map_col = rgba(COL_VERTEX_GRID, 0x60) + local half = t_size / 2 + for x = 0, map_w - 1 do + local px = x * t_size + half + engine.render.draw_line(px, 0, px, map_h * t_size, map_col) + end + for y = 0, map_h - 1 do + local py = y * t_size + half + engine.render.draw_line(0, py, map_w * t_size, py, map_col) end - end - -- Snapped vertex highlight (larger ring around the snap target). - if snap then - local px = snap.vx * t_size - local py = snap.vy * t_size - engine.render.draw_rect_lines(px - 6, py - 6, 12, 12, - rgba(COL_VERTEX_SNAP, 0xFF)) + -- Vertex dots (paint snap-targets, one per map-tile centre). + local dot_col = rgba(COL_VERTEX_DOT, 0xC0) + for vy = 0, map_h do + for vx = 0, map_w do + local cx = vx * t_size - 2 + local cy = vy * t_size - 2 + engine.render.draw_rect(cx, cy, 4, 4, dot_col) + end + end + + -- Snapped vertex highlight (where LMB will paint). + if snap then + local px = snap.vx * t_size + local py = snap.vy * t_size + engine.render.draw_rect_lines(px - 6, py - 6, 12, 12, + rgba(COL_VERTEX_SNAP, 0xFF)) + end + else + -- Direct mode: subtle render-cell grid only — that's the + -- grid LMB strokes write to. + local cell_col = rgba(COL_VERTEX_GRID, 0x25) + for x = 0, map_w do + engine.render.draw_line(x * t_size, 0, x * t_size, map_h * t_size, cell_col) + end + for y = 0, map_h do + engine.render.draw_line(0, y * t_size, map_w * t_size, y * t_size, cell_col) + end end end @@ -885,8 +914,9 @@ input.bind("toggle_cheatsheet", { "i" }) -- 0.2.0a — `i` for Info/help (no input.bind("flip", { "h" }) -- 0.2.0c input.bind("reset_transform", { "0" }) -- 0.2.0c input.bind("toggle_debug_overlay", { "d" }) -- 0.2.0c.3 +input.bind("toggle_world_overlay", { "g" }) -- 0.2.0c.4 -engine.print("map-editor v0.2.0c.3: ready. Tab=mode, I=help, D=debug, S=save, E=erase, R=rotate, H=flip, 0=reset, ESC=quit") +engine.print("map-editor v0.2.0c.4: ready. Tab=mode, I=help, G=grid, D=debug, S=save, E=erase, R=rotate, H=flip, 0=reset, ESC=quit") -- ===================================================================== -- Frame hooks @@ -968,6 +998,9 @@ function update(ctx, dt) if input.was_action_pressed("toggle_debug_overlay") then actions.toggle_debug_overlay() end + if input.was_action_pressed("toggle_world_overlay") then + actions.toggle_world_overlay() + end -- Mouse clicks + drag: hit-test UI on press; if consumed we never -- enter drag-mode for this stroke. Otherwise the press initiates a diff --git a/manifest.module b/manifest.module index 4777539..7ac9453 100644 --- a/manifest.module +++ b/manifest.module @@ -1,6 +1,6 @@ { "id": "map-editor", - "version": "0.2.0c.3", + "version": "0.2.0c.4", "kind": "module", "api": "^0.1", "ci_frames": 30, @@ -9,7 +9,7 @@ "blob_rect_stone": "lib-asset.prototype-blob-geom" }, "deps": [ - {"id":"lib-core.maps","version":"0.5.5"}, + {"id":"lib-core.maps","version":"0.5.6"}, {"id":"lib-core.render","version":"0.1.0"}, {"id":"lib-core.camera","version":"0.3.0"}, {"id":"lib-core.input","version":"0.4.0"},