Load schema-v2 maps with multi-layer storage and roof field
Replace the v1-only M.load path with a unified v2 load path that
auto-upgrades v1 JSON on read. Add build_map_v2 to produce map records
with atlases[], layers{}, and roof arrays. Add atlas_count, atlas_id_at,
has_layer, and is_indoor to the public API. Update tile_at to decode GIDs
from the surface layer for v2 maps, keeping M.create/build_map for
programmatic callers that supply a v1-style tilemap_table directly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
89
init.lua
89
init.lua
@@ -269,6 +269,26 @@ local function build_map(t_map, tilemap)
|
||||
}
|
||||
end
|
||||
|
||||
local function build_map_v2(t_map, resolved_atlases)
|
||||
local size = t_map.size
|
||||
return {
|
||||
id = t_map.id,
|
||||
schema_version = 2,
|
||||
size = size,
|
||||
tile_size = resolved_atlases[1].tile_size, -- assume uniform; first atlas wins
|
||||
atlases = resolved_atlases,
|
||||
atlas_aliases = t_map.atlases, -- alias strings, parallel to atlases
|
||||
layers = t_map.layers or {},
|
||||
roof = t_map.roof,
|
||||
-- DEPRECATED-MVP stubs
|
||||
walls = {},
|
||||
regions = {},
|
||||
edges = {},
|
||||
state = "Active",
|
||||
pinned = false,
|
||||
}
|
||||
end
|
||||
|
||||
-- =====================================================================
|
||||
-- Public API
|
||||
-- =====================================================================
|
||||
@@ -277,10 +297,20 @@ local M = {}
|
||||
|
||||
function M.load(path)
|
||||
local raw = engine.asset.load_json(path)
|
||||
validate_map_table(raw, path)
|
||||
local full_id = resolve_tilemap_id(raw.tilemap)
|
||||
local tilemap = load_tilemap(full_id)
|
||||
local map = build_map(raw, tilemap)
|
||||
if raw.schema_version == nil or raw.schema_version == 1 then
|
||||
raw = upgrade_v1_to_v2(raw)
|
||||
engine.print(string.format("maps.load: auto-upgraded v1 map '%s' to v2", raw.id or "<unnamed>"))
|
||||
end
|
||||
validate_map_table_v2(raw, path)
|
||||
|
||||
-- Resolve atlas-aliases (each entry is a tilemap-id, possibly local or fully-qualified)
|
||||
local resolved_atlases = {}
|
||||
for i, alias in ipairs(raw.atlases) do
|
||||
local full_id = resolve_tilemap_id(alias)
|
||||
resolved_atlases[i] = load_tilemap(full_id)
|
||||
end
|
||||
|
||||
local map = build_map_v2(raw, resolved_atlases)
|
||||
if map_registry[map.id] then
|
||||
error(string.format("maps.load: map-id '%s' already registered", map.id))
|
||||
end
|
||||
@@ -334,6 +364,18 @@ function M.tile_at(a, b, c)
|
||||
if tx < 0 or tx >= m.size.w or ty < 0 or ty >= m.size.h then
|
||||
return nil
|
||||
end
|
||||
-- v2 path: read from surface layer using GID encoding
|
||||
if m.schema_version == 2 then
|
||||
local surface = m.layers and m.layers.surface
|
||||
if not surface then return nil end
|
||||
local gid = surface.tiles[ty * m.size.w + tx + 1]
|
||||
if not gid or gid == 0 then return nil end
|
||||
local atlas_index, tile_id, _ = decode_gid(gid)
|
||||
local atlas = m.atlases[atlas_index + 1]
|
||||
if not atlas then return nil end
|
||||
return atlas.tiles[tile_id]
|
||||
end
|
||||
-- v1 legacy path (should not be reached after M.load auto-upgrades, kept for M.create)
|
||||
local palette_id = m.tiles[ty * m.size.w + tx + 1]
|
||||
return m.tilemap.tiles[palette_id]
|
||||
end
|
||||
@@ -346,7 +388,12 @@ end
|
||||
|
||||
function M.tilemap_id(map_id)
|
||||
local id = map_id or current_map_id
|
||||
return map_registry[id].tilemap.id
|
||||
local m = map_registry[id]
|
||||
-- v2: return first atlas alias (backwards-compat for single-atlas maps)
|
||||
if m.schema_version == 2 then
|
||||
return m.atlas_aliases and m.atlas_aliases[1] or (m.atlases[1] and m.atlases[1].id)
|
||||
end
|
||||
return m.tilemap.id
|
||||
end
|
||||
|
||||
function M.current()
|
||||
@@ -368,6 +415,38 @@ function M.list()
|
||||
return out
|
||||
end
|
||||
|
||||
function M.atlas_count(map_id)
|
||||
local id = map_id or current_map_id
|
||||
if not id then error("maps.atlas_count: no current map") end
|
||||
local m = map_registry[id]
|
||||
if m.schema_version ~= 2 then return 1 end -- v1 (pre-upgrade) always 1
|
||||
return #m.atlases
|
||||
end
|
||||
|
||||
function M.atlas_id_at(idx, map_id)
|
||||
local id = map_id or current_map_id
|
||||
if not id then error("maps.atlas_id_at: no current map") end
|
||||
local m = map_registry[id]
|
||||
return m.atlas_aliases and m.atlas_aliases[idx + 1]
|
||||
or m.atlases[idx + 1] and m.atlases[idx + 1].id
|
||||
end
|
||||
|
||||
function M.has_layer(layer_name, map_id)
|
||||
local id = map_id or current_map_id
|
||||
if not id then error("maps.has_layer: no current map") end
|
||||
local m = map_registry[id]
|
||||
return m.layers and m.layers[layer_name] ~= nil
|
||||
end
|
||||
|
||||
function M.is_indoor(x, y, map_id)
|
||||
local id = map_id or current_map_id
|
||||
if not id then error("maps.is_indoor: no current map") end
|
||||
local m = map_registry[id]
|
||||
if not m.roof then return false end
|
||||
if x < 0 or x >= m.size.w or y < 0 or y >= m.size.h then return false end
|
||||
return m.roof[y * m.size.w + x + 1] == 1
|
||||
end
|
||||
|
||||
-- ====================================================================
|
||||
-- Resolve tilemap-tile atlas-ids to texture-handles via asset-lib.
|
||||
-- Operates on the current map's tilemap; call after maps.set_current.
|
||||
|
||||
Reference in New Issue
Block a user