Add editor state module with accessor API

Singleton table covering map id+path, active layer/atlas/tile/rot,
roof-mode flag, per-layer visibility flags, dirty flag, and picker
open/close state. Mutations go through setters so the test module
can verify state transitions without poking the internal table.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Calic
2026-05-24 01:09:08 +02:00
parent 45992065a1
commit 0b2eb20199

96
src/state.lua Normal file
View File

@@ -0,0 +1,96 @@
-- src/state.lua
-- Editor state singleton + accessors.
local M = {}
-- =====================================================================
-- State (module-singleton)
-- =====================================================================
local state = {
map_id = nil, -- set by init.lua after maps.load()
map_path = "maps/work.map.json",
active_layer = "surface", -- one of VALID_LAYER_NAMES
active_atlas = 0, -- atlas_index 0..N-1
active_tile = 1, -- tile_id
active_rot = 0, -- 0..3
roof_mode = false, -- if true, paint targets roof
layer_visible = {
foundation=true, subsurface=true, surface=true, topsurface=true,
lower_wall=true, wall=true, upper_wall=true, canopy=true,
},
dirty = false,
picker_open = false, -- atlas+tile picker expanded?
mouse_cell = nil, -- {x, y} or nil if mouse off-map
}
-- =====================================================================
-- Setters / getters (encapsulate state for testability)
-- =====================================================================
function M.set_map(map_id, path)
state.map_id = map_id
state.map_path = path
end
function M.get_map_id() return state.map_id end
function M.get_map_path() return state.map_path end
function M.set_active_layer(name) state.active_layer = name end
function M.get_active_layer() return state.active_layer end
function M.set_active_atlas(idx) state.active_atlas = idx end
function M.get_active_atlas() return state.active_atlas end
function M.set_active_tile(id) state.active_tile = id end
function M.get_active_tile() return state.active_tile end
function M.cycle_rotation()
state.active_rot = (state.active_rot + 1) % 4
end
function M.get_active_rot() return state.active_rot end
function M.toggle_roof_mode()
state.roof_mode = not state.roof_mode
end
function M.is_roof_mode() return state.roof_mode end
function M.toggle_layer_visible(name)
state.layer_visible[name] = not state.layer_visible[name]
end
function M.is_layer_visible(name) return state.layer_visible[name] ~= false end
function M.mark_dirty() state.dirty = true end
function M.mark_clean() state.dirty = false end
function M.is_dirty() return state.dirty end
function M.toggle_picker_open()
state.picker_open = not state.picker_open
end
function M.is_picker_open() return state.picker_open end
function M.set_mouse_cell(x, y)
if x == nil then
state.mouse_cell = nil
else
state.mouse_cell = { x = x, y = y }
end
end
function M.get_mouse_cell() return state.mouse_cell end
-- Used by tests to reset between asserts
function M.reset()
state.map_id = nil
state.active_layer = "surface"
state.active_atlas = 0
state.active_tile = 1
state.active_rot = 0
state.roof_mode = false
state.dirty = false
state.picker_open = false
state.mouse_cell = nil
for name in pairs(state.layer_visible) do
state.layer_visible[name] = true
end
end
return M