From fc8eb8cb0405415cefc80756663b694a02a1a0e5 Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Sat, 19 Jul 2025 18:08:32 +0200 Subject: [PATCH 1/5] Fix Damage chat message (#377) * Fix Damage chat message * Fix damage save * Fix damage save 2 * Fix damage save 2 * Fix damage save 3 --- module/applications/ui/chatLog.mjs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 29a0f46e..13bfb5f9 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -211,19 +211,19 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected')); for (let target of targets) { - let damages = message.system.damage; + let damages = foundry.utils.deepClone(message.system.damage?.roll ?? message.system.roll); if (message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true) { const mod = CONFIG.DH.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1; - Object.entries(damages).forEach((k,v) => { - let newTotal = 0; - v.forEach(part => { - v.total = Math.ceil(v.total * mod); - newTotal += v.total; + Object.entries(damages).forEach(([k,v]) => { + v.total = 0; + v.parts.forEach(part => { + part.total = Math.ceil(part.total * mod); + v.total += part.total; }) }) } - target.actor.takeDamage(damages.roll); + target.actor.takeDamage(damages); } }; From 615df6541531e63dda198db6448e31ec14ec52cf Mon Sep 17 00:00:00 2001 From: George Brocklehurst Date: Sat, 19 Jul 2025 18:04:28 +0100 Subject: [PATCH 2/5] Fix ctrl+clicking on a downtime action on macOS (#378) * Refactor: handle button data attrs the same. A small refactor to handle `button.dataset.move` (which was assigned to a local const) and `button.dataset.category` (which was accessed directly) in the same way by assigning them both to local consts. * Fix right-click on downtime activities on macOS. On macOS with a single-button mouse (e.g. a laptop trackpad) it's common to trigger a right-click with ctrl+click. In Chrome, this triggers both a `contextmenu` event and a regular `click` event. In the context of downtime actions, this meant that we were deselecting an action in the `contextmenu` handler but then immediately re-selecting it again in the `click` handler. This commit works around the problem by stopping the event from propagating further. This fixes the bug, but also stops Foundry's default `contextmenu` handler from firing and preventing the browser context menu from appearing, so we also have prevent the event's default behaviour from firing. --- module/applications/dialogs/downtime.mjs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/module/applications/dialogs/downtime.mjs b/module/applications/dialogs/downtime.mjs index 15add3ad..4922b4ed 100644 --- a/module/applications/dialogs/downtime.mjs +++ b/module/applications/dialogs/downtime.mjs @@ -113,13 +113,24 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV deselectMove(event) { const button = event.target.closest('.activity-container'); - const move = button.dataset.move; - this.moveData[button.dataset.category].moves[move].selected = this.moveData[button.dataset.category].moves[move] - .selected - ? this.moveData[button.dataset.category].moves[move].selected - 1 + const { move, category } = button.dataset; + this.moveData[category].moves[move].selected = this.moveData[category].moves[move].selected + ? this.moveData[category].moves[move].selected - 1 : 0; this.render(); + + // On macOS with a single-button mouse (e.g. a laptop trackpad), + // right-click is triggered with ctrl+click, which triggers both a + // `contextmenu` event and a regular click event. We need to stop + // event propagation to prevent the click event from triggering the + // `selectMove` function and undoing the change we just made. + event.stopPropagation(); + + // Having stopped propagation, we're no longer subject to Foundry's + // default `contextmenu` handler, so we also have to prevent the + // default behaviour to prevent a context menu from appearing. + event.preventDefault(); } static async takeDowntime() { From 6772a8fbd8b12b48dc9be634b64496663f38c1ce Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 19 Jul 2025 20:15:49 +0200 Subject: [PATCH 3/5] Added DamageEnricher --- daggerheart.mjs | 28 +++++----- module/enrichers/DamageEnricher.mjs | 82 +++++++++++++++++++++++++++++ module/enrichers/_module.mjs | 22 +++++++- 3 files changed, 117 insertions(+), 15 deletions(-) create mode 100644 module/enrichers/DamageEnricher.mjs diff --git a/daggerheart.mjs b/daggerheart.mjs index 22586e90..5bb0b016 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -3,7 +3,7 @@ import * as applications from './module/applications/_module.mjs'; import * as models from './module/data/_module.mjs'; import * as documents from './module/documents/_module.mjs'; import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs'; -import { DhDualityRollEnricher, DhTemplateEnricher } from './module/enrichers/_module.mjs'; +import { enricherConfig } from './module/enrichers/_module.mjs'; import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs'; import { NarrativeCountdowns } from './module/applications/ui/countdowns.mjs'; import { DualityRollColor } from './module/data/settings/Appearance.mjs'; @@ -20,6 +20,7 @@ import { placeables } from './module/canvas/_module.mjs'; import { registerRollDiceHooks } from './module/dice/dhRoll.mjs'; import { registerDHActorHooks } from './module/documents/actor.mjs'; import './node_modules/@yaireo/tagify/dist/tagify.css'; +import { renderDamageButton } from './module/enrichers/DamageEnricher.mjs'; Hooks.once('init', () => { CONFIG.DH = SYSTEM; @@ -29,18 +30,7 @@ Hooks.once('init', () => { documents }; - CONFIG.TextEditor.enrichers.push( - ...[ - { - pattern: /\[\[\/dr\s?(.*?)\]\]/g, - enricher: DhDualityRollEnricher - }, - { - pattern: /^@Template\[(.*)\]$/g, - enricher: DhTemplateEnricher - } - ] - ); + CONFIG.TextEditor.enrichers.push(...enricherConfig); CONFIG.statusEffects = [ ...CONFIG.statusEffects.filter(x => !['dead', 'unconscious'].includes(x.id)), @@ -178,6 +168,10 @@ Hooks.on('ready', () => { Hooks.once('dicesoniceready', () => {}); Hooks.on('renderChatMessageHTML', (_, element) => { + element + .querySelectorAll('.enriched-damage-button') + .forEach(element => element.addEventListener('click', renderDamageButton)); + element .querySelectorAll('.duality-roll-button') .forEach(element => element.addEventListener('click', renderDualityButton)); @@ -188,6 +182,10 @@ Hooks.on('renderChatMessageHTML', (_, element) => { }); Hooks.on('renderJournalEntryPageProseMirrorSheet', (_, element) => { + element + .querySelectorAll('.enriched-damage-button') + .forEach(element => element.addEventListener('click', renderDamageButton)); + element .querySelectorAll('.duality-roll-button') .forEach(element => element.addEventListener('click', renderDualityButton)); @@ -198,6 +196,10 @@ Hooks.on('renderJournalEntryPageProseMirrorSheet', (_, element) => { }); Hooks.on('renderHandlebarsApplication', (_, element) => { + element + .querySelectorAll('.enriched-damage-button') + .forEach(element => element.addEventListener('click', renderDamageButton)); + element .querySelectorAll('.duality-roll-button') .forEach(element => element.addEventListener('click', renderDualityButton)); diff --git a/module/enrichers/DamageEnricher.mjs b/module/enrichers/DamageEnricher.mjs new file mode 100644 index 00000000..5c277b81 --- /dev/null +++ b/module/enrichers/DamageEnricher.mjs @@ -0,0 +1,82 @@ +import { rollCommandToJSON } from '../helpers/utils.mjs'; + +export default function DhDamageEnricher(match, _options) { + const parts = match[1].split('|').map(x => x.trim()); + + let value = null, + type = null; + + parts.forEach(part => { + const split = part.split(':').map(x => x.toLowerCase().trim()); + if (split.length === 2) { + switch (split[0]) { + case 'value': + value = split[1]; + break; + case 'type': + type = split[1]; + break; + } + } + }); + + if (!value || !value) return match[0]; + + return getDamageMessage(value, type, match[0]); +} + +export function getDamageMessage(damage, type, defaultElement) { + const dualityElement = document.createElement('span'); + const typeIcons = type + .replace('[', '') + .replace(']', '') + .split(',') + .map(x => x.trim()) + .map(x => { + return CONFIG.DH.GENERAL.damageTypes[x]?.icon ?? null; + }) + .filter(x => x); + + if (!typeIcons.length) return defaultElement; + + const iconNodes = typeIcons.map(x => ``).join(''); + + dualityElement.innerHTML = ` + + `; + + return dualityElement; +} + +export const renderDamageButton = async event => { + const button = event.currentTarget, + value = button.dataset.value, + type = button.dataset.type + .replace('[', '') + .replace(']', '') + .split(',') + .map(x => x.trim()); + + const config = { + event: event, + title: game.i18n.localize('Damage Roll'), + data: { bonuses: [] }, + source: {}, + roll: [ + { + formula: value, + applyTo: CONFIG.DH.GENERAL.healingTypes.hitPoints.id, + type: type + } + ] + }; + + CONFIG.Dice.daggerheart.DamageRoll.build(config); +}; diff --git a/module/enrichers/_module.mjs b/module/enrichers/_module.mjs index 1907f117..ed825f03 100644 --- a/module/enrichers/_module.mjs +++ b/module/enrichers/_module.mjs @@ -1,2 +1,20 @@ -export { default as DhDualityRollEnricher } from './DualityRollEnricher.mjs'; -export { default as DhTemplateEnricher } from './TemplateEnricher.mjs'; +import { default as DhDamageEnricher } from './DamageEnricher.mjs'; +import { default as DhDualityRollEnricher } from './DualityRollEnricher.mjs'; +import { default as DhTemplateEnricher } from './TemplateEnricher.mjs'; + +export { DhDamageEnricher, DhDualityRollEnricher, DhTemplateEnricher }; + +export const enricherConfig = [ + { + pattern: /^@Damage\[(.*)\]$/g, + enricher: DhDamageEnricher + }, + { + pattern: /\[\[\/dr\s?(.*?)\]\]/g, + enricher: DhDualityRollEnricher + }, + { + pattern: /^@Template\[(.*)\]$/g, + enricher: DhTemplateEnricher + } +]; From d3836fbeebeb89ba3049f27a527fe8431201c661 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 19 Jul 2025 22:19:55 +0200 Subject: [PATCH 4/5] Added effect enricher --- daggerheart.mjs | 38 +++--------------------- lang/en.json | 3 +- module/enrichers/DamageEnricher.mjs | 6 ++-- module/enrichers/DualityRollEnricher.mjs | 2 +- module/enrichers/EffectEnricher.mjs | 19 ++++++++++++ module/enrichers/_module.mjs | 31 ++++++++++++++++--- styles/less/ui/chat/chat.less | 1 + styles/less/ui/chat/sheet.less | 21 +++++++++++++ 8 files changed, 77 insertions(+), 44 deletions(-) create mode 100644 module/enrichers/EffectEnricher.mjs diff --git a/daggerheart.mjs b/daggerheart.mjs index 5bb0b016..a3bf35ca 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -3,7 +3,7 @@ import * as applications from './module/applications/_module.mjs'; import * as models from './module/data/_module.mjs'; import * as documents from './module/documents/_module.mjs'; import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs'; -import { enricherConfig } from './module/enrichers/_module.mjs'; +import { enricherConfig, enricherRenderSetup } from './module/enrichers/_module.mjs'; import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs'; import { NarrativeCountdowns } from './module/applications/ui/countdowns.mjs'; import { DualityRollColor } from './module/data/settings/Appearance.mjs'; @@ -168,45 +168,15 @@ Hooks.on('ready', () => { Hooks.once('dicesoniceready', () => {}); Hooks.on('renderChatMessageHTML', (_, element) => { - element - .querySelectorAll('.enriched-damage-button') - .forEach(element => element.addEventListener('click', renderDamageButton)); - - element - .querySelectorAll('.duality-roll-button') - .forEach(element => element.addEventListener('click', renderDualityButton)); - - element - .querySelectorAll('.measured-template-button') - .forEach(element => element.addEventListener('click', renderMeasuredTemplate)); + enricherRenderSetup(element); }); Hooks.on('renderJournalEntryPageProseMirrorSheet', (_, element) => { - element - .querySelectorAll('.enriched-damage-button') - .forEach(element => element.addEventListener('click', renderDamageButton)); - - element - .querySelectorAll('.duality-roll-button') - .forEach(element => element.addEventListener('click', renderDualityButton)); - - element - .querySelectorAll('.measured-template-button') - .forEach(element => element.addEventListener('click', renderMeasuredTemplate)); + enricherRenderSetup(element); }); Hooks.on('renderHandlebarsApplication', (_, element) => { - element - .querySelectorAll('.enriched-damage-button') - .forEach(element => element.addEventListener('click', renderDamageButton)); - - element - .querySelectorAll('.duality-roll-button') - .forEach(element => element.addEventListener('click', renderDualityButton)); - - element - .querySelectorAll('.measured-template-button') - .forEach(element => element.addEventListener('click', renderMeasuredTemplate)); + enricherRenderSetup(element); }); Hooks.on('chatMessage', (_, message) => { diff --git a/lang/en.json b/lang/en.json index 9f929c77..8a170bd6 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1590,7 +1590,8 @@ "sendToVault": "Send to Vault", "sendToLoadout": "Send to Loadout", "makeDeathMove": "Make a Death Move", - "rangeAndTarget": "Range & Target" + "rangeAndTarget": "Range & Target", + "dragApplyEffect": "Drag effect to apply it to an actor" } } } diff --git a/module/enrichers/DamageEnricher.mjs b/module/enrichers/DamageEnricher.mjs index 5c277b81..918edc39 100644 --- a/module/enrichers/DamageEnricher.mjs +++ b/module/enrichers/DamageEnricher.mjs @@ -1,5 +1,3 @@ -import { rollCommandToJSON } from '../helpers/utils.mjs'; - export default function DhDamageEnricher(match, _options) { const parts = match[1].split('|').map(x => x.trim()); @@ -25,8 +23,7 @@ export default function DhDamageEnricher(match, _options) { return getDamageMessage(value, type, match[0]); } -export function getDamageMessage(damage, type, defaultElement) { - const dualityElement = document.createElement('span'); +function getDamageMessage(damage, type, defaultElement) { const typeIcons = type .replace('[', '') .replace(']', '') @@ -41,6 +38,7 @@ export function getDamageMessage(damage, type, defaultElement) { const iconNodes = typeIcons.map(x => ``).join(''); + const dualityElement = document.createElement('span'); dualityElement.innerHTML = `