Release Healer Man 0.1.21
This commit is contained in:
1 parent
699fc858c9
commit
f6f2375145
210 files changed
+976039
-6247
No files matched your search
+29
-16
@@ -6,7 +6,12 @@ import { advanceDungeonPartyCombat } from "./partyRuntime";
|
||||
import { useGameStore } from "./store";
|
||||
import { activateManastormActiveSpellLoadout } from "./manastormSpellLoadout";
|
||||
import { useManastormStore } from "./manastormStore";
|
||||
import { useWailingEncounterStore } from "./wailingCavernsEncounter";
|
||||
import {
|
||||
activateDungeonEncounterDefinitions,
|
||||
advanceDungeonEncounters,
|
||||
resetDungeonEncounters,
|
||||
} from "./dungeonEncounterRuntime";
|
||||
import { requireDungeonDefinition } from "./dungeonRegistry";
|
||||
import {
|
||||
onlineSessionIsActive,
|
||||
onlineSessionOwnsWorldSimulation,
|
||||
@@ -21,9 +26,10 @@ import { combatSimulationTimerShouldRun } from "./combatSimulationLifecycle";
|
||||
export function CombatBridge() {
|
||||
const character = useShellStore((state) => state.activeCharacter);
|
||||
const gameMode = useGameStore((state) => state.gameMode);
|
||||
const activeDungeonId = useGameStore((state) => state.activeDungeonId);
|
||||
const activeRole = useShellStore((state) => state.activeDungeonRole);
|
||||
const resetRevision = useGameStore((state) => state.resetRevision);
|
||||
const mountedResetRevision = useRef(resetRevision);
|
||||
const encounterResetRevision = useGameStore((state) => state.encounterResetRevision);
|
||||
const mountedResetRevision = useRef(encounterResetRevision);
|
||||
|
||||
useEffect(() => {
|
||||
if (!character) return;
|
||||
@@ -37,11 +43,16 @@ export function CombatBridge() {
|
||||
}, [activeRole, character?.id, gameMode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (mountedResetRevision.current === resetRevision) return;
|
||||
mountedResetRevision.current = resetRevision;
|
||||
const definition = requireDungeonDefinition(activeDungeonId);
|
||||
activateDungeonEncounterDefinitions(activeDungeonId, definition.encounters ?? []);
|
||||
}, [activeDungeonId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (mountedResetRevision.current === encounterResetRevision) return;
|
||||
mountedResetRevision.current = encounterResetRevision;
|
||||
useCombatStore.getState().resetEncounter();
|
||||
useWailingEncounterStore.getState().reset();
|
||||
}, [resetRevision]);
|
||||
resetDungeonEncounters("dungeon", useGameStore.getState().activeDungeonId);
|
||||
}, [encounterResetRevision]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!character) return;
|
||||
@@ -113,15 +124,17 @@ export function CombatBridge() {
|
||||
advanceDungeonPartyCombat(epochNow);
|
||||
}
|
||||
useCombatStore.getState().synchronizeActors();
|
||||
if (
|
||||
onlineSessionOwnsWorldSimulation()
|
||||
&& game.gameMode === "dungeon"
|
||||
&& game.activeDungeonId === "wailing-caverns"
|
||||
) {
|
||||
useWailingEncounterStore.getState().advance(
|
||||
useCombatStore.getState().mobs,
|
||||
epochNow,
|
||||
);
|
||||
if (onlineSessionOwnsWorldSimulation() && game.gameMode === "dungeon") {
|
||||
const mobs = useCombatStore.getState().mobs;
|
||||
const registeredEntityIds = Object.keys(mobs);
|
||||
advanceDungeonEncounters(game.activeDungeonId, {
|
||||
kind: "tick",
|
||||
now: epochNow,
|
||||
facts: {
|
||||
registeredEntityIds,
|
||||
deadEntityIds: registeredEntityIds.filter((id) => mobs[id].dead),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { applyDungeonEncounterEffect } from "./DungeonEncounterEffectBridge";
|
||||
import { clearPartyRuntimeRegistry, getPartyRuntimePosition, updatePartyRuntimePosition } from "./partyRuntimeRegistry";
|
||||
import { usePartyStore } from "./partyStore";
|
||||
import { useGameStore } from "./store";
|
||||
|
||||
afterEach(() => {
|
||||
clearPartyRuntimeRegistry();
|
||||
usePartyStore.setState({ members: [] });
|
||||
});
|
||||
|
||||
describe("dungeon encounter effects", () => {
|
||||
it("teleports the player and every current party runtime actor", () => {
|
||||
usePartyStore.getState().initializeParty("encounter-effect-test", 1, 10);
|
||||
const allyId = usePartyStore.getState().members[0].id;
|
||||
updatePartyRuntimePosition(allyId, 0, 0, 0);
|
||||
applyDungeonEncounterEffect({
|
||||
dungeonId: useGameStore.getState().activeDungeonId,
|
||||
encounterId: "test:transition",
|
||||
effect: {
|
||||
kind: "party-teleport",
|
||||
actionId: "cross-threshold",
|
||||
destination: [12, 3, -8],
|
||||
},
|
||||
}, 100);
|
||||
expect(useGameStore.getState().playerPosition).toEqual([12, 3, -8]);
|
||||
expect(getPartyRuntimePosition(allyId)).toEqual([12, 3, -8]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import { useEffect } from "react";
|
||||
import { useShellStore } from "../app/shellStore";
|
||||
import {
|
||||
dispatchDungeonEncounterSignal,
|
||||
registerDungeonEncounterEffectHandler,
|
||||
} from "./dungeonEncounterRuntime";
|
||||
import { LOOT_CATALOG, rollBossLoot } from "./lootCatalog";
|
||||
import { updatePartyRuntimePosition } from "./partyRuntimeRegistry";
|
||||
import { usePartyStore } from "./partyStore";
|
||||
import { useCombatStore } from "./combatStore";
|
||||
import { useGameStore } from "./store";
|
||||
import type { DungeonEncounterEffectEnvelope } from "./dungeonEncounterTypes";
|
||||
import { publishOnlinePartyTeleport } from "./onlineSessionRuntime";
|
||||
|
||||
function grantEncounterLoot(lootTableId: string, now: number): boolean {
|
||||
const table = LOOT_CATALOG.bosses.find((candidate) => candidate.lootTableId === lootTableId);
|
||||
if (!table) return false;
|
||||
const combat = useCombatStore.getState();
|
||||
const drops = rollBossLoot(table.bossKey, combat.level, { now });
|
||||
if (!drops.length) return false;
|
||||
const inventory = [...combat.inventory, ...drops];
|
||||
useCombatStore.setState({ inventory });
|
||||
useShellStore.getState().saveActiveInventory(inventory);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Applies platform effects emitted by the renderer-neutral encounter reducer. */
|
||||
export function DungeonEncounterEffectBridge() {
|
||||
useEffect(() => registerDungeonEncounterEffectHandler((envelope) => {
|
||||
applyDungeonEncounterEffect(envelope, Date.now());
|
||||
}), []);
|
||||
return null;
|
||||
}
|
||||
|
||||
export function applyDungeonEncounterEffect(
|
||||
{ effect }: DungeonEncounterEffectEnvelope,
|
||||
now = Date.now(),
|
||||
): void {
|
||||
if (effect.kind === "signal") {
|
||||
dispatchDungeonEncounterSignal(effect.signalId, now);
|
||||
return;
|
||||
}
|
||||
if (effect.kind === "party-teleport") {
|
||||
const game = useGameStore.getState();
|
||||
game.relocatePlayer({ footPosition: effect.destination, yaw: game.cameraYaw }, true);
|
||||
for (const member of usePartyStore.getState().members) {
|
||||
updatePartyRuntimePosition(member.id, ...effect.destination);
|
||||
}
|
||||
publishOnlinePartyTeleport(effect.destination, effect.actionId, now);
|
||||
return;
|
||||
}
|
||||
if (effect.kind === "loot-grant" && !grantEncounterLoot(effect.lootTableId, now)) {
|
||||
useShellStore.getState().setNotice(`Loot table ${effect.lootTableId} is not packaged for this encounter.`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useShellStore } from "../app/shellStore";
|
||||
import {
|
||||
exportDungeonEncounterCheckpointSnapshots,
|
||||
subscribeDungeonEncounterSnapshots,
|
||||
} from "./dungeonEncounterRuntime";
|
||||
import { DUNGEON_RUN_CHECKPOINT_VERSION } from "./dungeonRunCheckpoint";
|
||||
import { useGameStore } from "./store";
|
||||
|
||||
function checkpointSignature(dungeonId: string): string {
|
||||
const game = useGameStore.getState();
|
||||
const encounters = exportDungeonEncounterCheckpointSnapshots(dungeonId, 0);
|
||||
return JSON.stringify({
|
||||
dungeonId,
|
||||
difficultyId: game.activeDifficultyId,
|
||||
spawn: game.activeSpawn,
|
||||
encounters,
|
||||
});
|
||||
}
|
||||
|
||||
/** Persists only reset-projected state when encounter progress reaches a new stable revision. */
|
||||
export function DungeonRunCheckpointBridge() {
|
||||
const phase = useShellStore((state) => state.phase);
|
||||
const activeCharacterId = useShellStore((state) => state.activeCharacter?.id ?? null);
|
||||
const lastSignature = useRef("");
|
||||
|
||||
useEffect(() => {
|
||||
if (phase !== "game" || !activeCharacterId) return undefined;
|
||||
const save = () => {
|
||||
const game = useGameStore.getState();
|
||||
if (game.gameMode !== "dungeon" || game.gmTestMode) return;
|
||||
const signature = checkpointSignature(game.activeDungeonId);
|
||||
if (signature === lastSignature.current) return;
|
||||
lastSignature.current = signature;
|
||||
const encounters = exportDungeonEncounterCheckpointSnapshots(game.activeDungeonId, 0);
|
||||
useShellStore.getState().saveActiveDungeonRunCheckpoint({
|
||||
version: DUNGEON_RUN_CHECKPOINT_VERSION,
|
||||
dungeonId: game.activeDungeonId,
|
||||
difficultyId: game.activeDifficultyId,
|
||||
checkpointId: encounters.find((entry) => entry.checkpointId)?.checkpointId ?? "entrance",
|
||||
spawn: game.activeSpawn,
|
||||
encounters,
|
||||
savedAt: Date.now(),
|
||||
});
|
||||
};
|
||||
save();
|
||||
return subscribeDungeonEncounterSnapshots(save);
|
||||
}, [activeCharacterId, phase]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -31,6 +31,14 @@ import { useGameStore } from "./store";
|
||||
import { useManastormStore } from "./manastormStore";
|
||||
import { advanceManastormSession } from "./manastormSession";
|
||||
import { useWailingEncounterStore } from "./wailingCavernsEncounter";
|
||||
import {
|
||||
exportDungeonEncounterSnapshots,
|
||||
importDungeonEncounterSnapshots,
|
||||
} from "./dungeonEncounterRuntime";
|
||||
import {
|
||||
dispatchDungeonWorldObjectInteraction,
|
||||
dungeonInteractionCandidateAt,
|
||||
} from "./dungeonWorldInteraction";
|
||||
|
||||
const SYNC_INTERVAL_MS = 100;
|
||||
const WORLD_INTERVAL_MS = 200;
|
||||
@@ -56,6 +64,10 @@ function sharedWorld(): OnlineSharedWorld {
|
||||
}),
|
||||
);
|
||||
return {
|
||||
dungeonId: game.activeDungeonId,
|
||||
encounters: game.gameMode === "dungeon"
|
||||
? exportDungeonEncounterSnapshots(game.activeDungeonId)
|
||||
: [],
|
||||
mobs: combat.mobs,
|
||||
mobTransforms,
|
||||
partyMembers: party.members,
|
||||
@@ -89,6 +101,10 @@ function applySharedWorld(world: OnlineSharedWorld): void {
|
||||
}
|
||||
if (world.wailing) useWailingEncounterStore.setState(world.wailing as never);
|
||||
if (world.manastorm) useManastormStore.setState(world.manastorm as never);
|
||||
const activeDungeonId = useGameStore.getState().activeDungeonId;
|
||||
if (world.dungeonId === activeDungeonId && world.encounters) {
|
||||
importDungeonEncounterSnapshots(activeDungeonId, world.encounters);
|
||||
}
|
||||
}
|
||||
|
||||
function eventForRelay(
|
||||
@@ -140,6 +156,26 @@ function applyRelayEvent(
|
||||
useCombatStore.getState().lootMob(event.targetActorId, event.occurredAt || Date.now());
|
||||
return;
|
||||
}
|
||||
if (event.kind === "interact" && snapshot.authority && event.targetActorId) {
|
||||
const source = snapshot.players.find((player) => player.accountId === event.sourceAccountId);
|
||||
const candidate = source ? dungeonInteractionCandidateAt(source.position) : null;
|
||||
if (candidate?.object.id === event.targetActorId) {
|
||||
dispatchDungeonWorldObjectInteraction(
|
||||
candidate.object,
|
||||
event.sourceActorId,
|
||||
event.occurredAt || Date.now(),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event.kind === "party-teleport" && !snapshot.authority && event.position) {
|
||||
const game = useGameStore.getState();
|
||||
game.relocatePlayer({ footPosition: event.position, yaw: game.cameraYaw }, true);
|
||||
for (const member of usePartyStore.getState().members) {
|
||||
updatePartyRuntimePosition(member.id, ...event.position);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event.targetActorId === localActorId) {
|
||||
if (event.kind === "damage") {
|
||||
const sourceLevel = useCombatStore.getState().mobs[event.sourceActorId]?.level;
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildBossGuideCatalog, buildDungeonBossGuide, bossGuideOverrideKey } from "./bossGuide";
|
||||
import { DUNGEON_DEFINITION_SCHEMA_VERSION, type DungeonDefinition } from "./dungeonTypes";
|
||||
import { DUNGEON_DEFINITIONS } from "./dungeonRegistry";
|
||||
import { BOSS_GUIDE_OVERRIDES } from "./bossGuideOverrides";
|
||||
|
||||
const fixture = {
|
||||
schemaVersion: DUNGEON_DEFINITION_SCHEMA_VERSION,
|
||||
id: "guide-fixture",
|
||||
title: "Guide Fixture",
|
||||
mapId: 1,
|
||||
lfgDungeonIds: [],
|
||||
expansion: "runewaker",
|
||||
environmentMode: "runewaker-ros",
|
||||
difficultyVariants: [{ id: "normal", difficulty: "normal", spawnMask: 1, levelRange: [1, 2] }],
|
||||
defaultDifficultyId: "normal",
|
||||
availability: "available",
|
||||
presentation: {
|
||||
location: "Test",
|
||||
theme: "Test",
|
||||
summary: "Test",
|
||||
loadingMessage: "Test",
|
||||
unchartedAreaName: "Test",
|
||||
background: "#000",
|
||||
fog: { color: "#000", near: 1, far: 2 },
|
||||
ambientLight: { color: "#fff", intensity: 1 },
|
||||
hemisphereLight: { skyColor: "#fff", groundColor: "#000", intensity: 1 },
|
||||
directionalLight: { color: "#fff", intensity: 1, offset: [0, 1, 0] },
|
||||
materials: { cutoutPatterns: [], cutoutNames: [], blendPatterns: [], blendNames: [], additivePatterns: [], additiveNames: [] },
|
||||
map: { width: 100, height: 100, padding: 1, contours: [] },
|
||||
},
|
||||
assets: { visual: [], collision: [] },
|
||||
transform: { position: [0, 0, 0], rotation: [0, 0, 0], scale: 1 },
|
||||
bounds: { min: [0, 0, 0], max: [1, 1, 1] },
|
||||
entrance: { name: "Entrance", footPosition: [0, 0, 0], forward: [0, 0, 1], yaw: 0 },
|
||||
areas: [],
|
||||
entities: {
|
||||
herald: {
|
||||
id: "herald",
|
||||
name: "The Herald",
|
||||
kind: "boss",
|
||||
title: "Keeper of Tests",
|
||||
combat: {
|
||||
attacks: [
|
||||
{
|
||||
id: "basic-attack",
|
||||
name: "Basic Attack",
|
||||
delivery: "melee",
|
||||
target: "primary",
|
||||
school: "physical",
|
||||
animation: "attack",
|
||||
range: 4,
|
||||
cooldownMs: 2_000,
|
||||
damageMultiplier: 1,
|
||||
},
|
||||
{
|
||||
id: "falling-test",
|
||||
name: "Falling Test",
|
||||
spellId: 42,
|
||||
delivery: "area",
|
||||
target: "random-party",
|
||||
school: "arcane",
|
||||
animation: "cast",
|
||||
castTimeMs: 2_000,
|
||||
range: 30,
|
||||
cooldownMs: 12_000,
|
||||
initialCooldownMs: [8_000, 10_000],
|
||||
repeatCooldownMs: [12_000, 14_000],
|
||||
maximumCasts: 1,
|
||||
damageMultiplier: 1.2,
|
||||
telegraph: { shape: "circle", anchor: "target", radius: 6, warningMs: 2_000 },
|
||||
condition: { kind: "self-health-percent", minimum: 0, maximum: 50 },
|
||||
},
|
||||
{
|
||||
id: "source-gap-sweep",
|
||||
name: "Fallback Sweep",
|
||||
delivery: "area",
|
||||
target: "nearby-party",
|
||||
school: "physical",
|
||||
animation: "attack",
|
||||
castTimeMs: 1_800,
|
||||
range: 8,
|
||||
cooldownMs: 10_000,
|
||||
damageMultiplier: 1,
|
||||
telegraph: { shape: "cone", anchor: "source", length: 8, angleDegrees: 90, warningMs: 1_800 },
|
||||
},
|
||||
],
|
||||
},
|
||||
visual: { primaryColor: "#000", accentColor: "#fff", scale: 1, archetype: "humanoid" },
|
||||
},
|
||||
sentinel: {
|
||||
id: "sentinel",
|
||||
name: "Silent Sentinel",
|
||||
kind: "boss",
|
||||
title: "The Unscripted",
|
||||
combat: { attacks: [] },
|
||||
visual: { primaryColor: "#000", accentColor: "#fff", scale: 1, archetype: "humanoid" },
|
||||
},
|
||||
},
|
||||
staticSpawns: [
|
||||
{ id: "herald-spawn", entityId: "herald", position: [0, 0, 0] },
|
||||
{ id: "sentinel-spawn", entityId: "sentinel", position: [1, 0, 0] },
|
||||
],
|
||||
roamingPacks: [],
|
||||
bosses: [
|
||||
{ id: "herald-spawn", name: "The Herald", position: [0, 0, 0] },
|
||||
{ id: "sentinel-spawn", name: "Silent Sentinel", position: [1, 0, 0] },
|
||||
],
|
||||
objectiveOrder: [],
|
||||
navigationLinks: [],
|
||||
serverGameObjects: [],
|
||||
provenance: { clientBuild: "test", clientArchiveHashes: {}, databaseAdapter: "test", coordinateTransform: "test" },
|
||||
validation: { status: "green", blockers: [], warnings: [] },
|
||||
} as const satisfies DungeonDefinition;
|
||||
|
||||
describe("boss guide model", () => {
|
||||
it("derives health triggers, timing, telegraph geometry, instructions, and fidelity from runtime attacks", () => {
|
||||
const guide = buildDungeonBossGuide(fixture, {
|
||||
[bossGuideOverrideKey("guide-fixture", "herald", "falling-test")]: {
|
||||
counterplay: "Use the authored safe spot.",
|
||||
},
|
||||
});
|
||||
const mechanic = guide.bosses[0].mechanics[0];
|
||||
|
||||
expect(mechanic).toMatchObject({
|
||||
id: "falling-test",
|
||||
trigger: "When the boss reaches 50% health",
|
||||
timing: "First: 8s–10s · Repeats: 12s–14s · Cast: 2s · Maximum uses: 1",
|
||||
targetAndEffect: "Arcane damage to a random party member (1.2× boss power)",
|
||||
telegraph: "6m radius circle centered on the target · 2s warning",
|
||||
counterplay: "Use the authored safe spot.",
|
||||
fidelity: "source",
|
||||
fidelityLabel: "Source backed",
|
||||
});
|
||||
expect(mechanic.testInstruction).toContain("cross the threshold");
|
||||
expect(guide.bosses[0].mechanics[1]).toMatchObject({
|
||||
fidelity: "fallback",
|
||||
fidelityLabel: "HealerMan fallback",
|
||||
});
|
||||
});
|
||||
|
||||
it("omits basic attacks and preserves an honest empty entry for a boss with no specials", () => {
|
||||
const guide = buildDungeonBossGuide(fixture);
|
||||
expect(guide.bosses[0].mechanics.map((mechanic) => mechanic.id)).toEqual([
|
||||
"falling-test",
|
||||
"source-gap-sweep",
|
||||
]);
|
||||
expect(guide.bosses[1].mechanics).toEqual([]);
|
||||
});
|
||||
|
||||
it("covers every registered boss objective and every non-basic runtime mechanic", () => {
|
||||
const catalog = buildBossGuideCatalog(DUNGEON_DEFINITIONS, BOSS_GUIDE_OVERRIDES);
|
||||
const guideByDungeon = new Map(catalog.map((guide) => [guide.id, guide]));
|
||||
|
||||
for (const dungeon of DUNGEON_DEFINITIONS) {
|
||||
const guide = guideByDungeon.get(dungeon.id);
|
||||
expect(guide, dungeon.id).toBeDefined();
|
||||
expect(guide!.bosses.length, dungeon.id).toBeGreaterThanOrEqual(dungeon.bosses.length);
|
||||
for (const objective of dungeon.bosses) {
|
||||
const boss = guide!.bosses.find((entry) => entry.id === objective.id);
|
||||
expect(boss, `${dungeon.id}:${objective.name}`).toBeDefined();
|
||||
expect(boss!.entityId, `${dungeon.id}:${objective.name}`).toBeDefined();
|
||||
}
|
||||
for (const boss of guide!.bosses) {
|
||||
if (!boss.entityId) continue;
|
||||
const expectedIds = dungeon.entities[boss.entityId]?.combat?.attacks
|
||||
.filter((attack) => attack.id !== "basic-attack")
|
||||
.map((attack) => attack.id) ?? [];
|
||||
expect(boss.mechanics.map((mechanic) => mechanic.id), `${dungeon.id}:${boss.name}`).toEqual(expectedIds);
|
||||
for (const mechanic of boss.mechanics) {
|
||||
expect(mechanic.trigger, `${dungeon.id}:${boss.name}:${mechanic.name}:trigger`).not.toBe("");
|
||||
expect(mechanic.timing, `${dungeon.id}:${boss.name}:${mechanic.name}:timing`).not.toBe("");
|
||||
expect(mechanic.telegraph, `${dungeon.id}:${boss.name}:${mechanic.name}:telegraph`).not.toBe("");
|
||||
expect(mechanic.counterplay, `${dungeon.id}:${boss.name}:${mechanic.name}:counterplay`).not.toBe("");
|
||||
expect(mechanic.testInstruction, `${dungeon.id}:${boss.name}:${mechanic.name}:test`).not.toBe("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const archaedas = guideByDungeon.get("uldaman")!.bosses.find((boss) => boss.name === "Archaedas")!;
|
||||
expect(archaedas.mechanics.find((mechanic) => mechanic.id === "archaedas-awaken-earthen-guardians"))
|
||||
.toMatchObject({
|
||||
fidelityLabel: "Source backed",
|
||||
counterplay: "At 70% health, the tank gathers the six awakened guardians while the party clears them together.",
|
||||
});
|
||||
const flowingChaos = guideByDungeon.get("forsaken-abbey")!.bosses.find((boss) => boss.name === "Flowing Chaos")!;
|
||||
expect(flowingChaos.mechanics[0].fidelityLabel).toBe("HealerMan fallback");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,350 @@
|
||||
import type { DungeonDefinition, DungeonExpansion } from "./dungeonTypes";
|
||||
import {
|
||||
normalizeEnemyAttack,
|
||||
type EnemyAbilityCondition,
|
||||
type EnemyAbilityEffect,
|
||||
type EnemyAttackDefinition,
|
||||
type EnemyAttackTarget,
|
||||
type EnemyAttackTelegraph,
|
||||
} from "./enemyAttacks";
|
||||
import type { PopulationEntityDefinition } from "./mobPopulation";
|
||||
|
||||
export type BossGuideFidelity = "source" | "fallback" | "authored";
|
||||
|
||||
export interface BossMechanicGuideOverride {
|
||||
readonly trigger?: string;
|
||||
readonly timing?: string;
|
||||
readonly targetAndEffect?: string;
|
||||
readonly telegraph?: string;
|
||||
readonly counterplay?: string;
|
||||
readonly testInstruction?: string;
|
||||
}
|
||||
|
||||
export type BossMechanicGuideOverrides = Readonly<Record<string, BossMechanicGuideOverride>>;
|
||||
|
||||
export interface BossMechanicGuideEntry {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly trigger: string;
|
||||
readonly timing: string;
|
||||
readonly targetAndEffect: string;
|
||||
readonly telegraph: string;
|
||||
readonly counterplay: string;
|
||||
readonly testInstruction: string;
|
||||
readonly fidelity: BossGuideFidelity;
|
||||
readonly fidelityLabel: string;
|
||||
readonly sourceDetail: string;
|
||||
}
|
||||
|
||||
export interface BossGuideEntry {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly title?: string;
|
||||
readonly entityId?: string;
|
||||
readonly mechanics: readonly BossMechanicGuideEntry[];
|
||||
}
|
||||
|
||||
export interface DungeonBossGuide {
|
||||
readonly id: string;
|
||||
readonly title: string;
|
||||
readonly expansion: DungeonExpansion;
|
||||
readonly bosses: readonly BossGuideEntry[];
|
||||
}
|
||||
|
||||
export function bossGuideOverrideKey(
|
||||
dungeonId: string,
|
||||
bossEntityId: string,
|
||||
mechanicId: string,
|
||||
): string {
|
||||
return `${dungeonId}:${bossEntityId}:${mechanicId}`;
|
||||
}
|
||||
|
||||
function seconds(milliseconds: number): string {
|
||||
const value = milliseconds / 1_000;
|
||||
return Number.isInteger(value) ? `${value}s` : `${value.toFixed(1)}s`;
|
||||
}
|
||||
|
||||
function rangeSeconds(range: readonly [number, number]): string {
|
||||
return range[0] === range[1]
|
||||
? seconds(range[0])
|
||||
: `${seconds(range[0])}–${seconds(range[1])}`;
|
||||
}
|
||||
|
||||
function titleCase(value: string): string {
|
||||
return value.replace(/-/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
|
||||
}
|
||||
|
||||
function describeCondition(condition: EnemyAbilityCondition | undefined): string {
|
||||
if (!condition || condition.kind === "in-combat") return "While the boss is in combat";
|
||||
if (condition.kind === "self-health-percent") {
|
||||
if (condition.minimum > 0) {
|
||||
return `While the boss is between ${Math.round(condition.minimum)}% and ${Math.round(condition.maximum)}% health`;
|
||||
}
|
||||
return `When the boss reaches ${Math.round(condition.maximum)}% health`;
|
||||
}
|
||||
if (condition.kind === "friendly-missing-health") {
|
||||
return `When an ally within ${condition.range}m is missing ${Math.round(condition.amount)} health`;
|
||||
}
|
||||
return `While encounter ${condition.encounterId} is in: ${condition.states.join(", ")}`;
|
||||
}
|
||||
|
||||
function describeTiming(attack: EnemyAttackDefinition): string {
|
||||
const parts: string[] = [];
|
||||
if (attack.initialCooldownMs) parts.push(`First: ${rangeSeconds(attack.initialCooldownMs)}`);
|
||||
if (attack.repeatCooldownMs) parts.push(`Repeats: ${rangeSeconds(attack.repeatCooldownMs)}`);
|
||||
else if (attack.cooldownMs > 0) parts.push(`Cooldown: ${seconds(attack.cooldownMs)}`);
|
||||
if ((attack.castTimeMs ?? 0) > 0) parts.push(`Cast: ${seconds(attack.castTimeMs!)}`);
|
||||
if (attack.maximumCasts) parts.push(`Maximum uses: ${attack.maximumCasts}`);
|
||||
return parts.join(" · ") || "Immediate encounter action";
|
||||
}
|
||||
|
||||
const TARGET_LABELS: Readonly<Record<EnemyAttackTarget, string>> = {
|
||||
primary: "the boss's current target",
|
||||
"random-party": "a random party member",
|
||||
"nearby-party": "party members in the affected area",
|
||||
self: "the boss",
|
||||
"lowest-health-friendly": "the lowest-health enemy ally",
|
||||
};
|
||||
|
||||
function describeEffect(effect: EnemyAbilityEffect | undefined, attack: EnemyAttackDefinition): string {
|
||||
const target = TARGET_LABELS[attack.target];
|
||||
if (!effect || effect.kind === "damage") {
|
||||
const multiplier = effect?.multiplier ?? attack.damageMultiplier;
|
||||
return `${titleCase(attack.school)} damage to ${target} (${Number(multiplier.toFixed(2))}× boss power)`;
|
||||
}
|
||||
if (effect.kind === "heal") return `Heals ${target} for ${effect.multiplier}× boss power`;
|
||||
if (effect.kind === "aura") {
|
||||
const magnitude = effect.magnitude === undefined ? "" : ` at ${effect.magnitude} strength`;
|
||||
return `Applies ${titleCase(effect.aura)} to ${target} for ${seconds(effect.durationMs)}${magnitude}`;
|
||||
}
|
||||
if (effect.kind === "target-aura") {
|
||||
return `Applies ${titleCase(effect.aura)} to ${target} for ${seconds(effect.durationMs)}`;
|
||||
}
|
||||
if (effect.kind === "control") {
|
||||
return `${titleCase(effect.mechanic)} controls ${target} for ${seconds(effect.durationMs)}`;
|
||||
}
|
||||
if (effect.kind === "transform") return `Transforms ${target} into ${titleCase(effect.form)}`;
|
||||
if (effect.kind === "summon") return `Summons ${effect.count} add${effect.count === 1 ? "" : "s"} (creature ${effect.creatureEntry})`;
|
||||
if (effect.kind === "call-for-help") return `Calls hostile allies within ${effect.radius}m into combat`;
|
||||
return `Advances encounter event “${effect.eventId}”`;
|
||||
}
|
||||
|
||||
function describeTelegraph(telegraph: EnemyAttackTelegraph | undefined, attack: EnemyAttackDefinition): string {
|
||||
if (!telegraph) {
|
||||
const warning = (attack.castTimeMs ?? 0) > 0 ? ` with a ${seconds(attack.castTimeMs!)} cast warning` : "";
|
||||
return attack.effect?.kind === "heal"
|
||||
|| attack.effect?.kind === "aura"
|
||||
|| attack.effect?.kind === "transform"
|
||||
|| attack.effect?.kind === "summon"
|
||||
|| attack.effect?.kind === "call-for-help"
|
||||
|| attack.effect?.kind === "encounter-event"
|
||||
? `No harmful ground area${warning}`
|
||||
: `Targeted mechanic; no ground area${warning}`;
|
||||
}
|
||||
const anchor = telegraph.anchor === "source" ? "centered on the boss" : "centered on the target";
|
||||
if (telegraph.shape === "circle") {
|
||||
return `${telegraph.radius}m radius circle ${anchor} · ${seconds(telegraph.warningMs)} warning`;
|
||||
}
|
||||
if (telegraph.shape === "cone") {
|
||||
return `${telegraph.length}m, ${telegraph.angleDegrees}° frontal cone · ${seconds(telegraph.warningMs)} warning`;
|
||||
}
|
||||
return `${telegraph.length}m × ${telegraph.width}m line from the boss · ${seconds(telegraph.warningMs)} warning`;
|
||||
}
|
||||
|
||||
function effectFollowup(effect: EnemyAbilityEffect | undefined): string {
|
||||
if (!effect) return "Heal the affected party members after it resolves.";
|
||||
if (effect.kind === "control") return "Stabilize the controlled player and remove the control if an available ability allows it.";
|
||||
if (effect.kind === "target-aura") return "Watch the affected player's debuff and respond with healing or removal if the effect permits it.";
|
||||
if (effect.kind === "summon") return "Swap damage to the summoned adds and keep them away from vulnerable party members.";
|
||||
if (effect.kind === "heal") return "Interrupt the heal when possible; otherwise plan for the boss to recover health.";
|
||||
if (effect.kind === "call-for-help") return "Clear or control nearby enemies before this ability resolves.";
|
||||
if (effect.kind === "transform") return "Expect the boss's behavior to change after the new form appears.";
|
||||
if (effect.kind === "encounter-event") return "Watch for the encounter-state change and handle the newly activated objective.";
|
||||
if (effect.kind === "aura") return "Adjust damage and healing for the aura while it remains active.";
|
||||
return "Heal the affected party members after it resolves.";
|
||||
}
|
||||
|
||||
function describeCounterplay(attack: EnemyAttackDefinition): string {
|
||||
const telegraph = attack.telegraph;
|
||||
let movement = "";
|
||||
if (telegraph?.shape === "circle") {
|
||||
movement = telegraph.anchor === "target"
|
||||
? "Move out of the marked circle; if it follows a player, carry it away from the group. "
|
||||
: "Leave the circle around the boss before the warning expires. ";
|
||||
} else if (telegraph?.shape === "cone") {
|
||||
movement = "Move beside or behind the boss; the tank should aim the frontal away from the party. ";
|
||||
} else if (telegraph?.shape === "line") {
|
||||
movement = "Sidestep the marked line before the warning expires. ";
|
||||
} else if (attack.effect?.kind !== "heal" && attack.interruptible !== false && (attack.castTimeMs ?? 0) > 0) {
|
||||
movement = "Interrupt the cast when possible. ";
|
||||
}
|
||||
return `${movement}${effectFollowup(attack.effect)}`.trim();
|
||||
}
|
||||
|
||||
function describeTestInstruction(attack: EnemyAttackDefinition): string {
|
||||
const checks: string[] = [];
|
||||
if (attack.condition?.kind === "self-health-percent") {
|
||||
checks.push(`stop just above ${Math.round(attack.condition.maximum)}% health, then cross the threshold`);
|
||||
} else if (attack.initialCooldownMs) {
|
||||
checks.push(attack.initialCooldownMs[1] === 0
|
||||
? "engage the boss and watch the opening cast"
|
||||
: `stay in combat through the ${rangeSeconds(attack.initialCooldownMs)} first-use window`);
|
||||
} else {
|
||||
checks.push("stay in combat until the mechanic becomes ready");
|
||||
}
|
||||
if (attack.telegraph) {
|
||||
checks.push(`confirm the ${attack.telegraph.shape} appears for ${seconds(attack.telegraph.warningMs)}`);
|
||||
} else if ((attack.castTimeMs ?? 0) > 0) {
|
||||
checks.push(`confirm the ${seconds(attack.castTimeMs!)} cast warning appears`);
|
||||
}
|
||||
checks.push("verify the stated target/effect when it resolves");
|
||||
return `${checks[0]}; ${checks.slice(1).join(", then ")}.`;
|
||||
}
|
||||
|
||||
function mechanicFidelity(
|
||||
dungeon: DungeonDefinition,
|
||||
attack: EnemyAttackDefinition,
|
||||
): Pick<BossMechanicGuideEntry, "fidelity" | "fidelityLabel" | "sourceDetail"> {
|
||||
if (/source-gap|fallback/i.test(attack.id)) {
|
||||
return {
|
||||
fidelity: "fallback",
|
||||
fidelityLabel: "HealerMan fallback",
|
||||
sourceDetail: "Playable behavior is supplied because the original encounter evidence is incomplete.",
|
||||
};
|
||||
}
|
||||
if (attack.source) {
|
||||
return {
|
||||
fidelity: "source",
|
||||
fidelityLabel: "Source backed",
|
||||
sourceDetail: attack.source.comment || `Server script row ${attack.source.rowId}`,
|
||||
};
|
||||
}
|
||||
if (dungeon.expansion === "runewaker") {
|
||||
return {
|
||||
fidelity: "source",
|
||||
fidelityLabel: "Source backed",
|
||||
sourceDetail: attack.spellId
|
||||
? `RuneWaker spell ${attack.spellId}`
|
||||
: "RuneWaker spell or encounter-script import",
|
||||
};
|
||||
}
|
||||
if (/azeroth|trinity/i.test(dungeon.provenance.databaseAdapter)) {
|
||||
return {
|
||||
fidelity: "source",
|
||||
fidelityLabel: "Source backed",
|
||||
sourceDetail: attack.spellId
|
||||
? `AzerothCore encounter import · spell ${attack.spellId}`
|
||||
: "AzerothCore encounter-script import",
|
||||
};
|
||||
}
|
||||
return {
|
||||
fidelity: "authored",
|
||||
fidelityLabel: "Gameplay authored",
|
||||
sourceDetail: attack.spellId ? `Spell ${attack.spellId}` : "HealerMan encounter definition",
|
||||
};
|
||||
}
|
||||
|
||||
function mechanicGuide(
|
||||
dungeon: DungeonDefinition,
|
||||
entityId: string,
|
||||
sourceAttack: EnemyAttackDefinition,
|
||||
overrides: BossMechanicGuideOverrides,
|
||||
): BossMechanicGuideEntry {
|
||||
const attack = normalizeEnemyAttack(sourceAttack);
|
||||
const override = overrides[bossGuideOverrideKey(dungeon.id, entityId, attack.id)] ?? {};
|
||||
return {
|
||||
id: attack.id,
|
||||
name: attack.name,
|
||||
trigger: override.trigger ?? describeCondition(attack.condition),
|
||||
timing: override.timing ?? describeTiming(attack),
|
||||
targetAndEffect: override.targetAndEffect ?? describeEffect(attack.effect, attack),
|
||||
telegraph: override.telegraph ?? describeTelegraph(attack.telegraph, attack),
|
||||
counterplay: override.counterplay ?? describeCounterplay(attack),
|
||||
testInstruction: override.testInstruction ?? describeTestInstruction(attack),
|
||||
...mechanicFidelity(dungeon, attack),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizedName(value: string): string {
|
||||
return value.toLowerCase().replace(/\s+\d+$/, "").replace(/[^a-z0-9]+/g, "");
|
||||
}
|
||||
|
||||
function bossEntityForObjective(
|
||||
dungeon: DungeonDefinition,
|
||||
objectiveId: string,
|
||||
objectiveName: string,
|
||||
): readonly [string, PopulationEntityDefinition] | undefined {
|
||||
const staticSpawn = dungeon.staticSpawns.find((spawn) => spawn.id === objectiveId);
|
||||
if (staticSpawn) {
|
||||
const entity = dungeon.entities[staticSpawn.entityId];
|
||||
if (entity) return [staticSpawn.entityId, entity];
|
||||
}
|
||||
|
||||
for (const pack of dungeon.roamingPacks) {
|
||||
const member = pack.members.find((candidate) => (
|
||||
`${pack.id}:${candidate.id}` === objectiveId || candidate.id === objectiveId
|
||||
));
|
||||
if (member) {
|
||||
const entity = dungeon.entities[member.entityId];
|
||||
if (entity) return [member.entityId, entity];
|
||||
}
|
||||
}
|
||||
|
||||
const wanted = normalizedName(objectiveName);
|
||||
return Object.entries(dungeon.entities).find(([, entity]) => (
|
||||
entity.kind === "boss"
|
||||
&& (normalizedName(entity.name) === wanted || normalizedName(entity.title) === wanted)
|
||||
));
|
||||
}
|
||||
|
||||
function bossGuide(
|
||||
dungeon: DungeonDefinition,
|
||||
bossId: string,
|
||||
bossName: string,
|
||||
resolved: readonly [string, PopulationEntityDefinition] | undefined,
|
||||
overrides: BossMechanicGuideOverrides,
|
||||
): BossGuideEntry {
|
||||
const [entityId, entity] = resolved ?? [];
|
||||
const mechanics = entityId && entity?.combat
|
||||
? entity.combat.attacks
|
||||
.filter((attack) => attack.id !== "basic-attack")
|
||||
.map((attack) => mechanicGuide(dungeon, entityId, attack, overrides))
|
||||
: [];
|
||||
return {
|
||||
id: bossId,
|
||||
name: bossName,
|
||||
...(entity?.kind === "boss" ? { title: entity.title } : {}),
|
||||
...(entityId ? { entityId } : {}),
|
||||
mechanics,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildDungeonBossGuide(
|
||||
dungeon: DungeonDefinition,
|
||||
overrides: BossMechanicGuideOverrides = {},
|
||||
): DungeonBossGuide {
|
||||
const representedEntityIds = new Set<string>();
|
||||
const objectiveBosses = dungeon.bosses.map((objective) => {
|
||||
const resolved = bossEntityForObjective(dungeon, objective.id, objective.name);
|
||||
if (resolved) representedEntityIds.add(resolved[0]);
|
||||
return bossGuide(dungeon, objective.id, objective.name, resolved, overrides);
|
||||
});
|
||||
const unplacedBosses = Object.entries(dungeon.entities)
|
||||
.filter(([entityId, entity]) => entity.kind === "boss" && !representedEntityIds.has(entityId))
|
||||
.map(([entityId, entity]) => bossGuide(dungeon, `entity:${entityId}`, entity.name, [entityId, entity], overrides));
|
||||
return {
|
||||
id: dungeon.id,
|
||||
title: dungeon.title,
|
||||
expansion: dungeon.expansion,
|
||||
bosses: [...objectiveBosses, ...unplacedBosses],
|
||||
};
|
||||
}
|
||||
|
||||
export function buildBossGuideCatalog(
|
||||
dungeons: readonly DungeonDefinition[],
|
||||
overrides: BossMechanicGuideOverrides = {},
|
||||
): readonly DungeonBossGuide[] {
|
||||
return dungeons
|
||||
.map((dungeon) => buildDungeonBossGuide(dungeon, overrides))
|
||||
.sort((left, right) => left.title.localeCompare(right.title));
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { BossMechanicGuideOverrides } from "./bossGuide";
|
||||
|
||||
/**
|
||||
* Optional wording hooks for mechanics whose encounter-specific response cannot
|
||||
* be inferred from the reusable attack/effect schema. Unlisted mechanics use
|
||||
* the generic guide derivation and appear automatically.
|
||||
*/
|
||||
export const BOSS_GUIDE_OVERRIDES: BossMechanicGuideOverrides = Object.freeze({
|
||||
"uldaman:entry-2748:archaedas-awaken-earthen-custodian": {
|
||||
counterplay: "Pick up the awakened custodian immediately and keep it away from the healer.",
|
||||
},
|
||||
"uldaman:entry-2748:archaedas-awaken-earthen-guardians": {
|
||||
counterplay: "At 70% health, the tank gathers the six awakened guardians while the party clears them together.",
|
||||
},
|
||||
"uldaman:entry-2748:archaedas-awaken-vault-warders": {
|
||||
counterplay: "At 40% health, swap to the two vault warders and keep their pressure off the healer.",
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { DUNGEON_DEFINITIONS } from "./dungeonRegistry";
|
||||
import { normalizeEnemyAttack } from "./enemyAttacks";
|
||||
|
||||
const NON_HARMFUL_EFFECTS = new Set([
|
||||
"heal",
|
||||
"aura",
|
||||
"transform",
|
||||
"summon",
|
||||
"call-for-help",
|
||||
"encounter-event",
|
||||
]);
|
||||
|
||||
describe("boss mechanic telegraph coverage", () => {
|
||||
it("uses the same warned geometry for every harmful boss area mechanic", () => {
|
||||
let bosses = 0;
|
||||
let mechanics = 0;
|
||||
for (const dungeon of DUNGEON_DEFINITIONS) {
|
||||
for (const entity of Object.values(dungeon.entities)) {
|
||||
if (entity.kind !== "boss" || !entity.combat) continue;
|
||||
bosses += 1;
|
||||
for (const rawAttack of entity.combat.attacks) {
|
||||
const attack = normalizeEnemyAttack(rawAttack);
|
||||
if (attack.id === "basic-attack") continue;
|
||||
mechanics += 1;
|
||||
const harmful = !attack.effect || !NON_HARMFUL_EFFECTS.has(attack.effect.kind);
|
||||
if (harmful && (attack.delivery === "area" || attack.radius !== undefined)) {
|
||||
expect(attack.telegraph, `${dungeon.id}:${entity.name}:${attack.name}`).toBeDefined();
|
||||
}
|
||||
if (attack.telegraph) {
|
||||
expect(attack.castTimeMs ?? 0, `${dungeon.id}:${entity.name}:${attack.name}`)
|
||||
.toBeGreaterThanOrEqual(attack.telegraph.warningMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(bosses).toBeGreaterThan(100);
|
||||
expect(mechanics).toBeGreaterThan(300);
|
||||
});
|
||||
});
|
||||
+20
-8
@@ -128,6 +128,7 @@ import {
|
||||
type TriggeredAuraProc,
|
||||
} from "./combatAuras";
|
||||
import { rollBossLoot } from "./lootCatalog";
|
||||
import { adaptRunewakerEnemyDamage } from "./runewakerCombatFormula";
|
||||
import { normalizeInventory, type InventoryItem } from "./lootTypes";
|
||||
import {
|
||||
PLAYER_EQUIPMENT_OWNER_ID,
|
||||
@@ -185,7 +186,10 @@ import {
|
||||
type AzerothCoreMechanic,
|
||||
type AzerothCoreSchool,
|
||||
} from "./azerothCoreReference";
|
||||
import { useWailingEncounterStore } from "./wailingCavernsEncounter";
|
||||
import {
|
||||
dispatchDungeonEncounterSignal,
|
||||
dungeonEncounterStateMatches,
|
||||
} from "./dungeonEncounterRuntime";
|
||||
import {
|
||||
PLAYER_AGGRO_ID,
|
||||
addActorThreat,
|
||||
@@ -325,6 +329,7 @@ export interface MobCombatState {
|
||||
readonly name: string;
|
||||
readonly serverEntry: number | null;
|
||||
readonly summonedBy?: string;
|
||||
readonly spawnGroupId?: string;
|
||||
readonly boss: boolean;
|
||||
readonly lootSourceId: string | null;
|
||||
readonly level: number;
|
||||
@@ -1762,8 +1767,7 @@ function enemyAbilityConditionMet(
|
||||
return percent >= condition.minimum && percent <= condition.maximum;
|
||||
}
|
||||
if (condition.kind === "encounter-state") {
|
||||
if (condition.encounterId !== "wailing-caverns:naralex") return false;
|
||||
return condition.states.includes(useWailingEncounterStore.getState().phase);
|
||||
return dungeonEncounterStateMatches(condition.encounterId, condition.states);
|
||||
}
|
||||
const origin = getMobPosition(mob.id);
|
||||
if (!origin) return false;
|
||||
@@ -1803,6 +1807,7 @@ function summonedMobCombatState(
|
||||
creatureEntry: number,
|
||||
targetActorId: AggroActorId,
|
||||
position: CombatPosition,
|
||||
spawnGroupId?: string,
|
||||
): MobCombatState {
|
||||
const stats = defaultMobStats(summoner.level, false, creatureEntry);
|
||||
const template = creatureTemplateReference(creatureEntry);
|
||||
@@ -1817,6 +1822,7 @@ function summonedMobCombatState(
|
||||
name: template?.name ?? `Summoned Creature ${creatureEntry}`,
|
||||
serverEntry: creatureEntry,
|
||||
summonedBy: summoner.id,
|
||||
...(spawnGroupId ? { spawnGroupId } : {}),
|
||||
boss: false,
|
||||
lootSourceId: null,
|
||||
level: summoner.level,
|
||||
@@ -6540,7 +6546,13 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
actorPosition(runtimeState, presentationTargetId),
|
||||
);
|
||||
if (effect.kind === "damage") {
|
||||
const attackDamage = Math.max(0, Math.round(mob.attackDamage * effect.multiplier));
|
||||
const baselineDamage = Math.max(0, Math.round(mob.attackDamage * effect.multiplier));
|
||||
const attackDamage = attack.runewakerFormula
|
||||
? adaptRunewakerEnemyDamage({
|
||||
formula: attack.runewakerFormula,
|
||||
baselineDamage: mob.attackDamage,
|
||||
}).amount
|
||||
: baselineDamage;
|
||||
for (const actorId of targets) {
|
||||
incomingHits.push({
|
||||
actorId,
|
||||
@@ -6668,7 +6680,8 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
} else if (effect.kind === "summon") {
|
||||
const castNumber = (abilityCastCounts[attack.id] ?? 0) + 1;
|
||||
for (let summonIndex = 0; summonIndex < effect.count; summonIndex += 1) {
|
||||
const summonId = `${id}:${attack.id}:summon:${castNumber}:${summonIndex + 1}`;
|
||||
const summonNamespace = effect.entityId?.trim() || `${id}:${attack.id}:summon`;
|
||||
const summonId = `${summonNamespace}:${castNumber}:${summonIndex + 1}`;
|
||||
if (mobs[summonId]) continue;
|
||||
const angle = summonIndex * 2.399963229728653;
|
||||
const summonPosition: CombatPosition = [
|
||||
@@ -6683,12 +6696,11 @@ export const useCombatStore = create<CombatState>((set, get) => ({
|
||||
effect.creatureEntry,
|
||||
resolution.targetActorId,
|
||||
summonPosition,
|
||||
effect.spawnGroupId,
|
||||
);
|
||||
}
|
||||
} else if (effect.kind === "encounter-event") {
|
||||
if (effect.eventId === "wailing-caverns:naralex-start") {
|
||||
useWailingEncounterStore.getState().start(now);
|
||||
}
|
||||
dispatchDungeonEncounterSignal(effect.eventId, now);
|
||||
}
|
||||
attackRevision += 1;
|
||||
abilityCastCounts[attack.id] = (abilityCastCounts[attack.id] ?? 0) + 1;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCombatStore } from "./combatStore";
|
||||
import { useGameStore } from "./store";
|
||||
import { useWailingEncounterStore } from "./wailingCavernsEncounter";
|
||||
import { resetDungeonEncounters } from "./dungeonEncounterRuntime";
|
||||
|
||||
/**
|
||||
* Atomically returns a defeated player to the dungeon entrance and restores
|
||||
@@ -15,7 +15,7 @@ export function respawnDefeatedPlayer(): boolean {
|
||||
document.exitPointerLock();
|
||||
}
|
||||
useGameStore.getState().resetAtEntrance();
|
||||
useWailingEncounterStore.getState().reset();
|
||||
resetDungeonEncounters("dungeon", useGameStore.getState().activeDungeonId);
|
||||
const entrance = useGameStore.getState().playerPosition;
|
||||
combat.resetEncounter();
|
||||
useCombatStore.getState().setPlayerPosition(entrance);
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
activateDungeonEncounterDefinitions,
|
||||
activeDungeonEncounterRevision,
|
||||
createDungeonEncounterSnapshot,
|
||||
dispatchActiveDungeonInteraction,
|
||||
dispatchDungeonEncounterSignal,
|
||||
drainDungeonEncounterEffects,
|
||||
dungeonEncounterSnapshot,
|
||||
exportDungeonEncounterCheckpointSnapshots,
|
||||
exportDungeonEncounterSnapshots,
|
||||
importDungeonEncounterSnapshots,
|
||||
reduceDungeonEncounter,
|
||||
registerDungeonEncounterEffectHandler,
|
||||
resetDungeonEncounterSnapshot,
|
||||
subscribeDungeonEncounterSnapshots,
|
||||
validateDungeonEncounterDefinition,
|
||||
} from "./dungeonEncounterRuntime";
|
||||
import type { DungeonEncounterDefinition } from "./dungeonEncounterTypes";
|
||||
import {
|
||||
WAILING_NARALEX_ENCOUNTER,
|
||||
useWailingEncounterStore,
|
||||
} from "./wailingCavernsEncounter";
|
||||
|
||||
const REDUCER_DEFINITION = {
|
||||
id: "test:reducer",
|
||||
dungeonId: "test-dungeon",
|
||||
initialPhase: "idle",
|
||||
initialState: {
|
||||
flags: { temporary: false, persistent: false },
|
||||
counters: { attempts: 0 },
|
||||
worldObjectStates: { gate: "closed" },
|
||||
},
|
||||
resetPolicy: { bossWipePhase: "idle", checkpointPhase: "checkpoint-ready" },
|
||||
rules: [
|
||||
{
|
||||
id: "pull-lever",
|
||||
trigger: { kind: "interact", objectId: "lever" },
|
||||
guards: [{ kind: "phase", phases: ["idle"] }],
|
||||
actions: [
|
||||
{ id: "enter-active", kind: "set-phase", phase: "active" },
|
||||
{ id: "mark-temporary", kind: "set-flag", flagId: "temporary", value: true },
|
||||
{ id: "mark-persistent", kind: "set-flag", flagId: "persistent", value: true, persistence: "checkpoint" },
|
||||
{ id: "open-gate", kind: "set-world-object-state", objectId: "gate", state: "open", persistence: "checkpoint" },
|
||||
{ id: "enable-route", kind: "set-transition-active", transitionId: "gate-route", active: true, persistence: "checkpoint" },
|
||||
{ id: "spawn-wave", kind: "activate-spawn-group", spawnGroupId: "wave-one" },
|
||||
{ id: "save-checkpoint", kind: "set-checkpoint", checkpointId: "lever", persistence: "checkpoint" },
|
||||
{ id: "move-party", kind: "teleport-party", destination: [1, 2, 3] },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "count-active-ticks",
|
||||
trigger: { kind: "tick" },
|
||||
guards: [{ kind: "phase", phases: ["active"] }],
|
||||
once: false,
|
||||
actions: [{ id: "count-attempt", kind: "increment-counter", counterId: "attempts" }],
|
||||
},
|
||||
{
|
||||
id: "finish-after-delay",
|
||||
trigger: { kind: "elapsed", afterMs: 5_000 },
|
||||
guards: [
|
||||
{ kind: "phase", phases: ["active"] },
|
||||
{ kind: "flag", flagId: "persistent", equals: true },
|
||||
],
|
||||
actions: [
|
||||
{ id: "finish", kind: "set-phase", phase: "complete", persistence: "run" },
|
||||
{ id: "complete-objective", kind: "complete-objective", objectiveId: "lever-boss", persistence: "run" },
|
||||
],
|
||||
},
|
||||
],
|
||||
} as const satisfies DungeonEncounterDefinition;
|
||||
|
||||
const MANAGER_A = {
|
||||
id: "test:manager-a",
|
||||
dungeonId: "manager-a",
|
||||
initialPhase: "idle",
|
||||
rules: [{
|
||||
id: "shared-signal",
|
||||
trigger: { kind: "signal", signalId: "test:shared" },
|
||||
once: false,
|
||||
actions: [{ id: "increment-a", kind: "increment-counter", counterId: "signals" }],
|
||||
}],
|
||||
} as const satisfies DungeonEncounterDefinition;
|
||||
|
||||
const MANAGER_B = {
|
||||
id: "test:manager-b",
|
||||
dungeonId: "manager-b",
|
||||
initialPhase: "idle",
|
||||
initialState: { worldObjectStates: { switch: "ready" } },
|
||||
rules: [
|
||||
{
|
||||
id: "shared-signal",
|
||||
trigger: { kind: "signal", signalId: "test:shared" },
|
||||
once: false,
|
||||
actions: [{ id: "increment-b", kind: "increment-counter", counterId: "signals" }],
|
||||
},
|
||||
{
|
||||
id: "use-switch",
|
||||
trigger: { kind: "interact", objectId: "switch" },
|
||||
guards: [{ kind: "world-object-state", objectId: "switch", states: ["ready"] }],
|
||||
actions: [
|
||||
{ id: "open-switch", kind: "set-world-object-state", objectId: "switch", state: "open", persistence: "checkpoint" },
|
||||
{ id: "start-group", kind: "activate-spawn-group", spawnGroupId: "switch-wave" },
|
||||
{ id: "switch-cue", kind: "emit-signal", signalId: "test:switch-opened" },
|
||||
],
|
||||
},
|
||||
],
|
||||
} as const satisfies DungeonEncounterDefinition;
|
||||
|
||||
afterEach(() => {
|
||||
activateDungeonEncounterDefinitions("wailing-caverns", [WAILING_NARALEX_ENCOUNTER]);
|
||||
useWailingEncounterStore.getState().reset();
|
||||
drainDungeonEncounterEffects("manager-a");
|
||||
drainDungeonEncounterEffects("manager-b");
|
||||
});
|
||||
|
||||
describe("dungeon encounter reducer", () => {
|
||||
it("applies stable reusable actions deterministically and remains JSON serializable", () => {
|
||||
const initial = createDungeonEncounterSnapshot(REDUCER_DEFINITION, 100);
|
||||
const event = { kind: "interact", objectId: "lever", actorId: "player", now: 1_000 } as const;
|
||||
const first = reduceDungeonEncounter(REDUCER_DEFINITION, initial, event);
|
||||
const second = reduceDungeonEncounter(REDUCER_DEFINITION, initial, event);
|
||||
|
||||
expect(second).toEqual(first);
|
||||
expect(first.snapshot).toMatchObject({
|
||||
phase: "active",
|
||||
phaseStartedAt: 1_000,
|
||||
flags: { temporary: true, persistent: true },
|
||||
worldObjectStates: { gate: "open" },
|
||||
transitionStates: { "gate-route": true },
|
||||
activeSpawnGroupIds: ["wave-one"],
|
||||
checkpointId: "lever",
|
||||
revision: 1,
|
||||
});
|
||||
expect(first.effects).toEqual(expect.arrayContaining([
|
||||
{ kind: "world-object-state", actionId: "open-gate", objectId: "gate", state: "open" },
|
||||
{ kind: "spawn-group", actionId: "spawn-wave", spawnGroupId: "wave-one" },
|
||||
{ kind: "party-teleport", actionId: "move-party", destination: [1, 2, 3] },
|
||||
]));
|
||||
expect(JSON.parse(JSON.stringify(first.snapshot))).toEqual(first.snapshot);
|
||||
|
||||
const duplicate = reduceDungeonEncounter(REDUCER_DEFINITION, first.snapshot, event);
|
||||
expect(duplicate.snapshot).toBe(first.snapshot);
|
||||
expect(duplicate.effects).toEqual([]);
|
||||
});
|
||||
|
||||
it("supports repeatable rules, elapsed transitions, and scoped resets", () => {
|
||||
const started = reduceDungeonEncounter(
|
||||
REDUCER_DEFINITION,
|
||||
createDungeonEncounterSnapshot(REDUCER_DEFINITION),
|
||||
{ kind: "interact", objectId: "lever", now: 1_000 },
|
||||
).snapshot;
|
||||
const tickOne = reduceDungeonEncounter(
|
||||
REDUCER_DEFINITION,
|
||||
started,
|
||||
{ kind: "tick", now: 2_000 },
|
||||
).snapshot;
|
||||
const tickTwo = reduceDungeonEncounter(
|
||||
REDUCER_DEFINITION,
|
||||
tickOne,
|
||||
{ kind: "tick", now: 3_000 },
|
||||
).snapshot;
|
||||
expect(tickTwo.counters.attempts).toBe(2);
|
||||
|
||||
const completed = reduceDungeonEncounter(
|
||||
REDUCER_DEFINITION,
|
||||
tickTwo,
|
||||
{ kind: "tick", now: 6_000 },
|
||||
).snapshot;
|
||||
expect(completed.phase).toBe("complete");
|
||||
expect(completed.completedObjectiveIds).toEqual(["lever-boss"]);
|
||||
|
||||
const checkpoint = resetDungeonEncounterSnapshot(
|
||||
REDUCER_DEFINITION,
|
||||
completed,
|
||||
"checkpoint",
|
||||
7_000,
|
||||
);
|
||||
expect(checkpoint).toMatchObject({
|
||||
phase: "checkpoint-ready",
|
||||
flags: { temporary: false, persistent: true },
|
||||
counters: { attempts: 0 },
|
||||
worldObjectStates: { gate: "open" },
|
||||
transitionStates: { "gate-route": true },
|
||||
activeSpawnGroupIds: [],
|
||||
completedObjectiveIds: ["lever-boss"],
|
||||
checkpointId: "lever",
|
||||
});
|
||||
expect(resetDungeonEncounterSnapshot(REDUCER_DEFINITION, completed, "dungeon")).toMatchObject({
|
||||
phase: "idle",
|
||||
flags: { temporary: false, persistent: false },
|
||||
worldObjectStates: { gate: "closed" },
|
||||
completedObjectiveIds: [],
|
||||
checkpointId: null,
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects duplicate action ids at definition validation", () => {
|
||||
expect(() => validateDungeonEncounterDefinition({
|
||||
id: "test:invalid",
|
||||
dungeonId: "test-dungeon",
|
||||
initialPhase: "idle",
|
||||
rules: [{
|
||||
id: "duplicate-actions",
|
||||
trigger: { kind: "tick" },
|
||||
actions: [
|
||||
{ id: "same", kind: "set-phase", phase: "one" },
|
||||
{ id: "same", kind: "set-phase", phase: "two" },
|
||||
],
|
||||
}],
|
||||
})).toThrow(/duplicate action id same/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("active dungeon encounter manager", () => {
|
||||
it("isolates shared signals to the active dungeon", () => {
|
||||
activateDungeonEncounterDefinitions("manager-a", [MANAGER_A]);
|
||||
dispatchDungeonEncounterSignal("test:shared", 1_000);
|
||||
expect(dungeonEncounterSnapshot(MANAGER_A.id)?.counters.signals).toBe(1);
|
||||
|
||||
activateDungeonEncounterDefinitions("manager-b", [MANAGER_B]);
|
||||
dispatchDungeonEncounterSignal("test:shared", 2_000);
|
||||
expect(dungeonEncounterSnapshot(MANAGER_A.id)?.counters.signals).toBe(1);
|
||||
expect(dungeonEncounterSnapshot(MANAGER_B.id)?.counters.signals).toBe(1);
|
||||
});
|
||||
|
||||
it("dispatches interactions, exposes effects, snapshots, hydration, and subscriptions", () => {
|
||||
activateDungeonEncounterDefinitions("manager-b", [MANAGER_B]);
|
||||
const subscriber = vi.fn();
|
||||
const effectHandler = vi.fn();
|
||||
const unsubscribe = subscribeDungeonEncounterSnapshots(subscriber);
|
||||
const unregisterEffect = registerDungeonEncounterEffectHandler(effectHandler);
|
||||
const revisionBefore = activeDungeonEncounterRevision();
|
||||
|
||||
const effects = dispatchActiveDungeonInteraction("switch", "player", 3_000);
|
||||
expect(effects).toContainEqual({
|
||||
kind: "signal",
|
||||
actionId: "switch-cue",
|
||||
signalId: "test:switch-opened",
|
||||
});
|
||||
expect(effectHandler).toHaveBeenCalled();
|
||||
expect(subscriber).toHaveBeenCalled();
|
||||
expect(activeDungeonEncounterRevision()).toBeGreaterThan(revisionBefore);
|
||||
expect(drainDungeonEncounterEffects("manager-b")).toHaveLength(3);
|
||||
expect(drainDungeonEncounterEffects("manager-b")).toEqual([]);
|
||||
|
||||
const exported = exportDungeonEncounterSnapshots("manager-b");
|
||||
expect(exported).toHaveLength(1);
|
||||
expect(exported[0]).toMatchObject({
|
||||
encounterId: MANAGER_B.id,
|
||||
worldObjectStates: { switch: "open" },
|
||||
activeSpawnGroupIds: ["switch-wave"],
|
||||
});
|
||||
const checkpoint = exportDungeonEncounterCheckpointSnapshots("manager-b", 4_000);
|
||||
expect(checkpoint[0]).toMatchObject({
|
||||
worldObjectStates: { switch: "open" },
|
||||
activeSpawnGroupIds: [],
|
||||
timers: {},
|
||||
});
|
||||
expect(exportDungeonEncounterSnapshots("manager-b")[0].activeSpawnGroupIds).toEqual(["switch-wave"]);
|
||||
|
||||
const hydrated = {
|
||||
...exported[0],
|
||||
phase: "restored",
|
||||
revision: exported[0].revision + 1,
|
||||
};
|
||||
expect(importDungeonEncounterSnapshots("manager-b", [hydrated])).toEqual([MANAGER_B.id]);
|
||||
expect(dungeonEncounterSnapshot(MANAGER_B.id)?.phase).toBe("restored");
|
||||
|
||||
unregisterEffect();
|
||||
unsubscribe();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,600 @@
|
||||
import {
|
||||
DUNGEON_ENCOUNTER_SNAPSHOT_SCHEMA_VERSION,
|
||||
type DungeonEncounterAction,
|
||||
type DungeonEncounterDefinition,
|
||||
type DungeonEncounterEffect,
|
||||
type DungeonEncounterEffectEnvelope,
|
||||
type DungeonEncounterEvent,
|
||||
type DungeonEncounterGuard,
|
||||
type DungeonEncounterReduction,
|
||||
type DungeonEncounterResetScope,
|
||||
type DungeonEncounterRule,
|
||||
type DungeonEncounterSnapshot,
|
||||
} from "./dungeonEncounterTypes";
|
||||
|
||||
function finiteTime(value: number): number {
|
||||
return Number.isFinite(value) ? Math.max(0, Math.round(value)) : 0;
|
||||
}
|
||||
|
||||
function unique(values: readonly string[]): readonly string[] {
|
||||
return Object.freeze([...new Set(values)].sort());
|
||||
}
|
||||
|
||||
export function createDungeonEncounterSnapshot(
|
||||
definition: DungeonEncounterDefinition,
|
||||
now = 0,
|
||||
revision = 0,
|
||||
): DungeonEncounterSnapshot {
|
||||
return Object.freeze({
|
||||
schemaVersion: DUNGEON_ENCOUNTER_SNAPSHOT_SCHEMA_VERSION,
|
||||
encounterId: definition.id,
|
||||
phase: definition.initialPhase,
|
||||
phaseStartedAt: finiteTime(now),
|
||||
flags: Object.freeze({ ...definition.initialState?.flags }),
|
||||
counters: Object.freeze({ ...definition.initialState?.counters }),
|
||||
timers: Object.freeze({}),
|
||||
worldObjectStates: Object.freeze({ ...definition.initialState?.worldObjectStates }),
|
||||
transitionStates: Object.freeze({ ...definition.initialState?.transitionStates }),
|
||||
activeSpawnGroupIds: Object.freeze([]),
|
||||
completedObjectiveIds: Object.freeze([]),
|
||||
consumedActionIds: Object.freeze([]),
|
||||
checkpointId: null,
|
||||
revision,
|
||||
});
|
||||
}
|
||||
|
||||
function validateStableId(value: string, label: string): void {
|
||||
if (!value.trim() || !/^[a-z0-9][a-z0-9:._-]*$/i.test(value)) {
|
||||
throw new Error(`${label} must have a stable non-empty id: ${JSON.stringify(value)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function validateDungeonEncounterDefinition(definition: DungeonEncounterDefinition): void {
|
||||
validateStableId(definition.id, "Encounter");
|
||||
validateStableId(definition.dungeonId, `Encounter ${definition.id} dungeon`);
|
||||
validateStableId(definition.initialPhase, `Encounter ${definition.id} initial phase`);
|
||||
const ruleIds = new Set<string>();
|
||||
const actionIds = new Set<string>();
|
||||
for (const rule of definition.rules) {
|
||||
validateStableId(rule.id, `Encounter ${definition.id} rule`);
|
||||
if (ruleIds.has(rule.id)) throw new Error(`Encounter ${definition.id} has duplicate rule id ${rule.id}`);
|
||||
ruleIds.add(rule.id);
|
||||
for (const action of rule.actions) {
|
||||
validateStableId(action.id, `Encounter ${definition.id} action`);
|
||||
if (actionIds.has(action.id)) {
|
||||
throw new Error(`Encounter ${definition.id} has duplicate action id ${action.id}`);
|
||||
}
|
||||
actionIds.add(action.id);
|
||||
if (action.kind === "grant-loot" && action.persistence !== "run") {
|
||||
throw new Error(`Encounter ${definition.id} loot action ${action.id} must use run persistence`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function guardMatches(snapshot: DungeonEncounterSnapshot, guard: DungeonEncounterGuard): boolean {
|
||||
if (guard.kind === "phase") return guard.phases.includes(snapshot.phase);
|
||||
if (guard.kind === "flag") return (snapshot.flags[guard.flagId] ?? false) === guard.equals;
|
||||
if (guard.kind === "objective-complete") {
|
||||
return snapshot.completedObjectiveIds.includes(guard.objectiveId) === guard.equals;
|
||||
}
|
||||
if (guard.kind === "spawn-group-active") {
|
||||
return snapshot.activeSpawnGroupIds.includes(guard.spawnGroupId) === guard.equals;
|
||||
}
|
||||
if (guard.kind === "world-object-state") {
|
||||
return guard.states.includes(snapshot.worldObjectStates[guard.objectId] ?? "inactive");
|
||||
}
|
||||
const current = snapshot.counters[guard.counterId] ?? 0;
|
||||
if (guard.comparison === "equal") return current === guard.value;
|
||||
if (guard.comparison === "at-least") return current >= guard.value;
|
||||
return current <= guard.value;
|
||||
}
|
||||
|
||||
function triggerMatches(
|
||||
snapshot: DungeonEncounterSnapshot,
|
||||
trigger: DungeonEncounterRule["trigger"],
|
||||
event: DungeonEncounterEvent,
|
||||
): boolean {
|
||||
if (trigger.kind === "tick") return event.kind === "tick";
|
||||
if (trigger.kind === "signal") return event.kind === "signal" && event.signalId === trigger.signalId;
|
||||
if (trigger.kind === "entity-engaged" || trigger.kind === "entity-death") {
|
||||
return event.kind === trigger.kind && event.entityId === trigger.entityId;
|
||||
}
|
||||
if (trigger.kind === "entity-health") {
|
||||
return event.kind === "entity-health"
|
||||
&& event.entityId === trigger.entityId
|
||||
&& event.healthPercent <= trigger.atOrBelowPercent;
|
||||
}
|
||||
if (trigger.kind === "interact") return event.kind === "interact" && event.objectId === trigger.objectId;
|
||||
if (trigger.kind === "elapsed") {
|
||||
return event.kind === "tick" && event.now - snapshot.phaseStartedAt >= trigger.afterMs;
|
||||
}
|
||||
if (event.kind !== "tick") return false;
|
||||
const registered = new Set(event.facts?.registeredEntityIds ?? []);
|
||||
const dead = new Set(event.facts?.deadEntityIds ?? []);
|
||||
const allDead = trigger.entityIds.length > 0
|
||||
&& trigger.entityIds.every((id) => registered.has(id) && dead.has(id));
|
||||
const timedOut = trigger.timeoutMs !== undefined
|
||||
&& event.now - snapshot.phaseStartedAt >= trigger.timeoutMs;
|
||||
return allDead || timedOut;
|
||||
}
|
||||
|
||||
function ruleAlreadyConsumed(snapshot: DungeonEncounterSnapshot, rule: DungeonEncounterRule): boolean {
|
||||
return (rule.once ?? true)
|
||||
&& rule.actions.length > 0
|
||||
&& rule.actions.every((action) => snapshot.consumedActionIds.includes(action.id));
|
||||
}
|
||||
|
||||
function actionEffect(action: DungeonEncounterAction): DungeonEncounterEffect | null {
|
||||
if (action.kind === "activate-spawn-group") {
|
||||
return { kind: "spawn-group", actionId: action.id, spawnGroupId: action.spawnGroupId };
|
||||
}
|
||||
if (action.kind === "deactivate-spawn-group") {
|
||||
return { kind: "despawn-group", actionId: action.id, spawnGroupId: action.spawnGroupId };
|
||||
}
|
||||
if (action.kind === "set-world-object-state") {
|
||||
return { kind: "world-object-state", actionId: action.id, objectId: action.objectId, state: action.state };
|
||||
}
|
||||
if (action.kind === "set-transition-active") {
|
||||
return { kind: "transition-state", actionId: action.id, transitionId: action.transitionId, active: action.active };
|
||||
}
|
||||
if (action.kind === "teleport-party") {
|
||||
return { kind: "party-teleport", actionId: action.id, destination: action.destination };
|
||||
}
|
||||
if (action.kind === "grant-loot") {
|
||||
return { kind: "loot-grant", actionId: action.id, lootTableId: action.lootTableId };
|
||||
}
|
||||
if (action.kind === "emit-signal") {
|
||||
return { kind: "signal", actionId: action.id, signalId: action.signalId };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function applyAction(
|
||||
snapshot: DungeonEncounterSnapshot,
|
||||
action: DungeonEncounterAction,
|
||||
now: number,
|
||||
consume: boolean,
|
||||
): { readonly snapshot: DungeonEncounterSnapshot; readonly effect: DungeonEncounterEffect | null } {
|
||||
if (consume && snapshot.consumedActionIds.includes(action.id)) return { snapshot, effect: null };
|
||||
let next = snapshot;
|
||||
if (action.kind === "set-phase") {
|
||||
next = { ...next, phase: action.phase, phaseStartedAt: finiteTime(now) };
|
||||
} else if (action.kind === "set-flag") {
|
||||
next = { ...next, flags: Object.freeze({ ...next.flags, [action.flagId]: action.value }) };
|
||||
} else if (action.kind === "set-counter") {
|
||||
next = { ...next, counters: Object.freeze({ ...next.counters, [action.counterId]: action.value }) };
|
||||
} else if (action.kind === "increment-counter") {
|
||||
next = {
|
||||
...next,
|
||||
counters: Object.freeze({
|
||||
...next.counters,
|
||||
[action.counterId]: (next.counters[action.counterId] ?? 0) + (action.amount ?? 1),
|
||||
}),
|
||||
};
|
||||
} else if (action.kind === "start-timer") {
|
||||
next = { ...next, timers: Object.freeze({ ...next.timers, [action.timerId]: finiteTime(now + action.durationMs) }) };
|
||||
} else if (action.kind === "clear-timer") {
|
||||
const timers = { ...next.timers };
|
||||
delete timers[action.timerId];
|
||||
next = { ...next, timers: Object.freeze(timers) };
|
||||
} else if (action.kind === "activate-spawn-group") {
|
||||
next = { ...next, activeSpawnGroupIds: unique([...next.activeSpawnGroupIds, action.spawnGroupId]) };
|
||||
} else if (action.kind === "deactivate-spawn-group") {
|
||||
next = { ...next, activeSpawnGroupIds: Object.freeze(next.activeSpawnGroupIds.filter((id) => id !== action.spawnGroupId)) };
|
||||
} else if (action.kind === "set-world-object-state") {
|
||||
next = { ...next, worldObjectStates: Object.freeze({ ...next.worldObjectStates, [action.objectId]: action.state }) };
|
||||
} else if (action.kind === "set-transition-active") {
|
||||
next = { ...next, transitionStates: Object.freeze({ ...next.transitionStates, [action.transitionId]: action.active }) };
|
||||
} else if (action.kind === "complete-objective") {
|
||||
next = { ...next, completedObjectiveIds: unique([...next.completedObjectiveIds, action.objectiveId]) };
|
||||
} else if (action.kind === "set-checkpoint") {
|
||||
next = { ...next, checkpointId: action.checkpointId };
|
||||
}
|
||||
return {
|
||||
snapshot: consume
|
||||
? { ...next, consumedActionIds: unique([...next.consumedActionIds, action.id]) }
|
||||
: next,
|
||||
effect: actionEffect(action),
|
||||
};
|
||||
}
|
||||
|
||||
export function reduceDungeonEncounter(
|
||||
definition: DungeonEncounterDefinition,
|
||||
snapshot: DungeonEncounterSnapshot,
|
||||
event: DungeonEncounterEvent,
|
||||
): DungeonEncounterReduction {
|
||||
if (snapshot.encounterId !== definition.id) {
|
||||
throw new Error(`Snapshot ${snapshot.encounterId} does not belong to encounter ${definition.id}`);
|
||||
}
|
||||
let next = snapshot;
|
||||
const effects: DungeonEncounterEffect[] = [];
|
||||
const firedRuleIds: string[] = [];
|
||||
const rules = definition.rules
|
||||
.map((rule, index) => ({ rule, index }))
|
||||
.sort((left, right) => (right.rule.priority ?? 0) - (left.rule.priority ?? 0) || left.index - right.index);
|
||||
for (const { rule } of rules) {
|
||||
if (ruleAlreadyConsumed(next, rule)) continue;
|
||||
if (!triggerMatches(next, rule.trigger, event)) continue;
|
||||
if (!rule.guards?.every((guard) => guardMatches(next, guard)) && rule.guards?.length) continue;
|
||||
let fired = false;
|
||||
for (const action of rule.actions) {
|
||||
const applied = applyAction(next, action, event.now, rule.once ?? true);
|
||||
if (applied.snapshot !== next) fired = true;
|
||||
next = applied.snapshot;
|
||||
if (applied.effect) effects.push(applied.effect);
|
||||
}
|
||||
if (fired || rule.actions.length === 0) firedRuleIds.push(rule.id);
|
||||
}
|
||||
if (next === snapshot) return { snapshot, effects: Object.freeze(effects), firedRuleIds: Object.freeze(firedRuleIds) };
|
||||
return {
|
||||
snapshot: Object.freeze({ ...next, revision: snapshot.revision + 1 }),
|
||||
effects: Object.freeze(effects),
|
||||
firedRuleIds: Object.freeze(firedRuleIds),
|
||||
};
|
||||
}
|
||||
|
||||
function retainedActionIds(
|
||||
definition: DungeonEncounterDefinition,
|
||||
snapshot: DungeonEncounterSnapshot,
|
||||
scope: DungeonEncounterResetScope,
|
||||
): readonly string[] {
|
||||
if (scope === "dungeon") return [];
|
||||
const retained = new Set(definition.rules.flatMap((rule) => rule.actions)
|
||||
.filter((action) => action.persistence === "checkpoint" || action.persistence === "run")
|
||||
.map((action) => action.id));
|
||||
return Object.freeze(snapshot.consumedActionIds.filter((id) => retained.has(id)));
|
||||
}
|
||||
|
||||
function retainedStateKeys(definition: DungeonEncounterDefinition): {
|
||||
readonly flags: ReadonlySet<string>;
|
||||
readonly counters: ReadonlySet<string>;
|
||||
readonly worldObjects: ReadonlySet<string>;
|
||||
readonly transitions: ReadonlySet<string>;
|
||||
readonly objectives: ReadonlySet<string>;
|
||||
readonly checkpoints: boolean;
|
||||
} {
|
||||
const actions = definition.rules.flatMap((rule) => rule.actions)
|
||||
.filter((action) => action.persistence === "checkpoint" || action.persistence === "run");
|
||||
return {
|
||||
flags: new Set(actions.filter((action) => action.kind === "set-flag").map((action) => action.flagId)),
|
||||
counters: new Set(actions
|
||||
.filter((action) => action.kind === "set-counter" || action.kind === "increment-counter")
|
||||
.map((action) => action.counterId)),
|
||||
worldObjects: new Set(actions
|
||||
.filter((action) => action.kind === "set-world-object-state")
|
||||
.map((action) => action.objectId)),
|
||||
transitions: new Set(actions
|
||||
.filter((action) => action.kind === "set-transition-active")
|
||||
.map((action) => action.transitionId)),
|
||||
objectives: new Set(actions
|
||||
.filter((action) => action.kind === "complete-objective")
|
||||
.map((action) => action.objectiveId)),
|
||||
checkpoints: actions.some((action) => action.kind === "set-checkpoint"),
|
||||
};
|
||||
}
|
||||
|
||||
function retainedRecord<T>(
|
||||
initial: Readonly<Record<string, T>>,
|
||||
current: Readonly<Record<string, T>>,
|
||||
keys: ReadonlySet<string>,
|
||||
): Readonly<Record<string, T>> {
|
||||
const retained: Record<string, T> = { ...initial };
|
||||
for (const key of keys) {
|
||||
if (current[key] !== undefined) retained[key] = current[key];
|
||||
}
|
||||
return Object.freeze(retained);
|
||||
}
|
||||
|
||||
export function resetDungeonEncounterSnapshot(
|
||||
definition: DungeonEncounterDefinition,
|
||||
snapshot: DungeonEncounterSnapshot,
|
||||
scope: DungeonEncounterResetScope,
|
||||
now = 0,
|
||||
): DungeonEncounterSnapshot {
|
||||
const initial = createDungeonEncounterSnapshot(definition, now, snapshot.revision + 1);
|
||||
if (scope === "dungeon") return initial;
|
||||
const phase = scope === "boss-wipe"
|
||||
? definition.resetPolicy?.bossWipePhase ?? definition.initialPhase
|
||||
: definition.resetPolicy?.checkpointPhase ?? definition.initialPhase;
|
||||
const retained = retainedStateKeys(definition);
|
||||
return Object.freeze({
|
||||
...initial,
|
||||
phase,
|
||||
phaseStartedAt: finiteTime(now),
|
||||
flags: retainedRecord(initial.flags, snapshot.flags, retained.flags),
|
||||
counters: retainedRecord(initial.counters, snapshot.counters, retained.counters),
|
||||
worldObjectStates: retainedRecord(
|
||||
initial.worldObjectStates,
|
||||
snapshot.worldObjectStates,
|
||||
retained.worldObjects,
|
||||
),
|
||||
transitionStates: retainedRecord(
|
||||
initial.transitionStates,
|
||||
snapshot.transitionStates,
|
||||
retained.transitions,
|
||||
),
|
||||
completedObjectiveIds: Object.freeze(snapshot.completedObjectiveIds
|
||||
.filter((id) => retained.objectives.has(id))),
|
||||
consumedActionIds: retainedActionIds(definition, snapshot, scope),
|
||||
checkpointId: retained.checkpoints ? snapshot.checkpointId : null,
|
||||
});
|
||||
}
|
||||
|
||||
export interface DungeonEncounterProvider {
|
||||
readonly definition: DungeonEncounterDefinition;
|
||||
readonly encounterId: string;
|
||||
readonly dungeonId: string;
|
||||
readonly getSnapshot: () => DungeonEncounterSnapshot;
|
||||
readonly dispatch: (event: DungeonEncounterEvent) => readonly DungeonEncounterEffect[];
|
||||
readonly reset: (scope: DungeonEncounterResetScope, now?: number) => void;
|
||||
/** Hydrates authoritative online/save state without involving renderer state. */
|
||||
readonly replaceSnapshot?: (snapshot: DungeonEncounterSnapshot) => void;
|
||||
}
|
||||
|
||||
const providers = new Map<string, DungeonEncounterProvider>();
|
||||
const pendingEffects: DungeonEncounterEffectEnvelope[] = [];
|
||||
const effectHandlers = new Set<(value: DungeonEncounterEffectEnvelope) => void>();
|
||||
let activeDungeonId: string | null = null;
|
||||
|
||||
function queueProviderEffects(
|
||||
provider: DungeonEncounterProvider,
|
||||
effects: readonly DungeonEncounterEffect[],
|
||||
): readonly DungeonEncounterEffect[] {
|
||||
for (const effect of effects) {
|
||||
const value = Object.freeze({
|
||||
dungeonId: provider.dungeonId,
|
||||
encounterId: provider.encounterId,
|
||||
effect,
|
||||
});
|
||||
pendingEffects.push(value);
|
||||
for (const handler of effectHandlers) handler(value);
|
||||
}
|
||||
return effects;
|
||||
}
|
||||
|
||||
export function registerDungeonEncounterProvider(provider: DungeonEncounterProvider): () => void {
|
||||
validateDungeonEncounterDefinition(provider.definition);
|
||||
if (provider.definition.id !== provider.encounterId || provider.definition.dungeonId !== provider.dungeonId) {
|
||||
throw new Error(`Encounter provider ${provider.encounterId} does not match its definition`);
|
||||
}
|
||||
const current = providers.get(provider.encounterId);
|
||||
if (current && current !== provider) throw new Error(`Duplicate encounter provider ${provider.encounterId}`);
|
||||
providers.set(provider.encounterId, provider);
|
||||
return () => {
|
||||
if (providers.get(provider.encounterId) === provider) providers.delete(provider.encounterId);
|
||||
};
|
||||
}
|
||||
|
||||
export function dungeonEncounterSnapshot(encounterId: string): DungeonEncounterSnapshot | null {
|
||||
return providers.get(encounterId)?.getSnapshot() ?? null;
|
||||
}
|
||||
|
||||
export function dungeonEncounterStateMatches(encounterId: string, states: readonly string[]): boolean {
|
||||
const snapshot = dungeonEncounterSnapshot(encounterId);
|
||||
return Boolean(snapshot && states.includes(snapshot.phase));
|
||||
}
|
||||
|
||||
export function dispatchDungeonEncounterEvent(
|
||||
encounterId: string,
|
||||
event: DungeonEncounterEvent,
|
||||
): readonly DungeonEncounterEffect[] {
|
||||
const provider = providers.get(encounterId);
|
||||
if (!provider) return [];
|
||||
const effects = queueProviderEffects(provider, provider.dispatch(event));
|
||||
notifyEncounterSubscribers();
|
||||
return effects;
|
||||
}
|
||||
|
||||
export function dispatchDungeonEncounterSignal(signalId: string, now: number): readonly DungeonEncounterEffect[] {
|
||||
const effects: DungeonEncounterEffect[] = [];
|
||||
for (const provider of providers.values()) {
|
||||
if (activeDungeonId && provider.dungeonId !== activeDungeonId) continue;
|
||||
effects.push(...queueProviderEffects(provider, provider.dispatch({ kind: "signal", signalId, now })));
|
||||
}
|
||||
notifyEncounterSubscribers();
|
||||
return Object.freeze(effects);
|
||||
}
|
||||
|
||||
export function dispatchActiveDungeonInteraction(
|
||||
objectId: string,
|
||||
actorId?: string,
|
||||
now = Date.now(),
|
||||
): readonly DungeonEncounterEffect[] {
|
||||
if (!activeDungeonId) return [];
|
||||
const effects: DungeonEncounterEffect[] = [];
|
||||
for (const provider of providers.values()) {
|
||||
if (provider.dungeonId !== activeDungeonId) continue;
|
||||
effects.push(...queueProviderEffects(provider, provider.dispatch({
|
||||
kind: "interact",
|
||||
objectId,
|
||||
...(actorId ? { actorId } : {}),
|
||||
now,
|
||||
})));
|
||||
}
|
||||
notifyEncounterSubscribers();
|
||||
return Object.freeze(effects);
|
||||
}
|
||||
|
||||
export function advanceDungeonEncounters(
|
||||
dungeonId: string,
|
||||
event: Extract<DungeonEncounterEvent, { readonly kind: "tick" }>,
|
||||
): readonly DungeonEncounterEffect[] {
|
||||
const effects: DungeonEncounterEffect[] = [];
|
||||
for (const provider of providers.values()) {
|
||||
if (provider.dungeonId === dungeonId) {
|
||||
effects.push(...queueProviderEffects(provider, provider.dispatch(event)));
|
||||
}
|
||||
}
|
||||
notifyEncounterSubscribers();
|
||||
return Object.freeze(effects);
|
||||
}
|
||||
|
||||
export function resetDungeonEncounters(
|
||||
scope: DungeonEncounterResetScope,
|
||||
dungeonId?: string,
|
||||
now = 0,
|
||||
): void {
|
||||
for (const provider of providers.values()) {
|
||||
if (!dungeonId || provider.dungeonId === dungeonId) provider.reset(scope, now);
|
||||
}
|
||||
notifyEncounterSubscribers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes every definition in the active dungeon executable. Authored adapters
|
||||
* (such as Naralex) win; otherwise a framework-neutral in-memory provider is
|
||||
* created and can later be hydrated by persistence or multiplayer.
|
||||
*/
|
||||
export function activateDungeonEncounterDefinitions(
|
||||
dungeonId: string,
|
||||
definitions: readonly DungeonEncounterDefinition[],
|
||||
): void {
|
||||
activeDungeonId = dungeonId;
|
||||
for (const definition of definitions) {
|
||||
validateDungeonEncounterDefinition(definition);
|
||||
if (definition.dungeonId !== dungeonId) {
|
||||
throw new Error(`Encounter ${definition.id} belongs to ${definition.dungeonId}, not ${dungeonId}`);
|
||||
}
|
||||
const existing = providers.get(definition.id);
|
||||
if (existing) {
|
||||
if (existing.dungeonId !== dungeonId) throw new Error(`Encounter ${definition.id} has conflicting dungeon owners`);
|
||||
continue;
|
||||
}
|
||||
let snapshot = createDungeonEncounterSnapshot(definition);
|
||||
const provider: DungeonEncounterProvider = {
|
||||
definition,
|
||||
encounterId: definition.id,
|
||||
dungeonId,
|
||||
getSnapshot: () => snapshot,
|
||||
dispatch: (event) => {
|
||||
const result = reduceDungeonEncounter(definition, snapshot, event);
|
||||
snapshot = result.snapshot;
|
||||
return result.effects;
|
||||
},
|
||||
reset: (scope, now = 0) => {
|
||||
snapshot = resetDungeonEncounterSnapshot(definition, snapshot, scope, now);
|
||||
},
|
||||
replaceSnapshot: (value) => {
|
||||
if (value.encounterId !== definition.id) {
|
||||
throw new Error(`Cannot hydrate ${definition.id} from ${value.encounterId}`);
|
||||
}
|
||||
snapshot = cloneEncounterSnapshot(value);
|
||||
},
|
||||
};
|
||||
providers.set(definition.id, provider);
|
||||
}
|
||||
notifyEncounterSubscribers();
|
||||
}
|
||||
|
||||
function cloneEncounterSnapshot(value: DungeonEncounterSnapshot): DungeonEncounterSnapshot {
|
||||
if (value.schemaVersion !== DUNGEON_ENCOUNTER_SNAPSHOT_SCHEMA_VERSION) {
|
||||
throw new Error(`Unsupported encounter snapshot schema ${value.schemaVersion}`);
|
||||
}
|
||||
return Object.freeze({
|
||||
...value,
|
||||
phaseStartedAt: finiteTime(value.phaseStartedAt),
|
||||
flags: Object.freeze({ ...value.flags }),
|
||||
counters: Object.freeze({ ...value.counters }),
|
||||
timers: Object.freeze({ ...value.timers }),
|
||||
worldObjectStates: Object.freeze({ ...value.worldObjectStates }),
|
||||
transitionStates: Object.freeze({ ...value.transitionStates }),
|
||||
activeSpawnGroupIds: unique(value.activeSpawnGroupIds),
|
||||
completedObjectiveIds: unique(value.completedObjectiveIds),
|
||||
consumedActionIds: unique(value.consumedActionIds),
|
||||
revision: Math.max(0, Math.trunc(value.revision)),
|
||||
});
|
||||
}
|
||||
|
||||
export function activeDungeonEncounterId(): string | null {
|
||||
return activeDungeonId;
|
||||
}
|
||||
|
||||
export function exportDungeonEncounterSnapshots(dungeonId = activeDungeonId): readonly DungeonEncounterSnapshot[] {
|
||||
if (!dungeonId) return [];
|
||||
return Object.freeze([...providers.values()]
|
||||
.filter((provider) => provider.dungeonId === dungeonId)
|
||||
.sort((left, right) => left.encounterId.localeCompare(right.encounterId))
|
||||
.map((provider) => cloneEncounterSnapshot(provider.getSnapshot())));
|
||||
}
|
||||
|
||||
/** Save-safe projection that strips timers and temporary waves without changing live state. */
|
||||
export function exportDungeonEncounterCheckpointSnapshots(
|
||||
dungeonId = activeDungeonId,
|
||||
now = Date.now(),
|
||||
): readonly DungeonEncounterSnapshot[] {
|
||||
if (!dungeonId) return [];
|
||||
return Object.freeze([...providers.values()]
|
||||
.filter((provider) => provider.dungeonId === dungeonId)
|
||||
.sort((left, right) => left.encounterId.localeCompare(right.encounterId))
|
||||
.map((provider) => {
|
||||
const live = provider.getSnapshot();
|
||||
const checkpoint = resetDungeonEncounterSnapshot(
|
||||
provider.definition,
|
||||
live,
|
||||
"checkpoint",
|
||||
now,
|
||||
);
|
||||
return cloneEncounterSnapshot({ ...checkpoint, revision: live.revision });
|
||||
}));
|
||||
}
|
||||
|
||||
export function importDungeonEncounterSnapshots(
|
||||
dungeonId: string,
|
||||
snapshots: readonly DungeonEncounterSnapshot[],
|
||||
): readonly string[] {
|
||||
const imported: string[] = [];
|
||||
for (const raw of snapshots) {
|
||||
const provider = providers.get(raw.encounterId);
|
||||
if (!provider || provider.dungeonId !== dungeonId || !provider.replaceSnapshot) continue;
|
||||
const snapshot = cloneEncounterSnapshot(raw);
|
||||
if (snapshot.revision < provider.getSnapshot().revision) continue;
|
||||
provider.replaceSnapshot(snapshot);
|
||||
imported.push(snapshot.encounterId);
|
||||
}
|
||||
if (imported.length) notifyEncounterSubscribers();
|
||||
return Object.freeze(imported.sort());
|
||||
}
|
||||
|
||||
export function drainDungeonEncounterEffects(
|
||||
dungeonId = activeDungeonId,
|
||||
): readonly DungeonEncounterEffectEnvelope[] {
|
||||
if (!dungeonId) return [];
|
||||
const drained = pendingEffects.filter((value) => value.dungeonId === dungeonId);
|
||||
for (let index = pendingEffects.length - 1; index >= 0; index -= 1) {
|
||||
if (pendingEffects[index].dungeonId === dungeonId) pendingEffects.splice(index, 1);
|
||||
}
|
||||
return Object.freeze(drained);
|
||||
}
|
||||
|
||||
/** Immediate application hook; effects also remain drainable for frame-bound consumers. */
|
||||
export function registerDungeonEncounterEffectHandler(
|
||||
handler: (value: DungeonEncounterEffectEnvelope) => void,
|
||||
): () => void {
|
||||
effectHandlers.add(handler);
|
||||
return () => effectHandlers.delete(handler);
|
||||
}
|
||||
|
||||
const encounterSubscribers = new Set<() => void>();
|
||||
|
||||
function notifyEncounterSubscribers(): void {
|
||||
for (const subscriber of encounterSubscribers) subscriber();
|
||||
}
|
||||
|
||||
/** React can consume this with useSyncExternalStore without owning simulation. */
|
||||
export function subscribeDungeonEncounterSnapshots(subscriber: () => void): () => void {
|
||||
encounterSubscribers.add(subscriber);
|
||||
return () => encounterSubscribers.delete(subscriber);
|
||||
}
|
||||
|
||||
export function activeDungeonEncounterRevision(): number {
|
||||
return exportDungeonEncounterSnapshots().reduce(
|
||||
(revision, snapshot) => revision + snapshot.revision,
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
export function activeDungeonEncounterSpawnGroupIds(): readonly string[] {
|
||||
return Object.freeze([...new Set(exportDungeonEncounterSnapshots()
|
||||
.flatMap((snapshot) => snapshot.activeSpawnGroupIds))].sort());
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { activeEncounterSpawnGroupSpawns } from "./dungeonEncounterSpawns";
|
||||
|
||||
const spawn = (id: string, spawnMask: number) => ({
|
||||
id,
|
||||
entityId: "test-mob",
|
||||
position: [0, 0, 0] as const,
|
||||
yaw: 0,
|
||||
spawnMask,
|
||||
});
|
||||
|
||||
describe("encounter spawn groups", () => {
|
||||
it("adds only active, difficulty-matching, non-duplicate runtime ids", () => {
|
||||
const result = activeEncounterSpawnGroupSpawns([
|
||||
{ id: "wave-a", spawns: [spawn("one", 1), spawn("two", 2)] },
|
||||
{ id: "wave-b", spawns: [spawn("three", 1)] },
|
||||
], ["wave-a"], 1, [spawn("one", 1)]);
|
||||
expect(result.map((entry) => entry.id)).toEqual([]);
|
||||
expect(activeEncounterSpawnGroupSpawns([
|
||||
{ id: "wave-a", spawns: [spawn("one", 1), spawn("two", 2)] },
|
||||
], ["wave-a"], 2).map((entry) => entry.id)).toEqual(["two"]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import { spawnMatchesDifficulty, type StaticMobSpawnDefinition } from "./mobPopulation";
|
||||
import type { DungeonEncounterSpawnGroupDefinition } from "./dungeonTypes";
|
||||
|
||||
export function activeEncounterSpawnGroupSpawns(
|
||||
groups: readonly DungeonEncounterSpawnGroupDefinition[],
|
||||
activeGroupIds: readonly string[],
|
||||
difficultyMask: number,
|
||||
existingSpawns: readonly StaticMobSpawnDefinition[] = [],
|
||||
): readonly StaticMobSpawnDefinition[] {
|
||||
const active = new Set(activeGroupIds);
|
||||
const existing = new Set(existingSpawns.map((spawn) => spawn.id));
|
||||
const included = new Set<string>();
|
||||
return groups
|
||||
.filter((group) => active.has(group.id))
|
||||
.flatMap((group) => group.spawns)
|
||||
.filter((spawn) => {
|
||||
if (existing.has(spawn.id) || included.has(spawn.id)) return false;
|
||||
if (!spawnMatchesDifficulty(spawn.spawnMask, difficultyMask)) return false;
|
||||
included.add(spawn.id);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { create, type StoreApi, type UseBoundStore } from "zustand";
|
||||
import {
|
||||
createDungeonEncounterSnapshot,
|
||||
reduceDungeonEncounter,
|
||||
registerDungeonEncounterProvider,
|
||||
resetDungeonEncounterSnapshot,
|
||||
validateDungeonEncounterDefinition,
|
||||
} from "./dungeonEncounterRuntime";
|
||||
import type {
|
||||
DungeonEncounterDefinition,
|
||||
DungeonEncounterEffect,
|
||||
DungeonEncounterEvent,
|
||||
DungeonEncounterResetScope,
|
||||
DungeonEncounterSnapshot,
|
||||
} from "./dungeonEncounterTypes";
|
||||
|
||||
export interface DungeonEncounterStoreState extends DungeonEncounterSnapshot {
|
||||
readonly dispatch: (event: DungeonEncounterEvent) => readonly DungeonEncounterEffect[];
|
||||
readonly reset: (scope?: DungeonEncounterResetScope, now?: number) => void;
|
||||
}
|
||||
|
||||
function serializableSnapshot(state: DungeonEncounterStoreState): DungeonEncounterSnapshot {
|
||||
return {
|
||||
schemaVersion: state.schemaVersion,
|
||||
encounterId: state.encounterId,
|
||||
phase: state.phase,
|
||||
phaseStartedAt: state.phaseStartedAt,
|
||||
flags: state.flags,
|
||||
counters: state.counters,
|
||||
timers: state.timers,
|
||||
worldObjectStates: state.worldObjectStates,
|
||||
transitionStates: state.transitionStates,
|
||||
activeSpawnGroupIds: state.activeSpawnGroupIds,
|
||||
completedObjectiveIds: state.completedObjectiveIds,
|
||||
consumedActionIds: state.consumedActionIds,
|
||||
checkpointId: state.checkpointId,
|
||||
revision: state.revision,
|
||||
};
|
||||
}
|
||||
|
||||
export function createDungeonEncounterStore(
|
||||
definition: DungeonEncounterDefinition,
|
||||
): UseBoundStore<StoreApi<DungeonEncounterStoreState>> {
|
||||
validateDungeonEncounterDefinition(definition);
|
||||
const useStore = create<DungeonEncounterStoreState>((set, get) => ({
|
||||
...createDungeonEncounterSnapshot(definition),
|
||||
dispatch: (event) => {
|
||||
const snapshot = serializableSnapshot(get());
|
||||
const result = reduceDungeonEncounter(definition, snapshot, event);
|
||||
if (result.snapshot !== snapshot) set(result.snapshot);
|
||||
return result.effects;
|
||||
},
|
||||
reset: (scope = "dungeon", now = 0) => {
|
||||
set(resetDungeonEncounterSnapshot(definition, serializableSnapshot(get()), scope, now));
|
||||
},
|
||||
}));
|
||||
const provider = {
|
||||
definition,
|
||||
encounterId: definition.id,
|
||||
dungeonId: definition.dungeonId,
|
||||
getSnapshot: () => serializableSnapshot(useStore.getState()),
|
||||
dispatch: (event: DungeonEncounterEvent) => useStore.getState().dispatch(event),
|
||||
reset: (scope: DungeonEncounterResetScope, now = 0) => useStore.getState().reset(scope, now),
|
||||
replaceSnapshot: (snapshot: DungeonEncounterSnapshot) => {
|
||||
if (snapshot.encounterId !== definition.id) {
|
||||
throw new Error(`Cannot hydrate ${definition.id} from ${snapshot.encounterId}`);
|
||||
}
|
||||
useStore.setState(snapshot);
|
||||
},
|
||||
};
|
||||
registerDungeonEncounterProvider(provider);
|
||||
return useStore;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import type { Vector3Tuple } from "./dungeonTypes";
|
||||
|
||||
export const DUNGEON_ENCOUNTER_SNAPSHOT_SCHEMA_VERSION = 1;
|
||||
|
||||
export type DungeonEncounterResetScope = "boss-wipe" | "checkpoint" | "dungeon";
|
||||
export type DungeonEncounterValue = string | number | boolean;
|
||||
|
||||
export interface DungeonEncounterFacts {
|
||||
readonly registeredEntityIds?: readonly string[];
|
||||
readonly deadEntityIds?: readonly string[];
|
||||
}
|
||||
|
||||
export type DungeonEncounterEvent =
|
||||
| {
|
||||
readonly kind: "tick";
|
||||
readonly now: number;
|
||||
readonly facts?: DungeonEncounterFacts;
|
||||
}
|
||||
| {
|
||||
readonly kind: "signal";
|
||||
readonly now: number;
|
||||
readonly signalId: string;
|
||||
readonly sourceEntityId?: string;
|
||||
}
|
||||
| { readonly kind: "entity-engaged"; readonly now: number; readonly entityId: string }
|
||||
| { readonly kind: "entity-death"; readonly now: number; readonly entityId: string }
|
||||
| {
|
||||
readonly kind: "entity-health";
|
||||
readonly now: number;
|
||||
readonly entityId: string;
|
||||
readonly healthPercent: number;
|
||||
}
|
||||
| {
|
||||
readonly kind: "interact";
|
||||
readonly now: number;
|
||||
readonly objectId: string;
|
||||
readonly actorId?: string;
|
||||
};
|
||||
|
||||
export type DungeonEncounterTrigger =
|
||||
| { readonly kind: "tick" }
|
||||
| { readonly kind: "signal"; readonly signalId: string }
|
||||
| { readonly kind: "entity-engaged"; readonly entityId: string }
|
||||
| { readonly kind: "entity-death"; readonly entityId: string }
|
||||
| {
|
||||
readonly kind: "entity-health";
|
||||
readonly entityId: string;
|
||||
readonly atOrBelowPercent: number;
|
||||
}
|
||||
| { readonly kind: "interact"; readonly objectId: string }
|
||||
| { readonly kind: "elapsed"; readonly afterMs: number }
|
||||
| {
|
||||
readonly kind: "entities-dead";
|
||||
readonly entityIds: readonly string[];
|
||||
/** A source-authored safety timeout prevents an encounter from soft locking. */
|
||||
readonly timeoutMs?: number;
|
||||
};
|
||||
|
||||
export type DungeonEncounterGuard =
|
||||
| { readonly kind: "phase"; readonly phases: readonly string[] }
|
||||
| { readonly kind: "flag"; readonly flagId: string; readonly equals: boolean }
|
||||
| {
|
||||
readonly kind: "counter";
|
||||
readonly counterId: string;
|
||||
readonly comparison: "equal" | "at-least" | "at-most";
|
||||
readonly value: number;
|
||||
}
|
||||
| { readonly kind: "objective-complete"; readonly objectiveId: string; readonly equals: boolean }
|
||||
| { readonly kind: "spawn-group-active"; readonly spawnGroupId: string; readonly equals: boolean }
|
||||
| { readonly kind: "world-object-state"; readonly objectId: string; readonly states: readonly string[] };
|
||||
|
||||
export type DungeonEncounterActionPersistence = "attempt" | "checkpoint" | "run";
|
||||
|
||||
interface DungeonEncounterActionBase {
|
||||
/** Stable, encounter-local id used to make rule execution idempotent. */
|
||||
readonly id: string;
|
||||
readonly persistence?: DungeonEncounterActionPersistence;
|
||||
}
|
||||
|
||||
export type DungeonEncounterAction =
|
||||
| (DungeonEncounterActionBase & { readonly kind: "set-phase"; readonly phase: string })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "set-flag"; readonly flagId: string; readonly value: boolean })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "set-counter"; readonly counterId: string; readonly value: number })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "increment-counter"; readonly counterId: string; readonly amount?: number })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "start-timer"; readonly timerId: string; readonly durationMs: number })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "clear-timer"; readonly timerId: string })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "activate-spawn-group"; readonly spawnGroupId: string })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "deactivate-spawn-group"; readonly spawnGroupId: string })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "set-world-object-state"; readonly objectId: string; readonly state: string })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "set-transition-active"; readonly transitionId: string; readonly active: boolean })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "complete-objective"; readonly objectiveId: string })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "set-checkpoint"; readonly checkpointId: string })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "teleport-party"; readonly destination: Vector3Tuple })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "grant-loot"; readonly lootTableId: string })
|
||||
| (DungeonEncounterActionBase & { readonly kind: "emit-signal"; readonly signalId: string });
|
||||
|
||||
export interface DungeonEncounterRule {
|
||||
readonly id: string;
|
||||
readonly trigger: DungeonEncounterTrigger;
|
||||
readonly guards?: readonly DungeonEncounterGuard[];
|
||||
readonly actions: readonly DungeonEncounterAction[];
|
||||
/** Defaults to true. Repeatable rules must remain safe when evaluated again. */
|
||||
readonly once?: boolean;
|
||||
readonly priority?: number;
|
||||
}
|
||||
|
||||
export interface DungeonEncounterInitialState {
|
||||
readonly flags?: Readonly<Record<string, boolean>>;
|
||||
readonly counters?: Readonly<Record<string, number>>;
|
||||
readonly worldObjectStates?: Readonly<Record<string, string>>;
|
||||
readonly transitionStates?: Readonly<Record<string, boolean>>;
|
||||
}
|
||||
|
||||
export interface DungeonEncounterResetPolicy {
|
||||
readonly bossWipePhase?: string;
|
||||
readonly checkpointPhase?: string;
|
||||
}
|
||||
|
||||
export interface DungeonEncounterDefinition {
|
||||
readonly id: string;
|
||||
readonly dungeonId: string;
|
||||
readonly initialPhase: string;
|
||||
readonly initialState?: DungeonEncounterInitialState;
|
||||
readonly resetPolicy?: DungeonEncounterResetPolicy;
|
||||
readonly rules: readonly DungeonEncounterRule[];
|
||||
}
|
||||
|
||||
/** JSON-safe source of truth; renderer objects and callbacks never enter this state. */
|
||||
export interface DungeonEncounterSnapshot {
|
||||
readonly schemaVersion: typeof DUNGEON_ENCOUNTER_SNAPSHOT_SCHEMA_VERSION;
|
||||
readonly encounterId: string;
|
||||
readonly phase: string;
|
||||
readonly phaseStartedAt: number;
|
||||
readonly flags: Readonly<Record<string, boolean>>;
|
||||
readonly counters: Readonly<Record<string, number>>;
|
||||
readonly timers: Readonly<Record<string, number>>;
|
||||
readonly worldObjectStates: Readonly<Record<string, string>>;
|
||||
readonly transitionStates: Readonly<Record<string, boolean>>;
|
||||
readonly activeSpawnGroupIds: readonly string[];
|
||||
readonly completedObjectiveIds: readonly string[];
|
||||
readonly consumedActionIds: readonly string[];
|
||||
readonly checkpointId: string | null;
|
||||
readonly revision: number;
|
||||
}
|
||||
|
||||
export type DungeonEncounterEffect =
|
||||
| { readonly kind: "spawn-group"; readonly actionId: string; readonly spawnGroupId: string }
|
||||
| { readonly kind: "despawn-group"; readonly actionId: string; readonly spawnGroupId: string }
|
||||
| { readonly kind: "world-object-state"; readonly actionId: string; readonly objectId: string; readonly state: string }
|
||||
| { readonly kind: "transition-state"; readonly actionId: string; readonly transitionId: string; readonly active: boolean }
|
||||
| { readonly kind: "party-teleport"; readonly actionId: string; readonly destination: Vector3Tuple }
|
||||
| { readonly kind: "loot-grant"; readonly actionId: string; readonly lootTableId: string }
|
||||
| { readonly kind: "signal"; readonly actionId: string; readonly signalId: string };
|
||||
|
||||
export interface DungeonEncounterReduction {
|
||||
readonly snapshot: DungeonEncounterSnapshot;
|
||||
readonly effects: readonly DungeonEncounterEffect[];
|
||||
readonly firedRuleIds: readonly string[];
|
||||
}
|
||||
|
||||
export interface DungeonEncounterEffectEnvelope {
|
||||
readonly dungeonId: string;
|
||||
readonly encounterId: string;
|
||||
readonly effect: DungeonEncounterEffect;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import * as recastPolicy from "../../scripts/dungeon-pipeline/recast-policy.mjs"
|
||||
const {
|
||||
nextTiledNavMeshSize,
|
||||
planNavigationBuild,
|
||||
shouldRetryFailedTiledBuildWithMonotone,
|
||||
shouldUseMonotonePartition,
|
||||
validateTiledBuild,
|
||||
} = recastPolicy;
|
||||
@@ -36,6 +37,14 @@ describe("dungeon Recast build policy", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("can explicitly tile compact but contour-complex source geometry", () => {
|
||||
expect(planNavigationBuild([[0, 0, 0], [40, 10, 40]], 0.2, {
|
||||
tileSize: 64,
|
||||
maxSoloAxis: 0,
|
||||
maxSoloCells: 0,
|
||||
})).toMatchObject({ mode: "tiled", tileSize: 64, tileColumns: 4, tileRows: 4 });
|
||||
});
|
||||
|
||||
it("accepts only confirmed empty-tile packing failures", () => {
|
||||
const intermediates = {
|
||||
buildContext: {
|
||||
@@ -156,4 +165,10 @@ describe("dungeon Recast build policy", () => {
|
||||
],
|
||||
})).toBe(false);
|
||||
});
|
||||
|
||||
it("retries watershed contour triangulation failures with monotone partition only", () => {
|
||||
expect(shouldRetryFailedTiledBuildWithMonotone("Failed to triangulate contours", "watershed")).toBe(true);
|
||||
expect(shouldRetryFailedTiledBuildWithMonotone("Failed to triangulate contours", "monotone")).toBe(false);
|
||||
expect(shouldRetryFailedTiledBuildWithMonotone("Out of memory", "watershed")).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -6,6 +6,7 @@ import { RUNEWAKER_DUNGEON_DEFINITIONS } from "./generated/runewakerDungeonDefin
|
||||
import compressedCampaign from "./generated/dungeonCampaignCatalog.compressed";
|
||||
import { gunzipSync, strFromU8 } from "fflate";
|
||||
import { DEFAULT_DUNGEON_ID as DEFAULT_DUNGEON_KEY } from "./dungeonDefaults";
|
||||
import { withRunewakerEncounterContent } from "./runewakerEncounterCatalog";
|
||||
|
||||
export { DEFAULT_DUNGEON_ID } from "./dungeonDefaults";
|
||||
|
||||
@@ -87,15 +88,19 @@ if (authoredWailingCaverns.id !== DEFAULT_DUNGEON_KEY) {
|
||||
throw new Error(`Default dungeon id mismatch: ${authoredWailingCaverns.id}`);
|
||||
}
|
||||
|
||||
export const DUNGEON_REGISTRY: Readonly<Record<string, DungeonDefinition>> = Object.freeze(
|
||||
Object.fromEntries([
|
||||
const dungeonRegistryEntries: readonly (readonly [string, DungeonDefinition])[] = [
|
||||
[authoredWailingCaverns.id, authoredWailingCaverns],
|
||||
[FORSAKEN_ABBEY_DEFINITION.id, FORSAKEN_ABBEY_DEFINITION],
|
||||
...RUNEWAKER_DUNGEON_DEFINITIONS.map((definition) => [definition.id, definition] as const),
|
||||
...generatedDefinitions
|
||||
.filter((definition) => definition.id !== authoredWailingCaverns.id)
|
||||
.map((definition) => [definition.id, definition] as const),
|
||||
]),
|
||||
];
|
||||
|
||||
export const DUNGEON_REGISTRY: Readonly<Record<string, DungeonDefinition>> = Object.freeze(
|
||||
Object.fromEntries(dungeonRegistryEntries.map(
|
||||
([id, definition]) => [id, withRunewakerEncounterContent(definition)] as const,
|
||||
)),
|
||||
);
|
||||
|
||||
export type DungeonId = string;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createDungeonEncounterSnapshot } from "./dungeonEncounterRuntime";
|
||||
import type { DungeonEncounterDefinition } from "./dungeonEncounterTypes";
|
||||
import {
|
||||
DUNGEON_RUN_CHECKPOINT_VERSION,
|
||||
normalizeDungeonRunCheckpoint,
|
||||
} from "./dungeonRunCheckpoint";
|
||||
|
||||
const definition: DungeonEncounterDefinition = {
|
||||
id: "test:boss",
|
||||
dungeonId: "test",
|
||||
initialPhase: "ready",
|
||||
rules: [],
|
||||
};
|
||||
|
||||
describe("dungeon run checkpoints", () => {
|
||||
it("normalizes JSON-safe encounter progress and world spawn", () => {
|
||||
const result = normalizeDungeonRunCheckpoint({
|
||||
version: DUNGEON_RUN_CHECKPOINT_VERSION,
|
||||
dungeonId: "test",
|
||||
difficultyId: "normal",
|
||||
checkpointId: "entrance",
|
||||
spawn: { footPosition: [1, 2, 3], yaw: 0.5 },
|
||||
encounters: [createDungeonEncounterSnapshot(definition)],
|
||||
savedAt: 123,
|
||||
});
|
||||
expect(result).toMatchObject({ dungeonId: "test", checkpointId: "entrance" });
|
||||
expect(result?.encounters[0]?.encounterId).toBe("test:boss");
|
||||
});
|
||||
|
||||
it("fails closed on malformed or unbounded data", () => {
|
||||
expect(normalizeDungeonRunCheckpoint({})).toBeNull();
|
||||
expect(normalizeDungeonRunCheckpoint({
|
||||
version: DUNGEON_RUN_CHECKPOINT_VERSION,
|
||||
dungeonId: "test",
|
||||
difficultyId: "normal",
|
||||
checkpointId: null,
|
||||
spawn: { footPosition: [Number.NaN, 2, 3], yaw: 0 },
|
||||
encounters: [],
|
||||
savedAt: 1,
|
||||
})).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,124 @@
|
||||
import type { DungeonEncounterSnapshot } from "./dungeonEncounterTypes";
|
||||
import { DUNGEON_ENCOUNTER_SNAPSHOT_SCHEMA_VERSION } from "./dungeonEncounterTypes";
|
||||
import type { Vector3Tuple, WorldSpawn } from "./dungeonTypes";
|
||||
|
||||
export const DUNGEON_RUN_CHECKPOINT_VERSION = 1;
|
||||
|
||||
export interface DungeonRunCheckpoint {
|
||||
readonly version: typeof DUNGEON_RUN_CHECKPOINT_VERSION;
|
||||
readonly dungeonId: string;
|
||||
readonly difficultyId: string;
|
||||
readonly checkpointId: string | null;
|
||||
readonly spawn: WorldSpawn;
|
||||
readonly encounters: readonly DungeonEncounterSnapshot[];
|
||||
readonly savedAt: number;
|
||||
}
|
||||
|
||||
function finiteVector(value: unknown): Vector3Tuple | null {
|
||||
if (!Array.isArray(value) || value.length !== 3 || value.some((entry) => (
|
||||
typeof entry !== "number" || !Number.isFinite(entry)
|
||||
))) return null;
|
||||
return [value[0], value[1], value[2]];
|
||||
}
|
||||
|
||||
function booleanRecord(value: unknown): Readonly<Record<string, boolean>> | null {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
||||
const entries = Object.entries(value as Record<string, unknown>);
|
||||
if (entries.some(([key, entry]) => !key || typeof entry !== "boolean")) return null;
|
||||
return Object.freeze(Object.fromEntries(entries) as Record<string, boolean>);
|
||||
}
|
||||
|
||||
function numberRecord(value: unknown): Readonly<Record<string, number>> | null {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
||||
const entries = Object.entries(value as Record<string, unknown>);
|
||||
if (entries.some(([key, entry]) => !key || typeof entry !== "number" || !Number.isFinite(entry))) return null;
|
||||
return Object.freeze(Object.fromEntries(entries) as Record<string, number>);
|
||||
}
|
||||
|
||||
function stringRecord(value: unknown): Readonly<Record<string, string>> | null {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
||||
const entries = Object.entries(value as Record<string, unknown>);
|
||||
if (entries.some(([key, entry]) => !key || typeof entry !== "string")) return null;
|
||||
return Object.freeze(Object.fromEntries(entries) as Record<string, string>);
|
||||
}
|
||||
|
||||
function stringList(value: unknown): readonly string[] | null {
|
||||
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || !entry)) return null;
|
||||
return Object.freeze([...new Set(value)]);
|
||||
}
|
||||
|
||||
function normalizeSnapshot(value: unknown): DungeonEncounterSnapshot | null {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
||||
const input = value as Partial<DungeonEncounterSnapshot>;
|
||||
if (
|
||||
input.schemaVersion !== DUNGEON_ENCOUNTER_SNAPSHOT_SCHEMA_VERSION
|
||||
|| typeof input.encounterId !== "string"
|
||||
|| !input.encounterId
|
||||
|| typeof input.phase !== "string"
|
||||
|| !input.phase
|
||||
|| typeof input.phaseStartedAt !== "number"
|
||||
|| !Number.isFinite(input.phaseStartedAt)
|
||||
|| typeof input.revision !== "number"
|
||||
|| !Number.isFinite(input.revision)
|
||||
|| (input.checkpointId !== null && typeof input.checkpointId !== "string")
|
||||
) return null;
|
||||
const flags = booleanRecord(input.flags);
|
||||
const counters = numberRecord(input.counters);
|
||||
const timers = numberRecord(input.timers);
|
||||
const worldObjectStates = stringRecord(input.worldObjectStates);
|
||||
const transitionStates = booleanRecord(input.transitionStates);
|
||||
const activeSpawnGroupIds = stringList(input.activeSpawnGroupIds);
|
||||
const completedObjectiveIds = stringList(input.completedObjectiveIds);
|
||||
const consumedActionIds = stringList(input.consumedActionIds);
|
||||
if (!flags || !counters || !timers || !worldObjectStates || !transitionStates
|
||||
|| !activeSpawnGroupIds || !completedObjectiveIds || !consumedActionIds) return null;
|
||||
return Object.freeze({
|
||||
schemaVersion: DUNGEON_ENCOUNTER_SNAPSHOT_SCHEMA_VERSION,
|
||||
encounterId: input.encounterId,
|
||||
phase: input.phase,
|
||||
phaseStartedAt: input.phaseStartedAt,
|
||||
flags,
|
||||
counters,
|
||||
timers,
|
||||
worldObjectStates,
|
||||
transitionStates,
|
||||
activeSpawnGroupIds,
|
||||
completedObjectiveIds,
|
||||
consumedActionIds,
|
||||
checkpointId: input.checkpointId ?? null,
|
||||
revision: Math.max(0, Math.trunc(input.revision)),
|
||||
});
|
||||
}
|
||||
|
||||
export function normalizeDungeonRunCheckpoint(value: unknown): DungeonRunCheckpoint | null {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
||||
const input = value as Partial<DungeonRunCheckpoint>;
|
||||
const position = finiteVector(input.spawn?.footPosition);
|
||||
const yaw = input.spawn?.yaw;
|
||||
if (
|
||||
input.version !== DUNGEON_RUN_CHECKPOINT_VERSION
|
||||
|| typeof input.dungeonId !== "string"
|
||||
|| !input.dungeonId
|
||||
|| typeof input.difficultyId !== "string"
|
||||
|| !input.difficultyId
|
||||
|| !position
|
||||
|| typeof yaw !== "number"
|
||||
|| !Number.isFinite(yaw)
|
||||
|| !Array.isArray(input.encounters)
|
||||
|| input.encounters.length > 128
|
||||
|| typeof input.savedAt !== "number"
|
||||
|| !Number.isFinite(input.savedAt)
|
||||
|| (input.checkpointId !== null && typeof input.checkpointId !== "string")
|
||||
) return null;
|
||||
const encounters = input.encounters.map(normalizeSnapshot);
|
||||
if (encounters.some((entry) => entry === null)) return null;
|
||||
return Object.freeze({
|
||||
version: DUNGEON_RUN_CHECKPOINT_VERSION,
|
||||
dungeonId: input.dungeonId,
|
||||
difficultyId: input.difficultyId,
|
||||
checkpointId: input.checkpointId ?? null,
|
||||
spawn: { footPosition: position, yaw },
|
||||
encounters: Object.freeze(encounters as DungeonEncounterSnapshot[]),
|
||||
savedAt: input.savedAt,
|
||||
});
|
||||
}
|
||||
@@ -10,6 +10,12 @@ import { dungeonCanEnter, dungeonDefinitionById, type DungeonId } from "./dungeo
|
||||
import type { WorldSpawn } from "./dungeonTypes";
|
||||
import { useManastormAdminStore } from "./manastormAdminStore";
|
||||
import type { GmReviewTarget } from "./gmContentReview";
|
||||
import {
|
||||
activateDungeonEncounterDefinitions,
|
||||
importDungeonEncounterSnapshots,
|
||||
resetDungeonEncounters,
|
||||
} from "./dungeonEncounterRuntime";
|
||||
import type { DungeonEncounterSnapshot } from "./dungeonEncounterTypes";
|
||||
|
||||
export interface DungeonSessionOptions {
|
||||
readonly gameMode?: GameMode;
|
||||
@@ -18,6 +24,8 @@ export interface DungeonSessionOptions {
|
||||
readonly gmReviewTarget?: GmReviewTarget | null;
|
||||
/** Used by in-game encounter transitions so character progression and inventory survive. */
|
||||
readonly preserveCharacter?: boolean;
|
||||
/** Hydrates save-safe state after definitions have been activated and reset. */
|
||||
readonly encounterSnapshots?: readonly DungeonEncounterSnapshot[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,6 +45,8 @@ export function activateDungeonSession(
|
||||
|
||||
const previousDefinition = dungeonDefinitionById(useGameStore.getState().activeDungeonId);
|
||||
if (previousDefinition) releaseDungeonGltfCache(previousDefinition);
|
||||
activateDungeonEncounterDefinitions(dungeonId, definition.encounters ?? []);
|
||||
resetDungeonEncounters("dungeon", dungeonId);
|
||||
clearMobRuntimeRegistry();
|
||||
clearPartyRuntimeRegistry();
|
||||
clearDungeonRuntimeCaches();
|
||||
@@ -80,6 +90,9 @@ export function activateDungeonSession(
|
||||
gmTestMode: options.gmTestMode,
|
||||
gmReviewTarget: options.gmReviewTarget,
|
||||
});
|
||||
if (options.encounterSnapshots?.length) {
|
||||
importDungeonEncounterSnapshots(dungeonId, options.encounterSnapshots);
|
||||
}
|
||||
useCombatStore.getState().setPlayerPosition(useGameStore.getState().playerPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import { usePartyStore } from "./partyStore";
|
||||
import { useGameStore } from "./store";
|
||||
import { DEFAULT_MANASTORM_ADMIN_CONFIG } from "./manastormAdminConfig";
|
||||
import { useManastormAdminStore } from "./manastormAdminStore";
|
||||
import { dungeonEncounterSnapshot } from "./dungeonEncounterRuntime";
|
||||
import { WAILING_NARALEX_ENCOUNTER, useWailingEncounterStore } from "./wailingCavernsEncounter";
|
||||
|
||||
describe("dungeon session asset eviction", () => {
|
||||
afterEach(() => {
|
||||
@@ -56,4 +58,13 @@ describe("dungeon session asset eviction", () => {
|
||||
expect(activateDungeonSession("wailing-caverns")).toBe(true);
|
||||
expect(useGameStore.getState().activeSpawn).toEqual(spawn);
|
||||
});
|
||||
|
||||
it("hydrates a safe encounter checkpoint after the dungeon reset", () => {
|
||||
const current = dungeonEncounterSnapshot(WAILING_NARALEX_ENCOUNTER.id)!;
|
||||
const saved = { ...current, phase: "ready", revision: current.revision + 10 } as const;
|
||||
expect(activateDungeonSession("wailing-caverns", "normal", {
|
||||
encounterSnapshots: [saved],
|
||||
})).toBe(true);
|
||||
expect(useWailingEncounterStore.getState().phase).toBe("ready");
|
||||
});
|
||||
});
|
||||
@@ -4,6 +4,8 @@ import type {
|
||||
PopulationDefinitionMap,
|
||||
StaticMobSpawnDefinition,
|
||||
} from "./mobPopulation";
|
||||
import type { DungeonEncounterDefinition } from "./dungeonEncounterTypes";
|
||||
import type { DungeonWorldObjectDefinition } from "./dungeonWorldObjects";
|
||||
|
||||
export type Vector3Tuple = MobVector3;
|
||||
export type DungeonEnvironmentMode = "global-wmo" | "adt-hybrid" | "runewaker-ros";
|
||||
@@ -154,6 +156,11 @@ export interface DungeonNavigationLink {
|
||||
readonly authoredReason: string;
|
||||
}
|
||||
|
||||
export interface DungeonEncounterSpawnGroupDefinition {
|
||||
readonly id: string;
|
||||
readonly spawns: readonly StaticMobSpawnDefinition[];
|
||||
}
|
||||
|
||||
export interface DungeonMapPresentation {
|
||||
readonly width: number;
|
||||
readonly height: number;
|
||||
@@ -232,7 +239,11 @@ export interface DungeonDefinition {
|
||||
readonly bosses: readonly DungeonBossObjective[];
|
||||
readonly objectiveOrder: readonly DungeonBossObjective[];
|
||||
readonly navigationLinks: readonly DungeonNavigationLink[];
|
||||
readonly serverGameObjects: readonly unknown[];
|
||||
/** Serializable dungeon-wide orchestration kept separate from render state. */
|
||||
readonly encounters?: readonly DungeonEncounterDefinition[];
|
||||
/** Declarative groups activated by encounter actions; renderer ownership stays outside the reducer. */
|
||||
readonly encounterSpawnGroups?: readonly DungeonEncounterSpawnGroupDefinition[];
|
||||
readonly serverGameObjects: readonly DungeonWorldObjectDefinition[];
|
||||
readonly provenance: DungeonSourceProvenance;
|
||||
readonly validation: DungeonValidationStatus;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { requireDungeonDefinition } from "./dungeonRegistry";
|
||||
import {
|
||||
dispatchActiveDungeonInteraction,
|
||||
dispatchDungeonEncounterSignal,
|
||||
exportDungeonEncounterSnapshots,
|
||||
} from "./dungeonEncounterRuntime";
|
||||
import { combatHasLineOfSight } from "./combatSpatial";
|
||||
import {
|
||||
nearestDungeonWorldInteraction,
|
||||
type DungeonWorldObjectDefinition,
|
||||
type DungeonWorldInteractionCandidate,
|
||||
} from "./dungeonWorldObjects";
|
||||
import type { MobVector3 } from "./mobPopulation";
|
||||
import {
|
||||
onlinePlayerActorId,
|
||||
onlineSessionLocalAccountId,
|
||||
requestOnlineInteraction,
|
||||
} from "./onlineSessionRuntime";
|
||||
import { useGameStore } from "./store";
|
||||
|
||||
export function activeDungeonWorldObjectStates(): Readonly<Record<string, string>> {
|
||||
const game = useGameStore.getState();
|
||||
const states: Record<string, string> = {};
|
||||
for (const snapshot of exportDungeonEncounterSnapshots(game.activeDungeonId)) {
|
||||
Object.assign(states, snapshot.worldObjectStates);
|
||||
}
|
||||
return states;
|
||||
}
|
||||
|
||||
export function activeDungeonInteractionCandidate(): DungeonWorldInteractionCandidate | null {
|
||||
const game = useGameStore.getState();
|
||||
return dungeonInteractionCandidateAt(game.playerPosition);
|
||||
}
|
||||
|
||||
export function dungeonInteractionCandidateAt(origin: MobVector3): DungeonWorldInteractionCandidate | null {
|
||||
const game = useGameStore.getState();
|
||||
if (game.gameMode !== "dungeon") return null;
|
||||
const definition = requireDungeonDefinition(game.activeDungeonId);
|
||||
return nearestDungeonWorldInteraction(
|
||||
definition.serverGameObjects,
|
||||
activeDungeonWorldObjectStates(),
|
||||
origin,
|
||||
combatHasLineOfSight,
|
||||
);
|
||||
}
|
||||
|
||||
export function dispatchDungeonWorldObjectInteraction(
|
||||
object: DungeonWorldObjectDefinition,
|
||||
actorId: string | undefined,
|
||||
now: number,
|
||||
): void {
|
||||
dispatchActiveDungeonInteraction(object.id, actorId, now);
|
||||
dispatchDungeonEncounterSignal(object.interaction!.signalId, now);
|
||||
}
|
||||
|
||||
/** Followers request; the party leader validates and mutates the shared encounter. */
|
||||
export function interactWithActiveDungeonObject(now = Date.now()): boolean {
|
||||
const candidate = activeDungeonInteractionCandidate();
|
||||
if (!candidate) return false;
|
||||
if (requestOnlineInteraction("interact", candidate.object.id)) return true;
|
||||
const localAccountId = onlineSessionLocalAccountId();
|
||||
const actorId = localAccountId ? onlinePlayerActorId(localAccountId) : undefined;
|
||||
dispatchDungeonWorldObjectInteraction(candidate.object, actorId, now);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
dungeonWorldObjectState,
|
||||
nearestDungeonWorldInteraction,
|
||||
validateDungeonWorldObject,
|
||||
type DungeonWorldObjectDefinition,
|
||||
} from "./dungeonWorldObjects";
|
||||
|
||||
const door: DungeonWorldObjectDefinition = {
|
||||
id: "test-door",
|
||||
name: "Test Door",
|
||||
kind: "door",
|
||||
position: [3, 0, 0],
|
||||
halfExtents: [1, 2, 0.25],
|
||||
initialState: "closed",
|
||||
states: [
|
||||
{ id: "closed", label: "Closed", visible: true, blocksMovement: true, interactionEnabled: true },
|
||||
{ id: "open", label: "Open", visible: false, blocksMovement: false, interactionEnabled: false },
|
||||
],
|
||||
interaction: {
|
||||
prompt: "Open door",
|
||||
range: 4,
|
||||
signalId: "open-test-door",
|
||||
allowedStates: ["closed"],
|
||||
},
|
||||
};
|
||||
|
||||
describe("dungeon world objects", () => {
|
||||
it("resolves authored and fallback object states", () => {
|
||||
expect(dungeonWorldObjectState(door, "open").blocksMovement).toBe(false);
|
||||
expect(dungeonWorldObjectState(door, "missing").id).toBe("closed");
|
||||
});
|
||||
|
||||
it("selects the nearest enabled visible interaction in range and line of sight", () => {
|
||||
const farther = { ...door, id: "far-door", position: [3.5, 0, 0] as const };
|
||||
expect(nearestDungeonWorldInteraction([farther, door], {}, [0, 0, 0])?.object.id).toBe("test-door");
|
||||
expect(nearestDungeonWorldInteraction([door], { "test-door": "open" }, [0, 0, 0])).toBeNull();
|
||||
expect(nearestDungeonWorldInteraction([door], {}, [0, 0, 0], () => false)).toBeNull();
|
||||
});
|
||||
|
||||
it("rejects malformed geometry, state, and interaction contracts", () => {
|
||||
expect(() => validateDungeonWorldObject(door)).not.toThrow();
|
||||
expect(() => validateDungeonWorldObject({ ...door, halfExtents: [0, 2, 1] })).toThrow("invalid geometry");
|
||||
expect(() => validateDungeonWorldObject({ ...door, initialState: "missing" })).toThrow("does not define");
|
||||
expect(() => validateDungeonWorldObject({
|
||||
...door,
|
||||
interaction: { ...door.interaction!, range: 0 },
|
||||
})).toThrow("invalid interaction");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,137 @@
|
||||
import type { MobVector3 } from "./mobPopulation";
|
||||
|
||||
export type DungeonWorldObjectKind =
|
||||
| "door"
|
||||
| "gate"
|
||||
| "bridge"
|
||||
| "switch"
|
||||
| "key"
|
||||
| "chest"
|
||||
| "npc"
|
||||
| "quest-object"
|
||||
| "trigger";
|
||||
|
||||
export type DungeonWorldObjectFidelity =
|
||||
| "source-exact"
|
||||
| "source-backed-adapted"
|
||||
| "healer-man-authored";
|
||||
|
||||
export interface DungeonWorldObjectSource {
|
||||
readonly fidelity: DungeonWorldObjectFidelity;
|
||||
readonly files: readonly string[];
|
||||
readonly templateId?: number;
|
||||
readonly spawnId?: number;
|
||||
readonly note?: string;
|
||||
}
|
||||
|
||||
export interface DungeonWorldObjectStateDefinition {
|
||||
readonly id: string;
|
||||
readonly label: string;
|
||||
readonly visible: boolean;
|
||||
readonly blocksMovement: boolean;
|
||||
readonly blocksSight?: boolean;
|
||||
readonly interactionEnabled?: boolean;
|
||||
}
|
||||
|
||||
export interface DungeonWorldObjectInteraction {
|
||||
readonly prompt: string;
|
||||
readonly range: number;
|
||||
readonly signalId: string;
|
||||
readonly allowedStates?: readonly string[];
|
||||
readonly oneShot?: boolean;
|
||||
readonly instructions?: string;
|
||||
}
|
||||
|
||||
export interface DungeonWorldObjectVisual {
|
||||
/** Optional independently packaged GLB. Procedural geometry remains the safe fallback. */
|
||||
readonly assetUrl?: string;
|
||||
readonly color?: string;
|
||||
readonly emissive?: string;
|
||||
readonly scale?: MobVector3;
|
||||
}
|
||||
|
||||
export interface DungeonWorldObjectDefinition {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly kind: DungeonWorldObjectKind;
|
||||
readonly position: MobVector3;
|
||||
readonly yaw?: number;
|
||||
/** Half-extents of the interaction/collision box in metres. */
|
||||
readonly halfExtents: MobVector3;
|
||||
readonly initialState: string;
|
||||
readonly states: readonly DungeonWorldObjectStateDefinition[];
|
||||
readonly interaction?: DungeonWorldObjectInteraction;
|
||||
readonly visual?: DungeonWorldObjectVisual;
|
||||
readonly source?: DungeonWorldObjectSource;
|
||||
}
|
||||
|
||||
export interface DungeonWorldInteractionCandidate {
|
||||
readonly object: DungeonWorldObjectDefinition;
|
||||
readonly state: DungeonWorldObjectStateDefinition;
|
||||
readonly distance: number;
|
||||
}
|
||||
|
||||
function finiteDistance(left: MobVector3, right: MobVector3): number {
|
||||
if (!left.every(Number.isFinite) || !right.every(Number.isFinite)) return Number.POSITIVE_INFINITY;
|
||||
return Math.hypot(left[0] - right[0], left[1] - right[1], left[2] - right[2]);
|
||||
}
|
||||
|
||||
export function dungeonWorldObjectState(
|
||||
object: DungeonWorldObjectDefinition,
|
||||
stateId: string | undefined,
|
||||
): DungeonWorldObjectStateDefinition {
|
||||
const requested = stateId ?? object.initialState;
|
||||
return object.states.find((state) => state.id === requested)
|
||||
?? object.states.find((state) => state.id === object.initialState)
|
||||
?? {
|
||||
id: object.initialState,
|
||||
label: object.initialState,
|
||||
visible: true,
|
||||
blocksMovement: false,
|
||||
interactionEnabled: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function validateDungeonWorldObject(object: DungeonWorldObjectDefinition): void {
|
||||
if (!object.id.trim()) throw new Error("Dungeon world objects require a stable id.");
|
||||
if (!object.name.trim()) throw new Error(`Dungeon world object ${object.id} requires a name.`);
|
||||
if (!object.position.every(Number.isFinite) || !object.halfExtents.every((value) => Number.isFinite(value) && value > 0)) {
|
||||
throw new Error(`Dungeon world object ${object.id} has invalid geometry.`);
|
||||
}
|
||||
const stateIds = new Set<string>();
|
||||
for (const state of object.states) {
|
||||
if (!state.id.trim() || stateIds.has(state.id)) {
|
||||
throw new Error(`Dungeon world object ${object.id} has an invalid or duplicate state id.`);
|
||||
}
|
||||
stateIds.add(state.id);
|
||||
}
|
||||
if (!stateIds.has(object.initialState)) {
|
||||
throw new Error(`Dungeon world object ${object.id} does not define initial state ${object.initialState}.`);
|
||||
}
|
||||
if (object.interaction && (!object.interaction.signalId.trim() || object.interaction.range <= 0)) {
|
||||
throw new Error(`Dungeon world object ${object.id} has an invalid interaction.`);
|
||||
}
|
||||
}
|
||||
|
||||
export function nearestDungeonWorldInteraction(
|
||||
objects: readonly DungeonWorldObjectDefinition[],
|
||||
states: Readonly<Record<string, string>>,
|
||||
origin: MobVector3,
|
||||
hasLineOfSight: (from: MobVector3, to: MobVector3) => boolean = () => true,
|
||||
): DungeonWorldInteractionCandidate | null {
|
||||
let nearest: DungeonWorldInteractionCandidate | null = null;
|
||||
for (const object of objects) {
|
||||
const interaction = object.interaction;
|
||||
if (!interaction) continue;
|
||||
const state = dungeonWorldObjectState(object, states[object.id]);
|
||||
if (!state.visible) continue;
|
||||
if (state.interactionEnabled === false) continue;
|
||||
if (interaction.allowedStates?.length && !interaction.allowedStates.includes(state.id)) continue;
|
||||
const distance = finiteDistance(origin, object.position);
|
||||
if (distance > interaction.range || !hasLineOfSight(origin, object.position)) continue;
|
||||
if (!nearest || distance < nearest.distance || (distance === nearest.distance && object.id < nearest.object.id)) {
|
||||
nearest = { object, state, distance };
|
||||
}
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
@@ -8,6 +8,10 @@ import {
|
||||
type DungeonDefinition,
|
||||
} from "../dungeonTypes";
|
||||
import { withWailingServerCombat } from "../wailingCavernsServer";
|
||||
import {
|
||||
WAILING_NARALEX_ENCOUNTER,
|
||||
WAILING_NARALEX_SPAWN_GROUPS,
|
||||
} from "../wailingCavernsEncounter";
|
||||
|
||||
export const WAILING_CAVERNS_DEFINITION = {
|
||||
schemaVersion: DUNGEON_DEFINITION_SCHEMA_VERSION,
|
||||
@@ -117,6 +121,8 @@ export const WAILING_CAVERNS_DEFINITION = {
|
||||
authoredReason: "Golden-fixture bridge for the Recast discontinuity between upper and lower cavern route surfaces.",
|
||||
},
|
||||
],
|
||||
encounters: [WAILING_NARALEX_ENCOUNTER],
|
||||
encounterSpawnGroups: WAILING_NARALEX_SPAWN_GROUPS,
|
||||
serverGameObjects: [],
|
||||
provenance: {
|
||||
clientBuild: "3.3.5a",
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import {
|
||||
normalizeRunewakerDamageFormula,
|
||||
type RunewakerDamageFormulaSource,
|
||||
} from "./runewakerCombatFormula";
|
||||
|
||||
export type EnemyAttackAnimation = "attack" | "cast";
|
||||
export type EnemyAttackDelivery = "melee" | "projectile" | "area";
|
||||
export type EnemyAttackTarget =
|
||||
@@ -52,7 +57,15 @@ export type EnemyAbilityEffect =
|
||||
readonly durationMs: number;
|
||||
}
|
||||
| { readonly kind: "transform"; readonly form: string }
|
||||
| { readonly kind: "summon"; readonly creatureEntry: number; readonly count: number }
|
||||
| {
|
||||
readonly kind: "summon";
|
||||
readonly creatureEntry: number;
|
||||
readonly count: number;
|
||||
/** Optional stable namespace for content-authored summoned entities. */
|
||||
readonly entityId?: string;
|
||||
/** Optional owner group used by encounter counters and reset cleanup. */
|
||||
readonly spawnGroupId?: string;
|
||||
}
|
||||
| { readonly kind: "call-for-help"; readonly radius: number }
|
||||
| { readonly kind: "encounter-event"; readonly eventId: string };
|
||||
|
||||
@@ -102,6 +115,8 @@ export interface EnemyAttackDefinition {
|
||||
readonly repeatCooldownMs?: readonly [minimum: number, maximum: number];
|
||||
readonly maximumCasts?: number;
|
||||
readonly damageMultiplier: number;
|
||||
/** Source-shaped RuneWaker formula evidence for imported damage mechanics. */
|
||||
readonly runewakerFormula?: RunewakerDamageFormulaSource;
|
||||
readonly radius?: number;
|
||||
readonly telegraph?: EnemyAttackTelegraph;
|
||||
readonly effect?: EnemyAbilityEffect;
|
||||
@@ -236,6 +251,7 @@ export function normalizeEnemyAttack(
|
||||
Math.round(finiteAtLeast(value.castTimeMs, inferredCastTimeMs, 0)),
|
||||
telegraph?.warningMs ?? 0,
|
||||
);
|
||||
const runewakerFormula = normalizeRunewakerDamageFormula(value.runewakerFormula);
|
||||
return Object.freeze({
|
||||
id: value.id.trim() || fallback.id,
|
||||
name: value.name.trim() || fallback.name,
|
||||
@@ -260,6 +276,7 @@ export function normalizeEnemyAttack(
|
||||
? { maximumCasts: value.maximumCasts }
|
||||
: {}),
|
||||
damageMultiplier: finiteAtLeast(value.damageMultiplier, fallback.damageMultiplier, 0),
|
||||
...(runewakerFormula ? { runewakerFormula } : {}),
|
||||
...(value.radius !== undefined
|
||||
? { radius: finiteAtLeast(value.radius, 0, 0) }
|
||||
: {}),
|
||||
|
||||
@@ -172,6 +172,48 @@ describe("server-backed enemy ability runtime", () => {
|
||||
expect(summonIds.every((id) => getMobPosition(id) === null)).toBe(true);
|
||||
});
|
||||
|
||||
it("uses content-authored summon namespaces and spawn-group ownership", () => {
|
||||
useCombatStore.getState().registerMob("wave-controller", {
|
||||
name: "Wave Controller",
|
||||
maxHealth: 1_000,
|
||||
attacks: [{
|
||||
id: "release-wave",
|
||||
name: "Release Wave",
|
||||
delivery: "area",
|
||||
target: "self",
|
||||
school: "shadow",
|
||||
animation: "cast",
|
||||
range: 0,
|
||||
cooldownMs: 60_000,
|
||||
initialCooldownMs: [0, 0],
|
||||
repeatCooldownMs: [60_000, 60_000],
|
||||
maximumCasts: 1,
|
||||
damageMultiplier: 0,
|
||||
effect: {
|
||||
kind: "summon",
|
||||
creatureEntry: 5048,
|
||||
count: 2,
|
||||
entityId: "forsaken-abbey:necromage:add",
|
||||
spawnGroupId: "forsaken-abbey:necromage:wave-one",
|
||||
},
|
||||
}],
|
||||
});
|
||||
setMobPosition("wave-controller", [2, 0, 0]);
|
||||
useCombatStore.getState().damageMob("wave-controller", 1, 1_000);
|
||||
useCombatStore.getState().advanceMobCombat(1_000, [0, 0, 0]);
|
||||
useCombatStore.getState().advanceMobCombat(2_500, [0, 0, 0]);
|
||||
|
||||
const summonIds = Object.keys(useCombatStore.getState().mobs)
|
||||
.filter((id) => id.startsWith("forsaken-abbey:necromage:add:"));
|
||||
expect(summonIds).toEqual([
|
||||
"forsaken-abbey:necromage:add:1:1",
|
||||
"forsaken-abbey:necromage:add:1:2",
|
||||
]);
|
||||
expect(summonIds.every((id) => (
|
||||
useCombatStore.getState().mobs[id].spawnGroupId === "forsaken-abbey:necromage:wave-one"
|
||||
))).toBe(true);
|
||||
});
|
||||
|
||||
it("applies a modular target aura as a dispellable player debuff", () => {
|
||||
useCombatStore.getState().registerMob("runewaker-debuffer", {
|
||||
name: "RuneWaker Debuffer",
|
||||
|
||||
@@ -124,7 +124,25 @@ export const AETERNAL_CIRCLE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495702,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 100,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -224,7 +242,25 @@ export const AETERNAL_CIRCLE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495702,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 100,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -284,7 +320,25 @@ export const AETERNAL_CIRCLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498065,
|
||||
"componentId": 621360,
|
||||
"spellLevel": 25,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-aeternal-circle-spell-498605",
|
||||
@@ -304,7 +358,25 @@ export const AETERNAL_CIRCLE_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498605,
|
||||
"componentId": 621360,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -364,7 +436,25 @@ export const AETERNAL_CIRCLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498065,
|
||||
"componentId": 621360,
|
||||
"spellLevel": 25,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-aeternal-circle-spell-498607",
|
||||
@@ -384,7 +474,25 @@ export const AETERNAL_CIRCLE_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498607,
|
||||
"componentId": 621361,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -444,7 +552,25 @@ export const AETERNAL_CIRCLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498065,
|
||||
"componentId": 621360,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-aeternal-circle-spell-498607",
|
||||
@@ -464,7 +590,25 @@ export const AETERNAL_CIRCLE_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498607,
|
||||
"componentId": 621361,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -524,7 +668,25 @@ export const AETERNAL_CIRCLE_ENTITIES = {
|
||||
6000,
|
||||
6000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498845,
|
||||
"componentId": 621361,
|
||||
"spellLevel": 25,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -624,7 +786,25 @@ export const AETERNAL_CIRCLE_ENTITIES = {
|
||||
3000,
|
||||
3000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498609,
|
||||
"componentId": 621361,
|
||||
"spellLevel": 25,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -724,7 +904,25 @@ export const AETERNAL_CIRCLE_ENTITIES = {
|
||||
3000,
|
||||
3000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498851,
|
||||
"componentId": 621378,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -784,7 +982,25 @@ export const AETERNAL_CIRCLE_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498852,
|
||||
"componentId": 621378,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-aeternal-circle-spell-498065",
|
||||
@@ -804,7 +1020,25 @@ export const AETERNAL_CIRCLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498065,
|
||||
"componentId": 621360,
|
||||
"spellLevel": 25,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2303,6 +2537,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -44,7 +44,25 @@ export const BARREN_CAVES_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492289,
|
||||
"componentId": 502503,
|
||||
"spellLevel": 6,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -104,7 +122,25 @@ export const BARREN_CAVES_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492290,
|
||||
"componentId": 502505,
|
||||
"spellLevel": 6,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -244,7 +280,25 @@ export const BARREN_CAVES_ENTITIES = {
|
||||
3000,
|
||||
3000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490629,
|
||||
"componentId": 501086,
|
||||
"spellLevel": 22,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-barren-caves-spell-490630",
|
||||
@@ -264,7 +318,25 @@ export const BARREN_CAVES_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490630,
|
||||
"componentId": 501087,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -324,7 +396,25 @@ export const BARREN_CAVES_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492291,
|
||||
"componentId": 501045,
|
||||
"spellLevel": 3,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -398,6 +488,24 @@ export const BARREN_CAVES_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492292,
|
||||
"componentId": 502507,
|
||||
"spellLevel": 3,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -426,6 +534,24 @@ export const BARREN_CAVES_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492293,
|
||||
"componentId": 502509,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1062,6 +1188,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -84,7 +84,25 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494168,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -144,7 +162,25 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494172,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -295,7 +331,25 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494249,
|
||||
"componentId": 504612,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -80,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-cave-water-dragon-102623-spell-494250",
|
||||
@@ -316,7 +370,25 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494250,
|
||||
"componentId": 504753,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -50,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-cave-water-dragon-102623-spell-494253",
|
||||
@@ -337,7 +409,25 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494253,
|
||||
"componentId": 504771,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -3500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-cave-water-dragon-102623-spell-494254",
|
||||
@@ -365,6 +455,24 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 10
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494254,
|
||||
"componentId": 504772,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -3000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -536,6 +644,24 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493996,
|
||||
"componentId": 502444,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -564,6 +690,24 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493999,
|
||||
"componentId": 502444,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -592,6 +736,24 @@ export const CAVE_WATER_DRAGON_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494000,
|
||||
"componentId": 502444,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1769,6 +1931,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} 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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -44,7 +44,25 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495054,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -184,7 +202,25 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495007,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-demon-stronghold-spell-495008",
|
||||
@@ -204,7 +240,25 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495008,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -264,7 +318,25 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495007,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 2,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-demon-stronghold-spell-495008",
|
||||
@@ -284,7 +356,25 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495008,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 2,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-demon-stronghold-spell-494887",
|
||||
@@ -304,7 +394,25 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494887,
|
||||
"componentId": 505891,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -5000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -364,7 +472,25 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494918,
|
||||
"componentId": 505891,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -5000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -464,7 +590,25 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494727,
|
||||
"componentId": 503019,
|
||||
"spellLevel": 40,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-demon-stronghold-spell-494710",
|
||||
@@ -484,7 +628,25 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494710,
|
||||
"componentId": 502683,
|
||||
"spellLevel": 2100,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-demon-stronghold-spell-495014",
|
||||
@@ -504,7 +666,25 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
3000,
|
||||
3000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495014,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 3,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -564,7 +744,25 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494708,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-demon-stronghold-spell-493195",
|
||||
@@ -584,7 +782,25 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493195,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 8,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -949,6 +1165,24 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494875,
|
||||
"componentId": 505593,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -4000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -970,7 +1204,25 @@ export const DEMON_STRONGHOLD_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494876,
|
||||
"componentId": 505768,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -3059,6 +3311,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -164,7 +164,25 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495464,
|
||||
"componentId": 506443,
|
||||
"spellLevel": 8,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -2000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -264,7 +282,25 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494765,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -444,7 +480,25 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495274,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -504,7 +558,25 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494239,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -604,7 +676,25 @@ export const DUNGEON_OF_DALANIS_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494239,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1567,6 +1657,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -1,4 +1,4 @@
|
||||
/* This file is generated by scripts/runewaker-pipeline/build-forsaken-population.mjs. */
|
||||
/* Generated by scripts/runewaker-pipeline/build-population.mjs. */
|
||||
import type { DungeonBossObjective } from "../dungeonTypes";
|
||||
import type { PopulationDefinitionMap, StaticMobSpawnDefinition } from "../mobPopulation";
|
||||
|
||||
@@ -699,7 +699,25 @@ export const FORSAKEN_ABBEY_ENTITIES = {
|
||||
3000,
|
||||
3000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490471,
|
||||
"componentId": 500913,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-fa-100074-spell-490489",
|
||||
@@ -727,6 +745,24 @@ export const FORSAKEN_ABBEY_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490489,
|
||||
"componentId": 501010,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2679,13 +2715,20 @@ export const FORSAKEN_ABBEY_BOSSES = [
|
||||
export const FORSAKEN_ABBEY_OBJECTIVE_ORDER = FORSAKEN_ABBEY_BOSSES;
|
||||
|
||||
export const FORSAKEN_ABBEY_POPULATION_PROVENANCE = {
|
||||
"schemaVersion": 1,
|
||||
"schemaVersion": 2,
|
||||
"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": {
|
||||
@@ -2699,7 +2742,7 @@ export const FORSAKEN_ABBEY_POPULATION_PROVENANCE = {
|
||||
"Resource/data/string_enus.db": "4fce021ba9ed110a213e08d3228b2de57cec66ddbbc8cdcf5753204874d9ab75"
|
||||
}
|
||||
},
|
||||
"coordinateTransform": "threeLocal=(inverse(WDB placement)*server)*(0.1,0.1,-0.1)",
|
||||
"coordinateTransform": "threeLocal=(inverse(WDB placement)*server)*(meters,meters,-meters)",
|
||||
"navmeshProjection": {
|
||||
"maximumAllowedDistance": 2,
|
||||
"maximumObservedDistance": 2.5965,
|
||||
@@ -2707,5 +2750,8 @@ export const FORSAKEN_ABBEY_POPULATION_PROVENANCE = {
|
||||
"deferredRowsBeyondCombatLimit": 1
|
||||
},
|
||||
"runeWakerIdsAreNamespaced": true,
|
||||
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry."
|
||||
"serverEntryPolicy": "RuneWaker numeric ids are never passed to AzerothCore serverEntry.",
|
||||
"sourceSpellCatalog": "scripts/runewaker-pipeline/snapshots/runewaker-npc-spell-catalog.json",
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -124,7 +124,25 @@ export const GRAFU_CASTLE_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497025,
|
||||
"componentId": 508731,
|
||||
"spellLevel": 12,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-grafu-castle-spell-490537",
|
||||
@@ -144,7 +162,25 @@ export const GRAFU_CASTLE_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490537,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -204,7 +240,25 @@ export const GRAFU_CASTLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.5,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497026,
|
||||
"componentId": 508733,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.5,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.5,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-grafu-castle-spell-497504",
|
||||
@@ -224,7 +278,25 @@ export const GRAFU_CASTLE_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497504,
|
||||
"componentId": 509463,
|
||||
"spellLevel": 12,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -284,7 +356,25 @@ export const GRAFU_CASTLE_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497508,
|
||||
"componentId": 509469,
|
||||
"spellLevel": 65,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-grafu-castle-spell-497038",
|
||||
@@ -304,7 +394,25 @@ export const GRAFU_CASTLE_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497038,
|
||||
"componentId": 500962,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -364,7 +472,25 @@ export const GRAFU_CASTLE_ENTITIES = {
|
||||
3000,
|
||||
3000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497020,
|
||||
"componentId": 508727,
|
||||
"spellLevel": 50,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 100,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-grafu-castle-spell-497021",
|
||||
@@ -384,7 +510,25 @@ export const GRAFU_CASTLE_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497021,
|
||||
"componentId": 508728,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -10,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -484,7 +628,25 @@ export const GRAFU_CASTLE_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497508,
|
||||
"componentId": 509469,
|
||||
"spellLevel": 65,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-grafu-castle-spell-497038",
|
||||
@@ -504,7 +666,25 @@ export const GRAFU_CASTLE_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497038,
|
||||
"componentId": 500962,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1306,6 +1486,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
File diff suppressed because it is too large.
Load diff
@@ -44,7 +44,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493201,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -104,7 +122,25 @@ export const HOTO_ENTITIES = {
|
||||
3000,
|
||||
3000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493196,
|
||||
"componentId": 502444,
|
||||
"spellLevel": 1200,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-spell-493192",
|
||||
@@ -124,7 +160,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493192,
|
||||
"componentId": 502444,
|
||||
"spellLevel": 900,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -224,7 +278,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493198,
|
||||
"componentId": 500240,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -25,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -284,7 +356,25 @@ export const HOTO_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493197,
|
||||
"componentId": 502444,
|
||||
"spellLevel": 1200,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-spell-493192",
|
||||
@@ -304,7 +394,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493192,
|
||||
"componentId": 502444,
|
||||
"spellLevel": 900,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -404,7 +512,25 @@ export const HOTO_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492864,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 8,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-spell-492865",
|
||||
@@ -424,7 +550,25 @@ export const HOTO_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492865,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -484,7 +628,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493201,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-spell-492724",
|
||||
@@ -504,7 +666,25 @@ export const HOTO_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492724,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -564,7 +744,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493195,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 6,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -624,7 +822,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493218,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -684,7 +900,25 @@ export const HOTO_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493111,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-spell-493108",
|
||||
@@ -704,7 +938,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493108,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-spell-493205",
|
||||
@@ -724,7 +976,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493205,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 6,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -784,7 +1054,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493223,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -884,7 +1172,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493217,
|
||||
"componentId": 503083,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-spell-493218",
|
||||
@@ -904,7 +1210,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493218,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1124,6 +1448,24 @@ export const HOTO_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493215,
|
||||
"componentId": 502838,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1145,7 +1487,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493307,
|
||||
"componentId": 503418,
|
||||
"spellLevel": 800,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-102021-aura-493308",
|
||||
@@ -1243,6 +1603,24 @@ export const HOTO_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493237,
|
||||
"componentId": 503618,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1264,7 +1642,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493239,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 14,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1421,6 +1817,24 @@ export const HOTO_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493210,
|
||||
"componentId": 502871,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1567,7 +1981,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493580,
|
||||
"componentId": 503928,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-102040-aura-493595",
|
||||
@@ -1692,7 +2124,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493201,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-spell-493195",
|
||||
@@ -1712,7 +2162,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493195,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1779,7 +2247,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493231,
|
||||
"componentId": 503925,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-102041-spell-493232",
|
||||
@@ -1800,7 +2286,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493232,
|
||||
"componentId": 503002,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-102041-aura-493233",
|
||||
@@ -1944,7 +2448,25 @@ export const HOTO_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493189,
|
||||
"componentId": 503409,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -3000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-102063-spell-493221",
|
||||
@@ -1972,6 +2494,24 @@ export const HOTO_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493221,
|
||||
"componentId": 503410,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2057,7 +2597,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493195,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-spell-493205",
|
||||
@@ -2077,7 +2635,25 @@ export const HOTO_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493205,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2137,7 +2713,25 @@ export const HOTO_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493226,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 12,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-hoto-spell-493227",
|
||||
@@ -2157,7 +2751,25 @@ export const HOTO_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493227,
|
||||
"componentId": 502443,
|
||||
"spellLevel": 1200,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -5646,6 +6258,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
File diff suppressed because it is too large.
Load diff
@@ -44,7 +44,25 @@ export const KAL_TUROK_HIVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 491020,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kal-turok-hive-spell-490517",
|
||||
@@ -64,7 +82,25 @@ export const KAL_TUROK_HIVE_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490517,
|
||||
"componentId": 501023,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kal-turok-hive-spell-490634",
|
||||
@@ -84,7 +120,25 @@ export const KAL_TUROK_HIVE_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490634,
|
||||
"componentId": 501091,
|
||||
"spellLevel": 210,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kal-turok-hive-spell-490489",
|
||||
@@ -104,7 +158,25 @@ export const KAL_TUROK_HIVE_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490489,
|
||||
"componentId": 501010,
|
||||
"spellLevel": 200,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -181,6 +253,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -124,7 +124,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 491027,
|
||||
"componentId": 501451,
|
||||
"spellLevel": 400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -184,7 +202,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 491027,
|
||||
"componentId": 501451,
|
||||
"spellLevel": 350,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -244,7 +280,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 491027,
|
||||
"componentId": 501451,
|
||||
"spellLevel": 400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -304,7 +358,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490581,
|
||||
"componentId": 501045,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kalins-shrine-spell-490584",
|
||||
@@ -324,7 +396,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490584,
|
||||
"componentId": 501048,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -384,7 +474,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490553,
|
||||
"componentId": 501029,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kalins-shrine-spell-490551",
|
||||
@@ -404,7 +512,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490551,
|
||||
"componentId": 501027,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -464,7 +590,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
8000,
|
||||
8000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492371,
|
||||
"componentId": 500919,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kalins-shrine-spell-492373",
|
||||
@@ -484,7 +628,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492373,
|
||||
"componentId": 500919,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -544,7 +706,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
8000,
|
||||
8000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492371,
|
||||
"componentId": 500919,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kalins-shrine-spell-492372",
|
||||
@@ -564,7 +744,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492372,
|
||||
"componentId": 500919,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -639,6 +837,24 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492371,
|
||||
"componentId": 500919,
|
||||
"spellLevel": 8,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -660,7 +876,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492372,
|
||||
"componentId": 500919,
|
||||
"spellLevel": 25,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kalins-shrine-101507-spell-492373",
|
||||
@@ -681,7 +915,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492373,
|
||||
"componentId": 500919,
|
||||
"spellLevel": 8,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kalins-shrine-101507-aura-492374",
|
||||
@@ -775,7 +1027,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492375,
|
||||
"componentId": 502444,
|
||||
"spellLevel": 300,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kalins-shrine-101502-debuff-492376",
|
||||
@@ -1130,7 +1400,25 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kalins-shrine-101500-spell-492367",
|
||||
@@ -1158,6 +1446,24 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492367,
|
||||
"componentId": 502045,
|
||||
"spellLevel": 14,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1284,6 +1590,24 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 10
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492379,
|
||||
"componentId": 503044,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1344,6 +1668,24 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492381,
|
||||
"componentId": 501107,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1372,6 +1714,24 @@ export const KALINS_SHRINE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492382,
|
||||
"componentId": 503044,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -3324,6 +3684,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -44,7 +44,25 @@ export const KAWAKS_TOMB_ENTITIES = {
|
||||
6000,
|
||||
6000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490478,
|
||||
"componentId": 501008,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -104,7 +122,25 @@ export const KAWAKS_TOMB_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495472,
|
||||
"componentId": 506644,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -164,7 +200,25 @@ export const KAWAKS_TOMB_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496297,
|
||||
"componentId": 507788,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -401,6 +455,24 @@ export const KAWAKS_TOMB_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496027,
|
||||
"componentId": 507420,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -50,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -429,6 +501,24 @@ export const KAWAKS_TOMB_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496028,
|
||||
"componentId": 507422,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -18000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -450,7 +540,25 @@ export const KAWAKS_TOMB_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496030,
|
||||
"componentId": 507425,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -5000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kawaks-tomb-104228-spell-496031",
|
||||
@@ -478,6 +586,24 @@ export const KAWAKS_TOMB_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496031,
|
||||
"componentId": 507427,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -10,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -762,7 +888,25 @@ export const KAWAKS_TOMB_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496302,
|
||||
"componentId": 507792,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -822,7 +966,25 @@ export const KAWAKS_TOMB_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496297,
|
||||
"componentId": 507788,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -922,7 +1084,25 @@ export const KAWAKS_TOMB_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496296,
|
||||
"componentId": 507788,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kawaks-tomb-spell-496048",
|
||||
@@ -942,7 +1122,25 @@ export const KAWAKS_TOMB_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496048,
|
||||
"componentId": 507434,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2465,6 +2663,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -44,7 +44,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498441,
|
||||
"componentId": 620906,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -104,7 +122,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498441,
|
||||
"componentId": 620906,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -164,7 +200,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -241,6 +295,24 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 7.5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496328,
|
||||
"componentId": 506500,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -262,7 +334,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496404,
|
||||
"componentId": 506501,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -750,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kulech-bones-nest-107306-spell-496405",
|
||||
@@ -290,6 +380,24 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496405,
|
||||
"componentId": 506503,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -2,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -361,7 +469,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496409,
|
||||
"componentId": 507955,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -421,7 +547,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 499434,
|
||||
"componentId": 622862,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kulech-bones-nest-spell-499435",
|
||||
@@ -441,7 +585,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 499435,
|
||||
"componentId": 622881,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -501,7 +663,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
8000,
|
||||
8000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 499420,
|
||||
"componentId": 622862,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -561,7 +741,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 499437,
|
||||
"componentId": 621378,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kulech-bones-nest-spell-499438",
|
||||
@@ -581,7 +779,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 499438,
|
||||
"componentId": 622862,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -641,7 +857,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498308,
|
||||
"componentId": 620886,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kulech-bones-nest-spell-499440",
|
||||
@@ -661,7 +895,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 499440,
|
||||
"componentId": 621378,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -761,7 +1013,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 499459,
|
||||
"componentId": 622862,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-kulech-bones-nest-spell-499461",
|
||||
@@ -781,7 +1051,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 499461,
|
||||
"componentId": 622931,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -841,7 +1129,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
13000,
|
||||
13000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498307,
|
||||
"componentId": 622862,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -901,7 +1207,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
6000,
|
||||
6000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 499464,
|
||||
"componentId": 622862,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -961,7 +1285,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
13000,
|
||||
13000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498307,
|
||||
"componentId": 622862,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1021,7 +1363,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
13000,
|
||||
13000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498307,
|
||||
"componentId": 622862,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1081,7 +1441,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
6000,
|
||||
6000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 499445,
|
||||
"componentId": 622862,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1141,7 +1519,25 @@ export const KULECH_BONES_NEST_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 499441,
|
||||
"componentId": 621378,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2911,6 +3307,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -44,7 +44,25 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494440,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-lair-demon-dragon-spell-494441",
|
||||
@@ -64,7 +82,25 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494441,
|
||||
"componentId": 504966,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -2000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -124,7 +160,25 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494445,
|
||||
"componentId": 504976,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -2000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -224,7 +278,25 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
|
||||
2500,
|
||||
2500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494452,
|
||||
"componentId": 502683,
|
||||
"spellLevel": 1600,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -284,7 +356,25 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494435,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 2,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -344,7 +434,25 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494447,
|
||||
"componentId": 502439,
|
||||
"spellLevel": 1400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -524,7 +632,25 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494480,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -584,7 +710,25 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
|
||||
6000,
|
||||
6000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490478,
|
||||
"componentId": 501008,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -684,7 +828,25 @@ export const LAIR_DEMON_DRAGON_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494433,
|
||||
"componentId": 502683,
|
||||
"spellLevel": 1400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1923,6 +2085,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -55,7 +55,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490476,
|
||||
"componentId": 500917,
|
||||
"spellLevel": 200,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-mystic-altar-100611-debuff-490480",
|
||||
@@ -108,6 +126,24 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490489,
|
||||
"componentId": 501010,
|
||||
"spellLevel": 400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -218,6 +254,24 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 491104,
|
||||
"componentId": 501479,
|
||||
"spellLevel": 350,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -278,7 +332,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490517,
|
||||
"componentId": 501023,
|
||||
"spellLevel": 8,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -338,7 +410,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490476,
|
||||
"componentId": 500917,
|
||||
"spellLevel": 250,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -398,7 +488,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490582,
|
||||
"componentId": 501046,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-mystic-altar-spell-490586",
|
||||
@@ -418,7 +526,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490586,
|
||||
"componentId": 501050,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -497,6 +623,24 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490489,
|
||||
"componentId": 501010,
|
||||
"spellLevel": 300,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -525,6 +669,24 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490633,
|
||||
"componentId": 501090,
|
||||
"spellLevel": 300,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -578,7 +740,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490637,
|
||||
"componentId": 501093,
|
||||
"spellLevel": 120,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-mystic-altar-100154-aura-490652",
|
||||
@@ -745,7 +925,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 80,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -805,7 +1003,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490520,
|
||||
"componentId": 500962,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-mystic-altar-spell-490521",
|
||||
@@ -825,7 +1041,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490521,
|
||||
"componentId": 500963,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-mystic-altar-spell-490477",
|
||||
@@ -845,7 +1079,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -905,7 +1157,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490521,
|
||||
"componentId": 500963,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-mystic-altar-spell-491020",
|
||||
@@ -925,7 +1195,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 491020,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 15,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-mystic-altar-spell-490522",
|
||||
@@ -945,7 +1233,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.7,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490522,
|
||||
"componentId": 500964,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.7,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.7,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1005,7 +1311,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 491020,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1065,7 +1389,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 491020,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 30,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-mystic-altar-spell-490525",
|
||||
@@ -1085,7 +1427,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490525,
|
||||
"componentId": 500967,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-mystic-altar-spell-490520",
|
||||
@@ -1105,7 +1465,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490520,
|
||||
"componentId": 500962,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1217,7 +1595,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 70,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-mystic-altar-100138-spell-490478",
|
||||
@@ -1238,7 +1634,25 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
6000,
|
||||
6000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490478,
|
||||
"componentId": 501008,
|
||||
"spellLevel": 70,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-mystic-altar-100138-spell-490520",
|
||||
@@ -1266,6 +1680,24 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490520,
|
||||
"componentId": 500962,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1294,6 +1726,24 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 7.5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490522,
|
||||
"componentId": 500964,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.7,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.7,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1347,6 +1797,24 @@ export const MYSTIC_ALTAR_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 8
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490525,
|
||||
"componentId": 500967,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -4259,6 +4727,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -61,6 +61,24 @@ export const PASPERS_SHRINE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 491903,
|
||||
"componentId": 502081,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -15,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -114,6 +132,24 @@ export const PASPERS_SHRINE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 491988,
|
||||
"componentId": 502203,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -900,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -337,6 +373,24 @@ export const PASPERS_SHRINE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492232,
|
||||
"componentId": 502431,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -493,7 +547,25 @@ export const PASPERS_SHRINE_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490529,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -553,7 +625,25 @@ export const PASPERS_SHRINE_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492027,
|
||||
"componentId": 502234,
|
||||
"spellLevel": 700,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -613,7 +703,25 @@ export const PASPERS_SHRINE_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492027,
|
||||
"componentId": 502234,
|
||||
"spellLevel": 700,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -689,6 +797,24 @@ export const PASPERS_SHRINE_ENTITIES = {
|
||||
"warningMs": 1800,
|
||||
"length": 6,
|
||||
"angleDegrees": 95
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490521,
|
||||
"componentId": 500963,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -885,6 +1011,24 @@ export const PASPERS_SHRINE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 491903,
|
||||
"componentId": 502081,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -15,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -995,6 +1139,24 @@ export const PASPERS_SHRINE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492232,
|
||||
"componentId": 502431,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1247,7 +1409,25 @@ export const PASPERS_SHRINE_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490530,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1347,7 +1527,25 @@ export const PASPERS_SHRINE_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492027,
|
||||
"componentId": 502234,
|
||||
"spellLevel": 700,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1407,7 +1605,25 @@ export const PASPERS_SHRINE_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490594,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 33,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1534,6 +1750,24 @@ export const PASPERS_SHRINE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492232,
|
||||
"componentId": 502431,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -4206,6 +4440,6 @@ export const PASPERS_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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -244,7 +244,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496048,
|
||||
"componentId": 507434,
|
||||
"spellLevel": 30,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -304,7 +322,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496049,
|
||||
"componentId": 507435,
|
||||
"spellLevel": 7,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -404,7 +440,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
2500,
|
||||
2500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496052,
|
||||
"componentId": 507438,
|
||||
"spellLevel": 70,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -464,7 +518,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
2500,
|
||||
2500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496053,
|
||||
"componentId": 507439,
|
||||
"spellLevel": 30,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": -20,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-raksha-temple-spell-496054",
|
||||
@@ -484,7 +556,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496054,
|
||||
"componentId": 507440,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -624,7 +714,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496048,
|
||||
"componentId": 507434,
|
||||
"spellLevel": 30,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -684,7 +792,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496049,
|
||||
"componentId": 507435,
|
||||
"spellLevel": 7,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -784,7 +910,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
2500,
|
||||
2500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496052,
|
||||
"componentId": 507438,
|
||||
"spellLevel": 70,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -844,7 +988,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
2500,
|
||||
2500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496053,
|
||||
"componentId": 507439,
|
||||
"spellLevel": 30,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": -20,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-raksha-temple-spell-496054",
|
||||
@@ -864,7 +1026,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496054,
|
||||
"componentId": 507440,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1004,7 +1184,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
13000,
|
||||
13000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492872,
|
||||
"componentId": 500963,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1144,7 +1342,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
2500,
|
||||
2500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496053,
|
||||
"componentId": 507439,
|
||||
"spellLevel": 30,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": -20,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-raksha-temple-spell-496054",
|
||||
@@ -1164,7 +1380,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496054,
|
||||
"componentId": 507440,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1264,7 +1498,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496049,
|
||||
"componentId": 507435,
|
||||
"spellLevel": 7,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1364,7 +1616,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
2500,
|
||||
2500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496052,
|
||||
"componentId": 507438,
|
||||
"spellLevel": 70,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1424,7 +1694,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
2500,
|
||||
2500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496053,
|
||||
"componentId": 507439,
|
||||
"spellLevel": 30,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": -20,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-raksha-temple-spell-496054",
|
||||
@@ -1444,7 +1732,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496054,
|
||||
"componentId": 507440,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1584,7 +1890,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
2500,
|
||||
2500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496058,
|
||||
"componentId": 507443,
|
||||
"spellLevel": 30,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1644,7 +1968,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
2500,
|
||||
2500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496057,
|
||||
"componentId": 507443,
|
||||
"spellLevel": 100,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1784,7 +2126,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
13000,
|
||||
13000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496062,
|
||||
"componentId": 507432,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1884,7 +2244,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
13000,
|
||||
13000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.2,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496062,
|
||||
"componentId": 507432,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -2039,6 +2417,24 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 7.5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495545,
|
||||
"componentId": 506802,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2067,6 +2463,24 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495591,
|
||||
"componentId": 506803,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2095,6 +2509,24 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 495996,
|
||||
"componentId": 507367,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -15,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2116,7 +2548,25 @@ export const RAKSHA_TEMPLE_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 496112,
|
||||
"componentId": 507524,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -3,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 3,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -3545,6 +3995,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -1131,10 +1131,10 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
|
||||
"report": "src/assets/game/dungeons/treasure-trove/instance-validation-report.json",
|
||||
"blockers": [],
|
||||
"warnings": [
|
||||
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
|
||||
"All valid static WDB ROS placements are assembled; filename-only dynamic candidates remain static until script evidence maps their behavior.",
|
||||
"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.",
|
||||
"The full WDB static assembly replaces the former source-anchor support pads; source-backed switch and drawbridge behavior is represented by encounter world objects pending original object-mesh animation mapping.",
|
||||
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
|
||||
]
|
||||
}
|
||||
@@ -2080,7 +2080,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
|
||||
"fog": {
|
||||
"color": "#0b0b10",
|
||||
"near": 34,
|
||||
"far": 684
|
||||
"far": 1195
|
||||
},
|
||||
"ambientLight": {
|
||||
"color": "#9d96a7",
|
||||
@@ -2109,8 +2109,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
|
||||
"additiveNames": []
|
||||
},
|
||||
"map": {
|
||||
"width": 494,
|
||||
"height": 634,
|
||||
"width": 1013,
|
||||
"height": 974,
|
||||
"padding": 24,
|
||||
"contours": []
|
||||
}
|
||||
@@ -2120,26 +2120,124 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
|
||||
{
|
||||
"id": "visual-000",
|
||||
"fileName": "the-origin-visual.glb",
|
||||
"checksum": "a6db29db31f88a45bd082d27130725d70b2fa1db82030530750492a54b3f9bf8",
|
||||
"size": 251132,
|
||||
"triangleCount": 2664
|
||||
"checksum": "d1ded0316b2cbaa07458c56864c47c01afaa85e4f33dd13f5d95984d7305fb92",
|
||||
"size": 28683180,
|
||||
"triangleCount": 1176881
|
||||
}
|
||||
],
|
||||
"collision": [
|
||||
{
|
||||
"id": "collision-000",
|
||||
"fileName": "the-origin-collision.glb",
|
||||
"checksum": "7cc2e9dfc649742d546a7771fb836cde3e5f708f2a7088bfc111457eb516c17a",
|
||||
"size": 72976,
|
||||
"triangleCount": 2664
|
||||
"fileName": "the-origin-wdb-collision-000.glb",
|
||||
"checksum": "7eb6e18cf11ac9a15b24c6300f8c62773df9d2cb045b16c9c4732e18016b18e7",
|
||||
"size": 2703604,
|
||||
"triangleCount": 78675
|
||||
},
|
||||
{
|
||||
"id": "collision-001",
|
||||
"fileName": "the-origin-wdb-collision-001.glb",
|
||||
"checksum": "31ec6603c7c100af9c0177cbbae3f13f029266a13c5b9690731089d8193deeb2",
|
||||
"size": 2757368,
|
||||
"triangleCount": 83558
|
||||
},
|
||||
{
|
||||
"id": "collision-002",
|
||||
"fileName": "the-origin-wdb-collision-002.glb",
|
||||
"checksum": "ccce739574edbeef58ed1bc502d9be77cb0565213c381113bcdb9944323b0be0",
|
||||
"size": 2538872,
|
||||
"triangleCount": 84659
|
||||
},
|
||||
{
|
||||
"id": "collision-003",
|
||||
"fileName": "the-origin-wdb-collision-003.glb",
|
||||
"checksum": "7f83d131a71bee2fb3f47e7c2fe94c39cb3f3f9bd7f333a994627528465e07d1",
|
||||
"size": 2438228,
|
||||
"triangleCount": 61945
|
||||
},
|
||||
{
|
||||
"id": "collision-004",
|
||||
"fileName": "the-origin-wdb-collision-004.glb",
|
||||
"checksum": "044bc594aa521c7b4a441b477c369b03083189068dbe6b17572851beb23531b1",
|
||||
"size": 3127712,
|
||||
"triangleCount": 87835
|
||||
},
|
||||
{
|
||||
"id": "collision-005",
|
||||
"fileName": "the-origin-wdb-collision-005.glb",
|
||||
"checksum": "8cd5228f6443dceb983e5e8c457ed97fdfdac2083049dce036157f3573a0f9ff",
|
||||
"size": 2827072,
|
||||
"triangleCount": 88513
|
||||
},
|
||||
{
|
||||
"id": "collision-006",
|
||||
"fileName": "the-origin-wdb-collision-006.glb",
|
||||
"checksum": "abca4c48c25d495e40399dfd5a9fbcab8941a056af1b8159ff0b4d3f453b0b52",
|
||||
"size": 3210756,
|
||||
"triangleCount": 86404
|
||||
},
|
||||
{
|
||||
"id": "collision-007",
|
||||
"fileName": "the-origin-wdb-collision-007.glb",
|
||||
"checksum": "9693440956d31489d888e30fd97de0427e9bf65bd02174a6bbad34b78a8b6a29",
|
||||
"size": 2364920,
|
||||
"triangleCount": 66883
|
||||
},
|
||||
{
|
||||
"id": "collision-008",
|
||||
"fileName": "the-origin-wdb-collision-008.glb",
|
||||
"checksum": "89f95739e8ac2384d0c125c1ab013e1803031f67f11d35fa94355719a4db2251",
|
||||
"size": 2811712,
|
||||
"triangleCount": 87544
|
||||
},
|
||||
{
|
||||
"id": "collision-009",
|
||||
"fileName": "the-origin-wdb-collision-009.glb",
|
||||
"checksum": "2bbddf84d5754100c3499d5e35cb1da45dfa920940c59a3007c0848b975d609d",
|
||||
"size": 3077192,
|
||||
"triangleCount": 84937
|
||||
},
|
||||
{
|
||||
"id": "collision-010",
|
||||
"fileName": "the-origin-wdb-collision-010.glb",
|
||||
"checksum": "73012e2dcb9892ad3aeccd3536e0c36f4365276226b265f82326a25e45d4ec4e",
|
||||
"size": 2989496,
|
||||
"triangleCount": 88994
|
||||
},
|
||||
{
|
||||
"id": "collision-011",
|
||||
"fileName": "the-origin-wdb-collision-011.glb",
|
||||
"checksum": "d19124247096c475e344ae467274be6a0320192cf2292f891970b102eb4fc1be",
|
||||
"size": 3128952,
|
||||
"triangleCount": 89430
|
||||
},
|
||||
{
|
||||
"id": "collision-012",
|
||||
"fileName": "the-origin-wdb-collision-012.glb",
|
||||
"checksum": "23870416ad12c179e576959df6f72370e209365e03d3510ceb3b1745f30fe930",
|
||||
"size": 2825584,
|
||||
"triangleCount": 89857
|
||||
},
|
||||
{
|
||||
"id": "collision-013",
|
||||
"fileName": "the-origin-wdb-collision-013.glb",
|
||||
"checksum": "5c9bb9f4b5d118c834f3b1d0b515b30d553a1e21423767c9e7dafcbd6d78d1c2",
|
||||
"size": 2856020,
|
||||
"triangleCount": 85511
|
||||
},
|
||||
{
|
||||
"id": "collision-014",
|
||||
"fileName": "the-origin-wdb-collision-014.glb",
|
||||
"checksum": "0743835882534516c770766cbbf3585386e05e96e95a2bdc24e3ca5d90482c57",
|
||||
"size": 533188,
|
||||
"triangleCount": 12136
|
||||
}
|
||||
],
|
||||
"navigation": {
|
||||
"id": "navigation",
|
||||
"fileName": "the-origin-navigation.glb",
|
||||
"checksum": "a9b97bc8cc62279486292d42aad3ae0a8e70638e7dc223dd0c392f32b3811781",
|
||||
"size": 54768,
|
||||
"triangleCount": 1213
|
||||
"checksum": "70c0a78073d7c3cd7a95e0dc3531c25c0de9815376001f9b80cfbb59a72596cd",
|
||||
"size": 4667144,
|
||||
"triangleCount": 97214
|
||||
}
|
||||
},
|
||||
"transform": {
|
||||
@@ -2158,22 +2256,22 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
|
||||
},
|
||||
"bounds": {
|
||||
"min": [
|
||||
-386.52997,
|
||||
-46.51506,
|
||||
-324.07977
|
||||
-630.039165,
|
||||
-94.763691,
|
||||
-431.593932
|
||||
],
|
||||
"max": [
|
||||
107.21951,
|
||||
11.46503,
|
||||
309.25803
|
||||
382.64336,
|
||||
260.724523,
|
||||
541.440422
|
||||
]
|
||||
},
|
||||
"entrance": {
|
||||
"name": "The Origin Entrance",
|
||||
"footPosition": [
|
||||
-380.368111,
|
||||
-7.734726,
|
||||
-292.973182
|
||||
-377.445404,
|
||||
-13.802109,
|
||||
-267.742004
|
||||
],
|
||||
"forward": [
|
||||
-0.292372,
|
||||
@@ -2187,9 +2285,9 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
|
||||
"id": "entrance",
|
||||
"name": "The Origin Entrance",
|
||||
"center": [
|
||||
-380.368111,
|
||||
-7.734726,
|
||||
-292.973182
|
||||
-377.445404,
|
||||
-13.802109,
|
||||
-267.742004
|
||||
],
|
||||
"radius": 24
|
||||
}
|
||||
@@ -2217,10 +2315,10 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
|
||||
"report": "src/assets/game/dungeons/the-origin/instance-validation-report.json",
|
||||
"blockers": [],
|
||||
"warnings": [
|
||||
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
|
||||
"All valid static WDB ROS placements are assembled; filename-only dynamic candidates remain static until script evidence maps their behavior.",
|
||||
"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.",
|
||||
"The full static WDB collision requires a tiled monotone Recast build because the solo watershed contour triangulator rejects this source geometry.",
|
||||
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
|
||||
]
|
||||
}
|
||||
@@ -4155,7 +4253,7 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
|
||||
"fog": {
|
||||
"color": "#0b0b10",
|
||||
"near": 34,
|
||||
"far": 635
|
||||
"far": 1461
|
||||
},
|
||||
"ambientLight": {
|
||||
"color": "#9d96a7",
|
||||
@@ -4184,8 +4282,8 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
|
||||
"additiveNames": []
|
||||
},
|
||||
"map": {
|
||||
"width": 377,
|
||||
"height": 644,
|
||||
"width": 1162,
|
||||
"height": 1266,
|
||||
"padding": 24,
|
||||
"contours": []
|
||||
}
|
||||
@@ -4195,26 +4293,68 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
|
||||
{
|
||||
"id": "visual-000",
|
||||
"fileName": "raksha-temple-visual.glb",
|
||||
"checksum": "3439e64f2a49d0545651bac35276867af83c3eb57b3c859f767de0c49c3dce66",
|
||||
"size": 937772,
|
||||
"triangleCount": 4302
|
||||
"checksum": "3a61098e772a0c656aa25d88aee357f78b84c738e45150fca6fd5059c2b675f7",
|
||||
"size": 23554528,
|
||||
"triangleCount": 605820
|
||||
}
|
||||
],
|
||||
"collision": [
|
||||
{
|
||||
"id": "collision-000",
|
||||
"fileName": "raksha-temple-collision.glb",
|
||||
"checksum": "a0b19ff9c1c5ef6abfbaee6080bec91bdcdc8eb3d2b8f6b3e3fad092fd71e548",
|
||||
"size": 161552,
|
||||
"triangleCount": 4302
|
||||
"fileName": "raksha-temple-wdb-collision-000.glb",
|
||||
"checksum": "22cfdc239b638f906f3cb77c9fb7b94c66eb483cfcbbe59e059bf8d5cbaad8a0",
|
||||
"size": 2584136,
|
||||
"triangleCount": 87109
|
||||
},
|
||||
{
|
||||
"id": "collision-001",
|
||||
"fileName": "raksha-temple-wdb-collision-001.glb",
|
||||
"checksum": "3a3f4448aa92276faa94c8b99ae49aa48660423fb42edf0f2403615e5e3b3453",
|
||||
"size": 2785392,
|
||||
"triangleCount": 86228
|
||||
},
|
||||
{
|
||||
"id": "collision-002",
|
||||
"fileName": "raksha-temple-wdb-collision-002.glb",
|
||||
"checksum": "b4b9e92646bbaf290263b601e7af7d6547f85a7b3fdae4b931ab930a263c2a36",
|
||||
"size": 2788332,
|
||||
"triangleCount": 89719
|
||||
},
|
||||
{
|
||||
"id": "collision-003",
|
||||
"fileName": "raksha-temple-wdb-collision-003.glb",
|
||||
"checksum": "846ac1301549d8e05c8efb59b9ddfc0ebec2fb36cf216a0d5c143ab3eb5ae423",
|
||||
"size": 2676988,
|
||||
"triangleCount": 89776
|
||||
},
|
||||
{
|
||||
"id": "collision-004",
|
||||
"fileName": "raksha-temple-wdb-collision-004.glb",
|
||||
"checksum": "100f27d9a2c46f1d30b0e395d60d99a3b62dfabc82474ed574f137c1513fb1bb",
|
||||
"size": 2328332,
|
||||
"triangleCount": 85897
|
||||
},
|
||||
{
|
||||
"id": "collision-005",
|
||||
"fileName": "raksha-temple-wdb-collision-005.glb",
|
||||
"checksum": "cb22cb168e559fa8ed6983109bdc02bc4fd11f7d8834649bfab1d9cd0448ba87",
|
||||
"size": 2960700,
|
||||
"triangleCount": 89896
|
||||
},
|
||||
{
|
||||
"id": "collision-006",
|
||||
"fileName": "raksha-temple-wdb-collision-006.glb",
|
||||
"checksum": "d8f6bd33ada7b6e0ee1b7cb4aacdf1c32e8ceb614f45d60ac2f24c9f1e0cef18",
|
||||
"size": 2345864,
|
||||
"triangleCount": 77195
|
||||
}
|
||||
],
|
||||
"navigation": {
|
||||
"id": "navigation",
|
||||
"fileName": "raksha-temple-navigation.glb",
|
||||
"checksum": "21e0aa6d58f8958fc6a21912cf72ec86188c13e87d70695504cee9e910b754b0",
|
||||
"size": 60088,
|
||||
"triangleCount": 1287
|
||||
"checksum": "1e1d1ad26907dc8c4fd0a917dba40b52d2aaaea4a0335af06871ee28230eddf1",
|
||||
"size": 8686564,
|
||||
"triangleCount": 180952
|
||||
}
|
||||
},
|
||||
"transform": {
|
||||
@@ -4233,22 +4373,22 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
|
||||
},
|
||||
"bounds": {
|
||||
"min": [
|
||||
-327.82031,
|
||||
-15.38933,
|
||||
-321.33469
|
||||
-764.460151,
|
||||
-118.862537,
|
||||
-658.700334
|
||||
],
|
||||
"max": [
|
||||
49.02461,
|
||||
51.30486,
|
||||
322.06534
|
||||
397.479134,
|
||||
368.792163,
|
||||
606.421358
|
||||
]
|
||||
},
|
||||
"entrance": {
|
||||
"name": "Raksha Temple Entrance",
|
||||
"footPosition": [
|
||||
-0.020312,
|
||||
21.013313,
|
||||
11.334656
|
||||
0.021786,
|
||||
10.664467,
|
||||
17.560761
|
||||
],
|
||||
"forward": [
|
||||
-0.173648,
|
||||
@@ -4262,9 +4402,9 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
|
||||
"id": "entrance",
|
||||
"name": "Raksha Temple Entrance",
|
||||
"center": [
|
||||
-0.020312,
|
||||
21.013313,
|
||||
11.334656
|
||||
0.021786,
|
||||
10.664467,
|
||||
17.560761
|
||||
],
|
||||
"radius": 24
|
||||
}
|
||||
@@ -4292,10 +4432,10 @@ export const RUNEWAKER_DUNGEON_DEFINITIONS = [
|
||||
"report": "src/assets/game/dungeons/raksha-temple/instance-validation-report.json",
|
||||
"blockers": [],
|
||||
"warnings": [
|
||||
"Secondary WDB doodads/effects remain in the audit; this pass exports the primary ROS hierarchy.",
|
||||
"All valid static WDB ROS placements are assembled; filename-only dynamic candidates remain static until script evidence maps their behavior.",
|
||||
"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.",
|
||||
"The full WDB static assembly replaces the former source-anchor support pads; entrance and combat anchors still require live collision/navigation review.",
|
||||
"Boss combat kits are compiled from RuneWaker spell/script evidence; complex doors, patrol scripts, and loot remain review-required."
|
||||
]
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -59,6 +59,24 @@ export const SAND_VORTEX_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 3
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494642,
|
||||
"componentId": 502512,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -87,6 +105,24 @@ export const SAND_VORTEX_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 8
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494643,
|
||||
"componentId": 502514,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -5000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -231,6 +267,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -164,7 +164,25 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
3000,
|
||||
3000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497667,
|
||||
"componentId": 509726,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-sardo-castle-spell-497668",
|
||||
@@ -184,7 +202,25 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497668,
|
||||
"componentId": 509727,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -244,7 +280,25 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497620,
|
||||
"componentId": 509674,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-sardo-castle-spell-497619",
|
||||
@@ -264,7 +318,25 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
14000,
|
||||
14000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497619,
|
||||
"componentId": 509673,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -324,7 +396,25 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497622,
|
||||
"componentId": 509718,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-sardo-castle-spell-497621",
|
||||
@@ -344,7 +434,25 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
13000,
|
||||
13000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497621,
|
||||
"componentId": 509716,
|
||||
"spellLevel": 70,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -404,7 +512,25 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497622,
|
||||
"componentId": 509718,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-sardo-castle-spell-497621",
|
||||
@@ -424,7 +550,25 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
13000,
|
||||
13000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497621,
|
||||
"componentId": 509716,
|
||||
"spellLevel": 70,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -484,7 +628,25 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497672,
|
||||
"componentId": 509733,
|
||||
"spellLevel": 35,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -5000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -584,7 +746,25 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497673,
|
||||
"componentId": 509734,
|
||||
"spellLevel": 35,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -5000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -738,6 +918,24 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497652,
|
||||
"componentId": 509747,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -766,6 +964,24 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497653,
|
||||
"componentId": 509748,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -10,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -851,7 +1067,25 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497620,
|
||||
"componentId": 509674,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-sardo-castle-spell-497619",
|
||||
@@ -871,7 +1105,25 @@ export const SARDO_CASTLE_ENTITIES = {
|
||||
14000,
|
||||
14000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497619,
|
||||
"componentId": 509673,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1826,6 +2078,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -44,7 +44,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493446,
|
||||
"componentId": 503354,
|
||||
"spellLevel": 80,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-493447",
|
||||
@@ -64,7 +82,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.6,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493447,
|
||||
"componentId": 503679,
|
||||
"spellLevel": 6,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.6,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.6,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -124,7 +160,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492756,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-492755",
|
||||
@@ -144,7 +198,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492755,
|
||||
"componentId": 503019,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -244,7 +316,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492755,
|
||||
"componentId": 503019,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-492756",
|
||||
@@ -264,7 +354,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492756,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -324,7 +432,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493504,
|
||||
"componentId": 502443,
|
||||
"spellLevel": 1200,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-493488",
|
||||
@@ -344,7 +470,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493488,
|
||||
"componentId": 502443,
|
||||
"spellLevel": 1200,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -404,7 +548,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.6,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493447,
|
||||
"componentId": 503679,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.6,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.6,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -464,7 +626,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493446,
|
||||
"componentId": 503354,
|
||||
"spellLevel": 40,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -524,7 +704,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490475,
|
||||
"componentId": 500916,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-490471",
|
||||
@@ -544,7 +742,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
3000,
|
||||
3000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490471,
|
||||
"componentId": 500913,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -644,7 +860,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492756,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-493446",
|
||||
@@ -664,7 +898,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493446,
|
||||
"componentId": 503354,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -724,7 +976,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493488,
|
||||
"componentId": 502443,
|
||||
"spellLevel": 1200,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -784,7 +1054,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493488,
|
||||
"componentId": 502443,
|
||||
"spellLevel": 1400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-493504",
|
||||
@@ -804,7 +1092,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493504,
|
||||
"componentId": 502443,
|
||||
"spellLevel": 1400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -864,7 +1170,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493446,
|
||||
"componentId": 503354,
|
||||
"spellLevel": 15,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-492756",
|
||||
@@ -884,7 +1208,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492756,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 15,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1024,7 +1366,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493463,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 50,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-490524",
|
||||
@@ -1044,7 +1404,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490524,
|
||||
"componentId": 500966,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1104,7 +1482,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490582,
|
||||
"componentId": 501046,
|
||||
"spellLevel": 15,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-490586",
|
||||
@@ -1124,7 +1520,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490586,
|
||||
"componentId": 501050,
|
||||
"spellLevel": 15,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1184,7 +1598,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490556,
|
||||
"componentId": 501032,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-490557",
|
||||
@@ -1204,7 +1636,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490557,
|
||||
"componentId": 501033,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-490637",
|
||||
@@ -1224,7 +1674,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490637,
|
||||
"componentId": 501093,
|
||||
"spellLevel": 40,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1324,7 +1792,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
3000,
|
||||
3000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490629,
|
||||
"componentId": 501086,
|
||||
"spellLevel": 1400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-493462",
|
||||
@@ -1344,7 +1830,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493462,
|
||||
"componentId": 501089,
|
||||
"spellLevel": 1300,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-spell-490630",
|
||||
@@ -1364,7 +1868,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490630,
|
||||
"componentId": 501087,
|
||||
"spellLevel": 1200,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1551,7 +2073,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493663,
|
||||
"componentId": 503912,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-102349-spell-493664",
|
||||
@@ -1572,7 +2112,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493664,
|
||||
"componentId": 503913,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -6500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-102349-aura-493665",
|
||||
@@ -1707,7 +2265,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493590,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-102307-aura-493592",
|
||||
@@ -1753,7 +2329,25 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
6500,
|
||||
6500
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493649,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-the-origin-102307-aura-493650",
|
||||
@@ -1831,6 +2425,24 @@ export const THE_ORIGIN_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493652,
|
||||
"componentId": 504022,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -8000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -3984,6 +4596,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -44,7 +44,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498254,
|
||||
"componentId": 508181,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-497622",
|
||||
@@ -64,7 +82,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497622,
|
||||
"componentId": 509718,
|
||||
"spellLevel": 80,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-497397",
|
||||
@@ -84,7 +120,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497397,
|
||||
"componentId": 509285,
|
||||
"spellLevel": 200,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -144,7 +198,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497397,
|
||||
"componentId": 509285,
|
||||
"spellLevel": 180,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-497399",
|
||||
@@ -164,7 +236,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
13000,
|
||||
13000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497399,
|
||||
"componentId": 509293,
|
||||
"spellLevel": 100,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -224,7 +314,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497401,
|
||||
"componentId": 509335,
|
||||
"spellLevel": 100,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-497397",
|
||||
@@ -244,7 +352,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497397,
|
||||
"componentId": 509285,
|
||||
"spellLevel": 100,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -304,7 +430,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497405,
|
||||
"componentId": 509342,
|
||||
"spellLevel": 220,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-497406",
|
||||
@@ -324,7 +468,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497406,
|
||||
"componentId": 508181,
|
||||
"spellLevel": 50,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -384,7 +546,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497405,
|
||||
"componentId": 509342,
|
||||
"spellLevel": 220,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-497585",
|
||||
@@ -404,7 +584,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
11000,
|
||||
11000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497585,
|
||||
"componentId": 620105,
|
||||
"spellLevel": 200,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -464,7 +662,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497405,
|
||||
"componentId": 509342,
|
||||
"spellLevel": 230,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-497586",
|
||||
@@ -484,7 +700,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
13000,
|
||||
13000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497586,
|
||||
"componentId": 620105,
|
||||
"spellLevel": 130,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -584,7 +818,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497405,
|
||||
"componentId": 509342,
|
||||
"spellLevel": 400,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-498256",
|
||||
@@ -604,7 +856,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
9000,
|
||||
9000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498256,
|
||||
"componentId": 620813,
|
||||
"spellLevel": 120,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-498257",
|
||||
@@ -624,7 +894,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498257,
|
||||
"componentId": 620814,
|
||||
"spellLevel": 120,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -684,7 +972,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497405,
|
||||
"componentId": 509342,
|
||||
"spellLevel": 410,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-497586",
|
||||
@@ -704,7 +1010,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
13000,
|
||||
13000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497586,
|
||||
"componentId": 620105,
|
||||
"spellLevel": 410,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -804,7 +1128,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498261,
|
||||
"componentId": 620818,
|
||||
"spellLevel": 400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-498482",
|
||||
@@ -824,7 +1166,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
11000,
|
||||
11000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498482,
|
||||
"componentId": 620820,
|
||||
"spellLevel": 150,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -924,7 +1284,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498264,
|
||||
"componentId": 620822,
|
||||
"spellLevel": 100,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-498266",
|
||||
@@ -944,7 +1322,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
6000,
|
||||
6000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498266,
|
||||
"componentId": 620845,
|
||||
"spellLevel": 400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-498267",
|
||||
@@ -964,7 +1360,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498267,
|
||||
"componentId": 620845,
|
||||
"spellLevel": 400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-498268",
|
||||
@@ -984,7 +1398,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
11000,
|
||||
11000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498268,
|
||||
"componentId": 620848,
|
||||
"spellLevel": 100,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1044,7 +1476,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
6000,
|
||||
6000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498266,
|
||||
"componentId": 620845,
|
||||
"spellLevel": 100,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1104,7 +1554,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498264,
|
||||
"componentId": 620822,
|
||||
"spellLevel": 100,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1164,7 +1632,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
6000,
|
||||
6000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498271,
|
||||
"componentId": 620852,
|
||||
"spellLevel": 400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1264,7 +1750,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498254,
|
||||
"componentId": 508181,
|
||||
"spellLevel": 100,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1324,7 +1828,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
12000,
|
||||
12000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 497025,
|
||||
"componentId": 508731,
|
||||
"spellLevel": 40,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -500,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-tomb-seven-heroes-spell-498295",
|
||||
@@ -1344,7 +1866,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
6000,
|
||||
6000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498295,
|
||||
"componentId": 620867,
|
||||
"spellLevel": 400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1404,7 +1944,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498254,
|
||||
"componentId": 508181,
|
||||
"spellLevel": 100,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1464,7 +2022,25 @@ export const TOMB_SEVEN_HEROES_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 498254,
|
||||
"componentId": 508181,
|
||||
"spellLevel": 100,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -4330,6 +4906,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -44,7 +44,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -112,7 +130,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 15,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-101533-aura-492431",
|
||||
@@ -183,7 +219,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492434,
|
||||
"componentId": 503002,
|
||||
"spellLevel": 25,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -308,6 +362,24 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492438,
|
||||
"componentId": 501098,
|
||||
"spellLevel": 24,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -393,7 +465,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492857,
|
||||
"componentId": 503019,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-490477",
|
||||
@@ -413,7 +503,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-492858",
|
||||
@@ -433,7 +541,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492858,
|
||||
"componentId": 500963,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -493,7 +619,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492857,
|
||||
"componentId": 503019,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-490477",
|
||||
@@ -513,7 +657,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-490521",
|
||||
@@ -533,7 +695,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490521,
|
||||
"componentId": 500963,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -593,7 +773,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492839,
|
||||
"componentId": 501106,
|
||||
"spellLevel": 30,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-490477",
|
||||
@@ -613,7 +811,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-492861",
|
||||
@@ -633,7 +849,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492861,
|
||||
"componentId": 500963,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -693,7 +927,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492857,
|
||||
"componentId": 503019,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-490477",
|
||||
@@ -713,7 +965,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -773,7 +1043,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492839,
|
||||
"componentId": 501106,
|
||||
"spellLevel": 30,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-492863",
|
||||
@@ -793,7 +1081,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492863,
|
||||
"componentId": 503210,
|
||||
"spellLevel": 9,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-492862",
|
||||
@@ -813,7 +1119,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492862,
|
||||
"componentId": 500963,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -873,7 +1197,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
6000,
|
||||
6000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490478,
|
||||
"componentId": 501008,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-492872",
|
||||
@@ -893,7 +1235,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
13000,
|
||||
13000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492872,
|
||||
"componentId": 500963,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -953,7 +1313,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492864,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 13,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-492865",
|
||||
@@ -973,7 +1351,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492865,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-492863",
|
||||
@@ -993,7 +1389,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492863,
|
||||
"componentId": 503210,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1053,7 +1467,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1113,7 +1545,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1173,7 +1623,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492863,
|
||||
"componentId": 503210,
|
||||
"spellLevel": 20,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-492862",
|
||||
@@ -1193,7 +1661,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492862,
|
||||
"componentId": 500963,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1253,7 +1739,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1313,7 +1817,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
6000,
|
||||
6000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490478,
|
||||
"componentId": 501008,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-spell-492858",
|
||||
@@ -1333,7 +1855,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
15000,
|
||||
15000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492858,
|
||||
"componentId": 500963,
|
||||
"spellLevel": 10,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1401,7 +1941,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 50,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-101534-spell-492428",
|
||||
@@ -1422,7 +1980,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492428,
|
||||
"componentId": 502580,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.2,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-treasure-trove-101534-debuff-492429",
|
||||
@@ -1482,6 +2058,24 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492430,
|
||||
"componentId": 503002,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -1000,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1582,7 +2176,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492893,
|
||||
"componentId": 502439,
|
||||
"spellLevel": 1000,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1642,7 +2254,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1702,7 +2332,25 @@ export const TREASURE_TROVE_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 1,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490477,
|
||||
"componentId": 501006,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -7303,6 +7951,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -56,7 +56,25 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
|
||||
20000,
|
||||
20000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490634,
|
||||
"componentId": 501091,
|
||||
"spellLevel": 150,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-ystra-labyrinth-boss1-102195-aura-491008",
|
||||
@@ -109,6 +127,24 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492307,
|
||||
"componentId": 502523,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -10,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -137,6 +173,24 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492308,
|
||||
"componentId": 502524,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -50,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -343,6 +397,24 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 7.5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490539,
|
||||
"componentId": 500962,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -389,7 +461,25 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492310,
|
||||
"componentId": 501091,
|
||||
"spellLevel": 150,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-ystra-labyrinth-boss1-101432-spell-492312",
|
||||
@@ -417,6 +507,24 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 7.5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492312,
|
||||
"componentId": 502439,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -470,6 +578,24 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 7.5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492314,
|
||||
"componentId": 502439,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -498,6 +624,24 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492334,
|
||||
"componentId": 501097,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -5,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -569,7 +713,25 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490524,
|
||||
"componentId": 500966,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-ystra-labyrinth-boss1-101433-aura-491008",
|
||||
@@ -672,6 +834,24 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492323,
|
||||
"componentId": 500962,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -725,6 +905,24 @@ export const YSTRA_LABYRINTH_BOSS1_ENTITIES = {
|
||||
"warningMs": 1800,
|
||||
"length": 30,
|
||||
"width": 2
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492327,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -852,6 +1050,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -62,6 +62,24 @@ export const YSTRA_LABYRINTH_BOSS2_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 7.5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490539,
|
||||
"componentId": 500962,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -108,7 +126,25 @@ export const YSTRA_LABYRINTH_BOSS2_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492310,
|
||||
"componentId": 501091,
|
||||
"spellLevel": 150,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-ystra-labyrinth-boss2-102196-spell-492312",
|
||||
@@ -136,6 +172,24 @@ export const YSTRA_LABYRINTH_BOSS2_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 7.5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492312,
|
||||
"componentId": 502439,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -189,6 +243,24 @@ export const YSTRA_LABYRINTH_BOSS2_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 7.5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492314,
|
||||
"componentId": 502439,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -217,6 +289,24 @@ export const YSTRA_LABYRINTH_BOSS2_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 15
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492334,
|
||||
"componentId": 501097,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -5,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -304,6 +394,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -55,7 +55,25 @@ export const YSTRA_LABYRINTH_BOSS3_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 490524,
|
||||
"componentId": 500966,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-ystra-labyrinth-boss3-102197-aura-491008",
|
||||
@@ -158,6 +176,24 @@ export const YSTRA_LABYRINTH_BOSS3_ENTITIES = {
|
||||
"anchor": "source",
|
||||
"warningMs": 1800,
|
||||
"radius": 5
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492323,
|
||||
"componentId": 500962,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -1,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 1,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -211,6 +247,24 @@ export const YSTRA_LABYRINTH_BOSS3_ENTITIES = {
|
||||
"warningMs": 1800,
|
||||
"length": 30,
|
||||
"width": 2
|
||||
},
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 492327,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -298,6 +352,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} 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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -44,7 +44,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494435,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 2,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -104,7 +122,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494447,
|
||||
"componentId": 502439,
|
||||
"spellLevel": 1400,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -204,7 +240,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494710,
|
||||
"componentId": 502683,
|
||||
"spellLevel": 2000,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-zurhidon-stronghold-spell-494733",
|
||||
@@ -224,7 +278,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
8000,
|
||||
8000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494733,
|
||||
"componentId": 505375,
|
||||
"spellLevel": 2000,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -284,7 +356,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494709,
|
||||
"componentId": 505354,
|
||||
"spellLevel": 5500,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -50,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-zurhidon-stronghold-spell-494710",
|
||||
@@ -304,7 +394,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494710,
|
||||
"componentId": 502683,
|
||||
"spellLevel": 2500,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -364,7 +472,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494710,
|
||||
"componentId": 502683,
|
||||
"spellLevel": 2500,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-zurhidon-stronghold-spell-494709",
|
||||
@@ -384,7 +510,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494709,
|
||||
"componentId": 505354,
|
||||
"spellLevel": 5000,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -50,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -484,7 +628,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494701,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 3,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-zurhidon-stronghold-spell-494702",
|
||||
@@ -504,7 +666,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494702,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -764,7 +944,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.4,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494703,
|
||||
"componentId": 503083,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.4,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.4,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-zurhidon-stronghold-spell-494704",
|
||||
@@ -784,7 +982,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494704,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 5,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-zurhidon-stronghold-spell-494705",
|
||||
@@ -804,7 +1020,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494705,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -904,7 +1138,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
8000,
|
||||
8000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494733,
|
||||
"componentId": 505375,
|
||||
"spellLevel": 1000,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -964,7 +1216,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494727,
|
||||
"componentId": 503019,
|
||||
"spellLevel": 49,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -100,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-zurhidon-stronghold-spell-494710",
|
||||
@@ -984,7 +1254,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494710,
|
||||
"componentId": 502683,
|
||||
"spellLevel": 2000,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1044,7 +1332,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494708,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 1,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-zurhidon-stronghold-spell-493195",
|
||||
@@ -1064,7 +1370,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
10000,
|
||||
10000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 493195,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 8,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1164,7 +1488,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494704,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1224,7 +1566,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494706,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 0,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rw-zurhidon-stronghold-spell-494702",
|
||||
@@ -1244,7 +1604,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
5000,
|
||||
5000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.8,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494702,
|
||||
"componentId": 500972,
|
||||
"spellLevel": 3,
|
||||
"calculationPath": "physical",
|
||||
"powerKind": "source-ratio",
|
||||
"damagePower": -0.8,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.8,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1344,7 +1722,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494710,
|
||||
"componentId": 502683,
|
||||
"spellLevel": 1000,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1404,7 +1800,25 @@ export const ZURHIDON_STRONGHOLD_ENTITIES = {
|
||||
7000,
|
||||
7000
|
||||
],
|
||||
"damageMultiplier": 0.85
|
||||
"damageMultiplier": 0.85,
|
||||
"runewakerFormula": {
|
||||
"schemaVersion": 1,
|
||||
"spellId": 494712,
|
||||
"componentId": 505357,
|
||||
"spellLevel": 1000,
|
||||
"calculationPath": "magic",
|
||||
"powerKind": "source-points",
|
||||
"damagePower": -20,
|
||||
"fixedValue": 0,
|
||||
"dotBase": 0,
|
||||
"runtimeBaselineMultiplier": 0.85,
|
||||
"provenance": {
|
||||
"sourceFile": "SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp",
|
||||
"physicalFunctionLine": 3366,
|
||||
"magicFunctionLine": 3651,
|
||||
"magicObjectDatabase": "Resource/data/magicobject.db"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -3193,6 +3607,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": "c47e67013e68ecf9c7ae30ef4b2058a850505385739dfe8c32ce8874e6ed077c",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; coefficients remain conservative HealerMan values."
|
||||
"sourceSpellCatalogSha256": "36bb578bbdda61af6ab864084907b347ff9a4f4c2fff671a2cb6586d535666ec",
|
||||
"sourceSpellPolicy": "Only positively classified direct-HP-damage families execute; source fields are preserved and live damage uses the labeled HealerMan baseline adapter when native stats are unavailable."
|
||||
} as const;
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
type WeaponStats,
|
||||
} from "./itemStats";
|
||||
import type { InventoryItem, ItemQuality } from "./lootTypes";
|
||||
import { rollRunewakerBossLoot, runewakerBossHasLoot } from "./runewakerLootCatalog";
|
||||
|
||||
export interface LootCatalogItem {
|
||||
readonly itemId: number;
|
||||
@@ -58,7 +59,8 @@ export function bossLootTable(bossKey: string): BossLootTable | null {
|
||||
}
|
||||
|
||||
export function bossHasLoot(bossKey: string): boolean {
|
||||
return (bossLootByKey.get(bossKey)?.items.length ?? 0) > 0;
|
||||
return (bossLootByKey.get(bossKey)?.items.length ?? 0) > 0
|
||||
|| runewakerBossHasLoot(bossKey);
|
||||
}
|
||||
|
||||
function boundedRandom(random: () => number): number {
|
||||
@@ -98,7 +100,9 @@ export function rollBossLoot(
|
||||
options: LootRollOptions = {},
|
||||
): readonly InventoryItem[] {
|
||||
const table = bossLootTable(bossKey);
|
||||
if (!table?.items.length) return [];
|
||||
if (!table?.items.length) {
|
||||
return rollRunewakerBossLoot(bossKey, playerLevel, options);
|
||||
}
|
||||
const random = options.random ?? Math.random;
|
||||
const acquiredAt = options.now ?? Date.now();
|
||||
const rolls = randomInteger(table.rollsMin, table.rollsMax, random);
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
onlineSessionOwnsWorldSimulation,
|
||||
onlineSynchronizedMobTransform,
|
||||
requestOnlineInteraction,
|
||||
publishOnlinePartyTeleport,
|
||||
} from "./onlineSessionRuntime";
|
||||
|
||||
const remotePlayer: OnlineSessionPlayer = {
|
||||
@@ -75,4 +76,18 @@ describe("online session runtime", () => {
|
||||
]);
|
||||
expect(drainOnlineInteractions()).toEqual([]);
|
||||
});
|
||||
|
||||
it("queues party transitions only for the activity authority", () => {
|
||||
configureOnlineSessionRuntime({ active: true, authority: false, localAccountId: "local" });
|
||||
expect(publishOnlinePartyTeleport([1, 2, 3], "cross-door", 50)).toBe(false);
|
||||
configureOnlineSessionRuntime({ active: true, authority: true, localAccountId: "local" });
|
||||
expect(publishOnlinePartyTeleport([1, 2, 3], "cross-door", 50)).toBe(true);
|
||||
expect(drainOnlineInteractions()).toEqual([
|
||||
expect.objectContaining({
|
||||
kind: "party-teleport",
|
||||
targetActorId: "party",
|
||||
position: [1, 2, 3],
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -117,7 +117,7 @@ export function onlineSynchronizedMobTransform(id: string): OnlineMobTransform |
|
||||
return active && !authority ? mobTransforms.get(id) ?? null : null;
|
||||
}
|
||||
|
||||
export function requestOnlineInteraction(kind: "loot" | "portal", targetActorId = ""): boolean {
|
||||
export function requestOnlineInteraction(kind: "loot" | "portal" | "interact", targetActorId = ""): boolean {
|
||||
if (!active || authority) return false;
|
||||
interactionSequence += 1;
|
||||
interactions.push({
|
||||
@@ -130,6 +130,25 @@ export function requestOnlineInteraction(kind: "loot" | "portal", targetActorId
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Queues a leader-only party transition for remote human members. */
|
||||
export function publishOnlinePartyTeleport(
|
||||
position: readonly [number, number, number],
|
||||
actionId: string,
|
||||
now = Date.now(),
|
||||
): boolean {
|
||||
if (!active || !authority || !localAccountId || !position.every(Number.isFinite)) return false;
|
||||
interactionSequence += 1;
|
||||
interactions.push({
|
||||
clientEventId: `party-teleport:${actionId}:${now}:${interactionSequence}`,
|
||||
kind: "party-teleport",
|
||||
sourceActorId: onlinePlayerActorId(localAccountId),
|
||||
targetActorId: "party",
|
||||
position,
|
||||
occurredAt: now,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
export function drainOnlineInteractions(): readonly OnlineRelayEventInput[] {
|
||||
const pending = interactions;
|
||||
interactions = [];
|
||||
|
||||
@@ -110,8 +110,11 @@ describe("party navigation fleet safety", () => {
|
||||
}
|
||||
}
|
||||
|
||||
expect(enabled).toHaveLength(31);
|
||||
expect(fallback).toHaveLength(53);
|
||||
// Raksha Temple and The Origin now ship their full source-assembled
|
||||
// navigation geometry, which correctly moves both above the synchronous
|
||||
// graph-build ceiling and onto the breadcrumb fallback path.
|
||||
expect(enabled).toHaveLength(29);
|
||||
expect(fallback).toHaveLength(55);
|
||||
});
|
||||
|
||||
it("covers every Manastorm package and keeps actual synchronous geometry under budget", () => {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
adaptRunewakerEnemyDamage,
|
||||
resolveRunewakerNativeResolvedPower,
|
||||
runewakerAttackDefenseRate,
|
||||
runewakerOrgPower,
|
||||
type RunewakerDamageFormulaSource,
|
||||
} from "./runewakerCombatFormula";
|
||||
|
||||
function formula(
|
||||
overrides: Partial<RunewakerDamageFormulaSource> = {},
|
||||
): RunewakerDamageFormulaSource {
|
||||
return {
|
||||
schemaVersion: 1,
|
||||
spellId: 492_289,
|
||||
componentId: 502_503,
|
||||
spellLevel: 6,
|
||||
calculationPath: "physical",
|
||||
powerKind: "source-ratio",
|
||||
damagePower: -0.7,
|
||||
fixedValue: 0,
|
||||
dotBase: 0,
|
||||
runtimeBaselineMultiplier: 0.85,
|
||||
provenance: {
|
||||
sourceFile: "MagicProcess.cpp",
|
||||
physicalFunctionLine: 3366,
|
||||
magicFunctionLine: 3651,
|
||||
magicObjectDatabase: "magicobject.db",
|
||||
},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("RuneWaker combat formula adapter", () => {
|
||||
it("preserves source ratio powers against HealerMan's explicit baseline", () => {
|
||||
expect(adaptRunewakerEnemyDamage({ formula: formula(), baselineDamage: 100 })).toMatchObject({
|
||||
amount: 70,
|
||||
fidelity: "source-ratio-adapter",
|
||||
appliedMultiplier: 0.7,
|
||||
});
|
||||
});
|
||||
|
||||
it("labels point-valued powers as scaled adapters instead of native damage", () => {
|
||||
const result = adaptRunewakerEnemyDamage({
|
||||
formula: formula({ powerKind: "source-points", damagePower: -300 }),
|
||||
baselineDamage: 100,
|
||||
});
|
||||
expect(result).toMatchObject({ amount: 85, fidelity: "scaled-point-adapter" });
|
||||
expect(result.unavailableSourceInputs).toContain("RuneWaker ATK or MATK");
|
||||
expect(result.unavailableSourceInputs).toContain("RuneWaker global NPC/PvP/AoE/target damage rates");
|
||||
});
|
||||
|
||||
it("fails closed when no damage family was proven", () => {
|
||||
expect(adaptRunewakerEnemyDamage({
|
||||
formula: formula({ powerKind: "unresolved", runtimeBaselineMultiplier: 0 }),
|
||||
baselineDamage: 100,
|
||||
})).toMatchObject({ amount: 0, fidelity: "unsupported" });
|
||||
});
|
||||
|
||||
it("implements the preserved org-power and attack-defense expressions", () => {
|
||||
expect(runewakerOrgPower(-100, 5, 10)).toBe(-150);
|
||||
expect(runewakerAttackDefenseRate(400, 300)).toBe(1);
|
||||
expect(runewakerAttackDefenseRate(0, 10_000)).toBe(0.2);
|
||||
});
|
||||
|
||||
it("requires every native rate in the source-shaped resolved-power path", () => {
|
||||
expect(resolveRunewakerNativeResolvedPower({
|
||||
power: -100,
|
||||
fixedValue: -0.5,
|
||||
fixedAttributeValue: 20,
|
||||
fixedDamageSkillLevelPercent: 10,
|
||||
spellLevel: 5,
|
||||
attackerPower: 400,
|
||||
defenderPower: 300,
|
||||
criticalMultiplier: 2,
|
||||
levelRate: 1,
|
||||
resistanceRate: 0.8,
|
||||
pvpOrNpcDamageRate: 1,
|
||||
areaPowerRate: 1,
|
||||
areaDefenseRate: 1,
|
||||
attackTargetRate: 1,
|
||||
attackerOriginDefenseRate: 1,
|
||||
sharedDamageRate: 1,
|
||||
})).toBe(184);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* Portable evidence attached to an imported RuneWaker damage mechanic.
|
||||
*
|
||||
* `runtimeBaselineMultiplier` is deliberately named as an adapter value: it
|
||||
* is not presented as a source coefficient when the original combat inputs
|
||||
* are unavailable in HealerMan.
|
||||
*/
|
||||
export interface RunewakerDamageFormulaSource {
|
||||
readonly schemaVersion: 1;
|
||||
readonly spellId: number;
|
||||
readonly componentId: number;
|
||||
readonly spellLevel: number;
|
||||
readonly calculationPath: "physical" | "magic" | "unknown";
|
||||
readonly powerKind: "source-ratio" | "source-points" | "dot-only" | "unresolved";
|
||||
readonly damagePower: number;
|
||||
readonly fixedValue: number;
|
||||
readonly dotBase: number;
|
||||
readonly runtimeBaselineMultiplier: number;
|
||||
readonly provenance: {
|
||||
readonly sourceFile: string;
|
||||
readonly physicalFunctionLine: number;
|
||||
readonly magicFunctionLine: number;
|
||||
readonly magicObjectDatabase: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RunewakerNativeResolvedPowerInput {
|
||||
/** The already-derived `Power` argument passed to CalPhyDamage/CalMagicDamage. */
|
||||
readonly power: number;
|
||||
readonly fixedValue: number;
|
||||
readonly fixedAttributeValue: number;
|
||||
readonly fixedDamageSkillLevelPercent: number;
|
||||
readonly spellLevel: number;
|
||||
readonly attackerPower: number;
|
||||
readonly defenderPower: number;
|
||||
readonly criticalMultiplier: number;
|
||||
readonly levelRate: number;
|
||||
readonly resistanceRate: number;
|
||||
readonly pvpOrNpcDamageRate: number;
|
||||
readonly areaPowerRate: number;
|
||||
readonly areaDefenseRate: number;
|
||||
readonly attackTargetRate: number;
|
||||
readonly attackerOriginDefenseRate: number;
|
||||
readonly sharedDamageRate: number;
|
||||
}
|
||||
|
||||
export interface RunewakerDamageAdapterInput {
|
||||
readonly formula: RunewakerDamageFormulaSource;
|
||||
/** HealerMan's level-scaled enemy attack budget before an ability multiplier. */
|
||||
readonly baselineDamage: number;
|
||||
}
|
||||
|
||||
export interface RunewakerDamageAdapterResult {
|
||||
readonly amount: number;
|
||||
readonly fidelity: "source-ratio-adapter" | "scaled-point-adapter" | "unsupported";
|
||||
readonly appliedMultiplier: number;
|
||||
readonly mappedInputs: readonly string[];
|
||||
readonly unavailableSourceInputs: readonly string[];
|
||||
}
|
||||
|
||||
const MISSING_NATIVE_INPUTS = Object.freeze([
|
||||
"RuneWaker ATK or MATK",
|
||||
"RuneWaker DEF or MDEF",
|
||||
"RuneWaker weapon or magic damage",
|
||||
"RuneWaker FixType attribute value",
|
||||
"RuneWaker level-difference rate table",
|
||||
"RuneWaker resistance calculation",
|
||||
"RuneWaker critical-power modifiers",
|
||||
"RuneWaker global NPC/PvP/AoE/target damage rates",
|
||||
] as const);
|
||||
|
||||
function finite(value: number, fallback = 0): number {
|
||||
return Number.isFinite(value) ? value : fallback;
|
||||
}
|
||||
|
||||
function nonNegative(value: number): number {
|
||||
return Math.max(0, finite(value));
|
||||
}
|
||||
|
||||
/** Exact ATK/DEF or MATK/MDEF ratio shared by the preserved source functions. */
|
||||
export function runewakerAttackDefenseRate(attackerPower: number, defenderPower: number): number {
|
||||
const attack = nonNegative(attackerPower) + 100;
|
||||
const defense = nonNegative(defenderPower) + 200;
|
||||
return Math.max(0.2, 1 + (attack - defense) / Math.max(attack, defense));
|
||||
}
|
||||
|
||||
/** Exact source expression for Atk.DmgPower and its per-skill-level argument. */
|
||||
export function runewakerOrgPower(
|
||||
damagePower: number,
|
||||
skillLevel: number,
|
||||
damagePowerSkillLevelPercent: number,
|
||||
): number {
|
||||
const power = finite(damagePower);
|
||||
return power + power * finite(damagePowerSkillLevelPercent) * nonNegative(skillLevel) / 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the source-shaped tail of CalPhyDamage/CalMagicDamage when every
|
||||
* native-only input is explicitly supplied. No global rate silently defaults
|
||||
* to one here, which prevents an "exact" result from hiding missing data.
|
||||
*/
|
||||
export function resolveRunewakerNativeResolvedPower(
|
||||
input: RunewakerNativeResolvedPowerInput,
|
||||
): number {
|
||||
let power = finite(input.power)
|
||||
+ finite(input.fixedValue)
|
||||
* finite(input.fixedAttributeValue)
|
||||
* (1 + nonNegative(input.spellLevel) * finite(input.fixedDamageSkillLevelPercent) / 100);
|
||||
if (power >= 0) return 0;
|
||||
power *= nonNegative(input.levelRate);
|
||||
power *= nonNegative(input.resistanceRate);
|
||||
power *= nonNegative(input.pvpOrNpcDamageRate);
|
||||
power *= nonNegative(input.criticalMultiplier);
|
||||
power *= runewakerAttackDefenseRate(input.attackerPower, input.defenderPower);
|
||||
power *= nonNegative(input.areaPowerRate);
|
||||
power *= nonNegative(input.areaDefenseRate);
|
||||
power *= nonNegative(input.attackTargetRate);
|
||||
power *= nonNegative(input.attackerOriginDefenseRate);
|
||||
power *= nonNegative(input.sharedDamageRate);
|
||||
return Math.max(0, Math.trunc(-power));
|
||||
}
|
||||
|
||||
export function normalizeRunewakerDamageFormula(
|
||||
value: RunewakerDamageFormulaSource | undefined,
|
||||
): RunewakerDamageFormulaSource | undefined {
|
||||
if (!value || value.schemaVersion !== 1) return undefined;
|
||||
if (!Number.isInteger(value.spellId) || value.spellId <= 0) return undefined;
|
||||
if (!Number.isInteger(value.componentId) || value.componentId <= 0) return undefined;
|
||||
const calculationPath = ["physical", "magic", "unknown"].includes(value.calculationPath)
|
||||
? value.calculationPath
|
||||
: "unknown";
|
||||
const powerKind = ["source-ratio", "source-points", "dot-only", "unresolved"].includes(value.powerKind)
|
||||
? value.powerKind
|
||||
: "unresolved";
|
||||
return Object.freeze({
|
||||
schemaVersion: 1,
|
||||
spellId: value.spellId,
|
||||
componentId: value.componentId,
|
||||
spellLevel: Math.max(0, Math.trunc(finite(value.spellLevel))),
|
||||
calculationPath,
|
||||
powerKind,
|
||||
damagePower: finite(value.damagePower),
|
||||
fixedValue: finite(value.fixedValue),
|
||||
dotBase: finite(value.dotBase),
|
||||
runtimeBaselineMultiplier: Math.min(4, nonNegative(value.runtimeBaselineMultiplier)),
|
||||
provenance: Object.freeze({ ...value.provenance }),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps imported source evidence to HealerMan's scaled combat budget. Ratio-like
|
||||
* source powers remain ratios. Point-valued powers use the separately labeled
|
||||
* runtime adapter multiplier because the native stat universe is unavailable.
|
||||
* WotLK-shaped hit, armor, resistance, crit, and aura processing happens later
|
||||
* in the existing combat-result pipeline and is not misreported as RuneWaker.
|
||||
*/
|
||||
export function adaptRunewakerEnemyDamage(
|
||||
input: RunewakerDamageAdapterInput,
|
||||
): RunewakerDamageAdapterResult {
|
||||
const formula = normalizeRunewakerDamageFormula(input.formula);
|
||||
const baselineDamage = nonNegative(input.baselineDamage);
|
||||
if (!formula || formula.powerKind === "unresolved" || formula.runtimeBaselineMultiplier <= 0) {
|
||||
return Object.freeze({
|
||||
amount: 0,
|
||||
fidelity: "unsupported",
|
||||
appliedMultiplier: 0,
|
||||
mappedInputs: Object.freeze(["No executable source damage family"]),
|
||||
unavailableSourceInputs: MISSING_NATIVE_INPUTS,
|
||||
});
|
||||
}
|
||||
const sourceRatio = formula.powerKind === "source-ratio"
|
||||
? Math.min(4, Math.max(0.2, Math.abs(formula.damagePower)))
|
||||
: null;
|
||||
const appliedMultiplier = sourceRatio ?? formula.runtimeBaselineMultiplier;
|
||||
return Object.freeze({
|
||||
amount: Math.max(0, Math.round(baselineDamage * appliedMultiplier)),
|
||||
fidelity: sourceRatio === null ? "scaled-point-adapter" : "source-ratio-adapter",
|
||||
appliedMultiplier,
|
||||
mappedInputs: Object.freeze([
|
||||
"HealerMan level-scaled enemy baseline damage",
|
||||
sourceRatio === null
|
||||
? "Explicit runtimeBaselineMultiplier for source point values"
|
||||
: "Absolute RuneWaker Atk.DmgPower ratio",
|
||||
"HealerMan combat-result mitigation after this adapter",
|
||||
]),
|
||||
unavailableSourceInputs: MISSING_NATIVE_INPUTS,
|
||||
});
|
||||
}
|
||||
@@ -301,7 +301,7 @@ describe("RuneWaker dungeon creature animation coverage", () => {
|
||||
}
|
||||
}
|
||||
expect(runtimeCounts, "runtime proxies: " + runtimeProxyIds.join(", ")).toEqual({
|
||||
native: 435,
|
||||
native: 436,
|
||||
poseOnly: 1,
|
||||
proxy: 0,
|
||||
});
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
createDungeonEncounterSnapshot,
|
||||
reduceDungeonEncounter,
|
||||
resetDungeonEncounterSnapshot,
|
||||
validateDungeonEncounterDefinition,
|
||||
} from "./dungeonEncounterRuntime";
|
||||
import type {
|
||||
DungeonEncounterAction,
|
||||
DungeonEncounterGuard,
|
||||
DungeonEncounterTrigger,
|
||||
} from "./dungeonEncounterTypes";
|
||||
import { DUNGEON_REGISTRY } from "./dungeonRegistry";
|
||||
import { validateDungeonWorldObject } from "./dungeonWorldObjects";
|
||||
import {
|
||||
RUNEWAKER_ENCOUNTER_CONTENT_CATALOG,
|
||||
RUNEWAKER_ENCOUNTER_CONTENT_LEDGER,
|
||||
} from "./runewakerEncounterCatalog";
|
||||
import type { DungeonDefinition } from "./dungeonTypes";
|
||||
|
||||
function runtimeSpawnIdList(definition: DungeonDefinition): readonly string[] {
|
||||
return [
|
||||
...definition.staticSpawns.map((spawn) => spawn.id),
|
||||
...definition.roamingPacks.flatMap((pack) => pack.members.map((member) => `${pack.id}:${member.id}`)),
|
||||
...(definition.encounterSpawnGroups ?? []).flatMap((group) => group.spawns.map((spawn) => spawn.id)),
|
||||
];
|
||||
}
|
||||
|
||||
function expectTriggerReferences(
|
||||
trigger: DungeonEncounterTrigger,
|
||||
spawnIds: ReadonlySet<string>,
|
||||
objectIds: ReadonlySet<string>,
|
||||
): void {
|
||||
if (trigger.kind === "entity-death" || trigger.kind === "entity-engaged" || trigger.kind === "entity-health") {
|
||||
expect(spawnIds, `trigger entity ${trigger.entityId}`).toContain(trigger.entityId);
|
||||
}
|
||||
if (trigger.kind === "entities-dead") {
|
||||
for (const entityId of trigger.entityIds) expect(spawnIds, `trigger entity ${entityId}`).toContain(entityId);
|
||||
}
|
||||
if (trigger.kind === "interact") expect(objectIds, `trigger object ${trigger.objectId}`).toContain(trigger.objectId);
|
||||
}
|
||||
|
||||
function expectGuardReferences(
|
||||
guard: DungeonEncounterGuard,
|
||||
spawnGroupIds: ReadonlySet<string>,
|
||||
objectIds: ReadonlySet<string>,
|
||||
objectiveIds: ReadonlySet<string>,
|
||||
): void {
|
||||
if (guard.kind === "spawn-group-active") {
|
||||
expect(spawnGroupIds, `guard spawn group ${guard.spawnGroupId}`).toContain(guard.spawnGroupId);
|
||||
}
|
||||
if (guard.kind === "world-object-state") {
|
||||
expect(objectIds, `guard object ${guard.objectId}`).toContain(guard.objectId);
|
||||
}
|
||||
if (guard.kind === "objective-complete") {
|
||||
expect(objectiveIds, `guard objective ${guard.objectiveId}`).toContain(guard.objectiveId);
|
||||
}
|
||||
}
|
||||
|
||||
function expectActionReferences(
|
||||
action: DungeonEncounterAction,
|
||||
spawnGroupIds: ReadonlySet<string>,
|
||||
objectsById: ReadonlyMap<string, DungeonDefinition["serverGameObjects"][number]>,
|
||||
objectiveIds: ReadonlySet<string>,
|
||||
): void {
|
||||
if (action.kind === "activate-spawn-group" || action.kind === "deactivate-spawn-group") {
|
||||
expect(spawnGroupIds, `action spawn group ${action.spawnGroupId}`).toContain(action.spawnGroupId);
|
||||
}
|
||||
if (action.kind === "set-world-object-state") {
|
||||
const object = objectsById.get(action.objectId);
|
||||
expect(object, `action object ${action.objectId}`).toBeDefined();
|
||||
expect(object?.states.map((state) => state.id), `state ${action.objectId}:${action.state}`).toContain(action.state);
|
||||
}
|
||||
if (action.kind === "complete-objective") {
|
||||
expect(objectiveIds, `action objective ${action.objectiveId}`).toContain(action.objectiveId);
|
||||
}
|
||||
}
|
||||
|
||||
describe("RuneWaker encounter content catalog", () => {
|
||||
it("wires reviewed packages into the runtime registry with complete references", () => {
|
||||
for (const content of RUNEWAKER_ENCOUNTER_CONTENT_CATALOG) {
|
||||
const definition = DUNGEON_REGISTRY[content.dungeonId];
|
||||
expect(definition, content.dungeonId).toBeDefined();
|
||||
expect(definition.expansion).toBe("runewaker");
|
||||
|
||||
const spawnIdList = runtimeSpawnIdList(definition);
|
||||
const spawnIds = new Set(spawnIdList);
|
||||
const spawnGroups = definition.encounterSpawnGroups ?? [];
|
||||
const spawnGroupIds = new Set(spawnGroups.map((group) => group.id));
|
||||
const objectIds = new Set(definition.serverGameObjects.map((object) => object.id));
|
||||
const objectsById = new Map(definition.serverGameObjects.map((object) => [object.id, object] as const));
|
||||
const objectiveIds = new Set([
|
||||
...definition.bosses.map((objective) => objective.id),
|
||||
...definition.objectiveOrder.map((objective) => objective.id),
|
||||
]);
|
||||
|
||||
for (const group of spawnGroups) {
|
||||
for (const spawn of group.spawns) {
|
||||
expect(definition.entities[spawn.entityId], `spawn entity ${spawn.entityId}`).toBeDefined();
|
||||
}
|
||||
}
|
||||
expect(spawnIds.size).toBe(spawnIdList.length);
|
||||
|
||||
for (const object of definition.serverGameObjects) validateDungeonWorldObject(object);
|
||||
for (const encounter of definition.encounters ?? []) {
|
||||
validateDungeonEncounterDefinition(encounter);
|
||||
expect(encounter.dungeonId).toBe(definition.id);
|
||||
for (const rule of encounter.rules) {
|
||||
expectTriggerReferences(rule.trigger, spawnIds, objectIds);
|
||||
for (const guard of rule.guards ?? []) {
|
||||
expectGuardReferences(guard, spawnGroupIds, objectIds, objectiveIds);
|
||||
}
|
||||
for (const action of rule.actions) {
|
||||
expectActionReferences(action, spawnGroupIds, objectsById, objectiveIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const encounter of content.encounters) {
|
||||
expect(definition.encounters?.map((entry) => entry.id)).toContain(encounter.id);
|
||||
}
|
||||
for (const group of content.spawnGroups) expect(spawnGroupIds).toContain(group.id);
|
||||
for (const object of content.worldObjects) expect(objectIds).toContain(object.id);
|
||||
}
|
||||
});
|
||||
|
||||
it("activates the three Forsaken source formations and exposes the reviewed hold phase", () => {
|
||||
const definition = DUNGEON_REGISTRY["forsaken-abbey"];
|
||||
const encounter = definition.encounters?.find((entry) => entry.id === "forsaken-abbey:follower-patrols");
|
||||
expect(encounter).toBeDefined();
|
||||
|
||||
const started = reduceDungeonEncounter(
|
||||
encounter!,
|
||||
createDungeonEncounterSnapshot(encounter!, 0),
|
||||
{ kind: "tick", now: 0 },
|
||||
).snapshot;
|
||||
expect(started.phase).toBe("formation-hold");
|
||||
expect(started.activeSpawnGroupIds).toEqual([
|
||||
"forsaken-abbey:follower-a",
|
||||
"forsaken-abbey:follower-b",
|
||||
"forsaken-abbey:follower-c",
|
||||
]);
|
||||
|
||||
const held = reduceDungeonEncounter(encounter!, started, { kind: "tick", now: 1_999 }).snapshot;
|
||||
expect(held.phase).toBe("formation-hold");
|
||||
const released = reduceDungeonEncounter(encounter!, held, { kind: "tick", now: 2_000 }).snapshot;
|
||||
expect(released.phase).toBe("patrols-active");
|
||||
});
|
||||
|
||||
it("keeps the Treasure Trove switch locked until Boulder Sidaar dies, then lowers the bridge once", () => {
|
||||
const definition = DUNGEON_REGISTRY["treasure-trove"];
|
||||
const encounter = definition.encounters?.find((entry) => entry.id === "treasure-trove:drawbridge-sequence");
|
||||
expect(encounter).toBeDefined();
|
||||
const initial = createDungeonEncounterSnapshot(encounter!, 0);
|
||||
|
||||
const premature = reduceDungeonEncounter(
|
||||
encounter!,
|
||||
initial,
|
||||
{ kind: "interact", objectId: "treasure-trove:drawbridge-switch", now: 10 },
|
||||
);
|
||||
expect(premature.snapshot).toBe(initial);
|
||||
|
||||
const armed = reduceDungeonEncounter(
|
||||
encounter!,
|
||||
initial,
|
||||
{ kind: "entity-death", entityId: "rw-treasure-trove-spawn-5952494", now: 100 },
|
||||
).snapshot;
|
||||
expect(armed).toMatchObject({
|
||||
phase: "switch-ready",
|
||||
worldObjectStates: {
|
||||
"treasure-trove:drawbridge": "raised",
|
||||
"treasure-trove:drawbridge-switch": "ready",
|
||||
},
|
||||
});
|
||||
|
||||
const completed = reduceDungeonEncounter(
|
||||
encounter!,
|
||||
armed,
|
||||
{ kind: "interact", objectId: "treasure-trove:drawbridge-switch", now: 200 },
|
||||
).snapshot;
|
||||
expect(completed).toMatchObject({
|
||||
phase: "complete",
|
||||
worldObjectStates: {
|
||||
"treasure-trove:drawbridge": "lowered",
|
||||
"treasure-trove:drawbridge-switch": "activated",
|
||||
},
|
||||
});
|
||||
expect(reduceDungeonEncounter(
|
||||
encounter!,
|
||||
completed,
|
||||
{ kind: "interact", objectId: "treasure-trove:drawbridge-switch", now: 300 },
|
||||
).snapshot).toBe(completed);
|
||||
|
||||
const reset = resetDungeonEncounterSnapshot(encounter!, completed, "boss-wipe", 400);
|
||||
expect(reset.worldObjectStates).toMatchObject({
|
||||
"treasure-trove:drawbridge": "lowered",
|
||||
"treasure-trove:drawbridge-switch": "activated",
|
||||
});
|
||||
expect(reduceDungeonEncounter(encounter!, reset, { kind: "tick", now: 401 }).snapshot.phase)
|
||||
.toBe("complete");
|
||||
});
|
||||
|
||||
it("keeps the bounded review ledger arithmetically honest and traceable", () => {
|
||||
const sourceRows = RUNEWAKER_ENCOUNTER_CONTENT_CATALOG.flatMap((entry) => (
|
||||
entry.reviews.flatMap((review) => review.sourceRows.map((sourceRow) => `${entry.dungeonId}:${sourceRow}`))
|
||||
));
|
||||
const evidenceIds = RUNEWAKER_ENCOUNTER_CONTENT_CATALOG.flatMap((entry) => (
|
||||
entry.reviews.flatMap((review) => review.evidenceIds)
|
||||
));
|
||||
expect(new Set(sourceRows).size).toBe(5);
|
||||
expect(new Set(evidenceIds).size).toBe(5);
|
||||
expect(RUNEWAKER_ENCOUNTER_CONTENT_LEDGER).toMatchObject({
|
||||
sourceReportDeferredRows: 4_113,
|
||||
mappedDeferredRows: 5,
|
||||
remainingDeferredRows: 4_108,
|
||||
sourceReportUnresolvedEvidence: 1_136,
|
||||
reviewedEvidenceRecords: 5,
|
||||
remainingUnresolvedEvidence: 1_131,
|
||||
});
|
||||
expect(RUNEWAKER_ENCOUNTER_CONTENT_LEDGER.sourceReportDeferredRows
|
||||
- RUNEWAKER_ENCOUNTER_CONTENT_LEDGER.mappedDeferredRows)
|
||||
.toBe(RUNEWAKER_ENCOUNTER_CONTENT_LEDGER.remainingDeferredRows);
|
||||
expect(RUNEWAKER_ENCOUNTER_CONTENT_LEDGER.sourceReportUnresolvedEvidence
|
||||
- RUNEWAKER_ENCOUNTER_CONTENT_LEDGER.reviewedEvidenceRecords)
|
||||
.toBe(RUNEWAKER_ENCOUNTER_CONTENT_LEDGER.remainingUnresolvedEvidence);
|
||||
|
||||
for (const entry of RUNEWAKER_ENCOUNTER_CONTENT_CATALOG) {
|
||||
for (const review of entry.reviews) {
|
||||
expect(review.fidelity).toBe("source-backed-adapted");
|
||||
expect(review.sourceFiles.length).toBeGreaterThan(0);
|
||||
expect(review.adaptations.length).toBeGreaterThan(0);
|
||||
expect(review.remainingGaps.length).toBeGreaterThan(0);
|
||||
for (const source of review.sourceFiles) expect(source.sha256).toMatch(/^[a-f0-9]{64}$/);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,406 @@
|
||||
import type {
|
||||
DungeonDefinition,
|
||||
DungeonEncounterSpawnGroupDefinition,
|
||||
} from "./dungeonTypes";
|
||||
import type { DungeonEncounterDefinition } from "./dungeonEncounterTypes";
|
||||
import type { DungeonWorldObjectDefinition } from "./dungeonWorldObjects";
|
||||
import type { PopulationEntityDefinition } from "./mobPopulation";
|
||||
|
||||
export type RunewakerEncounterContentFidelity = "source-exact" | "source-backed-adapted";
|
||||
|
||||
export interface RunewakerEncounterSourceFile {
|
||||
readonly path: string;
|
||||
readonly sha256: string;
|
||||
}
|
||||
|
||||
export interface RunewakerEncounterEvidenceReview {
|
||||
readonly id: string;
|
||||
readonly fidelity: RunewakerEncounterContentFidelity;
|
||||
readonly sourceRows: readonly number[];
|
||||
readonly evidenceIds: readonly string[];
|
||||
readonly sourceFiles: readonly RunewakerEncounterSourceFile[];
|
||||
readonly reviewedBehavior: string;
|
||||
readonly adaptations: readonly string[];
|
||||
readonly remainingGaps: readonly string[];
|
||||
}
|
||||
|
||||
interface RunewakerEncounterEntityAlias {
|
||||
readonly id: string;
|
||||
readonly sourceEntityId: string;
|
||||
readonly name: string;
|
||||
}
|
||||
|
||||
export interface RunewakerEncounterContentPackage {
|
||||
readonly dungeonId: string;
|
||||
readonly encounters: readonly DungeonEncounterDefinition[];
|
||||
readonly spawnGroups: readonly DungeonEncounterSpawnGroupDefinition[];
|
||||
readonly worldObjects: readonly DungeonWorldObjectDefinition[];
|
||||
readonly entityAliases: readonly RunewakerEncounterEntityAlias[];
|
||||
readonly reviews: readonly RunewakerEncounterEvidenceReview[];
|
||||
}
|
||||
|
||||
const FORSAKEN_PATROL_GROUP_IDS = Object.freeze([
|
||||
"forsaken-abbey:follower-a",
|
||||
"forsaken-abbey:follower-b",
|
||||
"forsaken-abbey:follower-c",
|
||||
] as const);
|
||||
|
||||
const FORSAKEN_PATROL_ENCOUNTER = Object.freeze({
|
||||
id: "forsaken-abbey:follower-patrols",
|
||||
dungeonId: "forsaken-abbey",
|
||||
initialPhase: "initializing",
|
||||
resetPolicy: { bossWipePhase: "initializing", checkpointPhase: "initializing" },
|
||||
rules: [
|
||||
{
|
||||
id: "create-source-formations",
|
||||
trigger: { kind: "tick" },
|
||||
guards: [{ kind: "phase", phases: ["initializing"] }],
|
||||
actions: [
|
||||
{ id: "enter-formation-hold", kind: "set-phase", phase: "formation-hold" },
|
||||
...FORSAKEN_PATROL_GROUP_IDS.map((spawnGroupId, index) => ({
|
||||
id: `activate-follower-${String.fromCharCode(97 + index)}`,
|
||||
kind: "activate-spawn-group" as const,
|
||||
spawnGroupId,
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "release-source-formations",
|
||||
trigger: { kind: "elapsed", afterMs: 2_000 },
|
||||
guards: [{ kind: "phase", phases: ["formation-hold"] }],
|
||||
actions: [{ id: "enter-patrol-phase", kind: "set-phase", phase: "patrols-active" }],
|
||||
},
|
||||
],
|
||||
} as const satisfies DungeonEncounterDefinition);
|
||||
|
||||
const FORSAKEN_PATROL_SPAWN_GROUPS = Object.freeze([
|
||||
{
|
||||
id: FORSAKEN_PATROL_GROUP_IDS[0],
|
||||
spawns: [
|
||||
{
|
||||
id: "forsaken-abbey:follower-a:ghoul",
|
||||
entityId: "rw-fa-100653",
|
||||
position: [119.523032, 4.05115, 5.524107],
|
||||
yaw: 4.865415815,
|
||||
spawnMask: 1,
|
||||
},
|
||||
{
|
||||
id: "forsaken-abbey:follower-a:spider",
|
||||
entityId: "rw-fa-100393",
|
||||
position: [121.104334, 4.05115, 5.767995],
|
||||
yaw: 4.865415815,
|
||||
spawnMask: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: FORSAKEN_PATROL_GROUP_IDS[1],
|
||||
spawns: [
|
||||
{
|
||||
id: "forsaken-abbey:follower-b:ghoul",
|
||||
entityId: "rw-fa-100653",
|
||||
position: [76.438701, -6.138638, 17.204093],
|
||||
yaw: 4.828148071,
|
||||
spawnMask: 1,
|
||||
},
|
||||
{
|
||||
id: "forsaken-abbey:follower-b:spider",
|
||||
entityId: "rw-fa-100393",
|
||||
position: [78.027993, -6.138638, 17.388895],
|
||||
yaw: 4.828148071,
|
||||
spawnMask: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: FORSAKEN_PATROL_GROUP_IDS[2],
|
||||
spawns: [
|
||||
{
|
||||
id: "forsaken-abbey:follower-c:ghoul",
|
||||
entityId: "rw-fa-100653",
|
||||
position: [129.755498, -15.077516, -74.285004],
|
||||
yaw: 1.132059978,
|
||||
spawnMask: 1,
|
||||
},
|
||||
{
|
||||
id: "forsaken-abbey:follower-c:spider",
|
||||
entityId: "rw-fa-100393",
|
||||
position: [128.307036, -15.077516, -73.60533],
|
||||
yaw: 1.132059978,
|
||||
spawnMask: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const satisfies readonly DungeonEncounterSpawnGroupDefinition[]);
|
||||
|
||||
const TREASURE_TROVE_DRAWBRIDGE_ID = "treasure-trove:drawbridge";
|
||||
const TREASURE_TROVE_SWITCH_ID = "treasure-trove:drawbridge-switch";
|
||||
|
||||
const TREASURE_TROVE_WORLD_OBJECTS = Object.freeze([
|
||||
{
|
||||
id: TREASURE_TROVE_DRAWBRIDGE_ID,
|
||||
name: "Drawbridge",
|
||||
kind: "bridge",
|
||||
position: [588.054827, -4.084724, -6.244854],
|
||||
yaw: 3.024162567,
|
||||
halfExtents: [3.5, 0.35, 5.5],
|
||||
initialState: "raised",
|
||||
states: [
|
||||
{ id: "raised", label: "Raised", visible: true, blocksMovement: true, blocksSight: false },
|
||||
{ id: "lowered", label: "Lowered", visible: true, blocksMovement: false, blocksSight: false },
|
||||
],
|
||||
visual: { color: "#6b5540", emissive: "#1b1209" },
|
||||
source: {
|
||||
fidelity: "source-backed-adapted",
|
||||
files: [
|
||||
"scripts/runewaker-pipeline/snapshots/treasure-trove-zone-108.json",
|
||||
"src/assets/game/dungeons/treasure-trove/population-import-report.json",
|
||||
"preserved RuneWaker Resource/luascript/01159.lua (sha256 23314a25d9654ec34a7d25de588974453f415c235804cc64e7240bf4e03423b6)",
|
||||
],
|
||||
templateId: 112098,
|
||||
spawnId: 5952428,
|
||||
note: "Source placement and state transition are preserved; the collision box and procedural visual are HealerMan adapters.",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: TREASURE_TROVE_SWITCH_ID,
|
||||
name: "Drawbridge Switch",
|
||||
kind: "switch",
|
||||
position: [585.282218, -4.084724, -6.571043],
|
||||
yaw: 1.470568359,
|
||||
halfExtents: [0.55, 0.8, 0.55],
|
||||
initialState: "locked",
|
||||
states: [
|
||||
{ id: "locked", label: "Locked by Boulder Sidaar", visible: true, blocksMovement: false, interactionEnabled: false },
|
||||
{ id: "ready", label: "Ready", visible: true, blocksMovement: false, interactionEnabled: true },
|
||||
{ id: "activated", label: "Activated", visible: true, blocksMovement: false, interactionEnabled: false },
|
||||
],
|
||||
interaction: {
|
||||
prompt: "Lower the drawbridge",
|
||||
range: 2.5,
|
||||
signalId: "treasure-trove:drawbridge-activated",
|
||||
allowedStates: ["ready"],
|
||||
oneShot: true,
|
||||
instructions: "Defeat Boulder Sidaar, then interact to lower the nearby drawbridge.",
|
||||
},
|
||||
visual: { color: "#a76e2a", emissive: "#3a2108" },
|
||||
source: {
|
||||
fidelity: "source-backed-adapted",
|
||||
files: [
|
||||
"scripts/runewaker-pipeline/snapshots/treasure-trove-zone-108.json",
|
||||
"src/assets/game/dungeons/treasure-trove/mechanics-audit.json",
|
||||
"preserved RuneWaker Resource/luascript/01159.lua (sha256 23314a25d9654ec34a7d25de588974453f415c235804cc64e7240bf4e03423b6)",
|
||||
],
|
||||
templateId: 112100,
|
||||
spawnId: 5952438,
|
||||
note: "Source placement and boss-gated interaction are preserved; interaction range and procedural visual are HealerMan adapters.",
|
||||
},
|
||||
},
|
||||
] as const satisfies readonly DungeonWorldObjectDefinition[]);
|
||||
|
||||
const TREASURE_TROVE_DRAWBRIDGE_ENCOUNTER = Object.freeze({
|
||||
id: "treasure-trove:drawbridge-sequence",
|
||||
dungeonId: "treasure-trove",
|
||||
initialPhase: "boss-active",
|
||||
initialState: {
|
||||
worldObjectStates: {
|
||||
[TREASURE_TROVE_DRAWBRIDGE_ID]: "raised",
|
||||
[TREASURE_TROVE_SWITCH_ID]: "locked",
|
||||
},
|
||||
},
|
||||
resetPolicy: { bossWipePhase: "boss-active", checkpointPhase: "boss-active" },
|
||||
rules: [
|
||||
{
|
||||
id: "boulder-sidaar-defeated",
|
||||
trigger: { kind: "entity-death", entityId: "rw-treasure-trove-spawn-5952494" },
|
||||
guards: [{ kind: "world-object-state", objectId: TREASURE_TROVE_SWITCH_ID, states: ["locked"] }],
|
||||
actions: [
|
||||
{ id: "arm-drawbridge-switch", kind: "set-phase", phase: "switch-ready", persistence: "run" },
|
||||
{
|
||||
id: "unlock-drawbridge-switch",
|
||||
kind: "set-world-object-state",
|
||||
objectId: TREASURE_TROVE_SWITCH_ID,
|
||||
state: "ready",
|
||||
persistence: "run",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "activate-drawbridge-switch",
|
||||
trigger: { kind: "interact", objectId: TREASURE_TROVE_SWITCH_ID },
|
||||
guards: [{ kind: "world-object-state", objectId: TREASURE_TROVE_SWITCH_ID, states: ["ready"] }],
|
||||
actions: [
|
||||
{ id: "complete-drawbridge-sequence", kind: "set-phase", phase: "complete", persistence: "run" },
|
||||
{
|
||||
id: "mark-drawbridge-switch-activated",
|
||||
kind: "set-world-object-state",
|
||||
objectId: TREASURE_TROVE_SWITCH_ID,
|
||||
state: "activated",
|
||||
persistence: "run",
|
||||
},
|
||||
{
|
||||
id: "lower-treasure-trove-drawbridge",
|
||||
kind: "set-world-object-state",
|
||||
objectId: TREASURE_TROVE_DRAWBRIDGE_ID,
|
||||
state: "lowered",
|
||||
persistence: "run",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "restore-ready-phase-after-reset",
|
||||
trigger: { kind: "tick" },
|
||||
guards: [
|
||||
{ kind: "phase", phases: ["boss-active"] },
|
||||
{ kind: "world-object-state", objectId: TREASURE_TROVE_SWITCH_ID, states: ["ready"] },
|
||||
],
|
||||
actions: [{ id: "restore-switch-ready-phase", kind: "set-phase", phase: "switch-ready" }],
|
||||
},
|
||||
{
|
||||
id: "restore-complete-phase-after-reset",
|
||||
trigger: { kind: "tick" },
|
||||
guards: [
|
||||
{ kind: "phase", phases: ["boss-active", "switch-ready"] },
|
||||
{ kind: "world-object-state", objectId: TREASURE_TROVE_DRAWBRIDGE_ID, states: ["lowered"] },
|
||||
],
|
||||
actions: [{ id: "restore-drawbridge-complete-phase", kind: "set-phase", phase: "complete" }],
|
||||
},
|
||||
],
|
||||
} as const satisfies DungeonEncounterDefinition);
|
||||
|
||||
const FORSAKEN_REVIEW = Object.freeze({
|
||||
id: "forsaken-abbey:follower-formations-review",
|
||||
fidelity: "source-backed-adapted",
|
||||
sourceRows: [12230, 12236, 12238],
|
||||
evidenceIds: [
|
||||
"signal:forsaken-abbey:template:110426:autoplot:f355bf45c2e5",
|
||||
"signal:forsaken-abbey:template:110426:autoplot:191cb880e4a9",
|
||||
"signal:forsaken-abbey:template:110426:autoplot:8cb0866b5187",
|
||||
],
|
||||
sourceFiles: [
|
||||
{ path: "scripts/runewaker-pipeline/snapshots/forsaken-abbey-zone-102.json", sha256: "3d19cf799b376b2cb2e0eccc4b509da22821e55f0a0581ce6406643759d37874" },
|
||||
{ path: "src/assets/game/dungeons/forsaken-abbey/mechanics-audit.json", sha256: "e6069c60e63de77b4f32731d8888a4025c1d5b916bfa699b206c18d16738beca" },
|
||||
{ path: "preserved RuneWaker Resource/luascript/00571.lua", sha256: "110efc48ad7a915ca76d269ec577a56c0e9e7b2edced0f146720a446aba33cf7" },
|
||||
{ path: "preserved RuneWaker Resource/luascript/00727.lua", sha256: "10e2e3e315e54dd0d7340fb314d88602a9692985ad7a67e79099a83bef0e387a" },
|
||||
],
|
||||
reviewedBehavior: "Each of three source initialization triggers creates a two-member Ghoul/Carrion Spider formation, holds it for two seconds, then hands it to an endless flag patrol.",
|
||||
adaptations: [
|
||||
"Source sleep units are converted from tenths of a second to a 2,000 ms encounter phase.",
|
||||
"Formation offsets are transformed into packaged dungeon coordinates and use projected trigger heights.",
|
||||
"The source-only level-scaled Ghoul 100653 reuses the packaged canonical Ghoul 100072 visual and combat definition.",
|
||||
],
|
||||
remainingGaps: [
|
||||
"The flag-path patrol primitive is not represented by encounter spawn groups, so the formations remain at their source start positions after the hold phase.",
|
||||
"Original reset/respawn cadence is not exposed by the preserved scripts and remains dungeon-reset scoped.",
|
||||
],
|
||||
} as const satisfies RunewakerEncounterEvidenceReview);
|
||||
|
||||
const TREASURE_TROVE_REVIEW = Object.freeze({
|
||||
id: "treasure-trove:drawbridge-review",
|
||||
fidelity: "source-backed-adapted",
|
||||
sourceRows: [5952428, 5952438],
|
||||
evidenceIds: [
|
||||
"signal:treasure-trove:template:101533:autoplot:3a096d804fa4",
|
||||
"signal:treasure-trove:template:112100:autoplot:9520ea9af0f5",
|
||||
],
|
||||
sourceFiles: [
|
||||
{ path: "scripts/runewaker-pipeline/snapshots/treasure-trove-zone-108.json", sha256: "aca7a3e57cbe704a6624f722c8060dc8e7be5d2014240781bc4668b162225a1e" },
|
||||
{ path: "src/assets/game/dungeons/treasure-trove/mechanics-audit.json", sha256: "a496185046e28657d76bd1d64353dcae070bee45c3e65a265f927a94f0c9fd86" },
|
||||
{ path: "preserved RuneWaker Resource/luascript/01159.lua", sha256: "23314a25d9654ec34a7d25de588974453f415c235804cc64e7240bf4e03423b6" },
|
||||
],
|
||||
reviewedBehavior: "Boulder Sidaar's death arms source switch 112100; touching that switch activates both it and nearby drawbridge 112098 exactly once.",
|
||||
adaptations: [
|
||||
"Source placements use the package's navmesh-projected local coordinates.",
|
||||
"The source motions are represented as explicit object states with procedural collision and visuals.",
|
||||
"The switch prompt is hidden until the boss death instead of showing the source failure message before it is armed.",
|
||||
],
|
||||
remainingGaps: [
|
||||
"Original ACT motion clips and object meshes are not packaged, so raised/lowered visuals use the generic world-object proxy.",
|
||||
"Black Mary's key-gated door remains deferred because the encounter guard model does not expose inventory item 203550.",
|
||||
],
|
||||
} as const satisfies RunewakerEncounterEvidenceReview);
|
||||
|
||||
export const RUNEWAKER_ENCOUNTER_CONTENT_CATALOG = Object.freeze([
|
||||
Object.freeze({
|
||||
dungeonId: "forsaken-abbey",
|
||||
encounters: [FORSAKEN_PATROL_ENCOUNTER],
|
||||
spawnGroups: FORSAKEN_PATROL_SPAWN_GROUPS,
|
||||
worldObjects: [],
|
||||
entityAliases: [{ id: "rw-fa-100653", sourceEntityId: "rw-fa-100072", name: "Ghoul (source summon 100653)" }],
|
||||
reviews: [FORSAKEN_REVIEW],
|
||||
}),
|
||||
Object.freeze({
|
||||
dungeonId: "treasure-trove",
|
||||
encounters: [TREASURE_TROVE_DRAWBRIDGE_ENCOUNTER],
|
||||
spawnGroups: [],
|
||||
worldObjects: TREASURE_TROVE_WORLD_OBJECTS,
|
||||
entityAliases: [],
|
||||
reviews: [TREASURE_TROVE_REVIEW],
|
||||
}),
|
||||
] as const satisfies readonly RunewakerEncounterContentPackage[]);
|
||||
|
||||
export const RUNEWAKER_ENCOUNTER_CONTENT_LEDGER = Object.freeze({
|
||||
sourceReportDeferredRows: 4_113,
|
||||
mappedDeferredRows: RUNEWAKER_ENCOUNTER_CONTENT_CATALOG.reduce(
|
||||
(count, entry) => count + entry.reviews.reduce((subtotal, review) => subtotal + review.sourceRows.length, 0),
|
||||
0,
|
||||
),
|
||||
remainingDeferredRows: 4_108,
|
||||
sourceReportUnresolvedEvidence: 1_136,
|
||||
reviewedEvidenceRecords: RUNEWAKER_ENCOUNTER_CONTENT_CATALOG.reduce(
|
||||
(count, entry) => count + entry.reviews.reduce((subtotal, review) => subtotal + review.evidenceIds.length, 0),
|
||||
0,
|
||||
),
|
||||
remainingUnresolvedEvidence: 1_131,
|
||||
});
|
||||
|
||||
const CONTENT_BY_DUNGEON: ReadonlyMap<string, RunewakerEncounterContentPackage> = new Map(
|
||||
RUNEWAKER_ENCOUNTER_CONTENT_CATALOG.map((entry) => [entry.dungeonId, entry] as const),
|
||||
);
|
||||
|
||||
function mergeUniqueById<T extends { readonly id: string }>(
|
||||
label: string,
|
||||
existing: readonly T[],
|
||||
additions: readonly T[],
|
||||
): readonly T[] {
|
||||
const merged = new Map(existing.map((entry) => [entry.id, entry] as const));
|
||||
for (const addition of additions) {
|
||||
if (merged.has(addition.id)) throw new Error(`Duplicate ${label} ${addition.id}`);
|
||||
merged.set(addition.id, addition);
|
||||
}
|
||||
return Object.freeze([...merged.values()]);
|
||||
}
|
||||
|
||||
function aliasedEntities(
|
||||
definition: DungeonDefinition,
|
||||
aliases: readonly RunewakerEncounterEntityAlias[],
|
||||
): Readonly<Record<string, PopulationEntityDefinition>> {
|
||||
const result: Record<string, PopulationEntityDefinition> = { ...definition.entities };
|
||||
for (const alias of aliases) {
|
||||
if (result[alias.id]) throw new Error(`Duplicate encounter entity ${alias.id}`);
|
||||
const source = definition.entities[alias.sourceEntityId];
|
||||
if (!source) throw new Error(`Encounter entity alias ${alias.id} references missing ${alias.sourceEntityId}`);
|
||||
result[alias.id] = Object.freeze({ ...source, id: alias.id, name: alias.name });
|
||||
}
|
||||
return Object.freeze(result);
|
||||
}
|
||||
|
||||
/** Adds only reviewed, source-linked encounter content; generated package files remain immutable. */
|
||||
export function withRunewakerEncounterContent(definition: DungeonDefinition): DungeonDefinition {
|
||||
const content = CONTENT_BY_DUNGEON.get(definition.id);
|
||||
if (!content) return definition;
|
||||
if (definition.expansion !== "runewaker") {
|
||||
throw new Error(`RuneWaker encounter content cannot attach to ${definition.expansion} dungeon ${definition.id}`);
|
||||
}
|
||||
return Object.freeze({
|
||||
...definition,
|
||||
entities: aliasedEntities(definition, content.entityAliases),
|
||||
encounters: mergeUniqueById("encounter", definition.encounters ?? [], content.encounters),
|
||||
encounterSpawnGroups: mergeUniqueById(
|
||||
"encounter spawn group",
|
||||
definition.encounterSpawnGroups ?? [],
|
||||
content.spawnGroups,
|
||||
),
|
||||
serverGameObjects: mergeUniqueById("world object", definition.serverGameObjects, content.worldObjects),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
RUNEWAKER_LOOT_CATALOG,
|
||||
rollRunewakerBossLoot,
|
||||
runewakerBossHasLoot,
|
||||
runewakerBossLootSource,
|
||||
} from "./runewakerLootCatalog";
|
||||
|
||||
describe("RuneWaker source loot catalog", () => {
|
||||
it("covers every source-classified boss template with explicit status", () => {
|
||||
expect(RUNEWAKER_LOOT_CATALOG.bossCount).toBe(RUNEWAKER_LOOT_CATALOG.bosses.length);
|
||||
expect(RUNEWAKER_LOOT_CATALOG.bossCount).toBeGreaterThanOrEqual(50);
|
||||
expect(RUNEWAKER_LOOT_CATALOG.bosses.every((boss) => Boolean(boss.status))).toBe(true);
|
||||
});
|
||||
|
||||
it("executes NPC slot and treasure probabilities deterministically", () => {
|
||||
const sequence = [0, 0, ...Array.from({ length: 28 }, () => 0.999999)];
|
||||
const drops = rollRunewakerBossLoot("rw-barren-caves-101462", 37, {
|
||||
random: () => sequence.shift() ?? 0.999999,
|
||||
now: 12_345,
|
||||
});
|
||||
expect(drops[0]).toMatchObject({
|
||||
itemId: 200004,
|
||||
name: "Healing Sigil",
|
||||
quantity: 1,
|
||||
itemLevel: 37,
|
||||
sourceBossName: "Anglo",
|
||||
inventorySlot: "misc",
|
||||
});
|
||||
expect(drops[0].stats).toMatchObject({ stamina: 0, armor: 0, meleeWeapon: null });
|
||||
});
|
||||
|
||||
it("keeps no-source tables and unknown bosses fail closed", () => {
|
||||
const noDrops = RUNEWAKER_LOOT_CATALOG.bosses.find((boss) => boss.status === "no-source-drops")!;
|
||||
expect(runewakerBossHasLoot(noDrops.bossKey)).toBe(false);
|
||||
expect(rollRunewakerBossLoot(noDrops.bossKey, 20, { random: () => 0 })).toEqual([]);
|
||||
expect(runewakerBossLootSource("unknown")).toBeNull();
|
||||
expect(rollRunewakerBossLoot("unknown", 20)).toEqual([]);
|
||||
});
|
||||
|
||||
it("exposes blocked source paths instead of executing them", () => {
|
||||
const partial = RUNEWAKER_LOOT_CATALOG.bosses.find((boss) => boss.status === "partial")!;
|
||||
expect(partial.blockedOutcomeCount).toBeGreaterThan(0);
|
||||
expect(partial.rolls.flatMap((roll) => roll.outcomes).some((outcome) => (
|
||||
outcome.runtimeStatus === "blocked" && Boolean(outcome.blockedReason)
|
||||
))).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,146 @@
|
||||
import generatedCatalog from "./generated/runewakerLootCatalog.json";
|
||||
import { EMPTY_ITEM_STATS } from "./itemStats";
|
||||
import type { InventoryItem } from "./lootTypes";
|
||||
|
||||
export interface RunewakerLootOutcome {
|
||||
readonly objectId: number;
|
||||
readonly name: string;
|
||||
readonly sourceCatalog: string | null;
|
||||
readonly sourceObjectType?: number;
|
||||
readonly runtimeStatus: "executable-generic-item" | "blocked";
|
||||
readonly blockedReason: string | null;
|
||||
readonly conditionalChance: number;
|
||||
readonly quantity: number;
|
||||
readonly sourcePath: readonly number[];
|
||||
readonly minimumLevelDelta?: number;
|
||||
readonly luaCheck?: string;
|
||||
}
|
||||
|
||||
export interface RunewakerLootRoll {
|
||||
readonly sourceSlot: number;
|
||||
readonly sourceObjectId: number;
|
||||
readonly sourceRatePer100000: number;
|
||||
readonly chancePerKill: number;
|
||||
readonly outcomes: readonly RunewakerLootOutcome[];
|
||||
}
|
||||
|
||||
export interface RunewakerBossLootSource {
|
||||
readonly bossKey: string;
|
||||
readonly dungeonId: string;
|
||||
readonly sourceNpcId: number;
|
||||
readonly bossName: string;
|
||||
readonly sourceLevel: number;
|
||||
readonly status: "portable" | "partial" | "no-source-drops" | "blocked";
|
||||
readonly blockedReasons: readonly string[];
|
||||
readonly rolls: readonly RunewakerLootRoll[];
|
||||
readonly executableOutcomeCount?: number;
|
||||
readonly blockedOutcomeCount?: number;
|
||||
}
|
||||
|
||||
export interface RunewakerLootCatalog {
|
||||
readonly schemaVersion: 1;
|
||||
readonly bossCount: number;
|
||||
readonly bosses: readonly RunewakerBossLootSource[];
|
||||
}
|
||||
|
||||
export interface RunewakerLootRollOptions {
|
||||
readonly random?: () => number;
|
||||
readonly now?: number;
|
||||
/** Source DLv: defeated NPC level minus the highest contributing player level. */
|
||||
readonly levelDelta?: number;
|
||||
/** HealerMan gameplay modifier: one extra authored slot roll may be attempted. */
|
||||
readonly bonusItemChance?: number;
|
||||
}
|
||||
|
||||
export const RUNEWAKER_LOOT_CATALOG = generatedCatalog as RunewakerLootCatalog;
|
||||
|
||||
const bossesByKey = new Map(
|
||||
RUNEWAKER_LOOT_CATALOG.bosses.map((boss) => [boss.bossKey, boss]),
|
||||
);
|
||||
let itemInstanceSequence = 0;
|
||||
|
||||
function boundedRandom(random: () => number): number {
|
||||
const value = random();
|
||||
if (!Number.isFinite(value)) return 0;
|
||||
return Math.max(0, Math.min(1 - Number.EPSILON, value));
|
||||
}
|
||||
|
||||
export function runewakerBossLootSource(bossKey: string): RunewakerBossLootSource | null {
|
||||
return bossesByKey.get(bossKey) ?? null;
|
||||
}
|
||||
|
||||
export function runewakerBossHasLoot(bossKey: string): boolean {
|
||||
return (bossesByKey.get(bossKey)?.executableOutcomeCount ?? 0) > 0;
|
||||
}
|
||||
|
||||
function selectedOutcome(
|
||||
roll: RunewakerLootRoll,
|
||||
random: () => number,
|
||||
): RunewakerLootOutcome | null {
|
||||
let cursor = boundedRandom(random);
|
||||
for (const outcome of roll.outcomes) {
|
||||
cursor -= Math.max(0, Math.min(1, outcome.conditionalChance));
|
||||
if (cursor < 0) return outcome;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function rollSourceSlot(
|
||||
boss: RunewakerBossLootSource,
|
||||
roll: RunewakerLootRoll,
|
||||
playerLevel: number,
|
||||
levelDelta: number,
|
||||
acquiredAt: number,
|
||||
random: () => number,
|
||||
): InventoryItem | null {
|
||||
if (boundedRandom(random) >= Math.max(0, Math.min(1, roll.chancePerKill))) return null;
|
||||
const outcome = selectedOutcome(roll, random);
|
||||
if (!outcome || outcome.runtimeStatus !== "executable-generic-item") return null;
|
||||
if (levelDelta < (outcome.minimumLevelDelta ?? Number.NEGATIVE_INFINITY)) return null;
|
||||
const itemLevel = Math.max(1, Math.min(255, Math.trunc(playerLevel)));
|
||||
itemInstanceSequence += 1;
|
||||
return Object.freeze({
|
||||
instanceId: `${boss.bossKey}:rw:${acquiredAt}:${itemInstanceSequence}`,
|
||||
itemId: outcome.objectId,
|
||||
itemKey: `runewaker-${outcome.objectId}`,
|
||||
name: outcome.name,
|
||||
quality: "common",
|
||||
inventorySlot: "misc",
|
||||
armorType: null,
|
||||
weaponType: null,
|
||||
itemLevel,
|
||||
sourceItemLevel: itemLevel,
|
||||
stats: EMPTY_ITEM_STATS,
|
||||
quantity: Math.max(1, Math.trunc(outcome.quantity)),
|
||||
sourceBossKey: boss.bossKey,
|
||||
sourceBossName: boss.bossName,
|
||||
acquiredAt,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the imported independent NPC drop slots and mutually exclusive
|
||||
* treasure outcomes. Unknown leaves and Lua-gated tables remain no-drop paths.
|
||||
*/
|
||||
export function rollRunewakerBossLoot(
|
||||
bossKey: string,
|
||||
playerLevel: number,
|
||||
options: RunewakerLootRollOptions = {},
|
||||
): readonly InventoryItem[] {
|
||||
const boss = bossesByKey.get(bossKey);
|
||||
if (!boss) return [];
|
||||
const random = options.random ?? Math.random;
|
||||
const acquiredAt = options.now ?? Date.now();
|
||||
const levelDelta = Math.trunc(options.levelDelta ?? 0);
|
||||
const drops = boss.rolls.flatMap((roll) => {
|
||||
const item = rollSourceSlot(boss, roll, playerLevel, levelDelta, acquiredAt, random);
|
||||
return item ? [item] : [];
|
||||
});
|
||||
const bonusChance = Math.max(0, Math.min(1, options.bonusItemChance ?? 0));
|
||||
if (boss.rolls.length && bonusChance > 0 && boundedRandom(random) < bonusChance) {
|
||||
const bonusRoll = boss.rolls[Math.floor(boundedRandom(random) * boss.rolls.length)];
|
||||
const item = rollSourceSlot(boss, bonusRoll, playerLevel, levelDelta, acquiredAt, random);
|
||||
if (item) drops.push(item);
|
||||
}
|
||||
return drops;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { BufferGeometry, Float32BufferAttribute } from "three";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { buildPartyNavigationGraph } from "./partyNavigation";
|
||||
import { auditOrderedObjectiveRoutes } from "../../scripts/runewaker-pipeline/lib/objective-route-audit.mjs";
|
||||
|
||||
@@ -21,4 +23,39 @@ describe("RuneWaker ordered objective route audit", () => {
|
||||
{ from: "Island Boss", to: "Home Boss", reachable: false },
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps the reviewed navigation blockers deterministic and fail-closed", () => {
|
||||
const report = JSON.parse(readFileSync(resolve(
|
||||
"scripts/runewaker-pipeline/RUNEWAKER_NAVIGATION_GAP_REPORT.json",
|
||||
), "utf8"));
|
||||
|
||||
expect(report.schemaVersion).toBe(2);
|
||||
expect(report).not.toHaveProperty("generatedAt");
|
||||
expect(report.unreachableLegs).toBe(19);
|
||||
expect(report.dungeons).toHaveLength(8);
|
||||
expect(report.resolutionSummary).toEqual({
|
||||
sourceAuthoredFixedOrders: 0,
|
||||
authoredOffMeshLinksAdded: 0,
|
||||
requirementDungeonCounts: {
|
||||
"dynamic-navigation-transition": 5,
|
||||
"encounter-gate-state": 3,
|
||||
"encounter-teleport-state": 2,
|
||||
"modular-environment-assembly": 3,
|
||||
"navigation-geometry-review": 1,
|
||||
"nonlinear-objectives": 1,
|
||||
"source-objective-review": 6,
|
||||
},
|
||||
});
|
||||
expect(report.dungeons.every((dungeon: {
|
||||
objectiveOrdering: { authoritative: boolean };
|
||||
reviewedFinding: { safeAutomaticLink: boolean; requirements: string[] };
|
||||
}) => (
|
||||
dungeon.objectiveOrdering.authoritative === false
|
||||
&& dungeon.reviewedFinding.safeAutomaticLink === false
|
||||
&& dungeon.reviewedFinding.requirements.length > 0
|
||||
))).toBe(true);
|
||||
expect(report.dungeons.find((dungeon: { id: string }) => (
|
||||
dungeon.id === "kalins-shrine"
|
||||
))?.reviewedFinding.objectiveOrderStatus).toBe("source-randomized-not-linear");
|
||||
});
|
||||
});
|
||||
@@ -29,6 +29,7 @@ interface ExecutableAttack {
|
||||
readonly range: number;
|
||||
readonly cooldownMs: number;
|
||||
readonly damageMultiplier: number;
|
||||
readonly runewakerFormula?: { readonly spellId: number; readonly runtimeBaselineMultiplier: number };
|
||||
}
|
||||
|
||||
const catalog = JSON.parse(readFileSync(
|
||||
@@ -43,6 +44,8 @@ const catalog = JSON.parse(readFileSync(
|
||||
readonly runtimeSqlDependency: boolean;
|
||||
readonly unknownEffectsExecute: boolean;
|
||||
readonly coefficientsAreSourceExact: boolean;
|
||||
readonly pointValuedDamageUsesLabeledScaledAdapter: boolean;
|
||||
readonly unavailableNativeInputsDefaulted: boolean;
|
||||
};
|
||||
readonly spells: readonly CatalogSpell[];
|
||||
};
|
||||
@@ -65,6 +68,8 @@ describe("portable RuneWaker source-spell mappings", () => {
|
||||
runtimeSqlDependency: false,
|
||||
unknownEffectsExecute: false,
|
||||
coefficientsAreSourceExact: false,
|
||||
pointValuedDamageUsesLabeledScaledAdapter: true,
|
||||
unavailableNativeInputsDefaulted: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -89,8 +94,9 @@ describe("portable RuneWaker source-spell mappings", () => {
|
||||
school: source?.runtime?.school,
|
||||
animation: source?.runtime?.animation,
|
||||
cooldownMs: source?.runtime?.cooldownMs,
|
||||
damageMultiplier: source?.runtime?.damageMultiplier,
|
||||
});
|
||||
expect(attack.damageMultiplier).toBeGreaterThan(0);
|
||||
expect(attack.runewakerFormula).toMatchObject({ spellId: attack.spellId });
|
||||
expect(["melee", "projectile", "area"]).toContain(attack.delivery);
|
||||
expect(["primary", "nearby-party", "random-party"]).toContain(attack.target);
|
||||
expect(attack.range).toBeGreaterThanOrEqual(source?.runtime?.range ?? 0);
|
||||
|
||||
+26
-1
@@ -8,7 +8,7 @@ import type { GmReviewTarget } from "./gmContentReview";
|
||||
export type AssetStatus = "checking" | "loading" | "ready" | "proxy" | "error";
|
||||
export type InputMode = "keyboard" | "gamepad";
|
||||
export type GameMode = "dungeon" | "manastorm";
|
||||
export type GameOverlay = "pause" | "party" | "inventory" | "map" | "spellbook" | "talents" | "bindings" | "options" | "gm" | null;
|
||||
export type GameOverlay = "pause" | "party" | "inventory" | "map" | "boss-guide" | "spellbook" | "talents" | "bindings" | "options" | "gm" | null;
|
||||
|
||||
interface PlayerSnapshot {
|
||||
position: Vector3Tuple;
|
||||
@@ -42,6 +42,10 @@ export interface GameState {
|
||||
distanceTravelled: number;
|
||||
hasMoved: boolean;
|
||||
resetRevision: number;
|
||||
/** Renderer relocation signal that does not imply a combat/encounter reset. */
|
||||
relocationRevision: number;
|
||||
/** Explicit local reset request; dungeon activation performs its reset before hydration. */
|
||||
encounterResetRevision: number;
|
||||
activateDungeon: (
|
||||
dungeonId: DungeonId,
|
||||
difficultyId?: string,
|
||||
@@ -65,6 +69,7 @@ export interface GameState {
|
||||
setActionLayer: (layer: ActionBindingLayer) => void;
|
||||
setAssetStatus: (status: AssetStatus, message?: string | null) => void;
|
||||
updatePlayerSnapshot: (snapshot: PlayerSnapshot) => void;
|
||||
relocatePlayer: (spawn: WorldSpawn, makeActiveSpawn?: boolean) => void;
|
||||
resetAtActiveSpawn: () => void;
|
||||
resetAtEntrance: () => void;
|
||||
}
|
||||
@@ -97,6 +102,8 @@ export const useGameStore = create<GameState>((set) => ({
|
||||
distanceTravelled: 0,
|
||||
hasMoved: false,
|
||||
resetRevision: 0,
|
||||
relocationRevision: 0,
|
||||
encounterResetRevision: 0,
|
||||
activateDungeon: (activeDungeonId, requestedDifficultyId, options) => set((state) => {
|
||||
const definition = requireDungeonDefinition(activeDungeonId);
|
||||
const activeDifficultyId = definition.difficultyVariants.some((variant) => variant.id === requestedDifficultyId)
|
||||
@@ -130,6 +137,7 @@ export const useGameStore = create<GameState>((set) => ({
|
||||
distanceTravelled: 0,
|
||||
hasMoved: false,
|
||||
resetRevision: state.resetRevision + 1,
|
||||
relocationRevision: state.relocationRevision + 1,
|
||||
};
|
||||
}),
|
||||
openOverlay: (overlay) => set({
|
||||
@@ -206,6 +214,19 @@ export const useGameStore = create<GameState>((set) => ({
|
||||
hasMoved: state.hasMoved || distanceDelta > 0.02,
|
||||
};
|
||||
}),
|
||||
relocatePlayer: (spawn, makeActiveSpawn = false) => set((state) => {
|
||||
const definition = requireDungeonDefinition(state.activeDungeonId);
|
||||
return {
|
||||
...(makeActiveSpawn ? { activeSpawn: spawn } : {}),
|
||||
playerPosition: spawn.footPosition,
|
||||
playerNavigationPosition: spawn.footPosition,
|
||||
playerGrounded: true,
|
||||
cameraYaw: spawn.yaw,
|
||||
areaName: resolveAreaNameForDungeon(definition, spawn.footPosition),
|
||||
cameraLookActive: false,
|
||||
relocationRevision: state.relocationRevision + 1,
|
||||
};
|
||||
}),
|
||||
resetAtActiveSpawn: () => set((state) => {
|
||||
const definition = requireDungeonDefinition(state.activeDungeonId);
|
||||
return {
|
||||
@@ -222,6 +243,8 @@ export const useGameStore = create<GameState>((set) => ({
|
||||
hasMoved: false,
|
||||
actionLayer: "primary",
|
||||
resetRevision: state.resetRevision + 1,
|
||||
relocationRevision: state.relocationRevision + 1,
|
||||
encounterResetRevision: state.encounterResetRevision + 1,
|
||||
};
|
||||
}),
|
||||
resetAtEntrance: () => set((state) => {
|
||||
@@ -246,6 +269,8 @@ export const useGameStore = create<GameState>((set) => ({
|
||||
hasMoved: false,
|
||||
actionLayer: "primary",
|
||||
resetRevision: state.resetRevision + 1,
|
||||
relocationRevision: state.relocationRevision + 1,
|
||||
encounterResetRevision: state.encounterResetRevision + 1,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
@@ -2,6 +2,20 @@ import { create } from "zustand";
|
||||
import referenceData from "./generated/azerothCoreReference.json";
|
||||
import type { MobCombatState } from "./combatStore";
|
||||
import type { MobVector3, StaticMobSpawnDefinition } from "./mobPopulation";
|
||||
import {
|
||||
createDungeonEncounterSnapshot,
|
||||
reduceDungeonEncounter,
|
||||
registerDungeonEncounterProvider,
|
||||
resetDungeonEncounterSnapshot,
|
||||
} from "./dungeonEncounterRuntime";
|
||||
import type {
|
||||
DungeonEncounterDefinition,
|
||||
DungeonEncounterEffect,
|
||||
DungeonEncounterEvent,
|
||||
DungeonEncounterResetScope,
|
||||
DungeonEncounterSnapshot,
|
||||
} from "./dungeonEncounterTypes";
|
||||
import type { DungeonEncounterSpawnGroupDefinition } from "./dungeonTypes";
|
||||
|
||||
export type WailingEncounterPhase =
|
||||
| "locked"
|
||||
@@ -89,85 +103,181 @@ function summonGroupSpawns(
|
||||
const SERPENT_SPAWNS = summonGroupSpawns(1, SERPENT_RUNTIME_PREFIX);
|
||||
const NIGHTMARE_SPAWNS = summonGroupSpawns(2, NIGHTMARE_RUNTIME_PREFIX);
|
||||
|
||||
function allDeadOrTimedOut(
|
||||
mobs: Readonly<Record<string, MobCombatState>>,
|
||||
ids: readonly string[],
|
||||
now: number,
|
||||
phaseStartedAt: number,
|
||||
timeoutMs: number,
|
||||
): boolean {
|
||||
const registered = ids.filter((id) => mobs[id]);
|
||||
return (
|
||||
registered.length === ids.length && registered.every((id) => mobs[id].dead)
|
||||
) || now - phaseStartedAt >= timeoutMs;
|
||||
export const WAILING_NARALEX_SPAWN_GROUPS: readonly DungeonEncounterSpawnGroupDefinition[] = Object.freeze([
|
||||
Object.freeze({ id: "naralex-ravagers", spawns: RAVAGER_SPAWNS }),
|
||||
Object.freeze({ id: "naralex-serpents", spawns: SERPENT_SPAWNS }),
|
||||
Object.freeze({ id: "naralex-nightmares", spawns: NIGHTMARE_SPAWNS }),
|
||||
]);
|
||||
|
||||
export const WAILING_NARALEX_ENCOUNTER = Object.freeze({
|
||||
id: "wailing-caverns:naralex",
|
||||
dungeonId: "wailing-caverns",
|
||||
initialPhase: "locked",
|
||||
resetPolicy: { bossWipePhase: "locked", checkpointPhase: "locked" },
|
||||
rules: [
|
||||
{
|
||||
id: "fanglords-defeated",
|
||||
trigger: { kind: "entities-dead", entityIds: FANG_LORD_RUNTIME_IDS },
|
||||
guards: [{ kind: "phase", phases: ["locked"] }],
|
||||
actions: [{ id: "unlock-naralex", kind: "set-phase", phase: "ready" }],
|
||||
},
|
||||
{
|
||||
id: "start-escort",
|
||||
trigger: { kind: "signal", signalId: "wailing-caverns:naralex-start" },
|
||||
guards: [{ kind: "phase", phases: ["ready"] }],
|
||||
actions: [{ id: "begin-ravager-escort", kind: "set-phase", phase: "escort-ravagers" }],
|
||||
},
|
||||
{
|
||||
id: "reach-ravager-wave",
|
||||
trigger: { kind: "elapsed", afterMs: 8_000 },
|
||||
guards: [{ kind: "phase", phases: ["escort-ravagers"] }],
|
||||
actions: [
|
||||
{ id: "enter-ravager-wave", kind: "set-phase", phase: "ravager-wave" },
|
||||
{ id: "activate-ravagers", kind: "activate-spawn-group", spawnGroupId: "naralex-ravagers" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "ravager-wave-cleared",
|
||||
trigger: { kind: "entities-dead", entityIds: RAVAGER_RUNTIME_IDS, timeoutMs: 60_000 },
|
||||
guards: [{ kind: "phase", phases: ["ravager-wave"] }],
|
||||
actions: [
|
||||
{ id: "finish-ravagers", kind: "deactivate-spawn-group", spawnGroupId: "naralex-ravagers" },
|
||||
{ id: "begin-serpent-escort", kind: "set-phase", phase: "escort-serpents" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "reach-serpent-wave",
|
||||
trigger: { kind: "elapsed", afterMs: 12_000 },
|
||||
guards: [{ kind: "phase", phases: ["escort-serpents"] }],
|
||||
actions: [
|
||||
{ id: "enter-serpent-wave", kind: "set-phase", phase: "serpent-wave" },
|
||||
{ id: "activate-serpents", kind: "activate-spawn-group", spawnGroupId: "naralex-serpents" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "serpent-wave-cleared",
|
||||
trigger: {
|
||||
kind: "entities-dead",
|
||||
entityIds: SERPENT_SPAWNS.map((spawn) => spawn.id),
|
||||
timeoutMs: 60_000,
|
||||
},
|
||||
guards: [{ kind: "phase", phases: ["serpent-wave"] }],
|
||||
actions: [
|
||||
{ id: "finish-serpents", kind: "deactivate-spawn-group", spawnGroupId: "naralex-serpents" },
|
||||
{ id: "begin-cleansing", kind: "set-phase", phase: "cleansing" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "finish-cleansing",
|
||||
trigger: { kind: "elapsed", afterMs: 32_500 },
|
||||
guards: [{ kind: "phase", phases: ["cleansing"] }],
|
||||
actions: [
|
||||
{ id: "enter-nightmare-wave", kind: "set-phase", phase: "nightmare-wave" },
|
||||
{ id: "activate-nightmares", kind: "activate-spawn-group", spawnGroupId: "naralex-nightmares" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "nightmare-wave-cleared",
|
||||
trigger: {
|
||||
kind: "entities-dead",
|
||||
entityIds: NIGHTMARE_SPAWNS.map((spawn) => spawn.id),
|
||||
timeoutMs: 90_000,
|
||||
},
|
||||
guards: [{ kind: "phase", phases: ["nightmare-wave"] }],
|
||||
actions: [
|
||||
{ id: "finish-nightmares", kind: "deactivate-spawn-group", spawnGroupId: "naralex-nightmares" },
|
||||
{ id: "summon-mutanus", kind: "set-phase", phase: "mutanus" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "mutanus-defeated",
|
||||
trigger: { kind: "entities-dead", entityIds: ["mutanus-spawn"] },
|
||||
guards: [{ kind: "phase", phases: ["mutanus"] }],
|
||||
actions: [
|
||||
{ id: "complete-naralex", kind: "set-phase", phase: "complete", persistence: "run" },
|
||||
{ id: "complete-mutanus-objective", kind: "complete-objective", objectiveId: "mutanus-spawn", persistence: "run" },
|
||||
],
|
||||
},
|
||||
],
|
||||
} as const satisfies DungeonEncounterDefinition);
|
||||
|
||||
function encounterSnapshot(state: WailingEncounterState): DungeonEncounterSnapshot {
|
||||
return {
|
||||
schemaVersion: state.schemaVersion,
|
||||
encounterId: state.encounterId,
|
||||
phase: state.phase,
|
||||
phaseStartedAt: state.phaseStartedAt,
|
||||
flags: state.flags,
|
||||
counters: state.counters,
|
||||
timers: state.timers,
|
||||
worldObjectStates: state.worldObjectStates,
|
||||
transitionStates: state.transitionStates,
|
||||
activeSpawnGroupIds: state.activeSpawnGroupIds,
|
||||
completedObjectiveIds: state.completedObjectiveIds,
|
||||
consumedActionIds: state.consumedActionIds,
|
||||
checkpointId: state.checkpointId,
|
||||
revision: state.revision,
|
||||
};
|
||||
}
|
||||
|
||||
function idsWithPrefix(mobs: Readonly<Record<string, MobCombatState>>, prefix: string): string[] {
|
||||
return Object.keys(mobs).filter((id) => id.startsWith(prefix));
|
||||
}
|
||||
|
||||
function fangLordsDefeated(mobs: Readonly<Record<string, MobCombatState>>): boolean {
|
||||
return FANG_LORD_RUNTIME_IDS.every((id) => mobs[id]?.dead);
|
||||
}
|
||||
|
||||
export interface WailingEncounterState {
|
||||
export interface WailingEncounterState extends Omit<DungeonEncounterSnapshot, "phase"> {
|
||||
readonly phase: WailingEncounterPhase;
|
||||
readonly phaseStartedAt: number;
|
||||
readonly revision: number;
|
||||
reset: () => void;
|
||||
dispatch: (event: DungeonEncounterEvent) => readonly DungeonEncounterEffect[];
|
||||
reset: (scope?: DungeonEncounterResetScope, now?: number) => void;
|
||||
start: (now?: number) => boolean;
|
||||
advance: (mobs: Readonly<Record<string, MobCombatState>>, now?: number) => void;
|
||||
}
|
||||
|
||||
export const useWailingEncounterStore = create<WailingEncounterState>((set, get) => ({
|
||||
...createDungeonEncounterSnapshot(WAILING_NARALEX_ENCOUNTER),
|
||||
phase: "locked",
|
||||
phaseStartedAt: 0,
|
||||
revision: 0,
|
||||
reset: () => set((state) => ({
|
||||
phase: "locked",
|
||||
phaseStartedAt: 0,
|
||||
revision: state.revision + 1,
|
||||
})),
|
||||
dispatch: (event) => {
|
||||
const snapshot = encounterSnapshot(get());
|
||||
const result = reduceDungeonEncounter(WAILING_NARALEX_ENCOUNTER, snapshot, event);
|
||||
if (result.snapshot !== snapshot) set(result.snapshot as Partial<WailingEncounterState>);
|
||||
return result.effects;
|
||||
},
|
||||
reset: (scope = "dungeon", now = 0) => set((state) => (
|
||||
resetDungeonEncounterSnapshot(
|
||||
WAILING_NARALEX_ENCOUNTER,
|
||||
encounterSnapshot(state),
|
||||
scope,
|
||||
now,
|
||||
) as Partial<WailingEncounterState>
|
||||
)),
|
||||
start: (now = Date.now()) => {
|
||||
if (get().phase !== "ready") return false;
|
||||
set({ phase: "escort-ravagers", phaseStartedAt: now });
|
||||
return true;
|
||||
get().dispatch({ kind: "signal", signalId: "wailing-caverns:naralex-start", now });
|
||||
return get().phase === "escort-ravagers";
|
||||
},
|
||||
advance: (mobs, now = Date.now()) => {
|
||||
const registeredEntityIds = Object.keys(mobs);
|
||||
get().dispatch({
|
||||
kind: "tick",
|
||||
now,
|
||||
facts: {
|
||||
registeredEntityIds,
|
||||
deadEntityIds: registeredEntityIds.filter((id) => mobs[id].dead),
|
||||
},
|
||||
});
|
||||
},
|
||||
advance: (mobs, now = Date.now()) => set((state) => {
|
||||
let phase = state.phase;
|
||||
if (phase === "locked" && fangLordsDefeated(mobs)) phase = "ready";
|
||||
else if (phase === "escort-ravagers" && now - state.phaseStartedAt >= 8_000) {
|
||||
phase = "ravager-wave";
|
||||
} else if (
|
||||
phase === "ravager-wave"
|
||||
&& allDeadOrTimedOut(mobs, RAVAGER_RUNTIME_IDS, now, state.phaseStartedAt, 60_000)
|
||||
) {
|
||||
phase = "escort-serpents";
|
||||
} else if (phase === "escort-serpents" && now - state.phaseStartedAt >= 12_000) {
|
||||
phase = "serpent-wave";
|
||||
} else if (phase === "serpent-wave") {
|
||||
const ids = idsWithPrefix(mobs, SERPENT_RUNTIME_PREFIX);
|
||||
if (
|
||||
(ids.length === SERPENT_SPAWNS.length && ids.every((id) => mobs[id].dead))
|
||||
|| now - state.phaseStartedAt >= 60_000
|
||||
) phase = "cleansing";
|
||||
} else if (phase === "cleansing" && now - state.phaseStartedAt >= 32_500) {
|
||||
phase = "nightmare-wave";
|
||||
} else if (phase === "nightmare-wave") {
|
||||
const ids = idsWithPrefix(mobs, NIGHTMARE_RUNTIME_PREFIX);
|
||||
if (
|
||||
(ids.length === NIGHTMARE_SPAWNS.length && ids.every((id) => mobs[id].dead))
|
||||
|| now - state.phaseStartedAt >= 90_000
|
||||
) phase = "mutanus";
|
||||
} else if (phase === "mutanus" && mobs["mutanus-spawn"]?.dead) {
|
||||
phase = "complete";
|
||||
}
|
||||
return phase === state.phase
|
||||
? state
|
||||
: { phase, phaseStartedAt: now, revision: state.revision + 1 };
|
||||
}),
|
||||
}));
|
||||
|
||||
registerDungeonEncounterProvider({
|
||||
definition: WAILING_NARALEX_ENCOUNTER,
|
||||
encounterId: WAILING_NARALEX_ENCOUNTER.id,
|
||||
dungeonId: WAILING_NARALEX_ENCOUNTER.dungeonId,
|
||||
getSnapshot: () => encounterSnapshot(useWailingEncounterStore.getState()),
|
||||
dispatch: (event) => useWailingEncounterStore.getState().dispatch(event),
|
||||
reset: (scope, now = 0) => useWailingEncounterStore.getState().reset(scope, now),
|
||||
replaceSnapshot: (snapshot) => {
|
||||
if (snapshot.encounterId !== WAILING_NARALEX_ENCOUNTER.id) {
|
||||
throw new Error(`Cannot hydrate ${WAILING_NARALEX_ENCOUNTER.id} from ${snapshot.encounterId}`);
|
||||
}
|
||||
useWailingEncounterStore.setState(snapshot as Partial<WailingEncounterState>);
|
||||
},
|
||||
});
|
||||
|
||||
export function wailingEventSpawns(
|
||||
phase: WailingEncounterPhase,
|
||||
): readonly StaticMobSpawnDefinition[] {
|
||||
@@ -227,4 +337,3 @@ export function wailingDisciplePosition(
|
||||
left[2] + (right[2] - left[2]) * alpha,
|
||||
];
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user