Compare commits

...

2 Commits

Author SHA1 Message Date
Axel Meyer
1ee19b4b7f Migrate test assertions to atlas tile-record shape; bump to 0.3.0
tile.id is now an integer; the stable string identifier is tile.name.
Updates all tile-record string-id assertions to use .name. Adds
walkable field to stub atlas JSONs so gameplay assertions pass without
the legacy tiles dir. Test bootstrap calls maps.load_textures with
aliases pointing to the test-lib itself as the asset provider. Moves
load_textures calls inline after each set_current (Task 4 and Task 9
blocks) so all tile-record shape and walkability checks use atlas
records throughout. Bumps version to 0.3.0, dep on maps 0.3.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 23:09:14 +02:00
Axel Meyer
394e8faef2 Bake stub atlas-sets for demo_tilemap and walls_tilemap
Replaces the legacy color-only *.tilemap.json fixtures with baked
2-tile atlas-sets in the new atlases/<id>/{diffuse,height,atlas-json,
lock-json} layout. Stub PNGs are 32x32 solid colors matching the
previous color values; height channel is synthesized L8=0 by the baker.
A scripts/bake.sh re-bake helper drives the atlas-baker tool against
assets/_sources/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:53:30 +02:00
18 changed files with 148 additions and 36 deletions

21
assets/_sources/_gen.js Normal file
View File

@@ -0,0 +1,21 @@
const { PNG } = require('pngjs');
const fs = require('node:fs');
const path = require('node:path');
function writeSolidRGBA(p, w, h, rgba) {
const png = new PNG({ width: w, height: h });
for (let i = 0; i < png.data.length; i += 4) {
png.data[i + 0] = rgba[0];
png.data[i + 1] = rgba[1];
png.data[i + 2] = rgba[2];
png.data[i + 3] = rgba[3];
}
fs.writeFileSync(p, PNG.sync.write(png));
}
const SOURCES = path.resolve(__dirname);
writeSolidRGBA(path.join(SOURCES, 'demo_tilemap', 'grass_diffuse.png'), 32, 32, [110, 170, 80, 255]);
writeSolidRGBA(path.join(SOURCES, 'demo_tilemap', 'stone_diffuse.png'), 32, 32, [120, 120, 120, 255]);
writeSolidRGBA(path.join(SOURCES, 'walls_tilemap', 'wall_brick_diffuse.png'), 32, 32, [180, 100, 80, 255]);
writeSolidRGBA(path.join(SOURCES, 'walls_tilemap', 'wall_wood_diffuse.png'), 32, 32, [140, 90, 50, 255]);
console.log('test-stub sources generated');

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

View File

@@ -0,0 +1,33 @@
{
"atlas_id": "demo_tilemap",
"atlas_version": 1,
"atlas_size_px": [
64,
64
],
"tile_size_px": 32,
"tiles": [
{
"id": 1,
"name": "grass",
"walkable": true,
"uv": [
0,
0,
32,
32
]
},
{
"id": 2,
"name": "stone",
"walkable": false,
"uv": [
32,
0,
32,
32
]
}
]
}

View File

