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}}
)