import type { TalentModifier } from "./talentModifiers"; import { WOW335_TALENT_NODES } from "./wow335Talents.generated"; export const WOW335_DRUID_MASTER_SHAPESHIFTER_TALENT_ID = "talent-1915"; export type Wow335DruidShapeshiftForm = "bear" | "cat" | "moonkin" | "tree-of-life"; const masterShapeshifterTalent = WOW335_TALENT_NODES.find( (talent) => talent.id === WOW335_DRUID_MASTER_SHAPESHIFTER_TALENT_ID, ) ?? (() => { throw new Error(`Generated Druid talent ${WOW335_DRUID_MASTER_SHAPESHIFTER_TALENT_ID} is missing`); })(); function normalizedRank(rank: number): number { return Number.isFinite(rank) ? Math.max(0, Math.min(masterShapeshifterTalent.rankDescriptions.length, Math.trunc(rank))) : 0; } /** * The generated rank tooltip has a separate bonus for each shapeshift form. * Return only the active form's modifier so the clauses cannot leak globally. */ export function wow335DruidMasterShapeshifterModifiersForForm( rank: number, form: Wow335DruidShapeshiftForm | null, ): readonly TalentModifier[] { const normalized = normalizedRank(rank); if (normalized <= 0 || !form) return []; const description = masterShapeshifterTalent.rankDescriptions[normalized - 1] ?? ""; const clauses = { bear: { pattern: /Bear Form\s*-\s*Increases physical damage by\s+(\d+(?:\.\d+)?)%/i, kind: "damage-percent" as const, scope: "physical damage", }, cat: { pattern: /Cat Form\s*-\s*Increases melee critical strike chance by\s+(\d+(?:\.\d+)?)%/i, kind: "critical-chance-percent" as const, scope: "melee critical strike chance", }, moonkin: { pattern: /Moonkin Form\s*-\s*Increases Arcane and Nature damage by\s+(\d+(?:\.\d+)?)%/i, kind: "damage-percent" as const, scope: "Arcane and Nature damage", }, "tree-of-life": { pattern: /Tree of Life Form\s*-\s*Increases healing by\s+(\d+(?:\.\d+)?)%/i, kind: "healing-percent" as const, scope: "healing", }, } as const; const clause = clauses[form]; const amount = Number(description.match(clause.pattern)?.[1] ?? 0); if (!Number.isFinite(amount) || amount <= 0) return []; return [{ kind: clause.kind, amount, unit: "percent", scope: clause.scope, sourceTalentId: WOW335_DRUID_MASTER_SHAPESHIFTER_TALENT_ID, sourceTalentName: "Master Shapeshifter", rank: normalized, description, }]; } /** Resolve the stock WoW shapeshift spell IDs used by the active aura runtime. */ export function wow335DruidShapeshiftFormForSpellId(spellId: number): Wow335DruidShapeshiftForm | null { switch (spellId) { case 5487: // Bear Form case 9634: // Dire Bear Form return "bear"; case 768: // Cat Form return "cat"; case 24858: // Moonkin Form return "moonkin"; case 33891: // Tree of Life case 65139: // Generated Tree of Life form row return "tree-of-life"; default: return null; } } /** Extract a form from the canonical aura IDs written by generated WoW forms. */ export function wow335DruidShapeshiftFormForAuraId(auraId: string): Wow335DruidShapeshiftForm | null { const spellId = auraId.match(/^wow335:(\d+):/)?.[1]; return spellId === undefined ? null : wow335DruidShapeshiftFormForSpellId(Number(spellId)); } /** Find an active generated shapeshift aura on the requested actor. */ export function wow335DruidShapeshiftFormForAuras( auras: readonly { readonly definition: { readonly id: string }; readonly targetId: string; readonly expiresAt: number | null; }[], actorId: string, now: number, ): Wow335DruidShapeshiftForm | null { const matching = auras .filter((aura) => aura.targetId === actorId && (aura.expiresAt === null || aura.expiresAt > now)) .map((aura) => wow335DruidShapeshiftFormForAuraId(aura.definition.id)) .filter((form): form is Wow335DruidShapeshiftForm => form !== null); // Normally the aura system removes the previous form. Taking the most recent // active form also makes stale imported form auras deterministic. return matching.at(-1) ?? null; }