@@ -0,0 +1,9 @@
{
"atlas_id": "demo_tilemap",
"bindings": {
"grass": 1,
"stone": 2
},
"deleted": [],
"next_id": 3
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

View File

@@ -0,0 +1,35 @@
{
"atlas_id": "walls_tilemap",
"atlas_version": 1,
"atlas_size_px": [
64,
64
],
"tile_size_px": 32,
"tiles": [
{
"id": 1,
"name": "wall_brick",
"walkable": false,
"uv": [
0,
0,
32,
32
],
"blocks_sight": true
},
{
"id": 2,
"name": "wall_wood",
"walkable": false,
"uv": [
32,
0,
32,
32
],
"blocks_sight": true
}
]
}

View File

@@ -0,0 +1,9 @@
{
"atlas_id": "walls_tilemap",
"bindings": {
"wall_brick": 1,
"wall_wood": 2
},
"deleted": [],
"next_id": 3
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

View File

@@ -1,8 +0,0 @@
{
"id": "demo_tilemap",
"tile_size": 32,
"tiles": [
{"id": "grass", "walkable": true, "color": [110, 170, 80]},
{"id": "stone", "walkable": false, "color": [120, 120, 120]}
]
}

View File

@@ -1,8 +0,0 @@
{
"id": "walls_tilemap",
"tile_size": 32,
"tiles": [
{"id": "wall_brick", "walkable": false, "color": [180, 100, 80]},
{"id": "wall_wood", "walkable": false, "color": [140, 90, 50]}
]
}

View File

@@ -8,6 +8,13 @@ local maps = require("lib-core.maps")
local M = {}
function M.run_tests(ctx)
-- Asset-aliases for texture-loading (test-libs lack manifest asset_aliases;
-- hard-code the test-lib itself as the asset provider for its own atlases).
local aliases = {
demo_tilemap = "lib-core.maps-test",
walls_tilemap = "lib-core.maps-test",
}
-- ---------- Task 1: GID encode/decode ----------
engine.test.equals(maps.encode_gid(0, 0, 0), 0, "encode_gid(0,0,0) = 0 (empty sentinel)")
engine.test.equals(maps.encode_gid(0, 12, 0), 192, "encode_gid(0,12,0) = 192 (= 12<<4)")
@@ -101,6 +108,7 @@ function M.run_tests(ctx)
local v2_id = maps.load("maps/demo_v2.map.json")
engine.test.equals(v2_id, "demo_v2", "v2 map loaded with id 'demo_v2'")
maps.set_current(v2_id)
maps.load_textures(aliases)
local v2_size = maps.size()
engine.test.equals(v2_size.w, 4, "v2 map width = 4")
engine.test.equals(v2_size.h, 4, "v2 map height = 4")
@@ -138,12 +146,12 @@ function M.run_tests(ctx)
-- ---------- Task 6: Per-layer tile_at ----------
-- tile_at_layer returns tile-record from the correct atlas
local surface_tile = maps.tile_at_layer("surface", 1, 1)
engine.test.assert(surface_tile ~= nil, "surface (1,1) returns tile")
engine.test.equals(surface_tile.id, "grass", "surface (1,1) = grass")
engine.test.assert(surface_tile ~= nil, "surface (1,1) returns tile")
engine.test.equals(surface_tile.name, "grass", "surface (1,1) name = grass")
local wall_tile = maps.tile_at_layer("wall", 0, 0)
engine.test.assert(wall_tile ~= nil, "wall (0,0) returns tile")
engine.test.equals(wall_tile.id, "wall_brick", "wall (0,0) = wall_brick (from walls_tilemap)")
engine.test.assert(wall_tile ~= nil, "wall (0,0) returns tile")
engine.test.equals(wall_tile.name, "wall_brick", "wall (0,0) name = wall_brick (from walls_tilemap)")
-- Empty cell returns nil
local empty_top = maps.tile_at_layer("topsurface", 0, 0)
@@ -154,7 +162,7 @@ function M.run_tests(ctx)
-- Legacy tile_at backwards-compat: queries surface layer
local legacy = maps.tile_at(1, 1)
engine.test.equals(legacy.id, "grass", "legacy tile_at(x,y) queries surface layer")
engine.test.equals(legacy.name, "grass", "legacy tile_at(x,y) queries surface layer")
-- ---------- Task 7: Gameplay queries ----------
-- Surface-only cells (inside, no wall) → walkable
@@ -205,36 +213,30 @@ function M.run_tests(ctx)
local v1_id = maps.load("maps/demo.map.json")
engine.test.equals(v1_id, "demo", "v1 demo map loaded")
maps.set_current(v1_id)
maps.load_textures(aliases)
engine.test.equals(maps.atlas_count(), 1, "v1 auto-upgrade has 1 atlas")
engine.test.equals(maps.atlas_id_at(0), "demo_tilemap", "v1 atlas[0] = demo_tilemap")
engine.test.assert(maps.has_layer("surface"), "v1 auto-upgrade has surface layer")
engine.test.assert(not maps.has_layer("wall"), "v1 has no wall layer")
-- Walkability via v2-API should match v1-API result
-- Walkability via v2-API should match v1-API result (atlas records now loaded)
engine.test.assert(maps.is_walkable(5, 5), "v1 (5,5) walkable (was grass in v1 test)")
engine.test.assert(not maps.is_walkable(0, 0), "v1 (0,0) not walkable (was stone in v1 test)")
-- Restore current to v2 map for subsequent tests if any
maps.set_current("demo_v2")
-- Setup (legacy demo assertions, now using already-loaded demo from Task 9 above)
local demo_map = "demo"
maps.set_current(demo_map)
-- Geometry assertions
-- Geometry assertions (demo map, already current after load_textures block)
local size = maps.size()
engine.test.equals(size.w, 16, "map width = 16")
engine.test.equals(size.h, 16, "map height = 16")
engine.test.equals(maps.tile_size(), 32, "tile_size = 32")
-- Tile lookup + walkability
engine.test.equals(maps.tile_at(0, 0).id, "stone", "tile_at(0,0) is stone")
engine.test.equals(maps.tile_at(5, 5).id, "grass", "tile_at(5,5) is grass")
-- Tile lookup + walkability (post-M.2: tile.id is int, tile.name is string)
engine.test.equals(maps.tile_at(0, 0).name, "stone", "tile_at(0,0) name = stone")
engine.test.equals(maps.tile_at(5, 5).name, "grass", "tile_at(5,5) name = grass")
engine.test.assert(not maps.is_walkable(0, 0), "stone border non-walkable")
engine.test.assert(maps.is_walkable(5, 5), "grass interior walkable")
-- Arity-flex tile_at: 3-arg with nil map_id falls back to current_map (P.2.1)
engine.test.equals(maps.tile_at(nil, 5, 5).id, "grass", "tile_at 3-arg nil-fallback")
engine.test.equals(maps.tile_at(nil, 5, 5).name, "grass", "tile_at 3-arg nil-fallback name = grass")
-- Out-of-bounds returns nil
engine.test.equals(maps.tile_at(-1, 0), nil, "tile_at OOB negative tx returns nil")

View File

@@ -1,9 +1,9 @@
{
"id": "lib-core.maps-test",
"role": "test",
"version": "0.2.0",
"version": "0.3.0",
"api_min": "0.1",
"deps": [
{"id": "lib-core.maps", "version": "0.2.0"}
{"id": "lib-core.maps", "version": "0.3.0"}
]
}

19
scripts/bake.sh Normal file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env bash
# Re-bake the stub atlases used by maps-test fixtures.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LIB_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
BAKER=~/Projects/Sporel/sporel-tool-atlas-baker/bin/atlas-baker.js
node "$BAKER" \
--in "$LIB_DIR/assets/_sources/demo_tilemap" \
--out "$LIB_DIR/assets/atlases/demo_tilemap" \
--atlas-id demo_tilemap \
--tile-size 32
node "$BAKER" \
--in "$LIB_DIR/assets/_sources/walls_tilemap" \
--out "$LIB_DIR/assets/atlases/walls_tilemap" \
--atlas-id walls_tilemap \
--tile-size 32 \
--blocks-sight-pattern '^wall_'