import { beforeEach, describe, expect, it } from "vitest"; import { PLAYER_AGGRO_ID } from "./aggro"; import { useCombatStore } from "./combatStore"; import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry"; import { usePartyStore } from "./partyStore"; import { EMPTY_ITEM_STATS } from "./itemStats"; const SMASH_ID = "rom-knight-rom-rogue-elite-20"; function registerTarget(): void { useCombatStore.getState().registerMob("smash-target", { name: "Smash target", maxHealth: 1_000_000, xpReward: 0, }); setMobPosition("smash-target", [2, 0, 0]); useCombatStore.getState().selectMob("smash-target"); } function playerDamageEvent(critical: boolean, id: number, sourceActorId = PLAYER_AGGRO_ID) { return { id, occurredAt: id * 1_000, kind: "damage" as const, sourceActorId, targetActorId: "smash-target", abilityId: "rom-knight-primary-1", school: "holy" as const, rawAmount: 100, effectiveAmount: 100, overheal: 0, absorbed: 0, critical, outcome: critical ? "crit" : "hit", }; } describe("RuneWaker Smash prerequisite", () => { beforeEach(() => { clearMobRuntimeRegistry(); useCombatStore.getState().initializeCharacter({ classId: "rom-knight", secondaryClassId: "rom-rogue", raceId: "rom-human", level: 80, xp: 0, }); useCombatStore.getState().setPlayerPosition([0, 0, 0]); usePartyStore.getState().initializeParty("rom-smash-test", 80, useCombatStore.getState().health); registerTarget(); }); it("rejects Smash without a preceding player critical damage event without spending health or cooldown", () => { const before = useCombatStore.getState(); const result = before.castAbility(SMASH_ID, undefined, 1_000); expect(result).toMatchObject({ ok: false, reason: "condition-not-met", abilityId: SMASH_ID }); expect(useCombatStore.getState().health).toBe(before.health); expect(useCombatStore.getState().cooldowns[SMASH_ID] ?? 0).toBe(0); expect(useCombatStore.getState().mobs["smash-target"]?.health).toBe(1_000_000); }); it("allows Smash after a real player critical hit and ignores the later cast event", () => { useCombatStore.setState({ gearStats: { ...EMPTY_ITEM_STATS, criticalStrikeRating: 1_000_000, hitRating: 1_000_000 }, }); const holySeal = useCombatStore.getState().abilities.find((ability) => ability.id === "rom-knight-primary-1"); expect(holySeal).toBeDefined(); expect(useCombatStore.getState().castAbility(holySeal!.id, undefined, 2_000).ok).toBe(true); expect(useCombatStore.getState().combatEvents.some((event) => ( event.kind === "damage" && event.sourceActorId === PLAYER_AGGRO_ID && event.critical && event.effectiveAmount > 0 ))).toBe(true); const result = useCombatStore.getState().castAbility(SMASH_ID, undefined, 4_000); expect(result).toMatchObject({ ok: true, abilityId: SMASH_ID }); expect(useCombatStore.getState().cooldowns[SMASH_ID]).toBe(9_000); }); it("requires the latest player damage event to be a landed crit", () => { useCombatStore.setState({ combatEvents: [ playerDamageEvent(true, 1), playerDamageEvent(false, 2), ] }); expect(useCombatStore.getState().castAbility(SMASH_ID, undefined, 3_000)) .toMatchObject({ ok: false, reason: "condition-not-met" }); useCombatStore.setState({ combatEvents: [playerDamageEvent(true, 1, "party-member")] }); expect(useCombatStore.getState().castAbility(SMASH_ID, undefined, 3_000)) .toMatchObject({ ok: false, reason: "condition-not-met" }); }); });