import { describe, expect, it } from "vitest"; import { buildBossGuideCatalog, buildDungeonBossGuide, bossGuideOverrideKey } from "./bossGuide"; import { DUNGEON_DEFINITION_SCHEMA_VERSION, type DungeonDefinition } from "./dungeonTypes"; import { DUNGEON_DEFINITIONS } from "./dungeonRegistry"; import { BOSS_GUIDE_OVERRIDES } from "./bossGuideOverrides"; const fixture = { schemaVersion: DUNGEON_DEFINITION_SCHEMA_VERSION, id: "guide-fixture", title: "Guide Fixture", mapId: 1, lfgDungeonIds: [], expansion: "runewaker", environmentMode: "runewaker-ros", difficultyVariants: [{ id: "normal", difficulty: "normal", spawnMask: 1, levelRange: [1, 2] }], defaultDifficultyId: "normal", availability: "available", presentation: { location: "Test", theme: "Test", summary: "Test", loadingMessage: "Test", unchartedAreaName: "Test", background: "#000", fog: { color: "#000", near: 1, far: 2 }, ambientLight: { color: "#fff", intensity: 1 }, hemisphereLight: { skyColor: "#fff", groundColor: "#000", intensity: 1 }, directionalLight: { color: "#fff", intensity: 1, offset: [0, 1, 0] }, materials: { cutoutPatterns: [], cutoutNames: [], blendPatterns: [], blendNames: [], additivePatterns: [], additiveNames: [] }, map: { width: 100, height: 100, padding: 1, contours: [] }, }, assets: { visual: [], collision: [] }, transform: { position: [0, 0, 0], rotation: [0, 0, 0], scale: 1 }, bounds: { min: [0, 0, 0], max: [1, 1, 1] }, entrance: { name: "Entrance", footPosition: [0, 0, 0], forward: [0, 0, 1], yaw: 0 }, areas: [], entities: { herald: { id: "herald", name: "The Herald", kind: "boss", title: "Keeper of Tests", combat: { attacks: [ { id: "basic-attack", name: "Basic Attack", delivery: "melee", target: "primary", school: "physical", animation: "attack", range: 4, cooldownMs: 2_000, damageMultiplier: 1, }, { id: "falling-test", name: "Falling Test", spellId: 42, delivery: "area", target: "random-party", school: "arcane", animation: "cast", castTimeMs: 2_000, range: 30, cooldownMs: 12_000, initialCooldownMs: [8_000, 10_000], repeatCooldownMs: [12_000, 14_000], maximumCasts: 1, damageMultiplier: 1.2, telegraph: { shape: "circle", anchor: "target", radius: 6, warningMs: 2_000 }, condition: { kind: "self-health-percent", minimum: 0, maximum: 50 }, }, { id: "source-gap-sweep", name: "Fallback Sweep", delivery: "area", target: "nearby-party", school: "physical", animation: "attack", castTimeMs: 1_800, range: 8, cooldownMs: 10_000, damageMultiplier: 1, telegraph: { shape: "cone", anchor: "source", length: 8, angleDegrees: 90, warningMs: 1_800 }, }, ], }, visual: { primaryColor: "#000", accentColor: "#fff", scale: 1, archetype: "humanoid" }, }, sentinel: { id: "sentinel", name: "Silent Sentinel", kind: "boss", title: "The Unscripted", combat: { attacks: [] }, visual: { primaryColor: "#000", accentColor: "#fff", scale: 1, archetype: "humanoid" }, }, }, staticSpawns: [ { id: "herald-spawn", entityId: "herald", position: [0, 0, 0] }, { id: "sentinel-spawn", entityId: "sentinel", position: [1, 0, 0] }, ], roamingPacks: [], bosses: [ { id: "herald-spawn", name: "The Herald", position: [0, 0, 0] }, { id: "sentinel-spawn", name: "Silent Sentinel", position: [1, 0, 0] }, ], objectiveOrder: [], navigationLinks: [], serverGameObjects: [], provenance: { clientBuild: "test", clientArchiveHashes: {}, databaseAdapter: "test", coordinateTransform: "test" }, validation: { status: "green", blockers: [], warnings: [] }, } as const satisfies DungeonDefinition; describe("boss guide model", () => { it("derives health triggers, timing, telegraph geometry, instructions, and fidelity from runtime attacks", () => { const guide = buildDungeonBossGuide(fixture, { [bossGuideOverrideKey("guide-fixture", "herald", "falling-test")]: { counterplay: "Use the authored safe spot.", }, }); const mechanic = guide.bosses[0].mechanics[0]; expect(mechanic).toMatchObject({ id: "falling-test", trigger: "When the boss reaches 50% health", timing: "First: 8s–10s · Repeats: 12s–14s · Cast: 2s · Maximum uses: 1", targetAndEffect: "Arcane damage to a random party member (1.2× boss power)", telegraph: "6m radius circle centered on the target · 2s warning", counterplay: "Use the authored safe spot.", fidelity: "source", fidelityLabel: "Source backed", }); expect(mechanic.testInstruction).toContain("cross the threshold"); expect(guide.bosses[0].mechanics[1]).toMatchObject({ fidelity: "fallback", fidelityLabel: "HealerMan fallback", }); }); it("omits basic attacks and preserves an honest empty entry for a boss with no specials", () => { const guide = buildDungeonBossGuide(fixture); expect(guide.bosses[0].mechanics.map((mechanic) => mechanic.id)).toEqual([ "falling-test", "source-gap-sweep", ]); expect(guide.bosses[1].mechanics).toEqual([]); }); it("covers every registered boss objective and every non-basic runtime mechanic", () => { const catalog = buildBossGuideCatalog(DUNGEON_DEFINITIONS, BOSS_GUIDE_OVERRIDES); const guideByDungeon = new Map(catalog.map((guide) => [guide.id, guide])); for (const dungeon of DUNGEON_DEFINITIONS) { const guide = guideByDungeon.get(dungeon.id); expect(guide, dungeon.id).toBeDefined(); expect(guide!.bosses.length, dungeon.id).toBeGreaterThanOrEqual(dungeon.bosses.length); for (const objective of dungeon.bosses) { const boss = guide!.bosses.find((entry) => entry.id === objective.id); expect(boss, `${dungeon.id}:${objective.name}`).toBeDefined(); expect(boss!.entityId, `${dungeon.id}:${objective.name}`).toBeDefined(); } for (const boss of guide!.bosses) { if (!boss.entityId) continue; const expectedIds = dungeon.entities[boss.entityId]?.combat?.attacks .filter((attack) => attack.id !== "basic-attack") .map((attack) => attack.id) ?? []; expect(boss.mechanics.map((mechanic) => mechanic.id), `${dungeon.id}:${boss.name}`).toEqual(expectedIds); for (const mechanic of boss.mechanics) { expect(mechanic.trigger, `${dungeon.id}:${boss.name}:${mechanic.name}:trigger`).not.toBe(""); expect(mechanic.timing, `${dungeon.id}:${boss.name}:${mechanic.name}:timing`).not.toBe(""); expect(mechanic.telegraph, `${dungeon.id}:${boss.name}:${mechanic.name}:telegraph`).not.toBe(""); expect(mechanic.counterplay, `${dungeon.id}:${boss.name}:${mechanic.name}:counterplay`).not.toBe(""); expect(mechanic.testInstruction, `${dungeon.id}:${boss.name}:${mechanic.name}:test`).not.toBe(""); } } } const archaedas = guideByDungeon.get("uldaman")!.bosses.find((boss) => boss.name === "Archaedas")!; expect(archaedas.mechanics.find((mechanic) => mechanic.id === "archaedas-awaken-earthen-guardians")) .toMatchObject({ fidelityLabel: "Source backed", counterplay: "At 70% health, the tank gathers the six awakened guardians while the party clears them together.", }); const flowingChaos = guideByDungeon.get("forsaken-abbey")!.bosses.find((boss) => boss.name === "Flowing Chaos")!; expect(flowingChaos.mechanics[0].fidelityLabel).toBe("HealerMan fallback"); const theSeven = guideByDungeon.get("blackrock-depths")!.bosses.find((boss) => boss.name === "The Seven")!; expect(DUNGEON_DEFINITIONS.find((dungeon) => dungeon.id === "blackrock-depths") ?.entities[theSeven.entityId!].combat?.serverEntry).toBe(9_039); const escape = guideByDungeon.get("halls-of-reflection")!.bosses .find((boss) => boss.name === "Escape from the Lich King")!; expect(DUNGEON_DEFINITIONS.find((dungeon) => dungeon.id === "halls-of-reflection") ?.entities[escape.entityId!].combat?.serverEntry).toBe(36_954); expect(escape.mechanics.length).toBeGreaterThan(0); }); });