Update Healer Man server

This commit is contained in:
phenom committed 2026-10-07 13:05:04 -04:00
1 parent 69809f7228
commit ff20ab8779
101 files changed
+16713 -945

No files matched your search

@@ -0,0 +1,161 @@
import { afterEach, beforeAll, describe, expect, it } from "vitest";
import { abilitiesForClass, abilityAtLevel, preloadAbilityCatalogsForClasses } from "./abilityCatalog";
import { PLAYER_AGGRO_ID } from "./aggro";
import { applyAura } from "./combatAuras";
import { useCombatStore } from "./combatStore";
import { clearMobRuntimeRegistry } from "./mobRuntimeRegistry";
import { advanceDungeonPartyCombat, resetPartyRuntimeClock } from "./partyRuntime";
import { clearPartyRuntimeRegistry, registerPartyRuntimePosition } from "./partyRuntimeRegistry";
import { createConfiguredPartyMember, usePartyStore } from "./partyStore";
const NOW = 2_000;
// Ten valid first-row Frost points satisfy Lichborne's current tree gate.
const TALENTS = { "talent-2031": 3, "talent-2020": 2, "talent-1968": 5, "talent-2215": 1 };
const LICHBORNE_ID = "wow335-death-knight-lichborne";
function prepare(options: {
readonly recipient: "player" | "party";
readonly spellId?: 19134 | 20511;
readonly learned?: boolean;
readonly genericControl?: "fear" | "stun" | "confuse" | "sleep";
readonly standaloneControl?: boolean;
readonly cooldownReadyAt?: number;
readonly globalCooldownEndsAt?: number;
}) {
const spellId = options.spellId ?? 19134;
const mechanic = options.standaloneControl ? options.genericControl!
: spellId === 20511 ? "stun" : "fear";
const controlledUntil = options.standaloneControl ? 4_000 : 9_000;
const talentRanks = options.learned === false ? {} : TALENTS;
useCombatStore.getState().initializeCharacter({
classId: options.recipient === "player" ? "death-knight" : "priest",
level: 80,
talentRanks: options.recipient === "player" ? talentRanks : {},
});
usePartyStore.getState().initializeParty("lichborne-escape-fixture", 80,
useCombatStore.getState().health, "damage", 1);
const member = createConfiguredPartyMember({
name: "Configured Lichborne Knight", classId: "death-knight",
specialization: "Frost", specializationId: "death-knight:frost",
talentTreeId: "death-knight-frost", talentRanks, role: "damage",
}, 80, 0, "lichborne-escape-fixture");
usePartyStore.setState({ members: options.recipient === "party" ? [{
...member, controlledUntil, controlMechanic: mechanic, status: "holding",
cooldowns: options.cooldownReadyAt === undefined ? member.cooldowns
: { ...member.cooldowns, [LICHBORNE_ID]: options.cooldownReadyAt },
globalCooldownEndsAt: options.globalCooldownEndsAt ?? 0,
}] : [] });
const targetId = options.recipient === "player" ? PLAYER_AGGRO_ID : member.id;
if (options.recipient === "party") registerPartyRuntimePosition(member.id, [0, 0, 0]);
useCombatStore.getState().synchronizeActors();
useCombatStore.setState((state) => {
let auras = options.standaloneControl ? state.auras : applyAura(state.auras, { definition: {
id: `enemy-control:source-spell-${spellId}`, name: "Pinned source shout fixture",
disposition: "debuff", durationMs: 8_000,
modifiers: [{ kind: "damage", direction: "taken", school: "physical", operation: "percent", value: 0.15 }],
}, sourceId: "synthetic-enemy", targetId, now: 1_000 }).auras;
if (options.genericControl) auras = applyAura(auras, { definition: {
id: `enemy-control:separate-enemy:separate-cast:${options.genericControl}`, name: "Independent control fixture",
disposition: "debuff", durationMs: 3_000,
}, sourceId: "separate-enemy", targetId, now: 1_000 }).auras;
return {
auras: [...auras],
controlledUntil: options.recipient === "player" ? controlledUntil : 0,
controlMechanic: options.recipient === "player" ? mechanic : null,
cooldowns: options.recipient === "player" && options.cooldownReadyAt !== undefined
? { ...state.cooldowns, [LICHBORNE_ID]: options.cooldownReadyAt } : state.cooldowns,
globalCooldownEndsAt: options.recipient === "player" ? options.globalCooldownEndsAt ?? 0 : 0,
};
});
return targetId;
}
function hasSourceControl(targetId: string) {
return useCombatStore.getState().auras.some((aura) => aura.targetId === targetId
&& aura.definition.id.startsWith("enemy-control:source-spell-"));
}
function hasLichborneFearGrant(targetId: string) {
return useCombatStore.getState().auras.some((aura) => aura.targetId === targetId
&& aura.definition.id.startsWith("wow335:49039:")
&& aura.definition.controlImmunities?.includes("fear"));
}
describe("source-bound Lichborne player and configured companion escape", () => {
beforeAll(async () => { await preloadAbilityCatalogsForClasses(["death-knight"]); });
afterEach(() => {
clearMobRuntimeRegistry(); clearPartyRuntimeRegistry(); resetPartyRuntimeClock();
});
it.each([19134, 20511] as const)("casts learned player Lichborne through source control %s", (spellId) => {
const id = prepare({ recipient: "player", spellId });
const selected = abilityAtLevel(abilitiesForClass("death-knight")
.find((ability) => ability.id === LICHBORNE_ID)!, 80);
expect(selected.dbcSpellId).toBe(49039);
expect(useCombatStore.getState().talentRanks["talent-2215"]).toBe(1);
const cast = useCombatStore.getState().castAbility(LICHBORNE_ID, { now: NOW });
expect(cast, JSON.stringify(cast)).toMatchObject({ ok: true });
expect(hasSourceControl(id)).toBe(false);
expect(hasLichborneFearGrant(id)).toBe(true);
expect(useCombatStore.getState()).toMatchObject({ controlledUntil: 0, controlMechanic: null });
expect(useCombatStore.getState().cooldowns[LICHBORNE_ID]).toBe(NOW + 120_000);
useCombatStore.getState().tick(0.1, NOW + 100);
expect(hasSourceControl(id)).toBe(false);
});
it.each([19134, 20511] as const)("uses learned companion Lichborne through source control %s", (spellId) => {
const id = prepare({ recipient: "party", spellId });
advanceDungeonPartyCombat(NOW);
expect(hasSourceControl(id)).toBe(false);
expect(hasLichborneFearGrant(id)).toBe(true);
expect(usePartyStore.getState().members[0]).toMatchObject({ controlledUntil: 0, controlMechanic: null });
expect(useCombatStore.getState().actors[id]?.controlledUntil).toBe(0);
expect(usePartyStore.getState().members[0]!.cooldowns[LICHBORNE_ID]).toBe(NOW + 120_000);
});
it.each(["player", "party"] as const)("keeps %s controlled when Lichborne is unlearned, cooling down or blocked by unsupported sleep", (recipient) => {
for (const condition of [{ learned: false }, { cooldownReadyAt: 10_000 }, { genericControl: "sleep" as const }]) {
const id = prepare({ recipient, ...condition });
if (recipient === "player") expect(useCombatStore.getState().castAbility(LICHBORNE_ID, { now: NOW }).ok).toBe(false);
else advanceDungeonPartyCombat(NOW);
expect(hasSourceControl(id)).toBe(true);
expect(hasLichborneFearGrant(id)).toBe(false);
if (condition.genericControl) expect(useCombatStore.getState().auras.some((aura) => aura.targetId === id
&& aura.definition.id === "enemy-control:separate-enemy:separate-cast:sleep")).toBe(true);
clearMobRuntimeRegistry(); clearPartyRuntimeRegistry(); resetPartyRuntimeClock();
}
});
it.each(["player", "party"] as const)("permits %s casts through source-authorized fear/stun/confuse while preserving unrelated control", (recipient) => {
for (const genericControl of ["fear", "stun", "confuse"] as const) {
for (const standaloneControl of [false, true]) {
const id = prepare({ recipient, genericControl, standaloneControl });
if (recipient === "player") {
const cast = useCombatStore.getState().castAbility(LICHBORNE_ID, { now: NOW });
expect(cast, JSON.stringify(cast)).toMatchObject({ ok: true });
}
else advanceDungeonPartyCombat(NOW);
expect(hasSourceControl(id)).toBe(false);
expect(hasLichborneFearGrant(id)).toBe(true);
expect(useCombatStore.getState().auras.some((aura) => aura.targetId === id
&& aura.definition.id === `enemy-control:separate-enemy:separate-cast:${genericControl}`)).toBe(true);
const actor = recipient === "player" ? useCombatStore.getState() : usePartyStore.getState().members[0]!;
expect(actor.controlledUntil).toBe(4_000);
expect(actor.controlMechanic).toBe(genericControl);
if (recipient === "party") expect(usePartyStore.getState().members[0]!.status).toBe("holding");
clearMobRuntimeRegistry(); clearPartyRuntimeRegistry(); resetPartyRuntimeClock();
}
}
});
it.each(["player", "party"] as const)("preserves the existing %s GCD when source off-GCD Lichborne escapes", (recipient) => {
const id = prepare({ recipient, globalCooldownEndsAt: 10_000 });
if (recipient === "player") {
const cast = useCombatStore.getState().castAbility(LICHBORNE_ID, { now: NOW });
expect(cast, JSON.stringify(cast)).toMatchObject({ ok: true });
}
else advanceDungeonPartyCombat(NOW);
expect(hasSourceControl(id)).toBe(false);
expect(recipient === "player" ? useCombatStore.getState().globalCooldownEndsAt
: usePartyStore.getState().members[0]!.globalCooldownEndsAt).toBe(10_000);
});
});