Ticket #3697: Trade.patch
File Trade.patch, 42.0 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/globalscripts/Templates.js
239 239 if (template.UnitMotion.Run) 240 240 ret.speed.run = func("UnitMotion/Run/Speed", +template.UnitMotion.Run.Speed, player, template); 241 241 } 242 243 if (template.Trader)244 {245 ret.trader = {246 "GainMultiplier": func("Trader/GainMultiplier", +template.Trader.GainMultiplier, player, template)247 };248 }249 242 250 243 if (template.WallSet) 251 244 { -
binaries/data/mods/public/gui/session/utility_functions.js
113 113 /** 114 114 * Returns a message with the details of the trade gain. 115 115 */ 116 function getTrad ingTooltip(gain)116 function getTradeRouteTooltip(gain) 117 117 { 118 119 var playerID = Engine.GetPlayerID();120 var simState = GetSimState();121 118 122 var gainString = gain.traderGain; 123 if (gain.market1Gain && gain.market1Owner == gain.traderOwner) 124 gainString += translate("+") + gain.market1Gain; 125 if (gain.market2Gain && gain.market2Owner == gain.traderOwner) 126 gainString += translate("+") + gain.market2Gain; 119 return translate("This trader is currently trading between two markets, creating wealth for his empire."); 120 } 127 121 128 var tooltip = sprintf(translate("%(gain)s (%(player)s)"), { 129 gain: gainString, 130 player: (!g_IsNetworked && gain.traderOwner == playerID) ? translate("You") : simState.players[gain.traderOwner].name 131 }); 132 133 if (gain.market1Gain && gain.market1Owner != gain.traderOwner) 134 tooltip += translateWithContext("Separation mark in an enumeration", ", ") + sprintf(translate("%(gain)s (%(player)s)"), { 135 gain: gain.market1Gain, 136 player: (!g_IsNetworked && gain.market1Owner == playerID) ? translate("You") : simState.players[gain.market1Owner].name 137 }); 138 if (gain.market2Gain && gain.market2Owner != gain.traderOwner) 139 tooltip += translateWithContext("Separation mark in an enumeration", ", ") + sprintf(translate("%(gain)s (%(player)s)"), { 140 gain: gain.market2Gain, 141 player: (!g_IsNetworked && gain.market2Owner == playerID) ? translate("You") : simState.players[gain.market2Owner].name 142 }); 143 122 /** 123 * Returns a message with the details of the trade income. 124 */ 125 function getTradeIncomeTooltip(details) 126 { 127 if (details.connections.length === 0) 128 return translate("This market has no trade routes to other markets. Use traders to connect markets together and create wealth."); 129 var avgStrength = 0.0 130 for (let connection in details.connections) 131 avgStrength += Math.min(100, details.connections[connection][1]); 132 avgStrength /= details.connections.length; 133 var tooltip = sprintf(translate("[font=\"sans-bold-13\"]Trade income:[/font] %(rate).1f [font=\"sans-10\"]resources/minute[/font]" 134 + "\n[font=\"sans-bold-13\"]Efficiency:[/font] %(eff)u%%\n[font=\"sans-bold-13\"]" 135 + "Nb of trade routes:[/font] %(connections)s\n[font=\"sans-bold-13\"]" 136 + "Average trade route effiency:[/font] %(strength)s%%"), 137 { "rate" : details.rate*60.0, "eff": details.rawRate*100, "connections": details.connections.length, "strength": avgStrength }); 138 tooltip += translate("[font=\"sans-10\"]\n\nTo increase your income, garrison workers until the efficiency is 100% and trade with more markets, or reinforce your current trade lines' strengths[/font]"); 144 139 return tooltip; 145 140 } 146 141 -
binaries/data/mods/public/gui/session/unit_actions.js
320 320 case "is first": 321 321 tooltip = translate("Origin trade market."); 322 322 if (tradingDetails.hasBothMarkets) 323 tooltip += "\n" + sprintf(translate("Gain: %(gain)s"), { 324 gain: getTradingTooltip(tradingDetails.gain) 325 }); 323 tooltip += "\n" + + getTradeRouteTooltip(tradingDetails.gain); 326 324 else 327 325 tooltip += "\n" + translate("Right-click on another market to set it as a destination trade market."); 328 326 break; 329 327 case "is second": 330 tooltip = translate("Destination trade market.") + "\n" + sprintf(translate("Gain: %(gain)s"), { 331 gain: getTradingTooltip(tradingDetails.gain) 332 }); 328 tooltip = translate("Destination trade market.") + "\n" + getTradeRouteTooltip(tradingDetails.gain); 333 329 break; 334 330 case "set first": 335 331 tooltip = translate("Right-click to set as origin trade market"); 336 332 break; 337 333 case "set second": 338 if (tradingDetails.gain.traderGain == 0) // markets too close 339 return false; 340 tooltip = translate("Right-click to set as destination trade market.") + "\n" + sprintf(translate("Gain: %(gain)s"), { 341 gain: getTradingTooltip(tradingDetails.gain) 342 }); 334 tooltip = translate("Right-click to set as destination trade market.") + "\n" + getTradeRouteTooltip(tradingDetails.gain); 343 335 break; 344 336 } 345 337 return {"possible": true, "tooltip": tooltip}; … … 348 340 "actionCheck": function(target) 349 341 { 350 342 var actionInfo = getActionInfo("setup-trade-route", target); 351 if (!actionInfo.possible) 343 344 if (actionInfo.possible === false && actionInfo.tooltip != undefined) 345 return {"type": "none", "cursor": "cursor-no", "tooltip": actionInfo.tooltip, "target": null}; 346 else if (actionInfo.possible === false) 352 347 return false; 353 348 return {"type": "setup-trade-route", "cursor": "action-setup-trade-route", "tooltip": actionInfo.tooltip, "target": target}; 354 349 }, … … 500 495 cursor = "action-attack-move"; 501 496 } 502 497 503 if (targetState.garrisonHolder && playerCheck(entState, targetState, ["Player", "Ally"])) 498 if (hasClass(entState, "Market") && hasClass(targetState, "Market") && entState.id != targetState.id && 499 (!hasClass(entState, "NavalMarket") || hasClass(targetState, "NavalMarket")) && !playerCheck(entState, targetState, ["Enemy"])) 504 500 { 501 // Find a trader (if any) that this building can produce. 502 var trader; 503 if (entState.production && entState.production.entities.length) 504 for (var i = 0; i < entState.production.entities.length; ++i) 505 if ((trader = GetTemplateData(entState.production.entities[i]).trader)) 506 break; 507 var traderData = { "firstMarket": entState.id, "secondMarket": targetState.id, "tradeOwner": entState.player }; 508 var gain = Engine.GuiInterfaceCall("GetTradingRouteGain", traderData); 509 if (gain) 510 { 511 data.command = "trade"; 512 data.target = traderData.secondMarket; 513 data.source = traderData.firstMarket; 514 cursor = "action-setup-trade-route"; 515 tooltip = translate("Right-click to establish a default route for new traders."); 516 } 517 } else if (targetState.garrisonHolder && playerCheck(entState, targetState, ["Player", "Ally"])) 518 { 505 519 data.command = "garrison"; 506 520 data.target = targetState.id; 507 521 cursor = "action-garrison"; … … 523 537 data.resourceType = resourceType; 524 538 data.resourceTemplate = targetState.template; 525 539 } 526 else if (hasClass(entState, "Market") && hasClass(targetState, "Market") && entState.id != targetState.id &&527 (!hasClass(entState, "NavalMarket") || hasClass(targetState, "NavalMarket")) && !playerCheck(entState, targetState, ["Enemy"]))528 {529 // Find a trader (if any) that this building can produce.530 var trader;531 if (entState.production && entState.production.entities.length)532 for (var i = 0; i < entState.production.entities.length; ++i)533 if ((trader = GetTemplateData(entState.production.entities[i]).trader))534 break;535 536 var traderData = { "firstMarket": entState.id, "secondMarket": targetState.id, "template": trader };537 var gain = Engine.GuiInterfaceCall("GetTradingRouteGain", traderData);538 if (gain && gain.traderGain)539 {540 data.command = "trade";541 data.target = traderData.secondMarket;542 data.source = traderData.firstMarket;543 cursor = "action-setup-trade-route";544 tooltip = translate("Right-click to establish a default route for new traders.");545 if (trader)546 tooltip += "\n" + sprintf(translate("Gain: %(gain)s"), { gain: getTradingTooltip(gain) });547 else // Foundation or cannot produce traders548 tooltip += "\n" + sprintf(translate("Expected gain: %(gain)s"), { gain: getTradingTooltip(gain) });549 }550 }551 540 else if (targetState.foundation && playerCheck(entState, targetState, ["Ally"])) 552 541 { 553 542 data.command = "build"; -
binaries/data/mods/public/gui/session/selection_panels.js
926 926 else 927 927 data.carried[carrying.type] = carrying.amount; 928 928 } 929 930 if (state.trader && state.trader.goods && state.trader.goods.amount)931 {932 if (!data.carried)933 data.carried = {};934 var amount = state.trader.goods.amount;935 var type = state.trader.goods.type;936 var totalGain = amount.traderGain;937 if (amount.market1Gain)938 totalGain += amount.market1Gain;939 if (amount.market2Gain)940 totalGain += amount.market2Gain;941 if (data.carried[type])942 data.carried[type] += totalGain;943 else944 data.carried[type] = totalGain;945 }946 929 } 947 930 return true; 948 931 }, -
binaries/data/mods/public/gui/session/selection_details.js
172 172 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = ""; 173 173 } 174 174 // Use the same indicators for traders 175 else if (entState.trader && entState.trader. goods.amount)175 else if (entState.trader && entState.trader.trading) 176 176 { 177 var nextMarket = GetEntityState(entState.trader.nextMarket); 178 // find the connection if it exists. 179 if (nextMarket && nextMarket.tradeIncome) 180 { 181 for each (let conn in nextMarket.tradeIncome.connections) 182 if (conn[0] === entState.trader.originMarket) 183 { 184 var connection = conn; 185 break; 186 } 187 } 177 188 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; 178 189 Engine.GetGUIObjectByName("resourceCarryingText").hidden = false; 179 Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/resources/"+entState.trader.goods.type+".png"; 180 var totalGain = entState.trader.goods.amount.traderGain; 181 if (entState.trader.goods.amount.market1Gain) 182 totalGain += entState.trader.goods.amount.market1Gain; 183 if (entState.trader.goods.amount.market2Gain) 184 totalGain += entState.trader.goods.amount.market2Gain; 185 Engine.GetGUIObjectByName("resourceCarryingText").caption = totalGain; 186 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = sprintf(translate("Gain: %(gain)s"), { "gain": getTradingTooltip(entState.trader.goods.amount) }); 190 Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/trade.png"; 191 Engine.GetGUIObjectByName("resourceCarryingText").caption = ""; 192 if (connection !== undefined) 193 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = sprintf(translate("This trader is part of a trade route between two markets.\nThis route's efficiency is %(strength)u%%."), { "strength": Math.min(connection[1], 100) }); 194 else 195 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = "This trader is trying to establish a trade route, but the number of traders isn't high enough for this trade route to be viable yet."; 187 196 } 188 197 // And for number of workers 189 198 else if (entState.foundation && entState.visibility == "visible") … … 202 211 else 203 212 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = translate("Number of builders."); 204 213 } 214 // And for the trade income icon 215 else if (entState.tradeIncome) 216 { 217 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; 218 Engine.GetGUIObjectByName("resourceCarryingText").hidden = false; 219 Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/trade.png"; 220 let caption = sprintf(translate("%(eff)u%% x%(nb)u"), 221 { "eff": +entState.tradeIncome.rawRate*100, 222 "nb": entState.tradeIncome.connections.length }); 223 Engine.GetGUIObjectByName("resourceCarryingText").caption = caption; 224 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = getTradeIncomeTooltip(entState.tradeIncome); 225 } 205 226 else if (entState.repairable && entState.repairable.numBuilders > 0 && entState.visibility == "visible") 206 227 { 207 228 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; -
binaries/data/mods/public/simulation/components/UnitAI.js
5194 5194 { 5195 5195 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 5196 5196 cmpTrader.PerformTrade(currentMarket); 5197 if (!cmpTrader.GetGain().traderGain)5198 {5199 this.StopTrading();5200 return;5201 }5197 // if (!cmpTrader.GetGain().traderGain) 5198 // { 5199 // this.StopTrading(); 5200 // return; 5201 // } 5202 5202 5203 5203 if (this.order.data.route && this.order.data.route.length) 5204 5204 { -
binaries/data/mods/public/simulation/components/Trader.js
1 // See helpers/TraderGain.js for the CalculateTaderGain() function which works out how many2 // resources a trader gets3 4 // Additional gain for ships for each garrisoned trader, in percents5 const GARRISONED_TRADER_ADDITION = 20;6 7 1 // Array of resource names 8 2 const RESOURCES = ["food", "wood", "stone", "metal"]; 9 3 … … 12 6 Trader.prototype.Schema = 13 7 "<a:help>Lets the unit generate resouces while moving between markets (or docks in case of water trading).</a:help>" + 14 8 "<a:example>" + 15 "<MaxDistance>2.0</MaxDistance>" + 16 "<GainMultiplier>1.0</GainMultiplier>" + 9 "<ConnectionStrength>5.0</ConnectionStrength>" + 17 10 "</a:example>" + 18 "<element name=' GainMultiplier' a:help='Additional gain multiplier'>" +11 "<element name='ConnectionStrength' a:help='How much more connection strength a trader gives.'>" + 19 12 "<ref name='positiveDecimal'/>" + 20 13 "</element>"; 21 14 … … 23 16 { 24 17 this.firstMarket = INVALID_ENTITY; 25 18 this.secondMarket = INVALID_ENTITY; 26 // Gain from one pass between markets 27 this.gain = null; 28 // Selected resource for trading 29 this.requiredGoods = undefined; 19 30 20 // Currently carried goods 31 this.goods = { "type": null, "amount": null, "origin": null };21 this.goodsOrigin = null; 32 22 }; 33 23 34 Trader.prototype.CalculateGain = function(firstMarket, secondMarket)35 {36 var gain = CalculateTraderGain(firstMarket, secondMarket, this.template, this.entity);37 if (!gain) // One of our markets must have been destroyed38 return null;39 40 // For ship increase gain for each garrisoned trader41 // Calculate this here to save passing unnecessary stuff into the CalculateTraderGain function42 var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);43 if (cmpIdentity && cmpIdentity.HasClass("Ship"))44 {45 var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);46 if (cmpGarrisonHolder)47 {48 var garrisonMultiplier = 1;49 var garrisonedTradersCount = 0;50 for each (var entity in cmpGarrisonHolder.GetEntities())51 {52 var cmpGarrisonedUnitTrader = Engine.QueryInterface(entity, IID_Trader);53 if (cmpGarrisonedUnitTrader)54 garrisonedTradersCount++;55 }56 garrisonMultiplier *= 1 + GARRISONED_TRADER_ADDITION * garrisonedTradersCount / 100;57 58 if (gain.traderGain)59 gain.traderGain = Math.round(garrisonMultiplier * gain.traderGain);60 if (gain.market1Gain)61 gain.market1Gain = Math.round(garrisonMultiplier * gain.market1Gain);62 if (gain.market2Gain)63 gain.market2Gain = Math.round(garrisonMultiplier * gain.market2Gain);64 }65 }66 67 return gain;68 };69 70 Trader.prototype.GetGain = function()71 {72 return this.gain;73 };74 75 24 // Set target as target market. 76 25 // Return true if at least one of markets was changed. 77 26 Trader.prototype.SetTargetMarket = function(target, source) … … 110 59 if (target == this.firstMarket) 111 60 marketsChanged = false; 112 61 else 113 {114 62 this.secondMarket = target; 115 this.gain = this.CalculateGain(this.firstMarket, this.secondMarket);116 }117 63 } 118 64 else 119 65 { … … 124 70 if (marketsChanged) 125 71 { 126 72 // Drop carried goods 127 this.goods .amount= null;73 this.goodsOrigin = null; 128 74 } 129 75 return marketsChanged; 130 76 }; … … 144 90 return this.firstMarket && this.secondMarket; 145 91 }; 146 92 147 Trader.prototype.GetRequiredGoods = function()148 {149 return this.requiredGoods;150 };151 152 Trader.prototype.SetRequiredGoods = function(requiredGoods)153 {154 // Check that argument is a correct resource name155 if (!requiredGoods || RESOURCES.indexOf(requiredGoods) == -1)156 this.requiredGoods = undefined;157 else158 this.requiredGoods = requiredGoods;159 };160 161 93 Trader.prototype.CanTrade = function(target) 162 94 { 163 95 var cmpTraderIdentity = Engine.QueryInterface(this.entity, IID_Identity); … … 186 118 187 119 Trader.prototype.PerformTrade = function(currentMarket) 188 120 { 189 if (this.goods .amount && this.goods.amount.traderGain)121 if (this.goodsOrigin && this.goodsOrigin !== currentMarket) 190 122 { 191 var cmpPlayer = QueryOwnerInterface(this.entity); 192 if (cmpPlayer) 193 cmpPlayer.AddResource(this.goods.type, this.goods.amount.traderGain); 194 195 var cmpStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker); 196 if (cmpStatisticsTracker) 197 cmpStatisticsTracker.IncreaseTradeIncomeCounter(this.goods.amount.traderGain); 198 199 if (this.goods.amount.market1Gain) 200 { 201 var cmpPlayer = QueryOwnerInterface(this.firstMarket); 202 if (cmpPlayer) 203 cmpPlayer.AddResource(this.goods.type, this.goods.amount.market1Gain); 204 205 var cmpStatisticsTracker = QueryOwnerInterface(this.firstMarket, IID_StatisticsTracker); 206 if (cmpStatisticsTracker) 207 cmpStatisticsTracker.IncreaseTradeIncomeCounter(this.goods.amount.market1Gain); 208 } 209 210 if (this.goods.amount.market2Gain) 211 { 212 var cmpPlayer = QueryOwnerInterface(this.secondMarket); 213 if (cmpPlayer) 214 cmpPlayer.AddResource(this.goods.type, this.goods.amount.market2Gain); 215 216 var cmpStatisticsTracker = QueryOwnerInterface(this.secondMarket, IID_StatisticsTracker); 217 if (cmpStatisticsTracker) 218 cmpStatisticsTracker.IncreaseTradeIncomeCounter(this.goods.amount.market2Gain); 219 } 123 var cmpTradeIncome = Engine.QueryInterface(currentMarket, IID_TradeIncome); 124 if (cmpTradeIncome) 125 cmpTradeIncome.AddConnectionWithMarket(this.goodsOrigin, this.template.ConnectionStrength); 220 126 } 221 222 // First take the preferred goods of the trader if any, 223 // otherwise choose one according to the player's trading priorities 224 // if still nothing (but should never happen), choose metal 225 // and recomputes the gain in case it has changed (for example by technology) 226 var nextGoods = this.GetRequiredGoods(); 227 if (!nextGoods || RESOURCES.indexOf(nextGoods) == -1) 228 { 229 var cmpPlayer = QueryOwnerInterface(this.entity); 230 if (cmpPlayer) 231 nextGoods = cmpPlayer.GetNextTradingGoods(); 232 233 if (!nextGoods || RESOURCES.indexOf(nextGoods) == -1) 234 nextGoods = "metal"; 235 } 236 this.goods.type = nextGoods; 237 this.goods.amount = this.CalculateGain(this.firstMarket, this.secondMarket); 238 this.goods.origin = currentMarket; 127 this.goodsOrigin = +currentMarket; 239 128 }; 240 129 241 Trader.prototype.GetGoods = function()242 {243 return this.goods;244 };245 246 130 Trader.prototype.GetNextMarket = function() 247 131 { 248 if (this.goods .amount && this.goods.origin == this.firstMarket)132 if (this.goodsOrigin == this.firstMarket) 249 133 return this.secondMarket; 250 134 251 if (this.goods .amount && this.goods.origin != this.secondMarket)252 this.goods .amount= null; // leftover from previous trading135 if (this.goodsOrigin != this.secondMarket) 136 this.goodsOrigin = null; // leftover from previous trading 253 137 return this.firstMarket; 254 138 }; 255 139 140 Trader.prototype.GetOriginMarket = function() 141 { 142 return (this.GetNextMarket() == this.firstMarket) ? this.secondMarket : this.firstMarket; 143 }; 144 256 145 Trader.prototype.StopTrading = function() 257 146 { 258 147 // Drop carried goods 259 this.goods .amount= null;148 this.goodsOrigin = null; 260 149 // Reset markets 261 150 this.firstMarket = INVALID_ENTITY; 262 151 this.secondMarket = INVALID_ENTITY; … … 274 163 return { "min": 0, "max": max}; 275 164 }; 276 165 277 Trader.prototype.OnGarrisonedUnitsChanged = function()278 {279 if (this.HasBothMarkets())280 this.gain = this.CalculateGain(this.firstMarket, this.secondMarket);281 };282 283 166 Engine.RegisterComponentType(IID_Trader, "Trader", Trader); -
binaries/data/mods/public/simulation/components/TradeIncome.js
1 // Array of resource names 2 const RESOURCES = ["food", "wood", "stone", "metal"]; 3 4 function TradeIncome() {} 5 6 TradeIncome.prototype.Schema = 7 "<a:help>Lets the unit generate resources that traders could pick up and trade with.</a:help>" + 8 "<a:example>" + 9 "<Efficiency>2.0</Efficiency>" + 10 "</a:example>" + 11 "<element name='Efficiency' a:help='Gives the basic trade income of the building at full efficiency.'>" + 12 "<ref name='positiveDecimal'/>" + 13 "</element>" + 14 "<optional>" + 15 "<element name='GarrisonEfficiency' a:help='Improve the efficiency of trading by garrisoning units inside'>" + 16 "<optional>" + 17 "<element name='Classes' a:help='only units having one of the following classes will be considered. Defaults to all.'>" + 18 "<text/>" + 19 "</element>" + 20 "</optional>" + 21 "<element name='EmptyEfficiency' a:help='Basic efficiency when empty. Expressed as a % of total efficiency.'>" + 22 "<ref name='nonNegativeDecimal'/>" + 23 "</element>" + 24 "<optional>" + 25 "<element name='MaxGarrison' a:help='Number of unit required for efficiency to be 100%. Defaults to \"garrison capacity\"'>" + 26 "<data type='nonNegativeInteger'/>" + 27 "</element>" + 28 "</optional>" + 29 "</element>" + 30 "</optional>"; 31 32 TradeIncome.prototype.Init = function() 33 { 34 this.connections = new Map(); 35 36 // Call the timer 37 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 38 cmpTimer.SetInterval(this.entity, IID_TradeIncome, "GenerateResources", 5000, 5000, undefined); 39 40 // quick sanity check 41 var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); 42 if (this.template.GarrisonEfficiency && !cmpGarrisonHolder) 43 warn("Entity " + this.entity + " has a TradeIncome component that makes use of garrisoned entity but does not support garrisoning."); 44 }; 45 46 // When a trader arrives, it either establishes or reinforces a connection. 47 TradeIncome.prototype.AddConnectionWithMarket = function(market, amount) 48 { 49 if (!this.connections.get(market)) 50 this.connections.set(market, { "strength" : 0, "distance" : 0, "international" : false }); 51 52 this.connections.get(market)["strength"] += +amount; 53 // set up a maximal amount so that you can't just get enough connection strength then delete your traders. 54 if (this.connections.get(market)["strength"] > 1000) 55 this.connections.get(market)["strength"] = 1000; 56 57 // reset distance. Ought not change. 58 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 59 var cmpConnectionPosition = Engine.QueryInterface(market, IID_Position); 60 if (!cmpPosition || !cmpPosition.IsInWorld() || !cmpConnectionPosition || !cmpConnectionPosition.IsInWorld()) 61 return; 62 63 var Position = cmpPosition.GetPosition2D(); 64 var ConnectionPosition = cmpConnectionPosition.GetPosition2D(); 65 var distance = Math.sqrt(Math.pow(Position.x - ConnectionPosition.x, 2) + Math.pow(Position.y - ConnectionPosition.y, 2)); 66 this.connections.get(market)["distance"] = distance; 67 68 // reset internationality, in case the owner changes: from our POV until a trader brings news, we are selling items from a faraway land. 69 var owner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner(); 70 var ownerConnection = Engine.QueryInterface(market, IID_Ownership).GetOwner(); 71 72 this.connections.get(market)["international"] = (owner != ownerConnection); 73 } 74 75 TradeIncome.prototype.GetConnections = function() 76 { 77 var ret = []; 78 for (let conn of this.connections.keys()) 79 ret.push([conn, this.connections.get(conn).strength, this.connections.get(conn).distance, this.connections.get(conn).international]); 80 return ret; 81 } 82 83 TradeIncome.prototype.GetTradeIncomeRate = function() 84 { 85 if (this.connections.size === 0) 86 return 0; 87 88 // Calculate our connection bonuses. 89 var connectionEffect = 0; 90 91 var owner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner(); 92 93 for (let i of this.connections.keys()) 94 { 95 let bonus = CalculateMarketConnectionBonusDirect(this.connections.get(i)["distance"], owner, this.connections.get(i)["international"]); 96 connectionEffect += bonus * Math.min(1.0, this.connections.get(i)["strength"]/100.0); 97 } 98 connectionEffect *= +ApplyValueModificationsToEntity("TradeIncome/ConnectionMulitplier", +this.template.ConnectionMultiplier, this.entity); 99 connectionEffect = Math.sqrt(connectionEffect); 100 101 connectionEffect *= this.GetRawEfficiency(); 102 103 return connectionEffect; 104 } 105 106 TradeIncome.prototype.GetRawEfficiency = function() 107 { 108 if (!this.template.GarrisonEfficiency) 109 return 1.0; 110 111 var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); 112 if (!cmpGarrisonHolder) 113 return 1.0; // probably should not happen 114 115 116 // Depending on how many units are garrisoned, modulate efficiency 117 var capacity = (this.template.GarrisonEfficiency.MaxGarrison === undefined) ? cmpGarrisonHolder.GetCapacity() : +this.template.GarrisonEfficiency.MaxGarrison; 118 var currentCount = (this.template.GarrisonEfficiency.Classes === undefined) ? cmpGarrisonHolder.GetEntities().length 119 : cmpGarrisonHolder.GetGarrisonedArcherCount(this.template.GarrisonEfficiency.Classes); 120 return 0.2 + (currentCount / capacity) * 0.8; 121 } 122 123 TradeIncome.prototype.GenerateResources = function() 124 { 125 if (this.connections.size === 0) 126 return; 127 128 var cmpPlayer = QueryOwnerInterface(this.entity); 129 if (!cmpPlayer) 130 return; 131 132 var cmpStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker); 133 134 var rate = this.GetTradeIncomeRate() * 5.0; 135 var goods = cmpPlayer.GetTradingGoods(); 136 137 for (let good in goods) 138 { 139 if (RESOURCES.indexOf(good) === -1) 140 continue; 141 142 cmpPlayer.AddResource(good, rate * goods[good]/100.0); // should add up to 100% 143 144 if (cmpStatisticsTracker) 145 cmpStatisticsTracker.IncreaseTradeIncomeCounter(rate * goods[good]); 146 } 147 148 for (let i of this.connections.keys()) 149 { 150 this.connections.get(i)["strength"] -= 10; 151 if (this.connections.get(i)["strength"] <= 0) 152 this.connections.delete(i); 153 } 154 }; 155 156 Engine.RegisterComponentType(IID_TradeIncome, "TradeIncome", TradeIncome); -
binaries/data/mods/public/simulation/components/Looter.js
Property changes on: binaries/data/mods/public/simulation/components/TradeIncome.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
36 36 for (let resource of cmpResourceGatherer.GetCarryingStatus()) 37 37 resources[resource.type] += resource.amount; 38 38 39 // Loot resources traders carry40 var cmpTrader = Engine.QueryInterface(targetEntity, IID_Trader);41 if (cmpTrader)42 {43 let carriedGoods = cmpTrader.GetGoods();44 if (carriedGoods.amount)45 {46 resources[carriedGoods.type] +=47 + (carriedGoods.amount.traderGain || 0)48 + (carriedGoods.amount.market1Gain || 0)49 + (carriedGoods.amount.market2Gain || 0);50 }51 }52 53 39 // Transfer resources 54 40 var cmpPlayer = QueryOwnerInterface(this.entity); 55 41 cmpPlayer.AddResources(resources); -
binaries/data/mods/public/simulation/components/GuiInterface.js
239 239 "resourceCarrying": null, 240 240 "rotation": null, 241 241 "trader": null, 242 "tradeIncome": null, 242 243 "unitAI": null, 243 244 "visibility": null, 244 245 }; … … 309 310 if (cmpTrader) 310 311 { 311 312 ret.trader = { 312 "goods": cmpTrader.GetGoods(), 313 "requiredGoods": cmpTrader.GetRequiredGoods() 313 "trading": cmpTrader.HasBothMarkets(), 314 "nextMarket": cmpTrader.GetNextMarket(), 315 "originMarket": cmpTrader.GetOriginMarket() 314 316 }; 315 317 } 316 318 319 var cmpTradeIncome = Engine.QueryInterface(ent, IID_TradeIncome); 320 if (cmpTradeIncome) 321 { 322 ret.tradeIncome = { 323 "connections": cmpTradeIncome.GetConnections(), 324 "rate": cmpTradeIncome.GetTradeIncomeRate(), 325 "rawRate": cmpTradeIncome.GetRawEfficiency() 326 }; 327 } 328 317 329 var cmpFogging = Engine.QueryInterface(ent, IID_Fogging); 318 330 if (cmpFogging) 319 331 { … … 1679 1691 if (!data.firstMarket || !data.secondMarket) 1680 1692 return null; 1681 1693 1682 return Calculate TraderGain(data.firstMarket, data.secondMarket, data.template);1694 return CalculateMarketConnectionBonus(data.firstMarket, data.secondMarket, data.tradeOwner); 1683 1695 }; 1684 1696 1685 1697 GuiInterface.prototype.GetTradingDetails = function(player, data) … … 1697 1709 "hasBothMarkets": cmpEntityTrader.HasBothMarkets() 1698 1710 }; 1699 1711 if (cmpEntityTrader.HasBothMarkets()) 1700 result.gain = cmpEntityTrader.GetGain();1712 result.gain = CalculateMarketConnectionBonus(firstMarket, secondMarket, player) 1701 1713 } 1702 1714 else if (data.target === secondMarket) 1703 1715 { 1704 1716 result = { 1705 1717 "type": "is second", 1706 "gain": cmpEntityTrader.GetGain(),1718 "gain": CalculateMarketConnectionBonus(firstMarket, data.target, player) 1707 1719 }; 1708 1720 } 1709 1721 else if (!firstMarket) … … 1714 1726 { 1715 1727 result = { 1716 1728 "type": "set second", 1717 "gain": cmpEntityTrader.CalculateGain(firstMarket, data.target),1729 "gain": CalculateMarketConnectionBonus(firstMarket, data.target, player) 1718 1730 }; 1719 1731 } 1720 1732 else -
binaries/data/mods/public/simulation/components/interfaces/TradeIncome.js
1 Engine.RegisterInterface("TradeIncome"); -
binaries/data/mods/public/simulation/helpers/TraderGain.js
Property changes on: binaries/data/mods/public/simulation/components/interfaces/TradeIncome.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
1 1 // This constant used to adjust gain value depending on distance 2 const DISTANCE_FACTOR = 1 / 1 15;2 const DISTANCE_FACTOR = 1 / 100.0; 3 3 4 // Additional gain (applying to each market) for trading performed between markets of different players, in percents 5 const INTERNATIONAL_TRADING_ADDITION = 25; 4 // When performing trade between markets owned by two different players, multiply trader gain by this value. 5 // When trading between your markets, you gain the resources at both markets. 6 // So this must be at least above 2 to make it interesting to trade with your ally. 7 const INTERNATIONAL_TRADING_MULTIPLICATION = 1.4; 6 8 7 // If trader undefined, the trader owner is supposed to be the same as the first market 8 function CalculateTraderGain(firstMarket, secondMarket, template, trader) 9 function CalculateMarketConnectionBonus(firstMarket, secondMarket, tradeOwner) 9 10 { 10 var gain = {};11 var bonus = 1; 11 12 12 13 var cmpFirstMarketPosition = Engine.QueryInterface(firstMarket, IID_Position); 13 14 var cmpSecondMarketPosition = Engine.QueryInterface(secondMarket, IID_Position); … … 19 20 // Calculate ordinary Euclidean distance between markets. 20 21 // We don't use pathfinder, because ordinary distance looks more fair. 21 22 var distance = Math.sqrt(Math.pow(firstMarketPosition.x - secondMarketPosition.x, 2) + Math.pow(firstMarketPosition.y - secondMarketPosition.y, 2)); 22 // We calculate gain as square of distance to encourage trading between remote markets23 gain.traderGain = Math.pow(distance * DISTANCE_FACTOR, 2);24 if (template && template.GainMultiplier)25 {26 if (trader)27 gain.traderGain *= ApplyValueModificationsToEntity("Trader/GainMultiplier", +template.GainMultiplier, trader);28 else // called from the gui with modifications already applied29 gain.traderGain *= template.GainMultiplier;30 }31 gain.traderGain = Math.round(gain.traderGain);32 // If trader undefined, the trader owner is supposed to be the same as the first market33 if (trader)34 var cmpOwnership = Engine.QueryInterface(trader, IID_Ownership);35 else36 var cmpOwnership = Engine.QueryInterface(firstMarket, IID_Ownership);37 gain.traderOwner = cmpOwnership.GetOwner();38 23 39 24 // If markets belong to different players, add gain from international trading 40 25 var ownerFirstMarket = Engine.QueryInterface(firstMarket, IID_Ownership).GetOwner(); 41 26 var ownerSecondMarket = Engine.QueryInterface(secondMarket, IID_Ownership).GetOwner(); 42 27 if (ownerFirstMarket != ownerSecondMarket) 43 { 44 var internationalGain1 = ApplyValueModificationsToEntity("Trade/International", INTERNATIONAL_TRADING_ADDITION, firstMarket); 45 gain.market1Gain = Math.round(gain.traderGain * internationalGain1 / 100); 46 gain.market1Owner = ownerFirstMarket; 47 var internationalGain2 = ApplyValueModificationsToEntity("Trade/International", INTERNATIONAL_TRADING_ADDITION, secondMarket); 48 gain.market2Gain = Math.round(gain.traderGain * internationalGain2 / 100); 49 gain.market2Owner = ownerSecondMarket; 28 return CalculateMarketConnectionBonusDirect(distance, tradeOwner, true); 29 30 return CalculateMarketConnectionBonusDirect(distance, tradeOwner); 31 } 50 32 51 } 52 53 return gain; 33 function CalculateMarketConnectionBonusDirect(distance, tradeOwner, international) 34 { 35 var bonus = Math.log(distance*DISTANCE_FACTOR); 36 if (international) 37 bonus *= +ApplyValueModificationsToEntity("Trade/International", INTERNATIONAL_TRADING_MULTIPLICATION, tradeOwner); 38 return Math.max(0, bonus); 54 39 } 55 40 56 Engine.RegisterGlobal("CalculateTraderGain", CalculateTraderGain); 41 Engine.RegisterGlobal("CalculateMarketConnectionBonusDirect", CalculateMarketConnectionBonusDirect); 42 Engine.RegisterGlobal("CalculateMarketConnectionBonus", CalculateMarketConnectionBonus); -
binaries/data/mods/public/simulation/templates/template_unit_support_trader.xml
39 39 </SoundGroups> 40 40 </Sound> 41 41 <Trader> 42 < GainMultiplier>1.0</GainMultiplier>42 <ConnectionStrength>50</ConnectionStrength> 43 43 </Trader> 44 44 <UnitAI> 45 45 <CanGuard>false</CanGuard> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_merchant.xml
45 45 <HeightOffset>6.0</HeightOffset> 46 46 </StatusBars> 47 47 <Trader> 48 < GainMultiplier>1.0</GainMultiplier>48 <ConnectionStrength>100</ConnectionStrength> 49 49 </Trader> 50 50 <UnitAI> 51 51 <DefaultStance>passive</DefaultStance> -
binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml
71 71 armor_ship_hullsheathing 72 72 </Technologies> 73 73 </ProductionQueue> 74 <TradeIncome> 75 <Efficiency>0.5</Efficiency> 76 </TradeIncome> 74 77 <Vision> 75 78 <Range>40</Range> 76 79 </Vision> -
binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml
2 2 <Entity parent="template_structure_economic"> 3 3 <BuildRestrictions> 4 4 <Category>Market</Category> 5 <Distance> 6 <FromClass>CivilCentre</FromClass> 7 <MaxDistance>140</MaxDistance> 8 </Distance> 5 9 </BuildRestrictions> 6 10 <Cost> 7 11 <BuildTime>150</BuildTime> … … 13 17 <Square width="33.0" depth="29.0"/> 14 18 <Height>8.0</Height> 15 19 </Footprint> 20 <GarrisonHolder> 21 <Max>10</Max> 22 <EjectHealth>0.2</EjectHealth> 23 <EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy> 24 <List datatype="tokens">Worker</List> 25 <BuffHeal>0</BuffHeal> 26 <LoadingRange>2</LoadingRange> 27 </GarrisonHolder> 16 28 <Health> 17 29 <Max>1500</Max> 18 30 <SpawnEntityOnDeath>rubble/rubble_stone_5x5</SpawnEntityOnDeath> … … 47 59 <Radius>40</Radius> 48 60 <Weight>30000</Weight> 49 61 </TerritoryInfluence> 62 <TradeIncome> 63 <Efficiency>1.0</Efficiency> 64 <GarrisonEfficiency> 65 <EmptyEfficiency>0.2</EmptyEfficiency> 66 </GarrisonEfficiency> 67 </TradeIncome> 50 68 <ProductionQueue> 51 69 <BatchTimeModifier>0.7</BatchTimeModifier> 52 70 <Technologies datatype="tokens"> -
binaries/data/mods/public/simulation/templates/template_formation.xml
40 40 <WalkSpeed>1.0</WalkSpeed> 41 41 <PassabilityClass>large</PassabilityClass> 42 42 </UnitMotion> 43 <Trader>44 <GainMultiplier>1.0</GainMultiplier>45 </Trader>46 43 </Entity> -
binaries/data/mods/public/simulation/templates/special/player.xml
30 30 <Library>1</Library> 31 31 <Lighthouse>1</Lighthouse> 32 32 <Juggernaut>1</Juggernaut> 33 <Market>0</Market> 33 34 </Limits> 34 35 <LimitChangers> 36 <Market> 37 <CivilCentre>1</CivilCentre> 38 </Market> 35 39 <WarDog> 36 40 <Kennel>10</Kennel> 37 41 </WarDog> -
binaries/data/mods/public/simulation/templates/structures/pers_market.xml
15 15 <Obstruction> 16 16 <Static width="24.0" depth="24.0"/> 17 17 </Obstruction> 18 <TradeIncome> 19 <Efficiency>1.25</Efficiency> 20 </TradeIncome> 18 21 <VisualActor> 19 22 <Actor>structures/persians/market.xml</Actor> 20 23 <FoundationActor>structures/fndn_5x5.xml</FoundationActor> -
binaries/data/mods/public/simulation/templates/structures/cart_dock.xml
27 27 -training_naval_architects 28 28 </Technologies> 29 29 </ProductionQueue> 30 <TradeIncome> 31 <Efficiency>0.8</Efficiency> 32 </TradeIncome> 30 33 <VisualActor> 31 34 <Actor>structures/carthaginians/dock.xml</Actor> 32 35 <FoundationActor>structures/fndn_6x4_dock.xml</FoundationActor> -
binaries/data/mods/public/simulation/templates/units/pers_support_trader.xml
20 20 <death>actor/fauna/death/death_camel.xml</death> 21 21 </SoundGroups> 22 22 </Sound> 23 <Trader>24 <GainMultiplier>1.25</GainMultiplier>25 </Trader>26 23 <VisualActor> 27 24 <Actor>units/persians/trader.xml</Actor> 28 25 </VisualActor> -
binaries/data/mods/public/simulation/templates/units/cart_ship_merchant.xml
12 12 <Icon>units/cart_ship_merchant.png</Icon> 13 13 <RequiredTechnology>phase_village</RequiredTechnology> 14 14 </Identity> 15 <Trader>16 <GainMultiplier>1.25</GainMultiplier>17 </Trader>18 15 <VisualActor> 19 16 <Actor>structures/carthaginians/merchant_ship.xml</Actor> 20 17 </VisualActor>