export class ItemL5r5e extends Item { /** * A reference to the Collection of embedded Item instances in the document, indexed by _id. * @returns {Collection} */ get items() { return this.data.data.items || null; } /** * Return the linked Actor instance if any (current or embed) * @return {Actor|null} */ get actor() { return super.actor || game.actors.get(this.data.data.parent_id?.actor_id) || null; } /** * Create a new entity using provided input data * @override */ static async create(data, context = {}) { if (data.img === undefined) { data.img = `${CONFIG.l5r5e.paths.assets}icons/items/${data.type}.svg`; } return super.create(data, context); } /** * Update this Document using incremental data, saving it to the database. * @see {@link Document.updateDocuments} * @param {object} [data={}] Differential update data which modifies the existing values of this document data * @param {DocumentModificationContext} [context={}] Additional context which customizes the update workflow * @returns {Promise} The updated Document instance */ async update(data = {}, context = {}) { // Regular if (!this.data.data.parent_id) { return super.update(data, context); } // **** Embed Items, need to get the parents **** const parentItem = this.getItemFromParentId(); if (!parentItem) { console.warn(`Embed parentItem not found`); return; } // Merge (DocumentData cannot be set) const result = foundry.utils.mergeObject(this.data, foundry.utils.expandObject(data)); if (result.name) { this.data.name = result.name; } if (result.img) { this.data.img = result.img; } if (result.data) { this.data.data = result.data; } // Update await parentItem.updateEmbedItem(this.data.toObject(false)); // Return new value for sheet return new Promise((resolve) => resolve(this)); } /** @override */ prepareData() { super.prepareData(); // Prepare Embed items if (!(this.data.data.items instanceof Map)) { const itemsData = Array.isArray(this.data.data.items) ? this.data.data.items : []; this.data.data.items = new Map(); itemsData.forEach((item) => { this.addEmbedItem(item, { save: false, newId: false, addBonusToActor: false }); }); } } // ***** parent ids management ***** /** * Return a string with idemId + actorId if any * @return {{item_id: (string|null), actor_id?: (string|null)}} */ getParentsIds() { const parent = { item_id: this.id, }; if (this.actor?.data?._id) { parent.actor_id = this.actor.data._id; } return parent; } /** * Return the Item Object for the "parentId" * @return {ItemL5r5e|null} */ getItemFromParentId() { const parentIds = this.data.data.parent_id; let parentItem; if (parentIds?.actor_id) { // Actor item object const parentActor = parentIds.actor_id ? game.actors.get(parentIds.actor_id) : null; parentItem = parentActor?.items.get(parentIds.item_id); } else if (parentIds?.item_id) { // World Object parentItem = game.items.get(parentIds.item_id); } return parentItem; } // ***** Embedded items management ***** /** * Shortcut for this.data.data.items.get * @param id * @return {ItemL5r5e|null} */ getEmbedItem(id) { return this.items?.get(id) || null; } /** * Add a Embed Item * @param {ItemL5r5e} item Object to add * @param {boolean} save if we save in db or not (used internally) * @param {boolean} newId if we change the id * @param {boolean} addBonusToActor if we update the actor bonus for advancements * @return {Promise} */ async addEmbedItem(item, { save = true, newId = true, addBonusToActor = true } = {}) { if (!item) { return; } if (!(item instanceof Item) && item._id) { // Data -> Item item = new ItemL5r5e(item); } // New id if (newId) { item.data._id = foundry.utils.randomID(); } // Tag parent (flags won't work as we have no id in db) item.data.data.parent_id = this.getParentsIds(); // Object this.data.data.items.set(item.data._id, item); // Add bonus to actor if (addBonusToActor) { const actor = this.actor; if (item instanceof Item && actor instanceof Actor) { actor.addBonus(item); } } if (save) { await this.saveEmbedItems(); } } /** * Update a Embed Item * @param {ItemL5r5e} item Object to add * @param {boolean} save if we save in db or not (used internally) * @return {Promise} */ async updateEmbedItem(item, { save = true } = {}) { await this.addEmbedItem(item, { save, newId: false, addBonusToActor: false }); } /** * Delete the Embed Item and clear the actor bonus if any * @param id Item id * @param {boolean} save if we save in db or not (used internally) * @param {boolean} removeBonusFromActor if we update the actor bonus for advancements * @return {Promise} */ async deleteEmbedItem(id, { save = true, removeBonusFromActor = true } = {}) { if (!this.data.data.items.has(id)) { return; } // Remove bonus from actor if (removeBonusFromActor) { const actor = this.actor; const item = this.data.data.items.get(id); if (item instanceof Item && actor instanceof Actor) { actor.removeBonus(item); } } // Remove the embed item this.data.data.items.delete(id); if (save) { await this.saveEmbedItems(); } } /** * Save all the Embed Items * @return {Promise} */ async saveEmbedItems() { await this.update({ "data.items": Array.from(this.data.data.items).map(([id, item]) => item.data.toObject(false)), }); this.sheet.render(false); } }