Files
i-want-to-heal/src/combat/spellModifiers.ts
T
2026-06-29 22:35:49 -04:00

82 lines
2.4 KiB
TypeScript

import type { Spell } from '../game'
import { stackCount, type StackCounts } from './stackCounts'
export type SlotModifier<T extends string> = {
stacks: StackCounts<T>
id: (slot: string) => T
multiplier?: number
}
export type StackModifier<T extends string> = {
stacks: StackCounts<T>
id: T
multiplier?: number
}
function slotKey(spell: Spell) {
return String(spell.key)
}
function slotModifierStacks<T extends string>(spell: Spell, modifier?: SlotModifier<T>) {
return modifier ? stackCount(modifier.stacks, modifier.id(slotKey(spell))) : 0
}
function stackModifierStacks<T extends string>(modifier?: StackModifier<T>) {
return modifier ? stackCount(modifier.stacks, modifier.id) : 0
}
function stackMultiplier<T extends string>(modifier?: StackModifier<T>) {
if (!modifier) return 1
return (modifier.multiplier ?? 1.25) ** stackModifierStacks(modifier)
}
export function spellSlotMultiplier<TPositive extends string, TNegative extends string = never>(
spell: Spell,
positive?: SlotModifier<TPositive>,
negative?: SlotModifier<TNegative>,
) {
const positiveMultiplier = (positive?.multiplier ?? 0.75) ** slotModifierStacks(spell, positive)
const negativeMultiplier = (negative?.multiplier ?? 1.25) ** slotModifierStacks(spell, negative)
return positiveMultiplier * negativeMultiplier
}
export function spellCooldownMultiplier<TPositive extends string, TNegative extends string = never>(
spell: Spell,
cooldownDown?: SlotModifier<TPositive>,
cooldownUp?: SlotModifier<TNegative>,
) {
return spellSlotMultiplier(spell, cooldownDown, cooldownUp)
}
export function spellResourceCost<TPositive extends string, TNegative extends string = never>({
spell,
costDown,
costUp,
freeCastReady = false,
freeCast,
}: {
spell: Spell
costDown?: SlotModifier<TPositive>
costUp?: SlotModifier<TNegative>
freeCastReady?: boolean
freeCast?: StackModifier<TPositive>
}) {
if (freeCastReady && stackModifierStacks(freeCast) > 0) return 0
return Math.ceil(spell.cost * spellSlotMultiplier(spell, costDown, costUp))
}
export function spellExtraTargets<T extends string>(
spell: Spell,
extraTarget: SlotModifier<T>,
) {
return slotModifierStacks(spell, extraTarget)
}
export function spellPowerMultiplier<T extends string>(modifier: StackModifier<T>) {
return stackMultiplier(modifier)
}
export function hasSpellStack<T extends string>(modifier: StackModifier<T>) {
return stackModifierStacks(modifier) > 0
}