Patrol units. Patch by Imarok, based on patch by svott, reviewed by fatherbushido and bb.

This was SVN commit r18779.
This commit is contained in:
elexis 2016-09-25 21:33:05 +00:00
parent 51b7b80de2
commit a85c6c6967
7 changed files with 215 additions and 2 deletions

View File

@ -287,6 +287,7 @@ attackmoveUnit = "Ctrl+Q" ; Modifier to attackmove targeting only units when
garrison = Ctrl ; Modifier to garrison when clicking on building garrison = Ctrl ; Modifier to garrison when clicking on building
autorallypoint = Ctrl ; Modifier to set the rally point on the building itself autorallypoint = Ctrl ; Modifier to set the rally point on the building itself
guard = "G" ; Modifier to escort/guard when clicking on unit/building guard = "G" ; Modifier to escort/guard when clicking on unit/building
patrol = "P" ; Modifier to patrol a unit
repair = "J" ; Modifier to repair when clicking on building/mechanical unit repair = "J" ; Modifier to repair when clicking on building/mechanical unit
queue = Shift ; Modifier to queue unit orders instead of replacing queue = Shift ; Modifier to queue unit orders instead of replacing
batchtrain = Shift ; Modifier to train units in batches batchtrain = Shift ; Modifier to train units in batches

View File

@ -85,6 +85,7 @@ Tab: See all status bars (which would also show the building progress)
[font="sans-bold-14"]Modify mouse action [font="sans-bold-14"]Modify mouse action
[font="sans-14"]Ctrl + Right Click on building: Garrison [font="sans-14"]Ctrl + Right Click on building: Garrison
J + Right Click on building: Repair J + Right Click on building: Repair
P + Right Click: Patrol
Shift + Right Click: Queue the move/build/gather/etc order Shift + Right Click: Queue the move/build/gather/etc order
Shift + Left click when training unit/s: Add units in batches of five Shift + Left click when training unit/s: Add units in batches of five
Shift + Left Click or Left Drag over unit on map: Add unit to selection Shift + Left Click or Left Drag over unit on map: Add unit to selection

View File

@ -16,6 +16,7 @@ const ACTION_NONE = 0;
const ACTION_GARRISON = 1; const ACTION_GARRISON = 1;
const ACTION_REPAIR = 2; const ACTION_REPAIR = 2;
const ACTION_GUARD = 3; const ACTION_GUARD = 3;
const ACTION_PATROL = 4;
var preSelectedAction = ACTION_NONE; var preSelectedAction = ACTION_NONE;
const INPUT_NORMAL = 0; const INPUT_NORMAL = 0;
@ -205,10 +206,16 @@ function getActionInfo(action, target)
data.targetClasses = Engine.HotkeyIsPressed("session.attackmoveUnit") ? { "attack": ["Unit"] } : { "attack": ["Unit", "Structure"] }; data.targetClasses = Engine.HotkeyIsPressed("session.attackmoveUnit") ? { "attack": ["Unit"] } : { "attack": ["Unit", "Structure"] };
cursor = "action-attack-move"; cursor = "action-attack-move";
} }
else if (Engine.HotkeyIsPressed("session.patrol"))
{
data.command = "patrol";
data.targetClasses = { "attack": ["Unit"] };
cursor = "action-patrol";
}
return { "possible": true, "data": data, "cursor": cursor }; return { "possible": true, "data": data, "cursor": cursor };
} }
return { "possible": (action == "move" || action == "attack-move" || action == "remove-guard") }; return { "possible": ["move", "attack-move", "remove-guard", "patrol"].indexOf(action) > -1 };
} }
// Look at the first targeted entity // Look at the first targeted entity

View File

