Files
healer-man/src/game/dungeonCampaignCatalog.test.ts
T
2026-10-06 22:25:34 -04:00

1198 lines
51 KiB
TypeScript

import { describe, expect, it } from "vitest";
import campaign from "../../dungeon-pipeline/dungeon-campaign.json";
import epochInstances from "../../dungeon-pipeline/epoch-five-player-instances.json";
import gameObjectAssetManifest from "../../dungeon-pipeline/WOW_GAMEOBJECT_ASSET_MANIFEST.json";
import stockadeRecipe from "../../dungeon-pipeline/recipes/stormwind-stockade.json";
import theNexusRecipe from "../../dungeon-pipeline/recipes/the-nexus.json";
import worldAi from "./generated/azerothCoreWorldAi.json";
import generated from "./generated/dungeonCampaignCatalog.json";
import manastormPackages from "./generated/manastormAssetPackages.json";
import { DUNGEON_DEFINITIONS, requireDungeonDefinition } from "./dungeonRegistry";
import {
createDungeonEncounterSnapshot,
reduceDungeonEncounter,
resetDungeonEncounterSnapshot,
validateDungeonEncounterDefinition,
} from "./dungeonEncounterRuntime";
import type { DungeonEncounterDefinition } from "./dungeonEncounterTypes";
import { validateDungeonWorldObject } from "./dungeonWorldObjects";
const SOURCE_NATIVE_DUNGEON_IDS = [
"ahnkahet-old-kingdom",
"arcatraz",
"auchenai-crypts",
"azjol-nerub",
"black-morass",
"blackfathom-deeps",
"blackrock-depths",
"blackrock-spire",
"blood-furnace",
"botanica",
"culling-of-stratholme",
"draktharon-keep",
"escape-from-durnholde",
"forge-of-souls",
"gnomeregan",
"gundrak",
"halls-of-lightning",
"halls-of-reflection",
"halls-of-stone",
"magisters-terrace",
"mana-tombs",
"maraudon",
"mechanar",
"pit-of-saron",
"ragefire-chasm",
"scarlet-monastery",
"sethekk-halls",
"shadow-labyrinth",
"shadowfang-keep",
"shattered-halls",
"slave-pens",
"steamvault",
"stormwind-stockade",
"sunken-temple",
"the-nexus",
"the-oculus",
"trial-of-the-champion",
"uldaman",
"underbog",
"utgarde-keep",
"utgarde-pinnacle",
"violet-hold",
"zulfarrak",
] as const;
const WDT_PLACEMENT_DUNGEON_IDS = [
"dire-maul",
"hellfire-ramparts",
"razorfen-downs",
"razorfen-kraul",
"scholomance",
"stratholme",
] as const;
describe("generated dungeon campaign catalog", () => {
it("covers only reviewed five-player instances with loadable assets and combat content", () => {
expect(generated.definitions).toHaveLength(54);
expect(DUNGEON_DEFINITIONS).toHaveLength(98);
expect(generated.definitions.map((dungeon) => dungeon.id))
.toEqual(campaign.dungeons.map((dungeon) => dungeon.slug));
for (const dungeon of generated.definitions) {
expect(dungeon.assets.visual.length, dungeon.id).toBeGreaterThan(0);
expect(dungeon.assets.collision.length, dungeon.id).toBeGreaterThan(0);
expect(dungeon.assets.navigation.url, dungeon.id)
.toMatch(/^\/assets\/game\/(?:dungeons|manastorm)\//);
expect(Object.keys(dungeon.entities).length, dungeon.id).toBeGreaterThan(0);
expect(dungeon.staticSpawns.length + dungeon.roamingPacks.length, dungeon.id).toBeGreaterThan(0);
expect(dungeon.objectiveOrder.length, dungeon.id).toBeGreaterThan(0);
}
});
it("keeps the six newer source-backed RuneWaker dungeons in the registry", () => {
const newerSourceDungeons = [
["kawaks-tomb", "scripts/runewaker-pipeline/snapshots/kawaks-tomb-zone-134.json"],
["grafu-castle", "scripts/runewaker-pipeline/snapshots/grafu-castle-zone-137.json"],
["sardo-castle", "scripts/runewaker-pipeline/snapshots/sardo-castle-zone-139.json"],
["tomb-seven-heroes", "scripts/runewaker-pipeline/snapshots/tomb-seven-heroes-zone-142.json"],
["aeternal-circle", "scripts/runewaker-pipeline/snapshots/aeternal-circle-zone-144.json"],
["kulech-bones-nest", "scripts/runewaker-pipeline/snapshots/kulech-bones-nest-zone-147.json"],
] as const;
for (const [id, sourceSnapshot] of newerSourceDungeons) {
const definition = DUNGEON_DEFINITIONS.find((candidate) => candidate.id === id);
expect(definition, id).toMatchObject({
expansion: "runewaker",
provenance: { sourceSnapshot },
});
}
});
it("never exposes raw database gameobject rows to the live world-object renderer", () => {
for (const dungeon of DUNGEON_DEFINITIONS) {
for (const object of dungeon.serverGameObjects) {
expect(() => validateDungeonWorldObject(object), `${dungeon.id}:${object.id}`).not.toThrow();
}
}
});
it("gives every WoW dungeon executable persisted boss progression", () => {
const wowDungeons = DUNGEON_DEFINITIONS.filter((dungeon) => (
campaign.dungeons.some((entry) => entry.slug === dungeon.id)
));
expect(wowDungeons).toHaveLength(54);
for (const dungeon of wowDungeons) {
const objectiveIds = dungeon.objectiveOrder.map((objective) => objective.id);
expect(new Set(objectiveIds).size, `${dungeon.id}:duplicate-objective-id`).toBe(objectiveIds.length);
const encounters = dungeon.encounters ?? [];
for (const encounter of encounters) {
expect(() => validateDungeonEncounterDefinition(encounter), `${dungeon.id}:${encounter.id}`).not.toThrow();
}
const allCompletedObjectiveIds = new Set(encounters.flatMap((encounter) => (
encounter.rules.flatMap((rule) => rule.actions.flatMap((action) => (
action.kind === "complete-objective" ? [action.objectiveId] : []
)))
)));
for (const objective of dungeon.objectiveOrder) {
expect(allCompletedObjectiveIds.has(objective.id), `${dungeon.id}:${objective.name}`).toBe(true);
}
const progression = dungeon.encounters?.find(
(encounter) => encounter.source?.scope === "boss-progression",
) as DungeonEncounterDefinition | undefined;
const instanceCompletion = encounters.find((encounter) => (
encounter.source?.scope === "instance-orchestration"
&& encounter.rules.some((rule) => rule.actions.some((action) => (
action.kind === "set-flag"
&& action.flagId === "dungeon-complete"
&& action.value
)))
));
const authority = progression ?? instanceCompletion;
expect(authority, dungeon.id).toBeDefined();
expect(authority!.source?.fidelity, dungeon.id).toBe(
dungeon.id === "tinkertech-showdown"
? "healer-man-authored"
: "source-backed-adapted",
);
if (!progression) {
expect(instanceCompletion?.source?.scope, dungeon.id).toBe("instance-orchestration");
const completedObjectiveIds = new Set(instanceCompletion!.rules.flatMap((rule) => (
rule.actions.flatMap((action) => (
action.kind === "complete-objective" ? [action.objectiveId] : []
))
)));
for (const objective of dungeon.objectiveOrder.filter((candidate) => !candidate.optional)) {
expect(completedObjectiveIds.has(objective.id), `${dungeon.id}:${objective.name}`).toBe(true);
}
continue;
}
const progressionObjectiveIds = new Set(progression.rules.flatMap((rule) => (
rule.actions.flatMap((action) => (
action.kind === "complete-objective" ? [action.objectiveId] : []
))
)));
const runtimeBosses = dungeon.objectiveOrder.filter((objective) => (
progressionObjectiveIds.has(objective.id)
));
let snapshot = createDungeonEncounterSnapshot(progression, 1_000);
const deadEntityIds: string[] = [];
for (const boss of runtimeBosses) {
deadEntityIds.push(boss.id);
snapshot = reduceDungeonEncounter(progression, snapshot, {
kind: "tick",
now: 1_000 + deadEntityIds.length * 100,
facts: {
registeredEntityIds: runtimeBosses.map((objective) => objective.id),
deadEntityIds,
},
}).snapshot;
expect(snapshot.completedObjectiveIds, `${dungeon.id}:${boss.id}`).toContain(boss.id);
}
const checkpoint = resetDungeonEncounterSnapshot(progression, snapshot, "checkpoint", 5_000);
expect(checkpoint.completedObjectiveIds, dungeon.id)
.toEqual(expect.arrayContaining(runtimeBosses.map((boss) => boss.id)));
const completesFromObjectivesAlone = progression.rules.some((rule) => (
rule.actions.some((action) => (
action.kind === "set-flag" && action.flagId === "dungeon-complete" && action.value
))
&& (rule.guards ?? []).every((guard) => guard.kind === "objective-complete")
));
if (!completesFromObjectivesAlone) {
// A full source adapter may own completion inside a separate encounter;
// its focused lifecycle suite exercises that path.
expect(snapshot.flags["dungeon-complete"], dungeon.id).toBe(false);
} else {
expect(snapshot.flags["dungeon-complete"], dungeon.id).toBe(true);
expect(snapshot.phase, dungeon.id).toBe("complete");
expect(checkpoint.flags["dungeon-complete"], dungeon.id).toBe(true);
}
}
});
it("has an instance-level source review for every campaign dungeon", () => {
const wowDungeons = DUNGEON_DEFINITIONS.filter((dungeon) => (
campaign.dungeons.some((entry) => entry.slug === dungeon.id)
));
const verifiedNoOps: string[] = [];
for (const dungeon of wowDungeons) {
const reviewed = (dungeon.encounters ?? []).filter(
(encounter) => encounter.source?.scope === "instance-orchestration",
);
expect(reviewed.length, dungeon.id).toBeGreaterThan(0);
for (const encounter of reviewed) {
expect(encounter.source?.files.length, `${dungeon.id}:${encounter.id}:files`).toBeGreaterThan(0);
expect(encounter.source?.reviewedBehavior.trim(), `${dungeon.id}:${encounter.id}:review`).not.toBe("");
if (!encounter.rules.length) {
expect(encounter.source?.fidelity, `${dungeon.id}:${encounter.id}`).toBe("source-exact");
expect(encounter.source?.adaptations, `${dungeon.id}:${encounter.id}`).toEqual([]);
expect(encounter.source?.remainingGaps, `${dungeon.id}:${encounter.id}`).toEqual([]);
verifiedNoOps.push(dungeon.id);
}
}
}
expect(wowDungeons).toHaveLength(54);
expect(verifiedNoOps.sort()).toEqual([
"ragefire-chasm",
"razorfen-kraul",
"stormwind-stockade",
]);
});
it("gates every required event-only objective behind an instance completion authority", () => {
const wowDungeons = DUNGEON_DEFINITIONS.filter((dungeon) => (
campaign.dungeons.some((entry) => entry.slug === dungeon.id)
));
for (const dungeon of wowDungeons) {
const requiredEvents = dungeon.objectiveOrder.filter(
(objective) => objective.eventOnly && !objective.optional,
);
if (!requiredEvents.length) continue;
const progression = dungeon.encounters?.find(
(encounter) => encounter.source?.scope === "boss-progression",
);
const baselineGatedObjectiveIds = new Set((progression?.rules ?? []).flatMap((rule) => (
rule.actions.some((action) => (
action.kind === "set-flag"
&& action.flagId === "dungeon-complete"
&& action.value
))
? (rule.guards ?? []).flatMap((guard) => (
guard.kind === "objective-complete" && guard.equals ? [guard.objectiveId] : []
))
: []
)));
const completionAuthorities = (dungeon.encounters ?? []).filter((encounter) => (
encounter.source?.scope === "instance-orchestration"
&& encounter.rules.some((rule) => rule.actions.some((action) => (
action.kind === "set-flag"
&& action.flagId === "dungeon-complete"
&& action.value
)))
));
for (const objective of requiredEvents) {
expect(
baselineGatedObjectiveIds.has(objective.id) || completionAuthorities.length > 0,
`${dungeon.id}:${objective.name}`,
).toBe(true);
if (progression && completionAuthorities.length) {
const completionRules = completionAuthorities.flatMap((encounter) => (
encounter.rules.filter((rule) => rule.actions.some((action) => (
action.kind === "set-flag"
&& action.flagId === "dungeon-complete"
&& action.value
)))
));
expect(completionRules.length, `${dungeon.id}:completion-authority`).toBeGreaterThan(0);
expect(completionRules.every((rule) => (
(rule.guards ?? []).some((guard) => (
guard.kind === "objective-complete"
&& guard.objectiveId === objective.id
&& guard.equals
))
|| rule.actions.some((action) => (
action.kind === "complete-objective" && action.objectiveId === objective.id
))
)), `${dungeon.id}:${objective.name}:premature-completion`).toBe(true);
}
}
}
});
it("only uses difficulty guards that match declared dungeon variants", () => {
const wowDungeons = DUNGEON_DEFINITIONS.filter((dungeon) => (
campaign.dungeons.some((entry) => entry.slug === dungeon.id)
));
for (const dungeon of wowDungeons) {
const difficultyIds = new Set(dungeon.difficultyVariants.map((variant) => variant.id));
for (const encounter of dungeon.encounters ?? []) {
for (const rule of encounter.rules) {
for (const guard of rule.guards ?? []) {
if (guard.kind !== "difficulty") continue;
expect(guard.difficultyIds.length, `${dungeon.id}:${rule.id}`).toBeGreaterThan(0);
for (const difficultyId of guard.difficultyIds) {
expect(difficultyIds.has(difficultyId), `${dungeon.id}:${rule.id}:${difficultyId}`).toBe(true);
}
}
}
}
}
});
it("completes ordinary dungeons regardless of required boss kill order", () => {
const dungeon = requireDungeonDefinition("deadmines");
const progression = dungeon.encounters?.find(
(encounter) => encounter.id === "deadmines:boss-progression",
);
expect(progression).toBeDefined();
const required = dungeon.objectiveOrder.filter((objective) => !objective.optional && !objective.eventOnly);
expect(required.length).toBeGreaterThan(1);
const killOrder = [required.at(-1)!, ...required.slice(0, -1)];
const deadEntityIds: string[] = [];
let snapshot = createDungeonEncounterSnapshot(progression!, 1_000);
for (const boss of killOrder) {
deadEntityIds.push(boss.id);
snapshot = reduceDungeonEncounter(progression!, snapshot, {
kind: "tick",
now: 1_000 + deadEntityIds.length * 100,
facts: {
registeredEntityIds: required.map((objective) => objective.id),
deadEntityIds,
},
}).snapshot;
}
expect(snapshot.flags["dungeon-complete"]).toBe(true);
expect(snapshot.phase).toBe("complete");
});
it("compiles every sourced WoW gameobject row with stable source provenance", () => {
const wowDungeons = DUNGEON_DEFINITIONS.filter((dungeon) => (
campaign.dungeons.some((entry) => entry.slug === dungeon.id)
));
const objects = wowDungeons.flatMap((dungeon) => dungeon.serverGameObjects);
const sourceRows = objects.filter((object) => (
object.source?.templateId !== undefined && object.source?.spawnId !== undefined
));
expect(wowDungeons).toHaveLength(54);
expect(sourceRows).toHaveLength(3_943);
expect(sourceRows.filter((object) => object.source?.clientDisplayId)).toHaveLength(3_905);
expect(sourceRows.filter((object) => object.source?.clientModelPath)).toHaveLength(3_837);
expect(sourceRows.filter((object) => object.source?.clientBounds)).toHaveLength(3_547);
for (const dungeon of wowDungeons) {
expect(new Set(dungeon.serverGameObjects.map((object) => object.id)).size)
.toBe(dungeon.serverGameObjects.length);
for (const object of dungeon.serverGameObjects) {
expect(object.source?.fidelity).toBe("source-backed-adapted");
expect(object.source?.files.length).toBeGreaterThan(0);
if (object.source?.spawnId !== undefined) {
expect(object.source.templateId).toBeGreaterThan(0);
expect(object.source.spawnId).toBeGreaterThan(0);
expect(object.source.gameObjectType).toBeGreaterThanOrEqual(0);
expect(object.source.gameObjectType).toBeLessThanOrEqual(35);
}
if (object.visual?.hidden) {
expect(object.interaction).toBeUndefined();
expect(object.states.every((state) => !state.blocksMovement && !state.blocksSight)).toBe(true);
}
}
}
});
it("publishes a deduplicated export manifest for source-backed gameobject visuals", () => {
expect(gameObjectAssetManifest.summary).toEqual({
typedInstances: 3_943,
visualDisplayInstances: 3_905,
nonVisualInstances: 38,
resolvedModelInstances: 3_837,
boundedInstances: 3_547,
uniqueAssets: 458,
unresolvedModelInstances: 68,
unresolvedBoundInstances: 358,
unresolvedModelDisplayIds: 9,
unresolvedBoundDisplayIds: 68,
});
expect(gameObjectAssetManifest.assets).toHaveLength(458);
expect(new Set(gameObjectAssetManifest.assets.map(
(asset) => asset.sourceModelPath.toLowerCase(),
)).size).toBe(gameObjectAssetManifest.assets.length);
expect(gameObjectAssetManifest.assets.every((asset) => (
["mdl", "mdx"].includes(asset.sourceType)
&& asset.targetUrl.startsWith("/assets/game/world-objects/")
&& asset.targetUrl.endsWith(".glb")
&& asset.instances > 0
&& asset.displayIds.length > 0
&& asset.dungeons.length > 0
))).toBe(true);
expect(gameObjectAssetManifest.assets.reduce((total, asset) => total + asset.instances, 0))
.toBe(gameObjectAssetManifest.summary.resolvedModelInstances);
expect(gameObjectAssetManifest.nonVisualObjects.reduce(
(total, object) => total + object.instances,
0,
)).toBe(gameObjectAssetManifest.summary.nonVisualInstances);
expect(gameObjectAssetManifest.unresolvedModels.map((entry) => entry.displayId)).toEqual([
9158, 9159, 9160, 9161, 9162, 9163, 9236, 9467, 9468,
]);
});
it("runs Wailing Caverns with the complete server population and authored presentation", () => {
const wailing = requireDungeonDefinition("wailing-caverns");
const runtimePopulation = wailing.staticSpawns.length
+ wailing.roamingPacks.reduce((total, pack) => total + pack.members.length, 0);
expect(runtimePopulation).toBeGreaterThan(250);
expect(wailing.staticSpawns.some((spawn) => spawn.id === "creature-18678")).toBe(true);
expect(wailing.staticSpawns.some((spawn) => spawn.id === "lady-anacondra-spawn")).toBe(true);
expect(wailing.roamingPacks.some((pack) => pack.id === "kresh-patrol")).toBe(true);
expect(wailing.entities["entry-3636"]).toMatchObject({
name: "Deviate Ravager",
combat: { serverEntry: 3636 },
visual: {
model: { url: "/assets/creatures/wailing-caverns/deviate-ravager-animated.glb" },
},
});
});
it("never replaces a generated dungeon with a smaller live population", () => {
for (const source of generated.definitions) {
const runtime = requireDungeonDefinition(source.id);
const sourcePopulation = source.staticSpawns.length
+ source.roamingPacks.reduce((total, pack) => total + pack.members.length, 0);
const runtimePopulation = runtime.staticSpawns.length
+ runtime.roamingPacks.reduce((total, pack) => total + pack.members.length, 0)
+ new Set((runtime.encounterSpawnGroups ?? [])
.flatMap((group) => group.spawns.map((spawn) => spawn.id))
.filter((id) => !runtime.staticSpawns.some((spawn) => spawn.id === id))).size;
if (source.id === "frozen-reach") {
expect(runtime.staticSpawns.some((spawn) => spawn.id.startsWith("generated-trash-"))).toBe(false);
expect(runtime.validation.warnings.join(" ")).toMatch(/recovered Alva\/Arktos/i);
} else {
expect(runtimePopulation, source.id).toBeGreaterThanOrEqual(sourcePopulation);
}
}
});
it("preserves source-side random rare spawns without making rare bosses mandatory", () => {
const chanceSpawns = generated.definitions.flatMap((dungeon) => [
...dungeon.staticSpawns.filter((spawn) => "spawnChance" in spawn),
...dungeon.roamingPacks.filter((pack) => "spawnChance" in pack),
]) as unknown as ReadonlyArray<{ readonly spawnChance: number }>;
const optionalBosses = generated.definitions.flatMap((dungeon) => (
dungeon.objectiveOrder.filter((objective) => {
if (!("optional" in objective) || !objective.optional) return false;
const spawn = dungeon.staticSpawns.find((candidate) => candidate.id === objective.id);
return Boolean(spawn && "spawnChance" in spawn);
})
));
expect(chanceSpawns).toHaveLength(24);
expect(chanceSpawns.every((spawn) => spawn.spawnChance > 0 && spawn.spawnChance < 1)).toBe(true);
expect(optionalBosses.map((boss) => boss.name)).toEqual([
"Earthcaller Halmgar",
"Ragglesnout",
"Crystal Fang",
"Spirestone Battle Lord",
"Spirestone Butcher",
"Meshlok the Harvester",
]);
});
it("reuses each complete optimized map package for dungeons and Manastorms", () => {
const packagesByMap = new Map(
manastormPackages.packages.map((assetPackage) => [assetPackage.mapId, assetPackage]),
);
const identity = (descriptor: { readonly url: string; readonly checksum: string }) => ({
url: descriptor.url,
checksum: descriptor.checksum,
});
for (const dungeon of generated.definitions) {
const assetPackage = packagesByMap.get(dungeon.mapId);
expect(assetPackage, dungeon.id).toBeTruthy();
expect(dungeon.assets.visual.map(identity), dungeon.id).toEqual(assetPackage!.visual.map(identity));
expect(dungeon.assets.collision.map(identity), dungeon.id)
.toEqual(assetPackage!.collision.map(identity));
expect(identity(dungeon.assets.navigation), dungeon.id).toEqual(identity(assetPackage!.navigation));
}
const shadowfang = generated.definitions.find((dungeon) => dungeon.id === "shadowfang-keep")!;
expect(shadowfang.assets.navigation.url)
.toMatch(/^\/assets\/game\/manastorm\/33-shadowfang\//);
expect(Object.values(shadowfang.entities).every((entity) => entity.visual.model)).toBe(true);
expect(
Object.values(shadowfang.entities).every(
(entity) => (
entity.visual.model?.url.includes("/dungeons/shadowfang-keep/creatures/")
|| entity.visual.model?.url.includes("/game/creatures/shared/")
),
),
).toBe(true);
});
it("ships every selected Epoch map as a true normal/heroic instanced dungeon", () => {
expect(epochInstances.dungeons).toHaveLength(18);
for (const source of epochInstances.dungeons) {
const dungeon = generated.definitions.find((candidate) => candidate.id === source.slug);
expect(dungeon, source.slug).toBeTruthy();
expect(dungeon!.mapId, source.slug).toBe(source.mapId);
expect(dungeon!.provenance.clientBuild, source.slug).toBe("3.3.5a-Epoch");
expect(dungeon!.difficultyVariants.map((variant) => variant.id), source.slug)
.toEqual(["normal", "heroic"]);
expect(dungeon!.objectiveOrder.length, source.slug).toBeGreaterThan(0);
expect(dungeon!.staticSpawns.length + dungeon!.roamingPacks.length, source.slug)
.toBeGreaterThan(0);
const entities = Object.values(dungeon!.entities) as ReadonlyArray<{
readonly id: string;
readonly name: string;
readonly visual: { readonly model?: { readonly url: string } };
}>;
const eventOnlyNames = new Set((
dungeon!.objectiveOrder as unknown as ReadonlyArray<{
readonly name: string;
readonly eventOnly?: boolean;
}>
).filter((objective) => objective.eventOnly).map((objective) => objective.name));
for (const entity of entities) {
if (entity.visual.model) {
expect(entity.visual.model.url, `${source.slug}:${entity.name}`)
.toMatch(new RegExp(
`^/assets/(?:game/creatures/shared|game/dungeons/${source.slug}/creatures)/.+\\.glb$`,
));
} else {
expect(
eventOnlyNames.has(entity.name) || /^entry-8\d{8}$/.test(entity.id),
`${source.slug}:${entity.name}`,
).toBe(true);
}
}
}
});
it("compiles exact scripted boss abilities before generic fallbacks", () => {
const forge = generated.definitions.find((dungeon) => dungeon.id === "forge-of-souls");
const culling = generated.definitions.find((dungeon) => dungeon.id === "culling-of-stratholme");
expect(forge).toBeTruthy();
expect(culling).toBeTruthy();
const attackNames = (
dungeon: NonNullable<typeof forge>,
bossName: string,
): string[] => {
const entity = Object.values(dungeon.entities).find((candidate) => candidate.name === bossName);
return entity?.combat.attacks.map((attack: { readonly name: string }) => attack.name) ?? [];
};
expect(attackNames(forge!, "Bronjahm")).toEqual([
"Basic Attack",
"Shadow Bolt",
"Fear",
"Magic's Bane",
"Corrupt Soul",
"Soulstorm",
]);
expect(attackNames(culling!, "Mal'Ganis")).toEqual([
"Basic Attack",
"Carrion Swarm",
"Mind Blast",
"Sleep",
"Vampiric Touch",
]);
const attackFor = (
dungeonId: string,
bossName: string,
attackName: string,
): { readonly damageMultiplier: number; readonly effect?: unknown } | undefined => {
const dungeon = generated.definitions.find((candidate) => candidate.id === dungeonId);
const entity = Object.values(dungeon?.entities ?? {}).find(
(candidate) => candidate.name === bossName,
) as undefined | {
readonly combat: {
readonly attacks: ReadonlyArray<{
readonly name: string;
readonly damageMultiplier: number;
readonly effect?: unknown;
}>;
};
};
return entity?.combat.attacks.find((attack) => attack.name === attackName);
};
expect(attackFor("forge-of-souls", "Bronjahm", "Fear")).toMatchObject({
effect: { kind: "control", mechanic: "fear", durationMs: 3_000 },
});
expect(attackFor("culling-of-stratholme", "Mal'Ganis", "Sleep")).toMatchObject({
effect: { kind: "control", mechanic: "sleep", durationMs: 10_000 },
});
expect(attackFor("blackrock-depths", "Warder Stilgiss", "Frostbolt")).toMatchObject({
damageMultiplier: 0,
effect: {
kind: "control",
mechanic: "slow",
durationMs: 8_000,
damageMultiplier: 1.35,
},
});
expect(attackFor("blackrock-depths", "Warder Stilgiss", "Frost Nova")).toMatchObject({
damageMultiplier: 0,
effect: {
kind: "control",
mechanic: "root",
durationMs: 8_000,
damageMultiplier: 0.82,
},
});
expect(attackFor("razorfen-kraul", "Aggem Thorncurse", "Summon Boar Spirit"))
.toMatchObject({
damageMultiplier: 0,
effect: { kind: "summon", creatureEntry: 6021, count: 1 },
});
expect(attackFor("blackrock-depths", "Bael'Gar", "Summon Spawn of Bael'Gar"))
.toMatchObject({
effect: {
kind: "composite",
effects: [
{ kind: "damage", multiplier: 1.35 },
{ kind: "summon", creatureEntry: 9436, count: 1 },
],
},
});
expect(attackFor("shattered-halls", "Grand Warlock Nethekurse", "Death Coil"))
.toMatchObject({
effect: {
kind: "composite",
effects: [
{ kind: "leech", damageMultiplier: 1.35, healMultiplier: 1.35 },
{ kind: "control", mechanic: "fear", durationMs: 4_000 },
],
},
});
expect(attackFor("magisters-terrace", "Kael'thas Sunstrider", "Shock Barrier"))
.toMatchObject({
effect: { kind: "aura", absorbMultiplier: 2.5, durationMs: 10_000 },
});
expect(attackFor("blackrock-spire", "General Drakkisath", "Pierce Armor"))
.toMatchObject({
effect: {
kind: "target-aura",
damageTakenMultiplier: 1.2,
damageSchool: "physical",
},
});
expect(attackFor("culling-of-stratholme", "Mal'Ganis", "Vampiric Touch"))
.toMatchObject({
effect: { kind: "aura", lifeStealPercent: 0.5, durationMs: 30_000 },
});
expect(attackFor("underbog", "The Black Stalker", "Summon Player"))
.toMatchObject({ effect: { kind: "pull", destination: "source" } });
const effectRegressionSpellIds = new Set([
246, 6_016, 7_645, 8_286, 8_398, 8_988, 9_080, 10_061, 10_180, 11_086,
11_700, 12_097, 12_479, 12_480, 12_556, 12_642, 13_323, 13_747, 13_895,
14_515, 14_907, 15_041, 15_044, 15_487, 15_530, 15_572, 15_859,
16_097, 17_235, 17_238, 17_279, 17_503, 17_773, 18_099, 18_327, 20_279, 21_707,
22_714, 30_500, 31_692, 32_358, 32_364, 32_365, 32_371, 32_424, 34_309,
34_557, 35_032, 35_280, 36_931, 36_932, 37_162, 38_245, 44_219, 44_294, 46_165,
49_037, 52_723, 53_030, 53_418, 66_043, 67_534, 67_705, 67_715,
]);
const observedEffectSpellIds = new Set<number>();
for (const dungeon of generated.definitions) {
for (const entity of Object.values(dungeon.entities)) {
if (entity.kind !== "boss") continue;
for (const attack of entity.combat.attacks) {
if (!effectRegressionSpellIds.has(attack.spellId ?? 0)) continue;
expect(attack.effect, `${dungeon.id}:${entity.name}:${attack.name}`).toBeDefined();
observedEffectSpellIds.add(attack.spellId!);
}
}
}
expect([...observedEffectSpellIds].sort((left, right) => left - right))
.toEqual([...effectRegressionSpellIds].sort((left, right) => left - right));
const auxiliarySpellIds = new Set([21_708, 47_669, 54_888]);
for (const dungeon of generated.definitions) {
for (const entity of Object.values(dungeon.entities)) {
if (entity.kind !== "boss") continue;
expect(
entity.combat.attacks.some((attack: { readonly spellId?: number }) => (
auxiliarySpellIds.has(attack.spellId ?? 0)
)),
`${dungeon.id}:${entity.name}:auxiliary-cast`,
).toBe(false);
}
}
expect(
attackFor("escape-from-durnholde", "Captain Skarloc", "Holy Light"),
).toMatchObject({
effect: { kind: "heal", multiplier: 1.2 },
});
});
it("compiles Archaedas's timed and health-based encounter mechanics", () => {
const uldaman = generated.definitions.find((dungeon) => dungeon.id === "uldaman")!;
const archaedas = Object.values(uldaman.entities).find(
(entity) => entity.name === "Archaedas",
) as unknown as {
readonly combat: {
readonly mechanicIds: readonly string[];
readonly attacks: ReadonlyArray<{
readonly id: string;
readonly initialCooldownMs?: readonly [number, number];
readonly repeatCooldownMs?: readonly [number, number];
readonly condition?: { readonly kind: string; readonly maximum?: number };
readonly effect?: { readonly kind: string; readonly creatureEntry?: number; readonly count?: number };
readonly telegraph?: { readonly shape: string; readonly anchor: string; readonly radius?: number };
}>;
};
};
expect(archaedas.combat.mechanicIds).toEqual([
"archaedas-awaken-earthen-custodian",
"archaedas-ground-tremor",
"archaedas-awaken-earthen-guardians",
"archaedas-awaken-vault-warders",
]);
expect(archaedas.combat.attacks.find((attack) => attack.id === "archaedas-ground-tremor"))
.toMatchObject({
initialCooldownMs: [0, 10_000],
repeatCooldownMs: [14_000, 20_000],
telegraph: { shape: "circle", anchor: "source", radius: 18 },
});
expect(archaedas.combat.attacks.find(
(attack) => attack.id === "archaedas-awaken-earthen-guardians",
)).toMatchObject({
condition: { kind: "self-health-percent", maximum: 70 },
effect: { kind: "summon", creatureEntry: 7076, count: 6 },
});
expect(archaedas.combat.attacks.find(
(attack) => attack.id === "archaedas-awaken-vault-warders",
)).toMatchObject({
condition: { kind: "self-health-percent", maximum: 40 },
effect: { kind: "summon", creatureEntry: 10120, count: 2 },
});
});
it("does not leak outdoor zones, arenas, or raids into the dungeon browser", () => {
const ids = new Set(generated.definitions.map((dungeon) => dungeon.id));
for (const excluded of [
"ashamanes-fall",
"redridge-mountains",
"westfall-outdoor",
"tanaris-outdoor",
"nagrand-outdoor",
"black-rook-hold-arena",
"molten-core",
"karazhan",
]) {
expect(ids.has(excluded), excluded).toBe(false);
}
});
it("gives every generated creature a complete animation-driving attack kit", () => {
for (const dungeon of generated.definitions) {
for (const entity of Object.values(dungeon.entities)) {
expect(entity.combat.attacks.length, `${dungeon.id}:${entity.id}`).toBeGreaterThan(0);
for (const attack of entity.combat.attacks) {
expect(["attack", "cast"], `${dungeon.id}:${entity.id}:${attack.id}`)
.toContain(attack.animation);
expect(attack.cooldownMs, `${dungeon.id}:${entity.id}:${attack.id}`).toBeGreaterThan(0);
if (attack.target === "self" || attack.target === "nearby-friendly") {
expect(attack.range, `${dungeon.id}:${entity.id}:${attack.id}`).toBeGreaterThanOrEqual(0);
} else {
expect(attack.range, `${dungeon.id}:${entity.id}:${attack.id}`).toBeGreaterThan(0);
}
}
}
}
});
it("binds every boss objective to an actual boss spawn", () => {
for (const dungeon of generated.definitions) {
const spawns = new Map(dungeon.staticSpawns.map((spawn) => [spawn.id, spawn]));
const entities = dungeon.entities as unknown as Readonly<Record<string, { readonly kind: string }>>;
for (const boss of dungeon.objectiveOrder) {
const spawn = spawns.get(boss.id);
if ("eventOnly" in boss && boss.eventOnly) {
expect(spawn, `${dungeon.id}:${boss.name}`).toBeUndefined();
expect(
Object.values(dungeon.entities).some((entity) => (
entity.kind === "boss" && entity.name === boss.name
)),
`${dungeon.id}:${boss.name}`,
).toBe(true);
continue;
}
expect(spawn, `${dungeon.id}:${boss.name}`).toBeTruthy();
expect(entities[spawn!.entityId]?.kind, `${dungeon.id}:${boss.name}`).toBe("boss");
}
}
});
it("makes every required boss objective available in every declared difficulty", () => {
for (const dungeon of generated.definitions) {
const spawns = new Map(dungeon.staticSpawns.map((spawn) => [spawn.id, spawn]));
for (const boss of dungeon.objectiveOrder) {
if (("eventOnly" in boss && boss.eventOnly) || ("optional" in boss && boss.optional)) continue;
const spawn = spawns.get(boss.id);
expect(spawn, `${dungeon.id}:${boss.name}`).toBeTruthy();
for (const difficulty of dungeon.difficultyVariants) {
expect(
(spawn!.spawnMask & difficulty.spawnMask) !== 0,
`${dungeon.id}:${boss.name}:${difficulty.id}`,
).toBe(true);
}
}
}
});
it("preserves heroic-only Epoch bosses without gating normal completion", () => {
for (const [dungeonId, bossName] of [
["the-nexus", "Frozen Commander"],
["ahnkahet-old-kingdom", "Amanitar"],
["gundrak", "Eck the Ferocious"],
] as const) {
const dungeon = generated.definitions.find((entry) => entry.id === dungeonId)!;
const boss = dungeon.objectiveOrder.find((objective) => objective.name === bossName)!;
const spawn = dungeon.staticSpawns.find((entry) => entry.id === boss.id)!;
expect("optional" in boss && boss.optional, `${dungeonId}:${bossName}`).toBe(true);
expect(spawn.spawnMask, `${dungeonId}:${bossName}`).toBe(2);
}
const blackMorass = generated.definitions.find((dungeon) => dungeon.id === "black-morass")!;
for (const boss of blackMorass.objectiveOrder) {
const spawn = blackMorass.staticSpawns.find((entry) => entry.id === boss.id)!;
expect("optional" in boss && boss.optional, boss.name).not.toBe(true);
expect(spawn.spawnMask, boss.name).toBe(3);
}
});
it("aligns the Deadmines source population to its packaged interior route", () => {
const deadmines = requireDungeonDefinition("deadmines");
const firstPack = deadmines.staticSpawns.find(
(spawn) => spawn.id === "creature-79127",
);
expect(deadmines.environmentMode).toBe("adt-hybrid");
expect(deadmines.provenance.coordinateTransform)
.toBe("three(x,y,z)=(wow.y,wow.z,wow.x)");
expect(deadmines.entrance.footPosition).toEqual([-383.0017, 61.9028, -16.4018]);
expect(firstPack?.position).toEqual([-401.1297, 55.8039, -50.2669]);
expect(deadmines.objectiveOrder[0]?.position)
.toEqual([-448.1427, 54.5567, -192.9168]);
expect(deadmines.objectiveOrder.at(-1)?.name).toBe("Edwin VanCleef");
expect(deadmines.objectiveOrder.at(-1)?.position)
.toEqual([-819.8267, 39.4232, -87.3708]);
});
it("keeps Stockades creatures in the package's native world coordinates", () => {
const stockades = requireDungeonDefinition("stormwind-stockade");
const firstSpawn = stockades.staticSpawns.find(
(spawn) => spawn.id === "creature-79035",
);
const targorr = stockades.staticSpawns.find(
(spawn) => spawn.id === "creature-84027",
);
expect(stockades.environmentMode).toBe("global-wmo");
expect(stockades.provenance.coordinateTransform)
.toBe("three(x,y,z)=draft(x,y,z)");
expect(stockades.entrance.footPosition).toEqual([-54.23, -18.34, 0.28]);
expect(firstSpawn?.position).toEqual([-82.3245, -26.4396, -10.6106]);
expect(targorr?.position).toEqual([-159.582, -25.6062, 1.2531]);
});
it("omits only the source-confirmed utility-only Stockade casts and preserves real damage", () => {
const stockades = requireDungeonDefinition("stormwind-stockade");
const blackrockDepths = requireDungeonDefinition("blackrock-depths");
const attacksFor = (dungeon: typeof stockades, entry: number) => (
dungeon.entities[`entry-${entry}`]?.combat?.attacks ?? []
);
const hasSpell = (dungeon: typeof stockades, entry: number, spellId: number) => (
attacksFor(dungeon, entry).some((attack) => attack.spellId === spellId)
);
const sourceSmartAction = (
entry: "1663" | "1666" | "1706" | "1708" | "1711" | "1715" | "1716" | "1717" | "1696",
spellId: number,
) => (
worldAi.smartScriptsByEntry[entry]?.find((row) => (
row.action_type === 11 && row.action_param1 === spellId
))
);
expect(sourceSmartAction("1666", 7164)).toMatchObject({ event_type: 0, target_type: 1 });
expect(sourceSmartAction("1708", 7165)).toMatchObject({ event_type: 1, target_type: 1 });
expect(sourceSmartAction("1715", 13730)).toMatchObject({ event_type: 0, target_type: 1 });
expect(worldAi.creatureSpellsByEntry["1711"]?.map((spell) => spell.spellId)).toContain(16244);
expect([1666, 1708, 1711, 1715].flatMap((entry) => (
attacksFor(stockades, entry).filter((attack) => [7164, 7165].includes(attack.spellId ?? 0))
)).map((attack) => attack.spellId).sort()).toEqual([7164, 7165]);
const kamAttacks = attacksFor(stockades, 1666);
const shieldSlam = kamAttacks.find((attack) => attack.spellId === 8242);
expect(shieldSlam?.effect).toMatchObject({ kind: "control", damageMultiplier: 1.35 });
expect(kamAttacks.find((attack) => attack.spellId === 3419)).toMatchObject({
target: "self", damageMultiplier: 0,
effect: { kind: "aura", durationMs: 6_000, wowAuraSource: { spellId: 3419, sourceProfile: "ascension" } },
});
expect(kamAttacks.some((attack) => attack.id.endsWith("-surge"))).toBe(false);
const inmateAttacks = attacksFor(stockades, 1708);
expect(hasSpell(stockades, 1708, 6547)).toBe(true);
expect(hasSpell(stockades, 1708, 6253)).toBe(true);
expect(hasSpell(stockades, 1711, 16509)).toBe(true);
const grebmarAttacks = attacksFor(blackrockDepths, 9319);
expect(hasSpell(blackrockDepths, 9319, 21049)).toBe(true);
expect(hasSpell(blackrockDepths, 9319, 15615)).toBe(true);
expect(grebmarAttacks.some((attack) => attack.id.endsWith("-surge"))).toBe(false);
});
it("preserves pinned Stockade targeting, supported utility and damage", () => {
const stockades = requireDungeonDefinition("stormwind-stockade");
const theNexus = requireDungeonDefinition("the-nexus");
const attacksFor = (entry: number) => (
stockades.entities[`entry-${entry}`]?.combat?.attacks ?? []
);
const attackFor = (entry: number, spellId: number) => (
attacksFor(entry).find((attack) => attack.spellId === spellId)
);
const sourceSmartAction = (
entry: "1663" | "1666" | "1706" | "1708" | "1711" | "1715" | "1716" | "1717" | "1696",
spellId: number,
) => (
worldAi.smartScriptsByEntry[entry]?.find((row) => (
row.action_type === 11 && row.action_param1 === spellId
))
);
expect(sourceSmartAction("1663", 11976)).toMatchObject({ target_type: 2 });
expect(sourceSmartAction("1663", 19134)).toMatchObject({ target_type: 2 });
expect(sourceSmartAction("1666", 8242)).toMatchObject({ target_type: 2 });
expect(sourceSmartAction("1716", 7964)).toMatchObject({ target_type: 1 });
expect(stockadeRecipe.clientBuild).toBe("3.3.5a-Ascension");
expect(theNexusRecipe.clientBuild).toBe("3.3.5a-Epoch");
expect(attackFor(1663, 2457)).toBeUndefined();
for (const [entry, spellId] of [[1706, 6713], [1707, 17230]] as const) {
expect(attackFor(entry, spellId)).toMatchObject({
target: "primary", delivery: "melee", damageMultiplier: 0,
effect: { kind: "target-aura", wowAuraSource: { spellId, sourceProfile: "ascension" } },
});
}
expect(attackFor(1716, 29651)?.effect).toMatchObject({
kind: "weapon-capability", capability: "dual-wield", sourceSpellId: 29651, sourceProfile: "ascension",
});
expect(attackFor(1696, 674)).toMatchObject({
source: { eventType: 0 }, initialCooldownMs: [1_000, 1_000],
effect: { kind: "weapon-capability", capability: "dual-wield", sourceSpellId: 674, sourceProfile: "ascension" },
});
expect(attackFor(1696, 3391)?.effect).toMatchObject({
kind: "extra-attacks", sourceSpellId: 3391, sourceProfile: "ascension", count: 2,
});
expect(attackFor(1717, 6742)).toMatchObject({
target: "self", damageMultiplier: 0, maximumCasts: 1,
initialCooldownMs: [5_000, 5_000], source: { eventType: 0, castFlags: 32 },
});
for (const [entry, spellId, durationMs] of [[1666, 7164, 180_000], [1666, 3419, 6_000], [1708, 7165, 0]]) {
expect(attackFor(entry, spellId)).toMatchObject({
target: "self", delivery: "area", range: 0, damageMultiplier: 0,
effect: { kind: "aura", durationMs, wowAuraSource: { spellId, sourceProfile: "ascension" } },
});
}
expect(attackFor(1708, 7165)).toMatchObject({
activation: "out-of-combat", initialCooldownMs: [1_000, 1_000], maximumCasts: 1,
source: { eventType: 1, targetType: 1 },
});
expect(attackFor(1666, 7164)).toMatchObject({
maximumCasts: 1, initialCooldownMs: [1_000, 1_000], source: { eventType: 0 },
});
expect(attackFor(1666, 8242)).toMatchObject({
target: "primary",
delivery: "melee",
range: 5,
effect: { kind: "control", mechanic: "stun", damageMultiplier: 1.35 },
});
expect(attackFor(1711, 8242)).toMatchObject({
target: "primary",
delivery: "melee",
range: 5,
effect: { kind: "control", mechanic: "stun", damageMultiplier: 1.12 },
});
expect(attackFor(1711, 6253)).toMatchObject({
target: "primary",
delivery: "melee",
range: 5,
effect: { kind: "control", mechanic: "stun", durationMs: 2_000 },
});
expect(attackFor(1708, 6547)).toMatchObject({ effect: { kind: "dot", ticks: 5, intervalMs: 3000,
wowPeriodicSource: { spellId: 6547, sourceProfile: "ascension-patch-d" } } });
expect(attackFor(1711, 16509)).toMatchObject({ effect: { kind: "dot", ticks: 5, intervalMs: 3000,
wowPeriodicSource: { spellId: 16509, sourceProfile: "ascension-patch-d" } } });
expect(attackFor(1711, 16244)).toMatchObject({ target: "nearby-party", range: 10, radius: 10,
damageMultiplier: 0, effect: { kind: "target-aura", wowAuraSource: { spellId: 16244, sourceProfile: "ascension" } } });
expect(attackFor(1715, 13730)).toMatchObject({ target: "nearby-party", range: 10, radius: 10,
damageMultiplier: 0, effect: { kind: "target-aura", wowAuraSource: { spellId: 13730, sourceProfile: "ascension" } } });
expect(attackFor(1716, 9128)).toMatchObject({ target: "nearby-friendly", range: 0, radius: 20,
damageMultiplier: 0, effect: { kind: "aura", wowAuraSource: { spellId: 9128, sourceProfile: "ascension" } } });
expect(attackFor(1663, 11976)).toMatchObject({
target: "primary",
delivery: "melee",
range: 5,
});
expect(attackFor(1707, 15657)).toMatchObject({
target: "primary",
delivery: "melee",
range: 5,
attackKind: "melee",
});
expect(attackFor(1706, 11978)).toMatchObject({
target: "primary",
delivery: "melee",
range: 5,
effect: { kind: "control", mechanic: "interrupt", durationMs: 4_000 },
});
expect(attackFor(1717, 421)).toMatchObject({
target: "primary",
delivery: "projectile",
range: 30,
attackKind: "spell",
});
expect(attackFor(1717, 421)?.damageMultiplier).toBeGreaterThan(0);
expect(attackFor(1663, 5246)).toMatchObject({
target: "nearby-party",
range: 8,
radius: 8,
effect: { kind: "control", mechanic: "fear" },
});
expect(attackFor(1663, 19134)).toMatchObject({
target: "nearby-party",
range: 10,
radius: 8,
effect: { kind: "control", mechanic: "fear", durationMs: 4_000 },
});
expect(attackFor(1716, 7964)).toMatchObject({
target: "nearby-party",
range: 5,
radius: 5,
effect: { kind: "control", mechanic: "stun", durationMs: 4_000 },
});
const epochFrighteningShout = theNexus.entities["entry-26796"]?.combat?.attacks
.find((attack) => attack.spellId === 19134);
expect(epochFrighteningShout).toMatchObject({
target: "nearby-party",
delivery: "area",
range: 10,
radius: 8,
effect: { kind: "control", mechanic: "fear", durationMs: 6_000 },
});
const targorrAttacks = attacksFor(1696);
expect(attackFor(1696, 8599)?.effect).toMatchObject({ kind: "aura" });
expect(targorrAttacks.some((attack) => attack.id.endsWith("-surge"))).toBe(false);
});
it("starts Ragefire Chasm in the dungeon instead of the Orgrimmar portal annex", () => {
const ragefire = requireDungeonDefinition("ragefire-chasm");
const firstSpawn = ragefire.staticSpawns.find(
(spawn) => spawn.id === "creature-38041",
);
const jergosh = ragefire.staticSpawns.find(
(spawn) => spawn.id === "creature-48740",
);
expect(ragefire.environmentMode).toBe("global-wmo");
expect(ragefire.provenance.coordinateTransform)
.toBe("three(x,y,z)=draft(x,y,z)");
expect(ragefire.entrance.footPosition).toEqual([-3.81, -17.84, -14.82]);
expect(firstSpawn?.position).toEqual([45.0298, -21.2917, -27.7645]);
expect(jergosh?.position).toEqual([376.811, -21.801, 209.224]);
});
it("keeps every navigation-audited dungeon in its native source coordinates", () => {
const sourceAligned = generated.definitions
.filter((dungeon) => dungeon.provenance.coordinateAlignment === "source")
.map((dungeon) => dungeon.id)
.sort();
expect(sourceAligned).toEqual([...SOURCE_NATIVE_DUNGEON_IDS].sort());
for (const dungeonId of SOURCE_NATIVE_DUNGEON_IDS) {
const dungeon = requireDungeonDefinition(dungeonId);
expect(dungeon.provenance.coordinateAlignment, dungeonId).toBe("source");
expect(dungeon.provenance.coordinateTransform, dungeonId)
.not.toContain("package-anchor-translation");
expect(
dungeon.validation.warnings.some((warning) => (
warning.includes("native world coordinates")
)),
dungeonId,
).toBe(true);
}
});
it("pins representative global-WMO and ADT entrances to their source basis", () => {
const expectedEntrances = {
"blackfathom-deeps": [151.89, -39.87, 106.96],
"blackrock-depths": [-456.929, -68.0896, 34.0923],
"blood-furnace": [3.9967, -44.8009, 14.6363],
"the-nexus": [-145.87, -16.636, -10.554],
"violet-hold": [803.93, 44.364, 1808.82],
"black-morass": [7034.7, 32.5619, -1496.24],
"culling-of-stratholme": [556.92, 36.69, 1431.1],
} as const;
for (const [dungeonId, expected] of Object.entries(expectedEntrances)) {
expect(requireDungeonDefinition(dungeonId).entrance.footPosition, dungeonId)
.toEqual(expected);
}
});
it("keeps scripted Trial of the Champion objectives on the native arena floor", () => {
const trial = requireDungeonDefinition("trial-of-the-champion");
const [entranceX, , entranceZ] = trial.entrance.footPosition;
expect(trial.objectiveOrder).toHaveLength(5);
for (const objective of trial.objectiveOrder) {
expect(objective.position[1], objective.name).toBeGreaterThanOrEqual(411);
expect(objective.position[1], objective.name).toBeLessThanOrEqual(413);
expect(
Math.hypot(objective.position[0] - entranceX, objective.position[2] - entranceZ),
objective.name,
).toBeLessThan(150);
}
});
it("uses client WDT placement records for rotated or translated root WMOs", () => {
for (const dungeonId of WDT_PLACEMENT_DUNGEON_IDS) {
expect(
requireDungeonDefinition(dungeonId).provenance.coordinateAlignment,
dungeonId,
).toBe("source-transform");
expect(
requireDungeonDefinition(dungeonId).provenance.coordinateTransform,
dungeonId,
).toContain("rotate-y");
expect(
requireDungeonDefinition(dungeonId).provenance.coordinateTransform,
dungeonId,
).not.toContain("package-anchor-translation");
}
});
it("pins every map found by the portal-map coordinate audit to its dungeon entrance", () => {
const expectedEntrances = {
arcatraz: [1.2316, -0.2043, 0.0143],
"dire-maul": [44.4499, -2.712, 154.822],
"hellfire-ramparts": [5.7204, 129.1393, -94.4661],
"razorfen-downs": [-190.2691, -10.71, -100.8219],
"razorfen-kraul": [-37.901, -5.684, 22.7908],
"scarlet-monastery": [1053.48, 18.6775, 1688.99],
scholomance: [-55.0407, -13.1583, 20.4141],
"shadowfang-keep": [2109.18, 76.8898, -229.135],
stratholme: [-143.4892, -5.8848, -266.8634],
zulfarrak: [841.59, 8.93, 1213.52],
} as const;
for (const [dungeonId, expected] of Object.entries(expectedEntrances)) {
expect(requireDungeonDefinition(dungeonId).entrance.footPosition, dungeonId)
.toEqual(expected);
}
});
it("starts Razorfen Kraul inside the portal and maps its first pack into the WMO", () => {
const razorfenKraul = requireDungeonDefinition("razorfen-kraul");
const firstPackSpawn = razorfenKraul.staticSpawns.find(
(spawn) => spawn.id === "creature-87284",
);
expect(razorfenKraul.entrance.footPosition).toEqual([-37.901, -5.684, 22.7908]);
expect(firstPackSpawn).toBeTruthy();
expect(firstPackSpawn!.position[0]).toBeGreaterThan(razorfenKraul.entrance.footPosition[0]);
expect(firstPackSpawn!.position[0]).toBeLessThan(100);
expect(razorfenKraul.provenance.coordinateTransform)
.toBe("three(x,y,z)=rotate-y(draft(x,y,z)-[-1952.1803,87.684,1587.8923],-133deg)");
});
});