import { triggeredAbilityBySpellId, type AbilityDefinition, type AbilityEffect } from "./abilityCatalog"; type ChannelAmountEffect = Extract; export function channelPulseCount(ability: AbilityDefinition): number { if (ability.channelPulses && ability.channelPulses > 0) return Math.max(1, Math.round(ability.channelPulses)); if (ability.channelTickMs && ability.channelTickMs > 0) { return Math.max(1, Math.round(ability.castTimeMs / ability.channelTickMs) + (ability.channelStartsWithPulse ? 1 : 0)); } return ability.dbcSpellId === 491164 ? 4 : 5; } export function channelPulseAt(ability: AbilityDefinition, startedAt: number, durationMs: number, pulseIndex: number): number { const intervals = Math.max(1, channelPulseCount(ability) - (ability.channelStartsWithPulse ? 1 : 0)); return startedAt + (pulseIndex + (ability.channelStartsWithPulse ? 0 : 1)) * durationMs / intervals; } /** Direct channel amounts span the cast; periodic trigger payloads fire each pulse. */ export function channelAmountEffects(ability: AbilityDefinition, totalTicks: number): readonly { effect: ChannelAmountEffect; multiplier: number; }[] { const ticks = Math.max(1, totalTicks); const amounts: { effect: ChannelAmountEffect; multiplier: number }[] = []; for (const effect of ability.effects) { if (effect.kind === "damage" || effect.kind === "heal" || effect.kind === "dot" || effect.kind === "hot") { amounts.push({ effect, multiplier: ability.name === "Penance" ? 1 : ability.dbcSpellId === 491164 && effect.kind === "damage" ? 1 : (effect.kind === "dot" || effect.kind === "hot" ? effect.ticks : 1) / ticks }); } else if (effect.kind === "apply-aura") { for (const proc of effect.aura.procs ?? []) { const triggers = Array.isArray(proc.triggers) ? proc.triggers : [proc.triggers]; const action = proc.action; if (!triggers.includes("periodic") || proc.chance !== 1 || proc.internalCooldownMs || proc.charges !== undefined || action.kind !== "custom" || !["wow335-trigger-spell", "rom-trigger-skill"].includes(action.id) || typeof action.data?.spellId !== "number") continue; const payload = triggeredAbilityBySpellId(action.data.spellId); for (const payloadEffect of payload?.effects ?? []) { if (payloadEffect.kind === "damage" || payloadEffect.kind === "heal") { amounts.push({ effect: payloadEffect, multiplier: 1 }); } } } } } return amounts; }