From 66c90d69e3aad53a2bb960780261e02ef43723d8 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 12 Apr 2026 11:10:02 +0200 Subject: [PATCH 01/25] Added saefety to updateActorsRangeDepenedentEffects --- daggerheart.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daggerheart.mjs b/daggerheart.mjs index 43aafce4..598c1a78 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -360,7 +360,7 @@ const updateActorsRangeDependentEffects = async token => { CONFIG.DH.SETTINGS.gameSettings.variantRules ).rangeMeasurement; - for (let effect of token.actor?.allApplicableEffects() ?? []) { + for (let effect of token.actor?.allApplicableEffects?.() ?? []) { if (!effect.system.rangeDependence?.enabled) continue; const { target, range, type } = effect.system.rangeDependence; From e003db3ec137fcc08f7e6ac4cfa0e87e2e183676 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 12 Apr 2026 11:22:00 +0200 Subject: [PATCH 02/25] Corrected updateActorsRangeDependentEffects when token is null --- daggerheart.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/daggerheart.mjs b/daggerheart.mjs index 598c1a78..064b1670 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -355,12 +355,14 @@ Hooks.on(CONFIG.DH.HOOKS.hooksConfig.groupRollStart, async data => { }); const updateActorsRangeDependentEffects = async token => { + if (!token) return; + const rangeMeasurement = game.settings.get( CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules ).rangeMeasurement; - for (let effect of token.actor?.allApplicableEffects?.() ?? []) { + for (let effect of token.actor?.allApplicableEffects() ?? []) { if (!effect.system.rangeDependence?.enabled) continue; const { target, range, type } = effect.system.rangeDependence; From 56a6613a73d3edce24a8a52ed217be0788cc3606 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 12 Apr 2026 11:38:15 +0200 Subject: [PATCH 03/25] Fixed more missing translations (#1792) --- lang/en.json | 20 ++++++++++++----- module/applications/ui/itemBrowser.mjs | 2 +- module/config/itemBrowserConfig.mjs | 22 +++++++++++-------- .../footer.hbs | 2 +- templates/sheets/items/weapon/header.hbs | 4 ++-- templates/ui/tooltip/battlepoints.hbs | 6 ++--- templates/ui/tooltip/weapon.hbs | 2 +- 7 files changed, 36 insertions(+), 22 deletions(-) diff --git a/lang/en.json b/lang/en.json index a2c3dc79..45a3b414 100755 --- a/lang/en.json +++ b/lang/en.json @@ -74,9 +74,7 @@ "name": "Summon", "tooltip": "Create tokens in the scene.", "error": "You do not have permission to summon tokens or there is no active scene.", - "invalidDrop": "You can only drop Actor entities to summon.", - "chatMessageTitle": "Test2", - "chatMessageHeaderTitle": "Summoning" + "invalidDrop": "You can only drop Actor entities to summon." }, "transform": { "name": "Transform", @@ -2016,6 +2014,10 @@ "hint": "Multiply any damage dealt to you by this number" } }, + "Battlepoints": { + "full": "Battlepoints", + "short": "BP" + }, "Bonuses": { "rest": { "downtimeAction": "Downtime Action", @@ -2457,6 +2459,7 @@ "rollDamage": "Roll Damage", "rollWith": "{roll} Roll", "save": "Save", + "saveSettings": "Save Settings", "scalable": "Scalable", "scars": "Scars", "situationalBonus": "Situational Bonus", @@ -2611,8 +2614,14 @@ }, "Weapon": { "weaponType": "Weapon Type", - "primaryWeapon": "Primary Weapon", - "secondaryWeapon": "Secondary Weapon" + "primaryWeapon": { + "full": "Primary Weapon", + "short": "Primary" + }, + "secondaryWeapon": { + "full": "Secondary Weapon", + "short": "Secondary" + } } }, "MACROS": { @@ -3067,6 +3076,7 @@ }, "ItemBrowser": { "title": "Daggerheart Compendium Browser", + "windowTitle": "Compendium Browser", "hint": "Select a Folder in sidebar to start browsing through the compendium", "browserSettings": "Browser Settings", "columnName": "Name", diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index 9ca328a0..22de38ab 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -37,7 +37,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { tag: 'div', window: { frame: true, - title: 'Compendium Browser', + title: 'DAGGERHEART.UI.ItemBrowser.windowTitle', icon: 'fa-solid fa-book-atlas', positioned: true, resizable: true diff --git a/module/config/itemBrowserConfig.mjs b/module/config/itemBrowserConfig.mjs index 0a4154a8..f20e56d0 100644 --- a/module/config/itemBrowserConfig.mjs +++ b/module/config/itemBrowserConfig.mjs @@ -74,12 +74,13 @@ export const typeConfig = { columns: [ { key: 'type', - label: 'DAGGERHEART.GENERAL.type' + label: 'DAGGERHEART.GENERAL.type', + format: type => type ? `TYPES.Item.${type}` : '-' }, { key: 'system.secondary', label: 'DAGGERHEART.UI.ItemBrowser.subtype', - format: isSecondary => (isSecondary ? 'secondary' : isSecondary === false ? 'primary' : '-') + format: isSecondary => (isSecondary ? 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon.short' : isSecondary === false ? 'DAGGERHEART.ITEMS.Weapon.primaryWeapon.short' : '-') }, { key: 'system.tier', @@ -99,8 +100,8 @@ export const typeConfig = { key: 'system.secondary', label: 'DAGGERHEART.UI.ItemBrowser.subtype', choices: [ - { value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon' }, - { value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon' } + { value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon.full' }, + { value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full' } ] }, { @@ -258,11 +259,13 @@ export const typeConfig = { columns: [ { key: 'system.type', - label: 'DAGGERHEART.GENERAL.type' + label: 'DAGGERHEART.GENERAL.type', + format: type => type ? `DAGGERHEART.CONFIG.DomainCardTypes.${type}` : '-' }, { key: 'system.domain', - label: 'DAGGERHEART.GENERAL.Domain.single' + label: 'DAGGERHEART.GENERAL.Domain.single', + format: domain => domain ? CONFIG.DH.DOMAIN.allDomains()[domain].label : '-' }, { key: 'system.level', @@ -374,7 +377,7 @@ export const typeConfig = { columns: [ { key: 'system.linkedClass', - label: 'Class', + label: 'TYPES.Item.class', format: linkedClass => linkedClass?.name ?? 'DAGGERHEART.UI.ItemBrowser.missing' }, { @@ -386,7 +389,7 @@ export const typeConfig = { filters: [ { key: 'system.linkedClass.uuid', - label: 'Class', + label: 'TYPES.Item.class', choices: items => { const list = items .filter(item => item.system.linkedClass) @@ -410,7 +413,8 @@ export const typeConfig = { }, { key: 'system.mainTrait', - label: 'DAGGERHEART.GENERAL.Trait.single' + label: 'DAGGERHEART.GENERAL.Trait.single', + format: trait => (trait ? `DAGGERHEART.CONFIG.Traits.${trait}.name` : '-') } ], filters: [ diff --git a/templates/dialogs/compendiumBrowserSettingsDialog/footer.hbs b/templates/dialogs/compendiumBrowserSettingsDialog/footer.hbs index 9dc61cbe..d9bb378e 100644 --- a/templates/dialogs/compendiumBrowserSettingsDialog/footer.hbs +++ b/templates/dialogs/compendiumBrowserSettingsDialog/footer.hbs @@ -1,3 +1,3 @@
- +
\ No newline at end of file diff --git a/templates/sheets/items/weapon/header.hbs b/templates/sheets/items/weapon/header.hbs index 9bbd9511..fabbb07f 100644 --- a/templates/sheets/items/weapon/header.hbs +++ b/templates/sheets/items/weapon/header.hbs @@ -5,9 +5,9 @@

{{#if source.system.secondary}} -

{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon"}}

+

{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full"}}

{{else}} -

{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon"}}

+

{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon.full"}}

{{/if}}

{{localize (concat 'DAGGERHEART.CONFIG.Traits.' source.system.attack.roll.trait '.short')}} diff --git a/templates/ui/tooltip/battlepoints.hbs b/templates/ui/tooltip/battlepoints.hbs index 9672698a..f2f42f53 100644 --- a/templates/ui/tooltip/battlepoints.hbs +++ b/templates/ui/tooltip/battlepoints.hbs @@ -7,9 +7,9 @@ {{#each category as |grouping index|}}
{{#if grouping.nr}} - + {{else}} - + {{/if}}
{{/each}} @@ -26,7 +26,7 @@ {{else}} {{/if}} - +

{{/each}} diff --git a/templates/ui/tooltip/weapon.hbs b/templates/ui/tooltip/weapon.hbs index a672c883..4adb9c46 100644 --- a/templates/ui/tooltip/weapon.hbs +++ b/templates/ui/tooltip/weapon.hbs @@ -3,7 +3,7 @@

{{item.name}}

- {{#if item.system.secondary}}{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon"}}{{else}}{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon"}}{{/if}} + {{#if item.system.secondary}}{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full"}}{{else}}{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon.full"}}{{/if}}
{{#with (lookup config.GENERAL.burden item.system.burden) as | burden |}} From f9000115102284082263a3a49573bfc7e309d4a9 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 12 Apr 2026 13:58:43 +0200 Subject: [PATCH 04/25] Fixed trait counting in CharacterCreation. Fixed description layout and labels in CharacterCreation. Fixed drag/drop images from Compendium Browser --- lang/en.json | 1 + .../characterCreation/characterCreation.mjs | 9 +++++++-- module/applications/ui/itemBrowser.mjs | 2 ++ .../selections-container.less | 14 +++++++++++++ .../characterCreation/tabs/experience.hbs | 20 +++++++++++++------ 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/lang/en.json b/lang/en.json index a2c3dc79..66df26b3 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2431,6 +2431,7 @@ "next": "Next", "none": "None", "noTarget": "No current target", + "optionalThing": "Optional {thing}", "partner": "Partner", "player": { "single": "Player", diff --git a/module/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index e6c0f299..936bb79d 100644 --- a/module/applications/characterCreation/characterCreation.mjs +++ b/module/applications/characterCreation/characterCreation.mjs @@ -11,7 +11,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl this.character = character; this.setup = { - traits: this.character.system.traits, + traits: Object.keys(this.character.system.traits).reduce((acc, key) => { + acc[key] = { value: null }; + return acc; + }, {}), ancestryName: { primary: '', secondary: '' @@ -377,8 +380,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl ]; return Object.values(this.setup.traits).reduce((acc, x) => { const index = traitCompareArray.indexOf(x.value); + if (index === -1) return acc; + traitCompareArray.splice(index, 1); - acc += index !== -1; + acc += 1; return acc; }, 0); } diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index 9ca328a0..4c64c39e 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -583,7 +583,9 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { const { itemUuid } = event.target.closest('[data-item-uuid]').dataset, item = await foundry.utils.fromUuid(itemUuid), dragData = item.toDragData(); + event.dataTransfer.setData('text/plain', JSON.stringify(dragData)); + event.dataTransfer.setDragImage(event.target.querySelector('img'), 0, 0); } _canDragStart() { diff --git a/styles/less/dialog/character-creation/selections-container.less b/styles/less/dialog/character-creation/selections-container.less index f9569fca..a68cd36c 100644 --- a/styles/less/dialog/character-creation/selections-container.less +++ b/styles/less/dialog/character-creation/selections-container.less @@ -175,6 +175,11 @@ opacity: 0.2; } + &.no-horizontal-padding { + padding-left: 0; + padding-right: 0; + } + legend { margin-left: auto; margin-right: auto; @@ -278,6 +283,15 @@ flex-direction: column; gap: 5px; + &.separated { + border-bottom: 2px solid; + padding-bottom: 8px; + } + + .form-group { + padding: 0 0.75rem; + } + .experience-inner-container { position: relative; display: flex; diff --git a/templates/characterCreation/tabs/experience.hbs b/templates/characterCreation/tabs/experience.hbs index 3eb92834..66363084 100644 --- a/templates/characterCreation/tabs/experience.hbs +++ b/templates/characterCreation/tabs/experience.hbs @@ -4,17 +4,25 @@ data-group='{{tabs.experience.group}}' >
-
+
{{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.initialExperiences"}} {{experience.nrSelected}}/{{experience.nrTotal}}
{{#each experience.values as |experience id|}} -
-
- - {{numberFormat this.value sign=true}} +
+
+ +
+ + {{numberFormat this.value sign=true}} +
- +
+ +
+ +
+
{{/each}}
From fb07938e54aefaa9528e7d5d4b27e5e955e4f90e Mon Sep 17 00:00:00 2001 From: Nikhil Nagarajan Date: Sun, 12 Apr 2026 13:10:51 -0400 Subject: [PATCH 05/25] container fix (#1795) --- .../less/dialog/character-creation/selections-container.less | 5 +++++ styles/less/sheets-settings/character-settings/sheet.less | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/styles/less/dialog/character-creation/selections-container.less b/styles/less/dialog/character-creation/selections-container.less index a68cd36c..2bbac484 100644 --- a/styles/less/dialog/character-creation/selections-container.less +++ b/styles/less/dialog/character-creation/selections-container.less @@ -255,6 +255,11 @@ height: 65px; background: url(../assets/svg/trait-shield.svg) no-repeat; background-size: 100%; + padding-top: 3px; + display: flex; + flex-direction: column; + align-items: center; + row-gap: 3px; div { filter: drop-shadow(0 0 3px black); diff --git a/styles/less/sheets-settings/character-settings/sheet.less b/styles/less/sheets-settings/character-settings/sheet.less index f7f16df4..eab29436 100644 --- a/styles/less/sheets-settings/character-settings/sheet.less +++ b/styles/less/sheets-settings/character-settings/sheet.less @@ -27,10 +27,11 @@ height: 65px; background: url(../assets/svg/trait-shield.svg) no-repeat; background-size: 100%; - padding-top: 4px; + padding-top: 3px; display: flex; flex-direction: column; align-items: center; + row-gap: 3px; span { font-size: var(--font-size-10); From 8d8dea81fe100220433252928254250673a09cf8 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Mon, 13 Apr 2026 20:41:28 +0200 Subject: [PATCH 06/25] [Fix] Compendium Advantage Sources (#1796) * Duration Description field didn't show initially for type temporary * Corrected SRD --- .../feature_Retract_UFR67BUOhNGLFyg9.json | 54 +++++++++++------- ...ure_Low_Light_Living_aMla3xQuCHEwORGD.json | 55 +++++++++---------- ...omainCard_Battle_Cry_Ef1JsUG50LIoKx2F.json | 44 +++++++++------ templates/sheets/activeEffect/settings.hbs | 8 +-- 4 files changed, 92 insertions(+), 69 deletions(-) diff --git a/src/packs/ancestries/feature_Retract_UFR67BUOhNGLFyg9.json b/src/packs/ancestries/feature_Retract_UFR67BUOhNGLFyg9.json index b17cd7da..eb9696b2 100644 --- a/src/packs/ancestries/feature_Retract_UFR67BUOhNGLFyg9.json +++ b/src/packs/ancestries/feature_Retract_UFR67BUOhNGLFyg9.json @@ -68,31 +68,33 @@ "type": "withinRange", "target": "hostile", "range": "melee" + }, + "changes": [ + { + "key": "system.resistance.physical.resistance", + "type": "override", + "value": 1, + "priority": null, + "phase": "initial" + }, + { + "key": "system.disadvantageSources", + "type": "add", + "value": "Action rolls", + "priority": null, + "phase": "initial" + } + ], + "duration": { + "type": "" } }, - "changes": [ - { - "key": "system.resistance.physical.resistance", - "mode": 5, - "value": "1", - "priority": null - }, - { - "key": "system.disadvantageSources", - "mode": 2, - "value": "Retract", - "priority": null - } - ], "disabled": true, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, "description": "

While in your shell, you have resistance to physical damage, you have disadvantage on action rolls, and you canโ€™t move.

", "tint": "#ffffff", @@ -102,6 +104,16 @@ "_stats": { "compendiumSource": null }, + "start": { + "time": 0, + "combat": null, + "combatant": null, + "initiative": null, + "round": null, + "turn": null + }, + "showIcon": 1, + "folder": null, "_key": "!items.effects!UFR67BUOhNGLFyg9.3V4FPoyjJUnFP9WS" } ], diff --git a/src/packs/communities/feature_Low_Light_Living_aMla3xQuCHEwORGD.json b/src/packs/communities/feature_Low_Light_Living_aMla3xQuCHEwORGD.json index f1ed3ace..c95d9132 100644 --- a/src/packs/communities/feature_Low_Light_Living_aMla3xQuCHEwORGD.json +++ b/src/packs/communities/feature_Low_Light_Living_aMla3xQuCHEwORGD.json @@ -29,39 +29,28 @@ "type": "withinRange", "target": "hostile", "range": "melee" + }, + "changes": [ + { + "key": "system.advantageSources", + "type": "add", + "value": "Rolls to hide, investigate, or perceive details in low light", + "priority": null, + "phase": "initial" + } + ], + "duration": { + "type": "" } }, - "changes": [ - { - "key": "system.advantageSources", - "mode": 2, - "value": "In an area with low light or heavy shadow: hide, investigate, or perceive", - "priority": null - }, - { - "key": "system.advantageSources", - "mode": 2, - "value": "", - "priority": null - }, - { - "key": "", - "mode": 2, - "value": "", - "priority": null - } - ], "disabled": false, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, - "description": "", + "description": "

When youโ€™re in an area with low light or heavy shadow, you have advantage on rolls to hide, investigate, or perceive details within that area.

", "origin": null, "tint": "#ffffff", "transfer": true, @@ -71,6 +60,16 @@ "_stats": { "compendiumSource": null }, + "start": { + "time": 0, + "combat": null, + "combatant": null, + "initiative": null, + "round": null, + "turn": null + }, + "showIcon": 1, + "folder": null, "_key": "!items.effects!aMla3xQuCHEwORGD.pCp32u7UwqxCI4WW" } ], diff --git a/src/packs/domains/domainCard_Battle_Cry_Ef1JsUG50LIoKx2F.json b/src/packs/domains/domainCard_Battle_Cry_Ef1JsUG50LIoKx2F.json index c9ae6071..1fde286d 100644 --- a/src/packs/domains/domainCard_Battle_Cry_Ef1JsUG50LIoKx2F.json +++ b/src/packs/domains/domainCard_Battle_Cry_Ef1JsUG50LIoKx2F.json @@ -132,27 +132,29 @@ "type": "withinRange", "target": "hostile", "range": "melee" + }, + "changes": [ + { + "key": "system.advantageSources", + "type": "add", + "value": "On Attacks", + "priority": null, + "phase": "initial" + } + ], + "duration": { + "type": "temporary", + "description": "

Until you or an ally rolls a failure with Fear.

" } }, - "changes": [ - { - "key": "system.advantageSources", - "mode": 2, - "value": "1", - "priority": null - } - ], "disabled": false, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, - "description": "", + "description": "

You gain advantage on attack rolls until you or an ally rolls a failure with Fear.

", "tint": "#ffffff", "statuses": [], "sort": 0, @@ -160,6 +162,16 @@ "_stats": { "compendiumSource": null }, + "start": { + "time": 0, + "combat": null, + "combatant": null, + "initiative": null, + "round": null, + "turn": null + }, + "showIcon": 1, + "folder": null, "_key": "!items.effects!Ef1JsUG50LIoKx2F.s7ma4TNgAvt0ZgEW" } ], diff --git a/templates/sheets/activeEffect/settings.hbs b/templates/sheets/activeEffect/settings.hbs index 9307ff65..09b78856 100644 --- a/templates/sheets/activeEffect/settings.hbs +++ b/templates/sheets/activeEffect/settings.hbs @@ -26,11 +26,11 @@ {{formGroup systemFields.duration.fields.type value=source.system.duration.type localize=true }} -
-
- {{formInput systemFields.duration.fields.description value=source.system.duration.description localize=true }} -
+
+
+ {{formInput systemFields.duration.fields.description value=source.system.duration.description localize=true }}
+
{{formGroup fields.start.fields.time value=source.start.time localize=true }} From a62d28cd96fdfdcdc74e3c8f9e6528edbf9840f9 Mon Sep 17 00:00:00 2001 From: CPTN_Cosmo Date: Tue, 14 Apr 2026 18:51:28 +0200 Subject: [PATCH 07/25] updated contributing guidelines (#1800) --- CONTRIBUTING.md | 79 ++++--------------------------------------------- 1 file changed, 5 insertions(+), 74 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b9099005..261c26bd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,78 +1,9 @@ -# Contributing to Foundryborne +# Contributing to Daggerheart -Welcome! This is a community-driven project to bring [Daggerheart](https://www.daggerheart.com/) to [FoundryVTT](https://foundryvtt.com/) as a full system. We're excited to have you here and appreciate your interest in contributing. +Thank you for your interest in contributing to the Foundryborne project! ---- +To ensure that all contributions align with our project goals and architectural standards, we ask that you **do not submit outside contributions without first receiving feedback from the development team.** -## ๐Ÿค How to Contribute +If you have an idea or a fix you'd like to contribute, please start a discussion or open an issue first. We'd love to hear from you and collaborate on the best way to move forward! -We welcome contributions of all kinds: - -- Bug reports -- Feature suggestions -- Code contributions -- UI/UX mockups -- Documentation improvements -- Questions and discussions - -Please be respectful and collaborative โ€” weโ€™re all here to build something great together. - -### Community Translations - -Please note that we are not accepting community translations in the main project. Instead, community translations should be published as a module. - ---- - -## ๐Ÿงญ General Guidelines - -- **Use GitHub Issues** to report bugs or propose features -- **Start a Discussion** for larger ideas or questions -- **Open a Pull Request** once you've confirmed your work aligns with project direction -- **Keep things modular and maintainable** โ€” if you're not sure how to structure something, ask! -- **Orient your code on existing examples**, and feel free to suggest a standard if it makes things clearer - ---- - -## ๐Ÿ—‚๏ธ Project Structure - -Please try to follow the general logic of the existing code when submitting PRs. - -We encourage contributors to leave comments or open Discussions when proposing structural or organizational changes. - ---- - -## ๐Ÿงพ Issue & PR Best Practices - -**For Issues:** - -- Use clear, descriptive titles -- Provide a concise explanation of the problem or idea -- Include reproduction steps or example scenarios if it's a bug -- Add screenshots or logs if helpful - -**For Pull Requests:** - -- Use a clear title summarizing the change -- Provide a brief description of what your code does and why -- Link to any related Issues -- Keep PRs focused โ€” smaller is better - ---- - -## ๐Ÿ”– Labels and Boards - -We use GitHub labels to help organize contributions. If your issue or PR relates to a specific category, feel free to tag it. There is also a GitHub Project Board to help track active work and priorities. - ---- - -## ๐Ÿ“ฃ Communication - -Discussions are currently happening on GitHub โ€” in Issues, PRs, and [GitHub Discussions](https://github.com/Foundryborne/daggerheart/discussions). You're welcome to use any of these, though we may consolidate to one in the future. - ---- - -## ๐Ÿค— Thank You! - -Whether you're fixing a typo or designing entire mechanics โ€” every contribution matters. Thank you for helping bring _Daggerheart_ to life in FoundryVTT through **Foundryborne**! - -๐Ÿธ๐Ÿ› ๏ธ +Thank you for your understanding and support. From 1176328f625c66a86fe5e6e497d1f01062d4ecef Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 14 Apr 2026 20:55:10 +0200 Subject: [PATCH 08/25] Updated deploy.yml --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e245c7fa..553a1a17 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -35,7 +35,7 @@ jobs: env: version: ${{steps.get_version.outputs.version-without-v}} url: https://github.com/${{github.repository}} - manifest: https://raw.githubusercontent.com/${{github.repository}}/main/system.json + manifest: https://raw.githubusercontent.com/${{github.repository}}/v14/system.json download: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/system.zip # Create a zip file with all files required by the module to add to the release From a77d2088a02a4ead25582b05ce74decee863defc Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Wed, 15 Apr 2026 12:42:30 -0400 Subject: [PATCH 09/25] Increase reuse of gold and inventory styling (#1804) --- module/data/actor/character.mjs | 9 +-- module/data/actor/party.mjs | 8 +-- module/data/fields/actorField.mjs | 13 ++++- module/systemRegistration/handlebars.mjs | 1 + .../sheets/actors/actor-sheet-shared.less | 53 ++++++++++++++++++ .../sheets/actors/character/inventory.less | 52 ----------------- .../less/sheets/actors/party/inventory.less | 56 ------------------- .../sheets/actors/character/inventory.hbs | 13 +---- templates/sheets/actors/party/inventory.hbs | 13 +---- templates/sheets/global/partials/gold.hbs | 12 ++++ 10 files changed, 84 insertions(+), 146 deletions(-) create mode 100644 templates/sheets/global/partials/gold.hbs diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 61338344..0e1e2259 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -3,7 +3,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs'; import DhLevelData from '../levelData.mjs'; import { commonActorRules } from './base.mjs'; import DhCreature from './creature.mjs'; -import { attributeField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs'; +import { attributeField, stressDamageReductionRule, bonusField, GoldField } from '../fields/actorField.mjs'; import { ActionField } from '../fields/actionField.mjs'; import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs'; import { getArmorSources } from '../../helpers/utils.mjs'; @@ -62,12 +62,7 @@ export default class DhCharacter extends DhCreature { core: new fields.BooleanField({ initial: false }) }) ), - gold: new fields.SchemaField({ - coins: new fields.NumberField({ initial: 0, integer: true }), - handfuls: new fields.NumberField({ initial: 1, integer: true }), - bags: new fields.NumberField({ initial: 0, integer: true }), - chests: new fields.NumberField({ initial: 0, integer: true }) - }), + gold: new GoldField(), scars: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.scars' }), biography: new fields.SchemaField({ background: new fields.HTMLField(), diff --git a/module/data/actor/party.mjs b/module/data/actor/party.mjs index ec1beb99..c9b99dcd 100644 --- a/module/data/actor/party.mjs +++ b/module/data/actor/party.mjs @@ -2,6 +2,7 @@ import BaseDataActor from './base.mjs'; import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; import TagTeamData from '../tagTeamData.mjs'; import GroupRollData from '../groupRollData.mjs'; +import { GoldField } from '../fields/actorField.mjs'; export default class DhParty extends BaseDataActor { /**@inheritdoc */ @@ -11,12 +12,7 @@ export default class DhParty extends BaseDataActor { ...super.defineSchema(), partyMembers: new ForeignDocumentUUIDArrayField({ type: 'Actor' }, { prune: true }), notes: new fields.HTMLField(), - gold: new fields.SchemaField({ - coins: new fields.NumberField({ initial: 0, integer: true }), - handfuls: new fields.NumberField({ initial: 1, integer: true }), - bags: new fields.NumberField({ initial: 0, integer: true }), - chests: new fields.NumberField({ initial: 0, integer: true }) - }), + gold: new GoldField(), tagTeam: new fields.EmbeddedDataField(TagTeamData), groupRoll: new fields.EmbeddedDataField(GroupRollData) }; diff --git a/module/data/fields/actorField.mjs b/module/data/fields/actorField.mjs index ae6f060c..d6b58675 100644 --- a/module/data/fields/actorField.mjs +++ b/module/data/fields/actorField.mjs @@ -103,4 +103,15 @@ class ResourcesField extends fields.TypedObjectField { } } -export { attributeField, ResourcesField, stressDamageReductionRule, bonusField }; +class GoldField extends fields.SchemaField { + constructor() { + super({ + coins: new fields.NumberField({ initial: 0, integer: true }), + handfuls: new fields.NumberField({ initial: 1, integer: true }), + bags: new fields.NumberField({ initial: 0, integer: true }), + chests: new fields.NumberField({ initial: 0, integer: true }) + }); + } +} + +export { attributeField, ResourcesField, GoldField, stressDamageReductionRule, bonusField }; diff --git a/module/systemRegistration/handlebars.mjs b/module/systemRegistration/handlebars.mjs index 63e591c6..ebd65fa2 100644 --- a/module/systemRegistration/handlebars.mjs +++ b/module/systemRegistration/handlebars.mjs @@ -10,6 +10,7 @@ export const preloadHandlebarsTemplates = async function () { 'templates/generic/tab-navigation.hbs', 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs', 'systems/daggerheart/templates/sheets/global/partials/action-item.hbs', + 'systems/daggerheart/templates/sheets/global/partials/gold.hbs', 'systems/daggerheart/templates/sheets/global/partials/domain-card-item.hbs', 'systems/daggerheart/templates/sheets/global/partials/item-resource.hbs', 'systems/daggerheart/templates/sheets/global/partials/resource-section/resource-section.hbs', diff --git a/styles/less/sheets/actors/actor-sheet-shared.less b/styles/less/sheets/actors/actor-sheet-shared.less index 23db088a..bd82ef83 100644 --- a/styles/less/sheets/actors/actor-sheet-shared.less +++ b/styles/less/sheets/actors/actor-sheet-shared.less @@ -37,6 +37,59 @@ color: light-dark(@chat-blue-bg, @beige-50); } + .tab.inventory { + .search-section { + display: flex; + gap: 10px; + align-items: center; + } + .search-bar { + position: relative; + color: light-dark(@dark-blue-50, @beige-50); + width: 100%; + padding-top: 5px; + + input { + border-radius: 50px; + background: light-dark(@dark-blue-10, @golden-10); + border: none; + outline: 2px solid transparent; + transition: all 0.3s ease; + padding: 0 20px; + + &:hover { + outline: 2px solid light-dark(@dark, @golden); + } + + &::-webkit-search-cancel-button { + -webkit-appearance: none; + display: none; + } + } + + .icon { + align-content: center; + height: 32px; + position: absolute; + right: 20px; + font-size: 16px; + z-index: 1; + color: light-dark(@dark-blue-50, @beige-50); + } + } + + .gold-section { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 10px; + padding: 10px 10px 0; + + .input { + color: light-dark(@dark, @beige); + } + } + } + &.limited { &.character, &.adversary, diff --git a/styles/less/sheets/actors/character/inventory.less b/styles/less/sheets/actors/character/inventory.less index b555aa3d..12f63753 100644 --- a/styles/less/sheets/actors/character/inventory.less +++ b/styles/less/sheets/actors/character/inventory.less @@ -3,47 +3,6 @@ .application.sheet.daggerheart.actor.dh-style.character { .tab.inventory { - .search-section { - display: flex; - gap: 10px; - align-items: center; - - .search-bar { - position: relative; - color: light-dark(@dark-blue-50, @beige-50); - width: 100%; - padding-top: 5px; - - input { - border-radius: 50px; - background: light-dark(@dark-blue-10, @golden-10); - border: none; - outline: 2px solid transparent; - transition: all 0.3s ease; - padding: 0 20px; - - &:hover { - outline: 2px solid light-dark(@dark, @golden); - } - - &::-webkit-search-cancel-button { - -webkit-appearance: none; - display: none; - } - } - - .icon { - align-content: center; - height: 32px; - position: absolute; - right: 20px; - font-size: var(--font-size-16); - z-index: 1; - color: light-dark(@dark-blue-50, @beige-50); - } - } - } - .items-section { display: flex; flex-direction: column; @@ -55,16 +14,5 @@ scrollbar-width: thin; scrollbar-color: light-dark(@dark-blue, @golden) transparent; } - - .currency-section { - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr; - gap: 10px; - padding: 10px 10px 0; - - .input { - color: light-dark(@dark, @beige); - } - } } } diff --git a/styles/less/sheets/actors/party/inventory.less b/styles/less/sheets/actors/party/inventory.less index 2dcc97d8..ac59e1de 100644 --- a/styles/less/sheets/actors/party/inventory.less +++ b/styles/less/sheets/actors/party/inventory.less @@ -3,51 +3,6 @@ .application.sheet.daggerheart.actor.dh-style.party { .tab.inventory { - .search-section { - display: flex; - gap: 10px; - align-items: center; - - .search-bar { - position: relative; - color: light-dark(@dark-blue-50, @beige-50); - width: 100%; - padding-top: 5px; - - input { - border-radius: 50px; - background: light-dark(@dark-blue-10, @golden-10); - border: none; - outline: 2px solid transparent; - transition: all 0.3s ease; - padding: 0 20px; - - &:hover { - outline: 2px solid light-dark(@dark, @golden); - } - - &:placeholder { - color: light-dark(@dark-blue-50, @beige-50); - } - - &::-webkit-search-cancel-button { - -webkit-appearance: none; - display: none; - } - } - - .icon { - align-content: center; - height: 32px; - position: absolute; - right: 20px; - font-size: 16px; - z-index: 1; - color: light-dark(@dark-blue-50, @beige-50); - } - } - } - .items-section { display: flex; flex-direction: column; @@ -59,16 +14,5 @@ scrollbar-width: thin; scrollbar-color: light-dark(@dark-blue, @golden) transparent; } - - .currency-section { - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr; - gap: 10px; - padding: 10px 10px 0; - - .input { - color: light-dark(@dark, @beige); - } - } } } diff --git a/templates/sheets/actors/character/inventory.hbs b/templates/sheets/actors/character/inventory.hbs index 711d0c9f..aad1cf7e 100644 --- a/templates/sheets/actors/character/inventory.hbs +++ b/templates/sheets/actors/character/inventory.hbs @@ -13,18 +13,7 @@
{{#if this.inventory.hasCurrency}} -
- {{#each this.inventory.currencies as |currency key|}} - {{#if currency.enabled}} -
- - {{localize currency.label}} - - -
- {{/if}} - {{/each}} -
+ {{> "systems/daggerheart/templates/sheets/global/partials/gold.hbs" currencies=inventory.currencies}} {{/if}}
diff --git a/templates/sheets/actors/party/inventory.hbs b/templates/sheets/actors/party/inventory.hbs index 186e2e99..8dd10154 100644 --- a/templates/sheets/actors/party/inventory.hbs +++ b/templates/sheets/actors/party/inventory.hbs @@ -16,18 +16,7 @@
{{#if inventory.hasCurrency}} -
- {{#each this.inventory.currencies as |currency key|}} - {{#if currency.enabled}} -
- - {{localize currency.label}} - - -
- {{/if}} - {{/each}} -
+ {{> "systems/daggerheart/templates/sheets/global/partials/gold.hbs" currencies=inventory.currencies}} {{/if}}
diff --git a/templates/sheets/global/partials/gold.hbs b/templates/sheets/global/partials/gold.hbs new file mode 100644 index 00000000..7aba2815 --- /dev/null +++ b/templates/sheets/global/partials/gold.hbs @@ -0,0 +1,12 @@ +
+ {{#each currencies as |currency key|}} + {{#if currency.enabled}} +
+ + {{localize currency.label}} + + +
+ {{/if}} + {{/each}} +
\ No newline at end of file From 8808e4646d0512e6f5d6f11100d9ec19df02835f Mon Sep 17 00:00:00 2001 From: WBHarry Date: Wed, 15 Apr 2026 18:47:20 +0200 Subject: [PATCH 10/25] Corrected use of Foundry's Reset translation --- module/applications/settings/appearanceSettings.mjs | 2 +- templates/dialogs/characterReset.hbs | 2 +- templates/settings/automation-settings/footer.hbs | 2 +- templates/settings/homebrew-settings/footer.hbs | 2 +- templates/settings/metagaming-settings/footer.hbs | 2 +- templates/settings/variant-rules.hbs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/module/applications/settings/appearanceSettings.mjs b/module/applications/settings/appearanceSettings.mjs index 64e5a076..9de9e752 100644 --- a/module/applications/settings/appearanceSettings.mjs +++ b/module/applications/settings/appearanceSettings.mjs @@ -122,7 +122,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App type: 'button', action: 'reset', icon: 'fa-solid fa-arrow-rotate-left', - label: game.i18n.localize('ACTIONS.Reset') + label: game.i18n.localize('SETTINGS.UI.ACTIONS.Reset') }, { type: 'submit', icon: 'fa-solid fa-floppy-disk', label: game.i18n.localize('EDITOR.Save') } ]; diff --git a/templates/dialogs/characterReset.hbs b/templates/dialogs/characterReset.hbs index 59f88437..0bd54f7c 100644 --- a/templates/dialogs/characterReset.hbs +++ b/templates/dialogs/characterReset.hbs @@ -28,6 +28,6 @@
- +
\ No newline at end of file diff --git a/templates/settings/automation-settings/footer.hbs b/templates/settings/automation-settings/footer.hbs index 7e9d1991..14ff5bb5 100644 --- a/templates/settings/automation-settings/footer.hbs +++ b/templates/settings/automation-settings/footer.hbs @@ -1,7 +1,7 @@
{{#if abilities}} From 16c07d23bbb50e7417b6d90c094e7f0ed397d195 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Thu, 16 Apr 2026 09:57:16 +0200 Subject: [PATCH 15/25] Changed diceFaces->dieFaces for consistency --- module/applications/dialogs/d20RollDialog.mjs | 2 +- templates/dialogs/dice-roll/rollSelection.hbs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index d8317d70..067aa473 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -123,7 +123,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio context.advantage = this.config.roll?.advantage; context.disadvantage = this.config.roll?.disadvantage; context.diceOptions = CONFIG.DH.GENERAL.diceTypes; - context.diceFaces = CONFIG.DH.GENERAL.dieFaces.reduce((acc, face) => { + context.dieFaces = CONFIG.DH.GENERAL.dieFaces.reduce((acc, face) => { acc[face] = `d${face}`; return acc; }, {}); diff --git a/templates/dialogs/dice-roll/rollSelection.hbs b/templates/dialogs/dice-roll/rollSelection.hbs index c30b0786..2c1a21b6 100644 --- a/templates/dialogs/dice-roll/rollSelection.hbs +++ b/templates/dialogs/dice-roll/rollSelection.hbs @@ -158,7 +158,7 @@ {{/times}} {{#if abilities}} From d9b322406d530a1ece39b25c7126ed9777ff0dfd Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Thu, 16 Apr 2026 04:23:55 -0400 Subject: [PATCH 16/25] Fix party sheet rerenders from member updates interfering with currency and note input (#1809) --- module/documents/actor.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 8ae2e062..4e20db0e 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -113,7 +113,7 @@ export default class DhpActor extends Actor { _onUpdate(changes, options, userId) { super._onUpdate(changes, options, userId); for (const party of this.parties) { - party.render(); + party.render({ parts: ['partyMembers'] }); } } @@ -132,7 +132,7 @@ export default class DhpActor extends Actor { _onDelete(options, userId) { super._onDelete(options, userId); for (const party of this.parties) { - party.render(); + party.render({ parts: ['partyMembers'] }); } } From 2fde61a1d5aac62d812b7e1e6862c565e952c515 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Thu, 16 Apr 2026 05:12:36 -0400 Subject: [PATCH 17/25] [Feature] Make all item types quantifiable in the party (#1808) * Show notification when invalid item types are added to actors * Make all item types quantifiable in the party actor * Remove from comment * Use isInventoryItem to set quantity * Fix formatting --- module/applications/dialogs/itemTransfer.mjs | 4 +- module/applications/sheets/api/base-actor.mjs | 98 ++++++++++++------- module/data/actor/character.mjs | 3 +- module/data/actor/party.mjs | 3 +- module/data/item/base.mjs | 4 +- module/data/item/consumable.mjs | 1 - module/data/item/loot.mjs | 1 - templates/dialogs/item-transfer.hbs | 2 +- .../sheets/actors/character/inventory.hbs | 2 + templates/sheets/actors/party/inventory.hbs | 4 + .../partials/inventory-fieldset-items-V2.hbs | 1 + .../global/partials/inventory-item-V2.hbs | 8 +- 12 files changed, 85 insertions(+), 46 deletions(-) diff --git a/module/applications/dialogs/itemTransfer.mjs b/module/applications/dialogs/itemTransfer.mjs index ad3cf103..42e3a727 100644 --- a/module/applications/dialogs/itemTransfer.mjs +++ b/module/applications/dialogs/itemTransfer.mjs @@ -38,13 +38,15 @@ export default class ItemTransferDialog extends HandlebarsApplicationMixin(Appli originActor ??= item?.actor; const homebrewKey = CONFIG.DH.SETTINGS.gameSettings.Homebrew; const currencySetting = game.settings.get(CONFIG.DH.id, homebrewKey).currency?.[currency] ?? null; + const max = item?.system.quantity ?? originActor.system.gold[currency] ?? 0; return { originActor, targetActor, itemImage: item?.img, currencyIcon: currencySetting?.icon, - max: item?.system.quantity ?? originActor.system.gold[currency] ?? 0, + max, + initial: targetActor.system.metadata.quantifiable?.includes(item.type) ? max : 1, title: item?.name ?? currencySetting?.label }; } diff --git a/module/applications/sheets/api/base-actor.mjs b/module/applications/sheets/api/base-actor.mjs index f009267e..e23a4426 100644 --- a/module/applications/sheets/api/base-actor.mjs +++ b/module/applications/sheets/api/base-actor.mjs @@ -298,47 +298,79 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { ); } - if (item.system.metadata.isQuantifiable) { - const actorItem = originActor.items.get(data.originId); - const quantityTransfered = await game.system.api.applications.dialogs.ItemTransferDialog.configure({ + // Perform the actual transfer, showing a dialog when doing it + const availableQuantity = Math.max(1, item.system.quantity); + const actorItem = originActor.items.get(data.originId) ?? item; + if (availableQuantity > 1) { + const quantityTransferred = await game.system.api.applications.dialogs.ItemTransferDialog.configure({ item, targetActor: this.document }); - - if (quantityTransfered) { - const existingItem = this.document.items.find(x => itemIsIdentical(x, item)); - if (existingItem) { - await existingItem.update({ - 'system.quantity': existingItem.system.quantity + quantityTransfered - }); - } else { - const createData = item.toObject(); - await this.document.createEmbeddedDocuments('Item', [ - { - ...createData, - system: { - ...createData.system, - quantity: quantityTransfered - } - } - ]); - } - - if (quantityTransfered === actorItem.system.quantity) { - await originActor.deleteEmbeddedDocuments('Item', [data.originId]); - } else { - await actorItem.update({ - 'system.quantity': actorItem.system.quantity - quantityTransfered - }); - } - } + return this.#transferItem(actorItem, quantityTransferred); } else { - await this.document.createEmbeddedDocuments('Item', [item.toObject()]); - await originActor.deleteEmbeddedDocuments('Item', [data.originId]); + return this.#transferItem(actorItem, availableQuantity); } } } + /** + * Helper to perform the actual transfer of an item to this actor, including stack/unstack logic based on target quantifiability. + * Make sure item is the actor item before calling this method or there will be issues + */ + async #transferItem(item, quantity) { + const originActor = item.actor; + const targetActor = this.document; + const allowStacking = targetActor.system.metadata.quantifiable?.includes(item.type); + + const batch = []; + + // First add/update the item to the target actor + const existing = allowStacking ? targetActor.items.find(x => itemIsIdentical(x, item)) : null; + if (existing) { + batch.push({ + action: 'update', + documentName: 'Item', + parent: targetActor, + updates: [{ '_id': existing.id, 'system.quantity': existing.system.quantity + quantity }] + }); + } else { + const itemsToCreate = []; + if (allowStacking) { + itemsToCreate.push(foundry.utils.mergeObject(item.toObject(true), { system: { quantity } })); + } else { + const createData = new Array(Math.max(1, quantity)) + .fill(0) + .map(() => foundry.utils.mergeObject(item.toObject(), { system: { quantity: 1 } })); + itemsToCreate.push(...createData); + } + batch.push({ + action: 'create', + documentName: 'Item', + parent: targetActor, + data: itemsToCreate + }); + } + + // Remove the item from the original actor (by either deleting it, or updating its quantity) + if (quantity >= item.system.quantity) { + batch.push({ + action: 'delete', + documentName: 'Item', + parent: originActor, + ids: [item.id] + }); + } else { + batch.push({ + action: 'update', + documentName: 'Item', + parent: originActor, + updates: [{ '_id': item.id, 'system.quantity': item.system.quantity - quantity }] + }); + } + + return foundry.documents.modifyBatch(batch); + } + /** * On dragStart on the item. * @param {DragEvent} event - The drag event diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index e6bcd294..bf3d7bb8 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -19,7 +19,8 @@ export default class DhCharacter extends DhCreature { type: 'character', settingSheet: DHCharacterSettings, isNPC: false, - hasInventory: true + hasInventory: true, + quantifiable: ["loot", "consumable"] }); } diff --git a/module/data/actor/party.mjs b/module/data/actor/party.mjs index 46635237..6ccf8852 100644 --- a/module/data/actor/party.mjs +++ b/module/data/actor/party.mjs @@ -8,7 +8,8 @@ export default class DhParty extends BaseDataActor { /** @inheritdoc */ static get metadata() { return foundry.utils.mergeObject(super.metadata, { - hasInventory: true + hasInventory: true, + quantifiable: ["weapon", "armor", "loot", "consumable"] }); } diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index 21a11149..72718c5e 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -4,7 +4,6 @@ * @property {string} label - A localizable label used on application. * @property {string} type - The system type that this data model represents. * @property {boolean} hasDescription - Indicates whether items of this type have description field - * @property {boolean} isQuantifiable - Indicates whether items of this type have quantity field * @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item */ @@ -24,7 +23,6 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { type: 'base', hasDescription: false, hasResource: false, - isQuantifiable: false, isInventoryItem: false, hasActions: false, hasAttribution: true @@ -83,7 +81,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { ); } - if (this.metadata.isQuantifiable) + if (this.metadata.isInventoryItem) schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true }); if (this.metadata.hasActions) schema.actions = new ActionsField(); diff --git a/module/data/item/consumable.mjs b/module/data/item/consumable.mjs index ab527967..e83a1a53 100644 --- a/module/data/item/consumable.mjs +++ b/module/data/item/consumable.mjs @@ -7,7 +7,6 @@ export default class DHConsumable extends BaseDataItem { label: 'TYPES.Item.consumable', type: 'consumable', hasDescription: true, - isQuantifiable: true, isInventoryItem: true, hasActions: true }); diff --git a/module/data/item/loot.mjs b/module/data/item/loot.mjs index cdb0855e..d4092934 100644 --- a/module/data/item/loot.mjs +++ b/module/data/item/loot.mjs @@ -7,7 +7,6 @@ export default class DHLoot extends BaseDataItem { label: 'TYPES.Item.loot', type: 'loot', hasDescription: true, - isQuantifiable: true, isInventoryItem: true, hasActions: true }); diff --git a/templates/dialogs/item-transfer.hbs b/templates/dialogs/item-transfer.hbs index 0e7df3dc..63972ed8 100644 --- a/templates/dialogs/item-transfer.hbs +++ b/templates/dialogs/item-transfer.hbs @@ -14,7 +14,7 @@
- +
diff --git a/templates/sheets/actors/character/inventory.hbs b/templates/sheets/actors/character/inventory.hbs index aad1cf7e..30812e17 100644 --- a/templates/sheets/actors/character/inventory.hbs +++ b/templates/sheets/actors/character/inventory.hbs @@ -39,6 +39,7 @@ collection=@root.inventory.consumables isGlassy=true canCreate=true + isQuantifiable=true }} {{> 'daggerheart.inventory-items' title='TYPES.Item.loot' @@ -47,6 +48,7 @@ isGlassy=true canCreate=true showActions=true + isQuantifiable=true }} \ No newline at end of file diff --git a/templates/sheets/actors/party/inventory.hbs b/templates/sheets/actors/party/inventory.hbs index 8dd10154..74492c73 100644 --- a/templates/sheets/actors/party/inventory.hbs +++ b/templates/sheets/actors/party/inventory.hbs @@ -29,6 +29,7 @@ canCreate=true hideResources=true hideContextMenu=true + isQuantifiable=true }} {{> 'daggerheart.inventory-items' title='TYPES.Item.armor' @@ -39,6 +40,7 @@ canCreate=true hideResources=true hideContextMenu=true + isQuantifiable=true }} {{> 'daggerheart.inventory-items' title='TYPES.Item.consumable' @@ -48,6 +50,7 @@ isGlassy=true canCreate=true hideContextMenu=true + isQuantifiable=true }} {{> 'daggerheart.inventory-items' title='TYPES.Item.loot' @@ -57,6 +60,7 @@ isGlassy=true canCreate=true hideContextMenu=true + isQuantifiable=true }} \ No newline at end of file diff --git a/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs index 31c8f7f5..5c6eae32 100644 --- a/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs +++ b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs @@ -66,6 +66,7 @@ Parameters: showLabels=../showLabels isAction=../isAction hideResources=../hideResources + isQuantifiable=../isQuantifiable showActions=../showActions }} diff --git a/templates/sheets/global/partials/inventory-item-V2.hbs b/templates/sheets/global/partials/inventory-item-V2.hbs index 2129b969..25f1715c 100644 --- a/templates/sheets/global/partials/inventory-item-V2.hbs +++ b/templates/sheets/global/partials/inventory-item-V2.hbs @@ -63,10 +63,10 @@ Parameters: {{#if (and (not hideResources) (not (eq item.system.resource.type 'diceValue')))}} {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} {{/if}} - {{#if (and (not hideResources) (gte item.system.quantity 0))}} -
- -
+ {{#if (or isQuantifiable (or (eq item.system.quantity 0) (gt item.system.quantity 1)))}} +
+ +
{{/if}} {{!-- Controls --}} From 4b92001f97e9dbaf933054ab9df27db65b9b5870 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 17 Apr 2026 13:29:37 -0400 Subject: [PATCH 18/25] Fix issues with party sheet resources in light mode (#1810) --- styles/less/sheets/actors/party/party-members.less | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/styles/less/sheets/actors/party/party-members.less b/styles/less/sheets/actors/party/party-members.less index 155fcc36..2e2f4cf8 100644 --- a/styles/less/sheets/actors/party/party-members.less +++ b/styles/less/sheets/actors/party/party-members.less @@ -35,8 +35,8 @@ body.game:is(.performance-low, .noblur) { .actor-img-frame { grid-area: img; - width: 7.5rem; - height: 7.5rem; + width: 7.375rem; + height: 7.375rem; position: relative; .actor-img { @@ -71,6 +71,7 @@ body.game:is(.performance-low, .noblur) { height: 1.75rem; background: url('../assets/svg/trait-shield.svg') no-repeat; background-size: 100%; + color: var(--color-light-1); font-size: var(--font-size-14); font-weight: 700; display: flex; @@ -87,7 +88,7 @@ body.game:is(.performance-low, .noblur) { display: flex; gap: 4px; - background-color: light-dark(transparent, @dark-blue); + background-color: light-dark(var(--color-light-1), @dark-blue); color: light-dark(@dark-blue, @golden); padding: 4px 6px; border: 1px solid light-dark(@dark-blue, @golden); @@ -125,7 +126,7 @@ body.game:is(.performance-low, .noblur) { width: 100%; z-index: 1; font-size: var(--font-size-20); - color: light-dark(@beige, @golden); + color: light-dark(@dark-blue, @golden); font-weight: bold; } From 4944722139a1f0c6ca33058374b0671f133e2a70 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Sat, 18 Apr 2026 17:31:56 -0400 Subject: [PATCH 19/25] Avoid error when backing out of action (#1813) --- module/data/action/attackAction.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs index a2d47309..5e93d70b 100644 --- a/module/data/action/attackAction.mjs +++ b/module/data/action/attackAction.mjs @@ -51,7 +51,7 @@ export default class DHAttackAction extends DHDamageAction { async use(event, options) { const result = await super.use(event, options); - if (result.message?.system.action.roll?.type === 'attack') { + if (result?.message?.system.action.roll?.type === 'attack') { const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns; await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.characterAttack.id); } From 1fea8438ba3e9b1644e89756252ffb9d2c2fe432 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 19 Apr 2026 11:29:47 +0200 Subject: [PATCH 20/25] Fixed DowntimeMove actions not opening --- templates/settings/downtime-config/actions.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/settings/downtime-config/actions.hbs b/templates/settings/downtime-config/actions.hbs index d197f983..feb05302 100644 --- a/templates/settings/downtime-config/actions.hbs +++ b/templates/settings/downtime-config/actions.hbs @@ -8,7 +8,7 @@
{{#each move.actions as |action|}} - {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" id=action.id }} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" id=action.id type="action" }} {{/each}}
From 03110377e17ab134937e849ea3834fa488e49afd Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 20 Apr 2026 00:04:03 +0200 Subject: [PATCH 21/25] Fixed so that resource reset on downtime can handle math expressions --- module/applications/dialogs/downtime.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/module/applications/dialogs/downtime.mjs b/module/applications/dialogs/downtime.mjs index 3475dee7..989e4625 100644 --- a/module/applications/dialogs/downtime.mjs +++ b/module/applications/dialogs/downtime.mjs @@ -259,8 +259,9 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV const resetValue = increasing ? 0 : feature.system.resource.max - ? Roll.replaceFormulaData(feature.system.resource.max, this.actor) + ? new Roll(Roll.replaceFormulaData(feature.system.resource.max, this.actor)).evaluateSync().total : 0; + await feature.update({ 'system.resource.value': resetValue }); } From fa04c9920f75efaeb13f94123434ad797c7f1141 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Mon, 20 Apr 2026 02:11:17 -0400 Subject: [PATCH 22/25] Fix translation string (#1817) --- templates/sheets/items/weapon/settings.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/sheets/items/weapon/settings.hbs b/templates/sheets/items/weapon/settings.hbs index e67e8dd7..ef47b323 100644 --- a/templates/sheets/items/weapon/settings.hbs +++ b/templates/sheets/items/weapon/settings.hbs @@ -7,7 +7,7 @@ {{localize tabs.settings.label}} {{localize "DAGGERHEART.GENERAL.Tiers.singular"}} {{formInput systemFields.tier value=source.system.tier}} - {{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon"}} + {{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full"}} {{formInput systemFields.secondary value=source.system.secondary}} {{localize "DAGGERHEART.GENERAL.Trait.single"}} {{formInput systemFields.attack.fields.roll.fields.trait value=document.system.attack.roll.trait name="system.attack.roll.trait" label="DAGGERHEART.GENERAL.Trait.single" localize=true}} From c683bc4352bfb5d07914b87a9bbe27b9eeb0748c Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 20 Apr 2026 15:20:35 +0200 Subject: [PATCH 23/25] Fixed IncludeBaseDamage to be an override --- lang/en.json | 6 ++- module/data/action/attackAction.mjs | 2 +- templates/actionTypes/damage.hbs | 70 +++++++++++++++-------------- templates/actionTypes/roll.hbs | 4 +- 4 files changed, 44 insertions(+), 38 deletions(-) diff --git a/lang/en.json b/lang/en.json index 7b8b45c7..48a59307 100755 --- a/lang/en.json +++ b/lang/en.json @@ -113,7 +113,9 @@ "deleteTriggerTitle": "Delete Trigger", "deleteTriggerContent": "Are you sure you want to delete the {trigger} trigger?", "advantageState": "Advantage State", - "damageOnSave": "Damage on Save" + "damageOnSave": "Damage on Save", + "useDefaultItemValues": "Use default Item values", + "itemDamageIsUsed": "Item Damage Is Used" }, "RollField": { "diceRolling": { @@ -128,7 +130,7 @@ "attackModifier": "Attack Modifier", "attackName": "Attack Name", "criticalThreshold": "Critical Threshold", - "includeBase": { "label": "Include Item Damage" }, + "includeBase": { "label": "Use Item Damage" }, "groupAttack": { "label": "Group Attack" }, "multiplier": "Multiplier", "saveHint": "Set a default Trait to enable Reaction Roll. It can be changed later in Reaction Roll Dialog.", diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs index 5e93d70b..f54ea282 100644 --- a/module/data/action/attackAction.mjs +++ b/module/data/action/attackAction.mjs @@ -13,7 +13,7 @@ export default class DHAttackAction extends DHDamageAction { if (!!this.item?.system?.attack) { if (this.damage.includeBase) { const baseDamage = this.getParentDamage(); - this.damage.parts.unshift(new DHDamageData(baseDamage)); + this.damage.parts.hitPoints = new DHDamageData(baseDamage); } if (this.roll.useDefault) { this.roll.trait = this.item.system.attack.roll.trait; diff --git a/templates/actionTypes/damage.hbs b/templates/actionTypes/damage.hbs index 192c5be5..454d0413 100644 --- a/templates/actionTypes/damage.hbs +++ b/templates/actionTypes/damage.hbs @@ -21,7 +21,7 @@ {{!-- Handlebars uses Symbol.Iterator to produce index|key. This isn't compatible with our parts object, so we instead use applyTo, which is the same value --}} - {{#each source.parts as |dmg|}} + {{#each source.parts as |dmg key|}}
@@ -31,40 +31,44 @@ {{/unless}} - {{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base))}} - {{formField ../fields.resultBased value=dmg.resultBased name=(concat "damage.parts." dmg.applyTo ".resultBased") localize=true classes="checkbox"}} - {{/if}} - {{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base) dmg.resultBased)}} -
-
- {{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.hope")}} - {{> formula fields=../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" key=dmg.applyTo path=../path}} -
-
- {{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.fear")}} - {{> formula fields=../fields.valueAlt.fields type=../fields.type dmg=dmg source=dmg.valueAlt target="valueAlt" key=dmg.applyTo path=../path}} -
-
- {{else}} - {{> formula fields=../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" key=dmg.applyTo path=../path}} - {{/if}} - - {{#if (and (eq dmg.applyTo 'hitPoints') (ne @root.source.type 'healing'))}} - {{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." dmg.applyTo ".type") localize=true}} - {{/if}} - - {{#if ../horde}} -
- {{localize "DAGGERHEART.ACTORS.Adversary.hordeDamage"}} + {{#unless (and @root.source.damage.includeBase (eq key 'hitPoints'))}} + {{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base))}} + {{formField ../fields.resultBased value=dmg.resultBased name=(concat "damage.parts." dmg.applyTo ".resultBased") localize=true classes="checkbox"}} + {{/if}} + {{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base) dmg.resultBased)}}
- - {{formField ../fields.valueAlt.fields.flatMultiplier value=dmg.valueAlt.flatMultiplier name=(concat ../path "damage.parts." dmg.applyTo ".valueAlt.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }} - {{formField ../fields.valueAlt.fields.dice value=dmg.valueAlt.dice name=(concat ../path "damage.parts." dmg.applyTo ".valueAlt.dice") classes="inline-child" localize=true}} - {{formField ../fields.valueAlt.fields.bonus value=dmg.valueAlt.bonus name=(concat ../path "damage.parts." dmg.applyTo ".valueAlt.bonus") localize=true classes="inline-child"}} +
+ {{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.hope")}} + {{> formula fields=../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" key=dmg.applyTo path=../path}} +
+
+ {{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.fear")}} + {{> formula fields=../fields.valueAlt.fields type=../fields.type dmg=dmg source=dmg.valueAlt target="valueAlt" key=dmg.applyTo path=../path}} +
-
- {{/if}} - + {{else}} + {{> formula fields=../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" key=dmg.applyTo path=../path}} + {{/if}} + + {{#if (and (eq dmg.applyTo 'hitPoints') (ne @root.source.type 'healing'))}} + {{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." dmg.applyTo ".type") localize=true}} + {{/if}} + + {{#if ../horde}} +
+ {{localize "DAGGERHEART.ACTORS.Adversary.hordeDamage"}} +
+ + {{formField ../fields.valueAlt.fields.flatMultiplier value=dmg.valueAlt.flatMultiplier name=(concat ../path "damage.parts." dmg.applyTo ".valueAlt.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }} + {{formField ../fields.valueAlt.fields.dice value=dmg.valueAlt.dice name=(concat ../path "damage.parts." dmg.applyTo ".valueAlt.dice") classes="inline-child" localize=true}} + {{formField ../fields.valueAlt.fields.bonus value=dmg.valueAlt.bonus name=(concat ../path "damage.parts." dmg.applyTo ".valueAlt.bonus") localize=true classes="inline-child"}} +
+
+ {{/if}} + + {{else}} + {{localize "DAGGERHEART.ACTIONS.Config.itemDamageIsUsed"}} + {{/unless}}
{{/each}} diff --git a/templates/actionTypes/roll.hbs b/templates/actionTypes/roll.hbs index 9784fc08..41f88ba2 100644 --- a/templates/actionTypes/roll.hbs +++ b/templates/actionTypes/roll.hbs @@ -1,7 +1,7 @@
- Roll - {{#if @root.hasBaseDamage}}{{formInput fields.useDefault name="roll.useDefault" value=source.useDefault dataset=(object tooltip="Use default Item values" tooltipDirection="UP")}}{{/if}} + {{localize "DAGGERHEART.GENERAL.roll"}} + {{#if @root.hasBaseDamage}}{{formInput fields.useDefault name="roll.useDefault" value=source.useDefault dataset=(object tooltip=(localize "DAGGERHEART.ACTIONS.Config.useDefaultItemValues") tooltipDirection="UP")}}{{/if}} {{formField fields.type label="DAGGERHEART.GENERAL.type" name="roll.type" value=source.type localize=true choices=@root.getRollTypeOptions localize=true}} From f850cbda76ac741e9ba812fdf4dc58a6bf29b378 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Mon, 20 Apr 2026 09:30:43 -0400 Subject: [PATCH 24/25] [Feature] Updates to inventory icons, context menus, action use, and observer visibility (#1814) * Update inventory controls and permissions filtering * Also disable prosemirror editors * Address context menu deprecation warnings * Fix context menu detection for actions * Refine logic for use action when hovering over item icon --- lang/en.json | 1 + .../applications/sheets/actors/character.mjs | 50 +++++--- .../sheets/api/application-mixin.mjs | 36 +++--- .../sidebar/tabs/actorDirectory.mjs | 8 +- module/applications/ui/chatLog.mjs | 17 +-- module/applications/ui/combatTracker.mjs | 8 +- module/data/action/baseAction.mjs | 11 ++ module/data/item/base.mjs | 2 + module/data/item/weapon.mjs | 4 +- module/documents/item.mjs | 7 + .../less/sheets/actors/character/sheet.less | 15 --- .../less/sheets/actors/character/sidebar.less | 2 +- templates/sheets/actors/adversary/effects.hbs | 4 +- .../sheets/actors/adversary/features.hbs | 4 +- templates/sheets/actors/character/effects.hbs | 4 +- .../sheets/actors/character/features.hbs | 6 +- templates/sheets/actors/character/header.hbs | 48 +++---- .../sheets/actors/character/inventory.hbs | 10 +- templates/sheets/actors/character/loadout.hbs | 4 +- templates/sheets/actors/character/sidebar.hbs | 8 +- templates/sheets/actors/companion/effects.hbs | 4 +- .../sheets/actors/environment/features.hbs | 4 +- templates/sheets/actors/party/inventory.hbs | 8 +- .../partials/inventory-fieldset-items-V2.hbs | 3 +- .../global/partials/inventory-item-V2.hbs | 121 +++++++++--------- .../partials/inventory-item-compact.hbs | 42 +++--- templates/sheets/global/tabs/tab-actions.hbs | 2 +- templates/sheets/global/tabs/tab-effects.hbs | 4 +- 28 files changed, 222 insertions(+), 215 deletions(-) diff --git a/lang/en.json b/lang/en.json index 48a59307..a824d379 100755 --- a/lang/en.json +++ b/lang/en.json @@ -3235,6 +3235,7 @@ "Tooltip": { "disableEffect": "Disable Effect", "enableEffect": "Enable Effect", + "edit": "Edit", "openItemWorld": "Open Item World", "openActorWorld": "Open Actor World", "sendToChat": "Send to Chat", diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index f2686fdd..3b029771 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -12,8 +12,6 @@ export default class CharacterSheet extends DHBaseActorSheet { static DEFAULT_OPTIONS = { classes: ['character'], position: { width: 850, height: 800 }, - /* Foundry adds disabled to all buttons and inputs if editPermission is missing. This is not desired. */ - editPermission: CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER, actions: { toggleVault: CharacterSheet.#toggleVault, rollAttribute: CharacterSheet.#rollAttribute, @@ -68,7 +66,7 @@ export default class CharacterSheet extends DHBaseActorSheet { } }, { - handler: CharacterSheet.#getEquipamentContextOptions, + handler: CharacterSheet.#getEquipmentContextOptions, selector: '[data-item-uuid][data-type="armor"], [data-item-uuid][data-type="weapon"]', options: { parentClassHooks: false, @@ -170,6 +168,16 @@ export default class CharacterSheet extends DHBaseActorSheet { return applicationOptions; } + /** @inheritdoc */ + _toggleDisabled(disabled) { + // Overriden to only disable text inputs by default. + // Everything else is done by checking @root.editable in the sheet + const form = this.form; + for (const input of form.querySelectorAll("input:not([type=search]), .editor.prosemirror")) { + input.disabled = disabled; + } + } + /** @inheritDoc */ async _onRender(context, options) { await super._onRender(context, options); @@ -315,11 +323,11 @@ export default class CharacterSheet extends DHBaseActorSheet { /**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */ const options = [ { - name: 'toLoadout', + label: 'toLoadout', icon: 'fa-solid fa-arrow-up', - condition: target => { + visible: target => { const doc = getDocFromElementSync(target); - return doc && doc.system.inVault; + return doc?.isOwner && doc.system.inVault; }, callback: async target => { const doc = await getDocFromElement(target); @@ -329,11 +337,11 @@ export default class CharacterSheet extends DHBaseActorSheet { } }, { - name: 'recall', + label: 'recall', icon: 'fa-solid fa-bolt-lightning', - condition: target => { + visible: target => { const doc = getDocFromElementSync(target); - return doc && doc.system.inVault; + return doc?.isOwner && doc.system.inVault; }, callback: async (target, event) => { const doc = await getDocFromElement(target); @@ -368,17 +376,17 @@ export default class CharacterSheet extends DHBaseActorSheet { } }, { - name: 'toVault', + label: 'toVault', icon: 'fa-solid fa-arrow-down', - condition: target => { + visible: target => { const doc = getDocFromElementSync(target); - return doc && !doc.system.inVault; + return doc?.isOwner && !doc.system.inVault; }, callback: async target => (await getDocFromElement(target)).update({ 'system.inVault': true }) } ].map(option => ({ ...option, - name: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.name}`, + label: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.label}`, icon: `` })); @@ -391,29 +399,29 @@ export default class CharacterSheet extends DHBaseActorSheet { * @this {CharacterSheet} * @protected */ - static #getEquipamentContextOptions() { + static #getEquipmentContextOptions() { const options = [ { - name: 'equip', + label: 'equip', icon: 'fa-solid fa-hands', - condition: target => { + visible: target => { const doc = getDocFromElementSync(target); - return doc && !doc.system.equipped; + return doc.isOwner && doc && !doc.system.equipped; }, callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target) }, { - name: 'unequip', + label: 'unequip', icon: 'fa-solid fa-hands', - condition: target => { + visible: target => { const doc = getDocFromElementSync(target); - return doc && doc.system.equipped; + return doc.isOwner && doc && doc.system.equipped; }, callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target) } ].map(option => ({ ...option, - name: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.name}`, + label: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.label}`, icon: `` })); diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index e93ce774..e0110ae3 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -418,18 +418,18 @@ export default function DHApplicationMixin(Base) { /**@type {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */ const options = [ { - name: 'disableEffect', + label: 'disableEffect', icon: 'fa-solid fa-lightbulb', - condition: element => { + visible: element => { const target = element.closest('[data-item-uuid]'); return !target.dataset.disabled && target.dataset.itemType !== 'beastform'; }, callback: async target => (await getDocFromElement(target)).update({ disabled: true }) }, { - name: 'enableEffect', + label: 'enableEffect', icon: 'fa-regular fa-lightbulb', - condition: element => { + visible: element => { const target = element.closest('[data-item-uuid]'); return target.dataset.disabled && target.dataset.itemType !== 'beastform'; }, @@ -437,7 +437,7 @@ export default function DHApplicationMixin(Base) { } ].map(option => ({ ...option, - name: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.name}`, + label: `DAGGERHEART.APPLICATIONS.ContextMenu.${option.label}`, icon: `` })); @@ -468,14 +468,14 @@ export default function DHApplicationMixin(Base) { _getContextMenuCommonOptions({ usable = false, toChat = false, deletable = true }) { const options = [ { - name: 'CONTROLS.CommonEdit', + label: 'CONTROLS.CommonEdit', icon: 'fa-solid fa-pen-to-square', - condition: target => { + visible: target => { const { dataset } = target.closest('[data-item-uuid]'); const doc = getDocFromElementSync(target); return ( (!dataset.noCompendiumEdit && !doc) || - (doc && (!doc?.hasOwnProperty('systemPath') || doc?.inCollection)) + (doc?.isOwner && (!doc?.hasOwnProperty('systemPath') || doc?.inCollection)) ); }, callback: async target => (await getDocFromElement(target)).sheet.render({ force: true }) @@ -484,11 +484,12 @@ export default function DHApplicationMixin(Base) { if (usable) { options.unshift({ - name: 'DAGGERHEART.GENERAL.damage', + label: 'DAGGERHEART.GENERAL.damage', icon: 'fa-solid fa-explosion', - condition: target => { + visible: target => { const doc = getDocFromElementSync(target); return ( + doc?.isOwner && !foundry.utils.isEmpty(doc?.system?.attack?.damage.parts) || !foundry.utils.isEmpty(doc?.damage?.parts) ); @@ -507,11 +508,11 @@ export default function DHApplicationMixin(Base) { }); options.unshift({ - name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem', + label: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem', icon: 'fa-solid fa-burst', - condition: target => { + visible: target => { const doc = getDocFromElementSync(target); - return doc && !(doc.type === 'domainCard' && doc.system.inVault); + return doc?.isOwner && !(doc.type === 'domainCard' && doc.system.inVault); }, callback: async (target, event) => (await getDocFromElement(target)).use(event) }); @@ -519,18 +520,19 @@ export default function DHApplicationMixin(Base) { if (toChat) options.push({ - name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat', + label: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat', icon: 'fa-solid fa-message', callback: async target => (await getDocFromElement(target)).toChat(this.document.uuid) }); if (deletable) options.push({ - name: 'CONTROLS.CommonDelete', + label: 'CONTROLS.CommonDelete', icon: 'fa-solid fa-trash', - condition: element => { + visible: element => { const target = element.closest('[data-item-uuid]'); - return target.dataset.itemType !== 'beastform'; + const doc = getDocFromElementSync(target); + return doc?.isOwner && target.dataset.itemType !== 'beastform'; }, callback: async (target, event) => { const doc = await getDocFromElement(target); diff --git a/module/applications/sidebar/tabs/actorDirectory.mjs b/module/applications/sidebar/tabs/actorDirectory.mjs index 1306de61..89da1426 100644 --- a/module/applications/sidebar/tabs/actorDirectory.mjs +++ b/module/applications/sidebar/tabs/actorDirectory.mjs @@ -48,9 +48,9 @@ export default class DhActorDirectory extends foundry.applications.sidebar.tabs. const options = super._getEntryContextOptions(); options.push( { - name: 'DAGGERHEART.UI.Sidebar.actorDirectory.duplicateToNewTier', + label: 'DAGGERHEART.UI.Sidebar.actorDirectory.duplicateToNewTier', icon: ``, - condition: li => { + visible: li => { const actor = game.actors.get(li.dataset.entryId); return actor?.type === 'adversary' && actor.system.type !== 'social'; }, @@ -92,9 +92,9 @@ export default class DhActorDirectory extends foundry.applications.sidebar.tabs. } }, { - name: 'DAGGERHEART.UI.Sidebar.actorDirectory.activateParty', + label: 'DAGGERHEART.UI.Sidebar.actorDirectory.activateParty', icon: ``, - condition: li => { + visible: li => { const actor = game.actors.get(li.dataset.entryId); return actor && actor.type === 'party' && !actor.system.active; }, diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 59939963..34b25591 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -103,23 +103,10 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo _getEntryContextOptions() { return [ ...super._getEntryContextOptions(), - // { - // name: 'Reroll', - // icon: '', - // condition: li => { - // const message = game.messages.get(li.dataset.messageId); - - // return (game.user.isGM || message.isAuthor) && message.rolls.length > 0; - // }, - // callback: li => { - // const message = game.messages.get(li.dataset.messageId); - // new game.system.api.applications.dialogs.RerollDialog(message).render({ force: true }); - // } - // }, { - name: game.i18n.localize('DAGGERHEART.UI.ChatLog.rerollDamage'), + label: 'DAGGERHEART.UI.ChatLog.rerollDamage', icon: '', - condition: li => { + visible: li => { const message = game.messages.get(li.dataset.messageId); const hasRolledDamage = message.system.hasDamage ? Object.keys(message.system.damage).length > 0 diff --git a/module/applications/ui/combatTracker.mjs b/module/applications/ui/combatTracker.mjs index 1043e128..fb19a17e 100644 --- a/module/applications/ui/combatTracker.mjs +++ b/module/applications/ui/combatTracker.mjs @@ -84,15 +84,15 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C _getCombatContextOptions() { return [ { - name: 'COMBAT.ClearMovementHistories', + label: 'COMBAT.ClearMovementHistories', icon: '', - condition: () => game.user.isGM && this.viewed?.combatants.size > 0, + visible: () => game.user.isGM && this.viewed?.combatants.size > 0, callback: () => this.viewed.clearMovementHistories() }, { - name: 'COMBAT.Delete', + label: 'COMBAT.Delete', icon: '', - condition: () => game.user.isGM && !!this.viewed, + visible: () => game.user.isGM && !!this.viewed, callback: () => this.viewed.endCombat() } ]; diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 0992350b..f4835f34 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -110,6 +110,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return this._id; } + /** Returns true if the current user is the owner of the containing item */ + get isOwner() { + return this.item?.isOwner ?? true; + } + /** * Return Item the action is attached too. */ @@ -143,6 +148,12 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel : null; } + /** Returns true if the action is usable */ + get usable() { + const actor = this.actor; + return this.isOwner && actor?.type === 'character'; + } + static getRollType(parent) { return 'trait'; } diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index 72718c5e..cc198dc4 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -108,6 +108,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { } get actionsList() { + // No actions on non-characters + if (this.actor && this.actor.type !== 'character') return []; return this.actions; } diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 9335037c..75e6dc8e 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -99,7 +99,9 @@ export default class DHWeapon extends AttachableItem { /* -------------------------------------------- */ get actionsList() { - return [this.attack, ...this.actions]; + // No actions on non-characters + if (this.actor && this.actor.type !== 'character') return []; + return [this.attack, ...super.actionsList]; } get customActions() { diff --git a/module/documents/item.mjs b/module/documents/item.mjs index a8b41b05..8ece56fa 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -76,6 +76,13 @@ export default class DHItem extends foundry.documents.Item { return this.system.metadata.isInventoryItem ?? false; } + /** Returns true if the item can be used */ + get usable() { + const actor = this.actor; + const actionsList = this.system.actionsList; + return this.isOwner && actor?.type === 'character' && (actionsList?.size || actionsList?.length); + } + /** @inheritdoc */ static async createDialog(data = {}, createOptions = {}, options = {}) { const { folders, types, template, context = {}, ...dialogOptions } = options; diff --git a/styles/less/sheets/actors/character/sheet.less b/styles/less/sheets/actors/character/sheet.less index ee6580fd..68792c99 100644 --- a/styles/less/sheets/actors/character/sheet.less +++ b/styles/less/sheets/actors/character/sheet.less @@ -11,21 +11,6 @@ padding-bottom: 0; overflow-x: auto; - &.viewMode { - button:not(.btn-toggle-view), - input:not(.search), - .controls, - .character-sidebar-sheet, - .img-portait, - .name-row, - .hope-section, - .downtime-section, - .character-traits, - .card-list { - pointer-events: none; - } - } - .character-sidebar-sheet { grid-row: 1 / span 2; grid-column: 1; diff --git a/styles/less/sheets/actors/character/sidebar.less b/styles/less/sheets/actors/character/sidebar.less index e7027163..b159a8e8 100644 --- a/styles/less/sheets/actors/character/sidebar.less +++ b/styles/less/sheets/actors/character/sidebar.less @@ -316,9 +316,9 @@ border-radius: 3px; background: light-dark(@dark-blue, @golden); clip-path: none; - cursor: pointer; display: flex; align-items: center; + justify-content: center; gap: 4px; border: 1px solid transparent; transition: all 0.3s ease; diff --git a/templates/sheets/actors/adversary/effects.hbs b/templates/sheets/actors/adversary/effects.hbs index cefb6e57..087e8b30 100644 --- a/templates/sheets/actors/adversary/effects.hbs +++ b/templates/sheets/actors/adversary/effects.hbs @@ -6,7 +6,7 @@ type='effect' isGlassy=true collection=effects.actives - canCreate=true + canCreate=@root.editable hideResources=true }} @@ -15,7 +15,7 @@ type='effect' isGlassy=true collection=effects.inactives - canCreate=true + canCreate=@root.editable hideResources=true }} diff --git a/templates/sheets/actors/adversary/features.hbs b/templates/sheets/actors/adversary/features.hbs index a24342fc..d320b0d8 100644 --- a/templates/sheets/actors/adversary/features.hbs +++ b/templates/sheets/actors/adversary/features.hbs @@ -6,8 +6,8 @@ type='feature' collection=@root.features hideContextMenu=true - canCreate=true - showActions=true + canCreate=@root.editable + showActions=@root.editable }} \ No newline at end of file diff --git a/templates/sheets/actors/character/effects.hbs b/templates/sheets/actors/character/effects.hbs index 3355d575..a2e5a420 100644 --- a/templates/sheets/actors/character/effects.hbs +++ b/templates/sheets/actors/character/effects.hbs @@ -7,7 +7,7 @@ type='effect' isGlassy=true collection=effects.actives - canCreate=true + canCreate=@root.editable hideResources=true }} @@ -16,7 +16,7 @@ type='effect' isGlassy=true collection=effects.inactives - canCreate=true + canCreate=@root.editable hideResources=true disabled=true }} diff --git a/templates/sheets/actors/character/features.hbs b/templates/sheets/actors/character/features.hbs index 3e942468..70544683 100644 --- a/templates/sheets/actors/character/features.hbs +++ b/templates/sheets/actors/character/features.hbs @@ -8,8 +8,8 @@ type='feature' actorType='character' collection=category.values - canCreate=true - showActions=true + canCreate=@root.editable + showActions=@root.editable }} {{else if category.values}} {{> 'daggerheart.inventory-items' @@ -18,7 +18,7 @@ actorType='character' collection=category.values canCreate=false - showActions=true + showActions=@root.editable }} {{/if}} diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 4ceba54d..a75b2c2f 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -4,22 +4,24 @@

{{source.name}}

- {{#if document.system.needsCharacterSetup}} - - {{else if document.system.levelData.canLevelUp}} - + {{#if @root.editable}} + {{#if document.system.needsCharacterSetup}} + + {{else if document.system.levelData.canLevelUp}} + + {{/if}} {{/if}} {{#unless document.system.needsCharacterSetup}} {{localize 'DAGGERHEART.GENERAL.level'}} @@ -110,12 +112,14 @@ {{/if}} - - + {{#if @root.editable}} + + + {{/if}}

diff --git a/templates/sheets/actors/character/inventory.hbs b/templates/sheets/actors/character/inventory.hbs index 30812e17..c3ddb0ad 100644 --- a/templates/sheets/actors/character/inventory.hbs +++ b/templates/sheets/actors/character/inventory.hbs @@ -22,7 +22,7 @@ type='weapon' collection=@root.inventory.weapons isGlassy=true - canCreate=true + canCreate=@root.editable hideResources=true }} {{> 'daggerheart.inventory-items' @@ -30,7 +30,7 @@ type='armor' collection=@root.inventory.armor isGlassy=true - canCreate=true + canCreate=@root.editable hideResources=true }} {{> 'daggerheart.inventory-items' @@ -38,7 +38,7 @@ type='consumable' collection=@root.inventory.consumables isGlassy=true - canCreate=true + canCreate=@root.editable isQuantifiable=true }} {{> 'daggerheart.inventory-items' @@ -46,8 +46,8 @@ type='loot' collection=@root.inventory.loot isGlassy=true - canCreate=true - showActions=true + canCreate=@root.editable + showActions=@root.editable isQuantifiable=true }} diff --git a/templates/sheets/actors/character/loadout.hbs b/templates/sheets/actors/character/loadout.hbs index 5e4c9f54..9ba3fb04 100644 --- a/templates/sheets/actors/character/loadout.hbs +++ b/templates/sheets/actors/character/loadout.hbs @@ -27,7 +27,7 @@ isGlassy=true cardView=cardView collection=document.system.domainCards.loadout - canCreate=true + canCreate=@root.editable }} {{> 'daggerheart.inventory-items' title='DAGGERHEART.GENERAL.Tabs.vault' @@ -35,7 +35,7 @@ isGlassy=true cardView=cardView collection=document.system.domainCards.vault - canCreate=true + canCreate=@root.editable inVault=true }} diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index d3be4983..0142ac1d 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -45,11 +45,11 @@ {{/times}} - + {{localize "DAGGERHEART.GENERAL.armorSlots"}}
{{document.system.armorScore.value}} / {{document.system.armorScore.max}} - + {{#if @root.editable}}{{/if}}
@@ -64,9 +64,9 @@ value='{{document.system.armorScore.value}}' max='{{document.system.armorScore.max}}' > - +

{{localize "DAGGERHEART.GENERAL.armorSlots"}}

- + {{#if @root.editable}}{{/if}}
{{/if}} diff --git a/templates/sheets/actors/companion/effects.hbs b/templates/sheets/actors/companion/effects.hbs index cefb6e57..087e8b30 100644 --- a/templates/sheets/actors/companion/effects.hbs +++ b/templates/sheets/actors/companion/effects.hbs @@ -6,7 +6,7 @@ type='effect' isGlassy=true collection=effects.actives - canCreate=true + canCreate=@root.editable hideResources=true }} @@ -15,7 +15,7 @@ type='effect' isGlassy=true collection=effects.inactives - canCreate=true + canCreate=@root.editable hideResources=true }} diff --git a/templates/sheets/actors/environment/features.hbs b/templates/sheets/actors/environment/features.hbs index 3ad36023..3fd512da 100644 --- a/templates/sheets/actors/environment/features.hbs +++ b/templates/sheets/actors/environment/features.hbs @@ -9,8 +9,8 @@ type='feature' collection=@root.features hideContextMenu=true - canCreate=true - showActions=true + canCreate=@root.editable + showActions=@root.editable }} \ No newline at end of file diff --git a/templates/sheets/actors/party/inventory.hbs b/templates/sheets/actors/party/inventory.hbs index 74492c73..cf056608 100644 --- a/templates/sheets/actors/party/inventory.hbs +++ b/templates/sheets/actors/party/inventory.hbs @@ -26,7 +26,7 @@ actorType='party' collection=@root.inventory.weapons isGlassy=true - canCreate=true + canCreate=@root.editable hideResources=true hideContextMenu=true isQuantifiable=true @@ -37,7 +37,7 @@ actorType='party' collection=@root.inventory.armor isGlassy=true - canCreate=true + canCreate=@root.editable hideResources=true hideContextMenu=true isQuantifiable=true @@ -48,7 +48,7 @@ actorType='party' collection=@root.inventory.consumables isGlassy=true - canCreate=true + canCreate=@root.editable hideContextMenu=true isQuantifiable=true }} @@ -58,7 +58,7 @@ actorType='party' collection=@root.inventory.loot isGlassy=true - canCreate=true + canCreate=@root.editable hideContextMenu=true isQuantifiable=true }} diff --git a/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs index 5c6eae32..3f58b80b 100644 --- a/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs +++ b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs @@ -52,12 +52,11 @@ Parameters: {{else}}