forked from phenom/healer-man
Update Healer Man server
This commit is contained in:
1 parent
35dfdaed61
commit
4fb7a0b9c8
161 files changed
+24618
-529
No files matched your search
@@ -0,0 +1,104 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { abilitiesForCharacter, type AbilityDefinition } from "./abilityCatalog";
|
||||
import { useCombatStore } from "./combatStore";
|
||||
import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry";
|
||||
import {
|
||||
clearPartyRuntimeRegistry,
|
||||
registerPartyRuntimePosition,
|
||||
updatePartyRuntimeTargetVisibility,
|
||||
} from "./partyRuntimeRegistry";
|
||||
import { advanceDungeonPartyCombat, resetPartyRuntimeClock } from "./partyRuntime";
|
||||
import { createConfiguredPartyMember, usePartyStore } from "./partyStore";
|
||||
|
||||
function playerWhirlwind(state: ReturnType<typeof useCombatStore.getState>): AbilityDefinition {
|
||||
const ability = state.abilities.find((candidate) => candidate.dbcSpellId === 490133);
|
||||
if (!ability) throw new Error("RuneWaker Warrior Whirlwind is missing from Knight/Warrior abilities.");
|
||||
return ability;
|
||||
}
|
||||
|
||||
describe("RuneWaker Knight/Warrior Deadly Whirlwind integration", () => {
|
||||
beforeEach(() => {
|
||||
clearMobRuntimeRegistry();
|
||||
clearPartyRuntimeRegistry();
|
||||
resetPartyRuntimeClock();
|
||||
});
|
||||
|
||||
it("reduces player Whirlwind to 40 Rage and a 10-second cooldown when Deadly Whirlwind is learned", () => {
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId: "rom-knight",
|
||||
secondaryClassId: "rom-warrior",
|
||||
raceId: "rom-human",
|
||||
level: 80,
|
||||
});
|
||||
const initial = useCombatStore.getState();
|
||||
const whirlwind = playerWhirlwind(initial);
|
||||
expect(initial.abilities.find((ability) => ability.dbcSpellId === 491639)).toMatchObject({
|
||||
classId: "rom-knight",
|
||||
secondaryClassId: "rom-warrior",
|
||||
passive: true,
|
||||
});
|
||||
useCombatStore.setState((state) => ({
|
||||
resourcePools: { ...state.resourcePools, rage: 40 },
|
||||
maxResourcePools: { ...state.maxResourcePools, rage: 100 },
|
||||
}));
|
||||
useCombatStore.getState().registerMob("knight-whirlwind-target", {
|
||||
name: "Whirlwind Target",
|
||||
maxHealth: 100_000,
|
||||
xpReward: 0,
|
||||
});
|
||||
setMobPosition("knight-whirlwind-target", [3, 0, 0]);
|
||||
useCombatStore.getState().selectMob("knight-whirlwind-target");
|
||||
|
||||
const result = useCombatStore.getState().castAbility(whirlwind.id, undefined, 1_000);
|
||||
|
||||
expect(result).toMatchObject({ ok: true });
|
||||
const final = useCombatStore.getState();
|
||||
expect(final.resourcePools.rage).toBe(0);
|
||||
expect(final.cooldowns[whirlwind.id]).toBe(11_000);
|
||||
expect(final.combatEvents.some((event) => event.kind === "damage"
|
||||
&& event.abilityId === whirlwind.id && event.targetActorId === "knight-whirlwind-target"
|
||||
&& event.effectiveAmount > 0)).toBe(true);
|
||||
});
|
||||
|
||||
it("uses the learned paired passive for companion Whirlwind cost and cooldown", () => {
|
||||
useCombatStore.getState().initializeCharacter({ classId: "priest", level: 80 });
|
||||
const knightSeed = createConfiguredPartyMember({
|
||||
name: "Knight Warrior",
|
||||
classId: "rom-knight",
|
||||
secondaryClassId: "rom-warrior",
|
||||
specialization: "Primary",
|
||||
role: "damage",
|
||||
}, 80, 0, "deadly-whirlwind-party");
|
||||
const abilities = abilitiesForCharacter("rom-knight", "rom-warrior");
|
||||
const whirlwind = abilities.find((ability) => ability.dbcSpellId === 490133)!;
|
||||
const knight = {
|
||||
...knightSeed,
|
||||
resourcePools: { ...knightSeed.resourcePools, rage: 40 },
|
||||
cooldowns: Object.fromEntries(abilities
|
||||
.filter((ability) => ability.id !== whirlwind.id)
|
||||
.map((ability) => [ability.id, 60_000])),
|
||||
};
|
||||
usePartyStore.setState({ members: [knight], command: "attack" });
|
||||
useCombatStore.getState().synchronizeActors();
|
||||
registerPartyRuntimePosition(knight.id, [0, 0, 0]);
|
||||
useCombatStore.getState().registerMob("party-knight-whirlwind-target", {
|
||||
name: "Party Whirlwind Target",
|
||||
maxHealth: 100_000,
|
||||
xpReward: 0,
|
||||
});
|
||||
// Keep the target inside Whirlwind's area but outside the companion's melee range
|
||||
// so its autoattack cannot add Rage after the discounted spell is spent.
|
||||
setMobPosition("party-knight-whirlwind-target", [10, 0, 0]);
|
||||
useCombatStore.getState().damageMob("party-knight-whirlwind-target", 1, 100);
|
||||
updatePartyRuntimeTargetVisibility(knight.id, "party-knight-whirlwind-target", true);
|
||||
|
||||
advanceDungeonPartyCombat(1_000);
|
||||
|
||||
const finalMember = usePartyStore.getState().members.find((member) => member.id === knight.id)!;
|
||||
expect(finalMember.resourcePools.rage).toBe(0);
|
||||
expect(finalMember.cooldowns[whirlwind.id]).toBe(11_000);
|
||||
expect(useCombatStore.getState().combatEvents.some((event) => event.kind === "damage"
|
||||
&& event.sourceActorId === knight.id && event.abilityId === whirlwind.id
|
||||
&& event.targetActorId === "party-knight-whirlwind-target" && event.effectiveAmount > 0)).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user