Ticket #2034: escort-v2.2.diff
File escort-v2.2.diff, 38.4 KB (added by , 10 years ago) |
---|
-
binaries/data/config/default.cfg
272 272 hotkey.session.attackmove = Ctrl ; Modifier to attackmove when clicking on a point 273 273 hotkey.session.garrison = Ctrl ; Modifier to garrison when clicking on building 274 274 hotkey.session.autorallypoint = Ctrl ; Modifier to set the rally point on the building itself 275 hotkey.session.guard = "G" ; Modifier to escort/guard when clicking on unit/building 275 276 hotkey.session.queue = Shift ; Modifier to queue unit orders instead of replacing 276 277 hotkey.session.batchtrain = Shift ; Modifier to train units in batches 277 278 hotkey.session.massbarter = Shift ; Modifier to barter bunch of resources … … 300 301 hotkey.menu.toggle = "F10" ; Toggle in-game menu 301 302 hotkey.timeelapsedcounter.toggle = "F12" ; Toggle time elapsed counter 302 303 hotkey.session.showstatusbars = Tab ; Toggle display of status bars 304 hotkey.session.highlightguarding = PgDn ; Toggle highlight of guarding units 305 hotkey.session.highlightguarded = PgUp ; Toggle highlight of guarded units 303 306 304 307 ; > HOTKEYS ONLY 305 308 hotkey.chat = Return ; Toggle chat window -
binaries/data/mods/public/art/textures/cursors/action-guard-disabled.png
Impossible d'afficher : fichier considéré comme binaire. svn:mime-type = application/octet-stream
-
binaries/data/mods/public/art/textures/cursors/action-guard-disabled.txt
Modification de propriétés sur binaries/data/mods/public/art/textures/cursors/action-guard-disabled.png ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property
1 1 1 -
binaries/data/mods/public/art/textures/cursors/action-remove-guard.png
Impossible d'afficher : fichier considéré comme binaire. svn:mime-type = application/octet-stream
-
binaries/data/mods/public/art/textures/cursors/action-remove-guard.txt
Modification de propriétés sur binaries/data/mods/public/art/textures/cursors/action-remove-guard.png ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property
1 1 1 -
binaries/data/mods/public/art/textures/ui/session/icons/add-guard.png
Impossible d'afficher : fichier considéré comme binaire. svn:mime-type = application/octet-stream
-
binaries/data/mods/public/art/textures/ui/session/icons/remove-guard.png
Modification de propriétés sur binaries/data/mods/public/art/textures/ui/session/icons/add-guard.png ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Impossible d'afficher : fichier considéré comme binaire. svn:mime-type = application/octet-stream
-
binaries/data/mods/public/gui/session/input.js
Modification de propriétés sur binaries/data/mods/public/art/textures/ui/session/icons/remove-guard.png ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property
15 15 const ACTION_NONE = 0; 16 16 const ACTION_GARRISON = 1; 17 17 const ACTION_REPAIR = 2; 18 const ACTION_GUARD = 3; 18 19 var preSelectedAction = ACTION_NONE; 19 20 20 21 const INPUT_NORMAL = 0; … … 200 201 } 201 202 else if (action == "move" || action == "attack-move") 202 203 return {"possible": true}; 204 else if (action == "remove-guard") 205 return {"possible": true}; 203 206 else 204 207 return {"possible": false}; 205 208 } … … 426 429 if (entState.attack && targetState.hitpoints && (enemyOwned || neutralOwned)) 427 430 return {"possible": Engine.GuiInterfaceCall("CanAttack", {"entity": entState.id, "target": target})}; 428 431 break; 432 case "guard": 433 if (targetState.guard && (playerOwned || mutualAllyOwned)) 434 return {"possible": (entState.unitAI && entState.unitAI.canGuard && !(targetState.unitAI && targetState.unitAI.isGuarding))}; 435 break; 429 436 } 430 437 } 431 438 if (action == "move" || action == "attack-move") … … 502 509 else 503 510 return {"type": "none", "cursor": "action-repair-disabled", "target": undefined}; 504 511 break; 512 case ACTION_GUARD: 513 if (getActionInfo("guard", target).possible) 514 return {"type": "guard", "cursor": "action-guard", "target": target}; 515 else 516 return {"type": "none", "cursor": "action-guard-disabled", "target": undefined}; 517 break; 505 518 } 506 519 } 507 520 else if (Engine.HotkeyIsPressed("session.attack") && getActionInfo("attack", target).possible) … … 516 529 { 517 530 return {"type": "attack-move", "cursor": "action-attack-move"}; 518 531 } 532 else if (Engine.HotkeyIsPressed("session.guard") && getActionInfo("guard", target).possible) 533 { 534 return {"type": "guard", "cursor": "action-guard", "target": target}; 535 } 536 else if (Engine.HotkeyIsPressed("session.guard") && getActionInfo("remove-guard", target).possible) 537 { 538 var isGuarding = selection.some(function(ent) { 539 var entState = GetEntityState(ent); 540 return entState && entState.unitAI && entState.unitAI.isGuarding; 541 }); 542 if (isGuarding) 543 return {"type": "remove-guard", "cursor": "action-remove-guard"}; 544 } 519 545 else 520 546 { 521 547 if ((actionInfo = getActionInfo("setup-trade-route", target)).possible) … … 1053 1079 recalculateStatusBarDisplay(); 1054 1080 } 1055 1081 1082 if (ev.hotkey == "session.highlightguarding") 1083 { 1084 g_ShowGuarding = (ev.type == "hotkeydown"); 1085 updateAdditionnalHighlight(); 1086 } 1087 1088 if (ev.hotkey == "session.highlightguarded") 1089 { 1090 g_ShowGuarded = (ev.type == "hotkeydown"); 1091 updateAdditionnalHighlight(); 1092 } 1093 1056 1094 // State-machine processing: 1057 1095 1058 1096 switch (inputState) … … 1114 1152 case INPUT_PRESELECTEDACTION: 1115 1153 switch (ev.type) 1116 1154 { 1155 case "mousemotion": 1156 // Highlight the first hovered entity (if any) 1157 var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y); 1158 if (ents.length) 1159 g_Selection.setHighlightList([ents[0]]); 1160 else 1161 g_Selection.setHighlightList([]); 1162 1163 return false; 1164 1117 1165 case "mousebuttondown": 1118 1166 if (ev.button == SDL_BUTTON_LEFT && preSelectedAction != ACTION_NONE) 1119 1167 { … … 1394 1442 Engine.GuiInterfaceCall("PlaySound", { "name": "order_garrison", "entity": selection[0] }); 1395 1443 return true; 1396 1444 1445 case "guard": 1446 Engine.PostNetworkCommand({"type": "guard", "entities": selection, "target": action.target, "queued": queued}); 1447 Engine.GuiInterfaceCall("PlaySound", { "name": "order_guard", "entity": selection[0] }); 1448 return true; 1449 1450 case "remove-guard": 1451 Engine.PostNetworkCommand({"type": "remove-guard", "entities": selection, "target": action.target, "queued": queued}); 1452 Engine.GuiInterfaceCall("PlaySound", { "name": "order_guard", "entity": selection[0] }); 1453 return true; 1454 1397 1455 case "set-rallypoint": 1398 1456 var pos = undefined; 1399 1457 // if there is a position set in the action then use this so that when setting a … … 1820 1878 inputState = INPUT_PRESELECTEDACTION; 1821 1879 preSelectedAction = ACTION_REPAIR; 1822 1880 break; 1881 case "add-guard": 1882 inputState = INPUT_PRESELECTEDACTION; 1883 preSelectedAction = ACTION_GUARD; 1884 break; 1885 case "remove-guard": 1886 removeGuard(); 1887 break; 1823 1888 case "unload-all": 1824 1889 unloadAll(); 1825 1890 break; … … 2106 2171 2107 2172 } 2108 2173 2174 function removeGuard() 2175 { 2176 // Filter out all entities that are currently guarding/escorting. 2177 var entities = g_Selection.toList().filter(function(e) { 2178 var state = GetEntityState(e); 2179 return (state && state.unitAI && state.unitAI.isGuarding); 2180 }); 2181 2182 Engine.PostNetworkCommand({"type": "remove-guard", "entities": entities}); 2183 } 2184 2109 2185 function clearSelection() 2110 2186 { 2111 2187 if(inputState==INPUT_BUILDING_PLACEMENT || inputState==INPUT_BUILDING_WALL_PATHING) -
binaries/data/mods/public/gui/session/session.js
46 46 const DEFAULT_POPULATION_COLOR = "white"; 47 47 const POPULATION_ALERT_COLOR = "orange"; 48 48 49 // List of additionnal entities to highlight 50 var g_ShowGuarding = false; 51 var g_ShowGuarded = false; 52 var g_AdditionnalHighlight = []; 53 49 54 function GetSimState() 50 55 { 51 56 if (!g_SimState) … … 443 448 if (g_ShowAllStatusBars) 444 449 recalculateStatusBarDisplay(); 445 450 451 if (g_ShowGuarding || g_ShowGuarded) 452 updateAdditionnalHighlight(); 453 446 454 updateHero(); 447 455 updateGroups(); 448 456 updateDebug(); … … 665 673 Engine.GuiInterfaceCall("SetStatusBars", { "entities": entities, "enabled": g_ShowAllStatusBars }); 666 674 } 667 675 676 // Update the additionnal list of entities to be highlighted. 677 function updateAdditionnalHighlight() 678 { 679 var entsAdd = []; // list of entities units to be highlighted 680 var entsRemove = []; 681 var highlighted = g_Selection.toList(); 682 for each (var ent in g_Selection.highlighted) 683 highlighted.push(ent); 684 685 if (g_ShowGuarding) 686 { 687 // flag the guarding entities to add in this additionnal highlight 688 for each (var sel in g_Selection.selected) 689 { 690 var state = GetEntityState(sel); 691 if (!state.guard || !state.guard.entities.length) 692 continue; 693 for each (var ent in state.guard.entities) 694 if (highlighted.indexOf(ent) == -1 && entsAdd.indexOf(ent) == -1) 695 entsAdd.push(ent); 696 } 697 } 698 699 if (g_ShowGuarded) 700 { 701 // flag the guarded entities to add in this additionnal highlight 702 for each (var sel in g_Selection.selected) 703 { 704 var state = GetEntityState(sel); 705 if (!state.unitAI || !state.unitAI.isGuarding) 706 continue; 707 var ent = state.unitAI.isGuarding; 708 if (highlighted.indexOf(ent) == -1 && entsAdd.indexOf(ent) == -1) 709 entsAdd.push(ent); 710 } 711 } 712 713 // flag the entities to remove (from the previously added) from this additionnal highlight 714 for each (var ent in g_AdditionnalHighlight) 715 if (highlighted.indexOf(ent) == -1 && entsAdd.indexOf(ent) == -1 && entsRemove.indexOf(ent) == -1) 716 entsRemove.push(ent); 717 718 _setHighlight(entsAdd , HIGHLIGHTED_ALPHA, true ); 719 _setHighlight(entsRemove, 0 , false); 720 g_AdditionnalHighlight = entsAdd; 721 } 722 668 723 // Temporarily adding this here 669 724 const AMBIENT_TEMPERATE = "temperate"; 670 725 var currentAmbient; -
binaries/data/mods/public/gui/session/utility_functions.js
303 303 }); 304 304 } 305 305 306 if (entState.unitAI && entState.unitAI.canGuard && !entState.unitAI.isGuarding) 307 { 308 commands.push({ 309 "name": "add-guard", 310 "tooltip": "Guard", 311 "icon": "add-guard.png" 312 }); 313 } 314 315 if (entState.unitAI && entState.unitAI.isGuarding) 316 { 317 commands.push({ 318 "name": "remove-guard", 319 "tooltip": "Remove guard", 320 "icon": "remove-guard.png" 321 }); 322 } 323 306 324 return commands; 307 325 } 308 326 -
binaries/data/mods/public/simulation/components/Guard.js
1 function Guard() {} 2 3 Guard.prototype.Schema = 4 "<empty/>"; 5 6 Guard.prototype.Init = function() 7 { 8 this.entities = []; 9 }; 10 11 Guard.prototype.GetRange = function(entity) 12 { 13 var range = 8; 14 var cmpFootprint = Engine.QueryInterface(entity, IID_Footprint); 15 if (cmpFootprint) 16 { 17 var shape = cmpFootprint.GetShape(); 18 if (shape.type == "square") 19 range += Math.sqrt(shape.depth*shape.depth + shape.width*shape.width)*2/3; 20 else if (shape.type == "circle") 21 range += shape.radius*3/2; 22 } 23 return range; 24 }; 25 26 Guard.prototype.GetEntities = function() 27 { 28 return this.entities.slice(); 29 }; 30 31 Guard.prototype.SetEntities = function(entities) 32 { 33 this.entities = entities; 34 }; 35 36 Guard.prototype.AddGuard = function(ent) 37 { 38 if (this.entities.indexOf(ent) != -1) 39 return; 40 this.entities.push(ent); 41 }; 42 43 Guard.prototype.RemoveGuard = function(ent) 44 { 45 var index = this.entities.indexOf(ent); 46 if (index != -1) 47 this.entities.splice(index, 1); 48 }; 49 50 Guard.prototype.OnAttacked = function(msg) 51 { 52 for each (var ent in this.entities) 53 Engine.PostMessage(ent, MT_GuardedAttacked, { "guarded": this.entity, "data": msg }); 54 }; 55 56 /** 57 * Update list of guarding/escorting entities if one gets renamed (e.g. by promotion) 58 */ 59 Guard.prototype.OnGlobalEntityRenamed = function(msg) 60 { 61 var entityIndex = this.entities.indexOf(msg.entity); 62 if (entityIndex != -1) 63 this.entities[entityIndex] = msg.newentity; 64 }; 65 66 /** 67 * If an entity is captured, or about to be killed (so its owner 68 * changes to '-1'), update the guards list 69 */ 70 Guard.prototype.OnGlobalOwnershipChanged = function(msg) 71 { 72 // the ownership change may be on the guarded 73 if (this.entity == msg.entity) 74 { 75 var entities = this.GetEntities(); 76 for each (var entity in entities) 77 { 78 if (msg.to == -1 || !IsOwnedByMutualAllyOfEntity(this.entity, entity)) 79 { 80 var cmpUnitAI = Engine.QueryInterface(entity, IID_UnitAI); 81 if (cmpUnitAI && cmpUnitAI.IsGuardOf() && cmpUnitAI.IsGuardOf() == this.entity) 82 cmpUnitAI.RemoveGuard(); 83 else 84 this.RemoveGuard(entity); 85 } 86 } 87 this.entities = entities; 88 return; 89 } 90 91 // or on some of its guards 92 if (this.entities.indexOf(msg.entity) != -1) 93 { 94 if (msg.to == -1) 95 this.RemoveGuard(msg.entity); 96 else if(!IsOwnedByMutualAllyOfEntity(this.entity, msg.entity)) 97 { 98 var cmpUnitAI = Engine.QueryInterface(msg.entity, IID_UnitAI); 99 if (cmpUnitAI) 100 cmpUnitAI.RemoveGuard(); 101 else 102 this.RemoveGuard(msg.entity); 103 } 104 } 105 }; 106 107 Engine.RegisterComponentType(IID_Guard, "Guard", Guard); -
binaries/data/mods/public/simulation/components/GuiInterface.js
350 350 "state": cmpUnitAI.GetCurrentState(), 351 351 "orders": cmpUnitAI.GetOrders(), 352 352 "hasWorkOrders": cmpUnitAI.HasWorkOrders(), 353 "canGuard": cmpUnitAI.CanGuard(), 354 "isGuarding": cmpUnitAI.IsGuardOf(), 353 355 }; 354 356 // Add some information needed for ungarrisoning 355 357 if (cmpUnitAI.isGarrisoned && ret.player !== undefined) 356 358 ret.template = "p" + ret.player + "&" + ret.template; 357 359 } 358 360 361 var cmpGuard = Engine.QueryInterface(ent, IID_Guard); 362 if (cmpGuard) 363 { 364 ret.guard = { 365 "entities": cmpGuard.GetEntities(), 366 }; 367 } 368 359 369 var cmpGate = Engine.QueryInterface(ent, IID_Gate); 360 370 if (cmpGate) 361 371 { -
binaries/data/mods/public/simulation/components/Pack.js
162 162 if (cmpUnitAI.GetStanceName()) 163 163 cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName()); 164 164 cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders()); 165 cmpNewUnitAI.SetGuardOf(cmpUnitAI.IsGuardOf()); 165 166 } 166 167 168 // Maintain the list of guards 169 var cmpGuard = Engine.QueryInterface(this.entity, IID_Guard); 170 var cmpNewGuard = Engine.QueryInterface(newEntity, IID_Guard); 171 if (cmpGuard && cmpNewGuard) 172 cmpNewGuard.SetEntities(cmpGuard.GetEntities()); 173 167 174 Engine.BroadcastMessage(MT_EntityRenamed, { entity: this.entity, newentity: newEntity }); 168 175 169 176 // Play notification sound -
binaries/data/mods/public/simulation/components/Promotion.js
80 80 cmpPromotedUnitAI.AddOrders(orders); 81 81 var workOrders = cmpCurrentUnitAI.GetWorkOrders(); 82 82 cmpPromotedUnitAI.SetWorkOrders(workOrders); 83 cmpPromotedUnitAI.SetGuardOf(cmpCurrentUnitAI.IsGuardOf()); 83 84 85 var cmpCurrentUnitGuard = Engine.QueryInterface(this.entity, IID_Guard); 86 var cmpPromotedUnitGuard = Engine.QueryInterface(promotedUnitEntity, IID_Guard); 87 if (cmpCurrentUnitGuard && cmpPromotedUnitGuard) 88 cmpPromotedUnitGuard.SetEntities(cmpCurrentUnitGuard.GetEntities()); 89 84 90 Engine.BroadcastMessage(MT_EntityRenamed, { entity: this.entity, newentity: promotedUnitEntity }); 85 91 86 92 // Destroy current entity -
binaries/data/mods/public/simulation/components/UnitAI.js
18 18 "<element name='FleeDistance'>" + 19 19 "<ref name='positiveDecimal'/>" + 20 20 "</element>" + 21 "<element name='CanGuard'>" + 22 "<data type='boolean'/>" + 23 "</element>" + 21 24 "<optional>" + 22 25 "<interleave>" + 23 26 "<element name='NaturalBehaviour' a:help='Behaviour of the unit in the absence of player commands (intended for animals)'>" + … … 164 167 // ignore 165 168 }, 166 169 170 "GuardedAttacked": function(msg) { 171 // ignore 172 }, 173 167 174 // Formation handlers: 168 175 169 176 "FormationLeave": function(msg) { … … 354 361 } 355 362 }, 356 363 364 "Order.Guard": function(msg) { 365 if (!this.AddGuard(this.order.data.target)) 366 { 367 this.FinishOrder(); 368 return; 369 } 370 371 if (this.MoveToTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) 372 this.SetNextState("INDIVIDUAL.GUARD.ESCORTING"); 373 else 374 this.SetNextState("INDIVIDUAL.GUARD.GUARDING"); 375 }, 376 357 377 "Order.Flee": function(msg) { 358 // We use the distance between the en ities to account for ranged attacks378 // We use the distance between the entities to account for ranged attacks 359 379 var distance = DistanceBetweenEntities(this.entity, this.order.data.target) + (+this.template.FleeDistance); 360 380 var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); 361 381 if (cmpUnitMotion.MoveToTargetRange(this.order.data.target, distance, -1)) … … 740 760 this.FinishOrder(); 741 761 }, 742 762 763 "Order.Guard": function(msg) { 764 var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); 765 cmpFormation.CallMemberFunction("Guard", [msg.data.target, false]); 766 cmpFormation.Disband(); 767 }, 768 743 769 "Order.Stop": function(msg) { 744 770 var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); 745 771 cmpFormation.CallMemberFunction("Stop", [false]); … … 1259 1285 } 1260 1286 }, 1261 1287 1288 "GuardedAttacked": function(msg) { 1289 // do nothing if we have a forced order in queue before the guard order 1290 for (var i = 0; i < this.orderQueue.length; ++i) 1291 { 1292 if (this.orderQueue[i].type == "Guard") 1293 break; 1294 if (this.orderQueue[i].data && this.orderQueue[i].data.force) 1295 return; 1296 } 1297 // if we already are targeting another unit still alive, finish with it first 1298 if (this.order && this.order.type == "WalkAndFight") 1299 if (this.order.data.target != msg.data.attacker && this.TargetIsAlive(msg.data.attacker)) 1300 return; 1301 1302 var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); 1303 var cmpHealth = Engine.QueryInterface(this.isGuardOf, IID_Health); 1304 if (cmpIdentity && cmpIdentity.HasClass("Support") && 1305 cmpHealth && cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints()) 1306 { 1307 if (this.CanHeal(this.isGuardOf)) 1308 this.PushOrderFront("Heal", { "target": this.isGuardOf, "force": false }); 1309 else if (this.CanRepair(this.isGuardOf) && cmpHealth.IsRepairable()) 1310 this.PushOrderFront("Repair", { "target": this.isGuardOf, "autocontinue": false, "force": false }); 1311 return; 1312 } 1313 1314 // target the unit 1315 var cmpPosition = Engine.QueryInterface(msg.data.attacker, IID_Position); 1316 if (!cmpPosition || !cmpPosition.IsInWorld()) 1317 return; 1318 var pos = cmpPosition.GetPosition(); 1319 this.PushOrderFront("WalkAndFight", { "x": pos.x, "z": pos.z, "target": msg.data.attacker, "force": false }); 1320 // if we already had a WalkAndFight, keep only the most recent one in case the target has moved 1321 if (this.orderQueue[1] && this.orderQueue[1].type == "WalkAndFight") 1322 this.orderQueue.splice(1, 1); 1323 }, 1324 1262 1325 "IDLE": { 1263 1326 "enter": function() { 1264 1327 // Switch back to idle animation to guarantee we won't 1265 1328 // get stuck with an incorrect animation 1266 1329 this.SelectAnimation("idle"); 1267 1330 1331 // If the unit is guarding/escorting, go back to its duty 1332 if (this.isGuardOf) 1333 { 1334 this.Guard(this.isGuardOf, false); 1335 return true; 1336 } 1337 1268 1338 // The GUI and AI want to know when a unit is idle, but we don't 1269 1339 // want to send frequent spurious messages if the unit's only 1270 1340 // idle for an instant and will quickly go off and do something else. … … 1366 1436 }, 1367 1437 }, 1368 1438 1439 "GUARD": { 1440 "enter": function () { 1441 }, 1442 1443 "RemoveGuard": function() { 1444 this.StopMoving(); 1445 this.FinishOrder(); 1446 }, 1447 1448 "leave": function () { 1449 }, 1450 1451 "ESCORTING": { 1452 "enter": function () { 1453 // Show weapons rather than carried resources. 1454 this.SetGathererAnimationOverride(true); 1455 1456 this.StartTimer(0, 1000); 1457 this.SelectAnimation("move"); 1458 this.SetHeldPositionOnEntity(this.isGuardOf); 1459 return false; 1460 }, 1461 1462 "Timer": function(msg) { 1463 // Check the target is alive 1464 if (!this.TargetIsAlive(this.isGuardOf)) 1465 { 1466 this.StopMoving(); 1467 this.FinishOrder(); 1468 return; 1469 } 1470 this.SetHeldPositionOnEntity(this.isGuardOf); 1471 }, 1472 1473 "leave": function(msg) { 1474 this.SetMoveSpeed(this.GetWalkSpeed()); 1475 this.StopTimer(); 1476 }, 1477 1478 "MoveStarted": function(msg) { 1479 // Adapt the speed to the one of the target if needed 1480 var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); 1481 if (cmpUnitMotion.IsInTargetRange(this.isGuardOf, 0, 3*this.guardRange)) 1482 { 1483 var cmpUnitAI = Engine.QueryInterface(this.isGuardOf, IID_UnitAI); 1484 if (cmpUnitAI) 1485 { 1486 var speed = cmpUnitAI.GetWalkSpeed(); 1487 if (speed < this.GetWalkSpeed()) 1488 this.SetMoveSpeed(speed); 1489 } 1490 } 1491 }, 1492 1493 "MoveCompleted": function() { 1494 this.SetMoveSpeed(this.GetWalkSpeed()); 1495 if (!this.MoveToTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) 1496 this.SetNextState("GUARDING"); 1497 }, 1498 }, 1499 1500 "GUARDING": { 1501 "enter": function () { 1502 this.StartTimer(1000, 1000); 1503 this.SetHeldPositionOnEntity(this.entity); 1504 this.SelectAnimation("idle"); 1505 return false; 1506 }, 1507 1508 "LosRangeUpdate": function(msg) { 1509 // Start attacking one of the newly-seen enemy (if any) 1510 if (this.GetStance().targetVisibleEnemies) 1511 this.AttackEntitiesByPreference(msg.data.added); 1512 }, 1513 1514 "LosGaiaRangeUpdate": function(msg) { 1515 // Start attacking one of the newly-seen enemy (if any) 1516 if (this.GetStance().targetVisibleEnemies) 1517 this.AttackGaiaEntitiesByPreference(msg.data.added); 1518 }, 1519 1520 "Timer": function(msg) { 1521 // Check the target is alive 1522 if (!this.TargetIsAlive(this.isGuardOf)) 1523 { 1524 this.FinishOrder(); 1525 return; 1526 } 1527 // then check is the target has moved 1528 if (this.MoveToTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) 1529 this.SetNextState("ESCORTING"); 1530 else 1531 { 1532 // if nothing better to do, check if the guarded needs to be healed or repaired 1533 var cmpHealth = Engine.QueryInterface(this.isGuardOf, IID_Health); 1534 if (cmpHealth && (cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints())) 1535 { 1536 if (this.CanHeal(this.isGuardOf)) 1537 this.PushOrderFront("Heal", { "target": this.isGuardOf, "force": false }); 1538 else if (this.CanRepair(this.isGuardOf) && cmpHealth.IsRepairable()) 1539 this.PushOrderFront("Repair", { "target": this.isGuardOf, "autocontinue": false, "force": false }); 1540 } 1541 } 1542 }, 1543 1544 "leave": function(msg) { 1545 this.StopTimer(); 1546 }, 1547 }, 1548 }, 1549 1369 1550 "FLEEING": { 1370 1551 "enter": function() { 1371 1552 this.PlaySound("panic"); … … 2715 2896 // Queue of remembered works 2716 2897 this.workOrders = []; 2717 2898 2899 this.isGuardOf = undefined; 2900 2718 2901 // For preventing increased action rate due to Stop orders or target death. 2719 2902 this.lastAttacked = undefined; 2720 2903 this.lastHealed = undefined; … … 3039 3222 error("FinishOrder called for entity " + this.entity + " (" + template + ") when order queue is empty\n" + stack); 3040 3223 } 3041 3224 3042 // Remove the order from the queue3043 3225 this.orderQueue.shift(); 3044 3226 this.order = this.orderQueue[0]; 3045 3227 … … 3391 3573 for each (var order in this.orderQueue) 3392 3574 if (order.data && order.data.target && order.data.target == msg.entity) 3393 3575 order.data.target = msg.newentity; 3576 3577 if (this.isGuardOf && this.isGuardOf == msg.entity) 3578 this.isGuardOf = msg.newentity; 3394 3579 }; 3395 3580 3396 3581 UnitAI.prototype.OnAttacked = function(msg) … … 3398 3583 UnitFsm.ProcessMessage(this, {"type": "Attacked", "data": msg}); 3399 3584 }; 3400 3585 3586 UnitAI.prototype.OnGuardedAttacked = function(msg) 3587 { 3588 UnitFsm.ProcessMessage(this, {"type": "GuardedAttacked", "data": msg.data}); 3589 }; 3590 3401 3591 UnitAI.prototype.OnHealthChanged = function(msg) 3402 3592 { 3403 3593 UnitFsm.ProcessMessage(this, {"type": "HealthChanged", "from": msg.from, "to": msg.to}); … … 4043 4233 if (force) 4044 4234 return false; 4045 4235 4236 // If we are guarding/escorting, don't abandon as long as the guarded is in target range of the attacker 4237 if (this.isGuardOf) 4238 { 4239 var cmpUnitAI = Engine.QueryInterface(target, IID_UnitAI); 4240 var cmpAttack = Engine.QueryInterface(target, IID_Attack); 4241 if (cmpUnitAI && cmpAttack) 4242 { 4243 for each (var type in cmpAttack.GetAttackTypes()) 4244 if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, IID_Attack, type)) 4245 return false; 4246 } 4247 } 4248 4046 4249 // Stop if we're in hold-ground mode and it's too far from the holding point 4047 4250 if (this.GetStance().respondHoldGround) 4048 4251 { … … 4077 4280 if (this.GetStance().respondChase) 4078 4281 return true; 4079 4282 4283 // If we are guarding/escorting, chase at least as long as the guarded is in target range of the attacker 4284 if (this.isGuardOf) 4285 { 4286 var cmpUnitAI = Engine.QueryInterface(target, IID_UnitAI); 4287 var cmpAttack = Engine.QueryInterface(target, IID_Attack); 4288 if (cmpUnitAI && cmpAttack) 4289 { 4290 for each (var type in cmpAttack.GetAttackTypes()) 4291 if (cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, IID_Attack, type)) 4292 return true; 4293 } 4294 } 4295 4080 4296 if (force) 4081 4297 return true; 4082 4298 … … 4177 4393 4178 4394 case "WalkToTarget": 4179 4395 case "WalkToTargetRange": // This doesn't move to the target (just into range), but a later order will. 4396 case "Guard": 4180 4397 case "Flee": 4181 4398 case "LeaveFoundation": 4182 4399 case "Attack": … … 4222 4439 }; 4223 4440 4224 4441 /** 4442 * Adds guard/escort order to the queue, forced by the player. 4443 */ 4444 UnitAI.prototype.Guard = function(target, queued) 4445 { 4446 if (!this.CanGuard()) 4447 { 4448 this.WalkToTarget(target, queued); 4449 return; 4450 } 4451 4452 // if we already had an old guard order, do nothing if the target is the same 4453 // and the order is running, otherwise remove the previous order 4454 if (this.isGuardOf) 4455 { 4456 if (this.isGuardOf == target && this.order && this.order.type == "Guard") 4457 return; 4458 else 4459 this.RemoveGuard(); 4460 } 4461 4462 this.AddOrder("Guard", { "target": target, "force": false }, queued); 4463 }; 4464 4465 UnitAI.prototype.AddGuard = function(target) 4466 { 4467 if (!this.CanGuard()) 4468 return false; 4469 var cmpGuard = Engine.QueryInterface(target, IID_Guard); 4470 if (!cmpGuard) 4471 return false; 4472 // Do not allow to guard a unit already guarding 4473 var cmpUnitAI = Engine.QueryInterface(target, IID_UnitAI); 4474 if (cmpUnitAI && cmpUnitAI.IsGuardOf()) 4475 return false; 4476 this.isGuardOf = target; 4477 this.guardRange = cmpGuard.GetRange(this.entity); 4478 cmpGuard.AddGuard(this.entity); 4479 return true; 4480 }; 4481 4482 UnitAI.prototype.RemoveGuard = function() 4483 { 4484 if (this.isGuardOf) 4485 { 4486 var cmpGuard = Engine.QueryInterface(this.isGuardOf, IID_Guard); 4487 if (cmpGuard) 4488 cmpGuard.RemoveGuard(this.entity); 4489 this.guardRange = undefined; 4490 this.isGuardOf = undefined; 4491 } 4492 4493 if (!this.order) 4494 return; 4495 4496 if (this.order.type == "Guard") 4497 UnitFsm.ProcessMessage(this, {"type": "RemoveGuard"}); 4498 else 4499 for (var i = 1; i < this.orderQueue.length; ++i) 4500 if (this.orderQueue[i].type == "Guard") 4501 this.orderQueue.splice(i, 1); 4502 }; 4503 4504 UnitAI.prototype.IsGuardOf = function() 4505 { 4506 return this.isGuardOf; 4507 }; 4508 4509 UnitAI.prototype.SetGuardOf = function(entity) 4510 { 4511 // entity may be undefined 4512 this.isGuardOf = entity; 4513 }; 4514 4515 UnitAI.prototype.CanGuard = function() 4516 { 4517 // Formation controllers should always respond to commands 4518 // (then the individual units can make up their own minds) 4519 if (this.IsFormationController()) 4520 return true; 4521 4522 // Do not let a unit already guarded to guard. This would work in principle, 4523 // but would clutter the gui with too much buttons to take all cases into account 4524 var cmpGuard = Engine.QueryInterface(this.entity, IID_Guard); 4525 if (cmpGuard && cmpGuard.GetEntities().length) 4526 return false; 4527 4528 return (this.template.CanGuard == "true"); 4529 }; 4530 4531 /** 4225 4532 * Adds walk order to queue, forced by the player. 4226 4533 */ 4227 4534 UnitAI.prototype.Walk = function(x, z, queued) … … 4682 4989 this.heldPosition = {"x": x, "z": z}; 4683 4990 }; 4684 4991 4992 UnitAI.prototype.SetHeldPositionOnEntity = function(entity) 4993 { 4994 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 4995 if (!cmpPosition || !cmpPosition.IsInWorld()) 4996 return; 4997 var pos = cmpPosition.GetPosition(); 4998 this.SetHeldPosition(pos.x, pos.z); 4999 }; 5000 4685 5001 UnitAI.prototype.GetHeldPosition = function(pos) 4686 5002 { 4687 5003 return this.heldPosition; -
binaries/data/mods/public/simulation/components/interfaces/Guard.js
1 Engine.RegisterInterface("Guard"); 2 3 // Message of the form { "guarded": entity, "data": msg }, 4 // sent whenever a guarded/escorted unit is attacked 5 Engine.RegisterMessageType("GuardedAttacked"); -
binaries/data/mods/public/simulation/helpers/Commands.js
180 180 } 181 181 break; 182 182 183 case "remove-guard": 184 for each (var ent in entities) 185 { 186 var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); 187 if(cmpUnitAI) 188 cmpUnitAI.RemoveGuard(); 189 } 190 break; 191 183 192 case "train": 184 193 // Check entity limits 185 194 var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); … … 328 337 } 329 338 break; 330 339 340 case "guard": 341 // Verify that the target can be controlled by the player or is mutualAlly 342 if (CanControlUnitOrIsAlly(cmd.target, player, controlAllUnits)) 343 { 344 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 345 cmpUnitAI.Guard(cmd.target, cmd.queued); 346 }); 347 } 348 else if (g_DebugCommands) 349 { 350 warn("Invalid command: guard/escort target cannot be controlled by player "+player+": "+uneval(cmd)); 351 } 352 break; 353 331 354 case "stop": 332 355 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 333 356 cmpUnitAI.Stop(cmd.queued); -
binaries/data/mods/public/simulation/templates/special/formation.xml
17 17 <DefaultStance>aggressive</DefaultStance> 18 18 <FleeDistance>12.0</FleeDistance> 19 19 <FormationController>true</FormationController> 20 <CanGuard>true</CanGuard> 20 21 </UnitAI> 21 22 <UnitMotion> 22 23 <FormationController>true</FormationController> -
binaries/data/mods/public/simulation/templates/template_structure.xml
15 15 <GarrisonArrowMultiplier>0</GarrisonArrowMultiplier> 16 16 <GarrisonArrowClasses>Ranged Infantry</GarrisonArrowClasses> 17 17 </BuildingAI> 18 <Guard/> 18 19 <BuildRestrictions> 19 20 <PlacementType>land</PlacementType> 20 21 <Territory>own</Territory> -
binaries/data/mods/public/simulation/templates/template_unit.xml
102 102 <DefaultStance>aggressive</DefaultStance> 103 103 <FleeDistance>12.0</FleeDistance> 104 104 <FormationController>false</FormationController> 105 <CanGuard>true</CanGuard> 105 106 </UnitAI> 107 <Guard/> 106 108 <UnitMotion> 107 109 <FormationController>false</FormationController> 108 110 <WalkSpeed>7.5</WalkSpeed> -
binaries/data/mods/public/simulation/templates/template_unit_fauna.xml
22 22 </Minimap> 23 23 <ResourceGatherer disable=""/> 24 24 <UnitAI> 25 <CanGuard>false</CanGuard> 25 26 <RoamDistance>8.0</RoamDistance> 26 27 <FleeDistance>24.0</FleeDistance> 27 28 <RoamTimeMin>2000</RoamTimeMin> … … 29 30 <FeedTimeMin>15000</FeedTimeMin> 30 31 <FeedTimeMax>60000</FeedTimeMax> 31 32 </UnitAI> 33 <Guard disable=""/> 32 34 <UnitMotion> 33 35 <WalkSpeed>5.0</WalkSpeed> 34 36 <Run> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fishing.xml
51 51 <order_attack>actor/ship/boat_move.xml</order_attack> 52 52 </SoundGroups> 53 53 </Sound> 54 <UnitAI> 55 <CanGuard>false</CanGuard> 56 </UnitAI> 54 57 <UnitMotion> 55 58 <WalkSpeed>8.5</WalkSpeed> 56 59 </UnitMotion> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_merchant.xml
48 48 <MaxDistance>10.0</MaxDistance> 49 49 <GainMultiplier>1.0</GainMultiplier> 50 50 </Trader> 51 <UnitAI> 52 <CanGuard>false</CanGuard> 53 </UnitAI> 51 54 <UnitMotion> 52 55 <WalkSpeed>10.5</WalkSpeed> 53 56 </UnitMotion> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege.xml
13 13 <GenericName>Siege</GenericName> 14 14 <RequiredTechnology>phase_city</RequiredTechnology> 15 15 </Identity> 16 <UnitAI> 17 <CanGuard>false</CanGuard> 18 </UnitAI> 16 19 <Position> 17 20 <Anchor>pitch-roll</Anchor> 18 21 </Position> -
binaries/data/mods/public/simulation/templates/template_unit_support_trader.xml
40 40 <MaxDistance>2.0</MaxDistance> 41 41 <GainMultiplier>1.0</GainMultiplier> 42 42 </Trader> 43 <UnitAI> 44 <CanGuard>false</CanGuard> 45 </UnitAI> 43 46 <UnitMotion> 44 47 <WalkSpeed>8.0</WalkSpeed> 45 48 </UnitMotion>