const test = require("node:test"); const assert = require("node:assert/strict"); const data = require("../assets/scripts/pages/archive-world-data.js"); const state = require("../assets/scripts/pages/archive-world-state.js"); const systems = require("../assets/scripts/pages/archive-world-systems.js"); test("movement normalization prevents faster diagonals", () => { const straight = systems.normalizedVector(1, 0, 175); const diagonal = systems.normalizedVector(1, 1, 175); assert.equal(Math.round(Math.hypot(straight.x, straight.y)), 175); assert.equal(Math.round(Math.hypot(diagonal.x, diagonal.y)), 175); }); test("movement acceleration is frame-rate aware, responsive, and speed capped", () => { const movement = data.AREAS.town.movement; const first = systems.approachVelocity(0, 0, 1, 1, movement, 16, false); assert.ok(Math.hypot(first.x, first.y) > 0); assert.ok(Math.hypot(first.x, first.y) < movement.speed); let velocity = { x: 0, y: 0 }; for (let frame = 0; frame < 120; frame += 1) { velocity = systems.approachVelocity(velocity.x, velocity.y, 1, 1, movement, 16, false); } assert.equal(Math.round(Math.hypot(velocity.x, velocity.y)), movement.speed); for (let frame = 0; frame < 20; frame += 1) { velocity = systems.approachVelocity(velocity.x, velocity.y, 0, 0, movement, 16, false); } assert.deepEqual(velocity, { x: 0, y: 0 }); }); test("landmark discoveries deduplicate and complete at eight", () => { let current = state.fresh("Ada", "brass"); current = systems.discover(current, "gate"); current = systems.discover(current, "gate"); assert.deepEqual(current.discovered, ["gate"]); data.LANDMARK_IDS.forEach((id) => { current = systems.discover(current, id); }); assert.equal(current.complete, true); }); test("quest progression and completion grant each reward once", () => { let current = state.fresh("Ada", "brass"); current = systems.startQuest(current, "gate"); assert.equal(current.quests.gate.status, "active"); current = systems.completeQuest(current, "gate"); const xp = current.xp; assert.equal(current.quests.gate.status, "complete"); assert.deepEqual(current.sigils, ["gate"]); assert.equal(current.inventory.some((item) => item.id === "lantern"), true); current = systems.completeQuest(current, "gate"); assert.equal(current.xp, xp); assert.deepEqual(current.sigils, ["gate"]); }); test("inventory collection caps stacks and consumables heal", () => { let current = state.fresh("Ada", "brass"); current = systems.addItem(current, "ink_vial", 30); assert.equal(systems.itemQuantity(current, "ink_vial"), 9); current.health = 30; const used = systems.consumeItem(current, "ink_vial"); assert.equal(used.used, true); assert.equal(used.state.health, 65); assert.equal(systems.itemQuantity(used.state, "ink_vial"), 8); }); test("damage respects invulnerability and assist, defeat preserves progress", () => { let current = state.fresh("Ada", "brass"); current.sigils = ["gate"]; const assisted = state.normalize({ ...current, settings: { ...current.settings, assist: true } }); const first = systems.takeDamage(assisted, 20, 2000, 0); assert.equal(first.state.health, 90); const ignored = systems.takeDamage(first.state, 20, 2200, 2000); assert.equal(ignored.hit, false); const defeated = systems.takeDamage({ ...first.state, health: 5, settings: { ...first.state.settings, assist: false } }, 20, 5000, 0); assert.equal(defeated.defeated, true); const respawned = systems.respawn(defeated.state); assert.deepEqual(respawned.sigils, ["gate"]); assert.equal(respawned.health >= 50, true); }); test("XP levels, boss completion and sigil story stage restore correctly", () => { let current = state.fresh("Ada", "brass"); current = systems.grantXp(current, 200); assert.equal(current.level, 3); assert.equal(current.maxHealth, 120); current = systems.recordBoss(current, "sentinel"); assert.equal(current.defeatedBosses.includes("sentinel"), true); assert.equal(current.quests.workshop.status, "complete"); data.LANDMARK_IDS.forEach((id) => { if (!current.sigils.includes(id)) current = systems.completeQuest(current, id); }); assert.equal(current.sigils.length, 8); assert.equal(current.story.stage, "vault"); current = systems.recordBoss(current, "redactor"); assert.equal(current.story.stage, "postgame"); }); test("invalid collision positions resolve to a named safe spawn", () => { assert.equal(systems.isSafe("town", 200, 150), false); const safe = systems.nearestSafeSpawn("town", 200, 150); assert.equal(systems.isSafe(safe.area, safe.x, safe.y), true); assert.ok(["gate", "square", "library", "study", "inn", "workshop", "playroom", "museum", "garden"].includes(safe.spawn)); }); test("locked-gate saves inside town recover to the outside opening spawn", () => { const current = state.fresh("Ada", "brass"); current.position = { area: "town", spawn: "square", x: 724, y: 555, facing: "south" }; const safe = systems.safeSpawnForState(current); assert.deepEqual( { area: safe.area, spawn: safe.spawn, x: safe.x, y: safe.y }, { area: "town", spawn: "gate", ...data.AREAS.town.safeSpawns.gate } ); current.sigils.push("gate"); const unlocked = systems.safeSpawnForState(current); assert.equal(unlocked.spawn, "saved"); assert.equal(unlocked.y, 555); }); test("interaction ranges are type-specific and NPC conversations require close approach", () => { const npc = { type: "npc", id: "orin", x: 0, y: 0 }; const landmark = { type: "landmark", id: "gate", x: 56, y: 0 }; assert.equal(systems.interactionRadius(npc), 46); assert.equal(systems.nearestInteraction([npc], 47, 0), null); assert.equal(systems.nearestInteraction([npc], 45, 0), npc); assert.equal(systems.nearestInteraction([npc, landmark], 0, 0), npc); assert.equal(systems.nearestInteraction([landmark], 0, 0), landmark); }); test("all declared safe spawns and enemy homes are outside collision geometry", () => { Object.entries(data.AREAS).forEach(([areaId, area]) => { Object.entries(area.safeSpawns).forEach(([spawnId, spawn]) => { assert.equal(systems.isSafe(areaId, spawn.x, spawn.y), true, `${areaId}:${spawnId}`); }); area.enemies.forEach((entry) => { const spawn = Array.isArray(entry) ? { type: entry[0], x: entry[1], y: entry[2] } : entry; assert.equal(systems.isSafe(areaId, spawn.x, spawn.y), true, `${areaId}:${spawn.type}`); assert.equal(systems.isCombatSafe(areaId, spawn.x, spawn.y), false, `${areaId}:${spawn.type} is in a safe zone`); }); }); }); test("town landmarks and NPC interaction points remain reachable", () => { Object.values(data.LANDMARKS).forEach((landmark) => { assert.equal(systems.isSafe("town", landmark.x, landmark.y), true, `landmark:${landmark.id}`); }); data.AREAS.town.npcs.forEach(([npcId, x, y]) => { assert.equal(systems.isSafe("town", x, y), true, `npc:${npcId}`); }); }); test("town safe spawns do not place the player inside an NPC body", () => { Object.entries(data.AREAS.town.safeSpawns).forEach(([spawnId, spawn]) => { data.AREAS.town.npcs.forEach(([npcId, x, y]) => { assert.ok(Math.hypot(spawn.x - x, spawn.y - y) >= 30, `${spawnId} overlaps npc:${npcId}`); }); }); });