diff --git a/module/data/Actor/player.mjs b/module/data/Actor/player.mjs index dfe0821..26ed8e1 100644 --- a/module/data/Actor/player.mjs +++ b/module/data/Actor/player.mjs @@ -1,8 +1,4 @@ import { __ID__ } from "../../consts.mjs"; -import { Logger } from "../../utils/Logger.mjs"; -import { EphemeralObjectField } from "../fields/EphemeralObjectField.mjs"; - -const { getProperty, hasProperty } = foundry.utils; export class PlayerData extends foundry.abstract.TypeDataModel { // #region Schema @@ -19,7 +15,7 @@ export class PlayerData extends foundry.abstract.TypeDataModel { nullable: true, initial: null, }), - attr: new EphemeralObjectField({ initial: {} }), + attr: new fields.ObjectField({ persisted: false, initial: {} }), }; }; // #endregion Schema @@ -42,14 +38,6 @@ export class PlayerData extends foundry.abstract.TypeDataModel { return super._preCreate(data, options, user); }; - /** - * Ensures that the required data structures exist in order for the - * derived data to be able to populate itself correctly. - */ - prepareBaseData() { - this.attr = {}; - }; - /** * For every attribute item that the character has, we want that data * accessible in the system data, so we create objects dynamically that @@ -69,29 +57,6 @@ export class PlayerData extends foundry.abstract.TypeDataModel { }; }; }; - - /** - * This handler makes it so that when a user updates the attributes - * using a "system.attr.*" property they correctly get removed from the - * update and are forwarded to the correct Item document instead - */ - async _preUpdate(data, options, user) { - if (hasProperty(data, `system.attr`)) { - Logger.info(`Forwarding attribute update(s) to embedded Item(s)`); - const items = this.parent.itemTypes?.attribute ?? []; - for (const attr of items) { - const key = `system.attr.${attr.system.key}`; - if (hasProperty(data, key)) { - let value = getProperty(data, key); - if (attr.system.isRange) { - attr.update({ system: value }); - } else { - attr.update({ system: { value }}); - }; - }; - }; - }; - }; // #endregion Lifecycle // #region Getters diff --git a/module/documents/Actor.mjs b/module/documents/Actor.mjs index af03605..22f7881 100644 --- a/module/documents/Actor.mjs +++ b/module/documents/Actor.mjs @@ -1,4 +1,5 @@ import { __ID__ } from "../consts.mjs"; +import { clamp } from "../utils/clamp.mjs"; const { Actor } = foundry.documents; const { deepClone, setProperty } = foundry.utils; @@ -28,7 +29,30 @@ export class TAFActor extends Actor { // #endregion Lifecycle // #region Token Attributes + /** + * @override + * This override exists in order to support making updates to the Actor's + * embedded attribute Items from the token, or falling back to the default + * handling if it's not one of our attributes. + */ async modifyTokenAttribute(attribute, value, isDelta = false, isBar = true) { + if (attribute.startsWith(`attr.`)) { + const key = attribute.slice(5); + const attr = this.getAttribute(key); + value = isDelta ? attr.system.value + value : value; + value = clamp(attr.system.min, value, attr.system.max); + const updates = { system: { value } }; + + const allowed = Hooks.call( + `modifyTokenAttribute`, + { attribute, value, isDelta, isBar, isEmbedded: true }, + updates, + this + ); + + return allowed !== false ? await attr.update(updates) : this; + }; + const attr = foundry.utils.getProperty(this.system, attribute); const current = isBar ? attr.value : attr; const update = isDelta ? current + value : value; @@ -36,18 +60,7 @@ export class TAFActor extends Actor { return this; }; - // Determine the updates to make to the actor data - let updates; - if (isBar) { - updates = {[`system.${attribute}.value`]: Math.clamp(update, 0, attr.max)}; - } else { - updates = {[`system.${attribute}`]: update}; - }; - - // Allow a hook to override these changes - const allowed = Hooks.call(`modifyTokenAttribute`, {attribute, value, isDelta, isBar}, updates, this); - - return allowed !== false ? this.update(updates) : this; + return super.modifyTokenAttribute(attribute, value, isDelta, isBar); }; // #endregion Token Attributes @@ -80,8 +93,15 @@ export class TAFActor extends Actor { }; // #endregion Roll Data - // #region Getters + // #region Methods #sortedTypes = null; + /** + * @override + * This override is intended to allow the "generic" item subtype to instead + * populate the Item types based on their "Group" property, for any other item + * subtype this function operates the same way that the default Foundry + * implementation does. + */ get itemTypes() { if (this.#sortedTypes) { return this.#sortedTypes }; const types = {}; @@ -97,7 +117,29 @@ export class TAFActor extends Actor { }; return this.#sortedTypes = types; }; - // #endregion Getters + + /** + * Retrieves an attribute Item from the actor, used to more easily + * + * @param {string} key The unique identifier of the attribute + * @returns The attribute's Item document, or undefined if not found + */ + getAttribute(key) { + const attrs = this.itemTypes.attribute ?? []; + return attrs.find(attr => attr.system.key === key); + }; + + /** + * Updates an embedded attribute Item with a new value. + * + * @param {string} key The unique identifier of the attribute + * @param {number} value The value to set the attribute to + */ + async setAttributeValue(key, value) { + const item = this.getAttribute(key); + await item?.update({system: { value }}); + }; + // #endregion Methods // #region Data Migration /** diff --git a/module/documents/Token.mjs b/module/documents/Token.mjs index 068c737..8b0b5bd 100644 --- a/module/documents/Token.mjs +++ b/module/documents/Token.mjs @@ -83,22 +83,12 @@ export class TAFTokenDocument extends TokenDocument { if (`value` in data && `max` in data) { let editable = hasProperty(system, `${attribute}.value`); - const isRange = getProperty(system, `${attribute}.isRange`); - if (isRange) { - return { - type: `bar`, - attribute, - value: parseInt(data.value || 0), - max: parseInt(data.max || 0), - editable, - }; - } else { - return { - type: `value`, - attribute: `${attribute}.value`, - value: Number(data.value), - editable, - }; + return { + type: `bar`, + attribute, + value: parseInt(data.value || 0), + max: parseInt(data.max || 0), + editable, }; };