// ─── Discipline Effects ─────────────────────────────────────────────────────── // Computes bonuses from active disciplines and integrates with the unified effect system import type { GameState } from '../types'; import { useDisciplineStore } from '../stores/discipline-slice'; import { ALL_DISCIPLINES } from '../data/disciplines'; import { calculateStatBonus, getUnlockedPerks } from '../utils/discipline-math'; export function computeDisciplineEffects(state: GameState): { bonuses: Record; multipliers: Record; specials: Set; } { const { disciplines } = useDisciplineStore.getState(); const activeDiscs = Object.entries(disciplines) .filter(([, disc]) => disc && !disc.paused) .map(([id, disc]) => ({ id, disc, def: ALL_DISCIPLINES.find(d => d.id === id) })) .filter((entry): entry is { id: string; disc: any; def: NonNullable } => !!entry.def); const bonuses: Record = {}; const multipliers: Record = {}; const specials = new Set(); for (const { disc, def } of activeDiscs) { // Continuous stat bonus const statBonus = calculateStatBonus(def.statBonus.baseValue, disc.xp, def.scalingFactor); if (def.statBonus.stat) { bonuses[def.statBonus.stat] = (bonuses[def.statBonus.stat] || 0) + statBonus; } // Perk unlocks const perks = getUnlockedPerks(def, disc.xp); for (const perk of perks) { if (perk.type === 'once' || perk.type === 'infinite') { // Once/infinite perks can be treated as additive bonuses or special flags // For simplicity, we add them as a special flag; actual effect depends on perk.id specials.add(perk.id); } else if (perk.type === 'capped') { // Capped perks act as multipliers after certain thresholds // For now, we treat them as additive to a multiplier stat (example) // In a real implementation, each perk would have a specific effect. // Here we just add a generic perk multiplier placeholder. multipliers[`perk_${perk.id}`] = (multipliers[`perk_${perk.id}`] || 1) + perk.value / 100; } } } return { bonuses, multipliers, specials }; }