/** * The mechanics kernel: structured, enforceable game rules derived on-demand * from the already-cached compendium JSON. No new assets are loaded — these * wrap the existing lazy loaders in src/lib/compendium, so the PWA cache config * is unchanged. Derived mechanics are memoized per process. */ import { loadSpells, loadArmor5e, loadMonsters } from '@/lib/compendium'; import { normalizeSpell5e } from './normalize/spell'; import { normalizeArmor5e } from './normalize/armor'; import { normalizeMonsterDefenses } from './normalize/monsterDefenses'; import type { ArmorMechanics, DamageDefenses, SpellMechanics } from './types'; export * from './types'; export type { EnforceResult } from './result'; export { normalizeSpell5e, normalizeSpellPf2e } from './normalize/spell'; export { normalizeArmor5e } from './normalize/armor'; export { normalizeMonsterDefenses } from './normalize/monsterDefenses'; export { castSpell, dropConcentration } from './cast'; export { spendResource, regainResource } from './resources'; export { rollDeathSave, type DeathSaveOutcome } from './deathSaves'; let spellMechCache: Map | null = null; let armorMechCache: Map | null = null; /** Mechanics for a single 5e spell by slug (undefined if not in the SRD set). */ export async function spellMechanics5e(slug: string): Promise { if (!spellMechCache) { const raw = await loadSpells(); spellMechCache = new Map(); for (const s of raw) { const m = normalizeSpell5e(s); if (m) spellMechCache.set(m.slug, m); } } return spellMechCache.get(slug); } /** Mechanics for a single 5e armor by name (case-insensitive). */ export async function armorMechanics5e(name: string): Promise { if (!armorMechCache) { const raw = await loadArmor5e(); armorMechCache = new Map(); for (const a of raw) { const m = normalizeArmor5e(a); if (m) armorMechCache.set(m.name.toLowerCase(), m); } } return armorMechCache.get(name.toLowerCase()); } /** All 5e armor mechanics (for the "equip" picker). */ export async function allArmorMechanics5e(): Promise { await armorMechanics5e(''); return [...(armorMechCache?.values() ?? [])]; } /** Damage/condition defenses for a 5e monster by slug. */ export async function monsterDefenses5e(slug: string): Promise { const raw = await loadMonsters(); const m = raw.find((x) => x.slug === slug); return m ? normalizeMonsterDefenses(m) : undefined; }