From bb005ee9fc22901231b35dbb906bfc47690f7225 Mon Sep 17 00:00:00 2001 From: LeRatierBretonnier Date: Sun, 28 Jun 2026 08:04:20 +0200 Subject: [PATCH] feat: full reroll includes D30 + shows dice breakdown in chat - Remove forceNoD30 from rerollConfiguredRoll so mulligan rerolls the D30 along with the d20 and modifier - Reset defenseD30Processed/attackD30Processed after mulligan so the new D30's effects (bonus dice, specials) are applied - Render reroll dice breakdown (diceResults + D30 result) as inline HTML in the reaction chat message using existing CSS classes so players see what was rolled --- lethal-fantasy.mjs | 40 ++++++++++++++++++++++++++++++++++++---- module/utils.mjs | 1 - 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/lethal-fantasy.mjs b/lethal-fantasy.mjs index b14e835..97b1fb1 100644 --- a/lethal-fantasy.mjs +++ b/lethal-fantasy.mjs @@ -572,7 +572,7 @@ Hooks.on("createChatMessage", async (message) => { return } - const { + let { attackerId, attackRoll, attackerName, @@ -588,7 +588,7 @@ Hooks.on("createChatMessage", async (message) => { defenderTokenId } = attackData let defenseRoll = message.rolls[0]?.options?.rollTotal || message.rolls[0]?.total || 0 - const defenseD30message = message.rolls[0]?.options?.D30message || null + let defenseD30message = message.rolls[0]?.options?.D30message || null log("Processing defense:", { attackRoll, defenseRoll, attackerId, defenderId }) @@ -806,7 +806,23 @@ Hooks.on("createChatMessage", async (message) => { canRerollDefense = false if (!reroll) continue defenseRoll = reroll.options?.rollTotal || reroll.total || oldDefenseRoll - await createReactionMessage(defender, `

${defenderName} uses Mulligan and re-rolls defense: ${oldDefenseRoll}${defenseRoll}. Both sides may now react to the new numbers.

`) + // Build dice breakdown HTML from the reroll + const rerollBreakdown = (reroll.options?.diceResults || []) + .map(r => `${r.dice}${r.value}`) + .join("") + const rerollD30 = reroll.options?.D30message + ? `
D30 → ${reroll.options.D30result || "?"} — ${reroll.options.D30message.description}
` + : "" + await createReactionMessage(defender, + `

${defenderName} uses Mulligan and re-rolls defense: ${oldDefenseRoll}${defenseRoll}.

+
${rerollBreakdown}
${rerollD30} +

Both sides may now react to the new numbers.

` + ) + // Apply new D30 result on the restart + if (reroll.options?.D30message) { + defenseD30message = reroll.options.D30message + defenseD30Processed = false + } // Restart the full comparison so both sides can react to the new roll mulliganRestart = true break @@ -1003,7 +1019,23 @@ Hooks.on("createChatMessage", async (message) => { canRerollAttack = false if (!reroll) continue attackRollFinal = reroll.options?.rollTotal || reroll.total || oldAttackRoll - await createReactionMessage(attacker, `

${attackerName} uses Mulligan and re-rolls attack: ${oldAttackRoll}${attackRollFinal}. Both sides may now react to the new numbers.

`) + // Build dice breakdown HTML from the reroll + const rerollBreakdown = (reroll.options?.diceResults || []) + .map(r => `${r.dice}${r.value}`) + .join("") + const rerollD30 = reroll.options?.D30message + ? `
D30 → ${reroll.options.D30result || "?"} — ${reroll.options.D30message.description}
` + : "" + await createReactionMessage(attacker, + `

${attackerName} uses Mulligan and re-rolls attack: ${oldAttackRoll}${attackRollFinal}.

+
${rerollBreakdown}
${rerollD30} +

Both sides may now react to the new numbers.

` + ) + // Apply new D30 result on the restart + if (reroll.options?.D30message) { + attackD30message = reroll.options.D30message + attackD30Processed = false + } // Restart the full comparison so both sides can react to the new roll mulliganRestart = true break diff --git a/module/utils.mjs b/module/utils.mjs index af3631a..3403a14 100644 --- a/module/utils.mjs +++ b/module/utils.mjs @@ -1128,7 +1128,6 @@ export default class LethalFantasyUtils { return await RollClass.prompt({ ...foundry.utils.duplicate(rerollContext), rollContext: foundry.utils.duplicate(rerollContext.rollContext || {}), - forceNoD30: true, hasTarget: false, target: false })