Ticket #30: trading_2012_02_06.diff
File trading_2012_02_06.diff, 32.7 KB (added by , 12 years ago) |
---|
-
source/gui/CGUI.cpp
438 438 AddObjectType("input", &CInput::ConstructObject); 439 439 AddObjectType("list", &CList::ConstructObject); 440 440 AddObjectType("dropdown", &CDropDown::ConstructObject); 441 AddObjectType("tooltip", &CTooltip::ConstructObject); 441 442 } 442 443 443 444 void CGUI::Draw() -
source/gui/CTooltip.cpp
135 135 136 136 void CTooltip::HandleMessage(SGUIMessage &Message) 137 137 { 138 if (Message.type == GUIM_MOUSE_MOTION) 139 if (GUI<CPos>::SetSetting(this, "_mousepos", GetMousePos()) != PSRETURN_OK) 140 debug_warn(L"Failed to set tooltip mouse position"); 138 141 IGUITextOwner::HandleMessage(Message); 139 142 } 140 143 -
binaries/data/mods/public/gui/session/session.xml
458 458 </object> 459 459 460 460 <!-- ================================ ================================ --> 461 <!-- Information tooltip --> 462 <!-- ================================ ================================ --> 463 <object name="informationTooltip" type="tooltip" style="informationTooltip" /> 464 465 <!-- ================================ ================================ --> 461 466 <!-- START of BOTTOM PANEL --> 462 467 <!-- ================================ ================================ --> 463 468 … … 596 601 <!-- Resource carrying icon/counter --> 597 602 <object size="0 40 48 88" type="image" name="resourceCarryingIcon" style="resourceIcon"/> 598 603 <object size="0 80 48 100" type="text" name="resourceCarryingText" style="statsText"/> 599 600 604 </object> 601 605 602 606 <!-- Big unit icon --> … … 751 755 </object> 752 756 </object> 753 757 758 <object name="unitTradingPanel" 759 size="14 12 100% 100%" 760 > 761 <object size="0 0 100% 100%"> 762 <repeat count="4"> 763 <object name="unitTradingButton[n]" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottom"> 764 <object name="unitTradingIcon[n]" type="image" ghost="true" size="3 3 43 43"/> 765 </object> 766 </repeat> 767 </object> 768 </object> 769 754 770 <object name="unitQueuePanel" 755 771 size="4 -56 100% 0" 756 772 type="image" -
binaries/data/mods/public/gui/session/styles.xml
161 161 tooltip_style="snToolTip" 162 162 /> 163 163 164 <style name="informationTooltip" 165 anchor="top" 166 buffer_zone="4" 167 font="serif-bold-14" 168 maxwidth="300" 169 offset="16 32" 170 sprite="BackgroundInformationTooltip" 171 textcolor="255 255 255" 172 /> 173 164 174 <!-- ================================ ================================ --> 165 175 <!-- Misc Styles --> 166 176 <!-- ================================ ================================ --> -
binaries/data/mods/public/gui/session/selection_details.js
135 135 // getGUIObjectByName("resourceCarryingText").hidden = true; 136 136 // } 137 137 } 138 // Use the same indicators for traders 139 else if (entState.trader && entState.trader.goods.amount > 0) 140 { 141 getGUIObjectByName("resourceCarryingIcon").hidden = false; 142 getGUIObjectByName("resourceCarryingText").hidden = false; 143 getGUIObjectByName("resourceCarryingIcon").cell_id = RESOURCE_ICON_CELL_IDS[entState.trader.goods.type]; 144 getGUIObjectByName("resourceCarryingText").caption = entState.trader.goods.amount; 145 } 138 146 else 139 147 { 140 148 getGUIObjectByName("resourceCarryingIcon").hidden = true; -
binaries/data/mods/public/gui/session/utility_functions.js
83 83 player.offline = true; 84 84 } 85 85 86 function hasClass(entState, className) 87 { 88 if (entState.identity) 89 { 90 var classes = entState.identity.classes; 91 if (classes && classes.length) 92 return (classes.indexOf(className) != -1); 93 } 94 return false; 95 } 96 86 97 function isUnit(entState) 87 98 { 88 99 if (entState.identity) -
binaries/data/mods/public/gui/session/input.js
52 52 var doublePressTimer = 0; 53 53 var prevHotkey = 0; 54 54 55 function updateCursor ()55 function updateCursorAndTooltip() 56 56 { 57 var cursorSet = false; 58 var tooltipSet = false; 59 var informationTooltip = getGUIObjectByName("informationTooltip"); 57 60 if (!mouseIsOverObject) 58 61 { 59 62 var action = determineAction(mouseX, mouseY); … … 64 67 if (action.cursor) 65 68 { 66 69 Engine.SetCursor(action.cursor); 67 return;70 cursorSet = true; 68 71 } 72 if (action.tooltip) 73 { 74 tooltipSet = true; 75 informationTooltip.caption = action.tooltip; 76 informationTooltip.hidden = false; 77 } 69 78 } 70 79 } 71 80 } 72 81 73 Engine.SetCursor("arrow-default"); 82 if (!cursorSet) 83 Engine.SetCursor("arrow-default"); 84 if (!tooltipSet) 85 informationTooltip.hidden = true; 74 86 } 75 87 76 88 function updateBuildingPlacementPreview() … … 255 267 } 256 268 } 257 269 break; 270 case "setup-trade-route": 271 // If ground or sea trade possible 272 if ((entState.trader && hasClass(entState, "Organic") && (playerOwned || allyOwned) && hasClass(targetState, "Market")) || 273 (entState.trader && hasClass(entState, "Ship") && (playerOwned || allyOwned) && hasClass(targetState, "SeaMarket"))) 274 { 275 var tradingData = {"trader": entState.id, "target": target}; 276 var tradingDetails = Engine.GuiInterfaceCall("GetTradingDetails", tradingData); 277 var tooltip; 278 switch (tradingDetails.type) 279 { 280 case "is first": 281 tooltip = "First trade market."; 282 if (tradingDetails.hasBothMarkets) 283 tooltip += " Click to establish another route." 284 else 285 tooltip += " Click on another market to establish a trade route." 286 break; 287 case "is second": 288 tooltip = "Second trade market. Click to establish another route."; 289 break; 290 case "set first": 291 tooltip = "Set as first trade market"; 292 break; 293 case "set second": 294 tooltip = "Set as second trade market. Gain: " + tradingDetails.gain + " " + tradingDetails.goods + "."; 295 break; 296 } 297 return {"possible": true, "tooltip": tooltip}; 298 } 299 break; 258 300 case "gather": 259 301 if (targetState.resourceSupply && (playerOwned || gaiaOwned)) 260 302 { … … 278 320 case "attack": 279 321 if (entState.attack && targetState.hitpoints && enemyOwned) 280 322 return {"possible": true}; 323 break; 281 324 } 282 325 } 283 326 if (action == "move") … … 362 405 else 363 406 { 364 407 var actionInfo = undefined; 365 if ((actionInfo = getActionInfo("gather", target)).possible) 408 if ((actionInfo = getActionInfo("setup-trade-route", target)).possible) 409 return {"type": "setup-trade-route", "cursor": "action-setup-trade-route", "tooltip": actionInfo.tooltip, "target": target}; 410 else if ((actionInfo = getActionInfo("gather", target)).possible) 366 411 return {"type": "gather", "cursor": actionInfo.cursor, "target": target}; 367 412 else if ((actionInfo = getActionInfo("returnresource", target)).possible) 368 413 return {"type": "returnresource", "cursor": actionInfo.cursor, "target": target}; … … 962 1007 updateBuildingPlacementPreview(); 963 1008 break; 964 1009 } 965 966 1010 break; 967 1011 968 1012 } … … 1008 1052 Engine.GuiInterfaceCall("PlaySound", { "name": "order_gather", "entity": selection[0] }); 1009 1053 return true; 1010 1054 1055 case "setup-trade-route": 1056 Engine.PostNetworkCommand({"type": "setup-trade-route", "entities": selection, "target": action.target}); 1057 return true; 1058 1011 1059 case "garrison": 1012 1060 Engine.PostNetworkCommand({"type": "garrison", "entities": selection, "target": action.target, "queued": queued}); 1013 1061 // TODO: Play a sound? … … 1100 1148 inputState = INPUT_BUILDING_PLACEMENT; 1101 1149 } 1102 1150 1151 // Called by GUI when user changes preferred trading goods 1152 function selectTradingPreferredGoods(data) 1153 { 1154 Engine.PostNetworkCommand({"type": "select-trading-goods", "trader": data.trader, "preferredGoods": data.preferredGoods }); 1155 } 1156 1103 1157 // Called by GUI when user clicks exchange resources button 1104 1158 function exchangeResources(command) 1105 1159 { 1106 1160 Engine.PostNetworkCommand({"type": "barter", "sell": command.sell, "buy": command.buy, "amount": command.amount}); 1107 1161 } 1108 1162 1109 1110 1163 // Batch training: 1111 1164 // When the user shift-clicks, we set these variables and switch to INPUT_BATCHTRAINING 1112 1165 // When the user releases shift, or clicks on a different training button, we create the batched units -
binaries/data/mods/public/gui/session/unit_commands.js
13 13 const UNIT_PANEL_BASE = -52; // QUEUE: The offset above the main panel (will often be negative) 14 14 const UNIT_PANEL_HEIGHT = 44; // QUEUE: The height needed for a row of buttons 15 15 16 // Trading constants 17 const TRADING_RESOURCES = ["food", "wood", "stone", "metal"]; 18 16 19 // Barter constants 17 20 const BARTER_RESOURCE_AMOUNT_TO_SELL = 100; 18 21 const BARTER_BUNCH_MULTIPLIER = 5; … … 20 23 const BARTER_ACTIONS = ["Sell", "Buy"]; 21 24 22 25 // The number of currently visible buttons (used to optimise showing/hiding) 23 var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, " Barter": 0, "Training": 0, "Construction": 0, "Command": 0, "Stance": 0};26 var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Barter": 0, "Trading": 0, "Construction": 0, "Command": 0, "Stance": 0}; 24 27 25 28 // Unit panels are panels with row(s) of buttons 26 var g_unitPanels = ["Selection", "Queue", "Formation", "Garrison", " Barter", "Training", "Construction", "Research", "Stance", "Command"];29 var g_unitPanels = ["Selection", "Queue", "Formation", "Garrison", "Training", "Barter", "Trading", "Construction", "Research", "Stance", "Command"]; 27 30 28 31 // Indexes of resources to sell and buy on barter panel 29 32 var g_barterSell = 0; … … 132 135 function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback) 133 136 { 134 137 usedPanels[guiName] = 1; 138 135 139 var numberOfItems = items.length; 136 140 var selection = g_Selection.toList(); 137 141 var garrisonGroups = new EntityGroups(); … … 389 393 g_unitPanelButtons[guiName] = numButtons; 390 394 } 391 395 396 // Sets up "unit trading panel" - special case for setupUnitPanel 397 function setupUnitTradingPanel(unitEntState) 398 { 399 for (var i = 0; i < TRADING_RESOURCES.length; i++) 400 { 401 var resource = TRADING_RESOURCES[i]; 402 var button = getGUIObjectByName("unitTradingButton["+i+"]"); 403 button.size = (i * 46) + " 0 " + ((i + 1) * 46) + " 46"; 404 var selectTradingPreferredGoodsData = { "trader": unitEntState.id, "preferredGoods": resource }; 405 button.onpress = (function(e){ return function() { selectTradingPreferredGoods(e); } })(selectTradingPreferredGoodsData); 406 button.enabled = true; 407 button.tooltip = "Set " + resource + " as trading goods"; 408 var icon = getGUIObjectByName("unitTradingIcon["+i+"]"); 409 var preferredGoods = unitEntState.trader.preferredGoods; 410 var imageNameSuffix = (resource == preferredGoods) ? "selected" : "inactive"; 411 icon.sprite = "stretched:session/resources/" + resource + "_" + imageNameSuffix + ".png"; 412 } 413 } 414 392 415 // Sets up "unit barter panel" - special case for setupUnitPanel 393 416 function setupUnitBarterPanel(unitEntState) 394 417 { … … 528 551 setupUnitPanel("Queue", usedPanels, entState, entState.training.queue, 529 552 function (item) { removeFromTrainingQueue(entState.id, item.id); } ); 530 553 554 if (entState.trader) 555 { 556 usedPanels["Trading"] = 1; 557 setupUnitTradingPanel(entState); 558 } 559 531 560 // supplementalDetailsPanel.hidden = false; 532 561 // commandsPanel.hidden = isInvisible; 533 562 } -
binaries/data/mods/public/gui/session/session.js
185 185 handleNetMessage(message); 186 186 } 187 187 188 updateCursor ();188 updateCursorAndTooltip(); 189 189 190 190 // If the selection changed, we need to regenerate the sim display 191 191 if (g_Selection.dirty) -
binaries/data/mods/public/gui/common/common_sprites.xml
70 70 /> 71 71 </sprite> 72 72 73 <sprite name="BackgroundInformationTooltip"> 74 <image 75 backcolor="0 0 0 191" 76 size="0 0 100% 100%" 77 border="false" 78 /> 79 </sprite> 80 73 81 <sprite name="BackgroundIndentFillDark"> 74 82 <!-- Starting with top left corner continuing in a clockwise manner --> 75 83 <!-- Top border --> -
binaries/data/mods/public/simulation/helpers/Commands.js
404 404 } 405 405 break; 406 406 407 case "setup-trade-route": 408 for each (var ent in cmd.entities) 409 { 410 var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); 411 if (cmpUnitAI) 412 cmpUnitAI.SetupTradeRoute(cmd.target); 413 } 414 break; 415 416 case "select-trading-goods": 417 var cmpTrader = Engine.QueryInterface(cmd.trader, IID_Trader); 418 cmpTrader.SetPreferredGoods(cmd.preferredGoods); 419 break; 420 407 421 case "barter": 408 422 var cmpBarter = Engine.QueryInterface(SYSTEM_ENTITY, IID_Barter); 409 423 cmpBarter.ExchangeResources(playerEnt, cmd.sell, cmd.buy, cmd.amount); -
binaries/data/mods/public/simulation/components/GuiInterface.js
185 185 }; 186 186 } 187 187 188 var cmpTrader = Engine.QueryInterface(ent, IID_Trader); 189 if (cmpTrader) 190 { 191 ret.trader = { 192 "goods": cmpTrader.GetGoods(), 193 "preferredGoods": cmpTrader.GetPreferredGoods() 194 }; 195 } 196 188 197 var cmpFoundation = Engine.QueryInterface(ent, IID_Foundation); 189 198 if (cmpFoundation) 190 199 { … … 680 689 return 0; 681 690 }; 682 691 692 GuiInterface.prototype.GetTradingDetails = function(player, data) 693 { 694 var cmpEntityTrader = Engine.QueryInterface(data.trader, IID_Trader); 695 var cmpEntityIdentity = Engine.QueryInterface(data.trader, IID_Identity); 696 var cmpEntityPlayer = QueryOwnerInterface(data.trader, IID_Player); 697 var entityPlayerId = cmpEntityPlayer.GetPlayerID(); 698 var cmpTargetIdentity = Engine.QueryInterface(data.target, IID_Identity); 699 var cmpTargetPlayer = QueryOwnerInterface(data.target, IID_Player); 700 var targetPlayerId = cmpTargetPlayer.GetPlayerID(); 701 702 var ownershipSuitableForTrading = (entityPlayerId == targetPlayerId) || cmpEntityPlayer.IsAlly(targetPlayerId); 703 var landTradingPossible = cmpEntityIdentity.HasClass("Organic") && cmpTargetIdentity.HasClass("Market"); 704 var seaTradingPossible = cmpEntityIdentity.HasClass("Ship") && cmpTargetIdentity.HasClass("SeaMarket"); 705 if (!(ownershipSuitableForTrading && cmpEntityTrader && (landTradingPossible || seaTradingPossible))) 706 return null; 707 var firstMarket = cmpEntityTrader.GetFirstMarket(); 708 var secondMarket = cmpEntityTrader.GetSecondMarket(); 709 var result = null; 710 if (data.target == firstMarket) 711 result = {"type": "is first", "hasBothMarkets": cmpEntityTrader.HasBothMarkets() }; 712 else if (data.target == secondMarket) 713 result = {"type": "is second"}; 714 else if (firstMarket == null) 715 result = {"type": "set first"}; 716 else if (secondMarket == null) 717 result = { 718 "type": "set second", 719 "gain": cmpEntityTrader.CalculateGain(firstMarket, data.target), 720 "goods": cmpEntityTrader.GetPreferredGoods() 721 }; 722 // Else both markets are not null and target is different from them 723 else 724 result = {"type": "set first"}; 725 return result; 726 }; 727 683 728 GuiInterface.prototype.SetPathfinderDebugOverlay = function(player, enabled) 684 729 { 685 730 var cmpPathfinder = Engine.QueryInterface(SYSTEM_ENTITY, IID_Pathfinder); … … 738 783 "GetFoundationSnapData": 1, 739 784 "PlaySound": 1, 740 785 "FindIdleUnit": 1, 786 "GetTradingDetails": 1, 741 787 742 788 "SetPathfinderDebugOverlay": 1, 743 789 "SetObstructionDebugOverlay": 1, -
binaries/data/mods/public/simulation/components/interfaces/Trader.js
1 Engine.RegisterInterface("Trader"); -
binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
12 12 Engine.LoadComponentScript("interfaces/ResourceGatherer.js"); 13 13 Engine.LoadComponentScript("interfaces/ResourceSupply.js"); 14 14 Engine.LoadComponentScript("interfaces/TrainingQueue.js"); 15 Engine.LoadComponentScript("interfaces/Trader.js") 15 16 Engine.LoadComponentScript("interfaces/Timer.js"); 16 17 Engine.LoadComponentScript("interfaces/StatisticsTracker.js"); 17 18 Engine.LoadComponentScript("interfaces/UnitAI.js"); -
binaries/data/mods/public/simulation/components/Identity.js
103 103 "<value>Slave</value>" + 104 104 "<value>CitizenSoldier</value>" + 105 105 "<value>Trade</value>" + 106 "<value>Market</value>" + 107 "<value>SeaMarket</value>" + 106 108 "<value>Warship</value>" + 107 109 "<value>SeaCreature</value>" + 108 110 "<value>ForestPlant</value>" + -
binaries/data/mods/public/simulation/components/UnitAI.js
341 341 return; 342 342 } 343 343 }, 344 344 345 "Order.Trade": function(msg) { 346 if (this.MoveToMarket(this.order.data.firstMarket)) 347 { 348 // We've started walking to the first market 349 this.SetNextState("INDIVIDUAL.TRADE.APPROACHINGFIRSTMARKET"); 350 } 351 }, 352 345 353 "Order.Repair": function(msg) { 346 354 // Try to move within range 347 355 if (this.MoveToTargetRange(this.order.data.target, IID_Builder)) … … 1048 1056 }, 1049 1057 }, 1050 1058 1059 "TRADE": { 1060 "Attacked": function(msg) { 1061 // Ignore attack 1062 // TODO: Inform player 1063 }, 1064 1065 "APPROACHINGFIRSTMARKET": { 1066 "enter": function () { 1067 this.SelectAnimation("move"); 1068 }, 1069 1070 "MoveCompleted": function() { 1071 this.PerformTradeAndMoveToNextMarket(this.order.data.firstMarket, this.order.data.secondMarket, "INDIVIDUAL.TRADE.APPROACHINGSECONDMARKET"); 1072 }, 1073 }, 1074 1075 "APPROACHINGSECONDMARKET": { 1076 "enter": function () { 1077 this.SelectAnimation("move"); 1078 }, 1079 1080 "MoveCompleted": function() { 1081 this.order.data.firstPass = false; 1082 this.PerformTradeAndMoveToNextMarket(this.order.data.secondMarket, this.order.data.firstMarket, "INDIVIDUAL.TRADE.APPROACHINGFIRSTMARKET"); 1083 }, 1084 }, 1085 }, 1086 1051 1087 "REPAIR": { 1052 1088 "APPROACHING": { 1053 1089 "enter": function () { … … 2364 2400 this.AddOrder("ReturnResource", { "target": target }, queued); 2365 2401 }; 2366 2402 2403 UnitAI.prototype.SetupTradeRoute = function(target, queued) 2404 { 2405 if (!this.CanTrade(target)) 2406 { 2407 this.WalkToTarget(target, queued); 2408 return; 2409 } 2410 2411 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2412 var marketsChanged = cmpTrader.SetTargetMarket(target); 2413 if (marketsChanged) 2414 { 2415 if (cmpTrader.HasBothMarkets()) 2416 this.AddOrder("Trade", { "firstMarket": cmpTrader.GetFirstMarket(), "secondMarket": cmpTrader.GetSecondMarket() }, queued); 2417 else 2418 this.WalkToTarget(cmpTrader.GetFirstMarket(), queued); 2419 } 2420 } 2421 2422 UnitAI.prototype.MoveToMarket = function(targetMarket) 2423 { 2424 if (this.MoveToTarget(targetMarket)) 2425 { 2426 // We've started walking to the market 2427 return true; 2428 } 2429 else 2430 { 2431 // We can't reach the market. 2432 // Give up. 2433 this.StopMoving(); 2434 this.FinishOrder(); 2435 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2436 cmpTrader.DropGoods(); 2437 return false; 2438 } 2439 } 2440 2441 UnitAI.prototype.PerformTradeAndMoveToNextMarket = function(currentMarket, nextMarket, nextFsmStateName) 2442 { 2443 if (this.CheckTargetRange(currentMarket, IID_Trader)) 2444 { 2445 this.PerformTrade(); 2446 if (this.MoveToMarket(nextMarket)) 2447 { 2448 // We've started walking to the next market 2449 this.SetNextState(nextFsmStateName); 2450 } 2451 } 2452 else 2453 { 2454 // Market don't reached, give up 2455 this.FinishOrder(); 2456 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2457 cmpTrader.DropGoods(); 2458 } 2459 } 2460 2461 UnitAI.prototype.PerformTrade = function() 2462 { 2463 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2464 cmpTrader.PerformTrade(); 2465 } 2466 2367 2467 UnitAI.prototype.Repair = function(target, autocontinue, queued) 2368 2468 { 2369 2469 if (!this.CanRepair(target)) … … 2583 2683 return true; 2584 2684 }; 2585 2685 2686 UnitAI.prototype.CanTrade = function(target) 2687 { 2688 // Formation controllers should always respond to commands 2689 // (then the individual units can make up their own minds) 2690 if (this.IsFormationController()) 2691 return true; 2692 2693 // Verify that we're able to respond to Trade commands 2694 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2695 if (!cmpTrader) 2696 return false; 2697 2698 return true; 2699 } 2700 2586 2701 UnitAI.prototype.CanRepair = function(target) 2587 2702 { 2588 2703 // Formation controllers should always respond to commands -
binaries/data/mods/public/simulation/components/Looter.js
21 21 } 22 22 var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); 23 23 cmpPlayer.AddResources(cmpLoot.GetResources()); 24 25 // If target entity has trader component, add carried goods to loot too 26 var cmpTrader = Engine.QueryInterface(targetEntity, IID_Trader); 27 if (cmpTrader) 28 { 29 var carriedGoods = cmpTrader.GetGoods(); 30 if (carriedGoods.amount > 0) 31 { 32 // Convert from {type:<type>,amount:<amount>} to {<type>:<amount>} 33 var resourcesToAdd = {}; 34 resourcesToAdd[carriedGoods.type] = carriedGoods.amount; 35 cmpPlayer.AddResources(resourcesToAdd); 36 } 37 } 24 38 } 25 39 26 40 Engine.RegisterComponentType(IID_Looter, "Looter", Looter); -
binaries/data/mods/public/simulation/components/Trader.js
1 // This constant used to adjust gain value depending on distance 2 const DISTANCE_FACTOR = 1 / 50; 3 4 // Additional gain for trading perfromed between markets of different players, in percents 5 const INTERNATIONAL_TRADING_ADDITION = 50; 6 // Additional gain for ships for each garrisoned trader, in percents 7 const GARRISONED_TRADER_ADDITION = 20; 8 9 function Trader() {} 10 11 Trader.prototype.Schema = 12 "<a:help>Lets the unit generate resouces while moving between markets (or docks in case of water trading).</a:help>" + 13 "<a:example>" + 14 "<MaxDistance>2.0</MaxDistance>" + 15 "<GainMultiplier>1.0</GainMultiplier>" + 16 "</a:example>" + 17 "<element name='MaxDistance' a:help='Max distance from market when performing deal'>" + 18 "<ref name='positiveDecimal'/>" + 19 "</element>" + 20 "<optional>" + 21 "<element name='GainMultiplier' a:help='Additional gain multiplier'>" + 22 "<ref name='positiveDecimal'/>" + 23 "</element>" + 24 "</optional>"; 25 26 Trader.prototype.Init = function() 27 { 28 this.firstMarket = null; 29 this.secondMarket = null; 30 // Gain from one pass between markets 31 this.gain = null; 32 // Selected resource for trading 33 this.preferredGoods = "metal"; 34 // Currently carried goods 35 this.goods = { "type": null, "amount": 0 }; 36 } 37 38 Trader.prototype.CalculateGain = function(firstMarket, secondMarket) 39 { 40 var cmpFirstMarketPosition = Engine.QueryInterface(firstMarket, IID_Position); 41 var cmpSecondMarketPosition = Engine.QueryInterface(secondMarket, IID_Position); 42 var firstMarketPosition = cmpFirstMarketPosition.GetPosition2D(); 43 var secondMarketPosition = cmpSecondMarketPosition.GetPosition2D(); 44 // Calculate ordinary Euclidean distance between markets. 45 // We don't use pathfinder, because ordinary distance looks more fair. 46 var distance = Math.sqrt(Math.pow(firstMarketPosition.x - secondMarketPosition.x, 2) + Math.pow(firstMarketPosition.y - secondMarketPosition.y, 2)); 47 // We calculate gain as square of distance to encourage trading between remote markets 48 var gain = Math.round(Math.pow(distance * DISTANCE_FACTOR, 2)); 49 // If markets belongs to different players, multiple gain to INTERNATIONAL_TRADING_MULTIPLIER 50 var cmpFirstMarketOwnership = Engine.QueryInterface(firstMarket, IID_Ownership); 51 var cmpSecondMarketOwnership = Engine.QueryInterface(secondMarket, IID_Ownership); 52 if (cmpFirstMarketOwnership.GetOwner() != cmpSecondMarketOwnership.GetOwner()) 53 gain *= 1 + INTERNATIONAL_TRADING_ADDITION / 100; 54 // For ship increase gain for each garrisoned trader 55 var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); 56 if (cmpIdentity.HasClass("Ship")) 57 { 58 var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); 59 if (cmpGarrisonHolder) 60 { 61 var garrisonedTradersCount = 0; 62 for each (var entity in cmpGarrisonHolder.GetEntities()) 63 { 64 var cmpGarrisonedUnitTrader = Engine.QueryInterface(entity, IID_Trader); 65 if (cmpGarrisonedUnitTrader) 66 garrisonedTradersCount++; 67 } 68 gain *= 1 + GARRISONED_TRADER_ADDITION * garrisonedTradersCount / 100; 69 } 70 } 71 if (this.template.GainMultiplier) 72 gain *= this.template.GainMultiplier; 73 gain = Math.round(gain); 74 return gain; 75 } 76 77 // Set target as target market. 78 // Return true if at least one of markets was changed. 79 Trader.prototype.SetTargetMarket = function(target) 80 { 81 var marketsChanged = false; 82 // If we already have both markets - drop them 83 // and use the target as first market 84 if (this.secondMarket) 85 { 86 this.firstMarket = target; 87 this.secondMarket = null; 88 marketsChanged = true; 89 } 90 // If we have only one market and target is different from it, 91 // set the target as second one 92 else if (this.firstMarket) 93 { 94 if (target != this.firstMarket) 95 { 96 this.secondMarket = target; 97 this.gain = this.CalculateGain(this.firstMarket, this.secondMarket); 98 marketsChanged = true; 99 } 100 } 101 // Else we don't have target markets at all, 102 // set the target as first market 103 else 104 { 105 this.firstMarket = target; 106 marketsChanged = true; 107 } 108 if (marketsChanged) 109 this.DropGoods(); 110 return marketsChanged; 111 } 112 113 Trader.prototype.GetFirstMarket = function() 114 { 115 return this.firstMarket; 116 } 117 118 Trader.prototype.GetSecondMarket = function() 119 { 120 return this.secondMarket; 121 } 122 123 Trader.prototype.HasBothMarkets = function() 124 { 125 return (this.firstMarket != null) && (this.secondMarket != null); 126 } 127 128 Trader.prototype.GetPreferredGoods = function() 129 { 130 return this.preferredGoods; 131 } 132 133 Trader.prototype.SetPreferredGoods = function(preferredGoods) 134 { 135 this.preferredGoods = preferredGoods; 136 } 137 138 Trader.prototype.PerformTrade = function() 139 { 140 if (this.goods.amount > 0) 141 { 142 var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); 143 cmpPlayer.AddResource(this.goods.type, this.goods.amount); 144 } 145 this.goods.type = this.preferredGoods; 146 this.goods.amount = this.gain; 147 } 148 149 Trader.prototype.GetGoods = function() 150 { 151 return this.goods; 152 } 153 154 // Called when the trader stops trading 155 Trader.prototype.DropGoods = function() 156 { 157 this.goods.amount = 0; 158 } 159 160 // Get range in which deals with market are available, 161 // i.e. trader should be in no more than MaxDistance from market 162 // to be able to trade with it. 163 Trader.prototype.GetRange = function() 164 { 165 return { "min": 0, "max": +this.template.MaxDistance }; 166 } 167 168 Engine.RegisterComponentType(IID_Trader, "Trader", Trader); 169 -
binaries/data/mods/public/simulation/templates/template_unit_support_trader.xml
18 18 <GenericName>Trader</GenericName> 19 19 <Rollover>Trade was a very important part of ancient civilisation - 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.</Rollover> 20 20 <Tooltip>Trades resources between allied Markets.</Tooltip> 21 <Classes datatype="tokens">Trade</Classes>22 21 </Identity> 22 <Trader> 23 <MaxDistance>2.0</MaxDistance> 24 </Trader> 23 25 <UnitMotion> 24 26 <WalkSpeed>7.0</WalkSpeed> 25 27 </UnitMotion> -
binaries/data/mods/public/simulation/templates/template_structure_economic_market.xml
24 24 <Identity> 25 25 <GenericName>Market</GenericName> 26 26 <Tooltip>Create Trade units and Barter resources.</Tooltip> 27 <Classes datatype="tokens">Town BarterMarket</Classes>27 <Classes datatype="tokens">Town Market BarterMarket</Classes> 28 28 <Icon>structures/market.png</Icon> 29 29 </Identity> 30 30 <Obstruction> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_merchant.xml
38 38 <BarHeight>0.5</BarHeight> 39 39 <HeightOffset>6.0</HeightOffset> 40 40 </StatusBars> 41 <Trader> 42 <MaxDistance>10.0</MaxDistance> 43 </Trader> 41 44 <UnitMotion> 42 45 <WalkSpeed>10.5</WalkSpeed> 43 46 </UnitMotion> -
binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml
27 27 <Identity> 28 28 <GenericName>Dock</GenericName> 29 29 <Tooltip>Build upon a shoreline to construct naval vessels and to open sea trade.</Tooltip> 30 <Classes datatype="tokens">Town </Classes>30 <Classes datatype="tokens">Town Market SeaMarket</Classes> 31 31 <Icon>structures/dock.png</Icon> 32 32 </Identity> 33 33 <Obstruction> -
binaries/data/mods/public/art/textures/cursors/action-setup-trade-route.txt
1 1 1 2 1 1