Files
fvtt-oath-hammer/module/applications/free-roll.mjs

124 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Free Dice Roll widget injected into the Foundry chat sidebar.
*
* Provides a compact bar for GM and players to roll any dice pool without
* needing an actor — useful for quick checks, table rolls, narration, etc.
*
* Features:
* - Pool size (120 dice)
* - Color: White (4+), Red (3+), Black (2+)
* - Explode on 5+ checkbox
*/
import { _rollPool, _diceHtml } from "../rolls.mjs"
/**
* Inject the Free Roll bar into the ChatLog HTML.
* Called from `Hooks.on("renderChatLog", ...)`.
*
* @param {Application} _chatLog
* @param {HTMLElement} html
*/
export function injectFreeRollBar(_chatLog, html) {
// Avoid double-injection on re-renders
if (html.querySelector(".oh-free-roll-bar")) return
const bar = document.createElement("div")
bar.className = "oh-free-roll-bar"
bar.innerHTML = `
<span class="oh-frb-label">
<i class="fa-solid fa-dice-d6"></i>
${game.i18n.localize("OATHHAMMER.FreeRoll.Label")}
</span>
<div class="oh-frb-controls">
<select class="oh-frb-pool" title="${game.i18n.localize("OATHHAMMER.FreeRoll.PoolTitle")}">
${Array.from({length: 16}, (_, i) => `<option value="${i+1}"${i+1===2?" selected":""}>${i+1}d</option>`).join("")}
</select>
<select class="oh-frb-color" title="${game.i18n.localize("OATHHAMMER.FreeRoll.ColorTitle")}">
<option value="white"> ${game.i18n.localize("OATHHAMMER.FreeRoll.ColorWhite")}</option>
<option value="red">🔴 ${game.i18n.localize("OATHHAMMER.FreeRoll.ColorRed")}</option>
<option value="black"> ${game.i18n.localize("OATHHAMMER.FreeRoll.ColorBlack")}</option>
</select>
<label class="oh-frb-explode-label" title="${game.i18n.localize("OATHHAMMER.FreeRoll.ExplodeTitle")}">
<input type="checkbox" class="oh-frb-explode">
💥 5+
</label>
<button type="button" class="oh-frb-roll-btn">
${game.i18n.localize("OATHHAMMER.FreeRoll.Roll")}
</button>
</div>
`
bar.querySelector(".oh-frb-roll-btn").addEventListener("click", () => {
const pool = parseInt(bar.querySelector(".oh-frb-pool").value) || 2
const color = bar.querySelector(".oh-frb-color").value
const explode5 = bar.querySelector(".oh-frb-explode").checked
rollFree(pool, color, explode5)
})
// Insert between .chat-scroll and .chat-form
const chatForm = html.querySelector(".chat-form")
if (chatForm) {
html.insertBefore(bar, chatForm)
} else {
html.appendChild(bar)
}
}
/**
* Execute a free dice roll and post the result to chat.
*
* @param {number} pool Number of d6 to roll
* @param {string} colorType "white" | "red" | "black"
* @param {boolean} explode5 True to explode on 5+
*/
export async function rollFree(pool, colorType, explode5 = false) {
const threshold = colorType === "black" ? 2 : colorType === "red" ? 3 : 4
const colorEmoji = colorType === "black" ? "⬛" : colorType === "red" ? "🔴" : "⬜"
const colorLabel = colorType === "black"
? game.i18n.localize("OATHHAMMER.FreeRoll.ColorBlack")
: colorType === "red"
? game.i18n.localize("OATHHAMMER.FreeRoll.ColorRed")
: game.i18n.localize("OATHHAMMER.FreeRoll.ColorWhite")
const { rolls, successes, diceResults } = await _rollPool(pool, threshold, explode5)
const explodedCount = diceResults.filter(d => d.exploded).length
const diceHtml = _diceHtml(diceResults, threshold)
const modParts = []
if (explode5) modParts.push(`💥 ${game.i18n.localize("OATHHAMMER.Dialog.ExplodeOn5")}`)
if (explodedCount > 0) modParts.push(`💥 ${explodedCount} ${game.i18n.localize("OATHHAMMER.Roll.Exploded")}`)
const modLine = modParts.length ? `<div class="oh-roll-mods">${modParts.join(" · ")}</div>` : ""
const resultClass = successes > 0 ? "roll-success" : "roll-failure"
const resultLabel = successes > 0
? game.i18n.localize("OATHHAMMER.Roll.Success")
: game.i18n.localize("OATHHAMMER.Roll.Failure")
const content = `
<div class="oh-roll-card">
<div class="oh-roll-header">${game.i18n.localize("OATHHAMMER.FreeRoll.CardTitle")}</div>
<div class="oh-roll-info">
<span>${colorEmoji} ${pool}d6 (${threshold}+) &mdash; ${colorLabel}</span>
</div>
${modLine}
<div class="oh-roll-dice">${diceHtml}</div>
<div class="oh-roll-result ${resultClass}">
<span class="oh-roll-successes">${successes}</span>
<span class="oh-roll-verdict">${resultLabel}</span>
</div>
</div>
`
const rollMode = game.settings.get("core", "rollMode")
const msgData = {
speaker: ChatMessage.getSpeaker(),
content,
rolls,
sound: CONFIG.sounds.dice,
}
ChatMessage.applyRollMode(msgData, rollMode)
await ChatMessage.create(msgData)
}