Index: ps/trunk/binaries/data/mods/public/simulation/components/Barter.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Barter.js (revision 21732)
+++ ps/trunk/binaries/data/mods/public/simulation/components/Barter.js (revision 21733)
@@ -1,157 +1,158 @@
function Barter() {}
Barter.prototype.Schema =
"";
/**
* The "true price" is a base price of 100 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.
*
* 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
*/
Barter.prototype.RESTORE_TIMER_INTERVAL = 5000;
Barter.prototype.Init = function()
{
this.priceDifferences = {};
for (let resource of Resources.GetCodes())
this.priceDifferences[resource] = 0;
this.restoreTimer = undefined;
};
Barter.prototype.GetPrices = function(playerID)
{
var prices = { "buy": {}, "sell": {} };
let multiplier = QueryPlayerIDInterface(playerID).GetBarterMultiplier();
for (let resource of Resources.GetCodes())
{
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;
}
return prices;
};
Barter.prototype.PlayerHasMarket = function(playerID)
{
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
var entities = cmpRangeManager.GetEntitiesByPlayer(playerID);
for (var entity of entities)
{
var cmpFoundation = Engine.QueryInterface(entity, IID_Foundation);
var cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
if (!cmpFoundation && cmpIdentity && cmpIdentity.HasClass("BarterMarket"))
return true;
}
return false;
};
Barter.prototype.ExchangeResources = function(playerID, resourceToSell, resourceToBuy, amount)
{
- // Data verification
if (amount <= 0)
{
warn("ExchangeResources: incorrect amount: " + uneval(amount));
return;
}
+
let availResources = Resources.GetCodes();
if (availResources.indexOf(resourceToSell) == -1)
{
warn("ExchangeResources: incorrect resource to sell: " + uneval(resourceToSell));
return;
}
+
if (availResources.indexOf(resourceToBuy) == -1)
{
warn("ExchangeResources: incorrect resource to buy: " + uneval(resourceToBuy));
return;
}
+
+ // This can occur when the player issues the order just before the market is destroyed or captured
if (!this.PlayerHasMarket(playerID))
- {
- warn("ExchangeResources: player has no markets");
return;
- }
+
if (amount != 100 && amount != 500)
return;
var cmpPlayer = QueryPlayerIDInterface(playerID);
var prices = this.GetPrices(playerID);
var amountsToSubtract = {};
amountsToSubtract[resourceToSell] = amount;
if (cmpPlayer.TrySubtractResources(amountsToSubtract))
{
var amountToAdd = Math.round(prices["sell"][resourceToSell] / prices["buy"][resourceToBuy] * amount);
cmpPlayer.AddResource(resourceToBuy, amountToAdd);
// Display chat message to observers
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
if (cmpGUIInterface)
cmpGUIInterface.PushNotification({
"type": "barter",
"players": [playerID],
"amountsSold": amount,
"amountsBought": amountToAdd,
"resourceSold": resourceToSell,
"resourceBought": resourceToBuy
});
var 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]));
}
if (this.restoreTimer === undefined)
{
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
this.restoreTimer = cmpTimer.SetInterval(this.entity, IID_Barter, "ProgressTimeout", this.RESTORE_TIMER_INTERVAL, this.RESTORE_TIMER_INTERVAL, {});
}
};
Barter.prototype.ProgressTimeout = function(data)
{
var needRestore = false;
for (let resource of Resources.GetCodes())
{
// Calculate value to restore, it should be limited to [-DIFFERENCE_RESTORE; DIFFERENCE_RESTORE] interval
var 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)
needRestore = true;
}
if (!needRestore)
{
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
cmpTimer.CancelTimer(this.restoreTimer);
this.restoreTimer = undefined;
}
};
Engine.RegisterSystemComponentType(IID_Barter, "Barter", Barter);