Adds attack move bound to the Ctrl hotkey. Patch from mimo. Refs #1001.
This was SVN commit r13200.
This commit is contained in:
parent
ac32c7266e
commit
18ee932a01
@ -243,8 +243,9 @@ hotkey.selection.group.add.9 = "Shift+9"
|
||||
|
||||
; > SESSION CONTROLS
|
||||
hotkey.session.kill = Delete ; Destroy selected units
|
||||
hotkey.session.attack = Ctrl ; Modifier to force attack instead of another action
|
||||
hotkey.session.attack = AltGr ; Modifier to force attack instead of another action
|
||||
hotkey.session.garrison = Ctrl ; Modifier to garrison when clicking on building
|
||||
hotkey.session.attackmove = Ctrl ; Modifier to attackmove when clicking on a point
|
||||
hotkey.session.queue = Shift ; Modifier to queue unit orders instead of replacing
|
||||
hotkey.session.batchtrain = Shift ; Modifier to train units in batches
|
||||
hotkey.session.massbarter = Shift ; Modifier to barter bunch of resources
|
||||
|
@ -76,6 +76,9 @@ Ctrl + Left Click or Left Drag over unit on map: Remove unit from selection
|
||||
Alt + Left Drag over units on map: Only select military units
|
||||
Ctrl + Left Click on unit/group icon with multiple units selected: Deselect
|
||||
Right Click with a building/buildings selected: sets a rally point for units created/ungarrisoned from that building.
|
||||
Ctrl + Right Click with units selected:
|
||||
- If the cursor is over a structure: Garrison
|
||||
- Otherwise: Attack move
|
||||
|
||||
[font="serif-bold-14"]Overlays
|
||||
[font="serif-14"]Alt + G: Hide/show the GUI
|
||||
|
@ -187,7 +187,7 @@ function getActionInfo(action, target)
|
||||
{
|
||||
if (action == "set-rallypoint" && haveRallyPoints)
|
||||
return {"possible": true};
|
||||
else if (action == "move")
|
||||
else if (action == "move" || action == "attack-move")
|
||||
return {"possible": true};
|
||||
else
|
||||
return {"possible": false};
|
||||
@ -404,7 +404,7 @@ function getActionInfo(action, target)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (action == "move")
|
||||
if (action == "move" || action == "attack-move")
|
||||
return {"possible": true};
|
||||
else
|
||||
return {"possible": false};
|
||||
@ -480,12 +480,13 @@ function determineAction(x, y, fromMinimap)
|
||||
{
|
||||
return {"type": "attack", "cursor": "action-attack", "target": target};
|
||||
}
|
||||
else if (Engine.HotkeyIsPressed("session.garrison"))
|
||||
else if (Engine.HotkeyIsPressed("session.garrison") && getActionInfo("garrison", target).possible)
|
||||
{
|
||||
if (getActionInfo("garrison", target).possible)
|
||||
return {"type": "garrison", "cursor": "action-garrison", "target": target};
|
||||
else
|
||||
return {"type": "none", "cursor": "action-garrison-disabled", "target": undefined};
|
||||
return {"type": "garrison", "cursor": "action-garrison", "target": target};
|
||||
}
|
||||
else if (Engine.HotkeyIsPressed("session.attackmove") && getActionInfo("attack-move", target).possible)
|
||||
{
|
||||
return {"type": "attack-move", "cursor": "action-attack"};
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1320,6 +1321,12 @@ function doAction(action, ev)
|
||||
Engine.GuiInterfaceCall("PlaySound", { "name": "order_walk", "entity": selection[0] });
|
||||
return true;
|
||||
|
||||
case "attack-move":
|
||||
var target = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
|
||||
Engine.PostNetworkCommand({"type": "attack-walk", "entities": selection, "x": target.x, "z": target.z, "queued": queued});
|
||||
Engine.GuiInterfaceCall("PlaySound", { "name": "order_walk", "entity": selection[0] });
|
||||
return true;
|
||||
|
||||
case "attack":
|
||||
Engine.PostNetworkCommand({"type": "attack", "entities": selection, "target": action.target, "queued": queued});
|
||||
Engine.GuiInterfaceCall("PlaySound", { "name": "order_attack", "entity": selection[0] });
|
||||
@ -1419,6 +1426,11 @@ function handleMinimapEvent(target)
|
||||
Engine.GuiInterfaceCall("PlaySound", { "name": "order_walk", "entity": selection[0] });
|
||||
return true;
|
||||
|
||||
case "attack-move":
|
||||
Engine.PostNetworkCommand({"type": "attack-walk", "entities": selection, "x": target.x, "z": target.z, "queued": queued});
|
||||
Engine.GuiInterfaceCall("PlaySound", { "name": "order_walk", "entity": selection[0] });
|
||||
return true;
|
||||
|
||||
case "set-rallypoint":
|
||||
Engine.PostNetworkCommand({"type": "set-rallypoint", "entities": selection, "x": target.x, "z": target.z});
|
||||
// Display rally point at the new coordinates, to avoid display lag
|
||||
|
@ -270,6 +270,33 @@ var UnitFsmSpec = {
|
||||
this.SetNextState("INDIVIDUAL.WALKING");
|
||||
},
|
||||
|
||||
"Order.WalkAndFight": function(msg) {
|
||||
// Let players move captured domestic animals around
|
||||
if (this.IsAnimal() && !this.IsDomestic())
|
||||
{
|
||||
this.FinishOrder();
|
||||
return;
|
||||
}
|
||||
|
||||
// For packable units:
|
||||
// 1. If packed, we can move.
|
||||
// 2. If unpacked, we first need to pack, then follow case 1.
|
||||
if (this.CanPack())
|
||||
{
|
||||
// Case 2: pack
|
||||
this.PushOrderFront("Pack", { "force": true });
|
||||
return;
|
||||
}
|
||||
|
||||
this.SetHeldPosition(this.order.data.x, this.order.data.z);
|
||||
this.MoveToPoint(this.order.data.x, this.order.data.z);
|
||||
if (this.IsAnimal())
|
||||
this.SetNextState("ANIMAL.WALKING"); // WalkAndFight not applicable for animals
|
||||
else
|
||||
this.SetNextState("INDIVIDUAL.WALKINGANDFIGHTING");
|
||||
},
|
||||
|
||||
|
||||
"Order.WalkToTarget": function(msg) {
|
||||
// Let players move captured domestic animals around
|
||||
if (this.IsAnimal() && !this.IsDomestic())
|
||||
@ -353,8 +380,10 @@ var UnitFsmSpec = {
|
||||
if (this.CanUnpack())
|
||||
{
|
||||
// Ignore unforced attacks
|
||||
// this would prevent attacks from AttackVisibleEntity or AttackEntityInZone ?
|
||||
// so we accept attacks against targets for which we have a bonus
|
||||
// TODO: use special stances instead?
|
||||
if (!this.order.data.force)
|
||||
if (!this.order.data.force && this.GetAttackBonus(type, this.order.data.target) < 1.5)
|
||||
{
|
||||
this.FinishOrder();
|
||||
return;
|
||||
@ -628,6 +657,14 @@ var UnitFsmSpec = {
|
||||
this.MoveToPoint(this.order.data.x, this.order.data.z);
|
||||
this.SetNextState("WALKING");
|
||||
},
|
||||
|
||||
"Order.WalkAndFight": function(msg) {
|
||||
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
|
||||
cmpFormation.CallMemberFunction("SetHeldPosition", [msg.data.x, msg.data.z]);
|
||||
|
||||
this.MoveToPoint(this.order.data.x, this.order.data.z);
|
||||
this.SetNextState("WALKINGANDFIGHTING");
|
||||
},
|
||||
|
||||
"Order.MoveIntoFormation": function(msg) {
|
||||
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
|
||||
@ -885,6 +922,54 @@ var UnitFsmSpec = {
|
||||
},
|
||||
},
|
||||
|
||||
"WALKINGANDFIGHTING": {
|
||||
"enter": function(msg) {
|
||||
this.StartTimer(0, 1000);
|
||||
},
|
||||
|
||||
"Timer": function(msg) {
|
||||
// check if there are no enemies to attack
|
||||
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
|
||||
for each (var ent in cmpFormation.members)
|
||||
{
|
||||
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
|
||||
if (cmpUnitAI.FindNewTargets())
|
||||
{
|
||||
if (cmpUnitAI.orderQueue[0] && cmpUnitAI.orderQueue[0].type == "Attack")
|
||||
{
|
||||
var data = cmpUnitAI.orderQueue[0].data;
|
||||
cmpUnitAI.FinishOrder();
|
||||
this.PushOrderFront("Attack", { "target": data.target, "force": false, "forceResponse": data.forceResponse });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"leave": function(msg) {
|
||||
this.StopTimer();
|
||||
},
|
||||
|
||||
"MoveStarted": function(msg) {
|
||||
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
|
||||
cmpFormation.SetRearrange(true);
|
||||
cmpFormation.MoveMembersIntoFormation(true, true);
|
||||
},
|
||||
|
||||
"MoveCompleted": function(msg) {
|
||||
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
|
||||
|
||||
if (this.FinishOrder())
|
||||
{
|
||||
cmpFormation.CallMemberFunction("ResetFinishOrder", []);
|
||||
return;
|
||||
}
|
||||
|
||||
// No more orders left.
|
||||
cmpFormation.Disband();
|
||||
},
|
||||
},
|
||||
|
||||
"FORMING": {
|
||||
"MoveStarted": function(msg) {
|
||||
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
|
||||
@ -938,7 +1023,27 @@ var UnitFsmSpec = {
|
||||
|
||||
// Execute the next order
|
||||
if (this.FinishOrder())
|
||||
{
|
||||
// if WalkAndFight order, look for new target before moving again
|
||||
if (this.orderQueue.length > 0 && this.orderQueue[0].type == "WalkAndFight")
|
||||
{
|
||||
for each (var ent in cmpFormation.members)
|
||||
{
|
||||
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
|
||||
if (cmpUnitAI.FindNewTargets())
|
||||
{
|
||||
if (cmpUnitAI.orderQueue[0] && cmpUnitAI.orderQueue[0].type == "Attack")
|
||||
{
|
||||
var data = cmpUnitAI.orderQueue[0].data;
|
||||
cmpUnitAI.FinishOrder();
|
||||
this.PushOrderFront("Attack", { "target": data.target, "force": false, "forceResponse": data.forceResponse });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// No more order left.
|
||||
cmpFormation.Disband();
|
||||
@ -1146,6 +1251,25 @@ var UnitFsmSpec = {
|
||||
},
|
||||
},
|
||||
|
||||
"WALKINGANDFIGHTING": {
|
||||
"enter": function () {
|
||||
this.StartTimer(0, 1000);
|
||||
this.SelectAnimation("move");
|
||||
},
|
||||
|
||||
"Timer": function(msg) {
|
||||
this.FindNewTargets();
|
||||
},
|
||||
|
||||
"leave": function(msg) {
|
||||
this.StopTimer();
|
||||
},
|
||||
|
||||
"MoveCompleted": function() {
|
||||
this.FinishOrder();
|
||||
},
|
||||
},
|
||||
|
||||
"FLEEING": {
|
||||
"enter": function() {
|
||||
this.PlaySound("panic");
|
||||
@ -1335,8 +1459,13 @@ var UnitFsmSpec = {
|
||||
}
|
||||
|
||||
// Can't reach it, no longer owned by enemy, or it doesn't exist any more - give up
|
||||
// Except if in WalkAndFight mode where we look for more ennemies around before moving again
|
||||
if (this.FinishOrder())
|
||||
{
|
||||
if (this.orderQueue.length > 0 && this.orderQueue[0].type == "WalkAndFight")
|
||||
this.FindNewTargets();
|
||||
return;
|
||||
}
|
||||
|
||||
// See if we can switch to a new nearby enemy
|
||||
if (this.FindNewTargets())
|
||||
@ -3213,6 +3342,14 @@ UnitAI.prototype.GetBestAttackAgainst = function(target)
|
||||
return cmpAttack.GetBestAttackAgainst(target);
|
||||
};
|
||||
|
||||
UnitAI.prototype.GetAttackBonus = function(type, target)
|
||||
{
|
||||
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
||||
if (!cmpAttack)
|
||||
return 1;
|
||||
return cmpAttack.GetAttackBonus(type, target);
|
||||
};
|
||||
|
||||
/**
|
||||
* Try to find one of the given entities which can be attacked,
|
||||
* and start attacking it.
|
||||
@ -3425,6 +3562,7 @@ UnitAI.prototype.ComputeWalkingDistance = function()
|
||||
switch (order.type)
|
||||
{
|
||||
case "Walk":
|
||||
case "WalkAndFight":
|
||||
case "WalkToPointRange":
|
||||
case "MoveIntoFormation":
|
||||
case "GatherNearPosition":
|
||||
@ -3510,6 +3648,15 @@ UnitAI.prototype.WalkToTarget = function(target, queued)
|
||||
this.AddOrder("WalkToTarget", { "target": target, "force": true }, queued);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds walk-and-fight order to queue, this only occurs in response
|
||||
* to a player order, and so is forced.
|
||||
*/
|
||||
UnitAI.prototype.WalkAndFight = function(x, z, queued)
|
||||
{
|
||||
this.AddOrder("WalkAndFight", { "x": x, "z": z, "force": true }, queued);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds leave foundation order to queue, treated as forced.
|
||||
*/
|
||||
|
@ -88,6 +88,13 @@ function ProcessCommand(player, cmd)
|
||||
});
|
||||
break;
|
||||
|
||||
case "attack-walk":
|
||||
var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
|
||||
GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) {
|
||||
cmpUnitAI.WalkAndFight(cmd.x, cmd.z, cmd.queued);
|
||||
});
|
||||
break;
|
||||
|
||||
case "attack":
|
||||
if (g_DebugCommands && !(IsOwnedByEnemyOfPlayer(player, cmd.target) || IsOwnedByNeutralOfPlayer(player, cmd.target)))
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user