forked from phenom/healer-man
Update Healer Man server
This commit is contained in:
1 parent
4c768fbec6
commit
b16821e92e
330 files changed
+35406
-1760
No files matched your search
@@ -5,8 +5,18 @@ import {
|
||||
} from "./talentRuntimeCatalog";
|
||||
import { talentBalanceOverride } from "./balanceTuning";
|
||||
import { wow335MageFlameThrowingProfile } from "./wow335MageFlameThrowingRuntime";
|
||||
import {
|
||||
WOW335_MAGE_IMPROVED_FIRE_BLAST_TALENT_ID,
|
||||
wow335MageImprovedFireBlastCooldownReductionMs,
|
||||
wow335MageImprovedFireBlastDamageBonusPercent,
|
||||
} from "./wow335MageImprovedFireBlastRuntime";
|
||||
import { wow335DruidTalentModifierOverrides } from "./wow335DruidTalentRuntime";
|
||||
import { WOW335_DRUID_PREDATORY_INSTINCTS_TALENT_ID } from "./wow335DruidPredatoryInstinctsRuntime";
|
||||
import { wow335ShamanConvectionModifiersForRank } from "./wow335ShamanTalentRuntime";
|
||||
import {
|
||||
WOW335_SHAMAN_ELEMENTAL_WARDING_TALENT_ID,
|
||||
wow335ShamanElementalWardingModifierForRank,
|
||||
} from "./wow335ShamanElementalWardingRuntime";
|
||||
|
||||
export type TalentModifierKind =
|
||||
| "damage-percent"
|
||||
@@ -67,6 +77,9 @@ const EXPLICIT_ACTIVATION_TALENTS = new Set([
|
||||
// Brain Freeze mixes a conditional next Fireball/Frostfire Bolt proc with
|
||||
// separate school and target riders; the proc bonus is applied at cast time.
|
||||
"Brain Freeze",
|
||||
// Netherwind Presence haste exists only on eligible spell-triggered stacks;
|
||||
// the helper/runtime applies that temporary state instead of parsing it as passive haste.
|
||||
"Netherwind Presence",
|
||||
// Aura Mastery and Divine Favor carry scoped next-cast/aura effects. Their
|
||||
// activation tooltips must not be parsed as persistent global modifiers.
|
||||
"Aura Mastery",
|
||||
@@ -81,6 +94,9 @@ const EXPLICIT_ACTIVATION_TALENTS = new Set([
|
||||
// These Shaman talents activate temporary effects from combat events;
|
||||
// their conditional percentages are applied by the Shaman runtime.
|
||||
"Elemental Focus",
|
||||
// The melee/ranged critical-strike rider only exists after an elemental
|
||||
// spell critical; the Shaman runtime applies it as a timed aura.
|
||||
"Elemental Devastation",
|
||||
"Reverberation",
|
||||
"Maelstrom Weapon",
|
||||
// Tidal Waves only affects casts while its earned charge aura is active.
|
||||
@@ -90,12 +106,24 @@ const EXPLICIT_ACTIVATION_TALENTS = new Set([
|
||||
"Convection",
|
||||
// Riptide's 25% rider only applies to the next Chain Heal on its HoT target.
|
||||
"Riptide",
|
||||
// Frost Warding combines an armor/resistance modifier with a Ward-only
|
||||
// damage proc. Both clauses need rank/aura/event scoping in its runtime.
|
||||
"Frost Warding",
|
||||
// Illumination's cooldown clause is paired with a critical-heal Mana proc;
|
||||
// its exact rank behavior belongs to the dedicated event helper.
|
||||
"Illumination",
|
||||
// Torment the Weak applies only to named damage spells and only against a
|
||||
// target that is currently slowed; its conditional part is target-aware.
|
||||
"Torment the Weak",
|
||||
// Feral Instinct has one named damage bonus plus two cast-state clauses;
|
||||
// only the named Swipe/Ravage clause is a persistent scalar modifier.
|
||||
"Feral Instinct",
|
||||
// Master Shapeshifter has four separate form-specific bonuses; the active
|
||||
// shapeshift decides which single clause the Druid runtime can apply.
|
||||
"Master Shapeshifter",
|
||||
// Paladin Vengeance is keyed by talent ID below because Druid has a distinct
|
||||
// same-named talent with different behavior.
|
||||
"talent-1402",
|
||||
]);
|
||||
|
||||
/**
|
||||
@@ -103,8 +131,9 @@ const EXPLICIT_ACTIVATION_TALENTS = new Set([
|
||||
* as always-on scalar bonuses merely because their tooltip contains phrases
|
||||
* such as "reduces cast time" or "reducing mana cost".
|
||||
*/
|
||||
export function talentUsesExplicitActivation(talentName: string): boolean {
|
||||
return EXPLICIT_ACTIVATION_TALENTS.has(talentName);
|
||||
export function talentUsesExplicitActivation(talentName: string, talentId?: string): boolean {
|
||||
return EXPLICIT_ACTIVATION_TALENTS.has(talentName)
|
||||
|| (talentId !== undefined && EXPLICIT_ACTIVATION_TALENTS.has(talentId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,10 +238,25 @@ export function talentModifiersForRanks(ranks: TalentRanks, classId?: ClassId):
|
||||
// by the Druid cast runtime.
|
||||
return wow335DruidTalentModifierOverrides(active.talentId, active.rank, active.description);
|
||||
}
|
||||
if (classId === "druid" && active.talentId === WOW335_DRUID_PREDATORY_INSTINCTS_TALENT_ID) {
|
||||
// Both clauses are gated by Cat Form; the AoE rider also needs authored
|
||||
// attack delivery metadata, so the generic scalar parser cannot apply them.
|
||||
return [];
|
||||
}
|
||||
// Elemental Oath's generated rank text repeats one crit bonus for the
|
||||
// owner and the party. CombatStore applies the rank once to each spell
|
||||
// attack/heal source, so the broad scalar parser must not apply it again.
|
||||
if (classId === "shaman" && active.talentId === "talent-2049") return [];
|
||||
if (classId === "shaman" && active.talentId === WOW335_SHAMAN_ELEMENTAL_WARDING_TALENT_ID) {
|
||||
const modifier = wow335ShamanElementalWardingModifierForRank(active.rank);
|
||||
return modifier ? [{
|
||||
...modifier,
|
||||
sourceTalentId: active.talentId,
|
||||
sourceTalentName: active.talentName,
|
||||
rank: active.rank,
|
||||
description: active.description,
|
||||
}] : [];
|
||||
}
|
||||
if (classId === "shaman" && active.talentId === "talent-564") {
|
||||
return wow335ShamanConvectionModifiersForRank(active.rank).map((modifier) => ({
|
||||
...modifier,
|
||||
@@ -235,6 +279,30 @@ export function talentModifiersForRanks(ranks: TalentRanks, classId?: ClassId):
|
||||
description: active.description,
|
||||
}] : [];
|
||||
}
|
||||
if (classId === "mage" && active.talentId === WOW335_MAGE_IMPROVED_FIRE_BLAST_TALENT_ID) {
|
||||
const cooldownReductionMs = wow335MageImprovedFireBlastCooldownReductionMs(active.rank, "Fire Blast");
|
||||
const damageBonusPercent = wow335MageImprovedFireBlastDamageBonusPercent(active.rank);
|
||||
return [
|
||||
...(cooldownReductionMs > 0 ? [{
|
||||
kind: "cooldown-flat-ms" as const,
|
||||
amount: -cooldownReductionMs,
|
||||
unit: "milliseconds" as const,
|
||||
scope: "cooldown of your Fire Blast spell",
|
||||
}] : []),
|
||||
...(damageBonusPercent > 0 ? [{
|
||||
kind: "damage-percent" as const,
|
||||
amount: damageBonusPercent,
|
||||
unit: "percent" as const,
|
||||
scope: "damage of your Fire Blast",
|
||||
}] : []),
|
||||
].map((modifier) => ({
|
||||
...modifier,
|
||||
sourceTalentId: active.talentId,
|
||||
sourceTalentName: active.talentName,
|
||||
rank: active.rank,
|
||||
description: active.description,
|
||||
}));
|
||||
}
|
||||
// Molten Fury is active only while the individual target is below 35%
|
||||
// health. The target-health predicate is applied by the Mage damage path,
|
||||
// so keep its generated rank amount out of always-on scalar modifiers.
|
||||
@@ -265,7 +333,7 @@ export function talentModifiersForRanks(ranks: TalentRanks, classId?: ClassId):
|
||||
description: active.description,
|
||||
}];
|
||||
}
|
||||
if (talentUsesExplicitActivation(active.talentName)) return [];
|
||||
if (talentUsesExplicitActivation(active.talentName, active.talentId)) return [];
|
||||
// Its healing and damage bonuses only apply below a target-health threshold;
|
||||
// the scalar parser cannot encode that condition. CombatStore applies the
|
||||
// rank-aware bonus at the individual target instead.
|
||||
@@ -381,6 +449,8 @@ function modifierApplies(modifier: TalentModifier, context: TalentModifierContex
|
||||
const scope = modifier.scope.toLowerCase();
|
||||
const tags = new Set((context.tags ?? []).map((tag) => tag.toLowerCase()));
|
||||
if (scope.includes("damaging spell") && !tags.has("damage")) return false;
|
||||
if (scope.includes("with a channeling or cast time")
|
||||
&& !tags.has("cast-time") && !tags.has("channel")) return false;
|
||||
if (scope.includes("instant") && !tags.has("instant")) return false;
|
||||
const coversDirectAndPeriodic = /\bdirect and periodic\b/.test(scope);
|
||||
if (scope.includes("periodic") && !coversDirectAndPeriodic && !tags.has("periodic")) return false;
|
||||
|
||||
Reference in new issue
Block a user