forked from phenom/healer-man
87 lines
4.7 KiB
TypeScript
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);
|
|
});
|
|
});
|