Files
healer-man/src/game/wowTbcEncounterBatchTwoCatalog.ts
T
2026-09-09 11:21:33 -04:00

1845 lines
82 KiB
TypeScript

import type {
DungeonBossObjective,
DungeonDefinition,
DungeonEncounterSpawnGroupDefinition,
Vector3Tuple,
} from "./dungeonTypes";
import type {
DungeonEncounterAction,
DungeonEncounterDefinition,
DungeonEncounterRule,
} from "./dungeonEncounterTypes";
import type {
DungeonWorldObjectDefinition,
DungeonWorldObjectStateDefinition,
} from "./dungeonWorldObjects";
import type {
PopulationDefinitionMap,
PopulationEntityDefinition,
StaticMobSpawnDefinition,
} from "./mobPopulation";
import type { EnemyAttackDefinition } from "./enemyAttacks";
export interface WowTbcEncounterBatchTwoSourceFile {
readonly path: string;
readonly sha256: string;
readonly anchors: readonly string[];
}
export interface WowTbcEncounterBatchTwoReview {
readonly dungeonId:
| "underbog"
| "slave-pens"
| "mana-tombs"
| "auchenai-crypts"
| "magisters-terrace";
readonly fidelity: "source-backed-adapted";
readonly sourceFiles: readonly WowTbcEncounterBatchTwoSourceFile[];
readonly reviewedBehavior: string;
readonly adaptations: readonly string[];
readonly remainingGaps: readonly string[];
}
interface AdaptedEntitySpec {
readonly entry: number;
readonly name: string;
readonly level: number;
readonly role: "melee" | "caster";
readonly kind?: "mob" | "boss";
readonly title?: string;
readonly attacks?: readonly EnemyAttackDefinition[];
readonly healthMultiplier?: number;
}
const SCRIPT_ROOT = "../HealerMan-Storage/pipeline-work/dungeons/azerothcore-scripts/src/server/scripts";
const DB_ROOT = "../HealerMan-Storage/pipeline-work/dungeons/azerothcore-world";
const EPOCH_SPELL_DBC = "../HealerMan-Storage/pipeline-work/dungeons/epoch-client/DBFilesClient/Spell.dbc";
const UNDERBOG_FILES = Object.freeze([
`${SCRIPT_ROOT}/Outland/CoilfangReservoir/underbog/instance_the_underbog.cpp`,
`${SCRIPT_ROOT}/Outland/CoilfangReservoir/underbog/boss_ghazan.cpp`,
`${SCRIPT_ROOT}/Outland/CoilfangReservoir/underbog/boss_hungarfen.cpp`,
`${SCRIPT_ROOT}/Outland/CoilfangReservoir/underbog/boss_the_black_stalker.cpp`,
`${SCRIPT_ROOT}/Outland/CoilfangReservoir/underbog/the_underbog.h`,
`${DB_ROOT}/smart_scripts.sql`,
`${DB_ROOT}/waypoint_data.sql`,
]);
const SLAVE_PENS_FILES = Object.freeze([
`${SCRIPT_ROOT}/Outland/CoilfangReservoir/SlavePens/instance_the_slave_pens.cpp`,
`${SCRIPT_ROOT}/Outland/CoilfangReservoir/SlavePens/boss_ahune.cpp`,
`${SCRIPT_ROOT}/Outland/CoilfangReservoir/SlavePens/the_slave_pens.h`,
`${DB_ROOT}/smart_scripts.sql`,
`${DB_ROOT}/creature_summon_groups.sql`,
`${DB_ROOT}/waypoints.sql`,
`${DB_ROOT}/gameobject.sql`,
]);
const MANA_TOMBS_FILES = Object.freeze([
`${SCRIPT_ROOT}/Outland/Auchindoun/ManaTombs/instance_mana_tombs.cpp`,
`${SCRIPT_ROOT}/Outland/Auchindoun/ManaTombs/boss_pandemonius.cpp`,
`${SCRIPT_ROOT}/Outland/Auchindoun/ManaTombs/boss_nexusprince_shaffar.cpp`,
`${SCRIPT_ROOT}/Outland/Auchindoun/ManaTombs/mana_tombs.h`,
`${DB_ROOT}/smart_scripts.sql`,
`${DB_ROOT}/creature_summon_groups.sql`,
`${DB_ROOT}/waypoints.sql`,
`${DB_ROOT}/gameobject.sql`,
]);
const AUCHENAI_FILES = Object.freeze([
`${SCRIPT_ROOT}/Outland/Auchindoun/AuchenaiCrypts/instance_auchenai_crypts.cpp`,
`${SCRIPT_ROOT}/Outland/Auchindoun/AuchenaiCrypts/boss_exarch_maladaar.cpp`,
`${SCRIPT_ROOT}/Outland/Auchindoun/AuchenaiCrypts/boss_shirrak_the_dead_watcher.cpp`,
`${SCRIPT_ROOT}/Outland/Auchindoun/AuchenaiCrypts/auchenai_crypts.h`,
`${DB_ROOT}/smart_scripts.sql`,
]);
const MAGISTERS_FILES = Object.freeze([
`${SCRIPT_ROOT}/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp`,
`${SCRIPT_ROOT}/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp`,
`${SCRIPT_ROOT}/EasternKingdoms/MagistersTerrace/boss_vexallus.cpp`,
`${SCRIPT_ROOT}/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp`,
`${SCRIPT_ROOT}/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp`,
`${SCRIPT_ROOT}/EasternKingdoms/MagistersTerrace/magisters_terrace.h`,
`${DB_ROOT}/gameobject.sql`,
]);
export const WOW_TBC_ENCOUNTER_BATCH_TWO_REVIEWS: readonly WowTbcEncounterBatchTwoReview[] = Object.freeze([
{
dungeonId: "underbog",
fidelity: "source-backed-adapted",
sourceFiles: [
{
path: UNDERBOG_FILES[0],
sha256: "89a7406ee90f7a068a9ca0e3c4d8b9f8995325a704bbf9e15dfa75a17c78deca",
anchors: ["Hungarfen/Ghazan object registration and four encounter states (lines 23-48)"],
},
{
path: UNDERBOG_FILES[1],
sha256: "57aa7cbbdeef9b2bf6bf911e99a118f8b8daa51b9d71e305c673c15b0d101dca",
anchors: ["Ghazan area trigger starts his twenty-point platform path (lines 79-102, 132-149)"],
},
{
path: UNDERBOG_FILES[2],
sha256: "c10c6e02bc9af2e9c8c876f77cda8abf295f79c3240e789315afd156511a25d9",
anchors: ["normal ten-second versus heroic 2.4-3.6-second mushroom cadence (lines 68-89)"],
},
{
path: UNDERBOG_FILES[3],
sha256: "28a3e2b836b349b2104d187fdf8b424455ded2b067f221b209b636c509933ac3",
anchors: ["heroic-only Spore Strider summons every 10-15 seconds (lines 84-103)"],
},
{
path: UNDERBOG_FILES[4],
sha256: "875f6aadf0a44d3119de5cc5751fba232d156ece30bfa5e522bb24f4c005884d",
anchors: ["boss, mushroom, and encounter ids (lines 25-40)"],
},
{
path: UNDERBOG_FILES[5],
sha256: "622b5865f171b0b7a4dd93ecba55afd673c393c579485c17f827b31cb6e47dd2",
anchors: ["Claw's 20-percent rescue action and faction/morph sequence (lines 19062-19069, 43898-43909)"],
},
{
path: UNDERBOG_FILES[6],
sha256: "5109b88955dfd4f718e581ca88f0f9ec478fc64cdf0904cea07e17157bd68ccb",
anchors: ["Ghazan spawn 138392 path 1383921 and exact point 20 platform endpoint (lines 125247-125266)"],
},
],
reviewedBehavior: "Ghazan is moved from his unsafe static pre-path spawn into a direct, finite platform release; Hungarfen and heroic Black Stalker receive bounded first summon cycles, while Claw changes from a hostile actor to a rescued presentation at the exact 20-percent DB threshold.",
adaptations: [
"An interaction at Ghazan's imported objective position replaces the area trigger and twenty waypoint movements, spawning him at source waypoint 20.",
"Hungarfen and Black Stalker each receive one finite summon instead of indefinitely repeating schedules; normal and heroic timing/availability are separate guarded branches.",
"Claw is moved from static population into Muselek's engagement group; the DB faction, morph, gossip, and movement sequence becomes a non-combat rescued marker.",
],
remainingGaps: [
"Ghazan path animation, mushroom growth/clouds, repeated summons, Foul Spores, Black Stalker levitation, Claw dialogue/quest credit, exact faction switching, and native actor presentation remain unported.",
],
},
{
dungeonId: "slave-pens",
fidelity: "source-backed-adapted",
sourceFiles: [
{
path: SLAVE_PENS_FILES[0],
sha256: "c1dc38b81d430d16b7a55ff128bb8989fa8ef487bc033540cb7cd5bf5d2f2967",
anchors: ["Ahune/core/flamecaller instance object registration (lines 31-44, 61-103)"],
},
{
path: SLAVE_PENS_FILES[1],
sha256: "1e83c465f3cf74797474ee51e3b8a7f6682ede63ba9b2fdd18841ce1b4bf91e8",
anchors: [
"Ahune/core source positions and three flamecaller spots (lines 157-178)",
"ritual starts Ahune at ten seconds and the first finite adds at fourteen/twenty-two seconds (lines 381-402, 421-471)",
"Ice Stone consumes the quest item and starts the event (lines 657-675)",
],
},
{
path: SLAVE_PENS_FILES[2],
sha256: "09c7f29871b5f2e429c5f643db6f645f0c33a163b143e48e4806ac7231d174d6",
anchors: ["Ahune, Frozen Core, support actor, Ice Spear, and Ice Stone ids (lines 28-77)"],
},
{
path: SLAVE_PENS_FILES[3],
sha256: "622b5865f171b0b7a4dd93ecba55afd673c393c579485c17f827b31cb6e47dd2",
anchors: ["Naturalist Bite gossip, cage release, and two waypoint callbacks (lines 19184-19192, 43938-43947)"],
},
{
path: SLAVE_PENS_FILES[4],
sha256: "f9130f5df57a62c4037dbd21b004537ffc6a7651cf9efe64785998d8fbe0828f",
anchors: ["Naturalist Bite exact Champion/Soothsayer/Enchantress summon group (lines 666-668)"],
},
{
path: SLAVE_PENS_FILES[5],
sha256: "23ef87c37ae66698a2b1aedd23b30b139b5be3bf6040220e6cd45cdcb28b9e67",
anchors: ["Naturalist Bite's exact two-point rescue path (lines 9320-9321)"],
},
{
path: SLAVE_PENS_FILES[6],
sha256: "3e5713c2a0fa480c82b1753bfea6f1b94a4dec4f29a052ba979c049915a90a42",
anchors: ["Ice Stone spawn 1267 and Naturalist cage spawn 50458 (lines 825, 36018)"],
},
{
path: `${DB_ROOT}/creature_template.sql`,
sha256: "0066e4fd35b809986dd325487515087f5a95f4ffe50ea03846c8d8bf5196870a",
anchors: ["Ahune and passive Frozen Core templates/script bindings (lines 19706, 19830)"],
},
{
path: EPOCH_SPELL_DBC,
sha256: "d5cce1a83550dcfa9eb2f0251dbb11fd24c272534b2b1a9b230924a44d817ab3",
anchors: ["records 45954, 46145, 46146, 46371, and 46430"],
},
],
reviewedBehavior: "The exact Naturalist cage releases a finite three-attacker rescue and a post-route rescued marker; the exact seasonal Ice Stone starts a bounded Ahune phase-one/add/core sequence without changing the ordinary three-boss route, with Ahune's initialization auras and the passive Frozen Core's three-second synchronization schedule represented explicitly.",
adaptations: [
"Naturalist's friendly escort, simultaneous attacker summon, and pause become a kill-gated three-actor sequence followed by a 3.44-second presentation handoff at source waypoint two.",
"The seasonal/item/quest gate is exposed as a retryable direct Ice Stone interaction because inventory, holiday, and quest guards are not encounter-reducer inputs.",
"Ahune's repeating 108/35-second shield/submerge loop is one finite phase-one cycle; Frozen Core defeat deterministically completes the optional encounter.",
"Invisible ritual helper rows are removed from unconditional static population instead of being rendered as combat mobs.",
"Frozen Core is an attackable passive mob rather than a second boss; its controller aura and three-second Synch Health cast are non-damaging schedules, and Ahune's shield/proc setup is likewise not fabricated into timed direct damage.",
],
remainingGaps: [
"Protected-friendly escort movement/failure, Mark of Bite, quest credit, dialogue, seasonal/item gating, functional shared-health/shield reduction, proc-driven Cold Slap, slippery floor, ice spears, repeated adds, achievements, and loot remain unported.",
],
},
{
dungeonId: "mana-tombs",
fidelity: "source-backed-adapted",
sourceFiles: [
{
path: MANA_TOMBS_FILES[0],
sha256: "f243c4ee42d272202023b0c81762221b96e62029006fcfbafbcd5d2bf7b40c7d",
anchors: ["four boss-state slots including optional Yor (lines 23-40)"],
},
{
path: MANA_TOMBS_FILES[1],
sha256: "dbb6971658836e2137e6c7a590ce13a790b3d75f79a97edc84e6c8ac8624de1b",
anchors: ["Pandemonius engages all three add entries inside the 70-yard and Y-bound room filter (lines 42-53, 89-123)"],
},
{
path: MANA_TOMBS_FILES[2],
sha256: "490d328811925cb815949e55ba88919512ae11fed6ce7e0c243947a6014a342c",
anchors: ["Shaffar's ten-second repeating Ethereal Beacon summon (lines 77-121) and Yor combat AI (lines 152-188)"],
},
{
path: MANA_TOMBS_FILES[3],
sha256: "f9407bd388db7434746b6f650ae98772933ef47908fb484e19a8460a16e0e783",
anchors: ["Pandemonius/Tavarok/Shaffar/Yor encounter order (lines 26-35)"],
},
{
path: MANA_TOMBS_FILES[4],
sha256: "622b5865f171b0b7a4dd93ecba55afd673c393c579485c17f827b31cb6e47dd2",
anchors: [
"transporter summons Sha'heen (line 39210)",
"Sha'heen escort milestones and protected-failure behavior (lines 21949-21968, 45229-45296)",
"Shaffar's Stasis Chamber summons and factions Yor (lines 39369-39370)",
],
},
{
path: MANA_TOMBS_FILES[5],
sha256: "f9130f5df57a62c4037dbd21b004537ffc6a7651cf9efe64785998d8fbe0828f",
anchors: ["Sha'heen's exact 4/4/1 ambush compositions and positions (lines 647-655)"],
},
{
path: MANA_TOMBS_FILES[6],
sha256: "23ef87c37ae66698a2b1aedd23b30b139b5be3bf6040220e6cd45cdcb28b9e67",
anchors: ["Sha'heen's 3/37/2 waypoint route segments (lines 11495-11536)"],
},
{
path: MANA_TOMBS_FILES[7],
sha256: "3e5713c2a0fa480c82b1753bfea6f1b94a4dec4f29a052ba979c049915a90a42",
anchors: ["transporter panel spawn 43146 and heroic-mask Yor chamber spawn 2134514 (lines 30612, 92147)"],
},
{
path: `${DB_ROOT}/creature_template.sql`,
sha256: "0066e4fd35b809986dd325487515087f5a95f4ffe50ea03846c8d8bf5196870a",
anchors: ["Yor base attack time and npc_yor binding (line 16973)"],
},
{
path: EPOCH_SPELL_DBC,
sha256: "d5cce1a83550dcfa9eb2f0251dbb11fd24c272534b2b1a9b230924a44d817ab3",
anchors: ["records 36405 and 38361"],
},
],
reviewedBehavior: "Pandemonius engagement activates the exact twelve imported actors matching the source entry/range/Y filter, Shaffar receives one finite Beacon, the exact transporter runs Sha'heen's three source ambush groups, and Shaffar's Stasis Chamber exposes optional Yor only on heroic with his exact Double Breath and Stomp task cadence.",
adaptations: [
"Pandemonius's already-placed room actors are withheld until engagement because the runtime can activate finite groups but cannot order existing static actors to enter combat.",
"Shaffar receives one Beacon rather than an unbounded ten-second summon loop.",
"Sha'heen's 42-waypoint protected escort is compressed to three exact kill-gated 4/4/1 ambushes started from the exact transporter panel.",
"The stasis chamber uses explicit normal-hidden and heroic-ready branches; Yor is removed from ordinary/static progression and spawned only by the reviewed heroic interaction.",
"Yor's source melee-distance Breath gate is retained by attack range and its cone, Stomp area, spell ids, schools, and randomized schedules are explicit; flat spell values are normalized to encounter-scaled damage.",
],
remainingGaps: [
"Existing-actor aggro orders, repeating Beacons and Apprentice conversion, Sha'heen movement/health/failure/dialogue/quest credit, transporter presentation, Yor faction animation, native GO models, and stasis loot remain unported.",
],
},
{
dungeonId: "auchenai-crypts",
fidelity: "source-backed-adapted",
sourceFiles: [
{
path: AUCHENAI_FILES[0],
sha256: "5a5192f9f89726bf0d73a9b92fccd5e684c47ef22a53465a1ccc6153dabf490c",
anchors: ["two ordinary boss-state slots and no route-wide GO gate (lines 23-40)"],
},
{
path: AUCHENAI_FILES[1],
sha256: "ee1e867c866a9c023d1257f90c99925975a321a67f2303e83a8373e2da3315e0",
anchors: ["Avatar summon at 25 percent (lines 66-75) and repeating 25-30-second class-shaped Stolen Soul (lines 88-116)"],
},
{
path: AUCHENAI_FILES[2],
sha256: "970edcd88536186ea675f84ef28cdb3f99b19554f289c5fbb1b42e895d13c8be",
anchors: ["Shirrak's 17-second Focus Fire and 3/3.5/4-second blast sequence (lines 101-151)"],
},
{
path: AUCHENAI_FILES[3],
sha256: "6e34fbe51bed87ca4495a8e0b006017ea2de52673d00d6d5148b00e138ebf752",
anchors: ["Shirrak then Maladaar encounter order (lines 26-33)"],
},
{
path: AUCHENAI_FILES[4],
sha256: "622b5865f171b0b7a4dd93ecba55afd673c393c579485c17f827b31cb6e47dd2",
anchors: ["Avatar's Mortal Strike/Sunder, zone combat, and out-of-combat despawn rows (lines 20130-20135)"],
},
{
path: `${DB_ROOT}/creature_template.sql`,
sha256: "0066e4fd35b809986dd325487515087f5a95f4ffe50ea03846c8d8bf5196870a",
anchors: ["Avatar base attack time and SmartAI binding (line 12660)"],
},
{
path: EPOCH_SPELL_DBC,
sha256: "d5cce1a83550dcfa9eb2f0251dbb11fd24c272534b2b1a9b230924a44d817ab3",
anchors: ["records 16145 and 16856"],
},
],
reviewedBehavior: "The ordinary two-boss route remains intact; Maladaar engagement gains one finite Stolen Soul proxy and his exact 25-percent Avatar transition, whose exact SmartAI Mortal Strike/Sunder schedule remains active through cleanup and wipe retry behavior.",
adaptations: [
"One generic hostile Stolen Soul replaces repeating player-class cloning because the reducer has no party-class target or appearance-copy action.",
"The Avatar is one finite source-entry actor at the exact health threshold and may remain after Maladaar dies, matching the source override that avoids boss-summon cleanup.",
"Mortal Strike keeps its 8.5-16.8/10.9-24.1-second SmartAI windows and 200-percent weapon hit; Sunder keeps its 6.5-11.5/6.2-15.7-second windows as a refreshable armor-debuff marker.",
],
remainingGaps: [
"Class-specific Stolen Soul visuals/spells/target aura, repeated souls, Avatar phase-in/combat orders, Mortal Strike's healing penalty, Sunder's armor math/five stacks, Shirrak distance-scaled Inhibit Magic, Focus Fire ground blasts, possession, dialogue, and summon ownership remain unported.",
],
},
{
dungeonId: "magisters-terrace",
fidelity: "source-backed-adapted",
sourceFiles: [
{
path: MAGISTERS_FILES[0],
sha256: "8aa316ceaa914b64e88fe420a0f37537bf016bf5635f8106bf32f82b3eb25728",
anchors: ["five exact door/orb bindings (lines 24-52) and delayed Kalecgos event with source position (lines 60-94)"],
},
{
path: MAGISTERS_FILES[1],
sha256: "43ff46dc05da63bfc4ed49cc5fce8451656857cce544f4ed146736285d4f626d",
anchors: ["five exact Fel Crystal positions (lines 44-50) and reset/drain lifecycle (lines 62-73, 93-170)"],
},
{
path: MAGISTERS_FILES[2],
sha256: "458daa44853b6a94a058ab8ccfe08ae4c8ccd34a0387cb766544e12bcbf45dee",
anchors: ["85/70/55/40/25 thresholds and normal one versus heroic two Pure Energies (lines 48-99)"],
},
{
path: MAGISTERS_FILES[3],
sha256: "9f5b4a05eb6b6e2acc361f764fd1e472ec255bb9a32369c16e7cc8303e5693e2",
anchors: ["eight-helper roster, exact four slots, random four selection, and five-death completion (lines 40-64, 73-125, 199-205)"],
},
{
path: MAGISTERS_FILES[4],
sha256: "25884cccec0c8bf123c73294bbcdcaee7f6da9a7a0a4af8b3e4d79b66c6b3024",
anchors: [
"50-percent Gravity Lapse transition and three spheres (lines 77-125)",
"Kael death unlocks the Escape Orb (lines 128-133)",
"six-callback intro has no reviewed caller in this source set (lines 158-183)",
],
},
{
path: MAGISTERS_FILES[5],
sha256: "ed4a734dcdf8b7e186dd4ceeb0ee7d890784aa18edfeab5acf03180a8da1d47f",
anchors: ["boss, crystal, Phoenix, door, Escape Orb, and Kalecgos ids (lines 26-70)"],
},
{
path: MAGISTERS_FILES[6],
sha256: "3e5713c2a0fa480c82b1753bfea6f1b94a4dec4f29a052ba979c049915a90a42",
anchors: ["exact Escape Orb/door spawn rows 99800, 99802, 99807-99809, and 99813 (lines 60865-60878)"],
},
],
reviewedBehavior: "Exact source doors follow Selin, Vexallus, Delrissa, and Kael state; Selin gets five exact crystals, Vexallus gets five normal one-add or heroic two-add thresholds, Delrissa is event-spawned with one deterministic valid four-of-eight team, and Kael gets one Phoenix plus a three-sphere 50-percent transition before unlocking the exact Escape Orb.",
adaptations: [
"Delrissa's random four-of-eight choice is deterministically the first four source entries so saves/retries serialize identically; all four exact source slots are retained.",
"Selin's crystals are finite engagement actors without mana drain ownership; Vexallus preserves all five health thresholds and uses real normal/heroic branches.",
"Kael's repeating Phoenix and Gravity Lapse schedules collapse to one Phoenix and one three-sphere threshold sequence; no flight state or player teleport is fabricated.",
"Each reviewed GO is replaced by exact source spawnId with visible procedural states; the Escape Orb unlock is presented but does not invent an unknown teleport destination.",
],
remainingGaps: [
"Native door/orb models, Selin movement/mana drain, Pure Energy follow/feedback, random Delrissa roster and class AI, heroic medallions, Kael flight/teleport/gravity/channel/Phoenix Egg/repeats, delayed death scene, orb transport, Kalecgos event caller/flight/dialogue, loot, and achievements remain unported.",
],
},
]);
const REVIEW_BY_DUNGEON = new Map(
WOW_TBC_ENCOUNTER_BATCH_TWO_REVIEWS.map((review) => [review.dungeonId, review] as const),
);
const PACK_OFFSETS = Object.freeze([
[0, 0], [4, 0], [-4, 0], [0, 4], [0, -4], [4, 4], [-4, 4], [4, -4], [-4, -4],
] as const);
function basicAttack(cooldownMs: number): EnemyAttackDefinition {
return Object.freeze({
id: "basic-attack",
name: "Basic Attack",
delivery: "melee",
target: "primary",
school: "physical",
animation: "attack",
range: 4.5,
cooldownMs,
damageMultiplier: 1,
});
}
function attack(definition: EnemyAttackDefinition): EnemyAttackDefinition {
return Object.freeze(definition);
}
const AVATAR_OF_THE_MARTYRED_ATTACKS = Object.freeze([
basicAttack(1_391),
attack({
id: "mortal-strike-16856",
name: "Mortal Strike",
spellId: 16_856,
delivery: "melee",
target: "primary",
school: "physical",
animation: "attack",
range: 5,
cooldownMs: 17_500,
initialCooldownMs: [8_500, 16_800] as const,
repeatCooldownMs: [10_900, 24_100] as const,
damageMultiplier: 2,
source: {
entry: 18_478,
sourceType: 0,
rowId: 0,
eventType: 0,
actionType: 11,
targetType: 2,
comment: "Avatar of the Martyred - In Combat - Cast 'Mortal Strike'",
},
}),
attack({
id: "sunder-armor-16145",
name: "Sunder Armor",
spellId: 16_145,
delivery: "melee",
target: "primary",
school: "physical",
animation: "attack",
range: 5,
cooldownMs: 10_950,
initialCooldownMs: [6_500, 11_500] as const,
repeatCooldownMs: [6_200, 15_700] as const,
damageMultiplier: 0,
effect: { kind: "target-aura", aura: "sunder-armor", durationMs: 30_000 },
source: {
entry: 18_478,
sourceType: 0,
rowId: 1,
eventType: 0,
actionType: 11,
targetType: 2,
comment: "Avatar of the Martyred - In Combat - Cast 'Sunder Armor'",
},
}),
] satisfies readonly EnemyAttackDefinition[]);
const YOR_ATTACKS = Object.freeze([
basicAttack(2_000),
attack({
id: "double-breath-38361",
name: "Double Breath",
spellId: 38_361,
delivery: "area",
target: "nearby-party",
school: "fire",
animation: "cast",
range: 5,
cooldownMs: 15_000,
initialCooldownMs: [25_500, 30_500] as const,
repeatCooldownMs: [10_000, 20_000] as const,
damageMultiplier: 1.15,
telegraph: {
shape: "cone",
anchor: "source",
warningMs: 1_650,
length: 20,
angleDegrees: 70,
},
}),
attack({
id: "stomp-36405",
name: "Stomp",
spellId: 36_405,
delivery: "area",
target: "nearby-party",
school: "physical",
animation: "attack",
range: 10,
radius: 10,
cooldownMs: 19_000,
initialCooldownMs: [12_000, 18_000] as const,
repeatCooldownMs: [14_000, 24_000] as const,
damageMultiplier: 1,
}),
] satisfies readonly EnemyAttackDefinition[]);
const AHUNE_ATTACKS = Object.freeze([
attack({
id: "ahunes-shield-45954",
name: "Ahune's Shield",
spellId: 45_954,
delivery: "area",
target: "self",
school: "physical",
animation: "cast",
castTimeMs: 0,
range: 0,
cooldownMs: 1,
initialCooldownMs: [4, 4] as const,
maximumCasts: 1,
damageMultiplier: 0,
effect: { kind: "aura", aura: "ahunes-shield", durationMs: 0, magnitude: 0.75 },
}),
attack({
id: "ahune-spanky-hands-46146",
name: "[PH] Ahune Spanky Hands",
spellId: 46_146,
delivery: "area",
target: "self",
school: "physical",
animation: "cast",
castTimeMs: 0,
range: 0,
cooldownMs: 1,
initialCooldownMs: [4, 4] as const,
maximumCasts: 1,
damageMultiplier: 0,
effect: { kind: "aura", aura: "ahune-spanky-hands", durationMs: 0 },
}),
] satisfies readonly EnemyAttackDefinition[]);
const FROZEN_CORE_ATTACKS = Object.freeze([
attack({
id: "ice-spear-control-aura-46371",
name: "Ice Spear Control Aura",
spellId: 46_371,
delivery: "area",
target: "self",
school: "physical",
animation: "cast",
castTimeMs: 0,
range: 0,
cooldownMs: 1,
initialCooldownMs: [0, 0] as const,
maximumCasts: 1,
damageMultiplier: 0,
effect: { kind: "aura", aura: "ice-spear-control", durationMs: 0 },
}),
attack({
id: "synch-health-46430",
name: "Synch Health",
spellId: 46_430,
delivery: "projectile",
target: "self",
school: "physical",
animation: "cast",
castTimeMs: 0,
range: 0,
cooldownMs: 3_000,
initialCooldownMs: [3_000, 3_000] as const,
repeatCooldownMs: [3_000, 3_000] as const,
damageMultiplier: 0,
effect: { kind: "encounter-event", eventId: "ahune-synch-health" },
}),
] satisfies readonly EnemyAttackDefinition[]);
function assertImportedTransform(definition: DungeonDefinition): void {
if (definition.transform.fromWow !== "three(x,y,z)=(-wow.x,wow.z,wow.y)") {
throw new Error(`TBC batch-two adapter ${definition.id} requires the pinned imported coordinate transform`);
}
}
function entityIdForEntry(entities: PopulationDefinitionMap, entry: number): string | undefined {
return Object.entries(entities).find(([, entity]) => entity.combat?.serverEntry === entry)?.[0];
}
function entryForSpawn(definition: DungeonDefinition, spawn: StaticMobSpawnDefinition): number | undefined {
return definition.entities[spawn.entityId]?.combat?.serverEntry;
}
function createAdaptedEntity(spec: AdaptedEntitySpec): PopulationEntityDefinition {
const caster = spec.role === "caster";
const attacks: readonly EnemyAttackDefinition[] = spec.attacks
? Object.freeze([...spec.attacks])
: Object.freeze([{
id: "basic-attack",
name: caster ? "Source Spell Proxy" : "Basic Attack",
delivery: caster ? "projectile" as const : "melee" as const,
target: "primary" as const,
school: caster ? "arcane" as const : "physical" as const,
animation: caster ? "cast" as const : "attack" as const,
range: caster ? 28 : 4.5,
cooldownMs: caster ? 3_000 : 1_900,
damageMultiplier: caster ? 0.82 : 1,
}]);
const base = {
id: `tbc-batch-two-entry-${spec.entry}`,
name: spec.name,
hasLoot: spec.kind === "boss",
combat: {
serverEntry: spec.entry,
level: spec.level,
healthMultiplier: spec.healthMultiplier ?? (spec.kind === "boss" ? 4.3 : caster ? 0.92 : 1.08),
damageMultiplier: spec.kind === "boss" ? 1.18 : 0.92,
moveSpeed: 3.657152,
aggroRange: 32,
leashRange: 90,
...(spec.attacks
? { mechanicIds: Object.freeze(spec.attacks.filter((attack) => attack.id !== "basic-attack").map((attack) => attack.id)) }
: {}),
attacks,
},
visual: {
primaryColor: caster ? "#526d91" : "#70505b",
accentColor: caster ? "#a8daf6" : "#dda58f",
scale: spec.kind === "boss" ? 1.16 : 1,
archetype: "humanoid" as const,
},
};
return spec.kind === "boss"
? Object.freeze({ ...base, kind: "boss" as const, title: spec.title ?? "Dungeon Boss" })
: Object.freeze({ ...base, kind: "mob" as const });
}
function ensureEntities(
definition: DungeonDefinition,
specs: readonly AdaptedEntitySpec[],
): PopulationDefinitionMap {
const entities: Record<string, PopulationEntityDefinition> = { ...definition.entities };
for (const spec of specs) {
if (entityIdForEntry(entities, spec.entry)) continue;
const entity = createAdaptedEntity(spec);
if (entities[entity.id]) throw new Error(`${definition.id} has duplicate adapted entity ${entity.id}`);
entities[entity.id] = entity;
}
return Object.freeze(entities);
}
function objectiveForEntry(definition: DungeonDefinition, entry: number): DungeonBossObjective {
const spawnIds = new Set(definition.staticSpawns
.filter((spawn) => entryForSpawn(definition, spawn) === entry)
.map((spawn) => spawn.id));
const objective = definition.bosses.find((candidate) => spawnIds.has(candidate.id));
if (!objective) throw new Error(`${definition.id} is missing boss objective for source entry ${entry}`);
return objective;
}
function sourceReview(review: WowTbcEncounterBatchTwoReview): DungeonEncounterDefinition["source"] {
return {
fidelity: review.fidelity,
scope: "instance-orchestration",
files: review.sourceFiles.map((file) => `${file.path} (sha256 ${file.sha256})`),
reviewedBehavior: review.reviewedBehavior,
adaptations: review.adaptations,
remainingGaps: review.remainingGaps,
};
}
function appendWarning(definition: DungeonDefinition, warning: string): DungeonDefinition["validation"] {
return Object.freeze({
...definition.validation,
warnings: Object.freeze([...new Set([...definition.validation.warnings, warning])]),
});
}
function spawnAt(
id: string,
entityId: string,
center: Vector3Tuple,
offset: readonly [x: number, z: number] = [0, 0],
spawnMask = 3,
): StaticMobSpawnDefinition {
return Object.freeze({
id,
entityId,
position: [center[0] + offset[0], center[1], center[2] + offset[1]] as const,
yaw: 0,
spawnMask,
});
}
function exactGroup(
id: string,
actors: readonly { readonly entry: number; readonly position: Vector3Tuple; readonly spawnMask?: number }[],
entities: PopulationDefinitionMap,
): DungeonEncounterSpawnGroupDefinition {
return Object.freeze({
id,
spawns: Object.freeze(actors.map((actor, index) => {
const entityId = entityIdForEntry(entities, actor.entry);
if (!entityId) throw new Error(`${id} missing source actor entry ${actor.entry}`);
return Object.freeze({
id: `${id}:actor-${index + 1}`,
entityId,
position: actor.position,
yaw: 0,
spawnMask: actor.spawnMask ?? 3,
});
})),
});
}
function packGroup(
id: string,
entries: readonly number[],
entities: PopulationDefinitionMap,
center: Vector3Tuple,
spawnMask = 3,
): DungeonEncounterSpawnGroupDefinition {
return exactGroup(id, entries.map((entry, index) => ({
entry,
position: [
center[0] + (PACK_OFFSETS[index]?.[0] ?? index * 3),
center[1],
center[2] + (PACK_OFFSETS[index]?.[1] ?? 0),
] as const,
spawnMask,
})), entities);
}
function objectForSpawn(definition: DungeonDefinition, spawnId: number): DungeonWorldObjectDefinition {
const matches = definition.serverGameObjects.filter((object) => object.source?.spawnId === spawnId);
if (matches.length !== 1) throw new Error(`${definition.id} expected one imported gameobject spawn ${spawnId}, got ${matches.length}`);
return matches[0];
}
const CLOSED_OPEN_STATES: readonly DungeonWorldObjectStateDefinition[] = Object.freeze([
{ id: "closed", label: "Closed", visible: true, blocksMovement: true, blocksSight: true, interactionEnabled: false },
{ id: "open", label: "Open", visible: false, blocksMovement: false, blocksSight: false, interactionEnabled: false },
]);
function reviewedDoor(
original: DungeonWorldObjectDefinition,
sourceFiles: readonly string[],
interactionPrompt?: string,
): DungeonWorldObjectDefinition {
return Object.freeze({
...original,
initialState: "closed",
states: interactionPrompt
? Object.freeze([
{ ...CLOSED_OPEN_STATES[0], interactionEnabled: true },
CLOSED_OPEN_STATES[1],
])
: CLOSED_OPEN_STATES,
interaction: interactionPrompt
? {
prompt: interactionPrompt,
range: 4,
signalId: `${original.id}:use`,
allowedStates: ["closed"],
oneShot: false,
}
: undefined,
visual: Object.freeze({ color: "#687181", emissive: "#151b24" }),
source: Object.freeze({
...original.source,
fidelity: "source-backed-adapted" as const,
files: sourceFiles,
note: "Exact imported spawnId, transform, and bounds are retained; visible procedural geometry represents reviewed collision/interaction states.",
}),
});
}
function reviewedControl(
original: DungeonWorldObjectDefinition,
sourceFiles: readonly string[],
prompt: string,
initialState: "hidden" | "ready" = "ready",
): DungeonWorldObjectDefinition {
return Object.freeze({
...original,
initialState,
states: Object.freeze([
{ id: "hidden", label: "Unavailable", visible: false, blocksMovement: false, interactionEnabled: false },
{ id: "ready", label: "Ready", visible: true, blocksMovement: false, interactionEnabled: true },
{ id: "active", label: "Active", visible: true, blocksMovement: false, interactionEnabled: false },
{ id: "complete", label: "Complete", visible: true, blocksMovement: false, interactionEnabled: false },
]),
interaction: {
prompt,
range: 4,
signalId: `${original.id}:use`,
allowedStates: ["ready"],
oneShot: false,
},
visual: Object.freeze({ color: "#9a7642", emissive: "#2a1d0c" }),
source: Object.freeze({
...original.source,
fidelity: "source-backed-adapted" as const,
files: sourceFiles,
note: "Exact imported spawnId, transform, and bounds are retained; a procedural interaction exposes the reviewed finite event.",
}),
});
}
function reviewedOrb(
original: DungeonWorldObjectDefinition,
sourceFiles: readonly string[],
): DungeonWorldObjectDefinition {
return Object.freeze({
...original,
initialState: "locked",
states: Object.freeze([
{ id: "locked", label: "Locked", visible: true, blocksMovement: false, interactionEnabled: false },
{ id: "unlocked", label: "Unlocked", visible: true, blocksMovement: false, interactionEnabled: false },
]),
interaction: undefined,
visual: Object.freeze({ color: "#8bb6dc", emissive: "#263f5a" }),
source: Object.freeze({
...original.source,
fidelity: "source-backed-adapted" as const,
files: sourceFiles,
note: "Exact imported Escape Orb spawnId, transform, and bounds are retained; transport remains disabled until its destination is source-reviewed.",
}),
});
}
function authoredMarker(
id: string,
name: string,
prompt: string | undefined,
position: Vector3Tuple,
sourceFiles: readonly string[],
initialState: "hidden" | "ready" = "ready",
): DungeonWorldObjectDefinition {
return Object.freeze({
id,
name,
kind: "npc",
position,
halfExtents: [0.8, 1.5, 0.8] as const,
initialState,
states: Object.freeze([
{ id: "hidden", label: "Unavailable", visible: false, blocksMovement: false, interactionEnabled: false },
{ id: "ready", label: "Ready", visible: true, blocksMovement: false, interactionEnabled: Boolean(prompt) },
{ id: "active", label: "Active", visible: true, blocksMovement: false, interactionEnabled: false },
{ id: "complete", label: "Complete", visible: true, blocksMovement: false, interactionEnabled: false },
]),
interaction: prompt
? { prompt, range: 4, signalId: `${id}:use`, allowedStates: ["ready"], oneShot: false }
: undefined,
visual: Object.freeze({ color: "#9a72b5", emissive: "#2d193b" }),
source: Object.freeze({
fidelity: "source-backed-adapted" as const,
files: sourceFiles,
note: "A source-position procedural marker replaces unsupported proximity, faction, movement, gossip, or presentation state.",
}),
});
}
function replaceObjects(
definition: DungeonDefinition,
replacements: readonly DungeonWorldObjectDefinition[],
additions: readonly DungeonWorldObjectDefinition[] = [],
): readonly DungeonWorldObjectDefinition[] {
const bySpawn = new Map(replacements.map((object) => [object.source?.spawnId, object] as const));
return Object.freeze([
...definition.serverGameObjects.map((object) => bySpawn.get(object.source?.spawnId) ?? object),
...additions,
]);
}
function eventObjective(objective: DungeonBossObjective): DungeonBossObjective {
return Object.freeze({ ...objective, eventOnly: true });
}
function replaceObjectiveLists(
definition: DungeonDefinition,
replacements: readonly DungeonBossObjective[],
additions: readonly { readonly objective: DungeonBossObjective; readonly afterId?: string }[] = [],
): Pick<DungeonDefinition, "bosses" | "objectiveOrder"> {
const byId = new Map(replacements.map((objective) => [objective.id, objective] as const));
const bosses = definition.bosses.map((objective) => byId.get(objective.id) ?? objective);
const order = definition.objectiveOrder.map((objective) => byId.get(objective.id) ?? objective);
for (const addition of additions) {
bosses.push(addition.objective);
const index = addition.afterId ? order.findIndex((objective) => objective.id === addition.afterId) : -1;
order.splice(index >= 0 ? index + 1 : order.length, 0, addition.objective);
}
return { bosses: Object.freeze(bosses), objectiveOrder: Object.freeze(order) };
}
function objectiveActions(prefix: string, objective: DungeonBossObjective): readonly DungeonEncounterAction[] {
return [
{ id: `${prefix}-complete-objective`, kind: "complete-objective", objectiveId: objective.id, persistence: "run" },
{ id: `${prefix}-checkpoint`, kind: "set-checkpoint", checkpointId: objective.id, persistence: "checkpoint" },
];
}
function buildUnderbog(definition: DungeonDefinition): DungeonDefinition {
assertImportedTransform(definition);
const review = REVIEW_BY_DUNGEON.get("underbog")!;
const entities = ensureEntities(definition, [
{ entry: 17_990, name: "Underbog Mushroom", level: 64, role: "melee" },
{ entry: 22_299, name: "Spore Strider", level: 70, role: "melee" },
]);
const hungarfen = objectiveForEntry(definition, 17_770);
const ghazanOriginal = objectiveForEntry(definition, 18_105);
const muselek = objectiveForEntry(definition, 17_826);
const blackStalker = objectiveForEntry(definition, 17_882);
const ghazan = eventObjective(ghazanOriginal);
const claw = definition.staticSpawns.find((spawn) => entryForSpawn(definition, spawn) === 17_827);
if (!claw) throw new Error("underbog is missing source Claw spawn 138499");
const ghazanStart = authoredMarker(
"underbog:ghazan-platform-trigger",
"Ghaz'an Platform Trigger",
"Disturb Ghaz'an",
ghazan.position,
UNDERBOG_FILES,
);
const rescuedClaw = authoredMarker(
"underbog:rescued-claw",
"Claw",
undefined,
[183.408, 43.7993, -780.199],
UNDERBOG_FILES,
"hidden",
);
const ghazanGroup: DungeonEncounterSpawnGroupDefinition = Object.freeze({
id: "underbog:ghazan-platform",
spawns: Object.freeze([spawnAt(ghazan.id, entityIdForEntry(entities, 18_105)!, [-256.279, 81.3728, -458.731])]),
});
const clawGroup: DungeonEncounterSpawnGroupDefinition = Object.freeze({
id: "underbog:claw",
spawns: Object.freeze([claw]),
});
const mushroomNormal = packGroup("underbog:hungarfen-mushroom-normal", [17_990], entities, hungarfen.position, 1);
const mushroomHeroic = packGroup("underbog:hungarfen-mushroom-heroic", [17_990], entities, hungarfen.position, 2);
const heroicSpore = packGroup("underbog:black-stalker-spore-heroic", [22_299], entities, blackStalker.position, 2);
const timedGroups = [mushroomNormal, mushroomHeroic, heroicSpore] as const;
const rules: DungeonEncounterRule[] = [
{
id: "hungarfen-engaged",
trigger: { kind: "entity-engaged", entityId: hungarfen.id },
actions: [{ id: "enter-hungarfen-active", kind: "set-phase", phase: "hungarfen-active" }],
},
{
id: "hungarfen-normal-mushroom",
trigger: { kind: "elapsed", afterMs: 10_000 },
guards: [
{ kind: "phase", phases: ["hungarfen-active"] },
{ kind: "difficulty", difficultyIds: ["normal"] },
],
actions: [{ id: "activate-normal-mushroom", kind: "activate-spawn-group", spawnGroupId: mushroomNormal.id }],
},
{
id: "hungarfen-heroic-mushroom",
trigger: { kind: "elapsed", afterMs: 3_000 },
guards: [
{ kind: "phase", phases: ["hungarfen-active"] },
{ kind: "difficulty", difficultyIds: ["heroic"] },
],
actions: [{ id: "activate-heroic-mushroom", kind: "activate-spawn-group", spawnGroupId: mushroomHeroic.id }],
},
{
id: "hungarfen-defeated",
trigger: { kind: "entity-death", entityId: hungarfen.id },
actions: [
{ id: "cleanup-normal-mushroom", kind: "deactivate-spawn-group", spawnGroupId: mushroomNormal.id },
{ id: "cleanup-heroic-mushroom", kind: "deactivate-spawn-group", spawnGroupId: mushroomHeroic.id },
{ id: "leave-hungarfen-active", kind: "set-phase", phase: "ready" },
],
},
{
id: "release-ghazan",
trigger: { kind: "interact", objectId: ghazanStart.id },
guards: [
{ kind: "phase", phases: ["ready"] },
{ kind: "flag", flagId: "ghazan-complete", equals: false },
],
actions: [
{ id: "lock-ghazan-trigger", kind: "set-world-object-state", objectId: ghazanStart.id, state: "active" },
{ id: "activate-platform-ghazan", kind: "activate-spawn-group", spawnGroupId: ghazanGroup.id },
{ id: "enter-ghazan-active", kind: "set-phase", phase: "ghazan-active" },
],
},
{
id: "ghazan-defeated",
trigger: { kind: "entity-death", entityId: ghazan.id },
guards: [{ kind: "phase", phases: ["ghazan-active"] }],
actions: [
{ id: "despawn-platform-ghazan", kind: "deactivate-spawn-group", spawnGroupId: ghazanGroup.id },
...objectiveActions("ghazan", ghazan),
{ id: "record-ghazan-complete", kind: "set-flag", flagId: "ghazan-complete", value: true, persistence: "run" },
{ id: "complete-ghazan-trigger", kind: "set-world-object-state", objectId: ghazanStart.id, state: "complete", persistence: "run" },
{ id: "leave-ghazan-active", kind: "set-phase", phase: "ready" },
],
},
{
id: "muselek-engaged-releases-claw",
trigger: { kind: "entity-engaged", entityId: muselek.id },
guards: [{ kind: "flag", flagId: "claw-rescued", equals: false }],
actions: [
{ id: "activate-hostile-claw", kind: "activate-spawn-group", spawnGroupId: clawGroup.id },
{ id: "enter-muselek-active", kind: "set-phase", phase: "muselek-active" },
],
},
{
id: "claw-rescued-at-twenty-percent",
trigger: { kind: "entity-health", entityId: claw.id, atOrBelowPercent: 20 },
guards: [
{ kind: "spawn-group-active", spawnGroupId: clawGroup.id, equals: true },
{ kind: "flag", flagId: "claw-rescued", equals: false },
],
actions: [
{ id: "despawn-hostile-claw", kind: "deactivate-spawn-group", spawnGroupId: clawGroup.id },
{ id: "record-claw-rescued", kind: "set-flag", flagId: "claw-rescued", value: true, persistence: "run" },
{ id: "show-rescued-claw", kind: "set-world-object-state", objectId: rescuedClaw.id, state: "complete", persistence: "run" },
],
},
{
id: "muselek-defeated",
trigger: { kind: "entity-death", entityId: muselek.id },
actions: [{ id: "leave-muselek-active", kind: "set-phase", phase: "ready" }],
},
{
id: "black-stalker-engaged",
trigger: { kind: "entity-engaged", entityId: blackStalker.id },
actions: [{ id: "enter-black-stalker-active", kind: "set-phase", phase: "black-stalker-active" }],
},
{
id: "black-stalker-heroic-spore",
trigger: { kind: "elapsed", afterMs: 10_000 },
guards: [
{ kind: "phase", phases: ["black-stalker-active"] },
{ kind: "difficulty", difficultyIds: ["heroic"] },
],
actions: [{ id: "activate-heroic-spore", kind: "activate-spawn-group", spawnGroupId: heroicSpore.id }],
},
{
id: "black-stalker-defeated",
trigger: { kind: "entity-death", entityId: blackStalker.id },
actions: [
{ id: "cleanup-heroic-spore", kind: "deactivate-spawn-group", spawnGroupId: heroicSpore.id },
{ id: "leave-black-stalker-active", kind: "set-phase", phase: "ready" },
],
},
{
id: "resume-underbog",
trigger: { kind: "tick" },
guards: [{ kind: "phase", phases: ["resume"] }],
once: false,
actions: [{ id: "resume-underbog-ready", kind: "set-phase", phase: "ready" }],
},
];
for (const group of timedGroups) {
rules.push({
id: `${group.id}:defeated`,
trigger: { kind: "entities-dead", entityIds: group.spawns.map((spawn) => spawn.id), timeoutMs: 900_000 },
guards: [{ kind: "spawn-group-active", spawnGroupId: group.id, equals: true }],
actions: [{ id: `${group.id}:deactivate`, kind: "deactivate-spawn-group", spawnGroupId: group.id }],
});
}
const encounter: DungeonEncounterDefinition = Object.freeze({
id: "underbog:tbc-batch-two-instance",
dungeonId: definition.id,
initialPhase: "ready",
initialState: {
flags: { "ghazan-complete": false, "claw-rescued": false },
worldObjectStates: { [ghazanStart.id]: "ready", [rescuedClaw.id]: "hidden" },
},
resetPolicy: { bossWipePhase: "resume", checkpointPhase: "resume" },
source: sourceReview(review),
rules: Object.freeze(rules),
});
const objectives = replaceObjectiveLists(definition, [ghazan]);
return Object.freeze({
...definition,
...objectives,
entities,
staticSpawns: Object.freeze(definition.staticSpawns.filter((spawn) => spawn.id !== ghazan.id && spawn.id !== claw.id)),
encounters: Object.freeze([...(definition.encounters ?? []), encounter]),
encounterSpawnGroups: Object.freeze([...(definition.encounterSpawnGroups ?? []), ghazanGroup, clawGroup, ...timedGroups]),
serverGameObjects: Object.freeze([...definition.serverGameObjects, ghazanStart, rescuedClaw]),
validation: appendWarning(definition, "Underbog now uses source-backed Ghaz'an release, difficulty-specific first summons, and Claw's 20-percent rescue; movement, repeats, and presentation remain explicitly adapted."),
});
}
function buildSlavePens(definition: DungeonDefinition): DungeonDefinition {
assertImportedTransform(definition);
const review = REVIEW_BY_DUNGEON.get("slave-pens")!;
const entities = ensureEntities(definition, [
{ entry: 17_893, name: "Naturalist Bite", level: 64, role: "caster" },
{ entry: 17_957, name: "Coilfang Champion", level: 64, role: "melee" },
{ entry: 17_960, name: "Coilfang Soothsayer", level: 64, role: "caster" },
{ entry: 17_961, name: "Coilfang Enchantress", level: 64, role: "caster" },
{
entry: 25_740,
name: "Ahune",
level: 73,
role: "caster",
kind: "boss",
title: "The Frost Lord",
attacks: AHUNE_ATTACKS,
},
{
entry: 25_865,
name: "Frozen Core",
level: 73,
role: "melee",
kind: "mob",
attacks: FROZEN_CORE_ATTACKS,
healthMultiplier: 4.3,
},
{ entry: 25_755, name: "Ahunite Hailstone", level: 70, role: "melee" },
{ entry: 25_756, name: "Ahunite Coldwave", level: 70, role: "caster" },
]);
const quagmirran = objectiveForEntry(definition, 17_942);
const naturalist = definition.staticSpawns.find((spawn) => entryForSpawn(definition, spawn) === 17_893);
if (!naturalist) throw new Error("slave-pens is missing Naturalist Bite spawn 79441");
const cage = reviewedControl(objectForSpawn(definition, 50_458), SLAVE_PENS_FILES, "Release Naturalist Bite");
const iceStone = reviewedControl(objectForSpawn(definition, 1_267), SLAVE_PENS_FILES, "Call forth Ahune");
const rescuedNaturalist = authoredMarker(
"slave-pens:naturalist-rescued",
"Naturalist Bite",
undefined,
[183.408, 43.7993, -780.199],
SLAVE_PENS_FILES,
"hidden",
);
const ahuneObjective: DungeonBossObjective = Object.freeze({
id: "slave-pens:ahune",
name: "Ahune",
position: [99.1021, -1.22307, -233.7526] as const,
optional: true,
eventOnly: true,
});
const naturalistAttackers = exactGroup("slave-pens:naturalist-attackers", [
{ entry: 17_957, position: [101.139, 35.4838, -745.353] },
{ entry: 17_960, position: [98.6582, 35.3191, -742.38] },
{ entry: 17_961, position: [97.3511, 35.6992, -745.455] },
], entities);
const ahuneGroup = exactGroup("slave-pens:ahune", [
{ entry: 25_740, position: ahuneObjective.position },
], entities);
const hailstoneGroup = exactGroup("slave-pens:ahune-hailstone", [
{ entry: 25_755, position: [99.1021, -1.22307, -233.7526] },
], entities);
const coldwaveGroup = exactGroup("slave-pens:ahune-coldwaves", [
{ entry: 25_756, position: [95.8, -1.22, -232.1] },
{ entry: 25_756, position: [102.4, -1.22, -232.1] },
], entities);
const coreGroup = exactGroup("slave-pens:frozen-core", [
{ entry: 25_865, position: [98.0151, -1.21089, -230.4555] },
], entities);
const groups = [naturalistAttackers, ahuneGroup, hailstoneGroup, coldwaveGroup, coreGroup] as const;
const ritualSupportEntries = new Set([25_745, 25_964, 25_965, 25_966, 25_971, 25_972, 25_973]);
const rules: DungeonEncounterRule[] = [
{
id: "release-naturalist",
trigger: { kind: "interact", objectId: cage.id },
guards: [
{ kind: "phase", phases: ["ready"] },
{ kind: "flag", flagId: "naturalist-complete", equals: false },
],
actions: [
{ id: "activate-naturalist-cage", kind: "set-world-object-state", objectId: cage.id, state: "active" },
{ id: "activate-naturalist-attackers", kind: "activate-spawn-group", spawnGroupId: naturalistAttackers.id },
{ id: "enter-naturalist-combat", kind: "set-phase", phase: "naturalist-combat" },
],
},
{
id: "naturalist-attackers-defeated",
trigger: { kind: "entities-dead", entityIds: naturalistAttackers.spawns.map((spawn) => spawn.id), timeoutMs: 900_000 },
guards: [{ kind: "phase", phases: ["naturalist-combat"] }],
actions: [
{ id: "despawn-naturalist-attackers", kind: "deactivate-spawn-group", spawnGroupId: naturalistAttackers.id },
{ id: "enter-naturalist-handoff", kind: "set-phase", phase: "naturalist-handoff" },
],
},
{
id: "naturalist-handoff-complete",
trigger: { kind: "elapsed", afterMs: 3_440 },
guards: [{ kind: "phase", phases: ["naturalist-handoff"] }],
actions: [
{ id: "record-naturalist-complete", kind: "set-flag", flagId: "naturalist-complete", value: true, persistence: "run" },
{ id: "complete-naturalist-cage", kind: "set-world-object-state", objectId: cage.id, state: "complete", persistence: "run" },
{ id: "show-naturalist-rescued", kind: "set-world-object-state", objectId: rescuedNaturalist.id, state: "complete", persistence: "run" },
{ id: "leave-naturalist-handoff", kind: "set-phase", phase: "ready" },
],
},
{
id: "start-ahune-ritual",
trigger: { kind: "interact", objectId: iceStone.id },
guards: [
{ kind: "phase", phases: ["ready"] },
{ kind: "flag", flagId: "ahune-complete", equals: false },
],
actions: [
{ id: "activate-ice-stone", kind: "set-world-object-state", objectId: iceStone.id, state: "active" },
{ id: "enter-ahune-ritual", kind: "set-phase", phase: "ahune-ritual" },
],
},
{
id: "ahune-emerges",
trigger: { kind: "elapsed", afterMs: 10_000 },
guards: [{ kind: "phase", phases: ["ahune-ritual"] }],
actions: [
{ id: "activate-ahune", kind: "activate-spawn-group", spawnGroupId: ahuneGroup.id },
{ id: "enter-ahune-active", kind: "set-phase", phase: "ahune-active" },
],
},
{
id: "ahune-first-hailstone",
trigger: { kind: "elapsed", afterMs: 4_000 },
guards: [{ kind: "phase", phases: ["ahune-active"] }],
actions: [{ id: "activate-ahune-hailstone", kind: "activate-spawn-group", spawnGroupId: hailstoneGroup.id }],
},
{
id: "ahune-first-coldwaves",
trigger: { kind: "elapsed", afterMs: 12_000 },
guards: [{ kind: "phase", phases: ["ahune-active"] }],
actions: [{ id: "activate-ahune-coldwaves", kind: "activate-spawn-group", spawnGroupId: coldwaveGroup.id }],
},
{
id: "ahune-core-phase",
trigger: { kind: "elapsed", afterMs: 98_000 },
guards: [{ kind: "phase", phases: ["ahune-active"] }],
actions: [
{ id: "hide-ahune-for-core", kind: "deactivate-spawn-group", spawnGroupId: ahuneGroup.id },
{ id: "activate-frozen-core", kind: "activate-spawn-group", spawnGroupId: coreGroup.id },
{ id: "enter-ahune-core", kind: "set-phase", phase: "ahune-core" },
],
},
{
id: "ahune-core-defeated",
trigger: { kind: "entity-death", entityId: coreGroup.spawns[0].id },
guards: [{ kind: "phase", phases: ["ahune-core"] }],
actions: [
{ id: "despawn-frozen-core", kind: "deactivate-spawn-group", spawnGroupId: coreGroup.id },
{ id: "cleanup-ahune-hailstone", kind: "deactivate-spawn-group", spawnGroupId: hailstoneGroup.id },
{ id: "cleanup-ahune-coldwaves", kind: "deactivate-spawn-group", spawnGroupId: coldwaveGroup.id },
...objectiveActions("ahune", ahuneObjective),
{ id: "record-ahune-complete", kind: "set-flag", flagId: "ahune-complete", value: true, persistence: "run" },
{ id: "complete-ice-stone", kind: "set-world-object-state", objectId: iceStone.id, state: "complete", persistence: "run" },
{ id: "leave-ahune-core", kind: "set-phase", phase: "ready" },
],
},
{
id: "resume-slave-pens",
trigger: { kind: "tick" },
guards: [{ kind: "phase", phases: ["resume"] }],
once: false,
actions: [{ id: "resume-slave-pens-ready", kind: "set-phase", phase: "ready" }],
},
];
const encounter: DungeonEncounterDefinition = Object.freeze({
id: "slave-pens:tbc-batch-two-instance",
dungeonId: definition.id,
initialPhase: "ready",
initialState: {
flags: { "naturalist-complete": false, "ahune-complete": false },
worldObjectStates: { [cage.id]: "ready", [iceStone.id]: "ready", [rescuedNaturalist.id]: "hidden" },
},
resetPolicy: { bossWipePhase: "resume", checkpointPhase: "resume" },
source: sourceReview(review),
rules: Object.freeze(rules),
});
const objectives = replaceObjectiveLists(definition, [], [{ objective: ahuneObjective, afterId: quagmirran.id }]);
return Object.freeze({
...definition,
...objectives,
entities,
staticSpawns: Object.freeze(definition.staticSpawns.filter((spawn) => (
spawn.id !== naturalist.id && !ritualSupportEntries.has(entryForSpawn(definition, spawn) ?? -1)
))),
encounters: Object.freeze([...(definition.encounters ?? []), encounter]),
encounterSpawnGroups: Object.freeze([...(definition.encounterSpawnGroups ?? []), ...groups]),
serverGameObjects: replaceObjects(definition, [cage, iceStone], [rescuedNaturalist]),
validation: appendWarning(definition, "Slave Pens now exposes the exact Naturalist cage and Ice Stone through retry-safe finite source sequences; escort motion and repeating Ahune phases remain adapted."),
});
}
function buildManaTombs(definition: DungeonDefinition): DungeonDefinition {
assertImportedTransform(definition);
const review = REVIEW_BY_DUNGEON.get("mana-tombs")!;
const entities = ensureEntities(definition, [
{ entry: 18_309, name: "Ethereal Scavenger", level: 65, role: "melee" },
{ entry: 18_311, name: "Ethereal Crypt Raider", level: 65, role: "melee" },
{ entry: 18_312, name: "Ethereal Spellbinder", level: 65, role: "caster" },
{ entry: 18_313, name: "Ethereal Sorcerer", level: 65, role: "caster" },
{ entry: 18_315, name: "Ethereal Theurgist", level: 65, role: "caster" },
{ entry: 18_431, name: "Ethereal Beacon", level: 65, role: "caster" },
{ entry: 19_666, name: "Shadow Lord Xiraxis", level: 66, role: "caster" },
{
entry: 22_930,
name: "Yor",
level: 72,
role: "melee",
kind: "boss",
title: "Stasis Prisoner",
attacks: YOR_ATTACKS,
},
]);
const pandemonius = objectiveForEntry(definition, 18_341);
const shaffar = objectiveForEntry(definition, 18_344);
const roomSpawnIds = new Set([
"creature-83309", "creature-83310", "creature-83311", "creature-83312",
"creature-83313", "creature-83314", "creature-83315", "creature-83316",
"creature-83317", "creature-83318", "creature-83319", "creature-83321",
]);
const roomSpawns = definition.staticSpawns.filter((spawn) => roomSpawnIds.has(spawn.id));
if (roomSpawns.length !== roomSpawnIds.size) {
throw new Error(`mana-tombs expected ${roomSpawnIds.size} reviewed Pandemonius room actors, got ${roomSpawns.length}`);
}
const pandemoniusRoom: DungeonEncounterSpawnGroupDefinition = Object.freeze({
id: "mana-tombs:pandemonius-room-pull",
spawns: Object.freeze(roomSpawns),
});
const shaffarBeacon = packGroup("mana-tombs:shaffar-finite-beacon", [18_431], entities, shaffar.position);
const transporter = reviewedControl(objectForSpawn(definition, 43_146), MANA_TOMBS_FILES, "Release Sha'heen");
const stasis = reviewedControl(objectForSpawn(definition, 2_134_514), MANA_TOMBS_FILES, "Open Yor's stasis chamber", "hidden");
const shaheenComplete = authoredMarker(
"mana-tombs:shaheen-safe",
"Sha'heen",
undefined,
[36.3667, -0.954333, 1.31717],
MANA_TOMBS_FILES,
"hidden",
);
const shaheenWaveOne = exactGroup("mana-tombs:shaheen-wave-1", [
{ entry: 18_315, position: [375.89, -0.958749, -130.798] },
{ entry: 18_315, position: [374.61, -0.979487, -197.673] },
{ entry: 18_312, position: [370.302, -0.958741, -131.253] },
{ entry: 18_312, position: [369.858, -0.968613, -197.361] },
], entities);
const shaheenWaveTwo = exactGroup("mana-tombs:shaheen-wave-2", [
{ entry: 18_313, position: [284.014, -0.35525, -188.331] },
{ entry: 18_313, position: [288.839, -0.555688, -178.644] },
{ entry: 18_313, position: [224.961, -1.04418, -172.398] },
{ entry: 18_311, position: [237.708, -0.951371, -186.748] },
], entities);
const shaheenWaveThree = exactGroup("mana-tombs:shaheen-wave-3", [
{ entry: 19_666, position: [36.3667, -0.954333, 1.31717] },
], entities);
const yorObjective: DungeonBossObjective = Object.freeze({
id: "mana-tombs:yor",
name: "Yor",
position: [235.563, 17.066, -13.4403] as const,
optional: true,
eventOnly: true,
});
const yorGroup = exactGroup("mana-tombs:yor", [
{ entry: 22_930, position: yorObjective.position, spawnMask: 2 },
], entities);
const groups = [pandemoniusRoom, shaffarBeacon, shaheenWaveOne, shaheenWaveTwo, shaheenWaveThree, yorGroup] as const;
const rules: DungeonEncounterRule[] = [
{
id: "pandemonius-pulls-room",
trigger: { kind: "entity-engaged", entityId: pandemonius.id },
actions: [{ id: "activate-pandemonius-room", kind: "activate-spawn-group", spawnGroupId: pandemoniusRoom.id }],
},
{
id: "pandemonius-death-clears-room",
trigger: { kind: "entity-death", entityId: pandemonius.id },
actions: [{ id: "deactivate-pandemonius-room", kind: "deactivate-spawn-group", spawnGroupId: pandemoniusRoom.id }],
},
{
id: "shaffar-engaged",
trigger: { kind: "entity-engaged", entityId: shaffar.id },
actions: [{ id: "enter-shaffar-active", kind: "set-phase", phase: "shaffar-active" }],
},
{
id: "shaffar-summons-first-beacon",
trigger: { kind: "elapsed", afterMs: 10_000 },
guards: [{ kind: "phase", phases: ["shaffar-active"] }],
actions: [{ id: "activate-shaffar-beacon", kind: "activate-spawn-group", spawnGroupId: shaffarBeacon.id }],
},
{
id: "shaffar-defeated",
trigger: { kind: "entity-death", entityId: shaffar.id },
actions: [
{ id: "deactivate-shaffar-beacon", kind: "deactivate-spawn-group", spawnGroupId: shaffarBeacon.id },
{ id: "leave-shaffar-active", kind: "set-phase", phase: "ready" },
],
},
{
id: "start-shaheen-escort",
trigger: { kind: "interact", objectId: transporter.id },
guards: [
{ kind: "phase", phases: ["ready"] },
{ kind: "flag", flagId: "shaheen-complete", equals: false },
],
actions: [
{ id: "activate-shaheen-panel", kind: "set-world-object-state", objectId: transporter.id, state: "active" },
{ id: "activate-shaheen-wave-1", kind: "activate-spawn-group", spawnGroupId: shaheenWaveOne.id },
{ id: "enter-shaheen-wave-1", kind: "set-phase", phase: "shaheen-wave-1" },
],
},
{
id: "shaheen-wave-1-defeated",
trigger: { kind: "entities-dead", entityIds: shaheenWaveOne.spawns.map((spawn) => spawn.id), timeoutMs: 900_000 },
guards: [{ kind: "phase", phases: ["shaheen-wave-1"] }],
actions: [
{ id: "deactivate-shaheen-wave-1", kind: "deactivate-spawn-group", spawnGroupId: shaheenWaveOne.id },
{ id: "activate-shaheen-wave-2", kind: "activate-spawn-group", spawnGroupId: shaheenWaveTwo.id },
{ id: "enter-shaheen-wave-2", kind: "set-phase", phase: "shaheen-wave-2" },
],
},
{
id: "shaheen-wave-2-defeated",
trigger: { kind: "entities-dead", entityIds: shaheenWaveTwo.spawns.map((spawn) => spawn.id), timeoutMs: 900_000 },
guards: [{ kind: "phase", phases: ["shaheen-wave-2"] }],
actions: [
{ id: "deactivate-shaheen-wave-2", kind: "deactivate-spawn-group", spawnGroupId: shaheenWaveTwo.id },
{ id: "activate-shaheen-wave-3", kind: "activate-spawn-group", spawnGroupId: shaheenWaveThree.id },
{ id: "enter-shaheen-wave-3", kind: "set-phase", phase: "shaheen-wave-3" },
],
},
{
id: "shaheen-wave-3-defeated",
trigger: { kind: "entities-dead", entityIds: shaheenWaveThree.spawns.map((spawn) => spawn.id), timeoutMs: 900_000 },
guards: [{ kind: "phase", phases: ["shaheen-wave-3"] }],
actions: [
{ id: "deactivate-shaheen-wave-3", kind: "deactivate-spawn-group", spawnGroupId: shaheenWaveThree.id },
{ id: "record-shaheen-complete", kind: "set-flag", flagId: "shaheen-complete", value: true, persistence: "run" },
{ id: "complete-shaheen-panel", kind: "set-world-object-state", objectId: transporter.id, state: "complete", persistence: "run" },
{ id: "show-safe-shaheen", kind: "set-world-object-state", objectId: shaheenComplete.id, state: "complete", persistence: "run" },
{ id: "leave-shaheen-wave-3", kind: "set-phase", phase: "ready" },
],
},
{
id: "heroic-enables-yor",
trigger: { kind: "tick" },
guards: [
{ kind: "difficulty", difficultyIds: ["heroic"] },
{ kind: "world-object-state", objectId: stasis.id, states: ["hidden"] },
{ kind: "flag", flagId: "yor-complete", equals: false },
],
actions: [{ id: "show-heroic-yor-stasis", kind: "set-world-object-state", objectId: stasis.id, state: "ready" }],
},
{
id: "release-yor",
trigger: { kind: "interact", objectId: stasis.id },
guards: [
{ kind: "difficulty", difficultyIds: ["heroic"] },
{ kind: "world-object-state", objectId: stasis.id, states: ["ready"] },
{ kind: "flag", flagId: "yor-complete", equals: false },
],
actions: [
{ id: "activate-yor-stasis", kind: "set-world-object-state", objectId: stasis.id, state: "active" },
{ id: "activate-yor", kind: "activate-spawn-group", spawnGroupId: yorGroup.id },
{ id: "enter-yor-active", kind: "set-phase", phase: "yor-active" },
],
},
{
id: "yor-defeated",
trigger: { kind: "entity-death", entityId: yorGroup.spawns[0].id },
guards: [{ kind: "phase", phases: ["yor-active"] }],
actions: [
{ id: "deactivate-yor", kind: "deactivate-spawn-group", spawnGroupId: yorGroup.id },
...objectiveActions("yor", yorObjective),
{ id: "record-yor-complete", kind: "set-flag", flagId: "yor-complete", value: true, persistence: "run" },
{ id: "complete-yor-stasis", kind: "set-world-object-state", objectId: stasis.id, state: "complete", persistence: "run" },
{ id: "leave-yor-active", kind: "set-phase", phase: "ready" },
],
},
{
id: "resume-mana-tombs",
trigger: { kind: "tick" },
guards: [{ kind: "phase", phases: ["resume"] }],
priority: 100,
once: false,
actions: [{ id: "resume-mana-tombs-ready", kind: "set-phase", phase: "ready" }],
},
];
const encounter: DungeonEncounterDefinition = Object.freeze({
id: "mana-tombs:tbc-batch-two-instance",
dungeonId: definition.id,
initialPhase: "ready",
initialState: {
flags: { "shaheen-complete": false, "yor-complete": false },
worldObjectStates: { [transporter.id]: "ready", [stasis.id]: "hidden", [shaheenComplete.id]: "hidden" },
},
resetPolicy: { bossWipePhase: "resume", checkpointPhase: "resume" },
source: sourceReview(review),
rules: Object.freeze(rules),
});
const objectives = replaceObjectiveLists(definition, [], [{ objective: yorObjective, afterId: shaffar.id }]);
return Object.freeze({
...definition,
...objectives,
entities,
staticSpawns: Object.freeze(definition.staticSpawns.filter((spawn) => !roomSpawnIds.has(spawn.id))),
encounters: Object.freeze([...(definition.encounters ?? []), encounter]),
encounterSpawnGroups: Object.freeze([...(definition.encounterSpawnGroups ?? []), ...groups]),
serverGameObjects: replaceObjects(definition, [transporter, stasis], [shaheenComplete]),
validation: appendWarning(definition, "Mana-Tombs now includes Pandemonius's bounded room pull, Shaffar's first beacon, Sha'heen's exact three ambush groups, and heroic-only Yor; movement and repeats remain explicit adaptations."),
});
}
function buildAuchenaiCrypts(definition: DungeonDefinition): DungeonDefinition {
assertImportedTransform(definition);
const review = REVIEW_BY_DUNGEON.get("auchenai-crypts")!;
const entities = ensureEntities(definition, [
{ entry: 18_441, name: "Stolen Soul", level: 66, role: "caster" },
{
entry: 18_478,
name: "Avatar of the Martyred",
level: 67,
role: "melee",
kind: "boss",
title: "Maladaar's Avatar",
attacks: AVATAR_OF_THE_MARTYRED_ATTACKS,
},
]);
const maladaar = objectiveForEntry(definition, 18_373);
const stolenSoul = packGroup("auchenai-crypts:stolen-soul", [18_441], entities, maladaar.position);
const avatar = packGroup("auchenai-crypts:avatar", [18_478], entities, maladaar.position);
const rules: DungeonEncounterRule[] = [
{
id: "maladaar-engaged",
trigger: { kind: "entity-engaged", entityId: maladaar.id },
actions: [{ id: "enter-maladaar-active", kind: "set-phase", phase: "maladaar-active" }],
},
{
id: "maladaar-steals-first-soul",
trigger: { kind: "elapsed", afterMs: 25_000 },
guards: [{ kind: "phase", phases: ["maladaar-active"] }],
actions: [{ id: "activate-stolen-soul", kind: "activate-spawn-group", spawnGroupId: stolenSoul.id }],
},
{
id: "maladaar-summons-avatar",
trigger: { kind: "entity-health", entityId: maladaar.id, atOrBelowPercent: 25 },
guards: [{ kind: "spawn-group-active", spawnGroupId: avatar.id, equals: false }],
actions: [{ id: "activate-maladaar-avatar", kind: "activate-spawn-group", spawnGroupId: avatar.id }],
},
{
id: "maladaar-defeated",
trigger: { kind: "entity-death", entityId: maladaar.id },
actions: [
{ id: "cleanup-stolen-soul", kind: "deactivate-spawn-group", spawnGroupId: stolenSoul.id },
{ id: "leave-maladaar-active", kind: "set-phase", phase: "ready" },
],
},
{
id: "avatar-defeated",
trigger: { kind: "entity-death", entityId: avatar.spawns[0].id },
guards: [{ kind: "spawn-group-active", spawnGroupId: avatar.id, equals: true }],
actions: [{ id: "deactivate-maladaar-avatar", kind: "deactivate-spawn-group", spawnGroupId: avatar.id }],
},
{
id: "resume-auchenai-crypts",
trigger: { kind: "tick" },
guards: [{ kind: "phase", phases: ["resume"] }],
once: false,
actions: [{ id: "resume-auchenai-ready", kind: "set-phase", phase: "ready" }],
},
];
const encounter: DungeonEncounterDefinition = Object.freeze({
id: "auchenai-crypts:tbc-batch-two-instance",
dungeonId: definition.id,
initialPhase: "ready",
resetPolicy: { bossWipePhase: "resume", checkpointPhase: "resume" },
source: sourceReview(review),
rules: Object.freeze(rules),
});
return Object.freeze({
...definition,
entities,
encounters: Object.freeze([...(definition.encounters ?? []), encounter]),
encounterSpawnGroups: Object.freeze([...(definition.encounterSpawnGroups ?? []), stolenSoul, avatar]),
validation: appendWarning(definition, "Auchenai Crypts now runs Maladaar's finite first Stolen Soul and 25-percent Avatar transition; Shirrak's untargetable Focus Fire hazard remains documented rather than fabricated as a blocker."),
});
}
function buildMagistersTerrace(definition: DungeonDefinition): DungeonDefinition {
assertImportedTransform(definition);
const review = REVIEW_BY_DUNGEON.get("magisters-terrace")!;
const entities = ensureEntities(definition, [
{ entry: 24_522, name: "Fel Crystal", level: 70, role: "caster" },
{ entry: 24_545, name: "Pure Energy", level: 70, role: "caster" },
{ entry: 24_560, name: "Priestess Delrissa", level: 70, role: "caster", kind: "boss", title: "Priestess of the Sun" },
{ entry: 24_557, name: "Kagani Nightstrike", level: 70, role: "melee" },
{ entry: 24_558, name: "Ellrys Duskhallow", level: 70, role: "caster" },
{ entry: 24_554, name: "Eramas Brightblaze", level: 70, role: "melee" },
{ entry: 24_561, name: "Yazzai", level: 70, role: "caster" },
{ entry: 24_674, name: "Phoenix", level: 70, role: "melee" },
{ entry: 24_708, name: "Arcane Sphere", level: 70, role: "caster" },
]);
const selin = objectiveForEntry(definition, 24_723);
const vexallus = objectiveForEntry(definition, 24_744);
const kael = objectiveForEntry(definition, 24_664);
const delrissaStatic = definition.staticSpawns.find((spawn) => entryForSpawn(definition, spawn) === 24_560);
if (!delrissaStatic) throw new Error("magisters-terrace is missing source Priestess Delrissa spawn 96966");
const delrissa: DungeonBossObjective = Object.freeze({
id: delrissaStatic.id,
name: "Priestess Delrissa",
position: delrissaStatic.position,
eventOnly: true,
});
const selinPassage = reviewedDoor(objectForSpawn(definition, 99_802), MAGISTERS_FILES);
const delrissaPassage = reviewedDoor(objectForSpawn(definition, 99_807), MAGISTERS_FILES);
const kaelRoom = Object.freeze({ ...reviewedDoor(objectForSpawn(definition, 99_808), MAGISTERS_FILES), initialState: "open" });
const selinRoom = Object.freeze({ ...reviewedDoor(objectForSpawn(definition, 99_809), MAGISTERS_FILES), initialState: "open" });
const vexallusPassage = reviewedDoor(objectForSpawn(definition, 99_813), MAGISTERS_FILES);
const escapeOrb = reviewedOrb(objectForSpawn(definition, 99_800), MAGISTERS_FILES);
const replacements = [escapeOrb, selinPassage, delrissaPassage, kaelRoom, selinRoom, vexallusPassage] as const;
const crystals = exactGroup("magisters-terrace:selin-crystals", [
{ entry: 24_522, position: [-248.053, 3.74882, 14.592] },
{ entry: 24_522, position: [-225.969, -2.9731, -20.0775] },
{ entry: 24_522, position: [-226.314, -2.98127, 20.2183] },
{ entry: 24_522, position: [-247.888, 3.80777, -14.6252] },
{ entry: 24_522, position: [-263.149, 1.32057, 0.309245] },
], entities);
const vexThresholds = [85, 70, 55, 40, 25] as const;
const vexNormalGroups = vexThresholds.map((threshold) => packGroup(
`magisters-terrace:vexallus-${threshold}-normal`, [24_545], entities, vexallus.position, 1,
));
const vexHeroicGroups = vexThresholds.map((threshold) => packGroup(
`magisters-terrace:vexallus-${threshold}-heroic`, [24_545, 24_545], entities, vexallus.position, 2,
));
const delrissaGroup: DungeonEncounterSpawnGroupDefinition = Object.freeze({
id: "magisters-terrace:delrissa-party",
spawns: Object.freeze([
delrissaStatic,
spawnAt("magisters-terrace:delrissa-helper-1", entityIdForEntry(entities, 24_557)!, [-123.77, -19.921, 17.6007]),
spawnAt("magisters-terrace:delrissa-helper-2", entityIdForEntry(entities, 24_558)!, [-131.731, -19.921, 15.0827]),
spawnAt("magisters-terrace:delrissa-helper-3", entityIdForEntry(entities, 24_554)!, [-121.563, -19.921, 15.6213]),
spawnAt("magisters-terrace:delrissa-helper-4", entityIdForEntry(entities, 24_561)!, [-129.988, -19.921, 17.2355]),
]),
});
const phoenix = packGroup("magisters-terrace:kael-phoenix", [24_674], entities, kael.position);
const spheres = packGroup("magisters-terrace:kael-spheres", [24_708, 24_708, 24_708], entities, kael.position);
const groups = [crystals, ...vexNormalGroups, ...vexHeroicGroups, delrissaGroup, phoenix, spheres] as const;
const rules: DungeonEncounterRule[] = [
{
id: "selin-engaged",
trigger: { kind: "entity-engaged", entityId: selin.id },
actions: [
{ id: "close-selin-room", kind: "set-world-object-state", objectId: selinRoom.id, state: "closed" },
{ id: "activate-selin-crystals", kind: "activate-spawn-group", spawnGroupId: crystals.id },
],
},
{
id: "selin-defeated",
trigger: { kind: "entity-death", entityId: selin.id },
actions: [
{ id: "cleanup-selin-crystals", kind: "deactivate-spawn-group", spawnGroupId: crystals.id },
{ id: "reopen-selin-room", kind: "set-world-object-state", objectId: selinRoom.id, state: "open", persistence: "run" },
{ id: "open-selin-passage", kind: "set-world-object-state", objectId: selinPassage.id, state: "open", persistence: "run" },
],
},
{
id: "vexallus-defeated",
trigger: { kind: "entity-death", entityId: vexallus.id },
actions: [
...[...vexNormalGroups, ...vexHeroicGroups].map((group, index) => ({
id: `cleanup-vexallus-energy-${index + 1}`,
kind: "deactivate-spawn-group" as const,
spawnGroupId: group.id,
})),
{ id: "open-vexallus-passage", kind: "set-world-object-state", objectId: vexallusPassage.id, state: "open", persistence: "run" },
{ id: "record-delrissa-unlocked", kind: "set-flag", flagId: "delrissa-unlocked", value: true, persistence: "run" },
{ id: "activate-delrissa-party", kind: "activate-spawn-group", spawnGroupId: delrissaGroup.id },
{ id: "enter-delrissa-active", kind: "set-phase", phase: "delrissa-active" },
],
},
{
id: "delrissa-party-defeated",
trigger: { kind: "entities-dead", entityIds: delrissaGroup.spawns.map((spawn) => spawn.id), timeoutMs: 900_000 },
guards: [{ kind: "phase", phases: ["delrissa-active"] }],
actions: [
{ id: "deactivate-delrissa-party", kind: "deactivate-spawn-group", spawnGroupId: delrissaGroup.id },
...objectiveActions("delrissa", delrissa),
{ id: "record-delrissa-complete", kind: "set-flag", flagId: "delrissa-complete", value: true, persistence: "run" },
{ id: "open-delrissa-passage", kind: "set-world-object-state", objectId: delrissaPassage.id, state: "open", persistence: "run" },
{ id: "leave-delrissa-active", kind: "set-phase", phase: "ready" },
],
},
{
id: "kael-engaged",
trigger: { kind: "entity-engaged", entityId: kael.id },
guards: [{ kind: "objective-complete", objectiveId: delrissa.id, equals: true }],
actions: [
{ id: "close-kael-room", kind: "set-world-object-state", objectId: kaelRoom.id, state: "closed" },
{ id: "enter-kael-active", kind: "set-phase", phase: "kael-active" },
],
},
{
id: "kael-first-phoenix",
trigger: { kind: "elapsed", afterMs: 15_000 },
guards: [{ kind: "phase", phases: ["kael-active"] }],
actions: [{ id: "activate-kael-phoenix", kind: "activate-spawn-group", spawnGroupId: phoenix.id }],
},
{
id: "kael-gravity-transition",
trigger: { kind: "entity-health", entityId: kael.id, atOrBelowPercent: 50 },
actions: [{ id: "activate-kael-spheres", kind: "activate-spawn-group", spawnGroupId: spheres.id }],
},
{
id: "kael-defeated",
trigger: { kind: "entity-death", entityId: kael.id },
guards: [{ kind: "objective-complete", objectiveId: delrissa.id, equals: true }],
actions: [
{ id: "cleanup-kael-phoenix", kind: "deactivate-spawn-group", spawnGroupId: phoenix.id },
{ id: "cleanup-kael-spheres", kind: "deactivate-spawn-group", spawnGroupId: spheres.id },
{ id: "reopen-kael-room", kind: "set-world-object-state", objectId: kaelRoom.id, state: "open", persistence: "run" },
{ id: "unlock-escape-orb", kind: "set-world-object-state", objectId: escapeOrb.id, state: "unlocked", persistence: "run" },
{ id: "record-magisters-complete", kind: "set-flag", flagId: "dungeon-complete", value: true, persistence: "run" },
{ id: "enter-magisters-complete", kind: "set-phase", phase: "complete" },
],
},
{
id: "resume-completed-magisters",
trigger: { kind: "tick" },
guards: [
{ kind: "phase", phases: ["resume"] },
{ kind: "flag", flagId: "dungeon-complete", equals: true },
],
priority: 20,
once: false,
actions: [{ id: "restore-magisters-complete", kind: "set-phase", phase: "complete" }],
},
{
id: "resume-delrissa-party",
trigger: { kind: "tick" },
guards: [
{ kind: "phase", phases: ["resume"] },
{ kind: "flag", flagId: "delrissa-unlocked", equals: true },
{ kind: "flag", flagId: "delrissa-complete", equals: false },
],
priority: 10,
once: false,
actions: [
{ id: "restore-delrissa-party", kind: "activate-spawn-group", spawnGroupId: delrissaGroup.id },
{ id: "restore-delrissa-active", kind: "set-phase", phase: "delrissa-active" },
],
},
{
id: "resume-magisters-ready",
trigger: { kind: "tick" },
guards: [{ kind: "phase", phases: ["resume"] }],
once: false,
actions: [{ id: "restore-magisters-ready", kind: "set-phase", phase: "ready" }],
},
];
vexThresholds.forEach((threshold, index) => {
const normal = vexNormalGroups[index];
const heroic = vexHeroicGroups[index];
rules.push(
{
id: `vexallus-${threshold}-normal`,
trigger: { kind: "entity-health", entityId: vexallus.id, atOrBelowPercent: threshold },
guards: [{ kind: "difficulty", difficultyIds: ["normal"] }],
actions: [{ id: `activate-vexallus-${threshold}-normal`, kind: "activate-spawn-group", spawnGroupId: normal.id }],
},
{
id: `vexallus-${threshold}-heroic`,
trigger: { kind: "entity-health", entityId: vexallus.id, atOrBelowPercent: threshold },
guards: [{ kind: "difficulty", difficultyIds: ["heroic"] }],
actions: [{ id: `activate-vexallus-${threshold}-heroic`, kind: "activate-spawn-group", spawnGroupId: heroic.id }],
},
{
id: `vexallus-${threshold}-normal-defeated`,
trigger: { kind: "entities-dead", entityIds: normal.spawns.map((spawn) => spawn.id), timeoutMs: 900_000 },
guards: [{ kind: "spawn-group-active", spawnGroupId: normal.id, equals: true }],
actions: [{ id: `deactivate-vexallus-${threshold}-normal`, kind: "deactivate-spawn-group", spawnGroupId: normal.id }],
},
{
id: `vexallus-${threshold}-heroic-defeated`,
trigger: { kind: "entities-dead", entityIds: heroic.spawns.map((spawn) => spawn.id), timeoutMs: 900_000 },
guards: [{ kind: "spawn-group-active", spawnGroupId: heroic.id, equals: true }],
actions: [{ id: `deactivate-vexallus-${threshold}-heroic`, kind: "deactivate-spawn-group", spawnGroupId: heroic.id }],
},
);
});
const encounter: DungeonEncounterDefinition = Object.freeze({
id: "magisters-terrace:tbc-batch-two-instance",
dungeonId: definition.id,
initialPhase: "ready",
initialState: {
flags: { "delrissa-unlocked": false, "delrissa-complete": false, "dungeon-complete": false },
worldObjectStates: {
[selinPassage.id]: "closed",
[selinRoom.id]: "open",
[vexallusPassage.id]: "closed",
[delrissaPassage.id]: "closed",
[kaelRoom.id]: "open",
[escapeOrb.id]: "locked",
},
},
resetPolicy: { bossWipePhase: "resume", checkpointPhase: "resume" },
source: sourceReview(review),
rules: Object.freeze(rules),
});
const objectives = replaceObjectiveLists(definition, [], [{ objective: delrissa, afterId: vexallus.id }]);
return Object.freeze({
...definition,
...objectives,
entities,
staticSpawns: Object.freeze(definition.staticSpawns.filter((spawn) => spawn.id !== delrissaStatic.id)),
encounters: Object.freeze([...(definition.encounters ?? []), encounter]),
encounterSpawnGroups: Object.freeze([...(definition.encounterSpawnGroups ?? []), ...groups]),
serverGameObjects: replaceObjects(definition, replacements),
validation: appendWarning(definition, "Magisters' Terrace now runs exact reviewed door lifecycle, five normal/heroic Vexallus thresholds, a deterministic source-valid Delrissa party, and finite Kael transitions; native movement, repeats, and transport remain adapted."),
});
}
/**
* Adds the second finite, checkpoint-safe source-reviewed TBC batch. Apply after
* the general WoW/progression-object adapters and after withWowTbcEncounterContent,
* then before Ascension/RuneWaker content.
*/
export function withWowTbcEncounterBatchTwoContent(definition: DungeonDefinition): DungeonDefinition {
if (definition.encounters?.some((encounter) => encounter.id === `${definition.id}:tbc-batch-two-instance`)) {
return definition;
}
switch (definition.id) {
case "underbog": return buildUnderbog(definition);
case "slave-pens": return buildSlavePens(definition);
case "mana-tombs": return buildManaTombs(definition);
case "auchenai-crypts": return buildAuchenaiCrypts(definition);
case "magisters-terrace": return buildMagistersTerrace(definition);
default: return definition;
}
}