mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-18 07:59:03 +01:00
Cost & Uses consume to execute
This commit is contained in:
parent
f747dc3c32
commit
9d9c636dbc
5 changed files with 109 additions and 101 deletions
|
|
@ -141,14 +141,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
if (this.chatDisplay) await this.toChat();
|
if (this.chatDisplay) await this.toChat();
|
||||||
|
|
||||||
let config = this.prepareConfig(event);
|
let config = this.prepareConfig(event);
|
||||||
|
if(!config) return;
|
||||||
/* 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 (Hooks.call(`${CONFIG.DH.id}.preUseAction`, this, config) === false) 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
|
// Execute the Action Worflow in order based of schema fields
|
||||||
await this.executeWorkflow(config);
|
await this.executeWorkflow(config);
|
||||||
|
|
||||||
// Consume resources
|
|
||||||
await this.consume(config);
|
|
||||||
|
|
||||||
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
|
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
|
@ -179,18 +169,15 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
action: this._id,
|
action: this._id,
|
||||||
actor: this.actor.uuid
|
actor: this.actor.uuid
|
||||||
},
|
},
|
||||||
dialog: {
|
dialog: {},
|
||||||
// configure: this.hasRoll
|
|
||||||
},
|
|
||||||
type: this.type,
|
type: this.type,
|
||||||
hasRoll: this.hasRoll,
|
hasRoll: this.hasRoll,
|
||||||
hasDamage: this.hasDamagePart && this.type !== 'healing',
|
hasDamage: this.hasDamage,
|
||||||
hasHealing: this.hasDamagePart && this.type === 'healing',
|
hasHealing: this.hasHealing,
|
||||||
hasEffect: !!this.effects?.length,
|
hasEffect: !!this.effects?.length,
|
||||||
hasSave: this.hasSave,
|
hasSave: this.hasSave,
|
||||||
isDirect: !!this.damage?.direct,
|
isDirect: !!this.damage?.direct,
|
||||||
selectedRollMode: game.settings.get('core', 'rollMode'),
|
selectedRollMode: game.settings.get('core', 'rollMode'),
|
||||||
// isFastForward: event.shiftKey,
|
|
||||||
data: this.getRollData(),
|
data: this.getRollData(),
|
||||||
evaluate: this.hasRoll
|
evaluate: this.hasRoll
|
||||||
};
|
};
|
||||||
|
|
@ -200,13 +187,10 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
|
|
||||||
prepareConfig(event) {
|
prepareConfig(event) {
|
||||||
const config = this.prepareBaseConfig(event);
|
const config = this.prepareBaseConfig(event);
|
||||||
Object.values(this.schema.fields).forEach( clsField => {
|
for(const clsField of Object.values(this.schema.fields)) {
|
||||||
if (clsField?.prepareConfig) {
|
if (clsField?.prepareConfig)
|
||||||
// const keep = clsField.prepareConfig.call(this, config);
|
if(clsField.prepareConfig.call(this, config) === false) return false;
|
||||||
// if (config.isFastForward && !keep) return;
|
}
|
||||||
if(clsField.prepareConfig.call(this, config) === false) return;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,8 +199,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
||||||
}
|
}
|
||||||
|
|
||||||
async consume(config, successCost = false) {
|
async consume(config, successCost = false) {
|
||||||
await game.system.api.fields.ActionFields.CostField.consume.call(this, config, successCost);
|
await this.workflow.get("cost")?.execute(config, successCost);
|
||||||
await game.system.api.fields.ActionFields.UsesField.consume.call(this, config, successCost);
|
await this.workflow.get("uses")?.execute(config, successCost);
|
||||||
|
|
||||||
if (config.roll && !config.roll.success && successCost) {
|
if (config.roll && !config.roll.success && successCost) {
|
||||||
setTimeout(() => {
|
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.
|
* 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() {
|
get hasRoll() {
|
||||||
return !!this.roll?.type;
|
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() {
|
get hasDamagePart() {
|
||||||
return this.damage?.parts?.length;
|
return this.damage?.parts?.length;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
export default class CostField extends fields.ArrayField {
|
export default class CostField extends fields.ArrayField {
|
||||||
|
/**
|
||||||
|
* Action Workflow order
|
||||||
|
*/
|
||||||
|
order = 150;
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
constructor(options = {}, context = {}) {
|
constructor(options = {}, context = {}) {
|
||||||
|
|
@ -22,18 +26,70 @@ export default class CostField extends fields.ArrayField {
|
||||||
super(element, options, context);
|
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.
|
* Update Action Workflow config object.
|
||||||
* Must be called within Action context.
|
* 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) {
|
prepareConfig(config) {
|
||||||
const costs = this.cost?.length ? foundry.utils.deepClone(this.cost) : [];
|
const costs = this.cost?.length ? foundry.utils.deepClone(this.cost) : [];
|
||||||
config.costs = CostField.calcCosts.call(this, costs);
|
config.costs = CostField.calcCosts.call(this, costs);
|
||||||
const hasCost = CostField.hasCost.call(this, config.costs);
|
const hasCost = CostField.hasCost.call(this, config.costs);
|
||||||
if (config.isFastForward && !hasCost)
|
if (config.dialog.configure === false && !hasCost) {
|
||||||
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.insufficientResources'));
|
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.insufficientResources'));
|
||||||
return hasCost;
|
return hasCost;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -148,53 +204,4 @@ export default class CostField extends fields.ArrayField {
|
||||||
}
|
}
|
||||||
return Number(max);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ export default class DamageField extends fields.SchemaField {
|
||||||
/**
|
/**
|
||||||
* Roll Damage/Healing Action Workflow part.
|
* Roll Damage/Healing Action Workflow part.
|
||||||
* Must be called within Action context.
|
* 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 {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.
|
* @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,10 @@ export default class TargetField extends fields.SchemaField {
|
||||||
}
|
}
|
||||||
config.targets = targets.map(t => TargetField.formatTarget.call(this, t));
|
config.targets = targets.map(t => TargetField.formatTarget.call(this, t));
|
||||||
const hasTargets = TargetField.checkTargets.call(this, this.target.amount, config.targets);
|
const hasTargets = TargetField.checkTargets.call(this, this.target.amount, config.targets);
|
||||||
if (config.isFastForward && !hasTargets)
|
if (config.dialog.configure === false && !hasTargets) {
|
||||||
return ui.notifications.warn('Too many targets selected for that actions.');
|
ui.notifications.warn('Too many targets selected for that actions.');
|
||||||
return hasTargets;
|
return hasTargets;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,10 @@ import FormulaField from '../formulaField.mjs';
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
export default class UsesField extends fields.SchemaField {
|
export default class UsesField extends fields.SchemaField {
|
||||||
|
/**
|
||||||
|
* Action Workflow order
|
||||||
|
*/
|
||||||
|
order = 160;
|
||||||
|
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
constructor(options = {}, context = {}) {
|
constructor(options = {}, context = {}) {
|
||||||
|
|
@ -22,18 +26,37 @@ export default class UsesField extends fields.SchemaField {
|
||||||
super(usesFields, options, context);
|
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.
|
* Update Action Workflow config object.
|
||||||
* Must be called within Action context.
|
* 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) {
|
prepareConfig(config) {
|
||||||
const uses = this.uses?.max ? foundry.utils.deepClone(this.uses) : null;
|
const uses = this.uses?.max ? foundry.utils.deepClone(this.uses) : null;
|
||||||
if (uses && !uses.value) uses.value = 0;
|
if (uses && !uses.value) uses.value = 0;
|
||||||
config.uses = uses;
|
config.uses = uses;
|
||||||
const hasUses = UsesField.hasUses.call(this, config.uses);
|
const hasUses = UsesField.hasUses.call(this, config.uses);
|
||||||
if (config.isFastForward && !hasUses) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.actionNoUsesRemaining'));
|
if (config.dialog.configure === false && !hasUses) {
|
||||||
return 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;
|
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 });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue