Update Healer Man server
This commit is contained in:
1 parent
4c768fbec6
commit
b16821e92e
330 files changed
+35406
-1760
No files matched your search
@@ -13,6 +13,7 @@ import {
|
||||
updatePartyRuntimeTargetVisibility,
|
||||
} from "./partyRuntimeRegistry";
|
||||
import { createConfiguredPartyMember, usePartyStore } from "./partyStore";
|
||||
import type { DamageSchool } from "./combatAuras";
|
||||
import {
|
||||
ROM_SCOUT_PRIEST_DRUMS_OF_WAR,
|
||||
ROM_SCOUT_PRIEST_DRUMS_OF_WAR_AURA,
|
||||
@@ -64,10 +65,41 @@ function landPlayerShot(now: number, seed: string) {
|
||||
);
|
||||
}
|
||||
|
||||
function activeDrumsAura(actorId: string, now: number) {
|
||||
function directHit(
|
||||
actorId: string,
|
||||
now: number,
|
||||
school: DamageSchool,
|
||||
attackKind: "melee" | "ranged" | "spell",
|
||||
seed: string,
|
||||
options: { readonly forceCrit?: boolean; readonly canCrit?: boolean } = {},
|
||||
) {
|
||||
const abilityId = `drums-direct-hit:${attackKind}:${school}`;
|
||||
return useCombatStore.getState().damageMob(
|
||||
TARGET_ID,
|
||||
1_000,
|
||||
now,
|
||||
{ actorId, role: "damage", abilityId },
|
||||
school,
|
||||
{
|
||||
attackKind,
|
||||
abilityId,
|
||||
seed,
|
||||
criticalChanceBonusPercent: options.forceCrit ? 100 : 0,
|
||||
rules: {
|
||||
canMiss: false,
|
||||
canDodge: false,
|
||||
canParry: false,
|
||||
canBlock: false,
|
||||
canCrit: options.canCrit,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function activeDrumsAura(actorId: string, now: number, sourceId = PLAYER_AGGRO_ID) {
|
||||
return useCombatStore.getState().auras.find((aura) => (
|
||||
aura.targetId === actorId
|
||||
&& aura.sourceId === PLAYER_AGGRO_ID
|
||||
&& aura.sourceId === sourceId
|
||||
&& aura.definition.id === ROM_SCOUT_PRIEST_DRUMS_OF_WAR_AURA.id
|
||||
&& (aura.expiresAt === null || aura.expiresAt > now)
|
||||
));
|
||||
@@ -81,7 +113,7 @@ describe("RoM Scout/Priest Drums of War integration", () => {
|
||||
initializeScoutPriest();
|
||||
});
|
||||
|
||||
it("applies the local critical-stat aura after a landed Shot and respects its per-caster ICD", () => {
|
||||
it("applies CoA critical damage after a landed Shot and waits 15 seconds after expiry", () => {
|
||||
registerTarget();
|
||||
const passive = useCombatStore.getState().abilities.find((ability) => ability.dbcSpellId === 493010);
|
||||
expect(passive).toMatchObject({ classId: "rom-scout", secondaryClassId: "rom-priest", passive: true });
|
||||
@@ -89,22 +121,76 @@ describe("RoM Scout/Priest Drums of War integration", () => {
|
||||
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,
|
||||
expiresAt: 16_000,
|
||||
definition: {
|
||||
modifiers: [
|
||||
{ kind: "stat", stat: "physical-critical-strike", operation: "flat", value: 20 },
|
||||
{ kind: "stat", stat: "magical-critical-strike", operation: "flat", value: 20 },
|
||||
{ kind: "stat", stat: "critical-damage", operation: "percent", value: 0.2 },
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(useCombatStore.getState().cooldowns[`${ROM_SCOUT_PRIEST_DRUMS_OF_WAR.cooldownKey}:${PLAYER_AGGRO_ID}`])
|
||||
.toBe(16_000);
|
||||
.toBe(31_000);
|
||||
|
||||
landPlayerShot(5_000, "drums-player-on-icd");
|
||||
expect(activeDrumsAura(PLAYER_AGGRO_ID, 5_000)?.expiresAt).toBe(9_000);
|
||||
expect(activeDrumsAura(PLAYER_AGGRO_ID, 5_000)?.expiresAt).toBe(16_000);
|
||||
|
||||
landPlayerShot(16_000, "drums-player-ready-again");
|
||||
expect(activeDrumsAura(PLAYER_AGGRO_ID, 16_000)?.expiresAt).toBe(24_000);
|
||||
landPlayerShot(16_000, "drums-player-lockout-after-expiry");
|
||||
expect(activeDrumsAura(PLAYER_AGGRO_ID, 16_000)).toBeUndefined();
|
||||
landPlayerShot(30_999, "drums-player-last-lockout-ms");
|
||||
expect(activeDrumsAura(PLAYER_AGGRO_ID, 30_999)).toBeUndefined();
|
||||
|
||||
landPlayerShot(31_000, "drums-player-ready-again");
|
||||
expect(activeDrumsAura(PLAYER_AGGRO_ID, 31_000)?.expiresAt).toBe(46_000);
|
||||
});
|
||||
|
||||
it("increases player physical and magical critical damage without changing crit chance or ordinary hits", () => {
|
||||
registerTarget();
|
||||
const baselinePhysicalChance = directHit(
|
||||
PLAYER_AGGRO_ID, 500, "physical", "ranged", "drums-player-physical-chance-before",
|
||||
);
|
||||
const baselineMagicChance = directHit(
|
||||
PLAYER_AGGRO_ID, 600, "fire", "spell", "drums-player-magic-chance-before",
|
||||
);
|
||||
const baselinePhysicalCrit = directHit(
|
||||
PLAYER_AGGRO_ID, 700, "physical", "ranged", "drums-player-physical-crit-before", { forceCrit: true },
|
||||
);
|
||||
const baselineMagicalCrit = directHit(
|
||||
PLAYER_AGGRO_ID, 800, "fire", "spell", "drums-player-magic-crit-before", { forceCrit: true },
|
||||
);
|
||||
expect(baselinePhysicalCrit.combatResult?.critical).toBe(true);
|
||||
expect(baselineMagicalCrit.combatResult?.critical).toBe(true);
|
||||
|
||||
landPlayerShot(1_000, "drums-player-crit-buff");
|
||||
|
||||
const buffedPhysicalChance = directHit(
|
||||
PLAYER_AGGRO_ID, 2_000, "physical", "ranged", "drums-player-physical-chance-after",
|
||||
);
|
||||
const buffedMagicChance = directHit(
|
||||
PLAYER_AGGRO_ID, 2_100, "fire", "spell", "drums-player-magic-chance-after",
|
||||
);
|
||||
expect(buffedPhysicalChance.combatResult?.attackTable.criticalPercent)
|
||||
.toBe(baselinePhysicalChance.combatResult?.attackTable.criticalPercent);
|
||||
expect(buffedMagicChance.combatResult?.attackTable.criticalPercent)
|
||||
.toBe(baselineMagicChance.combatResult?.attackTable.criticalPercent);
|
||||
|
||||
const physicalCrit = directHit(
|
||||
PLAYER_AGGRO_ID, 2_200, "physical", "ranged", "drums-player-physical-crit-after", { forceCrit: true },
|
||||
);
|
||||
const magicalCrit = directHit(
|
||||
PLAYER_AGGRO_ID, 2_300, "fire", "spell", "drums-player-magic-crit-after", { forceCrit: true },
|
||||
);
|
||||
expect(physicalCrit.combatResult?.critical).toBe(true);
|
||||
expect(magicalCrit.combatResult?.critical).toBe(true);
|
||||
expect(physicalCrit.combatResult?.damageBeforeMitigation)
|
||||
.toBe(Math.round((baselinePhysicalCrit.combatResult?.damageBeforeMitigation ?? 0) * 1.1));
|
||||
expect(magicalCrit.combatResult?.damageBeforeMitigation)
|
||||
.toBe(Math.round((baselineMagicalCrit.combatResult?.damageBeforeMitigation ?? 0) * 1.1));
|
||||
|
||||
const ordinaryHit = directHit(
|
||||
PLAYER_AGGRO_ID, 2_400, "fire", "spell", "drums-player-ordinary-hit", { canCrit: false },
|
||||
);
|
||||
expect(ordinaryHit.combatResult?.critical).toBe(false);
|
||||
expect(ordinaryHit.combatResult?.damageBeforeMitigation).toBe(1_000);
|
||||
});
|
||||
|
||||
it("applies only to living party members within 50 yards of the caster", () => {
|
||||
@@ -125,6 +211,10 @@ describe("RoM Scout/Priest Drums of War integration", () => {
|
||||
registerPartyRuntimePosition(near.id, [40, 0, 0]);
|
||||
registerPartyRuntimePosition(far.id, [51, 0, 0]);
|
||||
registerTarget();
|
||||
const nearCritBefore = directHit(
|
||||
near.id, 500, "fire", "spell", "drums-near-ally-crit-before", { forceCrit: true },
|
||||
);
|
||||
expect(nearCritBefore.combatResult?.critical).toBe(true);
|
||||
|
||||
landPlayerShot(1_000, "drums-player-party-radius");
|
||||
|
||||
@@ -135,6 +225,13 @@ describe("RoM Scout/Priest Drums of War integration", () => {
|
||||
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);
|
||||
|
||||
const nearCritAfter = directHit(
|
||||
near.id, 2_000, "fire", "spell", "drums-near-ally-crit-after", { forceCrit: true },
|
||||
);
|
||||
expect(nearCritAfter.combatResult?.critical).toBe(true);
|
||||
expect(nearCritAfter.combatResult?.damageBeforeMitigation)
|
||||
.toBe(Math.round((nearCritBefore.combatResult?.damageBeforeMitigation ?? 0) * 1.1));
|
||||
});
|
||||
|
||||
it("passes party Shot through the ranged hit-resolution path before proc application", () => {
|
||||
@@ -181,6 +278,10 @@ describe("RoM Scout/Priest Drums of War integration", () => {
|
||||
}] });
|
||||
registerPartyRuntimePosition(member.id, [0, 0, 0]);
|
||||
registerTarget();
|
||||
const companionCritBefore = directHit(
|
||||
member.id, 2_000, "physical", "ranged", "drums-companion-crit-before", { forceCrit: true },
|
||||
);
|
||||
expect(companionCritBefore.combatResult?.critical).toBe(true);
|
||||
useCombatStore.getState().damageMob(TARGET_ID, 1, 3_400);
|
||||
updatePartyRuntimeTargetVisibility(member.id, TARGET_ID, true);
|
||||
useCombatStore.getState().synchronizeActors();
|
||||
@@ -190,9 +291,26 @@ describe("RoM Scout/Priest Drums of War integration", () => {
|
||||
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);
|
||||
&& aura.expiresAt === 18_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);
|
||||
.toBe(33_500);
|
||||
expect(activeDrumsAura(PLAYER_AGGRO_ID, 3_500)?.expiresAt).toBe(16_000);
|
||||
|
||||
const companionAuras = useCombatStore.getState().auras;
|
||||
useCombatStore.setState({
|
||||
auras: companionAuras.filter((aura) => !(aura.targetId === member.id
|
||||
&& aura.sourceId === member.id
|
||||
&& aura.definition.id === ROM_SCOUT_PRIEST_DRUMS_OF_WAR_AURA.id)),
|
||||
});
|
||||
const companionCritWithoutDrums = directHit(
|
||||
member.id, 4_000, "physical", "ranged", "drums-companion-crit-without-aura", { forceCrit: true },
|
||||
);
|
||||
useCombatStore.setState({ auras: companionAuras });
|
||||
const companionCritAfter = directHit(
|
||||
member.id, 4_100, "physical", "ranged", "drums-companion-crit-after", { forceCrit: true },
|
||||
);
|
||||
expect(companionCritAfter.combatResult?.critical).toBe(true);
|
||||
expect(companionCritAfter.combatResult?.damageBeforeMitigation)
|
||||
.toBe(Math.round((companionCritWithoutDrums.combatResult?.damageBeforeMitigation ?? 0) * 1.1));
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user