Refactor org web platform and remove legacy code
All checks were successful
Build Org Website / build (push) Successful in 39s
All checks were successful
Build Org Website / build (push) Successful in 39s
This commit is contained in:
@@ -1,96 +0,0 @@
|
||||
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");
|
||||
|
||||
test("fresh v2 state applies safe defaults", () => {
|
||||
const fresh = state.fresh(" Ada ", "moss");
|
||||
assert.equal(fresh.version, 2);
|
||||
assert.equal(fresh.name, "Ada");
|
||||
assert.equal(fresh.palette, "moss");
|
||||
assert.equal(fresh.position.area, "town");
|
||||
assert.deepEqual(
|
||||
{ x: fresh.position.x, y: fresh.position.y },
|
||||
data.AREAS.town.safeSpawns.gate
|
||||
);
|
||||
assert.ok(fresh.position.y > data.AREAS.town.gates[0].y + data.AREAS.town.gates[0].height);
|
||||
assert.equal(fresh.health, 100);
|
||||
assert.equal(fresh.settings.soundEnabled, false);
|
||||
assert.deepEqual(fresh.sigils, []);
|
||||
assert.equal(fresh.inventory.find((item) => item.id === "ink_vial").quantity, 2);
|
||||
});
|
||||
|
||||
test("names and palettes are validated", () => {
|
||||
assert.equal(state.validateName(" Archive Guest "), "Archive Guest");
|
||||
assert.equal(state.validateName(""), null);
|
||||
assert.equal(state.validateName("x".repeat(21)), null);
|
||||
assert.equal(state.validatePalette("berry"), "berry");
|
||||
assert.equal(state.validatePalette("ultraviolet"), null);
|
||||
});
|
||||
|
||||
test("v1 data migrates identity, discoveries, position and sound", () => {
|
||||
const migrated = state.migrateV1({
|
||||
version: 1,
|
||||
name: "Mira",
|
||||
palette: "berry",
|
||||
discovered: ["gate", "gate", "museum", "missing"],
|
||||
complete: false,
|
||||
position: { x: 700, y: 850 },
|
||||
soundEnabled: true
|
||||
});
|
||||
assert.equal(migrated.version, 2);
|
||||
assert.deepEqual(migrated.discovered, ["gate", "museum"]);
|
||||
assert.equal(migrated.position.area, "town");
|
||||
assert.equal(migrated.position.x, 700);
|
||||
assert.equal(migrated.settings.soundEnabled, true);
|
||||
});
|
||||
|
||||
test("normalization deduplicates and filters restored collections", () => {
|
||||
const candidate = state.fresh("Zaine", "brass");
|
||||
candidate.discovered = ["gate", "gate", "missing"];
|
||||
candidate.sigils = ["garden", "garden", "bad"];
|
||||
candidate.inventory.push({ id: "ink_vial", quantity: 99 }, { id: "invalid", quantity: 2 });
|
||||
candidate.defeatedBosses = ["sentinel", "sentinel", "fake"];
|
||||
candidate.solvedPuzzles = ["gate", "gate", "fake"];
|
||||
const restored = state.normalize(candidate);
|
||||
assert.deepEqual(restored.discovered, ["gate"]);
|
||||
assert.deepEqual(restored.sigils, ["garden"]);
|
||||
assert.equal(restored.inventory.find((item) => item.id === "ink_vial").quantity, data.ITEMS.ink_vial.stack);
|
||||
assert.deepEqual(restored.defeatedBosses, ["sentinel"]);
|
||||
assert.deepEqual(restored.solvedPuzzles, ["gate"]);
|
||||
});
|
||||
|
||||
test("quest restoration accepts only known statuses and bounded counters", () => {
|
||||
const candidate = state.fresh("Ada", "brass");
|
||||
candidate.quests.gate = { status: "active", step: 200, count: -5 };
|
||||
candidate.quests.library = { status: "nonsense", step: 2, count: 3 };
|
||||
const restored = state.normalize(candidate);
|
||||
assert.deepEqual(restored.quests.gate, { status: "active", step: 20, count: 0 });
|
||||
assert.deepEqual(restored.quests.library, { status: "locked", step: 2, count: 3 });
|
||||
});
|
||||
|
||||
test("position, health, settings and unsupported schemas recover safely", () => {
|
||||
const candidate = state.fresh("Ada", "brass");
|
||||
candidate.position = { area: "missing", x: -100, y: 9000, facing: "downward" };
|
||||
candidate.health = 999;
|
||||
candidate.settings = { soundEnabled: true, ambience: 9, music: -2, effects: "bad", assist: true };
|
||||
const restored = state.normalize(candidate);
|
||||
assert.equal(restored.position.area, "town");
|
||||
assert.equal(restored.position.x, 24);
|
||||
assert.equal(restored.position.y, 1062);
|
||||
assert.equal(restored.position.facing, "north");
|
||||
assert.equal(restored.health, restored.maxHealth);
|
||||
assert.equal(restored.settings.ambience, 1);
|
||||
assert.equal(restored.settings.music, 0);
|
||||
assert.equal(restored.settings.effects, 0.65);
|
||||
assert.equal(state.parse("{"), null);
|
||||
assert.equal(state.parse(JSON.stringify({ version: 99 })), null);
|
||||
});
|
||||
|
||||
test("level thresholds and reset-ready fresh state are deterministic", () => {
|
||||
assert.equal(state.levelForXp(0), 1);
|
||||
assert.equal(state.levelForXp(80), 2);
|
||||
assert.equal(state.levelForXp(620), 5);
|
||||
assert.deepEqual(state.fresh("Mira", "berry").discovered, []);
|
||||
assert.equal(state.fresh("Mira", "berry").quests.gate.status, "locked");
|
||||
});
|
||||
@@ -1,160 +0,0 @@
|
||||
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}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
62
tests/princess-lima-state.test.cjs
Normal file
62
tests/princess-lima-state.test.cjs
Normal file
@@ -0,0 +1,62 @@
|
||||
const test = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
const data = require("../assets/scripts/pages/princess-lima-data.js");
|
||||
const state = require("../assets/scripts/pages/princess-lima-state.js");
|
||||
|
||||
test("fresh save uses the Princess Lima schema and safe village start", () => {
|
||||
const fresh = state.fresh(" Rowan ", "pine");
|
||||
assert.equal(fresh.version, 1);
|
||||
assert.equal(state.STORAGE_KEY, "zxh_princess_lima_rpg_v1");
|
||||
assert.equal(fresh.player.name, "Rowan");
|
||||
assert.equal(fresh.player.appearance, "pine");
|
||||
assert.equal(fresh.region, "village");
|
||||
assert.deepEqual({ x: fresh.position.x, y: fresh.position.y }, data.MAPS.village.spawns.start);
|
||||
assert.equal(fresh.settings.soundEnabled, false);
|
||||
});
|
||||
|
||||
test("names and appearances validate", () => {
|
||||
assert.equal(state.validName(" A Traveller "), "A Traveller");
|
||||
assert.equal(state.validName(""), null);
|
||||
assert.equal(state.validName("x".repeat(21)), null);
|
||||
assert.equal(state.normalize({ ...state.fresh("A", "azure"), player: { name: "A", appearance: "bad" } }), null);
|
||||
});
|
||||
|
||||
test("valid saves restore bounded state and derived equipment", () => {
|
||||
const candidate = state.fresh("Mira", "azure");
|
||||
candidate.health = 999;
|
||||
candidate.inventory = [
|
||||
{ id: "tempered_sword", quantity: 1 },
|
||||
{ id: "healing_tonic", quantity: 99 },
|
||||
{ id: "bad", quantity: 4 }
|
||||
];
|
||||
candidate.solvedPuzzles = ["forest_stones", "forest_stones", "bad"];
|
||||
candidate.defeatedBosses = ["captain", "captain", "bad"];
|
||||
const restored = state.normalize(candidate);
|
||||
assert.equal(restored.health, restored.maxHealth);
|
||||
assert.equal(restored.attack, 2);
|
||||
assert.equal(restored.inventory.find((item) => item.id === "healing_tonic").quantity, 9);
|
||||
assert.deepEqual(restored.solvedPuzzles, ["forest_stones"]);
|
||||
assert.deepEqual(restored.defeatedBosses, ["captain"]);
|
||||
});
|
||||
|
||||
test("invalid and unsupported saves recover without throwing", () => {
|
||||
assert.equal(state.parse("{"), null);
|
||||
assert.equal(state.parse(JSON.stringify({ version: 99 })), null);
|
||||
assert.equal(state.parse(JSON.stringify({ version: 1 })), null);
|
||||
});
|
||||
|
||||
test("quest, route, puzzle, boss and settings restoration filters malformed data", () => {
|
||||
const candidate = state.fresh("Mira", "ember");
|
||||
candidate.quests.aftermath = { status: "active", count: 999, rewarded: true };
|
||||
candidate.quests.village_defence = { status: "nonsense", count: -10 };
|
||||
candidate.unlockedRoutes = ["guide_found", "guide_found", "fake"];
|
||||
candidate.settings = { master: 4, music: -2, effects: "bad", textSpeed: "instant", highContrast: true };
|
||||
const restored = state.normalize(candidate);
|
||||
assert.equal(restored.quests.aftermath.count, 99);
|
||||
assert.equal(restored.quests.village_defence.status, "locked");
|
||||
assert.deepEqual(restored.unlockedRoutes, ["guide_found"]);
|
||||
assert.equal(restored.settings.master, 1);
|
||||
assert.equal(restored.settings.music, 0);
|
||||
assert.equal(restored.settings.effects, 0.7);
|
||||
assert.equal(restored.settings.textSpeed, "instant");
|
||||
});
|
||||
104
tests/princess-lima-systems.test.cjs
Normal file
104
tests/princess-lima-systems.test.cjs
Normal file
@@ -0,0 +1,104 @@
|
||||
const test = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
const data = require("../assets/scripts/pages/princess-lima-data.js");
|
||||
const state = require("../assets/scripts/pages/princess-lima-state.js");
|
||||
const systems = require("../assets/scripts/pages/princess-lima-systems.js");
|
||||
|
||||
test("movement is responsive and diagonal speed is normalized", () => {
|
||||
let velocity = { x: 0, y: 0 };
|
||||
for (let frame = 0; frame < 120; frame += 1) {
|
||||
velocity = systems.approachVelocity(velocity.x, velocity.y, 1, 1, 16, false);
|
||||
}
|
||||
assert.equal(Math.round(Math.hypot(velocity.x, velocity.y)), 176);
|
||||
for (let frame = 0; frame < 20; frame += 1) {
|
||||
velocity = systems.approachVelocity(velocity.x, velocity.y, 0, 0, 16, false);
|
||||
}
|
||||
assert.deepEqual(velocity, { x: 0, y: 0 });
|
||||
});
|
||||
|
||||
test("all named spawns are collision-safe and invalid positions recover", () => {
|
||||
const current = state.fresh("Ada", "azure");
|
||||
Object.entries(data.MAPS).forEach(([regionId, map]) => {
|
||||
Object.entries(map.spawns).forEach(([spawnId, spawn]) => {
|
||||
assert.equal(systems.isSafePosition(current, regionId, spawn.x, spawn.y), true, `${regionId}:${spawnId}`);
|
||||
});
|
||||
});
|
||||
const recovered = systems.nearestSafeSpawn(current, "village", 100, 100);
|
||||
assert.equal(systems.isSafePosition(current, recovered.region, recovered.x, recovered.y), true);
|
||||
});
|
||||
|
||||
test("quest progression grants rewards once and unlocks routes", () => {
|
||||
let current = state.fresh("Ada", "azure");
|
||||
current = systems.completeQuest(current, "aftermath");
|
||||
assert.equal(systems.quantity(current, "village_sword"), 1);
|
||||
current = systems.progressQuest(current, "village_defence", 3);
|
||||
assert.equal(current.quests.village_defence.status, "complete");
|
||||
assert.equal(current.unlockedRoutes.includes("village_defended"), true);
|
||||
const tonics = systems.quantity(current, "healing_tonic");
|
||||
current = systems.completeQuest(current, "village_defence");
|
||||
assert.equal(systems.quantity(current, "healing_tonic"), tonics);
|
||||
});
|
||||
|
||||
test("optional quests remain independent of chapter progression", () => {
|
||||
let current = state.fresh("Ada", "azure");
|
||||
current = systems.startQuest(current, "healer_herbs");
|
||||
current = systems.completeQuest(current, "healer_herbs");
|
||||
assert.equal(current.quests.healer_herbs.status, "complete");
|
||||
assert.equal(current.chapter, 1);
|
||||
});
|
||||
|
||||
test("inventory caps stacks, protects quest items and healing consumes safely", () => {
|
||||
let current = state.fresh("Ada", "azure");
|
||||
current = systems.addItem(current, "healing_tonic", 30);
|
||||
assert.equal(systems.quantity(current, "healing_tonic"), 9);
|
||||
current = systems.addItem(current, "prison_key", 1);
|
||||
assert.equal(systems.removeItem(current, "prison_key", 1).removed, false);
|
||||
current.health = 30;
|
||||
const result = systems.useItem(current, "healing_tonic");
|
||||
assert.equal(result.used, true);
|
||||
assert.equal(result.state.health, 70);
|
||||
assert.equal(systems.quantity(result.state, "healing_tonic"), 8);
|
||||
});
|
||||
|
||||
test("combat applies defence, invulnerability, defeat and checkpoint respawn", () => {
|
||||
let current = systems.addItem(state.fresh("Ada", "azure"), "buckler", 1);
|
||||
const hit = systems.damage(current, 10, 2000, 0);
|
||||
assert.equal(hit.amount, 8);
|
||||
assert.equal(hit.state.health, 92);
|
||||
assert.equal(systems.damage(hit.state, 10, 2100, 2000).hit, false);
|
||||
const defeated = systems.damage({ ...hit.state, health: 3 }, 20, 4000, 0);
|
||||
assert.equal(defeated.defeated, true);
|
||||
const respawned = systems.respawn(defeated.state);
|
||||
assert.equal(respawned.region, "village");
|
||||
assert.ok(respawned.health >= 50);
|
||||
});
|
||||
|
||||
test("puzzles unlock chapter routes and cannot reward twice", () => {
|
||||
let current = state.fresh("Ada", "azure");
|
||||
current = systems.solvePuzzle(current, "forest_stones");
|
||||
assert.equal(current.solvedPuzzles.includes("forest_stones"), true);
|
||||
assert.equal(current.unlockedRoutes.includes("guide_found"), true);
|
||||
const boots = systems.quantity(current, "trail_boots");
|
||||
current = systems.solvePuzzle(current, "forest_stones");
|
||||
assert.equal(systems.quantity(current, "trail_boots"), boots);
|
||||
});
|
||||
|
||||
test("boss phases and victories are deterministic", () => {
|
||||
assert.equal(systems.bossPhase(48, 48, 3), 1);
|
||||
assert.equal(systems.bossPhase(24, 48, 3), 2);
|
||||
assert.equal(systems.bossPhase(10, 48, 3), 3);
|
||||
let current = state.fresh("Ada", "azure");
|
||||
current = systems.recordBoss(current, "malrec");
|
||||
assert.equal(current.defeatedBosses.includes("malrec"), true);
|
||||
assert.equal(current.unlockedRoutes.includes("malrec_defeated"), true);
|
||||
assert.equal(current.story, "rescue");
|
||||
});
|
||||
|
||||
test("complete story path reaches the Princess Lima rescue state", () => {
|
||||
let current = state.fresh("Ada", "azure");
|
||||
["aftermath", "village_defence", "find_guide", "ruins_light", "wolf_miniboss", "repair_bridge", "stone_guardian", "free_scout", "free_prisoners", "break_wards", "defeat_malrec"]
|
||||
.forEach((id) => { current = systems.completeQuest(current, id); });
|
||||
assert.equal(current.chapter, 4);
|
||||
assert.equal(current.unlockedRoutes.includes("wards_broken"), true);
|
||||
assert.equal(current.unlockedRoutes.includes("malrec_defeated"), true);
|
||||
});
|
||||
Reference in New Issue
Block a user