From e9e8d2bd59e156ce1d9db25ff102ca46e9f7071c Mon Sep 17 00:00:00 2001 From: Gergely Brautigam <182850+Skarlso@users.noreply.github.com> Date: Sat, 4 Jul 2026 12:25:16 +0200 Subject: [PATCH 1/2] feat: add resource automatic cost modification on attack or action Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com> --- lang/en.json | 1 + .../applications/sheets-configs/action-base-config.mjs | 2 +- module/applications/sheets/items/weapon.mjs | 10 ++++++++++ ...journal_Welcome___Information_g7NhKvwltwafmMyR.json | 2 +- styles/less/sheets/items/index.less | 3 ++- styles/less/sheets/items/weapon.less | 6 ++++++ templates/sheets/items/weapon/settings.hbs | 5 ++++- 7 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 styles/less/sheets/items/weapon.less diff --git a/lang/en.json b/lang/en.json index b9a81b29..41d2cabd 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2654,6 +2654,7 @@ }, "Weapon": { "weaponType": "Weapon Type", + "configureAttack": "Configure Attack", "primaryWeapon": { "full": "Primary Weapon", "short": "Primary" diff --git a/module/applications/sheets-configs/action-base-config.mjs b/module/applications/sheets-configs/action-base-config.mjs index b65e1cdf..9f5c89c5 100644 --- a/module/applications/sheets-configs/action-base-config.mjs +++ b/module/applications/sheets-configs/action-base-config.mjs @@ -160,7 +160,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) context.allDamageTypesUsed = !getUnusedDamageTypes(this.action.damage.parts).length; if (this.action.damage.hasOwnProperty('includeBase') && this.action.type === 'attack') - context.hasBaseDamage = !!this.action.parent.attack; + context.hasBaseDamage = !!this.action.parent.attack && !this.action.baseAction; } context.costOptions = this.getCostOptions(); diff --git a/module/applications/sheets/items/weapon.mjs b/module/applications/sheets/items/weapon.mjs index f5c7dddf..e74a44db 100644 --- a/module/applications/sheets/items/weapon.mjs +++ b/module/applications/sheets/items/weapon.mjs @@ -5,6 +5,9 @@ export default class WeaponSheet extends ItemAttachmentSheet(DHBaseItemSheet) { /**@inheritdoc */ static DEFAULT_OPTIONS = { classes: ['weapon'], + actions: { + configureAttack: WeaponSheet.#configureAttack + }, tagifyConfigs: [ { selector: '.features-input', @@ -46,6 +49,13 @@ export default class WeaponSheet extends ItemAttachmentSheet(DHBaseItemSheet) { return context; } + /** + * Open the action configuration sheet for the weapon's base attack. + */ + static #configureAttack() { + this.document.system.attack.sheet.render({ force: true }); + } + /** * Callback function used by `tagifyElement`. * @param {Array} selectedOptions - The currently selected tag objects. diff --git a/src/packs/journals/journal_Welcome___Information_g7NhKvwltwafmMyR.json b/src/packs/journals/journal_Welcome___Information_g7NhKvwltwafmMyR.json index c01157c2..b87dd745 100644 --- a/src/packs/journals/journal_Welcome___Information_g7NhKvwltwafmMyR.json +++ b/src/packs/journals/journal_Welcome___Information_g7NhKvwltwafmMyR.json @@ -132,7 +132,7 @@ "image": {}, "text": { "format": 1, - "content": "

This product includes materials from the Daggerheart System Reference Document 1.0, © Critical Role, LLC. under the terms of the Darrington Press Community Gaming (DPCGL) License. More information can be found at https://www.daggerheart.com. There are no previous modifications by others.

The Foundryborne Team

The Foundryborne Team consists of:

With Art from:

And special thanks to our hard working community testers:

We would also like to thank the FoundryVTT team for their support in publishing this system, as well as providing countless Icons to be used in many places.

And, of course, special thanks to the teams at Critical Role and Darrington Press for making such a wonderful game and updating the license to allow a FoundryVTT version of the system.

The Foundryborne Community

Without our amazing community this project would not have been possible.

You kept us going with both direct contributions and just endless support!

We thank you with all our hearts.

Come join us!

" + "content": "

This product includes materials from the Daggerheart System Reference Document 1.0, © Critical Role, LLC. under the terms of the Darrington Press Community Gaming (DPCGL) License. More information can be found at https://www.daggerheart.com. There are no previous modifications by others.

The Foundryborne Team

The Foundryborne Team consists of:

With Art from:

And special thanks to our hard working community testers:

We would also like to thank the FoundryVTT team for their support in publishing this system, as well as providing countless Icons to be used in many places.

And, of course, special thanks to the teams at Critical Role and Darrington Press for making such a wonderful game and updating the license to allow a FoundryVTT version of the system.

The Foundryborne Community

Without our amazing community this project would not have been possible.

You kept us going with both direct contributions and just endless support!

We thank you with all our hearts.

Come join us!

" }, "video": { "controls": true, diff --git a/styles/less/sheets/items/index.less b/styles/less/sheets/items/index.less index 7c40a2e3..3122cb68 100644 --- a/styles/less/sheets/items/index.less +++ b/styles/less/sheets/items/index.less @@ -3,4 +3,5 @@ @import './domain-card.less'; @import './feature.less'; @import './heritage.less'; -@import './item-sheet-shared.less'; \ No newline at end of file +@import './item-sheet-shared.less'; +@import './weapon.less'; \ No newline at end of file diff --git a/styles/less/sheets/items/weapon.less b/styles/less/sheets/items/weapon.less new file mode 100644 index 00000000..5a00a393 --- /dev/null +++ b/styles/less/sheets/items/weapon.less @@ -0,0 +1,6 @@ +.application.sheet.daggerheart.dh-style.weapon { + section.tab { + height: 400px; + overflow-y: auto; + } +} diff --git a/templates/sheets/items/weapon/settings.hbs b/templates/sheets/items/weapon/settings.hbs index c9805e7c..3ac6da87 100644 --- a/templates/sheets/items/weapon/settings.hbs +++ b/templates/sheets/items/weapon/settings.hbs @@ -4,7 +4,10 @@ data-group='{{tabs.settings.group}}' >
- {{localize tabs.settings.label}} + + {{localize tabs.settings.label}} + + {{localize "DAGGERHEART.GENERAL.Tiers.singular"}} {{formInput systemFields.tier value=source.system.tier}} {{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full"}} From 9e4d8d637a377b2ca6e8873452078858d5ad1aa1 Mon Sep 17 00:00:00 2001 From: Gergely Brautigam <182850+Skarlso@users.noreply.github.com> Date: Tue, 14 Jul 2026 20:24:19 +0200 Subject: [PATCH 2/2] add reloading feature Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com> --- lang/en.json | 11 +++++++ module/config/generalConfig.mjs | 5 +++ module/data/fields/action/costField.mjs | 22 +++++++++++-- module/data/fields/action/damageField.mjs | 10 ++++-- module/documents/actor.mjs | 38 +++++++++++++++++------ templates/actionTypes/damage.hbs | 5 +++ templates/ui/chat/damageSummary.hbs | 10 +++++- templates/ui/chat/parts/damage-part.hbs | 6 ++-- 8 files changed, 91 insertions(+), 16 deletions(-) diff --git a/lang/en.json b/lang/en.json index 41d2cabd..ef8ba2e3 100755 --- a/lang/en.json +++ b/lang/en.json @@ -144,6 +144,10 @@ "resultBased": { "label": "Formula based on Hope/Fear result." }, + "fullRestore": { + "label": "Fully restore the resource.", + "inChatRoll": "Full" + }, "applyTo": { "label": "Targeted Resource" }, @@ -1279,6 +1283,11 @@ "name": "Fear", "abbreviation": "FR", "inChatRoll": "Fear" + }, + "weaponResource": { + "name": "Weapon Resource", + "abbreviation": "WR", + "inChatRoll": "Reload" } }, "ItemResourceProgression": { @@ -3090,6 +3099,8 @@ "applyHealing": "Apply Healing" }, "markResource": "Mark {quantity} {resource}", + "reloadWeapon": "Reload {weapon}", + "restoreWeaponResource": "Restore {quantity} on {weapon}", "refreshMessage": { "title": "Feature Refresh", "header": "Refreshed" diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 188efafb..c31313de 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -191,6 +191,11 @@ export const healingTypes = { id: 'fear', label: 'DAGGERHEART.CONFIG.HealingType.fear.name', abbreviation: 'DAGGERHEART.CONFIG.HealingType.fear.abbreviation' + }, + weaponResource: { + id: 'weaponResource', + label: 'DAGGERHEART.CONFIG.HealingType.weaponResource.name', + abbreviation: 'DAGGERHEART.CONFIG.HealingType.weaponResource.abbreviation' } }; diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index 82cfcd23..96032cb4 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -197,11 +197,29 @@ export default class CostField extends fields.ArrayField { static getItemIdCostUpdate(r) { switch (r.key) { - case CONFIG.DH.GENERAL.itemAbilityCosts.resource.id: + case CONFIG.DH.GENERAL.itemAbilityCosts.resource.id: { + const resource = r.target.system.resource; + let max = Number(resource.max); + if (!Number.isFinite(max) && resource.max) { + try { + max = Roll.safeEval(Roll.replaceFormulaData(resource.max, r.target.getRollData())); + } catch { + max = null; + } + } + const hasMax = Number.isFinite(max) && max > 0; + if (r.clear) { + return { + path: 'system.resource.value', + value: hasMax ? max : resource.value + }; + } + const newValue = resource.value + r.value; return { path: 'system.resource.value', - value: r.target.system.resource.value + r.value + value: Math.max(hasMax ? Math.min(newValue, max) : newValue, 0) }; + } case CONFIG.DH.GENERAL.itemAbilityCosts.quantity.id: return { path: 'system.quantity', diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 9b21d3ba..be4ec9b2 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -45,9 +45,10 @@ export default class DamageField extends fields.SchemaField { return; let formulas = this.damage.parts.map(p => ({ - formula: DamageField.getFormulaValue.call(this, p, config).getFormula(this.actor), + formula: p.fullRestore ? '0' : DamageField.getFormulaValue.call(this, p, config).getFormula(this.actor), damageTypes: p.applyTo === 'hitPoints' && !p.type.size ? new Set(['physical']) : p.type, - applyTo: p.applyTo + applyTo: p.applyTo, + fullRestore: p.fullRestore })); if (!formulas.length) return false; @@ -67,6 +68,7 @@ export default class DamageField extends fields.SchemaField { if (DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) damageConfig.dialog.configure = false; + if (formulas.every(f => f.fullRestore)) damageConfig.dialog.configure = false; if (config.hasSave) config.onSave = damageConfig.onSave = this.save.damageMod; damageConfig.source.message = messageId; @@ -297,6 +299,10 @@ export class DHResourceData extends foundry.abstract.DataModel { initial: false, label: 'DAGGERHEART.ACTIONS.Settings.resultBased.label' }), + fullRestore: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.ACTIONS.Settings.fullRestore.label' + }), value: new fields.EmbeddedDataField(DHActionDiceData), valueAlt: new fields.EmbeddedDataField(DHActionDiceData) }; diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 30901374..def7d5cd 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -766,17 +766,21 @@ export default class DhpActor extends Actor { Object.entries(healings).forEach(([key, healing]) => { healing.parts.forEach(part => { const update = updates.find(u => u.key === key); - if (update) update.value += part.total; - else updates.push({ value: part.total, key }); + if (update) { + update.value += part.total; + update.clear ||= !!part.fullRestore; + } else updates.push({ value: part.total, key, clear: !!part.fullRestore }); }); }); - updates.forEach( - u => - (u.value = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false) - ? u.value * -1 - : u.value) - ); + updates.forEach(u => { + if (u.key === CONFIG.DH.GENERAL.healingTypes.weaponResource.id) return; + u.value = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false) + ? u.value * -1 + : u.value; + }); + + this.convertResourceHealingToReload(updates); await this.modifyResource(updates); @@ -802,7 +806,7 @@ export default class DhpActor extends Actor { resources.forEach(r => { if (r.itemId) { const { path, value } = game.system.api.fields.ActionFields.CostField.getItemIdCostUpdate(r); - updates.items[r.key] = { + updates.items[`${r.itemId}-${r.key}`] = { target: r.target, resources: { [path]: value } }; @@ -873,6 +877,22 @@ export default class DhpActor extends Actor { return damage >= this.system.damageThresholds.severe ? 3 : damage >= this.system.damageThresholds.major ? 2 : 1; } + convertResourceHealingToReload(updates) { + const resourceIndex = updates.findIndex(u => u.key === CONFIG.DH.GENERAL.healingTypes.weaponResource.id); + if (resourceIndex === -1) return; + const [reload] = updates.splice(resourceIndex, 1); + const weapons = this.items.filter(i => i.type === 'weapon' && i.system.equipped && i.system.resource); + for (const weapon of weapons) { + updates.push({ + key: CONFIG.DH.GENERAL.itemAbilityCosts.resource.id, + value: reload.value, + clear: reload.clear, + itemId: weapon.id, + target: weapon + }); + } + } + convertStressDamageToHP(resources) { const stressDamage = resources.find(r => r.key === 'stress'), newValue = this.system.resources.stress.value + stressDamage.value; diff --git a/templates/actionTypes/damage.hbs b/templates/actionTypes/damage.hbs index 03300840..cbdf2278 100644 --- a/templates/actionTypes/damage.hbs +++ b/templates/actionTypes/damage.hbs @@ -31,6 +31,10 @@ {{/unless}} + {{#if (eq dmg.applyTo 'weaponResource')}} + {{formField ../fields.fullRestore value=dmg.fullRestore name=(concat ../path "damage.parts." dmg.applyTo ".fullRestore") localize=true classes="checkbox"}} + {{/if}} + {{#unless dmg.fullRestore}} {{#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}} @@ -48,6 +52,7 @@ {{else}} {{> formula fields=../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" key=dmg.applyTo path=../path}} {{/if}} + {{/unless}} {{#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}} diff --git a/templates/ui/chat/damageSummary.hbs b/templates/ui/chat/damageSummary.hbs index 721f153f..a3458185 100644 --- a/templates/ui/chat/damageSummary.hbs +++ b/templates/ui/chat/damageSummary.hbs @@ -8,7 +8,15 @@