Files
healer-man/src/game/playerPacifyAutoAttack.integration.test.ts
T
2026-10-07 13:05:04 -04:00

87 lines
4.7 KiB
TypeScript

import { afterEach, beforeAll, describe, expect, it } from "vitest";
import { preloadAbilityCatalogsForClasses } from "./abilityCatalog";
import { PLAYER_AGGRO_ID } from "./aggro";
import { applyAura } from "./combatAuras";
import { useCombatStore } from "./combatStore";
import { registerCombatSpatialProvider, resetCombatSpatialProvider } from "./combatSpatial";
import { clearMobRuntimeRegistry, registerMobRuntime } from "./mobRuntimeRegistry";
import { clearPartyRuntimeRegistry } from "./partyRuntimeRegistry";
import { usePartyStore } from "./partyStore";
import { ROM_SCOUT_AUTO_SHOT_ABILITY_ID } from "./romScoutAutoShot";
const TARGET = "pacify-weapon-swing-fixture";
const ORIGIN = [0, 0, 0] as const;
const FRONT = [0, 0, -2] as const;
function prepare(pacified: boolean, targetId = PLAYER_AGGRO_ID) {
useCombatStore.getState().initializeCharacter({ classId: "warrior", level: 30 });
usePartyStore.setState({ members: [] });
useCombatStore.getState().setPlayerPosition(ORIGIN);
useCombatStore.getState().registerMob(TARGET, { name: "Synthetic autoattack target",
level: 1, maxHealth: 1_000_000, xpReward: 0 });
registerMobRuntime(TARGET, FRONT, 0);
registerCombatSpatialProvider({ actor: (id) => id === TARGET
? { id, position: FRONT, yaw: 0 } : { id, position: ORIGIN, yaw: Math.PI },
hasLineOfSight: () => true, projectGroundPoint: (position) => position });
useCombatStore.setState((state) => ({ autoAttackTargetId: TARGET,
nextAutoAttackAt: 1_000, nextOffhandAutoAttackAt: 1_000,
auras: pacified ? [...applyAura(state.auras, { definition: {
id: "synthetic-source-pacify", name: "Accepted pacify aura fixture",
disposition: "buff", durationMs: 2_000, utilityTags: ["pacified"],
}, sourceId: targetId, targetId, now: 1_000 }).auras] : state.auras,
}));
}
function playerSwings() {
return useCombatStore.getState().combatEvents.filter((event) => event.sourceActorId === PLAYER_AGGRO_ID
&& event.targetActorId === TARGET && event.attackHand === "mainhand");
}
describe("accepted pacify aura suppresses player weapon autos", () => {
beforeAll(async () => { await preloadAbilityCatalogsForClasses(["warrior", "rom-scout", "rom-rogue"]); });
afterEach(() => { clearMobRuntimeRegistry(); clearPartyRuntimeRegistry(); resetCombatSpatialProvider(); });
it("holds due swings through the whole aura window and resumes once at expiry", () => {
prepare(true);
for (const now of [1_000, 1_500, 2_999]) useCombatStore.getState().tick(0.05, now);
expect(playerSwings()).toHaveLength(0);
expect(useCombatStore.getState().mobs[TARGET]!.health).toBe(1_000_000);
expect(useCombatStore.getState().autoAttackTargetId).toBe(TARGET);
useCombatStore.getState().tick(0.05, 3_000);
expect(playerSwings()).toHaveLength(1);
expect(useCombatStore.getState().nextAutoAttackAt).toBeGreaterThan(3_000);
useCombatStore.getState().tick(0.05, 3_001);
expect(playerSwings()).toHaveLength(1);
});
it("does not borrow another actor's pacify aura", () => {
prepare(true, "another-actor");
useCombatStore.getState().tick(0.05, 1_000);
expect(playerSwings()).toHaveLength(1);
});
it("also holds the owned Scout ranged-auto scheduler until pacify expires", () => {
prepare(false);
useCombatStore.getState().initializeCharacter({ classId: "rom-scout", secondaryClassId: "rom-rogue",
raceId: "rom-human", statRuleset: "rom", level: 30, inventory: [], equipment: {} });
useCombatStore.getState().registerMob(TARGET, { name: "Synthetic ranged auto target",
level: 1, maxHealth: 1_000_000, xpReward: 0 });
useCombatStore.getState().setPlayerPosition(ORIGIN);
// Synthetic already-selected ranged mode; the real native scheduler and
// automatic Training Gear profile resolve subsequent weapon results.
useCombatStore.setState((state) => ({ autoShotTargetId: TARGET, nextAutoShotAt: 1_000,
auras: [...applyAura(state.auras, { definition: {
id: "synthetic-ranged-pacify", name: "Accepted ranged pacify fixture",
disposition: "buff", durationMs: 2_000, utilityTags: ["pacified"],
}, sourceId: PLAYER_AGGRO_ID, targetId: PLAYER_AGGRO_ID, now: 1_000 }).auras],
}));
const rangedEvents = () => useCombatStore.getState().combatEvents.filter((event) => event.sourceActorId === PLAYER_AGGRO_ID
&& event.abilityId === ROM_SCOUT_AUTO_SHOT_ABILITY_ID && event.attackHand === "ranged");
for (const now of [1_000, 1_500, 2_999]) useCombatStore.getState().tick(0.05, now);
expect(rangedEvents()).toHaveLength(0);
useCombatStore.getState().tick(0.05, 3_000);
expect(rangedEvents()).toHaveLength(1);
useCombatStore.getState().tick(0.05, 3_001);
expect(rangedEvents()).toHaveLength(1);
});
});