daggerheart/module/data/item/attachableItem.mjs
WBHarry 4be3e6179c
[Feature] Item Resource Support (#328)
* Initial

* Resource setup finished

* Fixed so that costs can be used

* Corrected standard resources

* Actions can only use item resources from their parent item

* Fixed up dice

* Fixed resource dice positioning

* Fixed parsing of resource.max

* Fixed styling on settings tab

* Added manual input for Dice Resources

* Lightmode fixes

* Fixed Feature spellcasting modifier

* Bugfix for item input to resourceDiceDialog

* Item fix for TokenInput

* PR Fixes
2025-07-13 20:12:32 -03:00

160 lines
5.4 KiB
JavaScript

import BaseDataItem from './base.mjs';
export default class AttachableItem extends BaseDataItem {
static defineSchema() {
const fields = foundry.data.fields;
return {
...super.defineSchema(),
attached: new fields.ArrayField(new fields.DocumentUUIDField({ type: 'Item', nullable: true }))
};
}
async _preUpdate(changes, options, user) {
const allowed = await super._preUpdate(changes, options, user);
if (allowed === false) return false;
// Handle equipped status changes for attachment effects
if (changes.system?.equipped !== undefined && changes.system.equipped !== this.equipped) {
await this.#handleAttachmentEffectsOnEquipChange(changes.system.equipped);
}
}
async #handleAttachmentEffectsOnEquipChange(newEquippedStatus) {
const actor = this.parent.parent?.type === 'character' ? this.parent.parent : this.parent.parent?.parent;
const parentType = this.parent.type;
if (!actor || !this.attached?.length) {
return;
}
if (newEquippedStatus) {
// Item is being equipped - add attachment effects
for (const attachedUuid of this.attached) {
const attachedItem = await fromUuid(attachedUuid);
if (attachedItem && attachedItem.effects.size > 0) {
await this.#copyAttachmentEffectsToActor({
attachedItem,
attachedUuid,
parentType
});
}
}
} else {
// Item is being unequipped - remove attachment effects
await this.#removeAllAttachmentEffects(parentType);
}
}
async #copyAttachmentEffectsToActor({ attachedItem, attachedUuid, parentType }) {
const actor = this.parent.parent;
if (!actor || !attachedItem.effects.size > 0 || !this.equipped) {
return [];
}
const effectsToCreate = [];
for (const effect of attachedItem.effects) {
const effectData = effect.toObject();
effectData.origin = `${this.parent.uuid}:${attachedUuid}`;
const attachmentSource = {
itemUuid: attachedUuid,
originalEffectId: effect.id
};
attachmentSource[`${parentType}Uuid`] = this.parent.uuid;
effectData.flags = {
...effectData.flags,
[CONFIG.DH.id]: {
...effectData.flags?.[CONFIG.DH.id],
[CONFIG.DH.FLAGS.itemAttachmentSource]: attachmentSource
}
};
effectsToCreate.push(effectData);
}
if (effectsToCreate.length > 0) {
return await actor.createEmbeddedDocuments('ActiveEffect', effectsToCreate);
}
return [];
}
async #removeAllAttachmentEffects(parentType) {
const actor = this.parent.parent;
if (!actor) return;
const parentUuidProperty = `${parentType}Uuid`;
const effectsToRemove = actor.effects.filter(effect => {
const attachmentSource = effect.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.itemAttachmentSource);
return attachmentSource && attachmentSource[parentUuidProperty] === this.parent.uuid;
});
if (effectsToRemove.length > 0) {
await actor.deleteEmbeddedDocuments(
'ActiveEffect',
effectsToRemove.map(e => e.id)
);
}
}
/**
* Public method for adding an attachment
*/
async addAttachment(droppedItem) {
const newUUID = droppedItem.uuid;
if (this.attached.includes(newUUID)) {
ui.notifications.warn(`${droppedItem.name} is already attached to this ${this.parent.type}.`);
return;
}
const updatedAttached = [...this.attached, newUUID];
await this.parent.update({
'system.attached': updatedAttached
});
// Copy effects if equipped
if (this.equipped && droppedItem.effects.size > 0) {
await this.#copyAttachmentEffectsToActor({
attachedItem: droppedItem,
attachedUuid: newUUID,
parentType: this.parent.type
});
}
}
/**
* Public method for removing an attachment
*/
async removeAttachment(attachedUuid) {
await this.parent.update({
'system.attached': this.attached.filter(uuid => uuid !== attachedUuid)
});
// Remove effects
await this.#removeAttachmentEffects(attachedUuid);
}
async #removeAttachmentEffects(attachedUuid) {
const actor = this.parent.parent;
if (!actor) return;
const parentType = this.parent.type;
const parentUuidProperty = `${parentType}Uuid`;
const effectsToRemove = actor.effects.filter(effect => {
const attachmentSource = effect.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.itemAttachmentSource);
return (
attachmentSource &&
attachmentSource[parentUuidProperty] === this.parent.uuid &&
attachmentSource.itemUuid === attachedUuid
);
});
if (effectsToRemove.length > 0) {
await actor.deleteEmbeddedDocuments(
'ActiveEffect',
effectsToRemove.map(e => e.id)
);
}
}
}