Update Healer Man server

This commit is contained in:
phenom committed 2026-10-02 10:38:12 -04:00
1 parent 4c768fbec6
commit b16821e92e
330 files changed
+35406 -1760

No files matched your search

+73 -7
View File
@@ -140,13 +140,19 @@ export interface AbilityAmountScaling {
readonly bonusPowerCoefficient?: number;
readonly attackPowerCoefficient?: number;
readonly rangedAttackPowerCoefficient?: number;
/** Add a fraction of the source character's Intelligence to the resolved amount. */
readonly intellectCoefficient?: number;
readonly spiritCoefficient?: number;
/** Add a fraction of the source character's Stamina to the resolved amount. */
readonly staminaCoefficient?: number;
/** Add main-hand weapon DPS multiplied by this value to the resolved amount. */
readonly weaponDpsCoefficient?: number;
readonly sourceLevel?: number;
readonly maxScalingLevel?: number;
}
export interface AbilityHealingRecipients {
readonly recipient?: "caster" | "ability-target";
readonly recipient?: "caster" | "ability-target" | "pet";
readonly scope?: "party";
readonly center?: "caster" | "ability-target";
readonly radius?: number;
@@ -170,7 +176,7 @@ export type AbilityEffect =
/** Optional stack-based payload used by poison-consuming finishers. */
readonly poison?: import("./roguePoison").RoguePoisonFinisherScaling;
}
| ({ readonly kind: "heal"; readonly coefficient: number; readonly percentMaxHealth?: number; readonly percentOfDamage?: number } & AbilityAmountScaling & AbilityHealingRecipients)
| ({ readonly kind: "heal"; readonly coefficient: number; readonly percentMaxHealth?: number; readonly percentOfDamage?: number; readonly requiresLandedHit?: boolean } & AbilityAmountScaling & AbilityHealingRecipients)
| ({ readonly kind: "shield"; readonly coefficient: number; readonly durationMs: number } & AbilityAmountScaling & AbilityHealingRecipients)
| ({ readonly kind: "dot"; readonly coefficient: number; readonly school?: DamageSchool; readonly ticks: number; readonly intervalMs: number; readonly startsImmediately?: boolean; readonly tag?: string } & AbilityAmountScaling & AbilityDamageResource)
| ({ readonly kind: "hot"; readonly coefficient: number; readonly ticks: number; readonly intervalMs: number; readonly percentMaxHealth?: number } & AbilityAmountScaling & AbilityHealingRecipients)
@@ -185,7 +191,14 @@ export type AbilityEffect =
/** Source-defined party absorb area carried by a stationary Death Knight zone. */
readonly antiMagicZone?: { readonly radius: number; readonly capacityBase: number; readonly capacityPerAttackPower: number; readonly absorbFraction: number; readonly intervalMs: number };
}
| { readonly kind: "resource"; readonly amount: number; readonly resource?: ResourceType; readonly percentage?: boolean }
| {
readonly kind: "resource";
readonly amount: number;
readonly resource?: ResourceType;
readonly percentage?: boolean;
/** Party-scoped resource effects use the ability's radius and targeting shape. */
readonly scope?: "target" | "party";
}
| { readonly kind: "combo"; readonly amount: number }
| { readonly kind: "taunt"; readonly durationMs: number; readonly maxTargets?: number }
| { readonly kind: "status"; readonly status: MobStatusKind; readonly durationMs: number; readonly magnitude?: number; readonly maxTargets?: number; readonly breakOnDamage?: boolean }
@@ -196,6 +209,8 @@ export type AbilityEffect =
readonly recipient?: "ability-target" | "caster" | "pet";
/** Party/raid area auras are applied to the player and every active party member. */
readonly scope?: "target" | "party";
/** Optional range for a party aura sourced by a nearby-target proc. */
readonly radius?: number;
/** Some party effects benefit allies but exclude the caster. */
readonly excludeCaster?: boolean;
readonly control?: AbilityAuraControl;
@@ -203,6 +218,10 @@ export type AbilityEffect =
readonly sourceAuraType: number;
readonly miscValue: number;
readonly triggerSpellId: number;
/** Resolve this rider only if the cast landed at least one direct hit. */
readonly requiresLandedHit?: boolean;
/** Resolve this rider only if the cast landed at least one direct critical hit. */
readonly requiresCriticalHit?: boolean;
}
| {
readonly kind: "dispel";
@@ -227,6 +246,8 @@ export type AbilityEffect =
readonly kind: "movement";
readonly movement: "teleport" | "leap" | "charge" | "knockback" | "sanctuary";
readonly toward: "target" | "destination" | "away";
/** Source row movement distance in yards when the spell supplies one. */
readonly distance?: number;
}
| { readonly kind: "dispel-mechanic"; readonly mechanic: number; readonly maxCount: number }
| { readonly kind: "pet"; readonly action: "summon" | "call" | "recall" | "dismiss" | "resurrect" | "feed" | "tame"; readonly creatureId?: number }
@@ -340,6 +361,15 @@ export interface AbilityDefinition {
readonly talentNodeId?: string;
/** Legacy CoA source identifier; `abilityTalentNodeId` normalizes it. */
readonly talentEntryId?: number;
/** A pair-only upgrade applied after its source passive unlock level. */
readonly buildUpgrade?: AbilityBuildUpgrade;
}
export interface AbilityBuildUpgrade {
readonly unlockLevel: number;
readonly values: Partial<Pick<AbilityDefinition,
"description" | "target" | "castMode" | "castTimeMs" | "cooldownMs" | "cost" | "costs" | "effects"
>>;
}
export interface ClassResourceProfile {
@@ -1063,7 +1093,37 @@ export function abilitiesForCharacter(
ability.romSkillGroup === "general" || (ability.romSkillGroup === "passive" && ability.id.includes("-general-"))
));
const elites = romEliteAbilities.filter((ability) => ability.classId === classId && ability.secondaryClassId === secondaryClassId);
return [...primary, ...secondaryGeneral, ...elites];
const characterAbilities = [...primary, ...secondaryGeneral, ...elites];
if (classId !== "rom-warlock" || secondaryClassId !== "rom-priest") return characterAbilities;
return characterAbilities.map((ability) => {
if (ability.classId === "rom-priest" && ability.dbcSpellId === 490256) {
return {
...ability,
buildUpgrade: {
unlockLevel: 30,
values: {
castMode: "instant",
castTimeMs: 0,
cooldownMs: 5_000,
description: "Instant cast with a 5-second cooldown. Restores 1 Psi. A critical hit activates Healing Fortune. The simulator cannot redirect an ally-targeted cast to that ally's hostile target.",
},
},
};
}
if (ability.classId === "rom-warlock" && ability.dbcSpellId === 497958) {
return {
...ability,
buildUpgrade: {
unlockLevel: 15,
values: {
target: "any",
description: "Can target a hostile enemy to deal damage or a friendly ally to heal them when Healing Fortune is learned. Ally-target redirection is not modeled.",
},
},
};
}
return ability;
});
}
/** Return the RuneWaker Nature Power cap granted by the learned Brilliance passive. */
@@ -1140,6 +1200,12 @@ export function learnedAbilityForBuild(
return abilityAtLevel(ability, playerLevel, ability.talentEntryId === undefined ? undefined : learnedRank);
}
function applyBuildUpgradeAtLevel(ability: AbilityDefinition, playerLevel: number): AbilityDefinition {
return ability.buildUpgrade && playerLevel >= ability.buildUpgrade.unlockLevel
? { ...ability, ...ability.buildUpgrade.values }
: ability;
}
export function abilityAtLevel(
ability: AbilityDefinition,
playerLevel: number,
@@ -1148,8 +1214,8 @@ export function abilityAtLevel(
const rank = abilityRankForLevel(ability, playerLevel, learnedTalentRank);
const effects = rank?.effects ?? ability.effects;
const target = effects.length > 0 && effects.every((effect) => effect.kind === "dispel" && effect.relationship === "target" && effect.mode === "cleanse") ? "any" : ability.target;
if (!rank) return target === ability.target ? ability : { ...ability, target };
return {
if (!rank) return applyBuildUpgradeAtLevel(target === ability.target ? ability : { ...ability, target }, playerLevel);
return applyBuildUpgradeAtLevel({
...ability,
target,
dbcSpellId: rank.spellId,
@@ -1167,7 +1233,7 @@ export function abilityAtLevel(
targeting: rank.targeting ?? ability.targeting,
castRules: rank.castRules ?? ability.castRules,
runeCost: rank.runeCost ?? ability.runeCost,
};
}, playerLevel);
}
export function isAbilityUnlocked(ability: AbilityDefinition, playerLevel: number): boolean {