Ticket #3697: trade-v2.2.patch
File trade-v2.2.patch, 44.5 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/globalscripts/Templates.js
245 245 if (template.UnitMotion.Run) 246 246 ret.speed.run = func("UnitMotion/Run/Speed", +template.UnitMotion.Run.Speed, player, template); 247 247 } 248 249 if (template.Trader)250 {251 ret.trader = {252 "GainMultiplier": func("Trader/GainMultiplier", +template.Trader.GainMultiplier, player, template)253 };254 }255 248 256 249 if (template.WallSet) 257 250 { -
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 return {"possible": true, "tooltip": tooltip}; 346 337 if (tradingDetails.gain !== undefined && tradingDetails.gain == 0) 338 return { "possible": false, "tooltip": translate("This market is too close to trade with.") }; 339 else 340 return { "possible": true, "tooltip": tooltip }; 347 341 }, 348 342 "actionCheck": function(target) 349 343 { 350 344 var actionInfo = getActionInfo("setup-trade-route", target); 351 if (!actionInfo.possible) 345 346 if (!actionInfo.possible && actionInfo.tooltip != undefined) 347 return { "type": "none", "cursor": "cursor-no", "tooltip": actionInfo.tooltip, "target": null }; 348 else if (!actionInfo.possible) 352 349 return false; 353 return { "type": "setup-trade-route", "cursor": "action-setup-trade-route", "tooltip": actionInfo.tooltip, "target": target};350 return { "type": "setup-trade-route", "cursor": "action-setup-trade-route", "tooltip": actionInfo.tooltip, "target": target }; 354 351 }, 355 352 "specificness": 0, 356 353 }, … … 500 497 cursor = "action-attack-move"; 501 498 } 502 499 503 if (targetState.garrisonHolder && playerCheck(entState, targetState, ["Player", "Ally"])) 500 if (hasClass(entState, "Market") && hasClass(targetState, "Market") && entState.id != targetState.id && 501 (!hasClass(entState, "NavalMarket") || hasClass(targetState, "NavalMarket")) && !playerCheck(entState, targetState, ["Enemy"])) 504 502 { 503 // Find a trader (if any) that this building can produce. 504 let trader; 505 if (entState.production && entState.production.entities.length) 506 for (let i = 0; i < entState.production.entities.length; ++i) 507 if ((trader = GetTemplateData(entState.production.entities[i]).trader)) 508 break; 509 let traderData = { "firstMarket": entState.id, "secondMarket": targetState.id, "tradeOwner": entState.player }; 510 let gain = Engine.GuiInterfaceCall("GetTradingRouteGain", traderData); 511 if (gain) 512 { 513 data.command = "trade"; 514 data.target = traderData.secondMarket; 515 data.source = traderData.firstMarket; 516 cursor = "action-setup-trade-route"; 517 tooltip = translate("Right-click to establish a default route for new traders."); 518 } 519 } 520 else if (targetState.garrisonHolder && playerCheck(entState, targetState, ["Player", "Ally"])) 521 { 505 522 data.command = "garrison"; 506 523 data.target = targetState.id; 507 524 cursor = "action-garrison"; … … 523 540 data.resourceType = resourceType; 524 541 data.resourceTemplate = targetState.template; 525 542 } 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 543 else if (targetState.foundation && playerCheck(entState, targetState, ["Ally"])) 552 544 { 553 545 data.command = "build"; -
binaries/data/mods/public/gui/session/utility_functions.js
82 82 /** 83 83 * Returns a message with the details of the trade gain. 84 84 */ 85 function getTrad ingTooltip(gain)85 function getTradeRouteTooltip(gain) 86 86 { 87 return translate("This trader is currently trading between two markets, creating wealth for his empire.") + "\n" + 88 sprintf(translate("The basic value of this trade route is %(value).1f."), { value:gain }); 89 } 87 90 88 var playerID = Engine.GetPlayerID(); 89 var simState = GetSimState(); 90 91 var gainString = gain.traderGain; 92 if (gain.market1Gain && gain.market1Owner == gain.traderOwner) 93 gainString += translate("+") + gain.market1Gain; 94 if (gain.market2Gain && gain.market2Owner == gain.traderOwner) 95 gainString += translate("+") + gain.market2Gain; 96 97 var tooltip = sprintf(translate("%(gain)s (%(player)s)"), { 98 gain: gainString, 99 player: (!g_IsNetworked && gain.traderOwner == playerID) ? translate("You") : simState.players[gain.traderOwner].name 100 }); 101 102 if (gain.market1Gain && gain.market1Owner != gain.traderOwner) 103 tooltip += translateWithContext("Separation mark in an enumeration", ", ") + sprintf(translate("%(gain)s (%(player)s)"), { 104 gain: gain.market1Gain, 105 player: (!g_IsNetworked && gain.market1Owner == playerID) ? translate("You") : simState.players[gain.market1Owner].name 106 }); 107 if (gain.market2Gain && gain.market2Owner != gain.traderOwner) 108 tooltip += translateWithContext("Separation mark in an enumeration", ", ") + sprintf(translate("%(gain)s (%(player)s)"), { 109 gain: gain.market2Gain, 110 player: (!g_IsNetworked && gain.market2Owner == playerID) ? translate("You") : simState.players[gain.market2Owner].name 111 }); 112 91 /** 92 * Returns a message with the details of the trade income. 93 */ 94 function getTradeIncomeTooltip(details) 95 { 96 if (details.connections.length === 0) 97 return translate("This market has no trade routes to other markets. Use traders to connect markets together and create wealth."); 98 let avgStrength = 0.0; 99 for (let connection in details.connections) 100 avgStrength += Math.min(100, details.connections[connection][1]); 101 avgStrength /= details.connections.length; 102 let tooltip = sprintf("[font=\"sans-bold-13\"]" + translate("Trade income") + ":[/font] " + translate("%(tradeRate).1f") + " [font=\"sans-10\"]" + translate("resources/minute") + "[/font]" + 103 "\n[font=\"sans-bold-13\"]" + translate("Efficiency") + ":[/font] " + translate("%(tradeEfficiency)u%%") + 104 "\n[font=\"sans-bold-13\"]" + translate("Number of trade routes") + ":[/font] " + translate("%(MarketConnection)s") + 105 "\n[font=\"sans-bold-13\"]" + translate("Average trade route effiency") + ":[/font] " + translate("%(ConnectionStrenght)s%%"), 106 { "tradeRate" : details.rate*60.0, "tradeEfficiency": details.rawRate*100, "MarketConnection": details.connections.length, "ConnectionStrenght": avgStrength }); 107 tooltip += "[font=\"sans-10\"]\n\n" + translate("To increase your income, garrison workers until the efficiency is 100% and trade with more markets, or reinforce your current trade lines' strengths.") + "[/font]"; 113 108 return tooltip; 114 109 } 115 110 -
binaries/data/mods/public/gui/session/selection_panels.js
947 947 else 948 948 data.carried[carrying.type] = carrying.amount; 949 949 } 950 951 if (state.trader && state.trader.goods && state.trader.goods.amount)952 {953 if (!data.carried)954 data.carried = {};955 var amount = state.trader.goods.amount;956 var type = state.trader.goods.type;957 var totalGain = amount.traderGain;958 if (amount.market1Gain)959 totalGain += amount.market1Gain;960 if (amount.market2Gain)961 totalGain += amount.market2Gain;962 if (data.carried[type])963 data.carried[type] += totalGain;964 else965 data.carried[type] = totalGain;966 }967 950 } 968 951 return true; 969 952 }, -
binaries/data/mods/public/gui/session/selection_details.js
175 175 Engine.GetGUIObjectByName("resourceCarryingText").caption = sprintf(translate("%(amount)s / %(max)s"), { "amount": carried.amount, "max": carried.max }); 176 176 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = ""; 177 177 } 178 // Use the same indicators for traders 179 else if (entState.trader && entState.trader.goods.amount) 180 { 181 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; 182 Engine.GetGUIObjectByName("resourceCarryingText").hidden = false; 183 Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/resources/"+entState.trader.goods.type+".png"; 184 let totalGain = entState.trader.goods.amount.traderGain; 185 if (entState.trader.goods.amount.market1Gain) 186 totalGain += entState.trader.goods.amount.market1Gain; 187 if (entState.trader.goods.amount.market2Gain) 188 totalGain += entState.trader.goods.amount.market2Gain; 189 Engine.GetGUIObjectByName("resourceCarryingText").caption = totalGain; 190 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = sprintf(translate("Gain: %(gain)s"), { "gain": getTradingTooltip(entState.trader.goods.amount) }); 191 } 178 // Use the same indicators for traders 179 else if (entState.trader && entState.trader.trading) 180 { 181 var nextMarket = GetEntityState(entState.trader.nextMarket); 182 // find the connection if it exists. 183 let connection = null; 184 if (nextMarket && nextMarket.tradeIncome) 185 connection = nextMarket.tradeIncome.connections.find(conn => conn[0] === entState.trader.originMarket); 186 187 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; 188 Engine.GetGUIObjectByName("resourceCarryingText").hidden = false; 189 Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/trade.png"; 190 Engine.GetGUIObjectByName("resourceCarryingText").caption = ""; 191 if (connection) 192 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) }); 193 else 194 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = translate("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."); 195 } 192 196 // And for number of workers 193 197 else if (entState.foundation && entState.visibility == "visible") 194 198 { … … 206 210 else 207 211 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = translate("Number of builders."); 208 212 } 213 // And for the trade income icon 214 else if (entState.tradeIncome) 215 { 216 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; 217 Engine.GetGUIObjectByName("resourceCarryingText").hidden = false; 218 Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/trade.png"; 219 Engine.GetGUIObjectByName("resourceCarryingText").caption = sprintf(translate("%(eff)u%% x%(nb)u"), 220 { "eff": +entState.tradeIncome.rawRate*100, 221 "nb": entState.tradeIncome.connections.length }); 222 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = getTradeIncomeTooltip(entState.tradeIncome); 223 } 209 224 else if (entState.repairable && entState.repairable.numBuilders > 0 && entState.visibility == "visible") 210 225 { 211 226 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; -
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) 66 + Math.pow(Position.y - ConnectionPosition.y, 2)); 67 this.connections.get(market)["distance"] = distance; 68 69 // reset internationality, in case the owner changes: from our POV until a trader brings news, we are selling items from a faraway land. 70 var owner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner(); 71 var ownerConnection = Engine.QueryInterface(market, IID_Ownership).GetOwner(); 72 73 this.connections.get(market)["international"] = (owner != ownerConnection); 74 } 75 76 TradeIncome.prototype.GetConnections = function() 77 { 78 var ret = []; 79 for (let conn of this.connections.keys()) 80 ret.push([conn, this.connections.get(conn).strength, this.connections.get(conn).distance, this.connections.get(conn).international]); 81 return ret; 82 } 83 84 TradeIncome.prototype.GetTradeIncomeRate = function() 85 { 86 if (this.connections.size === 0) 87 return 0; 88 89 // Calculate our connection bonuses. 90 var connectionEffect = 0; 91 92 var owner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner(); 93 94 for (let i of this.connections.keys()) 95 { 96 let bonus = CalculateMarketConnectionBonusDirect(this.connections.get(i)["distance"], owner, this.connections.get(i)["international"]); 97 connectionEffect += bonus * Math.min(1.0, this.connections.get(i)["strength"]/100.0); 98 } 99 return Math.sqrt(connectionEffect) * this.GetRawEfficiency(); 100 } 101 102 TradeIncome.prototype.GetRawEfficiency = function() 103 { 104 if (!this.template.GarrisonEfficiency) 105 return 1.0; 106 107 var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); 108 if (!cmpGarrisonHolder) 109 return 1.0; // probably should not happen 110 111 112 // Depending on how many units are garrisoned, modulate efficiency 113 var capacity = (this.template.GarrisonEfficiency.MaxGarrison === undefined) ? cmpGarrisonHolder.GetCapacity() : +this.template.GarrisonEfficiency.MaxGarrison; 114 var currentCount = (this.template.GarrisonEfficiency.Classes === undefined) ? 115 cmpGarrisonHolder.GetEntities().length : cmpGarrisonHolder.GetGarrisonedArcherCount(this.template.GarrisonEfficiency.Classes); 116 return 0.2 + (currentCount / capacity) * 0.8; 117 } 118 119 TradeIncome.prototype.GenerateResources = function() 120 { 121 if (this.connections.size === 0) 122 return; 123 124 var cmpPlayer = QueryOwnerInterface(this.entity); 125 if (!cmpPlayer) 126 return; 127 128 var cmpStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker); 129 130 var rate = this.GetTradeIncomeRate() * 5.0; 131 var goods = cmpPlayer.GetTradingGoods(); 132 133 for (let good in goods) 134 { 135 if (RESOURCES.indexOf(good) === -1) 136 continue; 137 138 cmpPlayer.AddResource(good, rate * goods[good]/100.0); // should add up to 100% 139 140 if (cmpStatisticsTracker) 141 cmpStatisticsTracker.IncreaseTradeIncomeCounter(rate * goods[good]); 142 } 143 144 for (let i of this.connections.keys()) 145 { 146 this.connections.get(i)["strength"] -= 10; 147 if (this.connections.get(i)["strength"] <= 0) 148 this.connections.delete(i); 149 } 150 }; 151 152 Engine.RegisterComponentType(IID_TradeIncome, "TradeIncome", TradeIncome); -
binaries/data/mods/public/simulation/components/UnitAI.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
5221 5221 5222 5222 if (this.CheckTargetRange(currentMarket, IID_Trader)) 5223 5223 { 5224 varcmpTrader = Engine.QueryInterface(this.entity, IID_Trader);5224 let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 5225 5225 cmpTrader.PerformTrade(currentMarket); 5226 if (!cmpTrader.GetGain().traderGain)5227 {5228 this.StopTrading();5229 return;5230 }5231 5226 5232 5227 if (this.order.data.route && this.order.data.route.length) 5233 5228 { -
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 let 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/GuiInterface.js
240 240 "resourceCarrying": null, 241 241 "rotation": null, 242 242 "trader": null, 243 "tradeIncome": null, 243 244 "unitAI": null, 244 245 "visibility": null, 245 246 }; … … 303 304 let cmpTrader = Engine.QueryInterface(ent, IID_Trader); 304 305 if (cmpTrader) 305 306 ret.trader = { 306 "goods": cmpTrader.GetGoods(), 307 "requiredGoods": cmpTrader.GetRequiredGoods() 307 "trading": cmpTrader.HasBothMarkets(), 308 "nextMarket": cmpTrader.GetNextMarket(), 309 "originMarket": cmpTrader.GetOriginMarket() 308 310 }; 309 311 312 var cmpTradeIncome = Engine.QueryInterface(ent, IID_TradeIncome); 313 if (cmpTradeIncome) 314 ret.tradeIncome = { 315 "connections": cmpTradeIncome.GetConnections(), 316 "rate": cmpTradeIncome.GetTradeIncomeRate(), 317 "rawRate": cmpTradeIncome.GetRawEfficiency() 318 }; 319 310 320 let cmpFogging = Engine.QueryInterface(ent, IID_Fogging); 311 321 if (cmpFogging) 312 322 { … … 1646 1656 if (!data.firstMarket || !data.secondMarket) 1647 1657 return null; 1648 1658 1649 return Calculate TraderGain(data.firstMarket, data.secondMarket, data.template);1659 return CalculateMarketConnectionBonus(data.firstMarket, data.secondMarket, data.tradeOwner); 1650 1660 }; 1651 1661 1652 1662 GuiInterface.prototype.GetTradingDetails = function(player, data) … … 1665 1675 "hasBothMarkets": cmpEntityTrader.HasBothMarkets() 1666 1676 }; 1667 1677 if (cmpEntityTrader.HasBothMarkets()) 1668 result.gain = cmpEntityTrader.GetGain();1678 result.gain = CalculateMarketConnectionBonus(firstMarket, secondMarket, player) 1669 1679 } 1670 1680 else if (data.target === secondMarket) 1671 1681 { 1672 1682 result = { 1673 1683 "type": "is second", 1674 "gain": cmpEntityTrader.GetGain(),1684 "gain": CalculateMarketConnectionBonus(firstMarket, data.target, player) 1675 1685 }; 1676 1686 } 1677 1687 else if (!firstMarket) … … 1682 1692 { 1683 1693 result = { 1684 1694 "type": "set second", 1685 "gain": cmpEntityTrader.CalculateGain(firstMarket, data.target),1695 "gain": CalculateMarketConnectionBonus(firstMarket, data.target, player) 1686 1696 }; 1687 1697 } 1688 1698 else -
binaries/data/mods/public/simulation/components/Looter.js
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/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); … … 17 18 var firstMarketPosition = cmpFirstMarketPosition.GetPosition2D(); 18 19 var secondMarketPosition = cmpSecondMarketPosition.GetPosition2D(); 19 20 20 // Calculate ordinary Euclidean distance between markets. 21 // We don't use pathfinder, because ordinary distance looks more fair. 22 var distance = firstMarketPosition.distanceTo(secondMarketPosition); 23 // We calculate gain as square of distance to encourage trading between remote markets 24 gain.traderGain = Math.pow(distance * DISTANCE_FACTOR, 2); 25 if (template && template.GainMultiplier) 26 { 27 if (trader) 28 gain.traderGain *= ApplyValueModificationsToEntity("Trader/GainMultiplier", +template.GainMultiplier, trader); 29 else // called from the gui with modifications already applied 30 gain.traderGain *= template.GainMultiplier; 31 } 32 // If trader undefined, the trader owner is supposed to be the same as the first market 33 var cmpOwnership = trader ? Engine.QueryInterface(trader, IID_Ownership) : Engine.QueryInterface(firstMarket, IID_Ownership); 34 if (!cmpOwnership) 35 return null; 36 gain.traderOwner = cmpOwnership.GetOwner(); 37 38 // If markets belong to different players, add gain from international trading 39 var ownerFirstMarket = Engine.QueryInterface(firstMarket, IID_Ownership).GetOwner(); 40 var ownerSecondMarket = Engine.QueryInterface(secondMarket, IID_Ownership).GetOwner(); 41 if (ownerFirstMarket != ownerSecondMarket) 42 { 43 gain.market1Gain = gain.traderGain * ApplyValueModificationsToEntity("Trade/International", INTERNATIONAL_TRADING_ADDITION, firstMarket) / 100; 44 gain.market1Owner = ownerFirstMarket; 45 gain.market2Gain = gain.traderGain * ApplyValueModificationsToEntity("Trade/International", INTERNATIONAL_TRADING_ADDITION, secondMarket) / 100; 46 gain.market2Owner = ownerSecondMarket; 47 } 48 49 // Add potential trade multipliers and roundings 50 var cmpPlayer = trader ? QueryOwnerInterface(trader) : QueryOwnerInterface(firstMarket); 21 22 // Calculate ordinary Euclidean distance between markets. 23 // We don't use pathfinder, because ordinary distance looks more fair. 24 var distance = Math.sqrt(Math.pow(firstMarketPosition.x - secondMarketPosition.x, 2) + Math.pow(firstMarketPosition.y - secondMarketPosition.y, 2)); 25 26 // If markets belong to different players, add gain from international trading 27 var ownerFirstMarket = Engine.QueryInterface(firstMarket, IID_Ownership).GetOwner(); 28 var ownerSecondMarket = Engine.QueryInterface(secondMarket, IID_Ownership).GetOwner(); 29 if (ownerFirstMarket != ownerSecondMarket) 30 return CalculateMarketConnectionBonusDirect(distance, tradeOwner, true); 31 32 return CalculateMarketConnectionBonusDirect(distance, tradeOwner); 33 } 34 35 function CalculateMarketConnectionBonusDirect(distance, tradeOwner, international) 36 { 37 var bonus = Math.log(distance*DISTANCE_FACTOR); 38 var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); 39 var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(tradeOwner), IID_Player); 51 40 if (cmpPlayer) 52 gain.traderGain *= cmpPlayer.GetTradeRateMultiplier(); 53 gain.traderGain = Math.round(gain.traderGain); 41 bonus *= cmpPlayer.GetTradeRateMultiplier(); 54 42 55 if (ownerFirstMarket != ownerSecondMarket) 56 { 57 if ((cmpPlayer = QueryOwnerInterface(firstMarket))) 58 gain.market1Gain *= cmpPlayer.GetTradeRateMultiplier(); 59 gain.market1Gain = Math.round(gain.market1Gain); 60 61 if ((cmpPlayer = QueryOwnerInterface(secondMarket))) 62 gain.market2Gain *= cmpPlayer.GetTradeRateMultiplier(); 63 gain.market2Gain = Math.round(gain.market2Gain); 64 } 65 66 return gain; 43 if (international) 44 bonus *= +ApplyValueModificationsToEntity("Trade/International", INTERNATIONAL_TRADING_MULTIPLICATION, tradeOwner); 45 return Math.max(0, bonus); 67 46 } 68 69 Engine.RegisterGlobal("CalculateTraderGain", CalculateTraderGain); 47 48 Engine.RegisterGlobal("CalculateMarketConnectionBonusDirect", CalculateMarketConnectionBonusDirect); 49 Engine.RegisterGlobal("CalculateMarketConnectionBonus", CalculateMarketConnectionBonus); -
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/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_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_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_unit_support_trader.xml
18 18 <Tooltip>Trade resources between your own markets and those of your allies.</Tooltip> 19 19 <Formations disable=""/> 20 20 </Identity> 21 <Loot> 22 <xp>10</xp> 23 <food>25</food> 24 <wood>25</wood> 25 <stone>15</stone> 26 <metal>15</metal> 27 </Loot> 21 28 <Sound> 22 29 <SoundGroups> 23 30 <select>voice/{lang}/civ/civ_{gender}_select.xml</select> … … 39 46 </SoundGroups> 40 47 </Sound> 41 48 <Trader> 42 < GainMultiplier>1.0</GainMultiplier>49 <ConnectionStrength>50</ConnectionStrength> 43 50 </Trader> 44 51 <UnitAI> 45 52 <CanGuard>false</CanGuard> -
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/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/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/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> -
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>