Actions can only use item resources from their parent item

This commit is contained in:
WBHarry 2025-07-12 16:45:30 +02:00
parent 524b3fa921
commit e37fc83c59
7 changed files with 79 additions and 74 deletions

View file

@ -111,7 +111,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
context.hasBaseDamage = !!this.action.parent.attack; context.hasBaseDamage = !!this.action.parent.attack;
context.getRealIndex = this.getRealIndex.bind(this); context.getRealIndex = this.getRealIndex.bind(this);
context.getEffectDetails = this.getEffectDetails.bind(this); context.getEffectDetails = this.getEffectDetails.bind(this);
context.costOptions = await this.getCostOptions(); context.costOptions = this.getCostOptions();
context.disableOption = this.disableOption.bind(this); context.disableOption = this.disableOption.bind(this);
context.isNPC = this.action.actor && this.action.actor.type !== 'character'; context.isNPC = this.action.actor && this.action.actor.type !== 'character';
context.hasRoll = this.action.hasRoll; context.hasRoll = this.action.hasRoll;
@ -130,42 +130,17 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
this.render(true); this.render(true);
} }
async getCostOptions() { getCostOptions() {
const worldItems = this.action.actor ? this.action.actor.items : game.items; const options = foundry.utils.deepClone(CONFIG.DH.GENERAL.abilityCosts);
const compendiumItems = this.action.actor const resource = this.action.parent.resource;
? [] if (resource) {
: Array.from(game.packs).flatMap(x => options[this.action.parent.parent.id] = {
x.index.map(x => ({ label: this.action.parent.parent.name,
id: x._id, group: 'TYPES.Actor.character'
uuid: x.uuid, };
name: x.name }
}))
);
const resourceOptions = [...worldItems, ...compendiumItems].reduce((acc, x) => { return options;
if (['feature', 'domainCard'].includes(x.type) && x.system.resource?.max) {
acc.push({
id: x.id,
uuid: x.uuid,
label: x.name,
group: 'TYPES.Actor.character'
});
}
return acc;
}, []);
const abilityCosts = Object.values(CONFIG.DH.GENERAL.abilityCosts);
const sortedOptions = [...abilityCosts, ...resourceOptions].sort((a, b) => {
const groupSort = game.i18n.localize(a.group).localeCompare(game.i18n.localize(b.group));
return groupSort ? groupSort : game.i18n.localize(a.label).localeCompare(game.i18n.localize(b.label));
});
return sortedOptions.reduce((acc, x) => {
acc[x.uuid ?? x.id] = x;
return acc;
}, {});
} }
disableOption(index, costOptions, choices) { disableOption(index, costOptions, choices) {
@ -185,15 +160,15 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
return this.action.item.effects.get(id); return this.action.item.effects.get(id);
} }
async _prepareSubmitData(_event, formData) { _prepareSubmitData(_event, formData) {
const submitData = foundry.utils.expandObject(formData.object); const submitData = foundry.utils.expandObject(formData.object);
for (const keyPath of this.constructor.CLEAN_ARRAYS) { for (const keyPath of this.constructor.CLEAN_ARRAYS) {
const data = foundry.utils.getProperty(submitData, keyPath); const data = foundry.utils.getProperty(submitData, keyPath);
const dataValues = data ? Object.values(data) : []; const dataValues = data ? Object.values(data) : [];
if (keyPath === 'cost') { if (keyPath === 'cost') {
for (var value of dataValues) { for (var value of dataValues) {
const item = await foundry.utils.fromUuid(value.key ?? ''); const item = this.action.parent.parent.id === value.key;
value.keyIsUUID = Boolean(item); value.keyIsID = Boolean(item);
} }
} }
@ -203,7 +178,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
} }
static async updateForm(event, _, formData) { static async updateForm(event, _, formData) {
const submitData = await this._prepareSubmitData(event, formData), const submitData = this._prepareSubmitData(event, formData),
data = foundry.utils.mergeObject(this.action.toObject(), submitData), data = foundry.utils.mergeObject(this.action.toObject(), submitData),
container = foundry.utils.getProperty(this.action.parent, this.action.systemPath); container = foundry.utils.getProperty(this.action.parent, this.action.systemPath);
let newActions; let newActions;

View file

@ -24,7 +24,9 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
removeAction: DHBaseItemSheet.#removeAction, removeAction: DHBaseItemSheet.#removeAction,
addFeature: DHBaseItemSheet.#addFeature, addFeature: DHBaseItemSheet.#addFeature,
editFeature: DHBaseItemSheet.#editFeature, editFeature: DHBaseItemSheet.#editFeature,
removeFeature: DHBaseItemSheet.#removeFeature removeFeature: DHBaseItemSheet.#removeFeature,
addResource: DHBaseItemSheet.#addResource,
removeResource: DHBaseItemSheet.#removeResource
}, },
dragDrop: [ dragDrop: [
{ dragSelector: null, dropSelector: '.tab.features .drop-section' }, { dragSelector: null, dropSelector: '.tab.features .drop-section' },
@ -215,6 +217,26 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
}); });
} }
/**
* Add a resource to the item.
* @type {ApplicationClickAction}
*/
static async #addResource() {
await this.document.update({
'system.resource': { value: 0 }
});
}
/**
* Remove the resource from the item.
* @type {ApplicationClickAction}
*/
static async #removeResource() {
await this.document.update({
'system.resource': null
});
}
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Application Drag/Drop */ /* Application Drag/Drop */
/* -------------------------------------------- */ /* -------------------------------------------- */

