Update Healer Man server

This commit is contained in:
phenom committed 2026-09-28 22:46:45 -04:00
1 parent 9d7e0e2f9e
commit 35dfdaed61
94 files changed
+17034 -418

No files matched your search

+115 -1
View File
@@ -1,9 +1,11 @@
import { describe, expect, it } from "vitest";
import { abilityById, type AbilityEffect } from "./abilityCatalog";
import { romEffectsForCast, romTargetBleedCount, type RomCastMechanics } from "./romCombatMechanics";
import { ROM_ALL_ABILITIES } from "./romAbilityCatalog";
import { romAbilityNeedsRangedWeapon, romAbilityNeedsShield, romAbilityNeedsTwoHand, romAbilityRequiredTargetStateTag, romEffectsForCast, romTargetBleedCount, romTargetHasSlashBleed, romWarriorAbilityAdjustment, romWarriorThreatMultiplier, type RomCastMechanics } from "./romCombatMechanics";
const empty: RomCastMechanics = {
sealStacks: 0, bleedCount: 0, charged: false, mainHandDagger: false, casterMaxHealth: 2_000,
targetHealthPercent: 1,
};
function cast(id: string, overrides: Partial<RomCastMechanics> = {}): readonly AbilityEffect[] {
@@ -17,6 +19,28 @@ function kinds(effects: readonly AbilityEffect[]): readonly string[] {
}
describe("RoM primary class conditional combat effects", () => {
it("recognizes source-required shields and ranged weapons across primary and elite rows", () => {
const sourceAbility = (spellId: number) => ROM_ALL_ABILITIES.find((ability) => ability.dbcSpellId === spellId)!;
for (const spellId of [491333, 491484]) expect(romAbilityNeedsShield(sourceAbility(spellId))).toBe(true);
for (const spellId of [490438, 491452, 491616, 494021, 494037, 494039, 494040, 494042, 494629]) {
expect(romAbilityNeedsRangedWeapon(sourceAbility(spellId))).toBe(true);
}
});
it("selects the Nature's Power Healing Arrows HoT and consumes one point only when available", () => {
const baseline = cast("rom-druid-rom-scout-elite-25", { naturePower: 0 });
const empowered = cast("rom-druid-rom-scout-elite-25", { naturePower: 1 });
expect(baseline).toHaveLength(1);
expect(baseline[0]?.kind).toBe("hot");
if (baseline[0]?.kind === "hot") expect(baseline[0].basePoints).toBeCloseTo(18.88);
expect(empowered).toHaveLength(2);
expect(empowered[0]?.kind).toBe("hot");
if (empowered[0]?.kind === "hot") expect(empowered[0].basePoints).toBeCloseTo(23.6);
expect(empowered[1]).toEqual({ kind: "resource-drain", amount: 1, burn: false, resource: "nature-power" });
expect(cast("rom-druid-rom-scout-elite-25", { naturePower: 0.5 }).filter((effect) => effect.kind === "resource-drain"))
.toEqual([]);
});
it("counts only active Rogue bleeds applied by the caster to that target", () => {
const timed = [
{ kind: "dot", sourceAbilityId: "rom-rogue-primary-1", sourceActorId: "rogue", targetId: "mob", remainingTicks: 3 },
@@ -73,6 +97,83 @@ describe("RoM primary class conditional combat effects", () => {
expect(kinds(cast("rom-rogue-primary-9", { mainHandDagger: true }))).toEqual(["damage", "apply-aura", "dot"]);
});
it("requires an active Vulnerable target for Keen Attack", () => {
const keenAttackId = "rom-warrior-rom-rogue-elite-35";
expect(cast(keenAttackId)).toEqual([]);
expect(kinds(cast(keenAttackId, { targetTags: ["rom-vulnerable"] }))).toContain("damage");
});
it("gates Open Flank on Vulnerable and Opportunity on the resulting Weakened state", () => {
const sourceAbility = (spellId: number) => ROM_ALL_ABILITIES.find((ability) => ability.dbcSpellId === spellId)!;
expect(romAbilityRequiredTargetStateTag(sourceAbility(491136))).toBe("rom-vulnerable");
expect(romAbilityRequiredTargetStateTag(sourceAbility(492617))).toBe("rom-weakened");
expect(cast("rom-warrior-primary-2")).toEqual([]);
const openFlank = cast("rom-warrior-primary-2", { targetTags: ["rom-vulnerable"] });
expect(openFlank.some((effect) => effect.kind === "apply-aura"
&& effect.aura.utilityTags?.includes("rom-weakened"))).toBe(true);
expect(cast("rom-warrior-rom-priest-elite-35")).toEqual([]);
expect(kinds(cast("rom-warrior-rom-priest-elite-35", { targetTags: ["rom-weakened"] })))
.toContain("damage");
});
it("only applies Tactical Attack's Slash bleed when a Slash bleed is active", () => {
expect(kinds(cast("rom-warrior-primary-3"))).toEqual(["damage"]);
expect(kinds(cast("rom-warrior-primary-3", { slashBleed: true }))).toEqual(["damage", "dot"]);
const effects = [
{ kind: "dot", sourceAbilityId: "rom-warrior-general-0", sourceActorId: "warrior", targetId: "target", remainingTicks: 1 },
{ kind: "dot", sourceAbilityId: "rom-warrior-general-0", sourceActorId: "warrior", targetId: "other", remainingTicks: 3 },
{ kind: "dot", sourceAbilityId: "rom-warrior-general-0", sourceActorId: "warrior", targetId: "target", remainingTicks: 0 },
{ kind: "dot", sourceAbilityId: "rom-rogue-primary-1", sourceActorId: "warrior", targetId: "target", remainingTicks: 3 },
];
expect(romTargetHasSlashBleed(effects, "target")).toBe(true);
expect(romTargetHasSlashBleed(effects.slice(1), "target")).toBe(false);
});
it("applies explicit Warrior passive Rage and cooldown reductions to their source skills", () => {
const sourceAbility = (spellId: number) => ROM_ALL_ABILITIES.find((ability) => ability.dbcSpellId === spellId)!;
expect(romAbilityNeedsTwoHand(sourceAbility(491142))).toBe(true);
expect(romWarriorAbilityAdjustment(sourceAbility(490121), [491458]))
.toEqual({ rageCostReduction: 10, cooldownReductionMs: 20_000 });
expect(romWarriorAbilityAdjustment(sourceAbility(490133), [493248]))
.toEqual({ rageCostReduction: 20, cooldownReductionMs: 10_000 });
expect(romWarriorAbilityAdjustment(sourceAbility(490133), [499491]))
.toEqual({ rageCostReduction: 25, cooldownReductionMs: 15_000 });
expect(romWarriorAbilityAdjustment(sourceAbility(490133), [494377]))
.toEqual({ rageCostReduction: 10, cooldownReductionMs: 10_000 });
expect(romWarriorAbilityAdjustment(sourceAbility(490121), [])).toEqual({
rageCostReduction: 0, cooldownReductionMs: 0,
});
expect(romWarriorAbilityAdjustment(sourceAbility(490053), [499827]))
.toEqual({ rageCostReduction: 10, cooldownReductionMs: 0 });
});
it("applies Warrior/Warlock's physical-damage passive and Heroic Surprise Attack's on-hit payload", () => {
const sourceAbility = (spellId: number) => ROM_ALL_ABILITIES.find((ability) => ability.dbcSpellId === spellId)!;
const rageBlow = sourceAbility(498706);
const baseDamage = romEffectsForCast(rageBlow, empty).find((effect) => effect.kind === "damage");
const trainedDamage = romEffectsForCast(rageBlow, {
...empty, learnedPassiveSpellIds: [499806],
}).find((effect) => effect.kind === "damage");
expect(baseDamage?.kind).toBe("damage");
expect(trainedDamage?.kind).toBe("damage");
if (baseDamage?.kind === "damage" && trainedDamage?.kind === "damage") {
expect(baseDamage.school).toBe("physical");
expect(trainedDamage.coefficient).toBeCloseTo(baseDamage.coefficient * 1.2);
}
const surpriseAttack = sourceAbility(490129);
const heroicEffects = romEffectsForCast(surpriseAttack, {
...empty, learnedPassiveSpellIds: [492661],
});
const heroicHit = heroicEffects.find((effect) => effect.kind === "damage");
expect(heroicHit?.kind).toBe("damage");
if (heroicHit?.kind === "damage") {
expect(heroicHit.onDamageResource).toEqual({ resource: "rage", amount: 25 });
}
expect(romWarriorThreatMultiplier(surpriseAttack, [492661])).toBe(5);
expect(romWarriorThreatMultiplier(surpriseAttack, [])).toBe(1);
});
it("keeps auxiliary source rows from adding extra hits, heals, or false control", () => {
expect(kinds(cast("rom-priest-general-2"))).toEqual(["hot"]);
expect(kinds(cast("rom-mage-primary-1"))).toEqual(["damage"]);
@@ -88,6 +189,19 @@ describe("RoM primary class conditional combat effects", () => {
]);
});
it("executes Moon Cleave's three source-authored attacks", () => {
const effects = cast("rom-warrior-primary-8");
expect(effects.filter((effect) => effect.kind === "damage")).toHaveLength(3);
expect(effects.filter((effect) => effect.kind === "scripted")).toHaveLength(1);
});
it("only adds The Final Battle's second hit below 30% target health", () => {
const atThreshold = cast("rom-warrior-rom-scout-elite-50", { targetHealthPercent: 0.3 });
const belowThreshold = cast("rom-warrior-rom-scout-elite-50", { targetHealthPercent: 0.29 });
expect(atThreshold.filter((effect) => effect.kind === "damage")).toHaveLength(1);
expect(belowThreshold.filter((effect) => effect.kind === "damage")).toHaveLength(2);
});
it("holds Shield of Discipline for 30 seconds and reflects only on being hit", () => {
const effects = cast("rom-knight-general-7");
expect(kinds(effects)).toEqual(["apply-aura", "apply-aura", "apply-aura"]);