Update Healer Man server
This commit is contained in:
1 parent
35dfdaed61
commit
4fb7a0b9c8
161 files changed
+24618
-529
No files matched your search
@@ -0,0 +1,198 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { abilitiesForCharacter, abilityById } from "./abilityCatalog";
|
||||
import { PLAYER_AGGRO_ID } from "./aggro";
|
||||
import { useCombatStore } from "./combatStore";
|
||||
import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry";
|
||||
import {
|
||||
advanceDungeonPartyCombat,
|
||||
resetPartyRuntimeClock,
|
||||
} from "./partyRuntime";
|
||||
import {
|
||||
clearPartyRuntimeRegistry,
|
||||
registerPartyRuntimePosition,
|
||||
updatePartyRuntimeTargetVisibility,
|
||||
} from "./partyRuntimeRegistry";
|
||||
import { createConfiguredPartyMember, usePartyStore } from "./partyStore";
|
||||
import {
|
||||
ROM_SCOUT_PRIEST_DRUMS_OF_WAR,
|
||||
ROM_SCOUT_PRIEST_DRUMS_OF_WAR_AURA,
|
||||
} from "./romScoutPriestOverrides";
|
||||
|
||||
const TARGET_ID = "drums-of-war-target";
|
||||
|
||||
function initializeScoutPriest(): void {
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId: "rom-scout",
|
||||
secondaryClassId: "rom-priest",
|
||||
raceId: "rom-human",
|
||||
level: 80,
|
||||
});
|
||||
}
|
||||
|
||||
function registerTarget(): void {
|
||||
useCombatStore.getState().registerMob(TARGET_ID, {
|
||||
name: TARGET_ID,
|
||||
maxHealth: 1_000_000,
|
||||
xpReward: 0,
|
||||
});
|
||||
setMobPosition(TARGET_ID, [3, 0, 0]);
|
||||
useCombatStore.getState().selectMob(TARGET_ID);
|
||||
}
|
||||
|
||||
function shot() {
|
||||
const ability = useCombatStore.getState().abilities.find((candidate) => (
|
||||
candidate.source === "runewaker" && candidate.dbcSpellId === ROM_SCOUT_PRIEST_DRUMS_OF_WAR.triggerSpellId
|
||||
));
|
||||
if (!ability) throw new Error("RuneWaker Shot (490423) is missing from Scout/Priest abilities.");
|
||||
return ability;
|
||||
}
|
||||
|
||||
function landPlayerShot(now: number, seed: string) {
|
||||
const ability = shot();
|
||||
return useCombatStore.getState().damageMob(
|
||||
TARGET_ID,
|
||||
100,
|
||||
now,
|
||||
{ actorId: PLAYER_AGGRO_ID, role: "damage", abilityId: ability.id },
|
||||
"physical",
|
||||
{
|
||||
attackKind: "ranged",
|
||||
abilityId: ability.id,
|
||||
seed,
|
||||
rules: { canMiss: false, canDodge: false, canParry: false, canBlock: false },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function activeDrumsAura(actorId: string, now: number) {
|
||||
return useCombatStore.getState().auras.find((aura) => (
|
||||
aura.targetId === actorId
|
||||
&& aura.sourceId === PLAYER_AGGRO_ID
|
||||
&& aura.definition.id === ROM_SCOUT_PRIEST_DRUMS_OF_WAR_AURA.id
|
||||
&& (aura.expiresAt === null || aura.expiresAt > now)
|
||||
));
|
||||
}
|
||||
|
||||
describe("RoM Scout/Priest Drums of War integration", () => {
|
||||
beforeEach(() => {
|
||||
clearMobRuntimeRegistry();
|
||||
clearPartyRuntimeRegistry();
|
||||
resetPartyRuntimeClock();
|
||||
initializeScoutPriest();
|
||||
});
|
||||
|
||||
it("applies the local critical-stat aura after a landed Shot and respects its per-caster ICD", () => {
|
||||
registerTarget();
|
||||
const passive = useCombatStore.getState().abilities.find((ability) => ability.dbcSpellId === 493010);
|
||||
expect(passive).toMatchObject({ classId: "rom-scout", secondaryClassId: "rom-priest", passive: true });
|
||||
|
||||
const first = landPlayerShot(1_000, "drums-player-first");
|
||||
expect(first.combatResult?.landed).toBe(true);
|
||||
expect(activeDrumsAura(PLAYER_AGGRO_ID, 1_000)).toMatchObject({
|
||||
expiresAt: 9_000,
|
||||
definition: {
|
||||
modifiers: [
|
||||
{ kind: "stat", stat: "physical-critical-strike", operation: "flat", value: 20 },
|
||||
{ kind: "stat", stat: "magical-critical-strike", operation: "flat", value: 20 },
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(useCombatStore.getState().cooldowns[`${ROM_SCOUT_PRIEST_DRUMS_OF_WAR.cooldownKey}:${PLAYER_AGGRO_ID}`])
|
||||
.toBe(16_000);
|
||||
|
||||
landPlayerShot(5_000, "drums-player-on-icd");
|
||||
expect(activeDrumsAura(PLAYER_AGGRO_ID, 5_000)?.expiresAt).toBe(9_000);
|
||||
|
||||
landPlayerShot(16_000, "drums-player-ready-again");
|
||||
expect(activeDrumsAura(PLAYER_AGGRO_ID, 16_000)?.expiresAt).toBe(24_000);
|
||||
});
|
||||
|
||||
it("applies only to living party members within 50 yards of the caster", () => {
|
||||
const near = createConfiguredPartyMember({
|
||||
name: "Near Ally",
|
||||
classId: "priest",
|
||||
specialization: "Discipline",
|
||||
role: "healer",
|
||||
}, 80, 0, "drums-near-party-member");
|
||||
const far = createConfiguredPartyMember({
|
||||
name: "Far Ally",
|
||||
classId: "warrior",
|
||||
specialization: "Arms",
|
||||
role: "damage",
|
||||
}, 80, 1, "drums-far-party-member");
|
||||
usePartyStore.setState({ members: [near, far] });
|
||||
useCombatStore.getState().synchronizeActors();
|
||||
registerPartyRuntimePosition(near.id, [40, 0, 0]);
|
||||
registerPartyRuntimePosition(far.id, [51, 0, 0]);
|
||||
registerTarget();
|
||||
|
||||
landPlayerShot(1_000, "drums-player-party-radius");
|
||||
|
||||
expect(activeDrumsAura(PLAYER_AGGRO_ID, 1_000)).toBeDefined();
|
||||
expect(useCombatStore.getState().auras.some((aura) => aura.targetId === near.id
|
||||
&& aura.sourceId === PLAYER_AGGRO_ID
|
||||
&& aura.definition.id === ROM_SCOUT_PRIEST_DRUMS_OF_WAR_AURA.id)).toBe(true);
|
||||
expect(useCombatStore.getState().auras.some((aura) => aura.targetId === far.id
|
||||
&& aura.sourceId === PLAYER_AGGRO_ID
|
||||
&& aura.definition.id === ROM_SCOUT_PRIEST_DRUMS_OF_WAR_AURA.id)).toBe(false);
|
||||
});
|
||||
|
||||
it("passes party Shot through the ranged hit-resolution path before proc application", () => {
|
||||
registerTarget();
|
||||
const playerHit = landPlayerShot(1_000, "drums-player-independent-icd");
|
||||
expect(playerHit.combatResult?.landed).toBe(true);
|
||||
|
||||
const seed = "drums-party-scout";
|
||||
const member = createConfiguredPartyMember({
|
||||
name: "Scout Priest",
|
||||
classId: "rom-scout",
|
||||
secondaryClassId: "rom-priest",
|
||||
specialization: "Primary",
|
||||
role: "damage",
|
||||
}, 80, 0, seed);
|
||||
const partyShot = abilitiesForCharacter(member.classId, member.secondaryClassId)
|
||||
.find((ability) => ability.dbcSpellId === ROM_SCOUT_PRIEST_DRUMS_OF_WAR.triggerSpellId);
|
||||
if (!partyShot) throw new Error("RuneWaker Shot (490423) is missing from party Scout/Priest abilities.");
|
||||
const passive = abilitiesForCharacter(member.classId, member.secondaryClassId)
|
||||
.find((ability) => ability.dbcSpellId === ROM_SCOUT_PRIEST_DRUMS_OF_WAR.passiveSpellId);
|
||||
if (!passive) throw new Error("Drums of War (493010) is missing from party Scout/Priest abilities.");
|
||||
expect(abilityById(partyShot.id)?.dbcSpellId).toBe(490_423);
|
||||
|
||||
usePartyStore.getState().initializeParty(seed, 80, useCombatStore.getState().health, "damage", 2, "rom");
|
||||
usePartyStore.getState().setCommand("attack");
|
||||
usePartyStore.setState({ members: [{
|
||||
...member,
|
||||
resource: 10_000,
|
||||
maxResource: 10_000,
|
||||
resourcePools: { ...member.resourcePools, "nature-power": 10_000, mana: 10_000 },
|
||||
activeCast: {
|
||||
abilityId: partyShot.id,
|
||||
school: "physical",
|
||||
mode: "cast",
|
||||
startedAt: 500,
|
||||
completesAt: 3_500,
|
||||
originalDurationMs: 3_000,
|
||||
pushbackHits: 0,
|
||||
origin: [0, 0, 0],
|
||||
targetId: TARGET_ID,
|
||||
},
|
||||
activeCastTargetId: TARGET_ID,
|
||||
status: "attacking",
|
||||
}] });
|
||||
registerPartyRuntimePosition(member.id, [0, 0, 0]);
|
||||
registerTarget();
|
||||
useCombatStore.getState().damageMob(TARGET_ID, 1, 3_400);
|
||||
updatePartyRuntimeTargetVisibility(member.id, TARGET_ID, true);
|
||||
useCombatStore.getState().synchronizeActors();
|
||||
|
||||
advanceDungeonPartyCombat(3_500);
|
||||
|
||||
expect(useCombatStore.getState().auras.some((aura) => aura.sourceId === member.id
|
||||
&& aura.targetId === member.id
|
||||
&& aura.definition.id === ROM_SCOUT_PRIEST_DRUMS_OF_WAR_AURA.id
|
||||
&& aura.expiresAt === 11_500)).toBe(true);
|
||||
expect(useCombatStore.getState().cooldowns[`${ROM_SCOUT_PRIEST_DRUMS_OF_WAR.cooldownKey}:${member.id}`])
|
||||
.toBe(18_500);
|
||||
expect(activeDrumsAura(PLAYER_AGGRO_ID, 3_500)?.expiresAt).toBe(9_000);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user