add attachment only flag and logic

This commit is contained in:
psitacus 2025-07-08 22:35:57 -06:00
parent bee41cc546
commit 4e304adde7
5 changed files with 72 additions and 18 deletions

View file

@ -1026,7 +1026,11 @@
},
"Effect": {
"single": "Effect",
"plural": "Effects"
"plural": "Effects",
"AttachmentOnly": {
"Label": "Attachment Only",
"Hint": "When checked, this effect will only apply to the actor when this item is attached to another item (e.g., attached to armor). If unchecked, the effect applies normally based on item equipped status."
}
},
"Experience": {
"single": "Experience",

View file

@ -132,14 +132,15 @@ export default class ArmorSheet extends DHBaseItemSheet {
'system.attached': updatedAttached
});
// Copy effects from attached item to actor (only if armor is equipped)
// Copy ALL effects from attached item to actor (only if armor is equipped)
// Both attachment-only and regular effects should be copied when attached
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)`);
console.log(`Checking ${item.effects.size} effects from attached item ${item.name}`);
const effectsToCreate = [];
for (const effect of item.effects) {
// Create a copy of the effect with metadata to track its source
// Copy ALL effects when item is attached - attachment-only flag only matters for non-attached items
const effectData = effect.toObject();
effectData.origin = `${this.document.uuid}:${newUUID}`; // Track which armor and which item this came from
effectData.flags = {
@ -154,10 +155,17 @@ export default class ArmorSheet extends DHBaseItemSheet {
}
};
effectsToCreate.push(effectData);
const isAttachmentOnly = effect.flags?.daggerheart?.attachmentOnly === true;
console.log(`Effect ${effect.name} (attachment-only: ${isAttachmentOnly}) will be copied to actor`);
}
const createdEffects = await actor.createEmbeddedDocuments('ActiveEffect', effectsToCreate);
console.log(`Created ${createdEffects.length} effects on actor from attached item`);
if (effectsToCreate.length > 0) {
const createdEffects = await actor.createEmbeddedDocuments('ActiveEffect', effectsToCreate);
console.log(`Created ${createdEffects.length} effects on actor from attached item`);
} else {
console.log(`No effects found on ${item.name}, no effects copied to actor`);
}
} 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`);
}

View file

@ -115,6 +115,7 @@ export default class DHArmor extends BaseDataItem {
const attachedItem = await fromUuid(attachedUuid);
if (attachedItem && attachedItem.effects.size > 0) {
for (const effect of attachedItem.effects) {
// Copy ALL effects when item is attached - attachment-only flag only matters for non-attached items
const effectData = effect.toObject();
effectData.origin = `${this.parent.uuid}:${attachedUuid}`;
effectData.flags = {

View file

@ -1,16 +1,60 @@
export default class DhActiveEffect extends ActiveEffect {
get isSuppressed() {
if (['weapon', 'armor'].includes(this.parent.type)) {
// If this is a copied effect from an attachment, never suppress it
// (These effects have attachmentSource metadata)
if (this.flags?.daggerheart?.attachmentSource) {
return false;
}
// First check for attachment-only effects - these should be suppressed unless attached
if (this.isAttachmentOnly && !this.isAttached) {
return true;
}
// Then apply the standard suppression rules
if (['weapon', 'armor'].includes(this.parent?.type)) {
return !this.parent.system.equipped;
}
if (this.parent.type === 'domainCard') {
if (this.parent?.type === 'domainCard') {
return this.parent.system.inVault;
}
return super.isSuppressed;
}
/**
* Check if this effect is marked as attachment-only
* @returns {boolean}
*/
get isAttachmentOnly() {
return this.flags?.daggerheart?.attachmentOnly === true;
}
/**
* Check if the parent item is currently attached to another item
* @returns {boolean}
*/
get isAttached() {
if (!this.parent || !this.parent.parent) return false;
// Check if this item's UUID is in any actor's armor attachment lists
const actor = this.parent.parent;
if (!actor || !actor.items) return false;
try {
return actor.items.some(item => {
return item.type === 'armor' &&
item.system?.attached &&
Array.isArray(item.system.attached) &&
item.system.attached.includes(this.parent.uuid);
});
} catch (error) {
console.warn('Error checking if item is attached:', error);
return false;
}
}
async _preCreate(data, options, user) {
const update = {};
if (!data.img) {
@ -51,14 +95,4 @@ 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;
}
}

View file

@ -8,6 +8,13 @@
{{/if}}
{{#if isItemEffect}}
{{formGroup fields.transfer value=source.transfer rootId=rootId label=legacyTransfer.label hint=legacyTransfer.hint}}
{{!-- Attachment-only flag for item effects --}}
<div class="form-group">
<label for="{{rootId}}-attachmentOnly">{{localize "DAGGERHEART.Effect.AttachmentOnly.Label"}}</label>
<input type="checkbox" id="{{rootId}}-attachmentOnly" name="flags.daggerheart.attachmentOnly" {{#if source.flags.daggerheart.attachmentOnly}}checked{{/if}}>
<p class="hint">{{localize "DAGGERHEART.Effect.AttachmentOnly.Hint"}}</p>
</div>
{{/if}}
{{formGroup fields.statuses value=source.statuses options=statuses rootId=rootId classes="statuses"}}