@ -208,6 +208,52 @@ var unitActions =
"specificness": 10, "specificness": 10,
}, },
"patrol":
{
"execute": function(target, action, selection, queued)
{
Engine.PostNetworkCommand({
"type": "patrol",
"entities": selection,
"x": target.x,
"z": target.z,
"target": action.target,
"targetClasses": { "attack": ["Unit"] }, // patrol should only attack units
"queued": queued,
"allowCapture": false
});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_patrol", "entity": selection[0] });
return true;
},
"getActionInfo": function(entState, targetState)
{
return { "possible": true };
},
"hotkeyActionCheck": function(target, selection)
{
if (!someUnitAI(selection) ||
!Engine.HotkeyIsPressed("session.patrol") ||
!getActionInfo("patrol", target).possible)
return false;
return {
"type": "patrol",
"cursor": "action-patrol",
"target": target
};
},
"preSelectedActionCheck" : function(target)
{
if (preSelectedAction != ACTION_PATROL || !getActionInfo("patrol", target).possible)
return false;
return {
"type": "patrol",
"cursor": "action-patrol",
"target": target
};
},
"specificness": 37,
},
"heal": "heal":
{ {
"execute": function(target, action, selection, queued) "execute": function(target, action, selection, queued)
@ -1219,6 +1265,25 @@ var g_EntityCommands =
}, },
}, },
"patrol": {
"getInfo": function(entState)
{
if (g_Selection.toList().every(ent => !GetEntityState(ent).unitAI))
return false;
return {
"tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.patrol") +
translate("Patrol") + "\n" +
translate("Attack encountered encountered units while avoiding buildings."),
"icon": "patrol.png"
};
},
"execute": function(entState)
{
inputState = INPUT_PRESELECTEDACTION;
preSelectedAction = ACTION_PATROL;
},
},
"share-dropsite": { "share-dropsite": {
"getInfo": function(entState) "getInfo": function(entState)
{ {

View File

@ -514,6 +514,24 @@ UnitAI.prototype.UnitFsmSpec = {
this.FinishOrder(); this.FinishOrder();
}, },
"Order.Patrol": function(msg) {
// Let players move captured domestic animals around
if (this.IsAnimal() || this.IsTurret())
{
this.FinishOrder();
return;
}
if (this.CanPack())
{
this.PushOrderFront("Pack", { "force": true });
return;
}
this.MoveToPoint(this.order.data.x, this.order.data.z);
this.SetNextState("INDIVIDUAL.PATROL");
},
"Order.Heal": function(msg) { "Order.Heal": function(msg) {
// Check the target is alive // Check the target is alive
if (!this.TargetIsAlive(this.order.data.target)) if (!this.TargetIsAlive(this.order.data.target))
@ -829,6 +847,13 @@ UnitAI.prototype.UnitFsmSpec = {
this.FinishOrder(); this.FinishOrder();
}, },
"Order.Patrol": function(msg) {
this.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]);
this.MoveToPoint(this.order.data.x, this.order.data.z);
this.SetNextState("PATROL");
},
"Order.Guard": function(msg) { "Order.Guard": function(msg) {
this.CallMemberFunction("Guard", [msg.data.target, false]); this.CallMemberFunction("Guard", [msg.data.target, false]);
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
@ -1078,6 +1103,59 @@ UnitAI.prototype.UnitFsmSpec = {
}, },
}, },
"PATROL": {
"enter": function(msg) {
// Memorize the origin position in case that we want to go back
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
if (!cmpPosition || !cmpPosition.IsInWorld())
{
this.FinishOrder();
return;
}
if (!this.patrolStartPosOrder)
{
this.patrolStartPosOrder = cmpPosition.GetPosition();
this.patrolStartPosOrder.targetClasses = { "attack": ["Unit"] };
}
this.StartTimer(0, 1000);
},
"Timer": function(msg) {
// Check if there are no enemies to attack
this.FindWalkAndFightTargets();
},
"leave": function(msg) {
this.StopTimer();
delete this.patrolStartPosOrder;
},
"MoveStarted": function(msg) {
let cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
cmpFormation.SetRearrange(true);
cmpFormation.MoveMembersIntoFormation(true, true);
},
"MoveCompleted": function() {
/**
* A-B-A-B-..:
* if the user only commands one patrol order, the patrol will be between
* the last position and the defined waypoint
* A-B-C-..-A-B-..:
* otherwise, the patrol is only between the given patrol commands and the
* last position is not included (last position = the position where the unit
* is located at the time of the first patrol order)
*/
if (this.orderQueue.length == 1)
this.PushOrder("Patrol", this.patrolStartPosOrder);
this.PushOrder(this.order.type, this.order.data);
this.FinishOrder();
},
},
"GARRISON":{ "GARRISON":{
"enter": function() { "enter": function() {
// If the garrisonholder should pickup, warn it so it can take needed action // If the garrisonholder should pickup, warn it so it can take needed action
@ -1555,6 +1633,43 @@ UnitAI.prototype.UnitFsmSpec = {
}, },
}, },
"PATROL": {
"enter": function () {
// Memorize the origin position in case that we want to go back
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
if (!cmpPosition || !cmpPosition.IsInWorld())
{
this.FinishOrder();
return;
}
if (!this.patrolStartPosOrder)
{
this.patrolStartPosOrder = cmpPosition.GetPosition();
this.patrolStartPosOrder.targetClasses = { "attack": ["Unit"] };
}
this.StartTimer(0, 1000);
this.SelectAnimation("move");
},
"leave": function() {
this.StopTimer();
delete this.patrolStartPosOrder;
},
"Timer": function(msg) {
this.FindWalkAndFightTargets();
},
"MoveCompleted": function() {
if (this.orderQueue.length == 1)
this.PushOrder("Patrol",this.patrolStartPosOrder);
this.PushOrder(this.order.type, this.order.data);
this.FinishOrder();
},
},
"GUARD": { "GUARD": {
"RemoveGuard": function() { "RemoveGuard": function() {
this.StopMoving(); this.StopMoving();
@ -4762,6 +4877,7 @@ UnitAI.prototype.GetTargetPositions = function()
case "WalkToPointRange": case "WalkToPointRange":
case "MoveIntoFormation": case "MoveIntoFormation":
case "GatherNearPosition": case "GatherNearPosition":
case "Patrol":
targetPositions.push(new Vector2D(order.data.x, order.data.z)); targetPositions.push(new Vector2D(order.data.x, order.data.z));
break; // and continue the loop break; // and continue the loop
@ -4974,6 +5090,11 @@ UnitAI.prototype.WalkAndFight = function(x, z, targetClasses, queued)
this.AddOrder("WalkAndFight", { "x": x, "z": z, "targetClasses": targetClasses, "force": true }, queued); this.AddOrder("WalkAndFight", { "x": x, "z": z, "targetClasses": targetClasses, "force": true }, queued);
}; };
UnitAI.prototype.Patrol = function(x, z, targetClasses, queued)
{
this.AddOrder("Patrol", { "x": x, "z": z, "targetClasses": targetClasses, "force": true }, queued);
};
/** /**
* Adds leave foundation order to queue, treated as forced. * Adds leave foundation order to queue, treated as forced.
*/ */

View File

@ -176,6 +176,13 @@ var g_Commands = {
}); });
}, },
"patrol": function(player, cmd, data)
{
GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI =>
cmpUnitAI.Patrol(cmd.x, cmd.z, cmd.targetClasses, cmd.queued)
);
},
"heal": function(player, cmd, data) "heal": function(player, cmd, data)
{ {
if (g_DebugCommands && !(IsOwnedByPlayer(player, cmd.target) || IsOwnedByAllyOfPlayer(player, cmd.target))) if (g_DebugCommands && !(IsOwnedByPlayer(player, cmd.target) || IsOwnedByAllyOfPlayer(player, cmd.target)))

View File

@ -32,7 +32,7 @@ function GetRallyPointCommands(cmpRallyPoint, spawnedEnts)
switch (command) switch (command)
{ {
case "gather": case "gather":
ret.push( { ret.push({
"type": "gather-near-position", "type": "gather-near-position",
"entities": spawnedEnts, "entities": spawnedEnts,
"x": rallyPos[i].x, "x": rallyPos[i].x,
@ -70,6 +70,17 @@ function GetRallyPointCommands(cmpRallyPoint, spawnedEnts)
"queued": true "queued": true
}); });
break; break;
case "patrol":
ret.push( {
"type": "patrol",
"entities": spawnedEnts,
"x": rallyPos[i].x,
"z": rallyPos[i].z,
"target": data[i].target,
"targetClasses": data[i].targetClasses,
"queued": true
});
break;
case "attack": case "attack":
ret.push( { ret.push( {
"type": "attack", "type": "attack",