Index: binaries/data/mods/public/gui/session/input.js =================================================================== --- binaries/data/mods/public/gui/session/input.js +++ binaries/data/mods/public/gui/session/input.js @@ -17,6 +17,7 @@ const ACTION_REPAIR = 2; const ACTION_GUARD = 3; const ACTION_PATROL = 4; +const ACTION_CALLTOARMS = 5; var preSelectedAction = ACTION_NONE; const INPUT_NORMAL = 0; Index: binaries/data/mods/public/gui/session/unit_actions.js =================================================================== --- binaries/data/mods/public/gui/session/unit_actions.js +++ binaries/data/mods/public/gui/session/unit_actions.js @@ -238,6 +238,39 @@ "specificness": 10, }, + "call-to-arms": { + "execute": function(target, action, selection, queued) + { + 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": false, + "allowCapture": false + }); + + return true; + }, + "preSelectedActionCheck": function(target, selection) + { + if (preSelectedAction != ACTION_CALLTOARMS || !getActionInfo("attack-move", target, selection).possible) + return false; + return { + "type": "call-to-arms", + "cursor": "action-attack", + "target": target + }; + }, + "specificness": 50, + }, + "patrol": { "execute": function(target, action, selection, queued) @@ -1215,6 +1248,22 @@ }, }, + "call-to-arms": { + "getInfo": function(entStates) + { + 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" + }; + }, + "execute": function(entStates) + { + inputState = INPUT_PRESELECTEDACTION; + preSelectedAction = ACTION_CALLTOARMS; + }, + }, + "garrison": { "getInfo": function(entStates) { Index: binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- binaries/data/mods/public/simulation/components/UnitAI.js +++ binaries/data/mods/public/simulation/components/UnitAI.js @@ -4051,7 +4051,7 @@ UnitAI.prototype.UpdateWorkOrders = function(type) { - var isWorkType = type => type == "Gather" || type == "Trade" || type == "Repair" || type == "ReturnResource"; + let isWorkType = type => type == "Gather" || type == "Trade" || type == "Repair"; // If we are being re-affected to a work order, forget the previous ones if (isWorkType(type)) @@ -5396,6 +5396,31 @@ }; /** + * Adds CallToArms order to queue, forced by the player. + * This returns resources to the nearest dropsite and subsequently + * issues a walk-and-fight-order to the specified location. + * + * @param {Vector3D} target.x, target.y, target.z - the x,y,z-values to where the units need to attack-walk (after dropping resources). + * @param {string[]} targetClasses - The entities to attack when attack-walking. + * @param {boolean} allowCapture - Whether capturing is allowed. + * @param {boolean} queued - Whether the order is queued. + */ +UnitAI.prototype.CallToArms = function(target, targetClasses, allowCapture, queued) +{ + let nearby; + let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); + if (cmpResourceGatherer) + nearby = this.FindNearestDropsite(cmpResourceGatherer.GetMainCarryingType()); + + if (nearby) + this.ReturnResource(nearby, queued); + else + this.Stop(queued); + + this.WalkAndFight(target.x, target.z, targetClasses, allowCapture, true); +}; + +/** * Adds walk-to-target order to queue, this only occurs in response * to a player order, and so is forced. */ Index: binaries/data/mods/public/simulation/helpers/Commands.js =================================================================== --- binaries/data/mods/public/simulation/helpers/Commands.js +++ binaries/data/mods/public/simulation/helpers/Commands.js @@ -259,6 +259,13 @@ } }, + "call-to-arms": function(player, cmd, data) + { + GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => { + cmpUnitAI.CallToArms(cmd.target, cmd.targetClasses, cmd.allowCapture, cmd.queued); + }); + }, + "remove-guard": function(player, cmd, data) { for (let ent of data.entities)