feat: v0.1.2 sprite-mode in draw_map + tile_rotations + load_textures

Sprite-mode in draw_map:
- tilemap-tiles with texture atlas-id render via
  engine.render.draw_sprite_transform with rotation pivot at tile center
- tiles without texture continue to render via draw_rect with color
  (Phase 1 mode preserved)

Tile rotation:
- maps support an optional tile_rotations parallel array in the map
  JSON with per-cell 90-degree rotation values (0/90/180/270)
- absent or nil entries default to 0 (no rotation)
- enables 9-segment tile reuse via rotation rather than per-orientation
  sprites

New API:
- maps.load_textures(asset_aliases) resolves the current tilemap's
  tile atlas-ids to texture-handles via the asset-lib indirection
  (asset_aliases[tilemap.asset_pack] -> lib-id -> atlas.json lookup
  per tile)

Schema additions:
- tilemap.asset_pack (alias-key, optional)
- tile.texture (atlas-id, optional)
- map.tile_rotations (parallel array, optional)

All existing Phase 1 color-only tilemaps render unchanged.
This commit is contained in:
Axel Meyer
2026-05-18 03:21:52 +02:00
parent 34f8f897c8
commit 2b5acb5caf
3 changed files with 129 additions and 15 deletions

109
init.lua
View File

@@ -96,10 +96,22 @@ local function load_tilemap(full_id)
local path = "assets/tiles/" .. local_name .. ".tilemap.json"
local raw = engine.asset.load_json(path)
validate_tilemap_table(raw, path, local_name)
-- Cook tiles: copy raw fields and add texture + texture_handle slots.
local cooked = {}
for i, t in ipairs(raw.tiles) do
cooked[i] = {
id = t.id,
walkable = (t.walkable == true),
color = t.color, -- color-mode fallback
texture = t.texture, -- optional atlas-id
texture_handle = nil, -- populated by load_textures
}
end
local tilemap = {
id = full_id,
tile_size = raw.tile_size,
tiles = raw.tiles,
id = full_id,
tile_size = raw.tile_size,
asset_pack = raw.asset_pack, -- optional alias-key
tiles = cooked,
}
tilemap_registry[full_id] = tilemap
return tilemap
@@ -114,17 +126,18 @@ local function build_map(t_map, tilemap)
end
end
return {
id = t_map.id,
size = t_map.size,
tile_size = t_map.tile_size or tilemap.tile_size,
tiles = t_map.tiles, -- shallow-ref
tilemap = tilemap, -- shallow-ref
id = t_map.id,
size = t_map.size,
tile_size = t_map.tile_size or tilemap.tile_size,
tiles = t_map.tiles, -- shallow-ref
tile_rotations = t_map.tile_rotations, -- optional parallel array (NEW)
tilemap = tilemap, -- shallow-ref
-- DEPRECATED-MVP: forward-compat stubs (multi-map slice fills)
walls = {},
regions = {},
edges = {},
state = "Active",
pinned = false,
walls = {},
regions = {},
edges = {},
state = "Active",
pinned = false,
}
end
@@ -227,6 +240,76 @@ function M.list()
return out
end
-- ====================================================================
-- Resolve tilemap-tile atlas-ids to texture-handles via asset-lib.
-- Operates on the current map's tilemap; call after maps.set_current.
-- asset_aliases: { [alias-key] = asset-lib-id } from module's manifest.
-- ====================================================================
function M.load_textures(asset_aliases)
local map_id = M.current()
if map_id == nil then
error("maps.load_textures: no current map; call maps.set_current first")
end
local m = map_registry[map_id]
local tm = m.tilemap
if tm.asset_pack == nil then return end -- color-only tilemap, no textures
local lib_id = asset_aliases[tm.asset_pack]
if lib_id == nil then
error("maps.load_textures: asset-pack alias '" .. tm.asset_pack
.. "' not in asset_aliases")
end
local atlas_path = lib_id .. "/assets/atlas.json"
local atlas = engine.asset.load_json(atlas_path)
local pack = atlas[tm.asset_pack]
if pack == nil then
error("maps.load_textures: asset_pack '" .. tm.asset_pack
.. "' not declared in atlas of '" .. lib_id .. "'")
end
for _, tile in ipairs(tm.tiles) do
if tile.texture then
local entry = pack[tile.texture]
if entry == nil then
error("maps.load_textures: atlas-id '" .. tile.texture
.. "' not in asset_pack '" .. tm.asset_pack .. "'")
end
tile.texture_handle = engine.asset.load_texture(lib_id .. "/assets/" .. entry.file)
end
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
end
end
-- Forward-compat stubs (DEPRECATED-MVP — implemented in later slices)
function M.state(map_id)
-- DEPRECATED-MVP: lifecycle states (Virgin/Inert/Passive/Active/Pinned) — multi-map slice