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 @@ -112,7 +112,7 @@ for (let command in g_EntityCommands) { - let info = g_EntityCommands[command].getInfo(unitEntStates); + let info = g_EntityCommands[command].getInfo(unitEntStates, g_EntityCommands[command].allowedPlayers); if (info) { info.name = command; @@ -134,10 +134,8 @@ data.countDisplay.caption = data.item.count || ""; - data.button.enabled = - g_IsObserver && data.item.name == "focus-rally" || - controlsPlayer(data.player) && (data.item.name != "delete" || - data.unitEntStates.some(state => !isUndeletable(state))); + data.button.enabled = data.item.name != "delete" || + data.unitEntStates.some(state => !isUndeletable(state)); data.icon.sprite = "stretched:session/icons/" + data.item.icon; @@ -153,59 +151,6 @@ } }; -g_SelectionPanels.AllyCommand = { - "getMaxNumberOfItems": function() - { - return 2; - }, - "conflictsWith": ["Command"], - "getItems": function(unitEntStates) - { - let commands = []; - for (let command in g_AllyEntityCommands) - for (let state of unitEntStates) - { - let info = g_AllyEntityCommands[command].getInfo(state); - if (info) - { - info.name = command; - commands.push(info); - break; - } - } - return commands; - }, - "setupButton": function(data) - { - data.button.tooltip = data.item.tooltip; - - data.button.onPress = function() { - if (data.item.callback) - data.item.callback(data.item); - else - performAllyCommand(data.unitEntStates[0].id, data.item.name); - }; - - data.countDisplay.caption = data.item.count || ""; - - data.button.enabled = !!data.item.count; - - let grayscale = data.button.enabled ? "" : "grayscale:"; - data.icon.sprite = "stretched:" + grayscale + "session/icons/" + data.item.icon; - - let size = data.button.size; - // relative to the center ( = 50%) - size.rleft = 50; - size.rright = 50; - // offset from the center calculation, count on square buttons, so size.bottom is the width too - size.left = (data.i - data.numberOfItems / 2) * (size.bottom + 1); - size.right = size.left + size.bottom; - data.button.size = size; - - return true; - } -}; - g_SelectionPanels.Construction = { "getMaxNumberOfItems": function() { @@ -1182,7 +1127,6 @@ // UNIQUE PANES (importance doesn't matter) "Command", - "AllyCommand", "Queue", "Selection", ]; 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 @@ -237,30 +237,10 @@ if (!entStates.length) return; - // Don't check all entities, because we assume a player cannot - // select entities from more than one player - if (!controlsPlayer(entStates[0].player) && - !(g_IsObserver && commandName == "focus-rally")) - return; - - if (g_EntityCommands[commandName]) + if (g_EntityCommands[commandName] && g_EntityCommands[commandName].getInfo(entStates, g_EntityCommands[commandName].allowedPlayers)) g_EntityCommands[commandName].execute(entStates); } -function performAllyCommand(entity, commandName) -{ - if (!entity) - return; - - let entState = GetEntityState(entity); - let playerState = GetSimState().players[Engine.GetPlayerID()]; - if (!playerState.isMutualAlly[entState.player] || g_IsObserver) - return; - - if (g_AllyEntityCommands[commandName]) - g_AllyEntityCommands[commandName].execute(entState); -} - function performFormation(entities, formationTemplate) { if (!entities) 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 @@ -1015,12 +1015,27 @@ var g_EntityCommands = { "unload-all": { - "getInfo": function(entStates) + "getInfo": function(entStates, allowedPlayers) { + if (!allowedPlayersCheck(entStates, allowedPlayers)) + return false; + let count = 0; for (let entState of entStates) - if (entState.garrisonHolder) + { + if (!entState.garrisonHolder) + continue; + + if (allowedPlayersCheck([entState], ["Player"])) count += entState.garrisonHolder.entities.length; + else + for (let entity of entState.garrisonHolder.entities) + { + let state = GetEntityState(entity); + if (allowedPlayersCheck([state], ["Player"])) + ++count; + } + } if (!count) return false; @@ -1036,11 +1051,15 @@ { unloadAll(); }, + "allowedPlayers": ["Player", "Ally"] }, "delete": { - "getInfo": function(entStates) + "getInfo": function(entStates, allowedPlayers) { + if (!allowedPlayersCheck(entStates, allowedPlayers)) + return false; + return entStates.some(entState => !isUndeletable(entState)) ? { "tooltip": @@ -1084,11 +1103,15 @@ else (new DeleteSelectionConfirmation(deleteSelection)).display(); }, + "allowedPlayers": ["Player"] }, "stop": { - "getInfo": function(entStates) + "getInfo": function(entStates, allowedPlayers) { + if (!allowedPlayersCheck(entStates, allowedPlayers)) + return false; + if (entStates.every(entState => !entState.unitAI)) return false; @@ -1103,11 +1126,15 @@ if (entStates.length) stopUnits(entStates.map(entState => entState.id)); }, + "allowedPlayers": ["Player"] }, "garrison": { - "getInfo": function(entStates) + "getInfo": function(entStates, allowedPlayers) { + if (!allowedPlayersCheck(entStates, allowedPlayers)) + return false; + if (entStates.every(entState => !entState.unitAI || entState.turretParent || false)) return false; @@ -1122,11 +1149,15 @@ inputState = INPUT_PRESELECTEDACTION; preSelectedAction = ACTION_GARRISON; }, + "allowedPlayers": ["Player"] }, "unload": { - "getInfo": function(entStates) + "getInfo": function(entStates, allowedPlayers) { + if (!allowedPlayersCheck(entStates, allowedPlayers)) + return false; + if (entStates.every(entState => { if (!entState.unitAI || !entState.turretParent) return true; @@ -1144,11 +1175,15 @@ { unloadSelection(); }, + "allowedPlayers": ["Player"] }, "repair": { - "getInfo": function(entStates) + "getInfo": function(entStates, allowedPlayers) { + if (!allowedPlayersCheck(entStates, allowedPlayers)) + return false; + if (entStates.every(entState => !entState.builder)) return false; @@ -1163,11 +1198,15 @@ inputState = INPUT_PRESELECTEDACTION; preSelectedAction = ACTION_REPAIR; }, + "allowedPlayers": ["Player"] }, "focus-rally": { - "getInfo": function(entStates) + "getInfo": function(entStates, allowedPlayers) { + if (!allowedPlayersCheck(entStates, allowedPlayers)) + return false; + if (entStates.every(entState => !entState.rallyPoint)) return false; @@ -1198,11 +1237,15 @@ if (focusTarget) Engine.CameraMoveTo(focusTarget.x, focusTarget.z); }, + "allowedPlayers": ["Player", "Observer"] }, "back-to-work": { - "getInfo": function(entStates) + "getInfo": function(entStates, allowedPlayers) { + if (!allowedPlayersCheck(entStates, allowedPlayers)) + return false; + if (entStates.every(entState => !entState.unitAI || !entState.unitAI.hasWorkOrders)) return false; @@ -1216,11 +1259,15 @@ { backToWork(); }, + "allowedPlayers": ["Player"] }, "add-guard": { - "getInfo": function(entStates) + "getInfo": function(entStates, allowedPlayers) { + if (!allowedPlayersCheck(entStates, allowedPlayers)) + return false; + if (entStates.every(entState => !entState.unitAI || !entState.unitAI.canGuard || entState.unitAI.isGuarding)) return false; @@ -1236,11 +1283,15 @@ inputState = INPUT_PRESELECTEDACTION; preSelectedAction = ACTION_GUARD; }, + "allowedPlayers": ["Player"] }, "remove-guard": { - "getInfo": function(entStates) + "getInfo": function(entStates, allowedPlayers) { + if (!allowedPlayersCheck(entStates, allowedPlayers)) + return false; + if (entStates.every(entState => !entState.unitAI || !entState.unitAI.isGuarding)) return false; @@ -1253,11 +1304,15 @@ { removeGuard(); }, + "allowedPlayers": ["Player"] }, "select-trading-goods": { - "getInfo": function(entStates) + "getInfo": function(entStates, allowedPlayers) { + if (!allowedPlayersCheck(entStates, allowedPlayers)) + return false; + if (entStates.every(entState => !entState.market)) return false; @@ -1270,11 +1325,15 @@ { g_TradeDialog.toggle(); }, + "allowedPlayers": ["Player"] }, "patrol": { - "getInfo": function(entStates) + "getInfo": function(entStates, allowedPlayers) { + if (!allowedPlayersCheck(entStates, allowedPlayers)) + return false; + if (!entStates.some(entState => entState.unitAI && entState.unitAI.canPatrol)) return false; @@ -1290,91 +1349,45 @@ inputState = INPUT_PRESELECTEDACTION; preSelectedAction = ACTION_PATROL; }, + "allowedPlayers": ["Player"] }, "share-dropsite": { - "getInfo": function(entStates) + "getInfo": function(entStates, allowedPlayers) { - let sharableEntities = entStates.filter( - entState => entState.resourceDropsite && entState.resourceDropsite.sharable); - if (!sharableEntities.length) - return false; - - // Returns if none of the entities belong to a player with a mutual ally - if (entStates.every(entState => !GetSimState().players[entState.player].isMutualAlly.some( - (isAlly, playerId) => isAlly && playerId != entState.player))) + if (!allowedPlayersCheck(entStates, allowedPlayers)) return false; - return sharableEntities.some(entState => !entState.resourceDropsite.shared) ? - { - "tooltip": translate("Press to allow allies to use this dropsite"), - "icon": "locked_small.png" - } : - { - "tooltip": translate("Press to prevent allies from using this dropsite"), - "icon": "unlocked_small.png" - }; - }, - "execute": function(entStates) - { let sharableEntities = entStates.filter( entState => entState.resourceDropsite && entState.resourceDropsite.sharable); - Engine.PostNetworkCommand({ - "type": "set-dropsite-sharing", - "entities": sharableEntities.map(entState => entState.id), - "shared": sharableEntities.some(entState => !entState.resourceDropsite.shared) - }); - }, - } -}; - -var g_AllyEntityCommands = -{ - "unload-all": { - "getInfo": function(entState) - { - if (!entState.garrisonHolder) + if (!sharableEntities.length) return false; - let player = Engine.GetPlayerID(); - - let count = 0; - for (let ent in g_Selection.selected) + if (sharableEntities.every(entState => allowedPlayersCheck([entState], ["Player"]))) { - let selectedEntState = GetEntityState(+ent); - if (!selectedEntState.garrisonHolder) - continue; + // Returns if none of the entities belong to a player with a mutual ally. + if (entStates.every(entState => !GetSimState().players[entState.player].isMutualAlly.some( + (isAlly, playerId) => isAlly && playerId != entState.player))) + return false; - for (let entity of selectedEntState.garrisonHolder.entities) - { - let state = GetEntityState(entity); - if (state.player == player) - ++count; - } + return sharableEntities.some(entState => !entState.resourceDropsite.shared) ? + { + "tooltip": translate("Press to allow allies to use this dropsite"), + "icon": "locked_small.png" + } : + { + "tooltip": translate("Press to prevent allies from using this dropsite"), + "icon": "unlocked_small.png" + }; } - return { - "tooltip": colorizeHotkey("%(hotkey)s" + " ", "session.unload") + - translate("Unload All."), - "icon": "garrison-out.png", - "count": count, - }; - }, - "execute": function(entState) - { - unloadAll(); - }, - }, - - "share-dropsite": { - "getInfo": function(entState) - { - if (Engine.GetPlayerID() == -1 || - !GetSimState().players[Engine.GetPlayerID()].hasSharedDropsites || - !entState.resourceDropsite || !entState.resourceDropsite.sharable) + let player = Engine.GetPlayerID(); + if (player == -1 || entStates.every(entState => + !GetSimState().players[player].hasSharedDropsites || + !entState.resourceDropsite || !entState.resourceDropsite.sharable)) return false; - if (entState.resourceDropsite.shared) + if (entStates.every(entState => entState.resourceDropsite.shared)) return { "tooltip": translate("You are allowed to use this dropsite"), "icon": "unlocked_small.png" @@ -1385,10 +1398,18 @@ "icon": "locked_small.png" }; }, - "execute": function(entState) + "execute": function(entStates) { - // This command button is always disabled + let sharableEntities = entStates.filter( + entState => entState.resourceDropsite && entState.resourceDropsite.sharable && allowedPlayersCheck([entState], ["Player"])); + if (sharableEntities) + Engine.PostNetworkCommand({ + "type": "set-dropsite-sharing", + "entities": sharableEntities.map(entState => entState.id), + "shared": sharableEntities.some(entState => !entState.resourceDropsite.shared) + }); }, + "allowedPlayers": ["Player", "Ally"] } }; @@ -1404,6 +1425,32 @@ return false; } +/** + * Checks whether the entities have the right diplomatic status + * with respect to the currently active player. + * Also "Observer" can be used. + * + * @param {Object[]} entStates - An array containing the entity states to check. + * @param {string[]} validPlayers - An array containing the diplomatic statuses. + * + * @return {boolean} - Whether the currently active player is allowed. + */ +function allowedPlayersCheck(entStates, validPlayers) +{ + // Assume we can only select entities from one player, + // or it does not matter (e.g. observer). + let targetState = entStates[0]; + let playerState = GetSimState().players[Engine.GetPlayerID()]; + + for (let player of validPlayers) + if (player == "Observer" && !playerState || + player == "Player" && controlsPlayer(targetState.player) || + playerState && playerState["is" + player] && playerState["is" + player][targetState.player]) + return true; + + return false; +} + function hasClass(entState, className) { // note: use the functions in globalscripts/Templates.js for more versatile matching Index: binaries/data/mods/public/gui/session/unit_commands.js =================================================================== --- binaries/data/mods/public/gui/session/unit_commands.js +++ binaries/data/mods/public/gui/session/unit_commands.js @@ -10,7 +10,6 @@ "Barter": 0, "Construction": 0, "Command": 0, - "AllyCommand": 0, "Stance": 0, "Gate": 0, "Pack": 0, @@ -135,6 +134,10 @@ let playerStates = GetSimState().players; let playerState = playerStates[Engine.GetPlayerID()]; + // Command panel always shown for it can contain commands + // for which the entity does not need to be owned. + setupUnitPanel("Command", entStates, playerState); + if (g_IsObserver || entStates.every(entState => controlsPlayer(entState.player))) { for (let guiName of g_PanelsOrder) @@ -155,7 +158,6 @@ // TODO if there's a second panel needed for a different player // we should consider adding the players list to g_SelectionPanels setupUnitPanel("Garrison", entStates, playerState); - setupUnitPanel("AllyCommand", entStates, playerState); supplementalDetailsPanel.hidden = !g_SelectionPanels.Garrison.used;