From 0aabcec340eef7053353808721a42183eab600a6 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 19 Aug 2025 18:56:30 +0200 Subject: [PATCH 01/16] Raised version --- system.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system.json b/system.json index d954c6d5..e6b7650f 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.0.5", + "version": "1.0.6", "compatibility": { "minimum": "13", "verified": "13.347", From c48842dd2d25c0f1487c9bb123d15cb4dad95964 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Thu, 26 Feb 2026 20:04:59 +0100 Subject: [PATCH 02/16] Fixed error on deleting a sceneEnvironment item --- module/data/registeredTriggers.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/data/registeredTriggers.mjs b/module/data/registeredTriggers.mjs index ee4f3b49..ab86351c 100644 --- a/module/data/registeredTriggers.mjs +++ b/module/data/registeredTriggers.mjs @@ -75,7 +75,7 @@ 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; + if (!environment || environment.pack) continue; this.unregisterItemTriggers(environment.system.features); } } From 0d0b5125bacc5d61b4f0596490842e79e8eec3de Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Mon, 2 Mar 2026 09:37:33 +0100 Subject: [PATCH 03/16] [Fix] 1683 - Strange Patterns Explanation (#1693) * Added an explanation text to Strange Patterns trigger dialog * Update lang/en.json Co-authored-by: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> --------- Co-authored-by: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> --- lang/en.json | 1 + .../classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lang/en.json b/lang/en.json index 937de844..7b0840e4 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1294,6 +1294,7 @@ "triggerTexts": { "strangePatternsContentTitle": "Matched {nr} times.", "strangePatternsContentSubTitle": "Increase hope and stress to a total of {nr}.", + "strangePatternsActionExplanation": "Left click to increase, right click to decrease", "ferocityContent": "Spend 2 Hope to gain {bonus} bonus Evasion until after the next attack against you?", "ferocityEffectDescription": "Your evasion is increased by {bonus}. This bonus lasts until after the next attack made against you." }, diff --git a/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json b/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json index 95f42c06..953b3a2c 100644 --- a/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json +++ b/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json @@ -85,7 +85,7 @@ { "trigger": "dualityRoll", "triggeringActorType": "self", - "command": "/* Ignore if it's a TagTeam roll */\nconst tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);\nif (tagTeam.members[actor.id]) return;\n\n/* Check if there's a Strange Pattern match */\nconst dice = [roll.dFear.total, roll.dHope.total];\nconst resource = this.parent.resource?.diceStates ? Object.values(this.parent.resource.diceStates).map(x => x.value)[0] : null;\nconst nrMatches = dice.filter(x => x === resource).length;\n\nif (!nrMatches) return;\n\n/* Create a dialog to choose Hope or Stress - or to cancel*/\nconst content = `\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentTitle', { nr: nrMatches })}
\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentSubTitle', { nr: nrMatches })}
\n
\n \n \n
\n
`;\n\nconst result = await foundry.applications.api.DialogV2.input({\n classes: ['dh-style', 'two-big-buttons'],\n window: { title: this.item.name },\n content: content,\n render: (_, dialog) => {\n const hopeButton = dialog.element.querySelector('#hopeButton');\n const stressButton = dialog.element.querySelector('#stressButton');\ndialog.element.querySelector('button[type=\"submit\"]').disabled = true;\n \n const updateFunc = (event, selector, adding, clamp) => {\n const button = event.target.closest(`#${selector}Button`);\n const parent = event.target.closest('.flexrow');\n const hope = Number.parseInt(parent.querySelector('#hopeButton label').innerHTML);\n const stress = Number.parseInt(parent.querySelector('#stressButton label').innerHTML);\n const currentTotal = (Number.isNumeric(hope) ? hope : 0) + (Number.isNumeric(stress) ? stress : 0);\n if (adding && currentTotal === nrMatches) return;\n \n const current = Number.parseInt(button.querySelector('label').innerHTML);\n if (!adding && current === 0) return;\n \n const value = Number.isNumeric(current) ? adding ? current+1 : current-1 : 1;\n if (!dialog.data) dialog.data = {};\n dialog.data[selector] = clamp(value);\n button.querySelector('label').innerHTML = dialog.data[selector];\n\n event.target.closest('.dialog-form').querySelector('button[type=\"submit\"]').disabled = !adding || currentTotal < (nrMatches-1);\n \n };\n hopeButton.addEventListener('click', event => updateFunc(event, 'hope', true, x => Math.min(x, nrMatches)));\n hopeButton.addEventListener('contextmenu', event => updateFunc(event, 'hope', false, x => Math.max(x, 0)));\n stressButton.addEventListener('click', event => updateFunc(event, 'stress', true, x => Math.min(x, nrMatches)));\n stressButton.addEventListener('contextmenu', event => updateFunc(event, 'stress', false, x => Math.max(x, 0)));\n },\n ok: { callback: (_event, _result, dialog) => {\n const hope = dialog.data.hope ?? 0;\n const stress = dialog.data.stress ?? 0;\n if (!hope && !stress) return;\n\n /* Return resource update according to choices */\n const hopeUpdate = hope ? { key: 'hope', value: hope, total: -hope, enabled: true } : null;\n const stressUpdate = stress ? { key: 'stress', value: -stress, total: stress, enabled: true } : null;\n return { updates: [hopeUpdate, stressUpdate].filter(x => x) };\n }}\n});\n\nreturn result;" + "command": "/* Ignore if it's a TagTeam roll */\nconst tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);\nif (tagTeam.members[actor.id]) return;\n\n/* Check if there's a Strange Pattern match */\nconst dice = [roll.dFear.total, roll.dHope.total];\nconst resource = this.parent.resource?.diceStates ? Object.values(this.parent.resource.diceStates).map(x => x.value)[0] : null;\nconst nrMatches = dice.filter(x => x === resource).length;\n\nif (!nrMatches) return;\n\n/* Create a dialog to choose Hope or Stress - or to cancel*/\nconst content = `\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentTitle', { nr: nrMatches })}
\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentSubTitle', { nr: nrMatches })}
\n
${game.i18n.localize('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsActionExplanation')}
\n
\n \n \n
\n
`;\n\nconst result = await foundry.applications.api.DialogV2.input({\n classes: ['dh-style', 'two-big-buttons'],\n window: { title: this.item.name },\n content: content,\n render: (_, dialog) => {\n const hopeButton = dialog.element.querySelector('#hopeButton');\n const stressButton = dialog.element.querySelector('#stressButton');\ndialog.element.querySelector('button[type=\"submit\"]').disabled = true;\n \n const updateFunc = (event, selector, adding, clamp) => {\n const button = event.target.closest(`#${selector}Button`);\n const parent = event.target.closest('.flexrow');\n const hope = Number.parseInt(parent.querySelector('#hopeButton label').innerHTML);\n const stress = Number.parseInt(parent.querySelector('#stressButton label').innerHTML);\n const currentTotal = (Number.isNumeric(hope) ? hope : 0) + (Number.isNumeric(stress) ? stress : 0);\n if (adding && currentTotal === nrMatches) return;\n \n const current = Number.parseInt(button.querySelector('label').innerHTML);\n if (!adding && current === 0) return;\n \n const value = Number.isNumeric(current) ? adding ? current+1 : current-1 : 1;\n if (!dialog.data) dialog.data = {};\n dialog.data[selector] = clamp(value);\n button.querySelector('label').innerHTML = dialog.data[selector];\n\n event.target.closest('.dialog-form').querySelector('button[type=\"submit\"]').disabled = !adding || currentTotal < (nrMatches-1);\n \n };\n hopeButton.addEventListener('click', event => updateFunc(event, 'hope', true, x => Math.min(x, nrMatches)));\n hopeButton.addEventListener('contextmenu', event => updateFunc(event, 'hope', false, x => Math.max(x, 0)));\n stressButton.addEventListener('click', event => updateFunc(event, 'stress', true, x => Math.min(x, nrMatches)));\n stressButton.addEventListener('contextmenu', event => updateFunc(event, 'stress', false, x => Math.max(x, 0)));\n },\n ok: { callback: (_event, _result, dialog) => {\n const hope = dialog.data.hope ?? 0;\n const stress = dialog.data.stress ?? 0;\n if (!hope && !stress) return;\n\n /* Return resource update according to choices */\n const hopeUpdate = hope ? { key: 'hope', value: hope, total: -hope, enabled: true } : null;\n const stressUpdate = stress ? { key: 'stress', value: -stress, total: stress, enabled: true } : null;\n return { updates: [hopeUpdate, stressUpdate].filter(x => x) };\n }}\n});\n\nreturn result;" } ] } From 5459581f7fd0f33fbfe5f4ac3d84891ee086a332 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Wed, 4 Mar 2026 01:10:40 +0100 Subject: [PATCH 04/16] Fixed styling in firefox (#1692) --- styles/less/sheets/actors/actor-sheet-shared.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/styles/less/sheets/actors/actor-sheet-shared.less b/styles/less/sheets/actors/actor-sheet-shared.less index bf6393f4..23db088a 100644 --- a/styles/less/sheets/actors/actor-sheet-shared.less +++ b/styles/less/sheets/actors/actor-sheet-shared.less @@ -183,6 +183,11 @@ } } + .domain-details { + display: flex; + flex-direction: column; + } + .level-details { align-self: center; } From 986544a653c35b5a424990b69b86514ab90e123f Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Wed, 4 Mar 2026 13:16:11 +0100 Subject: [PATCH 05/16] [Fix] 1689 - Missing Feature Errors (#1690) * Fixed so that weaponfeatures and armorFeatures are tolerant of features having been removed * . --- module/config/itemConfig.mjs | 8 ++------ module/data/item/armor.mjs | 6 ++---- module/data/item/weapon.mjs | 6 ++---- module/helpers/utils.mjs | 2 +- system.json | 2 +- 5 files changed, 8 insertions(+), 16 deletions(-) diff --git a/module/config/itemConfig.mjs b/module/config/itemConfig.mjs index 7d80e597..77328987 100644 --- a/module/config/itemConfig.mjs +++ b/module/config/itemConfig.mjs @@ -467,9 +467,7 @@ export const allArmorFeatures = () => { }; export const orderedArmorFeatures = () => { - const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures - .armorFeatures; - const allFeatures = { ...armorFeatures, ...homebrewFeatures }; + const allFeatures = allArmorFeatures(); const all = Object.keys(allFeatures).map(key => { const feature = allFeatures[key]; return { @@ -1404,9 +1402,7 @@ export const allWeaponFeatures = () => { }; export const orderedWeaponFeatures = () => { - const homebrewFeatures = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).itemFeatures - .weaponFeatures; - const allFeatures = { ...weaponFeatures, ...homebrewFeatures }; + const allFeatures = allWeaponFeatures(); const all = Object.keys(allFeatures).map(key => { const feature = allFeatures[key]; return { diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 2d31c290..0958a9f3 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -23,9 +23,7 @@ export default class DHArmor extends AttachableItem { armorFeatures: new fields.ArrayField( new fields.SchemaField({ value: new fields.StringField({ - required: true, - choices: CONFIG.DH.ITEM.allArmorFeatures, - blank: true + required: true }), effectIds: new fields.ArrayField(new fields.StringField({ required: true })), actionIds: new fields.ArrayField(new fields.StringField({ required: true })) @@ -58,7 +56,7 @@ export default class DHArmor extends AttachableItem { async getDescriptionData() { const baseDescription = this.description; const allFeatures = CONFIG.DH.ITEM.allArmorFeatures(); - const features = this.armorFeatures.map(x => allFeatures[x.value]); + const features = this.armorFeatures.map(x => allFeatures[x.value]).filter(x => x); const prefix = await foundry.applications.handlebars.renderTemplate( 'systems/daggerheart/templates/sheets/items/armor/description.hbs', diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 5c6f8514..051fd42d 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -38,9 +38,7 @@ export default class DHWeapon extends AttachableItem { weaponFeatures: new fields.ArrayField( new fields.SchemaField({ value: new fields.StringField({ - required: true, - choices: CONFIG.DH.ITEM.allWeaponFeatures, - blank: true + required: true }), effectIds: new fields.ArrayField(new fields.StringField({ required: true })), actionIds: new fields.ArrayField(new fields.StringField({ required: true })) @@ -121,7 +119,7 @@ export default class DHWeapon extends AttachableItem { const burden = game.i18n.localize(CONFIG.DH.GENERAL.burden[this.burden].label); const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures(); - const features = this.weaponFeatures.map(x => allFeatures[x.value]); + const features = this.weaponFeatures.map(x => allFeatures[x.value]).filter(x => x); const prefix = await foundry.applications.handlebars.renderTemplate( 'systems/daggerheart/templates/sheets/items/weapon/description.hbs', diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 4ecc7809..57badd89 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -119,8 +119,8 @@ export const tagifyElement = (element, baseOptions, onChange, tagifyOptions = {} }), maxTags: typeof maxTags === 'function' ? maxTags() : maxTags, dropdown: { + searchKeys: ['value', 'name'], mapValueTo: 'name', - searchKeys: ['value'], enabled: 0, maxItems: 100, closeOnSelect: true, diff --git a/system.json b/system.json index fb23ad7b..40de5fa1 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.7.2", + "version": "1.7.3", "compatibility": { "minimum": "13.346", "verified": "13.351", From 3267f3f53143ef4a0e7454fdae610d21319db774 Mon Sep 17 00:00:00 2001 From: Psitacus <59754077+Psitacus@users.noreply.github.com> Date: Thu, 5 Mar 2026 01:56:35 -0700 Subject: [PATCH 06/16] fix instances of rolls being called checks (#1702) Co-authored-by: Psitacus --- lang/en.json | 8 ++++---- .../domains/domainCard_Wrangle_9DwSxHoUwl8Kxj3n.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lang/en.json b/lang/en.json index 7b0840e4..6c52beeb 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1166,12 +1166,12 @@ }, "far": { "name": "Far", - "description": "means a distance where one can see the appearance of a person or object, but probably not in great detail-- across a small battlefield or down a large corridor. This is usually about 30-100 feet away. While under danger, a PC will likely have to make an Agility check to get here safely. Anything on a battle map that is within the length of a standard piece of paper (~10-11 inches) can usually be considered far.", + "description": "means a distance where one can see the appearance of a person or object, but probably not in great detail-- across a small battlefield or down a large corridor. This is usually about 30-100 feet away. While under danger, a PC will likely have to make an Agility roll to get here safely. Anything on a battle map that is within the length of a standard piece of paper (~10-11 inches) can usually be considered far.", "short": "Far" }, "veryFar": { "name": "Very Far", - "description": "means a distance where you can see the shape of a person or object, but probably not make outany details-- across a large battlefield or down a long street, generally about 100-300 feet away. While under danger, a PC likely has to make an Agility check to get here safely. Anything on a battle map that is beyond far distance, but still within sight of the characters can usually be considered very far.", + "description": "means a distance where you can see the shape of a person or object, but probably not make outany details-- across a large battlefield or down a long street, generally about 100-300 feet away. While under danger, a PC likely has to make an Agility roll to get here safely. Anything on a battle map that is beyond far distance, but still within sight of the characters can usually be considered very far.", "short": "V. Far" } }, @@ -2806,7 +2806,7 @@ "title": "Domain Card" }, "dualityRoll": { - "abilityCheckTitle": "{ability} Check" + "abilityCheckTitle": "{ability} Roll" }, "effectSummary": { "title": "Effects Applied", @@ -2821,7 +2821,7 @@ "selectLeader": "Select a Leader", "selectMember": "Select a Member", "rerollTitle": "Reroll Group Roll", - "rerollContent": "Are you sure you want to reroll your {trait} check?", + "rerollContent": "Are you sure you want to reroll your {trait} roll?", "rerollTooltip": "Reroll", "wholePartySelected": "The whole party is selected" }, diff --git a/src/packs/domains/domainCard_Wrangle_9DwSxHoUwl8Kxj3n.json b/src/packs/domains/domainCard_Wrangle_9DwSxHoUwl8Kxj3n.json index 8cdb62b0..16753e1e 100644 --- a/src/packs/domains/domainCard_Wrangle_9DwSxHoUwl8Kxj3n.json +++ b/src/packs/domains/domainCard_Wrangle_9DwSxHoUwl8Kxj3n.json @@ -53,7 +53,7 @@ "difficulty": null, "damageMod": "none" }, - "name": "Agility Check", + "name": "Agility Roll", "img": "icons/skills/melee/sword-engraved-glow-purple.webp", "range": "close" } From 1212bd01f8eb45b3f2911b3536c72e438c6900a8 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Thu, 5 Mar 2026 15:30:31 -0500 Subject: [PATCH 07/16] Increase the click area of sidebar and inventory control buttons (#1703) --- styles/less/global/inventory-item.less | 7 ++++--- templates/sheets/global/partials/inventory-item-V2.hbs | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index 9045baf5..b6c09dbf 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -103,10 +103,9 @@ display: flex; align-items: center; justify-content: end; - gap: 8px; a { - width: 15px; + width: 20px; text-align: center; } @@ -275,8 +274,10 @@ grid-area: controls; align-self: start; padding-top: 0.3125rem; - gap: 4px; margin-bottom: -1px; + a { + width: 18px; + } } > .item-labels { align-self: start; diff --git a/templates/sheets/global/partials/inventory-item-V2.hbs b/templates/sheets/global/partials/inventory-item-V2.hbs index fec215a0..e496ce4b 100644 --- a/templates/sheets/global/partials/inventory-item-V2.hbs +++ b/templates/sheets/global/partials/inventory-item-V2.hbs @@ -109,7 +109,7 @@ Parameters: {{else if (eq type 'armor')}} - + {{/if}} {{#if (eq type 'domainCard')}} @@ -125,7 +125,7 @@ Parameters: {{/if}} {{#if (hasProperty item "toChat")}} - + {{/if}} {{else}} @@ -138,7 +138,7 @@ Parameters: {{/unless}} {{#unless hideContextMenu}} - + {{/unless}} {{/if}} From 0675e1f0199b2a195d77dcf9d41ff45f98b38ab7 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Thu, 5 Mar 2026 15:31:49 -0500 Subject: [PATCH 08/16] Add recall cost to domain cards in grid view (#1700) --- styles/less/global/inventory-item.less | 21 +++++++++++++++++++ .../global/partials/domain-card-item.hbs | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index b6c09dbf..d703d189 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -335,6 +335,27 @@ border-radius: 6px; } + .recall-cost { + position: absolute; + right: 4px; + top: 4px; + width: 1.75em; + height: 1.75em; + + align-items: center; + background: @dark-blue; + border-radius: 50%; + border: 1px solid @golden; + color: @golden; + display: flex; + justify-content: center; + padding-top: 0.1em; // compensate for font + + i { + font-size: 0.68em; + } + } + .card-label { display: flex; flex-direction: column; diff --git a/templates/sheets/global/partials/domain-card-item.hbs b/templates/sheets/global/partials/domain-card-item.hbs index ae95b7af..54e44e64 100644 --- a/templates/sheets/global/partials/domain-card-item.hbs +++ b/templates/sheets/global/partials/domain-card-item.hbs @@ -1,5 +1,9 @@
  • + + {{item.system.recallCost}} + +
    + {{formGroup settingFields.schema.fields.vulnerableAutomation value=settingFields._source.vulnerableAutomation localize=true}} {{formGroup settingFields.schema.fields.countdownAutomation value=settingFields._source.countdownAutomation localize=true}} {{formGroup settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints localize=true}} {{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}} From f1f5102af13cf8c69858ba6f9a8980decc587190 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 7 Mar 2026 01:33:45 +0100 Subject: [PATCH 16/16] Raised version --- system.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system.json b/system.json index 40de5fa1..fc5e1615 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.7.3", + "version": "1.8.0", "compatibility": { "minimum": "13.346", "verified": "13.351",