import { beforeEach, describe, expect, it } from "vitest"; import { abilityById } from "./abilityCatalog"; import { PLAYER_AGGRO_ID } from "./aggro"; import { useCombatStore } from "./combatStore"; import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry"; import { advanceDungeonPartyCombat, resetPartyRuntimeClock } from "./partyRuntime"; import { clearPartyRuntimeRegistry, registerPartyRuntimePosition, updatePartyRuntimeTargetVisibility, } from "./partyRuntimeRegistry"; import { createConfiguredPartyMember, usePartyStore } from "./partyStore"; import { ROM_MAGE_KNIGHT_LIGHT_OF_DAY, } from "./romMageRuntime"; const TARGET_ID = "light-of-day-target"; const ACTIVE_TAG = "rom-mage-knight-light-of-day-active"; function initializeMageKnight(): void { useCombatStore.getState().initializeCharacter({ classId: "rom-mage", secondaryClassId: "rom-knight", raceId: "rom-human", level: 80, }); } function registerTarget(): void { useCombatStore.getState().registerMob(TARGET_ID, { name: TARGET_ID, maxHealth: 1_000_000, xpReward: 0 }); setMobPosition(TARGET_ID, [3, 0, 0]); useCombatStore.getState().selectMob(TARGET_ID); } function aura(actorId: string, tag: string, now: number) { return useCombatStore.getState().auras.find((entry) => entry.targetId === actorId && (entry.expiresAt === null || entry.expiresAt > now) && entry.definition.utilityTags?.includes(tag)); } describe("RoM Mage/Knight Light of Day", () => { beforeEach(() => { clearMobRuntimeRegistry(); clearPartyRuntimeRegistry(); resetPartyRuntimeClock(); initializeMageKnight(); }); it("stays suppressed at startup, triggers on both named hits, and observes its 20-second ICD", () => { registerTarget(); const holyLightStrike = abilityById("rom-mage-rom-knight-elite-15"); const lightCharge = abilityById("rom-mage-rom-knight-elite-35"); const passive = abilityById("rom-mage-rom-knight-elite-45"); if (!holyLightStrike || !lightCharge || !passive) throw new Error("Missing local Mage/Knight Light of Day rows"); expect([holyLightStrike.dbcSpellId, lightCharge.dbcSpellId]).toEqual([491_594, 492_634]); expect(passive.dbcSpellId).toBe(ROM_MAGE_KNIGHT_LIGHT_OF_DAY.passiveSpellId); expect(passive.effects).toEqual([]); expect(aura(PLAYER_AGGRO_ID, ACTIVE_TAG, 1_000)).toBeUndefined(); expect(useCombatStore.getState().castAbility(holyLightStrike.id, undefined, 1_000)).toMatchObject({ ok: true }); useCombatStore.getState().tick(0.1, 3_100); expect(aura(PLAYER_AGGRO_ID, ACTIVE_TAG, 3_100)?.expiresAt).toBe(18_100); expect(aura(PLAYER_AGGRO_ID, ROM_MAGE_KNIGHT_LIGHT_OF_DAY.cooldownAuraTag, 3_100)?.expiresAt).toBe(23_100); expect(useCombatStore.getState().castAbility(lightCharge.id, undefined, 7_000)).toMatchObject({ ok: true }); expect(aura(PLAYER_AGGRO_ID, ACTIVE_TAG, 7_000)?.expiresAt).toBe(18_100); expect(useCombatStore.getState().castAbility(lightCharge.id, undefined, 24_000)).toMatchObject({ ok: true }); expect(aura(PLAYER_AGGRO_ID, ACTIVE_TAG, 24_000)?.expiresAt).toBe(39_000); expect(aura(PLAYER_AGGRO_ID, ROM_MAGE_KNIGHT_LIGHT_OF_DAY.cooldownAuraTag, 24_000)?.expiresAt).toBe(44_000); }); it("triggers for a landed party Mage/Knight hit with an independent caster ICD", () => { const seed = "light-of-day-party-mage-knight"; const member = createConfiguredPartyMember({ name: "Light of Day Mage", classId: "rom-mage", secondaryClassId: "rom-knight", specialization: "Primary", role: "damage", }, 80, 0, seed); const holyLightStrike = abilityById("rom-mage-rom-knight-elite-15"); if (!holyLightStrike) throw new Error("Missing local Mage/Knight Holy Light Strike row"); usePartyStore.getState().initializeParty(seed, 80, useCombatStore.getState().health, "damage", 2, "rom"); usePartyStore.getState().setCommand("attack"); usePartyStore.setState({ members: [{ ...member, resource: 10_000, maxResource: 10_000, resourcePools: { ...member.resourcePools, mana: 10_000 }, activeCast: { abilityId: holyLightStrike.id, school: "holy", mode: "cast", startedAt: 500, completesAt: 2_500, originalDurationMs: 2_000, pushbackHits: 0, origin: [0, 0, 0], targetId: TARGET_ID, }, activeCastTargetId: TARGET_ID, status: "attacking", }] }); registerPartyRuntimePosition(member.id, [0, 0, 0]); registerTarget(); useCombatStore.getState().damageMob(TARGET_ID, 1, 2_400); updatePartyRuntimeTargetVisibility(member.id, TARGET_ID, true); useCombatStore.getState().synchronizeActors(); advanceDungeonPartyCombat(2_500); expect(useCombatStore.getState().combatEvents.some((event) => event.sourceActorId === member.id && event.abilityId === holyLightStrike.id && event.outcome === "hit")).toBe(true); expect(aura(member.id, ACTIVE_TAG, 2_500)?.expiresAt).toBe(17_500); expect(aura(member.id, ROM_MAGE_KNIGHT_LIGHT_OF_DAY.cooldownAuraTag, 2_500)?.expiresAt).toBe(22_500); expect(aura(PLAYER_AGGRO_ID, ACTIVE_TAG, 2_500)).toBeUndefined(); }); });