diff --git a/binaries/data/mods/public/art/textures/ui/session/icons/call-to-arms.png b/binaries/data/mods/public/art/textures/ui/session/icons/call-to-arms.png new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ { + const classes = entState.identity.classes; + // Exclude ruins + if(classes.length == 0) + return true; + if (classes.includes("FemaleCitizen") || classes.includes("Structure") || classes.includes("Trader") || classes.includes("Relic")) + return true; + if (classes.includes("Ship") && !classes.includes("Warship")) + return true; + if (classes.includes("Support") && classes.includes("Elephant")) + return true; + return false; + })) + { + 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) { diff --git a/binaries/data/mods/public/simulation/components/UnitAI.js b/binaries/data/mods/public/simulation/components/UnitAI.js --- a/binaries/data/mods/public/simulation/components/UnitAI.js +++ b/binaries/data/mods/public/simulation/components/UnitAI.js @@ -4052,7 +4052,7 @@ UnitAI.prototype.UpdateWorkOrders = function(type) { - var isWorkType = type => type == "Gather" || type == "Trade" || type == "Repair" || type == "ReturnResource"; + const isWorkType = t => t == "Gather" || t == "Trade" || t == "Repair"; if (isWorkType(type)) { this.workOrders = []; @@ -5365,6 +5365,26 @@ this.AddOrder("Stop", { "force": true }, queued, pushFront); }; +/** + * The unit will drop all resources at the next dropsite. If this unit is no gatherer or + * no dropsite is available, it will do nothing. + * + * @return {boolean} Whether this unit could drop resources. + */ +UnitAI.prototype.DropAtNearestDropSite = function(queued) +{ + let nearby; + const cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); + if (cmpResourceGatherer) + nearby = this.FindNearestDropsite(cmpResourceGatherer.GetMainCarryingType()); + if (nearby) + { + this.ReturnResource(nearby, queued); + return true; + } + return false; +}; + /** * Adds walk-to-target order to queue, this only occurs in response * to a player order, and so is forced. diff --git a/binaries/data/mods/public/simulation/helpers/Commands.js b/binaries/data/mods/public/simulation/helpers/Commands.js --- a/binaries/data/mods/public/simulation/helpers/Commands.js +++ b/binaries/data/mods/public/simulation/helpers/Commands.js @@ -279,6 +279,40 @@ } }, + "call-to-arms": function(player, cmd, data) + { + const templateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); + // Filter, which units to move. + // Soldiers, Healers, Heros, Siege Engines, Warships are moved + // Female citizens, Traders, Fishingboats and other units with no attack stay. + const ents = data.entities.filter(ent => { + if (Engine.QueryInterface(ent, IID_Trader)) + return false; + if (Engine.QueryInterface(ent, IID_Heal)) + return true; + const template = templateManager.GetTemplate(templateManager.GetCurrentTemplateName(ent)); + const identity = Engine.QueryInterface(ent, IID_Identity); + const classes = identity.GetClassesList(); + // Unconditionally move heroes + if (classes.includes("Hero") || classes.includes("Champion")) + return true; + else if (classes.includes("FemaleCitizen")|| (classes.includes("Elephant") && + classes.includes("Support")) || (classes.includes("Ship") && !classes.includes("Warship"))) + return false; + const attack = Engine.QueryInterface(ent, IID_Attack); + // Move unit, if it can attack + if (attack && attack.GetAttackTypes().length > 0) + return true; + return false; + }); + GetFormationUnitAIs(ents, player).forEach(cmpUnitAI => { + if (!cmpUnitAI.DropAtNearestDropSite(cmd.queued)) + cmpUnitAI.Stop(cmd.queued); + const target = cmd.target; + cmpUnitAI.WalkAndFight(target.x, target.z, cmd.targetClasses, cmd.allowCapture, true); + }); + }, + "remove-guard": function(player, cmd, data) { for (let ent of data.entities)