diff --git a/module/applications/sheets/items/armor.mjs b/module/applications/sheets/items/armor.mjs index d0422c6f..c26c6514 100644 --- a/module/applications/sheets/items/armor.mjs +++ b/module/applications/sheets/items/armor.mjs @@ -98,27 +98,71 @@ export default class ArmorSheet extends DHBaseItemSheet { async _onDrop(event) { const data = TextEditor.getDragEventData(event); - // Only handle Item drops - if (data.type !== 'Item') return; - // Check if dropped on attachments section const attachmentsSection = event.target.closest('.attachments-section'); if (!attachmentsSection) return super._onDrop(event); + // Prevent event bubbling + event.preventDefault(); + event.stopPropagation(); + // Get the item being dropped const item = await Item.implementation.fromDropData(data); if (!item) return; + console.log(`Dropping item ${item.name} (${item.uuid}) onto armor ${this.document.name}`); + // Get current attached UUIDs const currentAttached = this.document.system.attached || []; const newUUID = item.uuid; - // Prevent duplicates - if (currentAttached.includes(newUUID)) return; + // Don't attach if already attached + if (currentAttached.includes(newUUID)) { + ui.notifications.warn(`${item.name} is already attached to this armor.`); + return; + } + + console.log(`Current attached items:`, currentAttached); + console.log(`Adding new UUID:`, newUUID); + + const updatedAttached = [...currentAttached, newUUID]; + console.log(`Updating armor with attached items:`, updatedAttached); await this.document.update({ - 'system.attached': [...currentAttached, newUUID] + 'system.attached': updatedAttached }); + + // Copy effects from attached item to actor (only if armor is equipped) + const actor = this.document.parent; + if (actor && item.effects.size > 0 && this.document.system.equipped) { + console.log(`Copying ${item.effects.size} effects from attached item ${item.name} to actor ${actor.name} (armor is equipped)`); + + const effectsToCreate = []; + for (const effect of item.effects) { + // Create a copy of the effect with metadata to track its source + const effectData = effect.toObject(); + effectData.origin = `${this.document.uuid}:${newUUID}`; // Track which armor and which item this came from + effectData.flags = { + ...effectData.flags, + daggerheart: { + ...effectData.flags?.daggerheart, + attachmentSource: { + armorUuid: this.document.uuid, + itemUuid: newUUID, + originalEffectId: effect.id + } + } + }; + effectsToCreate.push(effectData); + } + + const createdEffects = await actor.createEmbeddedDocuments('ActiveEffect', effectsToCreate); + console.log(`Created ${createdEffects.length} effects on actor from attached item`); + } else if (item.effects.size > 0 && !this.document.system.equipped) { + console.log(`Armor ${this.document.name} is not equipped, attachment effects will be applied when equipped`); + } + + console.log(`Armor updated successfully`); } /** @@ -130,8 +174,25 @@ export default class ArmorSheet extends DHBaseItemSheet { const uuid = target.dataset.uuid; const currentAttached = this.document.system.attached || []; + // Remove the attachment from the armor await this.document.update({ 'system.attached': currentAttached.filter(attachedUuid => attachedUuid !== uuid) }); + + // Remove any effects on the actor that came from this attached item + const actor = this.document.parent; + if (actor) { + const effectsToRemove = actor.effects.filter(effect => { + const attachmentSource = effect.flags?.daggerheart?.attachmentSource; + return attachmentSource && + attachmentSource.armorUuid === this.document.uuid && + attachmentSource.itemUuid === uuid; + }); + + if (effectsToRemove.length > 0) { + console.log(`Removing ${effectsToRemove.length} effects from actor that came from detached item ${uuid}`); + await actor.deleteEmbeddedDocuments('ActiveEffect', effectsToRemove.map(e => e.id)); + } + } } } diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 722a0f99..5ba96310 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -1,6 +1,7 @@ import BaseDataItem from './base.mjs'; import ActionField from '../fields/actionField.mjs'; import { armorFeatures } from '../../config/itemConfig.mjs'; +import { actionsTypes } from '../action/_module.mjs'; export default class DHArmor extends BaseDataItem { /** @inheritDoc */ @@ -53,6 +54,11 @@ export default class DHArmor extends BaseDataItem { 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); + } + if (changes.system.features) { const removed = this.features.filter(x => !changes.system.features.includes(x)); const added = changes.system.features.filter(x => !this.features.includes(x)); @@ -91,4 +97,59 @@ export default class DHArmor extends BaseDataItem { } } } + + /** + * Handle adding/removing attachment effects when armor is equipped/unequipped + * @param {boolean} newEquippedStatus - The new equipped status + */ + async _handleAttachmentEffectsOnEquipChange(newEquippedStatus) { + const actor = this.parent.parent; + if (!actor || !this.attached?.length) return; + + if (newEquippedStatus) { + // Armor is being equipped - add attachment effects + console.log(`Armor ${this.parent.name} being equipped, adding attachment effects`); + + const effectsToCreate = []; + for (const attachedUuid of this.attached) { + const attachedItem = await fromUuid(attachedUuid); + if (attachedItem && attachedItem.effects.size > 0) { + for (const effect of attachedItem.effects) { + const effectData = effect.toObject(); + effectData.origin = `${this.parent.uuid}:${attachedUuid}`; + effectData.flags = { + ...effectData.flags, + daggerheart: { + ...effectData.flags?.daggerheart, + attachmentSource: { + armorUuid: this.parent.uuid, + itemUuid: attachedUuid, + originalEffectId: effect.id + } + } + }; + effectsToCreate.push(effectData); + } + } + } + + if (effectsToCreate.length > 0) { + await actor.createEmbeddedDocuments('ActiveEffect', effectsToCreate); + console.log(`Created ${effectsToCreate.length} attachment effects on actor`); + } + } else { + // Armor is being unequipped - remove attachment effects + console.log(`Armor ${this.parent.name} being unequipped, removing attachment effects`); + + const effectsToRemove = actor.effects.filter(effect => { + const attachmentSource = effect.flags?.daggerheart?.attachmentSource; + return attachmentSource && attachmentSource.armorUuid === this.parent.uuid; + }); + + if (effectsToRemove.length > 0) { + await actor.deleteEmbeddedDocuments('ActiveEffect', effectsToRemove.map(e => e.id)); + console.log(`Removed ${effectsToRemove.length} attachment effects from actor`); + } + } + } } diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 65610e7c..23ba81a7 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -51,4 +51,14 @@ export default class DhActiveEffect extends ActiveEffect { cls.create(msg.toObject()); } + + /** + * Retrieve the Document that this ActiveEffect targets for modification. + * @type {Document|null} + */ + get target() { + if (this.parent instanceof Actor) return this.parent; + if (CONFIG.ActiveEffect.legacyTransferral) return this.transfer ? null : this.parent; + return this.transfer ? (this.parent.parent ?? null) : this.parent; + } }