From c99a76f4f44b06e1c95b4539ed20080579598c19 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Wed, 25 Feb 2026 17:40:40 +0100 Subject: [PATCH] More --- .../applications/levelup/companionLevelup.mjs | 2 +- .../sheets-configs/action-base-config.mjs | 8 +++-- .../sheets/api/application-mixin.mjs | 5 +++- module/data/action/attackAction.mjs | 10 +++---- module/data/action/baseAction.mjs | 4 +-- module/data/actor/adversary.mjs | 12 +++----- module/data/actor/character.mjs | 6 ++-- module/data/actor/companion.mjs | 10 ++++--- module/data/fields/_module.mjs | 2 +- module/data/fields/action/damageField.mjs | 10 ++----- module/data/fields/collectionField.mjs | 25 ---------------- .../data/fields/iterableTypedObjectField.mjs | 30 +++++++++++++++++++ module/data/item/weapon.mjs | 6 ++-- module/helpers/utils.mjs | 9 ++++++ templates/sheets/items/weapon/header.hbs | 6 ++-- 15 files changed, 78 insertions(+), 67 deletions(-) delete mode 100644 module/data/fields/collectionField.mjs create mode 100644 module/data/fields/iterableTypedObjectField.mjs diff --git a/module/applications/levelup/companionLevelup.mjs b/module/applications/levelup/companionLevelup.mjs index 7f11ccff..d6bf2d78 100644 --- a/module/applications/levelup/companionLevelup.mjs +++ b/module/applications/levelup/companionLevelup.mjs @@ -67,7 +67,7 @@ export default class DhCompanionLevelUp extends BaseLevelUp { break; case 'summary': const levelKeys = Object.keys(this.levelup.levels); - const actorDamageDice = this.actor.system.attack.damage.parts[0].value.dice; + const actorDamageDice = this.actor.system.attack.damage.parts.hitPoints.value.dice; const actorRange = this.actor.system.attack.range; let achievementExperiences = []; diff --git a/module/applications/sheets-configs/action-base-config.mjs b/module/applications/sheets-configs/action-base-config.mjs index 34543086..b3c79ff8 100644 --- a/module/applications/sheets-configs/action-base-config.mjs +++ b/module/applications/sheets-configs/action-base-config.mjs @@ -1,3 +1,4 @@ +import { getNextUnusedDamageType } from '../../helpers/utils.mjs'; import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs'; const { ApplicationV2 } = foundry.applications.api; @@ -268,10 +269,11 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) static addDamage(_event) { if (!this.action.damage.parts) return; - const data = this.action.toObject(), - part = {}; + const data = this.action.toObject(); + const type = getNextUnusedDamageType(this.action.damage.parts); + const part = { applyTo: type }; if (this.action.actor?.isNPC) part.value = { multiplier: 'flat' }; - data.damage.parts.push(part); + data.damage.parts[type] = part; this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 49f7dcf0..407c8250 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -499,7 +499,10 @@ export default function DHApplicationMixin(Base) { icon: 'fa-solid fa-explosion', condition: target => { const doc = getDocFromElementSync(target); - return doc?.system?.attack?.damage.parts.length || doc?.damage?.parts.length; + return ( + !foundry.utils.isEmpty(doc?.system?.attack?.damage.parts) || + !foundry.utils.isEmpty(doc?.damage?.parts) + ); }, callback: async (target, event) => { const doc = await getDocFromElement(target), diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs index 60112c40..3671613d 100644 --- a/module/data/action/attackAction.mjs +++ b/module/data/action/attackAction.mjs @@ -26,23 +26,23 @@ export default class DHAttackAction extends DHDamageAction { return { value: { multiplier: 'prof', - dice: this.item?.system?.attack.damage.parts[0].value.dice, - bonus: this.item?.system?.attack.damage.parts[0].value.bonus ?? 0 + dice: this.item?.system?.attack.damage.parts.hitPoints.value.dice, + bonus: this.item?.system?.attack.damage.parts.hitPoints.value.bonus ?? 0 }, - type: this.item?.system?.attack.damage.parts[0].type, + type: this.item?.system?.attack.damage.parts.hitPoints.type, base: true }; } get damageFormula() { - const hitPointsPart = this.damage.parts.find(x => x.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id); + const hitPointsPart = this.damage.parts.hitPoints; if (!hitPointsPart) return '0'; return hitPointsPart.value.getFormula(); } get altDamageFormula() { - const hitPointsPart = this.damage.parts.find(x => x.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id); + const hitPointsPart = this.damage.parts.hitPoints; if (!hitPointsPart) return '0'; return hitPointsPart.valueAlt.getFormula(); diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 8c172701..5cc57ad7 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -352,11 +352,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel } get hasDamage() { - return this.damage?.parts?.length && this.type !== 'healing'; + return !foundry.utils.isEmpty(this.damage.parts) && this.type !== 'healing'; } get hasHealing() { - return this.damage?.parts?.length && this.type === 'healing'; + return !foundry.utils.isEmpty(this.damage.parts) && this.type === 'healing'; } get hasSave() { diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index 78964720..437e92a0 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -89,14 +89,14 @@ export default class DhpAdversary extends DhCreature { type: 'attack' }, damage: { - parts: [ - { + parts: { + hitPoints: { type: ['physical'], value: { multiplier: 'flat' } } - ] + } } } }), @@ -374,13 +374,9 @@ export default class DhpAdversary extends DhCreature { * @returns the converted formula and value as a simplified term */ #adjustActionDamage(action, damageMeta) { - // The current algorithm only returns a value if there is a single damage part - const hpDamageParts = action.damage.parts.filter(d => d.applyTo === 'hitPoints'); - if (hpDamageParts.length !== 1) throw new Error('incorrect number of hp parts'); - const result = {}; for (const property of ['value', 'valueAlt']) { - const data = hpDamageParts[0][property]; + const data = action.damage.parts.hitPoints[property]; const previousFormula = data.custom.enabled ? data.custom.formula : [data.flatMultiplier ? `${data.flatMultiplier}${data.dice}` : 0, data.bonus ?? 0] diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 1ae6016f..414a9299 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -118,8 +118,8 @@ export default class DhCharacter extends DhCreature { trait: 'strength' }, damage: { - parts: [ - { + parts: { + hitPoints: { type: ['physical'], value: { custom: { @@ -128,7 +128,7 @@ export default class DhCharacter extends DhCreature { } } } - ] + } } } }), diff --git a/module/data/actor/companion.mjs b/module/data/actor/companion.mjs index 6f51b593..32ae661d 100644 --- a/module/data/actor/companion.mjs +++ b/module/data/actor/companion.mjs @@ -85,15 +85,15 @@ export default class DhCompanion extends DhCreature { bonus: 0 }, damage: { - parts: [ - { + parts: { + hitPoints: { type: ['physical'], value: { dice: 'd6', multiplier: 'prof' } } - ] + } } } }), @@ -138,7 +138,9 @@ export default class DhCompanion extends DhCreature { break; case 'vicious': if (selection.data[0] === 'damage') { - this.attack.damage.parts[0].value.dice = adjustDice(this.attack.damage.parts[0].value.dice); + this.attack.damage.parts.hitPoints.value.dice = adjustDice( + this.attack.damage.parts.hitPoints.value.dice + ); } else { this.attack.range = adjustRange(this.attack.range).id; } diff --git a/module/data/fields/_module.mjs b/module/data/fields/_module.mjs index 816f419a..bedd9680 100644 --- a/module/data/fields/_module.mjs +++ b/module/data/fields/_module.mjs @@ -1,5 +1,5 @@ export { ActionCollection } from './actionField.mjs'; -export { default as CollectionField } from './collectionField.mjs'; +export { default as IterableTypedObjectField } from './iterableTypedObjectField.mjs'; export { default as FormulaField } from './formulaField.mjs'; export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs'; export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs'; diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 4adfb9ec..d904c3a0 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -1,6 +1,6 @@ import FormulaField from '../formulaField.mjs'; import { setsEqual } from '../../../helpers/utils.mjs'; -import CollectionField from '../collectionField.mjs'; +import IterableTypedObjectField from '../iterableTypedObjectField.mjs'; const fields = foundry.data.fields; @@ -13,7 +13,7 @@ export default class DamageField extends fields.SchemaField { /** @inheritDoc */ constructor(options, context = {}) { const damageFields = { - parts: new CollectionField(DHDamageData, { collectionClass: DamagePartsCollection }), + parts: new IterableTypedObjectField(DHDamageData), includeBase: new fields.BooleanField({ initial: false, label: 'DAGGERHEART.ACTIONS.Settings.includeBase.label' @@ -301,9 +301,3 @@ export class DHDamageData extends DHResourceData { }; } } - -class DamagePartsCollection extends foundry.utils.Collection { - get hitPoints() { - return this.get('hitPoints'); - } -} diff --git a/module/data/fields/collectionField.mjs b/module/data/fields/collectionField.mjs deleted file mode 100644 index 9291cab8..00000000 --- a/module/data/fields/collectionField.mjs +++ /dev/null @@ -1,25 +0,0 @@ -export default class CollectionField extends foundry.data.fields.TypedObjectField { - constructor(model, options = { collectionClass: foundry.utils.Collection }, context = {}) { - super(new foundry.data.fields.EmbeddedDataField(model), options, context); - this.#elementClass = model; - this.#collectionClass = options.collectionClass; - } - - /** - * The collection class - */ - #collectionClass; - - /** - * The collection element class. - */ - #elementClass; - - initialize(value, model, _options = {}) { - console.log(model); - const collection = new this.#collectionClass( - Object.entries(value).map(([key, value]) => [key, new this.#elementClass(value)]) - ); - return collection; - } -} diff --git a/module/data/fields/iterableTypedObjectField.mjs b/module/data/fields/iterableTypedObjectField.mjs new file mode 100644 index 00000000..6d00763f --- /dev/null +++ b/module/data/fields/iterableTypedObjectField.mjs @@ -0,0 +1,30 @@ +export default class IterableTypedObjectField extends foundry.data.fields.TypedObjectField { + constructor(model, options = { collectionClass: foundry.utils.Collection }, context = {}) { + super(new foundry.data.fields.EmbeddedDataField(model), options, context); + this.#elementClass = model; + } + + #elementClass; + + initialize(value) { + return new IterableObject(value, this.#elementClass); + } +} + +class IterableObject { + constructor(values, elementClass) { + for (const [key, value] of Object.entries(values)) { + this[key] = new elementClass(value); + } + } + + *[Symbol.iterator]() { + for (const value of Object.values(this)) { + yield value; + } + } + + map(func) { + return Array.from(this, func); + } +} diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 5c6f8514..c9543288 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -65,15 +65,15 @@ export default class DHWeapon extends AttachableItem { type: 'attack' }, damage: { - parts: [ - { + parts: { + hitPoints: { type: ['physical'], value: { multiplier: 'prof', dice: 'd8' } } - ] + } } } }), diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 4ecc7809..3fd57a58 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -557,3 +557,12 @@ export function calculateExpectedValue(formulaOrTerms) { : [formulaOrTerms]; return terms.reduce((r, t) => r + (t.bonus ?? 0) + (t.diceQuantity ? (t.diceQuantity * (t.faces + 1)) / 2 : 0), 0); } + +export function getNextUnusedDamageType(parts) { + const usedKeys = Object.keys(parts); + for (const key of Object.keys(CONFIG.DH.GENERAL.healingTypes)) { + if (!usedKeys.includes(key)) return key; + } + + return null; +} diff --git a/templates/sheets/items/weapon/header.hbs b/templates/sheets/items/weapon/header.hbs index 349a9516..9bbd9511 100644 --- a/templates/sheets/items/weapon/header.hbs +++ b/templates/sheets/items/weapon/header.hbs @@ -14,13 +14,13 @@ - {{localize (concat 'DAGGERHEART.CONFIG.Range.' source.system.attack.range '.name')}} - - {{#if source.system.attack.damage.parts.0.value.custom.enabled}} + {{#if source.system.attack.damage.parts.hitPoints.value.custom.enabled}} {{localize "DAGGERHEART.GENERAL.custom"}} {{else}} - {{source.system.attack.damage.parts.0.value.dice}}{{#if source.system.attack.damage.parts.0.value.bonus}} + {{source.system.attack.damage.parts.0.value.bonus}}{{/if}} + {{source.system.attack.damage.parts.hitPoints.value.dice}}{{#if source.system.attack.damage.parts.hitPoints.value.bonus}} + {{source.system.attack.damage.parts.hitPoints.value.bonus}}{{/if}} {{/if}} ( - {{#each source.system.attack.damage.parts.0.type}} + {{#each source.system.attack.damage.parts.hitPoints.type}} {{localize (concat 'DAGGERHEART.CONFIG.DamageType.' this '.abbreviation')}} {{/each}} )