daggerheart/module/data/fields/action/costField.mjs
Dapoulp f04619f73b
Fix/1144 fix experiences roll use costs (#1150)
* Temp ActionField attack type missing

* Move missing attack type to getModel

* Fix costs object being updated during getRealCosts
2025-09-05 10:45:05 +10:00

116 lines
4.3 KiB
JavaScript

const fields = foundry.data.fields;
export default class CostField extends fields.ArrayField {
constructor(options = {}, context = {}) {
const element = new fields.SchemaField({
key: new fields.StringField({
nullable: false,
required: true,
initial: 'hope'
}),
keyIsID: new fields.BooleanField(),
value: new fields.NumberField({ nullable: true, initial: 1, min: 0 }),
scalable: new fields.BooleanField({ initial: false }),
step: new fields.NumberField({ nullable: true, initial: null }),
consumeOnSuccess: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.ACTIONS.Settings.consumeOnSuccess.label'
})
});
super(element, options, context);
}
static 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("You don't have the resources to use that action.");
return hasCost;
}
static calcCosts(costs) {
const resources = CostField.getResources.call(this, costs);
return costs.map(c => {
c.scale = c.scale ?? 0;
c.step = c.step ?? 1;
c.total = c.value + c.scale * c.step;
c.enabled = c.hasOwnProperty('enabled') ? c.enabled : true;
c.max =
c.key === 'fear'
? game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear)
: resources[c.key].isReversed
? resources[c.key].max
: resources[c.key].value;
if (c.scalable) c.maxStep = Math.floor((c.max - c.value) / c.step);
return c;
});
}
static hasCost(costs) {
const realCosts = CostField.getRealCosts.call(this, costs),
hasFearCost = realCosts.findIndex(c => c.key === 'fear');
if (hasFearCost > -1) {
const fearCost = realCosts.splice(hasFearCost, 1)[0];
if (
!game.user.isGM ||
fearCost.total > game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear)
)
return false;
}
/* isReversed is a sign that the resource is inverted, IE it counts upwards instead of down */
const resources = CostField.getResources.call(this, realCosts);
return realCosts.reduce(
(a, c) =>
!resources[c.key]
? a
: a && resources[c.key].isReversed
? resources[c.key].value + (c.total ?? c.value) <= resources[c.key].max
: resources[c.key]?.value >= (c.total ?? c.value),
true
);
}
static getResources(costs) {
const actorResources = foundry.utils.deepClone(this.actor.system.resources);
if (this.actor.system.partner)
actorResources.hope = foundry.utils.deepClone(this.actor.system.partner.system.resources.hope);
const itemResources = {};
for (let itemResource of costs) {
if (itemResource.keyIsID) {
itemResources[itemResource.key] = {
value: this.parent.resource.value ?? 0,
max: CostField.formatMax.call(this, this.parent?.resource?.max)
};
}
}
return {
...actorResources,
...itemResources
};
}
static getRealCosts(costs) {
const cloneCosts = foundry.utils.deepClone(costs),
realCosts = cloneCosts?.length ? cloneCosts.filter(c => c.enabled) : [];
let mergedCosts = [];
realCosts.forEach(c => {
const getCost = Object.values(mergedCosts).find(gc => gc.key === c.key);
if (getCost) getCost.total += c.total;
else mergedCosts.push(c);
});
return mergedCosts;
}
static formatMax(max) {
max ??= 0;
if (isNaN(max)) {
const roll = Roll.replaceFormulaData(max, this.getRollData());
max = roll.total;
}
return Number(max);
}
}