diff --git a/client/public/stylesheets/style/style.css b/client/public/stylesheets/style/style.css index 58ee2aa5..d1537c93 100644 --- a/client/public/stylesheets/style/style.css +++ b/client/public/stylesheets/style/style.css @@ -605,6 +605,12 @@ nav.ol-panel> :last-child { align-items: center; } +.ol-navbar-buttons-group > div { + align-items: center; + display:flex; + flex-direction: row; +} + .ol-navbar-buttons-group button { border: none; height: 32px; @@ -638,6 +644,28 @@ nav.ol-panel> :last-child { stroke: var(--background-steel) !important; } +.ol-navbar-buttons-group .protectable button:first-of-type { + border-bottom-right-radius: 0; + border-top-right-radius: 0; + width:28px; +} + +.ol-navbar-buttons-group > div > button.lock { + display:none; +} + +.ol-navbar-buttons-group > div.protectable > button.lock { + background-color: #990000; + border-bottom-left-radius: 0; + border-top-left-radius: 0; + display:inline-block; + width:18px; +} + +.ol-navbar-buttons-group > div.protectable > button[data-protected].lock { + background-color: #008800; +} + #roe-buttons-container button, #reaction-to-threat-buttons-container button, #emissions-countermeasures-buttons-container button { diff --git a/client/src/constants/constants.ts b/client/src/constants/constants.ts index b2ad78bc..1fc27028 100644 --- a/client/src/constants/constants.ts +++ b/client/src/constants/constants.ts @@ -154,7 +154,7 @@ export const MAP_MARKER_CONTROLS:MapMarkerControl[] = [{ "toggles": [ "human" ] }, { "image": "visibility/dcs.svg", - "initialState": "protected", + "isProtected": true, "name":"dcs", "protectable": true, "toggles": [ "dcs" ] diff --git a/client/src/map/map.ts b/client/src/map/map.ts index 734a8742..952e0fe5 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -40,7 +40,7 @@ require("../../public/javascripts/L.Path.Drag.js") export type MapMarkerControl = { "image": string; - "initialState"?: "protected" | null, + "isProtected"?: boolean, "name":string, "protectable"?: boolean, "toggles": string[] @@ -88,6 +88,7 @@ export class Map extends L.Map { #coalitionAreaContextMenu: CoalitionAreaContextMenu = new CoalitionAreaContextMenu("coalition-area-contextmenu"); #mapSourceDropdown: Dropdown; + #mapMarkerControls:MapMarkerControl[] = MAP_MARKER_CONTROLS; #mapVisibilityOptionsDropdown: Dropdown; #optionButtons: { [key: string]: HTMLButtonElement[] } = {} #visibilityOptions: { [key: string]: boolean } = {} @@ -209,16 +210,7 @@ export class Map extends L.Map { }, 20); /* Option buttons */ - this.#createOptionButtons(); - - /* - this.#optionButtons["visibility"] = visibilityControls.map((option: string, index: number) => { - var typesArrayString = `"${visibilityControlsTypes[index][0]}"`; - visibilityControlsTypes[index].forEach((type: string, idx: number) => { if (idx > 0) typesArrayString = `${typesArrayString}, "${type}"` }); - return this.#createOptionButton(option, `visibility/${option.toLowerCase()}.svg`, visibilityControlsTooltips[index], "toggleMarkerVisibility", `{"types": [${typesArrayString}]}`); - }); - document.querySelector("#unit-visibility-control")?.append(...this.#optionButtons["visibility"]); - //*/ + this.#createUnitMarkerControlButtons(); /* Create the checkboxes to select the advanced visibility options */ this.addVisibilityOption(SHOW_UNIT_CONTACTS, false); @@ -736,32 +728,53 @@ export class Map extends L.Map { return minimapBoundaries; } - #createOptionButtons() { + #createUnitMarkerControlButtons() { const unitVisibilityControls = document.getElementById("unit-visibility-control"); - const lockHTML = ``; - MAP_MARKER_CONTROLS.forEach( (control:MapMarkerControl) => { - console.log(`{"types":"${control.toggles.join('","')}"}`); - const html = ` - + `; - unitVisibilityControls.innerHTML += html; + unitVisibilityControls.appendChild(div); + + if ( control.protectable ) { + const btn = div.querySelector("button.lock"); + btn.addEventListener("click", (ev:MouseEventInit) => { + control.isProtected = !control.isProtected; + btn.toggleAttribute("data-protected", control.isProtected); + document.dispatchEvent(new CustomEvent("toggleMarkerProtection", { + detail: { + "_element": btn, + "control": control + } + })); + }); + } }); - unitVisibilityControls?.querySelectorAll(`img[src$=".svg"]`).forEach(img => SVGInjector(img)); + + unitVisibilityControls.querySelectorAll(`img[src$=".svg"]`).forEach(img => SVGInjector(img)); } - #createOptionButton(value: string, url: string, title: string, callback: string, argument: string) { - var button = document.createElement("button"); - const img = document.createElement("img"); - img.src = `/resources/theme/images/buttons/${url}`; - img.onload = () => SVGInjector(img); - button.title = title; - button.value = value; - button.appendChild(img); - button.setAttribute("data-on-click", callback); - button.setAttribute("data-on-click-params", argument); - return button; + unitIsProtected(unit:Unit) { + const toggles = this.#mapMarkerControls.reduce((list, control:MapMarkerControl) => { + if (control.isProtected) { + list = list.concat(control.toggles); + } + return list; + }, [] as string[]); + + if (toggles.length === 0) + return false; + + return toggles.some((toggle:string) => { + // Specific coding for robots - extend later if needed + return (toggle === "dcs" && !unit.getControlled() && !unit.getHuman()); + }); } #deselectCoalitionAreas() { diff --git a/client/src/unit/unit.ts b/client/src/unit/unit.ts index b28e88aa..87e9a2f1 100644 --- a/client/src/unit/unit.ts +++ b/client/src/unit/unit.ts @@ -95,45 +95,47 @@ export class Unit extends CustomMarker { #doubleClickTimer: number = 0; #hotgroup: number | null = null; #detectionMethods: number[] = []; + #isProtected:boolean = false; + getActivePath() { return this.#activePath }; getAlive() { return this.#alive }; - getHuman() { return this.#human }; - getControlled() { return this.#controlled }; + getAmmo() { return this.#ammo }; getCoalition() { return this.#coalition }; + getContacts() { return this.#contacts }; + getControlled() { return this.#controlled }; getCountry() { return this.#country }; - getName() { return this.#name }; - getUnitName() { return this.#unitName }; - getGroupName() { return this.#groupName }; - getState() { return this.#state }; - getTask() { return this.#task }; - getHasTask() { return this.#hasTask }; - getPosition() { return this.#position }; - getSpeed() { return this.#speed }; - getHeading() { return this.#heading }; - getIsActiveTanker() { return this.#isActiveTanker }; - getIsActiveAWACS() { return this.#isActiveAWACS }; - getOnOff() { return this.#onOff }; - getFollowRoads() { return this.#followRoads }; - getFuel() { return this.#fuel }; - getDesiredSpeed() { return this.#desiredSpeed }; - getDesiredSpeedType() { return this.#desiredSpeedType }; getDesiredAltitude() { return this.#desiredAltitude }; getDesiredAltitudeType() { return this.#desiredAltitudeType }; - getLeaderID() { return this.#leaderID }; + getDesiredSpeed() { return this.#desiredSpeed }; + getDesiredSpeedType() { return this.#desiredSpeedType }; + getEmissionsCountermeasures() { return this.#emissionsCountermeasures }; + getFollowRoads() { return this.#followRoads }; getFormationOffset() { return this.#formationOffset }; + getFuel() { return this.#fuel }; + getGeneralSettings() { return this.#generalSettings }; + getGroupName() { return this.#groupName }; + getHasTask() { return this.#hasTask }; + getHeading() { return this.#heading }; + getHuman() { return this.#human }; + getIsActiveAWACS() { return this.#isActiveAWACS }; + getIsActiveTanker() { return this.#isActiveTanker }; + getIsLeader() { return this.#isLeader }; + getLeaderID() { return this.#leaderID }; + getName() { return this.#name }; + getOnOff() { return this.#onOff }; + getOperateAs() { return this.#operateAs }; + getPosition() { return this.#position }; + getIsProtected() { return this.#isProtected }; + getRadio() { return this.#radio }; + getReactionToThreat() { return this.#reactionToThreat }; + getROE() { return this.#ROE }; + getSpeed() { return this.#speed }; + getState() { return this.#state }; + getTACAN() { return this.#TACAN }; getTargetID() { return this.#targetID }; getTargetPosition() { return this.#targetPosition }; - getROE() { return this.#ROE }; - getReactionToThreat() { return this.#reactionToThreat }; - getEmissionsCountermeasures() { return this.#emissionsCountermeasures }; - getTACAN() { return this.#TACAN }; - getRadio() { return this.#radio }; - getGeneralSettings() { return this.#generalSettings }; - getAmmo() { return this.#ammo }; - getContacts() { return this.#contacts }; - getActivePath() { return this.#activePath }; - getIsLeader() { return this.#isLeader }; - getOperateAs() { return this.#operateAs }; + getTask() { return this.#task }; + getUnitName() { return this.#unitName }; static getConstructor(type: string) { if (type === "GroundUnit") return GroundUnit; @@ -336,6 +338,10 @@ export class Unit extends CustomMarker { } } + setIsProtected(isProtected:boolean) { + this.#isProtected = isProtected; + } + setAlive(newAlive: boolean) { if (newAlive != this.#alive) document.dispatchEvent(new CustomEvent("unitDeath", { detail: this })); diff --git a/client/src/unit/unitsmanager.ts b/client/src/unit/unitsmanager.ts index 7e6fa97d..47dbbc86 100644 --- a/client/src/unit/unitsmanager.ts +++ b/client/src/unit/unitsmanager.ts @@ -47,6 +47,8 @@ export class UnitsManager { document.addEventListener('unitDeselection', (e: CustomEvent) => this.#onUnitDeselection(e.detail)); document.addEventListener('unitSelection', (e: CustomEvent) => this.#onUnitSelection(e.detail)); + document.addEventListener("toggleMarkerProtection", (ev:CustomEventInit) => { this.#showNumberOfSelectedProtectedUnits() }); + this.#slowDeleteDialog = new Dialog( "slow-delete-dialog" ); } @@ -221,16 +223,18 @@ export class UnitsManager { * @param options Selection options * @returns Array of selected units */ - getSelectedUnits(options?: { excludeHumans?: boolean, onlyOnePerGroup?: boolean }) { - var selectedUnits = []; - for (let ID in this.#units) { - if (this.#units[ID].getSelected()) { - selectedUnits.push(this.#units[ID]); + getSelectedUnits(options?: { excludeHumans?: boolean, excludeProtected?:boolean, onlyOnePerGroup?: boolean }) { + var selectedUnits:Unit[] = []; + for (const [ID, unit] of Object.entries(this.#units)) { + if (unit.getSelected()) { + if (options) { + if ((options.excludeHumans && unit.getHuman()) || (options.excludeProtected === true && this.#unitIsProtected(unit))) + continue + } + selectedUnits.push(unit); } } if (options) { - if (options.excludeHumans) - selectedUnits = selectedUnits.filter((unit: Unit) => { return !unit.getHuman() }); if (options.onlyOnePerGroup) { var temp: Unit[] = []; for (let unit of selectedUnits) { @@ -320,7 +324,7 @@ export class UnitsManager { * @param rotation Rotation in radians by which the formation will be rigidly rotated. E.g. a ( V ) formation will look like this ( < ) if rotated pi/4 radians (90 degrees) */ selectedUnitsAddDestination(latlng: L.LatLng, mantainRelativePosition: boolean, rotation: number) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); /* Compute the destination for each unit. If mantainRelativePosition is true, compute the destination so to hold the relative positions */ var unitDestinations: { [key: number]: LatLng } = {}; @@ -353,7 +357,7 @@ export class UnitsManager { * */ selectedUnitsClearDestinations() { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { const unit = selectedUnits[idx]; if (unit.getState() === "follow") { @@ -373,7 +377,7 @@ export class UnitsManager { * @param latlng Location where to land at */ selectedUnitsLandAt(latlng: LatLng) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].landAt(latlng); } @@ -385,7 +389,7 @@ export class UnitsManager { * @param speedChange Speed change, either "stop", "slow", or "fast". The specific value depends on the unit category */ selectedUnitsChangeSpeed(speedChange: string) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].changeSpeed(speedChange); } @@ -396,7 +400,7 @@ export class UnitsManager { * @param altitudeChange Altitude change, either "climb" or "descend". The specific value depends on the unit category */ selectedUnitsChangeAltitude(altitudeChange: string) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].changeAltitude(altitudeChange); } @@ -407,7 +411,7 @@ export class UnitsManager { * @param speed Value to set, in m/s */ selectedUnitsSetSpeed(speed: number) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].setSpeed(speed); } @@ -419,7 +423,7 @@ export class UnitsManager { * @param speedType Value to set, either "CAS" or "GS". If "CAS" is selected, the unit will try to maintain the selected Calibrated Air Speed, but DCS will still only maintain a Ground Speed value so errors may arise depending on wind. */ selectedUnitsSetSpeedType(speedType: string) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].setSpeedType(speedType); } @@ -431,7 +435,7 @@ export class UnitsManager { * @param altitude Value to set, in m */ selectedUnitsSetAltitude(altitude: number) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].setAltitude(altitude); } @@ -443,7 +447,7 @@ export class UnitsManager { * @param altitudeType Value to set, either "ASL" or "AGL". If "AGL" is selected, the unit will try to maintain the selected Above Ground Level altitude. Due to a DCS bug, this will only be true at the final position. */ selectedUnitsSetAltitudeType(altitudeType: string) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].setAltitudeType(altitudeType); } @@ -455,7 +459,7 @@ export class UnitsManager { * @param ROE Value to set, see constants for acceptable values */ selectedUnitsSetROE(ROE: string) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].setROE(ROE); } @@ -467,7 +471,7 @@ export class UnitsManager { * @param reactionToThreat Value to set, see constants for acceptable values */ selectedUnitsSetReactionToThreat(reactionToThreat: string) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].setReactionToThreat(reactionToThreat); } @@ -479,7 +483,7 @@ export class UnitsManager { * @param emissionCountermeasure Value to set, see constants for acceptable values */ selectedUnitsSetEmissionsCountermeasures(emissionCountermeasure: string) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].setEmissionsCountermeasures(emissionCountermeasure); } @@ -491,7 +495,7 @@ export class UnitsManager { * @param onOff If true, the unit will be turned on */ selectedUnitsSetOnOff(onOff: boolean) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].setOnOff(onOff); } @@ -503,7 +507,7 @@ export class UnitsManager { * @param followRoads If true, units will follow roads */ selectedUnitsSetFollowRoads(followRoads: boolean) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].setFollowRoads(followRoads); } @@ -516,7 +520,7 @@ export class UnitsManager { */ selectedUnitsSetOperateAs(operateAsBool: boolean) { var operateAs = operateAsBool? "blue": "red"; - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].setOperateAs(operateAs); } @@ -528,7 +532,7 @@ export class UnitsManager { * @param ID ID of the unit to attack */ selectedUnitsAttackUnit(ID: number) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].attackUnit(ID); } @@ -539,7 +543,7 @@ export class UnitsManager { * */ selectedUnitsRefuel() { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].refuel(); } @@ -565,7 +569,7 @@ export class UnitsManager { else offset = undefined; } - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); var count = 1; var xr = 0; var yr = 1; var zr = -1; @@ -601,7 +605,7 @@ export class UnitsManager { * @param latlng Location to bomb */ selectedUnitsBombPoint(latlng: LatLng) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].bombPoint(latlng); } @@ -613,7 +617,7 @@ export class UnitsManager { * @param latlng Location to bomb */ selectedUnitsCarpetBomb(latlng: LatLng) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].carpetBomb(latlng); } @@ -625,7 +629,7 @@ export class UnitsManager { * @param latlng Location to fire at */ selectedUnitsFireAtArea(latlng: LatLng) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].fireAtArea(latlng); } @@ -637,7 +641,7 @@ export class UnitsManager { * @param latlng Location to fire at */ selectedUnitsSimulateFireFight(latlng: LatLng) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); getGroundElevation(latlng, (response: string) => { var groundElevation: number | null = null; try { @@ -656,7 +660,7 @@ export class UnitsManager { * */ selectedUnitsScenicAAA() { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].scenicAAA(); } @@ -667,7 +671,7 @@ export class UnitsManager { * */ selectedUnitsMissOnPurpose() { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].missOnPurpose(); } @@ -679,7 +683,7 @@ export class UnitsManager { * @param latlng Point where to land */ selectedUnitsLandAtPoint(latlng: LatLng) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { selectedUnits[idx].landAtPoint(latlng); @@ -709,7 +713,7 @@ export class UnitsManager { * */ selectedUnitsCreateGroup() { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: false }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: false }); if (this.getUnitsCategories(selectedUnits).length == 1) { var units: { ID: number, location: LatLng }[] = []; for (let idx in selectedUnits) { @@ -750,7 +754,7 @@ export class UnitsManager { * @returns */ selectedUnitsDelete(explosion: boolean = false) { - var selectedUnits = this.getSelectedUnits(); /* Can be applied to humans too */ + var selectedUnits = this.getSelectedUnits({excludeProtected:true}); /* Can be applied to humans too */ const selectionContainsAHuman = selectedUnits.some((unit: Unit) => { return unit.getHuman() === true; }); @@ -760,7 +764,6 @@ export class UnitsManager { } const doDelete = (explosion = false, immediate = false) => { - const selectedUnits = this.getSelectedUnits(); for (let idx in selectedUnits) { selectedUnits[idx].delete(explosion, immediate); } @@ -786,7 +789,7 @@ export class UnitsManager { * @returns Array of positions for each unit, in order */ selectedUnitsComputeGroupDestination(latlng: LatLng, rotation: number) { - var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); + var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }); /* Compute the center of the group */ var center = { x: 0, y: 0 }; selectedUnits.forEach((unit: Unit) => { @@ -1065,6 +1068,7 @@ export class UnitsManager { window.setTimeout(() => { document.dispatchEvent(new CustomEvent("unitsSelection", { detail: this.getSelectedUnits() })); this.#selectionEventDisabled = false; + this.#showNumberOfSelectedProtectedUnits(); }, 100); this.#selectionEventDisabled = true; } @@ -1126,4 +1130,21 @@ export class UnitsManager { }, 250); }); } + + #showNumberOfSelectedProtectedUnits() { + const map = getApp().getMap(); + const selectedUnits = this.getSelectedUnits(); + const numSelectedUnits = selectedUnits.length; + const numProtectedUnits = selectedUnits.filter((unit:Unit) => map.unitIsProtected(unit) ).length; + + if (numProtectedUnits === 1 && numSelectedUnits === numProtectedUnits) + (getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Notice: unit is protected`); + + if (numProtectedUnits > 1) + (getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Notice: selection contains ${numProtectedUnits} protected units.`); + } + + #unitIsProtected(unit:Unit) { + return getApp().getMap().unitIsProtected(unit) + } } \ No newline at end of file