Update Healer Man server
This commit is contained in:
1 parent
39458d5de6
commit
6cc1b9217f
203 files changed
+133929
-31271
No files matched your search
@@ -0,0 +1,154 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { PLAYER_AURA_ENTITY_ID, auraAdjustedCharacterStats, useCombatStore } from "./combatStore";
|
||||
import { clearMobRuntimeRegistry } from "./mobRuntimeRegistry";
|
||||
import { clearPartyRuntimeRegistry } from "./partyRuntimeRegistry";
|
||||
import { advanceDungeonPartyCombat, resetPartyRuntimeClock } from "./partyRuntime";
|
||||
import { createConfiguredPartyMember, usePartyStore } from "./partyStore";
|
||||
import { deriveCharacterStats } from "./wotlkStats";
|
||||
import { romNativePeriodicManaEffectDuring, romNativeRecoveryPulseAmounts } from "./romNativeRecovery";
|
||||
|
||||
const ACTIVE_FIVE = ["rom-knight", "rom-priest", "rom-scout", "rom-rogue", "rom-mage"] as const;
|
||||
|
||||
describe("native RoM recovery runtime", () => {
|
||||
beforeEach(() => {
|
||||
clearMobRuntimeRegistry();
|
||||
clearPartyRuntimeRegistry();
|
||||
resetPartyRuntimeClock();
|
||||
useCombatStore.getState().initializeCharacter({ classId: "priest", level: 30 });
|
||||
usePartyStore.getState().initializeParty("native-recovery-tests", 30, useCombatStore.getState().health);
|
||||
});
|
||||
|
||||
it("pulses source HP and Mana on native player builds while ignoring the WoW mana-spend lockout", () => {
|
||||
for (const classId of ACTIVE_FIVE) {
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId,
|
||||
raceId: "rom-human",
|
||||
gender: "male",
|
||||
level: 30,
|
||||
statRuleset: "rom",
|
||||
});
|
||||
const initialized = useCombatStore.getState();
|
||||
const nativeBase = deriveCharacterStats(
|
||||
classId, 30, initialized.gearStats, initialized.raceId, {}, initialized.secondaryClassId,
|
||||
initialized.gender, "rom",
|
||||
);
|
||||
expect(nativeBase.romNative, classId).toBeDefined();
|
||||
const startAt = Date.now() + 50_000;
|
||||
useCombatStore.setState((state) => ({
|
||||
health: Math.max(1, state.maxHealth - 100),
|
||||
resourcePools: { ...state.resourcePools, ...(nativeBase.romNative!.usesMana ? { mana: 0 } : {}) },
|
||||
lastResourceSpentAt: { ...state.lastResourceSpentAt, mana: startAt + 1_500 },
|
||||
}));
|
||||
const startState = useCombatStore.getState();
|
||||
const startHealth = startState.health;
|
||||
const startMana = startState.resourcePools.mana ?? 0;
|
||||
|
||||
useCombatStore.getState().tick(1, startAt + 1_000);
|
||||
const beforePulse = useCombatStore.getState();
|
||||
const pulseAt = startAt + 2_000;
|
||||
const livePulseAuras = beforePulse.auras.filter((aura) => (
|
||||
aura.expiresAt === null || aura.expiresAt > pulseAt
|
||||
));
|
||||
const nativeAtPulse = auraAdjustedCharacterStats(
|
||||
nativeBase,
|
||||
livePulseAuras,
|
||||
PLAYER_AURA_ENTITY_ID,
|
||||
pulseAt,
|
||||
).romNative!;
|
||||
const pulse = romNativeRecoveryPulseAmounts(nativeAtPulse, "player", false);
|
||||
const manaOverTime = romNativePeriodicManaEffectDuring(
|
||||
beforePulse.auras.filter((aura) => aura.targetId === PLAYER_AURA_ENTITY_ID),
|
||||
startAt + 1_000,
|
||||
pulseAt,
|
||||
beforePulse.maxResourcePools.mana ?? beforePulse.maxResource,
|
||||
);
|
||||
|
||||
useCombatStore.getState().tick(1, pulseAt);
|
||||
const recovered = useCombatStore.getState();
|
||||
expect(recovered.health).toBeCloseTo(Math.min(recovered.maxHealth, startHealth + pulse.health), 6);
|
||||
if (nativeBase.romNative!.usesMana) {
|
||||
expect(recovered.resourcePools.mana).toBeCloseTo(
|
||||
Math.min(recovered.maxResourcePools.mana!, startMana + pulse.mana + manaOverTime), 6,
|
||||
);
|
||||
} else {
|
||||
expect(recovered.maxResourcePools.mana ?? 0).toBe(0);
|
||||
}
|
||||
expect(recovered.romNativeRecoveryClock?.elapsedMs).toBe(0);
|
||||
}
|
||||
});
|
||||
|
||||
it("uses the owned-NPC six-second pulse and party-wide combat gate, even while stopped", () => {
|
||||
const configured = createConfiguredPartyMember({
|
||||
name: "Native Recovery Priest",
|
||||
classId: "rom-priest",
|
||||
raceId: "rom-human",
|
||||
specialization: "Primary",
|
||||
role: "healer",
|
||||
statRuleset: "rom",
|
||||
}, 30, 0, "native-recovery-party-member");
|
||||
const member = {
|
||||
...configured,
|
||||
health: configured.maxHealth - 100,
|
||||
resourcePools: { ...configured.resourcePools, mana: 0 },
|
||||
nextActionAt: Number.MAX_SAFE_INTEGER,
|
||||
};
|
||||
usePartyStore.getState().initializeParty("native-recovery-party", 30, useCombatStore.getState().health, "healer", 1, "rom");
|
||||
usePartyStore.setState({ members: [member], command: "stop", activeMobId: null });
|
||||
useCombatStore.getState().synchronizeActors();
|
||||
const baseStats = deriveCharacterStats(
|
||||
member.classId, member.level, member.gearStats, member.raceId, {}, member.secondaryClassId,
|
||||
member.gender, "rom",
|
||||
);
|
||||
const startAt = Date.now() + 100_000;
|
||||
advanceDungeonPartyCombat(startAt);
|
||||
for (let second = 1; second <= 5; second += 1) advanceDungeonPartyCombat(startAt + second * 1_000);
|
||||
const beforePulse = usePartyStore.getState().members[0]!;
|
||||
expect(beforePulse.health).toBe(member.health);
|
||||
expect(beforePulse.resourcePools.mana).toBe(0);
|
||||
|
||||
// Stop suppresses companion commands, but an engaged enemy still holds source attack mode.
|
||||
useCombatStore.getState().registerMob("native-recovery-engaged-mob", {
|
||||
name: "Recovery combat gate", maxHealth: 5_000, xpReward: 0,
|
||||
});
|
||||
useCombatStore.getState().damageMob("native-recovery-engaged-mob", 1, startAt + 5_500);
|
||||
advanceDungeonPartyCombat(startAt + 6_000);
|
||||
const inCombat = usePartyStore.getState().members[0]!;
|
||||
expect(inCombat.health).toBe(member.health);
|
||||
const expectedMana = romNativeRecoveryPulseAmounts(baseStats.romNative!, "party-npc", true).mana;
|
||||
expect(inCombat.resourcePools.mana).toBeCloseTo(expectedMana, 6);
|
||||
});
|
||||
|
||||
it("clears clock state on player and companion death, immediate revive, and party reset", () => {
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId: "rom-priest", raceId: "rom-human", level: 30, statRuleset: "rom",
|
||||
});
|
||||
const startAt = Date.now() + 200_000;
|
||||
useCombatStore.getState().tick(1, startAt);
|
||||
expect(useCombatStore.getState().romNativeRecoveryClock?.elapsedMs).toBe(1_000);
|
||||
useCombatStore.getState().damagePlayer(useCombatStore.getState().maxHealth * 10, "physical", 30, startAt + 100);
|
||||
expect(useCombatStore.getState().romNativeRecoveryClock).toBeUndefined();
|
||||
useCombatStore.getState().revivePlayer();
|
||||
expect(useCombatStore.getState().romNativeRecoveryClock).toBeUndefined();
|
||||
|
||||
const configured = createConfiguredPartyMember({
|
||||
name: "Clock reset companion",
|
||||
classId: "rom-priest",
|
||||
raceId: "rom-human",
|
||||
specialization: "Primary",
|
||||
role: "healer",
|
||||
statRuleset: "rom",
|
||||
}, 30, 0, "native-recovery-clock-reset");
|
||||
const elapsed = {
|
||||
ruleset: "rom" as const,
|
||||
actorKind: "party-npc" as const,
|
||||
elapsedMs: 5_000,
|
||||
};
|
||||
usePartyStore.setState({ members: [{ ...configured, romNativeRecoveryClock: elapsed }] });
|
||||
usePartyStore.getState().damageMember(configured.id, configured.maxHealth * 10);
|
||||
expect(usePartyStore.getState().members[0]?.romNativeRecoveryClock).toBeUndefined();
|
||||
usePartyStore.getState().reviveMember(configured.id);
|
||||
expect(usePartyStore.getState().members[0]?.romNativeRecoveryClock).toBeUndefined();
|
||||
usePartyStore.getState().resetCombat(30, useCombatStore.getState().health);
|
||||
expect(usePartyStore.getState().members.every((candidate) => !candidate.romNativeRecoveryClock)).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user