diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 5a646395..914697df 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -141,14 +141,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel if (this.chatDisplay) await this.toChat(); let config = this.prepareConfig(event); - - /* Object.values(this.schema.fields).forEach( clsField => { - if (clsField?.prepareConfig) { - // const keep = clsField.prepareConfig.call(this, config); - // if (config.isFastForward && !keep) return; - if(clsField.prepareConfig.call(this, config) === false) return; - } - }) */ + if(!config) return; if (Hooks.call(`${CONFIG.DH.id}.preUseAction`, this, config) === false) return; @@ -161,9 +154,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel // Execute the Action Worflow in order based of schema fields await this.executeWorkflow(config); - // Consume resources - await this.consume(config); - if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return; return config; @@ -179,18 +169,15 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel action: this._id, actor: this.actor.uuid }, - dialog: { - // configure: this.hasRoll - }, + dialog: {}, type: this.type, hasRoll: this.hasRoll, - hasDamage: this.hasDamagePart && this.type !== 'healing', - hasHealing: this.hasDamagePart && this.type === 'healing', + hasDamage: this.hasDamage, + hasHealing: this.hasHealing, hasEffect: !!this.effects?.length, hasSave: this.hasSave, isDirect: !!this.damage?.direct, selectedRollMode: game.settings.get('core', 'rollMode'), - // isFastForward: event.shiftKey, data: this.getRollData(), evaluate: this.hasRoll }; @@ -200,13 +187,10 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel prepareConfig(event) { const config = this.prepareBaseConfig(event); - Object.values(this.schema.fields).forEach( clsField => { - if (clsField?.prepareConfig) { - // const keep = clsField.prepareConfig.call(this, config); - // if (config.isFastForward && !keep) return; - if(clsField.prepareConfig.call(this, config) === false) return; - } - }) + for(const clsField of Object.values(this.schema.fields)) { + if (clsField?.prepareConfig) + if(clsField.prepareConfig.call(this, config) === false) return false; + } return config; } @@ -215,8 +199,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel } async consume(config, successCost = false) { - await game.system.api.fields.ActionFields.CostField.consume.call(this, config, successCost); - await game.system.api.fields.ActionFields.UsesField.consume.call(this, config, successCost); + await this.workflow.get("cost")?.execute(config, successCost); + await this.workflow.get("uses")?.execute(config, successCost); if (config.roll && !config.roll.success && successCost) { setTimeout(() => { @@ -231,13 +215,21 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel /** * Getters to know which parts the action of composed of. A field can exist but configured to not be used. - * @returns {boolean} Does that part in the action. + * @returns {boolean} If that part is in the action. */ get hasRoll() { return !!this.roll?.type; } + get hasDamage() { + return this.damage?.parts?.length && this.type !== 'healing' + } + + get hasHealing() { + return this.damage?.parts?.length && this.type === 'healing' + } + get hasDamagePart() { return this.damage?.parts?.length; } diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index 199f7825..1f2ee630 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -1,6 +1,10 @@ const fields = foundry.data.fields; export default class CostField extends fields.ArrayField { + /** + * Action Workflow order + */ + order = 150; /** @inheritDoc */ constructor(options = {}, context = {}) { @@ -22,18 +26,70 @@ export default class CostField extends fields.ArrayField { super(element, options, context); } + /** + * Cost Consumption Action Workflow part. + * Consume configured action resources. + * Must be called within Action context. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @param {boolean} [successCost=false] Consume only resources configured as "On Success only" if not already consumed. + */ + async execute(config, successCost = false) { + const actor= this.actor.system.partner ?? this.actor, + usefulResources = { + ...foundry.utils.deepClone(actor.system.resources), + fear: { + value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), + max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, + reversed: false + } + }; + + for (var cost of config.costs) { + if (cost.keyIsID) { + usefulResources[cost.key] = { + value: cost.value, + target: this.parent.parent, + keyIsID: true + }; + } + } + + const resources = CostField.getRealCosts(config.costs) + .filter( + c => + (!successCost && (!c.consumeOnSuccess || config.roll?.success)) || + (successCost && c.consumeOnSuccess) + ) + .reduce((a, c) => { + const resource = usefulResources[c.key]; + if (resource) { + a.push({ + key: c.key, + value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1), + target: resource.target, + keyIsID: resource.keyIsID + }); + return a; + } + }, []); + + await actor.modifyResource(resources); + } + /** * Update Action Workflow config object. * Must be called within Action context. * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @returns {boolean} Return false if fast-forwarded and no more uses. */ prepareConfig(config) { const costs = this.cost?.length ? foundry.utils.deepClone(this.cost) : []; config.costs = CostField.calcCosts.call(this, costs); const hasCost = CostField.hasCost.call(this, config.costs); - if (config.isFastForward && !hasCost) - return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.insufficientResources')); - return hasCost; + if (config.dialog.configure === false && !hasCost) { + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.insufficientResources')); + return hasCost; + } } /** @@ -148,53 +204,4 @@ export default class CostField extends fields.ArrayField { } return Number(max); } - - /** - * Consume configured action resources. - * Must be called within Action context. - * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. - * @param {boolean} [successCost=false] Consume only resources configured as "On Success only" if not already consumed. - */ - static async consume(config, successCost = false) { - const actor= this.actor.system.partner ?? this.actor, - usefulResources = { - ...foundry.utils.deepClone(actor.system.resources), - fear: { - value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), - max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, - reversed: false - } - }; - - for (var cost of config.costs) { - if (cost.keyIsID) { - usefulResources[cost.key] = { - value: cost.value, - target: this.parent.parent, - keyIsID: true - }; - } - } - - const resources = CostField.getRealCosts(config.costs) - .filter( - c => - (!successCost && (!c.consumeOnSuccess || config.roll?.success)) || - (successCost && c.consumeOnSuccess) - ) - .reduce((a, c) => { - const resource = usefulResources[c.key]; - if (resource) { - a.push({ - key: c.key, - value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1), - target: resource.target, - keyIsID: resource.keyIsID - }); - return a; - } - }, []); - - await actor.modifyResource(resources); - } } diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 608e244a..acaedbca 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -25,7 +25,7 @@ export default class DamageField extends fields.SchemaField { /** * Roll Damage/Healing Action Workflow part. * Must be called within Action context. - * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. * @param {string} [messageId=null] ChatMessage Id where the clicked button belong. * @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example. */ diff --git a/module/data/fields/action/targetField.mjs b/module/data/fields/action/targetField.mjs index 98648eb0..4499dcc8 100644 --- a/module/data/fields/action/targetField.mjs +++ b/module/data/fields/action/targetField.mjs @@ -36,9 +36,10 @@ export default class TargetField extends fields.SchemaField { } config.targets = targets.map(t => TargetField.formatTarget.call(this, t)); const hasTargets = TargetField.checkTargets.call(this, this.target.amount, config.targets); - if (config.isFastForward && !hasTargets) - return ui.notifications.warn('Too many targets selected for that actions.'); - return hasTargets; + if (config.dialog.configure === false && !hasTargets) { + ui.notifications.warn('Too many targets selected for that actions.'); + return hasTargets; + } } /** diff --git a/module/data/fields/action/usesField.mjs b/module/data/fields/action/usesField.mjs index 1e393635..a990f77c 100644 --- a/module/data/fields/action/usesField.mjs +++ b/module/data/fields/action/usesField.mjs @@ -3,6 +3,10 @@ import FormulaField from '../formulaField.mjs'; const fields = foundry.data.fields; export default class UsesField extends fields.SchemaField { + /** + * Action Workflow order + */ + order = 160; /** @inheritDoc */ constructor(options = {}, context = {}) { @@ -22,18 +26,37 @@ export default class UsesField extends fields.SchemaField { super(usesFields, options, context); } + /** + * Uses Consumption Action Workflow part. + * Increment Action spent uses by 1. + * Must be called within Action context. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @param {boolean} [successCost=false] Consume only resources configured as "On Success only" if not already consumed. + */ + async execute(config, successCost = false) { + if ( + config.uses?.enabled && + ((!successCost && (!config.uses?.consumeOnSuccess || config.roll?.success)) || + (successCost && config.uses?.consumeOnSuccess)) + ) + this.update({ 'uses.value': this.uses.value + 1 }); + } + /** * Update Action Workflow config object. * Must be called within Action context. - * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @returns {boolean} Return false if fast-forwarded and no more uses. */ prepareConfig(config) { const uses = this.uses?.max ? foundry.utils.deepClone(this.uses) : null; if (uses && !uses.value) uses.value = 0; config.uses = uses; const hasUses = UsesField.hasUses.call(this, config.uses); - if (config.isFastForward && !hasUses) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.actionNoUsesRemaining')); - return hasUses; + if (config.dialog.configure === false && !hasUses) { + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.actionNoUsesRemaining')); + return hasUses; + } } /** @@ -66,19 +89,4 @@ export default class UsesField extends fields.SchemaField { } return (uses.hasOwnProperty('enabled') && !uses.enabled) || uses.value + 1 <= max; } - - /** - * Increment Action spent uses by 1. - * Must be called within Action context. - * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. - * @param {boolean} [successCost=false] Consume only resources configured as "On Success only" if not already consumed. - */ - static async consume(config, successCost = false) { - if ( - config.uses?.enabled && - ((!successCost && (!config.uses?.consumeOnSuccess || config.roll?.success)) || - (successCost && config.uses?.consumeOnSuccess)) - ) - this.update({ 'uses.value': this.uses.value + 1 }); - } }