Release Healer Man 0.1.14

This commit is contained in:
phenom committed 2026-09-05 11:40:50 -04:00
1 parent 67d7a8ebe7
commit 7b41b102fa
147 files changed
+169892 -1342

No files matched your search

+38 -1
View File
@@ -1,10 +1,20 @@
import { afterEach, describe, expect, it, vi } from "vitest";
const combatMedia = vi.hoisted(() => ({ ready: false }));
vi.mock("./combatMediaPack", async (importOriginal) => ({
...(await importOriginal<typeof import("./combatMediaPack")>()),
combatMediaPackReady: () => combatMedia.ready,
}));
import {
DEFAULT_COMBAT_PRESENTATION_SETTINGS,
type CombatPresentationEvent,
} from "./combatPresentation";
import {
combatAudioOutputEnabled,
authenticCombatAudioEnabled,
combatMediaPreloadEnabled,
configureCombatAudio,
masterAudioOutputEnabled,
playCombatAudio,
@@ -28,7 +38,10 @@ const fixtureEvent: CombatPresentationEvent = {
targetPosition: [1, 0, 0],
};
afterEach(() => vi.unstubAllGlobals());
afterEach(() => {
combatMedia.ready = false;
vi.unstubAllGlobals();
});
describe("combat audio output policy", () => {
it("does not create or fetch audio while the master volume is disabled", async () => {
@@ -50,6 +63,30 @@ describe("combat audio output policy", () => {
expect(masterAudioOutputEnabled(0.6)).toBe(true);
expect(combatAudioOutputEnabled(DEFAULT_COMBAT_PRESENTATION_SETTINGS, 0)).toBe(false);
expect(combatAudioOutputEnabled(DEFAULT_COMBAT_PRESENTATION_SETTINGS, 0.6)).toBe(true);
expect(authenticCombatAudioEnabled(DEFAULT_COMBAT_PRESENTATION_SETTINGS)).toBe(false);
combatMedia.ready = true;
expect(authenticCombatAudioEnabled({
...DEFAULT_COMBAT_PRESENTATION_SETTINGS,
originalMediaEnabled: true,
})).toBe(true);
expect(combatMediaPreloadEnabled(DEFAULT_COMBAT_PRESENTATION_SETTINGS, 0.6)).toBe(false);
expect(combatMediaPreloadEnabled({
...DEFAULT_COMBAT_PRESENTATION_SETTINGS,
originalMediaEnabled: true,
}, 0.6)).toBe(true);
});
it("does not fetch packaged sound effects before the player downloads them", async () => {
const AudioContextConstructor = vi.fn();
const fetch = vi.fn();
vi.stubGlobal("window", { AudioContext: AudioContextConstructor });
vi.stubGlobal("fetch", fetch);
configureCombatAudio(DEFAULT_COMBAT_PRESENTATION_SETTINGS, 0.6);
await expect(playMasterAudioCue("/assets/game/combat-effects/audio/fixture.wav")).resolves.toBeNull();
expect(AudioContextConstructor).not.toHaveBeenCalled();
expect(fetch).not.toHaveBeenCalled();
});
it("does not initialize audio for a zero-volume actor group", () => {
+22 -2
View File
@@ -6,6 +6,10 @@ import {
type CombatPresentationSettings,
type CombatPresentationSchool,
} from "./combatPresentation";
import {
combatMediaPackReady,
fetchOptionalMediaAsset,
} from "./combatMediaPack";
const MAX_COMBAT_VOICES = 32;
@@ -37,6 +41,19 @@ export function combatAudioOutputEnabled(
));
}
export function authenticCombatAudioEnabled(settings: CombatPresentationSettings): boolean {
return settings.originalMediaEnabled && combatMediaPackReady();
}
export function combatMediaPreloadEnabled(
settings: CombatPresentationSettings,
masterVolume: number,
): boolean {
return authenticCombatAudioEnabled(settings) && (
settings.vfxQuality !== "off" || combatAudioOutputEnabled(settings, masterVolume)
);
}
function combatAudioGroupOutputEnabled(
settings: CombatPresentationSettings,
masterVolume: number,
@@ -185,6 +202,7 @@ function playSynthesizedCombatCue(event: CombatPresentationEvent): void {
}
function weightedSound(event: CombatPresentationEvent): CombatEffectSoundAsset | null {
if (!configuredSettings || !authenticCombatAudioEnabled(configuredSettings)) return null;
const sounds = combatEffectManifestEntry(event.sourceSpellId, event.source)?.sounds ?? [];
const playable = sounds.filter((sound) => sound.url);
if (!playable.length) return null;
@@ -201,7 +219,7 @@ async function decodeAudio(url: string): Promise<AudioBuffer | null> {
const audioContext = ensureContext();
if (!audioContext) return null;
if (!decodedBuffers.has(url)) {
decodedBuffers.set(url, fetch(url)
decodedBuffers.set(url, fetchOptionalMediaAsset(url)
.then((response) => response.ok ? response.arrayBuffer() : Promise.reject(new Error(String(response.status))))
.then((data) => audioContext.decodeAudioData(data))
.catch(() => null));
@@ -213,7 +231,9 @@ export async function playMasterAudioCue(
url: string,
volume = 1,
): Promise<(() => void) | null> {
if (!masterAudioOutputEnabled(configuredMasterVolume)) return null;
if (!configuredSettings
|| !authenticCombatAudioEnabled(configuredSettings)
|| !masterAudioOutputEnabled(configuredMasterVolume)) return null;
const audioContext = ensureContext();
const buffer = await decodeAudio(url);
if (
+3
View File
@@ -116,6 +116,9 @@ describe("named aura combat runtime", () => {
school: "fire",
interruptible: true,
targetActorId: PLAYER_AGGRO_ID,
sourcePosition: [0, 0, 0],
targetPosition: [1, 0, 0],
sourceYaw: Math.PI / 2,
startedAt: 1_000,
completesAt: 6_000,
},
+77
View File
@@ -0,0 +1,77 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
const content = vi.hoisted(() => ({
usable: false,
install: vi.fn(async () => undefined),
remove: vi.fn(async () => undefined),
listener: null as (() => void) | null,
snapshot: {
downloadedBytes: 4,
totalBytes: 8,
manifest: {
packs: [{ id: "core:combat-media", fileCount: 12 }],
},
},
}));
vi.mock("../content/contentManager", () => ({
contentPackIsUsable: () => content.usable,
getContentManagerSnapshot: () => content.snapshot,
installContentPack: content.install,
removeContentPack: content.remove,
resolveContentUrl: (url: string) => `resolved:${url}`,
subscribeContentManager: (listener: () => void) => {
content.listener = listener;
return () => { content.listener = null; };
},
}));
import {
combatMediaPackReady,
downloadCombatMediaPack,
fetchCombatMediaAsset,
removeCombatMediaPack,
} from "./combatMediaPack";
beforeEach(() => {
content.usable = false;
content.install.mockClear();
content.remove.mockClear();
vi.unstubAllGlobals();
});
describe("optional combat media pack", () => {
it("uses the shared verified content-pack installer only after an explicit request", async () => {
const progress = vi.fn();
await expect(downloadCombatMediaPack(progress)).resolves.toEqual({ fileCount: 12 });
expect(content.install).toHaveBeenCalledWith("core:combat-media");
expect(progress).toHaveBeenLastCalledWith({ completed: 4, total: 8 });
});
it("does not fetch source media while its pack is offloaded", async () => {
const fetch = vi.fn();
vi.stubGlobal("fetch", fetch);
expect(combatMediaPackReady()).toBe(false);
await expect(fetchCombatMediaAsset("/assets/game/combat-effects/audio/shield.wav"))
.rejects.toThrow("not installed");
expect(fetch).not.toHaveBeenCalled();
});
it("resolves installed media through the shared content router and removes the same pack", async () => {
content.usable = true;
const response = new Response(new Uint8Array([1, 2, 3]));
const fetch = vi.fn(async () => response);
vi.stubGlobal("fetch", fetch);
await expect(fetchCombatMediaAsset("/assets/game/combat-effects/audio/shield.wav"))
.resolves.toBe(response);
expect(fetch).toHaveBeenCalledWith(
"resolved:/assets/game/combat-effects/audio/shield.wav",
{ cache: "no-store" },
);
await removeCombatMediaPack();
expect(content.remove).toHaveBeenCalledWith("core:combat-media");
});
});
+70
View File
@@ -0,0 +1,70 @@
import {
contentPackIsUsable,
getContentManagerSnapshot,
installContentPack,
removeContentPack,
resolveContentUrl,
subscribeContentManager,
} from "../content/contentManager";
export const COMBAT_MEDIA_MANIFEST_URL = "/assets/game/combat-effects/manifest.json";
export const COMBAT_MEDIA_CONTENT_PACK_ID = "core:combat-media";
export interface CombatMediaDownloadProgress {
readonly completed: number;
readonly total: number;
}
export interface CombatMediaDownloadResult {
readonly fileCount: number;
}
type ProgressListener = (progress: CombatMediaDownloadProgress) => void;
function isCombatMediaUrl(value: unknown): value is string {
return typeof value === "string" && value.startsWith("/assets/game/combat-effects/");
}
/** Callers must enforce the player's original-media opt-in before fetching. */
export async function fetchCombatMediaAsset(url: string): Promise<Response> {
if (!isCombatMediaUrl(url)) throw new Error(`Unsupported combat media URL: ${url}`);
return fetchOptionalMediaAsset(url);
}
/** Fetch source media only after its shared optional pack has been enabled. */
export async function fetchOptionalMediaAsset(url: string): Promise<Response> {
if (!combatMediaPackReady()) throw new Error("Original spell media is not installed.");
return fetch(resolveContentUrl(url), { cache: "no-store" });
}
/** Download and verify every file owned by the original combat-media pack. */
export async function downloadCombatMediaPack(
onProgress?: ProgressListener,
): Promise<CombatMediaDownloadResult> {
const publishProgress = () => {
const state = getContentManagerSnapshot();
onProgress?.({
completed: state.downloadedBytes,
total: Math.max(1, state.totalBytes),
});
};
const unsubscribe = subscribeContentManager(publishProgress);
try {
await installContentPack(COMBAT_MEDIA_CONTENT_PACK_ID);
publishProgress();
const pack = getContentManagerSnapshot().manifest?.packs.find((candidate) => (
candidate.id === COMBAT_MEDIA_CONTENT_PACK_ID
));
return { fileCount: pack?.fileCount ?? 0 };
} finally {
unsubscribe();
}
}
export async function removeCombatMediaPack(): Promise<void> {
await removeContentPack(COMBAT_MEDIA_CONTENT_PACK_ID);
}
export function combatMediaPackReady(): boolean {
return contentPackIsUsable(COMBAT_MEDIA_CONTENT_PACK_ID);
}
+6
View File
@@ -23,6 +23,7 @@ describe("combat presentation", () => {
audioEnabled: { enemy: false },
audioVolume: { player: 2, party: -1, enemy: 0.35 },
})).toEqual({
originalMediaEnabled: false,
vfxMode: "generated",
vfxQuality: "low",
vfxEnabled: { player: false, party: true, enemy: true },
@@ -31,8 +32,13 @@ describe("combat presentation", () => {
});
expect(normalizeCombatPresentationSettings({
...DEFAULT_COMBAT_PRESENTATION_SETTINGS,
originalMediaEnabled: true,
vfxMode: "original",
}).vfxMode).toBe("original");
expect(normalizeCombatPresentationSettings({
...DEFAULT_COMBAT_PRESENTATION_SETTINGS,
vfxMode: "original",
}).vfxMode).toBe("generated");
expect(normalizeCombatPresentationSettings({
...DEFAULT_COMBAT_PRESENTATION_SETTINGS,
vfxMode: "hybrid",
+22 -3
View File
@@ -1,4 +1,9 @@
import type { AbilityDefinition, AbilityEffect } from "./abilityCatalog";
import {
COMBAT_MEDIA_MANIFEST_URL,
combatMediaPackReady,
fetchCombatMediaAsset,
} from "./combatMediaPack";
export type CombatActorGroup = "player" | "party" | "enemy";
export type CombatVfxMode = "generated" | "original";
@@ -24,6 +29,7 @@ export type CombatPresentationSource = "wow335" | "ascension" | "runewaker" | "f
export type CombatPresentationPosition = readonly [number, number, number];
export interface CombatPresentationSettings {
readonly originalMediaEnabled: boolean;
readonly vfxMode: CombatVfxMode;
readonly vfxQuality: CombatVfxQuality;
readonly vfxEnabled: Readonly<Record<CombatActorGroup, boolean>>;
@@ -34,6 +40,7 @@ export interface CombatPresentationSettings {
const ACTOR_GROUPS: readonly CombatActorGroup[] = ["player", "party", "enemy"];
export const DEFAULT_COMBAT_PRESENTATION_SETTINGS: CombatPresentationSettings = Object.freeze({
originalMediaEnabled: false,
vfxMode: "generated",
vfxQuality: "high",
vfxEnabled: Object.freeze({ player: true, party: true, enemy: true }),
@@ -80,13 +87,20 @@ export function normalizeCombatPresentationSettings(
const record = value && typeof value === "object" && !Array.isArray(value)
? value as Partial<Record<keyof CombatPresentationSettings, unknown>>
: {};
const vfxMode = record.vfxMode === "generated" || record.vfxMode === "original"
const originalMediaEnabled = typeof record.originalMediaEnabled === "boolean"
? record.originalMediaEnabled
: fallback.originalMediaEnabled;
const requestedVfxMode = record.vfxMode === "generated" || record.vfxMode === "original"
? record.vfxMode
: fallback.vfxMode;
const vfxMode = requestedVfxMode === "original" && !originalMediaEnabled
? "generated"
: requestedVfxMode;
const vfxQuality = record.vfxQuality === "off" || record.vfxQuality === "low" || record.vfxQuality === "high"
? record.vfxQuality
: fallback.vfxQuality;
return Object.freeze({
originalMediaEnabled,
vfxMode,
vfxQuality,
vfxEnabled: booleanGroups(record.vfxEnabled, fallback.vfxEnabled),
@@ -191,7 +205,6 @@ interface GeneratedCombatEffectManifest {
readonly entries: readonly CombatEffectManifestEntry[];
}
const COMBAT_EFFECT_MANIFEST_URL = "/assets/game/combat-effects/manifest.json";
const manifestByKey = new Map<string, CombatEffectManifestEntry>();
const manifestBySpellId = new Map<number, CombatEffectManifestEntry>();
let manifestRequest: Promise<void> | null = null;
@@ -207,13 +220,19 @@ function installCombatEffectManifest(manifest: GeneratedCombatEffectManifest): v
export function preloadCombatEffectManifest(): Promise<void> {
if (manifestRequest) return manifestRequest;
if (typeof fetch === "undefined") return Promise.resolve();
manifestRequest = fetch(COMBAT_EFFECT_MANIFEST_URL)
manifestRequest = fetchCombatMediaAsset(COMBAT_MEDIA_MANIFEST_URL)
.then((response) => response.ok ? response.json() : Promise.reject(new Error(String(response.status))))
.then((manifest) => installCombatEffectManifest(manifest as GeneratedCombatEffectManifest))
.catch(() => undefined);
return manifestRequest;
}
export function effectiveCombatVfxMode(settings: CombatPresentationSettings): CombatVfxMode {
return settings.originalMediaEnabled && combatMediaPackReady()
? settings.vfxMode
: "generated";
}
export function combatEffectManifestEntry(
spellId: number | undefined,
source?: CombatPresentationSource,
+40
View File
@@ -83,12 +83,14 @@ import {
} from "./talentModifiers";
import {
getMobPosition,
getMobRuntimeTransform,
mobRuntimeDistanceSquared,
nearbyMobRuntimeIds,
removeMobPosition,
setMobPosition,
type CombatPosition,
} from "./mobRuntimeRegistry";
import { enemyTelegraphContainsPosition } from "./enemyTelegraphGeometry";
import {
getPartyRuntimePosition,
partyRuntimeDistanceSquared,
@@ -299,6 +301,9 @@ export interface EnemyActiveCast {
readonly school: EnemyDamageSchool;
readonly interruptible: boolean;
readonly targetActorId: AggroActorId | null;
readonly sourcePosition: CombatPosition;
readonly targetPosition: CombatPosition;
readonly sourceYaw: number;
readonly startedAt: number;
readonly completesAt: number;
}
@@ -6163,6 +6168,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
);
if (selectedAttack && startingEnemyCast) {
const attack = selectedAttack;
const sourceTransform = getMobRuntimeTransform(id);
enemyActiveCast = {
attackId: attack.id,
name: attack.name,
@@ -6170,6 +6176,10 @@ export const useCombatStore = create<CombatState>((set, get) => ({
school: attack.school,
interruptible: attack.interruptible !== false,
targetActorId: resolution.targetActorId,
sourcePosition: [...position],
targetPosition: [...targetPosition],
sourceYaw: sourceTransform?.yaw
?? Math.atan2(targetPosition[0] - position[0], targetPosition[2] - position[2]),
startedAt: now,
completesAt: now + (attack.castTimeMs ?? 0),
};
@@ -6187,6 +6197,7 @@ export const useCombatStore = create<CombatState>((set, get) => ({
);
} else if (selectedAttack && !cannotAttack) {
const attack = selectedAttack!;
const resolvedEnemyCast = enemyActiveCast;
enemyActiveCast = null;
const effect = attack.effect ?? {
kind: "damage" as const,
@@ -6198,10 +6209,24 @@ export const useCombatStore = create<CombatState>((set, get) => ({
stableCombatUnit(`${id}:${attack.id}:${attackRevision}:target`) * eligibleActors.length,
)]
: resolution.targetActorId;
const telegraphPlacement = resolvedEnemyCast
? {
sourcePosition: resolvedEnemyCast.sourcePosition,
targetPosition: resolvedEnemyCast.targetPosition,
sourceYaw: resolvedEnemyCast.sourceYaw,
}
: {
sourcePosition: position,
targetPosition,
sourceYaw: Math.atan2(targetPosition[0] - position[0], targetPosition[2] - position[2]),
};
const unvalidatedTargets = attack.target === "nearby-party"
? eligibleActors.filter((actorId) => {
const candidate = actorPosition(runtimeState, actorId);
if (!candidate) return false;
if (attack.telegraph) {
return enemyTelegraphContainsPosition(attack.telegraph, telegraphPlacement, candidate);
}
const radius = attack.radius ?? Math.max(1, attack.range * 0.35);
const targetDx = candidate[0] - position[0];
const targetDy = candidate[1] - position[1];
@@ -6298,6 +6323,21 @@ export const useCombatStore = create<CombatState>((set, get) => ({
targetId: id,
now,
}).auras];
} else if (effect.kind === "target-aura") {
for (const actorId of targets) {
namedAuras = [...applyCombatAura(namedAuras, {
definition: {
id: `enemy-debuff:${id}:${attack.id}:${effect.aura}`,
name: `${attack.name}: ${effect.aura}`,
disposition: "debuff",
durationMs: effect.durationMs > 0 ? effect.durationMs : null,
dispelCategory: "magic",
},
sourceId: id,
targetId: actorId,
now,
}).auras];
}
} else if (effect.kind === "control") {
for (const actorId of targets) {
if (hasControlImmunity(namedAuras, actorId, effect.mechanic)) continue;
+44
View File
@@ -167,6 +167,7 @@ describe("generated dungeon campaign catalog", () => {
"Fear",
"Magic's Bane",
"Corrupt Soul",
"Soulstorm",
]);
expect(attackNames(culling!, "Mal'Ganis")).toEqual([
"Basic Attack",
@@ -204,6 +205,49 @@ describe("generated dungeon campaign catalog", () => {
});
});
it("compiles Archaedas's timed and health-based encounter mechanics", () => {
const uldaman = generated.definitions.find((dungeon) => dungeon.id === "uldaman")!;
const archaedas = Object.values(uldaman.entities).find(
(entity) => entity.name === "Archaedas",
) as unknown as {
readonly combat: {
readonly mechanicIds: readonly string[];
readonly attacks: ReadonlyArray<{
readonly id: string;
readonly initialCooldownMs?: readonly [number, number];
readonly repeatCooldownMs?: readonly [number, number];
readonly condition?: { readonly kind: string; readonly maximum?: number };
readonly effect?: { readonly kind: string; readonly creatureEntry?: number; readonly count?: number };
readonly telegraph?: { readonly shape: string; readonly anchor: string; readonly radius?: number };
}>;
};
};
expect(archaedas.combat.mechanicIds).toEqual([
"archaedas-awaken-earthen-custodian",
"archaedas-ground-tremor",
"archaedas-awaken-earthen-guardians",
"archaedas-awaken-vault-warders",
]);
expect(archaedas.combat.attacks.find((attack) => attack.id === "archaedas-ground-tremor"))
.toMatchObject({
initialCooldownMs: [0, 10_000],
repeatCooldownMs: [14_000, 20_000],
telegraph: { shape: "circle", anchor: "source", radius: 18 },
});
expect(archaedas.combat.attacks.find(
(attack) => attack.id === "archaedas-awaken-earthen-guardians",
)).toMatchObject({
condition: { kind: "self-health-percent", maximum: 70 },
effect: { kind: "summon", creatureEntry: 7076, count: 6 },
});
expect(archaedas.combat.attacks.find(
(attack) => attack.id === "archaedas-awaken-vault-warders",
)).toMatchObject({
condition: { kind: "self-health-percent", maximum: 40 },
effect: { kind: "summon", creatureEntry: 10120, count: 2 },
});
});
it("does not leak outdoor zones, arenas, or raids into the dungeon browser", () => {
const ids = new Set(generated.definitions.map((dungeon) => dungeon.id));
for (const excluded of [
+114
View File
@@ -0,0 +1,114 @@
import { beforeEach, describe, expect, it } from "vitest";
import { encounterTimeline } from "./encounterTimeline";
import { useCombatStore } from "./combatStore";
describe("encounter mechanic timeline", () => {
beforeEach(() => {
useCombatStore.getState().initializeCharacter({ classId: "priest", level: 40 });
useCombatStore.getState().registerMob("boss", {
name: "Timeline Boss",
boss: true,
maxHealth: 1_000,
attacks: [{
id: "timed-quake",
name: "Timed Quake",
delivery: "area",
target: "nearby-party",
school: "nature",
animation: "cast",
range: 20,
radius: 10,
cooldownMs: 15_000,
repeatCooldownMs: [12_000, 18_000],
damageMultiplier: 1,
}, {
id: "health-adds",
name: "Awaken Adds",
delivery: "area",
target: "self",
school: "physical",
animation: "cast",
range: 0,
cooldownMs: 1_000,
maximumCasts: 1,
damageMultiplier: 0,
effect: { kind: "summon", creatureEntry: 1, count: 2 },
condition: { kind: "self-health-percent", minimum: 0, maximum: 70 },
}],
});
useCombatStore.setState((state) => ({
mobs: {
...state.mobs,
boss: {
...state.mobs.boss,
engaged: true,
abilityReadyAt: { "timed-quake": 16_000, "health-adds": 1_000 },
},
},
}));
});
it("shows both timer countdowns and pending health thresholds", () => {
const model = encounterTimeline(useCombatStore.getState().mobs, 10_000);
expect(model?.bossName).toBe("Timeline Boss");
expect(model?.rows.map((row) => [row.name, row.kind, row.value])).toEqual([
["Timed Quake", "timer", "6.0s"],
["Awaken Adds", "health", "70% HP"],
]);
});
it("promotes an active cast and hides a completed one-shot threshold", () => {
useCombatStore.setState((state) => ({
mobs: {
...state.mobs,
boss: {
...state.mobs.boss,
abilityCastCounts: { "health-adds": 1 },
activeCast: {
attackId: "timed-quake",
name: "Timed Quake",
school: "nature",
interruptible: true,
targetActorId: "__player__",
sourcePosition: [0, 0, 0],
targetPosition: [0, 0, 5],
sourceYaw: 0,
startedAt: 10_000,
completesAt: 12_000,
},
},
},
}));
expect(encounterTimeline(useCombatStore.getState().mobs, 11_250)?.rows).toMatchObject([{
name: "Timed Quake",
kind: "casting",
value: "0.8s",
}]);
});
it("keeps every pending health threshold visible beyond the countdown row limit", () => {
const boss = useCombatStore.getState().mobs.boss;
const extraTimers = Array.from({ length: 5 }, (_, index) => ({
...boss.attacks[0],
id: `extra-timer-${index}`,
name: `Extra Timer ${index}`,
}));
useCombatStore.setState((state) => ({
mobs: {
...state.mobs,
boss: {
...state.mobs.boss,
attacks: [...state.mobs.boss.attacks, ...extraTimers],
abilityReadyAt: {
...state.mobs.boss.abilityReadyAt,
...Object.fromEntries(extraTimers.map((attack, index) => [attack.id, 11_000 + index * 1_000])),
},
},
},
}));
const rows = encounterTimeline(useCombatStore.getState().mobs, 10_000, 4)?.rows ?? [];
expect(rows.filter((row) => row.kind === "timer")).toHaveLength(4);
expect(rows.filter((row) => row.kind === "health").map((row) => row.value)).toEqual(["70% HP"]);
});
});
+99
View File
@@ -0,0 +1,99 @@
import type { MobCombatState } from "./combatStore";
import type { EnemyDamageSchool, EnemyTelegraphShape } from "./enemyAttacks";
export type EncounterTimelineKind = "casting" | "timer" | "health";
export interface EncounterTimelineRow {
readonly id: string;
readonly name: string;
readonly kind: EncounterTimelineKind;
readonly value: string;
readonly progress: number;
readonly sortAt: number;
readonly school: EnemyDamageSchool;
readonly telegraphShape?: EnemyTelegraphShape;
}
export interface EncounterTimelineModel {
readonly bossId: string;
readonly bossName: string;
readonly rows: readonly EncounterTimelineRow[];
}
function clampedProgress(value: number): number {
return Math.min(1, Math.max(0, value));
}
export function encounterTimeline(
mobs: Readonly<Record<string, MobCombatState>>,
now: number,
limit = 4,
): EncounterTimelineModel | null {
const boss = Object.values(mobs)
.filter((mob) => mob.boss && mob.engaged && !mob.dead && !mob.despawned)
.sort((left, right) => (
Number(Boolean(right.activeCast)) - Number(Boolean(left.activeCast))
|| left.id.localeCompare(right.id)
))[0];
if (!boss) return null;
const healthPercent = (boss.health / Math.max(1, boss.maxHealth)) * 100;
const rows = boss.attacks.flatMap<EncounterTimelineRow>((attack) => {
if (attack.id === "basic-attack") return [];
const castCount = boss.abilityCastCounts[attack.id] ?? 0;
if (attack.maximumCasts && castCount >= attack.maximumCasts) return [];
const common = {
id: attack.id,
name: attack.name,
school: attack.school,
...(attack.telegraph ? { telegraphShape: attack.telegraph.shape } : {}),
};
if (boss.activeCast?.attackId === attack.id) {
const duration = Math.max(1, boss.activeCast.completesAt - boss.activeCast.startedAt);
const remaining = Math.max(0, boss.activeCast.completesAt - now);
return [{
...common,
kind: "casting",
value: `${(remaining / 1_000).toFixed(1)}s`,
progress: clampedProgress(remaining / duration),
sortAt: -1,
}];
}
if (attack.condition?.kind === "self-health-percent") {
const threshold = attack.condition.maximum;
return [{
...common,
kind: "health",
value: `${Math.round(threshold)}% HP`,
progress: clampedProgress(healthPercent / Math.max(1, threshold)),
sortAt: 1_000_000 + (100 - threshold) * 1_000,
}];
}
const readyAt = boss.abilityReadyAt[attack.id];
if (!Number.isFinite(readyAt)) return [];
const remaining = Math.max(0, (readyAt ?? now) - now);
const cooldown = Math.max(
1,
attack.repeatCooldownMs?.[1] ?? attack.initialCooldownMs?.[1] ?? attack.cooldownMs,
);
return [{
...common,
kind: "timer",
value: `${(remaining / 1_000).toFixed(1)}s`,
progress: clampedProgress(remaining / cooldown),
sortAt: remaining,
}];
}).sort((left, right) => left.sortAt - right.sortAt || left.id.localeCompare(right.id));
const alwaysVisibleHealthRows = rows.filter((row) => row.kind === "health");
const countdownRows = rows
.filter((row) => row.kind !== "health")
.slice(0, Math.max(1, limit));
const visibleRows = [...countdownRows, ...alwaysVisibleHealthRows];
return visibleRows.length ? {
bossId: boss.id,
bossName: boss.name,
rows: visibleRows,
} : null;
}
+56
View File
@@ -0,0 +1,56 @@
import { describe, expect, it } from "vitest";
import { normalizeEnemyAttack, type EnemyAttackDefinition } from "./enemyAttacks";
function attack(overrides: Partial<EnemyAttackDefinition>): EnemyAttackDefinition {
return {
id: "test-special",
name: "Test Special",
delivery: "area",
target: "nearby-party",
school: "physical",
animation: "attack",
range: 12,
cooldownMs: 10_000,
damageMultiplier: 1,
...overrides,
};
}
describe("enemy attack telegraphs", () => {
it("infers a warned cone for legacy frontal mechanics", () => {
const normalized = normalizeEnemyAttack(attack({ name: "Stone Cleave" }));
expect(normalized.telegraph).toMatchObject({
shape: "cone",
anchor: "source",
warningMs: 1_650,
});
expect(normalized.castTimeMs).toBe(1_650);
});
it("preserves authored geometry and extends too-short cast warnings", () => {
const normalized = normalizeEnemyAttack(attack({
name: "Falling Ruin",
castTimeMs: 500,
telegraph: {
shape: "circle",
anchor: "target",
warningMs: 2_200,
radius: 7,
},
}));
expect(normalized.castTimeMs).toBe(2_200);
expect(normalized.telegraph).toEqual({
shape: "circle",
anchor: "target",
warningMs: 2_200,
radius: 7,
});
});
it("does not telegraph basic attacks or beneficial mechanics", () => {
expect(normalizeEnemyAttack(attack({ id: "basic-attack" })).telegraph).toBeUndefined();
expect(normalizeEnemyAttack(attack({
effect: { kind: "heal", multiplier: 1 },
})).telegraph).toBeUndefined();
});
});
+106 -2
View File
@@ -17,6 +17,21 @@ export type EnemyDamageSchool =
export type EnemyControlMechanic = "fear" | "sleep" | "root" | "stun" | "silence";
export type EnemyTelegraphShape = "circle" | "cone" | "line";
export type EnemyTelegraphAnchor = "source" | "target";
/** World-space warning geometry shared by simulation and Three presentation. */
export interface EnemyAttackTelegraph {
readonly shape: EnemyTelegraphShape;
readonly anchor: EnemyTelegraphAnchor;
/** Minimum warning time before the mechanic resolves. */
readonly warningMs: number;
readonly radius?: number;
readonly length?: number;
readonly width?: number;
readonly angleDegrees?: number;
}
export type EnemyAbilityEffect =
| { readonly kind: "damage"; readonly multiplier: number }
| { readonly kind: "heal"; readonly multiplier: number }
@@ -26,6 +41,11 @@ export type EnemyAbilityEffect =
readonly durationMs: number;
readonly magnitude?: number;
}
| {
readonly kind: "target-aura";
readonly aura: string;
readonly durationMs: number;
}
| {
readonly kind: "control";
readonly mechanic: EnemyControlMechanic;
@@ -83,6 +103,7 @@ export interface EnemyAttackDefinition {
readonly maximumCasts?: number;
readonly damageMultiplier: number;
readonly radius?: number;
readonly telegraph?: EnemyAttackTelegraph;
readonly effect?: EnemyAbilityEffect;
readonly condition?: EnemyAbilityCondition;
readonly source?: EnemyAbilitySource;
@@ -96,6 +117,8 @@ export interface EnemyCombatDefinition {
readonly moveSpeed?: number;
readonly aggroRange?: number;
readonly leashRange?: number;
/** Stable special-attack ids available for future encounter loadout randomization. */
readonly mechanicIds?: readonly string[];
readonly attacks: readonly EnemyAttackDefinition[];
}
@@ -125,14 +148,94 @@ function normalizeCooldownRange(
return Object.freeze([Math.round(lower), Math.round(upper)]);
}
function harmfulEnemyAttack(value: EnemyAttackDefinition): boolean {
return !value.effect
|| value.effect.kind === "damage"
|| value.effect.kind === "control"
|| value.effect.kind === "target-aura";
}
/**
* Legacy/imported encounters receive safe defaults while authored mechanics can
* override every dimension in the modular mechanic catalog.
*/
export function inferEnemyAttackTelegraph(
value: EnemyAttackDefinition,
): EnemyAttackTelegraph | undefined {
if (value.id === "basic-attack" || !harmfulEnemyAttack(value)) return undefined;
const radius = value.radius ?? Math.max(3, value.range * 0.35);
if (/\b(?:breath|cleave|frontal|sweep|cone)\b/i.test(value.name)) {
return {
shape: "cone",
anchor: "source",
warningMs: 1_650,
length: Math.max(5.5, value.range),
angleDegrees: /breath/i.test(value.name) ? 70 : 95,
};
}
if (/\b(?:beam|charge|line|lance)\b/i.test(value.name)) {
return {
shape: "line",
anchor: "source",
warningMs: 1_650,
length: Math.max(7, value.range),
width: Math.max(2, radius * 0.45),
};
}
if (value.delivery === "area" || value.radius !== undefined) {
const targetAnchored = /\b(?:blizzard|rain|barrage|volley|eruption|ground|meteor)\b/i
.test(value.name) && value.target !== "self";
return {
shape: "circle",
anchor: targetAnchored ? "target" : "source",
warningMs: 1_650,
radius,
};
}
return undefined;
}
function normalizeTelegraph(value: EnemyAttackTelegraph): EnemyAttackTelegraph {
const warningMs = Math.round(finiteAtLeast(value.warningMs, 1_650, 250));
if (value.shape === "circle") {
return Object.freeze({
shape: value.shape,
anchor: value.anchor,
warningMs,
radius: finiteAtLeast(value.radius, 4, 0.5),
});
}
if (value.shape === "cone") {
return Object.freeze({
shape: value.shape,
anchor: "source",
warningMs,
length: finiteAtLeast(value.length, 8, 1),
angleDegrees: Math.min(180, finiteAtLeast(value.angleDegrees, 90, 5)),
});
}
return Object.freeze({
shape: value.shape,
anchor: "source",
warningMs,
length: finiteAtLeast(value.length, 8, 1),
width: finiteAtLeast(value.width, 3, 0.5),
});
}
export function normalizeEnemyAttack(
value: EnemyAttackDefinition,
fallback: EnemyAttackDefinition = DEFAULT_ENEMY_ATTACK,
): EnemyAttackDefinition {
const cooldownMs = Math.round(finiteAtLeast(value.cooldownMs, fallback.cooldownMs, 1));
const telegraph = value.telegraph ?? inferEnemyAttackTelegraph(value);
const inferredCastTimeMs = value.castTimeMs === undefined && value.animation === "cast"
? (fallback.castTimeMs ?? 1_500)
: (fallback.castTimeMs ?? 0);
: telegraph?.warningMs ?? fallback.castTimeMs ?? 0;
const castTimeMs = Math.max(
Math.round(finiteAtLeast(value.castTimeMs, inferredCastTimeMs, 0)),
telegraph?.warningMs ?? 0,
);
return Object.freeze({
id: value.id.trim() || fallback.id,
name: value.name.trim() || fallback.name,
@@ -143,7 +246,7 @@ export function normalizeEnemyAttack(
target: value.target,
school: value.school,
animation: value.animation,
castTimeMs: Math.round(finiteAtLeast(value.castTimeMs, inferredCastTimeMs, 0)),
castTimeMs,
interruptible: value.interruptible ?? fallback.interruptible ?? true,
range: finiteAtLeast(value.range, fallback.range, 0),
cooldownMs,
@@ -160,6 +263,7 @@ export function normalizeEnemyAttack(
...(value.radius !== undefined
? { radius: finiteAtLeast(value.radius, 0, 0) }
: {}),
...(telegraph ? { telegraph: normalizeTelegraph(telegraph) } : {}),
...(value.effect ? { effect: value.effect } : {}),
...(value.condition ? { condition: value.condition } : {}),
...(value.source ? { source: value.source } : {}),
+37
View File
@@ -1,4 +1,5 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { PLAYER_AGGRO_ID } from "./aggro";
import {
deterministicEnemyCooldown,
useCombatStore,
@@ -171,6 +172,42 @@ describe("server-backed enemy ability runtime", () => {
expect(summonIds.every((id) => getMobPosition(id) === null)).toBe(true);
});
it("applies a modular target aura as a dispellable player debuff", () => {
useCombatStore.getState().registerMob("runewaker-debuffer", {
name: "RuneWaker Debuffer",
maxHealth: 1_000,
attacks: [{
id: "conductivity",
name: "Conductivity",
delivery: "projectile",
target: "primary",
school: "arcane",
animation: "cast",
castTimeMs: 1_000,
range: 20,
cooldownMs: 10_000,
initialCooldownMs: [0, 0],
repeatCooldownMs: [10_000, 10_000],
damageMultiplier: 0,
effect: { kind: "target-aura", aura: "conductivity", durationMs: 6_000 },
}],
});
setMobPosition("runewaker-debuffer", [2, 0, 0]);
useCombatStore.getState().damageMob("runewaker-debuffer", 1, 1_000);
useCombatStore.getState().advanceMobCombat(1_000, [0, 0, 0]);
useCombatStore.getState().advanceMobCombat(2_000, [0, 0, 0]);
expect(useCombatStore.getState().auras).toContainEqual(expect.objectContaining({
sourceId: "runewaker-debuffer",
targetId: PLAYER_AGGRO_ID,
expiresAt: 8_000,
definition: expect.objectContaining({
disposition: "debuff",
dispelCategory: "magic",
}),
}));
});
it("honors encounter-state conditions before selecting an ability", () => {
useWailingEncounterStore.getState().reset();
useCombatStore.getState().registerMob("event-caster", {
+47
View File
@@ -0,0 +1,47 @@
import { describe, expect, it } from "vitest";
import { enemyTelegraphContainsPosition } from "./enemyTelegraphGeometry";
const placement = {
sourcePosition: [0, 0, 0] as const,
targetPosition: [0, 0, 10] as const,
sourceYaw: 0,
};
describe("enemy telegraph gameplay geometry", () => {
it("matches source and target anchored circles", () => {
expect(enemyTelegraphContainsPosition({
shape: "circle", anchor: "source", warningMs: 1_000, radius: 4,
}, placement, [3, 0, 0])).toBe(true);
expect(enemyTelegraphContainsPosition({
shape: "circle", anchor: "target", warningMs: 1_000, radius: 3,
}, placement, [0, 0, 8])).toBe(true);
expect(enemyTelegraphContainsPosition({
shape: "circle", anchor: "target", warningMs: 1_000, radius: 3,
}, placement, [0, 0, 4])).toBe(false);
});
it("keeps cone hits in front and within the visible angle", () => {
const cone = {
shape: "cone" as const,
anchor: "source" as const,
warningMs: 1_000,
length: 10,
angleDegrees: 90,
};
expect(enemyTelegraphContainsPosition(cone, placement, [2, 0, 7])).toBe(true);
expect(enemyTelegraphContainsPosition(cone, placement, [8, 0, 2])).toBe(false);
expect(enemyTelegraphContainsPosition(cone, placement, [0, 0, -2])).toBe(false);
});
it("uses the visible line width for collision", () => {
const line = {
shape: "line" as const,
anchor: "source" as const,
warningMs: 1_000,
length: 12,
width: 4,
};
expect(enemyTelegraphContainsPosition(line, placement, [1.9, 0, 8])).toBe(true);
expect(enemyTelegraphContainsPosition(line, placement, [2.1, 0, 8])).toBe(false);
});
});
+51
View File
@@ -0,0 +1,51 @@
import type { EnemyAttackTelegraph } from "./enemyAttacks";
import type { CombatPosition } from "./mobRuntimeRegistry";
export interface EnemyTelegraphPlacement {
readonly sourcePosition: CombatPosition;
readonly targetPosition: CombatPosition;
readonly sourceYaw: number;
}
export function enemyTelegraphYaw(placement: EnemyTelegraphPlacement): number {
const dx = placement.targetPosition[0] - placement.sourcePosition[0];
const dz = placement.targetPosition[2] - placement.sourcePosition[2];
return dx * dx + dz * dz > 0.0001 ? Math.atan2(dx, dz) : placement.sourceYaw;
}
export function enemyTelegraphOrigin(
telegraph: EnemyAttackTelegraph,
placement: EnemyTelegraphPlacement,
): CombatPosition {
return telegraph.anchor === "target"
? placement.targetPosition
: placement.sourcePosition;
}
/** Matches the visible warning mesh in the X/Z combat plane. */
export function enemyTelegraphContainsPosition(
telegraph: EnemyAttackTelegraph,
placement: EnemyTelegraphPlacement,
candidate: CombatPosition,
): boolean {
const origin = enemyTelegraphOrigin(telegraph, placement);
const dx = candidate[0] - origin[0];
const dz = candidate[2] - origin[2];
if (telegraph.shape === "circle") {
const radius = telegraph.radius ?? 0;
return dx * dx + dz * dz <= radius * radius;
}
const yaw = enemyTelegraphYaw(placement);
const forwardX = Math.sin(yaw);
const forwardZ = Math.cos(yaw);
const forward = dx * forwardX + dz * forwardZ;
const side = Math.abs(dx * forwardZ - dz * forwardX);
const length = telegraph.length ?? 0;
if (forward < 0 || forward > length) return false;
if (telegraph.shape === "line") return side <= (telegraph.width ?? 0) / 2;
const distance = Math.hypot(dx, dz);
if (distance <= 0.0001) return true;
const halfAngle = ((telegraph.angleDegrees ?? 0) * Math.PI) / 360;
return Math.acos(Math.min(1, Math.max(-1, forward / distance))) <= halfAngle;
}
@@ -16,7 +16,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -56,7 +56,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -96,7 +96,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -156,7 +156,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -196,7 +196,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -256,7 +256,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -336,7 +336,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -416,7 +416,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -496,7 +496,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -556,7 +556,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -596,7 +596,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -656,7 +656,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -696,7 +696,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -756,7 +756,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -836,7 +836,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -876,7 +876,7 @@ export const AETERNAL_CIRCLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-aeternal-circle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -2303,6 +2303,6 @@ export const AETERNAL_CIRCLE_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
File diff suppressed because it is too large. Load diff
@@ -16,7 +16,7 @@ export const BARREN_CAVES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-barren-caves-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -76,7 +76,7 @@ export const BARREN_CAVES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-barren-caves-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -136,7 +136,7 @@ export const BARREN_CAVES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-barren-caves-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -176,7 +176,7 @@ export const BARREN_CAVES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-barren-caves-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -216,7 +216,7 @@ export const BARREN_CAVES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-barren-caves-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -296,7 +296,7 @@ export const BARREN_CAVES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-barren-caves-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -355,9 +355,14 @@ export const BARREN_CAVES_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-barren-caves-101462-spell-492292",
"rw-barren-caves-101462-spell-492293",
"rw-barren-caves-101462-control-492294"
],
"attacks": [
{
"id": "rw-barren-caves-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -368,13 +373,14 @@ export const BARREN_CAVES_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-barren-caves-spell-492292",
"id": "rw-barren-caves-101462-spell-492292",
"name": "Trample",
"spellId": 492292,
"delivery": "projectile",
"target": "primary",
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "cast",
"castTimeMs": 1800,
"range": 10,
"cooldownMs": 20000,
"initialCooldownMs": [
@@ -385,16 +391,24 @@ export const BARREN_CAVES_ENTITIES = {
20000,
20000
],
"damageMultiplier": 0.85
"damageMultiplier": 0.85,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
}
},
{
"id": "rw-barren-caves-spell-492293",
"id": "rw-barren-caves-101462-spell-492293",
"name": "Bash",
"spellId": 492293,
"delivery": "melee",
"target": "primary",
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"castTimeMs": 1800,
"range": 5,
"cooldownMs": 20000,
"initialCooldownMs": [
@@ -405,7 +419,46 @@ export const BARREN_CAVES_ENTITIES = {
20000,
20000
],
"damageMultiplier": 0.85
"damageMultiplier": 0.85,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
}
},
{
"id": "rw-barren-caves-101462-control-492294",
"name": "Gulp",
"delivery": "area",
"target": "nearby-party",
"school": "shadow",
"animation": "cast",
"castTimeMs": 1800,
"range": 15,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
},
"effect": {
"kind": "control",
"mechanic": "stun",
"durationMs": 3000
}
}
]
},
@@ -1009,6 +1062,6 @@ export const BARREN_CAVES_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-cave-water-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -56,7 +56,7 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-cave-water-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -116,7 +116,7 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-cave-water-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -176,7 +176,7 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-cave-water-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -216,7 +216,7 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-cave-water-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -255,9 +255,18 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-cave-water-dragon-102623-spell-494249",
"rw-cave-water-dragon-102623-spell-494250",
"rw-cave-water-dragon-102623-spell-494253",
"rw-cave-water-dragon-102623-spell-494254",
"rw-cave-water-dragon-102623-debuff-494297",
"rw-cave-water-dragon-102623-debuff-494298",
"rw-cave-water-dragon-102623-phase-45"
],
"attacks": [
{
"id": "rw-cave-water-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -266,6 +275,193 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-cave-water-dragon-102623-spell-494249",
"name": "Manastorms Duo Lv5",
"spellId": 494249,
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 0,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
},
{
"id": "rw-cave-water-dragon-102623-spell-494250",
"name": "Manastorms Duo Lv5",
"spellId": 494250,
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 0,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
},
{
"id": "rw-cave-water-dragon-102623-spell-494253",
"name": "Water Dragon Bomb",
"spellId": 494253,
"delivery": "projectile",
"target": "primary",
"school": "frost",
"animation": "cast",
"castTimeMs": 2000,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
},
{
"id": "rw-cave-water-dragon-102623-spell-494254",
"name": "Dragon Attack",
"spellId": 494254,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "cast",
"castTimeMs": 1800,
"range": 15,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 10,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 10
}
},
{
"id": "rw-cave-water-dragon-102623-debuff-494297",
"name": "Law of the Dragons",
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 2000,
"range": 30,
"cooldownMs": 12000,
"initialCooldownMs": [
12000,
12000
],
"repeatCooldownMs": [
12000,
12000
],
"damageMultiplier": 0,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
},
"effect": {
"kind": "target-aura",
"aura": "confusion",
"durationMs": 6000
}
},
{
"id": "rw-cave-water-dragon-102623-debuff-494298",
"name": "Law of the Dragons",
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 2000,
"range": 30,
"cooldownMs": 12000,
"initialCooldownMs": [
12000,
12000
],
"repeatCooldownMs": [
12000,
12000
],
"damageMultiplier": 0,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
},
"effect": {
"kind": "target-aura",
"aura": "confusion",
"durationMs": 6000
}
},
{
"id": "rw-cave-water-dragon-102623-phase-45",
"name": "Phase Change",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"interruptible": false,
"range": 0,
"cooldownMs": 1000,
"initialCooldownMs": [
0,
0
],
"repeatCooldownMs": [
1000,
1000
],
"maximumCasts": 1,
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "phase-45",
"durationMs": 5000
},
"condition": {
"kind": "self-health-percent",
"minimum": 0,
"maximum": 45
}
}
]
},
@@ -296,9 +492,15 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-cave-water-dragon-104161-spell-493996",
"rw-cave-water-dragon-104161-spell-493999",
"rw-cave-water-dragon-104161-spell-494000",
"rw-cave-water-dragon-104161-aura-494386"
],
"attacks": [
{
"id": "rw-cave-water-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -307,6 +509,115 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-cave-water-dragon-104161-spell-493996",
"name": "Water Spirit Attack",
"spellId": 493996,
"delivery": "area",
"target": "nearby-party",
"school": "frost",
"animation": "cast",
"castTimeMs": 2000,
"range": 15,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-cave-water-dragon-104161-spell-493999",
"name": "Wave Bomb",
"spellId": 493999,
"delivery": "area",
"target": "nearby-party",
"school": "frost",
"animation": "cast",
"castTimeMs": 2000,
"range": 20,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-cave-water-dragon-104161-spell-494000",
"name": "Wave Strike",
"spellId": 494000,
"delivery": "area",
"target": "nearby-party",
"school": "frost",
"animation": "cast",
"castTimeMs": 2000,
"range": 20,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-cave-water-dragon-104161-aura-494386",
"name": "Soul of the Water Dragon",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "soul-of-the-water-dragon",
"durationMs": 7500
}
}
]
},
@@ -1458,6 +1769,6 @@ export const CAVE_WATER_DRAGON_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -48,6 +48,6 @@ export const DEMON_STRONGHOLD_125_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -76,7 +76,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -116,7 +116,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -156,7 +156,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -236,7 +236,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -336,7 +336,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -396,7 +396,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -436,7 +436,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -536,7 +536,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -616,7 +616,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -656,7 +656,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -696,7 +696,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -736,7 +736,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -776,7 +776,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -816,7 +816,7 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -855,9 +855,15 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-demon-stronghold-102701-aura-494871",
"rw-demon-stronghold-102701-aura-494873",
"rw-demon-stronghold-102701-spell-494875",
"rw-demon-stronghold-102701-spell-494876"
],
"attacks": [
{
"id": "rw-demon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -866,6 +872,105 @@ export const DEMON_STRONGHOLD_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-demon-stronghold-102701-aura-494871",
"name": "Hurricane",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 2000,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "hurricane",
"durationMs": 7500
}
},
{
"id": "rw-demon-stronghold-102701-aura-494873",
"name": "Struggle",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 10000,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "struggle",
"durationMs": 7500
}
},
{
"id": "rw-demon-stronghold-102701-spell-494875",
"name": "Double Cyclone",
"spellId": 494875,
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 3000,
"range": 15,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-demon-stronghold-102701-spell-494876",
"name": "Titanic Attack",
"spellId": 494876,
"delivery": "projectile",
"target": "primary",
"school": "physical",
"animation": "cast",
"castTimeMs": 2000,
"range": 15,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
}
]
},
@@ -2954,6 +3059,6 @@ export const DEMON_STRONGHOLD_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -1,2 +1,2 @@
// Generated by npm run dungeon:catalog. Do not edit.
export default "H4sIAAAAAAACCuy9bY/jOJIu+lcMnwOcWcBmMYLvBZwP/bKzM/dO7/Tt2jODi0FhQUlUpqecVl7ZWdXVjf7vF0FKtmzJaTntrsrKqf0w22WmRDIUEYyXJ4K/Ttf5bbjzfwv1elGtpq9hNl1XD3Uepq9/neb+7t4vblY/+NWiDOvN9PW0eFjdhGo1v1/ch+ViFV61P7R/y/65rlbT2TSs3i/qanUXVpsfff7O34T19PU/pus6f3Xj78Krm7AKtd+E4lW5eB9+XPqPof4+vevPd/dVvVm3bxp45M6v/HpT1XffrNdh+/74wOQPm0DP+/rjpPTLZebzd/82fUsLyquH1SbU3/mNX1Y309ePvvmnh9VmcReaP27X4n8JdbW5/a6qw3fV3d2CSCILV0AhM2VQBAsuV7lwvNS5BzAGrSiyMrNB7D/+JlL5L1X+bnghg3+6Jcl9WC63+/h16td5WKUPOGXs1V98sXi3/uHH/+ffi8Wmql/ly0VYbeb5Q12H1eaVr4OfK3x17zf57fz7V99/+8fFMqy/i3/16g29mxVZTh/xvspv0zv/FPwy1D/41fzNpqr9TXjVssD8Q1W/a/lg/So+M09THn31b7/Npnn1PtCLpq//8WvLV38uiBq3vqg+lH51M38Xwv10Nr3z9zQiROczrqevDf1zs9gsiLcEse7Gbxb5m3v/YbWevgbuZtO68neL1Q3xyHr6GuWOv9uvkVd1mP42O1gD8cCHxaqYrzdV/s4XobMMub8M1V0G9JbhxOEq+MhFFMEXd4tVWHfm1o+QALFHAtMngRg5+we/WC5WN/Pcvw/1qrMGefAZ7N4a+OEaUOHhGszIJdT+l6ouw2r+rvYPy84KzGOMYEdQAXDkErKlz9+VfnNb3c2LEO67dLD7q9B7dDA9OnDorWIsJzwsC3/nV7u5DT+LAoLjkylws6ruQh1uutO7g+nl45IoTG/rAsZK4sPqXVjNSasvO0IYhfv4EkSP+lIdLkGey4VF9aErB4DuMQZQPTZ0svcRxgrjOvf1MmzmdxWdT6H+2FmGdY/oI9HjQ0Fcd7AMPXIZvzwsS1/X/t1ueuSPTY8wQh2APEcY6yp/N1/fL+oON+Dhp4C9VZieQCite4oRz15FEe43tx2OQHEgFyC6y9ADR5Tsfww1miduq2V151d5lxKH3GBPaAZSVE9dwKYmxbi86x6Oh1/CdedXvdNBip5qoDeMmv/O1/6hqFbdo/nR3fe0gsKeVkA+Wi3chHJRh3l+69d3nTXYxzRT3z6A/hcYKw8FTX+3fzT2JAH3JKGnlYzqSYIa+wHWt36zCXUo5rd+uewIgpIHgrAnB6Jvpzj7ZNWYLauqmJcP9cp3BUFJfGQJ0FOLINyTl3AblsvICjV5QfVmjxIH1tLe54AeJax68kG93gR/994/LDfd6dV5VBhQB2OttYdVEeqsuunOrh8RhQFzVcOTzdX10r8P8/uw2iO+eWT3OCCKvbNJjKW+r3O/qf0vndkVPrZ7d/rTj5XDrNr41SL33bnFY6ey65vI8GTuvwv5rV/5uju7PIvrrHyySZCcxfnSZx/rxWpz212EOo/57NMVwDpsbsO7dz01qPRjwq8HzIEne6wUuZhvqrtsb/7H+D/SeH9+cE82BvxDfhtWfjHP64/3expQ2ceIIE/7y8KMpsHNgqzj9XwT6nr/OLCPKsK+zyh7ilCfYx/O76rar7u2oXaPLGDgOHqyVRDWub8P87Iml/WhXt1Wy274QunHzua+ahC2/0HGLuVhc+PrIhzEcZR5TD0MSCbg0w+lZgX3i9XK510PUpnH9MMYJTWWCpvbMF+Fnx+6QmH0Y75jTzOgeLqKpOmr/GG5P799bPNjPsHYgzF/WMY4UlXOhxwG5R5V0/1wEjw5nha1c1pHtdpbgjuLE4x5spVS1P7d5tbX1epAJjTnZ51Wzj3Zife//LNazlehfsi688Nj+rHnORn+VH7YfoXl4uZ2s1qsbrqrwLOYYYgtxwaWbh5WxV4kQ/PHArv9ya15cjzv/aKiaA5p5u789pFP0HPc3FPFwN+u3vnbsJlXy2L+brG6KaqO+6rh0XhO32YQ4sk2Q1nVNyEKZPXQtZu0wMdMhh4zyp7xasfqxnrhl7QCUpL5rb+7X3TjCVrxR4jR+yTqqZ/kfrGJZCDF0J39MYYQfWnQ9snO41Yq61AuQ77ZJ4N+1ITrL+TJKnqzWL0L9Sbkt/P1bfWBIq67ZVg80FHQXUafM/Vjq7ivqzwUD7Vfzts8YZ8/6+qXsJrXwee33eC3/cTLqOqbarMJqzllgzqRX2N/b4K8nU2LUC5WC2KIdUzV9TPGi+Gs3WaxWdKL32xHJv93L5+3LG/atG9B77eUqv353rfpzHzp1+tFvp9R/qEqIiMVm/ntx6xekBItFmW5yB+Wm49/8/XCrzZptXFtq6q+88u9P+r+uib6/ODX7+JuluF9WP7kVzEzCXYm+NuGDBRa+X77hj/vvdm/94ulzxbLRXx3889ob97XYR1W9CVoR79Ol1Xe/PcAaTa34Y729tf7sKLM6rKYhJ/vQ7FoJHL9cHfna5rij3R8Tja3dfVwczs5eNNsUoQy+M1ksVlPthwyqStyj2YTvyom9JUmm9swoYjVx4lfLt4HNp1Nl5UvFqubH8J6HbOz0x/rcO/rxermcBLG6O8fVvmtrzeh+KYO/j99XP3/aX+b9HdIjHVTVw8rot//4JYXnD5vmVLZebWs6jjgAYFPZ9NV8HVKfJb0H6j5b7Opv8sopfwXosHeY95m0pFds1htKCdOX4MzxN9m09twt1jf34Y6bB9bv/v4XfukNS63FEJJi9v+zh3PuDt8o5a/zWL8NapKv+yvpMDcuGL/OZhNq7Jchw2xFs6Qz+bm7W8kEJtAJ9E6vuFhUz1sfoyh1Shz0//+Fv4nYRjSCNGYfn47m2bLsCr2/xLjX8aBzh/6gjjofdj/Wxn/th3b/nlc0D0t5cOi2NxSkJ4T+dIWBVmq9/TQ6iYZBHm12lQPdXzpDygnyNXkO6snAHYCEicozQS5nYCByRsh5MTARHI9AdTT2fQHYSco3OQ74G4CVkzA4AS1naDk8bE3QsqJ0xPhzMSY6VsCD3gCfkRqvV+sH/xyJ+vNv2fTcrEMDTum39jNkhjjoV5OX09fpTck2MUW9fFKiPlOkb3aey6/Dfm79cPd9PVU5DJg4YKzgcAd4AnlEQQEoQCd1VwFb2VWWCEQweXccyO8B2kzV3pjSI4XvwSylJw1UstZNERWN8vwHUnq9LV0yIGT3smr5XKRlGG7xe1Pc875/k73hs7f8NDjnX1zV+Y65zygB1eW0uZQZLLIrAtZcByU4FnhLBgshTOZlgFcYYTnipeF0byzb5BO93atlRKaTr/eNuH4NuGybUJ/m1qDNaC8F76wXmPmjFXcAXijQTulNDfCclGgsKhECcCzHHOEkmORO9XdpqZoy+ht4vFt4mXbxP42jQKFslQ2A6N9gCxTWYG68EaZQuSGF2iUyApjjc2dUtzLzCuvyjwIrnzW2SZytGdsUxzfprhsm6K/zUyA17JEiSrnRa5UaQBQIqIw3JWuKJQP3ilecq28VCZ4m5fATebKkPuw9zXtOduUx7cpL9um7G8TMpCldzJDmxtZWs4DbYWjtNKWyktjpeCG+zLLi8y5LMtDKDOdO5tnKNS+bOIZ21THt6ku26YaYFpwTmbeGuXLzOSIFkvMNfhMSSu4A64CWu+VK2xWZAGzEoo8U7r0PofgOtuUTp/zNfXxberLtqn728SyhBJzg4XgptAauFZZ8KYseY5K0OIBICAP0mnIJGRCYGm5QZlpB7KzTWXlOds0x7dpLtum6W+zyJQ3nKPPvcid92BLMDxoHoQrNYpcWlAh1y5ojTKoUqIJeeaKQgjnMtvZppZcnrFNe3yb9rJt2oEDpfBZoYWQvNCobaa1R+O9DwY5VyorA2RYhKC9U4BBYs6tLrEUpdXS2e42BVfnyKY7vk132TZdf5tWccsDlmDyXFuygXyweVkI64PI8pBnRikRQDqjdCh5UFxaZ70zUhhfdg8UpfCMcxOOW0FwmRUEA1aQ45gJWwSUIstKEXwZiiCdLbISTZlxLaCQ4HhRClMi5JzzQkJJpgFCZnbbFE5Z3pdNo4Vzg9s8bgXBZVYQDFlBaHNhM2VlrvPCegxo0YFzygZrJHhe6kJlpQ82aCkNF7km9HPBQwiye6A4C+dYQXDcCoLLrCAYsIJUrp1x5Cy7TOvcGG4hZCYYHbDMJGgMMoCWPCAvueAoMGAuM8+d1KZzoCAIMaRptbFycJvHrSC4zAqCASvIQR5MnjtTGIVaZcgLcFoorRVA4bh1VqEDL/Lcl4UN0ijNy5znWgReio4KkgIk9G130lzDn/O4GQSXmUEwYAZpsmezTJKBIHRRZii01nnhvRWl5WC1Ba+dFegKlXsM2kmhfamx5NYZv/ucaMSAcGojrBrc5nEzCC4zg2DADOKZsJgJ8kc0Wh8ynkuvcy68kmB9yDEIIzELRYGZxcDzzJXCFybzJS9N1wwCZ844UeC4GQSXmUEwYAbZEHJeuMBLrpzTBhVmVsjcaVUKMhtUyCDjZaZtFlyOHp1KZqFUDsRum0ILPbBNIx0fFs7jZhBcZgbBgBmkbK6tDUpYyLzKMVjaKLfKF9rkQVqUhROIeemVlJrnWkEITkqJGUiDO6bVVg6pWmtxeJvHzSC4zAyCATPI2MJa7V1hHSg0PM8kyCC58JkSufUSkOcuK02JLkcltXdaYcBCC5VDKbbbVJw72bf2nAEcPjiPm0FwmRkEA2aQKAoLkMtCS+Ho0MyMFVmeq6wwDrhDKYALb3JbFNZTHCUrlC/KAnNuSreLDgmrxcDXNJoLMbRNPG4G4WVmEA6YQTLjvvDKKY5C0ukitPFGidLnqlDKW2O9w1BwoZwurc5kIaSCwpRZiZZ3XBRjnTzDPsDjZhBeZgbhgBlUeFdiWeRLine truncated
export default "H4sIAAAAAAACCuy9a5PbyJUo+FcY3N24MxFkKs/J10lF3A/9GI+96x57W147NhwdN0AgWcURH7UgS2p1h/77xskESJAASbBISaWy/EHuQhLIzJPn/crfh+v8Piyyv4dyPVsth69hNFyvHss8DF//PsyzxUM2u1v+lC1n07DeDF8Pi8flXVgtxw+zhzCfLcOr+kH9W/Hf69VyOBqG5btZuVouwnLz1yx/m92F9fD1P4frMn91ly3Cq7uwDGW2CcWr6exd+Os8+xDKH9O3/rR4WJWbdf2ljlcW2TJbb1bl4rv1Omy/H18Y/Nsm8PtZ+WEwzebzSZa//ffhL7ygfPW43ITyh2yTzVd3w9cnv/zz43IzW4Tqx/Vast9Cudrc/7Aqww+rxWLGINGFL6DQE+NQBQKfm1x5ObV5BuAckiom0wkFtf/6mwjlP6/yt90L6fzpFiQPYT7f7uP3YbbOwzId4FCIV3/Oitnb9U9//b//o5htVuWrfD4Ly804fyzLsNy8ysqQjQ2+esg2+f34x1c/fv+H2Tysf4i/evWGvy2KSc6H+LDK79M3/xiyeSh/ypbjN5tVmd2FVzUKjN+vyrc1HqxfxXfGacqjn/74cTTMV+8Cf2j4+p+/13j1p4KhcZ8Vq/fTbHk3fhvCw3A0XGQPPKJU4xjXw9eO/9zMNjPGLcWou8k2s/zNQ/Z+uR6+BulHw3KVLWbLO8aR9fA16h1+16eRr8ow/Dg6WAPjwPvZshivN6v8bVaExjL0/jJMcxnQWoZXh6uQPRdRhKxYzJZh3ZjbngABYgsErg0C1XP299lsPlvejfPsXSiXjTXog2OgvTXIwzWgwcM1uJ5LKLPfVuU0LMdvy+xx3liBO4UI1AMKgD2XMJln+dtptrlfLcZFCA9NOND+KuweHFwLDhJaq+iLCY/zIltky93cTl4EASXxyRC4W64WoQx3zen9wfT6NCUq19q6gr6U+Lh8G5Zj5urzBhFG4j6+BNWCvjaHS9CXYmGxet+kA0B/CgFMCw29bh1CX2Jc51k5D5vxYsXyKZQfGssgf4IfqRYeKsa6g2XYnsv47XE+zcoye7ubHuWp6RF6sAPQlxBjucrfjtcPs7KBDXh4FLC3CtciCGNtizHixasowsPmvoERqA7oAlRzGbZDROn2YZjeOHG/mq8W2TJvQuIQG+gMZ2BG9dQFbEpmjPNFUzgenoRvzm9a0kGrFmvgL/Saf5GV2WOxWjZF88ndt7iCwRZXQNmbLdyF6awM4/w+Wy8aa6BTnKmtH0D7BPrSQ8HTL/ZFY4sScI8SWlzJmRYlmL4HsL7PNptQhmJ8n83nDUIw+oAQ9uhAtfUUT09mjZP5alWMp4/lMmsSgtF4YgnQYoug/JOXcB/m84gKJVtB5WYPEgfa0t5xQAsSZJ4sqNebkC3eZY/zTXN6cxkUOthBX23tcVmEcrK6a85uT5BCh7pq4cnq6nqevQvjh7DcA747sXvsIMWWbFJ9oZ+VebYps98asxs8tXt//uj70uFktcmWszxrzq1OSWXfVpHhydi/CPl9tszK5uz6Iqwj/WSVIBmL43k2+VDOlpv75iLMZchHT2cA67C5D2/fttigsaeI33aoA0+2WNlzMd6sFpO9+U/hf4Tx/vzgn6wMZI/5fVhms3FefnjY44CGTgFBn7eXlesNg7sZa8fr8SaU5b44oJOMsG0z6hYjtJfoh+PFqszWTd3Q+hML6BBHT9YKwjrPHsJ4WrLJ+lgu71fzpvvC2FOyuc0aFLUPpO9SHjd3WVmEAz+OcafYQwdlAj5dKFUreJgtl1netCCNO8Uf+jCpvlDY3IfxMvz62CQKZ0/Zji3OgOrpLJKnX+WP8/356dTm+xxBX8GYP86jH2k1HXcZDMafZNNtdxI82Z8WuXNax2q5twR/ESY492QtpSizt5v7rFwtD2jCSnmRtPL+yUZ89tt/r+bjZSgfJ8354RR/bFlOTj4VH7anMJ/d3W+Ws+VdcxV4ETJ0oWVfx9Ld47LY82RYecqx256c3JP9ee9mK/bmMGduzk8njqBluPmnkkF2v3yb3YfNeDUvxm9ny7ti1TBfLZz057R1BqWerDNMV+VdiAS5emzqTVbhKZWhhYy6pbxSX95YzrI5r4CZZH6fLR5mTX+CNfIEMFpHYp56JA+zTQQDM4bm7KcQQrWpwdKTjcctVZZhOg/5Zh8M9qQK117Ik1n0ZrZ8G8pNyO/H6/vVe/a47pZBeMCjoLmMNmbaU6t4KFd5KB7LbD6u44Rt/CxXv4XluAxZft90ftNnXsaqvFttNmE55mhQw/Pr6FMD5JfRsAjT2XLGCLGOobp2xHjWHbXbzDZz/vCb7cjg/2rF8+bTuzrsW/D3iUO1vz5kdTgzn2fr9Szfjyj/tCoiIhWb8f2HSTljJlrMptNZ/jjffPh7Vs6y5SatNq5tuSoX2XzvR82na4bPT9n6bdzNPLwL85+zZYxMAo2U/KUCA7tWftx+4U97X87eZbN5NpnNZ/Hb1Z9R33wowzos+SR4R78P56u8+u8O0Gzuw4L39peHsOTI6rwYhF8fQjGrKHL9uFhkJU/xBxafg819uXq8ux8cfGk0KMI0ZJvBbLMebDFkUK7YPBoNsmUx4FMabO7DgD1WHwbZfPYuiOFoOF9lxWx591NYr2N0dvjXMjxk5Wx5dziJEPz7x2V+n5WbUHxXhuy/srj6/6d+NmjvkBHrrlw9Lhl+/5skWUg+3mkKZeer+aqMAxkgyOFouAxZmQKfU/4PtPLjaJgtJhxS/jPDYO+1jCbas14zW244Js6nIQXix9HwPixm64f7UIbta+u3H36o3yTnc2IXSlrc9rn0ciL94Ret/jiK/tfIKrN5eyUF5s4X++/BaLiaTtdhw6iFI5SjsfvlIxPEJrAkWscvPG5Wj5u/RtdqpLnh//oe/nfOYUgjDGN+/MtoOJmHZbH/S4y/jAONH2YFY9C7sP9bHX9bj21/Hhf0wEt5Pys29+yklwy+tEXFmuoDv7S8SwpBvlpuVo9l/OhPqAcozeAHsgMAGoDGAWo3QEkDcDB4o5QeOBhoaQeAdjga/qRogMoPfgDpB0BqAA4HaGmAWsbX3iitB94OlHcD54a/cPJAxokfEVrvZuvHbL6j9erv0XA6m4cKHdMzcTdnxHgs58PXw1fpCyntYpv18Uqp8Y6Rvdp7L78P+dv142L4eqhyHbDwwVPg5A7IOMsjKAjKAHqy0oSM9KQgpRDB5zKTTmUZaJr4aeYc0/Hst8CakienrR5FRWR5Nw8/MKUOX2uPEiTznXw1n88SM6y3uH00llLu73Rv6PINd73e2Lf009zmUgbMwE+nmnIoJrqYkA+T4CUYJSeFJ3A4Vd5NrA7gC6cyaeS0cFY29g3a29aurTHKsvRrbROObxOu2ya0t2ktkAOTZSorKLM48Y6M9ACZs2C9MVY6RVIVqAiNmgLISY45wlRikXvT3KZlb0vvbeLxbeJ128T2Np0Bg3pqaALOZgEmEzMp0BaZM65QuZMFOqMmhSNHuTdGZnqSmcxM86CkySaNbaJEumCb6vg21XXbVO1tThRkVk9Ro8llkRszdQCoEVE56ae+KEwWMm/kVFqTaeNCRvkUpJv4acizsHeadMk29fFt6uu2qdvbhAnoaeb1BCl3ekpSBt6KRE2apibTjrSSTmbTSV5MvJ9M8hCmE5t7yieozD5t4gXbNMe3aa7bpulAWvBeTzJyJptOXI5IOMXcQjYxmpT0IE1AyjLjC5oUk4CTKRT5xNhpluUQfGOb2ttLTtMe36a9bpu2vU2cTmGKucNCSVdYC9KaScjcdCpzNIoXDwABZdDewkTDRCmcknSoJ9aDbmzTkL5km+74Nt1123TtbRYTkzkpMcszlfssA5qCk8HKoPzUoso1gQm59cFa1MFMNbqQT3xRKOX9hBrbtFrqC7ZJx7dJ122TOgRKkU0Kq5SWhUVLE2szdFmWBYdSGjOZBphgEYLNvAEMGnNJdopTNSWrPTW3qaS5hDb98W3667bp29skI0kGnILLc0usA2WB8mmhKAtqkod84oxRAbR3xoapDEZq8pR5p5XLpk2BYgxeIDfhuBYE12lB0KEFeYkTRUVArSaTqQrZNBRBeyomU3TTibQKCg1eFlPlpgi5lLLQMGXVAGHidttU3pBs06azyvvObR7XguA6LQi6tCCkXNHEkM5tXlCGAQk9eG8okNOQyaktzGSaBQpWaydVbjn7uZAhBN0UKJ7gEi0IjmtBcJ0WBB1akMmtd56NZT+xNndOEoSJC84GnE40WAw6gNUyoJxKJVFhwFxPMum1dQ2BgqBUF6e1jnTnNo9rQXCdFgQdWpCHPLg8965wBq2ZoCzAW2WsNQCFl+TJoIdM5Xk2LShoZ6yc5jK3KsiparAgrUBDW3dnztV9nMfVILhODYIONciyPjuZaFYQlC2mE1TW2rzIMlJTkkCWILOeFPrC5BkG67Wy2dTiVJJ32e440akO4rROkenc5nE1CK5Tg6BDDZITRThRbI9YpCxMZK4zm0uVGQ2UhRyDchonoShwQhhkPvFTlRVukk3l1DXVIPDuAokCx9UguE4Ngg41iELIZeGDnErjvXVocEJK596aqWK1wYQJTOR0YmkSfI4ZepPUQm08qN02lVW2Y5tOe9lNnMfVILhODYIONchQbomCUQSTzOQYiDcqyWSFdXnQhLrwCjGfZkZrK3NrIASvtcYJaIc7pLWku1gtEXZv87gaBNepQdChBjkqiGzmC/Jg0Ml8okEHLVU2MSqnTAPK3E+mboo+R6Nt5q3BgIVVJoep2m7TSOl1W9vzDrBbcB5Xg+A6NQg61CBVFASQ68Jq5VloThypSZ6bSeE8SI9agVSZy6koKGM/yqQwWTEtMJdu6nfeIUVWdZyms1Kprm3icTUIr1ODsEMN0hOZFZnxRqLSLF2UdZkzaprlpjAmI0eZx1BIZbydkp3oQmkDhZtOpkiyYaI Line truncated
@@ -1 +1 @@
{"schemaVersion":1,"source":{"campaignManifest":"dungeon-pipeline/dungeon-campaign.json","environmentPackages":["src/game/generated/fivePlayerDungeonImports.json","src/game/generated/manastormAssetPackages.json (temporary fallback)"],"encounterCatalog":"src/game/generated/manastormRuntimeCatalog.json","azerothCoreCommit":"4d9d1d4b5723e819c5c390f6ca1177283dbfb8e3","azerothCoreSourceLock":"src/game/generated/azerothCoreSourceLock.json","spellCatalog":{"ascension":"../LadiksMPQEditor/client-current/area-52/patch-D/DBFilesClient/Spell.dbc","epoch":"../HealerMan-Storage/pipeline-work/dungeons/epoch-client/DBFilesClient/Spell.dbc"}},"coverage":[{"dungeonId":"shadowfang-keep","mapId":33,"encounters":7,"entities":31,"staticSpawns":109,"roamingPacks":24,"source":"azerothcore"},{"dungeonId":"stormwind-stockade","mapId":34,"encounters":5,"entities":11,"staticSpawns":93,"roamingPacks":0,"source":"azerothcore"},{"dungeonId":"deadmines","mapId":36,"encounters":7,"entities":22,"staticSpawns":179,"roamingPacks":23,"source":"azerothcore"},{"dungeonId":"wailing-caverns","mapId":43,"encounters":8,"entities":20,"staticSpawns":252,"roamingPacks":7,"source":"azerothcore"},{"dungeonId":"razorfen-kraul","mapId":47,"encounters":7,"entities":38,"staticSpawns":179,"roamingPacks":12,"source":"azerothcore"},{"dungeonId":"blackfathom-deeps","mapId":48,"encounters":6,"entities":27,"staticSpawns":201,"roamingPacks":10,"source":"azerothcore"},{"dungeonId":"uldaman","mapId":70,"encounters":7,"entities":38,"staticSpawns":302,"roamingPacks":12,"source":"azerothcore"},{"dungeonId":"gnomeregan","mapId":90,"encounters":4,"entities":31,"staticSpawns":371,"roamingPacks":31,"source":"azerothcore"},{"dungeonId":"sunken-temple","mapId":109,"encounters":4,"entities":33,"staticSpawns":245,"roamingPacks":4,"source":"azerothcore"},{"dungeonId":"razorfen-downs","mapId":129,"encounters":6,"entities":25,"staticSpawns":194,"roamingPacks":13,"source":"azerothcore"},{"dungeonId":"scarlet-monastery","mapId":189,"encounters":5,"entities":37,"staticSpawns":310,"roamingPacks":16,"source":"azerothcore"},{"dungeonId":"zulfarrak","mapId":209,"encounters":5,"entities":21,"staticSpawns":252,"roamingPacks":14,"source":"azerothcore"},{"dungeonId":"blackrock-spire","mapId":229,"encounters":15,"entities":78,"staticSpawns":566,"roamingPacks":22,"source":"azerothcore"},{"dungeonId":"blackrock-depths","mapId":230,"encounters":13,"entities":61,"staticSpawns":1040,"roamingPacks":15,"source":"azerothcore"},{"dungeonId":"scholomance","mapId":289,"encounters":8,"entities":38,"staticSpawns":327,"roamingPacks":15,"source":"azerothcore"},{"dungeonId":"stratholme","mapId":329,"encounters":9,"entities":50,"staticSpawns":431,"roamingPacks":29,"source":"azerothcore"},{"dungeonId":"maraudon","mapId":349,"encounters":8,"entities":33,"staticSpawns":525,"roamingPacks":20,"source":"azerothcore"},{"dungeonId":"ragefire-chasm","mapId":389,"encounters":4,"entities":11,"staticSpawns":127,"roamingPacks":4,"source":"azerothcore"},{"dungeonId":"dire-maul","mapId":429,"encounters":12,"entities":75,"staticSpawns":756,"roamingPacks":59,"source":"azerothcore"},{"dungeonId":"shattered-halls","mapId":540,"encounters":3,"entities":32,"staticSpawns":198,"roamingPacks":13,"source":"azerothcore"},{"dungeonId":"blood-furnace","mapId":542,"encounters":3,"entities":17,"staticSpawns":139,"roamingPacks":13,"source":"azerothcore"},{"dungeonId":"hellfire-ramparts","mapId":543,"encounters":2,"entities":12,"staticSpawns":85,"roamingPacks":12,"source":"azerothcore"},{"dungeonId":"steamvault","mapId":545,"encounters":3,"entities":17,"staticSpawns":127,"roamingPacks":17,"source":"azerothcore"},{"dungeonId":"underbog","mapId":546,"encounters":4,"entities":22,"staticSpawns":161,"roamingPacks":23,"source":"azerothcore"},{"dungeonId":"slave-pens","mapId":547,"encounters":3,"entities":21,"staticSpawns":122,"roamingPacks":32,"source":"azerothcore"},{"dungeonId":"arcatraz","mapId":552,"encounters":4,"entities":29,"staticSpawns":85,"roamingPacks":9,"source":"azerothcore"},{"dungeonId":"botanica","mapId":553,"encounters":5,"entities":29,"staticSpawns":171,"roamingPacks":13,"source":"azerothcore"},{"dungeonId":"mechanar","mapId":554,"encounters":3,"entities":17,"staticSpawns":84,"roamingPacks":14,"source":"azerothcore"},{"dungeonId":"shadow-labyrinth","mapId":555,"encounters":4,"entities":22,"staticSpawns":189,"roamingPacks":13,"source":"azerothcore"},{"dungeonId":"sethekk-halls","mapId":556,"encounters":2,"entities":16,"staticSpawns":104,"roamingPacks":24,"source":"azerothcore"},{"dungeonId":"mana-tombs","mapId":557,"encounters":3,"entities":14,"staticSpawns":119,"roamingPacks":15,"source":"azerothcore"},{"dungeonId":"auchenai-crypts","mapId":558,"encounters":2,"entities":14,"staticSpawns":93,"roamingPacks":37,"source":"azerothcore"},{"dungeonId":"magisters-terrace","mapId":585,"encounters":3,"entities":18,"staticSpawns":147,"roamingPacks":6,"source":"azerothcore"},{"dungeonId":"black-morass","mapId":269,"encounters":3,"entitiesLine truncated
{"schemaVersion":1,"source":{"campaignManifest":"dungeon-pipeline/dungeon-campaign.json","environmentPackages":["src/game/generated/fivePlayerDungeonImports.json","src/game/generated/manastormAssetPackages.json (temporary fallback)"],"encounterCatalog":"src/game/generated/manastormRuntimeCatalog.json","azerothCoreCommit":"4d9d1d4b5723e819c5c390f6ca1177283dbfb8e3","azerothCoreSourceLock":"src/game/generated/azerothCoreSourceLock.json","spellCatalog":{"ascension":"../LadiksMPQEditor/client-current/area-52/patch-D/DBFilesClient/Spell.dbc","epoch":"../HealerMan-Storage/pipeline-work/dungeons/epoch-client/DBFilesClient/Spell.dbc"}},"coverage":[{"dungeonId":"shadowfang-keep","mapId":33,"encounters":7,"entities":31,"staticSpawns":109,"roamingPacks":24,"source":"azerothcore"},{"dungeonId":"stormwind-stockade","mapId":34,"encounters":5,"entities":11,"staticSpawns":93,"roamingPacks":0,"source":"azerothcore"},{"dungeonId":"deadmines","mapId":36,"encounters":7,"entities":22,"staticSpawns":179,"roamingPacks":23,"source":"azerothcore"},{"dungeonId":"wailing-caverns","mapId":43,"encounters":8,"entities":20,"staticSpawns":252,"roamingPacks":7,"source":"azerothcore"},{"dungeonId":"razorfen-kraul","mapId":47,"encounters":7,"entities":38,"staticSpawns":179,"roamingPacks":12,"source":"azerothcore"},{"dungeonId":"blackfathom-deeps","mapId":48,"encounters":6,"entities":27,"staticSpawns":201,"roamingPacks":10,"source":"azerothcore"},{"dungeonId":"uldaman","mapId":70,"encounters":7,"entities":38,"staticSpawns":302,"roamingPacks":12,"source":"azerothcore"},{"dungeonId":"gnomeregan","mapId":90,"encounters":4,"entities":31,"staticSpawns":371,"roamingPacks":31,"source":"azerothcore"},{"dungeonId":"sunken-temple","mapId":109,"encounters":4,"entities":33,"staticSpawns":245,"roamingPacks":4,"source":"azerothcore"},{"dungeonId":"razorfen-downs","mapId":129,"encounters":6,"entities":25,"staticSpawns":194,"roamingPacks":13,"source":"azerothcore"},{"dungeonId":"scarlet-monastery","mapId":189,"encounters":5,"entities":37,"staticSpawns":310,"roamingPacks":16,"source":"azerothcore"},{"dungeonId":"zulfarrak","mapId":209,"encounters":5,"entities":21,"staticSpawns":252,"roamingPacks":14,"source":"azerothcore"},{"dungeonId":"blackrock-spire","mapId":229,"encounters":15,"entities":78,"staticSpawns":566,"roamingPacks":22,"source":"azerothcore"},{"dungeonId":"blackrock-depths","mapId":230,"encounters":13,"entities":61,"staticSpawns":1040,"roamingPacks":15,"source":"azerothcore"},{"dungeonId":"scholomance","mapId":289,"encounters":8,"entities":38,"staticSpawns":327,"roamingPacks":15,"source":"azerothcore"},{"dungeonId":"stratholme","mapId":329,"encounters":9,"entities":50,"staticSpawns":431,"roamingPacks":29,"source":"azerothcore"},{"dungeonId":"maraudon","mapId":349,"encounters":8,"entities":33,"staticSpawns":525,"roamingPacks":20,"source":"azerothcore"},{"dungeonId":"ragefire-chasm","mapId":389,"encounters":4,"entities":11,"staticSpawns":127,"roamingPacks":4,"source":"azerothcore"},{"dungeonId":"dire-maul","mapId":429,"encounters":12,"entities":75,"staticSpawns":756,"roamingPacks":59,"source":"azerothcore"},{"dungeonId":"shattered-halls","mapId":540,"encounters":3,"entities":32,"staticSpawns":198,"roamingPacks":13,"source":"azerothcore"},{"dungeonId":"blood-furnace","mapId":542,"encounters":3,"entities":17,"staticSpawns":139,"roamingPacks":13,"source":"azerothcore"},{"dungeonId":"hellfire-ramparts","mapId":543,"encounters":2,"entities":12,"staticSpawns":85,"roamingPacks":12,"source":"azerothcore"},{"dungeonId":"steamvault","mapId":545,"encounters":3,"entities":17,"staticSpawns":127,"roamingPacks":17,"source":"azerothcore"},{"dungeonId":"underbog","mapId":546,"encounters":4,"entities":22,"staticSpawns":161,"roamingPacks":23,"source":"azerothcore"},{"dungeonId":"slave-pens","mapId":547,"encounters":3,"entities":21,"staticSpawns":122,"roamingPacks":32,"source":"azerothcore"},{"dungeonId":"arcatraz","mapId":552,"encounters":4,"entities":29,"staticSpawns":85,"roamingPacks":9,"source":"azerothcore"},{"dungeonId":"botanica","mapId":553,"encounters":5,"entities":29,"staticSpawns":171,"roamingPacks":13,"source":"azerothcore"},{"dungeonId":"mechanar","mapId":554,"encounters":3,"entities":17,"staticSpawns":84,"roamingPacks":14,"source":"azerothcore"},{"dungeonId":"shadow-labyrinth","mapId":555,"encounters":4,"entities":22,"staticSpawns":189,"roamingPacks":13,"source":"azerothcore"},{"dungeonId":"sethekk-halls","mapId":556,"encounters":2,"entities":16,"staticSpawns":104,"roamingPacks":24,"source":"azerothcore"},{"dungeonId":"mana-tombs","mapId":557,"encounters":3,"entities":14,"staticSpawns":119,"roamingPacks":15,"source":"azerothcore"},{"dungeonId":"auchenai-crypts","mapId":558,"encounters":2,"entities":14,"staticSpawns":93,"roamingPacks":37,"source":"azerothcore"},{"dungeonId":"magisters-terrace","mapId":585,"encounters":3,"entities":18,"staticSpawns":147,"roamingPacks":6,"source":"azerothcore"},{"dungeonId":"black-morass","mapId":269,"encounters":3,"entitiesLine truncated
@@ -16,7 +16,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -56,7 +56,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -96,7 +96,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -136,7 +136,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -196,7 +196,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -236,7 +236,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Corrosive Slam",
"delivery": "melee",
"target": "primary",
@@ -296,7 +296,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -336,7 +336,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -376,7 +376,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -416,7 +416,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -476,7 +476,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -536,7 +536,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -576,7 +576,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -636,7 +636,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -676,7 +676,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -716,7 +716,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -756,7 +756,7 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-dungeon-of-dalanis-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1567,6 +1567,6 @@ export const DUNGEON_OF_DALANIS_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -1,4 +1,4 @@
/* Generated by scripts/runewaker-pipeline/build-population.mjs. */
/* This file is generated by scripts/runewaker-pipeline/build-forsaken-population.mjs. */
import type { DungeonBossObjective } from "../dungeonTypes";
import type { PopulationDefinitionMap, StaticMobSpawnDefinition } from "../mobPopulation";
@@ -16,7 +16,7 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Corrosive Slam",
"delivery": "melee",
"target": "primary",
@@ -56,7 +56,7 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -96,7 +96,7 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -136,7 +136,7 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -175,9 +175,12 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-fa-100070-source-gap-burst"
],
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -186,6 +189,33 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-fa-100070-source-gap-burst",
"name": "Arcane Burst",
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 2000,
"range": 12,
"cooldownMs": 12000,
"initialCooldownMs": [
8000,
8000
],
"repeatCooldownMs": [
12000,
12000
],
"damageMultiplier": 0.82,
"radius": 8,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 2000,
"radius": 8
}
}
]
},
@@ -217,7 +247,7 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -257,7 +287,7 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -297,7 +327,7 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Corrosive Slam",
"delivery": "melee",
"target": "primary",
@@ -336,9 +366,12 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-fa-100561-source-gap-sweep"
],
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Corrosive Slam",
"delivery": "melee",
"target": "primary",
@@ -347,6 +380,33 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-fa-100561-source-gap-sweep",
"name": "Corrosive Sweep",
"delivery": "area",
"target": "nearby-party",
"school": "nature",
"animation": "attack",
"castTimeMs": 1800,
"range": 7,
"cooldownMs": 10000,
"initialCooldownMs": [
7000,
7000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 1.1,
"telegraph": {
"shape": "cone",
"anchor": "source",
"warningMs": 1800,
"length": 7,
"angleDegrees": 100
}
}
]
},
@@ -378,7 +438,7 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -417,9 +477,12 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-fa-100562-source-gap-sweep"
],
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -428,6 +491,33 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-fa-100562-source-gap-sweep",
"name": "Heavy Sweep",
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"castTimeMs": 1800,
"range": 7,
"cooldownMs": 10000,
"initialCooldownMs": [
7000,
7000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 1.1,
"telegraph": {
"shape": "cone",
"anchor": "source",
"warningMs": 1800,
"length": 7,
"angleDegrees": 100
}
}
]
},
@@ -459,7 +549,7 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -498,9 +588,12 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-fa-100563-source-gap-sweep"
],
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -509,6 +602,33 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-fa-100563-source-gap-sweep",
"name": "Heavy Sweep",
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"castTimeMs": 1800,
"range": 7,
"cooldownMs": 10000,
"initialCooldownMs": [
7000,
7000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 1.1,
"telegraph": {
"shape": "cone",
"anchor": "source",
"warningMs": 1800,
"length": 7,
"angleDegrees": 100
}
}
]
},
@@ -539,9 +659,18 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-fa-100074-spell-490471",
"rw-fa-100074-spell-490489",
"rw-fa-100074-control-490636",
"rw-fa-100074-summon-100070-at-50",
"rw-fa-100074-summon-100070-at-25",
"rw-fa-100074-phase-50",
"rw-fa-100074-phase-25"
],
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -550,6 +679,215 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-fa-100074-spell-490471",
"name": "Fireball",
"spellId": 490471,
"delivery": "projectile",
"target": "primary",
"school": "fire",
"animation": "cast",
"castTimeMs": 2000,
"range": 22.5,
"cooldownMs": 3000,
"initialCooldownMs": [
3000,
3000
],
"repeatCooldownMs": [
3000,
3000
],
"damageMultiplier": 0.85
},
{
"id": "rw-fa-100074-spell-490489",
"name": "Dark Power Release",
"spellId": 490489,
"delivery": "area",
"target": "nearby-party",
"school": "fire",
"animation": "cast",
"castTimeMs": 5000,
"range": 15,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-fa-100074-control-490636",
"name": "Group Catch",
"delivery": "area",
"target": "nearby-party",
"school": "shadow",
"animation": "cast",
"castTimeMs": 1800,
"range": 10,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
},
"effect": {
"kind": "control",
"mechanic": "root",
"durationMs": 3000
}
},
{
"id": "rw-fa-100074-summon-100070-at-50",
"name": "Reinforcements at 50%",
"delivery": "area",
"target": "self",
"school": "physical",
"animation": "cast",
"castTimeMs": 1800,
"interruptible": false,
"range": 0,
"cooldownMs": 1000,
"initialCooldownMs": [
0,
0
],
"repeatCooldownMs": [
1000,
1000
],
"maximumCasts": 1,
"damageMultiplier": 0,
"effect": {
"kind": "summon",
"creatureEntry": 100070,
"count": 2
},
"condition": {
"kind": "self-health-percent",
"minimum": 0,
"maximum": 50
}
},
{
"id": "rw-fa-100074-summon-100070-at-25",
"name": "Reinforcements at 25%",
"delivery": "area",
"target": "self",
"school": "physical",
"animation": "cast",
"castTimeMs": 1800,
"interruptible": false,
"range": 0,
"cooldownMs": 1000,
"initialCooldownMs": [
0,
0
],
"repeatCooldownMs": [
1000,
1000
],
"maximumCasts": 1,
"damageMultiplier": 0,
"effect": {
"kind": "summon",
"creatureEntry": 100070,
"count": 2
},
"condition": {
"kind": "self-health-percent",
"minimum": 0,
"maximum": 25
}
},
{
"id": "rw-fa-100074-phase-50",
"name": "Phase Change",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"interruptible": false,
"range": 0,
"cooldownMs": 1000,
"initialCooldownMs": [
0,
0
],
"repeatCooldownMs": [
1000,
1000
],
"maximumCasts": 1,
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "phase-50",
"durationMs": 5000
},
"condition": {
"kind": "self-health-percent",
"minimum": 0,
"maximum": 50
}
},
{
"id": "rw-fa-100074-phase-25",
"name": "Phase Change",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"interruptible": false,
"range": 0,
"cooldownMs": 1000,
"initialCooldownMs": [
0,
0
],
"repeatCooldownMs": [
1000,
1000
],
"maximumCasts": 1,
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "phase-25",
"durationMs": 5000
},
"condition": {
"kind": "self-health-percent",
"minimum": 0,
"maximum": 25
}
}
]
},
@@ -581,7 +919,7 @@ export const FORSAKEN_ABBEY_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-fa-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -2341,20 +2679,13 @@ export const FORSAKEN_ABBEY_BOSSES = [
export const FORSAKEN_ABBEY_OBJECTIVE_ORDER = FORSAKEN_ABBEY_BOSSES;
export const FORSAKEN_ABBEY_POPULATION_PROVENANCE = {
"schemaVersion": 2,
"schemaVersion": 1,
"dungeonId": "forsaken-abbey",
"zoneId": 102,
"sourceRowCount": 251,
"importedSpawnCount": 150,
"excludedRowCount": 101,
"bossSpawnCount": 8,
"objectiveOrder": {
"authoritative": false,
"kind": "healerMan-distance-derived",
"evidence": [
"scripts/runewaker-pipeline/build-population.mjs"
]
},
"sourceSnapshot": "scripts/runewaker-pipeline/snapshots/forsaken-abbey-zone-102.json",
"sourceContentSha256": "13ef6edd3df6e71389aa92d01201889148c0794cf9ecaafdbaf90d3145d2bf7b",
"sourceHashes": {
@@ -2368,7 +2699,7 @@ export const FORSAKEN_ABBEY_POPULATION_PROVENANCE = {
"Resource/data/string_enus.db": "4fce021ba9ed110a213e08d3228b2de57cec66ddbbc8cdcf5753204874d9ab75"
}
},
"coordinateTransform": "threeLocal=(inverse(WDB placement)*server)*(meters,meters,-meters)",
"coordinateTransform": "threeLocal=(inverse(WDB placement)*server)*(0.1,0.1,-0.1)",
"navmeshProjection": {
"maximumAllowedDistance": 2,
"maximumObservedDistance": 2.5965,
@@ -2376,8 +2707,5 @@ export const FORSAKEN_ABBEY_POPULATION_PROVENANCE = {
"deferredRowsBeyondCombatLimit": 1
},
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry."
} as const;
@@ -16,7 +16,7 @@ export const GRAFU_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-grafu-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -56,7 +56,7 @@ export const GRAFU_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-grafu-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -96,7 +96,7 @@ export const GRAFU_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-grafu-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -176,7 +176,7 @@ export const GRAFU_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-grafu-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -256,7 +256,7 @@ export const GRAFU_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-grafu-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -336,7 +336,7 @@ export const GRAFU_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-grafu-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -416,7 +416,7 @@ export const GRAFU_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-grafu-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -456,7 +456,7 @@ export const GRAFU_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-grafu-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -536,7 +536,7 @@ export const GRAFU_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-grafu-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -576,7 +576,7 @@ export const GRAFU_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-grafu-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -616,7 +616,7 @@ export const GRAFU_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-grafu-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1306,6 +1306,6 @@ export const GRAFU_CASTLE_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
File diff suppressed because it is too large. Load diff
+618 -50
View File
@@ -16,7 +16,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -76,7 +76,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -156,7 +156,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -196,7 +196,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -256,7 +256,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -336,7 +336,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -376,7 +376,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -456,7 +456,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -536,7 +536,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -596,7 +596,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -656,7 +656,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -756,7 +756,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -816,7 +816,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -856,7 +856,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -936,7 +936,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -976,7 +976,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1016,7 +1016,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1055,9 +1055,15 @@ export const HOTO_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-hoto-102021-aura-492669",
"rw-hoto-102021-spell-493215",
"rw-hoto-102021-spell-493307",
"rw-hoto-102021-aura-493308"
],
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1068,13 +1074,67 @@ export const HOTO_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-hoto-spell-493307",
"id": "rw-hoto-102021-aura-492669",
"name": "Blue Explosion",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "blue-explosion",
"durationMs": 7500
}
},
{
"id": "rw-hoto-102021-spell-493215",
"name": "Water Gulp",
"spellId": 493215,
"delivery": "area",
"target": "nearby-party",
"school": "frost",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-hoto-102021-spell-493307",
"name": "Water Arrow",
"spellId": 493307,
"delivery": "projectile",
"target": "primary",
"school": "frost",
"animation": "cast",
"castTimeMs": 0,
"range": 30,
"cooldownMs": 10000,
"initialCooldownMs": [
@@ -1086,6 +1146,31 @@ export const HOTO_ENTITIES = {
10000
],
"damageMultiplier": 0.85
},
{
"id": "rw-hoto-102021-aura-493308",
"name": "Water Arrow Volley",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 25000,
"initialCooldownMs": [
25000,
25000
],
"repeatCooldownMs": [
25000,
25000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "water-arrow-volley",
"durationMs": 12000
}
}
]
},
@@ -1116,9 +1201,13 @@ export const HOTO_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-hoto-102046-spell-493237",
"rw-hoto-102046-spell-493239"
],
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1129,13 +1218,42 @@ export const HOTO_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-hoto-spell-493239",
"id": "rw-hoto-102046-spell-493237",
"name": "Splinter Magic",
"spellId": 493237,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"castTimeMs": 2000,
"range": 15,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-hoto-102046-spell-493239",
"name": "Giant Claw Attack",
"spellId": 493239,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 10000,
"initialCooldownMs": [
@@ -1147,26 +1265,6 @@ export const HOTO_ENTITIES = {
10000
],
"damageMultiplier": 0.85
},
{
"id": "rw-hoto-spell-493237",
"name": "Splinter Magic",
"spellId": 493237,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"range": 5,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0.85
}
]
},
@@ -1197,9 +1295,22 @@ export const HOTO_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-hoto-102040-aura-493207",
"rw-hoto-102040-aura-493208",
"rw-hoto-102040-debuff-493209",
"rw-hoto-102040-spell-493210",
"rw-hoto-102040-aura-493211",
"rw-hoto-102040-aura-493212",
"rw-hoto-102040-debuff-493305",
"rw-hoto-102040-aura-493525",
"rw-hoto-102040-aura-493578",
"rw-hoto-102040-spell-493580",
"rw-hoto-102040-aura-493595"
],
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1208,6 +1319,280 @@ export const HOTO_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-hoto-102040-aura-493207",
"name": "Manastorms Duo Lv5",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 45000,
"initialCooldownMs": [
45000,
45000
],
"repeatCooldownMs": [
45000,
45000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "manastorms-duo-lv5",
"durationMs": 12000
}
},
{
"id": "rw-hoto-102040-aura-493208",
"name": "Snake Lady's Seduction",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 18000,
"initialCooldownMs": [
18000,
18000
],
"repeatCooldownMs": [
18000,
18000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "petrify",
"durationMs": 9000
}
},
{
"id": "rw-hoto-102040-debuff-493209",
"name": "Rock Binding Magic",
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 18000,
"initialCooldownMs": [
18000,
18000
],
"repeatCooldownMs": [
18000,
18000
],
"damageMultiplier": 0,
"effect": {
"kind": "target-aura",
"aura": "rock-bind",
"durationMs": 9000
}
},
{
"id": "rw-hoto-102040-spell-493210",
"name": "Rock Shattering Wave",
"spellId": 493210,
"delivery": "area",
"target": "nearby-party",
"school": "nature",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 13000,
"initialCooldownMs": [
13000,
13000
],
"repeatCooldownMs": [
13000,
13000
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-hoto-102040-aura-493211",
"name": "Manastorms Duo Lv5",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 45000,
"initialCooldownMs": [
45000,
45000
],
"repeatCooldownMs": [
45000,
45000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "fortification",
"durationMs": 12000
}
},
{
"id": "rw-hoto-102040-aura-493212",
"name": "Summon Sound Shell",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 40000,
"initialCooldownMs": [
40000,
40000
],
"repeatCooldownMs": [
40000,
40000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "summon-sound-shell",
"durationMs": 12000
}
},
{
"id": "rw-hoto-102040-debuff-493305",
"name": "Magic Fortress",
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 30000,
"initialCooldownMs": [
30000,
30000
],
"repeatCooldownMs": [
30000,
30000
],
"damageMultiplier": 0,
"effect": {
"kind": "target-aura",
"aura": "magic-fortress",
"durationMs": 10000
}
},
{
"id": "rw-hoto-102040-aura-493525",
"name": "Manastorms Duo Lv5",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 10000,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "manastorms-duo-lv5",
"durationMs": 7500
}
},
{
"id": "rw-hoto-102040-aura-493578",
"name": "Fatal Snake Woman Scent",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 25000,
"initialCooldownMs": [
25000,
25000
],
"repeatCooldownMs": [
25000,
25000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "fatal-snake-woman-scent",
"durationMs": 12000
}
},
{
"id": "rw-hoto-102040-spell-493580",
"name": "Snake Staff Attack",
"spellId": 493580,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 10000,
"initialCooldownMs": [
10000,
10000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 0.85
},
{
"id": "rw-hoto-102040-aura-493595",
"name": "Manastorms Duo Lv5",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 35000,
"initialCooldownMs": [
35000,
35000
],
"repeatCooldownMs": [
35000,
35000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "manastorms-duo-lv5",
"durationMs": 12000
}
}
]
},
@@ -1239,7 +1624,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1279,7 +1664,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1358,9 +1743,14 @@ export const HOTO_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-hoto-102041-spell-493231",
"rw-hoto-102041-spell-493232",
"rw-hoto-102041-aura-493233"
],
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1371,13 +1761,14 @@ export const HOTO_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-hoto-spell-493231",
"id": "rw-hoto-102041-spell-493231",
"name": "Big Ripper",
"spellId": 493231,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 10000,
"initialCooldownMs": [
@@ -1389,6 +1780,52 @@ export const HOTO_ENTITIES = {
10000
],
"damageMultiplier": 0.85
},
{
"id": "rw-hoto-102041-spell-493232",
"name": "Scaly Surprise",
"spellId": 493232,
"delivery": "projectile",
"target": "primary",
"school": "physical",
"animation": "cast",
"castTimeMs": 0,
"range": 30,
"cooldownMs": 10000,
"initialCooldownMs": [
10000,
10000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 0.85
},
{
"id": "rw-hoto-102041-aura-493233",
"name": "Disperse Poison",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 2000,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "disperse-poison",
"durationMs": 7500
}
}
]
},
@@ -1419,9 +1856,16 @@ export const HOTO_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-hoto-102063-aura-493187",
"rw-hoto-102063-aura-493188",
"rw-hoto-102063-spell-493189",
"rw-hoto-102063-spell-493221",
"rw-hoto-102063-aura-493492"
],
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1430,6 +1874,130 @@ export const HOTO_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-hoto-102063-aura-493187",
"name": "Geba's Rage",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1500,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "geba-s-rage",
"durationMs": 7500
}
},
{
"id": "rw-hoto-102063-aura-493188",
"name": "Sonic Roar",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "sonic-roar",
"durationMs": 7500
}
},
{
"id": "rw-hoto-102063-spell-493189",
"name": "Geba's Fury",
"spellId": 493189,
"delivery": "projectile",
"target": "primary",
"school": "physical",
"animation": "cast",
"castTimeMs": 0,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
},
{
"id": "rw-hoto-102063-spell-493221",
"name": "Whirl Counterattack",
"spellId": 493221,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"castTimeMs": 1800,
"range": 5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
}
},
{
"id": "rw-hoto-102063-aura-493492",
"name": "Geba's Magic",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 2000,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "geba-s-magic",
"durationMs": 7500
}
}
]
},
@@ -1461,7 +2029,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1541,7 +2109,7 @@ export const HOTO_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-hoto-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -5078,6 +5646,6 @@ export const HOTO_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -96,7 +96,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -136,7 +136,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -196,7 +196,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -256,7 +256,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -316,7 +316,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -356,7 +356,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -436,7 +436,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -496,7 +496,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -556,7 +556,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -596,7 +596,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -676,7 +676,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -756,7 +756,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -836,7 +836,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -876,7 +876,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -916,7 +916,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -956,7 +956,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -996,7 +996,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1036,7 +1036,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1115,9 +1115,12 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-ice-dwarf-kingdom-101881-debuff-492657"
],
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1126,6 +1129,31 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-ice-dwarf-kingdom-101881-debuff-492657",
"name": "Conductivity",
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 6000,
"initialCooldownMs": [
6000,
6000
],
"repeatCooldownMs": [
6000,
6000
],
"damageMultiplier": 0,
"effect": {
"kind": "target-aura",
"aura": "conductivity",
"durationMs": 4000
}
}
]
},
@@ -1156,9 +1184,17 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-ice-dwarf-kingdom-101883-debuff-492533",
"rw-ice-dwarf-kingdom-101883-spell-492587",
"rw-ice-dwarf-kingdom-101883-spell-492601",
"rw-ice-dwarf-kingdom-101883-debuff-492602",
"rw-ice-dwarf-kingdom-101883-debuff-493085",
"rw-ice-dwarf-kingdom-101883-debuff-493241"
],
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1167,6 +1203,155 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-ice-dwarf-kingdom-101883-debuff-492533",
"name": "Runic Decline",
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 20,
"cooldownMs": 12000,
"initialCooldownMs": [
12000,
12000
],
"repeatCooldownMs": [
12000,
12000
],
"damageMultiplier": 0,
"radius": 3,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 3
},
"effect": {
"kind": "target-aura",
"aura": "runic-decline",
"durationMs": 6000
}
},
{
"id": "rw-ice-dwarf-kingdom-101883-spell-492587",
"name": "Energy Burn",
"spellId": 492587,
"delivery": "melee",
"target": "primary",
"school": "shadow",
"animation": "cast",
"castTimeMs": 0,
"range": 4.5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
},
{
"id": "rw-ice-dwarf-kingdom-101883-spell-492601",
"name": "Triple Slash",
"spellId": 492601,
"delivery": "projectile",
"target": "primary",
"school": "shadow",
"animation": "cast",
"castTimeMs": 2000,
"range": 7.5,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85
},
{
"id": "rw-ice-dwarf-kingdom-101883-debuff-492602",
"name": "Slipping Ice Block",
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 25,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0,
"effect": {
"kind": "target-aura",
"aura": "slipping-ice-block",
"durationMs": 10000
}
},
{
"id": "rw-ice-dwarf-kingdom-101883-debuff-493085",
"name": "RuneWaker Spell 493085",
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 20,
"cooldownMs": 12000,
"initialCooldownMs": [
12000,
12000
],
"repeatCooldownMs": [
12000,
12000
],
"damageMultiplier": 0,
"effect": {
"kind": "target-aura",
"aura": "repair",
"durationMs": 6000
}
},
{
"id": "rw-ice-dwarf-kingdom-101883-debuff-493241",
"name": "Manastorms Duo Lv5",
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "target-aura",
"aura": "food-preservative",
"durationMs": 7500
}
}
]
},
@@ -1197,9 +1382,13 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-ice-dwarf-kingdom-101884-spell-492759",
"rw-ice-dwarf-kingdom-101884-spell-492760"
],
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1210,13 +1399,14 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-ice-dwarf-kingdom-spell-492759",
"id": "rw-ice-dwarf-kingdom-101884-spell-492759",
"name": "Raging Flames",
"spellId": 492759,
"delivery": "melee",
"target": "primary",
"delivery": "area",
"target": "nearby-party",
"school": "fire",
"animation": "cast",
"castTimeMs": 1800,
"range": 5,
"cooldownMs": 15000,
"initialCooldownMs": [
@@ -1227,16 +1417,24 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
15000,
15000
],
"damageMultiplier": 0.85
"damageMultiplier": 0.85,
"radius": 3,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 3
}
},
{
"id": "rw-ice-dwarf-kingdom-spell-492760",
"id": "rw-ice-dwarf-kingdom-101884-spell-492760",
"name": "Bash",
"spellId": 492760,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 6500,
"initialCooldownMs": [
@@ -1278,9 +1476,14 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-ice-dwarf-kingdom-101268-spell-492821",
"rw-ice-dwarf-kingdom-101268-debuff-492822",
"rw-ice-dwarf-kingdom-101268-spell-492841"
],
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1291,34 +1494,15 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-ice-dwarf-kingdom-spell-492841",
"name": "Boulder Split",
"spellId": 492841,
"delivery": "projectile",
"target": "primary",
"school": "nature",
"animation": "cast",
"range": 10,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85
},
{
"id": "rw-ice-dwarf-kingdom-spell-492821",
"id": "rw-ice-dwarf-kingdom-101268-spell-492821",
"name": "Terrain Clear",
"spellId": 492821,
"delivery": "projectile",
"target": "primary",
"delivery": "area",
"target": "nearby-party",
"school": "nature",
"animation": "cast",
"range": 6,
"castTimeMs": 5000,
"range": 14,
"cooldownMs": 3000,
"initialCooldownMs": [
3000,
@@ -1328,7 +1512,74 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
3000,
3000
],
"damageMultiplier": 0.85
"damageMultiplier": 0.85,
"radius": 14,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 14
}
},
{
"id": "rw-ice-dwarf-kingdom-101268-debuff-492822",
"name": "Dust",
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 6000,
"initialCooldownMs": [
6000,
6000
],
"repeatCooldownMs": [
6000,
6000
],
"damageMultiplier": 0,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
},
"effect": {
"kind": "target-aura",
"aura": "dust",
"durationMs": 4000
}
},
{
"id": "rw-ice-dwarf-kingdom-101268-spell-492841",
"name": "Boulder Split",
"spellId": 492841,
"delivery": "area",
"target": "nearby-party",
"school": "nature",
"animation": "cast",
"castTimeMs": 1800,
"range": 14,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85,
"radius": 14,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 14
}
}
]
},
@@ -1359,9 +1610,15 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-ice-dwarf-kingdom-101584-spell-492847",
"rw-ice-dwarf-kingdom-101584-debuff-492848",
"rw-ice-dwarf-kingdom-101584-aura-492850",
"rw-ice-dwarf-kingdom-101584-debuff-492853"
],
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1370,6 +1627,123 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-ice-dwarf-kingdom-101584-spell-492847",
"name": "Ancient Fire",
"spellId": 492847,
"delivery": "area",
"target": "nearby-party",
"school": "fire",
"animation": "cast",
"castTimeMs": 1800,
"range": 15,
"cooldownMs": 10000,
"initialCooldownMs": [
10000,
10000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-ice-dwarf-kingdom-101584-debuff-492848",
"name": "Fire Chaos",
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 12000,
"initialCooldownMs": [
12000,
12000
],
"repeatCooldownMs": [
12000,
12000
],
"damageMultiplier": 0,
"radius": 7,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 7
},
"effect": {
"kind": "target-aura",
"aura": "fire-chaos",
"durationMs": 6000
}
},
{
"id": "rw-ice-dwarf-kingdom-101584-aura-492850",
"name": "Hellish Rage",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "hellish-rage",
"durationMs": 10000
}
},
{
"id": "rw-ice-dwarf-kingdom-101584-debuff-492853",
"name": "RuneWaker Spell 492853",
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 12000,
"initialCooldownMs": [
12000,
12000
],
"repeatCooldownMs": [
12000,
12000
],
"damageMultiplier": 0,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
},
"effect": {
"kind": "target-aura",
"aura": "manastorms-duo-lv5",
"durationMs": 6000
}
}
]
},
@@ -1401,7 +1775,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1501,7 +1875,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1581,7 +1955,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1681,7 +2055,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1741,7 +2115,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1801,7 +2175,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1881,7 +2255,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1961,7 +2335,7 @@ export const ICE_DWARF_KINGDOM_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-ice-dwarf-kingdom-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -8690,6 +9064,6 @@ export const ICE_DWARF_KINGDOM_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const KAL_TUROK_HIVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kal-turok-hive-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -181,6 +181,6 @@ export const KAL_TUROK_HIVE_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const KALINS_SHRINE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -56,7 +56,7 @@ export const KALINS_SHRINE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -96,7 +96,7 @@ export const KALINS_SHRINE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -156,7 +156,7 @@ export const KALINS_SHRINE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -216,7 +216,7 @@ export const KALINS_SHRINE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -276,7 +276,7 @@ export const KALINS_SHRINE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -356,7 +356,7 @@ export const KALINS_SHRINE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -436,7 +436,7 @@ export const KALINS_SHRINE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -516,7 +516,7 @@ export const KALINS_SHRINE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -595,9 +595,15 @@ export const KALINS_SHRINE_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-kalins-shrine-101507-spell-492371",
"rw-kalins-shrine-101507-spell-492372",
"rw-kalins-shrine-101507-spell-492373",
"rw-kalins-shrine-101507-aura-492374"
],
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -608,13 +614,42 @@ export const KALINS_SHRINE_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-kalins-shrine-spell-492372",
"id": "rw-kalins-shrine-101507-spell-492371",
"name": "Power Build-Up Strike",
"spellId": 492371,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"castTimeMs": 4000,
"range": 5,
"cooldownMs": 8000,
"initialCooldownMs": [
8000,
8000
],
"repeatCooldownMs": [
8000,
8000
],
"damageMultiplier": 0.85,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
}
},
{
"id": "rw-kalins-shrine-101507-spell-492372",
"name": "Earth Shattering Attack",
"spellId": 492372,
"delivery": "projectile",
"target": "primary",
"school": "physical",
"animation": "cast",
"castTimeMs": 2500,
"range": 25,
"cooldownMs": 20000,
"initialCooldownMs": [
@@ -628,13 +663,14 @@ export const KALINS_SHRINE_ENTITIES = {
"damageMultiplier": 0.85
},
{
"id": "rw-kalins-shrine-spell-492373",
"id": "rw-kalins-shrine-101507-spell-492373",
"name": "Hit on the Head",
"spellId": 492373,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 20000,
"initialCooldownMs": [
@@ -648,24 +684,29 @@ export const KALINS_SHRINE_ENTITIES = {
"damageMultiplier": 0.85
},
{
"id": "rw-kalins-shrine-spell-492371",
"name": "Power Build-Up Strike",
"spellId": 492371,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"range": 5,
"cooldownMs": 8000,
"id": "rw-kalins-shrine-101507-aura-492374",
"name": "Steel Body Protection",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 45000,
"initialCooldownMs": [
8000,
8000
45000,
45000
],
"repeatCooldownMs": [
8000,
8000
45000,
45000
],
"damageMultiplier": 0.85
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "steel-reinforcement",
"durationMs": 12000
}
}
]
},
@@ -696,9 +737,16 @@ export const KALINS_SHRINE_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-kalins-shrine-101502-spell-492375",
"rw-kalins-shrine-101502-debuff-492376",
"rw-kalins-shrine-101502-debuff-492377",
"rw-kalins-shrine-101502-aura-492536",
"rw-kalins-shrine-101502-debuff-492778"
],
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -709,13 +757,14 @@ export const KALINS_SHRINE_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-kalins-shrine-spell-492375",
"id": "rw-kalins-shrine-101502-spell-492375",
"name": "Icy Water Cannon",
"spellId": 492375,
"delivery": "projectile",
"target": "primary",
"school": "frost",
"animation": "cast",
"castTimeMs": 2000,
"range": 20,
"cooldownMs": 5000,
"initialCooldownMs": [
@@ -727,6 +776,113 @@ export const KALINS_SHRINE_ENTITIES = {
5000
],
"damageMultiplier": 0.85
},
{
"id": "rw-kalins-shrine-101502-debuff-492376",
"name": "Spray Mucus",
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 2000,
"range": 20,
"cooldownMs": 6000,
"initialCooldownMs": [
6000,
6000
],
"repeatCooldownMs": [
6000,
6000
],
"damageMultiplier": 0,
"effect": {
"kind": "target-aura",
"aura": "sticky",
"durationMs": 4000
}
},
{
"id": "rw-kalins-shrine-101502-debuff-492377",
"name": "Strange Egg Parasites",
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0,
"effect": {
"kind": "target-aura",
"aura": "strange-egg-parasites",
"durationMs": 10000
}
},
{
"id": "rw-kalins-shrine-101502-aura-492536",
"name": "Strange Egg Parasites",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "strange-egg-parasites",
"durationMs": 7500
}
},
{
"id": "rw-kalins-shrine-101502-debuff-492778",
"name": "Strange Egg Parasites",
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 10000,
"initialCooldownMs": [
10000,
10000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 0,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
},
"effect": {
"kind": "target-aura",
"aura": "strange-egg-parasites",
"durationMs": 5000
}
}
]
},
@@ -757,9 +913,16 @@ export const KALINS_SHRINE_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-kalins-shrine-101503-aura-490656",
"rw-kalins-shrine-101503-aura-492569",
"rw-kalins-shrine-101503-aura-492570",
"rw-kalins-shrine-101503-aura-492571",
"rw-kalins-shrine-101503-control-492682"
],
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -768,6 +931,138 @@ export const KALINS_SHRINE_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-kalins-shrine-101503-aura-490656",
"name": "Wave Armor",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 45000,
"initialCooldownMs": [
45000,
45000
],
"repeatCooldownMs": [
45000,
45000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "wave-armor",
"durationMs": 12000
}
},
{
"id": "rw-kalins-shrine-101503-aura-492569",
"name": "Kiss of Blood Clot",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "kiss-of-blood-clot",
"durationMs": 7500
}
},
{
"id": "rw-kalins-shrine-101503-aura-492570",
"name": "Lament of Dejection",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "lament-of-dejection",
"durationMs": 7500
}
},
{
"id": "rw-kalins-shrine-101503-aura-492571",
"name": "Confusion Curse",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 10000,
"initialCooldownMs": [
10000,
10000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "confusion-curse",
"durationMs": 5000
}
},
{
"id": "rw-kalins-shrine-101503-control-492682",
"name": "Witch's Song",
"delivery": "area",
"target": "nearby-party",
"school": "shadow",
"animation": "cast",
"castTimeMs": 1800,
"range": 4.5,
"cooldownMs": 25000,
"initialCooldownMs": [
25000,
25000
],
"repeatCooldownMs": [
25000,
25000
],
"damageMultiplier": 0,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
},
"effect": {
"kind": "control",
"mechanic": "fear",
"durationMs": 3000
}
}
]
},
@@ -798,9 +1093,15 @@ export const KALINS_SHRINE_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-kalins-shrine-101500-spell-490477",
"rw-kalins-shrine-101500-spell-492367",
"rw-kalins-shrine-101500-debuff-492383",
"rw-kalins-shrine-101500-aura-492384"
],
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -811,33 +1112,14 @@ export const KALINS_SHRINE_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-kalins-shrine-spell-492367",
"name": "Thorny Road",
"spellId": 492367,
"delivery": "projectile",
"target": "primary",
"school": "physical",
"animation": "cast",
"range": 25,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0.85
},
{
"id": "rw-kalins-shrine-spell-490477",
"id": "rw-kalins-shrine-101500-spell-490477",
"name": "Slash",
"spellId": 490477,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 10000,
"initialCooldownMs": [
@@ -849,6 +1131,84 @@ export const KALINS_SHRINE_ENTITIES = {
10000
],
"damageMultiplier": 0.85
},
{
"id": "rw-kalins-shrine-101500-spell-492367",
"name": "Thorny Road",
"spellId": 492367,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "cast",
"castTimeMs": 1800,
"range": 25,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-kalins-shrine-101500-debuff-492383",
"name": "Wine Spirit's Invitation",
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 2000,
"range": 25,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0,
"effect": {
"kind": "target-aura",
"aura": "slumber",
"durationMs": 10000
}
},
{
"id": "rw-kalins-shrine-101500-aura-492384",
"name": "Spiritual Divine Praise",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "heroic-blessing",
"durationMs": 10000
}
}
]
},
@@ -879,9 +1239,16 @@ export const KALINS_SHRINE_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-kalins-shrine-101501-spell-492379",
"rw-kalins-shrine-101501-debuff-492380",
"rw-kalins-shrine-101501-spell-492381",
"rw-kalins-shrine-101501-spell-492382",
"rw-kalins-shrine-101501-aura-492573"
],
"attacks": [
{
"id": "rw-kalins-shrine-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -890,6 +1257,147 @@ export const KALINS_SHRINE_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-kalins-shrine-101501-spell-492379",
"name": "High Speed Pressing",
"spellId": 492379,
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 10,
"cooldownMs": 10000,
"initialCooldownMs": [
10000,
10000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 0.85,
"radius": 10,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 10
}
},
{
"id": "rw-kalins-shrine-101501-debuff-492380",
"name": "Bite to Pieces",
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 10,
"cooldownMs": 10000,
"initialCooldownMs": [
10000,
10000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 0,
"radius": 10,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 10
},
"effect": {
"kind": "target-aura",
"aura": "bleed",
"durationMs": 5000
}
},
{
"id": "rw-kalins-shrine-101501-spell-492381",
"name": "Water Arrow Volley",
"spellId": 492381,
"delivery": "area",
"target": "nearby-party",
"school": "frost",
"animation": "cast",
"castTimeMs": 1800,
"range": 25,
"cooldownMs": 10000,
"initialCooldownMs": [
10000,
10000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 0.85,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
}
},
{
"id": "rw-kalins-shrine-101501-spell-492382",
"name": "Lake Master's Fury",
"spellId": 492382,
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 2500,
"range": 15,
"cooldownMs": 10000,
"initialCooldownMs": [
10000,
10000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-kalins-shrine-101501-aura-492573",
"name": "Stone Skin",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 35000,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "stone-skin",
"durationMs": 7500
}
}
]
},
@@ -2816,6 +3324,6 @@ export const KALINS_SHRINE_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -76,7 +76,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -136,7 +136,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -196,7 +196,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -236,7 +236,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -276,7 +276,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -316,7 +316,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -355,9 +355,17 @@ export const KAWAKS_TOMB_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-kawaks-tomb-104228-spell-496027",
"rw-kawaks-tomb-104228-spell-496028",
"rw-kawaks-tomb-104228-spell-496030",
"rw-kawaks-tomb-104228-spell-496031",
"rw-kawaks-tomb-104228-debuff-496036",
"rw-kawaks-tomb-104228-phase-50"
],
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -366,6 +374,175 @@ export const KAWAKS_TOMB_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-kawaks-tomb-104228-spell-496027",
"name": "Shake Heaven and Earth",
"spellId": 496027,
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 2000,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-kawaks-tomb-104228-spell-496028",
"name": "Wing Poison",
"spellId": 496028,
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-kawaks-tomb-104228-spell-496030",
"name": "Holy Stone Floor",
"spellId": 496030,
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 0,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
},
{
"id": "rw-kawaks-tomb-104228-spell-496031",
"name": "Double Horned Spike",
"spellId": 496031,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "cast",
"castTimeMs": 2000,
"range": 15,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-kawaks-tomb-104228-debuff-496036",
"name": "Pesticide",
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 5000,
"range": 30,
"cooldownMs": 12000,
"initialCooldownMs": [
12000,
12000
],
"repeatCooldownMs": [
12000,
12000
],
"damageMultiplier": 0,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
},
"effect": {
"kind": "target-aura",
"aura": "pesticide",
"durationMs": 6000
}
},
{
"id": "rw-kawaks-tomb-104228-phase-50",
"name": "Phase Change",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"interruptible": false,
"range": 0,
"cooldownMs": 1000,
"initialCooldownMs": [
0,
0
],
"repeatCooldownMs": [
1000,
1000
],
"maximumCasts": 1,
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "phase-50",
"durationMs": 5000
},
"condition": {
"kind": "self-health-percent",
"minimum": 0,
"maximum": 50
}
}
]
},
@@ -397,7 +574,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -437,7 +614,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -477,7 +654,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -517,7 +694,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -557,7 +734,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -617,7 +794,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -677,7 +854,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -717,7 +894,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -797,7 +974,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -837,7 +1014,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -877,7 +1054,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -917,7 +1094,7 @@ export const KAWAKS_TOMB_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kawaks-tomb-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -2288,6 +2465,6 @@ export const KAWAKS_TOMB_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -76,7 +76,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -136,7 +136,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -195,9 +195,17 @@ export const KULECH_BONES_NEST_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-kulech-bones-nest-107306-spell-496328",
"rw-kulech-bones-nest-107306-spell-496404",
"rw-kulech-bones-nest-107306-spell-496405",
"rw-kulech-bones-nest-107306-aura-496406",
"rw-kulech-bones-nest-107306-aura-496407",
"rw-kulech-bones-nest-107306-spell-496409"
],
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -206,6 +214,154 @@ export const KULECH_BONES_NEST_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-kulech-bones-nest-107306-spell-496328",
"name": "Stone Sickle Chop",
"spellId": 496328,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "cast",
"castTimeMs": 1800,
"range": 7.5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 7.5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 7.5
}
},
{
"id": "rw-kulech-bones-nest-107306-spell-496404",
"name": "Soul Killer Potion",
"spellId": 496404,
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 0,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
},
{
"id": "rw-kulech-bones-nest-107306-spell-496405",
"name": "Wind Sickle Shadow",
"spellId": 496405,
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-kulech-bones-nest-107306-aura-496406",
"name": "Awaken",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "awaken",
"durationMs": 7500
}
},
{
"id": "rw-kulech-bones-nest-107306-aura-496407",
"name": "Corrosive Mucus",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "acid-irritation",
"durationMs": 7500
}
},
{
"id": "rw-kulech-bones-nest-107306-spell-496409",
"name": "Wind Sickle",
"spellId": 496409,
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1000,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
}
]
},
@@ -237,7 +393,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -317,7 +473,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -377,7 +533,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -457,7 +613,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -537,7 +693,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -577,7 +733,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -657,7 +813,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -717,7 +873,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -777,7 +933,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -837,7 +993,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -897,7 +1053,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -957,7 +1113,7 @@ export const KULECH_BONES_NEST_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-kulech-bones-nest-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -2755,6 +2911,6 @@ export const KULECH_BONES_NEST_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -96,7 +96,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -156,7 +156,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -196,7 +196,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -256,7 +256,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -316,7 +316,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -376,7 +376,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -416,7 +416,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -456,7 +456,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -496,7 +496,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -556,7 +556,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -616,7 +616,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -656,7 +656,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -716,7 +716,7 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-lair-demon-dragon-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1923,6 +1923,6 @@ export const LAIR_DEMON_DRAGON_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -15,9 +15,18 @@ export const MYSTIC_ALTAR_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-mystic-altar-100611-spell-490476",
"rw-mystic-altar-100611-debuff-490480",
"rw-mystic-altar-100611-spell-490489",
"rw-mystic-altar-100611-control-491087",
"rw-mystic-altar-100611-aura-491095",
"rw-mystic-altar-100611-aura-491096",
"rw-mystic-altar-100611-spell-491104"
],
"attacks": [
{
"id": "rw-mystic-altar-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -28,13 +37,14 @@ export const MYSTIC_ALTAR_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-mystic-altar-spell-490476",
"id": "rw-mystic-altar-100611-spell-490476",
"name": "Bloodthirst",
"spellId": 490476,
"delivery": "melee",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 20000,
"initialCooldownMs": [
@@ -48,34 +58,40 @@ export const MYSTIC_ALTAR_ENTITIES = {
"damageMultiplier": 0.85
},
{
"id": "rw-mystic-altar-spell-490489",
"name": "Dark Power Release",
"spellId": 490489,
"delivery": "melee",
"id": "rw-mystic-altar-100611-debuff-490480",
"name": "Poisoned",
"delivery": "projectile",
"target": "primary",
"school": "fire",
"school": "arcane",
"animation": "cast",
"range": 4.5,
"cooldownMs": 20000,
"castTimeMs": 2000,
"range": 10,
"cooldownMs": 10000,
"initialCooldownMs": [
20000,
20000
10000,
10000
],
"repeatCooldownMs": [
20000,
20000
10000,
10000
],
"damageMultiplier": 0.85
"damageMultiplier": 0,
"effect": {
"kind": "target-aura",
"aura": "poisoned",
"durationMs": 5000
}
},
{
"id": "rw-mystic-altar-spell-491104",
"name": "Sound of the Underworld",
"spellId": 491104,
"delivery": "melee",
"target": "primary",
"school": "shadow",
"id": "rw-mystic-altar-100611-spell-490489",
"name": "Dark Power Release",
"spellId": 490489,
"delivery": "area",
"target": "nearby-party",
"school": "fire",
"animation": "cast",
"range": 4.5,
"castTimeMs": 5000,
"range": 15,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
@@ -85,7 +101,124 @@ export const MYSTIC_ALTAR_ENTITIES = {
20000,
20000
],
"damageMultiplier": 0.85
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-mystic-altar-100611-control-491087",
"name": "Dreadful Howl",
"delivery": "area",
"target": "nearby-party",
"school": "shadow",
"animation": "cast",
"castTimeMs": 1800,
"range": 4.5,
"cooldownMs": 25000,
"initialCooldownMs": [
25000,
25000
],
"repeatCooldownMs": [
25000,
25000
],
"damageMultiplier": 0,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
},
"effect": {
"kind": "control",
"mechanic": "fear",
"durationMs": 3000
}
},
{
"id": "rw-mystic-altar-100611-aura-491095",
"name": "Rampart",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 45000,
"initialCooldownMs": [
45000,
45000
],
"repeatCooldownMs": [
45000,
45000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "rampart",
"durationMs": 12000
}
},
{
"id": "rw-mystic-altar-100611-aura-491096",
"name": "Mana Insulation",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 45000,
"initialCooldownMs": [
45000,
45000
],
"repeatCooldownMs": [
45000,
45000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "mana-insulation",
"durationMs": 12000
}
},
{
"id": "rw-mystic-altar-100611-spell-491104",
"name": "Sound of the Underworld",
"spellId": 491104,
"delivery": "area",
"target": "nearby-party",
"school": "shadow",
"animation": "cast",
"castTimeMs": 5000,
"range": 15,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
}
]
},
@@ -117,7 +250,7 @@ export const MYSTIC_ALTAR_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-mystic-altar-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -177,7 +310,7 @@ export const MYSTIC_ALTAR_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-mystic-altar-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -237,7 +370,7 @@ export const MYSTIC_ALTAR_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-mystic-altar-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -316,9 +449,19 @@ export const MYSTIC_ALTAR_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-mystic-altar-100154-spell-490489",
"rw-mystic-altar-100154-spell-490633",
"rw-mystic-altar-100154-control-490636",
"rw-mystic-altar-100154-spell-490637",
"rw-mystic-altar-100154-aura-490652",
"rw-mystic-altar-100154-control-491086",
"rw-mystic-altar-100154-debuff-491097",
"rw-mystic-altar-100154-debuff-491101"
],
"attacks": [
{
"id": "rw-mystic-altar-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -329,54 +472,15 @@ export const MYSTIC_ALTAR_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-mystic-altar-spell-490637",
"name": "Silence",
"spellId": 490637,
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"range": 18,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0.85
},
{
"id": "rw-mystic-altar-spell-490633",
"name": "Thunderstorm",
"spellId": 490633,
"delivery": "projectile",
"target": "primary",
"school": "nature",
"animation": "cast",
"range": 18,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0.85
},
{
"id": "rw-mystic-altar-spell-490489",
"id": "rw-mystic-altar-100154-spell-490489",
"name": "Dark Power Release",
"spellId": 490489,
"delivery": "melee",
"target": "primary",
"delivery": "area",
"target": "nearby-party",
"school": "fire",
"animation": "cast",
"range": 4.5,
"castTimeMs": 5000,
"range": 15,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
@@ -386,7 +490,202 @@ export const MYSTIC_ALTAR_ENTITIES = {
20000,
20000
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-mystic-altar-100154-spell-490633",
"name": "Thunderstorm",
"spellId": 490633,
"delivery": "area",
"target": "nearby-party",
"school": "nature",
"animation": "cast",
"castTimeMs": 2000,
"range": 18,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0.85,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
}
},
{
"id": "rw-mystic-altar-100154-control-490636",
"name": "Group Catch",
"delivery": "area",
"target": "nearby-party",
"school": "shadow",
"animation": "cast",
"castTimeMs": 1800,
"range": 10,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
},
"effect": {
"kind": "control",
"mechanic": "root",
"durationMs": 3000
}
},
{
"id": "rw-mystic-altar-100154-spell-490637",
"name": "Silence",
"spellId": 490637,
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1000,
"range": 18,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0.85
},
{
"id": "rw-mystic-altar-100154-aura-490652",
"name": "Life Source",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 8000,
"range": 0,
"cooldownMs": 45000,
"initialCooldownMs": [
45000,
45000
],
"repeatCooldownMs": [
45000,
45000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "life-source",
"durationMs": 12000
}
},
{
"id": "rw-mystic-altar-100154-control-491086",
"name": "Dreadful Cry",
"delivery": "projectile",
"target": "primary",
"school": "shadow",
"animation": "cast",
"castTimeMs": 1800,
"range": 12,
"cooldownMs": 25000,
"initialCooldownMs": [
25000,
25000
],
"repeatCooldownMs": [
25000,
25000
],
"damageMultiplier": 0,
"effect": {
"kind": "control",
"mechanic": "fear",
"durationMs": 3000
}
},
{
"id": "rw-mystic-altar-100154-debuff-491097",
"name": "Life Sandglass",
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 25,
"cooldownMs": 12000,
"initialCooldownMs": [
12000,
12000
],
"repeatCooldownMs": [
12000,
12000
],
"damageMultiplier": 0,
"effect": {
"kind": "target-aura",
"aura": "life-sandglass",
"durationMs": 6000
}
},
{
"id": "rw-mystic-altar-100154-debuff-491101",
"name": "Life Sandglass",
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 15,
"cooldownMs": 12000,
"initialCooldownMs": [
12000,
12000
],
"repeatCooldownMs": [
12000,
12000
],
"damageMultiplier": 0,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
},
"effect": {
"kind": "target-aura",
"aura": "life-sandglass",
"durationMs": 6000
}
}
]
},
@@ -418,7 +717,7 @@ export const MYSTIC_ALTAR_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-mystic-altar-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -478,7 +777,7 @@ export const MYSTIC_ALTAR_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-mystic-altar-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -578,7 +877,7 @@ export const MYSTIC_ALTAR_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-mystic-altar-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -678,7 +977,7 @@ export const MYSTIC_ALTAR_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-mystic-altar-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -738,7 +1037,7 @@ export const MYSTIC_ALTAR_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-mystic-altar-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -838,7 +1137,7 @@ export const MYSTIC_ALTAR_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-mystic-altar-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -877,9 +1176,19 @@ export const MYSTIC_ALTAR_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-mystic-altar-100138-spell-490477",
"rw-mystic-altar-100138-spell-490478",
"rw-mystic-altar-100138-spell-490520",
"rw-mystic-altar-100138-spell-490522",
"rw-mystic-altar-100138-aura-490523",
"rw-mystic-altar-100138-spell-490525",
"rw-mystic-altar-100138-control-490636",
"rw-mystic-altar-100138-aura-491095"
],
"attacks": [
{
"id": "rw-mystic-altar-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -890,13 +1199,14 @@ export const MYSTIC_ALTAR_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-mystic-altar-spell-490477",
"id": "rw-mystic-altar-100138-spell-490477",
"name": "Slash",
"spellId": 490477,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 10000,
"initialCooldownMs": [
@@ -910,73 +1220,14 @@ export const MYSTIC_ALTAR_ENTITIES = {
"damageMultiplier": 0.85
},
{
"id": "rw-mystic-altar-spell-490520",
"name": "Whirlwind",
"spellId": 490520,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"range": 4.5,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85
},
{
"id": "rw-mystic-altar-spell-490525",
"name": "Blasting Cyclone",
"spellId": 490525,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"range": 4.5,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85
},
{
"id": "rw-mystic-altar-spell-490522",
"name": "Formation Breaker",
"spellId": 490522,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"range": 4.5,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85
},
{
"id": "rw-mystic-altar-spell-490478",
"id": "rw-mystic-altar-100138-spell-490478",
"name": "Slash",
"spellId": 490478,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 6000,
"initialCooldownMs": [
@@ -988,6 +1239,172 @@ export const MYSTIC_ALTAR_ENTITIES = {
6000
],
"damageMultiplier": 0.85
},
{
"id": "rw-mystic-altar-100138-spell-490520",
"name": "Whirlwind",
"spellId": 490520,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"castTimeMs": 1800,
"range": 5,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
}
},
{
"id": "rw-mystic-altar-100138-spell-490522",
"name": "Formation Breaker",
"spellId": 490522,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"castTimeMs": 3000,
"range": 7.5,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85,
"radius": 7.5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 7.5
}
},
{
"id": "rw-mystic-altar-100138-aura-490523",
"name": "Frenzy",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 45000,
"initialCooldownMs": [
45000,
45000
],
"repeatCooldownMs": [
45000,
45000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "frenzy",
"durationMs": 12000
}
},
{
"id": "rw-mystic-altar-100138-spell-490525",
"name": "Blasting Cyclone",
"spellId": 490525,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"castTimeMs": 1800,
"range": 8,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85,
"radius": 8,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 8
}
},
{
"id": "rw-mystic-altar-100138-control-490636",
"name": "Group Catch",
"delivery": "area",
"target": "nearby-party",
"school": "shadow",
"animation": "cast",
"castTimeMs": 1800,
"range": 10,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
},
"effect": {
"kind": "control",
"mechanic": "root",
"durationMs": 3000
}
},
{
"id": "rw-mystic-altar-100138-aura-491095",
"name": "Rampart",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 45000,
"initialCooldownMs": [
45000,
45000
],
"repeatCooldownMs": [
45000,
45000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "rampart",
"durationMs": 12000
}
}
]
},
@@ -3842,6 +4259,6 @@ export const MYSTIC_ALTAR_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
File diff suppressed because it is too large. Load diff
@@ -16,7 +16,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -56,7 +56,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -96,7 +96,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -136,7 +136,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -176,7 +176,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -216,7 +216,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -276,7 +276,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -336,7 +336,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -376,7 +376,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -436,7 +436,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -516,7 +516,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -556,7 +556,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -596,7 +596,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -656,7 +656,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -716,7 +716,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -756,7 +756,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -816,7 +816,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -896,7 +896,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -936,7 +936,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -976,7 +976,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1036,7 +1036,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1076,7 +1076,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1116,7 +1116,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1196,7 +1196,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1236,7 +1236,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1296,7 +1296,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1336,7 +1336,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1396,7 +1396,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1476,7 +1476,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1516,7 +1516,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1556,7 +1556,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1616,7 +1616,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1676,7 +1676,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1716,7 +1716,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1756,7 +1756,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1816,7 +1816,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1856,7 +1856,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1916,7 +1916,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1956,7 +1956,7 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1995,9 +1995,15 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-raksha-temple-103946-spell-495545",
"rw-raksha-temple-103946-spell-495591",
"rw-raksha-temple-103946-spell-495996",
"rw-raksha-temple-103946-spell-496112"
],
"attacks": [
{
"id": "rw-raksha-temple-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -2006,6 +2012,111 @@ export const RAKSHA_TEMPLE_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-raksha-temple-103946-spell-495545",
"name": "Soul Fire",
"spellId": 495545,
"delivery": "area",
"target": "nearby-party",
"school": "fire",
"animation": "cast",
"castTimeMs": 2000,
"range": 7.5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 7.5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 7.5
}
},
{
"id": "rw-raksha-temple-103946-spell-495591",
"name": "Void Fire",
"spellId": 495591,
"delivery": "area",
"target": "nearby-party",
"school": "fire",
"animation": "cast",
"castTimeMs": 2000,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-raksha-temple-103946-spell-495996",
"name": "Totem Burst",
"spellId": 495996,
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-raksha-temple-103946-spell-496112",
"name": "RuneWaker Spell 496112",
"spellId": 496112,
"delivery": "melee",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 0,
"range": 4.5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
}
]
},
@@ -3434,6 +3545,6 @@ export const RAKSHA_TEMPLE_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -102,8 +102,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "kalins-shrine-visual.glb",
"checksum": "0cfdb047990a4870d18e67fcf96170c3cd575fbb7f0739784dcf15edd5c8bddf",
"size": 14175516,
"checksum": "769accb48e23c9f80e28697e3e8454ef4148243a69ce247ed06ba4eb35bb0da7",
"size": 8747072,
"triangleCount": 202382
}
],
@@ -216,7 +216,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -291,8 +291,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "hoto-visual.glb",
"checksum": "4c69138c8bf6a6b1055f337a7c39204b977a6169905ab735e1742a139c76ad64",
"size": 16626184,
"checksum": "0d12b9c6dc477704d2833c81f76113d340da7f07f5885a40bafd1321524e455e",
"size": 6667208,
"triangleCount": 189855
}
],
@@ -405,7 +405,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -480,8 +480,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "mystic-altar-visual.glb",
"checksum": "9f395e5670c79fe306c2a5440125885c4c58b36d677f2bde74667d3779d1eab5",
"size": 6453276,
"checksum": "765dd1b15bbe79fe1ffc2104e10caece19acaf86c919f0327419cb3609dbf600",
"size": 5552872,
"triangleCount": 174521
}
],
@@ -588,7 +588,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Source-anchor support pads preserve combat and entrance coordinates where modular WDB sections or primary-ROS collision leave navmesh gaps; full secondary placement assembly remains a visual follow-up.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -663,8 +663,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "kal-turok-hive-visual.glb",
"checksum": "7a593b8b0b001d410213c09cf75f3b1edc3fb8543f412d2a5bb18e411506b0da",
"size": 5593304,
"checksum": "012867deefc4dc5a18c3e1a507309aa48f119cf4c2e88968fdd2787936ed7c24",
"size": 1777616,
"triangleCount": 24026
}
],
@@ -763,7 +763,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -838,8 +838,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "paspers-shrine-visual.glb",
"checksum": "564dc92bae483dd0be8fd0086215d4e2e6bd4dda45f2094a55f4045de8301f5f",
"size": 6262596,
"checksum": "82ea6cafd19c0a323c994795699223a07193807da29b300eb12dac41146f1034",
"size": 5625952,
"triangleCount": 169935
}
],
@@ -945,7 +945,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -1020,8 +1020,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "treasure-trove-visual.glb",
"checksum": "eb8f26f1594d8e10a060444899e91744c8c5f48af720b873504031d860d9f446",
"size": 10623004,
"checksum": "f4c4dc12df65f8afc3c696308db17cb42b6a5894086dbe870dae2e21e1258448",
"size": 7433280,
"triangleCount": 182387
}
],
@@ -1135,7 +1135,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Source-anchor support pads preserve combat and entrance coordinates where modular WDB sections or primary-ROS collision leave navmesh gaps; full secondary placement assembly remains a visual follow-up.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -1210,8 +1210,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "ystra-labyrinth-boss1-visual.glb",
"checksum": "54febe2d5e0740da790f6bdbad73e4f383ed9b633da9c096a6687ae28081eeaa",
"size": 8036460,
"checksum": "c8ca72a5fdf487fa71a1aecd1e66ea821183f476540be9a89444b409be957c25",
"size": 2709352,
"triangleCount": 64284
}
],
@@ -1324,7 +1324,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -1399,8 +1399,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "barren-caves-visual.glb",
"checksum": "71e264d5e757e2402c45e7c291b03af243c9defdd89d78ec736a3024d126d482",
"size": 1852500,
"checksum": "d0f733de4b26c0f432c5e160614765f320bf1753a829be269740b340e7415a12",
"size": 1710152,
"triangleCount": 47084
}
],
@@ -1499,7 +1499,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -1574,8 +1574,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "ystra-labyrinth-boss2-visual.glb",
"checksum": "12c32e7c72cb1a6ed8693999d9d9a99bfeeee28ce32f2415852d8c9a9f59cf6c",
"size": 7954300,
"checksum": "d52524621220e16d844badc24ee745f4374798031b2b0bc7236decbe3b9588ab",
"size": 2682688,
"triangleCount": 21428
}
],
@@ -1674,7 +1674,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -1749,8 +1749,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "ystra-labyrinth-boss3-visual.glb",
"checksum": "12c32e7c72cb1a6ed8693999d9d9a99bfeeee28ce32f2415852d8c9a9f59cf6c",
"size": 7954300,
"checksum": "d52524621220e16d844badc24ee745f4374798031b2b0bc7236decbe3b9588ab",
"size": 2682688,
"triangleCount": 21428
}
],
@@ -1849,7 +1849,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -1924,8 +1924,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "ice-dwarf-kingdom-visual.glb",
"checksum": "a0f4ce4572d6fe491c362a8494623b5b4f5a255c1a63c9a4b5509af633f05c72",
"size": 11348084,
"checksum": "a6b902e85bb6026f18281d77efa235ec880ffc2fc13ea6bfdc431d24af768e00",
"size": 9880192,
"triangleCount": 285517
}
],
@@ -2045,7 +2045,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -2120,8 +2120,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "the-origin-visual.glb",
"checksum": "d6e1b8aaa69d067068ea29ad38544466a3a5fab7e0a532ca15da60cbfceac51b",
"size": 643248,
"checksum": "a6db29db31f88a45bd082d27130725d70b2fa1db82030530750492a54b3f9bf8",
"size": 251132,
"triangleCount": 2664
}
],
@@ -2221,7 +2221,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Source-anchor support pads preserve combat and entrance coordinates where modular WDB sections or primary-ROS collision leave navmesh gaps; full secondary placement assembly remains a visual follow-up.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -2296,8 +2296,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "hall-of-survivors-visual.glb",
"checksum": "c22b5ae6b4a836a8861d90c8db249ed813caab8dfa6eb1311bc0730fd62e1150",
"size": 31415356,
"checksum": "18b9d116da599159719ad76eeee45a4d89056ee8c61cc4f21cab9a1d9228fff7",
"size": 15216768,
"triangleCount": 380192
}
],
@@ -2424,7 +2424,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -2499,8 +2499,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "cave-water-dragon-visual.glb",
"checksum": "56b9565c53ae8c644fb7ca7d070c02f39656e3ff30c407f1e04f28874566b781",
"size": 13081352,
"checksum": "cc57c0285de77690c4a1cdabaecd8b192791cbd05aca38aa21ed96462688ca31",
"size": 8893112,
"triangleCount": 249082
}
],
@@ -2613,7 +2613,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -2688,8 +2688,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "lair-demon-dragon-visual.glb",
"checksum": "4e3e15f5c5c3d02fab6a5eefa37962e614012cb2d255c56c841dadfd983db101",
"size": 11971136,
"checksum": "d28481504802203e800985836f2b0907d67f6d6d0495c67500914e749d771ffe",
"size": 4551300,
"triangleCount": 64688
}
],
@@ -2788,7 +2788,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -2863,8 +2863,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "zurhidon-stronghold-visual.glb",
"checksum": "ac533caeaf367039a48527d33695d9f28dfaffa6c29eac7b304e93334e5bb8d3",
"size": 38128108,
"checksum": "a2d450946ca9687395cf65815b931c7817d9f41041afeb36ba1a351ec24104c4",
"size": 17189492,
"triangleCount": 364765
}
],
@@ -2991,7 +2991,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -3167,7 +3167,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"The source WDB has no terrain ROS; a flat playable arena is derived from source descriptor bounds and must be visually reviewed.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -3242,8 +3242,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "demon-stronghold-visual.glb",
"checksum": "27d9026c01418e26959c2c24fb3929fc50a7a29c63922ef7223a7cf82aceb46f",
"size": 37609808,
"checksum": "9368984b3c183a8db6e8b7e18b7818edcf41b8edd45ffeca1d1df28aadc592a2",
"size": 15166984,
"triangleCount": 261609
}
],
@@ -3357,7 +3357,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Source-anchor support pads preserve combat and entrance coordinates where modular WDB sections or primary-ROS collision leave navmesh gaps; full secondary placement assembly remains a visual follow-up.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -3432,8 +3432,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "zurhidon-stronghold-124-visual.glb",
"checksum": "ac533caeaf367039a48527d33695d9f28dfaffa6c29eac7b304e93334e5bb8d3",
"size": 38128108,
"checksum": "a2d450946ca9687395cf65815b931c7817d9f41041afeb36ba1a351ec24104c4",
"size": 17189492,
"triangleCount": 364765
}
],
@@ -3560,7 +3560,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -3635,8 +3635,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "demon-stronghold-125-visual.glb",
"checksum": "e648750047d8bbcf77b3f1a7d1c79144e4dcf45bd209bc54f070777d1e55541c",
"size": 37597956,
"checksum": "ecbacfe274e7eecb104c5807eedf3cc51cd4b7a628967c6331fc93b86396b842",
"size": 15134500,
"triangleCount": 259377
}
],
@@ -3749,7 +3749,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -3824,8 +3824,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "dungeon-of-dalanis-visual.glb",
"checksum": "9dbf0d01dd3d6ba029a2647561076ccd049c29ea9391a45ade5c6259c7c11b9c",
"size": 14905224,
"checksum": "4c584ef34779e516f8e6ff3a4907eaab1c7c8d49d4c302de66e17b09222d8829",
"size": 11936864,
"triangleCount": 288949
}
],
@@ -3945,7 +3945,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -4020,8 +4020,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "warnorken-arena-visual.glb",
"checksum": "52d94f8fabf0c86fb24c57d5be36a490b26703b0acf84f83139adcd7df9710df",
"size": 10114952,
"checksum": "c452a843c2e4123d4fe0d20da41558f5b5a1906548b3d091dc691ffc42ed19d9",
"size": 3752136,
"triangleCount": 60751
}
],
@@ -4120,7 +4120,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -4195,8 +4195,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "raksha-temple-visual.glb",
"checksum": "91a2197f45cd7e63829c35a3748efc2311c7d5a40e6737a33f42d21163dc4b1d",
"size": 3274336,
"checksum": "3439e64f2a49d0545651bac35276867af83c3eb57b3c859f767de0c49c3dce66",
"size": 937772,
"triangleCount": 4302
}
],
@@ -4296,7 +4296,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Source-anchor support pads preserve combat and entrance coordinates where modular WDB sections or primary-ROS collision leave navmesh gaps; full secondary placement assembly remains a visual follow-up.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -4371,8 +4371,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "kawaks-tomb-visual.glb",
"checksum": "6a55e61218a56a0f21566555ad0b7bb88b29b52882317231e836cc76b359ba14",
"size": 21954824,
"checksum": "de54d223725e2c3dfb0c47a97c208a212ef9423860bd0ac390f1482b302d3a32",
"size": 8963188,
"triangleCount": 161613
}
],
@@ -4478,7 +4478,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -4553,8 +4553,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "grafu-castle-visual.glb",
"checksum": "d5561b8b427edd6dca0a675dabaf40b251430ad5511ab66217254bde45a7eda0",
"size": 21509296,
"checksum": "ba4552484ef9cddc1c00607d0dfd4c8fb0bae5b3cd3f992d215a580dac1e96ed",
"size": 11149716,
"triangleCount": 207925
}
],
@@ -4667,7 +4667,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -4742,8 +4742,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "sardo-castle-visual.glb",
"checksum": "e819c1afb60fd81f7e79f0e3fea0b3bf4e64f57eb41526761bf41ccce3876a51",
"size": 17572084,
"checksum": "16d759af9f8a3f98281e89362c7fb52dceff6543bbd837a7c6f89015920cdd94",
"size": 7756568,
"triangleCount": 147586
}
],
@@ -4849,7 +4849,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -4924,8 +4924,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "tomb-seven-heroes-visual.glb",
"checksum": "3659d1675240704a6f5e43cb59a2ffffc503c979adfe92afcc93ff4e18a0413c",
"size": 26962236,
"checksum": "d9f39da6b61c9f841c5d075e4985bc67e533ce422313fdf38d5f6c99dbb304c1",
"size": 10910804,
"triangleCount": 155422
}
],
@@ -5031,7 +5031,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -5106,8 +5106,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "aeternal-circle-visual.glb",
"checksum": "d182e54b99a9875600c89efea194d9e86aa508c800a4e2b4d6ac7420158906aa",
"size": 12997820,
"checksum": "42fdcff1b25b58688943a030f69fd14ea839247a7baabbc31b5f6f7faaf49785",
"size": 4963912,
"triangleCount": 61639
}
],
@@ -5207,7 +5207,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"Source-anchor support pads preserve combat and entrance coordinates where modular WDB sections or primary-ROS collision leave navmesh gaps; full secondary placement assembly remains a visual follow-up.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition,
@@ -5282,8 +5282,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
{
"id": "visual-000",
"fileName": "kulech-bones-nest-visual.glb",
"checksum": "a29116b276c7057ef30a6d7faacc8c7ff4430cea658b9bd9eb1edb86228f770c",
"size": 36555996,
"checksum": "2d912dcf7c0c8e59e36362fd947df1d5554c5dd5060590e1abb112e4c39f606f",
"size": 12146892,
"triangleCount": 168192
}
],
@@ -5390,7 +5390,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
"Collision initially includes decorative primary-ROS geometry.",
"Original GR2 actor animations are recovered where source graphs and validation permit; scripted encounter mechanics remain evidence-driven per-instance work.",
"1 runtime/dynamic actor containers use HealerMan procedural visuals because their ROS files expose no static triangles.",
"Mechanics candidates are source-audited; complex doors, summons, phases, patrol scripts, and loot remain review-required."
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
]
}
} as const satisfies DungeonDefinition
@@ -15,9 +15,15 @@ export const SAND_VORTEX_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-sand-vortex-100645-spell-494642",
"rw-sand-vortex-100645-spell-494643",
"rw-sand-vortex-100645-debuff-494644",
"rw-sand-vortex-100645-aura-494649"
],
"attacks": [
{
"id": "rw-sand-vortex-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -26,6 +32,119 @@ export const SAND_VORTEX_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-sand-vortex-100645-spell-494642",
"name": "Viscous Quicksand",
"spellId": 494642,
"delivery": "area",
"target": "nearby-party",
"school": "nature",
"animation": "cast",
"castTimeMs": 1800,
"range": 4.5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 3,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 3
}
},
{
"id": "rw-sand-vortex-100645-spell-494643",
"name": "Sand Fountain",
"spellId": 494643,
"delivery": "area",
"target": "nearby-party",
"school": "nature",
"animation": "cast",
"castTimeMs": 2000,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 8,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 8
}
},
{
"id": "rw-sand-vortex-100645-debuff-494644",
"name": "Sandy Ties",
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 12000,
"initialCooldownMs": [
12000,
12000
],
"repeatCooldownMs": [
12000,
12000
],
"damageMultiplier": 0,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
},
"effect": {
"kind": "target-aura",
"aura": "sandy-ties",
"durationMs": 6000
}
},
{
"id": "rw-sand-vortex-100645-aura-494649",
"name": "Corrosive Sand",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "corrosive-sand",
"durationMs": 7500
}
}
]
},
@@ -112,6 +231,6 @@ export const SAND_VORTEX_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -56,7 +56,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -96,7 +96,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -136,7 +136,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -216,7 +216,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -296,7 +296,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -376,7 +376,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -456,7 +456,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -516,7 +516,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -556,7 +556,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -616,7 +616,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -656,7 +656,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -695,9 +695,14 @@ export const SARDO_CASTLE_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-sardo-castle-105701-spell-497652",
"rw-sardo-castle-105701-spell-497653",
"rw-sardo-castle-105701-aura-497654"
],
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -706,6 +711,87 @@ export const SARDO_CASTLE_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-sardo-castle-105701-spell-497652",
"name": "Energy Release",
"spellId": 497652,
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 15,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-sardo-castle-105701-spell-497653",
"name": "Falling Flame",
"spellId": 497653,
"delivery": "area",
"target": "nearby-party",
"school": "fire",
"animation": "cast",
"castTimeMs": 1800,
"range": 25,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
},
{
"id": "rw-sardo-castle-105701-aura-497654",
"name": "Dark Healing",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 2000,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "dark-healing",
"durationMs": 7500
}
}
]
},
@@ -737,7 +823,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -817,7 +903,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -857,7 +943,7 @@ export const SARDO_CASTLE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-sardo-castle-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1740,6 +1826,6 @@ export const SARDO_CASTLE_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -96,7 +96,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -176,7 +176,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -216,7 +216,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -296,7 +296,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -376,7 +376,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -436,7 +436,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -496,7 +496,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -576,7 +576,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -616,7 +616,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -696,7 +696,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -756,7 +756,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -836,7 +836,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -916,7 +916,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -956,7 +956,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -996,7 +996,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1076,7 +1076,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1156,7 +1156,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1256,7 +1256,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1296,7 +1296,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1396,7 +1396,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1436,7 +1436,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1476,7 +1476,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1515,9 +1515,14 @@ export const THE_ORIGIN_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-the-origin-102349-spell-493663",
"rw-the-origin-102349-spell-493664",
"rw-the-origin-102349-aura-493665"
],
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1526,6 +1531,73 @@ export const THE_ORIGIN_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-the-origin-102349-spell-493663",
"name": "Spike Puncture",
"spellId": 493663,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
},
{
"id": "rw-the-origin-102349-spell-493664",
"name": "Earth Rupture",
"spellId": 493664,
"delivery": "projectile",
"target": "primary",
"school": "physical",
"animation": "cast",
"castTimeMs": 0,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
},
{
"id": "rw-the-origin-102349-aura-493665",
"name": "King Bug Shock",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 2000,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "king-bug-shock",
"durationMs": 7500
}
}
]
},
@@ -1557,7 +1629,7 @@ export const THE_ORIGIN_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1596,9 +1668,17 @@ export const THE_ORIGIN_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-the-origin-102307-spell-493590",
"rw-the-origin-102307-aura-493592",
"rw-the-origin-102307-spell-493649",
"rw-the-origin-102307-aura-493650",
"rw-the-origin-102307-aura-493651",
"rw-the-origin-102307-spell-493652"
],
"attacks": [
{
"id": "rw-the-origin-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1607,6 +1687,151 @@ export const THE_ORIGIN_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-the-origin-102307-spell-493590",
"name": "Power of the Ents",
"spellId": 493590,
"delivery": "projectile",
"target": "primary",
"school": "physical",
"animation": "cast",
"castTimeMs": 0,
"range": 10,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
},
{
"id": "rw-the-origin-102307-aura-493592",
"name": "Manastorms Duo Lv5",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "manastorms-duo-lv5",
"durationMs": 7500
}
},
{
"id": "rw-the-origin-102307-spell-493649",
"name": "Giant Tree Heavy Hammer",
"spellId": 493649,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85
},
{
"id": "rw-the-origin-102307-aura-493650",
"name": "Impaling Thorns",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "impaling-thorns",
"durationMs": 7500
}
},
{
"id": "rw-the-origin-102307-aura-493651",
"name": "Entangling Tree Vines",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "armor-penetrating-tree-vine",
"durationMs": 7500
}
},
{
"id": "rw-the-origin-102307-spell-493652",
"name": "Giant Wood Thicket",
"spellId": 493652,
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 15,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
}
]
},
@@ -3759,6 +3984,6 @@ export const THE_ORIGIN_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -116,7 +116,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -196,7 +196,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -276,7 +276,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -356,7 +356,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -436,7 +436,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -516,7 +516,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -556,7 +556,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -656,7 +656,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -736,7 +736,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -776,7 +776,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -856,7 +856,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -896,7 +896,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1016,7 +1016,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1076,7 +1076,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1136,7 +1136,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1196,7 +1196,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1236,7 +1236,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1296,7 +1296,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1376,7 +1376,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1436,7 +1436,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1496,7 +1496,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1536,7 +1536,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1576,7 +1576,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1616,7 +1616,7 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-tomb-seven-heroes-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -4330,6 +4330,6 @@ export const TOMB_SEVEN_HEROES_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -75,9 +75,15 @@ export const TREASURE_TROVE_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-treasure-trove-101533-spell-490477",
"rw-treasure-trove-101533-aura-492431",
"rw-treasure-trove-101533-debuff-492433",
"rw-treasure-trove-101533-spell-492434"
],
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -88,33 +94,14 @@ export const TREASURE_TROVE_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-treasure-trove-spell-492434",
"name": "Boulder Strike",
"spellId": 492434,
"delivery": "projectile",
"target": "primary",
"school": "physical",
"animation": "cast",
"range": 30,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85
},
{
"id": "rw-treasure-trove-spell-490477",
"id": "rw-treasure-trove-101533-spell-490477",
"name": "Slash",
"spellId": 490477,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 10000,
"initialCooldownMs": [
@@ -126,6 +113,77 @@ export const TREASURE_TROVE_ENTITIES = {
10000
],
"damageMultiplier": 0.85
},
{
"id": "rw-treasure-trove-101533-aura-492431",
"name": "Strengthen Body",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 10000,
"initialCooldownMs": [
10000,
10000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "strengthen-body",
"durationMs": 5000
}
},
{
"id": "rw-treasure-trove-101533-debuff-492433",
"name": "Armor Damaging Attack",
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 5,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0,
"effect": {
"kind": "target-aura",
"aura": "armor-damage",
"durationMs": 10000
}
},
{
"id": "rw-treasure-trove-101533-spell-492434",
"name": "Boulder Strike",
"spellId": 492434,
"delivery": "projectile",
"target": "primary",
"school": "physical",
"animation": "cast",
"castTimeMs": 0,
"range": 30,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85
}
]
},
@@ -156,9 +214,15 @@ export const TREASURE_TROVE_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-treasure-trove-101535-debuff-492435",
"rw-treasure-trove-101535-aura-492436",
"rw-treasure-trove-101535-spell-492438",
"rw-treasure-trove-101535-aura-492991"
],
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -169,14 +233,65 @@ export const TREASURE_TROVE_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-treasure-trove-spell-492438",
"id": "rw-treasure-trove-101535-debuff-492435",
"name": "Open Wound",
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0,
"effect": {
"kind": "target-aura",
"aura": "absorbed-heal",
"durationMs": 10000
}
},
{
"id": "rw-treasure-trove-101535-aura-492436",
"name": "Queen's Summons",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 30000,
"initialCooldownMs": [
30000,
30000
],
"repeatCooldownMs": [
30000,
30000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "queen-s-summons",
"durationMs": 12000
}
},
{
"id": "rw-treasure-trove-101535-spell-492438",
"name": "Snow's Dance",
"spellId": 492438,
"delivery": "melee",
"target": "primary",
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"range": 4.5,
"castTimeMs": 1800,
"range": 5,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
@@ -186,7 +301,39 @@ export const TREASURE_TROVE_ENTITIES = {
15000,
15000
],
"damageMultiplier": 0.85
"damageMultiplier": 0.85,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
}
},
{
"id": "rw-treasure-trove-101535-aura-492991",
"name": "Rose Flower Demon Vine",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 30000,
"initialCooldownMs": [
30000,
30000
],
"repeatCooldownMs": [
30000,
30000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "rose-flower-demon-vine",
"durationMs": 12000
}
}
]
},
@@ -218,7 +365,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -318,7 +465,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -418,7 +565,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -518,7 +665,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -598,7 +745,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -698,7 +845,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -778,7 +925,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -878,7 +1025,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -938,7 +1085,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -998,7 +1145,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1078,7 +1225,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1138,7 +1285,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1217,9 +1364,15 @@ export const TREASURE_TROVE_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-treasure-trove-101534-spell-490477",
"rw-treasure-trove-101534-spell-492428",
"rw-treasure-trove-101534-debuff-492429",
"rw-treasure-trove-101534-spell-492430"
],
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1230,13 +1383,14 @@ export const TREASURE_TROVE_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-treasure-trove-spell-490477",
"id": "rw-treasure-trove-101534-spell-490477",
"name": "Slash",
"spellId": 490477,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 10000,
"initialCooldownMs": [
@@ -1248,6 +1402,87 @@ export const TREASURE_TROVE_ENTITIES = {
10000
],
"damageMultiplier": 0.85
},
{
"id": "rw-treasure-trove-101534-spell-492428",
"name": "Dizzy Chop",
"spellId": 492428,
"delivery": "melee",
"target": "primary",
"school": "physical",
"animation": "attack",
"castTimeMs": 0,
"range": 5,
"cooldownMs": 20000,
"initialCooldownMs": [
20000,
20000
],
"repeatCooldownMs": [
20000,
20000
],
"damageMultiplier": 0.85
},
{
"id": "rw-treasure-trove-101534-debuff-492429",
"name": "Focused Gaze",
"delivery": "area",
"target": "nearby-party",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
},
"effect": {
"kind": "target-aura",
"aura": "weak-spot-exposure",
"durationMs": 7500
}
},
{
"id": "rw-treasure-trove-101534-spell-492430",
"name": "Aimed Throw",
"spellId": 492430,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
}
]
},
@@ -1279,7 +1514,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1319,7 +1554,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1379,7 +1614,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1439,7 +1674,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1499,7 +1734,7 @@ export const TREASURE_TROVE_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-treasure-trove-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -7068,6 +7303,6 @@ export const TREASURE_TROVE_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const WARNORKEN_ARENA_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-warnorken-arena-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -101,6 +101,6 @@ export const WARNORKEN_ARENA_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -15,9 +15,19 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-ystra-labyrinth-boss1-102195-spell-490634",
"rw-ystra-labyrinth-boss1-102195-aura-491008",
"rw-ystra-labyrinth-boss1-102195-spell-492307",
"rw-ystra-labyrinth-boss1-102195-spell-492308",
"rw-ystra-labyrinth-boss1-102195-phase-80",
"rw-ystra-labyrinth-boss1-102195-phase-60",
"rw-ystra-labyrinth-boss1-102195-phase-40",
"rw-ystra-labyrinth-boss1-102195-phase-20"
],
"attacks": [
{
"id": "rw-ystra-labyrinth-boss1-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -28,13 +38,14 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-ystra-labyrinth-boss1-spell-490634",
"id": "rw-ystra-labyrinth-boss1-102195-spell-490634",
"name": "Meteor Shower",
"spellId": 490634,
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 2000,
"range": 25,
"cooldownMs": 20000,
"initialCooldownMs": [
@@ -46,6 +57,215 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
20000
],
"damageMultiplier": 0.85
},
{
"id": "rw-ystra-labyrinth-boss1-102195-aura-491008",
"name": "Transport Magic",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "transport-magic",
"durationMs": 7500
}
},
{
"id": "rw-ystra-labyrinth-boss1-102195-spell-492307",
"name": "Guard Bomb",
"spellId": 492307,
"delivery": "area",
"target": "nearby-party",
"school": "fire",
"animation": "cast",
"castTimeMs": 2000,
"range": 5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
}
},
{
"id": "rw-ystra-labyrinth-boss1-102195-spell-492308",
"name": "Guard Bomb",
"spellId": 492308,
"delivery": "area",
"target": "nearby-party",
"school": "fire",
"animation": "cast",
"castTimeMs": 2000,
"range": 5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
}
},
{
"id": "rw-ystra-labyrinth-boss1-102195-phase-80",
"name": "Phase Change",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"interruptible": false,
"range": 0,
"cooldownMs": 1000,
"initialCooldownMs": [
0,
0
],
"repeatCooldownMs": [
1000,
1000
],
"maximumCasts": 1,
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "phase-80",
"durationMs": 5000
},
"condition": {
"kind": "self-health-percent",
"minimum": 0,
"maximum": 80
}
},
{
"id": "rw-ystra-labyrinth-boss1-102195-phase-60",
"name": "Phase Change",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"interruptible": false,
"range": 0,
"cooldownMs": 1000,
"initialCooldownMs": [
0,
0
],
"repeatCooldownMs": [
1000,
1000
],
"maximumCasts": 1,
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "phase-60",
"durationMs": 5000
},
"condition": {
"kind": "self-health-percent",
"minimum": 0,
"maximum": 60
}
},
{
"id": "rw-ystra-labyrinth-boss1-102195-phase-40",
"name": "Phase Change",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"interruptible": false,
"range": 0,
"cooldownMs": 1000,
"initialCooldownMs": [
0,
0
],
"repeatCooldownMs": [
1000,
1000
],
"maximumCasts": 1,
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "phase-40",
"durationMs": 5000
},
"condition": {
"kind": "self-health-percent",
"minimum": 0,
"maximum": 40
}
},
{
"id": "rw-ystra-labyrinth-boss1-102195-phase-20",
"name": "Phase Change",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"interruptible": false,
"range": 0,
"cooldownMs": 1000,
"initialCooldownMs": [
0,
0
],
"repeatCooldownMs": [
1000,
1000
],
"maximumCasts": 1,
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "phase-20",
"durationMs": 5000
},
"condition": {
"kind": "self-health-percent",
"minimum": 0,
"maximum": 20
}
}
]
},
@@ -76,9 +296,18 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-ystra-labyrinth-boss1-101432-spell-490539",
"rw-ystra-labyrinth-boss1-101432-aura-491008",
"rw-ystra-labyrinth-boss1-101432-spell-492310",
"rw-ystra-labyrinth-boss1-101432-spell-492312",
"rw-ystra-labyrinth-boss1-101432-aura-492313",
"rw-ystra-labyrinth-boss1-101432-spell-492314",
"rw-ystra-labyrinth-boss1-101432-spell-492334"
],
"attacks": [
{
"id": "rw-ystra-labyrinth-boss1-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -89,13 +318,67 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-ystra-labyrinth-boss1-spell-492310",
"id": "rw-ystra-labyrinth-boss1-101432-spell-490539",
"name": "Giant Whirlwind",
"spellId": 490539,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"castTimeMs": 1800,
"range": 7.5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 7.5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 7.5
}
},
{
"id": "rw-ystra-labyrinth-boss1-101432-aura-491008",
"name": "Transport Magic",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "transport-magic",
"durationMs": 7500
}
},
{
"id": "rw-ystra-labyrinth-boss1-101432-spell-492310",
"name": "Drop Magic",
"spellId": 492310,
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 0,
"range": 30,
"cooldownMs": 5000,
"initialCooldownMs": [
@@ -107,6 +390,115 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
5000
],
"damageMultiplier": 0.85
},
{
"id": "rw-ystra-labyrinth-boss1-101432-spell-492312",
"name": "Energy Overload",
"spellId": 492312,
"delivery": "area",
"target": "nearby-party",
"school": "shadow",
"animation": "cast",
"castTimeMs": 1800,
"range": 7.5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 7.5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 7.5
}
},
{
"id": "rw-ystra-labyrinth-boss1-101432-aura-492313",
"name": "Magic Focus",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "magic-focus",
"durationMs": 7500
}
},
{
"id": "rw-ystra-labyrinth-boss1-101432-spell-492314",
"name": "Magic Explosion",
"spellId": 492314,
"delivery": "area",
"target": "nearby-party",
"school": "shadow",
"animation": "cast",
"castTimeMs": 1800,
"range": 7.5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 7.5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 7.5
}
},
{
"id": "rw-ystra-labyrinth-boss1-101432-spell-492334",
"name": "Manastorms Duo Lv5",
"spellId": 492334,
"delivery": "area",
"target": "nearby-party",
"school": "shadow",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
}
]
},
@@ -137,9 +529,18 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-ystra-labyrinth-boss1-101433-spell-490524",
"rw-ystra-labyrinth-boss1-101433-aura-491008",
"rw-ystra-labyrinth-boss1-101433-aura-492256",
"rw-ystra-labyrinth-boss1-101433-aura-492322",
"rw-ystra-labyrinth-boss1-101433-spell-492323",
"rw-ystra-labyrinth-boss1-101433-aura-492324",
"rw-ystra-labyrinth-boss1-101433-spell-492327"
],
"attacks": [
{
"id": "rw-ystra-labyrinth-boss1-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -148,6 +549,183 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-ystra-labyrinth-boss1-101433-spell-490524",
"name": "Clash",
"spellId": 490524,
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 0,
"range": 6,
"cooldownMs": 10000,
"initialCooldownMs": [
10000,
10000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 0.85
},
{
"id": "rw-ystra-labyrinth-boss1-101433-aura-491008",
"name": "Transport Magic",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "transport-magic",
"durationMs": 7500
}
},
{
"id": "rw-ystra-labyrinth-boss1-101433-aura-492256",
"name": "Fog of Petrification",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 8000,
"initialCooldownMs": [
8000,
8000
],
"repeatCooldownMs": [
8000,
8000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "fog-of-petrification",
"durationMs": 4000
}
},
{
"id": "rw-ystra-labyrinth-boss1-101433-aura-492322",
"name": "Cursed Poison Fog",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 8000,
"initialCooldownMs": [
8000,
8000
],
"repeatCooldownMs": [
8000,
8000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "cursed-posion",
"durationMs": 4000
}
},
{
"id": "rw-ystra-labyrinth-boss1-101433-spell-492323",
"name": "Potent Poison Swipe",
"spellId": 492323,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"castTimeMs": 1800,
"range": 5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
}
},
{
"id": "rw-ystra-labyrinth-boss1-101433-aura-492324",
"name": "Demon's Roar",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "demon-s-roar",
"durationMs": 7500
}
},
{
"id": "rw-ystra-labyrinth-boss1-101433-spell-492327",
"name": "Charge Assault",
"spellId": 492327,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"telegraph": {
"shape": "line",
"anchor": "source",
"warningMs": 1800,
"length": 30,
"width": 2
}
}
]
},
@@ -274,6 +852,6 @@ export const YSTRA_LABYRINTH_BOSS1_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -15,9 +15,18 @@ export const YSTRA_LABYRINTH_BOSS2_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-ystra-labyrinth-boss2-102196-spell-490539",
"rw-ystra-labyrinth-boss2-102196-aura-491008",
"rw-ystra-labyrinth-boss2-102196-spell-492310",
"rw-ystra-labyrinth-boss2-102196-spell-492312",
"rw-ystra-labyrinth-boss2-102196-aura-492313",
"rw-ystra-labyrinth-boss2-102196-spell-492314",
"rw-ystra-labyrinth-boss2-102196-spell-492334"
],
"attacks": [
{
"id": "rw-ystra-labyrinth-boss2-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -28,13 +37,67 @@ export const YSTRA_LABYRINTH_BOSS2_ENTITIES = {
"damageMultiplier": 1
},
{
"id": "rw-ystra-labyrinth-boss2-spell-492310",
"id": "rw-ystra-labyrinth-boss2-102196-spell-490539",
"name": "Giant Whirlwind",
"spellId": 490539,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"castTimeMs": 1800,
"range": 7.5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 7.5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 7.5
}
},
{
"id": "rw-ystra-labyrinth-boss2-102196-aura-491008",
"name": "Transport Magic",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "transport-magic",
"durationMs": 7500
}
},
{
"id": "rw-ystra-labyrinth-boss2-102196-spell-492310",
"name": "Drop Magic",
"spellId": 492310,
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 0,
"range": 30,
"cooldownMs": 5000,
"initialCooldownMs": [
@@ -46,6 +109,115 @@ export const YSTRA_LABYRINTH_BOSS2_ENTITIES = {
5000
],
"damageMultiplier": 0.85
},
{
"id": "rw-ystra-labyrinth-boss2-102196-spell-492312",
"name": "Energy Overload",
"spellId": 492312,
"delivery": "area",
"target": "nearby-party",
"school": "shadow",
"animation": "cast",
"castTimeMs": 1800,
"range": 7.5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 7.5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 7.5
}
},
{
"id": "rw-ystra-labyrinth-boss2-102196-aura-492313",
"name": "Magic Focus",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "magic-focus",
"durationMs": 7500
}
},
{
"id": "rw-ystra-labyrinth-boss2-102196-spell-492314",
"name": "Magic Explosion",
"spellId": 492314,
"delivery": "area",
"target": "nearby-party",
"school": "shadow",
"animation": "cast",
"castTimeMs": 1800,
"range": 7.5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 7.5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 7.5
}
},
{
"id": "rw-ystra-labyrinth-boss2-102196-spell-492334",
"name": "Manastorms Duo Lv5",
"spellId": 492334,
"delivery": "area",
"target": "nearby-party",
"school": "shadow",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 15,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 15
}
}
]
},
@@ -132,6 +304,6 @@ export const YSTRA_LABYRINTH_BOSS2_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -15,9 +15,18 @@ export const YSTRA_LABYRINTH_BOSS3_ENTITIES = {
"damageMultiplier": 1.3,
"moveSpeed": 2.25,
"leashRange": 32,
"mechanicIds": [
"rw-ystra-labyrinth-boss3-102197-spell-490524",
"rw-ystra-labyrinth-boss3-102197-aura-491008",
"rw-ystra-labyrinth-boss3-102197-aura-492256",
"rw-ystra-labyrinth-boss3-102197-aura-492322",
"rw-ystra-labyrinth-boss3-102197-spell-492323",
"rw-ystra-labyrinth-boss3-102197-aura-492324",
"rw-ystra-labyrinth-boss3-102197-spell-492327"
],
"attacks": [
{
"id": "rw-ystra-labyrinth-boss3-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -26,6 +35,183 @@ export const YSTRA_LABYRINTH_BOSS3_ENTITIES = {
"range": 4.5,
"cooldownMs": 1900,
"damageMultiplier": 1
},
{
"id": "rw-ystra-labyrinth-boss3-102197-spell-490524",
"name": "Clash",
"spellId": 490524,
"delivery": "projectile",
"target": "primary",
"school": "arcane",
"animation": "cast",
"castTimeMs": 0,
"range": 6,
"cooldownMs": 10000,
"initialCooldownMs": [
10000,
10000
],
"repeatCooldownMs": [
10000,
10000
],
"damageMultiplier": 0.85
},
{
"id": "rw-ystra-labyrinth-boss3-102197-aura-491008",
"name": "Transport Magic",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "transport-magic",
"durationMs": 7500
}
},
{
"id": "rw-ystra-labyrinth-boss3-102197-aura-492256",
"name": "Fog of Petrification",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 8000,
"initialCooldownMs": [
8000,
8000
],
"repeatCooldownMs": [
8000,
8000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "fog-of-petrification",
"durationMs": 4000
}
},
{
"id": "rw-ystra-labyrinth-boss3-102197-aura-492322",
"name": "Cursed Poison Fog",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 8000,
"initialCooldownMs": [
8000,
8000
],
"repeatCooldownMs": [
8000,
8000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "cursed-posion",
"durationMs": 4000
}
},
{
"id": "rw-ystra-labyrinth-boss3-102197-spell-492323",
"name": "Potent Poison Swipe",
"spellId": 492323,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "attack",
"castTimeMs": 1800,
"range": 5,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"radius": 5,
"telegraph": {
"shape": "circle",
"anchor": "source",
"warningMs": 1800,
"radius": 5
}
},
{
"id": "rw-ystra-labyrinth-boss3-102197-aura-492324",
"name": "Demon's Roar",
"delivery": "area",
"target": "self",
"school": "arcane",
"animation": "cast",
"castTimeMs": 1200,
"range": 0,
"cooldownMs": 15000,
"initialCooldownMs": [
15000,
15000
],
"repeatCooldownMs": [
15000,
15000
],
"damageMultiplier": 0,
"effect": {
"kind": "aura",
"aura": "demon-s-roar",
"durationMs": 7500
}
},
{
"id": "rw-ystra-labyrinth-boss3-102197-spell-492327",
"name": "Charge Assault",
"spellId": 492327,
"delivery": "area",
"target": "nearby-party",
"school": "physical",
"animation": "cast",
"castTimeMs": 1800,
"range": 30,
"cooldownMs": 6500,
"initialCooldownMs": [
6500,
6500
],
"repeatCooldownMs": [
6500,
6500
],
"damageMultiplier": 0.85,
"telegraph": {
"shape": "line",
"anchor": "source",
"warningMs": 1800,
"length": 30,
"width": 2
}
}
]
},
@@ -112,6 +298,6 @@ export const YSTRA_LABYRINTH_BOSS3_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -48,6 +48,6 @@ export const ZURHIDON_STRONGHOLD_124_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
@@ -16,7 +16,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -76,7 +76,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -136,7 +136,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -176,7 +176,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -256,7 +256,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -336,7 +336,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -416,7 +416,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -456,7 +456,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -536,7 +536,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -576,7 +576,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -616,7 +616,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -656,7 +656,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -696,7 +696,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -736,7 +736,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -836,7 +836,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -876,7 +876,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -936,7 +936,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1016,7 +1016,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1096,7 +1096,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1136,7 +1136,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1196,7 +1196,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1276,7 +1276,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1316,7 +1316,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1376,7 +1376,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -1436,7 +1436,7 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
"leashRange": 24,
"attacks": [
{
"id": "rw-zurhidon-stronghold-basic-attack",
"id": "basic-attack",
"name": "Basic Attack",
"delivery": "melee",
"target": "primary",
@@ -3193,6 +3193,6 @@ export const ZURHIDON_STRONGHOLD_POPULATION_PROVENANCE = {
"runeWakerIdsAreNamespaced": true,
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
"sourceSpellCatalogSha256": "ad29efc1d4906fdd4f2ac7a69316cac32d8fcbaea674678f050265c86e0c506c",
"sourceSpellCatalogSha256": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
} as const;
+20 -1
View File
@@ -1,6 +1,10 @@
import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
MANASTORM_DEFAULT_PORTAL_READY_CUE_ID,
MANASTORM_PORTAL_AUDIO_ROOT,
MANASTORM_PORTAL_AUDIO_CUES,
manastormCueForStatusTransition,
manastormPortalAudioCue,
@@ -10,6 +14,8 @@ import {
} from "./manastormClientCues";
import { useManastormStore } from "./manastormStore";
const projectDirectory = fileURLToPath(new URL("../..", import.meta.url));
describe("Manastorm imported client cues", () => {
afterEach(() => {
stopManastormClientCue();
@@ -25,10 +31,23 @@ describe("Manastorm imported client cues", () => {
.toHaveLength(3);
for (const cue of MANASTORM_PORTAL_AUDIO_CUES) {
expect(cue.fileName).toMatch(/^Manastorm_BossKill_.+\.wav$/);
expect(cue.url).toContain(cue.fileName);
expect(cue.url).toBe(`${MANASTORM_PORTAL_AUDIO_ROOT}/${cue.fileName}`);
const audio = readFileSync(path.join(projectDirectory, "public", cue.url.slice(1)));
expect(audio.subarray(0, 4).toString("ascii")).toBe("RIFF");
expect(audio.subarray(8, 12).toString("ascii")).toBe("WAVE");
}
});
it("keeps optional portal audio out of Vite source imports", () => {
const source = readFileSync(
new URL("./manastormClientCues.ts", import.meta.url),
"utf8",
);
expect(source).not.toContain("LadiksMPQEditor");
expect(source).not.toMatch(/new URL\s*\(/);
});
it("maps only an actual portal-ready state edge to the automatic cue", () => {
expect(manastormCueForStatusTransition("awaiting-loot", "portal-ready"))
.toBe(MANASTORM_DEFAULT_PORTAL_READY_CUE_ID);
+13 -26
View File
@@ -1,3 +1,5 @@
import { playMasterAudioCue } from "./combatAudio";
export type ManastormPortalCueSignal = "portal-coming-up" | "portal-ready";
export interface ManastormPortalAudioCue {
@@ -24,26 +26,12 @@ export interface ManastormAudioHandle {
export type ManastormAudioFactory = (url: string) => ManastormAudioHandle;
const genericPortalComingUpUrl = new URL(
"../../../LadiksMPQEditor/patch-O/sound/Manastorm/Manastorm_BossKill_GenericPortalComingUp.wav",
import.meta.url,
).href;
const singlePortalComingUpUrl = new URL(
"../../../LadiksMPQEditor/patch-O/sound/Manastorm/Manastorm_BossKill_1PortalComingUp.wav",
import.meta.url,
).href;
const alternateSinglePortalComingUpUrl = new URL(
"../../../LadiksMPQEditor/patch-O/sound/Manastorm/Manastorm_BossKill_1PortalComingUp_2.wav",
import.meta.url,
).href;
const genericPortalsReadyUrl = new URL(
"../../../LadiksMPQEditor/patch-O/sound/Manastorm/Manastorm_BossKill_GenericPortalsReady.wav",
import.meta.url,
).href;
const alternateGenericPortalsReadyUrl = new URL(
"../../../LadiksMPQEditor/patch-O/sound/Manastorm/Manastorm_BossKill_GenericPortalsReady_2.wav",
import.meta.url,
).href;
export const MANASTORM_PORTAL_AUDIO_ROOT =
"/assets/game/combat-effects/audio/manastorm";
function packagedPortalAudioUrl(fileName: string): string {
return `${MANASTORM_PORTAL_AUDIO_ROOT}/${fileName}`;
}
export const MANASTORM_PORTAL_AUDIO_CUES = [
{
@@ -52,7 +40,7 @@ export const MANASTORM_PORTAL_AUDIO_CUES = [
variant: "generic",
alternate: false,
fileName: "Manastorm_BossKill_GenericPortalComingUp.wav",
url: genericPortalComingUpUrl,
url: packagedPortalAudioUrl("Manastorm_BossKill_GenericPortalComingUp.wav"),
},
{
id: "single-portal-coming-up",
@@ -60,7 +48,7 @@ export const MANASTORM_PORTAL_AUDIO_CUES = [
variant: "single",
alternate: false,
fileName: "Manastorm_BossKill_1PortalComingUp.wav",
url: singlePortalComingUpUrl,
url: packagedPortalAudioUrl("Manastorm_BossKill_1PortalComingUp.wav"),
},
{
id: "single-portal-coming-up-alt",
@@ -68,7 +56,7 @@ export const MANASTORM_PORTAL_AUDIO_CUES = [
variant: "single",
alternate: true,
fileName: "Manastorm_BossKill_1PortalComingUp_2.wav",
url: alternateSinglePortalComingUpUrl,
url: packagedPortalAudioUrl("Manastorm_BossKill_1PortalComingUp_2.wav"),
},
{
id: "generic-portals-ready",
@@ -76,7 +64,7 @@ export const MANASTORM_PORTAL_AUDIO_CUES = [
variant: "generic",
alternate: false,
fileName: "Manastorm_BossKill_GenericPortalsReady.wav",
url: genericPortalsReadyUrl,
url: packagedPortalAudioUrl("Manastorm_BossKill_GenericPortalsReady.wav"),
},
{
id: "generic-portals-ready-alt",
@@ -84,7 +72,7 @@ export const MANASTORM_PORTAL_AUDIO_CUES = [
variant: "generic",
alternate: true,
fileName: "Manastorm_BossKill_GenericPortalsReady_2.wav",
url: alternateGenericPortalsReadyUrl,
url: packagedPortalAudioUrl("Manastorm_BossKill_GenericPortalsReady_2.wav"),
},
] as const satisfies readonly ManastormPortalAudioCue[];
@@ -167,4 +155,3 @@ export function stopManastormClientCue(): void {
activeAudio?.pause();
activeAudio = null;
}
import { playMasterAudioCue } from "./combatAudio";
+3
View File
@@ -41,6 +41,9 @@ describe("catalog-driven party rotations", () => {
school: "shadow",
interruptible: true,
targetActorId: "__player__",
sourcePosition: [0, 0, 0],
targetPosition: [1, 0, 0],
sourceYaw: Math.PI / 2,
startedAt: 1_000,
completesAt: 5_000,
},
+75
View File
@@ -0,0 +1,75 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
import { DUNGEON_DEFINITIONS } from "./dungeonRegistry";
import { normalizeEnemyAttacks } from "./enemyAttacks";
const catalog = JSON.parse(readFileSync(
"dungeon-pipeline/boss-mechanics.json",
"utf8",
)) as {
readonly bosses: Readonly<Record<string, {
readonly source?: { readonly kind?: string };
readonly mechanics: readonly { readonly id: string }[];
}>>;
};
describe("modular RuneWaker boss mechanics", () => {
const dungeons = DUNGEON_DEFINITIONS.filter(
(definition) => definition.expansion === "runewaker",
);
const bosses = dungeons.flatMap((dungeon) => (
Object.values(dungeon.entities)
.filter((entity) => entity.kind === "boss")
.map((entity) => ({ dungeon, entity }))
));
it("gives every packaged boss a catalog-backed special-mechanic loadout", () => {
expect(bosses).toHaveLength(56);
for (const { dungeon, entity } of bosses) {
const label = `${dungeon.id}:${entity.id}`;
const authored = catalog.bosses[entity.id];
expect(authored, label).toBeDefined();
expect(authored.mechanics.length, label).toBeGreaterThan(0);
expect(entity.combat?.mechanicIds, label).toEqual(
authored.mechanics.map((mechanic) => mechanic.id),
);
expect(entity.combat?.attacks[0]?.id, label).toBe("basic-attack");
expect(entity.combat?.attacks.length, label).toBeGreaterThan(1);
}
});
it("normalizes every harmful spatial mechanic with matching warning geometry", () => {
let telegraphed = 0;
let healthTriggered = 0;
for (const { dungeon, entity } of bosses) {
const label = `${dungeon.id}:${entity.id}`;
const attacks = normalizeEnemyAttacks(entity.combat?.attacks);
for (const attack of attacks.slice(1)) {
if (attack.condition?.kind === "self-health-percent") healthTriggered += 1;
if (attack.telegraph) {
telegraphed += 1;
expect(attack.castTimeMs, label).toBeGreaterThanOrEqual(attack.telegraph.warningMs);
if (attack.telegraph.shape === "circle") {
expect(attack.telegraph.radius, label).toBeGreaterThan(0);
} else {
expect(attack.telegraph.length, label).toBeGreaterThan(0);
}
}
}
}
expect(telegraphed).toBeGreaterThanOrEqual(100);
expect(healthTriggered).toBeGreaterThanOrEqual(25);
});
it("keeps source-gap fallbacks explicit instead of presenting them as recovered mechanics", () => {
const sourceGaps = bosses.filter(({ entity }) => (
catalog.bosses[entity.id]?.source?.kind === "healer-man-source-gap"
));
expect(sourceGaps.map(({ entity }) => entity.id).sort()).toEqual([
"rw-fa-100070",
"rw-fa-100561",
"rw-fa-100562",
"rw-fa-100563",
]);
});
});
+3 -3
View File
@@ -86,14 +86,14 @@ describe("portable RuneWaker source-spell mappings", () => {
expect(source?.runtime).toBeDefined();
expect(attack).toMatchObject({
name: source?.name,
delivery: source?.runtime?.delivery,
target: source?.runtime?.target,
school: source?.runtime?.school,
animation: source?.runtime?.animation,
range: source?.runtime?.range,
cooldownMs: source?.runtime?.cooldownMs,
damageMultiplier: source?.runtime?.damageMultiplier,
});
expect(["melee", "projectile", "area"]).toContain(attack.delivery);
expect(["primary", "nearby-party", "random-party"]).toContain(attack.target);
expect(attack.range).toBeGreaterThanOrEqual(source?.runtime?.range ?? 0);
}
}
}
+4 -1
View File
@@ -18,6 +18,9 @@ const campaignIds = new Set(campaign.dungeons.map((dungeon) => dungeon.slug));
const basicOnlyBossExceptions = new Set([
"wailing-caverns:kresh",
"wailing-caverns:deviate-faerie-dragon",
"blackrock-spire:entry-9218",
"blackrock-spire:entry-9219",
"blackrock-spire:entry-10376",
]);
const noTrashExceptions = new Set(["trial-of-the-champion"]);
const animationCache = new Map<string, Promise<readonly { readonly name: string }[]>>();
@@ -171,4 +174,4 @@ describe("WoW dungeon combat and animation coverage", () => {
}
}
}, 120_000);
});
});