import { beforeEach, describe, expect, it } from "vitest"; import { abilitiesForCharacter, type AbilityDefinition } from "./abilityCatalog"; import { useCombatStore } from "./combatStore"; import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry"; import { commitPartyAbilityUse, partyAbilityCosts } from "./partyAbilityAi"; import { createConfiguredPartyMember, usePartyStore } from "./partyStore"; import { clearPartyRuntimeRegistry } from "./partyRuntimeRegistry"; import { resetPartyRuntimeClock } from "./partyRuntime"; function whirlwindFrom(abilities: readonly AbilityDefinition[]): AbilityDefinition { const ability = abilities.find((candidate) => candidate.dbcSpellId === 490133); if (!ability) throw new Error("RuneWaker general Whirlwind is missing from Warrior abilities."); return ability; } describe("Warrior/Druid Rage of Calmness combat integration", () => { beforeEach(() => { clearMobRuntimeRegistry(); clearPartyRuntimeRegistry(); resetPartyRuntimeClock(); usePartyStore.setState({ members: [] }); }); it("reduces player Warrior/Druid Whirlwind from 50 to 40 Rage and from 30s to 20s cooldown", () => { useCombatStore.getState().initializeCharacter({ classId: "rom-warrior", secondaryClassId: "rom-druid", raceId: "rom-human", level: 80, }); const initial = useCombatStore.getState(); const whirlwind = whirlwindFrom(initial.abilities); expect(initial.abilities.find((ability) => ability.dbcSpellId === 494377)).toMatchObject({ classId: "rom-warrior", secondaryClassId: "rom-druid", name: "Rage of Calmness", passive: true, }); useCombatStore.setState((state) => ({ resource: 40, resourcePools: { ...state.resourcePools, rage: 40 }, maxResourcePools: { ...state.maxResourcePools, rage: 100 }, })); useCombatStore.getState().registerMob("rage-of-calmness-player-target", { name: "Rage of Calmness Target", maxHealth: 100_000, xpReward: 0, }); setMobPosition("rage-of-calmness-player-target", [3, 0, 0]); useCombatStore.getState().selectMob("rage-of-calmness-player-target"); const result = useCombatStore.getState().castAbility(whirlwind.id, undefined, 1_000); expect(result, JSON.stringify(result)).toMatchObject({ ok: true }); expect(useCombatStore.getState().resourcePools.rage).toBe(0); expect(useCombatStore.getState().cooldowns[whirlwind.id]).toBe(21_000); }); it("reduces generated Warrior/Druid companion Whirlwind cost and committed cooldown", () => { useCombatStore.getState().initializeCharacter({ classId: "priest", level: 80 }); const seed = createConfiguredPartyMember({ name: "Warrior Druid", classId: "rom-warrior", secondaryClassId: "rom-druid", specialization: "Primary", role: "damage", }, 80, 0, "rage-of-calmness-party"); const abilities = abilitiesForCharacter("rom-warrior", "rom-druid"); const whirlwind = whirlwindFrom(abilities); const member = { ...seed, resource: 40, resourcePools: { ...seed.resourcePools, rage: 40 }, cooldowns: {}, }; expect(partyAbilityCosts(member, whirlwind)).toEqual([{ resource: "rage", amount: 40 }]); const committed = commitPartyAbilityUse(member, whirlwind, 1_000); expect(committed?.resourcePools.rage).toBe(0); expect(committed?.cooldowns[whirlwind.id]).toBe(21_000); }); it("leaves local Whirlwind unchanged for a Warrior without the Druid pairing", () => { useCombatStore.getState().initializeCharacter({ classId: "rom-warrior", level: 80 }); const abilities = useCombatStore.getState().abilities; const whirlwind = whirlwindFrom(abilities); useCombatStore.setState((state) => ({ resource: 50, resourcePools: { ...state.resourcePools, rage: 50 }, maxResourcePools: { ...state.maxResourcePools, rage: 100 }, })); useCombatStore.getState().registerMob("rage-of-calmness-solo-target", { name: "Solo Warrior Target", maxHealth: 100_000, xpReward: 0, }); setMobPosition("rage-of-calmness-solo-target", [3, 0, 0]); useCombatStore.getState().selectMob("rage-of-calmness-solo-target"); const result = useCombatStore.getState().castAbility(whirlwind.id, undefined, 1_000); expect(result, JSON.stringify(result)).toMatchObject({ ok: true }); expect(useCombatStore.getState().resourcePools.rage).toBe(0); expect(useCombatStore.getState().cooldowns[whirlwind.id]).toBe(31_000); }); });