Update Healer Man server
This commit is contained in:
1 parent
4c768fbec6
commit
b16821e92e
330 files changed
+35406
-1760
No files matched your search
@@ -0,0 +1,167 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { PLAYER_AGGRO_ID } from "./aggro";
|
||||
import { abilitiesForCharacter } from "./abilityCatalog";
|
||||
import { installCombatClock } from "./combatClock";
|
||||
import { createDeathKnightRunes } from "./combatActors";
|
||||
import { useCombatStore, PLAYER_AURA_ENTITY_ID } from "./combatStore";
|
||||
import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry";
|
||||
import { clearPartyRuntimeRegistry, registerPartyRuntimePosition, updatePartyRuntimeTargetVisibility } from "./partyRuntimeRegistry";
|
||||
import { advanceDungeonPartyCombat, resetPartyRuntimeClock } from "./partyRuntime";
|
||||
import { createConfiguredPartyMember, usePartyStore } from "./partyStore";
|
||||
import { EMPTY_ITEM_STATS } from "./itemStats";
|
||||
import {
|
||||
WOW335_DEATH_KNIGHT_BLOODY_VENGEANCE_TALENT_ID,
|
||||
} from "./wow335DeathKnightBloodyVengeanceRuntime";
|
||||
|
||||
const TARGET_ID = "bloody-vengeance-target";
|
||||
const PARTY_SEED = "bloody-vengeance-party";
|
||||
let restoreClock: (() => void) | undefined;
|
||||
|
||||
function bloodStrike() {
|
||||
const found = abilitiesForCharacter("death-knight", null)
|
||||
.find((ability) => ability.name === "Blood Strike");
|
||||
if (!found) throw new Error("Missing generated Death Knight Blood Strike.");
|
||||
return found;
|
||||
}
|
||||
|
||||
function registerTarget() {
|
||||
useCombatStore.getState().registerMob(TARGET_ID, {
|
||||
name: "Bloody Vengeance target",
|
||||
maxHealth: 1_000_000,
|
||||
xpReward: 0,
|
||||
});
|
||||
setMobPosition(TARGET_ID, [2, 0, 0]);
|
||||
useCombatStore.getState().selectMob(TARGET_ID);
|
||||
}
|
||||
|
||||
function guaranteedCriticalBloodStrike(sourceActorId: string, now: number) {
|
||||
const strike = bloodStrike();
|
||||
return useCombatStore.getState().damageMob(
|
||||
TARGET_ID,
|
||||
100,
|
||||
now,
|
||||
{ actorId: sourceActorId, role: "damage", abilityId: strike.id },
|
||||
"physical",
|
||||
{
|
||||
attackKind: "melee",
|
||||
seed: `${sourceActorId}:${now}:bloody-vengeance-test`,
|
||||
abilityId: strike.id,
|
||||
criticalChanceBonusPercent: 100,
|
||||
rules: {
|
||||
canMiss: false,
|
||||
canDodge: false,
|
||||
canParry: false,
|
||||
canBlock: false,
|
||||
resistanceMode: "none",
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
describe("WoW Death Knight Bloody Vengeance combat integration", () => {
|
||||
beforeEach(() => {
|
||||
restoreClock = installCombatClock(() => 1_000);
|
||||
clearMobRuntimeRegistry();
|
||||
clearPartyRuntimeRegistry();
|
||||
resetPartyRuntimeClock();
|
||||
useCombatStore.getState().initializeCharacter({ classId: "death-knight", level: 80, xp: 0 });
|
||||
usePartyStore.getState().initializeParty(
|
||||
"wow335-death-knight-bloody-vengeance", 80, useCombatStore.getState().health, "tank", 5,
|
||||
);
|
||||
useCombatStore.setState({ deathKnightRunes: createDeathKnightRunes(1_000) });
|
||||
registerTarget();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
restoreClock?.();
|
||||
restoreClock = undefined;
|
||||
});
|
||||
|
||||
it("adds up to three stacks for player critical hits and refreshes the generated duration", () => {
|
||||
useCombatStore.setState((state) => ({
|
||||
talentRanks: { ...state.talentRanks, [WOW335_DEATH_KNIGHT_BLOODY_VENGEANCE_TALENT_ID]: 3 },
|
||||
}));
|
||||
|
||||
const first = guaranteedCriticalBloodStrike(PLAYER_AGGRO_ID, 1_000);
|
||||
expect(first.combatResult?.critical).toBe(true);
|
||||
let aura = useCombatStore.getState().auras.find((candidate) => candidate.targetId === PLAYER_AURA_ENTITY_ID
|
||||
&& candidate.definition.name === "Bloody Vengeance");
|
||||
expect(aura).toMatchObject({ stacks: 1, appliedAt: 1_000, expiresAt: 31_000 });
|
||||
|
||||
for (const now of [2_000, 3_000, 4_000]) {
|
||||
expect(guaranteedCriticalBloodStrike(PLAYER_AGGRO_ID, now).combatResult?.critical).toBe(true);
|
||||
aura = useCombatStore.getState().auras.find((candidate) => candidate.targetId === PLAYER_AURA_ENTITY_ID
|
||||
&& candidate.definition.name === "Bloody Vengeance");
|
||||
}
|
||||
expect(aura).toMatchObject({ stacks: 3, appliedAt: 4_000, expiresAt: 34_000 });
|
||||
});
|
||||
|
||||
it("uses a companion Death Knight's own rank and applies its aura to that companion", () => {
|
||||
const talentRanks = { [WOW335_DEATH_KNIGHT_BLOODY_VENGEANCE_TALENT_ID]: 2 };
|
||||
const member = createConfiguredPartyMember({
|
||||
name: "Bloody Vengeance DK",
|
||||
classId: "death-knight",
|
||||
specialization: "Blood",
|
||||
specializationId: "death-knight:blood",
|
||||
talentTreeId: "death-knight-blood",
|
||||
talentRanks,
|
||||
role: "damage",
|
||||
}, 80, 0, PARTY_SEED);
|
||||
usePartyStore.setState({ members: [{
|
||||
...member,
|
||||
gearStats: EMPTY_ITEM_STATS,
|
||||
talentRanks,
|
||||
runes: createDeathKnightRunes(1_000),
|
||||
}] });
|
||||
useCombatStore.getState().synchronizeActors();
|
||||
|
||||
const result = guaranteedCriticalBloodStrike(member.id, 2_000);
|
||||
expect(result.combatResult?.critical).toBe(true);
|
||||
expect(useCombatStore.getState().auras).toContainEqual(expect.objectContaining({
|
||||
sourceId: member.id,
|
||||
targetId: member.id,
|
||||
appliedAt: 2_000,
|
||||
expiresAt: 32_000,
|
||||
definition: expect.objectContaining({
|
||||
name: "Bloody Vengeance",
|
||||
modifiers: [expect.objectContaining({ kind: "damage", direction: "dealt", value: 0.02, school: "physical" })],
|
||||
}),
|
||||
}));
|
||||
});
|
||||
|
||||
it("gives a talented companion critical-result contexts for auto attacks", () => {
|
||||
const talentRanks = { [WOW335_DEATH_KNIGHT_BLOODY_VENGEANCE_TALENT_ID]: 1 };
|
||||
const member = createConfiguredPartyMember({
|
||||
name: "Bloody Vengeance auto attacker",
|
||||
classId: "death-knight",
|
||||
specialization: "Blood",
|
||||
specializationId: "death-knight:blood",
|
||||
talentTreeId: "death-knight-blood",
|
||||
talentRanks,
|
||||
role: "damage",
|
||||
}, 80, 0, `${PARTY_SEED}:auto`);
|
||||
usePartyStore.setState({ members: [{
|
||||
...member,
|
||||
gearStats: {
|
||||
...EMPTY_ITEM_STATS,
|
||||
criticalStrikeRating: 1_000_000,
|
||||
hitRating: 1_000_000,
|
||||
expertiseRating: 1_000_000,
|
||||
},
|
||||
talentRanks,
|
||||
runes: createDeathKnightRunes(1_000),
|
||||
}] });
|
||||
useCombatStore.getState().synchronizeActors();
|
||||
registerPartyRuntimePosition(member.id, [0, 0, 0]);
|
||||
useCombatStore.getState().damageMob(TARGET_ID, 1, 100);
|
||||
updatePartyRuntimeTargetVisibility(member.id, TARGET_ID, true);
|
||||
|
||||
advanceDungeonPartyCombat(1_000);
|
||||
|
||||
expect(useCombatStore.getState().auras).toContainEqual(expect.objectContaining({
|
||||
sourceId: member.id,
|
||||
targetId: member.id,
|
||||
definition: expect.objectContaining({ name: "Bloody Vengeance" }),
|
||||
}));
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user