From 23ebe6f6b52999a357a5005fbf8740078698f58c Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 17 Jul 2026 23:55:05 -0400 Subject: [PATCH] Fix adversary damage --- .../sheets-configs/action-base-config.mjs | 73 ++++++-------- .../sheets-configs/adversary-settings.mjs | 35 ++++++- module/data/action/attackAction.mjs | 2 +- module/data/item/weapon.mjs | 4 +- templates/actionTypes/damage.hbs | 97 ++++++++++--------- .../adversary-settings/attack.hbs | 2 +- 6 files changed, 114 insertions(+), 99 deletions(-) diff --git a/module/applications/sheets-configs/action-base-config.mjs b/module/applications/sheets-configs/action-base-config.mjs index 46e75ba7..920cb6d0 100644 --- a/module/applications/sheets-configs/action-base-config.mjs +++ b/module/applications/sheets-configs/action-base-config.mjs @@ -159,7 +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 = !this.#getUnusedDamageTypes().length; + const allKeys = Object.keys(CONFIG.DH.GENERAL.healingTypes); + context.allDamageTypesUsed = allKeys.every(k => k in this.action._source.damage.resources); if (this.action.damage?.main?.hasOwnProperty('includeBase') && this.action.type === 'attack') context.hasBaseDamage = !!this.action.parent.attack; } @@ -232,23 +233,6 @@ 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); @@ -317,6 +301,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } + /** @this DHActionBaseConfig */ static #onAddDamage() { if (!this.action.damage || this.action.damage?.main) return; @@ -329,6 +314,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } + /** @this DHActionBaseConfig */ static #onRemoveDamage() { if (!this.action.damage?.main) return; const data = this.action.toObject(); @@ -340,50 +326,46 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) static #onAddDamageResource(_event) { if (!this.action.damage) return; - const choices = this.#getUnusedDamageTypes(); + const allKeys = Object.keys(CONFIG.DH.GENERAL.healingTypes); + const unused = allKeys.filter(k => !(k in this.action._source.damage.resources)); + const choices = unused.map(k => ({ value: k, label: _loc(CONFIG.DH.GENERAL.healingTypes[k].label) })); const content = new foundry.data.fields.StringField({ - label: game.i18n.localize('Damage Type'), + label: _loc('DAGGERHEART.GENERAL.damageType'), choices, required: true - }).toFormGroup( - {}, - { - name: 'type', - localize: true, - nameAttr: 'value', - labelAttr: 'label' - } - ).outerHTML; + }).toFormGroup({}, { + name: 'type', + localize: true, + nameAttr: 'value', + labelAttr: 'label' + }).outerHTML; 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.resources.element.getInitialValue(); - part.applyTo = type; - if (type === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) - part.type = this.action.schema.fields.damage.fields.resources.element.fields.type.element.initial; - - data.damage.resources[type] = part; + data.damage.resources[type] = { + ...this.action.schema.fields.damage.fields.resources.element.getInitialValue(), + applyTo: type + }; this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); }; const typeDialog = new foundry.applications.api.DialogV2({ buttons: [ - foundry.utils.mergeObject( - { - action: 'ok', - label: 'Confirm', - icon: 'fas fa-check', - default: true - }, - { callback: callback } - ) + { + action: 'ok', + label: 'Confirm', + icon: 'fas fa-check', + default: true, + callback + } ], content: content, rejectClose: false, modal: false, window: { - title: game.i18n.localize('Add Damage') + /** @todo localize */ + title: 'Add Damage' }, position: { width: 300 } }); @@ -391,6 +373,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) typeDialog.render(true); } + /** @this DHActionBaseConfig */ static #onRemoveDamageResource(_event, button) { if (!this.action.damage?.resources) return; const data = this.action.toObject(); diff --git a/module/applications/sheets-configs/adversary-settings.mjs b/module/applications/sheets-configs/adversary-settings.mjs index ff3f3039..c5f036ed 100644 --- a/module/applications/sheets-configs/adversary-settings.mjs +++ b/module/applications/sheets-configs/adversary-settings.mjs @@ -1,3 +1,4 @@ +import { DHDamageData } from '../../data/fields/action/damageField.mjs'; import DHBaseActorSettings from '../sheets/api/actor-setting.mjs'; /**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ @@ -8,8 +9,10 @@ export default class DHAdversarySettings extends DHBaseActorSettings { classes: ['adversary-settings'], position: { width: 455, height: 'auto' }, actions: { - addExperience: DHAdversarySettings.#addExperience, - removeExperience: DHAdversarySettings.#removeExperience + addExperience: DHAdversarySettings.#onAddExperience, + removeExperience: DHAdversarySettings.#onRemoveExperience, + addDamage: this.#onAddDamage, + removeDamage: this.#onRemoveDamage } }; @@ -71,7 +74,7 @@ export default class DHAdversarySettings extends DHBaseActorSettings { * Adds a new experience entry to the actor. * @type {ApplicationClickAction} */ - static async #addExperience() { + static async #onAddExperience() { const newExperience = { name: 'Experience', modifier: 0 @@ -83,7 +86,7 @@ export default class DHAdversarySettings extends DHBaseActorSettings { * Removes an experience entry from the actor. * @type {ApplicationClickAction} */ - static async #removeExperience(_, target) { + static async #onRemoveExperience(_, target) { const experience = this.actor.system.experiences[target.dataset.experience]; const confirmed = await foundry.applications.api.DialogV2.confirm({ window: { @@ -98,4 +101,28 @@ export default class DHAdversarySettings extends DHBaseActorSettings { await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del }); } + + /** + * @this DHAdversarySettings + * @type {ApplicationClickAction} + */ + static #onAddDamage() { + this.actor.update({ + 'system.attack.damage.main': { + ...DHDamageData.schema.getInitialValue(), + applyTo: 'hitPoints', + type: 'physical' + } + }); + } + + /** + * @this DHAdversarySettings + * @type {ApplicationClickAction} + */ + static #onRemoveDamage() { + this.actor.update({ + 'system.attack.damage.main': null + }); + } } diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs index a931f040..6c205de6 100644 --- a/module/data/action/attackAction.mjs +++ b/module/data/action/attackAction.mjs @@ -82,7 +82,7 @@ export default class DHAttackAction extends DHDamageAction { x: game.i18n.localize('DAGGERHEART.GENERAL.damage') }); - const icons = Array.from(type) + const icons = Array.from(type ?? []) .map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon) .filter(Boolean); diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 4a272b76..499b16c0 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -232,7 +232,7 @@ export default class DHWeapon extends AttachableItem { const parts = value.custom.enabled ? [game.i18n.localize('DAGGERHEART.GENERAL.custom')] : [value.dice]; if (!value.custom.enabled && value.bonus) parts.push(value.bonus.signedString()); - if (type.size > 0) { + if (type?.size) { const typeTags = Array.from(type) .map(t => game.i18n.localize(`DAGGERHEART.CONFIG.DamageType.${t}.abbreviation`)) .join(' | '); @@ -259,7 +259,7 @@ export default class DHWeapon extends AttachableItem { for (const { value, type } of [damage.main, ...damage.resources].filter(d => !!d)) { const str = Roll.replaceFormulaData(value.getFormula(), this.actor?.getRollData() ?? {}); - const icons = Array.from(type) + const icons = Array.from(type ?? []) .map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon) .filter(Boolean); diff --git a/templates/actionTypes/damage.hbs b/templates/actionTypes/damage.hbs index 88786850..f315f8a1 100644 --- a/templates/actionTypes/damage.hbs +++ b/templates/actionTypes/damage.hbs @@ -24,47 +24,53 @@ {{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")}} + {{> damageData damage=source.main fields=fields.main.fields basePath=(concat path "damage.main")}} + {{#if horde}} + {{> hordeDamage source=source.main fields=fields.main.fields basePath=(concat path "damage.main")}} + {{/if}} {{#if (ne @root.source.type 'healing')}} {{formField fields.main.fields.type value=source.main.type name=(concat path "damage.main.type") localize=true}} {{/if}} {{/if}} -
- - {{localize "DAGGERHEART.GENERAL.Resource.plural"}} - {{#unless (eq path 'system.attack.')}}{{/unless}} - - {{#each source.resources as |dmg key|}} -
- - - {{localize (concat "DAGGERHEART.CONFIG.HealingType." dmg.applyTo ".name")}} - {{#unless (or dmg.base ../path)}} - - {{/unless}} - - {{> damageData data=dmg fields=../fields.resources.element.fields basePath=(concat path "damage.resources." dmg.applyTo)}} -
- - {{/each}} - +{{#unless (eq path 'system.attack.')}} + {{! In the future, consider allowing this even on NPCs}} +
+ + {{localize "DAGGERHEART.GENERAL.Resource.plural"}} + {{#unless @root.allDamageTypesUsed}}{{/unless}} + + {{#each source.resources as |dmg key|}} +
+ + + {{localize (concat "DAGGERHEART.CONFIG.HealingType." dmg.applyTo ".name")}} + {{#unless (or dmg.base ../path)}} + + {{/unless}} + + {{> damageData damage=dmg fields=../fields.resources.element.fields basePath=(concat ../path "damage.resources." dmg.applyTo)}} +
+ + {{/each}} + +{{/unless}} {{#*inline "formula"}} - {{#unless dmg.base}} - {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat basePath "." target ".custom.enabled") classes="checkbox" localize=true}} + {{#unless isBase}} + {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat basePath ".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}} + {{formField fields.custom.fields.formula value=source.custom.formula name=(concat basePath ".custom.formula") localize=true}} {{else}}
{{#unless @root.isNPC}} - {{formField fields.multiplier value=source.multiplier name=(concat basePath "." target ".multiplier") localize=true}} + {{formField fields.multiplier value=source.multiplier name=(concat basePath ".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 (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat basePath ".flatMultiplier") localize=true }}{{/if}} + {{formField fields.dice value=source.dice name=(concat basePath ".dice") localize=true}} + {{formField fields.bonus value=source.bonus name=(concat basePath ".bonus") localize=true}}
{{/if}} {{#if @root.isNPC}} @@ -73,35 +79,34 @@ {{/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 (and (not @root.isNPC) @root.hasRoll (not damage.base))}} + {{formField fields.resultBased value=damage.resultBased name=(concat basePath ".resultBased") localize=true classes="checkbox"}} {{/if}} - {{#if (and (not @root.isNPC) @root.hasRoll (not data.base) data.resultBased)}} + {{#if (and (not @root.isNPC) @root.hasRoll (not damage.base) damage.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}} + {{> formula key=damage.applyTo fields=fields.value.fields type=fields.type isBase=damage.base source=damage.value basePath=(concat basePath ".value")}}
{{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}} + {{> formula key=damage.applyTo fields=fields.valueAlt.fields type=fields.type isBase=damage.base source=damage.valueAlt basePath=(concat basePath ".valueAlt")}}
{{else}} - {{> formula fields=fields.value.fields type=fields.type dmg=data source=data.value basePath=basePath target="value" key=data.applyTo path=../path}} + {{> formula key=damage.applyTo fields=fields.value.fields type=fields.type isBase=damage.base source=damage.value basePath=(concat basePath ".value")}} {{/if}} + +{{/inline}} - {{#if ../horde}} -
- {{localize "DAGGERHEART.ACTORS.Adversary.hordeDamage"}} -
- - - {{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}} - +{{#*inline "hordeDamage"}} +
+ {{localize "DAGGERHEART.ACTORS.Adversary.hordeDamage"}} +
+ + {{formField fields.valueAlt.fields.flatMultiplier value=source.valueAlt.flatMultiplier name=(concat basePath ".valueAlt.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }} + {{formField fields.valueAlt.fields.dice value=source.valueAlt.dice name=(concat basePath ".valueAlt.dice") classes="inline-child" localize=true}} + {{formField fields.valueAlt.fields.bonus value=source.valueAlt.bonus name=(concat basePath ".valueAlt.bonus") localize=true classes="inline-child"}} +
+
{{/inline}} \ No newline at end of file diff --git a/templates/sheets-settings/adversary-settings/attack.hbs b/templates/sheets-settings/adversary-settings/attack.hbs index 41960032..f81f0477 100644 --- a/templates/sheets-settings/adversary-settings/attack.hbs +++ b/templates/sheets-settings/adversary-settings/attack.hbs @@ -22,5 +22,5 @@ {{formGroup systemFields.criticalThreshold value=document._source.system.criticalThreshold label="DAGGERHEART.ACTIONS.Settings.criticalThreshold" name="system.criticalThreshold" localize=true}} - {{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=document.system.attack.damage path="system.attack." baseFields=systemFields.attack.fields.damage.fields horde=(eq document._source.system.type 'horde')}} + {{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields source=document.system.attack.damage path="system.attack." baseFields=systemFields.attack.fields.damage.fields horde=(eq document._source.system.type 'horde')}} \ No newline at end of file