Line data Source code
1 : function Market() {} 2 : 3 0 : Market.prototype.Schema = 4 : "<element name='TradeType' a:help='Specifies the type of possible trade route (land or naval).'>" + 5 : "<list>" + 6 : "<oneOrMore>" + 7 : "<choice>" + 8 : "<value>land</value>" + 9 : "<value>naval</value>" + 10 : "</choice>" + 11 : "</oneOrMore>" + 12 : "</list>" + 13 : "</element>" + 14 : "<element name='InternationalBonus' a:help='Additional part of the gain donated when two different players trade'>" + 15 : "<ref name='nonNegativeDecimal'/>" + 16 : "</element>"; 17 : 18 0 : Market.prototype.Init = function() 19 : { 20 0 : this.traders = new Set(); // list of traders with a route on this market 21 0 : this.tradeType = new Set(this.template.TradeType.split(/\s+/)); 22 : }; 23 : 24 0 : Market.prototype.AddTrader = function(ent) 25 : { 26 0 : this.traders.add(ent); 27 : }; 28 : 29 0 : Market.prototype.RemoveTrader = function(ent) 30 : { 31 0 : this.traders.delete(ent); 32 : }; 33 : 34 0 : Market.prototype.GetInternationalBonus = function() 35 : { 36 0 : return ApplyValueModificationsToEntity("Market/InternationalBonus", +this.template.InternationalBonus, this.entity); 37 : }; 38 : 39 0 : Market.prototype.HasType = function(type) 40 : { 41 0 : return this.tradeType.has(type); 42 : }; 43 : 44 0 : Market.prototype.GetType = function() 45 : { 46 0 : return this.tradeType; 47 : }; 48 : 49 0 : Market.prototype.GetTraders = function() 50 : { 51 0 : return this.traders; 52 : }; 53 : 54 : /** 55 : * Check if the traders attached to this market can still trade with it 56 : * Warning: traders currently trading with a mirage of this market are dealt with in Mirage.js 57 : */ 58 : 59 0 : Market.prototype.UpdateTraders = function(onDestruction) 60 : { 61 0 : for (let trader of this.traders) 62 : { 63 0 : let cmpTrader = Engine.QueryInterface(trader, IID_Trader); 64 0 : if (!cmpTrader) 65 : { 66 0 : this.RemoveTrader(trader); 67 0 : continue; 68 : } 69 0 : if (!cmpTrader.HasMarket(this.entity) || !onDestruction && cmpTrader.CanTrade(this.entity)) 70 0 : continue; 71 : // this trader can no more trade 72 0 : this.RemoveTrader(trader); 73 0 : cmpTrader.RemoveMarket(this.entity); 74 : } 75 : }; 76 : 77 0 : Market.prototype.CalculateTraderGain = function(secondMarket, traderTemplate, trader) 78 : { 79 0 : let cmpMarket2 = QueryMiragedInterface(secondMarket, IID_Market); 80 0 : if (!cmpMarket2) 81 0 : return null; 82 : 83 0 : let cmpMarket1Player = QueryOwnerInterface(this.entity); 84 0 : let cmpMarket2Player = QueryOwnerInterface(secondMarket); 85 0 : if (!cmpMarket1Player || !cmpMarket2Player) 86 0 : return null; 87 : 88 0 : let cmpFirstMarketPosition = Engine.QueryInterface(this.entity, IID_Position); 89 0 : let cmpSecondMarketPosition = Engine.QueryInterface(secondMarket, IID_Position); 90 0 : if (!cmpFirstMarketPosition || !cmpFirstMarketPosition.IsInWorld() || 91 : !cmpSecondMarketPosition || !cmpSecondMarketPosition.IsInWorld()) 92 0 : return null; 93 0 : let firstMarketPosition = cmpFirstMarketPosition.GetPosition2D(); 94 0 : let secondMarketPosition = cmpSecondMarketPosition.GetPosition2D(); 95 : 96 0 : let mapSize = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain).GetMapSize(); 97 0 : let gainMultiplier = TradeGainNormalization(mapSize); 98 0 : if (trader) 99 : { 100 0 : let cmpTrader = Engine.QueryInterface(trader, IID_Trader); 101 0 : if (!cmpTrader) 102 0 : return null; 103 0 : gainMultiplier *= cmpTrader.GetTraderGainMultiplier(); 104 : } 105 : // Called from the gui, modifications already applied. 106 : else 107 : { 108 0 : if (!traderTemplate || !traderTemplate.GainMultiplier) 109 0 : return null; 110 0 : gainMultiplier *= traderTemplate.GainMultiplier; 111 : } 112 : 113 0 : let gain = {}; 114 : 115 : // Calculate ordinary Euclidean distance between markets. 116 : // We don't use pathfinder, because ordinary distance looks more fair. 117 0 : let distanceSq = firstMarketPosition.distanceToSquared(secondMarketPosition); 118 : // We calculate gain as square of distance to encourage trading between remote markets 119 : // and gainMultiplier corresponds to the gain for a 100m distance 120 0 : gain.traderGain = Math.round(gainMultiplier * TradeGain(distanceSq, mapSize)); 121 : 122 0 : gain.market1Owner = cmpMarket1Player.GetPlayerID(); 123 0 : gain.market2Owner = cmpMarket2Player.GetPlayerID(); 124 : // If trader undefined, the trader owner is supposed to be the same as the first market. 125 0 : let cmpPlayer = trader ? QueryOwnerInterface(trader) : cmpMarket1Player; 126 0 : if (!cmpPlayer) 127 0 : return null; 128 0 : gain.traderOwner = cmpPlayer.GetPlayerID(); 129 : 130 0 : if (gain.market1Owner != gain.market2Owner) 131 : { 132 0 : let internationalBonus1 = this.GetInternationalBonus(); 133 0 : let internationalBonus2 = cmpMarket2.GetInternationalBonus(); 134 0 : gain.market1Gain = Math.round(gain.traderGain * internationalBonus1); 135 0 : gain.market2Gain = Math.round(gain.traderGain * internationalBonus2); 136 : } 137 : 138 0 : return gain; 139 : }; 140 : 141 0 : Market.prototype.OnDiplomacyChanged = function(msg) 142 : { 143 0 : this.UpdateTraders(false); 144 : }; 145 : 146 0 : Market.prototype.OnOwnershipChanged = function(msg) 147 : { 148 0 : this.UpdateTraders(msg.to == INVALID_PLAYER); 149 : }; 150 : 151 : function MarketMirage() {} 152 0 : MarketMirage.prototype.Init = function(cmpMarket, entity, parent, player) 153 : { 154 0 : this.entity = entity; 155 0 : this.parent = parent; 156 0 : this.player = player; 157 : 158 0 : this.traders = new Set(); 159 0 : for (let trader of cmpMarket.GetTraders()) 160 : { 161 0 : let cmpTrader = Engine.QueryInterface(trader, IID_Trader); 162 0 : let cmpOwnership = Engine.QueryInterface(trader, IID_Ownership); 163 0 : if (!cmpTrader || !cmpOwnership) 164 : { 165 0 : cmpMarket.RemoveTrader(trader); 166 0 : continue; 167 : } 168 0 : if (this.player != cmpOwnership.GetOwner()) 169 0 : continue; 170 0 : cmpTrader.SwitchMarket(cmpMarket.entity, this.entity); 171 0 : cmpMarket.RemoveTrader(trader); 172 0 : this.AddTrader(trader); 173 : } 174 0 : this.marketType = cmpMarket.GetType(); 175 0 : this.internationalBonus = cmpMarket.GetInternationalBonus(); 176 : }; 177 : 178 0 : MarketMirage.prototype.HasType = function(type) { return this.marketType.has(type); }; 179 0 : MarketMirage.prototype.GetInternationalBonus = function() { return this.internationalBonus; }; 180 0 : MarketMirage.prototype.AddTrader = function(trader) { this.traders.add(trader); }; 181 0 : MarketMirage.prototype.RemoveTrader = function(trader) { this.traders.delete(trader); }; 182 : 183 0 : MarketMirage.prototype.UpdateTraders = function(msg) 184 : { 185 0 : let cmpMarket = Engine.QueryInterface(this.parent, IID_Market); 186 0 : if (!cmpMarket) // The parent market does not exist anymore 187 : { 188 0 : for (let trader of this.traders) 189 : { 190 0 : let cmpTrader = Engine.QueryInterface(trader, IID_Trader); 191 0 : if (cmpTrader) 192 0 : cmpTrader.RemoveMarket(this.entity); 193 : } 194 0 : return; 195 : } 196 : 197 : // The market becomes visible, switch all traders from the mirage to the market 198 0 : for (let trader of this.traders) 199 : { 200 0 : let cmpTrader = Engine.QueryInterface(trader, IID_Trader); 201 0 : if (!cmpTrader) 202 0 : continue; 203 0 : cmpTrader.SwitchMarket(this.entity, cmpMarket.entity); 204 0 : this.RemoveTrader(trader); 205 0 : cmpMarket.AddTrader(trader); 206 : } 207 : }; 208 : 209 0 : MarketMirage.prototype.CalculateTraderGain = Market.prototype.CalculateTraderGain; 210 : 211 0 : Engine.RegisterGlobal("MarketMirage", MarketMirage); 212 : 213 0 : Market.prototype.Mirage = function(mirageID, miragePlayer) 214 : { 215 0 : let mirage = new MarketMirage(); 216 0 : mirage.Init(this, mirageID, this.entity, miragePlayer); 217 0 : return mirage; 218 : }; 219 : 220 0 : Engine.RegisterComponentType(IID_Market, "Market", Market);