Update Healer Man server

This commit is contained in:
phenom committed 2026-09-30 09:57:13 -04:00
1 parent 35dfdaed61
commit 4fb7a0b9c8
161 files changed
+24618 -529

No files matched your search

+158
View File
@@ -4,6 +4,7 @@ import { clearPartyRuntimeRegistry, registerPartyRuntimePosition } from "./party
import { installCombatClock } from "./combatClock";
import { createDeathKnightRunes } from "./combatActors";
import { useCombatStore, PLAYER_AURA_ENTITY_ID } from "./combatStore";
import { applyAura as applyCombatAura } from "./combatAuras";
import { usePartyStore } from "./partyStore";
import { talentNodesForTree } from "./talentCatalog";
@@ -60,6 +61,31 @@ function registerXpTarget(id: string, xpReward: number) {
setMobPosition(id, [2, 0, 0]);
}
function installAntiMagicShell(targetId: string, maximumHealth: number) {
const result = applyCombatAura(useCombatStore.getState().auras, {
definition: {
id: "wow335:48707:anti-magic-shell",
name: "Anti-Magic Shell",
disposition: "buff",
durationMs: 5_000,
maxStacks: 1,
stackBehavior: "replace",
utilityTags: ["wow335-death-knight-anti-magic-shell"],
absorbs: [{
id: "wow335:48707:anti-magic-shell-absorb",
amount: maximumHealth * 0.5,
schools: ["holy", "fire", "nature", "frost", "shadow", "arcane"],
fraction: 0.75,
}],
},
sourceId: targetId,
targetId,
now,
});
useCombatStore.setState({ auras: [...result.auras] });
return result.aura;
}
describe("Death Knight source-backed runtime mechanics", () => {
beforeEach(() => {
now = 1_000;
@@ -117,6 +143,7 @@ describe("Death Knight source-backed runtime mechanics", () => {
it("absorbs three quarters of magical damage up to half of maximum health", () => {
const shell = ability("Anti-Magic Shell");
expect(useCombatStore.getState().castAbility(shell.id, { now }).ok).toBe(true);
useCombatStore.setState({ resource: 0, resourcePools: { "runic-power": 0 } });
const aura = useCombatStore.getState().auras.find((candidate) => candidate.targetId === PLAYER_AURA_ENTITY_ID
&& candidate.definition.name === "Anti-Magic Shell");
expect(aura?.absorbs[0]).toMatchObject({
@@ -127,10 +154,141 @@ describe("Death Knight source-backed runtime mechanics", () => {
now += 1;
expect(useCombatStore.getState().damagePlayer(100, "fire")).toBe(25);
expect(useCombatStore.getState().resourcePools["runic-power"]).toBe(1.5);
expect(useCombatStore.getState().resource).toBe(1.5);
expect(useCombatStore.getState().auras.find((candidate) => candidate.instanceId === aura!.instanceId)
?.absorbs[0].remaining).toBe(Math.round(useCombatStore.getState().maxHealth * 0.5) - 75);
});
it("caps AMS absorption at half maximum health before awarding proportional Runic Power", () => {
useCombatStore.setState({
health: 1_000,
maxHealth: 1_000,
resource: 20,
resourcePools: { "runic-power": 20 },
});
expect(useCombatStore.getState().castAbility(ability("Anti-Magic Shell").id, { now }).ok).toBe(true);
useCombatStore.setState({ resource: 0, resourcePools: { "runic-power": 0 } });
expect(useCombatStore.getState().damagePlayer(1_000, "fire", undefined, now + 1)).toBe(500);
expect(useCombatStore.getState().resourcePools["runic-power"]).toBe(10);
expect(useCombatStore.getState().auras.find((aura) => aura.targetId === PLAYER_AURA_ENTITY_ID
&& aura.definition.utilityTags?.includes("wow335-death-knight-anti-magic-shell"))?.absorbs[0].remaining).toBe(0);
});
it("grants Runic Power for magic absorbed during the normal enemy-attack path", () => {
expect(useCombatStore.getState().castAbility(ability("Anti-Magic Shell").id, { now }).ok).toBe(true);
useCombatStore.setState({ resource: 0, resourcePools: { "runic-power": 0 } });
const shell = useCombatStore.getState().auras.find((aura) => aura.targetId === PLAYER_AURA_ENTITY_ID
&& aura.definition.utilityTags?.includes("wow335-death-knight-anti-magic-shell"))!;
const capacityBefore = shell.absorbs[0].remaining;
useCombatStore.getState().registerMob("ams-player-attacker", {
name: "AMS player attacker",
maxHealth: 1_000,
xpReward: 0,
attacks: [{
id: "ams-player-fire",
name: "Fire strike",
delivery: "melee",
target: "primary",
school: "fire",
animation: "attack",
range: 5,
cooldownMs: 1_000,
damageMultiplier: 1,
}],
});
setMobPosition("ams-player-attacker", [2, 0, 0]);
useCombatStore.getState().damageMob("ams-player-attacker", 1, now);
useCombatStore.getState().advanceMobCombat(now + 450, [0, 0, 0]);
const shellAfterHit = useCombatStore.getState().auras.find((aura) => aura.instanceId === shell.instanceId);
const absorbed = capacityBefore - (shellAfterHit?.absorbs[0].remaining ?? 0);
expect(absorbed).toBeGreaterThan(0);
expect(useCombatStore.getState().resourcePools["runic-power"]).toBeCloseTo(absorbed * 0.02);
});
it("clamps AMS Runic Power gain and ignores physical, missing, and expired shells", () => {
expect(useCombatStore.getState().castAbility(ability("Anti-Magic Shell").id, { now }).ok).toBe(true);
useCombatStore.setState({ resource: 0, resourcePools: { "runic-power": 0 } });
useCombatStore.getState().damagePlayer(100, "physical", undefined, now + 1);
expect(useCombatStore.getState().resourcePools["runic-power"]).toBe(0);
useCombatStore.setState({ auras: [] });
useCombatStore.getState().damagePlayer(100, "fire", undefined, now + 2);
expect(useCombatStore.getState().resourcePools["runic-power"]).toBe(0);
const shell = installAntiMagicShell(PLAYER_AURA_ENTITY_ID, useCombatStore.getState().maxHealth);
useCombatStore.setState({
resource: 99,
resourcePools: { "runic-power": 99 },
auras: useCombatStore.getState().auras.map((aura) => aura.instanceId === shell.instanceId
? { ...aura, expiresAt: now + 3 }
: aura),
});
useCombatStore.getState().damagePlayer(100, "fire", undefined, now + 4);
expect(useCombatStore.getState().resourcePools["runic-power"]).toBe(99);
useCombatStore.setState({
resource: 99,
resourcePools: { "runic-power": 99 },
auras: [shell],
});
useCombatStore.getState().damagePlayer(100, "fire", undefined, now + 1);
expect(useCombatStore.getState().resourcePools["runic-power"]).toBe(100);
});
it("grants a Death Knight companion Runic Power from its own live AMS absorb", () => {
const target = usePartyStore.getState().members[0];
if (!target) throw new Error("Expected a party member for the companion AMS test.");
const companion = {
...target,
classId: "death-knight" as const,
resourceType: "runic-power" as const,
resource: 0,
maxResource: 100,
resourcePools: { ...target.resourcePools, "runic-power": 0 },
};
usePartyStore.setState((state) => ({
members: state.members.map((member) => member.id === target.id ? companion : member),
}));
useCombatStore.setState((state) => {
const actors = { ...state.actors };
delete actors[target.id];
return { ...state, actors };
});
registerPartyRuntimePosition(target.id, [0, 0, 0]);
const aura = installAntiMagicShell(target.id, companion.maxHealth);
useCombatStore.getState().registerMob("ams-companion-attacker", {
name: "AMS companion attacker",
maxHealth: 1_000,
xpReward: 0,
attacks: [{
id: "ams-companion-fire",
name: "Fire strike",
delivery: "melee",
target: "primary",
school: "fire",
animation: "attack",
range: 5,
cooldownMs: 1_000,
damageMultiplier: 1,
}],
});
setMobPosition("ams-companion-attacker", [2, 0, 0]);
useCombatStore.getState().damageMob("ams-companion-attacker", 1, now, {
actorId: target.id,
role: target.role,
});
useCombatStore.getState().advanceMobCombat(now + 450, [0, 0, 0]);
const updated = usePartyStore.getState().members.find((member) => member.id === target.id)!;
const shellAfterHit = useCombatStore.getState().auras.find((candidate) => candidate.instanceId === aura.instanceId);
const absorbed = companion.maxHealth * 0.5 - (shellAfterHit?.absorbs[0].remaining ?? 0);
expect(absorbed).toBeGreaterThan(0);
expect(updated.resourcePools["runic-power"]).toBeCloseTo(absorbed * 0.02);
});
it("blocks harmful magic controls during Anti-Magic Shell while allowing physical controls", () => {
const shell = ability("Anti-Magic Shell");
expect(useCombatStore.getState().castAbility(shell.id, { now }).ok).toBe(true);