diff --git a/frontend/react/src/constants/constants.ts b/frontend/react/src/constants/constants.ts
index dd6aead3..4e58d9f4 100644
--- a/frontend/react/src/constants/constants.ts
+++ b/frontend/react/src/constants/constants.ts
@@ -289,6 +289,7 @@ export enum OlympusState {
AUDIO = "Audio",
AIRBASE = "Airbase",
GAME_MASTER = "Game master",
+ IMPORT_EXPORT = "Import/export"
}
export const NO_SUBSTATE = "No substate";
@@ -334,6 +335,13 @@ export enum OptionsSubstate {
KEYBIND = "Keybind",
}
+export enum ImportExportSubstate {
+ NO_SUBSTATE = "No substate",
+ IMPORT = "IMPORT",
+ EXPORT = "EXPORT"
+}
+
+
export type OlympusSubState = DrawSubState | JTACSubState | SpawnSubState | OptionsSubstate | string;
export const IADSTypes = ["AAA", "SAM Site", "Radar (EWR)"];
diff --git a/frontend/react/src/events.ts b/frontend/react/src/events.ts
index 97d55134..5e35c353 100644
--- a/frontend/react/src/events.ts
+++ b/frontend/react/src/events.ts
@@ -1,7 +1,7 @@
import { AudioSink } from "./audio/audiosink";
import { AudioSource } from "./audio/audiosource";
import { OlympusState, OlympusSubState } from "./constants/constants";
-import { CommandModeOptions, OlympusConfig, ServerStatus, SessionData, SpawnRequestTable, UnitData } from "./interfaces";
+import { CommandModeOptions, MissionData, OlympusConfig, ServerStatus, SessionData, SpawnRequestTable, UnitData } from "./interfaces";
import { CoalitionCircle } from "./map/coalitionarea/coalitioncircle";
import { CoalitionPolygon } from "./map/coalitionarea/coalitionpolygon";
import { Airbase } from "./mission/airbase";
@@ -12,6 +12,7 @@ import { ContextAction } from "./unit/contextaction";
import { ContextActionSet } from "./unit/contextactionset";
import { Unit } from "./unit/unit";
import { LatLng } from "leaflet";
+import { Weapon } from "./weapon/weapon";
export class BaseOlympusEvent {
static on(callback: () => void, singleShot = false) {
@@ -348,8 +349,33 @@ export class UnitSelectedEvent extends BaseUnitEvent {}
export class UnitDeselectedEvent extends BaseUnitEvent {}
export class UnitDeadEvent extends BaseUnitEvent {}
export class SelectionClearedEvent extends BaseOlympusEvent {}
-export class UnitsRefreshed extends BaseOlympusEvent {}
-export class WeaponsRefreshed extends BaseOlympusEvent {}
+
+export class UnitsRefreshedEvent {
+ static on(callback: (units: { [ID: number]: Unit }) => void, singleShot = false) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail);
+ }, {once: singleShot});
+ }
+
+ static dispatch(units: { [ID: number]: Unit }) {
+ document.dispatchEvent(new CustomEvent(this.name, { detail: units }));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
+export class WeaponsRefreshedEvent {
+ static on(callback: (weapons: { [ID: number]: Weapon }) => void, singleShot = false) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail);
+ }, {once: singleShot});
+ }
+
+ static dispatch(weapons: { [ID: number]: Weapon }) {
+ document.dispatchEvent(new CustomEvent(this.name, { detail: weapons }));
+ console.log(`Event ${this.name} dispatched`);
+ }
+}
+
export class SelectedUnitsChangedEvent {
static on(callback: (selectedUnits: Unit[]) => void, singleShot = false) {
@@ -361,7 +387,6 @@ export class SelectedUnitsChangedEvent {
static dispatch(selectedUnits: Unit[]) {
document.dispatchEvent(new CustomEvent(this.name, { detail: selectedUnits }));
console.log(`Event ${this.name} dispatched`);
- console.log(selectedUnits);
}
}
@@ -578,7 +603,7 @@ export class AudioManagerOutputChangedEvent {
}
/************** Mission data events ***************/
-export class BullseyesDataChanged {
+export class BullseyesDataChangedEvent {
static on(callback: (bullseyes: { [name: string]: Bullseye }) => void, singleShot = false) {
document.addEventListener(this.name, (ev: CustomEventInit) => {
callback(ev.detail.bullseyes);
@@ -590,3 +615,16 @@ export class BullseyesDataChanged {
// Logging disabled since periodic
}
}
+
+export class MissionDataChangedEvent {
+ static on(callback: (missionData: MissionData) => void, singleShot = false) {
+ document.addEventListener(this.name, (ev: CustomEventInit) => {
+ callback(ev.detail.missionData);
+ }, {once: singleShot});
+ }
+
+ static dispatch(missionData: MissionData) {
+ document.dispatchEvent(new CustomEvent(this.name, { detail: { missionData } }));
+ // Logging disabled since periodic
+ }
+}
diff --git a/frontend/react/src/interfaces.ts b/frontend/react/src/interfaces.ts
index 6b1eaa18..cc05c84c 100644
--- a/frontend/react/src/interfaces.ts
+++ b/frontend/react/src/interfaces.ts
@@ -200,6 +200,7 @@ export interface Offset {
export interface UnitData {
category: string;
+ markerCategory: string;
ID: number;
alive: boolean;
human: boolean;
diff --git a/frontend/react/src/mission/missionmanager.ts b/frontend/react/src/mission/missionmanager.ts
index 2a160349..692eee09 100644
--- a/frontend/react/src/mission/missionmanager.ts
+++ b/frontend/react/src/mission/missionmanager.ts
@@ -6,7 +6,7 @@ import { BLUE_COMMANDER, GAME_MASTER, NONE, RED_COMMANDER } from "../constants/c
import { AirbasesData, BullseyesData, CommandModeOptions, DateAndTime, MissionData } from "../interfaces";
import { Coalition } from "../types/types";
import { Carrier } from "./carrier";
-import { AirbaseSelectedEvent, AppStateChangedEvent, BullseyesDataChanged, CommandModeOptionsChangedEvent, InfoPopupEvent } from "../events";
+import { AirbaseSelectedEvent, AppStateChangedEvent, BullseyesDataChangedEvent, CommandModeOptionsChangedEvent, InfoPopupEvent, MissionDataChangedEvent } from "../events";
/** The MissionManager */
export class MissionManager {
@@ -61,7 +61,7 @@ export class MissionManager {
this.#bullseyes[idx].setCoalition(bullseye.coalition);
}
- BullseyesDataChanged.dispatch(this.#bullseyes)
+ BullseyesDataChangedEvent.dispatch(this.#bullseyes)
}
}
@@ -96,6 +96,8 @@ export class MissionManager {
*/
updateMission(data: MissionData) {
if (data.mission) {
+ MissionDataChangedEvent.dispatch(data);
+
/* Set the mission theatre */
if (data.mission.theatre != this.#theatre) {
this.#theatre = data.mission.theatre;
diff --git a/frontend/react/src/server/servermanager.ts b/frontend/react/src/server/servermanager.ts
index 28facd2a..9675381b 100644
--- a/frontend/react/src/server/servermanager.ts
+++ b/frontend/react/src/server/servermanager.ts
@@ -488,7 +488,7 @@ export class ServerManager {
this.PUT(data, callback);
}
- setAdvacedOptions(
+ setAdvancedOptions(
ID: number,
isActiveTanker: boolean,
isActiveAWACS: boolean,
diff --git a/frontend/react/src/ui/modals/importexportmodal.tsx b/frontend/react/src/ui/modals/importexportmodal.tsx
new file mode 100644
index 00000000..9c1d2ee2
--- /dev/null
+++ b/frontend/react/src/ui/modals/importexportmodal.tsx
@@ -0,0 +1,369 @@
+import React, { useEffect, useState } from "react";
+import { Modal } from "./components/modal";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faArrowRight } from "@fortawesome/free-solid-svg-icons";
+import { getApp } from "../../olympusapp";
+import { ImportExportSubstate, NO_SUBSTATE, OlympusState } from "../../constants/constants";
+import { AppStateChangedEvent, MissionDataChangedEvent, UnitsRefreshedEvent } from "../../events";
+import {
+ olButtonsVisibilityDcs,
+ olButtonsVisibilityGroundunit,
+ olButtonsVisibilityGroundunitSam,
+ olButtonsVisibilityNavyunit,
+ olButtonsVisibilityOlympus,
+} from "../components/olicons";
+import { OlToggle } from "../components/oltoggle";
+import { deepCopyTable } from "../../other/utils";
+import { OlCheckbox } from "../components/olcheckbox";
+import { Unit } from "../../unit/unit";
+import { MissionData, UnitData } from "../../interfaces";
+
+export function ImportExportModal(props: { open: boolean }) {
+ const [appState, setAppState] = useState(OlympusState.NOT_INITIALIZED);
+ const [appSubState, setAppSubState] = useState(NO_SUBSTATE);
+ const [units, setUnits] = useState({} as { [ID: number]: Unit });
+ const [missionData, setMissionData] = useState({} as MissionData);
+ const [importData, setImportData] = useState({} as { [key: string]: UnitData[] });
+
+ function resetFilter() {
+ return {
+ control: {
+ dcs: true,
+ olympus: true,
+ },
+ blue: {
+ "groundunit-sam": true,
+ groundunit: true,
+ navyunit: true,
+ },
+ neutral: {
+ "groundunit-sam": true,
+ groundunit: true,
+ navyunit: true,
+ },
+ red: {
+ "groundunit-sam": true,
+ groundunit: true,
+ navyunit: true,
+ }
+ };
+ }
+
+ const [selectionFilter, setSelectionFilter] = useState(resetFilter);
+
+ useEffect(() => {
+ AppStateChangedEvent.on((appState, appSubState) => {
+ setAppState(appState);
+ setAppSubState(appSubState);
+ });
+ UnitsRefreshedEvent.on((units) => setUnits(units));
+ MissionDataChangedEvent.on((missionData) => setMissionData(missionData));
+ }, []);
+
+ useEffect(() => {
+ setSelectionFilter(resetFilter);
+
+ if (appState === OlympusState.IMPORT_EXPORT && appSubState === ImportExportSubstate.IMPORT) {
+ setImportData({});
+ var input = document.createElement("input");
+ input.type = "file";
+
+ input.onchange = async (e) => {
+ // @ts-ignore TODO
+ var file = e.target?.files[0];
+ var reader = new FileReader();
+ reader.readAsText(file, "UTF-8");
+ reader.onload = (readerEvent) => {
+ // @ts-ignore TODO
+ var content = readerEvent.target.result;
+ if (content) {
+ setImportData(JSON.parse(content as string));
+ }
+ };
+ };
+
+ input.click();
+ }
+ }, [appState, appSubState]);
+
+ const selectableUnits = Object.values(units).filter((unit) => {
+ return (
+ unit.getAlive() &&
+ !unit.getHuman() &&
+ ((unit.isControlledByDCS() && selectionFilter.control.dcs) || (unit.isControlledByOlympus() && selectionFilter.control.olympus))
+ );
+ });
+
+ return (
+
+
+
+
+ {appSubState === ImportExportSubstate.EXPORT ? "Export to file" : "Import from file"}
+
+
+
+ {appSubState === ImportExportSubstate.EXPORT ? <>Select what units you want to export to file using the toggles below> : <>>}
+
+
+
+
+ Control mode
+
+
+
+ {Object.entries({
+ olympus: ["Olympus controlled", olButtonsVisibilityOlympus],
+ dcs: ["From DCS mission", olButtonsVisibilityDcs],
+ }).map((entry, idx) => {
+ return (
+
+
{entry[1][0] as string}
+
{
+ selectionFilter["control"][entry[0]] = !selectionFilter["control"][entry[0]];
+ setSelectionFilter(deepCopyTable(selectionFilter));
+ }}
+ toggled={selectionFilter["control"][entry[0]]}
+ />
+
+ );
+ })}
+
+
+
+ Types and coalitions
+
+
+
+
+
+
+ BLUE
+ NEUTRAL
+ RED
+
+ {Object.entries({
+ "groundunit-sam": olButtonsVisibilityGroundunitSam,
+ groundunit: olButtonsVisibilityGroundunit,
+ navyunit: olButtonsVisibilityNavyunit,
+ }).map((entry, idx) => {
+ return (
+
+
+
+
+ {["blue", "neutral", "red"].map((coalition) => {
+ return (
+
+ unit.getMarkerCategory() === entry[0] && unit.getCoalition() === coalition) !== undefined) ||
+ (appSubState === ImportExportSubstate.IMPORT &&
+ selectionFilter[coalition][entry[0]] &&
+ Object.values(importData).find((group) =>
+ group.find((unit) => unit.markerCategory === entry[0] && unit.coalition === coalition)
+ ) !== undefined)
+ }
+ disabled={
+ (appSubState === ImportExportSubstate.EXPORT &&
+ selectableUnits.find((unit) => unit.getMarkerCategory() === entry[0] && unit.getCoalition() === coalition) === undefined) ||
+ (appSubState === ImportExportSubstate.IMPORT &&
+ Object.values(importData).find((group) =>
+ group.find((unit) => unit.markerCategory === entry[0] && unit.coalition === coalition)
+ ) === undefined)
+ }
+ onChange={() => {
+ selectionFilter[coalition][entry[0]] = !selectionFilter[coalition][entry[0]];
+ setSelectionFilter(deepCopyTable(selectionFilter));
+ }}
+ />
+
+ {appSubState === ImportExportSubstate.EXPORT &&
+ selectableUnits.filter((unit) => unit.getMarkerCategory() === entry[0] && unit.getCoalition() === coalition).length}
+ {appSubState === ImportExportSubstate.IMPORT &&
+ Object.values(importData)
+ .flatMap((unit) => unit)
+ .filter((unit) => unit.markerCategory === entry[0] && unit.coalition === coalition).length}{" "}
+ units{" "}
+
+
+ );
+ })}
+
+ );
+ })}
+ {
+
+
+
+ value)}
+ onChange={() => {
+ const newValue = !Object.values(selectionFilter["blue"]).some((value) => value);
+ Object.keys(selectionFilter["blue"]).forEach((key) => {
+ selectionFilter["blue"][key] = newValue;
+ });
+ setSelectionFilter(deepCopyTable(selectionFilter));
+ }}
+ />
+
+
+ value)}
+ onChange={() => {
+ const newValue = !Object.values(selectionFilter["neutral"]).some((value) => value);
+ Object.keys(selectionFilter["neutral"]).forEach((key) => {
+ selectionFilter["neutral"][key] = newValue;
+ });
+ setSelectionFilter(deepCopyTable(selectionFilter));
+ }}
+ />
+
+
+ value)}
+ onChange={() => {
+ const newValue = !Object.values(selectionFilter["red"]).some((value) => value);
+ Object.keys(selectionFilter["red"]).forEach((key) => {
+ selectionFilter["red"][key] = newValue;
+ });
+ setSelectionFilter(deepCopyTable(selectionFilter));
+ }}
+ />
+
+
+ }
+
+
+
+
+
+
+ {
+ if (appSubState === ImportExportSubstate.EXPORT) {
+ var unitsToExport: { [key: string]: any } = {};
+ selectableUnits
+ .filter((unit) => selectionFilter[unit.getCoalition()][unit.getMarkerCategory()])
+ .forEach((unit: Unit) => {
+ var data: any = unit.getData();
+ if (unit.getGroupName() in unitsToExport) unitsToExport[unit.getGroupName()].push(data);
+ else unitsToExport[unit.getGroupName()] = [data];
+ });
+
+ const file = new Blob([JSON.stringify(unitsToExport)], {
+ type: "text/plain",
+ });
+
+ const defaultName = "olympus_export_" + missionData.mission.theatre + "_" + missionData.sessionHash + "_" + missionData.time + ".json";
+
+ //@ts-ignore TODO
+ if (window.showSaveFilePicker) {
+ const opts = {
+ types: [
+ {
+ description: "DCS Olympus export file",
+ accept: { "text/plain": [".json"] },
+ },
+ ],
+ suggestedName: defaultName,
+ };
+ //@ts-ignore TODO
+ showSaveFilePicker(opts)
+ .then((handle) => handle.createWritable())
+ .then((writeable) => {
+ getApp().setState(OlympusState.IDLE);
+ getApp().addInfoMessage("Exporting data please wait...");
+ writeable
+ .write(file)
+ .then(() => writeable.close())
+ .catch(() => getApp().addInfoMessage("An error occurred while exporting the data"));
+ })
+ .then(() => getApp().addInfoMessage("Data exported correctly"))
+ .catch((err) => getApp().addInfoMessage("An error occurred while exporting the data"));
+ } else {
+ const a = document.createElement("a");
+ const file = new Blob([JSON.stringify(unitsToExport)], {
+ type: "text/plain",
+ });
+ a.href = URL.createObjectURL(file);
+
+ var filename = defaultName;
+ a.download = filename;
+ a.click();
+ }
+ } else {
+ for (const [groupName, groupData] of Object.entries(importData)) {
+ if (groupName === "" || groupData.length === 0 || !groupData.some((unitData: UnitData) => unitData.alive)) continue;
+
+ let { markerCategory, coalition, category } = groupData[0];
+
+ if (selectionFilter[coalition][markerCategory] !== true) continue;
+
+ let unitsToSpawn = groupData.map((unitData: UnitData) => {
+ return { unitType: unitData.name, location: unitData.position, liveryID: "", skill: "High" };
+ });
+
+ getApp().getUnitsManager().spawnUnits(category.toLocaleLowerCase(), unitsToSpawn, coalition, false);
+ }
+ getApp().setState(OlympusState.IDLE);
+ }
+ }}
+ 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
+
+
+
+ getApp().setState(OlympusState.IDLE)}
+ className={`
+ mb-2 me-2 flex content-center items-center gap-2 rounded-sm
+ border-[1px] bg-blue-700 px-5 py-2.5 text-sm font-medium
+ text-white
+ dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400
+ dark:hover:bg-gray-700 dark:focus:ring-blue-800
+ focus:outline-none focus:ring-4 focus:ring-blue-300
+ hover:bg-blue-800
+ `}
+ >
+ Back
+
+
+
+
+ );
+}
diff --git a/frontend/react/src/ui/panels/awacsmenu.tsx b/frontend/react/src/ui/panels/awacsmenu.tsx
index 789ced1d..048e0166 100644
--- a/frontend/react/src/ui/panels/awacsmenu.tsx
+++ b/frontend/react/src/ui/panels/awacsmenu.tsx
@@ -4,7 +4,7 @@ import { OlToggle } from "../components/oltoggle";
import { MAP_OPTIONS_DEFAULTS } from "../../constants/constants";
import {
AWACSReferenceChangedEvent as AWACSReferenceUnitChangedEvent,
- BullseyesDataChanged,
+ BullseyesDataChangedEvent,
HotgroupsChangedEvent,
MapOptionsChangedEvent,
UnitUpdatedEvent,
@@ -27,13 +27,10 @@ export function AWACSMenu(props: { open: boolean; onClose: () => void; children?
MapOptionsChangedEvent.on((mapOptions) => setMapOptions({ ...mapOptions }));
HotgroupsChangedEvent.on((hotgroups) => setHotgroups({ ...hotgroups }));
AWACSReferenceUnitChangedEvent.on((unit) => setReferenceUnit(unit));
- BullseyesDataChanged.on((bullseyes) => setBullseyes(bullseyes));
+ BullseyesDataChangedEvent.on((bullseyes) => setBullseyes(bullseyes));
UnitUpdatedEvent.on((unit) => setRefreshTime(Date.now()));
}, []);
-
-
-
return (
setBullseyes(bullseyes));
+ BullseyesDataChangedEvent.on((bullseyes) => setBullseyes(bullseyes));
SelectedUnitsChangedEvent.on((selectedUnits) => setSelectedUnits(selectedUnits));
}, []);
diff --git a/frontend/react/src/ui/panels/header.tsx b/frontend/react/src/ui/panels/header.tsx
index 658ef227..c6d11a6e 100644
--- a/frontend/react/src/ui/panels/header.tsx
+++ b/frontend/react/src/ui/panels/header.tsx
@@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState } from "react";
import { OlRoundStateButton, OlStateButton, OlLockStateButton } from "../components/olstatebutton";
-import { faSkull, faCamera, faFlag, faLink, faUnlink, faBars, faVolumeHigh } from "@fortawesome/free-solid-svg-icons";
+import { faSkull, faCamera, faFlag, faVolumeHigh, faDownload, faUpload } from "@fortawesome/free-solid-svg-icons";
import { OlDropdownItem, OlDropdown } from "../components/oldropdown";
import { OlLabelToggle } from "../components/ollabeltoggle";
import { getApp, IP } from "../../olympusapp";
@@ -15,11 +15,11 @@ import {
olButtonsVisibilityNavyunit,
olButtonsVisibilityOlympus,
} from "../components/olicons";
-import { FaChevronLeft, FaChevronRight, FaComputer, FaFloppyDisk, FaTabletScreenButton } from "react-icons/fa6";
+import { FaChevronLeft, FaChevronRight, FaFloppyDisk } from "react-icons/fa6";
import { CommandModeOptionsChangedEvent, ConfigLoadedEvent, HiddenTypesChangedEvent, MapOptionsChangedEvent, MapSourceChangedEvent, SessionDataChangedEvent, SessionDataSavedEvent } from "../../events";
-import { BLUE_COMMANDER, COMMAND_MODE_OPTIONS_DEFAULTS, MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS } from "../../constants/constants";
+import { BLUE_COMMANDER, COMMAND_MODE_OPTIONS_DEFAULTS, ImportExportSubstate, MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS, OlympusState } from "../../constants/constants";
import { OlympusConfig } from "../../interfaces";
-import { FaCheck, FaSpinner } from "react-icons/fa";
+import { FaCheck, FaSave, FaSpinner } from "react-icons/fa";
export function Header() {
const [mapHiddenTypes, setMapHiddenTypes] = useState(MAP_HIDDEN_TYPES_DEFAULTS);
@@ -93,11 +93,11 @@ export function Header() {
>
-
+
}
+
{getApp().setState(OlympusState.IMPORT_EXPORT, ImportExportSubstate.EXPORT)}} checked={false}/>
+ {getApp().setState(OlympusState.IMPORT_EXPORT, ImportExportSubstate.IMPORT)}} checked={false}/>
{commandModeOptions.commandMode === BLUE_COMMANDER && (
@@ -130,16 +132,6 @@ export function Header() {
BLUE Commander ({commandModeOptions.spawnPoints.blue} points)
)}
-
{
- getApp().getMap().setOption("tabletMode", !mapOptions.tabletMode);
- }}
- >
- {mapOptions.tabletMode ? : }
-
+
>
)}
diff --git a/frontend/react/src/unit/unit.ts b/frontend/react/src/unit/unit.ts
index 6f677aac..9b95cdaf 100644
--- a/frontend/react/src/unit/unit.ts
+++ b/frontend/react/src/unit/unit.ts
@@ -632,6 +632,7 @@ export abstract class Unit extends CustomMarker {
getData(): UnitData {
return {
category: this.getCategory(),
+ markerCategory: this.getMarkerCategory(),
ID: this.ID,
alive: this.#alive,
human: this.#human,
@@ -1229,7 +1230,7 @@ export abstract class Unit extends CustomMarker {
}
setAdvancedOptions(isActiveTanker: boolean, isActiveAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings) {
- if (!this.#human) getApp().getServerManager().setAdvacedOptions(this.ID, isActiveTanker, isActiveAWACS, TACAN, radio, generalSettings);
+ if (!this.#human) getApp().getServerManager().setAdvancedOptions(this.ID, isActiveTanker, isActiveAWACS, TACAN, radio, generalSettings);
}
bombPoint(latlng: LatLng) {
@@ -1549,7 +1550,7 @@ export abstract class Unit extends CustomMarker {
/* Set BRAA */
const reference = getApp().getUnitsManager().getAWACSReference();
- if (reference && reference !== this) {
+ if (reference && reference !== this && reference.getAlive()) {
const BRAA = `${computeBearingRangeString(reference.getPosition(), this.getPosition())}`;
if (element.querySelector(".unit-braa")) (
element.querySelector(".unit-braa")).innerText = `${BRAA}`;
} else if (element.querySelector(".unit-braa")) (element.querySelector(".unit-braa")).innerText = ``;
diff --git a/frontend/react/src/unit/unitsmanager.ts b/frontend/react/src/unit/unitsmanager.ts
index e220082a..eb067683 100644
--- a/frontend/react/src/unit/unitsmanager.ts
+++ b/frontend/react/src/unit/unitsmanager.ts
@@ -35,7 +35,7 @@ import {
UnitDeadEvent,
UnitDeselectedEvent,
UnitSelectedEvent,
- UnitsRefreshed,
+ UnitsRefreshedEvent,
} from "../events";
import { UnitDatabase } from "./databases/unitdatabase";
import * as turf from "@turf/turf";
@@ -80,7 +80,7 @@ export class UnitsManager {
});
SessionDataLoadedEvent.on((sessionData) => {
- UnitsRefreshed.on(() => {
+ UnitsRefreshedEvent.on((units) => {
const localSessionData = deepCopyTable(sessionData);
if (localSessionData.hotgroups) {
Object.keys(localSessionData.hotgroups).forEach((hotgroup) => {
@@ -311,7 +311,7 @@ export class UnitsManager {
/* Compute the base clusters */
this.#clusters = this.computeClusters();
- if (fullUpdate) UnitsRefreshed.dispatch();
+ if (fullUpdate) UnitsRefreshedEvent.dispatch(this.#units);
return updateTime;
}
diff --git a/frontend/react/src/weapon/weapon.ts b/frontend/react/src/weapon/weapon.ts
index 2aa0ad03..b37bad0d 100644
--- a/frontend/react/src/weapon/weapon.ts
+++ b/frontend/react/src/weapon/weapon.ts
@@ -8,7 +8,7 @@ import { DataExtractor } from "../server/dataextractor";
import { ObjectIconOptions } from "../interfaces";
import { MapOptionsChangedEvent } from "../events";
-export class Weapon extends CustomMarker {
+export abstract class Weapon extends CustomMarker {
ID: number;
#alive: boolean = false;
@@ -54,10 +54,9 @@ export class Weapon extends CustomMarker {
MapOptionsChangedEvent.on(() => this.#updateMarker());
}
- getCategory() {
- // Overloaded by child classes
- return "";
- }
+ abstract getCategory(): string;
+ abstract getMarkerCategory(): string;
+ abstract getIconOptions(): ObjectIconOptions;
/********************** Unit data *************************/
setData(dataExtractor: DataExtractor) {
@@ -110,28 +109,7 @@ export class Weapon extends CustomMarker {
heading: this.#heading,
};
}
-
- getMarkerCategory(): string {
- return "";
- }
-
- getIconOptions(): ObjectIconOptions {
- // Default values, overloaded by child classes if needed
- return {
- showState: false,
- showVvi: false,
- showHealth: false,
- showHotgroup: false,
- showUnitIcon: true,
- showShortLabel: false,
- showFuel: false,
- showAmmo: false,
- showSummary: true,
- showCallsign: true,
- rotateToHeading: false,
- };
- }
-
+
setAlive(newAlive: boolean) {
this.#alive = newAlive;
}
diff --git a/frontend/react/src/weapon/weaponsmanager.ts b/frontend/react/src/weapon/weaponsmanager.ts
index 6daf6628..29eca286 100644
--- a/frontend/react/src/weapon/weaponsmanager.ts
+++ b/frontend/react/src/weapon/weaponsmanager.ts
@@ -3,7 +3,7 @@ import { Weapon } from "./weapon";
import { DataIndexes } from "../constants/constants";
import { DataExtractor } from "../server/dataextractor";
import { Contact } from "../interfaces";
-import { CommandModeOptionsChangedEvent, WeaponsRefreshed } from "../events";
+import { CommandModeOptionsChangedEvent, WeaponsRefreshedEvent } from "../events";
/** The WeaponsManager handles the creation and update of weapons. Data is strictly updated by the server ONLY. */
export class WeaponsManager {
@@ -83,7 +83,7 @@ export class WeaponsManager {
this.#weapons[ID]?.setData(dataExtractor);
}
- if (fullUpdate) WeaponsRefreshed.dispatch();
+ if (fullUpdate) WeaponsRefreshedEvent.dispatch(this.#weapons);
return updateTime;
}