mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-18 14:23:56 -07:00
It removes hardcoded value from Trader.js to templates. This change allows to have merchant ships among civilisations or one civilisation itself which would give different bonus when garrisoned traders. Supports modifications to that value with technologies and auras. Differential Revision: https://code.wildfiregames.com/D2106 This was SVN commit r23238.
293 lines
8.2 KiB
JavaScript
293 lines
8.2 KiB
JavaScript
// See helpers/TraderGain.js for the CalculateTaderGain() function which works out how many
|
|
// resources a trader gets
|
|
|
|
function Trader() {}
|
|
|
|
Trader.prototype.Schema =
|
|
"<a:help>Lets the unit generate resouces while moving between markets (or docks in case of water trading).</a:help>" +
|
|
"<a:example>" +
|
|
"<GainMultiplier>0.75</GainMultiplier>" +
|
|
"<GarrisonGainMultiplier>0.2</GarrisonGainMultiplier>" +
|
|
"</a:example>" +
|
|
"<element name='GainMultiplier' a:help='Trader gain for a 100m distance and mapSize = 1024'>" +
|
|
"<ref name='positiveDecimal'/>" +
|
|
"</element>" +
|
|
"<optional>" +
|
|
"<element name='GarrisonGainMultiplier' a:help='Additional gain for garrisonable unit for each garrisoned trader (1.0 means 100%)'>" +
|
|
"<ref name='positiveDecimal'/>" +
|
|
"</element>" +
|
|
"</optional>";
|
|
|
|
Trader.prototype.Init = function()
|
|
{
|
|
this.markets = [];
|
|
this.index = -1;
|
|
this.goods = {
|
|
"type": null,
|
|
"amount": null,
|
|
"origin": null
|
|
};
|
|
};
|
|
|
|
Trader.prototype.CalculateGain = function(currentMarket, nextMarket)
|
|
{
|
|
let gain = CalculateTraderGain(currentMarket, nextMarket, this.template, this.entity);
|
|
if (!gain) // One of our markets must have been destroyed
|
|
return null;
|
|
|
|
// For garrisonable unit increase gain for each garrisoned trader
|
|
// Calculate this here to save passing unnecessary stuff into the CalculateTraderGain function
|
|
let garrisonGainMultiplier = this.GetGarrisonGainMultiplier();
|
|
if (garrisonGainMultiplier === undefined)
|
|
return gain;
|
|
|
|
let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
|
|
if (!cmpGarrisonHolder)
|
|
return gain;
|
|
|
|
let garrisonMultiplier = 1;
|
|
let garrisonedTradersCount = 0;
|
|
for (let entity of cmpGarrisonHolder.GetEntities())
|
|
{
|
|
let cmpGarrisonedUnitTrader = Engine.QueryInterface(entity, IID_Trader);
|
|
if (cmpGarrisonedUnitTrader)
|
|
++garrisonedTradersCount;
|
|
}
|
|
garrisonMultiplier *= 1 + garrisonGainMultiplier * garrisonedTradersCount;
|
|
|
|
if (gain.traderGain)
|
|
gain.traderGain = Math.round(garrisonMultiplier * gain.traderGain);
|
|
if (gain.market1Gain)
|
|
gain.market1Gain = Math.round(garrisonMultiplier * gain.market1Gain);
|
|
if (gain.market2Gain)
|
|
gain.market2Gain = Math.round(garrisonMultiplier * gain.market2Gain);
|
|
|
|
return gain;
|
|
};
|
|
|
|
// Set target as target market.
|
|
// Return true if at least one of markets was changed.
|
|
Trader.prototype.SetTargetMarket = function(target, source)
|
|
{
|
|
let cmpTargetMarket = QueryMiragedInterface(target, IID_Market);
|
|
if (!cmpTargetMarket)
|
|
return false;
|
|
|
|
if (source)
|
|
{
|
|
// Establish a trade route with both markets in one go.
|
|
let cmpSourceMarket = QueryMiragedInterface(source, IID_Market);
|
|
if (!cmpSourceMarket)
|
|
return false;
|
|
this.markets = [source];
|
|
}
|
|
if (this.markets.length >= 2)
|
|
{
|
|
// If we already have both markets - drop them
|
|
// and use the target as first market
|
|
for (let market of this.markets)
|
|
{
|
|
let cmpMarket = QueryMiragedInterface(market, IID_Market);
|
|
if (cmpMarket)
|
|
cmpMarket.RemoveTrader(this.entity);
|
|
}
|
|
this.index = 0;
|
|
this.markets = [target];
|
|
cmpTargetMarket.AddTrader(this.entity);
|
|
}
|
|
else if (this.markets.length == 1)
|
|
{
|
|
// If we have only one market and target is different from it,
|
|
// set the target as second one
|
|
if (target == this.markets[0])
|
|
return false;
|
|
|
|
this.index = 0;
|
|
this.markets.push(target);
|
|
cmpTargetMarket.AddTrader(this.entity);
|
|
this.goods.amount = this.CalculateGain(this.markets[0], this.markets[1]);
|
|
}
|
|
else
|
|
{
|
|
// Else we don't have target markets at all,
|
|
// set the target as first market
|
|
this.index = 0;
|
|
this.markets = [target];
|
|
cmpTargetMarket.AddTrader(this.entity);
|
|
}
|
|
// Drop carried goods if markets were changed
|
|
this.goods.amount = null;
|
|
return true;
|
|
};
|
|
|
|
Trader.prototype.GetFirstMarket = function()
|
|
{
|
|
return this.markets[0] || null;
|
|
};
|
|
|
|
Trader.prototype.GetSecondMarket = function()
|
|
{
|
|
return this.markets[1] || null;
|
|
};
|
|
|
|
Trader.prototype.GetTraderGainMultiplier = function()
|
|
{
|
|
return ApplyValueModificationsToEntity("Trader/GainMultiplier", +this.template.GainMultiplier, this.entity);
|
|
};
|
|
|
|
Trader.prototype.GetGarrisonGainMultiplier = function()
|
|
{
|
|
if (this.template.GarrisonGainMultiplier === undefined)
|
|
return undefined;
|
|
return ApplyValueModificationsToEntity("Trader/GarrisonGainMultiplier", +this.template.GarrisonGainMultiplier, this.entity);
|
|
};
|
|
|
|
Trader.prototype.HasBothMarkets = function()
|
|
{
|
|
return this.markets.length >= 2;
|
|
};
|
|
|
|
Trader.prototype.CanTrade = function(target)
|
|
{
|
|
let cmpTraderIdentity = Engine.QueryInterface(this.entity, IID_Identity);
|
|
|
|
let cmpTargetMarket = QueryMiragedInterface(target, IID_Market);
|
|
if (!cmpTargetMarket)
|
|
return false;
|
|
|
|
let cmpTargetFoundation = Engine.QueryInterface(target, IID_Foundation);
|
|
if (cmpTargetFoundation)
|
|
return false;
|
|
|
|
if (!(cmpTraderIdentity.HasClass("Organic") && cmpTargetMarket.HasType("land")) &&
|
|
!(cmpTraderIdentity.HasClass("Ship") && cmpTargetMarket.HasType("naval")))
|
|
return false;
|
|
|
|
let cmpTraderPlayer = QueryOwnerInterface(this.entity, IID_Player);
|
|
let cmpTargetPlayer = QueryOwnerInterface(target, IID_Player);
|
|
|
|
return !cmpTraderPlayer.IsEnemy(cmpTargetPlayer.GetPlayerID());
|
|
};
|
|
|
|
Trader.prototype.AddResources = function(ent, gain)
|
|
{
|
|
let cmpPlayer = QueryOwnerInterface(ent);
|
|
if (cmpPlayer)
|
|
cmpPlayer.AddResource(this.goods.type, gain);
|
|
|
|
let cmpStatisticsTracker = QueryOwnerInterface(ent, IID_StatisticsTracker);
|
|
if (cmpStatisticsTracker)
|
|
cmpStatisticsTracker.IncreaseTradeIncomeCounter(gain);
|
|
};
|
|
|
|
Trader.prototype.GenerateResources = function(currentMarket, nextMarket)
|
|
{
|
|
this.AddResources(this.entity, this.goods.amount.traderGain);
|
|
|
|
if (this.goods.amount.market1Gain)
|
|
this.AddResources(currentMarket, this.goods.amount.market1Gain);
|
|
|
|
if (this.goods.amount.market2Gain)
|
|
this.AddResources(nextMarket, this.goods.amount.market2Gain);
|
|
};
|
|
|
|
Trader.prototype.PerformTrade = function(currentMarket)
|
|
{
|
|
let previousMarket = this.markets[this.index];
|
|
if (previousMarket != currentMarket) // Inconsistent markets
|
|
{
|
|
this.goods.amount = null;
|
|
return INVALID_ENTITY;
|
|
}
|
|
|
|
this.index = ++this.index % this.markets.length;
|
|
let nextMarket = this.markets[this.index];
|
|
|
|
if (this.goods.amount && this.goods.amount.traderGain)
|
|
this.GenerateResources(previousMarket, nextMarket);
|
|
|
|
let cmpPlayer = QueryOwnerInterface(this.entity);
|
|
if (!cmpPlayer)
|
|
return INVALID_ENTITY;
|
|
|
|
this.goods.type = cmpPlayer.GetNextTradingGoods();
|
|
this.goods.amount = this.CalculateGain(currentMarket, nextMarket);
|
|
this.goods.origin = currentMarket;
|
|
|
|
return nextMarket;
|
|
};
|
|
|
|
Trader.prototype.GetGoods = function()
|
|
{
|
|
return this.goods;
|
|
};
|
|
|
|
/**
|
|
* Returns true if the trader has the given market (can be either a market or a mirage)
|
|
*/
|
|
Trader.prototype.HasMarket = function(market)
|
|
{
|
|
return this.markets.indexOf(market) != -1;
|
|
};
|
|
|
|
/**
|
|
* Remove a market when this trader can no longer trade with it
|
|
*/
|
|
Trader.prototype.RemoveMarket = function(market)
|
|
{
|
|
let index = this.markets.indexOf(market);
|
|
if (index == -1)
|
|
return;
|
|
this.markets.splice(index, 1);
|
|
let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
|
|
if (cmpUnitAI)
|
|
cmpUnitAI.MarketRemoved(market);
|
|
};
|
|
|
|
/**
|
|
* Switch between a market and its mirage according to visibility
|
|
*/
|
|
Trader.prototype.SwitchMarket = function(oldMarket, newMarket)
|
|
{
|
|
let index = this.markets.indexOf(oldMarket);
|
|
if (index == -1)
|
|
return;
|
|
this.markets[index] = newMarket;
|
|
let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
|
|
if (cmpUnitAI)
|
|
cmpUnitAI.SwitchMarketOrder(oldMarket, newMarket);
|
|
};
|
|
|
|
Trader.prototype.StopTrading = function()
|
|
{
|
|
for (let market of this.markets)
|
|
{
|
|
let cmpMarket = QueryMiragedInterface(market, IID_Market);
|
|
if (cmpMarket)
|
|
cmpMarket.RemoveTrader(this.entity);
|
|
}
|
|
this.index = -1;
|
|
this.markets = [];
|
|
this.goods.amount = null;
|
|
this.markets = [];
|
|
};
|
|
|
|
// Get range in which deals with market are available,
|
|
// i.e. trader should be in no more than MaxDistance from market
|
|
// to be able to trade with it.
|
|
Trader.prototype.GetRange = function()
|
|
{
|
|
let cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
|
|
let max = 1;
|
|
if (cmpObstruction)
|
|
max += cmpObstruction.GetUnitRadius()*1.5;
|
|
return { "min": 0, "max": max };
|
|
};
|
|
|
|
Trader.prototype.OnGarrisonedUnitsChanged = function()
|
|
{
|
|
if (this.HasBothMarkets())
|
|
this.goods.amount = this.CalculateGain(this.markets[0], this.markets[1]);
|
|
};
|
|
|
|
Engine.RegisterComponentType(IID_Trader, "Trader", Trader);
|