LCOV - code coverage report
Current view: top level - simulation/components - Trader.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 161 0.0 %
Date: 2023-04-02 12:52:40 Functions: 0 21 0.0 %

          Line data    Source code
       1             : // See helpers/TraderGain.js for the CalculateTaderGain() function which works out how many
       2             : // resources a trader gets
       3             : 
       4             : function Trader() {}
       5             : 
       6           0 : Trader.prototype.Schema =
       7             :         "<a:help>Lets the unit generate resouces while moving between markets (or docks in case of water trading).</a:help>" +
       8             :         "<a:example>" +
       9             :                 "<GainMultiplier>0.75</GainMultiplier>" +
      10             :                 "<GarrisonGainMultiplier>0.2</GarrisonGainMultiplier>" +
      11             :         "</a:example>" +
      12             :         "<element name='GainMultiplier' a:help='Trader gain for a 100m distance and mapSize = 1024'>" +
      13             :                 "<ref name='positiveDecimal'/>" +
      14             :         "</element>" +
      15             :         "<optional>" +
      16             :                 "<element name='GarrisonGainMultiplier' a:help='Additional gain for garrisonable unit for each garrisoned trader (1.0 means 100%)'>" +
      17             :                         "<ref name='positiveDecimal'/>" +
      18             :                 "</element>" +
      19             :         "</optional>";
      20             : 
      21           0 : Trader.prototype.Init = function()
      22             : {
      23           0 :         this.markets = [];
      24           0 :         this.index = -1;
      25           0 :         this.goods = {
      26             :                 "type": null,
      27             :                 "amount": null
      28             :         };
      29             : };
      30             : 
      31           0 : Trader.prototype.CalculateGain = function(currentMarket, nextMarket)
      32             : {
      33           0 :         let cmpMarket = QueryMiragedInterface(currentMarket, IID_Market);
      34           0 :         let gain = cmpMarket && cmpMarket.CalculateTraderGain(nextMarket, this.template, this.entity);
      35           0 :         if (!gain)      // One of our markets must have been destroyed
      36           0 :                 return null;
      37             : 
      38             :         // For garrisonable unit increase gain for each garrisoned trader
      39             :         // Calculate this here to save passing unnecessary stuff into the CalculateTraderGain function
      40           0 :         let garrisonGainMultiplier = this.GetGarrisonGainMultiplier();
      41           0 :         if (garrisonGainMultiplier === undefined)
      42           0 :                 return gain;
      43             : 
      44           0 :         let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
      45           0 :         if (!cmpGarrisonHolder)
      46           0 :                 return gain;
      47             : 
      48           0 :         let garrisonMultiplier = 1;
      49           0 :         let garrisonedTradersCount = 0;
      50           0 :         for (let entity of cmpGarrisonHolder.GetEntities())
      51             :         {
      52           0 :                 let cmpGarrisonedUnitTrader = Engine.QueryInterface(entity, IID_Trader);
      53           0 :                 if (cmpGarrisonedUnitTrader)
      54           0 :                         ++garrisonedTradersCount;
      55             :         }
      56           0 :         garrisonMultiplier *= 1 + garrisonGainMultiplier * garrisonedTradersCount;
      57             : 
      58           0 :         if (gain.traderGain)
      59           0 :                 gain.traderGain = Math.round(garrisonMultiplier * gain.traderGain);
      60           0 :         if (gain.market1Gain)
      61           0 :                 gain.market1Gain = Math.round(garrisonMultiplier * gain.market1Gain);
      62           0 :         if (gain.market2Gain)
      63           0 :                 gain.market2Gain = Math.round(garrisonMultiplier * gain.market2Gain);
      64             : 
      65           0 :         return gain;
      66             : };
      67             : 
      68             : /**
      69             :  * Remove market from trade route iff only first market is set.
      70             :  * @param {number} id of market to be removed.
      71             :  * @return {boolean} true iff removal was successful.
      72             :  */
      73           0 : Trader.prototype.RemoveTargetMarket = function(target)
      74             : {
      75           0 :         if (this.markets.length != 1 || this.markets[0] != target)
      76           0 :                 return false;
      77           0 :         let cmpTargetMarket = QueryMiragedInterface(target, IID_Market);
      78           0 :         if (!cmpTargetMarket)
      79           0 :                 return false;
      80           0 :         cmpTargetMarket.RemoveTrader(this.entity);
      81           0 :         this.index = -1;
      82           0 :         this.markets = [];
      83           0 :         return true;
      84             : };
      85             : 
      86             : // Set target as target market.
      87             : // Return true if at least one of markets was changed.
      88           0 : Trader.prototype.SetTargetMarket = function(target, source)
      89             : {
      90           0 :         let cmpTargetMarket = QueryMiragedInterface(target, IID_Market);
      91           0 :         if (!cmpTargetMarket)
      92           0 :                 return false;
      93             : 
      94           0 :         if (source)
      95             :         {
      96             :                 // Establish a trade route with both markets in one go.
      97           0 :                 let cmpSourceMarket = QueryMiragedInterface(source, IID_Market);
      98           0 :                 if (!cmpSourceMarket)
      99           0 :                         return false;
     100           0 :                 this.markets = [source];
     101             :         }
     102           0 :         if (this.markets.length >= 2)
     103             :         {
     104             :                 // If we already have both markets - drop them
     105             :                 // and use the target as first market
     106           0 :                 for (let market of this.markets)
     107             :                 {
     108           0 :                         let cmpMarket = QueryMiragedInterface(market, IID_Market);
     109           0 :                         if (cmpMarket)
     110           0 :                                 cmpMarket.RemoveTrader(this.entity);
     111             :                 }
     112           0 :                 this.index = 0;
     113           0 :                 this.markets = [target];
     114           0 :                 cmpTargetMarket.AddTrader(this.entity);
     115             :         }
     116           0 :         else if (this.markets.length == 1)
     117             :         {
     118             :                 // If we have only one market and target is different from it,
     119             :                 // set the target as second one
     120           0 :                 if (target == this.markets[0])
     121           0 :                         return false;
     122             : 
     123           0 :                 this.index = 0;
     124           0 :                 this.markets.push(target);
     125           0 :                 cmpTargetMarket.AddTrader(this.entity);
     126           0 :                 this.goods.amount = this.CalculateGain(this.markets[0], this.markets[1]);
     127             :         }
     128             :         else
     129             :         {
     130             :                 // Else we don't have target markets at all,
     131             :                 // set the target as first market
     132           0 :                 this.index = 0;
     133           0 :                 this.markets = [target];
     134           0 :                 cmpTargetMarket.AddTrader(this.entity);
     135             :         }
     136             :         // Drop carried goods if markets were changed
     137           0 :         this.goods.amount = null;
     138           0 :         return true;
     139             : };
     140             : 
     141           0 : Trader.prototype.GetFirstMarket = function()
     142             : {
     143           0 :         return this.markets[0] || null;
     144             : };
     145             : 
     146           0 : Trader.prototype.GetSecondMarket = function()
     147             : {
     148           0 :         return this.markets[1] || null;
     149             : };
     150             : 
     151           0 : Trader.prototype.GetTraderGainMultiplier = function()
     152             : {
     153           0 :         return ApplyValueModificationsToEntity("Trader/GainMultiplier", +this.template.GainMultiplier, this.entity);
     154             : };
     155             : 
     156           0 : Trader.prototype.GetGarrisonGainMultiplier = function()
     157             : {
     158           0 :         if (this.template.GarrisonGainMultiplier === undefined)
     159           0 :                 return undefined;
     160           0 :         return ApplyValueModificationsToEntity("Trader/GarrisonGainMultiplier", +this.template.GarrisonGainMultiplier, this.entity);
     161             : };
     162             : 
     163           0 : Trader.prototype.HasBothMarkets = function()
     164             : {
     165           0 :         return this.markets.length >= 2;
     166             : };
     167             : 
     168           0 : Trader.prototype.CanTrade = function(target)
     169             : {
     170           0 :         let cmpTraderIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     171             : 
     172           0 :         let cmpTargetMarket = QueryMiragedInterface(target, IID_Market);
     173           0 :         if (!cmpTargetMarket)
     174           0 :                 return false;
     175             : 
     176           0 :         let cmpTargetFoundation = Engine.QueryInterface(target, IID_Foundation);
     177           0 :         if (cmpTargetFoundation)
     178           0 :                 return false;
     179             : 
     180           0 :         if (!(cmpTraderIdentity.HasClass("Organic") && cmpTargetMarket.HasType("land")) &&
     181             :                 !(cmpTraderIdentity.HasClass("Ship") && cmpTargetMarket.HasType("naval")))
     182           0 :                 return false;
     183             : 
     184           0 :         let cmpTraderPlayer = QueryOwnerInterface(this.entity, IID_Player);
     185           0 :         let cmpTargetPlayer = QueryOwnerInterface(target, IID_Player);
     186             : 
     187           0 :         return cmpTraderPlayer && cmpTargetPlayer && !cmpTraderPlayer.IsEnemy(cmpTargetPlayer.GetPlayerID());
     188             : };
     189             : 
     190           0 : Trader.prototype.AddResources = function(ent, gain)
     191             : {
     192           0 :         let cmpPlayer = QueryOwnerInterface(ent);
     193           0 :         if (cmpPlayer)
     194           0 :                 cmpPlayer.AddResource(this.goods.type, gain);
     195             : 
     196           0 :         let cmpStatisticsTracker = QueryOwnerInterface(ent, IID_StatisticsTracker);
     197           0 :         if (cmpStatisticsTracker)
     198           0 :                 cmpStatisticsTracker.IncreaseTradeIncomeCounter(gain);
     199             : };
     200             : 
     201           0 : Trader.prototype.GenerateResources = function(currentMarket, nextMarket)
     202             : {
     203           0 :         this.AddResources(this.entity, this.goods.amount.traderGain);
     204             : 
     205           0 :         if (this.goods.amount.market1Gain)
     206           0 :                 this.AddResources(currentMarket, this.goods.amount.market1Gain);
     207             : 
     208           0 :         if (this.goods.amount.market2Gain)
     209           0 :                 this.AddResources(nextMarket, this.goods.amount.market2Gain);
     210             : };
     211             : 
     212           0 : Trader.prototype.PerformTrade = function(currentMarket)
     213             : {
     214           0 :         let previousMarket = this.markets[this.index];
     215           0 :         if (previousMarket != currentMarket)  // Inconsistent markets
     216             :         {
     217           0 :                 this.goods.amount = null;
     218           0 :                 return INVALID_ENTITY;
     219             :         }
     220             : 
     221           0 :         this.index = ++this.index % this.markets.length;
     222           0 :         let nextMarket = this.markets[this.index];
     223             : 
     224           0 :         if (this.goods.amount && this.goods.amount.traderGain)
     225           0 :                 this.GenerateResources(previousMarket, nextMarket);
     226             : 
     227           0 :         let cmpPlayer = QueryOwnerInterface(this.entity);
     228           0 :         if (!cmpPlayer)
     229           0 :                 return INVALID_ENTITY;
     230             : 
     231           0 :         this.goods.type = cmpPlayer.GetNextTradingGoods();
     232           0 :         this.goods.amount = this.CalculateGain(currentMarket, nextMarket);
     233             : 
     234           0 :         return nextMarket;
     235             : };
     236             : 
     237           0 : Trader.prototype.GetGoods = function()
     238             : {
     239           0 :         return this.goods;
     240             : };
     241             : 
     242             : /**
     243             :  * Returns true if the trader has the given market (can be either a market or a mirage)
     244             :  */
     245           0 : Trader.prototype.HasMarket = function(market)
     246             : {
     247           0 :         return this.markets.indexOf(market) != -1;
     248             : };
     249             : 
     250             : /**
     251             :  * Remove a market when this trader can no longer trade with it
     252             :  */
     253           0 : Trader.prototype.RemoveMarket = function(market)
     254             : {
     255           0 :         let index = this.markets.indexOf(market);
     256           0 :         if (index == -1)
     257           0 :                 return;
     258           0 :         this.markets.splice(index, 1);
     259           0 :         let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
     260           0 :         if (cmpUnitAI)
     261           0 :                 cmpUnitAI.MarketRemoved(market);
     262             : };
     263             : 
     264             : /**
     265             :  * Switch between a market and its mirage according to visibility
     266             :  */
     267           0 : Trader.prototype.SwitchMarket = function(oldMarket, newMarket)
     268             : {
     269           0 :         let index = this.markets.indexOf(oldMarket);
     270           0 :         if (index == -1)
     271           0 :                 return;
     272           0 :         this.markets[index] = newMarket;
     273           0 :         let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
     274           0 :         if (cmpUnitAI)
     275           0 :                 cmpUnitAI.SwitchMarketOrder(oldMarket, newMarket);
     276             : };
     277             : 
     278           0 : Trader.prototype.StopTrading = function()
     279             : {
     280           0 :         for (let market of this.markets)
     281             :         {
     282           0 :                 let cmpMarket = QueryMiragedInterface(market, IID_Market);
     283           0 :                 if (cmpMarket)
     284           0 :                         cmpMarket.RemoveTrader(this.entity);
     285             :         }
     286           0 :         this.index = -1;
     287           0 :         this.markets = [];
     288           0 :         this.goods.amount = null;
     289           0 :         this.markets = [];
     290             : };
     291             : 
     292             : // Get range in which deals with market are available,
     293             : // i.e. trader should be in no more than MaxDistance from market
     294             : // to be able to trade with it.
     295           0 : Trader.prototype.GetRange = function()
     296             : {
     297           0 :         let cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
     298           0 :         let max = 1;
     299           0 :         if (cmpObstruction)
     300           0 :                 max += cmpObstruction.GetSize() * 1.5;
     301           0 :         return { "min": 0, "max": max };
     302             : };
     303             : 
     304           0 : Trader.prototype.OnGarrisonedUnitsChanged = function()
     305             : {
     306           0 :         if (this.HasBothMarkets())
     307           0 :                 this.goods.amount = this.CalculateGain(this.markets[0], this.markets[1]);
     308             : };
     309             : 
     310           0 : Engine.RegisterComponentType(IID_Trader, "Trader", Trader);

Generated by: LCOV version 1.14