From 77c5cfcbb79e7131d58650fdc73016aece3e04f5 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Wed, 3 Jun 2026 21:46:23 +0200 Subject: [PATCH 1/9] Fixed so that actions on homebrew downtime/items don't crash due to note having metadata --- module/applications/sheets-configs/action-base-config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/applications/sheets-configs/action-base-config.mjs b/module/applications/sheets-configs/action-base-config.mjs index e83dfae4..b65e1cdf 100644 --- a/module/applications/sheets-configs/action-base-config.mjs +++ b/module/applications/sheets-configs/action-base-config.mjs @@ -204,7 +204,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) }; } - if (this.action.parent.metadata.isInventoryItem) { + if (this.action.parent.metadata?.isInventoryItem) { options.quantity = { label: 'DAGGERHEART.GENERAL.itemQuantity', group: 'Global' From 6747be49b2089b4cc49200875c9cf82c553fa74f Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Thu, 4 Jun 2026 05:15:41 -0400 Subject: [PATCH 2/9] Allow removing empty string domains (#1968) --- module/applications/settings/homebrewSettings.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index 40ea0301..09bb00f2 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -111,7 +111,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli switch (partId) { case 'domains': - const selectedDomain = this.selected.domain ? this.settings.domains[this.selected.domain] : null; + const selectedDomain = this.settings.domains[this.selected.domain] ?? null; const enrichedDescription = selectedDomain ? await foundry.applications.ux.TextEditor.implementation.enrichHTML(selectedDomain.description) : null; From 5ac4fc3b9ceec0f83e829295e6b197a47cdd4e01 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Thu, 4 Jun 2026 05:42:17 -0400 Subject: [PATCH 3/9] [Fix] visual quirk with blur in unfocused countdown (#1970) * Fix visual quirk with blur in unfocused countdown * Snuck in fixes and refactors --- module/applications/ui/countdowns.mjs | 22 ++++++++++++---------- styles/less/ui/countdown/countdown.less | 13 ++++++------- templates/ui/countdowns.hbs | 12 +++++++----- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/module/applications/ui/countdowns.mjs b/module/applications/ui/countdowns.mjs index 76e2b399..6fa05e29 100644 --- a/module/applications/ui/countdowns.mjs +++ b/module/applications/ui/countdowns.mjs @@ -31,9 +31,9 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application minimizable: false }, actions: { - toggleViewMode: DhCountdowns.#toggleViewMode, - editCountdowns: DhCountdowns.#editCountdowns, - loopCountdown: DhCountdowns.#loopCountdown, + toggleViewMode: DhCountdowns.#onToggleViewMode, + editCountdowns: DhCountdowns.#onEditCountdowns, + loopCountdown: DhCountdowns.#onLoopCountdown, decreaseCountdown: (_, target) => this.editCountdown(false, target), increaseCountdown: (_, target) => this.editCountdown(true, target) }, @@ -147,7 +147,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application return true; } - static async #toggleViewMode() { + static async #onToggleViewMode() { const currentMode = game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownMode); const appMode = CONFIG.DH.GENERAL.countdownAppMode; const newMode = currentMode === appMode.textIcon ? appMode.iconOnly : appMode.textIcon; @@ -158,15 +158,16 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application this.render(); } - static async #editCountdowns() { + static async #onEditCountdowns() { new game.system.api.applications.ui.CountdownEdit().render(true); } - static async #loopCountdown(_, target) { + static async #onLoopCountdown(_, target) { if (!DhCountdowns.canPerformEdit()) return; const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - const countdown = settings.countdowns[target.id]; + const countdownId = target.closest('[data-countdown]').dataset.countdown; + const countdown = settings.countdowns[countdownId]; let progressMax = countdown.progress.start; let message = null; @@ -185,7 +186,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application await waitForDiceSoNice(message); await settings.updateSource({ - [`countdowns.${target.id}.progress`]: { + [`countdowns.${countdownId}.progress`]: { current: newMax, start: newMax } @@ -199,11 +200,12 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application if (!DhCountdowns.canPerformEdit()) return; const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - const countdown = settings.countdowns[target.id]; + const countdownId = target.closest('[data-countdown]').dataset.countdown; + const countdown = settings.countdowns[countdownId]; const newCurrent = increase ? Math.min(countdown.progress.current + 1, countdown.progress.start) : Math.max(countdown.progress.current - 1, 0); - await settings.updateSource({ [`countdowns.${target.id}.progress.current`]: newCurrent }); + await settings.updateSource({ [`countdowns.${countdownId}.progress.current`]: newCurrent }); await emitGMUpdate(GMUpdateEvent.UpdateCountdowns, DhCountdowns.gmSetSetting.bind(settings), settings, null, { refreshType: RefreshType.Countdown }); diff --git a/styles/less/ui/countdown/countdown.less b/styles/less/ui/countdown/countdown.less index 66a6c88a..63e539ba 100644 --- a/styles/less/ui/countdown/countdown.less +++ b/styles/less/ui/countdown/countdown.less @@ -18,7 +18,7 @@ border: 0; box-shadow: none; color: @color-text-primary; - width: 300px; + width: 18.75rem; pointer-events: all; align-self: flex-end; transition: 0.3s right ease-in-out; @@ -36,7 +36,7 @@ transition: opacity var(--ui-fade-duration); } - :not(.performance-low, .noblur) { + &:not(.performance-low, .noblur) { backdrop-filter: blur(5px); } @@ -49,8 +49,7 @@ } &.icon-only { - width: 180px; - min-width: 180px; + width: 12rem; } .countdowns-header, @@ -108,8 +107,8 @@ gap: 16px; img { - width: 44px; - height: 44px; + width: 2.75rem; + height: 2.75rem; border-radius: 6px; } @@ -127,7 +126,7 @@ .countdown-tool-controls { display: flex; align-items: center; - gap: 16px; + gap: var(--spacer-12); } .progress-tag { diff --git a/templates/ui/countdowns.hbs b/templates/ui/countdowns.hbs index 95067826..faaffdc5 100644 --- a/templates/ui/countdowns.hbs +++ b/templates/ui/countdowns.hbs @@ -11,18 +11,20 @@
{{#each countdowns as | countdown id |}} -
+
- {{#unless ../iconOnly}}{{/unless}} + {{#unless ../iconOnly}} +
{{countdown.name}}
+ {{/unless}}
- {{#if countdown.editable}}{{/if}} + {{#if countdown.editable}}{{/if}}
{{countdown.progress.current}}/{{countdown.progress.start}}
- {{#if countdown.editable}}{{/if}} + {{#if countdown.editable}}{{/if}}
{{#if (not ../iconOnly)}} @@ -31,7 +33,7 @@ {{/if}} {{#unless (eq countdown.progress.looping "noLooping")}} - + {{#if (eq countdown.progress.looping "increasing")}} From c0c909584792661089370877e0eb521a836f7ca2 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Thu, 4 Jun 2026 14:08:40 -0400 Subject: [PATCH 4/9] [Fix] Preload ancestry and community features in description (#1967) * Preload ancestry and community features in description * Corrected comments --------- Co-authored-by: WBHarry --- module/data/item/ancestry.mjs | 6 +++++- module/data/item/community.mjs | 6 +++++- module/data/item/subclass.mjs | 2 +- module/helpers/utils.mjs | 3 ++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/module/data/item/ancestry.mjs b/module/data/item/ancestry.mjs index b9253a3c..eae1136c 100644 --- a/module/data/item/ancestry.mjs +++ b/module/data/item/ancestry.mjs @@ -1,6 +1,6 @@ import BaseDataItem from './base.mjs'; import ItemLinkFields from '../../data/fields/itemLinkFields.mjs'; -import { getFeaturesHTMLData } from '../../helpers/utils.mjs'; +import { fromUuids, getFeaturesHTMLData } from '../../helpers/utils.mjs'; export default class DHAncestry extends BaseDataItem { /** @inheritDoc */ @@ -45,6 +45,10 @@ export default class DHAncestry extends BaseDataItem { /**@inheritdoc */ async getDescriptionData() { + // Preload all ancestry features for acquisition from the cache + // todo: make feature acquisition async and replace feature helpers for methods + await fromUuids(this._source.features.map(f => f.item)); + const baseDescription = this.description; const features = await getFeaturesHTMLData(this.features); diff --git a/module/data/item/community.mjs b/module/data/item/community.mjs index 6d054976..6f4470b8 100644 --- a/module/data/item/community.mjs +++ b/module/data/item/community.mjs @@ -1,4 +1,4 @@ -import { getFeaturesHTMLData } from '../../helpers/utils.mjs'; +import { fromUuids, getFeaturesHTMLData } from '../../helpers/utils.mjs'; import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; import BaseDataItem from './base.mjs'; @@ -27,6 +27,10 @@ export default class DHCommunity extends BaseDataItem { /**@inheritdoc */ async getDescriptionData() { + // Preload all community features for acquisition from the cache + // todo: make feature acquisition async and replace feature helpers for methods + await fromUuids(this._source.features); + const baseDescription = this.description; const features = await getFeaturesHTMLData(this.features); diff --git a/module/data/item/subclass.mjs b/module/data/item/subclass.mjs index 55b078c2..934b55d3 100644 --- a/module/data/item/subclass.mjs +++ b/module/data/item/subclass.mjs @@ -91,7 +91,7 @@ export default class DHSubclass extends BaseDataItem { ? game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.spellcastingTrait].label) : null; - // Preload all class features for acquisition from the cache + // Preload all subclass features for acquisition from the cache // todo: make feature acquisition async and replace feature helpers for methods await fromUuids(this._source.features.map(f => f.item)); diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 2f20175b..ddc353b1 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -879,6 +879,7 @@ export async function fromUuids(uuids) { const packEmbeddedEntries = entries.filter( e => !(e.value instanceof Document) && + e.parsed && e.parsed.collection instanceof foundry.documents.collections.CompendiumCollection && e.parsed.embedded.length > 0 ); @@ -895,7 +896,7 @@ export async function fromUuids(uuids) { const pack = game.packs.get(packGroup[0].value.pack); if (!pack) continue; - const ids = packGroup.map(p => p.parsed.id); + const ids = packGroup.map(p => p.parsed?.id).filter(id => !!id); const documents = await pack.getDocuments({ _id__in: ids }); for (const p of packGroup) { p.value = documents.find(d => d.id === p.parsed.id) ?? p.value; From 52b81de11f7224c15f2e76243f4228abb0ea859f Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Fri, 5 Jun 2026 00:30:41 +0200 Subject: [PATCH 5/9] Fixed so that the saved data for an experience that is in the character data is used over that in the levelup data if available (#1971) --- module/applications/levelup/levelup.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/applications/levelup/levelup.mjs b/module/applications/levelup/levelup.mjs index c4616d9a..03638548 100644 --- a/module/applications/levelup/levelup.mjs +++ b/module/applications/levelup/levelup.mjs @@ -358,14 +358,14 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) const experienceIncreaseTagify = htmlElement.querySelector('.levelup-experience-increases'); if (experienceIncreaseTagify) { const allExperiences = { - ...this.actor.system.experiences, ...Object.values(this.levelup.levels).reduce((acc, level) => { for (const key of Object.keys(level.achievements.experiences)) { acc[key] = level.achievements.experiences[key]; } return acc; - }, {}) + }, {}), + ...this.actor.system.experiences }; tagifyElement( experienceIncreaseTagify, From 2fc5b01f091edca3138aa83fdfbf1d055c1a6f19 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 5 Jun 2026 05:31:01 -0400 Subject: [PATCH 6/9] Fix rerolling when hope/fear automation is enabled (#1972) --- module/dice/helpers.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/module/dice/helpers.mjs b/module/dice/helpers.mjs index 33519949..35adb8b7 100644 --- a/module/dice/helpers.mjs +++ b/module/dice/helpers.mjs @@ -1,3 +1,5 @@ +import { ResourceUpdateMap } from '../data/action/baseAction.mjs'; + export function updateResourcesForDualityReroll(oldDuality, newDuality, actor) { const { hopeFear } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); if (game.user.isGM ? !hopeFear.gm : !hopeFear.players) return; From 5be79f4ab83b2b4483a0f4deb7f7e7f60bc60e34 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 5 Jun 2026 05:33:20 -0400 Subject: [PATCH 7/9] Fix several issues with inline damage (#1973) --- module/data/actor/tierAdjustment.mjs | 5 +++-- module/dice/dhRoll.mjs | 1 + module/enrichers/DamageEnricher.mjs | 4 ++-- module/enrichers/parser.mjs | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/module/data/actor/tierAdjustment.mjs b/module/data/actor/tierAdjustment.mjs index 785eec2b..bc6ad176 100644 --- a/module/data/actor/tierAdjustment.mjs +++ b/module/data/actor/tierAdjustment.mjs @@ -1,5 +1,6 @@ import { calculateExpectedValue, parseTermsFromSimpleFormula } from '../../helpers/utils.mjs'; import { adversaryExpectedDamage, adversaryScalingData } from '../../config/actorConfig.mjs'; +import { parseInlineParams } from '../../enrichers/parser.mjs'; export function getTierAdjustedAdversary(source, tier) { const currentTier = source.tier ?? 1; @@ -60,8 +61,8 @@ export function getTierAdjustedAdversary(source, tier) { const descriptionFormulas = []; for (const withDescription of [item.system, ...Object.values(item.system.actions)]) { withDescription.description = withDescription.description.replace(damageRegex, (match, inner) => { - const { value: formula } = parseInlineParams(inner); - if (!formula || !type) return match; + const { value: formula } = parseInlineParams(inner, { first: 'value' }); + if (!formula) return match; try { const newFormula = calculateAdjustedDamage(formula, 'action', damageMeta)?.formula; diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 02c4ab24..fb20870f 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -37,6 +37,7 @@ export default class DHRoll extends Roll { static async buildConfigure(config = {}, message = {}) { config.hooks = [...this.getHooks(), '']; config.dialog ??= {}; + config.damageOptions ??= {}; for (const hook of config.hooks) { if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null; diff --git a/module/enrichers/DamageEnricher.mjs b/module/enrichers/DamageEnricher.mjs index e3f9c42a..db0e8729 100644 --- a/module/enrichers/DamageEnricher.mjs +++ b/module/enrichers/DamageEnricher.mjs @@ -1,7 +1,7 @@ import { parseInlineParams } from './parser.mjs'; export default function DhDamageEnricher(match, _options) { - const { value, type, inline } = parseInlineParams(match[1]); + const { value, type, inline } = parseInlineParams(match[1], { first: 'value' }); if (!value || !type) return match[0]; return getDamageMessage(value, type, inline, match[0]); } @@ -59,7 +59,7 @@ export const renderDamageButton = async event => { { formula: value, applyTo: CONFIG.DH.GENERAL.healingTypes.hitPoints.id, - type: type + damageTypes: type } ] }; diff --git a/module/enrichers/parser.mjs b/module/enrichers/parser.mjs index 365caec9..76ea0b73 100644 --- a/module/enrichers/parser.mjs +++ b/module/enrichers/parser.mjs @@ -8,7 +8,7 @@ export function parseInlineParams(paramString, { first } = {}) { const parts = paramString.split('|').map(x => x.trim()); const params = {}; for (const [idx, param] of parts.entries()) { - if (first && idx === 0) { + if (first && idx === 0 && !param.includes(':')) { params[first] = param; } else { const parts = param.split(':'); From f0a7539018dd9f6c6c1320b1036adb084a483d33 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 5 Jun 2026 06:25:44 -0400 Subject: [PATCH 8/9] Update README.md (#1976) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f59143fd..ac3666b3 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,10 @@ You can find the documentation here: https://github.com/Foundryborne/daggerheart Looking to contribute to the project? Look no further, check out our [contributing guide](CONTRIBUTING.md), and keep the [Code of Conduct](coc.md) in mind when working on things. +## AI Policy + +The Foundryborne Daggerheart system does not make use of AI (generative or otherwise) for any area of its implementation. We expect all contributors to follow this same policy when contributing with a pull request; contributions made using AI will be rejected outright. + ## Disclaimer: **Daggerheart System** From 3527fd7959a54bbee9e4c0419d1a973d2c42c770 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 5 Jun 2026 12:31:30 +0200 Subject: [PATCH 9/9] Raised version --- system.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system.json b/system.json index 588ceafe..ed14a17b 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "2.3.1", + "version": "2.3.2", "compatibility": { "minimum": "14.361", "verified": "14.363", @@ -10,7 +10,7 @@ }, "url": "https://github.com/Foundryborne/daggerheart", "manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json", - "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.3.1/system.zip", + "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.3.2/system.zip", "authors": [ { "name": "WBHarry"