import { SYSTEM } from "../config/system.mjs" /** * Roll configuration dialog for Oath Hammer skill checks. * Uses DialogV2.wait() — pattern from fvtt-hellborn / fvtt-lethal-fantasy. * * Dice rules (from rulebook): * Pool = Attribute rank + Skill rank + per-skill modifier + bonus + (luckSpend × 2) + supporters * White dice: succeed on 4+ | Red: 3+ | Black: 2+ * All dice explode on 6 (roll extra die). * Luck Points: spending 1 LP adds +2 dice; LP restored each session. * Supporters: each ally with ranks in the skill adds +1 die. */ export default class OathHammerRollDialog { /** * Dual-attribute skills: show an alternate attribute option in the dialog. * key → { primary, alt, altLabel i18n key } */ static DUAL_ATTRIBUTE_SKILLS = { defense: { alt: "might", altLabelKey: "OATHHAMMER.Roll.DualAttr.DefenseMelee" }, fighting: { alt: "agility", altLabelKey: "OATHHAMMER.Roll.DualAttr.FightingNimble" }, magic: { alt: "intelligence", altLabelKey: "OATHHAMMER.Roll.DualAttr.MagicSpells" }, } /** * Show a skill check dialog and return the user's choices. * * @param {Actor} actor Actor performing the check * @param {string} skillKey SYSTEM.SKILLS key (e.g. "fortune", "fighting") * @returns {Promise<{dv, bonus, luckSpend, supporters, attrOverride, visibility}|null>} * Resolved options, or null if the dialog was cancelled. */ static async prompt(actor, skillKey) { const sys = actor.system const skillDef = SYSTEM.SKILLS[skillKey] if (!skillDef) throw new Error(`Unknown skill: ${skillKey}`) const defaultAttrKey = skillDef.attribute const attrRank = sys.attributes[defaultAttrKey].rank const skill = sys.skills[skillKey] const skillRank = skill.rank const skillMod = skill.modifier ?? 0 const baseTotal = attrRank + skillRank + skillMod const colorType = skill.colorDiceType ?? "white" const threshold = colorType === "black" ? 2 : colorType === "red" ? 3 : 4 const colorLabel = colorType === "black" ? "⬛" : colorType === "red" ? "🔴" : "⬜" const dualDef = this.DUAL_ATTRIBUTE_SKILLS[skillKey] // Build attribute options for dual-attribute skills let attrOptions = null if (dualDef) { const altRank = sys.attributes[dualDef.alt].rank attrOptions = [ { value: defaultAttrKey, label: `${game.i18n.localize(`OATHHAMMER.Attribute.${defaultAttrKey.charAt(0).toUpperCase()}${defaultAttrKey.slice(1)}`)} (${attrRank}) — default`, selected: true, }, { value: dualDef.alt, label: `${game.i18n.localize(`OATHHAMMER.Attribute.${dualDef.alt.charAt(0).toUpperCase()}${dualDef.alt.slice(1)}`)} (${altRank}) — ${game.i18n.localize(dualDef.altLabelKey)}`, selected: false, }, ] } const availableLuck = sys.luck?.value ?? 0 const isHuman = (sys.lineage?.name ?? "").toLowerCase() === "human" const luckDicePerPoint = isHuman ? 3 : 2 const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes) // Build select option arrays const dvOptions = Array.from({ length: 11 }, (_, i) => { const v = i // 0..10 const label = v === 0 ? "0 (opposed)" : String(v) return { value: v, label, selected: v === 2 } }) const bonusOptions = Array.from({ length: 13 }, (_, i) => { const v = i - 6 return { value: v, label: v > 0 ? `+${v}` : String(v), selected: v === 0 } }) const supportersOptions = Array.from({ length: 7 }, (_, i) => ({ value: i, label: String(i), selected: i === 0, })) const luckOptions = Array.from({ length: availableLuck + 1 }, (_, i) => ({ value: i, label: i === 0 ? `0` : `${i} (+${i * luckDicePerPoint}d)`, selected: i === 0, })) const context = { actorName: actor.name, skillKey, skillLabel: game.i18n.localize(skillDef.label), attrKey: defaultAttrKey, attrLabel: game.i18n.localize(`OATHHAMMER.Attribute.${defaultAttrKey.charAt(0).toUpperCase()}${defaultAttrKey.slice(1)}`), attrRank, skillRank, skillMod, baseTotal, colorType, colorLabel, threshold, availableLuck, isHuman, attrOptions, isDualAttr: !!dualDef, rollModes, visibility: game.settings.get("core", "rollMode"), dvOptions, bonusOptions, supportersOptions, luckOptions, } const content = await foundry.applications.handlebars.renderTemplate( "systems/fvtt-oath-hammer/templates/roll-dialog.hbs", context ) const title = game.i18n.format("OATHHAMMER.Dialog.SkillCheckTitle", { skill: context.skillLabel }) const result = await foundry.applications.api.DialogV2.wait({ window: { title }, classes: ["fvtt-oath-hammer"], content, rejectClose: false, buttons: [ { label: game.i18n.localize("OATHHAMMER.Dialog.Roll"), callback: (_event, button) => { const out = {} for (const el of button.form.elements) { if (!el.name) continue out[el.name] = el.type === "checkbox" ? String(el.checked) : el.value } return out }, }, ], }) if (!result) return null const attrOverride = result.attrOverride || defaultAttrKey return { dv: Math.max(0, parseInt(result.dv) ?? 2), bonus: parseInt(result.bonus) || 0, luckSpend: Math.min(Math.max(0, parseInt(result.luckSpend) || 0), availableLuck), luckIsHuman: result.luckIsHuman === "true", supporters: Math.max(0, parseInt(result.supporters) || 0), explodeOn5: result.explodeOn5 === "true", attrOverride, visibility: result.visibility ?? game.settings.get("core", "rollMode"), } } }