Implement "Call to the Arms"-button.

This allows a player to task entities to drop off their resources and
subsequently attack-move to a specified location with one button.

Patch by: @JCWasmx86
Icon by: @Stan
Differential revision: https://code.wildfiregames.com/D4149
Fixes: #1364
Comments by: @Langbart
Based on a patch by: @Freagarach (https://code.wildfiregames.com/D1868)
This was SVN commit r25868.
This commit is contained in:
Freagarach
2021-08-28 05:52:37 +00:00
parent 81ad9f746b
commit 4b1270f841
7 changed files with 123 additions and 0 deletions
+1
View File
@@ -353,6 +353,7 @@ snaptoedges = Ctrl ; Modifier to align new structures with nearby exis
toggledefaultformation = "" ; Switch between null default formation and the last default formation used (defaults to "box")
flare = K ; Modifier to send a flare to your allies
flareactivate = "" ; Modifier to activate the mode to send a flare to your allies
calltoarms = "" ; Modifier to call the selected units to the arms.
; Overlays
showstatusbars = Tab ; Toggle display of status bars
devcommands.toggle = "Alt+D" ; Toggle developer commands panel
@@ -174,6 +174,10 @@
"session.flareactivate": {
"name": "Flare (toggle)",
"desc": "Set the cursor to Flare. The hotkey can be released."
},
"session.calltoarms": {
"name": "Call to arms",
"desc": "Send the selected units on attack move to the specified location after dropping resources."
}
}
}
@@ -18,6 +18,7 @@ const ACTION_REPAIR = 2;
const ACTION_GUARD = 3;
const ACTION_PATROL = 4;
const ACTION_OCCUPY_TURRET = 5;
const ACTION_CALLTOARMS = 6;
var preSelectedAction = ACTION_NONE;
const INPUT_NORMAL = 0;
@@ -246,6 +246,48 @@ var g_UnitActions =
"specificness": 10,
},
"call-to-arms": {
"execute": function(target, action, selection, queued, pushFront)
{
let targetClasses;
if (Engine.HotkeyIsPressed("session.attackmoveUnit"))
targetClasses = { "attack": ["Unit"] };
else
targetClasses = { "attack": ["Unit", "Structure"] };
Engine.PostNetworkCommand({
"type": "call-to-arms",
"entities": selection,
"target": target,
"targetClasses": targetClasses,
"queued": queued,
"pushFront": pushFront,
"allowCapture": true,
"formation": g_AutoFormation.getNull()
});
return true;
},
"getActionInfo": function(entState, targetState)
{
return { "possible": !!entState.unitAI };
},
"actionCheck": function(target, selection)
{
const actionInfo = getActionInfo("call-to-arms", target, selection);
return actionInfo.possible && {
"type": "call-to-arms",
"cursor": "action-attack",
"target": target,
"firstAbleEntity": actionInfo.entity
};
},
"preSelectedActionCheck": function(target, selection)
{
return preSelectedAction == ACTION_CALLTOARMS &&
this.actionCheck(target, selection);
},
"specificness": 50,
},
"patrol":
{
"execute": function(target, action, selection, queued, pushFront)
@@ -1423,6 +1465,27 @@ var g_EntityCommands =
"allowedPlayers": ["Player"]
},
"call-to-arms": {
"getInfo": function(entStates)
{
const classes = ["Soldier", "Warship", "Siege", "Healer"];
if (entStates.every(entState => !MatchesClassList(entState.identity.classes, classes)))
return false;
return {
"tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.calltoarms") +
translate("Send the selected units on attack move to the specified location after dropping resources."),
"icon": "call-to-arms.png",
"enabled": true
};
},
"execute": function(entStates)
{
inputState = INPUT_PRESELECTEDACTION;
preSelectedAction = ACTION_CALLTOARMS;
},
"allowedPlayers": ["Player"]
},
"garrison": {
"getInfo": function(entStates)
{
@@ -556,6 +556,17 @@ UnitAI.prototype.UnitFsmSpec = {
return ACCEPT_ORDER;
},
"Order.DropAtNearestDropSite": function(msg) {
const cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
if (!cmpResourceGatherer)
return this.FinishOrder();
const nearby = this.FindNearestDropsite(cmpResourceGatherer.GetMainCarryingType());
if (!nearby)
return this.FinishOrder();
this.ReturnResource(nearby, false, true);
return ACCEPT_ORDER;
},
"Order.ReturnResource": function(msg) {
if (this.CheckTargetRange(msg.data.target, IID_ResourceGatherer))
this.SetNextState("INDIVIDUAL.RETURNRESOURCE.DROPPINGRESOURCES");
@@ -960,6 +971,13 @@ UnitAI.prototype.UnitFsmSpec = {
return ACCEPT_ORDER;
},
"Order.DropAtNearestDropSite": function(msg) {
this.CallMemberFunction("DropAtNearestDropSite", [false, false]);
this.SetNextState("MEMBER");
return ACCEPT_ORDER;
},
"IDLE": {
"enter": function(msg) {
// Turn rearrange off. Otherwise, if the formation is idle
@@ -5233,6 +5251,9 @@ UnitAI.prototype.GetTargetPositions = function()
case "Stop":
return [];
case "DropAtNearestDropSite":
break;
default:
error("GetTargetPositions: Unrecognised order type '"+order.type+"'");
return [];
@@ -5408,6 +5429,15 @@ UnitAI.prototype.Stop = function(queued, pushFront)
this.AddOrder("Stop", { "force": true }, queued, pushFront);
};
/**
* The unit will drop all resources at the closest dropsite. If this unit is no gatherer or
* no dropsite is available, it will do nothing.
*/
UnitAI.prototype.DropAtNearestDropSite = function(queued, pushFront)
{
this.AddOrder("DropAtNearestDropSite", { "force": true }, queued, pushFront);
};
/**
* Adds walk-to-target order to queue, this only occurs in response
* to a player order, and so is forced.
@@ -279,6 +279,27 @@ var g_Commands = {
}
},
"call-to-arms": function(player, cmd, data)
{
const unitsToMove = data.entities.filter(ent =>
MatchesClassList(Engine.QueryInterface(ent, IID_Identity).GetClassesList(),
["Soldier", "Warship", "Siege", "Healer"])
);
GetFormationUnitAIs(unitsToMove, player, cmd, data.formation).forEach(cmpUnitAI => {
const target = cmd.target;
if (cmd.pushFront)
{
cmpUnitAI.WalkAndFight(target.x, target.z, cmd.targetClasses, cmd.allowCapture, false, cmd.pushFront);
cmpUnitAI.DropAtNearestDropSite(false, cmd.pushFront);
}
else
{
cmpUnitAI.DropAtNearestDropSite(cmd.queued, false)
cmpUnitAI.WalkAndFight(target.x, target.z, cmd.targetClasses, cmd.allowCapture, true, false);
}
});
},
"remove-guard": function(player, cmd, data)
{
for (let ent of data.entities)