Index: ps/trunk/binaries/data/mods/public/simulation/components/Barter.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/Barter.js +++ ps/trunk/binaries/data/mods/public/simulation/components/Barter.js @@ -4,23 +4,37 @@ ""; /** - * The "true price" is a base price of 100 units of resource (for the case of some resources being of more worth than others). + * The "true price" is a base price of Barter.prototype.DEAL_AMOUNT units of resource (for the case of some resources being of more worth than others). * With current bartering system only relative values makes sense so if for example stone is two times more expensive than wood, * there will 2:1 exchange rate. * + * Keep gui/session/trade/BarterButton.js in sync with this value. + */ +Barter.prototype.DEAL_AMOUNT = 100; + +/** + * Deals per mass barter. + * Keep gui/session/trade/BarterButton.js in sync with this value. + */ +Barter.prototype.BATCH_SIZE = 5; + +/** * Constant part of price percentage difference between true price and buy/sell price. * Buy price equal to true price plus constant difference. * Sell price equal to true price minus constant difference. */ Barter.prototype.CONSTANT_DIFFERENCE = 10; + /** * Additional difference of prices in percents, added after each deal to specified resource price. */ Barter.prototype.DIFFERENCE_PER_DEAL = 2; + /** * Price difference percentage which restored each restore timer tick */ Barter.prototype.DIFFERENCE_RESTORE = 0.5; + /** * Interval of timer which slowly restore prices after deals */ @@ -29,20 +43,19 @@ Barter.prototype.Init = function() { this.priceDifferences = {}; - for (let resource of Resources.GetBarterableCodes()) + for (const resource of Resources.GetBarterableCodes()) this.priceDifferences[resource] = 0; - this.restoreTimer = undefined; }; Barter.prototype.GetPrices = function(cmpPlayer) { - let prices = { "buy": {}, "sell": {} }; - let multiplier = cmpPlayer.GetBarterMultiplier(); - for (let resource of Resources.GetBarterableCodes()) - { - let truePrice = Resources.GetResource(resource).truePrice; - prices.buy[resource] = truePrice * (100 + this.CONSTANT_DIFFERENCE + this.priceDifferences[resource]) * multiplier.buy[resource] / 100; - prices.sell[resource] = truePrice * (100 - this.CONSTANT_DIFFERENCE + this.priceDifferences[resource]) * multiplier.sell[resource] / 100; + const prices = { "buy": {}, "sell": {} }; + const multiplier = cmpPlayer.GetBarterMultiplier(); + for (const resource in this.priceDifferences) + { + const truePrice = Resources.GetResource(resource).truePrice; + prices.buy[resource] = truePrice * (this.DEAL_AMOUNT + this.CONSTANT_DIFFERENCE + this.priceDifferences[resource]) * multiplier.buy[resource] / this.DEAL_AMOUNT; + prices.sell[resource] = truePrice * (this.DEAL_AMOUNT - this.CONSTANT_DIFFERENCE + this.priceDifferences[resource]) * multiplier.sell[resource] / this.DEAL_AMOUNT; } return prices; }; @@ -55,85 +68,81 @@ return; } - let availResources = Resources.GetBarterableCodes(); - if (availResources.indexOf(resourceToSell) == -1) + if (!(resourceToSell in this.priceDifferences)) { warn("ExchangeResources: incorrect resource to sell: " + uneval(resourceToSell)); return; } - if (availResources.indexOf(resourceToBuy) == -1) + if (!(resourceToBuy in this.priceDifferences)) { warn("ExchangeResources: incorrect resource to buy: " + uneval(resourceToBuy)); return; } - if (amount != 100 && amount != 500) + if (amount !== this.DEAL_AMOUNT && amount !== (this.BATCH_SIZE * this.DEAL_AMOUNT)) return; - let cmpPlayer = QueryPlayerIDInterface(playerID); - if (!cmpPlayer || !cmpPlayer.CanBarter()) + const cmpPlayer = QueryPlayerIDInterface(playerID); + if (!cmpPlayer?.CanBarter()) return; - let prices = this.GetPrices(cmpPlayer); - let amountsToSubtract = {}; - amountsToSubtract[resourceToSell] = amount; - if (cmpPlayer.TrySubtractResources(amountsToSubtract)) - { - let amountToAdd = Math.round(prices.sell[resourceToSell] / prices.buy[resourceToBuy] * amount); - cmpPlayer.AddResource(resourceToBuy, amountToAdd); - - // Display chat message to observers. - let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); - if (cmpGUIInterface) - cmpGUIInterface.PushNotification({ - "type": "barter", - "players": [playerID], - "amountGiven": amount, - "amountGained": amountToAdd, - "resourceGiven": resourceToSell, - "resourceGained": resourceToBuy - }); - - let cmpStatisticsTracker = QueryPlayerIDInterface(playerID, IID_StatisticsTracker); - if (cmpStatisticsTracker) - { - cmpStatisticsTracker.IncreaseResourcesSoldCounter(resourceToSell, amount); - cmpStatisticsTracker.IncreaseResourcesBoughtCounter(resourceToBuy, amountToAdd); - } - - let difference = this.DIFFERENCE_PER_DEAL * amount / 100; - // Increase price difference for both exchange resources. - // Overall price difference (dynamic +/- constant) can't exceed +-99%. - this.priceDifferences[resourceToSell] -= difference; - this.priceDifferences[resourceToSell] = Math.min(99 - this.CONSTANT_DIFFERENCE, Math.max(this.CONSTANT_DIFFERENCE - 99, this.priceDifferences[resourceToSell])); - this.priceDifferences[resourceToBuy] += difference; - this.priceDifferences[resourceToBuy] = Math.min(99 - this.CONSTANT_DIFFERENCE, Math.max(this.CONSTANT_DIFFERENCE - 99, this.priceDifferences[resourceToBuy])); - } + const amountsToSubtract = { + [resourceToSell]: amount + }; + if (!cmpPlayer.TrySubtractResources(amountsToSubtract)) + return; + + const prices = this.GetPrices(cmpPlayer); + const amountToAdd = Math.round(prices.sell[resourceToSell] / prices.buy[resourceToBuy] * amount); + cmpPlayer.AddResource(resourceToBuy, amountToAdd); + + Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface)?.PushNotification({ + "type": "barter", + "players": [playerID], + "amountGiven": amount, + "amountGained": amountToAdd, + "resourceGiven": resourceToSell, + "resourceGained": resourceToBuy + }); + + const cmpStatisticsTracker = QueryPlayerIDInterface(playerID, IID_StatisticsTracker); + if (cmpStatisticsTracker) + { + cmpStatisticsTracker.IncreaseResourcesSoldCounter(resourceToSell, amount); + cmpStatisticsTracker.IncreaseResourcesBoughtCounter(resourceToBuy, amountToAdd); + } + + const difference = this.DIFFERENCE_PER_DEAL * amount / this.DEAL_AMOUNT; + // Overall price difference (dynamic +/- constant) can't exceed +-99%. + const maxDifference = this.DEAL_AMOUNT * 0.99; + + // Increase price difference for both exchanged resources. + this.priceDifferences[resourceToSell] -= difference; + this.priceDifferences[resourceToSell] = Math.min(maxDifference - this.CONSTANT_DIFFERENCE, Math.max(this.CONSTANT_DIFFERENCE - maxDifference, this.priceDifferences[resourceToSell])); + this.priceDifferences[resourceToBuy] += difference; + this.priceDifferences[resourceToBuy] = Math.min(maxDifference - this.CONSTANT_DIFFERENCE, Math.max(this.CONSTANT_DIFFERENCE - maxDifference, this.priceDifferences[resourceToBuy])); - if (this.restoreTimer === undefined) - this.restoreTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).SetInterval(this.entity, IID_Barter, "ProgressTimeout", this.RESTORE_TIMER_INTERVAL, this.RESTORE_TIMER_INTERVAL, {}); + if (!this.restoreTimer) + this.restoreTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).SetInterval(this.entity, IID_Barter, "ProgressTimeout", this.RESTORE_TIMER_INTERVAL, this.RESTORE_TIMER_INTERVAL, null); }; Barter.prototype.ProgressTimeout = function(data) { let needRestore = false; - for (let resource of Resources.GetBarterableCodes()) + for (const resource in this.priceDifferences) { // Calculate value to restore, it should be limited to [-DIFFERENCE_RESTORE; DIFFERENCE_RESTORE] interval - let differenceRestore = Math.min(this.DIFFERENCE_RESTORE, Math.max(-this.DIFFERENCE_RESTORE, this.priceDifferences[resource])); - differenceRestore = -differenceRestore; - this.priceDifferences[resource] += differenceRestore; - // If price difference still exists then set flag to run timer again - if (this.priceDifferences[resource] != 0) + this.priceDifferences[resource] -= Math.min(this.DIFFERENCE_RESTORE, Math.max(-this.DIFFERENCE_RESTORE, this.priceDifferences[resource])); + // If price difference still exists then set flag to keep the timer running. + if (this.priceDifferences[resource] !== 0) needRestore = true; } if (!needRestore) { - let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); - cmpTimer.CancelTimer(this.restoreTimer); - this.restoreTimer = undefined; + Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).CancelTimer(this.restoreTimer); + delete this.restoreTimer; } }; Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Barter.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Barter.js +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Barter.js @@ -144,10 +144,6 @@ }); cmpBarter.ExchangeResources(playerID, "wood", "stone", 100); -// It seems useless to try to activate the timer if we don't barter any resources. -TS_ASSERT_EQUALS(cmpBarter.restoreTimer, 7); -TS_ASSERT(timerActivated); - TS_ASSERT_UNEVAL_EQUALS(cmpBarter.priceDifferences, { "wood": 0, "stone": 0, "metal": 0 }); DeleteMock(playerEnt, IID_Player);