/**
* Free Dice Tray — injected into the Foundry chat sidebar.
*
* Provides a compact bar for GM and players to roll any standard die (d4–d30)
* or its exploding variant (dXx) without needing an actor sheet.
* Supports selecting how many dice to roll (1–9).
*/
/** Standard dice available in Lethal Fantasy */
const DICE_TYPES = ["d4", "d6", "d8", "d10", "d12", "d20", "d30"]
/**
* Inject the dice tray bar into the ChatLog HTML.
* Called from `Hooks.on("renderChatLog", ...)`.
*
* @param {Application} _chatLog
* @param {HTMLElement|jQuery} html
*/
export function injectDiceTray(_chatLog, html) {
const el = (html instanceof HTMLElement) ? html : (html[0] ?? html)
if (!el?.querySelector) return
if (el.querySelector(".lf-dice-tray")) return
const bar = document.createElement("div")
bar.className = "lf-dice-tray"
const diceButtons = DICE_TYPES.map(d =>
``
).join("")
const countOptions = Array.from({ length: 9 }, (_, i) =>
``
).join("")
bar.innerHTML = `
${diceButtons}
`
bar.addEventListener("click", async ev => {
const btn = ev.target.closest(".lf-dt-die-btn")
if (!btn) return
ev.stopPropagation()
const dieType = btn.dataset.die
const count = parseInt(bar.querySelector(".lf-dt-count").value) || 1
const explode = bar.querySelector(".lf-dt-explode").checked
try {
await rollFreeDie(dieType, count, explode)
} catch (err) {
console.error("Lethal Fantasy | Dice Tray error:", err)
ui.notifications?.error("Dice Tray roll failed — see console")
}
})
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)
}
}
/**
* Roll one or more dice of the given type and post the result to chat.
*
* @param {string} dieType Die face, e.g. "d20"
* @param {number} count Number of dice to roll (1–9)
* @param {boolean} explode Whether to use the exploding modifier (x = max-value explode)
* @returns {Promise}
*/
export async function rollFreeDie(dieType, count = 1, explode = false) {
const sides = parseInt(dieType.replace("d", "")) || 20
const formula = explode ? `${count}d${sides}x` : `${count}d${sides}`
const label = explode
? `${count}${dieType.toUpperCase()}E`
: `${count}${dieType.toUpperCase()}`
const roll = new Roll(formula)
await roll.evaluate()
const results = roll.terms
.filter(t => t.results)
.flatMap(t => t.results)
const total = roll.total
const dieLabel = dieType.toUpperCase()
const resultHtml = results.map(r => {
const val = r.result
const isMax = val === sides
const isMin = val === 1
const explodeIcon = r.exploded ? `` : ""
const classes = ["lf-frc-die-chip", isMax ? "lf-frc-max" : "", isMin ? "lf-frc-min" : ""].filter(Boolean).join(" ")
return `