forked from phenom/healer-man
Release Healer Man 0.1.6
This commit is contained in:
1 parent
55cfd43d66
commit
b12fb84859
96 files changed
+10874
-609
No files matched your search
@@ -0,0 +1,104 @@
|
||||
import type {
|
||||
AbilityAiHints,
|
||||
AbilityDefinition,
|
||||
AbilityRuneCost,
|
||||
AbilityTargetingRules,
|
||||
} from "./abilityCatalog";
|
||||
|
||||
const CHAIN_ABILITIES = new Set(["chain heal", "chain lightning", "prayer of mending"]);
|
||||
const GROUND_ABILITIES = new Set([
|
||||
"blizzard", "rain of fire", "flamestrike", "death and decay", "consecration",
|
||||
"volley", "hurricane", "earthquake", "meteor", "healing rain",
|
||||
]);
|
||||
const CONE_ABILITIES = new Set([
|
||||
"cone of cold", "dragon's breath", "dragons breath", "shockwave", "cleave",
|
||||
"swipe", "swipe (cat)", "fan of knives", "whirlwind",
|
||||
]);
|
||||
const BEHIND_ABILITIES = new Set(["backstab", "ambush", "garrote", "ravage", "shred", "pounce"]);
|
||||
const MOVEMENT_CAST_ABILITIES = new Set(["scorch", "steady shot", "spiritwalker's grace"]);
|
||||
const EMERGENCY_HEAL_ABILITIES = new Set([
|
||||
"flash heal", "flash of light", "swiftmend", "lay on hands", "nature's swiftness",
|
||||
]);
|
||||
|
||||
const RUNE_COSTS_BY_SPELL_ID: Readonly<Record<number, AbilityRuneCost>> = Object.freeze({
|
||||
49909: { frost: 1 }, 45524: { frost: 1 }, 49184: { frost: 1 }, 47528: { frost: 1 },
|
||||
49921: { unholy: 1 }, 55271: { unholy: 1 }, 49222: { unholy: 1 }, 51052: { unholy: 1 },
|
||||
49930: { blood: 1 }, 55262: { blood: 1 }, 49941: { blood: 1 }, 50842: { blood: 1 },
|
||||
48982: { blood: 1 }, 49005: { blood: 1 },
|
||||
49924: { frost: 1, unholy: 1 }, 51425: { frost: 1, unholy: 1 },
|
||||
49938: { blood: 1, frost: 1, unholy: 1 },
|
||||
});
|
||||
const RUNE_COSTS_BY_NAME: Readonly<Record<string, AbilityRuneCost>> = Object.freeze({
|
||||
"icy touch": { frost: 1 }, "chains of ice": { frost: 1 }, "howling blast": { frost: 1 }, "mind freeze": { frost: 1 },
|
||||
"plague strike": { unholy: 1 }, "scourge strike": { unholy: 1 }, "bone shield": { unholy: 1 }, "anti-magic zone": { unholy: 1 },
|
||||
"blood strike": { blood: 1 }, "heart strike": { blood: 1 }, "blood boil": { blood: 1 }, "pestilence": { blood: 1 },
|
||||
"rune tap": { blood: 1 }, "mark of blood": { blood: 1 },
|
||||
"death strike": { frost: 1, unholy: 1 }, "obliterate": { frost: 1, unholy: 1 },
|
||||
"death and decay": { blood: 1, frost: 1, unholy: 1 },
|
||||
});
|
||||
|
||||
export function targetingRulesForAbility(ability: AbilityDefinition): AbilityTargetingRules {
|
||||
if (ability.targeting) return ability.targeting;
|
||||
const name = ability.name.toLowerCase();
|
||||
const shape = CHAIN_ABILITIES.has(name)
|
||||
? "chain"
|
||||
: GROUND_ABILITIES.has(name) || name.endsWith(" trap")
|
||||
? "ground"
|
||||
: CONE_ABILITIES.has(name) || name.endsWith(" breath")
|
||||
? "cone"
|
||||
: ability.radius && ability.radius > 0
|
||||
? "circle"
|
||||
: "unit";
|
||||
const hostile = ability.target === "hostile" || ability.target === "any";
|
||||
return Object.freeze({
|
||||
shape,
|
||||
requiresLineOfSight: ability.target !== "self",
|
||||
// WoW friendly spells are deliberately facing independent.
|
||||
requiresFacing: hostile && shape !== "circle" && shape !== "ground",
|
||||
requiresBehindTarget: hostile && BEHIND_ABILITIES.has(name),
|
||||
attackArcRadians: shape === "cone" ? Math.PI / 2 : Math.PI,
|
||||
...(ability.radius ? { radius: ability.radius } : {}),
|
||||
...(shape === "chain" ? { maximumTargets: 3, jumpRange: 12 } : {}),
|
||||
...(shape === "ground" ? { groundRange: ability.range.max, radius: ability.radius ?? 8 } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
export function canAbilityCastWhileMoving(ability: AbilityDefinition): boolean {
|
||||
return ability.castRules?.canCastWhileMoving ?? MOVEMENT_CAST_ABILITIES.has(ability.name.toLowerCase());
|
||||
}
|
||||
|
||||
export function abilityPushbackResistance(ability: AbilityDefinition): number {
|
||||
if (ability.castRules?.pushbackImmune) return 1;
|
||||
return Math.max(0, Math.min(1, ability.castRules?.pushbackResistance ?? 0));
|
||||
}
|
||||
|
||||
/** WotLK 3.3.5 rune costs for the active DK toolkit imported by the game. */
|
||||
export function runeCostForAbility(ability: AbilityDefinition): AbilityRuneCost | null {
|
||||
if (ability.runeCost) return ability.runeCost;
|
||||
return RUNE_COSTS_BY_SPELL_ID[ability.dbcSpellId]
|
||||
?? RUNE_COSTS_BY_NAME[ability.name.toLowerCase()]
|
||||
?? null;
|
||||
}
|
||||
|
||||
export function aiHintsForAbility(ability: AbilityDefinition): AbilityAiHints {
|
||||
if (ability.ai) return ability.ai;
|
||||
const name = ability.name.toLowerCase();
|
||||
const categories = new Set<AbilityAiHints["categories"][number]>();
|
||||
for (const effect of ability.effects) {
|
||||
if (["damage", "dot", "finisher-damage"].includes(effect.kind)) categories.add("damage");
|
||||
if (effect.kind === "execute") categories.add("execute");
|
||||
if (effect.kind === "heal" || effect.kind === "hot") categories.add("heal");
|
||||
if (effect.kind === "shield") categories.add("mitigation");
|
||||
if (effect.kind === "taunt") categories.add("taunt");
|
||||
if (effect.kind === "interrupt") categories.add("interrupt");
|
||||
if (effect.kind === "dispel" || effect.kind === "dispel-mechanic") categories.add("dispel");
|
||||
if (effect.kind === "resurrection") categories.add("resurrection");
|
||||
if (effect.kind === "status") {
|
||||
categories.add(effect.status === "interrupt" ? "interrupt" : "control");
|
||||
}
|
||||
if (effect.kind === "dot" || (effect.kind === "apply-aura" && effect.aura.disposition === "debuff")) categories.add("debuff");
|
||||
}
|
||||
if (EMERGENCY_HEAL_ABILITIES.has(name)) categories.add("emergency-heal");
|
||||
if ((ability.radius ?? 0) > 0 && categories.has("heal")) categories.add("area-heal");
|
||||
return Object.freeze({ categories: Object.freeze([...categories]) });
|
||||
}
|
||||
Reference in new issue
Block a user