Update Healer Man server

This commit is contained in:
phenom committed 2026-10-04 13:13:35 -04:00
1 parent 39458d5de6
commit 6cc1b9217f
203 files changed
+133929 -31271

No files matched your search

+97
View File
@@ -4,6 +4,7 @@ import { applyAura } from "./combatAuras";
import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry";
import {
clearPartyRuntimeRegistry,
getPartyRuntimePosition,
registerPartyRuntimePosition,
updatePartyRuntimeAttackCorridor,
updatePartyRuntimeTargetVisibility,
@@ -1285,6 +1286,102 @@ describe("party combat runtime", () => {
expect(autoAttackTimes).toEqual([2_200, 3_400]);
});
it("runs party Rogue Killing Spree on its own timed window and frozen cast-time targets", () => {
useCombatStore.getState().initializeCharacter({ classId: "mage", role: "damage", level: 80 });
const seed = "killing-spree-party-runtime";
const spree = abilitiesForClass("rogue").find((candidate) => candidate.name === "Killing Spree");
if (!spree) throw new Error("Missing generated Rogue Killing Spree");
const spreeTalentNodeId = abilityTalentNodeId(spree);
if (!spreeTalentNodeId) throw new Error("Rogue Killing Spree has no generated talent node");
const rogue = createConfiguredPartyMember({
name: "Combat Rogue",
classId: "rogue",
specialization: "Combat",
specializationId: "rogue:combat",
talentTreeId: "rogue-combat",
talentRanks: { [spreeTalentNodeId]: 1 },
role: "damage",
}, 80, 0, seed);
usePartyStore.getState().initializeParty(seed, 80, useCombatStore.getState().health, "damage", 2);
usePartyStore.setState({
members: [{ ...rogue, nextActionAt: 0 }],
command: "attack",
activeMobId: "party-spree:a",
});
registerPartyRuntimePosition(rogue.id, [0, 0, 0]);
useCombatStore.getState().synchronizeActors();
registerEngagedMob("party-spree:a", 1_000_000);
registerEngagedMob("party-spree:b", 1_000_000);
useCombatStore.getState().selectMob("party-spree:a");
usePartyStore.setState({ activeMobId: "party-spree:a", command: "attack" });
setMobPosition("party-spree:b", [3, 0, 0]);
updatePartyRuntimeTargetVisibility(rogue.id, "party-spree:b", true);
updatePartyRuntimeTargetVisibility(rogue.id, "party-spree:a", true);
advanceDungeonPartyCombat(1_000);
const window = useCombatStore.getState().killingSpreeWindows.find((candidate) => candidate.ownerActorId === rogue.id);
expect(window).toMatchObject({
ownerActorId: rogue.id,
abilityId: spree.id,
role: "damage",
targetIds: ["party-spree:a", "party-spree:b"],
startedAt: 1_000,
nextAssaultAt: 1_000,
completedAssaults: 0,
});
expect(useCombatStore.getState().auras).toContainEqual(expect.objectContaining({
sourceId: rogue.id,
targetId: rogue.id,
expiresAt: 3_001,
definition: expect.objectContaining({
utilityTags: ["wow335-rogue-killing-spree"],
modifiers: [{ kind: "damage", direction: "dealt", operation: "percent", value: 0.2 }],
}),
}));
expect(usePartyStore.getState().members[0]?.cooldowns[spree.id]).toBeGreaterThan(1_000);
registerEngagedMob("party-spree:c", 1_000_000);
setMobPosition("party-spree:c", [2, 0, 2]);
updatePartyRuntimeTargetVisibility(rogue.id, "party-spree:c", true);
useCombatStore.getState().advanceMobCombat(1_000);
const firstPulse = useCombatStore.getState().combatEvents.filter((event) => (
event.kind === "damage" && event.sourceActorId === rogue.id && event.abilityId === spree.id
));
expect(firstPulse.length).toBeGreaterThan(0);
expect(firstPulse.every((event) => event.targetActorId !== "party-spree:c")).toBe(true);
expect(getPartyRuntimePosition(rogue.id)).not.toEqual([0, 0, 0]);
for (const now of [1_499, 1_500, 2_000, 2_500, 3_000]) {
useCombatStore.getState().advanceMobCombat(now);
}
const strikes = useCombatStore.getState().combatEvents.filter((event) => (
event.kind === "damage" && event.sourceActorId === rogue.id && event.abilityId === spree.id
));
expect([...new Set(strikes.map((event) => event.occurredAt))]).toEqual([1_000, 1_500, 2_000, 2_500, 3_000]);
expect(strikes.every((event) => event.targetActorId !== "party-spree:c")).toBe(true);
expect(strikes.filter((event) => event.occurredAt === 3_000).map((event) => event.rawAmount))
.toEqual(window!.hits.map((hit) => Math.round(hit.damage * 1.2)));
expect(useCombatStore.getState().killingSpreeWindows).toHaveLength(0);
expect(useCombatStore.getState().auras.some((aura) => aura.sourceId === rogue.id
&& aura.targetId === rogue.id
&& aura.definition.utilityTags?.includes("wow335-rogue-killing-spree"))).toBe(false);
expect(useCombatStore.getState().beginKillingSpree(
rogue.id,
spree,
["party-spree:a"],
[],
"damage",
3_001,
)).toBe(true);
expect(useCombatStore.getState().killingSpreeWindows).toHaveLength(1);
usePartyStore.setState({ members: [] });
useCombatStore.getState().advanceMobCombat(3_001);
expect(useCombatStore.getState().killingSpreeWindows).toHaveLength(0);
expect(useCombatStore.getState().auras.some((aura) => aura.sourceId === rogue.id
&& aura.definition.utilityTags?.includes("wow335-rogue-killing-spree"))).toBe(false);
});
it("precasts Earth Shield on the tank while the Restoration Shaman is at full health", () => {
useCombatStore.getState().initializeCharacter({ classId: "mage", role: "damage", level: 80 });
const tank = createConfiguredPartyMember({