import { describe, expect, it } from "vitest"; import { gunzipSync, strFromU8 } from "fflate"; import { createHash } from "node:crypto"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { createDungeonEncounterSnapshot, reduceDungeonEncounter, resetDungeonEncounterSnapshot, validateDungeonEncounterDefinition, } from "./dungeonEncounterRuntime"; import type { DungeonEncounterDefinition, DungeonEncounterEvent, DungeonEncounterSnapshot, } from "./dungeonEncounterTypes"; import type { DungeonDefinition, DungeonEncounterSpawnGroupDefinition } from "./dungeonTypes"; import { validateDungeonWorldObject } from "./dungeonWorldObjects"; import compressedCampaign from "./generated/dungeonCampaignCatalog.compressed"; import { withWowEncounterContent } from "./wowEncounterContentCatalog"; import { withWowProgressionObjectContent } from "./wowProgressionObjectContentCatalog"; import { withWowTbcEncounterContent } from "./wowTbcEncounterContentCatalog"; import { WOW_TBC_ENCOUNTER_BATCH_TWO_REVIEWS, withWowTbcEncounterBatchTwoContent, } from "./wowTbcEncounterBatchTwoCatalog"; const TARGETS = [ "underbog", "slave-pens", "mana-tombs", "auchenai-crypts", "magisters-terrace", ] as const; const EXPECTED_SOURCE_HASHES = Object.freeze({ underbog: [ "89a7406ee90f7a068a9ca0e3c4d8b9f8995325a704bbf9e15dfa75a17c78deca", "57aa7cbbdeef9b2bf6bf911e99a118f8b8daa51b9d71e305c673c15b0d101dca", "c10c6e02bc9af2e9c8c876f77cda8abf295f79c3240e789315afd156511a25d9", "28a3e2b836b349b2104d187fdf8b424455ded2b067f221b209b636c509933ac3", "875f6aadf0a44d3119de5cc5751fba232d156ece30bfa5e522bb24f4c005884d", "622b5865f171b0b7a4dd93ecba55afd673c393c579485c17f827b31cb6e47dd2", "5109b88955dfd4f718e581ca88f0f9ec478fc64cdf0904cea07e17157bd68ccb", ], "slave-pens": [ "c1dc38b81d430d16b7a55ff128bb8989fa8ef487bc033540cb7cd5bf5d2f2967", "1e83c465f3cf74797474ee51e3b8a7f6682ede63ba9b2fdd18841ce1b4bf91e8", "09c7f29871b5f2e429c5f643db6f645f0c33a163b143e48e4806ac7231d174d6", "622b5865f171b0b7a4dd93ecba55afd673c393c579485c17f827b31cb6e47dd2", "f9130f5df57a62c4037dbd21b004537ffc6a7651cf9efe64785998d8fbe0828f", "23ef87c37ae66698a2b1aedd23b30b139b5be3bf6040220e6cd45cdcb28b9e67", "3e5713c2a0fa480c82b1753bfea6f1b94a4dec4f29a052ba979c049915a90a42", "0066e4fd35b809986dd325487515087f5a95f4ffe50ea03846c8d8bf5196870a", "d5cce1a83550dcfa9eb2f0251dbb11fd24c272534b2b1a9b230924a44d817ab3", ], "mana-tombs": [ "f243c4ee42d272202023b0c81762221b96e62029006fcfbafbcd5d2bf7b40c7d", "dbb6971658836e2137e6c7a590ce13a790b3d75f79a97edc84e6c8ac8624de1b", "490d328811925cb815949e55ba88919512ae11fed6ce7e0c243947a6014a342c", "f9407bd388db7434746b6f650ae98772933ef47908fb484e19a8460a16e0e783", "622b5865f171b0b7a4dd93ecba55afd673c393c579485c17f827b31cb6e47dd2", "f9130f5df57a62c4037dbd21b004537ffc6a7651cf9efe64785998d8fbe0828f", "23ef87c37ae66698a2b1aedd23b30b139b5be3bf6040220e6cd45cdcb28b9e67", "3e5713c2a0fa480c82b1753bfea6f1b94a4dec4f29a052ba979c049915a90a42", "0066e4fd35b809986dd325487515087f5a95f4ffe50ea03846c8d8bf5196870a", "d5cce1a83550dcfa9eb2f0251dbb11fd24c272534b2b1a9b230924a44d817ab3", ], "auchenai-crypts": [ "5a5192f9f89726bf0d73a9b92fccd5e684c47ef22a53465a1ccc6153dabf490c", "ee1e867c866a9c023d1257f90c99925975a321a67f2303e83a8373e2da3315e0", "970edcd88536186ea675f84ef28cdb3f99b19554f289c5fbb1b42e895d13c8be", "6e34fbe51bed87ca4495a8e0b006017ea2de52673d00d6d5148b00e138ebf752", "622b5865f171b0b7a4dd93ecba55afd673c393c579485c17f827b31cb6e47dd2", "0066e4fd35b809986dd325487515087f5a95f4ffe50ea03846c8d8bf5196870a", "d5cce1a83550dcfa9eb2f0251dbb11fd24c272534b2b1a9b230924a44d817ab3", ], "magisters-terrace": [ "8aa316ceaa914b64e88fe420a0f37537bf016bf5635f8106bf32f82b3eb25728", "43ff46dc05da63bfc4ed49cc5fce8451656857cce544f4ed146736285d4f626d", "458daa44853b6a94a058ab8ccfe08ae4c8ccd34a0387cb766544e12bcbf45dee", "9f5b4a05eb6b6e2acc361f764fd1e472ec255bb9a32369c16e7cc8303e5693e2", "25884cccec0c8bf123c73294bbcdcaee7f6da9a7a0a4af8b3e4d79b66c6b3024", "ed4a734dcdf8b7e186dd4ceeb0ee7d890784aa18edfeab5acf03180a8da1d47f", "3e5713c2a0fa480c82b1753bfea6f1b94a4dec4f29a052ba979c049915a90a42", ], }); function decodeBase64(value: string): Uint8Array { const decoded = atob(value); const bytes = new Uint8Array(decoded.length); for (let index = 0; index < decoded.length; index += 1) bytes[index] = decoded.charCodeAt(index); return bytes; } const campaign = JSON.parse(strFromU8(gunzipSync(decodeBase64(compressedCampaign)))) as { readonly definitions: readonly DungeonDefinition[]; }; function adapted(id: typeof TARGETS[number]): DungeonDefinition { const generated = campaign.definitions.find((definition) => definition.id === id); if (!generated) throw new Error(`Missing generated dungeon ${id}`); const baseline = withWowTbcEncounterContent(withWowProgressionObjectContent(withWowEncounterContent(generated))); return withWowTbcEncounterBatchTwoContent(baseline); } function encounter(definition: DungeonDefinition): DungeonEncounterDefinition { const value = definition.encounters?.find((candidate) => candidate.id === `${definition.id}:tbc-batch-two-instance`); if (!value) throw new Error(`Missing TBC batch-two encounter for ${definition.id}`); return value; } function group(definition: DungeonDefinition, id: string): DungeonEncounterSpawnGroupDefinition { const value = definition.encounterSpawnGroups?.find((candidate) => candidate.id === id); if (!value) throw new Error(`Missing spawn group ${id}`); return value; } function objectIdForSpawn(definition: DungeonDefinition, spawnId: number): string { const object = definition.serverGameObjects.find((candidate) => candidate.source?.spawnId === spawnId); if (!object) throw new Error(`${definition.id} missing object spawn ${spawnId}`); return object.id; } function entryForSpawn(definition: DungeonDefinition, spawnId: string): number | undefined { const spawn = definition.staticSpawns.find((candidate) => candidate.id === spawnId) ?? definition.encounterSpawnGroups?.flatMap((candidate) => candidate.spawns).find((candidate) => candidate.id === spawnId); return spawn ? definition.entities[spawn.entityId]?.combat?.serverEntry : undefined; } function entityForEntry(definition: DungeonDefinition, entry: number) { const entity = Object.values(definition.entities).find((candidate) => candidate.combat?.serverEntry === entry); if (!entity) throw new Error(`${definition.id} missing entity entry ${entry}`); return entity; } function bossIdForEntry(definition: DungeonDefinition, entry: number): string { const spawn = definition.staticSpawns.find((candidate) => entryForSpawn(definition, candidate.id) === entry); if (!spawn) throw new Error(`${definition.id} missing static boss entry ${entry}`); return spawn.id; } function dispatch( definition: DungeonEncounterDefinition, snapshot: DungeonEncounterSnapshot, event: DungeonEncounterEvent, difficultyId?: "normal" | "heroic", ): DungeonEncounterSnapshot { return reduceDungeonEncounter(definition, snapshot, event, { difficultyId }).snapshot; } function killGroup( definition: DungeonEncounterDefinition, snapshot: DungeonEncounterSnapshot, spawnGroup: DungeonEncounterSpawnGroupDefinition, now: number, difficultyId?: "normal" | "heroic", ): DungeonEncounterSnapshot { const ids = spawnGroup.spawns.map((spawn) => spawn.id); return dispatch(definition, snapshot, { kind: "tick", now, facts: { registeredEntityIds: ids, deadEntityIds: ids }, }, difficultyId); } describe("source-backed adapted TBC encounter batch two", () => { it("pins every reviewed source hash/anchor and publishes explicit adaptations and gaps", () => { expect(WOW_TBC_ENCOUNTER_BATCH_TWO_REVIEWS.map((review) => review.dungeonId)).toEqual(TARGETS); for (const review of WOW_TBC_ENCOUNTER_BATCH_TWO_REVIEWS) { expect(review.fidelity).toBe("source-backed-adapted"); expect(review.sourceFiles.map((source) => source.sha256)).toEqual(EXPECTED_SOURCE_HASHES[review.dungeonId]); expect(review.adaptations.length).toBeGreaterThan(0); expect(review.remainingGaps.length).toBeGreaterThan(0); for (const source of review.sourceFiles) { expect(source.path).toMatch(/azerothcore-(?:scripts|world).+\.(?:cpp|h|sql)$|epoch-client.+\.dbc$/); expect(source.sha256).toMatch(/^[a-f0-9]{64}$/); expect(source.anchors.length).toBeGreaterThan(0); expect(source.anchors.every((anchor) => /lines? \d+|records? \d+/.test(anchor))).toBe(true); expect(createHash("sha256").update(readFileSync(resolve(source.path))).digest("hex"), source.path) .toBe(source.sha256); } const sourceText = encounter(adapted(review.dungeonId)).source?.files.join(" ") ?? ""; for (const hash of EXPECTED_SOURCE_HASHES[review.dungeonId]) expect(sourceText).toContain(hash); } }); it("gives each reviewed event actor its source schedule and keeps Frozen Core passive", () => { const auchenai = adapted("auchenai-crypts"); const avatar = entityForEntry(auchenai, 18_478); expect(avatar.combat?.attacks).toMatchObject([ { id: "basic-attack", cooldownMs: 1_391 }, { id: "mortal-strike-16856", name: "Mortal Strike", spellId: 16_856, target: "primary", school: "physical", initialCooldownMs: [8_500, 16_800], repeatCooldownMs: [10_900, 24_100], damageMultiplier: 2, source: { entry: 18_478, rowId: 0, eventType: 0, actionType: 11, targetType: 2 }, }, { id: "sunder-armor-16145", name: "Sunder Armor", spellId: 16_145, target: "primary", initialCooldownMs: [6_500, 11_500], repeatCooldownMs: [6_200, 15_700], effect: { kind: "target-aura", aura: "sunder-armor", durationMs: 30_000 }, source: { entry: 18_478, rowId: 1, eventType: 0, actionType: 11, targetType: 2 }, }, ]); const manaTombs = adapted("mana-tombs"); const yor = entityForEntry(manaTombs, 22_930); expect(yor.combat?.attacks).toMatchObject([ { id: "basic-attack", cooldownMs: 2_000 }, { id: "double-breath-38361", name: "Double Breath", spellId: 38_361, target: "nearby-party", school: "fire", range: 5, initialCooldownMs: [25_500, 30_500], repeatCooldownMs: [10_000, 20_000], telegraph: { shape: "cone", anchor: "source", length: 20 }, }, { id: "stomp-36405", name: "Stomp", spellId: 36_405, target: "nearby-party", school: "physical", initialCooldownMs: [12_000, 18_000], repeatCooldownMs: [14_000, 24_000], }, ]); const slavePens = adapted("slave-pens"); const ahune = entityForEntry(slavePens, 25_740); const frozenCore = entityForEntry(slavePens, 25_865); expect(ahune).toMatchObject({ kind: "boss", hasLoot: true }); expect(ahune.combat?.attacks).toMatchObject([ { id: "ahunes-shield-45954", name: "Ahune's Shield", spellId: 45_954, target: "self", castTimeMs: 0, initialCooldownMs: [4, 4], maximumCasts: 1, effect: { kind: "aura", aura: "ahunes-shield", durationMs: 0, magnitude: 0.75 }, }, { id: "ahune-spanky-hands-46146", name: "[PH] Ahune Spanky Hands", spellId: 46_146, target: "self", castTimeMs: 0, initialCooldownMs: [4, 4], maximumCasts: 1, }, ]); expect(frozenCore).toMatchObject({ kind: "mob", hasLoot: false }); expect(frozenCore.combat?.healthMultiplier).toBe(4.3); expect(frozenCore.combat?.attacks).toMatchObject([ { id: "ice-spear-control-aura-46371", name: "Ice Spear Control Aura", spellId: 46_371, target: "self", castTimeMs: 0, maximumCasts: 1, }, { id: "synch-health-46430", name: "Synch Health", spellId: 46_430, target: "self", castTimeMs: 0, initialCooldownMs: [3_000, 3_000], repeatCooldownMs: [3_000, 3_000], effect: { kind: "encounter-event", eventId: "ahune-synch-health" }, }, ]); for (const entity of [avatar, yor, ahune, frozenCore]) { expect(entity.combat?.attacks.some((attack) => attack.id !== "basic-attack")).toBe(true); expect(entity.combat?.mechanicIds).toEqual( entity.combat?.attacks.filter((attack) => attack.id !== "basic-attack").map((attack) => attack.id), ); } expect(WOW_TBC_ENCOUNTER_BATCH_TWO_REVIEWS.find((review) => review.dungeonId === "slave-pens") ?.remainingGaps.join(" ")).toContain("proc-driven Cold Slap"); }); it("validates definitions, stable action ids, references, timeouts, and wrapper idempotency", () => { for (const id of TARGETS) { const definition = adapted(id); const value = encounter(definition); validateDungeonEncounterDefinition(value); definition.serverGameObjects.forEach(validateDungeonWorldObject); const groups = new Set((definition.encounterSpawnGroups ?? []).map((candidate) => candidate.id)); const objects = new Set(definition.serverGameObjects.map((candidate) => candidate.id)); const spawns = new Set([ ...definition.staticSpawns.map((spawn) => spawn.id), ...(definition.encounterSpawnGroups ?? []).flatMap((candidate) => candidate.spawns.map((spawn) => spawn.id)), ]); const actionIds = value.rules.flatMap((rule) => rule.actions.map((action) => action.id)); expect(new Set(actionIds).size, id).toBe(actionIds.length); for (const rule of value.rules) { if (rule.trigger.kind === "entities-dead") { expect(rule.trigger.timeoutMs, `${id}:${rule.id}`).toBeGreaterThan(0); rule.trigger.entityIds.forEach((entityId) => expect(spawns.has(entityId), entityId).toBe(true)); } if (rule.trigger.kind === "entity-engaged" || rule.trigger.kind === "entity-health" || rule.trigger.kind === "entity-death") { expect(spawns.has(rule.trigger.entityId), `${id}:${rule.id}:${rule.trigger.entityId}`).toBe(true); } if (rule.trigger.kind === "interact") expect(objects.has(rule.trigger.objectId), rule.id).toBe(true); for (const action of rule.actions) { if (action.kind === "activate-spawn-group" || action.kind === "deactivate-spawn-group") { expect(groups.has(action.spawnGroupId), action.id).toBe(true); } if (action.kind === "set-world-object-state") expect(objects.has(action.objectId), action.id).toBe(true); } } expect(withWowTbcEncounterBatchTwoContent(definition)).toBe(definition); } }); it("suppresses event-only/static duplicates and preserves source order and cardinality", () => { const underbog = adapted("underbog"); expect(underbog.staticSpawns.some((spawn) => [18_105, 17_827].includes(entryForSpawn(underbog, spawn.id) ?? -1))).toBe(false); expect(group(underbog, "underbog:ghazan-platform").spawns).toHaveLength(1); expect(group(underbog, "underbog:claw").spawns).toHaveLength(1); expect(underbog.bosses.find((boss) => boss.name === "Ghaz'an")).toMatchObject({ eventOnly: true }); const slavePens = adapted("slave-pens"); expect(slavePens.staticSpawns.some((spawn) => entryForSpawn(slavePens, spawn.id) === 17_893)).toBe(false); expect(group(slavePens, "slave-pens:naturalist-attackers").spawns).toHaveLength(3); expect(group(slavePens, "slave-pens:ahune-coldwaves").spawns).toHaveLength(2); expect(slavePens.objectiveOrder.at(-1)).toMatchObject({ id: "slave-pens:ahune", optional: true, eventOnly: true }); const manaTombs = adapted("mana-tombs"); expect(group(manaTombs, "mana-tombs:pandemonius-room-pull").spawns).toHaveLength(12); expect(group(manaTombs, "mana-tombs:shaheen-wave-1").spawns).toHaveLength(4); expect(group(manaTombs, "mana-tombs:shaheen-wave-2").spawns).toHaveLength(4); expect(group(manaTombs, "mana-tombs:shaheen-wave-3").spawns).toHaveLength(1); expect(manaTombs.staticSpawns.some((spawn) => /^creature-833(0[9]|1[0-9]|21)$/.test(spawn.id))).toBe(false); expect(manaTombs.objectiveOrder.at(-1)).toMatchObject({ id: "mana-tombs:yor", optional: true, eventOnly: true }); const auchenai = adapted("auchenai-crypts"); expect(group(auchenai, "auchenai-crypts:stolen-soul").spawns).toHaveLength(1); expect(group(auchenai, "auchenai-crypts:avatar").spawns).toHaveLength(1); const magisters = adapted("magisters-terrace"); expect(magisters.staticSpawns.some((spawn) => entryForSpawn(magisters, spawn.id) === 24_560)).toBe(false); expect(group(magisters, "magisters-terrace:selin-crystals").spawns).toHaveLength(5); expect(group(magisters, "magisters-terrace:delrissa-party").spawns).toHaveLength(5); expect(group(magisters, "magisters-terrace:kael-spheres").spawns).toHaveLength(3); expect(magisters.objectiveOrder.map((boss) => boss.name)).toEqual([ "Selin Fireheart", "Vexallus", "Priestess Delrissa", "Kael'thas Sunstrider", ]); }); it("replaces only reviewed GO rows by exact source spawnId with visible procedural states", () => { const expected = Object.freeze({ underbog: [], "slave-pens": [50_458, 1_267], "mana-tombs": [43_146, 2_134_514], "auchenai-crypts": [], "magisters-terrace": [99_800, 99_802, 99_807, 99_808, 99_809, 99_813], } as const); for (const id of TARGETS) { const definition = adapted(id); for (const spawnId of expected[id]) { const object = definition.serverGameObjects.find((candidate) => candidate.source?.spawnId === spawnId)!; expect(object.source?.fidelity).toBe("source-backed-adapted"); expect(object.source?.files.length).toBeGreaterThan(0); expect(object.visual?.hidden).not.toBe(true); } } const magisters = adapted("magisters-terrace"); for (const spawnId of [99_802, 99_807, 99_808, 99_809, 99_813]) { const door = magisters.serverGameObjects.find((candidate) => candidate.source?.spawnId === spawnId)!; expect(door.states.find((state) => state.id === "closed")).toMatchObject({ visible: true, blocksMovement: true, blocksSight: true, }); expect(door.states.find((state) => state.id === "open")).toMatchObject({ visible: false, blocksMovement: false, blocksSight: false, }); } }); it("runs Underbog difficulty branches, Ghaz'an release, Claw rescue, and wipe retry", () => { const definition = adapted("underbog"); const value = encounter(definition); const hungarfen = bossIdForEntry(definition, 17_770); let normal = createDungeonEncounterSnapshot(value); normal = dispatch(value, normal, { kind: "entity-engaged", entityId: hungarfen, now: 0 }, "normal"); normal = dispatch(value, normal, { kind: "tick", now: 9_999 }, "normal"); expect(normal.activeSpawnGroupIds).toEqual([]); normal = dispatch(value, normal, { kind: "tick", now: 10_000 }, "normal"); expect(normal.activeSpawnGroupIds).toEqual(["underbog:hungarfen-mushroom-normal"]); let heroic = createDungeonEncounterSnapshot(value); heroic = dispatch(value, heroic, { kind: "entity-engaged", entityId: hungarfen, now: 0 }, "heroic"); heroic = dispatch(value, heroic, { kind: "tick", now: 3_000 }, "heroic"); expect(heroic.activeSpawnGroupIds).toEqual(["underbog:hungarfen-mushroom-heroic"]); heroic = resetDungeonEncounterSnapshot(value, heroic, "boss-wipe", 3_001); heroic = dispatch(value, heroic, { kind: "tick", now: 3_002 }, "heroic"); expect(heroic).toMatchObject({ phase: "ready", activeSpawnGroupIds: [] }); let snapshot = dispatch(value, heroic, { kind: "interact", objectId: "underbog:ghazan-platform-trigger", now: 4_000 }); expect(snapshot.activeSpawnGroupIds).toEqual(["underbog:ghazan-platform"]); snapshot = dispatch(value, snapshot, { kind: "entity-death", entityId: "creature-138392", now: 4_001 }); expect(snapshot.completedObjectiveIds).toContain("creature-138392"); expect(snapshot.flags["ghazan-complete"]).toBe(true); const reset = resetDungeonEncounterSnapshot(value, snapshot, "checkpoint", 4_002); expect(reset.worldObjectStates["underbog:ghazan-platform-trigger"]).toBe("complete"); snapshot = dispatch(value, reset, { kind: "tick", now: 4_003 }); snapshot = dispatch(value, snapshot, { kind: "entity-engaged", entityId: bossIdForEntry(definition, 17_826), now: 4_004 }); expect(snapshot.activeSpawnGroupIds).toContain("underbog:claw"); snapshot = dispatch(value, snapshot, { kind: "entity-health", entityId: "creature-138499", healthPercent: 20, now: 4_005 }); expect(snapshot.flags["claw-rescued"]).toBe(true); expect(snapshot.activeSpawnGroupIds).not.toContain("underbog:claw"); expect(snapshot.worldObjectStates["underbog:rescued-claw"]).toBe("complete"); }); it("runs Naturalist and Ahune as finite, persistent, retryable Slave Pens events", () => { const definition = adapted("slave-pens"); const value = encounter(definition); const cage = objectIdForSpawn(definition, 50_458); const iceStone = objectIdForSpawn(definition, 1_267); let snapshot = createDungeonEncounterSnapshot(value); snapshot = dispatch(value, snapshot, { kind: "interact", objectId: cage, now: 0 }); snapshot = killGroup(value, snapshot, group(definition, "slave-pens:naturalist-attackers"), 1); expect(snapshot.phase).toBe("naturalist-handoff"); snapshot = dispatch(value, snapshot, { kind: "tick", now: 3_441 }); expect(snapshot.flags["naturalist-complete"]).toBe(true); expect(snapshot.worldObjectStates[cage]).toBe("complete"); snapshot = dispatch(value, snapshot, { kind: "interact", objectId: iceStone, now: 4_000 }); snapshot = dispatch(value, snapshot, { kind: "tick", now: 14_000 }); expect(snapshot.activeSpawnGroupIds).toEqual(["slave-pens:ahune"]); snapshot = resetDungeonEncounterSnapshot(value, snapshot, "boss-wipe", 14_001); snapshot = dispatch(value, snapshot, { kind: "tick", now: 14_002 }); expect(snapshot).toMatchObject({ phase: "ready", activeSpawnGroupIds: [] }); expect(snapshot.flags["naturalist-complete"]).toBe(true); snapshot = dispatch(value, snapshot, { kind: "interact", objectId: iceStone, now: 15_000 }); snapshot = dispatch(value, snapshot, { kind: "tick", now: 25_000 }); snapshot = dispatch(value, snapshot, { kind: "tick", now: 29_000 }); expect(snapshot.activeSpawnGroupIds).toContain("slave-pens:ahune-hailstone"); snapshot = dispatch(value, snapshot, { kind: "tick", now: 37_000 }); expect(snapshot.activeSpawnGroupIds).toContain("slave-pens:ahune-coldwaves"); snapshot = dispatch(value, snapshot, { kind: "tick", now: 123_000 }); expect(snapshot.phase).toBe("ahune-core"); expect(snapshot.activeSpawnGroupIds).toContain("slave-pens:frozen-core"); snapshot = dispatch(value, snapshot, { kind: "entity-death", entityId: group(definition, "slave-pens:frozen-core").spawns[0].id, now: 123_001 }); expect(snapshot).toMatchObject({ phase: "ready", flags: { "ahune-complete": true }, activeSpawnGroupIds: [] }); expect(snapshot.completedObjectiveIds).toContain("slave-pens:ahune"); }); it("runs Mana-Tombs room pull, Sha'heen ambushes, and heroic-only Yor", () => { const definition = adapted("mana-tombs"); const value = encounter(definition); let snapshot = createDungeonEncounterSnapshot(value); snapshot = dispatch(value, snapshot, { kind: "entity-engaged", entityId: bossIdForEntry(definition, 18_341), now: 0 }); expect(snapshot.activeSpawnGroupIds).toEqual(["mana-tombs:pandemonius-room-pull"]); snapshot = resetDungeonEncounterSnapshot(value, snapshot, "boss-wipe", 1); snapshot = dispatch(value, snapshot, { kind: "tick", now: 2 }, "normal"); expect(snapshot.activeSpawnGroupIds).toEqual([]); const panel = objectIdForSpawn(definition, 43_146); snapshot = dispatch(value, snapshot, { kind: "interact", objectId: panel, now: 3 }); for (let index = 1; index <= 3; index += 1) { snapshot = killGroup(value, snapshot, group(definition, `mana-tombs:shaheen-wave-${index}`), 3 + index); } expect(snapshot.flags["shaheen-complete"]).toBe(true); expect(snapshot.worldObjectStates[panel]).toBe("complete"); const persisted = resetDungeonEncounterSnapshot(value, snapshot, "checkpoint", 7); expect(persisted.flags["shaheen-complete"]).toBe(true); const stasis = objectIdForSpawn(definition, 2_134_514); let normal = createDungeonEncounterSnapshot(value); normal = dispatch(value, normal, { kind: "tick", now: 0 }, "normal"); expect(normal.worldObjectStates[stasis]).toBe("hidden"); normal = dispatch(value, normal, { kind: "interact", objectId: stasis, now: 1 }, "normal"); expect(normal.activeSpawnGroupIds).toEqual([]); let heroic = createDungeonEncounterSnapshot(value); heroic = dispatch(value, heroic, { kind: "tick", now: 0 }, "heroic"); expect(heroic.worldObjectStates[stasis]).toBe("ready"); heroic = dispatch(value, heroic, { kind: "interact", objectId: stasis, now: 1 }, "heroic"); expect(heroic.activeSpawnGroupIds).toEqual(["mana-tombs:yor"]); heroic = dispatch(value, heroic, { kind: "entity-death", entityId: group(definition, "mana-tombs:yor").spawns[0].id, now: 2 }, "heroic"); expect(heroic.flags["yor-complete"]).toBe(true); expect(heroic.completedObjectiveIds).toContain("mana-tombs:yor"); }); it("runs Maladaar's finite soul/avatar transition and keeps the avatar independently killable", () => { const definition = adapted("auchenai-crypts"); const value = encounter(definition); const maladaar = bossIdForEntry(definition, 18_373); let snapshot = createDungeonEncounterSnapshot(value); snapshot = dispatch(value, snapshot, { kind: "entity-engaged", entityId: maladaar, now: 0 }); snapshot = dispatch(value, snapshot, { kind: "tick", now: 25_000 }); expect(snapshot.activeSpawnGroupIds).toContain("auchenai-crypts:stolen-soul"); snapshot = dispatch(value, snapshot, { kind: "entity-health", entityId: maladaar, healthPercent: 25, now: 25_001 }); expect(snapshot.activeSpawnGroupIds).toContain("auchenai-crypts:avatar"); snapshot = dispatch(value, snapshot, { kind: "entity-death", entityId: maladaar, now: 25_002 }); expect(snapshot.activeSpawnGroupIds).not.toContain("auchenai-crypts:stolen-soul"); expect(snapshot.activeSpawnGroupIds).toContain("auchenai-crypts:avatar"); snapshot = dispatch(value, snapshot, { kind: "entity-death", entityId: group(definition, "auchenai-crypts:avatar").spawns[0].id, now: 25_003 }); expect(snapshot.activeSpawnGroupIds).toEqual([]); const reset = resetDungeonEncounterSnapshot(value, snapshot, "boss-wipe", 25_004); expect(dispatch(value, reset, { kind: "tick", now: 25_005 }).phase).toBe("ready"); }); it("runs Magisters' exact doors, difficulty branches, Delrissa recovery, and reachable completion", () => { const definition = adapted("magisters-terrace"); const value = encounter(definition); const selin = bossIdForEntry(definition, 24_723); const vexallus = bossIdForEntry(definition, 24_744); const kael = bossIdForEntry(definition, 24_664); const selinPassage = objectIdForSpawn(definition, 99_802); const selinRoom = objectIdForSpawn(definition, 99_809); const vexPassage = objectIdForSpawn(definition, 99_813); const delrissaPassage = objectIdForSpawn(definition, 99_807); const kaelRoom = objectIdForSpawn(definition, 99_808); const orb = objectIdForSpawn(definition, 99_800); let normal = createDungeonEncounterSnapshot(value); normal = dispatch(value, normal, { kind: "entity-health", entityId: vexallus, healthPercent: 84, now: 1 }, "normal"); expect(normal.activeSpawnGroupIds).toEqual(["magisters-terrace:vexallus-85-normal"]); expect(group(definition, normal.activeSpawnGroupIds[0]).spawns).toHaveLength(1); let heroic = createDungeonEncounterSnapshot(value); heroic = dispatch(value, heroic, { kind: "entity-health", entityId: vexallus, healthPercent: 84, now: 1 }, "heroic"); expect(heroic.activeSpawnGroupIds).toEqual(["magisters-terrace:vexallus-85-heroic"]); expect(group(definition, heroic.activeSpawnGroupIds[0]).spawns).toHaveLength(2); let snapshot = createDungeonEncounterSnapshot(value); snapshot = dispatch(value, snapshot, { kind: "entity-engaged", entityId: selin, now: 0 }); expect(snapshot.worldObjectStates[selinRoom]).toBe("closed"); expect(snapshot.activeSpawnGroupIds).toContain("magisters-terrace:selin-crystals"); snapshot = dispatch(value, snapshot, { kind: "entity-death", entityId: selin, now: 1 }); expect(snapshot.worldObjectStates[selinPassage]).toBe("open"); expect(snapshot.worldObjectStates[selinRoom]).toBe("open"); snapshot = dispatch(value, snapshot, { kind: "entity-death", entityId: vexallus, now: 2 }); expect(snapshot.worldObjectStates[vexPassage]).toBe("open"); expect(snapshot.activeSpawnGroupIds).toContain("magisters-terrace:delrissa-party"); const prematureKael = dispatch(value, snapshot, { kind: "entity-death", entityId: kael, now: 2.5 }); expect(prematureKael.flags["dungeon-complete"]).not.toBe(true); expect(prematureKael.completedObjectiveIds).not.toContain("creature-96966"); snapshot = resetDungeonEncounterSnapshot(value, snapshot, "boss-wipe", 3); snapshot = dispatch(value, snapshot, { kind: "tick", now: 4 }); expect(snapshot.phase).toBe("delrissa-active"); expect(snapshot.activeSpawnGroupIds).toEqual(["magisters-terrace:delrissa-party"]); snapshot = killGroup(value, snapshot, group(definition, "magisters-terrace:delrissa-party"), 5); expect(snapshot.completedObjectiveIds).toContain("creature-96966"); expect(snapshot.worldObjectStates[delrissaPassage]).toBe("open"); snapshot = dispatch(value, snapshot, { kind: "entity-engaged", entityId: kael, now: 6 }); expect(snapshot.worldObjectStates[kaelRoom]).toBe("closed"); snapshot = dispatch(value, snapshot, { kind: "tick", now: 15_006 }); expect(snapshot.activeSpawnGroupIds).toContain("magisters-terrace:kael-phoenix"); snapshot = dispatch(value, snapshot, { kind: "entity-health", entityId: kael, healthPercent: 50, now: 15_007 }); expect(snapshot.activeSpawnGroupIds).toContain("magisters-terrace:kael-spheres"); snapshot = dispatch(value, snapshot, { kind: "entity-death", entityId: kael, now: 15_008 }); expect(snapshot).toMatchObject({ phase: "complete", flags: { "dungeon-complete": true } }); expect(snapshot.worldObjectStates[kaelRoom]).toBe("open"); expect(snapshot.worldObjectStates[orb]).toBe("unlocked"); const reset = resetDungeonEncounterSnapshot(value, snapshot, "checkpoint", 15_009); const resumed = dispatch(value, reset, { kind: "tick", now: 15_010 }); expect(resumed.phase).toBe("complete"); }); });