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
|
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
|
-- Public API
|
||||||
-- =====================================================================
|
-- =====================================================================
|
||||||
@@ -277,10 +297,20 @@ local M = {}
|
|||||||
|
|
||||||
function M.load(path)
|
function M.load(path)
|
||||||
local raw = engine.asset.load_json(path)
|
local raw = engine.asset.load_json(path)
|
||||||
validate_map_table(raw, path)
|
if raw.schema_version == nil or raw.schema_version == 1 then
|
||||||
local full_id = resolve_tilemap_id(raw.tilemap)
|
raw = upgrade_v1_to_v2(raw)
|
||||||
local tilemap = load_tilemap(full_id)
|
engine.print(string.format("maps.load: auto-upgraded v1 map '%s' to v2", raw.id or "<unnamed>"))
|
||||||
local map = build_map(raw, tilemap)
|
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
|
if map_registry[map.id] then
|
||||||
error(string.format("maps.load: map-id '%s' already registered", map.id))
|
error(string.format("maps.load: map-id '%s' already registered", map.id))
|
||||||
end
|
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
|
if tx < 0 or tx >= m.size.w or ty < 0 or ty >= m.size.h then
|
||||||
return nil
|
return nil
|
||||||
end
|
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]
|
local palette_id = m.tiles[ty * m.size.w + tx + 1]
|
||||||
return m.tilemap.tiles[palette_id]
|
return m.tilemap.tiles[palette_id]
|
||||||
end
|
end
|
||||||
@@ -346,7 +388,12 @@ end
|
|||||||
|
|
||||||
function M.tilemap_id(map_id)
|
function M.tilemap_id(map_id)
|
||||||
local id = map_id or current_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
|
end
|
||||||
|
|
||||||
function M.current()
|
function M.current()
|
||||||
@@ -368,6 +415,38 @@ function M.list()
|
|||||||
return out
|
return out
|
||||||
end
|
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.
|
-- Resolve tilemap-tile atlas-ids to texture-handles via asset-lib.
|
||||||
-- Operates on the current map's tilemap; call after maps.set_current.
|
-- Operates on the current map's tilemap; call after maps.set_current.
|
||||||
|
|||||||
Reference in New Issue
Block a user