Iterate layers in z-order with entity-slot split; multi-layer draw_map

Adds LAYER_ORDER_PRE/POST_ENTITIES constants, iterate_layers_pre/post_entities
helpers, draw_layer and draw_v1_legacy locals, and draw_map_pre/post_entities
public functions. draw_map becomes a backward-compat wrapper over both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Axel Meyer
2026-05-21 15:54:27 +02:00
parent 53c9fe4c0c
commit 8cec7b6812

158
init.lua
View File

@@ -99,6 +99,10 @@ end
-- Schema-v2 Layer-Whitelist + Validation
-- =====================================================================
-- Layer z-order (back-to-front) split at entity slot
local LAYER_ORDER_PRE_ENTITIES = { "foundation", "subsurface", "surface", "topsurface" }
local LAYER_ORDER_POST_ENTITIES = { "lower_wall", "wall", "upper_wall", "canopy" }
local VALID_LAYER_NAMES = {
foundation = true,
subsurface = true,
@@ -289,6 +293,80 @@ local function build_map_v2(t_map, resolved_atlases)
}
end
-- =====================================================================
-- Internal render helpers (draw_layer, draw_v1_legacy)
-- These must be declared before the public draw_map* functions that call them.
-- =====================================================================
local function draw_layer(m, layer_name)
local layer = m.layers[layer_name]
if not layer then return end
local sz = m.size
local ts = m.tile_size
for y = 0, sz.h - 1 do
for x = 0, sz.w - 1 do
local idx = y * sz.w + x + 1
local gid = layer.tiles[idx] or 0
if gid ~= 0 then
local atlas_idx, tile_id, rot_quad = decode_gid(gid)
local atlas = m.atlases[atlas_idx + 1]
if atlas then
local tile = atlas.tiles[tile_id]
if tile then
local px = x * ts
local py = y * ts
local rot_deg = rot_quad * 90.0
if tile.texture_handle then
engine.render.draw_sprite_transform(
tile.texture_handle,
px + ts / 2, py + ts / 2,
math.rad(rot_deg),
1.0, 1.0,
ts / 2, ts / 2,
0xFFFFFFFF
)
else
local c = tile.color or { 100, 100, 100 }
engine.render.draw_rect(px, py, ts, ts,
engine.render.rgb(c[1], c[2], c[3]))
end
end
end
end
end
end
end
local function draw_v1_legacy(m)
local tm = m.tilemap
if not tm then return end
local ts = tm.tile_size
for y = 0, m.size.h - 1 do
for x = 0, m.size.w - 1 do
local idx = y * m.size.w + x + 1
local tile_id = m.tiles[idx]
local tile = tm.tiles[tile_id]
local rot_deg = (m.tile_rotations and m.tile_rotations[idx]) or 0
local px = x * ts
local py = y * ts
if tile.texture_handle then
engine.render.draw_sprite_transform(
tile.texture_handle,
px + ts / 2, py + ts / 2,
math.rad(rot_deg),
1.0, 1.0,
ts / 2, ts / 2,
0xFFFFFFFF
)
else
local c = tile.color or { 100, 100, 100 }
engine.render.draw_rect(px, py, ts, ts,
engine.render.rgb(c[1], c[2], c[3]))
end
end
end
end
-- =====================================================================
-- Public API
-- =====================================================================
@@ -549,37 +627,57 @@ function M.load_textures(asset_aliases)
end
end
function M.draw_map()
local map_id = M.current()
if map_id == nil then return end
local m = map_registry[map_id]
local tm = m.tilemap
local ts = tm.tile_size
for y = 0, m.size.h - 1 do
for x = 0, m.size.w - 1 do
local idx = y * m.size.w + x + 1
local tile_id = m.tiles[idx]
local tile = tm.tiles[tile_id]
local rot_deg = (m.tile_rotations and m.tile_rotations[idx]) or 0
local px = x * ts
local py = y * ts
if tile.texture_handle then
-- Sprite mode: draw_sprite_transform with rotation about tile center.
engine.render.draw_sprite_transform(
tile.texture_handle,
px + ts / 2, py + ts / 2,
math.rad(rot_deg),
1.0, 1.0,
ts / 2, ts / 2,
0xFFFFFFFF
)
else
-- Color fallback (Phase 1 mode).
local c = tile.color or { 100, 100, 100 }
engine.render.draw_rect(px, py, ts, ts, engine.render.rgb(c[1], c[2], c[3]))
end
end
function M.iterate_layers_pre_entities(fn, map_id)
local id = map_id or current_map_id
if not id then error("maps.iterate_layers_pre_entities: no current map") end
local m = map_registry[id]
if m.schema_version ~= 2 then
-- v1 (legacy): only surface conceptually
fn("surface")
return
end
for _, name in ipairs(LAYER_ORDER_PRE_ENTITIES) do
if m.layers[name] then fn(name) end
end
end
function M.iterate_layers_post_entities(fn, map_id)
local id = map_id or current_map_id
if not id then error("maps.iterate_layers_post_entities: no current map") end
local m = map_registry[id]
if m.schema_version ~= 2 then return end -- v1 has nothing post
for _, name in ipairs(LAYER_ORDER_POST_ENTITIES) do
if m.layers[name] then fn(name) end
end
end
function M.draw_map_pre_entities()
local id = current_map_id
if id == nil then return end
local m = map_registry[id]
if m.schema_version ~= 2 then
draw_v1_legacy(m)
return
end
for _, name in ipairs(LAYER_ORDER_PRE_ENTITIES) do
if m.layers[name] then draw_layer(m, name) end
end
end
function M.draw_map_post_entities()
local id = current_map_id
if id == nil then return end
local m = map_registry[id]
if m.schema_version ~= 2 then return end
for _, name in ipairs(LAYER_ORDER_POST_ENTITIES) do
if m.layers[name] then draw_layer(m, name) end
end
end
-- Backward-compat: draw_map renders everything (pre + post, no entity slot)
function M.draw_map()
M.draw_map_pre_entities()
M.draw_map_post_entities()
end
-- Forward-compat stubs (DEPRECATED-MVP — implemented in later slices)