diff --git a/templates/settings/homebrew-settings/itemFeatures.hbs b/templates/settings/homebrew-settings/itemFeatures.hbs
index 22c23af6..df3419fa 100644
--- a/templates/settings/homebrew-settings/itemFeatures.hbs
+++ b/templates/settings/homebrew-settings/itemFeatures.hbs
@@ -8,7 +8,7 @@
@@ -22,7 +22,7 @@
From c90875fa7cb14553fd223362433daf186af3c11c Mon Sep 17 00:00:00 2001
From: WBHarry
Date: Wed, 21 Jan 2026 14:05:39 +0100
Subject: [PATCH 10/94] Changed ResetDialog to have all optional sections
initially kept
---
module/applications/dialogs/characterResetDialog.mjs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/module/applications/dialogs/characterResetDialog.mjs b/module/applications/dialogs/characterResetDialog.mjs
index 1f3f3d5a..0836af9c 100644
--- a/module/applications/dialogs/characterResetDialog.mjs
+++ b/module/applications/dialogs/characterResetDialog.mjs
@@ -16,7 +16,7 @@ export default class CharacterResetDialog extends HandlebarsApplicationMixin(App
portrait: { keep: true, label: 'DAGGERHEART.GENERAL.portrait' },
name: { keep: true, label: 'Name' },
biography: { keep: true, label: 'DAGGERHEART.GENERAL.Tabs.biography' },
- inventory: { keep: false, label: 'DAGGERHEART.GENERAL.inventory' }
+ inventory: { keep: true, label: 'DAGGERHEART.GENERAL.inventory' }
}
};
}
From 1f7d4d6f1e864fe1fd38aa59601a52f0e28c6553 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Thu, 22 Jan 2026 16:52:22 +0100
Subject: [PATCH 11/94] Fixed an error where a player having their token
initially selected caused an error in effectsDisplay.mjs (#1569)
---
module/applications/ui/effectsDisplay.mjs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/module/applications/ui/effectsDisplay.mjs b/module/applications/ui/effectsDisplay.mjs
index 0875e783..8c0c939c 100644
--- a/module/applications/ui/effectsDisplay.mjs
+++ b/module/applications/ui/effectsDisplay.mjs
@@ -76,6 +76,8 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica
};
toggleHidden(token, focused) {
+ if (!this.element) return;
+
const effects = DhEffectsDisplay.getTokenEffects(focused ? token : null);
this.element.hidden = effects.length === 0;
From fa3b3fa0a81401ce6e8022582d7bb096004d4806 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Thu, 22 Jan 2026 23:21:42 +0100
Subject: [PATCH 12/94] [Fix] Damage Rerolls (#1566)
* Fixed so that damage rerolls work again
* Set default data for a roll instead and fix title (#1570)
* Set default data for a roll instead and fix title
* Ensure same options object is used
---------
Co-authored-by: Carlos Fernandez
---
lang/en.json | 1 +
module/dice/dhRoll.mjs | 2 +-
system.json | 2 +-
3 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/lang/en.json b/lang/en.json
index 18048d9d..33f8c514 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -607,6 +607,7 @@
},
"RerollDialog": {
"title": "Reroll",
+ "damageTitle": "Reroll Damage",
"deselectDiceNotification": "Deselect one of the selected dice first",
"acceptCurrentRolls": "Accept Current Rolls"
},
diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs
index a5ac5091..1977c7ea 100644
--- a/module/dice/dhRoll.mjs
+++ b/module/dice/dhRoll.mjs
@@ -3,7 +3,7 @@ import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
export default class DHRoll extends Roll {
baseTerms = [];
constructor(formula, data = {}, options = {}) {
- super(formula, data, options);
+ super(formula, data, foundry.utils.mergeObject(options, { roll: [] }, { overwrite: false }));
options.bonusEffects = this.bonusEffectBuilder();
if (!this.data || !Object.keys(this.data).length) this.data = options.data;
}
diff --git a/system.json b/system.json
index 2c68f785..6521c6d6 100644
--- a/system.json
+++ b/system.json
@@ -2,7 +2,7 @@
"id": "daggerheart",
"title": "Daggerheart",
"description": "An unofficial implementation of the Daggerheart system",
- "version": "1.5.4",
+ "version": "1.5.5",
"compatibility": {
"minimum": "13.346",
"verified": "13.351",
From 21ef288283bef655918412b25fe4bf692e1e97d1 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Fri, 23 Jan 2026 11:51:14 +0100
Subject: [PATCH 13/94] Fixed when users drag in compendium environments to the
sceneEnvironments (#1573)
---
.../applications/scene/sceneConfigSettings.mjs | 13 ++++++++++++-
module/systemRegistration/migrations.mjs | 16 ++++++++++++++++
2 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/module/applications/scene/sceneConfigSettings.mjs b/module/applications/scene/sceneConfigSettings.mjs
index 8a58db5c..98e18f09 100644
--- a/module/applications/scene/sceneConfigSettings.mjs
+++ b/module/applications/scene/sceneConfigSettings.mjs
@@ -65,8 +65,15 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
const item = await foundry.utils.fromUuid(data.uuid);
if (item instanceof game.system.api.documents.DhpActor && item.type === 'environment') {
+ let sceneUuid = data.uuid;
+ if (item.pack) {
+ const inWorldActor = await game.system.api.documents.DhpActor.create([item.toObject()]);
+ if (!inWorldActor.length) return;
+ sceneUuid = inWorldActor[0].uuid;
+ }
+
await this.daggerheartFlag.updateSource({
- sceneEnvironments: [...this.daggerheartFlag.sceneEnvironments, data.uuid]
+ sceneEnvironments: [...this.daggerheartFlag.sceneEnvironments, sceneUuid]
});
this.render({ internalRefresh: true });
}
@@ -97,6 +104,10 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S
/** @override */
async _processSubmitData(event, form, submitData, options) {
submitData.flags.daggerheart = this.daggerheartFlag.toObject();
+ submitData.flags.daggerheart.sceneEnvironments = submitData.flags.daggerheart.sceneEnvironments.filter(x =>
+ foundry.utils.fromUuidSync(x)
+ );
+
for (const key of Object.keys(this.document._source.flags.daggerheart?.sceneEnvironments ?? {})) {
if (!submitData.flags.daggerheart.sceneEnvironments[key]) {
submitData.flags.daggerheart.sceneEnvironments[`-=${key}`] = null;
diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs
index b3116459..086647bf 100644
--- a/module/systemRegistration/migrations.mjs
+++ b/module/systemRegistration/migrations.mjs
@@ -210,6 +210,22 @@ export async function runMigrations() {
lastMigrationVersion = '1.2.7';
}
+
+ if (foundry.utils.isNewerVersion('1.5.5', lastMigrationVersion)) {
+ for (const scene of game.scenes) {
+ if (!scene.flags.daggerheart) continue;
+ const systemData = new game.system.api.data.scenes.DHScene(scene.flags.daggerheart);
+ const sceneEnvironments = systemData.sceneEnvironments;
+
+ const newEnvironments = sceneEnvironments.filter(x => !x?.pack);
+ if (newEnvironments.length !== sceneEnvironments.length)
+ await scene.update({ 'flags.daggerheart.sceneEnvironments': newEnvironments });
+ }
+
+ ui.nav.render(true);
+
+ lastMigrationVersion = '1.5.5';
+ }
//#endregion
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion);
From cbd268ea1f51ab59a1cede31fb28c6f91d3c112c Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Sat, 24 Jan 2026 11:10:30 +0100
Subject: [PATCH 14/94] [Feature] DR Command Resources (#1572)
* Dr chatcommand and buttons now grant resources via automation by default. Optionally turned off via parameter noResources=true
* .
---
daggerheart.mjs | 4 +++-
module/dice/dualityRoll.mjs | 2 +-
module/enrichers/DualityRollEnricher.mjs | 16 ++++++++++++----
3 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/daggerheart.mjs b/daggerheart.mjs
index 3abcd210..c3a5c348 100644
--- a/daggerheart.mjs
+++ b/daggerheart.mjs
@@ -296,6 +296,7 @@ Hooks.on('chatMessage', (_, message) => {
? CONFIG.DH.ACTIONS.advantageState.disadvantage.value
: undefined;
const difficulty = rollCommand.difficulty;
+ const grantResources = Boolean(rollCommand.grantResources);
const target = getCommandTarget({ allowNull: true });
const title = traitValue
@@ -312,7 +313,8 @@ Hooks.on('chatMessage', (_, message) => {
title,
label: game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll'),
actionType: null,
- advantage
+ advantage,
+ grantResources
});
return false;
}
diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs
index aaca7400..30b569ac 100644
--- a/module/dice/dualityRoll.mjs
+++ b/module/dice/dualityRoll.mjs
@@ -261,7 +261,7 @@ export default class DualityRoll extends D20Roll {
}
static async handleTriggers(roll, config) {
- if (!config.source?.actor) return;
+ if (!config.source?.actor || config.skips?.triggers) return;
const updates = [];
const dualityUpdates = await game.system.registeredTriggers.runTrigger(
diff --git a/module/enrichers/DualityRollEnricher.mjs b/module/enrichers/DualityRollEnricher.mjs
index 536847f7..67728a37 100644
--- a/module/enrichers/DualityRollEnricher.mjs
+++ b/module/enrichers/DualityRollEnricher.mjs
@@ -47,6 +47,7 @@ function getDualityMessage(roll, flavor) {
${roll?.trait && abilities[roll.trait] ? `data-trait="${roll.trait}"` : ''}
${roll?.advantage ? 'data-advantage="true"' : ''}
${roll?.disadvantage ? 'data-disadvantage="true"' : ''}
+ ${roll?.grantResources ? 'data-grant-resources="true"' : ''}
>
${roll?.reaction ? '' : ''}
${label}
@@ -63,7 +64,8 @@ export const renderDualityButton = async event => {
traitValue = button.dataset.trait?.toLowerCase(),
target = getCommandTarget({ allowNull: true }),
difficulty = button.dataset.difficulty,
- advantage = button.dataset.advantage ? Number(button.dataset.advantage) : undefined;
+ advantage = button.dataset.advantage ? Number(button.dataset.advantage) : undefined,
+ grantResources = Boolean(button.dataset?.grantResources);
await enrichedDualityRoll(
{
@@ -73,14 +75,15 @@ export const renderDualityButton = async event => {
difficulty,
title: button.dataset.title,
label: button.dataset.label,
- advantage
+ advantage,
+ grantResources
},
event
);
};
export const enrichedDualityRoll = async (
- { reaction, traitValue, target, difficulty, title, label, advantage },
+ { reaction, traitValue, target, difficulty, title, label, advantage, grantResources },
event
) => {
const config = {
@@ -93,12 +96,17 @@ export const enrichedDualityRoll = async (
advantage,
type: reaction ? 'reaction' : null
},
+ skips: {
+ resources: !grantResources,
+ triggers: !grantResources
+ },
type: 'trait',
hasRoll: true
};
if (target) {
- await target.diceRoll(config);
+ const result = await target.diceRoll(config);
+ result.resourceUpdates.updateResources();
} else {
// For no target, call DualityRoll directly with basic data
config.data = { experiences: {}, traits: {}, rules: {} };
From bdb89973248dc0001814a3e393697c850c528446 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Sat, 24 Jan 2026 11:15:07 +0100
Subject: [PATCH 15/94] [Feature] 1543 - SceneEnvironment Trigger Registration
(#1564)
* Added trigger Registration/Unregistration for scene environments
* Fixed so that saving throw damage mitigation works again (#1555)
* . (#1563)
* Fixed DowntimeMoves and ItemFeatures reset functions (#1568)
* Fixed an error where a player having their token initially selected caused an error in effectsDisplay.mjs (#1569)
* [Fix] Damage Rerolls (#1566)
* Fixed so that damage rerolls work again
* Set default data for a roll instead and fix title (#1570)
* Set default data for a roll instead and fix title
* Ensure same options object is used
---------
Co-authored-by: Carlos Fernandez
* Fixed when users drag in compendium environments to the sceneEnvironments (#1573)
* .
---------
Co-authored-by: Carlos Fernandez
---
module/data/registeredTriggers.mjs | 34 ++++++++++++++++++++----------
module/documents/scene.mjs | 21 ++++++++++++++++++
2 files changed, 44 insertions(+), 11 deletions(-)
diff --git a/module/data/registeredTriggers.mjs b/module/data/registeredTriggers.mjs
index 3fd9f82c..ee4f3b49 100644
--- a/module/data/registeredTriggers.mjs
+++ b/module/data/registeredTriggers.mjs
@@ -72,10 +72,21 @@ export default class RegisteredTriggers extends Map {
}
}
+ unregisterSceneEnvironmentTriggers(flagSystemData) {
+ const sceneData = new game.system.api.data.scenes.DHScene(flagSystemData);
+ for (const environment of sceneData.sceneEnvironments) {
+ if (environment.pack) continue;
+ this.unregisterItemTriggers(environment.system.features);
+ }
+ }
+
unregisterSceneTriggers(scene) {
+ this.unregisterSceneEnvironmentTriggers(scene.flags.daggerheart);
+
for (const triggerKey of Object.keys(CONFIG.DH.TRIGGER.triggers)) {
const existingTrigger = this.get(triggerKey);
if (!existingTrigger) continue;
+
const filtered = new Map();
for (const [uuid, data] of existingTrigger.entries()) {
if (!uuid.startsWith(scene.uuid)) filtered.set(uuid, data);
@@ -84,14 +95,17 @@ export default class RegisteredTriggers extends Map {
}
}
+ registerSceneEnvironmentTriggers(flagSystemData) {
+ const sceneData = new game.system.api.data.scenes.DHScene(flagSystemData);
+ for (const environment of sceneData.sceneEnvironments) {
+ for (const feature of environment.system.features) {
+ if (feature) this.registerItemTriggers(feature, true);
+ }
+ }
+ }
+
registerSceneTriggers(scene) {
- /* TODO: Finish sceneEnvironment registration and unreg */
- // const systemData = new game.system.api.data.scenes.DHScene(scene.flags.daggerheart);
- // for (const environment of systemData.sceneEnvironments) {
- // for (const feature of environment.system.features) {
- // if(feature) this.registerItemTriggers(feature, true);
- // }
- // }
+ this.registerSceneEnvironmentTriggers(scene.flags.daggerheart);
for (const actor of scene.tokens.filter(x => x.actor).map(x => x.actor)) {
if (actor.prototypeToken.actorLink) continue;
@@ -108,13 +122,11 @@ export default class RegisteredTriggers extends Map {
if (!triggerSettings.enabled) return updates;
const dualityTrigger = this.get(trigger);
- if (dualityTrigger) {
- const tokenBoundActors = ['adversary', 'environment'];
- const triggerActors = ['character', ...tokenBoundActors];
+ if (dualityTrigger?.size) {
+ const triggerActors = ['character', 'adversary', 'environment'];
for (let [itemUuid, { actor: actorUuid, triggeringActorType, commands }] of dualityTrigger.entries()) {
const actor = await foundry.utils.fromUuid(actorUuid);
if (!actor || !triggerActors.includes(actor.type)) continue;
- if (tokenBoundActors.includes(actor.type) && !actor.getActiveTokens().length) continue;
const triggerData = CONFIG.DH.TRIGGER.triggers[trigger];
if (triggerData.usesActor && triggeringActorType !== 'any') {
diff --git a/module/documents/scene.mjs b/module/documents/scene.mjs
index 7f880b1d..9e2a3f5b 100644
--- a/module/documents/scene.mjs
+++ b/module/documents/scene.mjs
@@ -51,6 +51,27 @@ export default class DhScene extends Scene {
}
}
+ async _preUpdate(changes, options, user) {
+ const allowed = await super._preUpdate(changes, options, user);
+ if (allowed === false) return false;
+
+ if (changes.flags?.daggerheart) {
+ if (this._source.flags.daggerheart) {
+ const unregisterTriggerData = this._source.flags.daggerheart.sceneEnvironments.reduce(
+ (acc, env) => {
+ if (!changes.flags.daggerheart.sceneEnvironments.includes(env)) acc.sceneEnvironments.push(env);
+
+ return acc;
+ },
+ { ...this._source.flags.daggerheart, sceneEnvironments: [] }
+ );
+ game.system.registeredTriggers.unregisterSceneEnvironmentTriggers(unregisterTriggerData);
+ }
+
+ game.system.registeredTriggers.registerSceneEnvironmentTriggers(changes.flags.daggerheart);
+ }
+ }
+
_onDelete(options, userId) {
super._onDelete(options, userId);
From cb998860d91cad3fa4d295677b78606ca156116c Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Sat, 24 Jan 2026 20:19:16 +0100
Subject: [PATCH 16/94] Spellcastmodifiers were not being sorted correctly for
use (#1578)
---
module/data/actor/character.mjs | 2 +-
templates/sheets/actors/character/sidebar.hbs | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs
index 12396384..47660da4 100644
--- a/module/data/actor/character.mjs
+++ b/module/data/actor/character.mjs
@@ -368,7 +368,7 @@ export default class DhCharacter extends BaseDataActor {
const modifiers = subClasses
?.map(sc => ({ ...this.traits[sc.system.spellcastingTrait], key: sc.system.spellcastingTrait }))
.filter(x => x);
- return modifiers.sort((a, b) => a.value - b.value)[0];
+ return modifiers.sort((a, b) => (b.value ?? 0) - (a.value ?? 0))[0];
}
get spellcastModifier() {
diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs
index 0db2bf42..24e68e02 100644
--- a/templates/sheets/actors/character/sidebar.hbs
+++ b/templates/sheets/actors/character/sidebar.hbs
@@ -1,12 +1,12 @@