diff --git a/src/state.lua b/src/state.lua new file mode 100644 index 0000000..cd3227f --- /dev/null +++ b/src/state.lua @@ -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