import { beforeEach, describe, expect, it } from "vitest"; import { clearPartyRuntimeRegistry, clearPartyRuntimeAttackCorridor, getPartyRuntimePosition, partyRuntimeDistanceSquared, partyRuntimeCenter, partyRuntimeTargetVisible, partyRuntimePositionAlongAttackCorridor, registerPartyRuntimePosition, updatePartyRuntimeTargetVisibility, updatePartyRuntimeAttackCorridor, updatePartyRuntimePosition, beginPartyRuntimeRejoin, consumePartyRuntimeRejoin, partyRuntimeIsRejoining, finishPartyRuntimeRejoin, } from "./partyRuntimeRegistry"; describe("party runtime position registry", () => { beforeEach(clearPartyRuntimeRegistry); it("consumes a copied relocation once and keeps the individual return task until completed", () => { const origin: [number, number, number] = [1, 2, 3]; beginPartyRuntimeRejoin("tank", origin); origin[0] = 999; const result = consumePartyRuntimeRejoin("tank")! as [number, number, number]; expect(result).toEqual([1, 2, 3]); result[0] = 888; expect(getPartyRuntimePosition("tank")).toEqual([1, 2, 3]); expect(consumePartyRuntimeRejoin("tank")).toBeNull(); expect(partyRuntimeIsRejoining("tank")).toBe(true); expect(partyRuntimeIsRejoining("healer")).toBe(false); finishPartyRuntimeRejoin("tank"); expect(partyRuntimeIsRejoining("tank")).toBe(false); }); it("clears pending resurrection tasks on actor unmount and dungeon switch", () => { const unregister = registerPartyRuntimePosition("tank", [0, 0, 0]); beginPartyRuntimeRejoin("tank", [1, 0, 0]); unregister(); expect(partyRuntimeIsRejoining("tank")).toBe(false); beginPartyRuntimeRejoin("tank", [1, 0, 0]); clearPartyRuntimeRegistry(); expect(consumePartyRuntimeRejoin("tank")).toBeNull(); expect(partyRuntimeIsRejoining("tank")).toBe(false); }); it("tracks world actors without exposing mutable registry storage", () => { const unregister = registerPartyRuntimePosition("tank", [1, 2, 3]); updatePartyRuntimePosition("tank", 4, 5, 6); const snapshot = getPartyRuntimePosition("tank")! as [number, number, number]; snapshot[0] = 999; expect(getPartyRuntimePosition("tank")).toEqual([4, 5, 6]); unregister(); expect(getPartyRuntimePosition("tank")).toBeNull(); }); it("computes a party center and uses a finite fallback before actors mount", () => { registerPartyRuntimePosition("a", [0, 2, 4]); registerPartyRuntimePosition("b", [2, 4, 6]); expect(partyRuntimeCenter(["a", "b"], [9, 9, 9])).toEqual([1, 3, 5]); expect(partyRuntimeCenter(["missing"], [9, 8, 7])).toEqual([9, 8, 7]); }); it("measures distance to registered members and rejects missing positions", () => { registerPartyRuntimePosition("party-a", [3, 4, 0]); expect(partyRuntimeDistanceSquared("party-a", [0, 0, 0])).toBe(25); expect(partyRuntimeDistanceSquared("missing", [0, 0, 0])).toBe(Number.POSITIVE_INFINITY); }); it("scopes collision-checked target visibility to the current target", () => { registerPartyRuntimePosition("party-a", [0, 0, 0]); updatePartyRuntimeTargetVisibility("party-a", "mob-a", true); expect(partyRuntimeTargetVisible("party-a", "mob-a")).toBe(true); expect(partyRuntimeTargetVisible("party-a", "mob-b")).toBe(false); updatePartyRuntimeTargetVisibility("party-a", "mob-a", false); expect(partyRuntimeTargetVisible("party-a", "mob-a")).toBe(false); }); it("keeps idle add acquisition on the remaining route and current floor", () => { updatePartyRuntimeAttackCorridor([0, 0, 0], [[10, 0, 0], [10, 0, 10]], 0); expect(partyRuntimePositionAlongAttackCorridor([6, 0, 4], 5)).toBe(true); expect(partyRuntimePositionAlongAttackCorridor([6, 8, 4], 5)).toBe(false); expect(partyRuntimePositionAlongAttackCorridor([-8, 0, 0], 5)).toBe(false); clearPartyRuntimeAttackCorridor(); expect(partyRuntimePositionAlongAttackCorridor([6, 0, 4], 5)).toBe(false); }); });