import { SYSTEM } from "../config/system.mjs" /** * Spell enhancements — applied before casting (p.96-97). * stress: Arcane Stress cost regardless of roll result * penalty: dice pool penalty * redDice: true = roll red dice (3+ threshold) on the check * noStress: true = 1s rolled do NOT add Arcane Stress (Safe Spell) */ export const SPELL_ENHANCEMENTS = { none: { label: "OATHHAMMER.Enhancement.None", stress: 0, penalty: 0, redDice: false, noStress: false }, focused: { label: "OATHHAMMER.Enhancement.Focused", stress: 1, penalty: 0, redDice: true, noStress: false }, controlled: { label: "OATHHAMMER.Enhancement.Controlled", stress: 1, penalty: 0, redDice: false, noStress: false }, empowered: { label: "OATHHAMMER.Enhancement.Empowered", stress: 2, penalty: 0, redDice: false, noStress: false }, extended: { label: "OATHHAMMER.Enhancement.Extended", stress: 1, penalty: -1, redDice: false, noStress: false }, penetrating: { label: "OATHHAMMER.Enhancement.Penetrating", stress: 1, penalty: -1, redDice: false, noStress: false }, lethal: { label: "OATHHAMMER.Enhancement.Lethal", stress: 2, penalty: -2, redDice: false, noStress: false }, hastened: { label: "OATHHAMMER.Enhancement.Hastened", stress: 3, penalty: -3, redDice: false, noStress: false }, safe: { label: "OATHHAMMER.Enhancement.Safe", stress: 0, penalty: -3, redDice: false, noStress: true }, } export default class OathHammerSpellDialog { static async prompt(actor, spell) { const sys = spell.system const actorSys = actor.system const intRank = actorSys.attributes.intelligence.rank const magicRank = actorSys.skills.magic.rank const basePool = intRank + magicRank const currentStress = actorSys.arcaneStress.value const stressThreshold = actorSys.arcaneStress.threshold const isOverThreshold = currentStress >= stressThreshold const isElemental = sys.tradition === "elemental" const dv = sys.difficultyValue const traditionLabel = (() => { const entry = SYSTEM.SORCEROUS_TRADITIONS?.[sys.tradition] return entry ? game.i18n.localize(entry.label) : (sys.tradition ?? "") })() const enhancementOptions = Object.entries(SPELL_ENHANCEMENTS).map(([key, def]) => ({ value: key, label: game.i18n.localize(def.label), selected: key === "none", })) const bonusOptions = Array.from({ length: 13 }, (_, i) => { const v = i - 6 return { value: v, label: v > 0 ? `+${v}` : String(v), selected: v === 0 } }) const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes) const context = { actorName: actor.name, spellName: spell.name, spellImg: spell.img, dv, traditionLabel, isRitual: sys.isRitual, isMagicMissile: sys.isMagicMissile, range: sys.range, duration: sys.duration, spellSave: sys.spellSave, isElemental, element: sys.element, intRank, magicRank, basePool, currentStress, stressThreshold, isOverThreshold, enhancementOptions, bonusOptions, rollModes, visibility: game.settings.get("core", "rollMode"), } const content = await foundry.applications.handlebars.renderTemplate( "systems/fvtt-oath-hammer/templates/spell-cast-dialog.hbs", context ) const result = await foundry.applications.api.DialogV2.wait({ window: { title: game.i18n.format("OATHHAMMER.Dialog.SpellCastTitle", { spell: spell.name }) }, classes: ["fvtt-oath-hammer"], content, rejectClose: false, buttons: [{ label: game.i18n.localize("OATHHAMMER.Dialog.CastSpell"), callback: (_ev, btn) => { const out = {} for (const el of btn.form.elements) { if (!el.name) continue out[el.name] = el.type === "checkbox" ? String(el.checked) : el.value } return out }, }], }) if (!result) return null const enhKey = result.enhancement ?? "none" const enh = SPELL_ENHANCEMENTS[enhKey] ?? SPELL_ENHANCEMENTS.none return { dv, enhancement: enhKey, stressCost: enh.stress, poolPenalty: enh.penalty, redDice: enh.redDice, noStress: enh.noStress, elementalBonus: parseInt(result.elementalBonus) || 0, bonus: parseInt(result.bonus) || 0, grimPenalty: parseInt(result.noGrimoire) || 0, visibility: result.visibility ?? game.settings.get("core", "rollMode"), explodeOn5: result.explodeOn5 === "true", } } }