import { beforeEach, describe, expect, it } from "vitest"; import { resolveDamageValue, resolveHealingValue } from "./combatAuras"; import { abilityById } from "./abilityCatalog"; import { useCombatStore } from "./combatStore"; import { clearPartyRuntimeRegistry, registerPartyRuntimePosition } from "./partyRuntimeRegistry"; import { advanceDungeonPartyCombat, resetPartyRuntimeClock } from "./partyRuntime"; import { PLAYER_AGGRO_ID } from "./aggro"; import { createConfiguredPartyMember, usePartyStore } from "./partyStore"; import { clearMobRuntimeRegistry } from "./mobRuntimeRegistry"; const SOUL_ENLIGHTENMENT_ID = "rom-warlock-rom-priest-elite-40"; const SOUL_ENLIGHTENMENT_AURA_ID = "rom-warlock-priest-soul-enlightenment-498649"; function activeCast(abilityId: string, targetId: string, at: number) { return { abilityId, school: "shadow" as const, mode: "cast" as const, startedAt: at, completesAt: at, originalDurationMs: 0, pushbackHits: 0, origin: [0, 0, 0] as const, targetId, }; } describe("CoA Warlock/Priest Soul Enlightenment integration", () => { beforeEach(() => { clearMobRuntimeRegistry(); clearPartyRuntimeRegistry(); resetPartyRuntimeClock(); useCombatStore.getState().initializeCharacter({ classId: "rom-warlock", secondaryClassId: "rom-priest", raceId: "rom-human", level: 80, }); useCombatStore.setState((state) => ({ resourcePools: { ...state.resourcePools, psi: 6 }, maxResourcePools: { ...state.maxResourcePools, psi: 6 }, })); usePartyStore.getState().initializeParty( "rom-warlock-priest-soul-enlightenment", 80, useCombatStore.getState().health, "damage", 2, "rom", ); }); it("casts for the player, pays Psi, and applies the radius-limited modifiers", () => { const seed = "rom-warlock-priest-soul-enlightenment-player"; const inside = createConfiguredPartyMember({ name: "Inside recipient", classId: "rom-warrior", raceId: "rom-human", specialization: "Primary", role: "damage", }, 80, 0, seed); const outside = createConfiguredPartyMember({ name: "Outside recipient", classId: "rom-priest", raceId: "rom-human", specialization: "Primary", role: "healer", }, 80, 1, seed); usePartyStore.setState({ members: [inside, outside] }); registerPartyRuntimePosition(inside.id, [7.5, 0, 0]); registerPartyRuntimePosition(outside.id, [7.51, 0, 0]); useCombatStore.getState().synchronizeActors(); const ability = abilityById(SOUL_ENLIGHTENMENT_ID)!; expect(useCombatStore.getState().castAbility(ability.id, { now: 1_000 })).toMatchObject({ ok: true }); expect(useCombatStore.getState().resourcePools.psi).toBe(4); const auras = useCombatStore.getState().auras; const casterAura = auras.find((aura) => aura.sourceId === PLAYER_AGGRO_ID && aura.targetId === PLAYER_AGGRO_ID && aura.definition.id === SOUL_ENLIGHTENMENT_AURA_ID); expect(casterAura?.expiresAt).toBe(31_000); expect(auras.some((aura) => aura.sourceId === PLAYER_AGGRO_ID && aura.targetId === inside.id && aura.definition.id === SOUL_ENLIGHTENMENT_AURA_ID)).toBe(true); expect(auras.some((aura) => aura.sourceId === PLAYER_AGGRO_ID && aura.targetId === outside.id && aura.definition.id === SOUL_ENLIGHTENMENT_AURA_ID)).toBe(false); expect(resolveDamageValue(1_000, [casterAura!], { direction: "dealt", school: "physical", attackKind: "melee", })).toBe(1_057); expect(resolveHealingValue(1_000, [casterAura!], "taken")).toBe(1_171); expect(casterAura?.definition.modifiers?.some((modifier) => modifier.kind === "stat" && modifier.stat === "maximum-health")).toBe(false); }); it("uses the existing companion party-cast route and applies the same area aura", () => { const seed = "rom-warlock-priest-soul-enlightenment-companion"; const caster = createConfiguredPartyMember({ name: "Warlock/Priest Companion", classId: "rom-warlock", secondaryClassId: "rom-priest", raceId: "rom-dwarf", specialization: "Warlock/Priest", role: "damage", }, 80, 0, seed); const inside = createConfiguredPartyMember({ name: "Inside companion recipient", classId: "rom-warrior", raceId: "rom-human", specialization: "Primary", role: "damage", }, 80, 1, seed); const outside = createConfiguredPartyMember({ name: "Outside companion recipient", classId: "rom-priest", raceId: "rom-human", specialization: "Primary", role: "healer", }, 80, 2, seed); const soulEnlightenment = abilityById(SOUL_ENLIGHTENMENT_ID)!; usePartyStore.setState({ command: "stop", members: [{ ...caster, resourcePools: { ...caster.resourcePools, psi: 6 }, activeCast: activeCast(soulEnlightenment.id, caster.id, 1_000), activeCastTargetId: caster.id, status: "attacking", }, inside, outside], }); registerPartyRuntimePosition(caster.id, [0, 0, 0]); registerPartyRuntimePosition(inside.id, [7.5, 0, 0]); registerPartyRuntimePosition(outside.id, [7.51, 0, 0]); useCombatStore.getState().synchronizeActors(); advanceDungeonPartyCombat(1_000); const auras = useCombatStore.getState().auras; const casterAura = auras.find((aura) => aura.sourceId === caster.id && aura.targetId === caster.id && aura.definition.id === SOUL_ENLIGHTENMENT_AURA_ID); expect(casterAura?.expiresAt).toBe(31_000); expect(auras.some((aura) => aura.sourceId === caster.id && aura.targetId === inside.id && aura.definition.id === SOUL_ENLIGHTENMENT_AURA_ID)).toBe(true); expect(auras.some((aura) => aura.sourceId === caster.id && aura.targetId === outside.id && aura.definition.id === SOUL_ENLIGHTENMENT_AURA_ID)).toBe(false); expect(resolveDamageValue(1_000, [casterAura!], { direction: "dealt", school: "shadow", attackKind: "spell", })).toBe(1_057); expect(resolveHealingValue(1_000, [casterAura!], "taken")).toBe(1_171); }); });