From d799fb1c56aec2f52de23152bed15b7983efd8f4 Mon Sep 17 00:00:00 2001 From: fyorl Date: Wed, 12 Jan 2022 21:03:22 +0000 Subject: [PATCH] [#25] Ensure resource attribute values are clamped between their min and max. Ensure token attribute update deltas are applied correctly to resource attributes. Draw attribute bars appropriately when attribute min is non-zero. --- module/actor.js | 14 ++++++++++++++ module/helper.js | 17 +++++++++++++++++ module/item.js | 1 + module/simple.js | 5 ++--- module/token.js | 38 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 module/token.js diff --git a/module/actor.js b/module/actor.js index 8288404..acaee33 100644 --- a/module/actor.js +++ b/module/actor.js @@ -11,6 +11,7 @@ export class SimpleActor extends Actor { super.prepareDerivedData(); this.data.data.groups = this.data.data.groups || {}; this.data.data.attributes = this.data.data.attributes || {}; + EntitySheetHelper.clampResourceValues(this.data.data.attributes); } /* -------------------------------------------- */ @@ -236,4 +237,17 @@ export class SimpleActor extends Actor { } } } + + /* -------------------------------------------- */ + + /** @inheritdoc */ + async modifyTokenAttribute(attribute, value, isDelta = false, isBar = true) { + const current = foundry.utils.getProperty(this.data.data, attribute); + if ( !isBar || !isDelta || (current?.dtype !== "Resource") ) { + return super.modifyTokenAttribute(attribute, value, isDelta, isBar); + } + const updates = {[`data.${attribute}.value`]: Math.clamped(current.value + value, current.min, current.max)}; + const allowed = Hooks.call("modifyTokenAttribute", {attribute, value, isDelta, isBar}, updates); + return allowed !== false ? this.update(updates) : this; + } } diff --git a/module/helper.js b/module/helper.js index 4997798..dd04769 100644 --- a/module/helper.js +++ b/module/helper.js @@ -587,4 +587,21 @@ export class EntitySheetHelper { options: options }); } + + /* -------------------------------------------- */ + + /** + * Ensure the resource values are within the specified min and max. + * @param {object} attrs The Document's attributes. + */ + static clampResourceValues(attrs) { + const flat = foundry.utils.flattenObject(attrs); + for ( const [attr, value] of Object.entries(flat) ) { + const parts = attr.split("."); + if ( parts.pop() !== "value" ) continue; + const current = foundry.utils.getProperty(attrs, parts.join(".")); + if ( current?.dtype !== "Resource" ) continue; + foundry.utils.setProperty(attrs, attr, Math.clamped(value, current.min || 0, current.max || 0)); + } + } } diff --git a/module/item.js b/module/item.js index b4e0cce..5cfb6a8 100644 --- a/module/item.js +++ b/module/item.js @@ -11,6 +11,7 @@ export class SimpleItem extends Item { super.prepareDerivedData(); this.data.data.groups = this.data.data.groups || {}; this.data.data.attributes = this.data.data.attributes || {}; + EntitySheetHelper.clampResourceValues(this.data.data.attributes); } /* -------------------------------------------- */ diff --git a/module/simple.js b/module/simple.js index 059bea6..59b663c 100644 --- a/module/simple.js +++ b/module/simple.js @@ -10,7 +10,7 @@ import { SimpleItemSheet } from "./item-sheet.js"; import { SimpleActorSheet } from "./actor-sheet.js"; import { preloadHandlebarsTemplates } from "./templates.js"; import { createWorldbuildingMacro } from "./macro.js"; -import { SimpleTokenDocument } from "./simpletokendocument.js"; +import { SimpleToken, SimpleTokenDocument } from "./token.js"; /* -------------------------------------------- */ /* Foundry VTT Initialization */ @@ -40,9 +40,8 @@ Hooks.once("init", async function() { // Define custom Document classes CONFIG.Actor.documentClass = SimpleActor; CONFIG.Item.documentClass = SimpleItem; - - // Update TokenDocument with overrided getBarAttribute method CONFIG.Token.documentClass = SimpleTokenDocument; + CONFIG.Token.objectClass = SimpleToken; // Register sheet application classes Actors.unregisterSheet("core", ActorSheet); diff --git a/module/token.js b/module/token.js new file mode 100644 index 0000000..220826b --- /dev/null +++ b/module/token.js @@ -0,0 +1,38 @@ +/** + * Extend the base TokenDocument to allow resource to support resource type attributes. + * @extends {TokenDocument} + */ +export class SimpleTokenDocument extends TokenDocument { + /** @inheritdoc */ + getBarAttribute(barName, {alternative}={}) { + const data = super.getBarAttribute(barName, {alternative}); + const attr = alternative || this.data[barName]?.attribute; + if ( !data || !attr || !this.actor ) return data; + const current = foundry.utils.getProperty(this.actor.data.data, attr); + if ( "min" in current ) data.min = parseInt(current.min || 0); + data.editable = true; + return data; + } +} + + +/* -------------------------------------------- */ + + +/** + * Extend the base Token class to implement additional system-specific logic. + * @extends {Token} + */ +export class SimpleToken extends Token { + _drawBar(number, bar, data) { + if ( "min" in data ) { + // Copy the data to avoid mutating what the caller gave us. + data = {...data}; + // Shift the value and max by the min to ensure that the bar's percentage is drawn accurately if this resource has + // a non-zero min. + data.value -= data.min; + data.max -= data.min; + } + return super._drawBar(number, bar, data); + } +}