Update Healer Man server

This commit is contained in:
phenom committed 2026-10-04 13:13:35 -04:00
1 parent 39458d5de6
commit 6cc1b9217f
203 files changed
+133929 -31271

No files matched your search

@@ -0,0 +1,164 @@
import { describe, expect, it } from "vitest";
import { PLAYER_AGGRO_ID } from "./aggro";
import { abilityAtLevel, abilityById } from "./abilityCatalog";
import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry";
import { resetPartyRuntimeClock } from "./partyRuntime";
import { clearPartyRuntimeRegistry } from "./partyRuntimeRegistry";
import { useCombatStore } from "./combatStore";
import { usePartyStore } from "./partyStore";
import { HOTO_ENTITIES } from "./generated/hotoPopulation.generated";
import type { EnemyAttackDefinition } from "./enemyAttacks";
const LEVEL = 80;
const SOURCE_ABILITY_ID = "rom-mage-primary-53";
const SOURCE_SKILL_ID = 491_168;
const SOURCE_DAMAGE_COMPONENT_ID = 620_205;
const SOURCE_DOT_COMPONENT_ID = 501_550;
const NATIVE_NPC_ID = 102_041;
function setupMage() {
clearMobRuntimeRegistry();
clearPartyRuntimeRegistry();
resetPartyRuntimeClock();
usePartyStore.setState({ members: [] });
useCombatStore.getState().initializeCharacter({
classId: "rom-mage",
secondaryClassId: "rom-warlock",
raceId: "rom-human",
role: "damage",
level: LEVEL,
statRuleset: "rom",
});
useCombatStore.getState().setPlayerPosition([0, 0, 0]);
}
function registerTarget(id: string) {
useCombatStore.getState().registerMob(id, {
name: "Native hit-gate target",
runewakerNpcId: NATIVE_NPC_ID,
level: 85,
maxHealth: 2_000_000,
xpReward: 0,
attacks: [],
});
setMobPosition(id, [2, 0, 0]);
}
function resolveImportedEnemyGate(attempt: number) {
setupMage();
const mobId = `native-shared-hit-enemy-${attempt}`;
const sourceEntity = HOTO_ENTITIES["rw-hoto-102041"];
const sourceAttacks: readonly EnemyAttackDefinition[] = sourceEntity.combat?.attacks ?? [];
const sourceAttack = sourceAttacks.find((attack) => attack.spellId === 493_231);
if (!sourceAttack) throw new Error("Missing imported Big Ripper source attack.");
useCombatStore.getState().registerMob(mobId, {
name: "Imported Teeth of the Reef",
runewakerNpcId: NATIVE_NPC_ID,
level: sourceEntity.combat?.level ?? 56,
maxHealth: 1_000_000,
xpReward: 0,
attacks: [{ ...sourceAttack, initialCooldownMs: [0, 0] }],
});
setMobPosition(mobId, [2, 0, 0]);
useCombatStore.getState().selectMob(mobId);
const now = 1_000 + attempt * 173;
useCombatStore.getState().damageMob(mobId, 1, now,
{ actorId: PLAYER_AGGRO_ID, role: "damage" });
useCombatStore.setState((state) => ({
mobs: {
...state.mobs,
[mobId]: {
...state.mobs[mobId]!,
engaged: true,
targetActorId: PLAYER_AGGRO_ID,
abilityReadyAt: { [sourceAttack.id]: now },
},
},
}));
useCombatStore.getState().advanceMobCombat(now, [0, 0, 0]);
const state = useCombatStore.getState();
const result = state.lastCombatResult;
const dot = state.enemyPeriodicEffects.find((entry) => (
entry.sourceMob.id === mobId && entry.attack.spellId === sourceAttack.spellId
));
return { result, dot, sourceAttack };
}
describe("native Runewaker shared hit gates", () => {
it("uses one target hit gate for a direct packet and its DotMagic in one cast", () => {
const catalog = abilityById(SOURCE_ABILITY_ID);
if (!catalog) throw new Error("Missing active-five Electric Bolt source ability.");
const ability = abilityAtLevel(catalog, LEVEL);
expect(ability.effects).toEqual(expect.arrayContaining([
expect.objectContaining({
kind: "damage",
romNativeSource: { skillId: SOURCE_SKILL_ID, componentId: SOURCE_DAMAGE_COMPONENT_ID, kind: "damage" },
}),
expect.objectContaining({
kind: "dot",
romNativeSource: { skillId: SOURCE_SKILL_ID, componentId: SOURCE_DOT_COMPONENT_ID, kind: "dot" },
}),
]));
const gateResults: Array<{ directGatePassed: boolean; dotApplied: boolean }> = [];
for (let attempt = 0; attempt < 32; attempt += 1) {
setupMage();
const targetId = `native-shared-hit-target-${attempt}`;
registerTarget(targetId);
const now = 1_000 + attempt * 137;
const cast = useCombatStore.getState().castAbility(ability.id, {
targetActorId: targetId,
origin: [0, 0, 0],
now,
});
expect(cast.ok, JSON.stringify(cast)).toBe(true);
const activeCast = useCombatStore.getState().activeCast;
if (activeCast) useCombatStore.getState().castAbility(ability.id, {
targetActorId: targetId,
origin: [0, 0, 0],
now: activeCast.completesAt,
completingCast: activeCast,
});
const state = useCombatStore.getState();
const direct = state.lastCombatResult;
expect(direct).toBeDefined();
const dot = state.effects.find((effect) => effect.kind === "dot"
&& effect.targetId === targetId && effect.sourceAbilityId === ability.id);
if (dot) expect(dot.nativePeriodicDamageSnapshot).toMatchObject({
sourceSkillId: SOURCE_SKILL_ID,
componentId: SOURCE_DOT_COMPONENT_ID,
});
gateResults.push({ directGatePassed: !direct!.missed && !direct!.dodged, dotApplied: Boolean(dot) });
}
// This source has a sub-100% native magical hit chance against the higher-level target,
// so both passed and failed gates occur in the fixed seed sweep.
expect(gateResults.some((result) => result.directGatePassed)).toBe(true);
expect(gateResults.some((result) => !result.directGatePassed)).toBe(true);
expect(gateResults.every((result) => result.directGatePassed === result.dotApplied)).toBe(true);
});
it("uses one target hit gate for an imported NPC direct packet and its DotMagic", () => {
const gateResults: Array<{ directGatePassed: boolean; dotApplied: boolean }> = [];
for (let attempt = 0; attempt < 32; attempt += 1) {
const { result, dot, sourceAttack } = resolveImportedEnemyGate(attempt);
expect(sourceAttack).toMatchObject({ spellId: 493_231, runewakerFormula: { componentId: 503_925 } });
expect(result).toBeDefined();
expect(dot?.nativeSnapshot).toMatchObject({
sourceSkillId: 493_231,
componentId: 503_485,
});
gateResults.push({
directGatePassed: !result!.missed && !result!.dodged,
dotApplied: Boolean(dot),
});
}
// MagicCollect uses the deterministic 100% hit function for this spell. A
// generic legacy miss reroll on its unsupported direct CalcType would break
// parity with the native DotMagic application gate.
expect(gateResults.every((result) => result.directGatePassed)).toBe(true);
expect(gateResults.every((result) => result.dotApplied)).toBe(true);
expect(gateResults.every((result) => result.directGatePassed === result.dotApplied)).toBe(true);
});
});