Index: binaries/data/config/default.cfg =================================================================== --- binaries/data/config/default.cfg +++ binaries/data/config/default.cfg @@ -307,6 +307,7 @@ batchtrain = Shift ; Modifier to train units in batches massbarter = Shift ; Modifier to barter bunch of resources masstribute = Shift ; Modifier to tribute bunch of resources +assignall = Shift ; Modifier to assign all idle villagers to a selected task using the command button. noconfirmation = Shift ; Do not ask confirmation when deleting a building/unit fulltradeswap = Shift ; Modifier to put the desired trade resource to 100% unloadtype = Shift ; Modifier to unload all units of type 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 @@ -129,7 +129,7 @@ if (data.item.callback) data.item.callback(data.item); else - performCommand(data.unitEntStates, data.item.name); + performCommand(data.unitEntStates, data.item.name, data.item.data); }; data.countDisplay.caption = data.item.count || ""; @@ -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 @@ -232,7 +232,7 @@ Engine.HotkeyIsPressed("session.deselectgroup") || deselectGroup); } -function performCommand(entStates, commandName) +function performCommand(entStates, commandName, data) { if (!entStates.length) return; @@ -244,7 +244,7 @@ return; if (g_EntityCommands[commandName]) - g_EntityCommands[commandName].execute(entStates); + g_EntityCommands[commandName].execute(entStates, data); } function performAllyCommand(entity, commandName) @@ -355,6 +355,13 @@ }); } +function assignIdleVillager(data) +{ + let targets = Object.keys(data); + for (let target of targets) + g_UnitActions[data[target].action].execute(undefined, { "type": data[target].action, "target": +target }, data[target].entities, Engine.HotkeyIsPressed("session.queue")); +} + 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,83 @@ */ var g_EntityCommands = { + "assign-idle-villager": { + "getInfo": function(entStates) + { + let workers = Engine.GuiInterfaceCall("FindIdleUnits", { + "idleClasses": g_WorkerTypes, + "excludeUnits": [] + }); + if (!workers.length) + return { + "tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.assignidlevillager") + + translate("There are no idle villagers."), + "icon": "production.png", + "active": "false" + }; + + // Object of the form: { "targetEntityID": { "action": toPerform, "entities": [toAssign] } }. + let assignmentData = {}; + for (let entState of entStates) + { + let maxWorkers; + let action; + let filteredWorkers; + + if (entState.foundation || entState.needsRepair) + action = "repair"; + + if (entState.resourceSupply && entState.resourceSupply.maxGatherers - entState.resourceSupply.numGatherers > 0) + { + action = "gather"; + maxWorkers = entState.resourceSupply.maxGatherers - entState.resourceSupply.numGatherers; + } + filteredWorkers = workers.filter(worker => + ["preSelectedActionCheck", "hotkeyActionCheck", "actionCheck"].some(method => + g_UnitActions[action] && + g_UnitActions[action][method] && + g_UnitActions[action][method](entState.id, [worker]) + )); + if (maxWorkers) + filteredWorkers.slice(0, maxWorkers); + + // No appropriate idle workers. + if (!filteredWorkers.length) + continue; + + // If not all workers are desired select only the first. + // Could be improved to use the closest? + if (!Engine.HotkeyIsPressed("session.assignall")) + filteredWorkers = [filteredWorkers[0]]; + + assignmentData[entState.id] = { + "action": action, + "entities": filteredWorkers + }; + // Exclude the previously used workers. + workers = workers.filter(worker => filteredWorkers.indexOf(worker) == -1); + } + if (!Object.keys(assignmentData).length) + return { + "tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.assignidlevillager") + + translate("There are no appropriate idle villagers that can be assigned."), + "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", + "data": assignmentData + }; + }, + "execute": function(entStates, data) + { + assignIdleVillager(data, Engine.HotkeyIsPressed("session.assignall")); + }, + }, + "unload-all": { "getInfo": function(entStates) {