Files
healer-man/src/game/romPriestWarlockRuntime.test.ts
T
2026-09-28 22:46:45 -04:00

111 lines
4.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { abilityById } from "./abilityCatalog";
import { channelAmountEffects, channelPulseCount } from "./combatChannelAmounts";
import { useCombatStore } from "./combatStore";
import { clearPartyRuntimeRegistry, registerPartyRuntimePosition } from "./partyRuntimeRegistry";
import { usePartyStore } from "./partyStore";
import { romEffectsForCast } from "./romCombatMechanics";
const prayer = abilityById("rom-priest-rom-warlock-elite-25")!;
function prayerEffects(targetHealthPercent: number) {
return romEffectsForCast(prayer, {
sealStacks: 0,
bleedCount: 0,
charged: false,
mainHandDagger: false,
casterMaxHealth: 1_000,
targetHealthPercent: 1,
friendlyTargetHealthPercent: targetHealthPercent,
});
}
describe("RoM Priest/Warlock Prayer of Tribulation", () => {
it("channels four source-authored friendly heals and fills missing timing from CoA", () => {
expect(prayer).toMatchObject({
dbcSpellId: 498770,
target: "friendly",
castMode: "channel",
castTimeMs: 1_500,
cooldownMs: 5_000,
channelPulses: 4,
});
const heals = prayer.effects.filter((effect) => effect.kind === "heal");
expect(heals).toHaveLength(4);
expect(prayer.effects.some((effect) => effect.kind === "damage")).toBe(false);
expect(channelPulseCount(prayer)).toBe(4);
expect(channelAmountEffects(prayer, 4).filter(({ effect }) => effect.kind === "heal"))
.toEqual(heals.map((effect) => ({ effect, multiplier: 0.25 })));
});
it("applies one +2% defense stack above 40%, two below, and none at 40%", () => {
const buffStacks = (effects: ReturnType<typeof prayerEffects>) => effects.filter((effect) => (
effect.kind === "apply-aura" && effect.aura.modifiers?.some((modifier) => (
modifier.kind === "stat" && modifier.stat === "armor"
))
));
expect(buffStacks(prayerEffects(0.41))).toHaveLength(1);
expect(buffStacks(prayerEffects(0.4))).toHaveLength(0);
expect(buffStacks(prayerEffects(0.39))).toHaveLength(2);
for (const effect of buffStacks(prayerEffects(0.39))) {
expect(effect).toMatchObject({
kind: "apply-aura",
aura: { durationMs: 10_000, maxStacks: 8, stackBehavior: "add" },
});
if (effect.kind === "apply-aura") {
expect(effect.aura.modifiers?.some((modifier) => (
modifier.kind === "stat" && modifier.stat === "armor" && modifier.value === 0.02 && modifier.perStack
))).toBe(true);
expect(effect.aura.modifiers?.some((modifier) => (
modifier.kind === "stat" && modifier.stat === "magical-defense" && modifier.value === 0.02 && modifier.perStack
))).toBe(true);
}
}
});
it("heals the selected ally over four pulses and applies two stacks below 40%", () => {
clearPartyRuntimeRegistry();
useCombatStore.getState().initializeCharacter({
classId: "rom-priest",
secondaryClassId: "rom-warlock",
raceId: "rom-dwarf",
level: 30,
role: "healer",
});
usePartyStore.getState().initializeParty("rom-priest-warlock-prayer", 30, useCombatStore.getState().health, "healer", 2, "rom");
const member = usePartyStore.getState().members[0]!;
usePartyStore.getState().damageMember(member.id, member.maxHealth - 1);
// Keep the target below 40% through the four small heals so this cast
// exercises the tooltip's low-health double-stack branch at resolve time.
usePartyStore.setState((state) => ({
members: state.members.map((partyMember) => partyMember.id === member.id
? { ...partyMember, maxHealth: 10_000 }
: partyMember),
}));
registerPartyRuntimePosition(member.id, [3, 0, 0]);
usePartyStore.getState().selectMember(member.id);
const healthAtCast = usePartyStore.getState().members[0]!.health;
expect(useCombatStore.getState().castAbility(prayer.id, undefined, 1_000)).toMatchObject({ ok: true });
const healthByPulse: number[] = [];
let previousAt = 1_000;
for (const now of [1_375, 1_750, 2_125, 2_500]) {
useCombatStore.getState().tick((now - previousAt) / 1_000, now);
healthByPulse.push(usePartyStore.getState().members[0]!.health);
previousAt = now;
}
expect(healthByPulse[0]).toBeGreaterThan(healthAtCast);
expect(healthByPulse[1]).toBeGreaterThan(healthByPulse[0]!);
expect(healthByPulse[2]).toBeGreaterThan(healthByPulse[1]!);
expect(healthByPulse[3]).toBeGreaterThan(healthByPulse[2]!);
const defenseAura = useCombatStore.getState().auras.find((aura) => (
aura.targetId === member.id && aura.definition.name.includes("Prayer of Tribulation")
));
expect(defenseAura?.stacks).toBe(2);
expect(defenseAura?.definition.modifiers?.some((modifier) => (
modifier.kind === "stat" && modifier.stat === "armor" && modifier.perStack
))).toBe(true);
});
});