From aa8771bf0d0b87da3734199ad7016966ce45a632 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Thu, 16 Apr 2026 02:23:25 -0400 Subject: [PATCH] Show notification when invalid item types are added to actors (#1807) --- lang/en.json | 3 +++ module/applications/sheets/api/base-actor.mjs | 8 ++------ module/applications/sheets/items/ancestry.mjs | 11 ++++++----- module/data/actor/adversary.mjs | 2 +- module/data/actor/base.mjs | 8 +++++++- module/data/actor/character.mjs | 8 +++++++- module/data/actor/companion.mjs | 4 ---- module/data/actor/environment.mjs | 2 +- module/data/actor/party.mjs | 11 +++++++---- module/documents/item.mjs | 9 +++++++-- 10 files changed, 41 insertions(+), 25 deletions(-) diff --git a/lang/en.json b/lang/en.json index 5c0c7470..77c1489f 100755 --- a/lang/en.json +++ b/lang/en.json @@ -213,6 +213,9 @@ "headerTitle": "Adversary Reaction Roll" } }, + "Base": { + "CannotAddType": "Cannot add {itemType} items to {actorType} actors." + }, "Character": { "advantageSources": { "label": "Advantage Sources", diff --git a/module/applications/sheets/api/base-actor.mjs b/module/applications/sheets/api/base-actor.mjs index 4a550d72..f009267e 100644 --- a/module/applications/sheets/api/base-actor.mjs +++ b/module/applications/sheets/api/base-actor.mjs @@ -73,7 +73,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { .hideAttribution; // Prepare inventory data - if (['party', 'character'].includes(this.document.type)) { + if (this.document.system.metadata.hasInventory) { context.inventory = { currencies: {}, weapons: this.document.itemTypes.weapon.sort((a, b) => a.sort - b.sort), @@ -283,11 +283,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { async _onDropItem(event, item) { const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); const originActor = item.actor; - if ( - item.actor?.uuid === this.document.uuid || - !originActor || - !['character', 'party'].includes(this.document.type) - ) { + if (!originActor || originActor.uuid === this.document.uuid || !this.document.system.metadata.hasInventory) { return super._onDropItem(event, item); } diff --git a/module/applications/sheets/items/ancestry.mjs b/module/applications/sheets/items/ancestry.mjs index 8c0a5620..cf040e02 100644 --- a/module/applications/sheets/items/ancestry.mjs +++ b/module/applications/sheets/items/ancestry.mjs @@ -31,11 +31,12 @@ export default class AncestrySheet extends DHHeritageSheet { if (data.type === 'ActiveEffect') return super._onDrop(event); const target = event.target.closest('fieldset.drop-section'); - const typeField = - this.document.system[target.dataset.type === 'primary' ? 'primaryFeature' : 'secondaryFeature']; - - if (!typeField) { - super._onDrop(event); + if (target) { + const typeField = + this.document.system[target.dataset.type === 'primary' ? 'primaryFeature' : 'secondaryFeature']; + if (!typeField) { + super._onDrop(event); + } } } } diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index 73673896..d30e090a 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -133,7 +133,7 @@ export default class DhpAdversary extends DhCreature { } isItemValid(source) { - return source.type === 'feature'; + return super.isItemValid(source) || source.type === 'feature'; } async _preUpdate(changes, options, user) { diff --git a/module/data/actor/base.mjs b/module/data/actor/base.mjs index 89ba5db2..13ff0a38 100644 --- a/module/data/actor/base.mjs +++ b/module/data/actor/base.mjs @@ -107,7 +107,8 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel { hasResistances: true, hasAttribution: false, hasLimitedView: true, - usesSize: false + usesSize: false, + hasInventory: false }; } @@ -168,6 +169,11 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel { /* -------------------------------------------- */ + isItemValid(source) { + const inventoryTypes = ['weapon', 'armor', 'consumable', 'loot']; + return this.metadata.hasInventory && inventoryTypes.includes(source.type); + } + /** * Obtain a data object used to evaluate any dice rolls associated with this Item Type * @param {object} [options] - Options which modify the getRollData method. diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 0e1e2259..13a47b45 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -18,7 +18,8 @@ export default class DhCharacter extends DhCreature { label: 'TYPES.Actor.character', type: 'character', settingSheet: DHCharacterSettings, - isNPC: false + isNPC: false, + hasInventory: true }); } @@ -429,6 +430,11 @@ export default class DhCharacter extends DhCreature { return attack; } + /* All items are valid on characters */ + isItemValid() { + return true; + } + /** @inheritDoc */ isItemAvailable(item) { if (!super.isItemAvailable(this)) return false; diff --git a/module/data/actor/companion.mjs b/module/data/actor/companion.mjs index 81d0aa8a..9a21ba23 100644 --- a/module/data/actor/companion.mjs +++ b/module/data/actor/companion.mjs @@ -118,10 +118,6 @@ export default class DhCompanion extends DhCreature { return this.levelupChoicesLeft > 0; } - isItemValid() { - return false; - } - prepareBaseData() { super.prepareBaseData(); this.attack.roll.bonus = this.partner?.system?.spellcastModifier ?? 0; diff --git a/module/data/actor/environment.mjs b/module/data/actor/environment.mjs index e06f038c..e037e004 100644 --- a/module/data/actor/environment.mjs +++ b/module/data/actor/environment.mjs @@ -56,7 +56,7 @@ export default class DhEnvironment extends BaseDataActor { } isItemValid(source) { - return source.type === 'feature'; + return super.isItemValid(source) || source.type === 'feature'; } _onUpdate(changes, options, userId) { diff --git a/module/data/actor/party.mjs b/module/data/actor/party.mjs index ea09c622..46635237 100644 --- a/module/data/actor/party.mjs +++ b/module/data/actor/party.mjs @@ -5,6 +5,13 @@ import GroupRollData from '../groupRollData.mjs'; import { GoldField } from '../fields/actorField.mjs'; export default class DhParty extends BaseDataActor { + /** @inheritdoc */ + static get metadata() { + return foundry.utils.mergeObject(super.metadata, { + hasInventory: true + }); + } + /**@inheritdoc */ static defineSchema() { const fields = foundry.data.fields; @@ -29,10 +36,6 @@ export default class DhParty extends BaseDataActor { /* -------------------------------------------- */ - isItemValid(source) { - return ['weapon', 'armor', 'consumable', 'loot'].includes(source.type); - } - prepareBaseData() { super.prepareBaseData(); diff --git a/module/documents/item.mjs b/module/documents/item.mjs index d1a618c7..a8b41b05 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -31,8 +31,13 @@ export default class DHItem extends foundry.documents.Item { static async createDocuments(sources, operation) { // Ensure that items being created are valid to the actor its being added to const actor = operation.parent; - sources = actor?.system?.isItemValid ? sources.filter(s => actor.system.isItemValid(s)) : sources; - return super.createDocuments(sources, operation); + const filtered = actor ? sources.filter(s => actor.system.isItemValid(s)) : sources; + if (actor && filtered.length === 0 && sources.length > 0) { + const itemType = _loc(`TYPES.Item.${sources[0].type}`); + const actorType = _loc(`TYPES.Actor.${actor.type}`); + ui.notifications.error('DAGGERHEART.ACTORS.Base.CannotAddType', { format: { itemType, actorType } }); + } + return super.createDocuments(filtered, operation); } /* -------------------------------------------- */