feat(map-editor): v0.2.0c.4 — G-toggle mode-aware world overlay

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) <noreply@anthropic.com>
This commit is contained in:
Calic
2026-06-01 17:19:30 +02:00
parent 7df21d62fa
commit 5a3f633ab2
2 changed files with 63 additions and 30 deletions

View File

@@ -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 -- Interactive map editor — Rev 4 UX architecture (see design paper
-- 2026-05-28-autotile-blob-styles-design.md §11 + plan stub -- 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 -- 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) 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) 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 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.toggle_debug_overlay() state.debug_overlay = not state.debug_overlay end
function M.is_debug_overlay() return 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 -- Used by tests to reset between asserts
function M.reset() function M.reset()
state.map_id = nil state.map_id = nil
@@ -182,6 +186,7 @@ local state = (function()
state.snap_vertex = nil state.snap_vertex = nil
state.drag_paint = nil state.drag_paint = nil
state.debug_overlay = false state.debug_overlay = false
state.world_overlay = true
for name in pairs(state.layer_visible) do for name in pairs(state.layer_visible) do
state.layer_visible[name] = true state.layer_visible[name] = true
end end
@@ -313,6 +318,7 @@ local actions = (function()
-- 0.2.0c.3 actions -- 0.2.0c.3 actions
function M.toggle_debug_overlay() state.toggle_debug_overlay() end function M.toggle_debug_overlay() state.toggle_debug_overlay() end
function M.toggle_world_overlay() state.toggle_world_overlay() end
return M return M
end)() end)()
@@ -423,6 +429,7 @@ local ui = (function()
{ key="H", action="Toggle tile flip 0<->1 (Direct-mode override)" }, { key="H", action="Toggle tile flip 0<->1 (Direct-mode override)" },
{ key="0", action="Reset transform (rot=0, flip=0)" }, { key="0", action="Reset transform (rot=0, flip=0)" },
{ key="D", action="Toggle debug overlay (per-cell slot/rot labels)" }, { 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="Esc", action="Quit editor" },
{ key="LMB-drag canvas (Auto-Tile)", action="Stroke-paint vertices (any-corner fill)" }, { key="LMB-drag canvas (Auto-Tile)", action="Stroke-paint vertices (any-corner fill)" },
{ key="RMB-drag canvas (Auto-Tile)", action="Stroke-clear vertices" }, { key="RMB-drag canvas (Auto-Tile)", action="Stroke-clear vertices" },
@@ -750,22 +757,33 @@ local ui = (function()
end end
-- World-space overlay rendered between maps.draw_map and camera.finish. -- World-space overlay rendered between maps.draw_map and camera.finish.
-- Auto-Tile mode only — shows the cell grid + vertex dots + snapped -- 0.2.0c.4: G-toggle gates the overlay; the displayed grid is
-- vertex highlight so the painter can see where a click will land. -- 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. -- snap = { vx, vy } or nil when the cursor is off-map.
function M.draw_world_overlay(map_w, map_h, t_size, snap) 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. if mode == "auto-tile" then
local grid_col = rgba(COL_VERTEX_GRID, 0x40) -- Map-grid lines: boundaries between adjacent map-tiles, which
for x = 0, map_w do -- in dual-grid sit at half-tile offsets from render-cell
engine.render.draw_line(x * t_size, 0, x * t_size, map_h * t_size, grid_col) -- 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 end
for y = 0, map_h do for y = 0, map_h - 1 do
engine.render.draw_line(0, y * t_size, map_w * t_size, y * t_size, grid_col) local py = y * t_size + half
engine.render.draw_line(0, py, map_w * t_size, py, map_col)
end end
-- Vertex dots (small 4 px squares centered on each vertex). -- Vertex dots (paint snap-targets, one per map-tile centre).
local dot_col = rgba(COL_VERTEX_DOT, 0xC0) local dot_col = rgba(COL_VERTEX_DOT, 0xC0)
for vy = 0, map_h do for vy = 0, map_h do
for vx = 0, map_w do for vx = 0, map_w do
@@ -775,13 +793,24 @@ local ui = (function()
end end
end end
-- Snapped vertex highlight (larger ring around the snap target). -- Snapped vertex highlight (where LMB will paint).
if snap then if snap then
local px = snap.vx * t_size local px = snap.vx * t_size
local py = snap.vy * t_size local py = snap.vy * t_size
engine.render.draw_rect_lines(px - 6, py - 6, 12, 12, engine.render.draw_rect_lines(px - 6, py - 6, 12, 12,
rgba(COL_VERTEX_SNAP, 0xFF)) rgba(COL_VERTEX_SNAP, 0xFF))
end 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 end
-- Dispatch a toolbar-button click to the appropriate action. -- Dispatch a toolbar-button click to the appropriate action.
@@ -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("flip", { "h" }) -- 0.2.0c
input.bind("reset_transform", { "0" }) -- 0.2.0c input.bind("reset_transform", { "0" }) -- 0.2.0c
input.bind("toggle_debug_overlay", { "d" }) -- 0.2.0c.3 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 -- Frame hooks
@@ -968,6 +998,9 @@ function update(ctx, dt)
if input.was_action_pressed("toggle_debug_overlay") then if input.was_action_pressed("toggle_debug_overlay") then
actions.toggle_debug_overlay() actions.toggle_debug_overlay()
end 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 -- Mouse clicks + drag: hit-test UI on press; if consumed we never
-- enter drag-mode for this stroke. Otherwise the press initiates a -- enter drag-mode for this stroke. Otherwise the press initiates a

View File

@@ -1,6 +1,6 @@
{ {
"id": "map-editor", "id": "map-editor",
"version": "0.2.0c.3", "version": "0.2.0c.4",
"kind": "module", "kind": "module",
"api": "^0.1", "api": "^0.1",
"ci_frames": 30, "ci_frames": 30,
@@ -9,7 +9,7 @@
"blob_rect_stone": "lib-asset.prototype-blob-geom" "blob_rect_stone": "lib-asset.prototype-blob-geom"
}, },
"deps": [ "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.render","version":"0.1.0"},
{"id":"lib-core.camera","version":"0.3.0"}, {"id":"lib-core.camera","version":"0.3.0"},
{"id":"lib-core.input","version":"0.4.0"}, {"id":"lib-core.input","version":"0.4.0"},