From f614456e86b5823aadcff57a7b8946535c3fae7c Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 21 Jul 2025 03:14:41 +0200 Subject: [PATCH] Initial --- lang/en.json | 2 - module/applications/sheets/api/base-item.mjs | 11 +-- module/applications/sheets/items/ancestry.mjs | 69 +------------------ module/data/fields/_module.mjs | 1 + module/data/fields/itemLinksField.mjs | 32 +++++++++ module/data/item/ancestry.mjs | 12 +--- module/data/item/base.mjs | 9 ++- module/data/item/feature.mjs | 3 +- module/documents/item.mjs | 11 +++ templates/sheets/items/ancestry/features.hbs | 8 +-- 10 files changed, 68 insertions(+), 90 deletions(-) create mode 100644 module/data/fields/itemLinksField.mjs diff --git a/lang/en.json b/lang/en.json index ae6ccdf4..4226d5cc 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1661,8 +1661,6 @@ "unownedAttackMacro": "Cannot make a Use macro for an Attack that doesn't belong to one of your characters", "featureNotHope": "This feature is used as something else than a Hope feature and cannot be used here.", "featureNotClass": "This feature is used as something else than a Class feature and cannot be used here.", - "featureNotPrimary": "This feature is used as something else than a Primary feature and cannot be used here.", - "featureNotSecondary": "This feature is used as something else than a Secondary feature and cannot be used here.", "featureNotFoundation": "This feature is used as something else than a Foundation feature and cannot be used here.", "featureNotSpecialization": "This feature is used as something else than a Specialization feature and cannot be used here.", "featureNotMastery": "This feature is used as something else than a Mastery feature and cannot be used here.", diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs index 1888be9e..1259757c 100644 --- a/module/applications/sheets/api/base-item.mjs +++ b/module/applications/sheets/api/base-item.mjs @@ -179,9 +179,9 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { const { type } = target.dataset; const cls = foundry.documents.Item.implementation; const feature = await cls.create({ - 'type': 'feature', - 'name': cls.defaultName({ type: 'feature' }), - 'system.subType': CONFIG.DH.ITEM.featureSubTypes[type] + type: 'feature', + name: cls.defaultName({ type: 'feature' }), + [`system.itemLinks.["${this.document.uuid}"]`]: CONFIG.DH.ITEM.featureSubTypes[type] }); await this.document.update({ 'system.features': [...this.document.system.features, feature].map(f => f.uuid) @@ -195,7 +195,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { static async #deleteFeature(_, target) { const feature = getDocFromElement(target); if (!feature) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); - await feature.update({ 'system.subType': null }); + await feature.update({ [`system.itemLinks.-=${this.document.uuid}`]: null }); await this.document.update({ 'system.features': this.document.system.features.map(x => x.uuid).filter(uuid => uuid !== feature.uuid) }); @@ -272,6 +272,9 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { const item = await fromUuid(data.uuid); if (item?.type === 'feature') { + const { type } = target.dataset; + await item.update({ [`system.itemLinks.${this.document.uuid}`]: CONFIG.DH.ITEM.featureSubTypes[type] }); + const current = this.document.system.features.map(x => x.uuid); await this.document.update({ 'system.features': [...current, item.uuid] }); } diff --git a/module/applications/sheets/items/ancestry.mjs b/module/applications/sheets/items/ancestry.mjs index fc0b8a52..2767f815 100644 --- a/module/applications/sheets/items/ancestry.mjs +++ b/module/applications/sheets/items/ancestry.mjs @@ -3,12 +3,7 @@ import DHHeritageSheet from '../api/heritage-sheet.mjs'; export default class AncestrySheet extends DHHeritageSheet { /**@inheritdoc */ static DEFAULT_OPTIONS = { - classes: ['ancestry'], - actions: { - editFeature: AncestrySheet.#editFeature, - removeFeature: AncestrySheet.#removeFeature - }, - dragDrop: [{ dragSelector: null, dropSelector: '.tab.features .drop-section' }] + classes: ['ancestry'] }; /**@inheritdoc */ @@ -17,66 +12,4 @@ export default class AncestrySheet extends DHHeritageSheet { ...super.PARTS, features: { template: 'systems/daggerheart/templates/sheets/items/ancestry/features.hbs' } }; - - /* -------------------------------------------- */ - /* Application Clicks Actions */ - /* -------------------------------------------- */ - - /** - * Edit an existing feature on the item - * @type {ApplicationClickAction} - */ - static async #editFeature(_event, button) { - const target = button.closest('.feature-item'); - const feature = this.document.system[`${target.dataset.type}Feature`]; - if (!feature || Object.keys(feature).length === 0) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); - return; - } - - feature.sheet.render(true); - } - - /** - * Remove a feature from the item. - * @type {ApplicationClickAction} - */ - static async #removeFeature(event, button) { - event.stopPropagation(); - const target = button.closest('.feature-item'); - const feature = this.document.system[`${target.dataset.type}Feature`]; - - if (feature) await feature.update({ 'system.subType': null }); - await this.document.update({ - 'system.features': this.document.system.features.filter(x => x && x.uuid !== feature.uuid).map(x => x.uuid) - }); - } - - /* -------------------------------------------- */ - /* Application Drag/Drop */ - /* -------------------------------------------- */ - - /** - * On drop on the item. - * @param {DragEvent} event - The drag event - */ - async _onDrop(event) { - event.stopPropagation(); - const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); - - const item = await fromUuid(data.uuid); - if (item?.type === 'feature') { - const subType = event.target.closest('.primary-feature') ? 'primary' : 'secondary'; - if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes[subType]) { - const error = subType === 'primary' ? 'featureNotPrimary' : 'featureNotSecondary'; - ui.notifications.warn(game.i18n.localize(`DAGGERHEART.UI.Notifications.${error}`)); - return; - } - - await item.update({ 'system.subType': subType }); - await this.document.update({ - 'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid] - }); - } - } } diff --git a/module/data/fields/_module.mjs b/module/data/fields/_module.mjs index 682ff1c4..82d98267 100644 --- a/module/data/fields/_module.mjs +++ b/module/data/fields/_module.mjs @@ -1,3 +1,4 @@ export { default as FormulaField } from './formulaField.mjs'; export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs'; export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs'; +export { default as ItemLinksField } from './itemLinksField.mjs'; diff --git a/module/data/fields/itemLinksField.mjs b/module/data/fields/itemLinksField.mjs new file mode 100644 index 00000000..fb54cff8 --- /dev/null +++ b/module/data/fields/itemLinksField.mjs @@ -0,0 +1,32 @@ +export default class ItemLinksField extends foundry.data.fields.TypedObjectField { + /** + * @param {DataFieldOptions} [options] Options which configure the behavior of the field. + * @param {DataFieldContext} [context] Additional context which describes the field + */ + constructor(options, context) { + super( + new foundry.data.fields.StringField({ + choices: CONFIG.DH.ITEM.featureSubTypes, + nullable: true, + initial: null + }), + options, + context + ); + } + + /** @inheritDoc */ + static get _defaults() { + return mergeObject(super._defaults, { validateKey: this.validateKey }); + } + + /** + * @param {Object} [value] The candidate object to be added. + */ + static validateKey(value) { + return true; + const parsed = foundry.utils.parseUuid(value); + if (!parsed || parsed.type !== CONFIG.Item.documentClass.documentName) return false; + if (!foundry.packages.BasePackage.validateId(parsed.documentId)) return false; + } +} diff --git a/module/data/item/ancestry.mjs b/module/data/item/ancestry.mjs index 71b7683d..afe9cdf7 100644 --- a/module/data/item/ancestry.mjs +++ b/module/data/item/ancestry.mjs @@ -20,18 +20,10 @@ export default class DHAncestry extends BaseDataItem { } get primaryFeature() { - return ( - this.features.find(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.primary) ?? - (this.features.filter(x => !x).length > 0 ? {} : null) - ); + return this.features.find(x => x.system.itemLinks[this.parent.id] === CONFIG.DH.ITEM.featureSubTypes.primary); } get secondaryFeature() { - return ( - this.features.find(x => x?.system?.subType === CONFIG.DH.ITEM.featureSubTypes.secondary) ?? - (this.features.filter(x => !x || x.system.subType === CONFIG.DH.ITEM.featureSubTypes.primary).length > 1 - ? {} - : null) - ); + return this.features.find(x => x.system.itemLinks[this.parent.id] === CONFIG.DH.ITEM.featureSubTypes.secondary); } } diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index 24e5e0cc..e924f292 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -8,6 +8,8 @@ * @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item */ +import ItemLinksField from '../fields/itemLinksField.mjs'; + const fields = foundry.data.fields; export default class BaseDataItem extends foundry.abstract.TypeDataModel { @@ -21,7 +23,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { hasDescription: false, hasResource: false, isQuantifiable: false, - isInventoryItem: false + isInventoryItem: false, + isItemLinkable: true }; } @@ -69,6 +72,10 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { if (this.metadata.isQuantifiable) schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true }); + if (this.metadata.isItemLinkable) { + schema.itemLinks = new ItemLinksField(); + } + return schema; } diff --git a/module/data/item/feature.mjs b/module/data/item/feature.mjs index 62b955e9..c01d100c 100644 --- a/module/data/item/feature.mjs +++ b/module/data/item/feature.mjs @@ -8,7 +8,8 @@ export default class DHFeature extends BaseDataItem { label: 'TYPES.Item.feature', type: 'feature', hasDescription: true, - hasResource: true + hasResource: true, + isItemLinkable: true }); } diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 6c3732db..d8a02335 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -3,6 +3,17 @@ * @extends {foundry.documents.Item} */ export default class DHItem extends foundry.documents.Item { + /**@inheritdoc */ + static async create(data, operation = {}) { + if (data.type === 'feature') { + const linkUuid = Object.keys(data) + .find(x => x.startsWith('system.itemLinks.')) + ?.slice(17); + } + + return super.create(data, operation); + } + /** @inheritDoc */ prepareEmbeddedDocuments() { super.prepareEmbeddedDocuments(); diff --git a/templates/sheets/items/ancestry/features.hbs b/templates/sheets/items/ancestry/features.hbs index 6bc108b1..2e84e652 100644 --- a/templates/sheets/items/ancestry/features.hbs +++ b/templates/sheets/items/ancestry/features.hbs @@ -12,12 +12,12 @@ {{#if document.system.primaryFeature}}
{{document.system.primaryFeature.name}}
- +
{{/if}} @@ -33,12 +33,12 @@ {{#if document.system.secondaryFeature}}
{{document.system.secondaryFeature.name}}
- +
{{/if}}