Skip to content

Commit

Permalink
Merge pull request #127 from Streyder/FixWrongCurrencyExchange
Browse files Browse the repository at this point in the history
Fix change from merchant
  • Loading branch information
jopeek authored Mar 22, 2021
2 parents 5330b9f + b408ee9 commit a83d721
Showing 1 changed file with 85 additions and 37 deletions.
122 changes: 85 additions & 37 deletions lootsheetnpc5e.js
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,7 @@ Hooks.once("init", () => {
hint: "If enabled, all currency will be converted to the highest denomination possible after a purchase. If disabled, currency will subtracted simply.",
scope: "world",
config: true,
default: true,
default: false,
type: Boolean
});

Expand Down Expand Up @@ -1433,71 +1433,119 @@ Hooks.once("init", () => {
let sellerModifier = seller.getFlag("lootsheetnpc5e", "priceModifier");
if (!sellerModifier) sellerModifier = 1.0;

let itemCost = Math.round(sellItem.data.price * sellerModifier * 100) / 100;
itemCost *= quantity;
let itemCostInGold = Math.round(sellItem.data.price * sellerModifier * 100) / 100;

itemCostInGold *= quantity;
// console.log(`ItemCost: ${itemCostInGold}`)
let buyerFunds = duplicate(buyer.data.data.currency);
const conversionRate = {
"pp": CONFIG.DND5E.currencyConversion.gp.each,
"gp": 1,
"ep": 1 / CONFIG.DND5E.currencyConversion.ep.each,
"sp": 1 / CONFIG.DND5E.currencyConversion.ep.each / CONFIG.DND5E.currencyConversion.sp.each,
"cp": 1 / CONFIG.DND5E.currencyConversion.ep.each / CONFIG.DND5E.currencyConversion.sp.each / CONFIG.DND5E.currencyConversion.cp.each

console.log(`Funds before purchase: ${buyerFunds}`);

const conversionRates = {
"pp": 1,
"gp": CONFIG.DND5E.currencyConversion.gp.each,
"ep": CONFIG.DND5E.currencyConversion.ep.each,
"sp": CONFIG.DND5E.currencyConversion.sp.each,
"cp": CONFIG.DND5E.currencyConversion.cp.each
};
let buyerFundsAsGold = 0;

for (let currency in buyerFunds) {
buyerFundsAsGold += buyerFunds[currency] * conversionRate[currency];
}
const compensationCurrency = {"pp": "gp", "gp": "ep", "ep": "sp", "sp": "cp"};

let itemCostInPlatinum = itemCostInGold / conversionRates["gp"]
// console.log(`itemCostInGold : ${itemCostInGold}`);
// console.log(`itemCostInPlatinum : ${itemCostInPlatinum}`);
// console.log(`conversionRates["gp"] : ${conversionRates["gp"]}`);
// console.log(`conversionRates["ep"] : ${conversionRates["ep"]}`);

let buyerFundsAsPlatinum = buyerFunds["pp"];
buyerFundsAsPlatinum += buyerFunds["gp"] / conversionRates["gp"];
buyerFundsAsPlatinum += buyerFunds["ep"] / conversionRates["gp"] / conversionRates["ep"];
buyerFundsAsPlatinum += buyerFunds["sp"] / conversionRates["gp"] / conversionRates["ep"] / conversionRates["sp"];
buyerFundsAsPlatinum += buyerFunds["cp"] / conversionRates["gp"] / conversionRates["ep"] / conversionRates["sp"] / conversionRates["cp"];

// console.log(`buyerFundsAsPlatinum : ${buyerFundsAsPlatinum}`);

if (itemCost > buyerFundsAsGold) {
if (itemCostInPlatinum > buyerFundsAsPlatinum) {
errorMessageToActor(buyer, `Not enough funds to purchase item.`);
return;
}

let convertCurrency = game.settings.get("lootsheetnpc5e", "convertCurrency");

if (convertCurrency) {
buyerFundsAsGold -= itemCost;
buyerFundsAsPlatinum -= itemCostInPlatinum;

// Remove every coin we have
for (let currency in buyerFunds) {
buyerFunds[currency] = Math.floor(buyerFundsAsGold / conversionRate[currency]);
buyerFundsAsGold -= buyerFunds[currency] * conversionRate[currency];
buyerFunds[currency] = 0
}

// Give us fractions of platinum coins, which will be smoothed out below
buyerFunds["pp"] = buyerFundsAsPlatinum

} else {
let itemCostSubtracted = itemCost;
let giveChange = false;
// We just pay in partial platinum.
// We dont care if we get partial coins or negative once because we compensate later
buyerFunds["pp"] -= itemCostInPlatinum

// Now we exchange all negative funds with coins of lower value
// We dont need to care about running out of money because we checked that earlier
for (let currency in buyerFunds) {
while (itemCostSubtracted >= conversionRate[currency] && buyerFunds[currency] > 0) {
buyerFunds[currency] -= 1;
itemCostSubtracted -= conversionRate[currency];
}

if (giveChange) {
buyerFunds[currency] -= Math.round(itemCostSubtracted * 100) / 100;
itemCostSubtracted -= itemCostSubtracted;
let amount = buyerFunds[currency]
// console.log(`${currency} : ${amount}`);
if (amount >= 0) continue;

// If we have ever so slightly negative cp, it is likely due to floating point error
// We dont care and just give it to the player
if (currency == "cp") {
buyerFunds["cp"] = 0;
continue;
}

if (currency != "cp") {
let nextKey = Object.keys(conversionRate)[Object.keys(conversionRate).indexOf(currency) + 1];
let compCurrency = compensationCurrency[currency]

if (itemCostSubtracted % conversionRate[currency] != 0 && conversionRate[nextKey] < itemCostSubtracted && buyerFunds[nextKey] < itemCostSubtracted) {
buyerFunds[currency] -= 1;
itemCostSubtracted -= conversionRate[currency];
giveChange = true;
}
}
buyerFunds[currency] = 0;
buyerFunds[compCurrency] += amount * conversionRates[compCurrency]; // amount is a negative value so we add it
// console.log(`Substracted: ${amount * conversionRates[compCurrency]} ${compCurrency}`);
}
}

// Update buyer's gold from the buyer.
// console.log(`Smoothing out`);
// Finally we exchange partial coins with as little change as possible
for (let currency in buyerFunds) {
let amount = buyerFunds[currency]

// console.log(`${currency} : ${amount}: ${conversionRates[currency]}`);

// We round to 5 decimals. 1 pp is 1000cp, so 5 decimals always rounds good enough
// We need to round because otherwise we get 15.99999999999918 instead of 16 due to floating point precision
// If we would floor 15.99999999999918 everything explodes
let newFund = Math.floor(Math.round(amount * 1e5) / 1e5);
buyerFunds[currency] = newFund;

// console.log(`New Buyer funds ${currency}: ${buyerFunds[currency]}`);
let compCurrency = compensationCurrency[currency]

// We dont care about fractions of CP
if (currency != "cp") {
// We calculate the amount of lower currency we get for the fraction of higher currency we have
let toAdd = Math.round((amount - newFund) * 1e5) / 1e5 * conversionRates[compCurrency]
buyerFunds[compCurrency] += toAdd
// console.log(`Added ${toAdd} to ${compCurrency} it is now ${buyerFunds[compCurrency]}`);
}
}

// Update buyer's funds
buyer.update({ "data.currency": buyerFunds });

console.log(`Funds after purchase: ${buyerFunds}`);

let moved = await moveItems(seller, buyer, [{ itemId, quantity }]);

for (let m of moved) {
chatMessage(
seller, buyer,
`${buyer.name} purchases ${quantity} x ${m.item.name} for ${itemCost}gp.`,
`${buyer.name} purchases ${quantity} x ${m.item.name} for ${itemCostInGold}gp.`,
m.item);
}
}
Expand Down

0 comments on commit a83d721

Please sign in to comment.