diff --git a/frontend/react/src/constants/constants.ts b/frontend/react/src/constants/constants.ts
index dd4506dc..0cc857a7 100644
--- a/frontend/react/src/constants/constants.ts
+++ b/frontend/react/src/constants/constants.ts
@@ -240,6 +240,7 @@ export const defaultMapLayers = {};
export const NOT_INITIALIZED = "Not initialized";
export const IDLE = "Idle";
export const SPAWN_UNIT = "Spawn unit";
+export const SPAWN_EFFECT = "Spawn effect";
export const CONTEXT_ACTION = "Context action";
export const COALITIONAREA_DRAW_POLYGON = "Draw Coalition Area polygon";
export const COALITIONAREA_DRAW_CIRCLE = "Draw Coalition Area circle";
@@ -275,6 +276,7 @@ export const MAP_OPTIONS_DEFAULTS = {
showUnitsAcquisitionRings: true,
fillSelectedRing: false,
showMinimap: false,
+ protectDCSUnits: true
} as MapOptions;
export const MAP_HIDDEN_TYPES_DEFAULTS = {
diff --git a/frontend/react/src/interfaces.ts b/frontend/react/src/interfaces.ts
index 07f139f4..f0a1b03f 100644
--- a/frontend/react/src/interfaces.ts
+++ b/frontend/react/src/interfaces.ts
@@ -71,6 +71,10 @@ export interface SpawnRequestTable {
unit: UnitSpawnTable;
}
+export interface EffectRequestTable {
+ type: string;
+}
+
export interface UnitSpawnTable {
unitType: string;
location: LatLng;
diff --git a/frontend/react/src/map/map.ts b/frontend/react/src/map/map.ts
index 993683be..841ec3b2 100644
--- a/frontend/react/src/map/map.ts
+++ b/frontend/react/src/map/map.ts
@@ -20,10 +20,11 @@ import {
COALITIONAREA_EDIT,
COALITIONAREA_DRAW_CIRCLE,
NOT_INITIALIZED,
+ SPAWN_EFFECT,
} from "../constants/constants";
import { CoalitionPolygon } from "./coalitionarea/coalitionpolygon";
import { MapHiddenTypes, MapOptions } from "../types/types";
-import { SpawnRequestTable } from "../interfaces";
+import { EffectRequestTable, SpawnRequestTable } from "../interfaces";
import { ContextAction } from "../unit/contextaction";
/* Stylesheets */
@@ -35,6 +36,7 @@ import { CoalitionCircle } from "./coalitionarea/coalitioncircle";
import { initDraggablePath } from "./coalitionarea/draggablepath";
import { faDrawPolygon, faHandPointer, faJetFighter, faMap } from "@fortawesome/free-solid-svg-icons";
+import { ExplosionMarker } from "./markers/explosionmarker";
/* Register the handler for the box selection */
L.Map.addInitHook("addHandler", "boxSelect", BoxSelect);
@@ -108,6 +110,7 @@ export class Map extends L.Map {
/* Unit spawning */
#spawnRequestTable: SpawnRequestTable | null = null;
+ #effectRequestTable: EffectRequestTable | null = null;
#temporaryMarkers: TemporaryUnitMarker[] = [];
#currentSpawnMarker: TemporaryUnitMarker | null = null;
@@ -342,6 +345,7 @@ export class Map extends L.Map {
state: string,
options?: {
spawnRequestTable?: SpawnRequestTable;
+ effectRequestTable?: EffectRequestTable;
contextAction?: ContextAction | null;
defaultContextAction?: ContextAction | null;
}
@@ -366,6 +370,13 @@ export class Map extends L.Map {
console.log(this.#spawnRequestTable);
this.#currentSpawnMarker = new TemporaryUnitMarker(new L.LatLng(0, 0), this.#spawnRequestTable?.unit.unitType ?? "", this.#spawnRequestTable?.coalition ?? "neutral")
this.#currentSpawnMarker.addTo(this);
+ } else if (this.#state === SPAWN_EFFECT) {
+ this.deselectAllCoalitionAreas();
+ this.#effectRequestTable = options?.effectRequestTable ?? null;
+ console.log(`Effect request table:`);
+ console.log(this.#effectRequestTable);
+ //this.#currentEffectMarker = new TemporaryUnitMarker(new L.LatLng(0, 0), this.#spawnRequestTable?.unit.unitType ?? "", this.#spawnRequestTable?.coalition ?? "neutral")
+ //this.#currentEffectMarker.addTo(this);
} else if (this.#state === CONTEXT_ACTION) {
this.deselectAllCoalitionAreas();
this.#contextAction = options?.contextAction ?? null;
@@ -435,6 +446,24 @@ export class Map extends L.Map {
text: "Move map location",
},
];
+ } else if (this.#state === SPAWN_EFFECT) {
+ return [
+ {
+ actions: [touch ? faHandPointer : "LMB"],
+ target: faMap,
+ text: "Spawn effect",
+ },
+ {
+ actions: [touch ? faHandPointer : "LMB", 2],
+ target: faMap,
+ text: "Exit spawn mode",
+ },
+ {
+ actions: [touch ? faHandPointer : "LMB", "Drag"],
+ target: faMap,
+ text: "Move map location",
+ },
+ ];
} else if (this.#state === CONTEXT_ACTION) {
let controls = [
{
@@ -670,6 +699,12 @@ export class Map extends L.Map {
return marker;
}
+ addExplosionMarker(latlng: L.LatLng, timeout: number = 30) {
+ var marker = new ExplosionMarker(latlng, timeout);
+ marker.addTo(this);
+ return marker;
+ }
+
getSelectedCoalitionArea() {
const coalitionArea = this.#coalitionAreas.find((coalitionArea: CoalitionPolygon | CoalitionCircle) => {
return coalitionArea.getSelected();
@@ -695,24 +730,6 @@ export class Map extends L.Map {
return this.#previousZoom;
}
- getIsUnitProtected(unit: Unit) {
- //const toggles = this.#mapMarkerVisibilityControls.reduce((list, control: MapMarkerVisibilityControl) => {
- // 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());
- //});
- return false;
- }
-
setSlaveDCSCamera(newSlaveDCSCamera: boolean) {
this.#slaveDCSCamera = newSlaveDCSCamera;
let button = document.getElementById("camera-link-control");
@@ -829,6 +846,7 @@ export class Map extends L.Map {
this.setState(COALITIONAREA_EDIT);
} else {
this.setState(IDLE);
+ document.dispatchEvent(new CustomEvent("hideAllMenus"))
}
}
@@ -861,7 +879,11 @@ export class Map extends L.Map {
}
);
}
- } else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
+ } else if (this.#state === SPAWN_EFFECT) {
+ if (e.originalEvent.button != 2 && this.#effectRequestTable !== null) {
+ getApp().getServerManager().spawnExplosion(50, 'normal', pressLocation);
+ }
+ } else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
const selectedArea = this.getSelectedCoalitionArea();
if (selectedArea && selectedArea instanceof CoalitionPolygon) {
selectedArea.addTemporaryLatLng(pressLocation);
diff --git a/frontend/react/src/map/markers/explosionmarker.ts b/frontend/react/src/map/markers/explosionmarker.ts
new file mode 100644
index 00000000..44beedaf
--- /dev/null
+++ b/frontend/react/src/map/markers/explosionmarker.ts
@@ -0,0 +1,38 @@
+import { CustomMarker } from "./custommarker";
+import { DivIcon, LatLng } from "leaflet";
+import { SVGInjector } from "@tanem/svg-injector";
+import { getApp } from "../../olympusapp";
+
+export class ExplosionMarker extends CustomMarker {
+ #timer: number = 0;
+ #timeout: number = 0;
+
+ constructor(latlng: LatLng, timeout: number) {
+ super(latlng, { interactive: false });
+
+ this.#timeout = timeout;
+
+ this.#timer = window.setTimeout(() => {
+ this.removeFrom(getApp().getMap());
+ }, timeout * 1000);
+ }
+
+ createIcon() {
+ /* Set the icon */
+ var icon = new DivIcon({
+ className: "leaflet-explosion-icon",
+ iconAnchor: [25, 25],
+ iconSize: [50, 50],
+ });
+ this.setIcon(icon);
+
+ var el = document.createElement("div");
+ var img = document.createElement("img");
+ img.src = `/vite/images/markers/smoke.svg`;
+ img.onload = () => SVGInjector(img);
+ el.append(img);
+
+ this.getElement()?.appendChild(el);
+ this.getElement()?.classList.add("ol-temporary-marker");
+ }
+}
diff --git a/frontend/react/src/mission/airbase.ts b/frontend/react/src/mission/airbase.ts
index 1c4ec32c..2d5a7117 100644
--- a/frontend/react/src/mission/airbase.ts
+++ b/frontend/react/src/mission/airbase.ts
@@ -2,6 +2,8 @@ import { DivIcon } from "leaflet";
import { CustomMarker } from "../map/markers/custommarker";
import { SVGInjector } from "@tanem/svg-injector";
import { AirbaseChartData, AirbaseOptions } from "../interfaces";
+import { getApp } from "../olympusapp";
+import { IDLE } from "../constants/constants";
export class Airbase extends CustomMarker {
#name: string = "";
@@ -21,6 +23,12 @@ export class Airbase extends CustomMarker {
this.#name = options.name;
this.#img = document.createElement("img");
+
+ this.addEventListener("click", (ev) => {
+ if (getApp().getMap().getState() === IDLE) {
+ document.dispatchEvent(new CustomEvent("airbaseClick", { detail: ev.target }));
+ }
+ });
}
createIcon() {
@@ -34,7 +42,7 @@ export class Airbase extends CustomMarker {
var el = document.createElement("div");
el.classList.add("airbase-icon");
el.setAttribute("data-object", "airbase");
-
+
this.#img.src = "/vite/images/markers/airbase.svg";
this.#img.onload = () => SVGInjector(this.#img);
el.appendChild(this.#img);
diff --git a/frontend/react/src/mission/carrier.ts b/frontend/react/src/mission/carrier.ts
index 50c030ac..35b5412b 100644
--- a/frontend/react/src/mission/carrier.ts
+++ b/frontend/react/src/mission/carrier.ts
@@ -2,8 +2,6 @@ import { DivIcon, LatLng, Map } from "leaflet";
import { Airbase } from "./airbase";
export class Carrier extends Airbase {
- #heading: number = 0;
-
createIcon() {
var icon = new DivIcon({
className: "leaflet-airbase-marker",
@@ -42,7 +40,6 @@ export class Carrier extends Airbase {
}
setHeading(heading: number) {
- this.#heading = heading;
this.getImg().style.transform = `rotate(${heading - 3.14 / 2}rad)`;
}
@@ -53,6 +50,7 @@ export class Carrier extends Airbase {
const maxMeters = this._map.containerPointToLatLng([0, y]).distanceTo(this._map.containerPointToLatLng([x, y]));
const meterPerPixel = maxMeters / x;
this.getImg().style.width = `${Math.round(333 / meterPerPixel)}px`;
+ this.setZIndexOffset(-10000);
}
}
}
diff --git a/frontend/react/src/mission/missionmanager.ts b/frontend/react/src/mission/missionmanager.ts
index 8215f9e0..1167a38b 100644
--- a/frontend/react/src/mission/missionmanager.ts
+++ b/frontend/react/src/mission/missionmanager.ts
@@ -91,7 +91,6 @@ export class MissionManager {
position: new LatLng(airbase.latitude, airbase.longitude),
name: airbaseCallsign,
}).addTo(getApp().getMap());
- this.#airbases[airbaseCallsign].on("click", (e) => this.#onAirbaseClick(e));
this.#loadAirbaseChartData(airbaseCallsign);
}
}
@@ -321,10 +320,6 @@ export class MissionManager {
if (requestRefresh) getApp().getServerManager().refreshAll();
}
- #onAirbaseClick(ev: any) {
- document.dispatchEvent(new CustomEvent("airbaseclick", { detail: ev.target }));
- }
-
#loadAirbaseChartData(callsign: string) {
if (!this.#theatre) {
return;
diff --git a/frontend/react/src/server/servermanager.ts b/frontend/react/src/server/servermanager.ts
index c4198ceb..1d4f8d02 100644
--- a/frontend/react/src/server/servermanager.ts
+++ b/frontend/react/src/server/servermanager.ts
@@ -349,7 +349,7 @@ export class ServerManager {
this.PUT(data, callback);
}
- createFormation(ID: number, isLeader: boolean, wingmenIDs: number[], callback: CallableFunction = () => {}) {
+ showFormationMenu(ID: number, isLeader: boolean, wingmenIDs: number[], callback: CallableFunction = () => {}) {
var command = { ID: ID, wingmenIDs: wingmenIDs, isLeader: isLeader };
var data = { setLeader: command };
this.PUT(data, callback);
diff --git a/frontend/react/src/types/types.ts b/frontend/react/src/types/types.ts
index 11f7ddad..abffc856 100644
--- a/frontend/react/src/types/types.ts
+++ b/frontend/react/src/types/types.ts
@@ -20,6 +20,7 @@ export type MapOptions = {
showUnitsAcquisitionRings: boolean;
fillSelectedRing: boolean;
showMinimap: boolean;
+ protectDCSUnits: boolean;
};
export type MapHiddenTypes = {
diff --git a/frontend/react/src/ui/components/oleffectlistentry.tsx b/frontend/react/src/ui/components/oleffectlistentry.tsx
new file mode 100644
index 00000000..bb535e7d
--- /dev/null
+++ b/frontend/react/src/ui/components/oleffectlistentry.tsx
@@ -0,0 +1,28 @@
+import React from "react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { IconProp } from "@fortawesome/fontawesome-svg-core";
+import { faArrowRight } from "@fortawesome/free-solid-svg-icons/faArrowRight";
+
+export function OlEffectListEntry(props: { icon: IconProp; label: string, onClick: () => void }) {
+ return (
+
+ );
+}
diff --git a/frontend/react/src/ui/components/olunitlistentry.tsx b/frontend/react/src/ui/components/olunitlistentry.tsx
index 1cfccda8..537534fc 100644
--- a/frontend/react/src/ui/components/olunitlistentry.tsx
+++ b/frontend/react/src/ui/components/olunitlistentry.tsx
@@ -2,17 +2,16 @@ import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { IconProp } from "@fortawesome/fontawesome-svg-core";
import { UnitBlueprint } from "../../interfaces";
-import { faArrowRightLong, faCaretRight, faCircleArrowRight, faLongArrowAltRight } from "@fortawesome/free-solid-svg-icons";
import { faArrowRight } from "@fortawesome/free-solid-svg-icons/faArrowRight";
-export function OlUnitEntryList(props: { icon: IconProp; blueprint: UnitBlueprint; onClick: () => void }) {
+export function OlUnitListEntry(props: { icon: IconProp; blueprint: UnitBlueprint; onClick: () => void }) {
return (
diff --git a/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx b/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx
index 357affad..e95db72d 100644
--- a/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx
+++ b/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx
@@ -84,6 +84,7 @@ export function MapContextMenu(props: {}) {
getApp()
.getUnitsManager()
.getSelectedUnits()
+ .filter(unit => !unit.getHuman())
.forEach((unit: Unit) => {
unit.appendContextActions(newContextActionSet);
});
diff --git a/frontend/react/src/ui/modals/protectionprompt.tsx b/frontend/react/src/ui/modals/protectionprompt.tsx
new file mode 100644
index 00000000..69002077
--- /dev/null
+++ b/frontend/react/src/ui/modals/protectionprompt.tsx
@@ -0,0 +1,90 @@
+import React from "react";
+import { Modal } from "./components/modal";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faArrowRight } from "@fortawesome/free-solid-svg-icons";
+import { Unit } from "../../unit/unit";
+import { FaLock } from "react-icons/fa6";
+
+export function ProtectionPrompt(props: {onContinue: (units: Unit[]) => void, onBack: () => void, units: Unit[] }) {
+ return (
+
+
+
+
+ Your selection contains protected units, are you sure you want to continue?
+
+
+ Pressing "Continue" will cause all DCS controlled units in the current selection to abort their mission and start following Olympus commands only.
+
+
+ If you are trying to delete a human player unit, they will be killed and de-slotted. Be careful!
+
+
+ To disable this warning, press on the button
+
+
+
+ {props.onContinue(props.units);}}
+ className={`
+ mb-2 me-2 ml-auto flex content-center items-center gap-2
+ rounded-sm bg-blue-700 px-5 py-2.5 text-sm font-medium text-white
+ dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
+ focus:outline-none focus:ring-4 focus:ring-blue-300
+ hover:bg-blue-800
+ `}
+ >
+ Continue
+
+
+
+ Back
+
+
+
+
+ );
+}
diff --git a/frontend/react/src/ui/panels/airbasemenu.tsx b/frontend/react/src/ui/panels/airbasemenu.tsx
index 91fa1b1f..a7c46f18 100644
--- a/frontend/react/src/ui/panels/airbasemenu.tsx
+++ b/frontend/react/src/ui/panels/airbasemenu.tsx
@@ -7,7 +7,7 @@ import { getUnitsByLabel } from "../../other/utils";
import { UnitBlueprint } from "../../interfaces";
import { OlSearchBar } from "../components/olsearchbar";
import { OlAccordion } from "../components/olaccordion";
-import { OlUnitEntryList } from "../components/olunitlistentry";
+import { OlUnitListEntry } from "../components/olunitlistentry";
import { olButtonsVisibilityAircraft, olButtonsVisibilityHelicopter } from "../components/olicons";
import { UnitSpawnMenu } from "./unitspawnmenu";
@@ -103,7 +103,7 @@ export function AirbaseMenu(props: { open: boolean; onClose: () => void; airbase
`}
>
{Object.entries(filteredAircraft).map((entry) => {
- return
setBlueprint(entry[1])} />;
+ return setBlueprint(entry[1])} />;
})}
@@ -114,7 +114,7 @@ export function AirbaseMenu(props: { open: boolean; onClose: () => void; airbase
`}
>
{Object.entries(filteredHelicopters).map((entry) => {
- return setBlueprint(entry[1])} />;
+ return setBlueprint(entry[1])} />;
})}
diff --git a/frontend/react/src/ui/panels/components/menu.tsx b/frontend/react/src/ui/panels/components/menu.tsx
index 8477f51f..d415a4f8 100644
--- a/frontend/react/src/ui/panels/components/menu.tsx
+++ b/frontend/react/src/ui/panels/components/menu.tsx
@@ -51,7 +51,7 @@ export function Menu(props: {
onClick={props.onBack ?? (() => {})}
icon={faArrowLeft}
className={`
- mr-1 h-8 cursor-pointer rounded-md p-2
+ mr-1 h-4 cursor-pointer rounded-md p-2
dark:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-white
`}
/>
diff --git a/frontend/react/src/ui/panels/effectspawnmenu.tsx b/frontend/react/src/ui/panels/effectspawnmenu.tsx
new file mode 100644
index 00000000..0aab3822
--- /dev/null
+++ b/frontend/react/src/ui/panels/effectspawnmenu.tsx
@@ -0,0 +1,45 @@
+import React, { useEffect, useState } from "react";
+import { OlDropdown, OlDropdownItem } from "../components/oldropdown";
+import { getApp } from "../../olympusapp";
+import { IDLE, SPAWN_EFFECT } from "../../constants/constants";
+
+export function EffectSpawnMenu(props: { effect: string }) {
+ const [explosionType, setExplosionType] = useState("High explosive");
+
+ /* When the menu is opened show the unit preview on the map as a cursor */
+ useEffect(() => {
+ if (props.effect !== null) {
+ getApp()
+ ?.getMap()
+ ?.setState(SPAWN_EFFECT, {
+ effectRequestTable: {
+ type: props.effect,
+ }
+ });
+ } else {
+ if (getApp().getMap().getState() === SPAWN_EFFECT) getApp().getMap().setState(IDLE);
+ }
+
+ });
+
+ return (
+
+
Explosion type
+
+
+ {["High explosive", "Napalm", "White phosphorous"].map((optionExplosionType) => {
+ return (
+ {
+ setExplosionType(optionExplosionType);
+ }}
+ >
+ {optionExplosionType}
+
+ );
+ })}
+
+
+ );
+}
diff --git a/frontend/react/src/ui/panels/header.tsx b/frontend/react/src/ui/panels/header.tsx
index 561725da..de76d58d 100644
--- a/frontend/react/src/ui/panels/header.tsx
+++ b/frontend/react/src/ui/panels/header.tsx
@@ -115,7 +115,7 @@ export function Header() {
flex h-fit flex-row items-center justify-start gap-1
`}
>
- {}} tooltip="Lock/unlock protected units (from scripted mission)" />
+ {getApp().getMap().setOption("protectDCSUnits", !appState.mapOptions.protectDCSUnits)}} tooltip="Lock/unlock protected units (from scripted mission)" />
{
diff --git a/frontend/react/src/ui/panels/sidebar.tsx b/frontend/react/src/ui/panels/sidebar.tsx
index b5edf8a7..c7512d3d 100644
--- a/frontend/react/src/ui/panels/sidebar.tsx
+++ b/frontend/react/src/ui/panels/sidebar.tsx
@@ -1,10 +1,9 @@
-import React, { useState } from "react";
+import React from "react";
import { OlStateButton } from "../components/olstatebutton";
-import { faGamepad, faRuler, faPencil, faEllipsisV, faCog, faQuestionCircle, faPlusSquare, faMagnifyingGlass, faPlaneDeparture, faRadio, faVolumeHigh } from "@fortawesome/free-solid-svg-icons";
+import { faGamepad, faRuler, faPencil, faEllipsisV, faCog, faQuestionCircle, faPlusSquare, faMagnifyingGlass, faRadio, faVolumeHigh } from "@fortawesome/free-solid-svg-icons";
import { EventsConsumer } from "../../eventscontext";
import { StateConsumer } from "../../statecontext";
import { IDLE } from "../../constants/constants";
-import { faSpeakerDeck } from "@fortawesome/free-brands-svg-icons";
export function SideBar() {
return (
@@ -53,12 +52,6 @@ export function SideBar() {
icon={faPencil}
tooltip="Hide/show drawing menu"
>
-
void; children?: JSX.Element | JSX.Element[] }) {
const [blueprint, setBlueprint] = useState(null as null | UnitBlueprint);
+ const [effect, setEffect] = useState(null as null | string);
const [filterString, setFilterString] = useState("");
const [filteredAircraft, filteredHelicopters, filteredAirDefense, filteredGroundUnits, filteredNavyUnits] = getUnitsByLabel(filterString);
@@ -25,7 +29,10 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
useEffect(() => {
if (!props.open && getApp()) {
if (getApp().getMap().getState() === SPAWN_UNIT) getApp().getMap().setState(IDLE);
+ else if (getApp().getMap().getState() === SPAWN_EFFECT) getApp().getMap().setState(IDLE);
+
if (blueprint !== null) setBlueprint(null);
+ if (effect !== null) setEffect(null);
}
});
@@ -33,15 +40,16 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
{
getApp().getMap().setState(IDLE);
setBlueprint(null);
+ setEffect(null);
}}
>
<>
- {blueprint === null && (
+ {blueprint === null && effect === null && (
setFilterString(value)} text={filterString} />
@@ -51,7 +59,7 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
`}
>
{Object.entries(filteredAircraft).map((entry) => {
- return setBlueprint(entry[1])} />;
+ return setBlueprint(entry[1])} />;
})}
@@ -62,7 +70,7 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
`}
>
{Object.entries(filteredHelicopters).map((entry) => {
- return setBlueprint(entry[1])} />;
+ return setBlueprint(entry[1])} />;
})}
@@ -73,7 +81,7 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
`}
>
{Object.entries(filteredAirDefense).map((entry) => {
- return setBlueprint(entry[1])} />;
+ return setBlueprint(entry[1])} />;
})}
@@ -84,7 +92,7 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
`}
>
{Object.entries(filteredGroundUnits).map((entry) => {
- return setBlueprint(entry[1])} />;
+ return setBlueprint(entry[1])} />;
})}
@@ -95,15 +103,39 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
`}
>
{Object.entries(filteredNavyUnits).map((entry) => {
- return setBlueprint(entry[1])} />;
+ return setBlueprint(entry[1])} />;
})}
-
+
+
+
{
+ setEffect("explosion");
+ }}
+ />
+ {
+ setEffect("smoke");
+ }}
+ />
+
+
)}
{!(blueprint === null) && }
+ {!(effect === null) && }
>
);
diff --git a/frontend/react/src/ui/panels/unitcontrolmenu.tsx b/frontend/react/src/ui/panels/unitcontrolmenu.tsx
index 10818e1c..a9f6253d 100644
--- a/frontend/react/src/ui/panels/unitcontrolmenu.tsx
+++ b/frontend/react/src/ui/panels/unitcontrolmenu.tsx
@@ -61,6 +61,8 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
ROE: undefined as undefined | string,
reactionToThreat: undefined as undefined | string,
emissionsCountermeasures: undefined as undefined | string,
+ scenicAAA: undefined as undefined | boolean,
+ missOnPurpose: undefined as undefined | boolean,
shotsScatter: undefined as undefined | number,
shotsIntensity: undefined as undefined | number,
operateAs: undefined as undefined | Coalition,
@@ -112,42 +114,6 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
if (!props.open && filterString !== "") setFilterString("");
});
- /* */
- const minAltitude = 0;
- const maxAltitude = getApp()
- ?.getUnitsManager()
- ?.getSelectedUnitsCategories()
- .every((category) => {
- return category === "Helicopter";
- })
- ? 20000
- : 60000;
- const altitudeStep = getApp()
- ?.getUnitsManager()
- ?.getSelectedUnitsCategories()
- .every((category) => {
- return category === "Helicopter";
- })
- ? 100
- : 500;
- const minSpeed = 0;
- const maxSpeed = getApp()
- ?.getUnitsManager()
- ?.getSelectedUnitsCategories()
- .every((category) => {
- return category === "Helicopter";
- })
- ? 200
- : 800;
- const speedStep = getApp()
- ?.getUnitsManager()
- ?.getSelectedUnitsCategories()
- .every((category) => {
- return category === "Helicopter";
- })
- ? 5
- : 10;
-
useEffect(() => {
/* When a unit is selected, update the data */
document.addEventListener("unitsSelection", (ev: CustomEventInit) => {
@@ -190,6 +156,12 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
emissionsCountermeasures: (unit: Unit) => {
return unit.getEmissionsCountermeasures();
},
+ scenicAAA: (unit: Unit) => {
+ return unit.getState() === "scenic-aaa";
+ },
+ missOnPurpose: (unit: Unit) => {
+ return unit.getState() === "miss-on-purpose";
+ },
shotsScatter: (unit: Unit) => {
return unit.getShotsScatter();
},
@@ -213,8 +185,12 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
},
isAudioSink: (unit: Unit) => {
return (
- getApp()?.getAudioManager().getSinks().filter((sink) => {
- return sink instanceof UnitSink}).length > 0 &&
+ getApp()
+ ?.getAudioManager()
+ .getSinks()
+ .filter((sink) => {
+ return sink instanceof UnitSink;
+ }).length > 0 &&
getApp()
?.getAudioManager()
.getSinks()
@@ -257,6 +233,37 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
[key: string]: UnitBlueprint;
};
+ const everyUnitIsGround = selectedCategories.every((category) => {
+ return category === "GroundUnit";
+ });
+ const everyUnitIsNavy = selectedCategories.every((category) => {
+ return category === "NavyUnit";
+ });
+ const everyUnitIsHelicopter = selectedCategories.every((category) => {
+ return category === "Helicopter";
+ });
+
+ const minAltitude = 0;
+ const minSpeed = 0;
+
+ let maxAltitude = 60000;
+ let maxSpeed = 800;
+
+ let altitudeStep = 500;
+ let speedStep = 10;
+
+ if (everyUnitIsHelicopter) {
+ maxAltitude = 20000;
+ maxSpeed = 200;
+ speedStep = 5;
+ altitudeStep = 100;
+ }
+
+ if (everyUnitIsGround || everyUnitIsNavy) {
+ maxSpeed = 60;
+ speedStep = 1;
+ }
+
return (
void }) {
{selectedUnitsData.desiredSpeed !== undefined ? selectedUnitsData.desiredSpeed + " KTS" : "Different values"}
- {
- selectedUnits.forEach((unit) => {
- unit.setSpeedType(selectedUnitsData.desiredSpeedType === "CAS" ? "GS" : "CAS");
- setSelectedUnitsData({
- ...selectedUnitsData,
- desiredSpeedType: selectedUnitsData.desiredSpeedType === "CAS" ? "GS" : "CAS",
+ {!(everyUnitIsGround || everyUnitIsNavy) && (
+ {
+ selectedUnits.forEach((unit) => {
+ unit.setSpeedType(selectedUnitsData.desiredSpeedType === "CAS" ? "GS" : "CAS");
+ setSelectedUnitsData({
+ ...selectedUnitsData,
+ desiredSpeedType: selectedUnitsData.desiredSpeedType === "CAS" ? "GS" : "CAS",
+ });
});
- });
- }}
- />
+ }}
+ />
+ )}
{
@@ -853,94 +862,152 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
return ["GroundUnit", "NavyUnit"].includes(category);
}) && (
<>
- {/* ============== Shots scatter START ============== */}
-
-
- Shots scatter
-
-
- {[olButtonsScatter1, olButtonsScatter2, olButtonsScatter3].map((icon, idx) => {
- return (
- {
- selectedUnits.forEach((unit) => {
- unit.setShotsScatter(idx + 1);
- setSelectedUnitsData({
- ...selectedUnitsData,
- shotsScatter: idx + 1,
- });
- });
- }}
- active={selectedUnitsData.shotsScatter === idx + 1}
- icon={icon}
- />
- );
- })}
-
-
- {/* ============== Shots scatter END ============== */}
- {/* ============== Shots intensity START ============== */}
-
-
- Shots intensity
-
-
- {[olButtonsIntensity1, olButtonsIntensity2, olButtonsIntensity3].map((icon, idx) => {
- return (
- {
- selectedUnits.forEach((unit) => {
- unit.setShotsIntensity(idx + 1);
- setSelectedUnitsData({
- ...selectedUnitsData,
- shotsIntensity: idx + 1,
- });
- });
- }}
- active={selectedUnitsData.shotsIntensity === idx + 1}
- icon={icon}
- />
- );
- })}
-
-
- {/* ============== Shots intensity END ============== */}
- {/* ============== Operate as toggle START ============== */}
-
-
- Operate as
-
-
{
- selectedUnits.forEach((unit) => {
- unit.setOperateAs(selectedUnitsData.operateAs === "blue" ? "red" : "blue");
- setSelectedUnitsData({
- ...selectedUnitsData,
- operateAs: selectedUnitsData.operateAs === "blue" ? "red" : "blue",
+
+ {/* ============== Scenic AAA toggle START ============== */}
+
+
+ Scenic AAA mode
+
+
{
+ selectedUnits.forEach((unit) => {
+ selectedUnitsData.scenicAAA ? unit.changeSpeed("stop") : unit.scenicAAA();
+ setSelectedUnitsData({
+ ...selectedUnitsData,
+ scenicAAA: !selectedUnitsData.scenicAAA,
+ missOnPurpose: false,
+ });
});
- });
- }}
- />
+ }}
+ />
+
+ {/* ============== Scenic AAA toggle END ============== */}
+ {/* ============== Miss on purpose toggle START ============== */}
+
+
+ Miss on purpose mode
+
+
{
+ selectedUnits.forEach((unit) => {
+ selectedUnitsData.missOnPurpose ? unit.changeSpeed("stop") : unit.missOnPurpose();
+ setSelectedUnitsData({
+ ...selectedUnitsData,
+ scenicAAA: false,
+ missOnPurpose: !selectedUnitsData.missOnPurpose,
+ });
+ });
+ }}
+ />
+
+ {/* ============== Miss on purpose toggle END ============== */}
+
+ {/* ============== Shots scatter START ============== */}
+
+
+ Shots scatter
+
+
+ {[olButtonsScatter1, olButtonsScatter2, olButtonsScatter3].map((icon, idx) => {
+ return (
+ {
+ selectedUnits.forEach((unit) => {
+ unit.setShotsScatter(idx + 1);
+ setSelectedUnitsData({
+ ...selectedUnitsData,
+ shotsScatter: idx + 1,
+ });
+ });
+ }}
+ active={selectedUnitsData.shotsScatter === idx + 1}
+ icon={icon}
+ />
+ );
+ })}
+
+
+ {/* ============== Shots scatter END ============== */}
+ {/* ============== Shots intensity START ============== */}
+
+
+ Shots intensity
+
+
+ {[olButtonsIntensity1, olButtonsIntensity2, olButtonsIntensity3].map((icon, idx) => {
+ return (
+ {
+ selectedUnits.forEach((unit) => {
+ unit.setShotsIntensity(idx + 1);
+ setSelectedUnitsData({
+ ...selectedUnitsData,
+ shotsIntensity: idx + 1,
+ });
+ });
+ }}
+ active={selectedUnitsData.shotsIntensity === idx + 1}
+ icon={icon}
+ />
+ );
+ })}
+
+
+ {/* ============== Shots intensity END ============== */}
+
+ {/* ============== Operate as toggle START ============== */}
+
+
+ Operate as
+
+
{
+ selectedUnits.forEach((unit) => {
+ unit.setOperateAs(selectedUnitsData.operateAs === "blue" ? "red" : "blue");
+ setSelectedUnitsData({
+ ...selectedUnitsData,
+ operateAs: selectedUnitsData.operateAs === "blue" ? "red" : "blue",
+ });
+ });
+ }}
+ />
+
+ {/* ============== Operate as toggle END ============== */}
- {/* ============== Operate as toggle END ============== */}
{/* ============== Follow roads toggle START ============== */}
void }) {
value={activeAdvancedSettings ? activeAdvancedSettings.TACAN.channel : 1}
>
-
+
{
@@ -1250,9 +1318,11 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
className={`
flex content-center gap-2 rounded-full
${selectedUnits[0].getFuel() > 40 && `bg-green-700`}
- ${selectedUnits[0].getFuel() > 10 && selectedUnits[0].getFuel() <= 40 && `
- bg-yellow-700
- `}
+ ${
+ selectedUnits[0].getFuel() > 10 &&
+ selectedUnits[0].getFuel() <= 40 &&
+ `bg-yellow-700`
+ }
${selectedUnits[0].getFuel() <= 10 && `bg-red-700`}
px-2 py-1 text-sm font-bold text-white
`}
diff --git a/frontend/react/src/ui/panels/unitexplosionmenu.tsx b/frontend/react/src/ui/panels/unitexplosionmenu.tsx
new file mode 100644
index 00000000..72be6675
--- /dev/null
+++ b/frontend/react/src/ui/panels/unitexplosionmenu.tsx
@@ -0,0 +1,56 @@
+import React, { useState } from "react";
+import { Menu } from "./components/menu";
+import { OlDropdown, OlDropdownItem } from "../components/oldropdown";
+import { Unit } from "../../unit/unit";
+import { getApp } from "../../olympusapp";
+
+export function UnitExplosionMenu(props: { open: boolean; onClose: () => void; units: Unit[] | null; children?: JSX.Element | JSX.Element[] }) {
+ const [explosionType, setExplosionType] = useState("High explosive");
+
+ return (
+
+
+
Explosion type
+
+
+ {["High explosive", "Napalm", "White phosphorous"].map((optionExplosionType) => {
+ return (
+ {
+ setExplosionType(optionExplosionType);
+ }}
+ >
+ {optionExplosionType}
+
+ );
+ })}
+
+ {props.units !== null && (
+
{
+ if (explosionType === "High explosive") {
+ getApp()?.getUnitsManager().delete(true, "normal", props.units);
+ } else if (explosionType === "Napalm") {
+ getApp()?.getUnitsManager().delete(true, "napalm", props.units);
+ } else if (explosionType === "White phosphorous") {
+ getApp()?.getUnitsManager().delete(true, "phosphorous", props.units);
+ }
+ props.onClose();
+ }}
+ className={`
+ mb-2 rounded-lg bg-blue-700 px-5 py-2.5 text-md font-medium
+ text-white
+ dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
+ focus:outline-none focus:ring-4 focus:ring-blue-300
+ hover:bg-blue-800
+ `}
+ >
+ Apply
+
+ )}
+
+
+ );
+}
diff --git a/frontend/react/src/ui/ui.tsx b/frontend/react/src/ui/ui.tsx
index 632a260d..bca7f513 100644
--- a/frontend/react/src/ui/ui.tsx
+++ b/frontend/react/src/ui/ui.tsx
@@ -26,6 +26,8 @@ import { RadioMenu } from "./panels/radiomenu";
import { AudioMenu } from "./panels/audiomenu";
import { FormationMenu } from "./panels/formationmenu";
import { Unit } from "../unit/unit";
+import { ProtectionPrompt } from "./modals/protectionprompt";
+import { UnitExplosionMenu } from "./panels/unitexplosionmenu";
export type OlympusUIState = {
mainMenuVisible: boolean;
@@ -51,6 +53,7 @@ export function UI() {
const [optionsMenuVisible, setOptionsMenuVisible] = useState(false);
const [airbaseMenuVisible, setAirbaseMenuVisible] = useState(false);
const [formationMenuVisible, setFormationMenuVisible] = useState(false);
+ const [unitExplosionMenuVisible, setUnitExplosionMenuVisible] = useState(false);
const [mapHiddenTypes, setMapHiddenTypes] = useState(MAP_HIDDEN_TYPES_DEFAULTS);
const [mapOptions, setMapOptions] = useState(MAP_OPTIONS_DEFAULTS);
const [checkingPassword, setCheckingPassword] = useState(false);
@@ -62,6 +65,10 @@ export function UI() {
const [airbase, setAirbase] = useState(null as null | Airbase);
const [formationLeader, setFormationLeader] = useState(null as null | Unit);
const [formationWingmen, setFormationWingmen] = useState(null as null | Unit[]);
+ const [protectionPromptVisible, setProtectionPromptVisible] = useState(false);
+ const [protectionCallback, setProtectionCallback] = useState(null as any);
+ const [protectionUnits, setProtectionUnits] = useState([] as Unit[]);
+ const [unitExplosionUnits, setUnitExplosionUnits] = useState([] as Unit[]);
useEffect(() => {
document.addEventListener("hiddenTypesChanged", (ev) => {
@@ -73,11 +80,15 @@ export function UI() {
});
document.addEventListener("mapStateChanged", (ev) => {
- if ((ev as CustomEvent).detail === IDLE) hideAllMenus();
- else if ((ev as CustomEvent).detail === CONTEXT_ACTION && window.innerWidth > 1000) setUnitControlMenuVisible(true);
+ //if ((ev as CustomEvent).detail === IDLE) hideAllMenus();
+ /*else*/ if ((ev as CustomEvent).detail === CONTEXT_ACTION && window.innerWidth > 1000) setUnitControlMenuVisible(true);
setMapState(String((ev as CustomEvent).detail));
});
+ document.addEventListener("hideAllMenus", (ev) => {
+ hideAllMenus();
+ });
+
document.addEventListener("mapSourceChanged", (ev) => {
var source = (ev as CustomEvent).detail;
setActiveMapSource(source);
@@ -90,18 +101,29 @@ export function UI() {
setActiveMapSource(sources[0]);
});
- document.addEventListener("airbaseclick", (ev) => {
+ document.addEventListener("airbaseClick", (ev) => {
hideAllMenus();
getApp().getMap().setState(IDLE);
setAirbase((ev as CustomEvent).detail);
setAirbaseMenuVisible(true);
});
- document.addEventListener("createFormation", (ev) => {
+ document.addEventListener("showFormationMenu", (ev) => {
setFormationMenuVisible(true);
setFormationLeader((ev as CustomEvent).detail.leader);
setFormationWingmen((ev as CustomEvent).detail.wingmen);
});
+
+ document.addEventListener("showProtectionPrompt", (ev: CustomEventInit) => {
+ setProtectionPromptVisible(true);
+ setProtectionCallback(() => {return ev.detail.callback});
+ setProtectionUnits(ev.detail.units);
+ });
+
+ document.addEventListener("showUnitExplosionMenu", (ev) => {
+ setUnitExplosionMenuVisible(true);
+ setUnitExplosionUnits((ev as CustomEvent).detail.units);
+ })
}, []);
function hideAllMenus() {
@@ -115,6 +137,7 @@ export function UI() {
setRadioMenuVisible(false);
setAudioMenuVisible(false);
setFormationMenuVisible(false);
+ setUnitExplosionMenuVisible(false);
}
function checkPassword(password: string) {
@@ -246,16 +269,36 @@ export function UI() {
/>
>
)}
+ {protectionPromptVisible && (
+ <>
+
+ {
+ protectionCallback(units);
+ setProtectionPromptVisible(false);
+ }}
+ onBack={() => {
+ setProtectionPromptVisible(false);
+ }}
+ units={protectionUnits}
+ />
+ >
+ )}
setMainMenuVisible(false)} />
setSpawnMenuVisible(false)} />
setOptionsMenuVisible(false)} options={mapOptions} />
setUnitControlMenuVisible(false)} />
setDrawingMenuVisible(false)} />
- setAirbaseMenuVisible(false)} airbase={airbase}/>
+ setAirbaseMenuVisible(false)} airbase={airbase} />
setRadioMenuVisible(false)} />
setAudioMenuVisible(false)} />
setFormationMenuVisible(false)} />
+ setUnitExplosionMenuVisible(false)} />
diff --git a/frontend/react/src/unit/unit.ts b/frontend/react/src/unit/unit.ts
index 1ec7bf35..d58ef9bf 100644
--- a/frontend/react/src/unit/unit.ts
+++ b/frontend/react/src/unit/unit.ts
@@ -12,7 +12,6 @@ import {
rad2deg,
bearing,
deg2rad,
- ftToM,
getGroundElevation,
coalitionToEnum,
nmToM,
@@ -51,30 +50,22 @@ import { Group } from "./group";
import { ContextActionSet } from "./contextactionset";
import * as turf from "@turf/turf";
import {
- olButtonsContextMissOnPurpose,
- olButtonsContextScenicAaa,
olButtonsContextSimulateFireFight,
- olButtonsContextDiamond,
- olButtonsContextEchelonLh,
- olButtonsContextEchelonRh,
olButtonsContextFollow,
- olButtonsContextFront,
olButtonsContextLandAtPoint,
- olButtonsContextLineAbreast,
- olButtonsContextTrail,
olButtonsContextAttack,
olButtonsContextRefuel,
} from "../ui/components/olicons";
import {
- faArrowDown,
- faExclamation,
+ faExplosion,
faLocationCrosshairs,
faLocationDot,
faMapLocation,
faPeopleGroup,
+ faPlaneArrival,
faQuestionCircle,
faRoute,
- faVolumeHigh,
+ faTrash,
faXmarksLines,
} from "@fortawesome/free-solid-svg-icons";
import { Carrier } from "../mission/carrier";
@@ -841,7 +832,7 @@ export abstract class Unit extends CustomMarker {
contextActionSet.addContextAction(
this,
"path",
- "Append destination",
+ "Create route",
"Click on the map to add a destination to the path",
faRoute,
"position",
@@ -850,6 +841,36 @@ export abstract class Unit extends CustomMarker {
}
);
+ contextActionSet.addContextAction(
+ this,
+ "delete",
+ "Delete unit",
+ "Deletes the unit",
+ faTrash,
+ null,
+ (units: Unit[], _1, _2) => {
+ getApp().getUnitsManager().delete(false);
+ },
+ {
+ executeImmediately: true,
+ }
+ );
+
+ contextActionSet.addContextAction(
+ this,
+ "explode",
+ "Explode unit",
+ "Explodes the unit",
+ faExplosion,
+ null,
+ (units: Unit[], _1, _2) => {
+ document.dispatchEvent(new CustomEvent("showUnitExplosionMenu", { detail: { units: units } }));
+ },
+ {
+ executeImmediately: true,
+ }
+ );
+
contextActionSet.addDefaultContextAction(this, "default", "Set destination", "", faRoute, null, (units: Unit[], targetUnit, targetPosition) => {
if (targetPosition) {
getApp().getUnitsManager().clearDestinations(units);
@@ -1211,7 +1232,24 @@ export abstract class Unit extends CustomMarker {
}
delete(explosion: boolean, explosionType: string, immediate: boolean) {
- getApp().getServerManager().deleteUnit(this.ID, explosion, explosionType, immediate);
+ getApp()
+ .getServerManager()
+ .deleteUnit(this.ID, explosion, explosionType, immediate, (commandHash) => {
+ /* When the command is executed, add an explosion marker where the unit was */
+ if (explosion) {
+ // TODO some commands don't currently return a commandHash, fix that!
+ let timer = window.setTimeout(() => {
+ //getApp()
+ // .getServerManager()
+ // .isCommandExecuted((res: any) => {
+ // if (res.commandExecuted) {
+ getApp().getMap().addExplosionMarker(this.getPosition());
+ window.clearInterval(timer);
+ // }
+ // }, commandHash);
+ }, 500);
+ }
+ });
}
refuel() {
@@ -1294,38 +1332,6 @@ export abstract class Unit extends CustomMarker {
this.#redrawMarker();
}
- applyFollowOptions(formation: string, units: Unit[]) {
- if (formation === "custom") {
- document.getElementById("custom-formation-dialog")?.classList.remove("hide");
- document.addEventListener("applyCustomFormation", () => {
- var dialog = document.getElementById("custom-formation-dialog");
- if (dialog) {
- dialog.classList.add("hide");
- var clock = 1;
- while (clock < 8) {
- if ((dialog.querySelector(`#formation-${clock}`)).checked) break;
- clock++;
- }
- var angleDeg = 360 - (clock - 1) * 45;
- var angleRad = deg2rad(angleDeg);
- var distance = ftToM(parseInt((dialog.querySelector(`#distance`)?.querySelector("input")).value));
- var upDown = ftToM(parseInt((dialog.querySelector(`#up-down`)?.querySelector("input")).value));
-
- // X: front-rear, positive front
- // Y: top-bottom, positive top
- // Z: left-right, positive right
- var x = distance * Math.cos(angleRad);
- var y = upDown;
- var z = distance * Math.sin(angleRad);
-
- getApp().getUnitsManager().followUnit(this.ID, { x: x, y: y, z: z }, undefined, units);
- }
- });
- } else {
- getApp().getUnitsManager().followUnit(this.ID, undefined, formation, units);
- }
- }
-
/***********************************************/
#onMouseUp(e: any) {
this.#isMouseDown = false;
@@ -1847,7 +1853,7 @@ export abstract class AirUnit extends Unit {
(units: Unit[], targetUnit: Unit | null, _) => {
if (targetUnit) {
document.dispatchEvent(
- new CustomEvent("createFormation", {
+ new CustomEvent("showFormationMenu", {
detail: {
leader: targetUnit,
wingmen: units.filter((unit) => unit !== targetUnit),
@@ -1881,6 +1887,18 @@ export abstract class AirUnit extends Unit {
if (targetPosition) getApp().getUnitsManager().carpetBomb(targetPosition, units);
}
);
+
+ contextActionSet.addContextAction(
+ this,
+ "land",
+ "Land",
+ "Click on a point to land at the nearest airbase",
+ faPlaneArrival,
+ "position",
+ (units: Unit[], _, targetPosition: LatLng | null) => {
+ if (targetPosition) getApp().getUnitsManager().landAt(targetPosition, units);
+ }
+ );
}
}
@@ -1990,33 +2008,6 @@ export class GroundUnit extends Unit {
{ executeImmediately: true }
);
- if (this.canAAA()) {
- contextActionSet.addContextAction(
- this,
- "scenic-aaa",
- "Scenic AAA",
- "Shoot AAA in the air without aiming at any target, when an enemy unit gets close enough. WARNING: works correctly only on neutral units, blue or red units will aim",
- olButtonsContextScenicAaa,
- null,
- (units: Unit[]) => {
- getApp().getUnitsManager().scenicAAA(units);
- },
- { executeImmediately: true }
- );
- contextActionSet.addContextAction(
- this,
- "miss-aaa",
- "Dynamic accuracy AAA",
- "Shoot AAA towards the closest enemy unit, but don't aim precisely. WARNING: works correctly only on neutral units, blue or red units will aim",
- olButtonsContextMissOnPurpose,
- null,
- (units: Unit[]) => {
- getApp().getUnitsManager().missOnPurpose(units);
- },
- { executeImmediately: true }
- );
- }
-
/* Context actions that require a target unit */
contextActionSet.addContextAction(
this,
diff --git a/frontend/react/src/unit/unitsmanager.ts b/frontend/react/src/unit/unitsmanager.ts
index 9cd6e7d7..98ef83f5 100644
--- a/frontend/react/src/unit/unitsmanager.ts
+++ b/frontend/react/src/unit/unitsmanager.ts
@@ -62,23 +62,10 @@ export class UnitsManager {
this.#requestDetectionUpdate = true;
});
document.addEventListener("copy", () => this.copy());
- document.addEventListener("deleteSelectedUnits", () => this.delete());
- document.addEventListener("explodeSelectedUnits", (e: any) => this.delete(true, e.detail.type));
- document.addEventListener("exportToFile", () => this.exportToFile());
- document.addEventListener("importFromFile", () => this.importFromFile());
document.addEventListener("keyup", (event) => this.#onKeyUp(event));
document.addEventListener("paste", () => this.paste());
- document.addEventListener("selectedUnitsChangeAltitude", (e: any) => {
- this.changeAltitude(e.detail.type);
- });
- document.addEventListener("selectedUnitsChangeSpeed", (e: any) => {
- this.changeSpeed(e.detail.type);
- });
document.addEventListener("unitDeselection", (e) => this.#onUnitDeselection((e as CustomEvent).detail));
document.addEventListener("unitSelection", (e) => this.#onUnitSelection((e as CustomEvent).detail));
- document.addEventListener("toggleMarkerProtection", (e) => {
- this.#showNumberOfSelectedProtectedUnits();
- });
//this.#slowDeleteDialog = new Dialog("slow-delete-dialog");
}
@@ -127,45 +114,6 @@ export class UnitsManager {
}
}
- /** Sort units segregated groups based on controlling type and protection, if DCS-controlled
- *
- * @param units
- * @returns Object
- */
- segregateUnits(units: Unit[]): { [key: string]: [] } {
- const data: any = {
- controllable: [],
- dcsProtected: [],
- dcsUnprotected: [],
- human: [],
- olympus: [],
- };
- const map = getApp().getMap();
-
- units.forEach((unit) => {
- if (unit.getHuman()) data.human.push(unit);
- else if (unit.isControlledByOlympus()) data.olympus.push(unit);
- else if (map.getIsUnitProtected(unit)) data.dcsProtected.push(unit);
- else data.dcsUnprotected.push(unit);
- });
- data.controllable = [].concat(data.dcsUnprotected, data.human, data.olympus);
- return data;
- }
-
- /**
- *
- * @param numOfProtectedUnits number
- */
- showProtectedUnitsPopup(numOfProtectedUnits: number) {
- if (numOfProtectedUnits < 1) return;
- const messageText = numOfProtectedUnits === 1 ? `Unit is protected` : `All selected units are protected`;
- //(getApp().getPopupsManager().get("infoPopup") as Popup).setText(messageText);
- // Cheap way for now until we use more locks
- let lock = document.querySelector("#unit-visibility-control button.lock");
- lock.classList.add("prompt");
- setTimeout(() => lock.classList.remove("prompt"), 4000);
- }
-
/** Update the data of all the units. The data is directly decoded from the binary buffer received from the REST Server. This is necessary for performance and bandwidth reasons.
*
* @param buffer The arraybuffer, encoded according to the ICD defined in: TODO Add reference to ICD
@@ -301,38 +249,10 @@ export class UnitsManager {
/** Get all the currently selected units
*
- * @param options Selection options
* @returns Array of selected units
*/
- getSelectedUnits(options?: { excludeHumans?: boolean; excludeProtected?: boolean; onlyOnePerGroup?: boolean; showProtectionReminder?: boolean }) {
- let selectedUnits: Unit[] = [];
- let numProtectedUnits = 0;
- for (const [ID, unit] of Object.entries(this.#units)) {
- if (unit.getSelected()) {
- if (options) {
- if (options.excludeHumans && unit.getHuman()) continue;
-
- if (options.excludeProtected === true && this.#unitIsProtected(unit)) {
- numProtectedUnits++;
- continue;
- }
- }
- selectedUnits.push(unit);
- }
- }
- if (options) {
- if (options.showProtectionReminder === true && numProtectedUnits > selectedUnits.length && selectedUnits.length === 0)
- this.showProtectedUnitsPopup(numProtectedUnits);
-
- if (options.onlyOnePerGroup) {
- var temp: Unit[] = [];
- for (let unit of selectedUnits) {
- if (!temp.some((otherUnit: Unit) => unit.getGroupName() == otherUnit.getGroupName())) temp.push(unit);
- }
- selectedUnits = temp;
- }
- }
- return selectedUnits;
+ getSelectedUnits() {
+ return Object.values(this.#units).filter((unit) => unit.getSelected());
}
/** Deselects all currently selected units
@@ -344,15 +264,7 @@ export class UnitsManager {
}
}
- /** Deselect a specific unit
- *
- * @param ID ID of the unit to deselect
- */
- deselectUnit(ID: number) {
- this.#units[ID]?.setSelected(false);
- }
-
- /** This function allows to quickly determine the categories (Aircraft, Helicopter, GroundUnit, NavyUnit) of an array units. This allows to enable/disable specific controls which can only be applied
+ /** This function allows to quickly determine the categories (Aircraft, Helicopter, GroundUnit, NavyUnit) of an array of units. This allows to enable/disable specific controls which can only be applied
* to specific categories.
*
* @param units Array of units of which to retrieve the categories
@@ -418,71 +330,62 @@ export class UnitsManager {
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
addDestination(latlng: L.LatLng, mantainRelativePosition: boolean, rotation: number, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
-
- units = segregatedUnits.controllable;
-
- /* Compute the destination for each unit. If mantainRelativePosition is true, compute the destination so to hold the relative positions */
- var unitDestinations: { [key: number]: LatLng } = {};
- if (mantainRelativePosition) unitDestinations = this.computeGroupDestination(latlng, rotation);
- else
- units.forEach((unit: Unit) => {
- unitDestinations[unit.ID] = latlng;
- });
-
- units.forEach((unit: Unit) => {
- /* If a unit is following another unit, and that unit is also selected, send the command to the followed ("leader") unit */
- if (unit.getState() === "follow") {
- const leader = this.getUnitByID(unit.getLeaderID());
- if (leader && leader.getSelected()) leader.addDestination(latlng);
- else unit.addDestination(latlng);
- } else {
- if (unit.ID in unitDestinations) unit.addDestination(unitDestinations[unit.ID]);
- }
+ units = units.filter((unit) => {
+ return !unit.getHuman();
});
- this.#showActionMessage(units, " new destination added");
+
+ let callback = (units) => {
+ /* Compute the destination for each unit. If mantainRelativePosition is true, compute the destination so to hold the relative positions */
+ var unitDestinations: { [key: number]: LatLng } = {};
+ if (mantainRelativePosition) unitDestinations = this.computeGroupDestination(latlng, rotation);
+ else
+ units.forEach((unit: Unit) => {
+ unitDestinations[unit.ID] = latlng;
+ });
+
+ units.forEach((unit: Unit) => {
+ /* If a unit is following another unit, and that unit is also selected, send the command to the followed ("leader") unit */
+ if (unit.getState() === "follow") {
+ const leader = this.getUnitByID(unit.getLeaderID());
+ if (leader && leader.getSelected()) leader.addDestination(latlng);
+ else unit.addDestination(latlng);
+ } else {
+ if (unit.ID in unitDestinations) unit.addDestination(unitDestinations[unit.ID]);
+ }
+ });
+ this.#showActionMessage(units, " new destination added");
+ };
+
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
/** Clear the destinations of all the selected units
*
*/
clearDestinations(units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: false,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ for (let idx in units) {
+ const unit = units[idx];
+ if (unit.getState() === "follow") {
+ const leader = this.getUnitByID(unit.getLeaderID());
+ if (leader && leader.getSelected()) leader.clearDestinations();
+ else unit.clearDestinations();
+ } else unit.clearDestinations();
+ }
- units = segregatedUnits.controllable;
-
- for (let idx in units) {
- const unit = units[idx];
- if (unit.getState() === "follow") {
- const leader = this.getUnitByID(unit.getLeaderID());
- if (leader && leader.getSelected()) leader.clearDestinations();
- else unit.clearDestinations();
- } else unit.clearDestinations();
- }
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
+ };
}
/** Instruct all the selected units to land at a specific location
@@ -491,311 +394,239 @@ export class UnitsManager {
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
landAt(latlng: LatLng, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.landAt(latlng));
- units = segregatedUnits.controllable;
+ this.#showActionMessage(units, " landing");
+ };
- units.forEach((unit: Unit) => unit.landAt(latlng));
-
- this.#showActionMessage(units, " landing");
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Instruct all the selected units to change their speed
*
* @param speedChange Speed change, either "stop", "slow", or "fast". The specific value depends on the unit category
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
changeSpeed(speedChange: string, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.changeSpeed(speedChange));
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.changeSpeed(speedChange));
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Instruct all the selected units to change their altitude
*
* @param altitudeChange Altitude change, either "climb" or "descend". The specific value depends on the unit category
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
changeAltitude(altitudeChange: string, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.changeAltitude(altitudeChange));
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.changeAltitude(altitudeChange));
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Set a specific speed to all the selected units
*
* @param speed Value to set, in m/s
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
setSpeed(speed: number, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.setSpeed(speed));
+ this.#showActionMessage(units, `setting speed to ${msToKnots(speed)} kts`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.setSpeed(speed));
- this.#showActionMessage(units, `setting speed to ${msToKnots(speed)} kts`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Set a specific speed type to all the selected units
*
* @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.
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
setSpeedType(speedType: string, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.setSpeedType(speedType));
+ this.#showActionMessage(units, `setting speed type to ${speedType}`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.setSpeedType(speedType));
- this.#showActionMessage(units, `setting speed type to ${speedType}`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Set a specific altitude to all the selected units
*
* @param altitude Value to set, in m
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
setAltitude(altitude: number, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.setAltitude(altitude));
+ this.#showActionMessage(units, `setting altitude to ${mToFt(altitude)} ft`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.setAltitude(altitude));
- this.#showActionMessage(units, `setting altitude to ${mToFt(altitude)} ft`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Set a specific altitude type to all the selected units
*
* @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.
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
setAltitudeType(altitudeType: string, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.setAltitudeType(altitudeType));
+ this.#showActionMessage(units, `setting altitude type to ${altitudeType}`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.setAltitudeType(altitudeType));
- this.#showActionMessage(units, `setting altitude type to ${altitudeType}`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Set a specific ROE to all the selected units
*
* @param ROE Value to set, see constants for acceptable values
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
setROE(ROE: string, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.setROE(ROE));
+ this.#showActionMessage(units, `ROE set to ${ROE}`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.setROE(ROE));
- this.#showActionMessage(units, `ROE set to ${ROE}`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Set a specific reaction to threat to all the selected units
*
* @param reactionToThreat Value to set, see constants for acceptable values
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
setReactionToThreat(reactionToThreat: string, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.setReactionToThreat(reactionToThreat));
+ this.#showActionMessage(units, `reaction to threat set to ${reactionToThreat}`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.setReactionToThreat(reactionToThreat));
- this.#showActionMessage(units, `reaction to threat set to ${reactionToThreat}`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Set a specific emissions & countermeasures to all the selected units
*
* @param emissionCountermeasure Value to set, see constants for acceptable values
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
setEmissionsCountermeasures(emissionCountermeasure: string, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.setEmissionsCountermeasures(emissionCountermeasure));
+ this.#showActionMessage(units, `emissions & countermeasures set to ${emissionCountermeasure}`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.setEmissionsCountermeasures(emissionCountermeasure));
- this.#showActionMessage(units, `emissions & countermeasures set to ${emissionCountermeasure}`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Turn selected units on or off, only works on ground and navy units
*
* @param onOff If true, the unit will be turned on
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
setOnOff(onOff: boolean, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.setOnOff(onOff));
+ this.#showActionMessage(units, `unit active set to ${onOff}`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.setOnOff(onOff));
- this.#showActionMessage(units, `unit active set to ${onOff}`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Instruct the selected units to follow roads, only works on ground units
*
* @param followRoads If true, units will follow roads
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
setFollowRoads(followRoads: boolean, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.setFollowRoads(followRoads));
+ this.#showActionMessage(units, `follow roads set to ${followRoads}`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.setFollowRoads(followRoads));
- this.#showActionMessage(units, `follow roads set to ${followRoads}`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Instruct selected units to operate as a certain coalition
*
* @param operateAsBool If true, units will operate as blue
@@ -803,74 +634,59 @@ export class UnitsManager {
*/
setOperateAs(operateAsBool: boolean, units: Unit[] | null = null) {
var operateAs = operateAsBool ? "blue" : "red";
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.setOperateAs(operateAs));
+ this.#showActionMessage(units, `operate as set to ${operateAs}`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.setOperateAs(operateAs));
- this.#showActionMessage(units, `operate as set to ${operateAs}`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Instruct units to attack a specific unit
*
* @param ID ID of the unit to attack
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
attackUnit(ID: number, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.attackUnit(ID));
+ this.#showActionMessage(units, `attacking unit ${this.getUnitByID(ID)?.getUnitName()}`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.attackUnit(ID));
- this.#showActionMessage(units, `attacking unit ${this.getUnitByID(ID)?.getUnitName()}`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Instruct units to refuel at the nearest tanker, if possible. Else units will RTB
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
refuel(units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- segregatedUnits.controllable.forEach((unit: Unit) => unit.refuel());
- this.#showActionMessage(segregatedUnits.controllable, `sent to nearest tanker`);
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.refuel());
+ this.#showActionMessage(units, `sent to nearest tanker`);
+ };
+
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Instruct the selected units to follow another unit in a formation. Only works for aircrafts and helicopters.
*
* @param ID ID of the unit to follow
@@ -879,52 +695,46 @@ export class UnitsManager {
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
followUnit(ID: number, offset?: { x: number; y: number; z: number }, formation?: string, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
-
- units = segregatedUnits.controllable;
-
- if (offset == undefined) {
- /* Simple formations with fixed offsets */
- offset = { x: 0, y: 0, z: 0 };
- if (formation === "trail") {
- offset.x = -50;
- offset.y = -30;
- offset.z = 0;
- } else if (formation === "echelon-lh") {
- offset.x = -50;
- offset.y = -10;
- offset.z = -50;
- } else if (formation === "echelon-rh") {
- offset.x = -50;
- offset.y = -10;
- offset.z = 50;
- } else if (formation === "line-abreast-lh") {
- offset.x = 0;
- offset.y = 0;
- offset.z = -50;
- } else if (formation === "line-abreast-rh") {
- offset.x = 0;
- offset.y = 0;
- offset.z = 50;
- } else if (formation === "front") {
- offset.x = 100;
- offset.y = 0;
- offset.z = 0;
- } else offset = undefined;
- }
+ let callback = (units) => {
+ if (offset == undefined) {
+ /* Simple formations with fixed offsets */
+ offset = { x: 0, y: 0, z: 0 };
+ if (formation === "trail") {
+ offset.x = -50;
+ offset.y = -30;
+ offset.z = 0;
+ } else if (formation === "echelon-lh") {
+ offset.x = -50;
+ offset.y = -10;
+ offset.z = -50;
+ } else if (formation === "echelon-rh") {
+ offset.x = -50;
+ offset.y = -10;
+ offset.z = 50;
+ } else if (formation === "line-abreast-lh") {
+ offset.x = 0;
+ offset.y = 0;
+ offset.z = -50;
+ } else if (formation === "line-abreast-rh") {
+ offset.x = 0;
+ offset.y = 0;
+ offset.z = 50;
+ } else if (formation === "front") {
+ offset.x = 100;
+ offset.y = 0;
+ offset.z = 0;
+ } else offset = undefined;
+ }
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
+ };
var count = 1;
var xr = 0;
var yr = 1;
@@ -974,234 +784,183 @@ export class UnitsManager {
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
bombPoint(latlng: LatLng, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.bombPoint(latlng));
+ this.#showActionMessage(units, `unit bombing point`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.bombPoint(latlng));
- this.#showActionMessage(units, `unit bombing point`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Instruct the selected units to perform carpet bombing of specific coordinates
*
* @param latlng Location to bomb
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
carpetBomb(latlng: LatLng, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.carpetBomb(latlng));
+ this.#showActionMessage(units, `unit carpet bombing point`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.carpetBomb(latlng));
- this.#showActionMessage(units, `unit carpet bombing point`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Instruct the selected units to fire at specific coordinates
*
* @param latlng Location to fire at
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
fireAtArea(latlng: LatLng, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.fireAtArea(latlng));
+ this.#showActionMessage(units, `unit firing at area`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.fireAtArea(latlng));
- this.#showActionMessage(units, `unit firing at area`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Instruct the selected units to simulate a fire fight at specific coordinates
*
* @param latlng Location to fire at
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
simulateFireFight(latlng: LatLng, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
-
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
-
- units = segregatedUnits.controllable;
-
- getGroundElevation(latlng, (response: string) => {
- var groundElevation: number | null = null;
- try {
- groundElevation = parseFloat(response);
- } catch {
- console.warn("Simulate fire fight: could not retrieve ground elevation");
- }
- units?.forEach((unit: Unit) => unit.simulateFireFight(latlng, groundElevation));
- });
- this.#showActionMessage(units, `unit simulating fire fight`);
+ // TODO
+ // if (units === null)
+ // units = this.getSelectedUnits();
+ // units = units.filter((unit => {!unit.getHuman()}));
+ //
+ // let callback = (units) => {
+ //
+ // getGroundElevation(latlng, (response: string) => {
+ // var groundElevation: number | null = null;
+ // try {
+ // groundElevation = parseFloat(response);
+ // } catch {
+ // console.warn("Simulate fire fight: could not retrieve ground elevation");
+ // }
+ //
+ //if (getApp().getMap().getOptions().protectDCSUnits && !units.every(unit => unit.isControlledByOlympus()))
+ // this.showProtectedUnitsPopup(units.filter(unit => unit.isControlledByDCS()).length, callback);} units?.forEach((unit: Unit) => unit.simulateFireFight(latlng, groundElevation));
+ // });
+ // this.#showActionMessage(units, `unit simulating fire fight`);
}
/** Instruct units to enter into scenic AAA mode. Units will shoot in the air without aiming
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
scenicAAA(units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.scenicAAA());
+ this.#showActionMessage(units, `unit set to perform scenic AAA`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.scenicAAA());
- this.#showActionMessage(units, `unit set to perform scenic AAA`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Instruct units to enter into dynamic accuracy/miss on purpose mode. Units will aim to the nearest enemy unit but not precisely.
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
missOnPurpose(units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.missOnPurpose());
+ this.#showActionMessage(units, `unit set to perform miss-on-purpose AAA`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.missOnPurpose());
- this.#showActionMessage(units, `unit set to perform miss-on-purpose AAA`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Instruct units to land at specific point
*
* @param latlng Point where to land
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
landAtPoint(latlng: LatLng, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.landAtPoint(latlng));
+ this.#showActionMessage(units, `unit landing at point`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.landAtPoint(latlng));
- this.#showActionMessage(units, `unit landing at point`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Set a specific shots scatter to all the selected units
*
* @param shotsScatter Value to set
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
setShotsScatter(shotsScatter: number, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- onlyOnePerGroup: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.setShotsScatter(shotsScatter));
+ this.#showActionMessage(units, `shots scatter set to ${shotsScatter}`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.setShotsScatter(shotsScatter));
- this.#showActionMessage(units, `shots scatter set to ${shotsScatter}`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/** Set a specific shots intensity to all the selected units
*
* @param shotsScatter Value to set
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
setShotsIntensity(shotsIntensity: number, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- onlyOnePerGroup: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ units.forEach((unit: Unit) => unit.setShotsIntensity(shotsIntensity));
+ this.#showActionMessage(units, `shots intensity set to ${shotsIntensity}`);
+ };
- units = segregatedUnits.controllable;
-
- units.forEach((unit: Unit) => unit.setShotsIntensity(shotsIntensity));
- this.#showActionMessage(units, `shots intensity set to ${shotsIntensity}`);
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
-
/*********************** Control operations on selected units ************************/
/** See getUnitsCategories for more info
*
@@ -1224,30 +983,25 @@ export class UnitsManager {
*
*/
createGroup(units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: false,
- showProtectionReminder: true,
- });
+ if (units === null) units = this.getSelectedUnits();
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
+ let callback = (units) => {
+ if (this.getUnitsCategories(units).length == 1) {
+ var unitsData: { ID: number; location: LatLng }[] = [];
+ units.forEach((unit: Unit) => unitsData.push({ ID: unit.ID, location: unit.getPosition() }));
+ getApp().getServerManager().cloneUnits(unitsData, true, 0 /* No spawn points, we delete the original units */);
+ this.#showActionMessage(units, `created a group`);
+ } else {
+ //(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Groups can only be created from units of the same category`);
+ }
- units = segregatedUnits.controllable;
-
- if (this.getUnitsCategories(units).length == 1) {
- var unitsData: { ID: number; location: LatLng }[] = [];
- units.forEach((unit: Unit) => unitsData.push({ ID: unit.ID, location: unit.getPosition() }));
- getApp().getServerManager().cloneUnits(unitsData, true, 0 /* No spawn points, we delete the original units */);
- this.#showActionMessage(units, `created a group`);
- } else {
- //(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Groups can only be created from units of the same category`);
- }
+ if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
+ };
}
/** Set the hotgroup for the selected units. It will be the only hotgroup of the unit
@@ -1279,45 +1033,17 @@ export class UnitsManager {
* @returns
*/
delete(explosion: boolean = false, explosionType: string = "", units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeProtected: true,
- showProtectionReminder: true,
- }); /* Can be applied to humans too */
+ // TODO add fast delete option
+ if (units === null) units = this.getSelectedUnits(); /* Can be applied to humans too */
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return;
- }
-
- units = segregatedUnits.controllable;
-
- const selectionContainsAHuman = units.some((unit: Unit) => {
- return unit.getHuman() === true;
- });
-
- if (
- selectionContainsAHuman &&
- !confirm("Your selection includes a human player. Deleting humans causes their vehicle to crash.\n\nAre you sure you want to do this?")
- ) {
- return;
- }
-
- const doDelete = (explosion = false, explosionType = "", immediate = false) => {
- units?.forEach((unit: Unit) => unit.delete(explosion, explosionType, immediate));
+ let callback = (units) => {
+ units?.forEach((unit: Unit) => unit.delete(explosion, explosionType, false));
this.#showActionMessage(units as Unit[], `deleted`);
};
- //if (units.length >= DELETE_SLOW_THRESHOLD)
- // //this.#showSlowDeleteDialog(units).then((action: any) => {
- // // if (action === "delete-slow")
- // // doDelete(explosion, explosionType, false);
- // // else if (action === "delete-immediate")
- // // doDelete(explosion, explosionType, true);
- // //})
- //else
- doDelete(explosion, explosionType);
+ if ((getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus())) || units.find((unit) => unit.getHuman()))
+ document.dispatchEvent(new CustomEvent("showProtectionPrompt", { detail: { callback: callback, units: units } }));
+ else callback(units);
}
/** Compute the destinations of every unit in the selected units. This function preserves the relative positions of the units, and rotates the whole formation by rotation.
@@ -1328,20 +1054,12 @@ export class UnitsManager {
* @returns Array of positions for each unit, in order
*/
computeGroupDestination(latlng: LatLng, rotation: number, units: Unit[] | null = null) {
- if (units === null)
- units = this.getSelectedUnits({
- excludeHumans: true,
- excludeProtected: true,
- onlyOnePerGroup: true,
- });
+ // TODO handle protected units
+ if (units === null) units = this.getSelectedUnits();
- const segregatedUnits = this.segregateUnits(units);
- if (segregatedUnits.controllable.length === 0) {
- this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
- return {};
- }
-
- units = segregatedUnits.controllable;
+ units = units.filter((unit) => {
+ return !unit.getHuman();
+ });
if (units.length === 0) return {};
@@ -1818,7 +1536,7 @@ export class UnitsManager {
const map = getApp().getMap();
const units = this.getSelectedUnits();
const numSelectedUnits = units.length;
- const numProtectedUnits = units.filter((unit: Unit) => map.getIsUnitProtected(unit)).length;
+ //const numProtectedUnits = units.filter((unit: Unit) => map.getIsUnitProtected(unit)).length;
//if (numProtectedUnits === 1 && numSelectedUnits === numProtectedUnits)
//(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Notice: unit is protected`);
@@ -1826,8 +1544,4 @@ export class UnitsManager {
//if (numProtectedUnits > 1)
//(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Notice: selection contains ${numProtectedUnits} protected units.`);
}
-
- #unitIsProtected(unit: Unit) {
- return getApp().getMap().getIsUnitProtected(unit);
- }
}