Ticket #3697: trade-v2.4.patch
File trade-v2.4.patch, 49.5 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/globalscripts/Templates.js
function GetTemplateDataHelper(template, 243 243 "walk": func("UnitMotion/WalkSpeed", +template.UnitMotion.WalkSpeed, player, template), 244 244 }; 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 { 258 251 ret.wallSet = { 259 252 "templates": { -
binaries/data/mods/public/gui/session/selection_details.js
function displaySingle(entState) 174 174 Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/resources/"+carried.type+".png"; 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 178 // Use the same indicators for traders 179 else if (entState.trader && entState.trader. goods.amount)179 else if (entState.trader && entState.trader.trading) 180 180 { 181 var nextMarket = GetEntityState(entState.trader.nextMarket); 182 // Find the connection if it exists. 183 let connection = null; 184 185 if (nextMarket && nextMarket.tradeIncome) 186 connection = nextMarket.tradeIncome.connections.find(conn => conn.marketID === entState.trader.originMarket); 187 181 188 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; 182 189 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) }); 190 Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/trade.png"; 191 Engine.GetGUIObjectByName("resourceCarryingText").caption = ""; 192 if (connection) 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.strength, 100) }); 194 else 195 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."); 191 196 } 192 197 // And for number of workers 193 198 else if (entState.foundation && entState.visibility == "visible") 194 199 { 195 200 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; … … function displaySingle(entState) 204 209 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = sprintf(translatePlural("Number of builders.\nTasking another to this foundation would speed construction up by %(speedup)s second.", "Number of builders.\nTasking another to this foundation would speed construction up by %(speedup)s seconds.", timeSpeedup), { "speedup": timeSpeedup }); 205 210 } 206 211 else 207 212 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = translate("Number of builders."); 208 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 Engine.GetGUIObjectByName("resourceCarryingText").caption = sprintf(translate("%(MarketEfficiency)u%% x%(NumberOfConnections)u"), 221 { "MarketEfficiency": +entState.tradeIncome.rawRate*100, 222 "NumberOfConnections": entState.tradeIncome.connections.length }); 223 Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = getTradeIncomeTooltip(entState.tradeIncome); 224 } 209 225 else if (entState.repairable && entState.repairable.numBuilders > 0 && entState.visibility == "visible") 210 226 { 211 227 Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false; 212 228 Engine.GetGUIObjectByName("resourceCarryingText").hidden = false; 213 229 Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/repair.png"; -
binaries/data/mods/public/gui/session/selection_panels.js
g_SelectionPanels.Selection = { 949 949 if (data.carried[carrying.type]) 950 950 data.carried[carrying.type] += carrying.amount; 951 951 else 952 952 data.carried[carrying.type] = carrying.amount; 953 953 } 954 955 if (state.trader && state.trader.goods && state.trader.goods.amount)956 {957 if (!data.carried)958 data.carried = {};959 var amount = state.trader.goods.amount;960 var type = state.trader.goods.type;961 var totalGain = amount.traderGain;962 if (amount.market1Gain)963 totalGain += amount.market1Gain;964 if (amount.market2Gain)965 totalGain += amount.market2Gain;966 if (data.carried[type])967 data.carried[type] += totalGain;968 else969 data.carried[type] = totalGain;970 }971 954 } 972 955 return true; 973 956 }, 974 957 "setTooltip": function(data) 975 958 { -
binaries/data/mods/public/gui/session/unit_actions.js
var unitActions = 318 318 switch (tradingDetails.type) 319 319 { 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 }, 357 354 358 355 "garrison": … … var unitActions = 498 495 data.command = "attack-walk"; 499 496 data.targetClasses = targetClasses; 500 497 cursor = "action-attack-move"; 501 498 } 502 499 503 if (targetState.garrisonHolder && playerCheck(entState, targetState, ["Player", "MutualAlly"])) 500 if (hasClass(entState, "Market") && hasClass(targetState, "Market") && entState.id != targetState.id && 501 (!hasClass(entState, "NavalMarket") || hasClass(targetState, "NavalMarket")) && playerCheck(entState, targetState, ["MutualAlly"])) 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", "MutualAlly"])) 504 521 { 505 522 data.command = "garrison"; 506 523 data.target = targetState.id; 507 524 cursor = "action-garrison"; 508 525 tooltip = sprintf(translate("Current garrison: %(garrisoned)s/%(capacity)s"), { … … var unitActions = 521 538 cursor = "action-gather-" + resourceType.specific; 522 539 data.command = "gather"; 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"; 554 546 data.target = targetState.id; 555 547 cursor = "action-build"; -
binaries/data/mods/public/gui/session/utility_functions.js
function getRankIconSprite(entState) 70 70 } 71 71 72 72 /** 73 73 * Returns a message with the details of the trade gain. 74 74 */ 75 function getTrad ingTooltip(gain)75 function getTradeRouteTooltip(gain) 76 76 { 77 return translate("This trader is currently trading between two markets, creating wealth for his empire.") + "\n" + 78 sprintf(translate("The basic value of this trade route is %(value).1f."), { "value": gain }); 79 } 80 81 /** 82 * Returns a message with the details of the trade income. 83 */ 84 function getTradeIncomeTooltip(details) 85 { 86 if (details.connections.length === 0) 87 return translate("This market has no trade routes. Use traders to connect markets and create wealth."); 88 89 let avgStrength = 0.0; 90 for (let connection in details.connections) 91 avgStrength += Math.min(100, details.connections[connection].strength); 92 avgStrength /= details.connections.length; 93 94 let tooltip = "[font=\"sans-bold-13\"]" + translate("Trade income") + ":[/font] " + 95 sprintf(translate("%(tradeRate).1f"), { "tradeRate": details.rate * 60.0 }) + 96 " [font=\"sans-10\"]" + translate("resources/minute") + "[/font]\n"; 97 98 tooltip += "[font=\"sans-bold-13\"]" + translate("Efficiency") + ":[/font] " + 99 sprintf(translate("%(tradeEfficiency)u%%"), { "tradeEfficiency": details.rawRate * 100 }) + "\n"; 100 101 tooltip += "[font=\"sans-bold-13\"]" + translate("Number of trade routes") + ":[/font] " + 102 sprintf(translateWithContext("trade", "%(connections)s"), { "connections": details.connections.length }) + "\n"; 103 104 tooltip += "[font=\"sans-bold-13\"]" + translate("Average trade route effiency") + ":[/font] " + 105 sprintf(translateWithContext("trade", "%(connectionStrength)s%%"), { "connectionStrength": avgStrength }); 77 106 78 var playerID = Engine.GetPlayerID(); 79 var simState = GetSimState(); 80 81 var gainString = gain.traderGain; 82 if (gain.market1Gain && gain.market1Owner == gain.traderOwner) 83 gainString += translate("+") + gain.market1Gain; 84 if (gain.market2Gain && gain.market2Owner == gain.traderOwner) 85 gainString += translate("+") + gain.market2Gain; 86 87 var tooltip = sprintf(translate("%(gain)s (%(player)s)"), { 88 gain: gainString, 89 player: (!g_IsNetworked && gain.traderOwner == playerID) ? translate("You") : simState.players[gain.traderOwner].name 90 }); 91 92 if (gain.market1Gain && gain.market1Owner != gain.traderOwner) 93 tooltip += translateWithContext("Separation mark in an enumeration", ", ") + sprintf(translate("%(gain)s (%(player)s)"), { 94 gain: gain.market1Gain, 95 player: (!g_IsNetworked && gain.market1Owner == playerID) ? translate("You") : simState.players[gain.market1Owner].name 96 }); 97 if (gain.market2Gain && gain.market2Owner != gain.traderOwner) 98 tooltip += translateWithContext("Separation mark in an enumeration", ", ") + sprintf(translate("%(gain)s (%(player)s)"), { 99 gain: gain.market2Gain, 100 player: (!g_IsNetworked && gain.market2Owner == playerID) ? translate("You") : simState.players[gain.market2Owner].name 101 }); 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]"; 102 108 103 109 return tooltip; 104 110 } 105 111 106 112 /** -
binaries/data/mods/public/simulation/components/GuiInterface.js
GuiInterface.prototype.GetEntityState = 240 240 "production": null, 241 241 "rallyPoint": null, 242 242 "resourceCarrying": null, 243 243 "rotation": null, 244 244 "trader": null, 245 "tradeIncome": null, 245 246 "unitAI": null, 246 247 "visibility": null, 247 248 }; 248 249 249 250 let cmpMirage = Engine.QueryInterface(ent, IID_Mirage); … … GuiInterface.prototype.GetEntityState = 303 304 }; 304 305 305 306 let cmpTrader = Engine.QueryInterface(ent, IID_Trader); 306 307 if (cmpTrader) 307 308 ret.trader = { 308 "goods": cmpTrader.GetGoods(), 309 "requiredGoods": cmpTrader.GetRequiredGoods() 309 "trading": cmpTrader.HasBothMarkets(), 310 "nextMarket": cmpTrader.GetNextMarket(), 311 "originMarket": cmpTrader.GetOriginMarket() 312 }; 313 314 let cmpTradeIncome = Engine.QueryInterface(ent, IID_TradeIncome); 315 if (cmpTradeIncome) 316 ret.tradeIncome = { 317 "connections": cmpTradeIncome.GetConnections(), 318 "rate": cmpTradeIncome.GetTradeIncomeRate(), 319 "rawRate": cmpTradeIncome.GetRawEfficiency() 310 320 }; 311 321 312 322 let cmpFogging = Engine.QueryInterface(ent, IID_Fogging); 313 323 if (cmpFogging) 314 324 { … … GuiInterface.prototype.FindIdleUnits = f 1646 1656 GuiInterface.prototype.GetTradingRouteGain = function(player, data) 1647 1657 { 1648 1658 if (!data.firstMarket || !data.secondMarket) 1649 1659 return null; 1650 1660 1651 return Calculate TraderGain(data.firstMarket, data.secondMarket, data.template);1661 return CalculateMarketConnectionBonus(data.firstMarket, data.secondMarket, data.tradeOwner); 1652 1662 }; 1653 1663 1654 1664 GuiInterface.prototype.GetTradingDetails = function(player, data) 1655 1665 { 1656 1666 let cmpEntityTrader = Engine.QueryInterface(data.trader, IID_Trader); … … GuiInterface.prototype.GetTradingDetails 1665 1675 result = { 1666 1676 "type": "is first", 1667 1677 "hasBothMarkets": cmpEntityTrader.HasBothMarkets() 1668 1678 }; 1669 1679 if (cmpEntityTrader.HasBothMarkets()) 1670 result.gain = cmpEntityTrader.GetGain();1680 result.gain = CalculateMarketConnectionBonus(firstMarket, secondMarket, player) 1671 1681 } 1672 1682 else if (data.target === secondMarket) 1673 1683 { 1674 1684 result = { 1675 1685 "type": "is second", 1676 "gain": cmpEntityTrader.GetGain(),1686 "gain": CalculateMarketConnectionBonus(firstMarket, data.target, player) 1677 1687 }; 1678 1688 } 1679 1689 else if (!firstMarket) 1680 1690 { 1681 1691 result = { "type": "set first" }; 1682 1692 } 1683 1693 else if (!secondMarket) 1684 1694 { 1685 1695 result = { 1686 1696 "type": "set second", 1687 "gain": cmpEntityTrader.CalculateGain(firstMarket, data.target),1697 "gain": CalculateMarketConnectionBonus(firstMarket, data.target, player) 1688 1698 }; 1689 1699 } 1690 1700 else 1691 1701 { 1692 1702 // Else both markets are not null and target is different from them -
binaries/data/mods/public/simulation/components/Looter.js
Looter.prototype.Collect = function(targ 34 34 var cmpResourceGatherer = Engine.QueryInterface(targetEntity, IID_ResourceGatherer); 35 35 if (cmpResourceGatherer) 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); 56 42 57 43 // Update statistics -
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 let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 38 cmpTimer.SetInterval(this.entity, IID_TradeIncome, "GenerateResources", 5000, 5000, undefined); 39 40 // Quick sanity check 41 let 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 let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 59 let cmpConnectionPosition = Engine.QueryInterface(market, IID_Position); 60 if (!cmpPosition || !cmpPosition.IsInWorld() || !cmpConnectionPosition || !cmpConnectionPosition.IsInWorld()) 61 return; 62 63 let Position = cmpPosition.GetPosition2D(); 64 let ConnectionPosition = cmpConnectionPosition.GetPosition2D(); 65 let 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 let owner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner(); 71 let 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 let ret = []; 79 for (let conn of this.connections.keys()) 80 ret.push( 81 { 82 "marketID": conn, 83 "strength": this.connections.get(conn).strength, 84 "distance": this.connections.get(conn).distance, 85 "international": this.connections.get(conn).international 86 }); 87 return ret; 88 }; 89 90 TradeIncome.prototype.GetTradeIncomeRate = function() 91 { 92 if (this.connections.size === 0) 93 return 0; 94 95 // Calculate our connection bonuses 96 let connectionEffect = 0; 97 98 let owner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner(); 99 100 for (let i of this.connections.keys()) 101 { 102 let bonus = CalculateMarketConnectionBonusDirect(this.connections.get(i).distance, owner, this.connections.get(i).international); 103 connectionEffect += bonus * Math.min(1.0, this.connections.get(i).strength/100.0); 104 } 105 return Math.sqrt(connectionEffect) * this.GetRawEfficiency(); 106 }; 107 108 TradeIncome.prototype.GetRawEfficiency = function() 109 { 110 if (!this.template.GarrisonEfficiency) 111 return 1.0; 112 113 let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); 114 if (!cmpGarrisonHolder) 115 return 1.0; 116 117 // Depending on how many units are garrisoned, modulate efficiency 118 let capacity = this.template.GarrisonEfficiency.MaxGarrison === undefined ? cmpGarrisonHolder.GetCapacity() : +this.template.GarrisonEfficiency.MaxGarrison; 119 let currentCount = this.template.GarrisonEfficiency.Classes === undefined ? 120 cmpGarrisonHolder.GetEntities().length : cmpGarrisonHolder.GetGarrisonedArcherCount(this.template.GarrisonEfficiency.Classes); 121 return 0.2 + (currentCount / capacity) * 0.8; 122 }; 123 124 TradeIncome.prototype.GenerateResources = function() 125 { 126 if (this.connections.size === 0) 127 return; 128 129 let cmpPlayer = QueryOwnerInterface(this.entity); 130 if (!cmpPlayer) 131 return; 132 133 let cmpStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker); 134 135 let rate = this.GetTradeIncomeRate() * 5; 136 let goods = cmpPlayer.GetTradingGoods(); 137 138 for (let good in goods) 139 { 140 if (RESOURCES.indexOf(good) === -1) 141 continue; 142 143 cmpPlayer.AddResource(good, rate * goods[good]/100.0); // Should add up to 100% 144 145 if (cmpStatisticsTracker) 146 cmpStatisticsTracker.IncreaseTradeIncomeCounter(rate * goods[good]); 147 } 148 149 for (let i of this.connections.keys()) 150 { 151 this.connections.get(i).strength -= 10; 152 if (this.connections.get(i).strength <= 0) 153 this.connections.delete(i); 154 } 155 }; 156 157 Engine.RegisterComponentType(IID_TradeIncome, "TradeIncome", TradeIncome); -
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 10 4 function Trader() {} 11 5 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 22 15 Trader.prototype.Init = function() 23 16 { 24 17 this.firstMarket = INVALID_ENTITY; 25 18 this.secondMarket = INVALID_ENTITY; 26 // Gain from one pass between markets27 this.gain = null;28 // Selected resource for trading29 this.requiredGoods = undefined;30 // Currently carried goods31 this.goods = { "type": null, "amount": null, "origin": null };32 };33 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 19 70 Trader.prototype.GetGain = function() 71 { 72 return this.gain; 20 // Currently carried goods 21 this.goodsOrigin = null; 73 22 }; 74 23 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) … … Trader.prototype.SetTargetMarket = funct 108 57 // If we have only one market and target is different from it, 109 58 // set the target as second one 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 { 120 66 // Else we don't have target markets at all, 121 67 // set the target as first market 122 68 this.firstMarket = target; 123 69 } 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 }; 131 77 132 78 Trader.prototype.GetFirstMarket = function() … … Trader.prototype.GetSecondMarket = funct 142 88 Trader.prototype.HasBothMarkets = function() 143 89 { 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); 164 96 var cmpTargetIdentity = Engine.QueryInterface(target, IID_Identity); 165 97 // Check that the target exists … … Trader.prototype.CanTrade = function(tar 184 116 return true; 185 117 }; 186 118 187 119 Trader.prototype.PerformTrade = function(currentMarket) 188 120 { 189 if (this.goods.amount && this.goods.amount.traderGain) 190 { 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 } 220 } 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) 121 if (this.goodsOrigin && this.goodsOrigin !== currentMarket) 228 122 { 229 var cmpPlayer = QueryOwnerInterface(this.entity); 230 if (cmpPlayer) 231 nextGoods = cmpPlayer.GetNextTradingGoods(); 232 233 if (!nextGoods || RESOURCES.indexOf(nextGoods) == -1) 234 nextGoods = "metal"; 123 let cmpTradeIncome = Engine.QueryInterface(currentMarket, IID_TradeIncome); 124 if (cmpTradeIncome) 125 cmpTradeIncome.AddConnectionWithMarket(this.goodsOrigin, this.template.ConnectionStrength); 235 126 } 236 this.goods.type = nextGoods; 237 this.goods.amount = this.CalculateGain(this.firstMarket, this.secondMarket); 238 this.goods.origin = currentMarket; 239 }; 240 241 Trader.prototype.GetGoods = function() 242 { 243 return this.goods; 127 this.goodsOrigin = +currentMarket; 244 128 }; 245 129 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; 263 152 }; 264 153 … … Trader.prototype.GetRange = function() 272 161 if (cmpObstruction) 273 162 max += cmpObstruction.GetUnitRadius()*1.5; 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/UnitAI.js
UnitAI.prototype.PerformTradeAndMoveToNe 5238 5238 return; 5239 5239 } 5240 5240 5241 5241 if (this.CheckTargetRange(currentMarket, IID_Trader)) 5242 5242 { 5243 varcmpTrader = Engine.QueryInterface(this.entity, IID_Trader);5243 let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 5244 5244 cmpTrader.PerformTrade(currentMarket); 5245 if (!cmpTrader.GetGain().traderGain)5246 {5247 this.StopTrading();5248 return;5249 }5250 5245 5251 5246 if (this.order.data.route && this.order.data.route.length) 5252 5247 { 5253 5248 this.waypoints = this.order.data.route.slice(); 5254 5249 if (nextFsmStateName == "APPROACHINGSECONDMARKET") -
binaries/data/mods/public/simulation/components/interfaces/TradeIncome.js
1 Engine.RegisterInterface("TradeIncome"); -
binaries/data/mods/public/simulation/helpers/TraderGain.js
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 12 var cmpFirstMarketPosition = Engine.QueryInterface(firstMarket, IID_Position); 13 var cmpSecondMarketPosition = Engine.QueryInterface(secondMarket, IID_Position); 11 let cmpFirstMarketPosition = Engine.QueryInterface(firstMarket, IID_Position); 12 let cmpSecondMarketPosition = Engine.QueryInterface(secondMarket, IID_Position); 14 13 if (!cmpFirstMarketPosition || !cmpFirstMarketPosition.IsInWorld() || 15 14 !cmpSecondMarketPosition || !cmpSecondMarketPosition.IsInWorld()) 16 15 return null; 17 varfirstMarketPosition = cmpFirstMarketPosition.GetPosition2D();18 varsecondMarketPosition = cmpSecondMarketPosition.GetPosition2D();16 let firstMarketPosition = cmpFirstMarketPosition.GetPosition2D(); 17 let secondMarketPosition = cmpSecondMarketPosition.GetPosition2D(); 19 18 20 19 // Calculate ordinary Euclidean distance between markets. 21 20 // 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(); 21 let distance = Math.sqrt(Math.pow(firstMarketPosition.x - secondMarketPosition.x, 2) + 22 Math.pow(firstMarketPosition.y - secondMarketPosition.y, 2)); 37 23 38 24 // 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 } 25 let ownerFirstMarket = Engine.QueryInterface(firstMarket, IID_Ownership).GetOwner(); 26 let ownerSecondMarket = Engine.QueryInterface(secondMarket, IID_Ownership).GetOwner(); 48 27 49 // Add potential trade multipliers and roundings 50 var cmpPlayer = trader ? QueryOwnerInterface(trader) : QueryOwnerInterface(firstMarket); 51 if (cmpPlayer) 52 gain.traderGain *= cmpPlayer.GetTradeRateMultiplier(); 53 gain.traderGain = Math.round(gain.traderGain); 28 return CalculateMarketConnectionBonusDirect(distance, tradeOwner, ownerFirstMarket != ownerSecondMarket); 29 } 54 30 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 } 31 function CalculateMarketConnectionBonusDirect(distance, tradeOwner, international) 32 { 33 let bonus = Math.log(distance * DISTANCE_FACTOR); 34 let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); 35 let cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(tradeOwner), IID_Player); 36 if (cmpPlayer) 37 bonus *= cmpPlayer.GetTradeRateMultiplier(); 65 38 66 return gain; 39 if (international) 40 bonus *= +ApplyValueModificationsToEntity("Trade/International", INTERNATIONAL_TRADING_MULTIPLICATION, tradeOwner); 41 return Math.max(0, bonus); 67 42 } 68 43 69 Engine.RegisterGlobal("CalculateTraderGain", CalculateTraderGain); 44 Engine.RegisterGlobal("CalculateMarketConnectionBonusDirect", CalculateMarketConnectionBonusDirect); 45 Engine.RegisterGlobal("CalculateMarketConnectionBonus", CalculateMarketConnectionBonus); -
binaries/data/mods/public/simulation/templates/special/player.xml
28 28 <Theater>1</Theater> 29 29 <Council>1</Council> 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> 38 42 <Pillar> 39 43 <Ashoka>5</Ashoka> -
binaries/data/mods/public/simulation/templates/structures/cart_dock.xml
25 25 <ProductionQueue> 26 26 <Technologies datatype="tokens"> 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> 33 36 </VisualActor> 34 37 </Entity> -
binaries/data/mods/public/simulation/templates/structures/pers_market.xml
13 13 <History>Traders from all distant parts of the huge empire met, exchanged and sold goods in the huge bazaars present in almost every big city. Babylon and Susa were the largest and most frequented trade centers.</History> 14 14 </Identity> 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> 21 24 </VisualActor> 22 25 </Entity> -
binaries/data/mods/public/simulation/templates/template_formation.xml
38 38 <UnitMotion> 39 39 <FormationController>true</FormationController> 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_economic_market.xml
1 1 <?xml version="1.0" encoding="utf-8"?> 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> 8 12 <Resources> 9 13 <wood>300</wood> … … 11 15 </Cost> 12 16 <Footprint> 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> 19 31 </Health> 20 32 <Identity> … … 45 57 <TerritoryInfluence> 46 58 <Root>false</Root> 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"> 53 71 unlock_shared_los 54 72 trade_convoys_speed -
binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml
69 69 armor_ship_reinforcedhull 70 70 armor_ship_hypozomata 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> 77 80 <VisualActor> 78 81 <FoundationActor>structures/fndn_4x4_dock.xml</FoundationActor> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_merchant.xml
43 43 <BarWidth>6.0</BarWidth> 44 44 <BarHeight>0.5</BarHeight> 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> 52 52 <CanGuard>false</CanGuard> 53 53 </UnitAI> -
binaries/data/mods/public/simulation/templates/template_unit_support_trader.xml
16 16 <GenericName>Trader</GenericName> 17 17 <History>Trade was a very important part of ancient civilization - effective trading and control of trade routes equaled wealth. Trade took place by many forms from foot to caravans to merchant ships. One of the most notorious examples of the power of trade was the Silk Road.</History> 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> 24 31 <order_trade>voice/{lang}/civ/civ_{gender}_trade.xml</order_trade> 25 32 <order_walk>voice/{lang}/civ/civ_{gender}_walk.xml</order_walk> … … 37 44 <gather_rock>resource/mining/pickaxe.xml</gather_rock> 38 45 <gather_ore>resource/mining/mining.xml</gather_ore> 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> 46 53 </UnitAI> 47 54 <UnitMotion> -
binaries/data/mods/public/simulation/templates/units/cart_ship_merchant.xml
10 10 <History>The entire purpose of the Phoenicians/Carthaginians was to conduct trade and commerce principally upon the sea (though not limited to that as far as Carthage was concerned). </History> 11 11 <Tooltip>Trade between docks. Garrison a Trader aboard for additional profit (+20% for each garrisoned). Gather profitable aquatic treasures. Carthaginians have +25% sea trading bonus.</Tooltip> 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> 21 18 </Entity> -
binaries/data/mods/public/simulation/templates/units/pers_support_trader.xml
18 18 <select>actor/fauna/animal/camel.xml</select> 19 19 <order_walk>actor/fauna/movement/camel.xml</order_walk> 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> 29 26 </Entity>