Index: binaries/data/mods/public/gui/session/input.js =================================================================== --- binaries/data/mods/public/gui/session/input.js +++ binaries/data/mods/public/gui/session/input.js @@ -294,7 +294,7 @@ { Engine.GuiInterfaceCall("PlaySound", { "name": "invalid_building_placement", - "entity": g_Selection.toList()[0] + "entity": g_Selection.getFirstSelected() }); return false; } @@ -318,7 +318,7 @@ }); Engine.GuiInterfaceCall("PlaySound", { "name": "order_build", "entity": selection[0] }); - if (!queued || !g_Selection.toList().length) + if (!queued || !g_Selection.size()) placementSupport.Reset(); else placementSupport.RandomizeActorSeed(); @@ -578,7 +578,7 @@ let queued = Engine.HotkeyIsPressed("session.queue"); if (tryPlaceBuilding(queued, Engine.HotkeyIsPressed("session.pushorderfront"))) { - if (queued && g_Selection.toList().length) + if (queued && g_Selection.size()) inputState = INPUT_BUILDING_PLACEMENT; else inputState = INPUT_NORMAL; @@ -736,7 +736,7 @@ let queued = Engine.HotkeyIsPressed("session.queue"); if (tryPlaceBuilding(queued, Engine.HotkeyIsPressed("session.pushorderfront"))) { - if (queued && g_Selection.toList().length) + if (queued && g_Selection.size()) inputState = INPUT_BUILDING_PLACEMENT; else inputState = INPUT_NORMAL; @@ -887,7 +887,7 @@ } default: // Slight hack: If selection is empty, reset the input state. - if (g_Selection.toList().length == 0) + if (!g_Selection.size()) { preSelectedAction = ACTION_NONE; inputState = INPUT_NORMAL; @@ -1163,7 +1163,7 @@ for (let i = 1; i < inputLine.length; ++i) lengthOfLine += inputLine[i].distanceTo(inputLine[i - 1]); - let selection = g_Selection.toList().filter(ent => !!GetEntityState(ent).unitAI).sort((a, b) => a - b); + const selection = g_Selection.filter(ent => !!GetEntityState(ent).unitAI).sort((a, b) => a - b); // Checking the line for a minimum length to save performance. if (lengthOfLine < g_FreehandSelection_MinLengthOfLine || selection.length < g_FreehandSelection_MinNumberOfUnits) Index: binaries/data/mods/public/gui/session/selection.js =================================================================== --- binaries/data/mods/public/gui/session/selection.js +++ binaries/data/mods/public/gui/session/selection.js @@ -165,11 +165,10 @@ function EntitySelection() { // Private properties: - this.selected = {}; // { id:id, id:id, ... } for each selected entity ID 'id' + this.selected = new Set(); - // { id:id, ... } for mouseover-highlighted entity IDs in these, the key is a string and the value is an int; - // we want to use the int form wherever possible since it's more efficient to send to the simulation code) - this.highlighted = {}; + // For mouseover-highlighted entity IDs in these. + this.highlighted = new Set(); this.motionDebugOverlay = false; @@ -196,11 +195,10 @@ */ EntitySelection.prototype.getTemplateNames = function() { - var templateNames = []; - - for (let ent in this.selected) + const templateNames = []; + for (const ent of this.selected) { - let entState = GetEntityState(+ent); + const entState = GetEntityState(ent); if (entState) templateNames.push(entState.template); } @@ -208,26 +206,25 @@ }; /** - * Update the selection to take care of changes (like units that have been killed) + * Update the selection to take care of changes (like units that have been killed). */ EntitySelection.prototype.update = function() { this.checkRenamedEntities(); - let controlsAll = g_SimState.players[g_ViewedPlayer] && g_SimState.players[g_ViewedPlayer].controlsAll; - let removeOwnerChanges = !g_IsObserver && !controlsAll && this.toList().length > 1; + const controlsAll = g_SimState.players[g_ViewedPlayer] && g_SimState.players[g_ViewedPlayer].controlsAll; + const removeOwnerChanges = !g_IsObserver && !controlsAll && this.selected.size > 1; let changed = false; - for (let ent in this.selected) + for (const ent of this.selected) { - let entState = GetEntityState(+ent); + const entState = GetEntityState(ent); - // Remove deleted units if (!entState) { - delete this.selected[ent]; - this.groups.removeEnt(+ent); + this.selected.delete(ent); + this.groups.removeEnt(ent); changed = true; continue; } @@ -240,12 +237,12 @@ removeOwnerChanges && entState.player != g_ViewedPlayer) { // Disable any highlighting of the disappeared unit - _setHighlight([+ent], 0, false); - _setStatusBars([+ent], false); - _setMotionOverlay([+ent], false); + _setHighlight([ent], 0, false); + _setStatusBars([ent], false); + _setMotionOverlay([ent], false); - delete this.selected[ent]; - this.groups.removeEnt(+ent); + this.selected.delete(ent); + this.groups.removeEnt(ent); changed = true; continue; } @@ -269,7 +266,7 @@ // Reconstruct the selection if at least one entity has been renamed. for (let renamedEntity of renamedEntities) - if (this.selected[renamedEntity.entity]) + if (this.selected.has(renamedEntity.entity)) { this.rebuildSelection(renamedLookup); return; @@ -282,25 +279,22 @@ */ EntitySelection.prototype.addList = function(ents, quiet, force = false) { - let selection = this.toList(); - - // If someone else's player is the sole selected unit, don't allow adding to the selection - let firstEntState = selection.length == 1 && GetEntityState(selection[0]); + // If someone else's player is the sole selected unit, don't allow adding to the selection. + const firstEntState = this.selected.size == 1 && GetEntityState(this.getFirstSelected()); if (firstEntState && firstEntState.player != g_ViewedPlayer && !force) return; - let i = 1; let added = []; - for (let ent of ents) + for (const ent of ents) { - if (selection.length + i > g_MaxSelectionSize) + if (this.selected.size >= g_MaxSelectionSize) break; - if (this.selected[ent]) + if (this.selected.has(ent)) continue; - var entState = GetEntityState(ent); + const entState = GetEntityState(ent); if (!entState) continue; @@ -308,12 +302,11 @@ g_ViewedPlayer == -1 && entState.player == 0; // Don't add unowned entities to the list, unless a single entity was selected - if (isUnowned && (ents.length > 1 || selection.length) && !force) + if (isUnowned && (ents.length > 1 || this.selected.size) && !force) continue; added.push(ent); - this.selected[ent] = ent; - ++i; + this.selected.add(ent); } _setHighlight(added, 1, true); @@ -336,11 +329,11 @@ var removed = []; for (let ent of ents) - if (this.selected[ent]) + if (this.selected.has(ent)) { this.groups.removeEnt(ent); removed.push(ent); - delete this.selected[ent]; + this.selected.delete(ent); } _setHighlight(removed, 0, false); @@ -355,27 +348,25 @@ _setHighlight(this.toList(), 0, false); _setStatusBars(this.toList(), false); _setMotionOverlay(this.toList(), false); - this.selected = {}; + this.selected.clear(); this.groups.reset(); this.onChange(); }; EntitySelection.prototype.rebuildSelection = function(renamed) { - var oldSelection = this.selected; + const toAdd = []; + for (const ent of this.selected) + toAdd.push(renamed[ent] || ent); this.reset(); - var toAdd = []; - for (let ent in oldSelection) - toAdd.push(renamed[ent] || +ent); - this.addList(toAdd, true); // don't play selection sounds }; EntitySelection.prototype.getFirstSelected = function() { - for (let ent in this.selected) - return +ent; + for (const ent of this.selected) + return ent; return undefined; }; @@ -384,39 +375,57 @@ */ EntitySelection.prototype.toList = function() { - let ents = []; - for (let ent in this.selected) - ents.push(+ent); - return ents; + return Array.from(this.selected); +}; + +/** + * @return {number} - The number of entities selected. + */ +EntitySelection.prototype.size = function() +{ + return this.selected.size; }; EntitySelection.prototype.find = function(condition) { - for (let ent in this.selected) + for (const ent of this.selected) if (condition(ent)) - return +ent; + return ent; return null; }; +/** + * @param {function} condition - A function. + * @return {number[]} - The entities passing the condition. + */ +EntitySelection.prototype.filter = function(condition) +{ + const result = []; + for (const ent of this.selected) + if (condition(ent)) + result.push(ent); + return result; +}; + EntitySelection.prototype.setHighlightList = function(ents) { - var highlighted = {}; - for (let ent of ents) - highlighted[ent] = ent; + const highlighted = new Set(); + for (const ent of ents) + highlighted.add(ent); - var removed = []; - var added = []; + const removed = []; + const added = []; // Remove highlighting for the old units that are no longer highlighted - // (excluding ones that are actively selected too) - for (let ent in this.highlighted) - if (!highlighted[ent] && !this.selected[ent]) - removed.push(+ent); + // (excluding ones that are actively selected too). + for (const ent of this.highlighted) + if (!highlighted.has(ent) && !this.selected.has(ent)) + removed.push(ent); - // Add new highlighting for units that aren't already highlighted - for (let ent of ents) - if (!this.highlighted[ent] && !this.selected[ent]) - added.push(+ent); + // Add new highlighting for units that aren't already highlighted. + for (const ent of ents) + if (!this.highlighted.has(ent) && !this.selected.has(ent)) + added.push(ent); _setHighlight(removed, 0, false); _setStatusBars(removed, false); @@ -424,7 +433,6 @@ _setHighlight(added, g_HighlightedAlpha, true); _setStatusBars(added, true); - // Store the new highlight list this.highlighted = highlighted; }; 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 @@ -408,7 +408,7 @@ "template": template, "owner": owner, // Filter out all entities that aren't garrisonable. - "garrisonHolders": g_Selection.toList().filter(ent => { + "garrisonHolders": g_Selection.filter(ent => { let state = GetEntityState(ent); return state && !!state.garrisonHolder; }) @@ -417,7 +417,7 @@ function unloadAll() { - let garrisonHolders = g_Selection.toList().filter(e => { + const garrisonHolders = g_Selection.filter(e => { let state = GetEntityState(e); return state && !!state.garrisonHolder; }); @@ -451,7 +451,7 @@ function unloadAllTurrets() { - let turretHolders = g_Selection.toList().filter(e => { + const turretHolders = g_Selection.filter(e => { let state = GetEntityState(e); return state && !!state.turretHolder; }); @@ -489,7 +489,7 @@ function leaveTurretPoints() { - let entities = g_Selection.toList().filter(entity => { + const entities = g_Selection.filter(entity => { let entState = GetEntityState(entity); return entState && entState.turretable && entState.turretable.holder != INVALID_ENTITY; @@ -506,7 +506,7 @@ Engine.PostNetworkCommand({ "type": "back-to-work", // Filter out all entities that can't go back to work. - "entities": g_Selection.toList().filter(ent => { + "entities": g_Selection.filter(ent => { let state = GetEntityState(ent); return state && state.unitAI && state.unitAI.hasWorkOrders; }) @@ -518,7 +518,7 @@ Engine.PostNetworkCommand({ "type": "remove-guard", // Filter out all entities that are currently guarding/escorting. - "entities": g_Selection.toList().filter(ent => { + "entities": g_Selection.filter(ent => { let state = GetEntityState(ent); return state && state.unitAI && state.unitAI.isGuarding; }) @@ -529,7 +529,7 @@ { Engine.PostNetworkCommand({ "type": "alert-raise", - "entities": g_Selection.toList().filter(ent => { + "entities": g_Selection.filter(ent => { let state = GetEntityState(ent); return state && !!state.alertRaiser; }) @@ -540,7 +540,7 @@ { Engine.PostNetworkCommand({ "type": "alert-end", - "entities": g_Selection.toList().filter(ent => { + "entities": g_Selection.filter(ent => { let state = GetEntityState(ent); return state && !!state.alertRaiser; }) @@ -551,7 +551,7 @@ { Engine.PostNetworkCommand({ "type": "autoqueue-on", - "entities": g_Selection.toList().filter(ent => { + "entities": g_Selection.filter(ent => { let state = GetEntityState(ent); return !!state?.production?.entities.length && !state.production.autoqueue; @@ -563,7 +563,7 @@ { Engine.PostNetworkCommand({ "type": "autoqueue-off", - "entities": g_Selection.toList().filter(ent => { + "entities": g_Selection.filter(ent => { let state = GetEntityState(ent); return !!state?.production?.entities.length && state.production.autoqueue; Index: binaries/data/mods/public/gui/session/session.js =================================================================== --- binaries/data/mods/public/gui/session/session.js +++ binaries/data/mods/public/gui/session/session.js @@ -613,7 +613,7 @@ { g_Selection.dirty = false; // When selection changed, get the entityStates of new entities - GetMultipleEntityStates(g_Selection.toList().filter(entId => !g_EntityStates[entId])); + GetMultipleEntityStates(g_Selection.filter(entId => !g_EntityStates[entId])); for (let handler of g_EntitySelectionChangeHandlers) handler(); @@ -779,8 +779,8 @@ else { let selected = g_Selection.toList(); - for (let ent in g_Selection.highlighted) - selected.push(g_Selection.highlighted[ent]); + for (const ent of g_Selection.highlighted) + selected.push(ent); // Remove selected entities from the 'all entities' array, // to avoid disabling their status bars. @@ -830,14 +830,14 @@ let entsAdd = []; // list of entities units to be highlighted let entsRemove = []; let highlighted = g_Selection.toList(); - for (let ent in g_Selection.highlighted) - highlighted.push(g_Selection.highlighted[ent]); + for (const ent of g_Selection.highlighted) + highlighted.push(ent); if (g_ShowGuarding) // flag the guarding entities to add in this additional highlight - for (let sel in g_Selection.selected) + for (const sel of g_Selection.toList()) { - let state = GetEntityState(g_Selection.selected[sel]); + let state = GetEntityState(sel); if (!state.guard || !state.guard.entities.length) continue; @@ -848,9 +848,9 @@ if (g_ShowGuarded) // flag the guarded entities to add in this additional highlight - for (let sel in g_Selection.selected) + for (const sel of g_Selection.toList()) { - let state = GetEntityState(g_Selection.selected[sel]); + const state = GetEntityState(sel); if (!state.unitAI || !state.unitAI.isGuarding) continue; let ent = state.unitAI.isGuarding; 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 @@ -1007,8 +1007,8 @@ if (targetState && (!Engine.HotkeyIsPressed("session.autorallypoint") || !targetState.production || !targetState.production.entities.length)) - for (let ent in g_Selection.selected) - if (targetState.id == +ent) + for (const ent of g_Selection.toList()) + if (targetState.id == ent) return false; let tooltip;