Ticket #30: trading_2011_10_22.diff
File trading_2011_10_22.diff, 25.9 KB (added by , 13 years ago) |
---|
-
binaries/data/mods/public/gui/session/session.xml
535 535 <!-- Resource carrying icon/counter --> 536 536 <object size="0 40 48 88" type="image" name="resourceCarryingIcon" style="resourceIcon"/> 537 537 <object size="0 80 48 100" type="text" name="resourceCarryingText" style="statsText"/> 538 539 538 </object> 540 539 541 540 <!-- Big unit icon --> … … 690 689 </object> 691 690 </object> 692 691 692 <object name="unitTradingPanel" 693 size="14 12 100% 100%" 694 > 695 <object size="0 0 100% 100%"> 696 <repeat count="4"> 697 <object name="unitTradingButton[n]" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottom"> 698 <object name="unitTradingIcon[n]" type="image" ghost="true" size="3 3 43 43"/> 699 </object> 700 </repeat> 701 </object> 702 </object> 703 693 704 <object name="unitQueuePanel" 694 705 size="4 -56 100% 0" 695 706 type="image" -
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
82 82 player.offline = true; 83 83 } 84 84 85 function hasClass(entState, className) 86 { 87 if (entState.identity) 88 { 89 var classes = entState.identity.classes; 90 if (classes && classes.length) 91 return (classes.indexOf(className) != -1); 92 } 93 return false; 94 } 95 85 96 function isUnit(entState) 86 97 { 87 98 if (entState.identity) -
binaries/data/mods/public/gui/session/input.js
195 195 } 196 196 } 197 197 break; 198 case "setup-trade-route": 199 // If ground or sea trade possible 200 if ((entState.trader && hasClass(entState, "Organic") && (playerOwned || allyOwned) && hasClass(targetState, "Market")) || 201 (entState.trader && hasClass(entState, "Ship") && (playerOwned || allyOwned) && hasClass(targetState, "SeaMarket"))) 202 return {"possible": true}; 203 break; 198 204 case "gather": 199 205 if (targetState.resourceSupply && (playerOwned || gaiaOwned)) 200 206 { … … 218 224 case "attack": 219 225 if (entState.attack && targetState.hitpoints && enemyOwned) 220 226 return {"possible": true}; 227 break; 221 228 } 222 229 } 223 230 if (action == "move") … … 302 309 else 303 310 { 304 311 var actionInfo = undefined; 305 if ((actionInfo = getActionInfo("gather", target)).possible) 312 if (getActionInfo("setup-trade-route", target).possible) 313 return {"type": "setup-trade-route", "cursor": "action-setup-trade-route", "target": target}; 314 else if ((actionInfo = getActionInfo("gather", target)).possible) 306 315 return {"type": "gather", "cursor": actionInfo.cursor, "target": target}; 307 316 else if ((actionInfo = getActionInfo("returnresource", target)).possible) 308 317 return {"type": "returnresource", "cursor": actionInfo.cursor, "target": target}; … … 880 889 updateBuildingPlacementPreview(); 881 890 break; 882 891 } 883 884 892 break; 885 893 886 894 } … … 926 934 Engine.GuiInterfaceCall("PlaySound", { "name": "order_gather", "entity": selection[0] }); 927 935 return true; 928 936 937 case "setup-trade-route": 938 Engine.PostNetworkCommand({"type": "setup-trade-route", "entities": selection, "target": action.target}); 939 return true; 940 929 941 case "garrison": 930 942 Engine.PostNetworkCommand({"type": "garrison", "entities": selection, "target": action.target, "queued": queued}); 931 943 // TODO: Play a sound? … … 1008 1020 inputState = INPUT_BUILDING_PLACEMENT; 1009 1021 } 1010 1022 1023 // Called by GUI when user changes preferred trading goods 1024 function selectTradingPreferredGoods(data) 1025 { 1026 warn(data.trader + " " + data.preferredGoods); 1027 Engine.PostNetworkCommand({"type": "select-trading-goods", "trader": data.trader, "preferredGoods": data.preferredGoods }); 1028 } 1029 1011 1030 // Batch training: 1012 1031 // When the user shift-clicks, we set these variables and switch to INPUT_BATCHTRAINING 1013 1032 // 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 // The number of currently visible buttons (used to optimise showing/hiding) 17 var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, " Construction": 0, "Command": 0, "Stance": 0};20 var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Trading": 0, "Construction": 0, "Command": 0, "Stance": 0}; 18 21 19 22 // Unit panels are panels with row(s) of buttons 20 var g_unitPanels = ["Selection", "Queue", "Formation", "Garrison", "Training", " Construction", "Research", "Stance", "Command"];23 var g_unitPanels = ["Selection", "Queue", "Formation", "Garrison", "Training", "Trading", "Construction", "Research", "Stance", "Command"]; 21 24 22 25 // Lay out a row of centered buttons (does not work inside a loop like the other function) 23 26 function layoutButtonRowCentered(rowNumber, guiName, startIndex, endIndex, width) … … 112 115 function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback) 113 116 { 114 117 usedPanels[guiName] = 1; 118 119 // Use separate logic for trading panel 120 if (guiName == "Trading") 121 { 122 setupUnitTradingPanel(unitEntState); 123 return; 124 } 125 115 126 var numberOfItems = items.length; 116 127 var selection = g_Selection.toList(); 117 128 var garrisonGroups = new EntityGroups(); … … 354 365 g_unitPanelButtons[guiName] = numButtons; 355 366 } 356 367 368 // Sets up "unit trading panel" - special case for setupUnitPanel 369 function setupUnitTradingPanel(unitEntState) 370 { 371 for (var i = 0; i < TRADING_RESOURCES.length; i++) 372 { 373 var resource = TRADING_RESOURCES[i]; 374 var button = getGUIObjectByName("unitTradingButton["+i+"]"); 375 button.size = (i * 46) + " 0 " + ((i + 1) * 46) + " 46"; 376 var selectTradingPreferredGoodsData = { "trader": unitEntState.id, "preferredGoods": resource }; 377 button.onpress = (function(e){ return function() { selectTradingPreferredGoods(e); } })(selectTradingPreferredGoodsData); 378 button.enabled = true; 379 button.tooltip = "Set " + resource + " as trading goods"; 380 var icon = getGUIObjectByName("unitTradingIcon["+i+"]"); 381 var preferredGoods = unitEntState.trader.preferredGoods; 382 var imageNameSuffix = (resource == preferredGoods) ? "selected" : "inactive"; 383 icon.sprite = "stretched:session/resources/" + resource + "_" + imageNameSuffix + ".png"; 384 } 385 } 386 357 387 // Updates right Unit Commands Panel - runs in the main session loop via updateSelectionDetails() 358 388 function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, selection) 359 389 { … … 427 457 setupUnitPanel("Queue", usedPanels, entState, entState.training.queue, 428 458 function (item) { removeFromTrainingQueue(entState.id, item.id); } ); 429 459 460 if (entState.trader) 461 { 462 setupUnitPanel("Trading", usedPanels, entState, [], null); 463 } 464 430 465 // supplementalDetailsPanel.hidden = false; 431 466 // commandsPanel.hidden = isInvisible; 432 467 } -
binaries/data/mods/public/simulation/helpers/Commands.js
323 323 } 324 324 break; 325 325 326 case "setup-trade-route": 327 for each (var ent in cmd.entities) 328 { 329 var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); 330 if (cmpUnitAI) 331 cmpUnitAI.SetupTradeRoute(cmd.target); 332 } 333 break; 334 335 case "select-trading-goods": 336 var cmpTrader = Engine.QueryInterface(cmd.trader, IID_Trader); 337 cmpTrader.SetPreferredGoods(cmd.preferredGoods); 338 break; 339 326 340 default: 327 341 error("Ignoring unrecognised command type '" + cmd.type + "'"); 328 342 } -
binaries/data/mods/public/simulation/components/GuiInterface.js
179 179 }; 180 180 } 181 181 182 var cmpTrader = Engine.QueryInterface(ent, IID_Trader); 183 if (cmpTrader) 184 { 185 ret.trader = { 186 "goods": cmpTrader.GetGoods(), 187 "preferredGoods": cmpTrader.GetPreferredGoods() 188 }; 189 } 190 182 191 var cmpFoundation = Engine.QueryInterface(ent, IID_Foundation); 183 192 if (cmpFoundation) 184 193 { -
binaries/data/mods/public/simulation/components/interfaces/Trader.js
1 Engine.RegisterInterface("Trader"); -
binaries/data/mods/public/simulation/components/Identity.js
89 89 "<value>Worker</value>" + 90 90 "<value>CitizenSoldier</value>" + 91 91 "<value>Trade</value>" + 92 "<value>Market</value>" + 93 "<value>SeaMarket</value>" + 92 94 "<value>Warship</value>" + 93 95 "<value>SeaCreature</value>" + 94 96 "<value>ForestPlant</value>" + -
binaries/data/mods/public/simulation/components/UnitAI.js
334 334 return; 335 335 } 336 336 }, 337 337 338 "Order.Trade": function(msg) { 339 if (this.MoveToMarket(this.order.data.firstMarket)) 340 { 341 // We've started walking to the first market 342 this.SetNextState("INDIVIDUAL.TRADE.APPROACHINGFIRSTMARKET"); 343 } 344 }, 345 338 346 "Order.Repair": function(msg) { 339 347 // Try to move within range 340 348 if (this.MoveToTargetRange(this.order.data.target, IID_Builder)) … … 1007 1015 }, 1008 1016 }, 1009 1017 1018 "TRADE": { 1019 "APPROACHINGFIRSTMARKET": { 1020 "enter": function () { 1021 this.SelectAnimation("move"); 1022 }, 1023 1024 "MoveCompleted": function() { 1025 this.PerformTrade(); 1026 if (this.MoveToMarket(this.order.data.secondMarket)) 1027 { 1028 // We've started walking to the second market 1029 this.SetNextState("INDIVIDUAL.TRADE.APPROACHINGSECONDMARKET"); 1030 } 1031 }, 1032 }, 1033 1034 "APPROACHINGSECONDMARKET": { 1035 "enter": function () { 1036 this.SelectAnimation("move"); 1037 }, 1038 1039 "MoveCompleted": function() { 1040 this.order.data.firstPass = false; 1041 this.PerformTrade(); 1042 if (this.MoveToMarket(this.order.data.firstMarket)) 1043 { 1044 // We've started walking to the first market 1045 this.SetNextState("INDIVIDUAL.TRADE.APPROACHINGFIRSTMARKET"); 1046 } 1047 }, 1048 }, 1049 }, 1050 1010 1051 "REPAIR": { 1011 1052 "APPROACHING": { 1012 1053 "enter": function () { … … 2313 2354 this.AddOrder("ReturnResource", { "target": target }, queued); 2314 2355 }; 2315 2356 2357 UnitAI.prototype.SetupTradeRoute = function(target, queued) 2358 { 2359 if (!this.CanTrade(target)) 2360 { 2361 this.WalkToTarget(target, queued); 2362 return; 2363 } 2364 2365 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2366 var secondMarketChanged = cmpTrader.SetTargetMarket(target); 2367 if (secondMarketChanged) 2368 { 2369 this.AddOrder("Trade", { "firstMarket": cmpTrader.GetFirstMarket(), "secondMarket": cmpTrader.GetSecondMarket() }, queued); 2370 } 2371 } 2372 2373 UnitAI.prototype.MoveToMarket = function(targetMarket) 2374 { 2375 if (this.MoveToTarget(targetMarket)) 2376 { 2377 // We've started walking to the first market 2378 return true; 2379 } 2380 else 2381 { 2382 // We can't reach the frist market. 2383 // Give up. 2384 this.StopMoving(); 2385 this.FinishOrder(); 2386 return false; 2387 } 2388 } 2389 2390 UnitAI.prototype.PerformTrade = function() 2391 { 2392 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2393 cmpTrader.PerformTrade(); 2394 } 2395 2316 2396 UnitAI.prototype.Repair = function(target, autocontinue, queued) 2317 2397 { 2318 2398 if (!this.CanRepair(target)) … … 2505 2585 return true; 2506 2586 }; 2507 2587 2588 UnitAI.prototype.CanTrade = function(target) 2589 { 2590 // Formation controllers should always respond to commands 2591 // (then the individual units can make up their own minds) 2592 if (this.IsFormationController()) 2593 return true; 2594 2595 // Verify that we're able to respond to Trade commands 2596 var cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); 2597 if (!cmpTrader) 2598 return false; 2599 2600 return true; 2601 } 2602 2508 2603 UnitAI.prototype.CanRepair = function(target) 2509 2604 { 2510 2605 // Formation controllers should always respond to commands -
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 "<empty/>"; 14 15 Trader.prototype.Init = function() 16 { 17 this.firstMarket = null; 18 this.secondMarket = null; 19 // Gain from one pass between markets 20 this.gain = null; 21 // Selected resource for trading 22 this.preferredGoods = "metal"; 23 // Currently carried goods 24 this.goods = { "type": null, "amount": 0 }; 25 } 26 27 Trader.prototype.CalculateGain = function() 28 { 29 var cmpFirstMarketPosition = Engine.QueryInterface(this.firstMarket, IID_Position); 30 var cmpSecondMarketPosition = Engine.QueryInterface(this.secondMarket, IID_Position); 31 var firstMarketPosition = cmpFirstMarketPosition.GetPosition2D(); 32 var secondMarketPosition = cmpSecondMarketPosition.GetPosition2D(); 33 // Calculate ordinary Euclidean distance between markets. 34 // We don't use pathfinder, because ordinary distance looks more fair. 35 var distance = Math.sqrt(Math.pow(firstMarketPosition.x - secondMarketPosition.x, 2) + Math.pow(firstMarketPosition.y - secondMarketPosition.y, 2)); 36 // We calculate gain as square of distance to encourage trading between remote markets 37 this.gain = Math.round(Math.pow(distance * DISTANCE_FACTOR, 2)); 38 // If markets belongs to different players, multiple gain to INTERNATIONAL_TRADING_MULTIPLIER 39 var cmpFirstMarketOwnership = Engine.QueryInterface(this.firstMarket, IID_Ownership); 40 var cmpSecondMarketOwnership = Engine.QueryInterface(this.secondMarket, IID_Ownership); 41 if (cmpFirstMarketOwnership.GetOwner() != cmpSecondMarketOwnership.GetOwner()) 42 this.gain *= (100 + INTERNATIONAL_TRADING_ADDITION) / 100; 43 // For ship increase gain for each garrisoned trader 44 var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); 45 if (cmpIdentity.HasClass("Ship")) 46 { 47 warn("Ship"); 48 var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); 49 if (cmpGarrisonHolder) 50 { 51 warn("Garrison holder"); 52 var garrisonedTradersCount = 0; 53 for each (var entity in cmpGarrisonHolder.GetEntities()) 54 { 55 var cmpGarrisonedUnitTrader = Engine.QueryInterface(entity, IID_Trader); 56 if (cmpGarrisonedUnitTrader) 57 garrisonedTradersCount++; 58 } 59 warn("Garrisoned traders count: " + garrisonedTradersCount); 60 this.gain *= (100 + GARRISONED_TRADER_ADDITION * garrisonedTradersCount) / 100; 61 } 62 } 63 this.gain = Math.round(this.gain); 64 warn("gain: " + this.gain); 65 } 66 67 // Set target as last of target markets. 68 // Return true if both markets defined and second market was changed, 69 // i.e. we should start trading. 70 Trader.prototype.SetTargetMarket = function(target) 71 { 72 if (this.secondMarket) 73 { 74 if (target != this.secondMarket) 75 { 76 this.firstMarket = this.secondMarket; 77 this.secondMarket = target; 78 } 79 else 80 { 81 return false; 82 } 83 } 84 else if (this.firstMarket) 85 { 86 if (target != this.firstMarket) 87 { 88 this.secondMarket = target; 89 } 90 else 91 { 92 return false; 93 } 94 } 95 else 96 { 97 this.firstMarket = target; 98 return false; 99 } 100 this.CalculateGain(); 101 // Drop carried goods 102 this.goods.amount = 0; 103 return true; 104 } 105 106 Trader.prototype.GetFirstMarket = function() 107 { 108 return this.firstMarket; 109 } 110 111 Trader.prototype.GetSecondMarket = function() 112 { 113 return this.secondMarket; 114 } 115 116 Trader.prototype.GetPreferredGoods = function() 117 { 118 return this.preferredGoods; 119 } 120 121 Trader.prototype.SetPreferredGoods = function(preferredGoods) 122 { 123 this.preferredGoods = preferredGoods; 124 } 125 126 Trader.prototype.PerformTrade = function() 127 { 128 if (this.goods.amount > 0) 129 { 130 var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); 131 cmpPlayer.AddResource(this.goods.type, this.goods.amount); 132 } 133 this.goods.type = this.preferredGoods; 134 this.goods.amount = this.gain; 135 } 136 137 Trader.prototype.GetGoods = function() 138 { 139 return this.goods; 140 } 141 142 Engine.RegisterComponentType(IID_Trader, "Trader", Trader); -
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 23 <UnitMotion> 24 24 <WalkSpeed>7.0</WalkSpeed> 25 25 </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. (Currently a useless structure)</Tooltip> 27 <Classes datatype="tokens">Town </Classes>27 <Classes datatype="tokens">Town Market</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/> 41 42 <UnitMotion> 42 43 <WalkSpeed>10.5</WalkSpeed> 43 44 </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