/**
* 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 (1โ20 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) {
// Normalise: renderChatLog may pass jQuery (AppV1) or HTMLElement (AppV2/v13)
const el = (html instanceof HTMLElement) ? html : (html[0] ?? html)
if (!el?.querySelector) return
// Avoid double-injection on re-renders
if (el.querySelector(".oh-free-roll-bar")) return
const bar = document.createElement("div")
bar.className = "oh-free-roll-bar"
bar.innerHTML = `
${game.i18n.localize("OATHHAMMER.FreeRoll.Label")}
`
// Use event delegation on the bar container โ direct child listeners can be
// swallowed by Foundry's own delegated click handlers in the sidebar.
bar.addEventListener("click", async (ev) => {
if (!ev.target.closest(".oh-frb-roll-btn")) return
ev.stopPropagation()
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
try {
await rollFree(pool, color, explode5)
} catch (err) {
console.error("Oath Hammer | Free Roll error:", err)
ui.notifications?.error("Free Roll failed โ see console")
}
})
// Insert before the chat form โ try multiple selectors for v12/v13 compatibility
const anchor = el.querySelector(".chat-form")
?? el.querySelector(".chat-message-form")
?? el.querySelector("form")
if (anchor) {
anchor.parentElement.insertBefore(bar, anchor)
} else {
el.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 ? `