const test = require("node:test"); const assert = require("node:assert/strict"); const fs = require("node:fs"); const path = require("node:path"); const data = require("../assets/scripts/pages/princess-lima-v2-data.js"); const state = require("../assets/scripts/pages/princess-lima-v2-state.js"); const systems = require("../assets/scripts/pages/princess-lima-v2-systems.js"); const root = path.resolve(__dirname, ".."); const source = (file) => fs.readFileSync(path.join(root, file), "utf8"); test("v2 content contracts and all dialogue graphs validate", () => { assert.deepEqual(data.validateAll(), []); assert.equal(data.SCHEMA_VERSION, 2); assert.equal(Object.keys(data.DIALOGUES).length >= 8, true); Object.values(data.DIALOGUES).forEach((dialogue) => assert.equal(data.validateDialogue(dialogue), true)); }); test("all nine regions are genuine Tiled maps with authored runtime layers", () => { assert.equal(data.MAP_IDS.length, 9); data.MAP_IDS.forEach((region) => { const map = JSON.parse(source(`assets/maps/princess-lima/${region}.json`)); assert.equal(map.type, "map"); assert.equal(map.tilewidth, 32); assert.equal(map.tileheight, 32); assert.deepEqual(systems.validateMap(map, data.MAP_IDS), [], region); assert.deepEqual(map.layers.map((layer) => layer.name), systems.REQUIRED_MAP_LAYERS); assert.ok(map.layers.find((layer) => layer.name === "Base terrain").data.some((tile) => tile > 0)); }); }); test("the runtime loads Tiled maps rather than regional backdrop frames", () => { const maps = source("assets/scripts/pages/princess-lima-v2-maps.js"); const scenes = source("assets/scripts/pages/princess-lima-v2-scenes.js"); assert.match(maps, /make\.tilemap/); assert.match(maps, /createLayer/); assert.match(scenes, /tilemapTiledJSON/); assert.doesNotMatch(maps, /regional-style-atlas|REGION_FRAME/); }); test("character animation, cinematic dialogue and eight enemy roles are present", () => { const roles = new Set(Object.values(data.ENEMIES).filter((enemy) => !enemy.boss).map((enemy) => enemy.role)); ["melee", "shield", "ranged", "fast", "ambush", "caster", "flying", "support", "elite"].forEach((role) => assert.ok(roles.has(role))); assert.ok(fs.existsSync(path.join(root, "assets/images/play/princess-lima/actor-atlas-v2.png"))); assert.ok(fs.existsSync(path.join(root, "assets/images/play/princess-lima/portrait-atlas-v2.png"))); const ui = source("assets/scripts/pages/princess-lima-v2-ui.js"); assert.match(ui, /lima-cinematic-dialogue/); assert.match(ui, /aria-label/); assert.match(ui, /Conversation history/); }); test("the standalone page loads only the local v2 runtime and accessibility surface", () => { const rpg = source("play/rpg.org"); assert.match(rpg, /easystar-0\.4\.4\.min\.js/); assert.match(rpg, /princess-lima-v2-game\.js/); assert.match(rpg, /data-lima-screenreader/); assert.match(rpg, /Block Shift/); assert.doesNotMatch(rpg, /princess-lima-game\.js\?v=/); assert.doesNotMatch(rpg, /https?:\/\//); }); test("v1 progress migrates to v2 without overwriting legacy fields", () => { const legacy = { version: 1, player: { name: "Rowan", appearance: "pine" }, chapter: 4, region: "fortressInterior", quests: { break_wards: { status: "complete", count: 3, rewarded: true } }, inventory: [{ id: "tempered_sword", quantity: 1 }, { id: "bad_item", quantity: 5 }], defeatedBosses: ["stone_guardian", "unknown"], settings: { subtitles: false, highContrast: true }, introSeen: true }; const migrated = state.migrateLegacy(legacy); assert.equal(migrated.version, 2); assert.equal(migrated.player.name, "Rowan"); assert.equal(migrated.quests.break_wards.status, "complete"); assert.equal(migrated.inventory.some((item) => item.id === "bad_item"), false); assert.equal(migrated.position.spawn, "frontHall"); assert.equal(migrated.settings.highContrast, true); }); test("combat phases, hitboxes and enemy states are deterministic", () => { assert.equal(systems.attackPhase(0, "charged"), "windup"); assert.equal(systems.attackPhase(520, "charged"), "active"); assert.equal(systems.attackPhase(670, "charged"), "recovery"); const north = systems.attackHitbox("north", 100, 100, "light1"); assert.ok(north.y + north.height <= 100); const enemy = data.ENEMIES.raider; assert.equal(systems.nextEnemyState("patrol", { distance: 30, homeDistance: 0, spec: enemy, health: 5, leash: 190, stunned: false, telegraphDone: true, attackDone: true, cooldownDone: true }), "telegraph"); });