Release Healer Man 0.1.22

This commit is contained in:
phenom committed 2026-09-09 11:21:33 -04:00
1 parent f6f2375145
commit 7f6001723a
144 files changed
+54107 -690

No files matched your search

+369 -3
View File
@@ -1,9 +1,18 @@
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 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",
@@ -77,6 +86,313 @@ describe("generated dungeon campaign catalog", () => {
}
});
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) => dungeon.expansion !== "runewaker");
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
@@ -101,9 +417,17 @@ describe("generated dungeon campaign catalog", () => {
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);
+ 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;
expect(runtimePopulation, source.id).toBeGreaterThanOrEqual(sourcePopulation);
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);
}
}
});
@@ -113,7 +437,11 @@ describe("generated dungeon campaign catalog", () => {
...dungeon.roamingPacks.filter((pack) => "spawnChance" in pack),
]) as unknown as ReadonlyArray<{ readonly spawnChance: number }>;
const optionalBosses = generated.definitions.flatMap((dungeon) => (
dungeon.objectiveOrder.filter((objective) => "optional" in objective && objective.optional)
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);
@@ -355,6 +683,44 @@ describe("generated dungeon campaign catalog", () => {
}
});
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(