Update Healer Man server
This commit is contained in:
1 parent
6cc1b9217f
commit
6114a2295f
67 files changed
+6241
-347
No files matched your search
@@ -2,6 +2,9 @@ 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";
|
||||
@@ -72,7 +75,7 @@ const WDT_PLACEMENT_DUNGEON_IDS = [
|
||||
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(92);
|
||||
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) {
|
||||
@@ -86,6 +89,25 @@ describe("generated dungeon campaign catalog", () => {
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
@@ -857,6 +879,163 @@ describe("generated dungeon campaign catalog", () => {
|
||||
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, 16244, 13730].includes(attack.spellId ?? 0))
|
||||
))).toEqual([]);
|
||||
|
||||
const kamAttacks = attacksFor(stockades, 1666);
|
||||
const shieldSlam = kamAttacks.find((attack) => attack.spellId === 8242);
|
||||
expect(shieldSlam?.effect).toMatchObject({ kind: "control", damageMultiplier: 1.35 });
|
||||
expect(kamAttacks.map((attack) => attack.spellId)).not.toContain(3419);
|
||||
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 hostile targeting, utility omissions, and supported 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");
|
||||
|
||||
for (const [entry, spellId] of [
|
||||
[1666, 3419], [1708, 7165], [1711, 16244], [1715, 13730],
|
||||
[1706, 6713], [1707, 17230], [1663, 2457], [1717, 6742],
|
||||
[1716, 9128], [1716, 29651], [1696, 674], [1696, 3391],
|
||||
] as const) {
|
||||
expect(attackFor(entry, spellId), `entry ${entry}, spell ${spellId}`).toBeUndefined();
|
||||
}
|
||||
|
||||
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)).toBeDefined();
|
||||
expect(attackFor(1711, 16509)).toBeDefined();
|
||||
|
||||
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(
|
||||
|
||||
Reference in new issue
Block a user