From 318d00b47d9173f52adf749aeeb83664f6bafd92 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 1 Jun 2026 10:24:44 +0200
Subject: [PATCH 01/19] Add NPC Improvements
---
module/applications/sheets/actors/party.mjs | 15 +------
styles/less/sheets/actors/npc/header.less | 2 +-
templates/sheets/actors/npc/header.hbs | 10 +++--
.../sheets/actors/party/party-members.hbs | 42 ++++++++++---------
4 files changed, 31 insertions(+), 38 deletions(-)
diff --git a/module/applications/sheets/actors/party.mjs b/module/applications/sheets/actors/party.mjs
index cec1e1f0..927a8810 100644
--- a/module/applications/sheets/actors/party.mjs
+++ b/module/applications/sheets/actors/party.mjs
@@ -47,11 +47,6 @@ export default class Party extends DHBaseActorSheet {
template: 'systems/daggerheart/templates/sheets/actors/party/party-members.hbs',
scrollable: ['']
},
- /* NOT YET IMPLEMENTED */
- // projects: {
- // template: 'systems/daggerheart/templates/sheets/actors/party/projects.hbs',
- // scrollable: ['']
- // },
inventory: {
template: 'systems/daggerheart/templates/sheets/actors/party/inventory.hbs',
scrollable: ['.tab.inventory .items-section']
@@ -62,19 +57,13 @@ export default class Party extends DHBaseActorSheet {
/** @inheritdoc */
static TABS = {
primary: {
- tabs: [
- { id: 'partyMembers' },
- /* NOT YET IMPLEMENTED */
- // { id: 'projects' },
- { id: 'inventory' },
- { id: 'notes' }
- ],
+ tabs: [{ id: 'partyMembers' }, { id: 'inventory' }, { id: 'notes' }],
initial: 'partyMembers',
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
}
};
- static ALLOWED_ACTOR_TYPES = ['character', 'companion', 'adversary'];
+ static ALLOWED_ACTOR_TYPES = ['character', 'companion', 'adversary', 'npc'];
static DICE_ROLL_ACTOR_TYPES = ['character'];
async _onRender(context, options) {
diff --git a/styles/less/sheets/actors/npc/header.less b/styles/less/sheets/actors/npc/header.less
index d49d763c..086a254c 100644
--- a/styles/less/sheets/actors/npc/header.less
+++ b/styles/less/sheets/actors/npc/header.less
@@ -5,7 +5,7 @@
.portrait {
cursor: pointer;
- width: 275px;
+ max-width: 275px;
img {
height: 275px;
diff --git a/templates/sheets/actors/npc/header.hbs b/templates/sheets/actors/npc/header.hbs
index 8dc345dc..fce300da 100644
--- a/templates/sheets/actors/npc/header.hbs
+++ b/templates/sheets/actors/npc/header.hbs
@@ -31,10 +31,12 @@
{{{description}}}
-
- {{localize 'DAGGERHEART.ACTORS.NPC.FIELDS.motives.label'}}:
- {{source.system.motives}}
-
+ {{#if source.system.motives}}
+
+ {{localize 'DAGGERHEART.ACTORS.NPC.FIELDS.motives.label'}}:
+ {{source.system.motives}}
+
+ {{/if}}
\ No newline at end of file
diff --git a/templates/sheets/actors/party/party-members.hbs b/templates/sheets/actors/party/party-members.hbs
index c7870bc6..55cacb79 100644
--- a/templates/sheets/actors/party/party-members.hbs
+++ b/templates/sheets/actors/party/party-members.hbs
@@ -42,7 +42,7 @@
{{#if member.difficulty includeZero=true}}
{{member.difficulty}}
{{/if}}
- {{#unless (eq member.type 'companion')}}
+ {{#unless (or (eq member.type 'companion') (eq member.type 'npc'))}}
{{localize "DAGGERHEART.ACTORS.Party.Thresholds.minor"}}
{{member.damageThresholds.major}}
@@ -58,7 +58,7 @@
- {{#unless (or (eq member.type 'companion') (eq member.type 'adversary')) }}
+ {{#unless (or (eq member.type 'companion') (eq member.type 'adversary') (eq member.type 'npc')) }}
{{localize "DAGGERHEART.GENERAL.hope"}}
{{#times member.resources.hope.max}}
@@ -79,7 +79,7 @@
- {{#unless (eq member.type 'companion') }}
+ {{#unless (or (eq member.type 'companion') (eq member.type 'npc')) }}
@@ -101,25 +101,27 @@
{{/unless}}
-
-
-
-
-
-
- {{member.resources.stress.value}}
- /
- {{member.resources.stress.max}}
-
-
-
- {{#times member.resources.stress.max}}
-
+ {{#unless (eq member.type 'npc')}}
+
+
+
+
- {{/times}}
+
+ {{member.resources.stress.value}}
+ /
+ {{member.resources.stress.max}}
+
+
+
+ {{#times member.resources.stress.max}}
+
+
+ {{/times}}
+
-
+ {{/unless}}
{{#if member.armorScore.max}}
From 98ce49b928196c931f315bf2be03e98150ddbb50 Mon Sep 17 00:00:00 2001
From: Carlos Fernandez
Date: Mon, 1 Jun 2026 05:06:24 -0400
Subject: [PATCH 02/19] Avoid default type on name and item create dialogs
(#1958)
---
daggerheart.mjs | 30 ++++++++++++++++++++++++++++++
module/documents/actor.mjs | 5 +++++
module/documents/item.mjs | 1 +
3 files changed, 36 insertions(+)
diff --git a/daggerheart.mjs b/daggerheart.mjs
index 23977628..25c41ced 100644
--- a/daggerheart.mjs
+++ b/daggerheart.mjs
@@ -446,3 +446,33 @@ Hooks.on('canvasTearDown', canvas => {
Hooks.on('canvasReady', canas => {
game.system.registeredTriggers.registerSceneTriggers(canvas.scene);
});
+
+/** Make the user to select a document type, instead of having a default doc type for them to accidentally keep */
+Hooks.on('renderDialogV2', (_dialog, html) => {
+ if (!html.classList.contains('dialog')) return;
+ const cls = html.classList.contains('item-create')
+ ? documents.DHItem.implementation
+ : html.classList.contains('actor-create')
+ ? documents.DhpActor.implementation
+ : null;
+ if (!cls) return;
+
+ const form = html.querySelector('form');
+ const submit = html.querySelector('button[type=submit]');
+ const select = html.querySelector('select[name=type]');
+ const nameInput = html.querySelector('input[name=name]');
+ if (!form || !select || !submit || !nameInput) return;
+
+ nameInput.placeholder = cls.defaultName({});
+ const emptyOption = document.createElement('option');
+ emptyOption.value = '';
+ emptyOption.selected = true;
+ select.required = true;
+ select.prepend(emptyOption);
+ submit.addEventListener('click', event => {
+ if (!form.reportValidity()) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ });
+});
diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs
index 6c462d98..fb10435f 100644
--- a/module/documents/actor.mjs
+++ b/module/documents/actor.mjs
@@ -65,6 +65,11 @@ export default class DhpActor extends Actor {
};
}
+ static createDialog(data, createOptions, options, renderOptions) {
+ options.classes = [options.classes ?? [], 'actor-create'].flat(); // handled in hook
+ return super.createDialog(data, createOptions, options, renderOptions);
+ }
+
/* -------------------------------------------- */
/** @inheritDoc */
diff --git a/module/documents/item.mjs b/module/documents/item.mjs
index 603ca594..f46e24e6 100644
--- a/module/documents/item.mjs
+++ b/module/documents/item.mjs
@@ -82,6 +82,7 @@ export default class DHItem extends foundry.documents.Item {
/** @inheritdoc */
static async createDialog(data = {}, createOptions = {}, options = {}) {
const { folders, types, template, context = {}, ...dialogOptions } = options;
+ dialogOptions.classes = [options.classes ?? [], 'item-create'].flat(); // handled in hook
if (types?.length === 0) {
throw new Error('The array of sub-types to restrict to must not be empty.');
From d0c29ede56b9834c4a71ac58656524b1996b724f Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 1 Jun 2026 12:24:00 +0200
Subject: [PATCH 03/19] Fixed so the combat tracker doesn't error out if a
combatant has no attached token (#1960)
---
module/applications/ui/combatTracker.mjs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/module/applications/ui/combatTracker.mjs b/module/applications/ui/combatTracker.mjs
index 0989bcb8..8dd76850 100644
--- a/module/applications/ui/combatTracker.mjs
+++ b/module/applications/ui/combatTracker.mjs
@@ -133,7 +133,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
canPing: combatant.sceneId === canvas.scene?.id && game.user.hasPermission('PING_CANVAS'),
type: combatant.actor?.system?.type,
img: await this._getCombatantThumbnail(combatant),
- disposition: combatant.token.disposition
+ disposition: combatant.token?.disposition
};
turn.css = [turn.active ? 'active' : null, hidden ? 'hide' : null, isDefeated ? 'defeated' : null].filterJoin(
From 6448666579f4cca9a3f72c15db9cbe2e049d3208 Mon Sep 17 00:00:00 2001
From: WBHarry
Date: Mon, 1 Jun 2026 19:47:06 +0200
Subject: [PATCH 04/19] Updated combat contextmenu
---
module/applications/ui/combatTracker.mjs | 44 ++++++++++++++++---
.../ui/combatTracker/combatTrackerHeader.hbs | 5 +++
2 files changed, 42 insertions(+), 7 deletions(-)
diff --git a/module/applications/ui/combatTracker.mjs b/module/applications/ui/combatTracker.mjs
index 8dd76850..25f3e06b 100644
--- a/module/applications/ui/combatTracker.mjs
+++ b/module/applications/ui/combatTracker.mjs
@@ -84,19 +84,49 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
});
}
+ /**
+ * Open the dialog used to edit the name of the currently viewed Combat encounter.
+ * @this {CombatTracker}
+ * @returns {Promise}
+ */
+ static async #onEditName() {
+ const combat = this.viewed;
+ if (!combat || !game.user.isGM) return null;
+ const field = combat.schema.fields.name;
+ const inputHTML = field.toFormGroup({}, { name: 'name', value: combat.name, autofocus: true }).outerHTML;
+ const formData = await foundry.applications.api.DialogV2.input({
+ window: { icon: 'fa-solid fa-tag', title: 'COMBAT.ACTIONS.EditNameTitle' },
+ position: { width: 480 },
+ content: inputHTML
+ });
+ await combat.update({ name: formData.name || '' });
+ }
+
_getCombatContextOptions() {
return [
{
- label: 'COMBAT.ClearMovementHistories',
- icon: '',
- visible: () => game.user.isGM && this.viewed?.combatants.size > 0,
- callback: () => this.viewed.clearMovementHistories()
+ label: 'COMBAT.ACTIONS.EditName',
+ icon: 'fa-solid fa-tag',
+ visible: () => game.user.isGM && !!this.viewed,
+ onClick: () => DhCombatTracker.#onEditName.call(this)
},
{
- label: 'COMBAT.Delete',
- icon: '',
+ label: 'COMBAT.ACTIONS.LinkToScene',
+ icon: '',
+ visible: () => game.user.isGM && !this.scene,
+ onClick: () => this.viewed.toggleSceneLink()
+ },
+ {
+ label: 'COMBAT.ACTIONS.UnlinkFromScene',
+ icon: '',
+ visible: () => game.user.isGM && !!this.scene,
+ onClick: () => this.viewed.toggleSceneLink()
+ },
+ {
+ label: 'COMBAT.End',
+ icon: 'fa-solid fa-xmark',
visible: () => game.user.isGM && !!this.viewed,
- callback: () => this.viewed.endCombat()
+ onClick: () => this.viewed.endCombat()
}
];
}
diff --git a/templates/ui/combatTracker/combatTrackerHeader.hbs b/templates/ui/combatTracker/combatTrackerHeader.hbs
index 803286ab..9f5a7561 100644
--- a/templates/ui/combatTracker/combatTrackerHeader.hbs
+++ b/templates/ui/combatTracker/combatTrackerHeader.hbs
@@ -50,6 +50,11 @@
{{/if}}
+ {{!-- Encounter Name --}}
+ {{#if combat.name}}
+ {{ combat.name }}
+ {{/if}}
+
{{!-- Combat Status --}}
From 646ebc8bdf0fa6bcb18a2997c76802bff44a59a1 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 1 Jun 2026 21:52:27 +0200
Subject: [PATCH 05/19] Added a temp fix for status effect rows (#1965)
---
styles/less/hud/token-hud/token-hud.less | 3 +++
1 file changed, 3 insertions(+)
diff --git a/styles/less/hud/token-hud/token-hud.less b/styles/less/hud/token-hud/token-hud.less
index 3cb94e1e..3b998f4e 100644
--- a/styles/less/hud/token-hud/token-hud.less
+++ b/styles/less/hud/token-hud/token-hud.less
@@ -38,6 +38,9 @@
}
.status-effects {
+ // TODO: Remove when the issue https://github.com/foundryvtt/foundryvtt/issues/14410 is resolved and Foundry handles it cleanly themselves.
+ grid-template-rows: min-content;
+
.effect-control-container {
position: relative;
From df4a2c5d57c3592d3c15550c1f2fadc36bdba655 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 1 Jun 2026 21:53:28 +0200
Subject: [PATCH 06/19] Fixed Countdown.migrationData assuming an array when
it's actually an object (#1962)
---
module/data/action/countdownAction.mjs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/module/data/action/countdownAction.mjs b/module/data/action/countdownAction.mjs
index abcc6b40..cb141637 100644
--- a/module/data/action/countdownAction.mjs
+++ b/module/data/action/countdownAction.mjs
@@ -36,7 +36,7 @@ export default class DhCountdownAction extends DHBaseAction {
/** @inheritDoc */
static migrateData(source) {
- for (const countdown of source.countdown) {
+ for (const countdown of Object.values(source.countdown)) {
if (countdown.progress.max) {
countdown.progress.startFormula = countdown.progress.max;
countdown.progress.start = 1;
From bcf274f1d05819cf9148ab597e558962e270a2e2 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 1 Jun 2026 21:59:44 +0200
Subject: [PATCH 07/19] [Fix] ChatMessage Saves Pending Confirmation (#1963)
---
lang/en.json | 6 +++---
module/documents/chatMessage.mjs | 24 ++++++++++++++++++++++--
2 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/lang/en.json b/lang/en.json
index 9ce515d9..3a1340e0 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -711,9 +711,9 @@
},
"PendingReactionsDialog": {
"title": "Pending Reaction Rolls Found",
- "unfinishedRolls": "Some Tokens still need to roll their Reaction Roll.",
- "confirmation": "Are you sure you want to continue ?",
- "warning": "Undone reaction rolls will be considered as failed"
+ "unfinishedRolls": "Some Tokens have not finished their Reaction Rolls.",
+ "warning": "Unfinished reaction rolls will be considered as failed.",
+ "confirmation": "Are you sure you want to continue?"
},
"ReactionRoll": {
"title": "Reaction Roll: {trait}"
diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs
index 78bab016..893e6e5c 100644
--- a/module/documents/chatMessage.mjs
+++ b/module/documents/chatMessage.mjs
@@ -183,7 +183,11 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
if (pendingingSaves.length) {
const confirm = await foundry.applications.api.DialogV2.confirm({
window: { title: game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.title') },
- content: `${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.unfinishedRolls')}
${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.confirmation')}
${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.warning')}
`
+ content: `
+ ${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.unfinishedRolls')}
+ ${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.warning')}
+ ${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.confirmation')}
+ `
});
if (!confirm) return;
}
@@ -247,8 +251,24 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
const targets = this.filterPermTargets(this.system.hitTargets),
config = foundry.utils.deepClone(this.system);
config.event = event;
+
if (targets.length === 0)
- ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
+ return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
+ else if (config.hasSave) {
+ const pendingingSaves = targets.filter(t => t.saved.success === null);
+ if (pendingingSaves.length) {
+ const confirm = await foundry.applications.api.DialogV2.confirm({
+ window: { title: game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.title') },
+ content: `
+ ${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.unfinishedRolls')}
+ ${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.warning')}
+ ${game.i18n.localize('DAGGERHEART.APPLICATIONS.PendingReactionsDialog.confirmation')}
+ `
+ });
+ if (!confirm) return;
+ }
+ }
+
this.consumeOnSuccess();
this.system.action?.workflow.get('effects')?.execute(config, targets, true);
}
From 3c36c5747d477a831d6b0d5a653a414073f6f39c Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 1 Jun 2026 22:02:42 +0200
Subject: [PATCH 08/19] [Fix] Base Attack Context Menu (#1961)
* Fixed Adversary standard attack context menu
* Fixed Character base attack context menu
* Fixed Companion base attack context menu
---
.../applications/sheets/actors/adversary.mjs | 10 +++++
.../applications/sheets/actors/character.mjs | 8 ++++
.../applications/sheets/actors/companion.mjs | 12 +++++-
module/applications/sheets/api/base-actor.mjs | 37 +++++++++++++++++++
module/data/action/attackAction.mjs | 7 +++-
5 files changed, 72 insertions(+), 2 deletions(-)
diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs
index 04be3efb..06dd4a0f 100644
--- a/module/applications/sheets/actors/adversary.mjs
+++ b/module/applications/sheets/actors/adversary.mjs
@@ -31,6 +31,16 @@ export default class AdversarySheet extends DHBaseActorSheet {
dragSelector: '[data-item-id][draggable="true"], [data-item-id] [draggable="true"]',
dropSelector: null
}
+ ],
+ contextMenus: [
+ {
+ handler: DHBaseActorSheet.getBaseAttackContextOptions,
+ selector: '[data-item-uuid][data-type="attack"]',
+ options: {
+ parentClassHooks: false,
+ fixed: true
+ }
+ }
]
};
diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs
index 19b82712..e4d0e6d9 100644
--- a/module/applications/sheets/actors/character.mjs
+++ b/module/applications/sheets/actors/character.mjs
@@ -65,6 +65,14 @@ export default class CharacterSheet extends DHBaseActorSheet {
fixed: true
}
},
+ {
+ handler: DHBaseActorSheet.getBaseAttackContextOptions,
+ selector: '[data-item-uuid][data-type="attack"]',
+ options: {
+ parentClassHooks: false,
+ fixed: true
+ }
+ },
{
handler: CharacterSheet.#getDomainCardContextOptions,
selector: '[data-item-uuid][data-type="domainCard"]',
diff --git a/module/applications/sheets/actors/companion.mjs b/module/applications/sheets/actors/companion.mjs
index b30b9c07..a01b4a64 100644
--- a/module/applications/sheets/actors/companion.mjs
+++ b/module/applications/sheets/actors/companion.mjs
@@ -11,7 +11,17 @@ export default class DhCompanionSheet extends DHBaseActorSheet {
toggleStress: DhCompanionSheet.#toggleStress,
actionRoll: DhCompanionSheet.#actionRoll,
levelManagement: DhCompanionSheet.#levelManagement
- }
+ },
+ contextMenus: [
+ {
+ handler: DHBaseActorSheet.getBaseAttackContextOptions,
+ selector: '[data-item-uuid][data-type="attack"]',
+ options: {
+ parentClassHooks: false,
+ fixed: true
+ }
+ }
+ ]
};
static PARTS = {
diff --git a/module/applications/sheets/api/base-actor.mjs b/module/applications/sheets/api/base-actor.mjs
index 5cd0f6a5..7b820822 100644
--- a/module/applications/sheets/api/base-actor.mjs
+++ b/module/applications/sheets/api/base-actor.mjs
@@ -189,6 +189,43 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true });
}
+ /**
+ * Get the set of ContextMenu options for the base attack.
+ * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
+ * @this {CharacterSheet}
+ * @protected
+ */
+ static getBaseAttackContextOptions() {
+ /**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */
+ return [
+ {
+ label: 'DAGGERHEART.CONFIG.RollTypes.attack.name',
+ icon: 'fa-solid fa-burst',
+ onClick: async (event, target) => (await getDocFromElement(target)).use(event)
+ },
+ {
+ label: 'DAGGERHEART.GENERAL.damage',
+ icon: 'fa-solid fa-explosion',
+ onClick: async (event, target) => {
+ const doc = await getDocFromElement(target),
+ action = doc?.system?.attack ?? doc;
+ const config = action.prepareConfig(event);
+ config.effects = await game.system.api.data.actions.actionsTypes.base.getEffects(
+ this.document,
+ doc
+ );
+ config.hasRoll = false;
+ return action && action.workflow.get('damage').execute(config, null, true);
+ }
+ },
+ {
+ label: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
+ icon: 'fa-solid fa-message',
+ onClick: async (_, target) => (await getDocFromElement(target)).toChat(this.document.uuid)
+ }
+ ];
+ }
+
/* -------------------------------------------- */
/* Application Listener Actions */
/* -------------------------------------------- */
diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs
index c4d07c25..1f7e1c92 100644
--- a/module/data/action/attackAction.mjs
+++ b/module/data/action/attackAction.mjs
@@ -75,7 +75,12 @@ export default class DHAttackAction extends DHDamageAction {
const useAltDamage = this.actor?.effects?.find(x => x.type === 'horde')?.active;
for (const { value, valueAlt, type } of damage.parts) {
const usedValue = useAltDamage ? valueAlt : value;
- const str = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {});
+ const damageString = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {});
+ const str = damageString
+ ? damageString
+ : game.i18n.format('DAGGERHEART.GENERAL.missingX', {
+ x: game.i18n.localize('DAGGERHEART.GENERAL.damage')
+ });
const icons = Array.from(type)
.map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon)
From d98a7c951e5363ce1980b1540767bad923385e64 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 1 Jun 2026 22:20:06 +0200
Subject: [PATCH 09/19] [Fix] Tooltip Color Scope (#1964)
* Added DH style to tooltips
* Setting dh-style for ResourceManagementTooltip and ArmorManagementTooltip
---
module/applications/sheets/actors/character.mjs | 4 ++--
module/documents/tooltipManager.mjs | 1 -
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs
index e4d0e6d9..5d0e7144 100644
--- a/module/applications/sheets/actors/character.mjs
+++ b/module/applications/sheets/actors/character.mjs
@@ -1053,7 +1053,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
game.tooltip.activate(target, {
html,
locked: true,
- cssClass: 'bordered-tooltip',
+ cssClass: 'bordered-tooltip dh-style',
direction: 'DOWN'
});
@@ -1149,7 +1149,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
game.tooltip.activate(target, {
html,
locked: true,
- cssClass: 'bordered-tooltip',
+ cssClass: 'bordered-tooltip dh-style',
direction: 'DOWN',
noOffset: true
});
diff --git a/module/documents/tooltipManager.mjs b/module/documents/tooltipManager.mjs
index 18c03169..3e3f4a16 100644
--- a/module/documents/tooltipManager.mjs
+++ b/module/documents/tooltipManager.mjs
@@ -3,7 +3,6 @@ import { AdversaryBPPerEncounter, BaseBPPerEncounter } from '../config/encounter
export default class DhTooltipManager extends foundry.helpers.interaction.TooltipManager {
#wide = false;
#bordered = false;
- #active = false;
async activate(element, options = {}) {
const { TextEditor } = foundry.applications.ux;
From 5dbcd9448064f97d202abb541cd5b4c5072fe544 Mon Sep 17 00:00:00 2001
From: WBHarry
Date: Mon, 1 Jun 2026 22:22:50 +0200
Subject: [PATCH 10/19] Raised version
---
system.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/system.json b/system.json
index 5994c576..588ceafe 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.0",
+ "version": "2.3.1",
"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.0/system.zip",
+ "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.3.1/system.zip",
"authors": [
{
"name": "WBHarry"
From 77c5cfcbb79e7131d58650fdc73016aece3e04f5 Mon Sep 17 00:00:00 2001
From: WBHarry
Date: Wed, 3 Jun 2026 21:46:23 +0200
Subject: [PATCH 11/19] 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 12/19] 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 13/19] [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}}
+
+ {{/unless}}