126 lines
5.6 KiB
TypeScript
126 lines
5.6 KiB
TypeScript
import { beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
import { abilitiesForCharacter, learnedAbilityForBuild, preloadAbilityCatalogsForClasses } from "./abilityCatalog";
|
|
import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry";
|
|
import { clearPartyRuntimeRegistry, registerPartyRuntimePosition, updatePartyRuntimeTargetVisibility } from "./partyRuntimeRegistry";
|
|
import { advanceDungeonPartyCombat, resetPartyRuntimeClock } from "./partyRuntime";
|
|
import { useCombatStore } from "./combatStore";
|
|
import { createConfiguredPartyMember, usePartyStore } from "./partyStore";
|
|
import { ROM_COA_WARLOCK_WARRIOR_ENRAGED } from "./romWarlockWarriorEnragedCoaMechanics";
|
|
|
|
const ABILITY_ID = "rom-warlock-rom-warrior-general-coa-499871";
|
|
const TARGET_ID = "rom-warlock-warrior-enraged-target";
|
|
|
|
function enragedAbility() {
|
|
return abilitiesForCharacter("rom-warlock", "rom-warrior").find((entry) => entry.id === ABILITY_ID)!;
|
|
}
|
|
|
|
function setPlayerPools(pools: { rage: number; focus: number; psi: number }) {
|
|
useCombatStore.setState((state) => ({
|
|
resource: pools.focus,
|
|
maxResource: 100,
|
|
resourcePools: { ...state.resourcePools, ...pools },
|
|
maxResourcePools: { ...state.maxResourcePools, rage: 100, focus: 100, psi: 6 },
|
|
}));
|
|
}
|
|
|
|
function prepareTarget() {
|
|
useCombatStore.getState().registerMob(TARGET_ID, { name: "Enraged Test Target", maxHealth: 100_000 });
|
|
setMobPosition(TARGET_ID, [5, 0, 0]);
|
|
}
|
|
|
|
describe("current CoA Warlock/Warrior Enraged runtime", () => {
|
|
beforeAll(async () => {
|
|
await preloadAbilityCatalogsForClasses(["rom-warlock"]);
|
|
});
|
|
|
|
beforeEach(() => {
|
|
clearMobRuntimeRegistry();
|
|
clearPartyRuntimeRegistry();
|
|
resetPartyRuntimeClock();
|
|
useCombatStore.setState({ playerPosition: [0, 0, 0] });
|
|
});
|
|
|
|
it("is learnable only at level 60 for Warlock/Warrior and does not replace the other Enraged row", () => {
|
|
const ability = enragedAbility();
|
|
expect(learnedAbilityForBuild(ability, 59, {})).toBeNull();
|
|
expect(learnedAbilityForBuild(ability, 60, {})).toMatchObject({ dbcSpellId: 499_871 });
|
|
expect(abilitiesForCharacter("rom-warlock", "rom-priest").some((candidate) => (
|
|
candidate.dbcSpellId === ROM_COA_WARLOCK_WARRIOR_ENRAGED.spellId
|
|
))).toBe(false);
|
|
expect(abilitiesForCharacter("rom-warrior", "rom-warlock").some((candidate) => (
|
|
candidate.dbcSpellId === ROM_COA_WARLOCK_WARRIOR_ENRAGED.spellId
|
|
))).toBe(false);
|
|
expect(abilitiesForCharacter("rom-warrior", undefined).find((candidate) => (
|
|
candidate.dbcSpellId === ROM_COA_WARLOCK_WARRIOR_ENRAGED.localWarriorGeneralSpellId
|
|
))).toBeDefined();
|
|
});
|
|
|
|
it("restores all three player resources, clamps to their caps, and honors the 40-second cooldown", () => {
|
|
useCombatStore.getState().initializeCharacter({
|
|
classId: "rom-warlock",
|
|
secondaryClassId: "rom-warrior",
|
|
raceId: "rom-dwarf",
|
|
level: 80,
|
|
});
|
|
setPlayerPools({ rage: 40, focus: 50, psi: 5 });
|
|
const ability = enragedAbility();
|
|
|
|
expect(useCombatStore.getState().castAbility(ability.id, { now: 1_000 })).toMatchObject({ ok: true });
|
|
expect(useCombatStore.getState().resourcePools).toMatchObject({ rage: 100, focus: 85, psi: 6 });
|
|
expect(useCombatStore.getState().cooldowns[ability.id]).toBe(41_000);
|
|
|
|
setPlayerPools({ rage: 50, focus: 80, psi: 5 });
|
|
expect(useCombatStore.getState().castAbility(ability.id, { now: 3_000 })).toMatchObject({
|
|
ok: false,
|
|
reason: "cooldown",
|
|
});
|
|
expect(useCombatStore.getState().castAbility(ability.id, { now: 41_000 })).toMatchObject({ ok: true });
|
|
expect(useCombatStore.getState().resourcePools).toMatchObject({ rage: 100, focus: 100, psi: 6 });
|
|
});
|
|
|
|
it("restores and caps companion Rage, Focus, and Psi through the shared resource effects", () => {
|
|
useCombatStore.getState().initializeCharacter({ classId: "rom-priest", raceId: "rom-dwarf", level: 80 });
|
|
usePartyStore.getState().initializeParty("rom-warlock-warrior-enraged-party", 80,
|
|
useCombatStore.getState().health, "damage", 2, "rom");
|
|
const member = createConfiguredPartyMember({
|
|
name: "Enraged Companion",
|
|
classId: "rom-warlock",
|
|
secondaryClassId: "rom-warrior",
|
|
raceId: "rom-dwarf",
|
|
specialization: "Warlock",
|
|
role: "damage",
|
|
}, 80, 0, "rom-warlock-warrior-enraged-party");
|
|
const ability = enragedAbility();
|
|
const pairAbilities = abilitiesForCharacter("rom-warlock", "rom-warrior");
|
|
const maximumCooldowns = Object.fromEntries(pairAbilities
|
|
.filter((candidate) => candidate.id !== ability.id)
|
|
.map((candidate) => [candidate.id, 100_000]));
|
|
usePartyStore.setState({
|
|
command: "attack",
|
|
members: [{
|
|
...member,
|
|
cooldowns: maximumCooldowns,
|
|
resourcePools: { ...member.resourcePools, focus: 20, rage: 40, psi: 5 },
|
|
resource: 20,
|
|
maxResource: 100,
|
|
}],
|
|
});
|
|
useCombatStore.getState().synchronizeActors();
|
|
registerPartyRuntimePosition(member.id, [0, 0, 0]);
|
|
prepareTarget();
|
|
useCombatStore.getState().damageMob(TARGET_ID, 100, 900);
|
|
updatePartyRuntimeTargetVisibility(member.id, TARGET_ID, true);
|
|
|
|
advanceDungeonPartyCombat(1_000);
|
|
advanceDungeonPartyCombat(3_100);
|
|
|
|
const result = usePartyStore.getState().members.find((candidate) => candidate.id === member.id);
|
|
expect(result?.cooldowns[ability.id]).toBe(41_000);
|
|
expect(result?.resourcePools.rage).toBe(100);
|
|
expect(result?.resourcePools.focus).toBeGreaterThanOrEqual(55);
|
|
expect(result?.resourcePools.focus).toBeLessThanOrEqual(100);
|
|
expect(result?.resourcePools.psi).toBe(6);
|
|
expect(useCombatStore.getState().mobs[TARGET_ID]?.health).toBe(99_900);
|
|
});
|
|
});
|