From 7df43d71e018f4748de3510abae31c0ccfc69a27 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Sun, 16 Nov 2025 01:14:01 +0100
Subject: [PATCH 1/8] [Fix] Item Browser Corrections (#1290)
* Fixed so lists do not fail to show items when search is empty
* Fixed so that the clear button loads in the items
* .
* .
---
lang/en.json | 1 +
module/applications/ui/itemBrowser.mjs | 5 +++--
module/config/itemBrowserConfig.mjs | 12 +++++++-----
3 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/lang/en.json b/lang/en.json
index d6b19a45..86f0359e 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -2570,6 +2570,7 @@
"evasionMin": "Evasion (Min)",
"evasionMax": "Evasion (Max)",
"subtype": "Subtype",
+ "missing": "Missing ",
"folders": {
"characters": "Characters",
"adversaries": "Adversaries",
diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs
index 33995aa9..07d5cd74 100644
--- a/module/applications/ui/itemBrowser.mjs
+++ b/module/applications/ui/itemBrowser.mjs
@@ -294,7 +294,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
const property = foundry.utils.getProperty(item, field.key);
if (Array.isArray(property)) property.join(', ');
if (typeof field.format !== 'function') return property ?? '-';
- return field.format(property);
+ return game.i18n.localize(field.format(property));
}
formatChoices(data) {
@@ -430,7 +430,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
if (matchesMenu) this.#filteredItems.browser.input.add(item.id);
const { search } = this.#filteredItems.browser;
- li.hidden = !(search.has(item.id) && matchesMenu);
+ li.hidden = !((this.#search.browser.query.length === 0 || search.has(item.id)) && matchesMenu);
}
}
@@ -469,6 +469,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
static resetFilters() {
this.render({ force: true });
+ this.loadItems();
}
static getFolderConfig(folder, property = 'columns') {
diff --git a/module/config/itemBrowserConfig.mjs b/module/config/itemBrowserConfig.mjs
index 2c3e1dfb..3b10409c 100644
--- a/module/config/itemBrowserConfig.mjs
+++ b/module/config/itemBrowserConfig.mjs
@@ -363,7 +363,7 @@ export const typeConfig = {
{
key: 'system.linkedClass',
label: 'Class',
- format: linkedClass => linkedClass.name
+ format: linkedClass => linkedClass?.name ?? 'DAGGERHEART.UI.ItemBrowser.missing'
},
{
key: 'system.spellcastingTrait',
@@ -375,10 +375,12 @@ export const typeConfig = {
key: 'system.linkedClass.uuid',
label: 'Class',
choices: items => {
- const list = items.map(item => ({
- value: item.system.linkedClass.uuid,
- label: item.system.linkedClass.name
- }));
+ const list = items
+ .filter(item => item.system.linkedClass)
+ .map(item => ({
+ value: item.system.linkedClass.uuid,
+ label: item.system.linkedClass.name
+ }));
return list.reduce((a, c) => {
if (!a.find(i => i.value === c.value)) a.push(c);
return a;
From 481ce46edf326da9828c5c43d3f064505c560896 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Sun, 16 Nov 2025 01:52:19 +0100
Subject: [PATCH 2/8] [Fix] Party Fixes (#1284)
* Fixed deletion of characters in the world locking up the party actor
* .
* Fixed so leader in group roll gains resourcse
* Fixed so party.inventory has the right controls
* Corrected for added character purning
* .
---
lang/en.json | 5 +-
module/applications/sheets/actors/party.mjs | 24 ++++---
module/applications/ui/chatLog.mjs | 14 +++-
module/data/actor/character.mjs | 4 +-
module/data/actor/party.mjs | 2 +-
module/systemRegistration/migrations.mjs | 2 +
.../less/sheets/actors/party/resources.less | 2 +-
system.json | 4 +-
.../sheets/actors/party/party-members.hbs | 1 +
templates/sheets/actors/party/resources.hbs | 68 ++++++++++---------
.../partials/inventory-fieldset-items-V2.hbs | 1 +
.../global/partials/inventory-item-V2.hbs | 44 ++++++------
12 files changed, 104 insertions(+), 67 deletions(-)
diff --git a/lang/en.json b/lang/en.json
index 86f0359e..eb736f5e 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -2624,7 +2624,7 @@
"cardTooHighLevel": "The card is too high level!",
"duplicateCard": "You cannot select the same card more than once.",
"duplicateCharacter": "This actor is already registered in the party members list.",
- "onlyCharactersInPartySheet": "You can drag only characters to a party sheet.",
+ "onlyCharactersInPartySheet": "You can only drag characters, companions and adverasries to the party sheet.",
"notPrimary": "The weapon is not a primary weapon!",
"notSecondary": "The weapon is not a secondary weapon!",
"itemTooHighTier": "The item must be from Tier1",
@@ -2661,7 +2661,8 @@
"subclassAlreadyLinked": "{name} is already a subclass in the class {class}. Remove it from there if you want it to be a subclass to this class.",
"gmRequired": "This action requires an online GM",
"gmOnly": "This can only be accessed by the GM",
- "noActorOwnership": "You do not have permissions for this character"
+ "noActorOwnership": "You do not have permissions for this character",
+ "documentIsMissing": "The {documentType} is missing from the world."
},
"Sidebar": {
"daggerheartMenu": {
diff --git a/module/applications/sheets/actors/party.mjs b/module/applications/sheets/actors/party.mjs
index a622dcec..8d009778 100644
--- a/module/applications/sheets/actors/party.mjs
+++ b/module/applications/sheets/actors/party.mjs
@@ -7,6 +7,7 @@ import { socketEvent } from '../../../systemRegistration/socket.mjs';
import GroupRollDialog from '../../dialogs/group-roll-dialog.mjs';
import DhpActor from '../../../documents/actor.mjs';
import DHItem from '../../../documents/item.mjs';
+import DhParty from '../../../data/actor/party.mjs';
export default class Party extends DHBaseActorSheet {
constructor(options) {
@@ -79,6 +80,9 @@ export default class Party extends DHBaseActorSheet {
}
};
+ static ALLOWED_ACTOR_TYPES = ['character', 'companion', 'adversary'];
+ static DICE_ROLL_ACTOR_TYPES = ['character'];
+
async _onRender(context, options) {
await super._onRender(context, options);
this._createFilterMenus();
@@ -277,13 +281,17 @@ export default class Party extends DHBaseActorSheet {
}
static async #tagTeamRoll() {
- new game.system.api.applications.dialogs.TagTeamDialog(this.document.system.partyMembers).render({
+ new game.system.api.applications.dialogs.TagTeamDialog(
+ this.document.system.partyMembers.filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type))
+ ).render({
force: true
});
}
- static async #groupRoll(params) {
- new GroupRollDialog(this.document.system.partyMembers).render({ force: true });
+ static async #groupRoll(_params) {
+ new GroupRollDialog(
+ this.document.system.partyMembers.filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type))
+ ).render({ force: true });
}
/**
@@ -453,17 +461,17 @@ export default class Party extends DHBaseActorSheet {
event.stopPropagation();
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
- const item = await foundry.utils.fromUuid(data.uuid);
+ const document = await foundry.utils.fromUuid(data.uuid);
- if (item instanceof DhpActor) {
+ if (document instanceof DhpActor && Party.ALLOWED_ACTOR_TYPES.includes(document.type)) {
const currentMembers = this.document.system.partyMembers.map(x => x.uuid);
if (currentMembers.includes(data.uuid)) {
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.duplicateCharacter'));
}
- await this.document.update({ 'system.partyMembers': [...currentMembers, item.uuid] });
- } else if (item instanceof DHItem) {
- this.document.createEmbeddedDocuments('Item', [item.toObject()]);
+ await this.document.update({ 'system.partyMembers': [...currentMembers, document.uuid] });
+ } else if (document instanceof DHItem) {
+ this.document.createEmbeddedDocuments('Item', [document.toObject()]);
} else {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.onlyCharactersInPartySheet'));
}
diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs
index 6b05fe74..c62affeb 100644
--- a/module/applications/ui/chatLog.mjs
+++ b/module/applications/ui/chatLog.mjs
@@ -192,9 +192,18 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
async groupRollButton(event, message) {
const path = event.currentTarget.dataset.path;
+ const isLeader = path === 'leader';
const { actor: actorData, trait } = foundry.utils.getProperty(message.system, path);
const actor = game.actors.get(actorData._id);
+ if (!actor) {
+ return ui.notifications.error(
+ game.i18n.format('DAGGERHEART.UI.Notifications.documentIsMissing', {
+ documentType: game.i18n.localize('TYPES.Actor.character')
+ })
+ );
+ }
+
if (!actor.testUserPermission(game.user, 'OWNER')) {
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.noActorOwnership'));
}
@@ -214,7 +223,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
hasRoll: true,
skips: {
createMessage: true,
- resources: true
+ resources: !isLeader
}
};
const result = await actor.diceRoll({
@@ -225,6 +234,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
})
});
+ if (!result) return;
+ await game.system.api.fields.ActionFields.CostField.execute.call({ actor }, result);
+
const newMessageData = foundry.utils.deepClone(message.system);
foundry.utils.setProperty(newMessageData, `${path}.result`, result.roll);
const renderData = { system: new game.system.api.models.chatMessages.config.groupRoll(newMessageData) };
diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs
index 1cf082f7..645a50da 100644
--- a/module/data/actor/character.mjs
+++ b/module/data/actor/character.mjs
@@ -675,6 +675,8 @@ export default class DhCharacter extends BaseDataActor {
}
_getTags() {
- return [this.class.value?.name, this.class.subclass?.name, this.community?.name, this.ancestry?.name].filter((t) => !!t);
+ return [this.class.value?.name, this.class.subclass?.name, this.community?.name, this.ancestry?.name].filter(
+ t => !!t
+ );
}
}
diff --git a/module/data/actor/party.mjs b/module/data/actor/party.mjs
index 77b17045..06731246 100644
--- a/module/data/actor/party.mjs
+++ b/module/data/actor/party.mjs
@@ -41,7 +41,7 @@ export default class DhParty extends BaseDataActor {
// Clear this party from all members that aren't deleted
for (const member of this.partyMembers) {
- member.parties?.delete(this.parent);
+ member?.parties?.delete(this.parent);
}
}
}
diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs
index e3777a94..00b07dc1 100644
--- a/module/systemRegistration/migrations.mjs
+++ b/module/systemRegistration/migrations.mjs
@@ -4,6 +4,7 @@ export async function runMigrations() {
let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion);
if (!lastMigrationVersion) lastMigrationVersion = game.system.version;
+ //#region old migrations
if (foundry.utils.isNewerVersion('1.1.0', lastMigrationVersion)) {
const lockedPacks = [];
const compendiumActors = [];
@@ -190,6 +191,7 @@ export async function runMigrations() {
lastMigrationVersion = '1.2.0';
}
+ //#endregion
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion);
}
diff --git a/styles/less/sheets/actors/party/resources.less b/styles/less/sheets/actors/party/resources.less
index fc7e0110..4db254bf 100644
--- a/styles/less/sheets/actors/party/resources.less
+++ b/styles/less/sheets/actors/party/resources.less
@@ -32,7 +32,7 @@ body.game:is(.performance-low, .noblur) {
background: light-dark(@dark-blue-40, @dark-golden-40);
border-radius: 6px;
border: 1px solid light-dark(@dark-blue, @golden);
- max-width: 230px;
+ width: 230px;
height: -webkit-fill-available;
.actor-name {
diff --git a/system.json b/system.json
index a7c79226..467e9f7d 100644
--- a/system.json
+++ b/system.json
@@ -2,10 +2,10 @@
"id": "daggerheart",
"title": "Daggerheart",
"description": "An unofficial implementation of the Daggerheart system",
- "version": "1.2.1",
+ "version": "1.2.2",
"compatibility": {
"minimum": "13",
- "verified": "13.350",
+ "verified": "13.351",
"maximum": "13"
},
"authors": [
diff --git a/templates/sheets/actors/party/party-members.hbs b/templates/sheets/actors/party/party-members.hbs
index a34959fd..b5903cfc 100644
--- a/templates/sheets/actors/party/party-members.hbs
+++ b/templates/sheets/actors/party/party-members.hbs
@@ -28,6 +28,7 @@
item=actor
type='character'
isActor=true
+ hideContextMenu=true
}}
{{/each}}
diff --git a/templates/sheets/actors/party/resources.hbs b/templates/sheets/actors/party/resources.hbs
index edb58248..74f94806 100644
--- a/templates/sheets/actors/party/resources.hbs
+++ b/templates/sheets/actors/party/resources.hbs
@@ -22,19 +22,21 @@
{{actor.name}}
-
-
- {{#times actor.system.resources.hitPoints.max}}
-
-
- {{/times}}
+ {{#unless (eq actor.type 'companion') }}
+
+
+ {{#times actor.system.resources.hitPoints.max}}
+
+
+ {{/times}}
+
+
+ {{localize "DAGGERHEART.GENERAL.HitPoints.short"}}
+ {{actor.system.resources.hitPoints.value}} / {{actor.system.resources.hitPoints.max}}
+
-
- {{localize "DAGGERHEART.GENERAL.HitPoints.short"}}
- {{actor.system.resources.hitPoints.value}} / {{actor.system.resources.hitPoints.max}}
-
-
+ {{/unless}}
@@ -71,26 +73,30 @@
{{/if}}
+ {{#unless (or (eq actor.type 'companion') (eq actor.type 'adversary')) }}
+
+
{{localize "DAGGERHEART.GENERAL.hope"}}
+ {{#times actor.system.resources.hope.max}}
+
+ {{#if (gte actor.system.resources.hope.value (add this 1))}}
+
+ {{else}}
+
+ {{/if}}
+
+ {{/times}}
+
+ {{/unless}}
-
-
{{localize "DAGGERHEART.GENERAL.hope"}}
- {{#times actor.system.resources.hope.max}}
-
- {{#if (gte actor.system.resources.hope.value (add this 1))}}
-
- {{else}}
-
- {{/if}}
-
- {{/times}}
-
-
-
{{localize "DAGGERHEART.GENERAL.DamageThresholds.minor"}}
- {{actor.system.damageThresholds.major}}
- {{localize "DAGGERHEART.GENERAL.DamageThresholds.major"}}
- {{actor.system.damageThresholds.severe}}
- {{localize "DAGGERHEART.GENERAL.DamageThresholds.severe"}}
-
+ {{#unless (eq actor.type 'companion')}}
+
+
{{localize "DAGGERHEART.GENERAL.DamageThresholds.minor"}}
+ {{actor.system.damageThresholds.major}}
+ {{localize "DAGGERHEART.GENERAL.DamageThresholds.major"}}
+ {{actor.system.damageThresholds.severe}}
+ {{localize "DAGGERHEART.GENERAL.DamageThresholds.severe"}}
+
+ {{/unless}}
{{/each}}
diff --git a/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs
index e97bfd80..d2534a5a 100644
--- a/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs
+++ b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs
@@ -10,6 +10,7 @@ Parameters:
- isGlassy {boolean} : If true, applies the 'glassy' class to the fieldset.
- cardView {boolean} : If true and type is 'domainCard', renders using domain card layout.
- isActor {boolean} : Passed through to inventory-item partials.
+- isItem {boolean} : Passed through to inventory-item partials
- actorType {boolean} : The actor type of the parent actor
- canCreate {boolean} : If true, show createDoc anchor on legend
- inVault {boolean} : If true, the domainCard is created with inVault=true
diff --git a/templates/sheets/global/partials/inventory-item-V2.hbs b/templates/sheets/global/partials/inventory-item-V2.hbs
index 37c53dcc..2d8eb560 100644
--- a/templates/sheets/global/partials/inventory-item-V2.hbs
+++ b/templates/sheets/global/partials/inventory-item-V2.hbs
@@ -4,6 +4,7 @@
Parameters:
- type {string} : The type of items in the list
- isActor {boolean} : Passed through to inventory-item partials.
+- isItem {boolean} : Passed through to inventory-item partials
- actorType {boolean} : The actor type of the parent actor
- categoryAdversary {string} : Category adversary id.
- noExtensible {boolean} : If true, the inventory-item-content would be collapsable/extendible else it always be showed
@@ -18,7 +19,7 @@ Parameters:
-
-
- {{localize this.inventory.currency.coins}}
- {{formInput systemFields.gold.fields.coins value=source.system.gold.coins enriched=source.system.gold.coins
- localize=true toggled=true}}
-
-
- {{localize this.inventory.currency.handfuls}}
- {{formInput systemFields.gold.fields.handfuls value=source.system.gold.handfuls
- enriched=source.system.gold.handfuls localize=true toggled=true}}
-
-
- {{localize this.inventory.currency.bags}}
- {{formInput systemFields.gold.fields.bags value=source.system.gold.bags enriched=source.system.gold.bags
- localize=true toggled=true}}
-
-
- {{localize this.inventory.currency.chests}}
- {{formInput systemFields.gold.fields.chests value=source.system.gold.chests
- enriched=source.system.gold.chests localize=true toggled=true}}
-
-
+ {{#each this.inventory.currencies as | currency |}}
+ {{#if currency.enabled}}
+
+ {{localize currency.label}}
+ {{formInput currency.field value=currency.value enriched=currency.value toggled=true}}
+
+ {{/if}}
+ {{/each}}
diff --git a/templates/sheets/actors/party/inventory.hbs b/templates/sheets/actors/party/inventory.hbs
index 95845197..9a299536 100644
--- a/templates/sheets/actors/party/inventory.hbs
+++ b/templates/sheets/actors/party/inventory.hbs
@@ -16,7 +16,15 @@
From 97f408c185fe83b33d335583e77b2210903647a5 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 17 Nov 2025 09:22:24 +0100
Subject: [PATCH 4/8] [Fix] Beastform Actor Wildcard Image (#1292)
* .
* .
---
module/data/activeEffect/beastformEffect.mjs | 28 ++++++++++++++---
module/data/item/beastform.mjs | 32 +++++++++-----------
module/helpers/utils.mjs | 10 ++++--
3 files changed, 47 insertions(+), 23 deletions(-)
diff --git a/module/data/activeEffect/beastformEffect.mjs b/module/data/activeEffect/beastformEffect.mjs
index b5cbdb28..769b8153 100644
--- a/module/data/activeEffect/beastformEffect.mjs
+++ b/module/data/activeEffect/beastformEffect.mjs
@@ -6,10 +6,12 @@ export default class BeastformEffect extends BaseEffect {
const fields = foundry.data.fields;
return {
characterTokenData: new fields.SchemaField({
+ usesDynamicToken: new fields.BooleanField({ initial: false }),
tokenImg: new fields.FilePathField({
categories: ['IMAGE'],
base64: false,
- nullable: true
+ nullable: true,
+ wildcard: true
}),
tokenRingImg: new fields.FilePathField({
initial: 'icons/svg/mystery-man.svg',
@@ -38,20 +40,38 @@ export default class BeastformEffect extends BaseEffect {
async _preDelete() {
if (this.parent.parent.type === 'character') {
- const update = {
+ const baseUpdate = {
height: this.characterTokenData.tokenSize.height,
- width: this.characterTokenData.tokenSize.width,
+ width: this.characterTokenData.tokenSize.width
+ };
+ const update = {
+ ...baseUpdate,
texture: {
src: this.characterTokenData.tokenImg
},
ring: {
+ enabled: this.characterTokenData.usesDynamicToken,
subject: {
texture: this.characterTokenData.tokenRingImg
}
}
};
- await updateActorTokens(this.parent.parent, update);
+ const updateToken = token => ({
+ ...baseUpdate,
+ 'texture': {
+ enabled: this.characterTokenData.usesDynamicToken,
+ src: token.flags.daggerheart.beastformTokenImg
+ },
+ 'ring': {
+ subject: {
+ texture: token.flags.daggerheart.beastformSubjectTexture
+ }
+ },
+ 'flags.daggerheart': { '-=beastformTokenImg': null, '-=beastformSubjectTexture': null }
+ });
+
+ await updateActorTokens(this.parent.parent, update, updateToken);
await this.parent.parent.deleteEmbeddedDocuments('Item', this.featureIds);
await this.parent.parent.deleteEmbeddedDocuments('ActiveEffect', this.effectIds);
diff --git a/module/data/item/beastform.mjs b/module/data/item/beastform.mjs
index 8f5dffe6..51ca298d 100644
--- a/module/data/item/beastform.mjs
+++ b/module/data/item/beastform.mjs
@@ -160,7 +160,6 @@ export default class DHBeastform extends BaseDataItem {
this.parent.effects.filter(x => x.type !== 'beastform').map(x => x.toObject())
);
- const tokenImages = await this.parent.parent.getTokenImages();
const beastformEffect = this.parent.effects.find(x => x.type === 'beastform');
await beastformEffect.updateSource({
changes: [
@@ -175,7 +174,8 @@ export default class DHBeastform extends BaseDataItem {
],
system: {
characterTokenData: {
- tokenImg: tokenImages[0],
+ usesDynamicToken: this.parent.parent.prototypeToken.ring.enabled,
+ tokenImg: this.parent.parent.prototypeToken.texture.src,
tokenRingImg: this.parent.parent.prototypeToken.ring.subject.texture,
tokenSize: {
height: this.parent.parent.prototypeToken.height,
@@ -190,7 +190,7 @@ export default class DHBeastform extends BaseDataItem {
await this.parent.parent.createEmbeddedDocuments('ActiveEffect', [beastformEffect.toObject()]);
- await updateActorTokens(this.parent.parent, {
+ const prototypeTokenUpdate = {
height: this.tokenSize.height,
width: this.tokenSize.width,
texture: {
@@ -201,22 +201,20 @@ export default class DHBeastform extends BaseDataItem {
texture: this.tokenRingImg
}
}
+ };
+
+ const tokenUpdate = token => ({
+ ...prototypeTokenUpdate,
+ flags: {
+ daggerheart: {
+ beastformTokenImg: token.texture.src,
+ beastformSubjectTexture: token.ring.subject.texture
+ }
+ }
});
+ await updateActorTokens(this.parent.parent, prototypeTokenUpdate, tokenUpdate);
+
return false;
}
-
- _onCreate(_data, _options, userId) {
- if (userId !== game.user.id) return;
-
- if (!this.parent.effects.find(x => x.type === 'beastform')) {
- this.parent.createEmbeddedDocuments('ActiveEffect', [
- {
- type: 'beastform',
- name: game.i18n.localize('DAGGERHEART.ITEMS.Beastform.beastformEffect'),
- img: 'icons/creatures/abilities/paw-print-pair-purple.webp'
- }
- ]);
- }
- }
}
diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs
index d5a402f2..f1bec45c 100644
--- a/module/helpers/utils.mjs
+++ b/module/helpers/utils.mjs
@@ -251,7 +251,13 @@ export const adjustRange = (rangeVal, decrease) => {
return range[rangeKeys[newIndex]];
};
-export const updateActorTokens = async (actor, update) => {
+/**
+ *
+ * @param {DhActor} actor - The actor for which all tokens will run a data update.
+ * @param {string} update - The data update to be applied to all tokens.
+ * @param {func} updateToken - Optional, specific data update for the non-prototype tokens as a function using the token data. Useful to handle wildcard images where each token has a different image but the prototype has a wildcard path.
+ */
+export const updateActorTokens = async (actor, update, updateToken) => {
await actor.prototypeToken.update({ ...update });
/* Update the tokens in all scenes belonging to Actor */
@@ -259,7 +265,7 @@ export const updateActorTokens = async (actor, update) => {
const tokenActor = token.baseActor ?? token.actor;
if (token.id && tokenActor?.id === actor.id) {
await token.update({
- ...update,
+ ...(updateToken ? updateToken(token) : update),
_id: token.id
});
}
From c3660ffa345c7cd0601865b81c3a3d82cb5ae21b Mon Sep 17 00:00:00 2001
From: Carlos Fernandez
Date: Mon, 17 Nov 2025 00:52:48 -0800
Subject: [PATCH 5/8] Fix party sheet tab height when expanded (#1296)
---
module/applications/sheets/actors/party.mjs | 3 ++-
styles/less/sheets/actors/party/sheet.less | 4 ++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/module/applications/sheets/actors/party.mjs b/module/applications/sheets/actors/party.mjs
index e89a2b14..9b48a797 100644
--- a/module/applications/sheets/actors/party.mjs
+++ b/module/applications/sheets/actors/party.mjs
@@ -20,7 +20,8 @@ export default class Party extends DHBaseActorSheet {
static DEFAULT_OPTIONS = {
classes: ['party'],
position: {
- width: 550
+ width: 550,
+ height: 900,
},
window: {
resizable: true
diff --git a/styles/less/sheets/actors/party/sheet.less b/styles/less/sheets/actors/party/sheet.less
index 658d9446..2d1344e8 100644
--- a/styles/less/sheets/actors/party/sheet.less
+++ b/styles/less/sheets/actors/party/sheet.less
@@ -14,11 +14,11 @@
.application.sheet.daggerheart.actor.dh-style.party {
.tab {
- height: -webkit-fill-available;
- max-height: 514px;
+ flex: 1;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
+ scrollbar-gutter: stable;
&.active {
overflow: auto;
From fe8e98ef35549f02dc26d85ebde3d39cbefc78f8 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 17 Nov 2025 09:53:03 +0100
Subject: [PATCH 6/8] Fixed so that status effects are shown on
ActiveEffectConfig again when useGeneric is not active (#1293)
---
module/applications/sheets-configs/activeEffectConfig.mjs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/module/applications/sheets-configs/activeEffectConfig.mjs b/module/applications/sheets-configs/activeEffectConfig.mjs
index 6a466c55..468aba5c 100644
--- a/module/applications/sheets-configs/activeEffectConfig.mjs
+++ b/module/applications/sheets-configs/activeEffectConfig.mjs
@@ -112,7 +112,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
CONFIG.DH.SETTINGS.gameSettings.appearance
).showGenericStatusEffects;
if (!useGeneric) {
- partContext.statuses = Object.values(CONFIG.DH.GENERAL.conditions).map(status => ({
+ partContext.statuses = Object.values(CONFIG.DH.GENERAL.conditions()).map(status => ({
value: status.id,
label: game.i18n.localize(status.name)
}));
From b9d67e44dad38487b7c9fbdd81d4e669e4fc7393 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 17 Nov 2025 10:17:22 +0100
Subject: [PATCH 7/8] [Fix] Downtime Actions (#1295)
* Fixed so downtime actiosn can be used again
* Update module/data/fields/action/targetField.mjs
Co-authored-by: Carlos Fernandez
* .
---------
Co-authored-by: Carlos Fernandez
---
module/applications/dialogs/downtime.mjs | 15 ++++++++----
module/applications/ui/chatLog.mjs | 13 +++++++++--
module/config/itemConfig.mjs | 5 ++++
module/data/action/baseAction.mjs | 3 +++
module/data/chat-message/actorRoll.mjs | 28 +++++++++++++++++++----
module/data/fields/action/damageField.mjs | 4 +++-
module/data/fields/action/targetField.mjs | 18 +++++++--------
module/documents/chatMessage.mjs | 8 ++++---
styles/less/ui/chat/damage-summary.less | 9 +++++---
styles/less/ui/chat/effect-summary.less | 9 +++++---
templates/ui/chat/damageSummary.hbs | 2 +-
templates/ui/chat/downtime.hbs | 2 +-
templates/ui/chat/effectSummary.hbs | 2 +-
13 files changed, 86 insertions(+), 32 deletions(-)
diff --git a/module/applications/dialogs/downtime.mjs b/module/applications/dialogs/downtime.mjs
index b0f51c4e..2ed2302c 100644
--- a/module/applications/dialogs/downtime.mjs
+++ b/module/applications/dialogs/downtime.mjs
@@ -178,10 +178,17 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
}
static async takeDowntime() {
- const moves = Object.values(this.moveData).flatMap(category => {
- return Object.values(category.moves)
- .filter(x => x.selected)
- .flatMap(move => [...Array(move.selected).keys()].map(_ => move));
+ const moves = Object.keys(this.moveData).flatMap(categoryKey => {
+ const category = this.moveData[categoryKey];
+ return Object.keys(category.moves)
+ .filter(x => category.moves[x].selected)
+ .flatMap(key => {
+ const move = category.moves[key];
+ return [...Array(move.selected).keys()].map(_ => ({
+ ...move,
+ movePath: `${categoryKey}.moves.${key}`
+ }));
+ });
});
const cls = getDocumentClass('ChatMessage');
diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs
index c62affeb..e2e3b6d4 100644
--- a/module/applications/ui/chatLog.mjs
+++ b/module/applications/ui/chatLog.mjs
@@ -132,12 +132,21 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
}
async actionUseButton(event, message) {
- const { moveIndex, actionIndex } = event.currentTarget.dataset;
+ const { moveIndex, actionIndex, movePath } = event.currentTarget.dataset;
const parent = await foundry.utils.fromUuid(message.system.actor);
const actionType = message.system.moves[moveIndex].actions[actionIndex];
const cls = game.system.api.models.actions.actionsTypes[actionType.type];
const action = new cls(
- { ...actionType, _id: foundry.utils.randomID(), name: game.i18n.localize(actionType.name) },
+ {
+ ...actionType,
+ _id: foundry.utils.randomID(),
+ name: game.i18n.localize(actionType.name),
+ originItem: {
+ type: CONFIG.DH.ITEM.originItemType.restMove,
+ itemPath: movePath,
+ actionIndex: actionIndex
+ }
+ },
{ parent: parent.system }
);
diff --git a/module/config/itemConfig.mjs b/module/config/itemConfig.mjs
index d815181b..544d6b2d 100644
--- a/module/config/itemConfig.mjs
+++ b/module/config/itemConfig.mjs
@@ -1547,3 +1547,8 @@ export const beastformTypes = {
label: 'DAGGERHEART.CONFIG.BeastformType.hybrid'
}
};
+
+export const originItemType = {
+ itemCollection: 'itemCollection',
+ restMove: 'restMove'
+};
diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs
index 8cadd4f8..ba401ae9 100644
--- a/module/data/action/baseAction.mjs
+++ b/module/data/action/baseAction.mjs
@@ -1,6 +1,7 @@
import DhpActor from '../../documents/actor.mjs';
import D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs';
import { ActionMixin } from '../fields/actionField.mjs';
+import { originItemField } from '../chat-message/actorRoll.mjs';
const fields = foundry.data.fields;
@@ -25,6 +26,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
description: new fields.HTMLField(),
img: new fields.FilePathField({ initial: undefined, categories: ['IMAGE'], base64: false }),
chatDisplay: new fields.BooleanField({ initial: true, label: 'DAGGERHEART.ACTIONS.Config.displayInChat' }),
+ originItem: originItemField(),
actionType: new fields.StringField({
choices: CONFIG.DH.ITEM.actionTypes,
initial: 'action',
@@ -215,6 +217,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
title: `${this.item instanceof CONFIG.Actor.documentClass ? '' : `${this.item.name}: `}${game.i18n.localize(this.name)}`,
source: {
item: this.item._id,
+ originItem: this.originItem,
action: this._id,
actor: this.actor.uuid
},
diff --git a/module/data/chat-message/actorRoll.mjs b/module/data/chat-message/actorRoll.mjs
index a2cb03f9..61262529 100644
--- a/module/data/chat-message/actorRoll.mjs
+++ b/module/data/chat-message/actorRoll.mjs
@@ -17,6 +17,16 @@ const targetsField = () =>
})
);
+export const originItemField = () =>
+ new fields.SchemaField({
+ type: new fields.StringField({
+ choices: CONFIG.DH.ITEM.originItemType,
+ initial: CONFIG.DH.ITEM.originItemType.itemCollection
+ }),
+ itemPath: new fields.StringField(),
+ actionIndex: new fields.StringField()
+ });
+
export default class DHActorRoll extends foundry.abstract.TypeDataModel {
static defineSchema() {
return {
@@ -35,6 +45,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
source: new fields.SchemaField({
actor: new fields.StringField(),
item: new fields.StringField(),
+ originItem: originItemField(),
action: new fields.StringField()
}),
damage: new fields.ObjectField(),
@@ -51,14 +62,23 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
get actionItem() {
const actionActor = this.actionActor;
if (!actionActor || !this.source.item) return null;
- return actionActor.items.get(this.source.item);
+
+ switch (this.source.originItem.type) {
+ case CONFIG.DH.ITEM.originItemType.restMove:
+ const restMoves = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves;
+ return Array.from(foundry.utils.getProperty(restMoves, `${this.source.originItem.itemPath}`).actions)[
+ this.source.originItem.actionIndex
+ ];
+ default:
+ const item = actionActor.items.get(this.source.item);
+ return item ? item.system.actionsList?.find(a => a.id === this.source.action) : null;
+ }
}
get action() {
- const actionActor = this.actionActor,
- actionItem = this.actionItem;
+ const { actionActor, actionItem: itemAction } = this;
if (!this.source.action) return null;
- if (actionItem) return actionItem.system.actionsList?.find(a => a.id === this.source.action);
+ if (itemAction) return itemAction;
else if (actionActor?.system.attack?._id === this.source.action) return actionActor.system.attack;
return null;
}
diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs
index c5fd19b9..bb81c702 100644
--- a/module/data/fields/action/damageField.mjs
+++ b/module/data/fields/action/damageField.mjs
@@ -98,7 +98,9 @@ export default class DamageField extends fields.SchemaField {
});
}
- const token = game.scenes.find(x => x.active).tokens.find(x => x.id === target.id);
+ const token = target.id
+ ? game.scenes.find(x => x.active).tokens.find(x => x.id === target.id)
+ : actor.prototypeToken;
if (config.hasHealing)
damagePromises.push(
actor.takeHealing(config.damage).then(updates => targetDamage.push({ token, updates }))
diff --git a/module/data/fields/action/targetField.mjs b/module/data/fields/action/targetField.mjs
index 439f2be1..41383fea 100644
--- a/module/data/fields/action/targetField.mjs
+++ b/module/data/fields/action/targetField.mjs
@@ -24,7 +24,7 @@ export default class TargetField extends fields.SchemaField {
if (!this.target?.type) return (config.targets = []);
config.hasTarget = true;
let targets;
- // If the Action is configured as self-targeted, set targets as the owner.
+ // If the Action is configured as self-targeted, set targets as the owner. Probably better way than to fallback to getDependentTokens
if (this.target?.type === CONFIG.DH.GENERAL.targetTypes.self.id)
targets = [this.actor.token ?? this.actor.prototypeToken];
else {
@@ -72,17 +72,17 @@ export default class TargetField extends fields.SchemaField {
/**
* Format actor to useful datas for Action roll workflow.
- * @param {*} actor Actor object to format.
+ * @param {*} token Token object to format.
* @returns {*} Formatted Actor.
*/
- static formatTarget(actor) {
+ static formatTarget(token) {
return {
- id: actor.id,
- actorId: actor.actor.uuid,
- name: actor.actor.name,
- img: actor.actor.img,
- difficulty: actor.actor.system.difficulty,
- evasion: actor.actor.system.evasion,
+ id: token.id,
+ actorId: token.actor.uuid,
+ name: token.actor.name,
+ img: token.actor.img,
+ difficulty: token.actor.system.difficulty,
+ evasion: token.actor.system.evasion,
saved: {
value: null,
success: null
diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs
index ec4c5a49..7e313891 100644
--- a/module/documents/chatMessage.mjs
+++ b/module/documents/chatMessage.mjs
@@ -145,9 +145,11 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
});
html.querySelectorAll('.token-target-container').forEach(element => {
- element.addEventListener('pointerover', this.hoverTarget);
- element.addEventListener('pointerout', this.unhoverTarget);
- element.addEventListener('click', this.clickTarget);
+ if (element.dataset.token) {
+ element.addEventListener('pointerover', this.hoverTarget);
+ element.addEventListener('pointerout', this.unhoverTarget);
+ element.addEventListener('click', this.clickTarget);
+ }
});
}
diff --git a/styles/less/ui/chat/damage-summary.less b/styles/less/ui/chat/damage-summary.less
index 02fdbadf..3fea45e5 100644
--- a/styles/less/ui/chat/damage-summary.less
+++ b/styles/less/ui/chat/damage-summary.less
@@ -28,12 +28,15 @@
display: flex;
flex-direction: column;
gap: 2px;
- cursor: pointer;
transition: all 0.3s ease;
border-radius: 6px;
- &:hover {
- background: @golden-10;
+ &.clickable {
+ cursor: pointer;
+
+ &:hover {
+ background: @golden-10;
+ }
}
header {
diff --git a/styles/less/ui/chat/effect-summary.less b/styles/less/ui/chat/effect-summary.less
index 9bea1fd9..053c0be8 100644
--- a/styles/less/ui/chat/effect-summary.less
+++ b/styles/less/ui/chat/effect-summary.less
@@ -90,11 +90,14 @@
background: transparent;
transition: all 0.3s ease;
padding: 5px;
- cursor: pointer;
transition: all 0.3s ease;
- &:hover {
- background: @golden-10;
+ &.clickable {
+ cursor: pointer;
+
+ &:hover {
+ background: @golden-10;
+ }
}
img {
diff --git a/templates/ui/chat/damageSummary.hbs b/templates/ui/chat/damageSummary.hbs
index aa8246e1..721f153f 100644
--- a/templates/ui/chat/damageSummary.hbs
+++ b/templates/ui/chat/damageSummary.hbs
@@ -1,6 +1,6 @@
{{#each targets}}
-
+
{{this.token.name}}
diff --git a/templates/ui/chat/downtime.hbs b/templates/ui/chat/downtime.hbs
index 9d7e4f8c..ef1f44c4 100644
--- a/templates/ui/chat/downtime.hbs
+++ b/templates/ui/chat/downtime.hbs
@@ -15,7 +15,7 @@
{{#each move.actions as | action index |}}
-
+
{{localize action.name}}
{{/each}}
diff --git a/templates/ui/chat/effectSummary.hbs b/templates/ui/chat/effectSummary.hbs
index 1b7badb2..a856b4d2 100644
--- a/templates/ui/chat/effectSummary.hbs
+++ b/templates/ui/chat/effectSummary.hbs
@@ -23,7 +23,7 @@
{{#each targets}}
-
+
{{this.name}}
From 5c52a33496b9aed9d3050f1efcdcd7fa071a89c2 Mon Sep 17 00:00:00 2001
From: Carlos Fernandez
Date: Mon, 17 Nov 2025 02:05:26 -0800
Subject: [PATCH 8/8] Fix styling of used action tokens for players (#1297)
---
styles/less/ui/combat-sidebar/token-actions.less | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/styles/less/ui/combat-sidebar/token-actions.less b/styles/less/ui/combat-sidebar/token-actions.less
index 41fb38ab..f3a11235 100644
--- a/styles/less/ui/combat-sidebar/token-actions.less
+++ b/styles/less/ui/combat-sidebar/token-actions.less
@@ -21,11 +21,6 @@
padding: 8px;
--button-size: 0;
- &.used {
- opacity: 0.5;
- background: transparent;
- }
-
&.inactive {
background: var(--color-warm-2);
color: var(--color-light-1);
@@ -35,6 +30,11 @@
filter: none;
}
}
+
+ &.used {
+ opacity: 0.5;
+ background: transparent;
+ }
}
}