Files
healer-man/src/game/partyRuntimeRegistry.test.ts
2026-10-04 13:13:35 -04:00

104 lines
4.6 KiB
TypeScript

import { beforeEach, describe, expect, it } from "vitest";
import {
clearPartyRuntimeRegistry,
clearPartyRuntimeAttackCorridor,
consumePartyRuntimeCombatRelocation,
getPartyRuntimePosition,
partyRuntimeDistanceSquared,
partyRuntimeCenter,
partyRuntimeTargetVisible,
partyRuntimePositionAlongAttackCorridor,
registerPartyRuntimePosition,
requestPartyRuntimeCombatRelocation,
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("consumes combat teleports once without creating a rejoin task", () => {
const origin: [number, number, number] = [1, 2, 3];
registerPartyRuntimePosition("rogue", [0, 0, 0]);
requestPartyRuntimeCombatRelocation("rogue", origin, 1.25);
origin[0] = 999;
expect(getPartyRuntimePosition("rogue")).toEqual([1, 2, 3]);
const relocation = consumePartyRuntimeCombatRelocation("rogue")!;
expect(relocation).toEqual({ position: [1, 2, 3], yaw: 1.25 });
expect(consumePartyRuntimeCombatRelocation("rogue")).toBeNull();
expect(partyRuntimeIsRejoining("rogue")).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);
});
});