import type { AbilityDefinition } from "./abilityCatalog"; import type { AuraDefinition, DamageSchool } from "./combatAuras"; export const WOW335_WARLOCK_METAMORPHOSIS_AURA_TAG = "wow335-warlock-metamorphosis"; export const WOW335_WARLOCK_METAMORPHOSIS_FORM_ID = 22; export const WOW335_WARLOCK_METAMORPHOSIS_IMMOLATION_TAG = "wow335-warlock-immolation-aura"; export const WOW335_WARLOCK_METAMORPHOSIS_IMMOLATION_AP_AURA_TAG = "wow335-warlock-immolation-ap"; export const WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITY_IDS = Object.freeze([ "wow335-warlock-shadow-cleave", "wow335-warlock-challenging-howl", "wow335-warlock-demon-charge", "wow335-warlock-immolation-aura", ] as const); export interface WarlockMetamorphosisEffects { readonly rank: 1; readonly durationMs: 30_000; readonly cooldownMs: 180_000; readonly armorContributionPercent: 130; readonly damagePercent: 15; readonly meleeCritChanceTakenReductionPercent: 6; readonly stunSnareDurationReductionPercent: 25; readonly allowedSchools: readonly ["fire", "shadow"]; /** PvP-only modifier is sourced but has no player-target model here. */ readonly pvpDamageBonusPercent: null; } /** * Values come from the generated local Metamorphosis talent-1886 row and its * source spells 47241, 54817, and 54879 in the local 3.3.5a talent DBC. * The PvP-only +10% damage tooltip rider is omitted because this runtime has * no PvP player-target model. */ export function wow335WarlockMetamorphosisEffectsForRank(rank: number): WarlockMetamorphosisEffects | null { if (!Number.isFinite(rank) || Math.trunc(rank) < 1) return null; return { rank: 1, durationMs: 30_000, cooldownMs: 180_000, armorContributionPercent: 130, damagePercent: 15, meleeCritChanceTakenReductionPercent: 6, stunSnareDurationReductionPercent: 25, allowedSchools: ["fire", "shadow"], pvpDamageBonusPercent: null, }; } export interface WarlockMetamorphosisAbilityContext { readonly school?: DamageSchool; readonly schools?: readonly DamageSchool[]; /** Explicit local metadata for spells that are allowed during shapeshift. */ readonly usableWhileShapeshifted?: boolean; } /** The local Metamorphosis description permits Fire/Shadow or shapeshift-safe spells. */ export function warlockMetamorphosisAbilityEligible( isMetamorphosisActive: boolean, ability: WarlockMetamorphosisAbilityContext, ): boolean { const schools = [ability.school, ...(ability.schools ?? [])]; return !isMetamorphosisActive || ability.usableWhileShapeshifted === true || schools.some((school) => school === "fire" || school === "shadow"); } /** Gate based only on explicit spell schools or explicit shapeshift-safe text. */ export function warlockMetamorphosisCanCastAbilityWhileActive( ability: Pick, ): boolean { if ((WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITY_IDS as readonly string[]).includes(ability.id)) return true; const schools: DamageSchool[] = ability.effects.flatMap((effect) => ( (effect.kind === "damage" || effect.kind === "dot") && effect.school ? [effect.school] : [] )); if (ability.castRules?.school) schools.push(ability.castRules.school); if (!schools.length && ability.effects.some((effect) => effect.kind === "damage" || effect.kind === "dot")) { const text = ability.description.toLowerCase(); if (/\bshadow\s+(?:damage|spell|magic)\b/.test(text)) schools.push("shadow"); else if (/\bfire\s+(?:damage|spell|magic)\b/.test(text)) schools.push("fire"); } return warlockMetamorphosisAbilityEligible(true, { schools, usableWhileShapeshifted: /\busable while shapeshifted\b/i.test(ability.description), }); } export function warlockMetamorphosisFormActionReady(abilityId: string, isFormActive: boolean): boolean { return !(WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITY_IDS as readonly string[]).includes(abilityId) || isFormActive; } export function wow335WarlockMetamorphosisControlDuration( durationMs: number, mechanic: string, isFormActive: boolean, ): number { const safeDuration = Math.max(0, Number.isFinite(durationMs) ? durationMs : 0); return isFormActive && ["stun", "root", "slow"].includes(mechanic) ? Math.round(safeDuration * 0.75) : safeDuration; } /** Spell.dbc Immolated child: tooltip formula is 4.5 × player level − 3. */ export function wow335WarlockImmolationAttackPowerReduction(level: number): number { const safeLevel = Math.max(1, Math.trunc(Number.isFinite(level) ? level : 1)); return Math.max(0, Math.round(4.5 * safeLevel - 3)); } export function wow335WarlockImmolationAttackPowerAura(level: number): AuraDefinition { const reduction = wow335WarlockImmolationAttackPowerReduction(level); return { id: `wow335:warlock:immolation-ap:${reduction}`, name: "Immolated", description: `Reduces attack power by ${reduction} for 30 seconds.`, disposition: "debuff", durationMs: 30_000, maxStacks: 1, stackBehavior: "replace", uniqueBySource: true, utilityTags: [WOW335_WARLOCK_METAMORPHOSIS_IMMOLATION_AP_AURA_TAG], modifiers: [{ kind: "stat", stat: "attack-power", operation: "flat", value: -reduction }], }; } /** Numeric modifiers currently expressible by the shared aura resolver. */ export function wow335WarlockMetamorphosisAuraForRank(rank: number): AuraDefinition | null { const effects = wow335WarlockMetamorphosisEffectsForRank(rank); if (!effects) return null; return { id: "wow335:warlock:metamorphosis", name: "Metamorphosis", description: "Increases armor contribution from cloth and leather by 130% and damage dealt by 15%; reduces melee critical hits against you by 6% and stun and snare durations by 25% while transformed. The PvP-only +10% damage rider is not applied because PvP player targets are unsupported.", disposition: "buff", durationMs: effects.durationMs, maxStacks: 1, stackBehavior: "replace", uniqueBySource: true, exclusiveGroup: "warlock:form", utilityTags: ["shapeshift", WOW335_WARLOCK_METAMORPHOSIS_AURA_TAG], modifiers: [ { kind: "stat", stat: "armor", operation: "percent", value: effects.armorContributionPercent / 100 }, { kind: "stat", stat: "incoming-melee-critical-strike", operation: "flat", value: -effects.meleeCritChanceTakenReductionPercent }, { kind: "damage", direction: "dealt", operation: "percent", value: effects.damagePercent / 100 }, ], }; } const formOnly = { sourceRankText: "Demon", formOnly: true } as const; /** These rankless buttons are reconstructed from their local Spell.dbc rows and children. */ export const WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITIES: readonly AbilityDefinition[] = [ { id: WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITY_IDS[0], classId: "warlock", dbcSpellId: 50581, name: "Shadow Cleave", unlockLevel: 1, sourceUnlockLevel: 1, icon: "/assets/ui/spells/spell_shadow_shadowfury.png", sigil: "SC", description: "Inflicts 110 Shadow damage to an enemy target and nearby allies, affecting up to 3 targets. Costs 4% base mana.", target: "hostile", range: { min: 0, max: 5 }, castMode: "instant", castTimeMs: 0, cooldownMs: 6_000, gcdMs: 0, cost: { resource: "mana", amount: 4, percentage: true, percentageBasis: "base-mana" }, effects: [{ kind: "damage", coefficient: 0, school: "shadow", basePoints: 110, dieSides: 1, bonusPowerCoefficient: 1 }], targeting: { shape: "chain", maximumTargets: 3 }, source: "wow335-progression", ...formOnly, } as AbilityDefinition, { id: WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITY_IDS[1], classId: "warlock", dbcSpellId: 59671, name: "Challenging Howl", unlockLevel: 1, sourceUnlockLevel: 1, icon: "/assets/ui/spells/ability_warlock_demonicempowerment.png", sigil: "CH", description: "Forces all nearby enemies within 10 yards to focus attacks on you for 6 seconds.", target: "self", range: { min: 0, max: 0 }, radius: 10, castMode: "instant", castTimeMs: 0, cooldownMs: 180_000, gcdMs: 1_500, cost: { resource: "rage", amount: 0 }, effects: [{ kind: "taunt", durationMs: 6_000 }], targeting: { shape: "circle", radius: 10 }, source: "wow335-progression", ...formOnly, } as AbilityDefinition, { id: WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITY_IDS[2], classId: "warlock", dbcSpellId: 54785, name: "Demon Charge", unlockLevel: 1, sourceUnlockLevel: 1, icon: "/assets/ui/spells/ability_warlock_demonicempowerment.png", sigil: "DC", description: "Charge an enemy, stunning it for 3 seconds. Range 8–25 yards; 45 second cooldown.", target: "hostile", range: { min: 8, max: 25 }, castMode: "instant", castTimeMs: 0, cooldownMs: 45_000, gcdMs: 1_500, cost: { resource: "mana", amount: 0 }, effects: [ { kind: "movement", movement: "charge", toward: "target" }, { kind: "status", status: "stun", durationMs: 3_000 }, ], source: "wow335-progression", ...formOnly, } as AbilityDefinition, { id: WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITY_IDS[3], classId: "warlock", dbcSpellId: 50589, name: "Immolation Aura", unlockLevel: 1, sourceUnlockLevel: 1, icon: "/assets/ui/spells/spell_fire_selfdestruct.png", sigil: "IA", description: "For 10 seconds, burns nearby enemies for 251 Fire damage plus 11.5 per level and 14.3% spell power every second within 8 yards, reducing their attack power by 4.5 × level − 3 for 30 seconds. Costs 64% base mana.", target: "self", range: { min: 0, max: 0 }, radius: 8, castMode: "instant", castTimeMs: 0, cooldownMs: 30_000, gcdMs: 1_500, cost: { resource: "mana", amount: 64, percentage: true, percentageBasis: "base-mana" }, effects: [ { kind: "dot", coefficient: 0, school: "fire", basePoints: 251, dieSides: 1, pointsPerLevel: 11.5, bonusPowerCoefficient: 0.143, sourceLevel: 1, ticks: 10, intervalMs: 1_000, tag: WOW335_WARLOCK_METAMORPHOSIS_IMMOLATION_TAG }, { kind: "apply-aura", recipient: "caster", aura: { id: "wow335:warlock:immolation-aura", name: "Immolation Aura", description: "Emits Fire pulses for 10 seconds. Loss of control suppresses pulses.", disposition: "buff", durationMs: 10_000, maxStacks: 1, stackBehavior: "replace", utilityTags: [WOW335_WARLOCK_METAMORPHOSIS_IMMOLATION_TAG], }, sourceEffectType: 6, sourceAuraType: 23, miscValue: 0, triggerSpellId: 50590 }, ], targeting: { shape: "circle", radius: 8 }, source: "wow335-progression", ...formOnly, } as AbilityDefinition, ]; export function wow335WarlockMetamorphosisAbility(): AbilityDefinition { return { id: "wow335-warlock-metamorphosis", classId: "warlock", dbcSpellId: 59672, name: "Metamorphosis", unlockLevel: 1, sourceUnlockLevel: 1, icon: "/assets/ui/spells/spell_shadow_demonform.png", sigil: "ME", description: "Transform into a Demon for 30 seconds. Increases armor contribution from cloth and leather items by 130% and damage dealt by 15%; reduces melee critical chance taken by 6% and stun/snare duration by 25%. Grants four Demon-only abilities. Only Fire/Shadow spells and abilities or spells explicitly usable while shapeshifted may be cast. PvP-only +10% damage is unsupported.", target: "self", range: { min: 0, max: 0 }, castMode: "instant", castTimeMs: 0, cooldownMs: 180_000, gcdMs: 0, cost: { resource: "mana", amount: 0 }, effects: [{ kind: "apply-aura", recipient: "caster", aura: wow335WarlockMetamorphosisAuraForRank(1)!, sourceEffectType: 6, sourceAuraType: 36, miscValue: WOW335_WARLOCK_METAMORPHOSIS_FORM_ID, triggerSpellId: 0 }], talentNodeId: "talent-1886", source: "wow335-progression", }; }