diff --git a/daggerheart.mjs b/daggerheart.mjs index 1366dc60..f939743c 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -187,12 +187,15 @@ Hooks.on('renderHandlebarsApplication', (_, element) => { Hooks.on('chatMessage', (_, message) => { if (message.startsWith('/dr')) { - const rollCommand = rollCommandToJSON(message.replace(/\/dr\s?/, '')); - if (!rollCommand) { + const result = rollCommandToJSON(message.replace(/\/dr\s?/, '')); + if (!result) { ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.dualityParsing')); return false; } + const { result: rollCommand, flavor } = result; + + const reaction = rollCommand.reaction; const traitValue = rollCommand.trait?.toLowerCase(); const advantage = rollCommand.advantage ? CONFIG.DH.ACTIONS.advantageState.advantage.value @@ -208,7 +211,16 @@ Hooks.on('chatMessage', (_, message) => { }) : game.i18n.localize('DAGGERHEART.GENERAL.duality'); - enrichedDualityRoll({ traitValue, target, difficulty, title, label: 'test', actionType: null, advantage }); + enrichedDualityRoll({ + reaction, + traitValue, + target, + difficulty, + title, + label: 'test', + actionType: null, + advantage + }); return false; } }); diff --git a/lang/en.json b/lang/en.json index 8846f995..1ccfeac8 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1821,7 +1821,6 @@ "basics": "Basics", "bonus": "Bonus", "burden": "Burden", - "check": "{check} Check", "continue": "Continue", "criticalSuccess": "Critical Success", "damage": "Damage", @@ -1866,6 +1865,7 @@ "proficiency": "Proficiency", "quantity": "Quantity", "range": "Range", + "reactionRoll": "Reaction Roll", "recovery": "Recovery", "reroll": "Reroll", "rerollThing": "Reroll {thing}", @@ -1873,6 +1873,7 @@ "roll": "Roll", "rollAll": "Roll All", "rollDamage": "Roll Damage", + "rollWith": "{roll} Roll", "save": "Save", "scalable": "Scalable", "situationalBonus": "Situational Bonus", diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index 76871b6a..9075c454 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -7,6 +7,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.roll = roll; this.config = config; this.config.experiences = []; + this.reactionOverride = config.roll.type === 'reaction'; if (config.source?.action) { this.item = config.data.parent.items.get(config.source.item) ?? config.data.parent; @@ -30,6 +31,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio actions: { updateIsAdvantage: this.updateIsAdvantage, selectExperience: this.selectExperience, + toggleReaction: this.toggleReaction, submitRoll: this.submitRoll }, form: { @@ -103,6 +105,9 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio context.isLite = this.config.roll?.lite; context.extraFormula = this.config.extraFormula; context.formula = this.roll.constructFormula(this.config); + + context.showReaction = !context.rollConfig.type && context.rollType === 'DualityRoll'; + context.reactionOverride = this.reactionOverride; } return context; } @@ -141,7 +146,19 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.render(); } + static toggleReaction() { + if (this.config.roll) { + this.reactionOverride = !this.reactionOverride; + this.render(); + } + } + static async submitRoll() { + this.config.roll.type = this.reactionOverride + ? CONFIG.DH.ITEM.actionTypes.reaction.id + : this.config.roll.type === CONFIG.DH.ITEM.actionTypes.reaction.id + ? null + : this.config.roll.type; await this.close({ submitted: true }); } diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index bacaf47b..fcf30be5 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -69,6 +69,7 @@ export default class DHRoll extends Roll { static postEvaluate(roll, config = {}) { return { + type: config.roll.type, total: roll.total, formula: roll.formula, dice: roll.dice.map(d => ({ diff --git a/module/enrichers/DualityRollEnricher.mjs b/module/enrichers/DualityRollEnricher.mjs index 24dd0602..4a6fac23 100644 --- a/module/enrichers/DualityRollEnricher.mjs +++ b/module/enrichers/DualityRollEnricher.mjs @@ -2,22 +2,23 @@ import { abilities } from '../config/actorConfig.mjs'; import { getCommandTarget, rollCommandToJSON } from '../helpers/utils.mjs'; export default function DhDualityRollEnricher(match, _options) { - const roll = rollCommandToJSON(match[1]); + const roll = rollCommandToJSON(match[1], match[0]); if (!roll) return match[0]; - return getDualityMessage(roll); + return getDualityMessage(roll.result, roll.flavor); } -function getDualityMessage(roll) { - const traitLabel = - roll.trait && abilities[roll.trait] - ? game.i18n.format('DAGGERHEART.GENERAL.check', { - check: game.i18n.localize(abilities[roll.trait].label) - }) - : null; +function getDualityMessage(roll, flavor) { + const trait = roll.trait && abilities[roll.trait] ? game.i18n.localize(abilities[roll.trait].label) : null; + const label = + flavor ?? + (roll.trait + ? game.i18n.format('DAGGERHEART.GENERAL.rollWith', { roll: trait }) + : roll.reaction + ? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll') + : game.i18n.localize('DAGGERHEART.GENERAL.duality')); - const label = traitLabel ?? game.i18n.localize('DAGGERHEART.GENERAL.duality'); - const dataLabel = traitLabel + const dataLabel = trait ? game.i18n.localize(abilities[roll.trait].label) : game.i18n.localize('DAGGERHEART.GENERAL.duality'); @@ -38,6 +39,7 @@ function getDualityMessage(roll) { `; @@ -57,6 +59,7 @@ function getDualityMessage(roll) { export const renderDualityButton = async event => { const button = event.currentTarget, + reaction = button.dataset.reaction === 'true', traitValue = button.dataset.trait?.toLowerCase(), target = getCommandTarget({ allowNull: true }), difficulty = button.dataset.difficulty, @@ -64,12 +67,12 @@ export const renderDualityButton = async event => { await enrichedDualityRoll( { + reaction, traitValue, target, difficulty, title: button.dataset.title, label: button.dataset.label, - actionType: button.dataset.actionType, advantage }, event @@ -77,7 +80,7 @@ export const renderDualityButton = async event => { }; export const enrichedDualityRoll = async ( - { traitValue, target, difficulty, title, label, actionType, advantage }, + { reaction, traitValue, target, difficulty, title, label, advantage }, event ) => { const config = { @@ -88,7 +91,7 @@ export const enrichedDualityRoll = async ( label: label, difficulty: difficulty, advantage, - type: actionType ?? null // Need check, + type: reaction ? 'reaction' : null }, chatMessage: { template: 'systems/daggerheart/templates/ui/chat/duality-roll.hbs' diff --git a/module/enrichers/_module.mjs b/module/enrichers/_module.mjs index abfd8158..3b597dd5 100644 --- a/module/enrichers/_module.mjs +++ b/module/enrichers/_module.mjs @@ -7,19 +7,19 @@ export { DhDamageEnricher, DhDualityRollEnricher, DhEffectEnricher, DhTemplateEn export const enricherConfig = [ { - pattern: /^@Damage\[(.*)\]$/g, + pattern: /^@Damage\[(.*)\]({.*})?$/g, enricher: DhDamageEnricher }, { - pattern: /\[\[\/dr\s?(.*?)\]\]/g, + pattern: /\[\[\/dr\s?(.*?)\]\]({.*})?/g, enricher: DhDualityRollEnricher }, { - pattern: /^@Effect\[(.*)\]$/g, + pattern: /^@Effect\[(.*)\]({.*})?$/g, enricher: DhEffectEnricher }, { - pattern: /^@Template\[(.*)\]$/g, + pattern: /^@Template\[(.*)\]({.*})?$/g, enricher: DhTemplateEnricher } ]; diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 66e743a3..9d22906c 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -5,9 +5,12 @@ export const capitalize = string => { return string.charAt(0).toUpperCase() + string.slice(1); }; -export function rollCommandToJSON(text) { +export function rollCommandToJSON(text, raw) { if (!text) return {}; + const flavorMatch = raw?.match(/{(.*)}$/); + const flavor = flavorMatch ? flavorMatch[1] : null; + // Match key="quoted string" OR key=unquotedValue const PAIR_RE = /(\w+)=("(?:[^"\\]|\\.)*"|\S+)/g; const result = {}; @@ -28,7 +31,7 @@ export function rollCommandToJSON(text) { } result[key] = value; } - return Object.keys(result).length > 0 ? result : null; + return Object.keys(result).length > 0 ? { result, flavor } : null; } export const getCommandTarget = (options = {}) => { diff --git a/styles/less/dialog/dice-roll/roll-selection.less b/styles/less/dialog/dice-roll/roll-selection.less index 4ee2ee1f..d536ee04 100644 --- a/styles/less/dialog/dice-roll/roll-selection.less +++ b/styles/less/dialog/dice-roll/roll-selection.less @@ -9,6 +9,36 @@ } .application.daggerheart.dialog.dh-style.views.roll-selection { + .dialog-header { + display: flex; + justify-content: center; + + h1 { + width: auto; + display: flex; + align-items: center; + gap: 8px; + + .reaction-roll-controller { + width: auto; + opacity: 0.3; + border-radius: 50%; + font-size: 18px; + font-weight: bold; + + &:hover { + opacity: 0.5; + background: light-dark(transparent, @golden); + color: light-dark(@dark-blue, @dark-blue); + } + + &.active { + opacity: 1; + } + } + } + } + .roll-dialog-container { display: flex; flex-direction: column; @@ -16,6 +46,7 @@ max-width: 550px; .dices-section { + position: relative; display: flex; gap: 60px; justify-content: center; diff --git a/templates/dialogs/dice-roll/header.hbs b/templates/dialogs/dice-roll/header.hbs index 462408f6..cea07209 100644 --- a/templates/dialogs/dice-roll/header.hbs +++ b/templates/dialogs/dice-roll/header.hbs @@ -1,7 +1,10 @@
- {{#if rollConfig.headerTitle}} -

{{rollConfig.headerTitle}}

- {{else}} -

{{rollConfig.title}}

- {{/if}} +

+ {{ifThen rollConfig.headerTitle rollConfig.headerTitle rollConfig.title}} + {{#if showReaction}} + + {{/if}} +

\ No newline at end of file diff --git a/templates/dialogs/dice-roll/rollSelection.hbs b/templates/dialogs/dice-roll/rollSelection.hbs index 995be022..e517bc6d 100644 --- a/templates/dialogs/dice-roll/rollSelection.hbs +++ b/templates/dialogs/dice-roll/rollSelection.hbs @@ -40,6 +40,7 @@ +