Index: ps/trunk/binaries/data/mods/public/simulation/components/Trader.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/Trader.js (revision 23237) +++ ps/trunk/binaries/data/mods/public/simulation/components/Trader.js (revision 23238) @@ -1,283 +1,293 @@ // See helpers/TraderGain.js for the CalculateTaderGain() function which works out how many // resources a trader gets -// Additional gain for ships for each garrisoned trader, in percents -const GARRISONED_TRADER_ADDITION = 20; - function Trader() {} Trader.prototype.Schema = "Lets the unit generate resouces while moving between markets (or docks in case of water trading)." + "" + "0.75" + + "0.2" + "" + "" + "" + - ""; + "" + + "" + + "" + + "" + + "" + + ""; 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 ship increase gain for each garrisoned trader + // For garrisonable unit increase gain for each garrisoned trader // Calculate this here to save passing unnecessary stuff into the CalculateTraderGain function - let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); - if (cmpIdentity && cmpIdentity.HasClass("Ship")) + 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 cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); - if (cmpGarrisonHolder) - { - let garrisonMultiplier = 1; - let garrisonedTradersCount = 0; - for (let entity of cmpGarrisonHolder.GetEntities()) - { - let cmpGarrisonedUnitTrader = Engine.QueryInterface(entity, IID_Trader); - if (cmpGarrisonedUnitTrader) - ++garrisonedTradersCount; - } - garrisonMultiplier *= 1 + GARRISONED_TRADER_ADDITION * garrisonedTradersCount / 100; - - 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); - } + 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); Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_ship_merchant.xml =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_ship_merchant.xml (revision 23237) +++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_ship_merchant.xml (revision 23238) @@ -1,66 +1,67 @@ 2 5 2 0 100 15 0 FemaleCitizen Infantry Healer Dog Support Infantry Cavalry Dog 0 10 true 400 -ConquestCritical Trader Bribable Merchantman phase_town Trade between docks. Garrison a Trader aboard for additional profit (+20% for each garrisoned). Gather profitable aquatic treasures. 25 15 0 12.0 75 75 50 50 0.75 + 0.2 passive false false ship-small 1.35 50 true