View file

@ -41,7 +41,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
required: true, required: true,
initial: 'hope' initial: 'hope'
}), }),
keyIsUUID: new fields.BooleanField(), keyIsID: new fields.BooleanField(),
value: new fields.NumberField({ nullable: true, initial: 1 }), value: new fields.NumberField({ nullable: true, initial: 1 }),
scalable: new fields.BooleanField({ initial: false }), scalable: new fields.BooleanField({ initial: false }),
step: new fields.NumberField({ nullable: true, initial: null }) step: new fields.NumberField({ nullable: true, initial: null })
@ -337,12 +337,11 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
async consume(config) { async consume(config) {
const usefulResources = foundry.utils.deepClone(this.actor.system.resources); const usefulResources = foundry.utils.deepClone(this.actor.system.resources);
for (var cost of config.costs) { for (var cost of config.costs) {
if (cost.keyIsUUID) { if (cost.keyIsID) {
const item = await foundry.utils.fromUuid(cost.key);
usefulResources[cost.key] = { usefulResources[cost.key] = {
value: item.system.resource.value, value: cost.value,
target: item, target: this.parent.parent,
keyIsUUID: true keyIsID: true
}; };
} }
} }
@ -354,7 +353,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
key: c.key, key: c.key,
value: (c.total ?? c.value) * (resource.hasOwnProperty('maxTotal') ? 1 : -1), value: (c.total ?? c.value) * (resource.hasOwnProperty('maxTotal') ? 1 : -1),
target: resource.target, target: resource.target,
keyIsUUID: resource.keyIsUUID keyIsID: resource.keyIsID
}; };
}); });
@ -400,10 +399,9 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
const actorResources = this.actor.system.resources; const actorResources = this.actor.system.resources;
const itemResources = {}; const itemResources = {};
for (var itemResource of costs) { for (var itemResource of costs) {
if (itemResource.keyIsUUID) { if (itemResource.keyIsID) {
const item = await foundry.utils.fromUuid(itemResource.key);
itemResources[itemResource.key] = { itemResources[itemResource.key] = {
value: item?.system?.resource?.value ?? 0 value: this.parent.resource.value ?? 0
}; };
} }
} }
@ -414,6 +412,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
}; };
} }
/* COST */
async hasCost(costs) { async hasCost(costs) {
const realCosts = this.getRealCosts(costs), const realCosts = this.getRealCosts(costs),
hasFearCost = realCosts.findIndex(c => c.key === 'fear'); hasFearCost = realCosts.findIndex(c => c.key === 'fear');
@ -436,7 +435,6 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
true true
); );
} }
/* COST */
/* USES */ /* USES */
calcUses(uses) { calcUses(uses) {
@ -451,7 +449,6 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
if (!uses) return true; if (!uses) return true;
return (uses.hasOwnProperty('enabled') && !uses.enabled) || uses.value + 1 <= uses.max; return (uses.hasOwnProperty('enabled') && !uses.enabled) || uses.value + 1 <= uses.max;
} }
/* USES */
/* TARGET */ /* TARGET */
isTargetFriendly(target) { isTargetFriendly(target) {

View file

@ -37,16 +37,19 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
if (this.metadata.hasDescription) schema.description = new fields.HTMLField({ required: true, nullable: true }); if (this.metadata.hasDescription) schema.description = new fields.HTMLField({ required: true, nullable: true });
if (this.metadata.hasResource) { if (this.metadata.hasResource) {
schema.resource = new fields.SchemaField({ schema.resource = new fields.SchemaField(
value: new fields.NumberField({ integer: true, min: 0, nullable: true, initial: null }), {
max: new fields.NumberField({ nullable: true, initial: null }), value: new fields.NumberField({ integer: true, min: 0, initial: 0 }),
icon: new fields.StringField(), max: new fields.NumberField({ nullable: true, initial: null }),
recovery: new fields.StringField({ icon: new fields.StringField(),
choices: CONFIG.DH.GENERAL.refreshTypes, recovery: new fields.StringField({
initial: null, choices: CONFIG.DH.GENERAL.refreshTypes,
nullable: true initial: null,
}) nullable: true
}); })
},
{ nullable: true, initial: null }
);
} }
if (this.metadata.isQuantifiable) if (this.metadata.isQuantifiable)

View file

@ -526,9 +526,8 @@ export default class DhpActor extends Actor {
items: {} items: {}
}; };
resources.forEach(r => { resources.forEach(r => {
if (r.keyIsUUID) { if (r.keyIsID) {
updates.items[r.key] = { updates.items[r.key] = {
uuid: r.key,
target: r.target, target: r.target,
resources: { resources: {
'system.resource.value': r.target.system.resource.value + r.value 'system.resource.value': r.target.system.resource.value + r.value

View file

@ -1,7 +1,7 @@
<li class="inventory-item" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-type="{{type}}"> <li class="inventory-item" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-type="{{type}}">
<img src="{{item.img}}" class="item-img {{#if isActor}}actor-img{{/if}}" data-action="useItem" {{#if (not noTooltip)}}data-tooltip="{{concat "#item#" item.uuid}}"{{/if}} /> <img src="{{item.img}}" class="item-img {{#if isActor}}actor-img{{/if}}" data-action="useItem" {{#if (not noTooltip)}}data-tooltip="{{concat "#item#" item.uuid}}"{{/if}} />
<div class="item-label-wrapper"> <div class="item-label-wrapper">
<div class="item-label {{#unless (and (not isSidebar) (or item.system.resource.max item.system.quantity))}}fullWidth{{/unless}}"> <div class="item-label {{#unless (and (not isSidebar) (or item.system.resource item.system.quantity))}}fullWidth{{/unless}}">
{{#if isCompanion}} {{#if isCompanion}}
<a class="item-name" data-action="attackRoll">{{item.name}}</a> <a class="item-name" data-action="attackRoll">{{item.name}}</a>
{{else}} {{else}}
@ -123,7 +123,7 @@
</div> </div>
{{/if}} {{/if}}
</div> </div>
{{#if (and (not isSidebar) item.system.resource.max)}} {{#if (and (not isSidebar) item.system.resource)}}
<div class="item-resource"> <div class="item-resource">
<i class="{{#if item.system.resource.icon}}{{item.system.resource.icon}}{{else}}fa-solid fa-hashtag{{/if}}"></i> <i class="{{#if item.system.resource.icon}}{{item.system.resource.icon}}{{else}}fa-solid fa-hashtag{{/if}}"></i>
<input type="number" class="inventory-item-resource" value="{{item.system.resource.value}}" step="1" /> <input type="number" class="inventory-item-resource" value="{{item.system.resource.value}}" step="1" />

View file

@ -1,10 +1,19 @@
<fieldset> <fieldset>
<legend>{{localize "DAGGERHEART.GENERAL.Resource.single"}}</legend> <legend>
{{localize "DAGGERHEART.GENERAL.Resource.single"}}
{{#unless source.system.resource}}
<a data-action="addResource"><i class="fa-solid fa-plus icon-button"></i></a>
{{else}}
<a data-action="removeResource"><i class="fa-solid fa-trash"></i></a>
{{/unless}}
</legend>
<div class="two-columns even"> {{#if source.system.resource}}
{{formGroup systemFields.resource.fields.value value=source.system.resource.value localize=true}} <div class="two-columns even">
{{formGroup systemFields.resource.fields.max value=source.system.resource.max localize=true}} {{formGroup systemFields.resource.fields.value value=source.system.resource.value localize=true}}
{{formGroup systemFields.resource.fields.icon value=source.system.resource.icon localize=true placeholder="fa-solid fa-hashtag"}} {{formGroup systemFields.resource.fields.max value=source.system.resource.max localize=true}}
{{formGroup systemFields.resource.fields.recovery value=source.system.resource.recovery localize=true}} {{formGroup systemFields.resource.fields.icon value=source.system.resource.icon localize=true placeholder="fa-solid fa-hashtag"}}
</div> {{formGroup systemFields.resource.fields.recovery value=source.system.resource.recovery localize=true}}
</div>
{{/if}}
</fieldset> </fieldset>