Release Healer Man 0.1.49

This commit is contained in:
phenom committed 2026-09-17 09:13:15 -04:00
1 parent 39425f2494
commit 612170afd0
91 files changed
+2278 -263

No files matched your search

+24 -3
View File
@@ -67,8 +67,10 @@ interface Wow335Chain {
readonly id: string;
readonly name: string;
readonly iconBasename: string;
readonly iconPath?: string;
readonly sourceUnlockLevel: number;
readonly passive?: boolean;
readonly talentNodeId?: string;
readonly ranks: readonly Wow335Rank[];
}
@@ -309,6 +311,7 @@ function controlFromAura(auraType: number): AbilityAuraControl | undefined {
}
function exclusiveGroupForAbility(classId: BaseClassId, abilityName: string): string | undefined {
if (["Arcane Intellect", "Arcane Brilliance"].includes(abilityName)) return "mage:intellect";
if (/\bStance$/i.test(abilityName)) return `${classId}:stance`;
if (/^Seal of\b/i.test(abilityName)) return "paladin:seal";
if (/\bAura$/i.test(abilityName)) return `${classId}:aura`;
@@ -318,6 +321,7 @@ function exclusiveGroupForAbility(classId: BaseClassId, abilityName: string): st
if (/\b(?:Bear|Cat|Travel|Aquatic|Flight|Tree of Life) Form$/i.test(abilityName)) return "druid:form";
if (/\bArmor$/i.test(abilityName)) return `${classId}:armor`;
if (/\bPresence$/i.test(abilityName)) return "death-knight:presence";
if (["Lightning Shield", "Water Shield"].includes(abilityName)) return "shaman:elemental-shield";
if (/ Weapon$/i.test(abilityName)) return "shaman:weapon-enchant";
return undefined;
}
@@ -758,6 +762,11 @@ function classScriptEffects(
case "rogue:Cloak of Shadows": return [{
kind: "dispel", mode: "cleanse", relationship: "friendly", categories: ["magic"], maxCount: 10,
}];
// Both spells place a channel aura whose actual pulses live in non-player
// trigger spells. Keep the imported cast/cost/rank data and supply only the
// missing executable payload.
case "priest:Mind Flay": return [{ kind: "damage", coefficient: 0.9 }];
case "priest:Penance": return [{ kind: "damage", coefficient: 0.8 }];
case "priest:Shadowfiend": return [{ kind: "summon", creatureId: 19668, coefficient: 0.5, durationMs: 15_000 }];
case "priest:Prayer of Mending": return [{
kind: "apply-aura",
@@ -815,6 +824,11 @@ function classScriptEffects(
case "hunter:Misdirection": return [{ kind: "threat", mode: "redirect", amount: 100 }];
case "rogue:Tricks of the Trade": return [{ kind: "threat", mode: "redirect", amount: 100 }];
case "mage:Blink": return [{ kind: "movement", movement: "leap", toward: "away" }];
// Arcane Missiles stores its per-tick payload in a server-triggered child spell
// that is not a player-facing SkillLine entry. Model the channel payload here so
// both players and shared-rotation consumers execute the imported spell instead
// of completing an inert channel.
case "mage:Arcane Missiles": return [{ kind: "damage", coefficient: 1.2 }];
case "mage:Arcane Blast": return [{
kind: "apply-aura",
recipient: "caster",
@@ -855,6 +869,7 @@ function effectsForRank(classId: BaseClassId, rank: Wow335Rank, abilityName: str
effects.push({
kind: "apply-aura",
aura,
...(effect.implicitTargetA === 1 ? { recipient: "caster" as const } : {}),
...([35, 65].includes(effect.effectType) ? { scope: "party" as const } : {}),
...(controlFromAura(effect.auraType) ? { control: controlFromAura(effect.auraType) } : {}),
sourceEffectType: effect.effectType,
@@ -1108,8 +1123,12 @@ function effectsForRank(classId: BaseClassId, rank: Wow335Rank, abilityName: str
label: `DBC combat effect ${effect.effectType}${effect.auraType ? ` / aura ${effect.auraType}` : ""}`,
});
}
if (!effects.some((effect) => ["damage", "heal", "dot", "hot", "shield"].includes(effect.kind))) {
if (/\b(?:heals?|restore|mend|rejuvenat)\w*\b/i.test(rank.description)) {
const auraOnlyStance = classId === "death-knight"
&& ["Blood Presence", "Frost Presence", "Unholy Presence"].includes(abilityName);
if (!auraOnlyStance && !effects.some((effect) => ["damage", "heal", "dot", "hot", "shield"].includes(effect.kind))) {
if (classId === "death-knight" && abilityName === "Death Coil") {
effects.unshift({ kind: "damage", coefficient: 1 });
} else if (/\b(?:heals?|restore|mend|rejuvenat)\w*\b/i.test(rank.description)) {
effects.unshift({ kind: "heal", coefficient: 1 });
} else if (/\b(?:deals?|causes?|inflicts?)\b[^.]*\bdamage\b/i.test(rank.description)) {
effects.unshift({ kind: "damage", coefficient: 1 });
@@ -1265,7 +1284,7 @@ function abilityDefinition(classId: BaseClassId, chain: Wow335Chain): AbilityDef
name: chain.name,
unlockLevel: starterUnlockLevel(classId, chain.sourceUnlockLevel, chain.name),
sourceUnlockLevel: chain.sourceUnlockLevel,
icon: `/assets/ui/spells/${chain.iconBasename}.png`,
icon: chain.iconPath ?? `/assets/ui/spells/${chain.iconBasename}.png`,
sigil: sigil(chain.name),
description: first.description,
target,
@@ -1282,6 +1301,7 @@ function abilityDefinition(classId: BaseClassId, chain: Wow335Chain): AbilityDef
effects: first.effects,
passive: chain.passive,
source: "wow335-progression",
talentNodeId: chain.talentNodeId,
ranks,
};
}
@@ -1296,6 +1316,7 @@ function abilityNameRadius(abilityName: string): number {
function abilityNameTarget(abilityName: string): AbilityTarget | undefined {
if (["Divine Hymn", "Tranquility", "Holy Nova", "Hellfire", "Army of the Dead"].includes(abilityName)) return "self";
if (abilityName === "Death Coil") return "hostile";
return undefined;
}