Index: binaries/data/mods/public/gui/session/selection_panels_helpers.js =================================================================== --- binaries/data/mods/public/gui/session/selection_panels_helpers.js +++ binaries/data/mods/public/gui/session/selection_panels_helpers.js @@ -360,6 +360,21 @@ }); } +/** + * Command the entity/entities to drop off their resources + * at the closest suitable dropsite. + * + * @param {number[]} entities - An array of the entity/entities given the command + */ +function returnResources(entities) +{ + Engine.PostNetworkCommand({ + "type": "returnresources", + "entities": entities, + "queued": false + }); +} + function unloadTemplate(template, owner) { Engine.PostNetworkCommand({ 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 @@ -1095,6 +1095,27 @@ }, }, + "returnresources": { + "getInfo": function(entStates) + { + if (entStates.every(entState => + !entState.resourceCarrying || !entState.resourceCarrying.length + )) + return false; + + return { + "tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.returnresources") + + translate("Return the carried resources to the closest dropsite."), + "icon": "attack-request.png" + }; + }, + "execute": function(entStates) + { + if (entStates.length) + returnResources(entStates.map(entState => entState.id)); + }, + }, + "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 @@ -4935,6 +4935,23 @@ }; /** + * Adds ReturnResource order to queue, forced by the player, + * targeting the nearest dropsite. + * + * @param {boolean} queued - Whether the order is queued or not + */ +UnitAI.prototype.ReturnResources = function(queued) +{ + let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); + let resourceType; + if (cmpResourceGatherer) + resourceType = cmpResourceGatherer.GetMainCarryingType(); + let nearby = this.FindNearestDropsite(resourceType); + if (nearby) + this.AddOrder("ReturnResource", { "target": nearby, "force": false }, queued); +}; + +/** * 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 @@ -269,6 +269,13 @@ } }, + "returnresources": function(player, cmd, data) + { + GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => { + cmpUnitAI.ReturnResources(cmd.queued); + }); + }, + "remove-guard": function(player, cmd, data) { for (let ent of data.entities)