From eefaac63efabe9e0341b08c0e52ac424f8f835ba Mon Sep 17 00:00:00 2001 From: Haxxer Date: Mon, 29 Jul 2024 23:27:05 +0100 Subject: [PATCH 1/8] Added secret option, fixed character dropdown selection in give items --- changelog.md | 4 + languages/en.json | 1 + src/API/chat-api.js | 42 +- src/API/private-api.js | 8 +- .../give-items-dialog/give-items-shell.svelte | 28 +- src/systems/dnd5e-2.4.1.js | 467 +++++++++--------- src/systems/dnd5e.js | 3 +- 7 files changed, 290 insertions(+), 263 deletions(-) diff --git a/changelog.md b/changelog.md index 1e9d6a7b..2a015c91 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Item Piles Changelog +## Version 3.0.9 + +- Added support for secretly giving items to others via the right-click context menu on items on the D&D5e system + ## Version 3.0.8 - Fixed not being able to drop items onto scenes (thanks diwako on github!) diff --git a/languages/en.json b/languages/en.json index 318a239c..2173ecd0 100644 --- a/languages/en.json +++ b/languages/en.json @@ -267,6 +267,7 @@ "Header": "Giving Item: {item_name}", "SelectPlaceholder": "Pick character to send item to", "ContentMultipleQuantity": "You have {quantity} of this item, how many do you want to give away?", + "Secret": "Give without letting others know (except GMs)", "Submit": "Send" }, "ReceiveItem": { diff --git a/src/API/chat-api.js b/src/API/chat-api.js index 0b95f2b2..00cad161 100644 --- a/src/API/chat-api.js +++ b/src/API/chat-api.js @@ -8,7 +8,7 @@ import TradeAPI from "./trade-api.js"; export default class ChatAPI { - static CHAT_MESSAGE_STYLES = CONSTANTS.IS_V12 ? CONST.CHAT_MESSAGE_STYLES : CONST.CHAT_MESSAGE_TYPES; + static CHAT_MESSAGE_STYLES = CONSTANTS.IS_V12 ? CONST.CHAT_MESSAGE_TYPES : CONST.CHAT_MESSAGE_STYLES; static initialize() { @@ -152,12 +152,14 @@ export default class ChatAPI { * @param target * @param item * @param userId + * @param targetUserId + * @param secret * @returns {Promise} */ - static async _outputGiveItem(source, target, item, userId) { + static async _outputGiveItem(source, target, item, userId, targetUserId, secret) { if (game.user.id !== userId || !Helpers.getSetting(SETTINGS.OUTPUT_TO_CHAT)) return; const [itemData, itemCurrencies] = await this._formatItemData(source, [item]); - return this._giveChatMessage(source, target, itemData.concat(itemCurrencies), userId); + return this._giveChatMessage(source, target, itemData.concat(itemCurrencies), userId, targetUserId, secret); } /** @@ -434,7 +436,7 @@ export default class ChatAPI { return this._createNewChatMessage(game.user.id, { user: game.user.id, - type: isPrivate ? CONST.CHAT_MESSAGE_STYLES.WHISPER : ChatAPI.CHAT_MESSAGE_STYLES.OTHER, + type: isPrivate ? ChatAPI.CHAT_MESSAGE_STYLES.WHISPER : ChatAPI.CHAT_MESSAGE_STYLES.OTHER, content: chatCardHtml, flavor: "Item Piles" + (isPrivate ? ": " + game.i18n.localize("ITEM-PILES.Chat.PrivateTrade") : ""), speaker: ChatMessage.getSpeaker({ alias: game.user.name }), @@ -495,7 +497,7 @@ export default class ChatAPI { } - static async _giveChatMessage(source, target, items) { + static async _giveChatMessage(source, target, items, userId, targetUserId, secret) { const now = (+new Date()); @@ -510,7 +512,7 @@ export default class ChatAPI { for (const message of messages) { const flags = foundry.utils.getProperty(message, CONSTANTS.FLAGS.PILE); - if (flags && flags.version && !foundry.utils.isNewerVersion(Helpers.getModuleVersion(), flags.version) && flags.source === sourceActor.uuid && flags.target === targetActor.uuid && message.isAuthor) { + if (flags && flags.version && !foundry.utils.isNewerVersion(Helpers.getModuleVersion(), flags.version) && flags.source === sourceActor.uuid && flags.target === targetActor.uuid && message.isAuthor && (flags.secret === undefined || flags.secret === secret)) { return this._updateExistingGiveMessage(message, sourceActor, targetActor, items) } } @@ -522,19 +524,33 @@ export default class ChatAPI { items: items }); - return this._createNewChatMessage(game.user.id, { - user: game.user.id, + const user = game.users.get(userId); + + const chatData = { + user: user.id, type: ChatAPI.CHAT_MESSAGE_STYLES.OTHER, content: chatCardHtml, flavor: "Item Piles", - speaker: ChatMessage.getSpeaker({ alias: game.user.name }), + speaker: ChatMessage.getSpeaker({ alias: user.name }), [CONSTANTS.FLAGS.PILE]: { version: Helpers.getModuleVersion(), source: sourceActor.uuid, target: targetActor.uuid, - items: items + items: items, + secret } - }) + } + + if (secret) { + chatData.whisper = Array.from(game.users) + .filter(user => user.isGM) + .map(user => user.id); + chatData.whisper.push(userId); + chatData.whisper.push(targetUserId); + chatData.type = ChatAPI.CHAT_MESSAGE_STYLES.WHISPER; + } + + return this._createNewChatMessage(user.id, chatData) } @@ -631,9 +647,11 @@ export default class ChatAPI { if (mode === 2) { chatData.whisper.push(userId); } - chatData.type = CONST.CHAT_MESSAGE_STYLES.WHISPER; + chatData.type = ChatAPI.CHAT_MESSAGE_STYLES.WHISPER; } + } else if (chatData.whisper.length) { + chatData.whisper = Array.from(new Set(chatData.whisper)); } return ChatMessage.create(chatData); diff --git a/src/API/private-api.js b/src/API/private-api.js index 6dc9c0ba..62064666 100644 --- a/src/API/private-api.js +++ b/src/API/private-api.js @@ -1875,13 +1875,13 @@ export default class PrivateAPI { Helpers.custom_notify(game.i18n.format("ITEM-PILES.Notifications.ItemTransferred", { source_actor_name: sourceActor.name, target_actor_name: targetActor.name, item_name: item.name })); - Hooks.callAll(CONSTANTS.HOOKS.ITEM.GIVE, sourceActor, targetActor, dropData.itemData, game.user.id); + Hooks.callAll(CONSTANTS.HOOKS.ITEM.GIVE, sourceActor, targetActor, dropData.itemData, game.user.id, game.user.id, dropData?.secret); return this._transferItems(sourceUuid, targetUuid, [dropData.itemData], game.user.id) } } return ItemPileSocket.executeForUsers(ItemPileSocket.HANDLERS.GIVE_ITEMS, [user ? user.id : gms[0]], { - userId: game.user.id, sourceUuid, targetUuid, itemData: dropData.itemData + userId: game.user.id, sourceUuid, targetUuid, itemData: dropData.itemData, secret: dropData?.secret }); } } @@ -1911,10 +1911,10 @@ export default class PrivateAPI { } - static async _giveItemsResponse({ userId, accepted, sourceUuid, targetUuid, itemData } = {}) { + static async _giveItemsResponse({ userId, accepted, sourceUuid, targetUuid, itemData, secret } = {}) { const user = game.users.get(userId); if (accepted) { - await ItemPileSocket.callHook(CONSTANTS.HOOKS.ITEM.GIVE, sourceUuid, targetUuid, itemData, game.user.id, userId) + await ItemPileSocket.callHook(CONSTANTS.HOOKS.ITEM.GIVE, sourceUuid, targetUuid, itemData, game.user.id, userId, secret) await PrivateAPI._removeItems(sourceUuid, [itemData], game.user.id); return Helpers.custom_notify(game.i18n.format("ITEM-PILES.Notifications.GiveItemAccepted", { user_name: user.name })); } diff --git a/src/applications/dialogs/give-items-dialog/give-items-shell.svelte b/src/applications/dialogs/give-items-dialog/give-items-shell.svelte index 0f379cc7..aea87960 100644 --- a/src/applications/dialogs/give-items-dialog/give-items-shell.svelte +++ b/src/applications/dialogs/give-items-dialog/give-items-shell.svelte @@ -17,23 +17,26 @@ const canItemStack = PileUtilities.canItemStack(item); let quantity = 1; + let secret = false; let selectedActor = localStorage.getItem("item-piles-give-item") ?? false; let items = Array.from(game.actors) .filter(actor => { - return (actor.type === "character" || actor.type === "npc") - && actor !== item.parent - && !PileUtilities.isValidItemPile(actor) - && (game.user.isGM || (actor.ownership["default"] >= 1 || actor.ownership[game.user.id] >= 1)) + return game.user.isGM || (actor.ownership["default"] >= 1 || actor.ownership[game.user.id] >= 1); + }) + .concat(game.users.map(user => user.character).filter(Boolean)) + .filter(actor => actor !== item.parent) + .filter((actor, index, self) => { + return index === self.findIndex(a => a.uuid === actor.uuid) }) .map(actor => ({ value: actor.uuid, label: actor.name, actor, - group: actor.hasPlayerOwner ? "Player Characters" : "Other Characters" + group: actor.hasPlayerOwner ? "Player Characters" : "Unassigned Characters" })) .sort((a, b) => { - return a.name > b.name ? ((b.actor.hasPlayerOwner - a.actor.hasPlayerOwner) - 1) : ((b.actor.hasPlayerOwner - a.actor.hasPlayerOwner) + 1); + return (a.group >= b.group ? 100000 : -100000) + (a.label >= b.label ? 1 : -1); }); if (selectedActor && !items.some(data => data.value === selectedActor)) { @@ -48,7 +51,7 @@ function submit() { localStorage.setItem("item-piles-give-item", selectedActor.value) - application.options.resolve({ quantity, target: selectedActor.value }); + application.options.resolve({ quantity, secret, target: selectedActor.value }); application.close(); } @@ -73,7 +76,6 @@ {/if} -
+
+ + +
+
- - diff --git a/src/systems/dnd5e-2.4.1.js b/src/systems/dnd5e-2.4.1.js index c96cb994..8ba4a9fa 100644 --- a/src/systems/dnd5e-2.4.1.js +++ b/src/systems/dnd5e-2.4.1.js @@ -3,267 +3,268 @@ import PrivateAPI from "../API/private-api.js"; export default { - "VERSION": "1.0.7", + "VERSION": "1.0.7", - // The actor class type is the type of actor that will be used for the default item pile actor that is created on first item drop. - "ACTOR_CLASS_TYPE": "character", + // The actor class type is the type of actor that will be used for the default item pile actor that is created on first item drop. + "ACTOR_CLASS_TYPE": "character", // The item class type is the type of item that will be used for the default loot item "ITEM_CLASS_LOOT_TYPE": "loot", // The item class type is the type of item that will be used for the default weapon item - "ITEM_CLASS_WEAPON_TYPE": "weapon", + "ITEM_CLASS_WEAPON_TYPE": "weapon", // The item class type is the type of item that will be used for the default equipment item "ITEM_CLASS_EQUIPMENT_TYPE": "equipment", - // The item quantity attribute is the path to the attribute on items that denote how many of that item that exists - "ITEM_QUANTITY_ATTRIBUTE": "system.quantity", + // The item quantity attribute is the path to the attribute on items that denote how many of that item that exists + "ITEM_QUANTITY_ATTRIBUTE": "system.quantity", - // The item price attribute is the path to the attribute on each item that determine how much it costs - "ITEM_PRICE_ATTRIBUTE": "system.price.value", + // The item price attribute is the path to the attribute on each item that determine how much it costs + "ITEM_PRICE_ATTRIBUTE": "system.price.value", - // Item filters actively remove items from the item pile inventory UI that users cannot loot, such as spells, feats, and classes - "ITEM_FILTERS": [ - { - "path": "type", - "filters": "spell,feat,class,subclass,background" - }, - { - "path": "system.weaponType", - "filters": "natural" - } - ], + // Item filters actively remove items from the item pile inventory UI that users cannot loot, such as spells, feats, and classes + "ITEM_FILTERS": [ + { + "path": "type", + "filters": "spell,feat,class,subclass,background" + }, + { + "path": "system.weaponType", + "filters": "natural" + } + ], - // This function is an optional system handler that specifically transforms an item when it is added to actors - "ITEM_TRANSFORMER": async (itemData) => { - ["equipped", "proficient", "prepared"].forEach(key => { - if (itemData?.system?.[key] !== undefined) { - delete itemData.system[key]; - } - }); - foundry.utils.setProperty(itemData, "system.attunement", Math.min(CONFIG.DND5E.attunementTypes.REQUIRED, itemData?.system?.attunement ?? 0)); - if (itemData.type === "spell") { - try { - const scroll = await Item.implementation.createScrollFromSpell(itemData); - itemData = scroll.toObject(); - } catch (err) { - } - } - return itemData; - }, + // This function is an optional system handler that specifically transforms an item when it is added to actors + "ITEM_TRANSFORMER": async (itemData) => { + ["equipped", "proficient", "prepared"].forEach(key => { + if (itemData?.system?.[key] !== undefined) { + delete itemData.system[key]; + } + }); + foundry.utils.setProperty(itemData, "system.attunement", Math.min(CONFIG.DND5E.attunementTypes.REQUIRED, itemData?.system?.attunement ?? 0)); + if (itemData.type === "spell") { + try { + const scroll = await Item.implementation.createScrollFromSpell(itemData); + itemData = scroll.toObject(); + } catch (err) { + } + } + return itemData; + }, - // This function is an optional system handler that specifically transforms an item's price into a more unified numeric format - "ITEM_COST_TRANSFORMER": (item, currencies) => { - const overallCost = Number(foundry.utils.getProperty(item, "system.price.value")) ?? 0; - const priceDenomination = foundry.utils.getProperty(item, "system.price.denomination"); - if (priceDenomination) { - const currencyDenomination = currencies - .filter(currency => currency.type === "attribute") - .find(currency => { - return currency.data.path.toLowerCase().endsWith(priceDenomination); - }); - if (currencyDenomination) { - return overallCost * currencyDenomination.exchangeRate; - } - } - return overallCost ?? 0; - }, + // This function is an optional system handler that specifically transforms an item's price into a more unified numeric format + "ITEM_COST_TRANSFORMER": (item, currencies) => { + const overallCost = Number(foundry.utils.getProperty(item, "system.price.value")) ?? 0; + const priceDenomination = foundry.utils.getProperty(item, "system.price.denomination"); + if (priceDenomination) { + const currencyDenomination = currencies + .filter(currency => currency.type === "attribute") + .find(currency => { + return currency.data.path.toLowerCase().endsWith(priceDenomination); + }); + if (currencyDenomination) { + return overallCost * currencyDenomination.exchangeRate; + } + } + return overallCost ?? 0; + }, - "PRICE_MODIFIER_TRANSFORMER": ({ - buyPriceModifier, - sellPriceModifier, - actor = false, - actorPriceModifiers = [] - } = {}) => { + "PRICE_MODIFIER_TRANSFORMER": ({ + buyPriceModifier, + sellPriceModifier, + actor = false, + actorPriceModifiers = [] + } = {}) => { - const modifiers = { - buyPriceModifier, - sellPriceModifier - }; + const modifiers = { + buyPriceModifier, + sellPriceModifier + }; - if (!actor) return modifiers; + if (!actor) return modifiers; - const groupModifiers = actorPriceModifiers - .map(data => ({ ...data, actor: fromUuidSync(data.actorUuid) })) - .filter(data => { - return data.actor && data.actor.type === "group" && data.actor.system.members.some(member => member === actor) - }); + const groupModifiers = actorPriceModifiers + .map(data => ({ ...data, actor: fromUuidSync(data.actorUuid) })) + .filter(data => { + return data.actor && data.actor.type === "group" && data.actor.system.members.some(member => member === actor) + }); - modifiers.buyPriceModifier = groupModifiers.reduce((acc, data) => { - return data.override ? data.buyPriceModifier ?? acc : acc * data.buyPriceModifier; - }, buyPriceModifier); + modifiers.buyPriceModifier = groupModifiers.reduce((acc, data) => { + return data.override ? data.buyPriceModifier ?? acc : acc * data.buyPriceModifier; + }, buyPriceModifier); - modifiers.sellPriceModifier = groupModifiers.reduce((acc, data) => { - return data.override ? data.sellPriceModifier ?? acc : acc * data.sellPriceModifier; - }, sellPriceModifier); + modifiers.sellPriceModifier = groupModifiers.reduce((acc, data) => { + return data.override ? data.sellPriceModifier ?? acc : acc * data.sellPriceModifier; + }, sellPriceModifier); - return modifiers; + return modifiers; - }, + }, - "SOFT_MIGRATIONS": { - "1.0.6-1.0.7": { - "ITEM_FILTERS": [ - { - "path": "type", - "filters": "spell,feat,class,subclass,background,race" - } - ] - } - }, + "SOFT_MIGRATIONS": { + "1.0.6-1.0.7": { + "ITEM_FILTERS": [ + { + "path": "type", + "filters": "spell,feat,class,subclass,background,race" + } + ] + } + }, - "PILE_DEFAULTS": { - merchantColumns: [{ - label: "", - path: "system.equipped", - formatting: "{#}", - buying: false, - selling: true, - mapping: { - "true": "✔", - "false": "" - } - }, { - label: "Rarity", - path: "system.rarity", - formatting: "{#}", - buying: true, - selling: true, - mapping: { - "common": "DND5E.ItemRarityCommon", - "uncommon": "DND5E.ItemRarityUncommon", - "rare": "DND5E.ItemRarityRare", - "veryRare": "DND5E.ItemRarityVeryRare", - "legendary": "DND5E.ItemRarityLegendary", - "artifact": "DND5E.ItemRarityArtifact" - } - }] - }, + "PILE_DEFAULTS": { + merchantColumns: [{ + label: "", + path: "system.equipped", + formatting: "{#}", + buying: false, + selling: true, + mapping: { + "true": "✔", + "false": "" + } + }, { + label: "Rarity", + path: "system.rarity", + formatting: "{#}", + buying: true, + selling: true, + mapping: { + "common": "DND5E.ItemRarityCommon", + "uncommon": "DND5E.ItemRarityUncommon", + "rare": "DND5E.ItemRarityRare", + "veryRare": "DND5E.ItemRarityVeryRare", + "legendary": "DND5E.ItemRarityLegendary", + "artifact": "DND5E.ItemRarityArtifact" + } + }] + }, - // Item similarities determines how item piles detect similarities and differences in the system - "ITEM_SIMILARITIES": ["name", "type"], + // Item similarities determines how item piles detect similarities and differences in the system + "ITEM_SIMILARITIES": ["name", "type"], - // Currencies in item piles is a versatile system that can accept actor attributes (a number field on the actor's sheet) or items (actual items in their inventory) - // In the case of attributes, the path is relative to the "actor.system" - // In the case of items, it is recommended you export the item with `.toObject()` and strip out any module data - "CURRENCIES": [ - { - type: "attribute", - name: "DND5E.CurrencyPP", - img: "icons/commodities/currency/coin-inset-snail-silver.webp", - abbreviation: "{#}PP", - data: { - path: "system.currency.pp" - }, - primary: false, - exchangeRate: 10 - }, - { - type: "attribute", - name: "DND5E.CurrencyGP", - img: "icons/commodities/currency/coin-embossed-crown-gold.webp", - abbreviation: "{#}GP", - data: { - path: "system.currency.gp", - }, - primary: true, - exchangeRate: 1 - }, - { - type: "attribute", - name: "DND5E.CurrencyEP", - img: "icons/commodities/currency/coin-inset-copper-axe.webp", - abbreviation: "{#}EP", - data: { - path: "system.currency.ep", - }, - primary: false, - exchangeRate: 0.5 - }, - { - type: "attribute", - name: "DND5E.CurrencySP", - img: "icons/commodities/currency/coin-engraved-moon-silver.webp", - abbreviation: "{#}SP", - data: { - path: "system.currency.sp", - }, - primary: false, - exchangeRate: 0.1 - }, - { - type: "attribute", - name: "DND5E.CurrencyCP", - img: "icons/commodities/currency/coin-engraved-waves-copper.webp", - abbreviation: "{#}CP", - data: { - path: "system.currency.cp", - }, - primary: false, - exchangeRate: 0.01 - } - ], + // Currencies in item piles is a versatile system that can accept actor attributes (a number field on the actor's sheet) or items (actual items in their inventory) + // In the case of attributes, the path is relative to the "actor.system" + // In the case of items, it is recommended you export the item with `.toObject()` and strip out any module data + "CURRENCIES": [ + { + type: "attribute", + name: "DND5E.CurrencyPP", + img: "icons/commodities/currency/coin-inset-snail-silver.webp", + abbreviation: "{#}PP", + data: { + path: "system.currency.pp" + }, + primary: false, + exchangeRate: 10 + }, + { + type: "attribute", + name: "DND5E.CurrencyGP", + img: "icons/commodities/currency/coin-embossed-crown-gold.webp", + abbreviation: "{#}GP", + data: { + path: "system.currency.gp", + }, + primary: true, + exchangeRate: 1 + }, + { + type: "attribute", + name: "DND5E.CurrencyEP", + img: "icons/commodities/currency/coin-inset-copper-axe.webp", + abbreviation: "{#}EP", + data: { + path: "system.currency.ep", + }, + primary: false, + exchangeRate: 0.5 + }, + { + type: "attribute", + name: "DND5E.CurrencySP", + img: "icons/commodities/currency/coin-engraved-moon-silver.webp", + abbreviation: "{#}SP", + data: { + path: "system.currency.sp", + }, + primary: false, + exchangeRate: 0.1 + }, + { + type: "attribute", + name: "DND5E.CurrencyCP", + img: "icons/commodities/currency/coin-engraved-waves-copper.webp", + abbreviation: "{#}CP", + data: { + path: "system.currency.cp", + }, + primary: false, + exchangeRate: 0.01 + } + ], - "VAULT_STYLES": [ - { - path: "system.rarity", - value: "artifact", - styling: { - "box-shadow": "inset 0px 0px 7px 0px rgba(255,191,0,1)" - } - }, - { - path: "system.rarity", - value: "legendary", - styling: { - "box-shadow": "inset 0px 0px 7px 0px rgba(255,119,0,1)" - } - }, - { - path: "system.rarity", - value: "veryRare", - styling: { - "box-shadow": "inset 0px 0px 7px 0px rgba(255,0,247,1)" - } - }, - { - path: "system.rarity", - value: "rare", - styling: { - "box-shadow": "inset 0px 0px 7px 0px rgba(0,136,255,1)" - } - }, - { - path: "system.rarity", - value: "uncommon", - styling: { - "box-shadow": "inset 0px 0px 7px 0px rgba(0,255,9,1)" - } - } - ], + "VAULT_STYLES": [ + { + path: "system.rarity", + value: "artifact", + styling: { + "box-shadow": "inset 0px 0px 7px 0px rgba(255,191,0,1)" + } + }, + { + path: "system.rarity", + value: "legendary", + styling: { + "box-shadow": "inset 0px 0px 7px 0px rgba(255,119,0,1)" + } + }, + { + path: "system.rarity", + value: "veryRare", + styling: { + "box-shadow": "inset 0px 0px 7px 0px rgba(255,0,247,1)" + } + }, + { + path: "system.rarity", + value: "rare", + styling: { + "box-shadow": "inset 0px 0px 7px 0px rgba(0,136,255,1)" + } + }, + { + path: "system.rarity", + value: "uncommon", + styling: { + "box-shadow": "inset 0px 0px 7px 0px rgba(0,255,9,1)" + } + } + ], - "SYSTEM_HOOKS": () => { + "SYSTEM_HOOKS": () => { - Hooks.on("dnd5e.getItemContextOptions", (item, options) => { - options.push({ - name: "Give to character", - icon: "", - callback: async () => { - const result = await GiveItems.show(item); - if (!result) return; - PrivateAPI._giveItem({ - itemData: { - item: item.toObject(), - quantity: result.quantity - }, - source: item.parent.uuid, - target: result.target, - }, { skipQuantityDialog: true }) - }, - condition: !game.itempiles.API.isItemInvalid(item) - }) - }); + Hooks.on("dnd5e.getItemContextOptions", (item, options) => { + options.push({ + name: "Give to character", + icon: "", + callback: async () => { + const result = await GiveItems.show(item); + if (!result) return; + PrivateAPI._giveItem({ + itemData: { + item: item.toObject(), + quantity: result.quantity + }, + source: item.parent.uuid, + target: result.target, + secret: result.secret, + }, { skipQuantityDialog: true }) + }, + condition: !game.itempiles.API.isItemInvalid(item) + }) + }); - } + } } diff --git a/src/systems/dnd5e.js b/src/systems/dnd5e.js index e45a46e4..a8efc173 100644 --- a/src/systems/dnd5e.js +++ b/src/systems/dnd5e.js @@ -12,7 +12,7 @@ export default { "ITEM_CLASS_LOOT_TYPE": "loot", // The item class type is the type of item that will be used for the default weapon item - "ITEM_CLASS_WEAPON_TYPE": "weapon", + "ITEM_CLASS_WEAPON_TYPE": "weapon", // The item class type is the type of item that will be used for the default equipment item "ITEM_CLASS_EQUIPMENT_TYPE": "equipment", @@ -248,6 +248,7 @@ export default { }, source: item.parent.uuid, target: result.target, + secret: result.secret, }, { skipQuantityDialog: true }) }, condition: !game.itempiles.API.isItemInvalid(item) From 13ed3718ef4d8ad39c988fd78e69b18d21423fa5 Mon Sep 17 00:00:00 2001 From: Haxxer Date: Mon, 29 Jul 2024 23:38:21 +0100 Subject: [PATCH 2/8] Fixed previewing items --- changelog.md | 1 + src/stores/pile-item.js | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 2a015c91..0b34f841 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## Version 3.0.9 - Added support for secretly giving items to others via the right-click context menu on items on the D&D5e system +- Fixed some systems not allowing players to preview items by clicking on the names in item piles when the player had no permissions configured on the item pile ## Version 3.0.8 diff --git a/src/stores/pile-item.js b/src/stores/pile-item.js index 426925bf..101e9e54 100644 --- a/src/stores/pile-item.js +++ b/src/stores/pile-item.js @@ -213,11 +213,14 @@ export class PileItem extends PileBaseItem { return; } } - if (game.user.isGM || this.item.permission[game.user.id] === 3) { + if (game.user.isGM || this.item.ownership[game.user.id] === 3) { return this.item.sheet.render(true); } - const cls = this.item._getSheetClass(); - const sheet = new cls(this.item, { editable: false }); + const itemData = this.item.toObject(); + itemData.ownership[game.user.id] = 1; + const newItem = new Item.implementation(itemData); + const cls = newItem._getSheetClass(); + const sheet = new cls(newItem, { editable: false }); return sheet._render(true); } } From 74388280147bacb7862d19f7a8cde3f328bb5ef5 Mon Sep 17 00:00:00 2001 From: Haxxer Date: Mon, 29 Jul 2024 23:46:33 +0100 Subject: [PATCH 3/8] Fixed macro compendiums --- changelog.md | 1 + src/helpers/utilities.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 0b34f841..1f4a801e 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ - Added support for secretly giving items to others via the right-click context menu on items on the D&D5e system - Fixed some systems not allowing players to preview items by clicking on the names in item piles when the player had no permissions configured on the item pile +- Fixed item pile on interact macros not working when utilizing module or system macro compendiums ## Version 3.0.8 diff --git a/src/helpers/utilities.js b/src/helpers/utilities.js index 91d69e80..11cda604 100644 --- a/src/helpers/utilities.js +++ b/src/helpers/utilities.js @@ -353,7 +353,9 @@ export async function runMacro(macroId, macroData) { if (!compendium) { throw Helpers.custom_error(`Compendium ${packArray[1]}.${packArray[2]} was not found`); } - let findMacro = (await compendium.getDocuments()).find(m => m.name === packArray[3] || m.id === packArray[3]) + let findMacro = (await compendium.getDocuments()).find(m => { + return m.name === packArray[3] || m.id === packArray[3] || m.id === packArray[4]; + }) if (!findMacro) { throw Helpers.custom_error(`The "${packArray[3]}" macro was not found in Compendium ${packArray[1]}.${packArray[2]}`); } From 0b86cf9e7657be0151e4bd50fd938672e455381e Mon Sep 17 00:00:00 2001 From: Haxxer Date: Mon, 29 Jul 2024 23:58:22 +0100 Subject: [PATCH 4/8] Fixed updating item piles tokens resetting back to normal tokens --- changelog.md | 1 + src/API/private-api.js | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 1f4a801e..591c8c21 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ - Added support for secretly giving items to others via the right-click context menu on items on the D&D5e system - Fixed some systems not allowing players to preview items by clicking on the names in item piles when the player had no permissions configured on the item pile - Fixed item pile on interact macros not working when utilizing module or system macro compendiums +- Fixed updating item piles tokens would sometimes reset it to a default non-item pile token ## Version 3.0.8 diff --git a/src/API/private-api.js b/src/API/private-api.js index 62064666..89df11d5 100644 --- a/src/API/private-api.js +++ b/src/API/private-api.js @@ -151,11 +151,12 @@ export default class PrivateAPI { } static _onPreUpdateToken(doc, changes) { - if (!foundry.utils.hasProperty(changes, "actorLink")) return; + const diff = foundry.utils.diffObject(doc, changes); + if (!foundry.utils.hasProperty(diff, "actorLink")) return; if (!PileUtilities.isValidItemPile(doc)) return; const flagData = PileUtilities.getActorFlagData(doc); const cleanFlagData = PileUtilities.cleanFlagData(flagData); - changes[CONSTANTS.FLAGS.PILE] = doc.actorLink ? cleanFlagData : null; + changes[CONSTANTS.FLAGS.PILE] = diff.actorLink ? cleanFlagData : null; } /** @@ -1477,7 +1478,8 @@ export default class PrivateAPI { * @private */ static async _evaluateItemPileChange(doc, changes = {}, force = false) { - const duplicatedChanges = foundry.utils.deepClone(changes); + const diff = foundry.utils.diffObject(doc, changes); + const duplicatedChanges = foundry.utils.deepClone(diff); const target = doc?.token ?? doc; if (!Helpers.isResponsibleGM()) return; if (!force && !PileUtilities.shouldEvaluateChange(target, duplicatedChanges)) return; From 60a01949e69fd732938576d295b6f9acf6c7a358 Mon Sep 17 00:00:00 2001 From: Haxxer Date: Tue, 30 Jul 2024 00:13:41 +0100 Subject: [PATCH 5/8] Fixed tokens not being deleted --- changelog.md | 1 + .../item-pile-inventory-shell.svelte | 8 ++++---- src/helpers/pile-utilities.js | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 591c8c21..1bb11830 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ - Fixed some systems not allowing players to preview items by clicking on the names in item piles when the player had no permissions configured on the item pile - Fixed item pile on interact macros not working when utilizing module or system macro compendiums - Fixed updating item piles tokens would sometimes reset it to a default non-item pile token +- Fixed item piles not being deleted after being emptied even though they were configured to be deleted ## Version 3.0.8 diff --git a/src/applications/item-pile-inventory-app/item-pile-inventory-shell.svelte b/src/applications/item-pile-inventory-app/item-pile-inventory-shell.svelte index 640c857f..79815333 100644 --- a/src/applications/item-pile-inventory-app/item-pile-inventory-shell.svelte +++ b/src/applications/item-pile-inventory-app/item-pile-inventory-shell.svelte @@ -176,13 +176,13 @@ {/if}