From e7c3bcc362cf90567fbff504ab1a8fbc10e04ce8 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 22 Jul 2025 22:16:05 +0200 Subject: [PATCH] Added support for d20 rolls --- lang/en.json | 5 +++ module/applications/ui/chatLog.mjs | 61 +++++++--------------------- module/dice/d20Roll.mjs | 18 ++++++++ module/dice/dualityRoll.mjs | 50 +++++++++++++++++++++++ styles/less/ui/chat/chat.less | 13 ++++++ templates/ui/chat/adversary-roll.hbs | 15 +++---- 6 files changed, 107 insertions(+), 55 deletions(-) diff --git a/lang/en.json b/lang/en.json index 9c7bae99..8af39fa3 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1339,6 +1339,7 @@ "quantity": "Quantity", "range": "Range", "recovery": "Recovery", + "reroll": "Reroll", "rerollThing": "Reroll {thing}", "resource": "Resource", "roll": "Roll", @@ -1616,6 +1617,10 @@ "title": "Heal - {healing}", "heal": "Heal" }, + "reroll": { + "confirmTitle": "Reroll Dice", + "confirmText": "Are you sure you want to reroll?" + }, "resourceRoll": { "playerMessage": "{user} rerolled their {name}" } diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 4c9ce4f3..d32a515c 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -1,5 +1,3 @@ -import { getDiceSoNicePresets } from '../../config/generalConfig.mjs'; - export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog { constructor(options) { super(options); @@ -316,52 +314,23 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo }; rerollEvent = async (event, message) => { - const target = event.target.closest('button[data-die-index]'); - let originalRoll_parsed = message.rolls.map(roll => JSON.parse(roll))[0]; - let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...originalRoll_parsed, evaluated: false }); - const term = parsedRoll.terms[target.dataset.dieIndex]; - await term.reroll(`/r1=${term.total}`); - if (game.modules.get('dice-so-nice')?.active) { - const diceSoNiceRoll = { - _evaluated: true, - dice: [ - new foundry.dice.terms.Die({ - ...term, - faces: term._faces, - results: term.results.filter(x => !x.rerolled) - }) - ], - options: { appearance: {} } - }; - const diceSoNicePresets = getDiceSoNicePresets(); - switch (target.dataset.type) { - case 'hope': - diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.hope }; - break; - case 'fear': - diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.fear }; - break; - case 'advantage': - diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.advantage }; - break; - case 'disadvantage': - diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.disadvantage }; - break; - } - - await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true); + if (!event.shiftKey) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.localize('DAGGERHEART.UI.Chat.reroll.confirmTitle') + }, + content: game.i18n.localize('DAGGERHEART.UI.Chat.reroll.confirmText') + }); + if (!confirmed) return; } - await parsedRoll.evaluate(); - - const newRoll = game.system.api.dice.DualityRoll.postEvaluate(parsedRoll, { - targets: message.system.targets, - roll: { - advantage: message.system.roll.advantage?.type, - difficulty: message.system.roll.difficulty ? Number(message.system.roll.difficulty) : null - } - }); - newRoll.extra = newRoll.extra.slice(2); + const target = event.target.closest('button[data-die-index]'); + let originalRoll_parsed = message.rolls.map(roll => JSON.parse(roll))[0]; + const rollClass = + game.system.api.dice[ + message.type === 'dualityRoll' ? 'DualityRoll' : target.dataset.type === 'damage' ? 'DHRoll' : 'D20Roll' + ]; + const { newRoll, parsedRoll } = await rollClass.reroll(originalRoll_parsed, target, message); await game.messages.get(message._id).update({ 'system.roll': newRoll, diff --git a/module/dice/d20Roll.mjs b/module/dice/d20Roll.mjs index 1bcf7348..5f95b80d 100644 --- a/module/dice/d20Roll.mjs +++ b/module/dice/d20Roll.mjs @@ -172,4 +172,22 @@ export default class D20Roll extends DHRoll { resetFormula() { return (this._formula = this.constructor.getFormula(this.terms)); } + + static async reroll(rollString, _target, message) { + let parsedRoll = game.system.api.dice.D20Roll.fromData(rollString); + parsedRoll = await parsedRoll.reroll(); + const newRoll = game.system.api.dice.D20Roll.postEvaluate(parsedRoll, { + targets: message.system.targets, + roll: { + advantage: message.system.roll.advantage?.type, + difficulty: message.system.roll.difficulty ? Number(message.system.roll.difficulty) : null + } + }); + + if (game.modules.get('dice-so-nice')?.active) { + await game.dice3d.showForRoll(parsedRoll, game.user, true); + } + + return { newRoll, parsedRoll }; + } } diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 38a3a870..900102b8 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -1,6 +1,7 @@ import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs'; import D20Roll from './d20Roll.mjs'; import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs'; +import { getDiceSoNicePresets } from '../config/generalConfig.mjs'; export default class DualityRoll extends D20Roll { _advantageFaces = 6; @@ -193,4 +194,53 @@ export default class DualityRoll extends D20Roll { return data; } + + static async reroll(rollString, target, message) { + let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...rollString, evaluated: false }); + const term = parsedRoll.terms[target.dataset.dieIndex]; + await term.reroll(`/r1=${term.total}`); + if (game.modules.get('dice-so-nice')?.active) { + const diceSoNiceRoll = { + _evaluated: true, + dice: [ + new foundry.dice.terms.Die({ + ...term, + faces: term._faces, + results: term.results.filter(x => !x.rerolled) + }) + ], + options: { appearance: {} } + }; + const diceSoNicePresets = getDiceSoNicePresets(); + switch (target.dataset.type) { + case 'hope': + diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.hope }; + break; + case 'fear': + diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.fear }; + break; + case 'advantage': + diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.advantage }; + break; + case 'disadvantage': + diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.disadvantage }; + break; + } + + await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true); + } + + await parsedRoll.evaluate(); + + const newRoll = game.system.api.dice.DualityRoll.postEvaluate(parsedRoll, { + targets: message.system.targets, + roll: { + advantage: message.system.roll.advantage?.type, + difficulty: message.system.roll.difficulty ? Number(message.system.roll.difficulty) : null + } + }); + newRoll.extra = newRoll.extra.slice(2); + + return { newRoll, parsedRoll }; + } } diff --git a/styles/less/ui/chat/chat.less b/styles/less/ui/chat/chat.less index 5c646efc..c593a919 100644 --- a/styles/less/ui/chat/chat.less +++ b/styles/less/ui/chat/chat.less @@ -60,6 +60,19 @@ } } } + + &.rerollable { + .reroll-button { + border: none; + background: initial; + + &:hover { + background: var(--button-background-color); + border: 1px solid var(--button-border-color); + } + } + } + // margin: 0; > .roll { display: flex; diff --git a/templates/ui/chat/adversary-roll.hbs b/templates/ui/chat/adversary-roll.hbs index a37f93e3..acf9a759 100644 --- a/templates/ui/chat/adversary-roll.hbs +++ b/templates/ui/chat/adversary-roll.hbs @@ -12,16 +12,13 @@ {{total}}
-
    - {{#each results}} -
  1. {{result}}
  2. - {{/each}} +
      +
    - {{#if (eq index 0)}} -
    - {{#if (eq ../roll.advantage.type 1)}}{{localize "DAGGERHEART.GENERAL.Advantage.full"}}{{/if}}{{#if (eq ../roll.advantage.type -1)}}{{localize "DAGGERHEART.GENERAL.Disadvantage.full"}}{{/if}} -
    - {{/if}}
{{/each}}