Add initiative rolls
This commit is contained in:
167
module/rolls.mjs
167
module/rolls.mjs
@@ -24,7 +24,7 @@ import { SYSTEM } from "./config/system.mjs"
|
||||
* @returns {Promise<{successes: number, dv: number, isSuccess: boolean}>}
|
||||
*/
|
||||
export async function rollSkillCheck(actor, skillKey, dv, options = {}) {
|
||||
const { bonus = 0, luckSpend = 0, supporters = 0, attrOverride, visibility, flavor, explodeOn5 = false } = options
|
||||
const { bonus = 0, luckSpend = 0, luckIsHuman = false, supporters = 0, attrOverride, visibility, flavor, explodeOn5 = false } = options
|
||||
|
||||
const sys = actor.system
|
||||
const skillDef = SYSTEM.SKILLS[skillKey]
|
||||
@@ -40,8 +40,10 @@ export async function rollSkillCheck(actor, skillKey, dv, options = {}) {
|
||||
const colorType = skill.colorDiceType ?? "white"
|
||||
const threshold = colorType === "black" ? 2 : colorType === "red" ? 3 : 4
|
||||
|
||||
const luckDicePerPoint = luckIsHuman ? 3 : 2
|
||||
|
||||
// Total dice pool (never below 1)
|
||||
const totalDice = Math.max(attrRank + skillRank + skillMod + bonus + (luckSpend * 2) + supporters, 1)
|
||||
const totalDice = Math.max(attrRank + skillRank + skillMod + bonus + (luckSpend * luckDicePerPoint) + supporters, 1)
|
||||
|
||||
// Deduct spent Luck Points from actor
|
||||
if (luckSpend > 0) {
|
||||
@@ -51,6 +53,7 @@ export async function rollSkillCheck(actor, skillKey, dv, options = {}) {
|
||||
|
||||
// Roll the dice pool
|
||||
const roll = await new Roll(`${totalDice}d6`).evaluate()
|
||||
const allRolls = [roll]
|
||||
|
||||
// Count successes — exploding dice produce additional dice
|
||||
const explodeThreshold = explodeOn5 ? 5 : 6
|
||||
@@ -67,6 +70,7 @@ export async function rollSkillCheck(actor, skillKey, dv, options = {}) {
|
||||
|
||||
while (extraDice > 0) {
|
||||
const xRoll = await new Roll(`${extraDice}d6`).evaluate()
|
||||
allRolls.push(xRoll)
|
||||
extraDice = 0
|
||||
for (const r of xRoll.dice[0].results) {
|
||||
const val = r.result
|
||||
@@ -94,7 +98,7 @@ export async function rollSkillCheck(actor, skillKey, dv, options = {}) {
|
||||
const explodedCount = diceResults.filter(d => d.exploded).length
|
||||
const modParts = []
|
||||
if (bonus !== 0) modParts.push(`${bonus > 0 ? "+" : ""}${bonus} ${game.i18n.localize("OATHHAMMER.Dialog.Modifier")}`)
|
||||
if (luckSpend > 0) modParts.push(`+${luckSpend * 2} ${game.i18n.localize("OATHHAMMER.Dialog.LuckSpend")} (${luckSpend}LP)`)
|
||||
if (luckSpend > 0) modParts.push(`+${luckSpend * luckDicePerPoint} ${game.i18n.localize("OATHHAMMER.Dialog.LuckSpend")} (${luckSpend}LP${luckIsHuman ? " 👤" : ""})`)
|
||||
if (supporters > 0) modParts.push(`+${supporters} ${game.i18n.localize("OATHHAMMER.Dialog.Supporters")}`)
|
||||
if (explodedCount > 0) modParts.push(`💥 ${explodedCount} ${game.i18n.localize("OATHHAMMER.Roll.Exploded")}`)
|
||||
const modLine = modParts.length ? `<div class="oh-roll-mods">${modParts.join(" · ")}</div>` : ""
|
||||
@@ -132,7 +136,7 @@ export async function rollSkillCheck(actor, skillKey, dv, options = {}) {
|
||||
const msgData = {
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
content,
|
||||
rolls: [roll],
|
||||
rolls: allRolls,
|
||||
sound: CONFIG.sounds.dice,
|
||||
}
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
@@ -179,8 +183,10 @@ export async function rollRarityCheck(actor, rarityKey, itemName) {
|
||||
* @param {number} threshold Minimum value to count as a success
|
||||
* @returns {Promise<{roll: Roll, successes: number, diceResults: Array}>}
|
||||
*/
|
||||
async function _rollPool(pool, threshold) {
|
||||
async function _rollPool(pool, threshold, explodeOn5 = false) {
|
||||
const explodeThreshold = explodeOn5 ? 5 : 6
|
||||
const roll = await new Roll(`${Math.max(pool, 1)}d6`).evaluate()
|
||||
const rolls = [roll]
|
||||
let successes = 0
|
||||
const diceResults = []
|
||||
let extraDice = 0
|
||||
@@ -188,22 +194,23 @@ async function _rollPool(pool, threshold) {
|
||||
for (const r of roll.dice[0].results) {
|
||||
const val = r.result
|
||||
if (val >= threshold) successes++
|
||||
if (val === 6) extraDice++
|
||||
if (val >= explodeThreshold) extraDice++
|
||||
diceResults.push({ val, exploded: false })
|
||||
}
|
||||
|
||||
while (extraDice > 0) {
|
||||
const xRoll = await new Roll(`${extraDice}d6`).evaluate()
|
||||
rolls.push(xRoll)
|
||||
extraDice = 0
|
||||
for (const r of xRoll.dice[0].results) {
|
||||
const val = r.result
|
||||
if (val >= threshold) successes++
|
||||
if (val === 6) extraDice++
|
||||
if (val >= explodeThreshold) extraDice++
|
||||
diceResults.push({ val, exploded: true })
|
||||
}
|
||||
}
|
||||
|
||||
return { roll, successes, diceResults }
|
||||
return { roll, rolls, successes, diceResults }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,7 +236,7 @@ function _diceHtml(diceResults, threshold) {
|
||||
* @param {object} options From OathHammerWeaponDialog.promptAttack()
|
||||
*/
|
||||
export async function rollWeaponAttack(actor, weapon, options = {}) {
|
||||
const { attackBonus = 0, rangeCondition = 0, attrOverride, visibility, autoAttackBonus = 0 } = options
|
||||
const { attackBonus = 0, rangeCondition = 0, attrOverride, colorOverride, visibility, autoAttackBonus = 0, explodeOn5 = false } = options
|
||||
|
||||
const sys = weapon.system
|
||||
const actorSys = actor.system
|
||||
@@ -242,23 +249,24 @@ export async function rollWeaponAttack(actor, weapon, options = {}) {
|
||||
const attrKey = attrOverride && actorSys.attributes[attrOverride] ? attrOverride : defaultAttr
|
||||
const attrRank = actorSys.attributes[attrKey].rank
|
||||
const skillRank = actorSys.skills[skillKey].rank
|
||||
const colorType = actorSys.skills[skillKey].colorDiceType ?? "white"
|
||||
const threshold = colorType === "black" ? 2 : colorType === "red" ? 3 : 4
|
||||
const baseColorType = actorSys.skills[skillKey].colorDiceType ?? "white"
|
||||
const colorType = colorOverride || baseColorType
|
||||
const threshold = colorType === "black" ? 2 : colorType === "red" ? 3 : 4
|
||||
const colorEmoji = colorType === "black" ? "⬛" : colorType === "red" ? "🔴" : "⬜"
|
||||
|
||||
const totalDice = Math.max(attrRank + skillRank + attackBonus + rangeCondition + autoAttackBonus, 1)
|
||||
|
||||
const { roll, successes, diceResults } = await _rollPool(totalDice, threshold)
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold, explodeOn5)
|
||||
|
||||
const skillLabel = game.i18n.localize(skillDef.label)
|
||||
const attrLabel = game.i18n.localize(`OATHHAMMER.Attribute.${attrKey.charAt(0).toUpperCase() + attrKey.slice(1)}`)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
|
||||
// Modifier summary
|
||||
const modParts = []
|
||||
if (attackBonus !== 0) modParts.push(`${attackBonus > 0 ? "+" : ""}${attackBonus} ${game.i18n.localize("OATHHAMMER.Dialog.AttackModifier")}`)
|
||||
if (rangeCondition !== 0) modParts.push(`${rangeCondition} ${game.i18n.localize("OATHHAMMER.Dialog.RangeCondition")}`)
|
||||
if (autoAttackBonus > 0) modParts.push(`+${autoAttackBonus} auto`)
|
||||
if (explodeOn5) modParts.push(`💥 ${game.i18n.localize("OATHHAMMER.Dialog.ExplodeOn5")}`)
|
||||
const modLine = modParts.length ? `<div class="oh-roll-mods">${modParts.join(" · ")}</div>` : ""
|
||||
|
||||
const content = `
|
||||
@@ -289,7 +297,7 @@ export async function rollWeaponAttack(actor, weapon, options = {}) {
|
||||
const msgData = {
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
content,
|
||||
rolls: [roll],
|
||||
rolls: rolls,
|
||||
sound: CONFIG.sounds.dice,
|
||||
flags: { "fvtt-oath-hammer": { weaponAttack: flagData } },
|
||||
}
|
||||
@@ -327,7 +335,7 @@ export async function rollWeaponDamage(actor, weapon, options = {}) {
|
||||
const baseDamageDice = sys.usesMight ? Math.max(mightRank + sys.damageMod, 1) : Math.max(sys.damageMod, 1)
|
||||
const totalDice = Math.max(baseDamageDice + sv + damageBonus + autoDamageBonus, 1)
|
||||
|
||||
const { roll, successes, diceResults } = await _rollPool(totalDice, threshold)
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
|
||||
const modParts = []
|
||||
@@ -361,7 +369,7 @@ export async function rollWeaponDamage(actor, weapon, options = {}) {
|
||||
const msgData = {
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
content,
|
||||
rolls: [roll],
|
||||
rolls: rolls,
|
||||
sound: CONFIG.sounds.dice,
|
||||
}
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
@@ -395,6 +403,7 @@ export async function rollSpellCast(actor, spell, options = {}) {
|
||||
bonus = 0,
|
||||
grimPenalty = 0,
|
||||
visibility,
|
||||
explodeOn5 = false,
|
||||
} = options
|
||||
|
||||
const sys = spell.system
|
||||
@@ -406,7 +415,7 @@ export async function rollSpellCast(actor, spell, options = {}) {
|
||||
const threshold = redDice ? 3 : 4
|
||||
const colorEmoji = redDice ? "🔴" : "⬜"
|
||||
|
||||
const { roll, successes, diceResults } = await _rollPool(totalDice, threshold)
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold, explodeOn5)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
|
||||
// Count 1s for Arcane Stress (unless Safe Spell enhancement)
|
||||
@@ -436,6 +445,7 @@ export async function rollSpellCast(actor, spell, options = {}) {
|
||||
if (poolPenalty !== 0) modParts.push(`${poolPenalty} ${game.i18n.localize("OATHHAMMER.Enhancement." + _cap(enhancement))}`)
|
||||
if (elementalBonus > 0) modParts.push(`+${elementalBonus} ${game.i18n.localize("OATHHAMMER.Dialog.ElementMet")}`)
|
||||
if (grimPenalty < 0) modParts.push(`${grimPenalty} ${game.i18n.localize("OATHHAMMER.Dialog.GrimoireNo")}`)
|
||||
if (explodeOn5) modParts.push(`💥 ${game.i18n.localize("OATHHAMMER.Dialog.ExplodeOn5")}`)
|
||||
const modLine = modParts.length ? `<div class="oh-roll-mods">${modParts.join(" · ")}</div>` : ""
|
||||
|
||||
const stressLine = `<div class="oh-stress-line${isBlocked ? " stress-blocked" : ""}">
|
||||
@@ -468,7 +478,7 @@ export async function rollSpellCast(actor, spell, options = {}) {
|
||||
const msgData = {
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
content,
|
||||
rolls: [roll],
|
||||
rolls: rolls,
|
||||
sound: CONFIG.sounds.dice,
|
||||
}
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
@@ -495,6 +505,7 @@ export async function rollMiracleCast(actor, miracle, options = {}) {
|
||||
isRitual = false,
|
||||
bonus = 0,
|
||||
visibility,
|
||||
explodeOn5 = false,
|
||||
} = options
|
||||
|
||||
const sys = miracle.system
|
||||
@@ -506,7 +517,7 @@ export async function rollMiracleCast(actor, miracle, options = {}) {
|
||||
const threshold = 4
|
||||
const colorEmoji = "⬜"
|
||||
|
||||
const { roll, successes, diceResults } = await _rollPool(totalDice, threshold)
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold, explodeOn5)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
const isSuccess = successes >= dv
|
||||
|
||||
@@ -517,9 +528,10 @@ export async function rollMiracleCast(actor, miracle, options = {}) {
|
||||
? game.i18n.localize("OATHHAMMER.Roll.Success")
|
||||
: game.i18n.localize("OATHHAMMER.Roll.Failure")
|
||||
|
||||
const modLine = bonus !== 0
|
||||
? `<div class="oh-roll-mods">${bonus > 0 ? "+" : ""}${bonus} ${game.i18n.localize("OATHHAMMER.Dialog.Modifier")}</div>`
|
||||
: ""
|
||||
const modParts = []
|
||||
if (bonus !== 0) modParts.push(`${bonus > 0 ? "+" : ""}${bonus} ${game.i18n.localize("OATHHAMMER.Dialog.Modifier")}`)
|
||||
if (explodeOn5) modParts.push(`💥 ${game.i18n.localize("OATHHAMMER.Dialog.ExplodeOn5")}`)
|
||||
const modLine = modParts.length ? `<div class="oh-roll-mods">${modParts.join(" · ")}</div>` : ""
|
||||
|
||||
const blockedLine = !isSuccess
|
||||
? `<div class="oh-miracle-blocked">⚠ ${game.i18n.localize("OATHHAMMER.Roll.MiracleBlocked")}</div>`
|
||||
@@ -553,7 +565,7 @@ export async function rollMiracleCast(actor, miracle, options = {}) {
|
||||
const msgData = {
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
content,
|
||||
rolls: [roll],
|
||||
rolls: rolls,
|
||||
sound: CONFIG.sounds.dice,
|
||||
}
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
@@ -595,7 +607,7 @@ export async function rollDefense(actor, options = {}) {
|
||||
const threshold = redDice ? 3 : 4
|
||||
const colorEmoji = redDice ? "🔴" : "⬜"
|
||||
|
||||
const { roll, successes, diceResults } = await _rollPool(totalDice, threshold)
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
|
||||
const attrLabel = game.i18n.localize(`OATHHAMMER.Attribute.${attrChoice.charAt(0).toUpperCase() + attrChoice.slice(1)}`)
|
||||
@@ -631,7 +643,7 @@ export async function rollDefense(actor, options = {}) {
|
||||
const msgData = {
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
content,
|
||||
rolls: [roll],
|
||||
rolls: rolls,
|
||||
sound: CONFIG.sounds.dice,
|
||||
}
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
@@ -666,20 +678,21 @@ export async function rollWeaponDefense(actor, weapon, options = {}) {
|
||||
attackType = "melee",
|
||||
attrRank = 0,
|
||||
attrChoice = "agility",
|
||||
redDice = false,
|
||||
colorOverride = "white",
|
||||
traitBonus = 0,
|
||||
armorPenalty = 0,
|
||||
diminishPenalty = 0,
|
||||
bonus = 0,
|
||||
visibility,
|
||||
explodeOn5 = false,
|
||||
} = options
|
||||
|
||||
const defRank = actor.system.skills.defense.rank
|
||||
const totalDice = Math.max(attrRank + defRank + traitBonus + armorPenalty + diminishPenalty + bonus, 1)
|
||||
const threshold = redDice ? 3 : 4
|
||||
const colorEmoji = redDice ? "🔴" : "⬜"
|
||||
const threshold = colorOverride === "black" ? 2 : colorOverride === "red" ? 3 : 4
|
||||
const colorEmoji = colorOverride === "black" ? "⬛" : colorOverride === "red" ? "🔴" : "⬜"
|
||||
|
||||
const { roll, successes, diceResults } = await _rollPool(totalDice, threshold)
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(totalDice, threshold, explodeOn5)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
|
||||
const attrLabel = game.i18n.localize(`OATHHAMMER.Attribute.${_cap(attrChoice)}`)
|
||||
@@ -691,6 +704,7 @@ export async function rollWeaponDefense(actor, weapon, options = {}) {
|
||||
if (armorPenalty < 0) modParts.push(`${armorPenalty} ${game.i18n.localize("OATHHAMMER.Dialog.ArmorPenalty")}`)
|
||||
if (diminishPenalty < 0) modParts.push(`${diminishPenalty} ${game.i18n.localize("OATHHAMMER.Dialog.DiminishingDefense")}`)
|
||||
if (bonus !== 0) modParts.push(`${bonus > 0 ? "+" : ""}${bonus} ${game.i18n.localize("OATHHAMMER.Dialog.Modifier")}`)
|
||||
if (explodeOn5) modParts.push(`💥 ${game.i18n.localize("OATHHAMMER.Dialog.ExplodeOn5")}`)
|
||||
const modLine = modParts.length ? `<div class="oh-roll-mods">${modParts.join(" · ")}</div>` : ""
|
||||
|
||||
const content = `
|
||||
@@ -716,7 +730,7 @@ export async function rollWeaponDefense(actor, weapon, options = {}) {
|
||||
const msgData = {
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
content,
|
||||
rolls: [roll],
|
||||
rolls: rolls,
|
||||
sound: CONFIG.sounds.dice,
|
||||
}
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
@@ -740,36 +754,43 @@ export async function rollWeaponDefense(actor, weapon, options = {}) {
|
||||
*/
|
||||
export async function rollArmorSave(actor, armor, options = {}) {
|
||||
const {
|
||||
av = armor.system.armorValue ?? 0,
|
||||
isReinforced = false,
|
||||
apPenalty = 0,
|
||||
bonus = 0,
|
||||
av = armor.system.armorValue ?? 0,
|
||||
isReinforced = false,
|
||||
colorOverride = null,
|
||||
apPenalty = 0,
|
||||
bonus = 0,
|
||||
visibility,
|
||||
explodeOn5 = false,
|
||||
} = options
|
||||
|
||||
// Armor CAN be reduced to 0 dice (fully bypassed by AP)
|
||||
const totalDice = Math.max(av + apPenalty + bonus, 0)
|
||||
const threshold = isReinforced ? 3 : 4
|
||||
const colorEmoji = isReinforced ? "🔴" : "⬜"
|
||||
const totalDice = Math.max(av + apPenalty + bonus, 0)
|
||||
const colorType = colorOverride || (isReinforced ? "red" : "white")
|
||||
const threshold = colorType === "black" ? 2 : colorType === "red" ? 3 : 4
|
||||
const colorEmoji = colorType === "black" ? "⬛" : colorType === "red" ? "🔴" : "⬜"
|
||||
|
||||
let successes = 0
|
||||
let diceHtml = `<em>${game.i18n.localize("OATHHAMMER.Roll.ArmorBypassed")}</em>`
|
||||
let roll
|
||||
let rolls = []
|
||||
|
||||
if (totalDice > 0) {
|
||||
const result = await _rollPool(totalDice, threshold)
|
||||
const result = await _rollPool(totalDice, threshold, explodeOn5)
|
||||
roll = result.roll
|
||||
rolls = result.rolls
|
||||
successes = result.successes
|
||||
diceHtml = _diceHtml(result.diceResults, threshold)
|
||||
} else {
|
||||
// Zero dice — create a dummy roll with no results so Foundry can still attach it
|
||||
roll = new Roll("0d6")
|
||||
await roll.evaluate()
|
||||
rolls = [roll]
|
||||
}
|
||||
|
||||
const modParts = []
|
||||
if (apPenalty < 0) modParts.push(`AP ${apPenalty}`)
|
||||
if (bonus !== 0) modParts.push(`${bonus > 0 ? "+" : ""}${bonus} ${game.i18n.localize("OATHHAMMER.Dialog.Modifier")}`)
|
||||
if (explodeOn5) modParts.push(`💥 ${game.i18n.localize("OATHHAMMER.Dialog.ExplodeOn5")}`)
|
||||
const modLine = modParts.length ? `<div class="oh-roll-mods">${modParts.join(" · ")}</div>` : ""
|
||||
|
||||
const content = `
|
||||
@@ -795,7 +816,7 @@ export async function rollArmorSave(actor, armor, options = {}) {
|
||||
const msgData = {
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
content,
|
||||
rolls: [roll],
|
||||
rolls: rolls,
|
||||
sound: CONFIG.sounds.dice,
|
||||
}
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
@@ -803,3 +824,71 @@ export async function rollArmorSave(actor, armor, options = {}) {
|
||||
|
||||
return { successes, totalDice }
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// INITIATIVE ROLL
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* Roll an initiative check for an actor and post the result to chat.
|
||||
*
|
||||
* Characters: opposed Leadership check (Fate + Leadership skill, DV=0).
|
||||
* NPCs: pool of Fate rank + initiativeBonus dice (no skill), threshold 4+.
|
||||
*
|
||||
* @param {Actor} actor
|
||||
* @param {object} [options]
|
||||
* @param {number} [options.bonus] Extra dice modifier
|
||||
* @param {string} [options.visibility] Roll mode
|
||||
* @param {boolean} [options.explodeOn5] Explode on 5+
|
||||
* @returns {Promise<{successes: number, dv: number, isSuccess: null}>}
|
||||
*/
|
||||
export async function rollInitiativeCheck(actor, options = {}) {
|
||||
const { bonus = 0, visibility, explodeOn5 = false } = options
|
||||
|
||||
if (actor.type === "character") {
|
||||
return rollSkillCheck(actor, "leadership", 0, {
|
||||
bonus,
|
||||
visibility,
|
||||
explodeOn5,
|
||||
flavor: game.i18n.localize("OATHHAMMER.Roll.Initiative"),
|
||||
})
|
||||
}
|
||||
|
||||
// NPC: Fate rank + initiativeBonus
|
||||
const sys = actor.system
|
||||
const fateRank = sys.attributes?.fate?.rank ?? 1
|
||||
const initBonus = sys.initiativeBonus ?? 0
|
||||
const pool = Math.max(fateRank + initBonus + bonus, 1)
|
||||
const threshold = 4
|
||||
|
||||
const { roll, rolls, successes, diceResults } = await _rollPool(pool, threshold, explodeOn5)
|
||||
const diceHtml = _diceHtml(diceResults, threshold)
|
||||
|
||||
const modParts = []
|
||||
if (bonus !== 0) modParts.push(`${bonus > 0 ? "+" : ""}${bonus} ${game.i18n.localize("OATHHAMMER.Dialog.Modifier")}`)
|
||||
if (explodeOn5) modParts.push(`💥 ${game.i18n.localize("OATHHAMMER.Dialog.ExplodeOn5")}`)
|
||||
const modLine = modParts.length ? `<div class="oh-roll-mods">${modParts.join(" · ")}</div>` : ""
|
||||
|
||||
const content = `
|
||||
<div class="oh-roll-card">
|
||||
<div class="oh-roll-header">⚔ ${game.i18n.localize("OATHHAMMER.Roll.Initiative")} — ${actor.name}</div>
|
||||
<div class="oh-roll-info">
|
||||
<span>${game.i18n.localize("OATHHAMMER.Attribute.Fate")} ${fateRank}${initBonus ? ` + ${initBonus}` : ""}</span>
|
||||
<span>⬜ ${pool}d6 (4+)</span>
|
||||
</div>
|
||||
${modLine}
|
||||
<div class="oh-roll-dice">${diceHtml}</div>
|
||||
<div class="oh-roll-result roll-opposed">
|
||||
<span class="oh-roll-successes">${successes}</span>
|
||||
<span class="oh-roll-verdict">${game.i18n.localize("OATHHAMMER.Roll.Opposed")}</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
const rollMode = visibility ?? game.settings.get("core", "rollMode")
|
||||
const msgData = { speaker: ChatMessage.getSpeaker({ actor }), content, rolls: rolls, sound: CONFIG.sounds.dice }
|
||||
ChatMessage.applyRollMode(msgData, rollMode)
|
||||
await ChatMessage.create(msgData)
|
||||
|
||||
return { successes, dv: 0, isSuccess: null }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user