Ticket #30: trading_2012_02_24.diff
File trading_2012_02_24.diff, 35.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
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 += " 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}; … … 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 { 712 result = { 713 "type": "is first", 714 "goods": cmpEntityTrader.GetPreferredGoods(), 715 "hasBothMarkets": cmpEntityTrader.HasBothMarkets() 716 }; 717 if (cmpEntityTrader.HasBothMarkets()) 718 result.gain = cmpEntityTrader.GetGain(); 719 } 720 else if (data.target == secondMarket) 721 { 722 result = { 723 "type": "is second", 724 "gain": cmpEntityTrader.GetGain(), 725 "goods": cmpEntityTrader.GetPreferredGoods() 726 }; 727 } 728 else if (firstMarket == null) 729 { 730 result = {"type": "set first"}; 731 } 732 else if (secondMarket == null) 733 { 734 result = { 735 "type": "set second", 736 "gain": cmpEntityTrader.CalculateGain(firstMarket, data.target), 737 "goods": cmpEntityTrader.GetPreferredGoods() 738 }; 739 } 740 else 741 { 742 // Else both markets are not null and target is different from them 743 result = {"type": "set first"}; 744 } 745 return result; 746 }; 747 683 748 GuiInterface.prototype.SetPathfinderDebugOverlay = function(player, enabled) 684 749 { 685 750 var cmpPathfinder = Engine.QueryInterface(SYSTEM_ENTITY, IID_Pathfinder); … … 738 803 "GetFoundationSnapData": 1, 739 804 "PlaySound": 1, 740 805 "FindIdleUnit": 1, 806 "GetTradingDetails": 1, 741 807 742 808 "SetPathfinderDebugOverlay": 1, 743 809 "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)) … … 1063 1071 }, 1064 1072 }, 1065 1073 1074 "TRADE": { 1075 "Attacked": function(msg) { 1076 // Ignore attack 1077 // TODO: Inform player 1078 }, 1079 1080 "APPROACHINGFIRSTMARKET": { 1081 "enter": function () { 1082 this.SelectAnimation("move"); 1083 }, 1084 1085 "MoveCompleted": function() { 1086 this.PerformTradeAndMoveToNextMarket(this.order.data.firstMarket, this.order.data.secondMarket, "INDIVIDUAL.TRADE.APPROACHINGSECONDMARKET"); 1087 }, 1088 }, 1089 1090 "APPROACHINGSECONDMARKET": { 1091 "enter": function () { 1092 this.SelectAnimation("move"); 1093 }, 1094 1095 "MoveCompleted": function() { 1096 this.order.data.firstPass = false; 1097 this.PerformTradeAndMoveToNextMarket(this.order.data.secondMarket, this.order.data.firstMarket, "INDIVIDUAL.TRADE.APPROACHINGFIRSTMARKET"); 1098 }, 1099 }, 1100 }, 1101 1066 1102 "REPAIR": { 1067 1103 "APPROACHING": { 1068 1104 "enter": function () { … … 2370 2406 this.AddOrder("ReturnResource", { "target": target }, queued); 2371 2407 }; 2372 2408 2409 UnitAI.prototype.SetupTradeRoute = function(target, queued) 2410 { 2411 if (!this.CanTrade(target)) 2412 { 2413 this.WalkToTarget(target, queued); 2414 return; 2415 } 2416 2417 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2418 var marketsChanged = cmpTrader.SetTargetMarket(target); 2419 if (marketsChanged) 2420 { 2421 if (cmpTrader.HasBothMarkets()) 2422 this.AddOrder("Trade", { "firstMarket": cmpTrader.GetFirstMarket(), "secondMarket": cmpTrader.GetSecondMarket() }, queued); 2423 else 2424 this.WalkToTarget(cmpTrader.GetFirstMarket(), queued); 2425 } 2426 } 2427 2428 UnitAI.prototype.MoveToMarket = function(targetMarket) 2429 { 2430 if (this.MoveToTarget(targetMarket)) 2431 { 2432 // We've started walking to the market 2433 return true; 2434 } 2435 else 2436 { 2437 // We can't reach the market. 2438 // Give up. 2439 this.StopMoving(); 2440 this.FinishOrder(); 2441 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2442 cmpTrader.DropGoods(); 2443 return false; 2444 } 2445 } 2446 2447 UnitAI.prototype.PerformTradeAndMoveToNextMarket = function(currentMarket, nextMarket, nextFsmStateName) 2448 { 2449 if (this.CheckTargetRange(currentMarket, IID_Trader)) 2450 { 2451 this.PerformTrade(); 2452 if (this.MoveToMarket(nextMarket)) 2453 { 2454 // We've started walking to the next market 2455 this.SetNextState(nextFsmStateName); 2456 } 2457 } 2458 else 2459 { 2460 // Market wasn't reached, give up 2461 this.FinishOrder(); 2462 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2463 cmpTrader.DropGoods(); 2464 } 2465 } 2466 2467 UnitAI.prototype.PerformTrade = function() 2468 { 2469 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2470 cmpTrader.PerformTrade(); 2471 } 2472 2373 2473 UnitAI.prototype.Repair = function(target, autocontinue, queued) 2374 2474 { 2375 2475 if (!this.CanRepair(target)) … … 2589 2689 return true; 2590 2690 }; 2591 2691 2692 UnitAI.prototype.CanTrade = function(target) 2693 { 2694 // Formation controllers should always respond to commands 2695 // (then the individual units can make up their own minds) 2696 if (this.IsFormationController()) 2697 return true; 2698 2699 // Verify that we're able to respond to Trade commands 2700 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2701 if (!cmpTrader) 2702 return false; 2703 2704 // Check that target is a market 2705 var cmpTargetIdentity = Engine.QueryInterface(target, IID_Identity); 2706 if (!cmpTargetIdentity) 2707 return false; 2708 if (!cmpTargetIdentity.HasClass("Market") && !cmpTargetIdentity.HasClass("SeaMarket")) 2709 return false; 2710 2711 return true; 2712 } 2713 2592 2714 UnitAI.prototype.CanRepair = function(target) 2593 2715 { 2594 2716 // 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 this.DropGoods(); 126 return marketsChanged; 127 } 128 129 Trader.prototype.GetFirstMarket = function() 130 { 131 return this.firstMarket; 132 } 133 134 Trader.prototype.GetSecondMarket = function() 135 { 136 return this.secondMarket; 137 } 138 139 Trader.prototype.HasBothMarkets = function() 140 { 141 return (this.firstMarket != null) && (this.secondMarket != null); 142 } 143 144 Trader.prototype.GetPreferredGoods = function() 145 { 146 return this.preferredGoods; 147 } 148 149 Trader.prototype.SetPreferredGoods = function(preferredGoods) 150 { 151 // Check that argument is a correct resource name 152 if (RESOURCES.indexOf(preferredGoods) == -1) 153 return; 154 this.preferredGoods = preferredGoods; 155 } 156 157 Trader.prototype.PerformTrade = function() 158 { 159 if (this.goods.amount > 0) 160 { 161 var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); 162 cmpPlayer.AddResource(this.goods.type, this.goods.amount); 163 } 164 this.goods.type = this.preferredGoods; 165 this.goods.amount = this.gain; 166 } 167 168 Trader.prototype.GetGoods = function() 169 { 170 return this.goods; 171 } 172 173 // Called when the trader stops trading 174 Trader.prototype.DropGoods = function() 175 { 176 this.goods.amount = 0; 177 } 178 179 // Get range in which deals with market are available, 180 // i.e. trader should be in no more than MaxDistance from market 181 // to be able to trade with it. 182 Trader.prototype.GetRange = function() 183 { 184 return { "min": 0, "max": +this.template.MaxDistance }; 185 } 186 187 Engine.RegisterComponentType(IID_Trader, "Trader", Trader); 188 -
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