Update Healer Man server
This commit is contained in:
1 parent
9d7e0e2f9e
commit
35dfdaed61
94 files changed
+17034
-418
No files matched your search
@@ -0,0 +1,110 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user