From ed30fd21225841cd067f1ad67c41415cbd92c581 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 17 Jul 2026 20:52:00 -0400 Subject: [PATCH] Fix most issues with damage effects tab --- .../sheets-configs/action-base-config.mjs | 69 ++++++-- module/data/fields/action/damageField.mjs | 5 +- module/helpers/utils.mjs | 13 -- templates/actionTypes/damage.hbs | 150 ++++++++++-------- .../action-settings/effect.hbs | 2 +- 5 files changed, 140 insertions(+), 99 deletions(-) diff --git a/module/applications/sheets-configs/action-base-config.mjs b/module/applications/sheets-configs/action-base-config.mjs index b65e1cdf..46e75ba7 100644 --- a/module/applications/sheets-configs/action-base-config.mjs +++ b/module/applications/sheets-configs/action-base-config.mjs @@ -1,4 +1,4 @@ -import { getUnusedDamageTypes } from '../../helpers/utils.mjs'; +import { DHDamageData } from '../../data/fields/action/damageField.mjs'; import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs'; const { ApplicationV2 } = foundry.applications.api; @@ -31,8 +31,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) removeElement: this.removeElement, removeTransformActor: this.removeTransformActor, editEffect: this.editEffect, - addDamage: this.addDamage, - removeDamage: this.removeDamage, + addDamage: this.#onAddDamage, + removeDamage: this.#onRemoveDamage, + addDamageResource: this.#onAddDamageResource, + removeDamageResource: this.#onRemoveDamageResource, editDoc: this.editDoc, addTrigger: this.addTrigger, removeTrigger: this.removeTrigger, @@ -157,9 +159,8 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) context.tabs = this._getTabs(this.constructor.TABS); context.config = CONFIG.DH; if (this.action.damage) { - context.allDamageTypesUsed = !getUnusedDamageTypes(this.action.damage.parts).length; - - if (this.action.damage.hasOwnProperty('includeBase') && this.action.type === 'attack') + context.allDamageTypesUsed = !this.#getUnusedDamageTypes().length; + if (this.action.damage?.main?.hasOwnProperty('includeBase') && this.action.type === 'attack') context.hasBaseDamage = !!this.action.parent.attack; } @@ -231,6 +232,23 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) return filtered; } + /** + * Gets unused resource types of the damage field + * @returns {{ value: string; label: string }[]} + */ + #getUnusedDamageTypes() { + const usedKeys = Object.keys(this.action._source.damage.resources); + return Object.keys(CONFIG.DH.GENERAL.healingTypes).reduce((acc, key) => { + if (!usedKeys.includes(key)) + acc.push({ + value: key, + label: game.i18n.localize(CONFIG.DH.GENERAL.healingTypes[key].label) + }); + + return acc; + }, []); + } + _prepareSubmitData(_event, formData) { const submitData = foundry.utils.expandObject(formData.object); @@ -299,10 +317,30 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } - static addDamage(_event) { - if (!this.action.damage.parts) return; + static #onAddDamage() { + if (!this.action.damage || this.action.damage?.main) return; - const choices = getUnusedDamageTypes(this.action._source.damage.parts); + const data = this.action.toObject(); + data.damage.main = { + ...DHDamageData.schema.getInitialValue(), + applyTo: 'hitPoints', + type: 'physical' + }; + this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); + } + + static #onRemoveDamage() { + if (!this.action.damage?.main) return; + const data = this.action.toObject(); + data.damage.main = null; + this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); + } + + /** @this DHActionBaseConfig */ + static #onAddDamageResource(_event) { + if (!this.action.damage) return; + + const choices = this.#getUnusedDamageTypes(); const content = new foundry.data.fields.StringField({ label: game.i18n.localize('Damage Type'), choices, @@ -320,12 +358,12 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) const callback = (_, button) => { const data = this.action.toObject(); const type = choices[button.form.elements.type.value].value; - const part = this.action.schema.fields.damage.fields.parts.element.getInitialValue(); + const part = this.action.schema.fields.damage.fields.resources.element.getInitialValue(); part.applyTo = type; if (type === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) - part.type = this.action.schema.fields.damage.fields.parts.element.fields.type.element.initial; + part.type = this.action.schema.fields.damage.fields.resources.element.fields.type.element.initial; - data.damage.parts[type] = part; + data.damage.resources[type] = part; this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); }; @@ -353,12 +391,11 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) typeDialog.render(true); } - static removeDamage(_event, button) { - if (!this.action.damage.parts) return; + static #onRemoveDamageResource(_event, button) { + if (!this.action.damage?.resources) return; const data = this.action.toObject(); const key = button.dataset.key; - delete data.damage.parts[key]; - data.damage.parts[`${key}`] = _del; + data.damage.resources[key] = _del; this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 87f50e4b..a425b852 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -12,11 +12,10 @@ export default class DamageField extends fields.SchemaField { /** @inheritDoc */ constructor(options, context = {}) { - const damageFields = { + super({ main: new fields.EmbeddedDataField(DHDamageData, { nullable: true }), resources: new IterableTypedObjectField(DHResourceData) - }; - super(damageFields, options, context); + }, options, context); } /** diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 84bcacf2..f600eae6 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -700,19 +700,6 @@ export async function RefreshFeatures( return refreshedActors; } -export function getUnusedDamageTypes(parts) { - const usedKeys = Object.keys(parts); - return Object.keys(CONFIG.DH.GENERAL.healingTypes).reduce((acc, key) => { - if (!usedKeys.includes(key)) - acc.push({ - value: key, - label: game.i18n.localize(CONFIG.DH.GENERAL.healingTypes[key].label) - }); - - return acc; - }, []); -} - /** Returns resolved armor sources ordered by application order */ export function getArmorSources(actor) { const rawArmorSources = Array.from(actor.allApplicableEffects()).filter(x => x.system.armorData); diff --git a/templates/actionTypes/damage.hbs b/templates/actionTypes/damage.hbs index 03300840..995f8949 100644 --- a/templates/actionTypes/damage.hbs +++ b/templates/actionTypes/damage.hbs @@ -1,4 +1,3 @@ -
{{#if (eq @root.source.type 'healing')}} @@ -6,87 +5,106 @@ {{else}} {{localize "DAGGERHEART.GENERAL.damage"}} {{/if}} - {{#unless (eq path 'system.attack.')}}{{/unless}} + {{#if source.main}} + + {{else}} + + {{/if}} + + + {{#if source.main}} +
+ {{#if @root.hasBaseDamage}} + {{formField @root.fields.damage.fields.main.fields.includeBase value=@root.source.damage.includeBase name="damage.main.includeBase" classes="checkbox" localize=true }} + {{/if}} + {{#unless (eq @root.source.type 'healing')}} + {{formField baseFields.main.fields.direct value=source.main.direct name=(concat path "damage.main.direct") localize=true classes="checkbox"}} + {{/unless}} + {{#if (and @root.isNPC (not (eq path 'system.attack.')))}} + {{formField baseFields.main.fields.groupAttack value=source.main.groupAttack name=(concat path "damage.main.groupAttack") localize=true classes="select"}} + {{/if}} +
+ {{> damageData data=source.main fields=fields.main.fields basePath=(concat path "damage.main")}} + {{/if}} +
+ +
+ + {{localize "DAGGERHEART.GENERAL.Resource.plural"}} + {{#unless (eq path 'system.attack.')}}{{/unless}} -
- {{#if @root.hasBaseDamage}} - {{formField @root.fields.damage.fields.includeBase value=@root.source.damage.includeBase name="damage.includeBase" classes="checkbox" localize=true }} - {{/if}} - {{#unless (eq @root.source.type 'healing')}} - {{formField baseFields.direct value=source.direct name=(concat path "damage.direct") localize=true classes="checkbox"}} - {{/unless}} - {{#if (and @root.isNPC (not (eq path 'system.attack.')))}} - {{formField baseFields.groupAttack value=source.groupAttack name=(concat path "damage.groupAttack") localize=true classes="select"}} - {{/if}} -
{{!-- 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 key|}} + {{#each source.resources as |dmg key|}}
{{localize (concat "DAGGERHEART.CONFIG.HealingType." dmg.applyTo ".name")}} {{#unless (or dmg.base ../path)}} - + {{/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"}} -
- - {{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}} - + {{> damageData data=dmg fields=../fields.resources.element.fields basePath=(concat path "damage.resources." dmg.applyTo)}}
{{/each}} {{#*inline "formula"}} - {{#unless dmg.base}} - {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat path "damage.parts." key "." target ".custom.enabled") classes="checkbox" localize=true}} - {{/unless}} - {{#if source.custom.enabled}} - {{formField fields.custom.fields.formula value=source.custom.formula name=(concat path "damage.parts." key "." target ".custom.formula") localize=true}} - {{else}} + {{#unless dmg.base}} + {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat basePath "." target ".custom.enabled") classes="checkbox" localize=true}} + {{/unless}} + {{#if source.custom.enabled}} + {{formField fields.custom.fields.formula value=source.custom.formula name=(concat basePath "." target ".custom.formula") localize=true}} + {{else}} +
+ {{#unless @root.isNPC}} + {{formField fields.multiplier value=source.multiplier name=(concat basePath "." target ".multiplier") localize=true}} + {{/unless}} + {{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat basePath "." target ".flatMultiplier") localize=true }}{{/if}} + {{formField fields.dice value=source.dice name=(concat basePath "." target ".dice") localize=true}} + {{formField fields.bonus value=source.bonus name=(concat basePath "." target ".bonus") localize=true}} +
+ {{/if}} + {{#if @root.isNPC}} + + {{/if}} +{{/inline}} + +{{#*inline "damageData"}} + {{#if (and (not @root.isNPC) @root.hasRoll (not data.base))}} + {{formField fields.resultBased value=data.resultBased name=(concat basePath ".resultBased") localize=true classes="checkbox"}} + {{/if}} + {{#if (and (not @root.isNPC) @root.hasRoll (not data.base) data.resultBased)}} +
+
+ {{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.hope")}} + {{> formula fields=fields.value.fields type=fields.type dmg=data source=data.value target="value" key=data.applyTo path=../path}} +
+
+ {{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.fear")}} + {{> formula fields=fields.valueAlt.fields type=fields.type dmg=data source=data.valueAlt target="valueAlt" key=data.applyTo path=../path}} +
+
+ {{else}} + {{> formula fields=fields.value.fields type=fields.type dmg=data source=data.value basePath=basePath target="value" key=data.applyTo path=../path}} + {{/if}} + + {{#if (ne @root.source.type 'healing')}} + {{formField fields.type value=data.type name=(concat basePath ".type") localize=true}} + {{/if}} + + {{#if ../horde}} +
+ {{localize "DAGGERHEART.ACTORS.Adversary.hordeDamage"}}
- {{#unless @root.isNPC}} - {{formField fields.multiplier value=source.multiplier name=(concat path "damage.parts." key "." target ".multiplier") localize=true}} - {{/unless}} - {{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat path "damage.parts." key "." target ".flatMultiplier") localize=true }}{{/if}} - {{formField fields.dice value=source.dice name=(concat path "damage.parts." key "." target ".dice") localize=true}} - {{formField fields.bonus value=source.bonus name=(concat path "damage.parts." key "." target ".bonus") localize=true}} + + + {{formField fields.valueAlt.fields.flatMultiplier value=data.valueAlt.flatMultiplier name=(concat basePath ".valueAlt.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }} + {{formField fields.valueAlt.fields.dice value=data.valueAlt.dice name=(concat basePath ".valueAlt.dice") classes="inline-child" localize=true}} + {{formField fields.valueAlt.fields.bonus value=data.valueAlt.bonus name=(concat basePath ".valueAlt.bonus") localize=true classes="inline-child"}}
- {{/if}} - {{#if @root.isNPC}} - - {{/if}} +
+ {{/if}} + {{/inline}} \ No newline at end of file diff --git a/templates/sheets-settings/action-settings/effect.hbs b/templates/sheets-settings/action-settings/effect.hbs index 567cb81c..67017a5a 100644 --- a/templates/sheets-settings/action-settings/effect.hbs +++ b/templates/sheets-settings/action-settings/effect.hbs @@ -5,7 +5,7 @@ > {{#if fields.roll}}{{> 'systems/daggerheart/templates/actionTypes/roll.hbs' fields=fields.roll.fields source=source.roll}}{{/if}} {{#if fields.save}}{{> 'systems/daggerheart/templates/actionTypes/save.hbs' fields=fields.save.fields source=source.save}}{{/if}} - {{#if fields.damage}}{{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=fields.damage.fields.parts.element.fields source=source.damage baseFields=fields.damage.fields }}{{/if}} + {{#if fields.damage}}{{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=fields.damage.fields source=source.damage baseFields=fields.damage.fields }}{{/if}} {{#if fields.macro}}{{> 'systems/daggerheart/templates/actionTypes/macro.hbs' fields=fields.macro source=source.macro}}{{/if}} {{#if fields.effects}}{{> 'systems/daggerheart/templates/actionTypes/effect.hbs' fields=fields.effects.element.fields source=source.effects}}{{/if}} {{#if fields.beastform}}{{> 'systems/daggerheart/templates/actionTypes/beastform.hbs' fields=fields.beastform.fields source=source.beastform}}{{/if}}