From 6bca3777cceb3921f71afd69ee5f25e9d9988862 Mon Sep 17 00:00:00 2001 From: p4535992 Date: Sun, 7 Jan 2024 11:21:15 +0100 Subject: [PATCH 1/6] add new api method for update the currencies and bug fix on the method getStringFromCurrencies for the case unsensitive check --- src/API/api.js | 31 ++++++++++++++++++++++ src/API/private-api.js | 48 +++++++++++++++++++++++++++++++++++ src/constants/constants.js | 2 ++ src/helpers/pile-utilities.js | 6 ++++- src/socket.js | 2 ++ 5 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/API/api.js b/src/API/api.js index 08216516..9b32cc43 100644 --- a/src/API/api.js +++ b/src/API/api.js @@ -1845,6 +1845,37 @@ class API { return this.getPaymentData(...args); } + /** + * Update currencies to the target + * + * @param {Actor/Token/TokenDocument} target The actor to update the currencies to + * @param {string} currencies A string of currencies to update (eg, "5gp 25sp") + * @param {object} options Options to pass to the function + * @param {string/boolean} [options.interactionId=false] The ID of this interaction + * + * @returns {Promise} An object containing the items and attributes update to the target + */ + static updateCurrencies(target, currencies, { interactionId = false } = {}) { + + const targetActor = Utilities.getActor(target); + const targetUuid = Utilities.getUuid(targetActor); + if (!targetUuid) throw Helpers.custom_error(`updateCurrency | Could not determine the UUID, please provide a valid target`); + + if (typeof currencies !== "string") { + throw Helpers.custom_error(`updateCurrency | currencies must be of type string`) + } + + const currenciesToUpdate = PileUtilities.getPriceFromString(currencies).currencies + .filter(currency => currency.quantity); + + if (!currenciesToUpdate.length) { + throw Helpers.custom_error(`updateCurrency | Could not determine currencies to update with string "${currencies}"`); + } + + return ItemPileSocket.executeAsGM(ItemPileSocket.HANDLERS.UPDATE_CURRENCIES, targetUuid, currencies, game.user.id, { interactionId }); + + } + /** * Adds currencies to the target * diff --git a/src/API/private-api.js b/src/API/private-api.js index 09488c88..6aabd3f4 100644 --- a/src/API/private-api.js +++ b/src/API/private-api.js @@ -359,6 +359,54 @@ export default class PrivateAPI { return itemDeltas; } + static async _updateCurrencies(targetUuid, currencies, userId, { + skipVaultLogging = false, interactionId = false + } = {}) { + + const targetActor = Utilities.getActor(targetUuid); + + const transaction = new Transaction(targetActor); + + const currenciesToUpdate = PileUtilities.getPriceFromString(currencies).currencies + .filter(currency => currency.quantity); + + const itemsToUpdate2 = currenciesToUpdate.filter(currency => currency.type === "item") + .map(currency => ({ item: currency.data.item, quantity: currency.quantity })); + + const attributesToUpdate = currenciesToUpdate.filter(currency => currency.type === "attribute") + .map(currency => ({ path: currency.data.path, quantity: currency.quantity })); + + await transaction.appendItemChanges(itemsToUpdate2, { type: "currency" }); + await transaction.appendActorChanges(attributesToUpdate, { type: "currency", set: true }); + + const { actorUpdates, itemsToCreate, itemsToUpdate } = transaction.prepare(); // Prepare data + + const hookResult = Helpers.hooks.call(CONSTANTS.HOOKS.CURRENCY.PRE_UPDATE, targetActor, actorUpdates, itemsToCreate, itemsToUpdate, interactionId); + if (hookResult === false) return false; // Call pre-hook to allow user to interrupt it + + const { itemDeltas, attributeDeltas } = await transaction.commit(); // Actually update the items to the actor + + await ItemPileSocket.callHook(CONSTANTS.HOOKS.CURRENCY.UPDATE, targetUuid, itemDeltas, attributeDeltas, userId, interactionId); + + await this._executeItemPileMacro(targetUuid, { + action: "updateCurrencies", + target: targetUuid, + items: itemDeltas, + attributes: attributeDeltas, + userId: userId, + interactionId: interactionId + }); + + if (!skipVaultLogging && PileUtilities.isItemPileVault(targetActor)) { + await PileUtilities.updateVaultLog(targetActor, { + userId, items: itemDeltas, attributes: attributeDeltas, withdrawal: false + }); + } + + return { itemDeltas, attributeDeltas }; + + } + static async _addCurrencies(targetUuid, currencies, userId, { skipVaultLogging = false, interactionId = false } = {}) { diff --git a/src/constants/constants.js b/src/constants/constants.js index 2e32f9a3..965e59ca 100644 --- a/src/constants/constants.js +++ b/src/constants/constants.js @@ -259,6 +259,8 @@ CONSTANTS.HOOKS = { CURRENCY: { PRE_TRANSFER: module`preTransferCurrencies`, TRANSFER: module`transferCurrencies`, + PRE_UPDATE: module`preUpdateCurrencies`, + UPDATE: module`updateCurrencies`, PRE_ADD: module`preAddCurrencies`, ADD: module`addCurrencies`, PRE_REMOVE: module`preRemoveCurrencies`, diff --git a/src/helpers/pile-utilities.js b/src/helpers/pile-utilities.js index 6c7583b9..1961c9b6 100644 --- a/src/helpers/pile-utilities.js +++ b/src/helpers/pile-utilities.js @@ -814,7 +814,11 @@ export function getStringFromCurrencies(currencies) { Helpers.custom_error(`getStringFromCurrencies | The currency element is not valid with cost '${cost}' and abbreviation '${abbreviation}'`, false); return ""; } - if (!allAbbreviationsArray.includes(abbreviation?.replace("{#}", ""))) { + // Check abbreviation by case unsensitive... + const indexAbbreviation = allAbbreviationsArray.findIndex(a => { + return a?.replace("{#}", "")?.toLowerCase() === abbreviation?.replace("{#}", "")?.toLowerCase(); + }); + if (indexAbbreviation === -1) { Helpers.custom_error(`getStringFromCurrencies | The currency abbreviation '${abbreviation?.replace("{#}", "")}' is not registered`, false); return ""; } diff --git a/src/socket.js b/src/socket.js index be0367fb..e1d29379 100644 --- a/src/socket.js +++ b/src/socket.js @@ -56,6 +56,7 @@ export default class ItemPileSocket { REMOVE_ITEMS: "removeItems", TRANSFER_ITEMS: "transferItems", TRANSFER_ALL_ITEMS: "transferAllItems", + UPDATE_CURRENCIES: "updateCurrencies", ADD_CURRENCIES: "addCurrencies", REMOVE_CURRENCIES: "removeCurrencies", TRANSFER_CURRENCIES: "transferCurrencies", @@ -108,6 +109,7 @@ export default class ItemPileSocket { [this.HANDLERS.REMOVE_ITEMS]: (...args) => PrivateAPI._removeItems(...args), [this.HANDLERS.TRANSFER_ITEMS]: (...args) => PrivateAPI._transferItems(...args), [this.HANDLERS.TRANSFER_ALL_ITEMS]: (...args) => PrivateAPI._transferAllItems(...args), + [this.HANDLERS.UPDATE_CURRENCIES]: (...args) => PrivateAPI._updateCurrencies(...args), [this.HANDLERS.ADD_CURRENCIES]: (...args) => PrivateAPI._addCurrencies(...args), [this.HANDLERS.REMOVE_CURRENCIES]: (...args) => PrivateAPI._removeCurrencies(...args), [this.HANDLERS.TRANSFER_CURRENCIES]: (...args) => PrivateAPI._transferCurrencies(...args), From a992a299ed69f1602589618feec61f8d5ff06556 Mon Sep 17 00:00:00 2001 From: p4535992 Date: Sun, 7 Jan 2024 11:45:12 +0100 Subject: [PATCH 2/6] update documentation api and hooks --- docs/api.md | 117 ++++++++++++++++++++++++++++++++++++++++++-------- docs/hooks.md | 86 +++++++++++++++++++++++++------------ 2 files changed, 157 insertions(+), 46 deletions(-) diff --git a/docs/api.md b/docs/api.md index e77ea83e..ad8e87da 100644 --- a/docs/api.md +++ b/docs/api.md @@ -61,6 +61,7 @@ * [transferAttributes](#transferAttributes) * [transferAllAttributes](#transferAllAttributes) * [transferEverything](#transferEverything) + * [updateCurrencies](#updateCurrencies) * [addCurrencies](#addCurrencies) * [removeCurrencies](#removeCurrencies) * [transferCurrencies](#transferCurrencies) @@ -859,6 +860,39 @@ Transfers all items and attributes between the source and the target. --- +### updateCurrencies + +`game.itempiles.API.updateCurrencies(target, currencies, options)` ⇒ `Promise` + +Updates currencies to the target. +It differs from the add and remove operations in that it "sets" the currency of the target with the passed value. +This is useful in cases where you want to pre-fill something with a specific amount of currency. + +**Returns**: `Promise` - An object containing the items and attributes added to the target + +| Param | Type | Default | Description | +|-------------------------|----------------------------------|---------|------------------------------------------------| +| target | `Actor/TokenDocument/Token/UUID` | | The target to add the currencies to | +| currencies | `string` | | A string of currencies to add (eg, "5gp 25sp") | +| options | `object` | | Options to pass to the function | +| [options.interactionId] | `string/boolean` | `false` | The interaction ID of this action | + +#### Example + +```javascript + +const tokenOrActor = game.actors.getName("Bharash"); + +// Set the value on the GP currency with the quantity 10 to the target +game.itempiles.API.updateCurrencies(tokenOrActor, "10gp"); + +// Set the value on the GP currency with the quantity 10 and on the SP currency with the quantity 5 to the target +game.itempiles.API.updateCurrencies(tokenOrActor, "10gp 5sp"); + +``` + +--- + ### addCurrencies `game.itempiles.API.addCurrencies(target, currencies, options)` ⇒ `Promise` @@ -867,12 +901,26 @@ Adds currencies to the target **Returns**: `Promise` - An object containing the items and attributes added to the target -| Param | Type | Default | Description | -|-------------------------|-----------------------------|---------|------------------------------------------------| -| target | `Actor/TokenDocument/Token` | | The target to add the currencies to | -| currencies | `string` | | A string of currencies to add (eg, "5gp 25sp") | -| options | `object` | | Options to pass to the function | -| [options.interactionId] | `string/boolean` | `false` | The interaction ID of this action | +| Param | Type | Default | Description | +|-------------------------|----------------------------------|---------|------------------------------------------------| +| target | `Actor/TokenDocument/Token/UUID` | | The target to add the currencies to | +| currencies | `string` | | A string of currencies to add (eg, "5gp 25sp") | +| options | `object` | | Options to pass to the function | +| [options.interactionId] | `string/boolean` | `false` | The interaction ID of this action | + +#### Example + +```javascript + +const tokenOrActor = game.actors.getName("Bharash"); + +// Add 10 GP to the target +game.itempiles.API.addCurrencies(tokenOrActor, "10gp"); + +// Add 10 GP and 5 SP to the target +game.itempiles.API.removeCurrencies(tokenOrActor, "10gp 5sp"); + +``` --- @@ -884,13 +932,27 @@ Removes currencies from the target **Returns**: `Promise` - An object containing the items and attributes removed from the target -| Param | Type | Default | Description | -|-------------------------|-----------------------------|---------|---------------------------------------------------| -| target | `Actor/Token/TokenDocument` | | The target to remove currencies from | -| currencies | `string` | | A string of currencies to remove (eg, "5gp 25sp") | -| options | `object` | | Options to pass to the function | -| [options.change] | `boolean` | `true` | Whether the actor can get change back | -| [options.interactionId] | `string/boolean` | `false` | The interaction ID of this action | +| Param | Type | Default | Description | +|-------------------------|----------------------------------|---------|---------------------------------------------------| +| target | `Actor/Token/TokenDocument/UUID` | | The target to remove currencies from | +| currencies | `string` | | A string of currencies to remove (eg, "5gp 25sp") | +| options | `object` | | Options to pass to the function | +| [options.change] | `boolean` | `true` | Whether the actor can get change back | +| [options.interactionId] | `string/boolean` | `false` | The interaction ID of this action | + +#### Example + +```javascript + +const tokenOrActor = game.actors.getName("Bharash"); + +// Remove 10 GP from the target +game.itempiles.API.removeCurrencies(tokenOrActor, "10gp"); + +// Remove 10 GP and 5 SP from the target +game.itempiles.API.removeCurrencies(tokenOrActor, "10gp 5sp"); + +``` --- @@ -999,16 +1061,33 @@ Turns a string of currencies or a number into an object containing payment data, **Returns**: `object` - An object containing the price data -| Param | Type | Default | Description | -|--------------------|------------------------------------|---------|------------------------------------------------------------| -| price | `string/number` | | A string of currencies to add (eg, "5gp 25sp") or a number | -| options | `object` | | Options to pass to the function | -| [options.quantity] | `number` | `1` | The number of this to buy | -| [options.target] | `string/Actor/TokenDocument/Token` | `false` | The target whose currencies to check against | +| Param | Type | Default | Description | +|--------------------|----------------------------------|---------|------------------------------------------------------------| +| price | `string/number` | | A string of currencies to add (eg, "5gp 25sp") or a number | +| options | `object` | | Options to pass to the function | +| [options.quantity] | `number` | `1` | The number of this to buy | +| [options.target] | `Actor/TokenDocument/Token/UUID` | `false` | The target whose currencies to check against | **Previously (now deprecated):** `game.itempiles.API.getPaymentDataFromString` +#### Example + +```javascript + +// The actor/token can afford a 10 GP item ? + +const tokenOrActor = game.actors.getName("Bharash"); +const currencies = { + cost: 10, + abbreviation: "GP", +}; +const currencyS = game.itempiles.API.getStringFromCurrencies(currencies); +const currencyData = game.itempiles.API.getPaymentData(currencyS, { target: tokenOrActor }); +return currencyData.canBuy; // true or false + +``` + --- ### getActorItems diff --git a/docs/hooks.md b/docs/hooks.md index b541808c..7541f033 100644 --- a/docs/hooks.md +++ b/docs/hooks.md @@ -81,6 +81,8 @@ - [Currencies][#Currencies] - [item-piles-preTransferCurrencies](#item-piles-preTransferCurrencies) - [item-piles-transferCurrencies](#item-piles-transferCurrencies) + - [item-piles-preUpdateCurrencies](#item-piles-preUpdateCurrencies) + - [item-piles-updateCurrencies](#item-piles-updateCurrencies) - [item-piles-preAddCurrencies](#item-piles-preAddCurrencies) - [item-piles-addCurrencies](#item-piles-addCurrencies) - [item-piles-preRemoveCurrencies](#item-piles-preRemoveCurrencies) @@ -943,17 +945,47 @@ Called after all attributes' values was transferred from the source to the targe ## Currencies +### item-piles-preUpdateCurrencies + +Called before currencies are updated to an actor. Not called in the case of a transfer. + +| Param | Type | Description | +|---------------|---------------------------------------|------------------------------------------------------------------------------------------------------------------------------| +| target | Actor/TokenDocument/UUID | The target that will have currencies added to it | +| actorUpdates | object | An object, where the keys are the attribute that is going to be updated, the value being the quantity is going to be changed | +| itemsToCreate | array | An array of objects each containing the item id and the quantity that was added | +| itemsToUpdate | array | An array of objects each containing the item id and the quantity that was updated | +| interactionId | string/boolean | The ID of this interaction, to identify ongoing transfers | + +If the hook returns `false`, the action is interrupted. + +--- + +### item-piles-updateCurrencies + +Called after currencies were updated to the target. Not called in the case of a transfer. + +| Param | Type | Description | +|-----------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------| +| target | Actor/TokenDocument/UUID | The target whose currencies were added to | +| itemDeltas | array | An array of objects each containing the item id and the quantity that was changed | +| attributeDeltas | object | An object, where the keys are the attribute that was updated, and the value being the quantity that was changed | +| userId | string | The ID of the user that initiated this action | +| interactionId | string/boolean | The ID of this interaction, to identify ongoing transfers | + +--- + ### item-piles-preAddCurrencies Called before currencies are added to an actor. Not called in the case of a transfer. -| Param | Type | Description | -|---------------|----------------------------------|------------------------------------------------------------------------------------------------------------------------------| -| target | Actor/TokenDocument | The target that will have currencies added to it | -| actorUpdates | object | An object, where the keys are the attribute that is going to be updated, the value being the quantity is going to be changed | -| itemsToCreate | array | An array of objects each containing the item id and the quantity that was added | -| itemsToUpdate | array | An array of objects each containing the item id and the quantity that was updated | -| interactionId | string/boolean | The ID of this interaction, to identify ongoing transfers | +| Param | Type | Description | +|---------------|---------------------------------------|------------------------------------------------------------------------------------------------------------------------------| +| target | Actor/TokenDocument/UUID | The target that will have currencies added to it | +| actorUpdates | object | An object, where the keys are the attribute that is going to be updated, the value being the quantity is going to be changed | +| itemsToCreate | array | An array of objects each containing the item id and the quantity that was added | +| itemsToUpdate | array | An array of objects each containing the item id and the quantity that was updated | +| interactionId | string/boolean | The ID of this interaction, to identify ongoing transfers | If the hook returns `false`, the action is interrupted. @@ -963,13 +995,13 @@ If the hook returns `false`, the action is interrupted. Called after currencies were added to the target. Not called in the case of a transfer. -| Param | Type | Description | -|-----------------|----------------------------------|-----------------------------------------------------------------------------------------------------------------| -| target | Actor/TokenDocument | The target whose currencies were added to | -| itemDeltas | array | An array of objects each containing the item id and the quantity that was changed | -| attributeDeltas | object | An object, where the keys are the attribute that was updated, and the value being the quantity that was changed | -| userId | string | The ID of the user that initiated this action | -| interactionId | string/boolean | The ID of this interaction, to identify ongoing transfers | +| Param | Type | Description | +|-----------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------| +| target | Actor/TokenDocument/UUID | The target whose currencies were added to | +| itemDeltas | array | An array of objects each containing the item id and the quantity that was changed | +| attributeDeltas | object | An object, where the keys are the attribute that was updated, and the value being the quantity that was changed | +| userId | string | The ID of the user that initiated this action | +| interactionId | string/boolean | The ID of this interaction, to identify ongoing transfers | --- @@ -977,12 +1009,12 @@ Called after currencies were added to the target. Not called in the case of a tr Called before currencies were removed from the target. Not called in the case of a transfer. -| Param | Type | Description | -|---------------|----------------------------------|------------------------------------------------------------------------------------------------------------------------------| -| target | Actor/TokenDocument | The target that will have its currencies removed | -| actorUpdates | object | An object, where the keys are the attribute that is going to be updated, the value being the quantity is going to be changed | -| itemsToUpdate | array | An array of objects each containing the item id and the quantity that was updated | -| interactionId | string/boolean | The ID of this interaction, to identify ongoing transfers | +| Param | Type | Description | +|---------------|---------------------------------------|------------------------------------------------------------------------------------------------------------------------------| +| target | Actor/TokenDocument/UUID | The target that will have its currencies removed | +| actorUpdates | object | An object, where the keys are the attribute that is going to be updated, the value being the quantity is going to be changed | +| itemsToUpdate | array | An array of objects each containing the item id and the quantity that was updated | +| interactionId | string/boolean | The ID of this interaction, to identify ongoing transfers | If the hook returns `false`, the action is interrupted. @@ -992,13 +1024,13 @@ If the hook returns `false`, the action is interrupted. Called after currencies was removed from the target. Not called in the case of a transfer. -| Param | Type | Description | -|-----------------|----------------------------------|-----------------------------------------------------------------------------------------------------------------| -| target | Actor/TokenDocument | The target whose currencies were removed from | -| itemDeltas | array | An array of objects each containing the item id and the quantity that was changed | -| attributeDeltas | object | An object, where the keys are the attribute that was updated, and the value being the quantity that was changed | -| userId | string | The ID of the user that initiated this action | -| interactionId | string/boolean | The ID of this interaction, to identify ongoing transfers | +| Param | Type | Description | +|-----------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------| +| target | Actor/TokenDocument/UUID | The target whose currencies were removed from | +| itemDeltas | array | An array of objects each containing the item id and the quantity that was changed | +| attributeDeltas | object | An object, where the keys are the attribute that was updated, and the value being the quantity that was changed | +| userId | string | The ID of the user that initiated this action | +| interactionId | string/boolean | The ID of this interaction, to identify ongoing transfers | --- From 81e1c7a6d819b8657c602d10721586e31eb07e46 Mon Sep 17 00:00:00 2001 From: p4535992 Date: Sun, 7 Jan 2024 17:57:13 +0100 Subject: [PATCH 3/6] add better check for quantity on currencies methods and add filter on the method getPriceFromString by identifier --- src/API/api.js | 10 +++++----- src/API/private-api.js | 6 +++--- src/helpers/pile-utilities.js | 13 ++++++++++--- src/helpers/transaction.js | 5 +++-- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/API/api.js b/src/API/api.js index 9b32cc43..4363770f 100644 --- a/src/API/api.js +++ b/src/API/api.js @@ -1817,7 +1817,7 @@ class API { let secondaryPrices = false; if (typeof price === "string") { const priceData = PileUtilities.getPriceFromString(price) - const currenciesToRemove = priceData.currencies.filter(currency => currency.quantity); + const currenciesToRemove = priceData.currencies.filter(currency => Helpers.isRealNumber(currency.quantity) && currency.quantity > 0); if (!currenciesToRemove.length) { throw Helpers.custom_error(`getPaymentData | Could not determine currencies to remove with string "${price}"`); } @@ -1866,7 +1866,7 @@ class API { } const currenciesToUpdate = PileUtilities.getPriceFromString(currencies).currencies - .filter(currency => currency.quantity); + .filter(currency => Helpers.isRealNumber(currency.quantity) && currency.quantity >= 0); if (!currenciesToUpdate.length) { throw Helpers.custom_error(`updateCurrency | Could not determine currencies to update with string "${currencies}"`); @@ -1897,7 +1897,7 @@ class API { } const currenciesToAdd = PileUtilities.getPriceFromString(currencies).currencies - .filter(currency => currency.quantity); + .filter(currency => Helpers.isRealNumber(currency.quantity) && currency.quantity > 0); if (!currenciesToAdd.length) { throw Helpers.custom_error(`addCurrency | Could not determine currencies to add with string "${currencies}"`); @@ -1928,7 +1928,7 @@ class API { let secondaryPrices = false; if (typeof currencies === "string") { const priceData = PileUtilities.getPriceFromString(currencies) - const currenciesToRemove = priceData.currencies.filter(currency => currency.quantity); + const currenciesToRemove = priceData.currencies.filter(currency => Helpers.isRealNumber(currency.quantity) && currency.quantity > 0); if (!currenciesToRemove.length) { throw Helpers.custom_error(`removeCurrencies | Could not determine currencies to remove with string "${currencies}"`); } @@ -1980,7 +1980,7 @@ class API { let secondaryPrices = false; if (typeof currencies === "string") { const priceData = PileUtilities.getPriceFromString(currencies) - const currenciesToRemove = priceData.currencies.filter(currency => currency.quantity); + const currenciesToRemove = priceData.currencies.filter(currency => Helpers.isRealNumber(currency.quantity) && currency.quantity > 0); if (!currenciesToRemove.length) { throw Helpers.custom_error(`transferCurrencies | Could not determine currencies to remove with string "${currencies}"`); } diff --git a/src/API/private-api.js b/src/API/private-api.js index 6aabd3f4..d1b522cd 100644 --- a/src/API/private-api.js +++ b/src/API/private-api.js @@ -368,7 +368,7 @@ export default class PrivateAPI { const transaction = new Transaction(targetActor); const currenciesToUpdate = PileUtilities.getPriceFromString(currencies).currencies - .filter(currency => currency.quantity); + .filter(currency => Helpers.isRealNumber(currency.quantity) && currency.quantity >= 0); const itemsToUpdate2 = currenciesToUpdate.filter(currency => currency.type === "item") .map(currency => ({ item: currency.data.item, quantity: currency.quantity })); @@ -376,7 +376,7 @@ export default class PrivateAPI { const attributesToUpdate = currenciesToUpdate.filter(currency => currency.type === "attribute") .map(currency => ({ path: currency.data.path, quantity: currency.quantity })); - await transaction.appendItemChanges(itemsToUpdate2, { type: "currency" }); + await transaction.appendItemChanges(itemsToUpdate2, { type: "currency", set: true }); await transaction.appendActorChanges(attributesToUpdate, { type: "currency", set: true }); const { actorUpdates, itemsToCreate, itemsToUpdate } = transaction.prepare(); // Prepare data @@ -416,7 +416,7 @@ export default class PrivateAPI { const transaction = new Transaction(targetActor); const currenciesToAdd = PileUtilities.getPriceFromString(currencies).currencies - .filter(currency => currency.quantity); + .filter(currency => Helpers.isRealNumber(currency.quantity) && currency.quantity > 0); const itemsToAdd = currenciesToAdd.filter(currency => currency.type === "item") .map(currency => ({ item: currency.data.item, quantity: currency.quantity })); diff --git a/src/helpers/pile-utilities.js b/src/helpers/pile-utilities.js index 1961c9b6..577d23ac 100644 --- a/src/helpers/pile-utilities.js +++ b/src/helpers/pile-utilities.js @@ -837,7 +837,7 @@ export function getPriceFromString(str, currencyList = false) { currencyList = getCurrencyList() } - const currencies = foundry.utils.duplicate(currencyList) + let currencies = foundry.utils.duplicate(currencyList) .map(currency => { currency.quantity = 0 currency.identifier = currency.abbreviation.toLowerCase().replace("{#}", "").trim() @@ -849,11 +849,15 @@ export function getPriceFromString(str, currencyList = false) { const splitBy = new RegExp("(.*?) *(" + sortedCurrencies.join("|") + ")", "g"); const parts = [...str.split(",").join("").split(" ").join("").trim().toLowerCase().matchAll(splitBy)]; - + const identifierFilter = []; let overallCost = 0; for (const part of parts) { for (const currency of currencies) { + if(part[2]) { + identifierFilter.push(part[2]); + } + if (part[2] !== currency.identifier) continue; try { @@ -870,8 +874,11 @@ export function getPriceFromString(str, currencyList = false) { } } } + + // Maybe there is a better method for this ? + currencies = currencies.filter(currency => identifierFilter.includes(currency.identifier)); - if (!currencies.some(currency => currency.quantity)) { + if (!currencies.some(currency => Helpers.isRealNumber(currency.quantity) && currency.quantity >= 0)) { try { const roll = new Roll(str).evaluate({ async: false }); if (roll.total) { diff --git a/src/helpers/transaction.js b/src/helpers/transaction.js index 9f079632..c9a45b94 100644 --- a/src/helpers/transaction.js +++ b/src/helpers/transaction.js @@ -26,7 +26,7 @@ export default class Transaction { } async appendItemChanges(items, { - remove = false, type = "item", keepIfZero = false, onlyDelta = false, + set = false, remove = false, type = "item", keepIfZero = false, onlyDelta = false, } = {}) { for (let data of items) { @@ -135,7 +135,8 @@ export default class Transaction { } } - async appendActorChanges(attributes, { set = false, remove = false, type = "attribute", onlyDelta = false } = {}) { + async appendActorChanges(attributes, { + set = false, remove = false, type = "attribute", onlyDelta = false } = {}) { if (!Array.isArray(attributes)) { attributes = Object.entries(attributes).map(entry => ({ path: entry[0], quantity: entry[1] })); } From fb617b44151ab9617d12138d6c880d53260f78f7 Mon Sep 17 00:00:00 2001 From: p4535992 Date: Sun, 7 Jan 2024 18:27:06 +0100 Subject: [PATCH 4/6] "try" to add the set parameter to the method appendItemChanges --- src/API/private-api.js | 2 +- src/helpers/transaction.js | 21 +++++++++++++++++++-- src/helpers/utilities.js | 37 +++++++++++++++++++++++++++++++++++-- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/API/private-api.js b/src/API/private-api.js index d1b522cd..b67b8333 100644 --- a/src/API/private-api.js +++ b/src/API/private-api.js @@ -371,7 +371,7 @@ export default class PrivateAPI { .filter(currency => Helpers.isRealNumber(currency.quantity) && currency.quantity >= 0); const itemsToUpdate2 = currenciesToUpdate.filter(currency => currency.type === "item") - .map(currency => ({ item: currency.data.item, quantity: currency.quantity })); + .map(currency => ({ item: currency.data.item, quantity: 1, cost: currency.quantity })); const attributesToUpdate = currenciesToUpdate.filter(currency => currency.type === "attribute") .map(currency => ({ path: currency.data.path, quantity: currency.quantity })); diff --git a/src/helpers/transaction.js b/src/helpers/transaction.js index c9a45b94..ec151c85 100644 --- a/src/helpers/transaction.js +++ b/src/helpers/transaction.js @@ -40,7 +40,12 @@ export default class Transaction { if (SYSTEMS.DATA.ITEM_TRANSFORMER && !remove) { itemData = await SYSTEMS.DATA.ITEM_TRANSFORMER(itemData); } - const incomingQuantity = Math.abs(data.quantity ?? Utilities.getItemQuantity(itemData)) * (remove ? -1 : 1); + const incomingQuantity = set + ? Math.abs(data.quantity ?? Utilities.getItemQuantity(itemData)) + : Math.abs(data.quantity ?? Utilities.getItemQuantity(itemData)) * (remove ? -1 : 1); + const incomingCost = set + ? Math.abs(data.cost ?? Utilities.getItemCost(itemData)) + : Math.abs(data.cost ?? Utilities.getItemCost(itemData)) * (remove ? -1 : 1); let itemId = itemData._id ?? itemData.id; let actorHasItem = false; let actorExistingItem = false; @@ -59,7 +64,8 @@ export default class Transaction { areItemsColliding(item, itemData) ) }); - } else { + } + else { actorHasItem = this.actor.items.get(itemId); actorExistingItem = actorHasItem || Utilities.findSimilarItem(this.actor.items, itemData); } @@ -77,10 +83,12 @@ export default class Transaction { if (actorExistingItem) { const itemQuantity = Utilities.getItemQuantity(actorExistingItem); + const itemCost = Utilities.getItemCost(actorExistingItem); if (itemQuantity > 1 || canItemStack) { const newQuantity = itemQuantity + incomingQuantity; + const newCost = itemCost + incomingCost; const existingItemUpdate = remove ? this.itemsToUpdate.find(item => item._id === itemId) @@ -90,11 +98,15 @@ export default class Transaction { Utilities.setItemQuantity(existingItemUpdate, newQuantity); if (keepIfZero && type !== "currency") { setProperty(existingItemUpdate, CONSTANTS.FLAGS.ITEM + ".notForSale", newQuantity === 0); + } else if(keepIfZero && type === "currency"){ + Utilities.setItemCost(existingItemUpdate, newCost); } } else { const update = Utilities.setItemQuantity(actorExistingItem.toObject(), newQuantity); if (keepIfZero && type !== "currency") { setProperty(update, CONSTANTS.FLAGS.ITEM + ".notForSale", newQuantity === 0); + } else if(keepIfZero && type === "currency"){ + Utilities.setItemCost(update, newCost); } this.itemTypeMap.set(actorExistingItem.id, type) this.itemsToUpdate.push(update); @@ -113,6 +125,7 @@ export default class Transaction { itemData._id = randomID(); } Utilities.setItemQuantity(itemData, incomingQuantity); + Utilities.setItemCost(itemData, incomingCost); this.itemsToCreate.push(itemData); this.itemTypeMap.set(itemData._id, type) @@ -123,11 +136,15 @@ export default class Transaction { if (existingItemCreation && canItemStack) { const newQuantity = Utilities.getItemQuantity(existingItemCreation) + incomingQuantity; Utilities.setItemQuantity(existingItemCreation, newQuantity); + + const newCost = Utilities.getItemCost(existingItemCreation) + incomingCost; + Utilities.setItemCost(existingItemCreation, newCost); } else { if (!itemData._id) { itemData._id = randomID(); } Utilities.setItemQuantity(itemData, incomingQuantity); + Utilities.setItemCost(itemData, incomingCost); this.itemsToCreate.push(itemData); this.itemTypeMap.set(itemData._id, type) } diff --git a/src/helpers/utilities.js b/src/helpers/utilities.js index ba38ced8..1f6befe0 100644 --- a/src/helpers/utilities.js +++ b/src/helpers/utilities.js @@ -214,19 +214,52 @@ export function hasItemQuantity(item) { * @param {Boolean} requiresExistingQuantity * @returns {Object} */ -export function setItemQuantity(itemData, quantity, requiresExistingQuantity = false) { +export function setItemQuantity(item, quantity, requiresExistingQuantity = false) { + const itemData = item instanceof Item ? item.toObject() : item; if (!requiresExistingQuantity || getItemTypesThatCanStack().has(itemData.type) || hasItemQuantity(itemData)) { setProperty(itemData, game.itempiles.API.ITEM_QUANTITY_ATTRIBUTE, quantity); } return itemData; } - +/** + * Returns a given item's cost/price + * + * @param {Item/Object} item + * @returns {number} + */ export function getItemCost(item) { const itemData = item instanceof Item ? item.toObject() : item; return getProperty(itemData, game.itempiles.API.ITEM_PRICE_ATTRIBUTE) ?? 0; } +/** + * Returns whether an item has the cost/price property + * + * @param {Item/Object} item + * @returns {Boolean} + */ +export function hasItemCost(item) { + const itemData = item instanceof Item ? item.toObject() : item; + return hasProperty(itemData, game.itempiles.API.ITEM_PRICE_ATTRIBUTE); +} + +/** + * Returns a given item's quantity + * + * @param {Object} itemData + * @param {Number} cost + * @param {Boolean} requiresExistingCost + * @returns {Object} + */ +export function setItemCost(item, cost, requiresExistingCost = false) { + const itemData = item instanceof Item ? item.toObject() : item; + if (!requiresExistingCost || hasItemCost(itemData)) { + setProperty(itemData, game.itempiles.API.ITEM_PRICE_ATTRIBUTE, cost); + } + return itemData +} + /** * Retrieves all visible tokens on a given location * From b6ff05f3ece1688146ee1fd0ea373e6028fb4d43 Mon Sep 17 00:00:00 2001 From: p4535992 Date: Wed, 10 Jan 2024 20:40:50 +0100 Subject: [PATCH 5/6] add unsensitive on the filter --- src/helpers/pile-utilities.js | 4 ++-- src/helpers/utilities.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/helpers/pile-utilities.js b/src/helpers/pile-utilities.js index 577d23ac..d17b095e 100644 --- a/src/helpers/pile-utilities.js +++ b/src/helpers/pile-utilities.js @@ -855,7 +855,7 @@ export function getPriceFromString(str, currencyList = false) { for (const currency of currencies) { if(part[2]) { - identifierFilter.push(part[2]); + identifierFilter.push(part[2]?.toLowerCase()); } if (part[2] !== currency.identifier) continue; @@ -876,7 +876,7 @@ export function getPriceFromString(str, currencyList = false) { } // Maybe there is a better method for this ? - currencies = currencies.filter(currency => identifierFilter.includes(currency.identifier)); + currencies = currencies.filter(currency => identifierFilter.includes(currency.identifier?.toLowerCase())); if (!currencies.some(currency => Helpers.isRealNumber(currency.quantity) && currency.quantity >= 0)) { try { diff --git a/src/helpers/utilities.js b/src/helpers/utilities.js index 1f6befe0..87ddebb4 100644 --- a/src/helpers/utilities.js +++ b/src/helpers/utilities.js @@ -245,7 +245,7 @@ export function hasItemCost(item) { } /** - * Returns a given item's quantity + * Returns a given item's cost/price * * @param {Object} itemData * @param {Number} cost From 670a55afea2d69cd7a61fa5f9b3dcfc8f83107b0 Mon Sep 17 00:00:00 2001 From: p4535992 Date: Sat, 13 Jan 2024 09:25:21 +0100 Subject: [PATCH 6/6] remove "remove" from "appendItemChnages" for the cost behaviour --- src/helpers/transaction.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/helpers/transaction.js b/src/helpers/transaction.js index ec151c85..26a90802 100644 --- a/src/helpers/transaction.js +++ b/src/helpers/transaction.js @@ -43,9 +43,9 @@ export default class Transaction { const incomingQuantity = set ? Math.abs(data.quantity ?? Utilities.getItemQuantity(itemData)) : Math.abs(data.quantity ?? Utilities.getItemQuantity(itemData)) * (remove ? -1 : 1); - const incomingCost = set - ? Math.abs(data.cost ?? Utilities.getItemCost(itemData)) - : Math.abs(data.cost ?? Utilities.getItemCost(itemData)) * (remove ? -1 : 1); + // Remove is ignored because when you remove a item the cost cannot change + const incomingCost = Math.abs(data.cost ?? Utilities.getItemCost(itemData)); + let itemId = itemData._id ?? itemData.id; let actorHasItem = false; let actorExistingItem = false;