Ticket #30: trading_2012_03_06.diff
File trading_2012_03_06.diff, 36.0 KB (added by , 12 years ago) |
---|
-
source/gui/CGUI.cpp
445 445 AddObjectType("input", &CInput::ConstructObject); 446 446 AddObjectType("list", &CList::ConstructObject); 447 447 AddObjectType("dropdown", &CDropDown::ConstructObject); 448 AddObjectType("tooltip", &CTooltip::ConstructObject); 448 449 } 449 450 450 451 void CGUI::Draw() -
source/gui/CTooltip.cpp
36 36 AddSetting(GUIST_float, "maxwidth"); 37 37 AddSetting(GUIST_CPos, "offset"); 38 38 AddSetting(GUIST_EVAlign, "anchor"); 39 AddSetting(GUIST_bool, "independent"); 39 40 40 41 // If the tooltip is just a reference to another object: 41 42 AddSetting(GUIST_CStr, "use_object"); … … 84 85 85 86 CPos mousepos, offset; 86 87 EVAlign anchor; 87 GUI<CPos>::GetSetting(this, "_mousepos", mousepos); 88 bool independent; 89 GUI<bool>::GetSetting(this, "independent", independent); 90 if (independent) 91 mousepos = GetMousePos(); 92 else 93 GUI<CPos>::GetSetting(this, "_mousepos", mousepos); 88 94 GUI<CPos>::GetSetting(this, "offset", offset); 89 95 GUI<EVAlign>::GetSetting(this, "anchor", anchor); 90 96 -
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" independent="true" 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 += " Gain: " + tradingDetails.gain + " " + tradingDetails.goods + ". 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. Gain: " + tradingDetails.gain + " " + tradingDetails.goods + "." + " 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}; … … 963 1008 updateBuildingPlacementPreview(); 964 1009 break; 965 1010 } 966 967 1011 break; 968 1012 969 1013 } … … 1009 1053 Engine.GuiInterfaceCall("PlaySound", { "name": "order_gather", "entity": selection[0] }); 1010 1054 return true; 1011 1055 1056 case "setup-trade-route": 1057 Engine.PostNetworkCommand({"type": "setup-trade-route", "entities": selection, "target": action.target}); 1058 return true; 1059 1012 1060 case "garrison": 1013 1061 Engine.PostNetworkCommand({"type": "garrison", "entities": selection, "target": action.target, "queued": queued}); 1014 1062 // TODO: Play a sound? … … 1101 1149 inputState = INPUT_BUILDING_PLACEMENT; 1102 1150 } 1103 1151 1152 // Called by GUI when user changes preferred trading goods 1153 function selectTradingPreferredGoods(data) 1154 { 1155 Engine.PostNetworkCommand({"type": "select-trading-goods", "trader": data.trader, "preferredGoods": data.preferredGoods }); 1156 } 1157 1104 1158 // Called by GUI when user clicks exchange resources button 1105 1159 function exchangeResources(command) 1106 1160 { 1107 1161 Engine.PostNetworkCommand({"type": "barter", "sell": command.sell, "buy": command.buy, "amount": command.amount}); 1108 1162 } 1109 1163 1110 1111 1164 // Batch training: 1112 1165 // When the user shift-clicks, we set these variables and switch to INPUT_BATCHTRAINING 1113 1166 // 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 if (!cmpEntityTrader || !cmpEntityTrader.CanTrade(data.target)) 696 return null; 697 var firstMarket = cmpEntityTrader.GetFirstMarket(); 698 var secondMarket = cmpEntityTrader.GetSecondMarket(); 699 var result = null; 700 if (data.target == firstMarket) 701 { 702 result = { 703 "type": "is first", 704 "goods": cmpEntityTrader.GetPreferredGoods(), 705 "hasBothMarkets": cmpEntityTrader.HasBothMarkets() 706 }; 707 if (cmpEntityTrader.HasBothMarkets()) 708 result.gain = cmpEntityTrader.GetGain(); 709 } 710 else if (data.target == secondMarket) 711 { 712 result = { 713 "type": "is second", 714 "gain": cmpEntityTrader.GetGain(), 715 "goods": cmpEntityTrader.GetPreferredGoods() 716 }; 717 } 718 else if (firstMarket == null) 719 { 720 result = {"type": "set first"}; 721 } 722 else if (secondMarket == null) 723 { 724 result = { 725 "type": "set second", 726 "gain": cmpEntityTrader.CalculateGain(firstMarket, data.target), 727 "goods": cmpEntityTrader.GetPreferredGoods() 728 }; 729 } 730 else 731 { 732 // Else both markets are not null and target is different from them 733 result = {"type": "set first"}; 734 } 735 return result; 736 }; 737 683 738 GuiInterface.prototype.SetPathfinderDebugOverlay = function(player, enabled) 684 739 { 685 740 var cmpPathfinder = Engine.QueryInterface(SYSTEM_ENTITY, IID_Pathfinder); … … 738 793 "GetFoundationSnapData": 1, 739 794 "PlaySound": 1, 740 795 "FindIdleUnit": 1, 796 "GetTradingDetails": 1, 741 797 742 798 "SetPathfinderDebugOverlay": 1, 743 799 "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/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)) … … 1062 1070 }, 1063 1071 }, 1064 1072 1073 "TRADE": { 1074 "Attacked": function(msg) { 1075 // Ignore attack 1076 // TODO: Inform player 1077 }, 1078 1079 "APPROACHINGFIRSTMARKET": { 1080 "enter": function () { 1081 this.SelectAnimation("move"); 1082 }, 1083 1084 "MoveCompleted": function() { 1085 this.PerformTradeAndMoveToNextMarket(this.order.data.firstMarket, this.order.data.secondMarket, "INDIVIDUAL.TRADE.APPROACHINGSECONDMARKET"); 1086 }, 1087 }, 1088 1089 "APPROACHINGSECONDMARKET": { 1090 "enter": function () { 1091 this.SelectAnimation("move"); 1092 }, 1093 1094 "MoveCompleted": function() { 1095 this.order.data.firstPass = false; 1096 this.PerformTradeAndMoveToNextMarket(this.order.data.secondMarket, this.order.data.firstMarket, "INDIVIDUAL.TRADE.APPROACHINGFIRSTMARKET"); 1097 }, 1098 }, 1099 }, 1100 1065 1101 "REPAIR": { 1066 1102 "APPROACHING": { 1067 1103 "enter": function () { … … 2369 2405 this.AddOrder("ReturnResource", { "target": target }, queued); 2370 2406 }; 2371 2407 2408 UnitAI.prototype.SetupTradeRoute = function(target, queued) 2409 { 2410 if (!this.CanTrade(target)) 2411 { 2412 this.WalkToTarget(target, queued); 2413 return; 2414 } 2415 2416 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2417 var marketsChanged = cmpTrader.SetTargetMarket(target); 2418 if (marketsChanged) 2419 { 2420 if (cmpTrader.HasBothMarkets()) 2421 this.AddOrder("Trade", { "firstMarket": cmpTrader.GetFirstMarket(), "secondMarket": cmpTrader.GetSecondMarket() }, queued); 2422 else 2423 this.WalkToTarget(cmpTrader.GetFirstMarket(), queued); 2424 } 2425 } 2426 2427 UnitAI.prototype.MoveToMarket = function(targetMarket) 2428 { 2429 if (this.MoveToTarget(targetMarket)) 2430 { 2431 // We've started walking to the market 2432 return true; 2433 } 2434 else 2435 { 2436 // We can't reach the market. 2437 // Give up. 2438 this.StopMoving(); 2439 this.StopTrading(); 2440 return false; 2441 } 2442 } 2443 2444 UnitAI.prototype.PerformTradeAndMoveToNextMarket = function(currentMarket, nextMarket, nextFsmStateName) 2445 { 2446 // Check that the ownership of trader and market is still suitable for trading 2447 var cmpEntityPlayer = QueryOwnerInterface(this.entity, IID_Player); 2448 var entityPlayerId = cmpEntityPlayer.GetPlayerID(); 2449 var cmpTargetPlayer = QueryOwnerInterface(currentMarket, IID_Player); 2450 if (cmpTargetPlayer) 2451 { 2452 var targetPlayerId = cmpTargetPlayer.GetPlayerID(); 2453 var ownershipSuitableForTrading = (entityPlayerId == targetPlayerId) || cmpEntityPlayer.IsAlly(targetPlayerId); 2454 } 2455 2456 if (cmpTargetPlayer && ownershipSuitableForTrading && this.CheckTargetRange(currentMarket, IID_Trader)) 2457 { 2458 this.PerformTrade(); 2459 if (this.MoveToMarket(nextMarket)) 2460 { 2461 // We've started walking to the next market 2462 this.SetNextState(nextFsmStateName); 2463 } 2464 } 2465 else 2466 { 2467 // Market wasn't reached, give up 2468 this.StopTrading(); 2469 } 2470 } 2471 2472 UnitAI.prototype.PerformTrade = function() 2473 { 2474 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2475 cmpTrader.PerformTrade(); 2476 } 2477 2478 UnitAI.prototype.StopTrading = function() 2479 { 2480 this.FinishOrder(); 2481 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2482 cmpTrader.StopTrading(); 2483 } 2484 2372 2485 UnitAI.prototype.Repair = function(target, autocontinue, queued) 2373 2486 { 2374 2487 if (!this.CanRepair(target)) … … 2588 2701 return true; 2589 2702 }; 2590 2703 2704 UnitAI.prototype.CanTrade = function(target) 2705 { 2706 // Formation controllers should always respond to commands 2707 // (then the individual units can make up their own minds) 2708 if (this.IsFormationController()) 2709 return true; 2710 2711 // Verify that we're able to respond to Trade commands 2712 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2713 if (!cmpTrader || !cmpTrader.CanTrade(target)) 2714 return false; 2715 2716 return true; 2717 } 2718 2591 2719 UnitAI.prototype.CanRepair = function(target) 2592 2720 { 2593 2721 // Formation controllers should always respond to commands -
binaries/data/mods/public/simulation/components/Identity.js
49 49 "</element>" + 50 50 "</optional>" + 51 51 "<optional>" + 52 "<element name='Classes' a:help='Optional list of space-separated classes applying to this entity. Choices include: Unit, Infantry, Melee, Cavalry, Ranged, Mechanical, Ship, Siege, Champion, Hero, Elephant, Chariot, Mercenary, Spear, Sword, Bow, Javelin, Sling, Support, Animal, Organic, Structure, Civic, CivCentre, Economic, Defensive, Gates, Wall, BarterMarket, Village, Town, City, ConquestCritical, Worker, Female, Healer, Slave, CitizenSoldier, Trade, Warship, SeaCreature, ForestPlant, DropsiteFood, DropsiteWood, DropsiteStone, DropsiteMetal'>" +52 "<element name='Classes' a:help='Optional list of space-separated classes applying to this entity. Choices include: Unit, Infantry, Melee, Cavalry, Ranged, Mechanical, Ship, Siege, Champion, Hero, Elephant, Chariot, Mercenary, Spear, Sword, Bow, Javelin, Sling, Support, Animal, Organic, Structure, Civic, CivCentre, Economic, Defensive, Gates, Wall, BarterMarket, Village, Town, City, ConquestCritical, Worker, Female, Healer, Slave, CitizenSoldier, Trade, Market, SeaMarket, Warship, SeaCreature, ForestPlant, DropsiteFood, DropsiteWood, DropsiteStone, DropsiteMetal'>" + 53 53 "<attribute name='datatype'>" + 54 54 "<value>tokens</value>" + 55 55 "</attribute>" + -
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 // Array of resource names 10 const RESOURCES = ["food", "wood", "stone", "metal"]; 11 12 function Trader() {} 13 14 Trader.prototype.Schema = 15 "<a:help>Lets the unit generate resouces while moving between markets (or docks in case of water trading).</a:help>" + 16 "<a:example>" + 17 "<MaxDistance>2.0</MaxDistance>" + 18 "<GainMultiplier>1.0</GainMultiplier>" + 19 "</a:example>" + 20 "<element name='MaxDistance' a:help='Max distance from market when performing deal'>" + 21 "<ref name='positiveDecimal'/>" + 22 "</element>" + 23 "<optional>" + 24 "<element name='GainMultiplier' a:help='Additional gain multiplier'>" + 25 "<ref name='positiveDecimal'/>" + 26 "</element>" + 27 "</optional>"; 28 29 Trader.prototype.Init = function() 30 { 31 this.firstMarket = null; 32 this.secondMarket = null; 33 // Gain from one pass between markets 34 this.gain = null; 35 // Selected resource for trading 36 this.preferredGoods = "metal"; 37 // Currently carried goods 38 this.goods = { "type": null, "amount": 0 }; 39 } 40 41 Trader.prototype.CalculateGain = function(firstMarket, secondMarket) 42 { 43 var cmpFirstMarketPosition = Engine.QueryInterface(firstMarket, IID_Position); 44 var cmpSecondMarketPosition = Engine.QueryInterface(secondMarket, IID_Position); 45 if (!cmpFirstMarketPosition || !cmpFirstMarketPosition.IsInWorld() || !cmpSecondMarketPosition || !cmpSecondMarketPosition.IsInWorld()) 46 return null; 47 var firstMarketPosition = cmpFirstMarketPosition.GetPosition2D(); 48 var secondMarketPosition = cmpSecondMarketPosition.GetPosition2D(); 49 // Calculate ordinary Euclidean distance between markets. 50 // We don't use pathfinder, because ordinary distance looks more fair. 51 var distance = Math.sqrt(Math.pow(firstMarketPosition.x - secondMarketPosition.x, 2) + Math.pow(firstMarketPosition.y - secondMarketPosition.y, 2)); 52 // We calculate gain as square of distance to encourage trading between remote markets 53 var gain = Math.round(Math.pow(distance * DISTANCE_FACTOR, 2)); 54 // If markets belongs to different players, multiple gain to INTERNATIONAL_TRADING_MULTIPLIER 55 var cmpFirstMarketOwnership = Engine.QueryInterface(firstMarket, IID_Ownership); 56 var cmpSecondMarketOwnership = Engine.QueryInterface(secondMarket, IID_Ownership); 57 if (cmpFirstMarketOwnership.GetOwner() != cmpSecondMarketOwnership.GetOwner()) 58 gain *= 1 + INTERNATIONAL_TRADING_ADDITION / 100; 59 // For ship increase gain for each garrisoned trader 60 var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); 61 if (cmpIdentity.HasClass("Ship")) 62 { 63 var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); 64 if (cmpGarrisonHolder) 65 { 66 var garrisonedTradersCount = 0; 67 for each (var entity in cmpGarrisonHolder.GetEntities()) 68 { 69 var cmpGarrisonedUnitTrader = Engine.QueryInterface(entity, IID_Trader); 70 if (cmpGarrisonedUnitTrader) 71 garrisonedTradersCount++; 72 } 73 gain *= 1 + GARRISONED_TRADER_ADDITION * garrisonedTradersCount / 100; 74 } 75 } 76 if (this.template.GainMultiplier) 77 gain *= this.template.GainMultiplier; 78 gain = Math.round(gain); 79 return gain; 80 } 81 82 Trader.prototype.GetGain = function() 83 { 84 return this.gain; 85 } 86 87 // Set target as target market. 88 // Return true if at least one of markets was changed. 89 Trader.prototype.SetTargetMarket = function(target) 90 { 91 // Check that target is a market 92 var cmpTargetIdentity = Engine.QueryInterface(target, IID_Identity); 93 if (!cmpTargetIdentity) 94 return false; 95 if (!cmpTargetIdentity.HasClass("Market") && !cmpTargetIdentity.HasClass("SeaMarket")) 96 return false; 97 var marketsChanged = false; 98 if (this.secondMarket) 99 { 100 // If we already have both markets - drop them 101 // and use the target as first market 102 this.firstMarket = target; 103 this.secondMarket = null; 104 marketsChanged = true; 105 } 106 else if (this.firstMarket) 107 { 108 // If we have only one market and target is different from it, 109 // set the target as second one 110 if (target != this.firstMarket) 111 { 112 this.secondMarket = target; 113 this.gain = this.CalculateGain(this.firstMarket, this.secondMarket); 114 marketsChanged = true; 115 } 116 } 117 else 118 { 119 // Else we don't have target markets at all, 120 // set the target as first market 121 this.firstMarket = target; 122 marketsChanged = true; 123 } 124 if (marketsChanged) 125 { 126 // Drop carried goods 127 this.goods.amount = 0; 128 } 129 return marketsChanged; 130 } 131 132 Trader.prototype.GetFirstMarket = function() 133 { 134 return this.firstMarket; 135 } 136 137 Trader.prototype.GetSecondMarket = function() 138 { 139 return this.secondMarket; 140 } 141 142 Trader.prototype.HasBothMarkets = function() 143 { 144 return (this.firstMarket != null) && (this.secondMarket != null); 145 } 146 147 Trader.prototype.GetPreferredGoods = function() 148 { 149 return this.preferredGoods; 150 } 151 152 Trader.prototype.SetPreferredGoods = function(preferredGoods) 153 { 154 // Check that argument is a correct resource name 155 if (RESOURCES.indexOf(preferredGoods) == -1) 156 return; 157 this.preferredGoods = preferredGoods; 158 } 159 160 Trader.prototype.CanTrade = function(target) 161 { 162 var cmpTraderIdentity = Engine.QueryInterface(this.entity, IID_Identity); 163 var cmpTargetIdentity = Engine.QueryInterface(target, IID_Identity); 164 // Check that the target exists 165 if (!cmpTargetIdentity) 166 return false; 167 var landTradingPossible = cmpTraderIdentity.HasClass("Organic") && cmpTargetIdentity.HasClass("Market"); 168 var seaTradingPossible = cmpTraderIdentity.HasClass("Ship") && cmpTargetIdentity.HasClass("SeaMarket"); 169 if (!landTradingPossible && !seaTradingPossible) 170 return false; 171 172 var cmpTraderPlayer = QueryOwnerInterface(this.entity, IID_Player); 173 var traderPlayerId = cmpTraderPlayer.GetPlayerID(); 174 var cmpTargetPlayer = QueryOwnerInterface(target, IID_Player); 175 var targetPlayerId = cmpTargetPlayer.GetPlayerID(); 176 var ownershipSuitableForTrading = (traderPlayerId == targetPlayerId) || cmpTraderPlayer.IsAlly(targetPlayerId); 177 if (!ownershipSuitableForTrading) 178 return false; 179 return true; 180 } 181 182 Trader.prototype.PerformTrade = function() 183 { 184 if (this.goods.amount > 0) 185 { 186 var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); 187 cmpPlayer.AddResource(this.goods.type, this.goods.amount); 188 } 189 this.goods.type = this.preferredGoods; 190 this.goods.amount = this.gain; 191 } 192 193 Trader.prototype.GetGoods = function() 194 { 195 return this.goods; 196 } 197 198 Trader.prototype.StopTrading = function() 199 { 200 // Drop carried goods 201 this.goods.amount = 0; 202 // Reset markets 203 this.firstMarket = null; 204 this.secondMarket = null; 205 } 206 207 // Get range in which deals with market are available, 208 // i.e. trader should be in no more than MaxDistance from market 209 // to be able to trade with it. 210 Trader.prototype.GetRange = function() 211 { 212 return { "min": 0, "max": +this.template.MaxDistance }; 213 } 214 215 Engine.RegisterComponentType(IID_Trader, "Trader", Trader); 216 -
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
39 39 <BarHeight>0.5</BarHeight> 40 40 <HeightOffset>6.0</HeightOffset> 41 41 </StatusBars> 42 <Trader> 43 <MaxDistance>10.0</MaxDistance> 44 </Trader> 42 45 <UnitMotion> 43 46 <WalkSpeed>10.5</WalkSpeed> 44 47 </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