Ticket #865: diffStances-29-05-2011.patch
File diffStances-29-05-2011.patch, 26.8 KB (added by , 13 years ago) |
---|
-
source/simulation2/components/ICmpRangeManager.cpp
36 36 BEGIN_INTERFACE_WRAPPER(RangeManager) 37 37 DEFINE_INTERFACE_METHOD_5("ExecuteQuery", std::vector<entity_id_t>, ICmpRangeManager, ExecuteQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int) 38 38 DEFINE_INTERFACE_METHOD_5("CreateActiveQuery", ICmpRangeManager::tag_t, ICmpRangeManager, CreateActiveQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int) 39 DEFINE_INTERFACE_METHOD_3("ModifyRangeActiveQuery", std::vector<entity_id_t>, ICmpRangeManager, ModifyRangeActiveQuery, ICmpRangeManager::tag_t, entity_pos_t, entity_pos_t) 39 40 DEFINE_INTERFACE_METHOD_1("DestroyActiveQuery", void, ICmpRangeManager, DestroyActiveQuery, ICmpRangeManager::tag_t) 40 41 DEFINE_INTERFACE_METHOD_1("EnableActiveQuery", void, ICmpRangeManager, EnableActiveQuery, ICmpRangeManager::tag_t) 41 42 DEFINE_INTERFACE_METHOD_1("DisableActiveQuery", void, ICmpRangeManager, DisableActiveQuery, ICmpRangeManager::tag_t) -
source/simulation2/components/ICmpRangeManager.h
102 102 entity_pos_t minRange, entity_pos_t maxRange, std::vector<int> owners, int requiredInterface) = 0; 103 103 104 104 /** 105 * Modify an active query's range parameters and execute a ResetActiveQuery. 106 * @param tag identifier of query. 107 * @param minRange non-negative minimum distance in metres (inclusive). 108 * @param maxRange non-negative maximum distance in metres (inclusive); or -1.0 to ignore distance. 109 * @return list of entities matching the query, ordered by increasing distance from the source entity. 110 */ 111 virtual std::vector<entity_id_t> ModifyRangeActiveQuery(tag_t tag, 112 entity_pos_t minRange, entity_pos_t maxRange) = 0; 113 114 /** 105 115 * Destroy a query and clean up resources. This must be called when an entity no longer needs its 106 116 * query (e.g. when the entity is destroyed). 107 117 * @param tag identifier of query. -
source/simulation2/components/CCmpRangeManager.cpp
459 459 return (tag_t)id; 460 460 } 461 461 462 virtual std::vector<entity_id_t> ModifyRangeActiveQuery(tag_t tag, 463 entity_pos_t minRange, entity_pos_t maxRange) 464 { 465 std::map<tag_t, Query>::iterator it = m_Queries.find(tag); 466 if (it == m_Queries.end()) 467 { 468 LOGERROR(L"CCmpRangeManager: ModifyRangeActiveQuery called with invalid tag %d", tag); 469 std::vector<entity_id_t> r; 470 return r; 471 } 472 473 Query& q = it->second; 474 q.minRange = minRange; 475 q.maxRange = maxRange; 476 return ResetActiveQuery(tag); 477 } 478 462 479 virtual void DestroyActiveQuery(tag_t tag) 463 480 { 464 481 if (m_Queries.find(tag) == m_Queries.end()) -
binaries/data/mods/public/maps/scenarios/Combat_demo.xml
40 40 "Civ": "hele" 41 41 } 42 42 ], 43 "RevealMap": true 43 "RevealMap": true, 44 "DefaultStance": "stand" 44 45 } 45 46 ]]></ScriptSettings> 46 47 <Entities> … … 1285 1286 </Entity> 1286 1287 </Entities> 1287 1288 <Paths/> 1288 </Scenario> 1289 No newline at end of file 1289 </Scenario> -
binaries/data/mods/public/maps/scenarios/Pathfinding_demo.xml
41 41 } 42 42 ], 43 43 "RevealMap": true, 44 "DefaultStance": " holdfire",44 "DefaultStance": "defensive", 45 45 "GameType": "endless" 46 46 } 47 47 ]]></ScriptSettings> … … 684 684 </Entity> 685 685 </Entities> 686 686 <Paths/> 687 </Scenario> 688 No newline at end of file 687 </Scenario> -
binaries/data/mods/public/gui/session/session.xml
408 408 </object> 409 409 410 410 <!-- ================================ ================================ --> 411 <!-- Stance Selection (Temporary location) --> 412 <!-- ================================ ================================ --> 413 <object name="unitStancePanel" 414 style="goldPanelFrilly" 415 size="50 50 300 106" 416 type="text" 417 > 418 <object size="-5 -2 59 62" type="image" sprite="snIconSheetTab" tooltip_style="sessionToolTipBottom" 419 cell_id="4" tooltip="Stances"/> 420 421 <object size="56 12 100% 100%"> 422 <repeat count="5"> 423 <object name="unitStanceButton[n]" hidden="true" style="iconButton" type="button" size="0 0 36 36" tooltip_style="sessionToolTipBottomBold" z="100"> 424 <object name="unitStanceIcon[n]" type="image" style="formationIcon" ghost="true" size="3 3 33 33"/> 425 </object> 426 </repeat> 427 </object> 428 </object> 429 430 <!-- ================================ ================================ --> 411 431 <!-- Idle Worker Button --> 412 432 <!-- ================================ ================================ --> 413 433 <object type="image" … … 640 660 </object> 641 661 </object> 642 662 643 <object name="unitStancePanel"644 style="goldPanelFrilly"645 size="0 100%-56 100% 100%"646 type="text"647 >648 <object size="-5 -2 59 62" type="image" sprite="snIconSheetTab" tooltip_style="sessionToolTipBottom"649 cell_id="4" tooltip="Stances"/>650 651 [stance commands]652 </object>653 654 663 <object name="unitResearchPanel" 655 664 style="goldPanelFrilly" 656 665 size="0 100%-56 100% 100%" -
binaries/data/mods/public/gui/session/styles.xml
123 123 ghost="true" 124 124 /> 125 125 126 <style name="stanceIcon" 127 sprite="snIconSheetStance" 128 sprite_disabled="snIconSheetStance" 129 ghost="true" 130 /> 131 126 132 <style name="commandIcon" 127 133 sprite="commands" 128 134 ghost="true" -
binaries/data/mods/public/gui/session/input.js
46 46 var doubleClicked = false; 47 47 // Store the previously clicked entity - ensure a double/triple click happens on the same entity 48 48 var prevClickedEntity = 0; 49 var prevIsRight = false; 49 50 50 51 // Same double-click behaviour for hotkey presses 51 52 const doublePressTime = 500; … … 641 642 { 642 643 dragStart = [ ev.x, ev.y ]; 643 644 inputState = INPUT_SELECTING; 645 prevIsRight = false; 644 646 return true; 645 647 } 646 648 else if (ev.button == SDL_BUTTON_RIGHT) … … 648 650 var action = determineAction(ev.x, ev.y); 649 651 if (!action) 650 652 break; 651 return doAction(action, ev); 653 654 var v = false; 655 656 var now = new Date(); 657 if ((now.getTime() - doubleClickTimer < doubleClickTime) && (prevIsRight == true)) 658 { 659 v = true; 660 } 661 else 662 { 663 prevIsRight = true; 664 doubleClickTimer = now.getTime(); 665 } 666 667 return doAction(action, ev, v); 652 668 } 653 669 break; 654 670 … … 861 877 return false; 862 878 } 863 879 864 function doAction(action, ev )880 function doAction(action, ev, d) 865 881 { 866 882 var selection = g_Selection.toList(); 867 883 … … 873 889 { 874 890 case "move": 875 891 var target = Engine.GetTerrainAtPoint(ev.x, ev.y); 876 Engine.PostNetworkCommand({"type": "walk", "entities": selection, "x": target.x, "z": target.z, "queued": queued}); 892 var type = d ? "run" : "walk"; 893 Engine.PostNetworkCommand({"type": type, "entities": selection, "x": target.x, "z": target.z, "queued": queued}); 877 894 Engine.GuiInterfaceCall("PlaySound", { "name": "order_walk", "entity": selection[0] }); 878 895 return true; 879 896 … … 1111 1128 } 1112 1129 } 1113 1130 1131 // Performs the specified stance 1132 function performStance(entity, stanceName) 1133 { 1134 if (entity) 1135 { 1136 var selection = g_Selection.toList(); 1137 Engine.PostNetworkCommand({ 1138 "type": "stance", 1139 "entities": selection, 1140 "name": stanceName 1141 }); 1142 } 1143 } 1144 1114 1145 // Performs the specified group 1115 1146 function performGroup(action, groupId) 1116 1147 { -
binaries/data/mods/public/gui/session/unit_commands.js
6 6 const TRAINING = "Training"; 7 7 const CONSTRUCTION = "Construction"; 8 8 const COMMAND = "Command"; 9 const STANCE = "Stance"; 9 10 10 11 // Constants 11 12 const COMMANDS_PANEL_WIDTH = 228; … … 13 14 const UNIT_PANEL_HEIGHT = 44; // QUEUE: The height needed for a row of buttons 14 15 15 16 // The number of currently visible buttons (used to optimise showing/hiding) 16 var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Construction": 0, "Command": 0 };17 var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Construction": 0, "Command": 0, "Stance": 0}; 17 18 18 19 // Unit panels are panels with row(s) of buttons 19 20 var g_unitPanels = ["Selection", "Queue", "Formation", "Garrison", "Training", "Construction", "Research", "Stance", "Command"]; … … 153 154 var item = items[i]; 154 155 var entType = ((guiName == "Queue")? item.template : item); 155 156 var template; 156 if (guiName != "Formation" && guiName != "Command" )157 if (guiName != "Formation" && guiName != "Command" && guiName != "Stance") 157 158 { 158 159 template = GetTemplateData(entType); 159 160 if (!template) … … 190 191 getGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (count > 1 ? count : ""); 191 192 break; 192 193 194 case STANCE: 193 195 case FORMATION: 194 196 var tooltip = toTitleCase(item); 195 197 break; … … 265 267 icon.sprite = "formation"; 266 268 } 267 269 } 270 else if (guiName == "Stance") 271 { 272 icon.cell_id = i; 273 icon.sprite = "snIconSheetStance"; 274 } 268 275 else if (guiName == "Command") 269 276 { 270 277 //icon.cell_id = i; … … 368 375 setupUnitPanel("Formation", usedPanels, entState, formations, 369 376 function (item) { performFormation(entState.id, item); } ); 370 377 378 var stances = ["violent","aggressive","passive","defensive","stand"]; 379 if (isUnit(entState) && !isAnimal(entState) && !entState.garrisonHolder && stances.length) 380 setupUnitPanel("Stance", usedPanels, entState, stances, 381 function (item) { performStance(entState.id, item); } ); 382 371 383 if (entState.buildEntities && entState.buildEntities.length) 372 384 { 373 385 setupUnitPanel("Construction", usedPanels, entState, entState.buildEntities, startBuildingPlacement); -
binaries/data/mods/public/simulation/helpers/Setup.js
14 14 for each (var ent in Engine.GetEntitiesWithInterface(IID_UnitAI)) 15 15 { 16 16 var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); 17 cmpUnitAI.S etStance(settings.DefaultStance);17 cmpUnitAI.SwitchToStance(settings.DefaultStance); 18 18 } 19 19 } 20 20 -
binaries/data/mods/public/simulation/helpers/Commands.js
27 27 cmpUnitAI.Walk(cmd.x, cmd.z, cmd.queued); 28 28 break; 29 29 30 case "run": 31 var cmpUnitAI = GetFormationUnitAI(cmd.entities); 32 if (cmpUnitAI) 33 cmpUnitAI.Run(cmd.x, cmd.z, cmd.queued); 34 break; 35 30 36 case "attack": 31 37 var cmpUnitAI = GetFormationUnitAI(cmd.entities); 32 38 if (cmpUnitAI) … … 261 267 } 262 268 break; 263 269 270 case "stance": 271 for each (var ent in cmd.entities) 272 { 273 var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); 274 if (cmpUnitAI) 275 cmpUnitAI.SwitchToStance(cmd.name); 276 } 277 break; 278 264 279 default: 265 280 error("Ignoring unrecognised command type '" + cmd.type + "'"); 266 281 } -
binaries/data/mods/public/simulation/components/UnitAI.js
6 6 "<element name='DefaultStance'>" + 7 7 "<choice>" + 8 8 "<value>aggressive</value>" + 9 "<value> holdfire</value>" +10 "<value> noncombat</value>" +9 "<value>defensive</value>" + 10 "<value>passive</value>" + 11 11 "</choice>" + 12 12 "</element>" + 13 13 "<element name='FormationController'>" + … … 59 59 // TODO: even this limited version isn't implemented properly yet (e.g. it can't handle 60 60 // dynamic stance changes). 61 61 var g_Stances = { 62 " aggressive": {62 "violent": { 63 63 targetVisibleEnemies: true, 64 64 targetAttackers: true, 65 65 respondFlee: false, 66 66 respondChase: true, 67 respondStandGround : false, 68 respondHoldGround : false, 67 69 }, 68 " holdfire": {69 targetVisibleEnemies: false,70 "aggressive": { 71 targetVisibleEnemies: true, 70 72 targetAttackers: true, 71 73 respondFlee: false, 72 74 respondChase: true, 75 respondStandGround : false, 76 respondHoldGround : false, 73 77 }, 74 "noncombat": { 78 "defensive": { 79 targetVisibleEnemies: true, 80 targetAttackers: true, 81 respondFlee: false, 82 respondChase: false, 83 respondStandGround : false, 84 respondHoldGround : true, 85 }, 86 "passive": { 75 87 targetVisibleEnemies: false, 76 88 targetAttackers: true, 77 89 respondFlee: true, 78 90 respondChase: false, 91 respondStandGround : false, 92 respondHoldGround : false, 79 93 }, 94 "stand": { 95 targetVisibleEnemies: true, 96 targetAttackers: true, 97 respondFlee: false, 98 respondChase: false, 99 respondStandGround : true, 100 respondHoldGround : false, 101 }, 80 102 }; 81 103 82 104 // See ../helpers/FSM.js for some documentation of this FSM specification syntax … … 114 136 // ignore 115 137 }, 116 138 139 "StanceChanged": function(msg) { 140 if (this.StanceSpecificQuery(this.stance,true)) 141 return; 142 }, 143 117 144 // Formation handlers: 118 145 119 146 "FormationLeave": function(msg) { … … 154 181 return; 155 182 } 156 183 184 this.SetReturnPoint({"x": this.order.data.x, "z": this.order.data.z}); 157 185 this.MoveToPoint(this.order.data.x, this.order.data.z); 158 186 this.SetNextState("INDIVIDUAL.WALKING"); 159 187 }, … … 180 208 } 181 209 }, 182 210 211 "Order.Run": function(msg) { 212 if (this.IsAnimal()) 213 { 214 // TODO: let players move captured animals around 215 this.FinishOrder(); 216 return; 217 } 218 219 this.SetReturnPoint({"x": this.order.data.x, "z": this.order.data.z}); 220 this.MoveToPoint(this.order.data.x, this.order.data.z); 221 this.SetNextState("INDIVIDUAL.RUNNING"); 222 }, 223 183 224 "Order.Flee": function(msg) { 184 225 // TODO: if we were attacked by a ranged unit, we need to flee much further away 185 226 var ok = this.MoveToTargetRangeExplicit(this.order.data.target, +this.template.FleeDistance, -1); … … 217 258 } 218 259 this.attackType = type; 219 260 261 if (this.CheckTargetRange(this.order.data.target, IID_Attack, this.attackType)) 262 { 263 // We are already at the target 264 // so try attacking it from here. 265 this.StopMoving(); 266 if (this.IsAnimal()) 267 this.SetNextState("ANIMAL.COMBAT.ATTACKING"); 268 else 269 this.SetNextState("INDIVIDUAL.COMBAT.ATTACKING"); 270 } 220 271 // Try to move within attack range 221 if (this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType))272 else if (!this.order.data.stand && this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType)) 222 273 { 223 274 // We've started walking to the given point 224 275 if (this.IsAnimal()) … … 227 278 this.SetNextState("INDIVIDUAL.COMBAT.APPROACHING"); 228 279 } 229 280 else 230 { 231 // We are already at the target, or can't move at all, 232 // so try attacking it from here. 233 // TODO: need better handling of the can't-reach-target case 234 this.StopMoving(); 235 if (this.IsAnimal()) 236 this.SetNextState("ANIMAL.COMBAT.ATTACKING"); 237 else 238 this.SetNextState("INDIVIDUAL.COMBAT.ATTACKING"); 239 } 281 this.FinisOrder(); 240 282 }, 241 283 242 284 "Order.Gather": function(msg) { … … 484 526 // If we entered the idle state we must have nothing better to do, 485 527 // so immediately check whether there's anybody nearby to attack. 486 528 // (If anyone approaches later, it'll be handled via LosRangeUpdate.) 487 if (this.losRangeQuery) 488 { 489 var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 490 var ents = rangeMan.ResetActiveQuery(this.losRangeQuery); 491 if (this.GetStance().targetVisibleEnemies) 492 { 493 if (this.RespondToTargetedEntities(ents)) 494 return true; // (abort the FSM transition since we may have already switched state) 495 } 496 } 529 if (this.StanceSpecificQuery(this.stance)) 530 return true; 497 531 498 532 // Nobody to attack - stay in idle 499 533 return false; … … 551 585 } 552 586 }, 553 587 588 "StanceChanged": function(msg) { 589 if (this.StanceSpecificQuery(this.stance)) 590 return; 591 }, 554 592 }, 555 593 556 594 "WALKING": { … … 563 601 }, 564 602 }, 565 603 604 "RUNNING": { 605 "enter": function () { 606 // Run quickly 607 var speed = this.GetRunSpeed(); 608 this.SelectAnimation("move"); 609 this.SetMoveSpeed(speed); 610 // TODO: stamina 611 }, 612 613 "leave": function() { 614 var speed = this.GetWalkSpeed(); 615 this.SelectAnimation("move"); 616 this.SetMoveSpeed(speed); 617 }, 618 619 "MoveCompleted": function() { 620 this.FinishOrder(); 621 }, 622 }, 623 566 624 "FLEEING": { 567 625 "enter": function() { 568 626 this.PlaySound("panic"); … … 605 663 "MoveCompleted": function() { 606 664 this.SetNextState("ATTACKING"); 607 665 }, 666 667 "Attacked": function(msg) { 668 // If we're attacked by a close enemy, we should try to defend ourself 669 if (this.GetStance().targetAttackers && msg.data.type == "Melee") 670 { 671 this.RespondToTargetedEntities([msg.data.attacker]); 672 } 673 }, 608 674 }, 609 675 610 676 "ATTACKING": { … … 638 704 } 639 705 640 706 // Can't reach it - try to chase after it 641 if (this. MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType))707 if (this.GetStance().respondChase && this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType)) 642 708 { 643 this.SetNextState("COMBAT. CHASING");709 this.SetNextState("COMBAT.APPROACHING"); 644 710 return; 645 711 } 712 713 if (this.GetStance().respondHoldGround && this.CheckTargetDistance(this.order.data.target, this.attackType, 50)) 714 { 715 if (this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType)) 716 { 717 this.SetNextState("COMBAT.APPROACHING"); 718 return; 719 } 720 } 646 721 } 647 722 648 723 // Can't reach it, or it doesn't exist any more - give up 649 this.FinishOrder(); 724 if (this.FinishOrder()) 725 return; 650 726 651 // TODO: see if we can switch to a new nearby enemy 727 // see if we can switch to a new nearby enemy 728 if (this.StanceSpecificQuery(this.stance, true)) 729 return; 730 731 if (this.GetStance().respondHoldGround) 732 this.Walk(this.returnPoint.x, this.returnPoint.z, false); 652 733 }, 653 734 654 735 // TODO: respond to target deaths immediately, rather than waiting 655 736 // until the next Timer event 656 737 }, 657 658 "CHASING": {659 "enter": function () {660 this.SelectAnimation("move");661 },662 663 "MoveCompleted": function() {664 this.SetNextState("ATTACKING");665 },666 },667 738 }, 668 739 669 740 "GATHER": { … … 1303 1374 // This should be called whenever our ownership changes. 1304 1375 UnitAI.prototype.SetupRangeQuery = function(owner) 1305 1376 { 1306 var cmpVision = Engine.QueryInterface(this.entity, IID_Vision);1307 if (!cmpVision)1308 return;1309 1310 1377 var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 1311 1378 var playerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); 1312 1379 1313 1380 if (this.losRangeQuery) 1314 1381 rangeMan.DestroyActiveQuery(this.losRangeQuery); 1315 1382 1316 var range = cmpVision.GetRange();1317 1318 1383 var players = []; 1319 1384 1320 1385 if (owner != -1) 1321 1386 { 1322 1387 // If unit not just killed, get enemy players via diplomacy … … 1334 1399 players.push(i); 1335 1400 } 1336 1401 } 1337 1338 this.losRangeQuery = rangeMan.CreateActiveQuery(this.entity, 0, range, players, IID_DamageReceiver); 1402 1403 var range = this.StanceSpecificRange(); 1404 1405 this.losRangeQuery = rangeMan.CreateActiveQuery(this.entity, 0, range.max, players, IID_DamageReceiver); 1339 1406 rangeMan.EnableActiveQuery(this.losRangeQuery); 1340 } ;1407 } 1341 1408 1342 1409 //// FSM linkage functions //// 1343 1410 … … 1812 1879 return cmpUnitMotion.IsInTargetRange(target, range.min, range.max); 1813 1880 }; 1814 1881 1882 UnitAI.prototype.CheckTargetDistance = function(target, type, distance) 1883 { 1884 var cmpRanged = Engine.QueryInterface(this.entity, IID_Attack); 1885 var range = cmpRanged.GetRange(type); 1886 1887 var cmpPosition = Engine.QueryInterface(target, IID_Position); 1888 if (!cmpPosition || !cmpPosition.IsInWorld()) 1889 return false; 1890 1891 var pos = cmpPosition.GetPosition(); 1892 var dx = this.returnPoint.x - pos.x; 1893 var dz = this.returnPoint.z - pos.z; 1894 var dist = Math.sqrt(dx*dx + dz*dz); 1895 1896 return dist < distance + range.max; 1897 }; 1898 1815 1899 UnitAI.prototype.GetBestAttack = function() 1816 1900 { 1817 1901 var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); … … 1839 1923 }; 1840 1924 1841 1925 /** 1926 * Try to find one of the given entities which can be attacked, 1927 * without moving. 1928 * Returns true if it found something to attack. 1929 */ 1930 UnitAI.prototype.AttackEntityInRange = function(ents) 1931 { 1932 var type = this.GetBestAttack(); 1933 for each (var target in ents) 1934 { 1935 if (this.CanAttack(target) && this.CheckTargetRange(target, IID_Attack, type)) 1936 { 1937 this.PushOrderFront("Attack", { "target": target, "stand": true }); 1938 return true; 1939 } 1940 } 1941 return false; 1942 }; 1943 1944 /** 1945 * Try to find one of the given entities which can be attacked, 1946 * within zone. 1947 * Returns true if it found something to attack. 1948 */ 1949 UnitAI.prototype.AttackEntityInZone = function(ents) 1950 { 1951 var type = this.GetBestAttack(); 1952 for each (var target in ents) 1953 { 1954 if (this.CanAttack(target) && this.CheckTargetDistance(target, type, 50)) 1955 { 1956 this.PushOrderFront("Attack", { "target": target }); 1957 return true; 1958 } 1959 } 1960 return false; 1961 }; 1962 1963 /** 1842 1964 * Try to respond appropriately given our current stance, 1843 1965 * given a list of entities that match our stance's target criteria. 1844 1966 * Returns true if it responded. … … 1851 1973 if (this.GetStance().respondChase) 1852 1974 return this.AttackVisibleEntity(ents); 1853 1975 1976 if (this.GetStance().respondStandGround) 1977 return this.AttackEntityInRange(ents); 1978 1979 if (this.GetStance().respondHoldGround) 1980 return this.AttackEntityInZone(ents); 1981 1854 1982 if (this.GetStance().respondFlee) 1855 1983 { 1856 1984 this.PushOrderFront("Flee", { "target": ents[0] }); … … 1919 2047 switch (order.type) 1920 2048 { 1921 2049 case "Walk": 2050 case "Run": 1922 2051 // Add the distance to the target point 1923 2052 var dx = order.data.x - pos.x; 1924 2053 var dz = order.data.z - pos.z; … … 1981 2110 this.AddOrder("WalkToTarget", { "target": target }, queued); 1982 2111 }; 1983 2112 2113 UnitAI.prototype.Run = function(x, z, queued) 2114 { 2115 if (this.IsFormationController()) 2116 this.AddOrder("Walk", { "x": x, "z": z }, queued); 2117 else 2118 this.AddOrder("Run", { "x": x, "z": z }, queued); 2119 }; 2120 1984 2121 UnitAI.prototype.LeaveFoundation = function(target) 1985 2122 { 1986 2123 // TODO: we should verify this is a friendly foundation, otherwise … … 2088 2225 this.stance = stance; 2089 2226 else 2090 2227 error("UnitAI: Setting to invalid stance '"+stance+"'"); 2228 } 2229 2230 UnitAI.prototype.SwitchToStance = function(stance) 2231 { 2232 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 2233 if (!cmpPosition || !cmpPosition.IsInWorld()) 2234 return; 2235 var pos = cmpPosition.GetPosition(); 2236 this.SetReturnPoint({"x": pos.x, "z": pos.z}); 2237 2238 this.SetStance(stance); 2239 if (stance == "stand" || stance == "defensive" || stance == "passive") 2240 this.StopMoving(); 2241 2242 UnitFsm.ProcessMessage(this, {"type": "StanceChanged", "stance": stance}); 2243 } 2244 2245 UnitAI.prototype.StanceSpecificQuery = function(stance, disable) 2246 { 2247 if (!g_Stances[stance]) 2248 { 2249 error("UnitAI: Setting to invalid stance '"+stance+"'"); 2250 return false; 2251 } 2252 2253 if (!this.losRangeQuery) 2254 return false; 2255 2256 var range = this.StanceSpecificRange(); 2257 2258 var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 2259 var ents = rangeMan.ModifyRangeActiveQuery(this.losRangeQuery, range.min, range.max); 2260 2261 if (disable && !this.IsAnimal()) 2262 rangeMan.DisableActiveQuery(this.losRangeQuery); 2263 2264 return this.RespondToTargetedEntities(ents); 2091 2265 }; 2092 2266 2267 UnitAI.prototype.StanceSpecificRange = function() 2268 { 2269 var ret = { "min": 0, "max": 0 }; 2270 if (this.GetStance().respondStandGround) 2271 { 2272 var cmpRanged = Engine.QueryInterface(this.entity, IID_Attack); 2273 if (!cmpRanged) 2274 return ret; 2275 var range = cmpRanged.GetRange(cmpRanged.GetBestAttack()); 2276 ret.min = range.min; 2277 ret.max = range.max; 2278 } 2279 else if (this.GetStance().respondChase) 2280 { 2281 var cmpVision = Engine.QueryInterface(this.entity, IID_Vision); 2282 if (!cmpVision) 2283 return ret; 2284 var range = cmpVision.GetRange(); 2285 ret.max = range; 2286 } 2287 else if (this.GetStance().respondHoldGround) 2288 { 2289 var cmpRanged = Engine.QueryInterface(this.entity, IID_Attack); 2290 if (!cmpRanged) 2291 return ret; 2292 var range = cmpRanged.GetRange(cmpRanged.GetBestAttack()); 2293 ret.max = range.max + 50; 2294 } 2295 return ret; 2296 }; 2297 2093 2298 UnitAI.prototype.GetStance = function() 2094 2299 { 2095 2300 return g_Stances[this.stance]; … … 2234 2439 cmpMotion.SetSpeed(speed); 2235 2440 }; 2236 2441 2442 UnitAI.prototype.SetReturnPoint = function(pos) 2443 { 2444 this.returnPoint = {"x": pos.x, "z": pos.z}; 2445 }; 2446 2237 2447 Engine.RegisterComponentType(IID_UnitAI, "UnitAI", UnitAI); -
binaries/data/mods/public/simulation/components/Attack.js
245 245 } 246 246 247 247 Engine.PostMessage(data.target, MT_Attacked, 248 { "attacker": this.entity, "target": data.target });248 { "attacker": this.entity, "target": data.target, "type": data.type }); 249 249 250 250 PlaySound("attack_impact", this.entity); 251 251 }; -
binaries/data/mods/public/simulation/templates/template_unit_support.xml
8 8 <Type>support</Type> 9 9 </Minimap> 10 10 <UnitAI> 11 <DefaultStance> noncombat</DefaultStance>11 <DefaultStance>passive</DefaultStance> 12 12 </UnitAI> 13 13 <Cost> 14 14 <BuildTime>15</BuildTime>