Update Healer Man server

This commit is contained in:
phenom committed 2026-09-28 22:46:45 -04:00
1 parent 9d7e0e2f9e
commit 35dfdaed61
94 files changed
+17034 -418

No files matched your search

+249
View File
@@ -0,0 +1,249 @@
import { beforeEach, describe, expect, it } from "vitest";
import { PLAYER_AGGRO_ID } from "./aggro";
import { applyAura, resolveStatValue } from "./combatAuras";
import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry";
import { clearPartyRuntimeRegistry } from "./partyRuntimeRegistry";
import { preloadAbilityCatalogsForClasses } from "./abilityCatalog";
import { PLAYER_AURA_ENTITY_ID, useCombatStore } from "./combatStore";
import { usePartyStore } from "./partyStore";
const TARGET_ID = "druid-proc-target";
function unit(seed: string): number {
let hash = 2166136261;
for (let index = 0; index < seed.length; index += 1) {
hash ^= seed.charCodeAt(index);
hash = Math.imul(hash, 16777619);
}
return (hash >>> 0) / 0x1_0000_0000;
}
function abilityId(name: string): string {
const ability = useCombatStore.getState().abilities.find((candidate) => candidate.name === name);
expect(ability, name).toBeDefined();
return ability!.id;
}
function findDruidProcStart(name: string, startAt: number, chance: number, procTag: string): number {
const ability = useCombatStore.getState().abilities.find((candidate) => candidate.name === name);
expect(ability, name).toBeDefined();
for (let start = startAt; start < startAt + 100_000; start += 100) {
const impactAt = start + ability!.castTimeMs;
const directSeed = `${PLAYER_AGGRO_ID}:${ability!.id}:${TARGET_ID}:${impactAt}:direct`;
if (unit(`${directSeed}:outcome`) > 0.1 && unit(`${directSeed}:${procTag}`) < chance) return start;
}
throw new Error(`No deterministic ${name} proc seed found.`);
}
function castAndFinish(name: string, startedAt: number) {
const id = abilityId(name);
const result = useCombatStore.getState().castAbility(id, { targetActorId: TARGET_ID, now: startedAt });
expect(result.ok, JSON.stringify(result)).toBe(true);
const activeCast = useCombatStore.getState().activeCast;
if (activeCast?.abilityId === id) {
useCombatStore.getState().tick(Math.max(0.1, (activeCast.completesAt - startedAt) / 1_000), activeCast.completesAt);
}
return useCombatStore.getState().combatEvents.filter((event) => (
event.kind === "damage" && event.abilityId === id
)).at(-1);
}
describe("WoW Druid proc runtime", () => {
beforeEach(async () => {
clearMobRuntimeRegistry();
clearPartyRuntimeRegistry();
await preloadAbilityCatalogsForClasses(["druid"]);
useCombatStore.getState().initializeCharacter({ classId: "druid", level: 80, xp: 0 });
useCombatStore.getState().resetSettings();
useCombatStore.getState().setPlayerPosition([0, 0, 0]);
usePartyStore.getState().initializeParty("wow335-druid-proc-tests", 1, useCombatStore.getState().health);
useCombatStore.setState((state) => ({
resource: state.maxResource,
resourcePools: { ...state.resourcePools, mana: state.maxResourcePools.mana },
}));
useCombatStore.getState().registerMob(TARGET_ID, { name: "Target", maxHealth: 1_000_000 });
setMobPosition(TARGET_ID, [4, 0, 0]);
useCombatStore.getState().selectMob(TARGET_ID);
});
it("procs Solar Eclipse from Starfire and gives Wrath its source 30% damage bonus", () => {
const baseline = castAndFinish("Wrath", 1_000);
expect(baseline?.kind).toBe("damage");
useCombatStore.setState((state) => ({ talentRanks: { ...state.talentRanks, "talent-1924": 3 } }));
castAndFinish("Starfire", 5_000);
const solar = useCombatStore.getState().auras.find((aura) => (
aura.targetId === PLAYER_AURA_ENTITY_ID
&& aura.definition.utilityTags?.includes("wow335-druid-eclipse-solar")
));
expect(solar?.expiresAt).toBe(23_500);
expect(useCombatStore.getState().cooldowns["wow335-druid-eclipse-solar"]).toBe(38_500);
const buffed = castAndFinish("Wrath", 10_000);
expect(buffed?.kind).toBe("damage");
expect(buffed?.rawAmount).toBe(Math.round(baseline!.rawAmount * 1.3));
});
it("procs Lunar Eclipse from Wrath and adds 30 percentage points to Starfire crit chance", () => {
const baselineEvent = castAndFinish("Starfire", 1_000);
expect(baselineEvent?.kind).toBe("damage");
const baselineCritChance = useCombatStore.getState().lastCombatResult?.attackTable.criticalPercent;
expect(baselineCritChance).toBeDefined();
useCombatStore.setState((state) => ({ talentRanks: { ...state.talentRanks, "talent-1924": 3 } }));
const wrathStart = findDruidProcStart("Wrath", 6_000, 0.6, "druid-eclipse");
castAndFinish("Wrath", wrathStart);
const lunar = useCombatStore.getState().auras.find((aura) => (
aura.targetId === PLAYER_AURA_ENTITY_ID
&& aura.definition.utilityTags?.includes("wow335-druid-eclipse-lunar")
));
expect(lunar).toBeDefined();
castAndFinish("Starfire", wrathStart + 2_100);
expect(useCombatStore.getState().lastCombatResult?.attackTable.criticalPercent)
.toBe(baselineCritChance! + 30);
});
it("grants Nature's Grace after a direct healing critical and applies 20% cast haste for four seconds", () => {
const healingTouch = abilityId("Healing Touch");
let startedAt = 1_000;
while (unit(`${PLAYER_AGGRO_ID}:${healingTouch}:${PLAYER_AURA_ENTITY_ID}:${startedAt + 3_000}:heal:druid-omen-of-clarity`) >= 0.06) {
startedAt += 100;
}
useCombatStore.setState((state) => ({
health: Math.round(state.maxHealth / 2),
talentRanks: { ...state.talentRanks, "talent-789": 3, "talent-827": 3 },
auras: [...applyAura(state.auras, {
definition: {
id: "test-druid-guaranteed-spell-crit",
name: "Test Critical Chance",
disposition: "buff",
durationMs: null,
modifiers: [{ kind: "stat", stat: "spell-critical-strike", operation: "flat", value: 100 }],
},
sourceId: PLAYER_AURA_ENTITY_ID,
targetId: PLAYER_AURA_ENTITY_ID,
now: startedAt,
}).auras],
}));
expect(useCombatStore.getState().castAbility(healingTouch, { targetActorId: null, now: startedAt }).ok).toBe(true);
const firstCast = useCombatStore.getState().activeCast;
expect(firstCast?.abilityId).toBe(healingTouch);
useCombatStore.getState().tick(0.1, firstCast!.completesAt);
expect(useCombatStore.getState().combatEvents.some((event) => (
event.kind === "healing" && event.abilityId === healingTouch && event.critical
))).toBe(true);
const natureGrace = useCombatStore.getState().auras.find((aura) => (
aura.targetId === PLAYER_AURA_ENTITY_ID
&& aura.definition.utilityTags?.includes("wow335-druid-natures-grace")
));
expect(natureGrace?.expiresAt).toBe(firstCast!.completesAt + 4_000);
expect(resolveStatValue(0, [natureGrace!], "haste")).toBe(20);
expect(useCombatStore.getState().auras.some((aura) => (
aura.targetId === PLAYER_AURA_ENTITY_ID
&& aura.definition.utilityTags?.includes("wow335-druid-clearcasting")
))).toBe(true);
const manaAfterFirstHeal = useCombatStore.getState().resource;
const nextStart = firstCast!.completesAt + 1_500;
useCombatStore.setState((state) => ({
lastResourceSpentAt: { ...state.lastResourceSpentAt, mana: nextStart },
}));
expect(useCombatStore.getState().castAbility(healingTouch, { targetActorId: null, now: nextStart }).ok).toBe(true);
expect(useCombatStore.getState().resource).toBe(manaAfterFirstHeal);
const hastedCast = useCombatStore.getState().activeCast;
expect(hastedCast?.originalDurationMs).toBe(Math.round(3_000 / 1.2));
useCombatStore.getState().tick(0.1, hastedCast!.completesAt);
const overhealClearcasting = useCombatStore.getState().auras.find((aura) => (
aura.targetId === PLAYER_AURA_ENTITY_ID
&& aura.definition.utilityTags?.includes("wow335-druid-clearcasting")
));
expect(overhealClearcasting?.expiresAt).toBe(hastedCast!.completesAt + 15_000);
expect(useCombatStore.getState().cooldowns["wow335-druid-omen-of-clarity"])
.toBe(hastedCast!.completesAt + 2_000);
});
it("procs Omen of Clarity from damage, makes the next eligible cast free, and respects its two-second ICD", () => {
useCombatStore.setState((state) => ({ talentRanks: { ...state.talentRanks, "talent-827": 3 } }));
const moonfire = useCombatStore.getState().abilities.find((ability) => ability.name === "Moonfire");
expect(moonfire).toBeDefined();
const start = (() => {
for (let now = 1_000; now < 100_000; now += 1) {
const seed = `${PLAYER_AGGRO_ID}:${moonfire!.id}:${TARGET_ID}:${now}:direct`;
if (unit(`${seed}:outcome`) > 0.1 && unit(`${seed}:druid-omen-of-clarity`) < 0.06) return now;
}
throw new Error("No deterministic Omen of Clarity proc seed found.");
})();
const first = useCombatStore.getState().castAbility(moonfire!.id, { targetActorId: TARGET_ID, now: start });
expect(first.ok, JSON.stringify(first)).toBe(true);
const manaAfterFirst = useCombatStore.getState().resource;
const clearcasting = useCombatStore.getState().auras.find((aura) => (
aura.targetId === PLAYER_AURA_ENTITY_ID
&& aura.definition.utilityTags?.includes("wow335-druid-clearcasting")
));
expect(clearcasting?.expiresAt).toBe(start + 15_000);
expect(useCombatStore.getState().cooldowns["wow335-druid-omen-of-clarity"]).toBe(start + 2_000);
const second = useCombatStore.getState().castAbility(moonfire!.id, {
targetActorId: TARGET_ID,
now: start + 1_500,
});
expect(second.ok, JSON.stringify(second)).toBe(true);
expect(useCombatStore.getState().resource).toBe(manaAfterFirst);
expect(useCombatStore.getState().auras.some((aura) => (
aura.targetId === PLAYER_AURA_ENTITY_ID
&& aura.definition.utilityTags?.includes("wow335-druid-clearcasting")
))).toBe(false);
});
it("allows a successful healing spell that fully overheals to proc Omen of Clarity", () => {
const healingTouch = abilityId("Healing Touch");
useCombatStore.setState((state) => ({
health: state.maxHealth,
talentRanks: { ...state.talentRanks, "talent-827": 3 },
}));
let startedAt = 1_000;
while (unit(`${PLAYER_AGGRO_ID}:${healingTouch}:${PLAYER_AURA_ENTITY_ID}:${startedAt + 3_000}:heal:druid-omen-of-clarity`) >= 0.06) {
startedAt += 100;
}
const result = useCombatStore.getState().castAbility(healingTouch, { targetActorId: null, now: startedAt });
expect(result.ok, JSON.stringify(result)).toBe(true);
const activeCast = useCombatStore.getState().activeCast;
expect(activeCast?.abilityId).toBe(healingTouch);
useCombatStore.getState().tick(0.1, activeCast!.completesAt);
const healingEvent = useCombatStore.getState().combatEvents.find((event) => (
event.kind === "healing" && event.abilityId === healingTouch && event.occurredAt === activeCast!.completesAt
));
expect(healingEvent).toMatchObject({ rawAmount: expect.any(Number), effectiveAmount: 0 });
expect(useCombatStore.getState().auras.some((aura) => (
aura.targetId === PLAYER_AURA_ENTITY_ID
&& aura.definition.utilityTags?.includes("wow335-druid-clearcasting")
))).toBe(true);
});
it("allows landed player auto attacks to trigger Omen of Clarity", () => {
useCombatStore.setState((state) => ({ talentRanks: { ...state.talentRanks, "talent-827": 3 } }));
let now = 1_000;
let seed = `druid-auto-attack:${now}`;
while (unit(`${seed}:druid-omen-of-clarity`) >= 0.06) {
now += 1;
seed = `druid-auto-attack:${now}`;
}
const result = useCombatStore.getState().damageMob(TARGET_ID, 100, now, undefined, "physical", {
attackKind: "melee",
seed,
rules: { isAutoAttack: true, canMiss: false, canDodge: false, canParry: false, canBlock: false, canCrit: false },
});
expect(result.amount).toBeGreaterThan(0);
expect(useCombatStore.getState().auras.some((aura) => (
aura.targetId === PLAYER_AURA_ENTITY_ID
&& aura.definition.utilityTags?.includes("wow335-druid-clearcasting")
))).toBe(true);
});
});