Index: binaries/data/config/default.cfg =================================================================== --- binaries/data/config/default.cfg +++ binaries/data/config/default.cfg @@ -313,6 +313,7 @@ deselectgroup = Ctrl ; Modifier to deselect units when clicking group icon, instead of selecting rotate.cw = RightBracket ; Rotate building placement preview clockwise rotate.ccw = LeftBracket ; Rotate building placement preview anticlockwise +assignall = Shift ; Whether to assign all or only one idle villager to a task. [hotkey.session.gui] toggle = "Alt+G" ; Toggle visibility of session GUI Index: binaries/data/mods/public/gui/session/selection_panels.js =================================================================== --- binaries/data/mods/public/gui/session/selection_panels.js +++ binaries/data/mods/public/gui/session/selection_panels.js @@ -137,7 +137,8 @@ data.button.enabled = g_IsObserver && data.item.name == "focus-rally" || controlsPlayer(data.player) && (data.item.name != "delete" || - data.unitEntStates.some(state => !isUndeletable(state))); + data.unitEntStates.some(state => !isUndeletable(state))) && + (!data.item.active || data.item.active == "true"); data.icon.sprite = "stretched:session/icons/" + data.item.icon; 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 @@ -355,6 +355,20 @@ }); } +function assignIdleVillager(entities, all) +{ + let workers = Engine.GuiInterfaceCall("FindIdleUnits", { + "idleClasses": g_WorkerTypes, + "excludeUnits": [] + }); + Engine.PostNetworkCommand({ + "type": "assign-idle-villager", + "entities": entities, + "idlers": workers, + "all": all + }); +} + 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 @@ -1014,6 +1014,39 @@ */ var g_EntityCommands = { + "assign-idle-villager": { + "getInfo": function(entStates) + { + if (entStates.every(entState => !entState.resourceSupply && + !entState.needsRepair && !entState.foundation)) + return false; + + // This should return a tooltip explaining that one does not have any + // idle villies, with a disabled button. Like "delete". + if (!Engine.GuiInterfaceCall("HasIdleUnits", { + "viewedPlayer": g_ViewedPlayer, + "idleClasses": g_WorkerTypes, + "excludeUnits": [] + })) + return { + "tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.assignidlevillager") + + translate("There are no idle villagers to assign.."), + "icon": "production.png", + "active": "false" + }; + + return { + "tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.assignidlevillager") + + translate("Assign an idle villager to work on this entity."), + "icon": "production.png" + }; + }, + "execute": function(entStates) + { + assignIdleVillager(entStates.map(entState => entState.id), Engine.HotkeyIsPressed("session.assignall")); + }, + }, + "unload-all": { "getInfo": function(entStates) { Index: binaries/data/mods/public/simulation/components/ResourceSupply.js =================================================================== --- binaries/data/mods/public/simulation/components/ResourceSupply.js +++ binaries/data/mods/public/simulation/components/ResourceSupply.js @@ -72,6 +72,11 @@ return this.gatherers.reduce((a, b) => a + b.length, 0); }; +ResourceSupply.prototype.GetFreeSpots = function() +{ + return +this.template.MaxGatherers - this.GetNumGatherers(); +}; + /** * @return {{ "generic": string, "specific": string }} An object containing the subtype and the generic type. All resources must have both. */ 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,62 @@ } }, + "assign-idle-villager": function(player, cmd, data) + { + let workers = cmd.idlers; + for (let entity of data.entities) + { + let action; + let filteredWorkers; + + let cmpFoundation = Engine.QueryInterface(entity, IID_Foundation); + let cmpRepairable = Engine.QueryInterface(entity, IID_Repairable); + if (cmpFoundation || cmpRepairable) + { + filteredWorkers = workers.filter(worker => { + let cmpUnitAI = Engine.QueryInterface(worker, IID_UnitAI); + return cmpUnitAI && cmpUnitAI.CanRepair(entity); + }) + action = "repair"; + } + + let cmpResourceSupply = Engine.QueryInterface(entity, IID_ResourceSupply); + if (cmpResourceSupply && cmpResourceSupply.GetCurrentAmount() > 0 && cmpResourceSupply.GetFreeSpots() > 0) + { + filteredWorkers = workers.filter(worker => { + let cmpUnitAI = Engine.QueryInterface(worker, IID_UnitAI); + return cmpUnitAI && cmpUnitAI.CanGather(entity); + }).slice(0, cmpResourceSupply.GetFreeSpots()); + + action = "gather"; + } + + // No appropriate idle workers. + if (!filteredWorkers.length) + { + notifyAssignIdleVillagersFailure(player, action); + return; + } + + // If not all workers are desired select only the first. + // Could be improved to use the closest? + if (!cmd.all) + filteredWorkers = [filteredWorkers[0]]; + + if (action == "gather") + GetFormationUnitAIs(filteredWorkers, player).forEach(cmpUnitAI => { + cmpUnitAI.Gather(entity, false); + }); + else if (action == "repair") + GetFormationUnitAIs(filteredWorkers, player).forEach(cmpUnitAI => { + cmpUnitAI.Repair(entity, true, false); + }); + + // Exclude the previously used workers. + workers = workers.filter(worker => filteredWorkers.indexOf(worker) == -1); + } + }, + "remove-guard": function(player, cmd, data) { for (let ent of data.entities) @@ -877,6 +933,20 @@ } /** + * Sends a GUI notification about no worker(s) that can be assigned to the specific task. + */ +function notifyAssignIdleVillagersFailure(player, type) +{ + let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); + cmpGUIInterface.PushNotification({ + "type": "text", + "players": [player], + "message": markForTranslation("There are no idle villagers that can " + type), + "translateMessage": true + }); +} + +/** * Get some information about the formations used by entities. * The entities must have a UnitAI component. */