Update Healer Man server

This commit is contained in:
phenom committed 2026-09-20 11:23:13 -04:00
1 parent 1fedb9368d
commit cc3b2fa4d5
27 files changed
+1988 -83

No files matched your search

+91 -3
View File
@@ -211,7 +211,7 @@ describe("shared combat rotations", () => {
{ classId: "priest" as const, specialization: "Discipline", id: "priest:discipline", actions: ["Penance", "Power Word: Shield", "Flash Heal", "Greater Heal", "Renew", "Dispel Magic", "Cure Disease", "Resurrection", "Pain Suppression", "Inner Focus", "Power Infusion", "Shadowfiend"] },
{ classId: "hunter" as const, specialization: "Marksmanship", id: "hunter:marksmanship", actions: ["Call Pet", "Aspect of the Dragonhawk", "Serpent Sting", "Chimera Shot", "Aimed Shot", "Arcane Shot", "Steady Shot", "Kill Shot", "Rapid Fire", "Readiness", "Misdirection", "Feign Death", "Silencing Shot", "Disengage"] },
{ classId: "mage" as const, specialization: "Fire", id: "mage:fire", actions: ["Molten Armor", "Living Bomb", "Fireball", "Scorch", "Fire Blast", "Combustion", "Mirror Image", "Counterspell", "Fire Ward", "Frost Ward", "Ice Block", "Blink"] },
{ classId: "rogue" as const, specialization: "Assassination", id: "rogue:assassination", actions: ["Instant Poison", "Deadly Poison", "Mutilate", "Rupture", "Envenom", "Cold Blood", "Kick", "Tricks of the Trade", "Evasion", "Cloak of Shadows", "Vanish"] },
{ classId: "rogue" as const, specialization: "Assassination", id: "rogue:assassination", actions: ["Instant Poison", "Deadly Poison", "Mutilate", "Shiv", "Rupture", "Envenom", "Cold Blood", "Kick", "Tricks of the Trade", "Evasion", "Cloak of Shadows", "Vanish"] },
];
for (const contract of contracts) {
const profile = rotationProfileFor(contract.classId, contract.id)!;
@@ -263,7 +263,7 @@ describe("shared combat rotations", () => {
const hunter = context({ classId: "hunter", specializationId: "hunter:marksmanship", primaryResource: "mana", resources: { mana: 10_000 }, maximumResources: { mana: 10_000 }, friendlyTargets: [{ id: "tank", health: 1_000, maxHealth: 1_000, role: "tank" }] });
expect(evaluateCombatRotation({ ...hunter, abilities: [misdirection], hostileTarget: { id: "boss", health: 100_000, maxHealth: 100_000, boss: true } }))
.toMatchObject({ ability: { name: "Misdirection" }, targetId: "tank" });
expect(evaluateCombatRotation({ ...hunter, abilities: [disengage], moving: true })?.ability.name).toBe("Disengage");
expect(evaluateCombatRotation({ ...hunter, abilities: [disengage], needsEscape: true })?.ability.name).toBe("Disengage");
});
it("keeps specialization talent actions isolated", () => {
@@ -310,11 +310,83 @@ describe("shared combat rotations", () => {
expect(defensive, `${row.specializationId}:${row.defensive}`).toBeDefined();
const defensiveContext = row.defensive === "Feign Death"
? { ...shared, abilities: [defensive], hasDangerousThreat: true }
: { ...shared, abilities: [defensive], health: 250 };
: row.defensive === "Evasion"
? { ...shared, abilities: [defensive], health: 400, enemyUsesMeleeAttacks: true }
: { ...shared, abilities: [defensive], health: 250 };
expect(evaluateCombatRotation(defensiveContext)?.ability.name).toBe(row.defensive);
}
});
it("prepares emergency healing with Inner Focus before direct or area healing", () => {
const talents = runtimePartyTalentRanksForLevel("priest", "Discipline", 80);
const innerFocus = learnedAbilityForBuild(ability("priest", "Inner Focus"), 80, talents)!;
const priest = context({
classId: "priest",
specializationId: "priest:discipline",
role: "healer",
primaryResource: "mana",
resources: { mana: 10_000 },
maximumResources: { mana: 10_000 },
abilities: [innerFocus],
});
expect(evaluateCombatRotation({
...priest,
friendlyTargets: [{ id: "tank", health: 400, maxHealth: 1_000, role: "tank" }],
})).toMatchObject({ ability: { name: "Inner Focus" }, ruleId: "prepare-emergency-heal" });
expect(evaluateCombatRotation({
...priest,
friendlyTargets: [
{ id: "tank", health: 600, maxHealth: 1_000, role: "tank" },
{ id: "rogue", health: 600, maxHealth: 1_000, role: "damage" },
],
})).toMatchObject({ ability: { name: "Inner Focus" }, ruleId: "prepare-area-heal" });
expect(evaluateCombatRotation({
...priest,
friendlyTargets: [{ id: "tank", health: 800, maxHealth: 1_000, role: "tank" }],
})).toBeNull();
});
it("uses Rogue defenses for boss threat, melee danger, and lethal threat resets", () => {
const talents = runtimePartyTalentRanksForLevel("rogue", "Assassination", 80);
const evasion = learnedAbilityForBuild(ability("rogue", "Evasion"), 80, talents)!;
const vanish = learnedAbilityForBuild(ability("rogue", "Vanish"), 80, talents)!;
const rogue = context({
classId: "rogue",
specializationId: "rogue:assassination",
primaryResource: "energy",
resources: { energy: 100 },
maximumResources: { energy: 100 },
hostileTarget: { id: "boss", health: 100_000, maxHealth: 100_000, boss: true },
});
expect(evaluateCombatRotation({ ...rogue, abilities: [evasion], hasDangerousThreat: true })?.ability.name)
.toBe("Evasion");
expect(evaluateCombatRotation({ ...rogue, abilities: [evasion], health: 400,
enemyUsesMeleeAttacks: true })?.ability.name).toBe("Evasion");
expect(evaluateCombatRotation({ ...rogue, abilities: [evasion], incomingMeleeDanger: true }))
.toMatchObject({ ability: { name: "Evasion" }, ruleId: "preemptive-melee-evasion" });
expect(evaluateCombatRotation({ ...rogue, abilities: [vanish], hasDangerousThreat: true })?.ability.name)
.toBe("Vanish");
expect(evaluateCombatRotation({ ...rogue, abilities: [vanish], health: 200 }))
.toMatchObject({ ability: { name: "Vanish" }, ruleId: "emergency-vanish" });
});
it("uses Blink and Disengage to create distance, while Fire Blast covers movement", () => {
const mage = context({ classId: "mage", specializationId: "mage:fire", primaryResource: "mana",
resources: { mana: 10_000 }, maximumResources: { mana: 10_000 } });
expect(evaluateCombatRotation({ ...mage, abilities: [ability("mage", "Blink")], needsEscape: true })?.ability.name)
.toBe("Blink");
expect(evaluateCombatRotation({ ...mage, abilities: [ability("mage", "Fire Blast")], moving: true })?.ability.name)
.toBe("Fire Blast");
const talents = runtimePartyTalentRanksForLevel("hunter", "Marksmanship", 80);
const disengage = learnedAbilityForBuild(ability("hunter", "Disengage"), 80, talents)!;
const hunter = context({ classId: "hunter", specializationId: "hunter:marksmanship", primaryResource: "mana",
resources: { mana: 10_000 }, maximumResources: { mana: 10_000 }, abilities: [disengage], needsEscape: true });
expect(evaluateCombatRotation(hunter)?.ability.name).toBe("Disengage");
});
it("uses Marksmanship Readiness only on a durable target", () => {
const talents = runtimePartyTalentRanksForLevel("hunter", "Marksmanship", 80);
const readiness = learnedAbilityForBuild(ability("hunter", "Readiness"), 80, talents)!;
@@ -344,6 +416,22 @@ describe("shared combat rotations", () => {
expect(finishing?.ability.id).toBe(finisher.id);
});
it("uses Envenom as the Playerbots Assassination finisher ahead of Rupture", () => {
const talents = runtimePartyTalentRanksForLevel("rogue", "Assassination", 80);
const rupture = learnedAbilityForBuild(ability("rogue", "Rupture"), 80, talents)!;
const envenom = learnedAbilityForBuild(ability("rogue", "Envenom"), 80, talents)!;
const assassination = context({
specializationId: "rogue:assassination",
comboPoints: 5,
comboTargetId: "target",
abilities: [envenom, rupture],
});
expect(evaluateCombatRotation(assassination)?.ability.name).toBe("Envenom");
expect(evaluateCombatRotation({ ...assassination, maintainedAbilityIds: new Set([rupture.id]) })?.ability.name)
.toBe("Envenom");
});
it("reacts before throughput and conserves long offensive cooldowns for durable targets", () => {
const kick = ability("rogue", "Kick");
const strike = ability("rogue", "Sinister Strike");