import { beforeEach, describe, expect, it } from "vitest"; import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry"; import { clearPartyRuntimeRegistry } from "./partyRuntimeRegistry"; import { PLAYER_AGGRO_ID } from "./aggro"; import { useCombatStore } from "./combatStore"; import { usePartyStore } from "./partyStore"; import { abilityById } from "./abilityCatalog"; function registerFrozenTestTarget(id: string, now: number, frozen: boolean): void { useCombatStore.getState().registerMob(id, { name: id, maxHealth: 100_000, xpReward: 0 }); setMobPosition(id, [8, 0, 0]); if (!frozen) return; useCombatStore.setState((state) => { const target = state.mobs[id]!; return { mobs: { ...state.mobs, [id]: { ...target, statuses: [{ kind: "freeze", sourceAbilityId: "shatter-test-freeze", endsAt: now + 5_000 }], }, }, }; }); } function spellCritPercent(targetId: string, abilityId: string, now: number, periodic = false, sourceActorId?: string): number { const member = sourceActorId ? usePartyStore.getState().members.find((candidate) => candidate.id === sourceActorId) : null; useCombatStore.getState().damageMob(targetId, 100, now, sourceActorId && member ? { actorId: sourceActorId, role: member.role, abilityId, } : { actorId: PLAYER_AGGRO_ID, role: "damage", abilityId }, "frost", { attackKind: "spell", seed: `mage-shatter-${sourceActorId ?? "player"}-${targetId}-${now}`, abilityId, periodic, rules: { canMiss: false, canCrit: true, canDodge: false, canParry: false, canBlock: false }, }); return useCombatStore.getState().lastCombatResult!.attackTable.criticalPercent; } describe("WoW Mage Shatter combat integration", () => { beforeEach(() => { clearMobRuntimeRegistry(); clearPartyRuntimeRegistry(); useCombatStore.getState().initializeCharacter({ classId: "mage", level: 80, xp: 0 }); usePartyStore.getState().initializeParty("mage-shatter-tests", 1, useCombatStore.getState().health); }); it("adds the generated rank bonus only for nonperiodic Mage spells against a frozen target", () => { const fireball = useCombatStore.getState().abilities.find((ability) => ability.name === "Fireball")!; useCombatStore.setState((state) => ({ talentRanks: { ...state.talentRanks, "talent-67": 3 }, })); registerFrozenTestTarget("shatter-unfrozen", 1_000, false); const unfrozenCrit = spellCritPercent("shatter-unfrozen", fireball.id, 1_000); registerFrozenTestTarget("shatter-frozen", 2_000, true); const frozenCrit = spellCritPercent("shatter-frozen", fireball.id, 2_000); const periodicCrit = spellCritPercent("shatter-frozen", fireball.id, 3_000, true); expect(frozenCrit - unfrozenCrit).toBe(50); expect(periodicCrit).toBe(unfrozenCrit); }); it("uses the party Mage's own Shatter rank against frozen targets", () => { const fireball = abilityById("wow335-mage-fireball")!; const member = usePartyStore.getState().members[0]!; usePartyStore.setState((state) => ({ members: state.members.map((candidate) => candidate.id === member.id ? { ...candidate, classId: "mage", talentRanks: {}, resourceType: "mana", resourceName: "Mana", maxResource: 10_000, resource: 9_000, resourcePools: { ...candidate.resourcePools, mana: 9_000 }, } : candidate), })); registerFrozenTestTarget("party-shatter-frozen", 1_000, true); const baselineCrit = spellCritPercent("party-shatter-frozen", fireball.id, 1_000, false, member.id); usePartyStore.setState((state) => ({ members: state.members.map((candidate) => candidate.id === member.id ? { ...candidate, talentRanks: { "talent-67": 2 }, } : candidate), })); const partyCrit = spellCritPercent("party-shatter-frozen", fireball.id, 2_000, false, member.id); expect(partyCrit - baselineCrit).toBe(34); }); });