import { beforeEach, describe, expect, it } from "vitest"; import { abilityTalentNodeId } from "./abilityCatalog"; import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry"; import { clearPartyRuntimeRegistry } from "./partyRuntimeRegistry"; import { PLAYER_AURA_ENTITY_ID, useCombatStore } from "./combatStore"; import { usePartyStore } from "./partyStore"; function unlockShamanTalent(abilityName: string): string { const ability = useCombatStore.getState().abilities.find((candidate) => candidate.name === abilityName); expect(ability, abilityName).toBeDefined(); const nodeId = abilityTalentNodeId(ability!); expect(nodeId, abilityName).toBeTruthy(); useCombatStore.setState((state) => ({ talentRanks: { ...state.talentRanks, [nodeId!]: 1 } })); return ability!.id; } describe("WoW Shaman Shamanistic Rage source proc", () => { beforeEach(() => { clearMobRuntimeRegistry(); clearPartyRuntimeRegistry(); useCombatStore.getState().initializeCharacter({ classId: "shaman", level: 80, xp: 0 }); useCombatStore.getState().resetSettings(); useCombatStore.getState().setPlayerPosition([0, 0, 0]); usePartyStore.getState().initializeParty("wow-shamanistic-rage-runtime-tests", 1, useCombatStore.getState().health); useCombatStore.getState().registerMob("shamanistic-rage-target", { name: "Shamanistic Rage target", maxHealth: 100_000, xpReward: 0 }); setMobPosition("shamanistic-rage-target", [2, 0, 0]); useCombatStore.getState().selectMob("shamanistic-rage-target"); }); it("restores 15% attack power as mana on landed melee hits for the 15-second aura window", () => { const rageId = unlockShamanTalent("Shamanistic Rage"); const rageAbility = useCombatStore.getState().abilities.find((candidate) => candidate.id === rageId); expect(rageAbility?.dbcSpellId).toBe(30823); useCombatStore.setState((state) => ({ resource: 300, resourcePools: { ...state.resourcePools, mana: 300 }, })); const rageCast = useCombatStore.getState().castAbility(rageId, undefined, 1_000); expect(rageCast.ok).toBe(true); const aura = useCombatStore.getState().auras.find((candidate) => ( candidate.targetId === PLAYER_AURA_ENTITY_ID && candidate.definition.procs?.some((proc) => proc.action.kind === "custom" && proc.action.id === "wow335-shamanistic-rage-mana") )); expect(aura).toBeDefined(); expect(aura?.expiresAt).toBe(16_000); for (let now = 3_000; now < 16_000; now += 2_500) { useCombatStore.setState({ autoAttackTargetId: "shamanistic-rage-target", nextAutoAttackAt: now }); useCombatStore.getState().tick(0.1, now); if (useCombatStore.getState().combatEvents.some((event) => ( event.kind === "resource" && event.abilityId === rageId && event.effectiveAmount > 0 ))) break; } const afterMelee = useCombatStore.getState(); expect(afterMelee.resourcePools.mana).toBeGreaterThan(0); expect(afterMelee.combatEvents.some((event) => ( event.kind === "resource" && event.abilityId === rageId && event.effectiveAmount > 0 ))).toBe(true); const rageRestoresBeforeExpiry = afterMelee.combatEvents.filter((event) => ( event.kind === "resource" && event.abilityId === rageId )).length; useCombatStore.setState({ autoAttackTargetId: "shamanistic-rage-target", nextAutoAttackAt: 20_000, }); useCombatStore.getState().tick(0.1, 20_000); expect(useCombatStore.getState().auras.some((entry) => entry.instanceId === aura?.instanceId)).toBe(false); expect(useCombatStore.getState().combatEvents.filter((event) => ( event.kind === "resource" && event.abilityId === rageId ))).toHaveLength(rageRestoresBeforeExpiry); }); });