From b4255b3f9efaf7be381d52b055311e187b2aaee3 Mon Sep 17 00:00:00 2001 From: Haxxer Date: Mon, 3 Oct 2022 21:14:48 +0100 Subject: [PATCH] Fixed currency pickup --- changelog.md | 1 + src/API/chat-api.js | 190 ++++++++++++++++++++-------------------- src/stores/pile-item.js | 56 ++++++------ 3 files changed, 124 insertions(+), 123 deletions(-) diff --git a/changelog.md b/changelog.md index b3e94d5c..ee90cdb0 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ - Fixed merchant items not being alphabetically sorted by default - Fixed dropping spells in certain interfaces would not turn them into spell scrolls (DnD5e) - Fixed mystified items not staying mystified in the trading interface (PF2e) +- Fixed currencies not showing up in loot chat cards ## Version 2.2.2 diff --git a/src/API/chat-api.js b/src/API/chat-api.js index 934f801e..d3be67c9 100644 --- a/src/API/chat-api.js +++ b/src/API/chat-api.js @@ -8,9 +8,9 @@ import * as Utilities from "../helpers/utilities.js"; import TradeAPI from "./trade-api.js"; export default class ChatAPI { - + static initialize() { - + Hooks.on("preCreateChatMessage", this._preCreateChatMessage.bind(this)); Hooks.on("renderChatMessage", this._renderChatMessage.bind(this)); Hooks.on(HOOKS.ITEM.TRANSFER, this._outputTransferItem.bind(this)); @@ -20,40 +20,40 @@ export default class ChatAPI { Hooks.on(HOOKS.TRADE.STARTED, this._outputTradeStarted.bind(this)); Hooks.on(HOOKS.TRADE.COMPLETE, this._outputTradeComplete.bind(this)); Hooks.on(HOOKS.ITEM.TRADE, this._outputMerchantTradeComplete.bind(this)); - + $(document).on("click", ".item-piles-chat-card .item-piles-collapsible", async function () { if ($(this).attr("open")) return; await Helpers.wait(25); $(this).parent()[0].scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' }); }); } - + static _preCreateChatMessage(chatMessage) { - + if (!Helpers.getSetting(SETTINGS.ENABLE_TRADING)) return; - + const content = chatMessage.content.toLowerCase(); - + if (!(content.startsWith("!itempiles") || content.startsWith("!ip"))) return; - + const args = content.split(" ").slice(1); - + if (args[0] === "trade") { setTimeout(() => { game.itempiles.API.requestTrade(); }); } - + return false; - + } - + static _renderChatMessage(app, html) { html.find(".item-piles-specate-trade").click(function () { game.itempiles.API.spectateTrade($(this).data()); }); } - + static _disableTradingButton(publicTradeId) { const message = Array.from(game.messages).find(message => { return getProperty(message, CONSTANTS.FLAGS.PUBLIC_TRADE_ID) === publicTradeId; @@ -62,14 +62,14 @@ export default class ChatAPI { const update = this._replaceChatContent(message); return message.update(update) } - + static async disablePastTradingButtons() { if (!game.user.isGM) return; - + const messages = Array.from(game.messages).filter(message => { return getProperty(message, CONSTANTS.FLAGS.PUBLIC_TRADE_ID); }); - + if (!messages.length) return; const updates = []; for (let message of messages) { @@ -87,13 +87,13 @@ export default class ChatAPI { } } } - + if (!updates.length) return; - + return ChatMessage.updateDocuments(updates); - + } - + static _replaceChatContent(message) { const tradeId = getProperty(message, CONSTANTS.FLAGS.PUBLIC_TRADE_ID); const stringToFind = `data-trade-id="${tradeId}"`; @@ -107,8 +107,8 @@ export default class ChatAPI { [`flags.-=${CONSTANTS.MODULE_NAME}`]: null }; } - - + + /** * Outputs to chat based on transferring an item from or to an item pile * @@ -125,7 +125,7 @@ export default class ChatAPI { const itemData = await this._formatItemData(items); return ItemPileSocket.executeAsGM(ItemPileSocket.HANDLERS.PICKUP_CHAT_MESSAGE, source.uuid, target.uuid, itemData, [], userId, interactionId); } - + /** * Outputs to chat based on transferring a currency from or to an item pile * @@ -142,7 +142,7 @@ export default class ChatAPI { const currencyData = this._formatCurrencyData(source, currencies); return ItemPileSocket.executeAsGM(ItemPileSocket.HANDLERS.PICKUP_CHAT_MESSAGE, source.uuid, target.uuid, [], currencyData, userId, interactionId); } - + /** * Outputs to chat based on transferring everything from or to an item pile * @@ -161,30 +161,30 @@ export default class ChatAPI { const currencyData = this._formatCurrencyData(source, currencies); return ItemPileSocket.executeAsGM(ItemPileSocket.HANDLERS.PICKUP_CHAT_MESSAGE, source.uuid, target.uuid, itemData, currencyData, userId, interactionId); } - + static _outputSplitItemPileInventory(source, transferData, userId) { if (!PileUtilities.isValidItemPile(source)) return; if (game.user.id !== userId || !Helpers.getSetting(SETTINGS.OUTPUT_TO_CHAT)) return; return ItemPileSocket.executeAsGM(ItemPileSocket.HANDLERS.SPLIT_CHAT_MESSAGE, source.uuid, transferData, userId); } - + static async _outputTradeStarted(party_1, party_2, publicTradeId, isPrivate) { if (party_1.user !== game.user.id || !Helpers.getSetting(SETTINGS.OUTPUT_TO_CHAT) || isPrivate) return; return this._outputTradeStartedToChat(party_1, party_2, publicTradeId); } - + static async _outputTradeComplete(party_1, party_2, publicTradeId, isPrivate) { if (!Helpers.getSetting(SETTINGS.OUTPUT_TO_CHAT)) return; return this._outputTradeCompleteToChat(party_1, party_2, publicTradeId, isPrivate); } - + static async _outputMerchantTradeComplete(source, target, priceInformation, userId, interactionId) { if (!Helpers.getSetting(SETTINGS.OUTPUT_TO_CHAT)) return; if (!PileUtilities.isItemPileMerchant(source)) return; if (!interactionId || game.user.id !== userId || !Helpers.getSetting(SETTINGS.OUTPUT_TO_CHAT)) return; return ItemPileSocket.executeAsGM(ItemPileSocket.HANDLERS.MERCHANT_TRADE_CHAT_MESSAGE, source.uuid, target.uuid, priceInformation, userId, interactionId); } - + /** * Formats item data to a chat friendly structure * @@ -203,7 +203,7 @@ export default class ChatAPI { } return formattedItems; } - + /** * Formats currency data to a chat friendly structure * @@ -212,7 +212,7 @@ export default class ChatAPI { * @returns {Array} */ static _formatCurrencyData(itemPile, currencies) { - const currencyList = PileUtilities.getActorCurrencies(itemPile); + const currencyList = PileUtilities.getActorCurrencies(itemPile, { getAll: true }); return Object.entries(currencies).map(entry => { const currency = currencyList.find(currency => currency.id === entry[0]); return { @@ -223,7 +223,7 @@ export default class ChatAPI { } }); } - + /** * Outputs the transferred data in chat * @@ -236,23 +236,23 @@ export default class ChatAPI { * @returns {Promise} */ static async _outputPickupToChat(sourceUuid, targetUuid, items, currencies, userId, interactionId) { - + const sourceActor = Utilities.getActor(sourceUuid); const targetActor = Utilities.getActor(targetUuid); - + const now = (+new Date()); - + // Get all messages younger than 3 hours, and grab the last 10, then reverse them (latest to oldest) const messages = Array.from(game.messages).filter(message => (now - message.timestamp) <= (10800000)).slice(-10); messages.reverse() - + for (let [index, message] of messages.entries()) { const flags = getProperty(message, CONSTANTS.FLAGS.PILE); if (flags && flags.source === sourceUuid && flags.target === targetUuid && (flags.interactionId === interactionId || index === 0)) { return this._updateExistingPickupMessage(message, sourceActor, targetActor, items, currencies, interactionId) } } - + const chatCardHtml = await renderTemplate(CONSTANTS.PATH + "templates/chat/looted.html", { message: game.i18n.format("ITEM-PILES.Chat.Pickup", { name: targetActor.name }), itemPile: sourceActor, @@ -260,7 +260,7 @@ export default class ChatAPI { items: items, currencies: currencies }); - + return this._createNewChatMessage(userId, { user: game.user.id, type: CONST.CHAT_MESSAGE_TYPES.OTHER, @@ -275,11 +275,11 @@ export default class ChatAPI { interactionId: interactionId } }) - + } - + static _matchEntries(existingEntries, incomingEntries) { - + const combinedEntries = existingEntries.map(existingEntry => { const foundEntry = incomingEntries.find(item => item.name === existingEntry.name && existingEntry.img === item.img); if (foundEntry) { @@ -288,24 +288,24 @@ export default class ChatAPI { } return existingEntry; }); - + incomingEntries.forEach(item => combinedEntries.push(item)); - + return combinedEntries; - + } - + static async _updateExistingPickupMessage(message, sourceActor, targetActor, items, currencies, interactionId) { - + const flags = getProperty(message, CONSTANTS.FLAGS.PILE); - + const newItems = this._matchEntries(flags.items, items); const newCurrencies = this._matchEntries(flags.currencies, currencies); - + newCurrencies.sort((a, b) => { return a.index - b.index; }) - + const chatCardHtml = await renderTemplate(CONSTANTS.PATH + "templates/chat/looted.html", { message: game.i18n.format("ITEM-PILES.Chat.Pickup", { name: targetActor.name }), itemPile: sourceActor, @@ -313,29 +313,29 @@ export default class ChatAPI { items: newItems, currencies: newCurrencies }); - + return message.update({ content: chatCardHtml, [`${CONSTANTS.FLAGS.PILE}.interactionId`]: interactionId, [`${CONSTANTS.FLAGS.PILE}.items`]: newItems, [`${CONSTANTS.FLAGS.PILE}.currencies`]: newCurrencies, }); - + } - + static async _outputSplitToChat(sourceUuid, transferData, userId) { - + const source = await fromUuid(sourceUuid); - + const sourceActor = source?.actor ?? source; - + const chatCardHtml = await renderTemplate(CONSTANTS.PATH + "templates/chat/looted.html", { message: game.i18n.format("ITEM-PILES.Chat.Split", { num_players: transferData.num_players }), itemPile: sourceActor, items: transferData.items, currencies: transferData.currencies }); - + return this._createNewChatMessage(userId, { user: game.user.id, type: CONST.CHAT_MESSAGE_TYPES.OTHER, @@ -343,21 +343,21 @@ export default class ChatAPI { flavor: "Item Piles", speaker: ChatMessage.getSpeaker({ alias: game.user.name }) }); - + } - + static async _outputTradeStartedToChat(party_1, party_2, publicTradeId) { - + const party_1_actor = Utilities.getActor(party_1.actor); const party_2_actor = Utilities.getActor(party_2.actor); - + const chatCardHtml = await renderTemplate(CONSTANTS.PATH + "templates/chat/trade-started.html", { party_1_actor, party_2_actor, publicTradeId, userId: game.user.id }); - + return this._createNewChatMessage(game.user.id, { user: game.user.id, type: CONST.CHAT_MESSAGE_TYPES.OTHER, @@ -368,11 +368,11 @@ export default class ChatAPI { [CONSTANTS.FLAGS.TRADE_USERS]: [party_1.user, party_2.user] }); } - + static async _outputTradeCompleteToChat(party_1, party_2, publicTradeId, isPrivate) { - + if (party_1.user !== game.user.id) return; - + let party_1_actor = await fromUuid(party_1.actor); party_1_actor = party_1_actor?.actor ?? party_1_actor; const party_1_data = { @@ -381,7 +381,7 @@ export default class ChatAPI { currencies: party_2.currencies } party_1_data.got_nothing = !party_1_data.items.length && !party_1_data.currencies.length; - + let party_2_actor = await fromUuid(party_2.actor); party_2_actor = party_2_actor?.actor ?? party_2_actor; const party_2_data = { @@ -390,11 +390,11 @@ export default class ChatAPI { currencies: party_1.currencies } party_2_data.got_nothing = !party_2_data.items.length && !party_2_data.currencies.length; - + if (party_1.got_nothing && party_2.got_nothing) return; - + const enableCollapse = (party_1_data.items.length + party_1_data.currencies.length + party_2_data.items.length + party_2_data.currencies.length) > 6; - + const chatCardHtml = await renderTemplate(CONSTANTS.PATH + "templates/chat/trade-complete.html", { party_1: party_1_data, party_2: party_2_data, @@ -402,7 +402,7 @@ export default class ChatAPI { isPrivate, enableCollapse }); - + return this._createNewChatMessage(game.user.id, { user: game.user.id, type: isPrivate ? CONST.CHAT_MESSAGE_TYPES.WHISPER : CONST.CHAT_MESSAGE_TYPES.OTHER, @@ -411,31 +411,31 @@ export default class ChatAPI { speaker: ChatMessage.getSpeaker({ alias: game.user.name }), whisper: isPrivate ? [party_2.user] : [] }); - + } - + static async _outputMerchantTradeToChat(sourceUuid, targetUuid, priceInformation, userId, interactionId) { - + const sourceActor = Utilities.getActor(sourceUuid); const targetActor = Utilities.getActor(targetUuid); - + const newItems = priceInformation.buyerReceive; - + const now = (+new Date()); - + // Get all messages younger than 3 hours, and grab the last 10, then reverse them (latest to oldest) const messages = Array.from(game.messages).filter(message => (now - message.timestamp) <= (10800000)).slice(-10); messages.reverse() - + for (let [index, message] of messages.entries()) { const flags = getProperty(message, CONSTANTS.FLAGS.PILE); if (flags && flags.source === sourceUuid && flags.target === targetUuid && (flags.interactionId === interactionId || index === 0)) { return this._updateExistingMerchantMessage(message, sourceActor, targetActor, newItems, interactionId) } } - + const pileData = PileUtilities.getActorFlagData(sourceActor); - + const chatCardHtml = await renderTemplate(CONSTANTS.PATH + "templates/chat/merchant-traded.html", { message: game.i18n.format("ITEM-PILES.Chat.MerchantTraded", { name: targetActor.name, @@ -448,7 +448,7 @@ export default class ChatAPI { actor: targetActor, items: newItems }); - + return this._createNewChatMessage(userId, { user: game.user.id, type: CONST.CHAT_MESSAGE_TYPES.OTHER, @@ -462,17 +462,17 @@ export default class ChatAPI { interactionId: interactionId } }) - + } - + static async _updateExistingMerchantMessage(message, sourceActor, targetActor, newItems, interactionId) { - + const flags = getProperty(message, CONSTANTS.FLAGS.PILE); - + const mergedItems = this._matchEntries(flags.items, newItems); - + const pileData = PileUtilities.getActorFlagData(sourceActor); - + const chatCardHtml = await renderTemplate(CONSTANTS.PATH + "templates/chat/merchant-traded.html", { message: game.i18n.format("ITEM-PILES.Chat.MerchantTraded", { name: targetActor.name, @@ -485,21 +485,21 @@ export default class ChatAPI { actor: targetActor, items: mergedItems }); - + return message.update({ content: chatCardHtml, [`${CONSTANTS.FLAGS.PILE}.interactionId`]: interactionId, [`${CONSTANTS.FLAGS.PILE}.items`]: mergedItems }); - + } - + static _createNewChatMessage(userId, chatData) { - + if (!chatData.whisper) { - + const mode = Helpers.getSetting(SETTINGS.OUTPUT_TO_CHAT); - + if (mode > 1) { chatData.whisper = Array.from(game.users) .filter(user => user.isGM) @@ -509,11 +509,11 @@ export default class ChatAPI { } chatData.type = CONST.CHAT_MESSAGE_TYPES.WHISPER; } - + } - + return ChatMessage.create(chatData); - + } - + } \ No newline at end of file diff --git a/src/stores/pile-item.js b/src/stores/pile-item.js index 6191a055..d74cccc6 100644 --- a/src/stores/pile-item.js +++ b/src/stores/pile-item.js @@ -6,13 +6,13 @@ import * as SharingUtilities from "../helpers/sharing-utilities.js"; import { hasItemQuantity } from "../helpers/utilities.js"; class PileBaseItem { - + constructor(store, data) { this.store = store; this.subscriptions = []; this.setup(data); } - + setupStores() { this.quantity = writable(1); this.currentQuantity = writable(1); @@ -20,21 +20,21 @@ class PileBaseItem { this.filtered = writable(true); this.presentFromTheStart = writable(false); } - + setupSubscriptions() { // Higher order implementation } - + setup(data) { this.unsubscribe(); this.setupStores(data); this.setupSubscriptions(data); } - + subscribeTo(target, callback) { this.subscriptions.push(target.subscribe(callback)); } - + unsubscribe() { this.subscriptions.forEach(unsubscribe => unsubscribe()); this.subscriptions = []; @@ -42,7 +42,7 @@ class PileBaseItem { } export class PileItem extends PileBaseItem { - + setupStores(item) { super.setupStores(); this.item = item; @@ -57,12 +57,12 @@ export class PileItem extends PileBaseItem { this.abbreviation = this.item.abbreviation; this.identifier = this.id; } - + setupSubscriptions() { super.setupSubscriptions() - + this.subscribeTo(this.store.pileData, this.setupProperties.bind(this)); - + this.subscribeTo(this.store.shareData, () => { if (!this.toShare) { this.quantityLeft.set(get(this.quantity)); @@ -71,7 +71,7 @@ export class PileItem extends PileBaseItem { const quantityLeft = SharingUtilities.getItemSharesLeftForActor(this.store.actor, this.item, this.store.recipient); this.quantityLeft.set(quantityLeft); }); - + this.subscribeTo(this.itemDocument, () => { const { data } = this.itemDocument.updateOptions; this.name.set(this.item.name); @@ -83,11 +83,11 @@ export class PileItem extends PileBaseItem { this.currentQuantity.set(quantity); } }); - + this.subscribeTo(this.quantity, this.filter.bind(this)); this.subscribeTo(this.store.search, this.filter.bind(this)); } - + setupProperties() { this.isCurrency = PileUtilities.isItemCurrency(this.item, { target: this.store.actor }); this.similarities = Utilities.setSimilarityProperties({}, this.item); @@ -95,7 +95,7 @@ export class PileItem extends PileBaseItem { ? get(this.store.pileData).shareCurrenciesEnabled && !!this.store.recipient : get(this.store.pileData).shareItemsEnabled && !!this.store.recipient; } - + filter() { const search = get(this.store.search); const presentFromTheSTart = get(this.presentFromTheStart); @@ -108,7 +108,7 @@ export class PileItem extends PileBaseItem { this.filtered.set(!presentFromTheSTart && quantity === 0); } } - + take() { const quantity = Math.min(get(this.currentQuantity), get(this.quantityLeft)); if (!quantity) return; @@ -119,7 +119,7 @@ export class PileItem extends PileBaseItem { { interactionId: this.store.interactionId } ); } - + updateQuantity(quantity) { const roll = new Roll(quantity).evaluate({ async: false }); this.quantity.set(roll.total); @@ -128,7 +128,7 @@ export class PileItem extends PileBaseItem { } export class PileAttribute extends PileBaseItem { - + setupStores(attribute) { super.setupStores(); this.attribute = attribute; @@ -142,12 +142,12 @@ export class PileAttribute extends PileBaseItem { this.quantity.set(startingQuantity); this.currentQuantity.set(Math.min(get(this.currentQuantity), get(this.quantityLeft), get(this.quantity))); } - + setupSubscriptions() { super.setupSubscriptions(); - + this.subscribeTo(this.store.pileData, this.setupProperties.bind(this)); - + this.subscribeTo(this.store.shareData, (val) => { if (!this.toShare) { this.quantityLeft.set(get(this.quantity)); @@ -156,7 +156,7 @@ export class PileAttribute extends PileBaseItem { const quantityLeft = SharingUtilities.getAttributeSharesLeftForActor(this.store.actor, this.path, this.store.recipient); this.quantityLeft.set(quantityLeft); }); - + this.subscribeTo(this.store.document, () => { const { data } = this.store.document.updateOptions; this.path = this.attribute.path; @@ -167,15 +167,15 @@ export class PileAttribute extends PileBaseItem { this.currentQuantity.set(Math.min(get(this.currentQuantity), get(this.quantityLeft), get(this.quantity))); } }); - + this.subscribeTo(this.quantity, this.filter.bind(this)); this.subscribeTo(this.store.search, this.filter.bind(this)); } - + setupProperties() { this.toShare = get(this.store.pileData).shareCurrenciesEnabled && !!this.store.recipient; } - + filter() { const name = get(this.name); const search = get(this.store.search); @@ -189,21 +189,21 @@ export class PileAttribute extends PileBaseItem { this.filtered.set(!presentFromTheSTart && quantity === 0 && !this.store.editQuantities); } } - + take() { const quantity = Math.min(get(this.currentQuantity), get(this.quantityLeft)); return game.itempiles.API.transferAttributes( this.store.actor, this.store.recipient, { [this.path]: quantity }, - { interactionId: this.interactionId } + { interactionId: this.store.interactionId } ); } - + updateQuantity() { return this.store.actor.update({ [this.path]: get(this.quantity) }); } - + } \ No newline at end of file