setShowMore(!showMore)} icon={faEllipsisVertical} tooltip="Show more options" />
{showMore && (
diff --git a/frontend/react/src/ui/panels/maptoolbar.tsx b/frontend/react/src/ui/panels/maptoolbar.tsx
index 981027e3..a088a49c 100644
--- a/frontend/react/src/ui/panels/maptoolbar.tsx
+++ b/frontend/react/src/ui/panels/maptoolbar.tsx
@@ -244,11 +244,8 @@ export function MapToolBar(props: {}) {
tooltipPosition="side"
buttonColor={CONTEXT_ACTION_COLORS[contextActionIt.getOptions().type ?? 0]}
onClick={() => {
- if (contextActionIt.getTarget() === ContextActionTarget.NONE) {
- contextActionIt.executeCallback(null, null);
- } else {
- contextActionIt !== contextAction ? getApp().getMap().setContextAction(contextActionIt) : getApp().getMap().setContextAction(null);
- }
+ if (contextActionIt.getTarget() === ContextActionTarget.NONE) contextActionIt.executeCallback(null, null);
+ else contextActionIt !== contextAction ? getApp().getMap().setContextAction(contextActionIt) : getApp().getMap().setContextAction(null);
}}
/>
diff --git a/frontend/react/src/ui/panels/minimappanel.tsx b/frontend/react/src/ui/panels/minimappanel.tsx
index d0a201f9..8a1b03a5 100644
--- a/frontend/react/src/ui/panels/minimappanel.tsx
+++ b/frontend/react/src/ui/panels/minimappanel.tsx
@@ -4,7 +4,7 @@ import { DateAndTime, ServerStatus } from "../../interfaces";
import { getApp } from "../../olympusapp";
import { FaChevronDown, FaChevronUp } from "react-icons/fa6";
import { MapOptionsChangedEvent, ServerStatusUpdatedEvent } from "../../events";
-import { MAP_OPTIONS_DEFAULTS } from "../../constants/constants";
+import { colors, MAP_OPTIONS_DEFAULTS } from "../../constants/constants";
export function MiniMapPanel(props: {}) {
const [serverStatus, setServerStatus] = useState({} as ServerStatus);
@@ -41,15 +41,13 @@ export function MiniMapPanel(props: {}) {
let timeString = `${zeroAppend(hours, 2)}:${zeroAppend(minutes, 2)}:${zeroAppend(seconds, 2)}`;
- // Choose frame rate string color
- let frameRateColor = "#8BFF63";
- if (serverStatus.frameRate < 30) frameRateColor = "#F05252";
- else if (serverStatus.frameRate >= 30 && serverStatus.frameRate < 60) frameRateColor = "#FF9900";
+ let loadColor = colors.OLYMPUS_GREEN;
+ if (serverStatus.load > 1000) loadColor = colors.OLYMPUS_RED;
+ else if (serverStatus.load >= 100 && serverStatus.load < 1000) loadColor = colors.OLYMPUS_ORANGE;
- // Choose load string color
- let loadColor = "#8BFF63";
- if (serverStatus.load > 1000) loadColor = "#F05252";
- else if (serverStatus.load >= 100 && serverStatus.load < 1000) loadColor = "#FF9900";
+ let frameRateColor = colors.OLYMPUS_GREEN;
+ if (serverStatus.frameRate < 30) frameRateColor = colors.OLYMPUS_RED;
+ else if (serverStatus.frameRate >= 30 && serverStatus.frameRate < 60) frameRateColor = colors.OLYMPUS_ORANGE;
return (
setServerStatus(status));
}, []);
- let loadColor = "#8BFF63";
- if (serverStatus.load > 1000) loadColor = "#F05252";
- else if (serverStatus.load >= 100 && serverStatus.load < 1000) loadColor = "#FF9900";
-
- let frameRateColor = "#8BFF63";
- if (serverStatus.frameRate < 30) frameRateColor = "#F05252";
- else if (serverStatus.frameRate >= 30 && serverStatus.frameRate < 60) frameRateColor = "#FF9900";
+ let loadColor = colors.OLYMPUS_GREEN;
+ if (serverStatus.load > 1000) loadColor = colors.OLYMPUS_RED;
+ else if (serverStatus.load >= 100 && serverStatus.load < 1000) loadColor = colors.OLYMPUS_ORANGE;
+ let frameRateColor = colors.OLYMPUS_GREEN;
+ if (serverStatus.frameRate < 30) frameRateColor = colors.OLYMPUS_RED;
+ else if (serverStatus.frameRate >= 30 && serverStatus.frameRate < 60) frameRateColor = colors.OLYMPUS_ORANGE;
const MThours = serverStatus.missionTime? serverStatus.missionTime.h: 0;
const MTminutes = serverStatus.missionTime? serverStatus.missionTime.m: 0;
diff --git a/frontend/react/src/unit/importexport/unitdatafile.ts b/frontend/react/src/unit/importexport/unitdatafile.ts
deleted file mode 100644
index e53e0f4e..00000000
--- a/frontend/react/src/unit/importexport/unitdatafile.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-//import { Dialog } from "../../dialog/dialog";
-//import { createCheckboxOption } from "../../other/utils";
-
-var categoryMap = {
- Aircraft: "Aircraft",
- Helicopter: "Helicopter",
- GroundUnit: "Ground units",
- NavyUnit: "Naval units",
-};
-
-export abstract class UnitDataFile {
- protected data: any;
- //protected dialog!: Dialog;
-
- constructor() {}
-
- buildCategoryCoalitionTable() {
- const categories = this.#getCategoriesFromData();
- const coalitions = ["blue", "neutral", "red"];
-
- let headersHTML: string = ``;
- let matrixHTML: string = ``;
-
- //categories.forEach((category: string, index) => {
- // matrixHTML += `
| ${categoryMap[category as keyof typeof categoryMap]} | `;
- //
- // coalitions.forEach((coalition: string) => {
- // if (index === 0)
- // headersHTML += `${coalition[0].toUpperCase() + coalition.substring(1)} | `;
- //
- // const optionIsValid = this.data[category].hasOwnProperty(coalition);
- // let checkboxHTML = createCheckboxOption(``, category, optionIsValid, () => { }, {
- // "disabled": !optionIsValid,
- // "name": "category-coalition-selection",
- // "readOnly": !optionIsValid,
- // "value" : `${category}:${coalition}`
- // }).outerHTML;
- //
- // if (optionIsValid)
- // checkboxHTML = checkboxHTML.replace(`"checkbox"`, `"checkbox" checked`); // inner and outerHTML screw default checked up
- //
- // matrixHTML += `${checkboxHTML} | `;
- //
- // });
- // matrixHTML += "
";
- //});
- //
- //const table = this.dialog.getElement().querySelector("table.categories-coalitions");
-
- //(table.tHead).innerHTML = `| | ${headersHTML}
`;
- //(table.querySelector(`tbody`)).innerHTML = matrixHTML;
- }
-
- #getCategoriesFromData() {
- const categories = Object.keys(this.data);
- categories.sort();
- return categories;
- }
-
- getData() {
- return this.data;
- }
-}
diff --git a/frontend/react/src/unit/importexport/unitdatafileexport.ts b/frontend/react/src/unit/importexport/unitdatafileexport.ts
deleted file mode 100644
index 0e3fbee2..00000000
--- a/frontend/react/src/unit/importexport/unitdatafileexport.ts
+++ /dev/null
@@ -1,98 +0,0 @@
-import { getApp } from "../../olympusapp";
-//import { Dialog } from "../../dialog/dialog";
-import { zeroAppend } from "../../other/utils";
-import { Unit } from "../unit";
-import { UnitDataFile } from "./unitdatafile";
-
-export class UnitDataFileExport extends UnitDataFile {
- protected data!: any;
- //protected dialog: Dialog;
- #element!: HTMLElement;
- #filename: string = "export.json";
-
- constructor(elementId: string) {
- super();
- //this.dialog = new Dialog(elementId);
- //this.#element = this.dialog.getElement();
-
- this.#element.querySelector(".start-transfer")?.addEventListener("click", (ev: MouseEventInit) => {
- this.#doExport();
- });
- }
-
- /**
- * Show the form to start the export journey
- */
- showForm(units: Unit[]) {
- //this.dialog.getElement().querySelectorAll("[data-on-error]").forEach((el:Element) => {
- // el.classList.toggle("hide", el.getAttribute("data-on-error") === "show");
- //});
- //
- //const data: any = {};
- //const unitCanBeExported = (unit: Unit) => !["Aircraft", "Helicopter"].includes(unit.getCategory());
- //
- //units.filter((unit: Unit) => unit.getAlive() && unitCanBeExported(unit)).forEach((unit: Unit) => {
- // const category = unit.getCategory();
- // const coalition = unit.getCoalition();
- //
- // if (!data.hasOwnProperty(category)) {
- // data[category] = {};
- // }
- //
- // if (!data[category].hasOwnProperty(coalition))
- // data[category][coalition] = [];
- //
- // data[category][coalition].push(unit);
- //});
- //
- //this.data = data;
- //this.buildCategoryCoalitionTable();
- //this.dialog.show();
- //
- //const date = new Date();
- //this.#filename = `olympus_${getApp().getMissionManager().getTheatre().replace(/[^\w]/gi, "").toLowerCase()}_${date.getFullYear()}${zeroAppend(date.getMonth() + 1, 2)}${zeroAppend(date.getDate(), 2)}_${zeroAppend(date.getHours(), 2)}${zeroAppend(date.getMinutes(), 2)}${zeroAppend(date.getSeconds(), 2)}.json`;
- //var input = this.#element.querySelector("#export-filename") as HTMLInputElement;
- //input.onchange = (ev: Event) => {
- // this.#filename = (ev.currentTarget as HTMLInputElement).value;
- //}
- //if (input)
- // input.value = this.#filename;
- }
-
- #doExport() {
- let selectedUnits: Unit[] = [];
-
- this.#element
- .querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`)
- .forEach((checkbox: HTMLInputElement) => {
- if (checkbox instanceof HTMLInputElement) {
- const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition"
- selectedUnits = selectedUnits.concat(this.data[category][coalition]);
- }
- });
-
- if (selectedUnits.length === 0) {
- alert("Please select at least one option for export.");
- return;
- }
-
- var unitsToExport: { [key: string]: any } = {};
- selectedUnits.forEach((unit: Unit) => {
- var data: any = unit.getData();
- if (unit.getGroupName() in unitsToExport) unitsToExport[unit.getGroupName()].push(data);
- else unitsToExport[unit.getGroupName()] = [data];
- });
-
- const a = document.createElement("a");
- const file = new Blob([JSON.stringify(unitsToExport)], {
- type: "text/plain",
- });
- a.href = URL.createObjectURL(file);
-
- var filename = this.#filename;
- if (!this.#filename.toLowerCase().endsWith(".json")) filename += ".json";
- a.download = filename;
- a.click();
- //this.dialog.hide();
- }
-}
diff --git a/frontend/react/src/unit/importexport/unitdatafileimport.ts b/frontend/react/src/unit/importexport/unitdatafileimport.ts
deleted file mode 100644
index b6f74dad..00000000
--- a/frontend/react/src/unit/importexport/unitdatafileimport.ts
+++ /dev/null
@@ -1,147 +0,0 @@
-import { getApp } from "../../olympusapp";
-//import { Dialog } from "../../dialog/dialog";
-import { UnitData } from "../../interfaces";
-//import { ImportFileJSONSchemaValidator } from "../../schemas/schema";
-import { UnitDataFile } from "./unitdatafile";
-
-export class UnitDataFileImport extends UnitDataFile {
- protected data!: any;
- //protected dialog: Dialog;
- #fileData!: { [key: string]: UnitData[] };
-
- constructor(elementId: string) {
- super();
- //this.dialog = new Dialog(elementId);
- //this.dialog.getElement().querySelector(".start-transfer")?.addEventListener("click", (ev: MouseEventInit) => {
- // this.#doImport();
- // this.dialog.hide();
- //});
- }
-
- #doImport() {
- //let selectedCategories: any = {};
- //const unitsManager = getApp().getUnitsManager();
- //
- //this.dialog.getElement().querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`).forEach((checkbox: HTMLInputElement) => {
- // if (checkbox instanceof HTMLInputElement) {
- // const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition"
- // selectedCategories[category] = selectedCategories[category] || {};
- // selectedCategories[category][coalition] = true;
- // }
- //});
- //
- //for (const [groupName, groupData] of Object.entries(this.#fileData)) {
- // if (groupName === "" || groupData.length === 0 || !this.#unitGroupDataCanBeImported(groupData))
- // continue;
- //
- // let { category, coalition } = groupData[0];
- //
- // if (!selectedCategories.hasOwnProperty(category)
- // || !selectedCategories[category].hasOwnProperty(coalition)
- // || selectedCategories[category][coalition] !== true)
- // continue;
- //
- // let unitsToSpawn = groupData.filter((unitData: UnitData) => this.#unitDataCanBeImported(unitData)).map((unitData: UnitData) => {
- // return { unitType: unitData.name, location: unitData.position, liveryID: "", skill: "High" }
- // });
- //
- // unitsManager.spawnUnits(category, unitsToSpawn, coalition, false);
- //}
- }
-
- selectFile() {
- var input = document.createElement("input");
- input.type = "file";
- input.addEventListener("change", (e: any) => {
- var file = e.target.files[0];
- if (!file) {
- return;
- }
- var reader = new FileReader();
- reader.onload = (e: any) => {
- try {
- this.#fileData = JSON.parse(e.target.result);
-
- //const validator = new ImportFileJSONSchemaValidator();
- //if (!validator.validate(this.#fileData)) {
- // const errors = validator.getErrors().reduce((acc:any, error:any) => {
- // let errorString = error.instancePath.substring(1) + ": " + error.message;
- // if (error.params) {
- // const {allowedValues} = error.params;
- // if (allowedValues)
- // errorString += ": " + allowedValues.join(', ');
- // }
- // acc.push(errorString);
- // return acc;
- // }, [] as string[]);
- // this.#showFileDataErrors(errors);
- //} else {
- // this.#showForm();
- //}
- } catch (e: any) {
- this.#showFileDataErrors([e]);
- }
- };
- reader.readAsText(file);
- });
- input.click();
- }
-
- #showFileDataErrors(reasons: string[]) {
- //this.dialog.getElement().querySelectorAll("[data-on-error]").forEach((el:Element) => {
- // el.classList.toggle("hide", el.getAttribute("data-on-error") === "hide");
- //});
- //
- //const reasonsList = this.dialog.getElement().querySelector(".import-error-reasons");
- //if (reasonsList instanceof HTMLElement)
- // reasonsList.innerHTML = `${reasons.join("")}`;
- //
- //this.dialog.show();
- }
-
- #showForm() {
- //this.dialog.getElement().querySelectorAll("[data-on-error]").forEach((el:Element) => {
- // el.classList.toggle("hide", el.getAttribute("data-on-error") === "show");
- //});
- //
- //const data: any = {};
- //
- //for (const [group, units] of Object.entries(this.#fileData)) {
- // if (group === "" || units.length === 0)
- // continue;
- //
- // if (units.some((unit: UnitData) => !this.#unitDataCanBeImported(unit)))
- // continue;
- //
- // const category = units[0].category;
- //
- // if (!data.hasOwnProperty(category)) {
- // data[category] = {};
- // }
- //
- // units.forEach((unit: UnitData) => {
- // if (!data[category].hasOwnProperty(unit.coalition))
- // data[category][unit.coalition] = [];
- //
- // data[category][unit.coalition].push(unit);
- // });
- //
- //}
- //
- //this.data = data;
- //this.buildCategoryCoalitionTable();
- //this.dialog.show();
- }
-
- #unitDataCanBeImported(unitData: UnitData) {
- return unitData.alive && this.#unitGroupDataCanBeImported([unitData]);
- }
-
- #unitGroupDataCanBeImported(unitGroupData: UnitData[]) {
- return (
- unitGroupData.every((unitData: UnitData) => {
- return !["Aircraft", "Helicopter"].includes(unitData.category);
- }) && unitGroupData.some((unitData: UnitData) => unitData.alive)
- );
- }
-}
diff --git a/frontend/react/src/unit/unit.ts b/frontend/react/src/unit/unit.ts
index 7e13b76f..26deaedb 100644
--- a/frontend/react/src/unit/unit.ts
+++ b/frontend/react/src/unit/unit.ts
@@ -16,6 +16,7 @@ import {
nmToM,
zeroAppend,
computeBearingRangeString,
+ adjustBrightness,
} from "../other/utils";
import { CustomMarker } from "../map/markers/custommarker";
import { SVGInjector } from "@tanem/svg-injector";
@@ -43,6 +44,7 @@ import {
ContextActionTarget,
SHORT_PRESS_MILLISECONDS,
TRAIL_LENGTH,
+ colors,
} from "../constants/constants";
import { DataExtractor } from "../server/dataextractor";
import { Weapon } from "../weapon/weapon";
@@ -319,7 +321,7 @@ export abstract class Unit extends CustomMarker {
this.ID = ID;
this.#pathPolyline = new Polyline([], {
- color: "#2d3e50",
+ color: colors.GRAY,
weight: 3,
opacity: 0.5,
smoothFactor: 1,
@@ -327,7 +329,7 @@ export abstract class Unit extends CustomMarker {
this.#pathPolyline.addTo(getApp().getMap());
this.#targetPositionMarker = new TargetMarker(new LatLng(0, 0));
this.#targetPositionPolyline = new Polyline([], {
- color: "#FF0000",
+ color: colors.WHITE,
weight: 3,
opacity: 0.5,
smoothFactor: 1,
@@ -601,8 +603,9 @@ export abstract class Unit extends CustomMarker {
if (!this.#blueprint && this.#name !== "") {
const blueprint = getApp().getUnitsManager().getDatabase().getByName(this.#name);
this.#blueprint = blueprint ?? null;
- /* Refresh the marker */
- this.#redrawMarker();
+ if (this.#blueprint)
+ /* Refresh the marker */
+ this.#redrawMarker();
}
/* Update the blueprint to use when the unit is grouped */
@@ -1438,9 +1441,9 @@ export abstract class Unit extends CustomMarker {
if (this.#alive && drawMiniMapMarker) {
if (this.#miniMapMarker == null) {
this.#miniMapMarker = new CircleMarker(new LatLng(this.#position.lat, this.#position.lng), { radius: 0.5 });
- if (this.#coalition == "neutral") this.#miniMapMarker.setStyle({ color: "#CFD9E8", radius: 2 });
- else if (this.#coalition == "red") this.#miniMapMarker.setStyle({ color: "#ff5858", radius: 2 });
- else this.#miniMapMarker.setStyle({ color: "#247be2", radius: 2 });
+ if (this.#coalition == "neutral") this.#miniMapMarker.setStyle({ color: colors.NEUTRAL_COALITION, radius: 2 });
+ else if (this.#coalition == "red") this.#miniMapMarker.setStyle({ color: colors.RED_COALITION, radius: 2 });
+ else this.#miniMapMarker.setStyle({ color: colors.BLUE_COALITION, radius: 2 });
this.#miniMapMarker.addTo(getApp().getMap().getMiniMapLayerGroup());
this.#miniMapMarker.bringToBack();
} else {
@@ -1585,7 +1588,7 @@ export abstract class Unit extends CustomMarker {
/* Draw the contact trail */
if (/*TODO getApp().getMap().getOptions().AWACSMode*/ false) {
this.#trailPolylines = this.#trailPositions.map(
- (latlng, idx) => new Polyline([latlng, latlng], { color: "#FFFFFF", opacity: 1 - (idx + 1) / TRAIL_LENGTH })
+ (latlng, idx) => new Polyline([latlng, latlng], { color: colors.WHITE, opacity: 1 - (idx + 1) / TRAIL_LENGTH })
);
this.#trailPolylines.forEach((polyline) => polyline.addTo(getApp().getMap()));
}
@@ -1678,10 +1681,10 @@ export abstract class Unit extends CustomMarker {
} else endLatLng = new LatLng(contact.getPosition().lat, contact.getPosition().lng);
var color;
- if (contactData.detectionMethod === VISUAL || contactData.detectionMethod === OPTIC) color = "#FF00FF";
- else if (contactData.detectionMethod === RADAR || contactData.detectionMethod === IRST) color = "#FFFF00";
- else if (contactData.detectionMethod === RWR) color = "#00FF00";
- else color = "#FFFFFF";
+ if (contactData.detectionMethod === VISUAL || contactData.detectionMethod === OPTIC) color = colors.MAGENTA;
+ else if (contactData.detectionMethod === RADAR || contactData.detectionMethod === IRST) color = colors.YELLOW;
+ else if (contactData.detectionMethod === RWR) color = colors.GREEN;
+ else color = colors.WHITE;
var contactPolyline = new Polyline([startLatLng, endLatLng], {
color: color,
weight: 3,
@@ -1742,13 +1745,13 @@ export abstract class Unit extends CustomMarker {
this.#acquisitionCircle.addTo(getApp().getMap());
switch (this.getCoalition()) {
case "red":
- this.#acquisitionCircle.options.color = "#D42121";
+ this.#acquisitionCircle.options.color = adjustBrightness(colors.RED_COALITION, -20);
break;
case "blue":
- this.#acquisitionCircle.options.color = "#017DC1";
+ this.#acquisitionCircle.options.color = adjustBrightness(colors.BLUE_COALITION, -20);
break;
default:
- this.#acquisitionCircle.options.color = "#111111";
+ this.#acquisitionCircle.options.color = adjustBrightness(colors.NEUTRAL_COALITION, -20);
break;
}
}
@@ -1768,13 +1771,13 @@ export abstract class Unit extends CustomMarker {
this.#engagementCircle.addTo(getApp().getMap());
switch (this.getCoalition()) {
case "red":
- this.#engagementCircle.options.color = "#FF5858";
+ this.#engagementCircle.options.color = colors.RED_COALITION;
break;
case "blue":
- this.#engagementCircle.options.color = "#3BB9FF";
+ this.#engagementCircle.options.color = colors.BLUE_COALITION;
break;
default:
- this.#engagementCircle.options.color = "#CFD9E8";
+ this.#engagementCircle.options.color = colors.NEUTRAL_COALITION;
break;
}
}
diff --git a/frontend/react/src/unit/unitsmanager.ts b/frontend/react/src/unit/unitsmanager.ts
index aff29e9b..648f7ec3 100644
--- a/frontend/react/src/unit/unitsmanager.ts
+++ b/frontend/react/src/unit/unitsmanager.ts
@@ -19,8 +19,6 @@ import { citiesDatabase } from "./databases/citiesdatabase";
import { TemporaryUnitMarker } from "../map/markers/temporaryunitmarker";
import { Contact, UnitBlueprint, UnitData, UnitSpawnTable } from "../interfaces";
import { Group } from "./group";
-import { UnitDataFileExport } from "./importexport/unitdatafileexport";
-import { UnitDataFileImport } from "./importexport/unitdatafileimport";
import { CoalitionCircle } from "../map/coalitionarea/coalitioncircle";
import { ContextActionSet } from "./contextactionset";
import {
@@ -52,8 +50,6 @@ export class UnitsManager {
#selectionEventDisabled: boolean = false;
#units: { [ID: number]: Unit } = {};
#groups: { [groupName: string]: Group } = {};
- #unitDataExport!: UnitDataFileExport;
- #unitDataImport!: UnitDataFileImport;
#unitDatabase: UnitDatabase;
#protectionCallback: (units: Unit[]) => void = (units) => {};
#AWACSReference: Unit | null = null;
@@ -1421,22 +1417,6 @@ export class UnitsManager {
});
}
- /** Export all the ground and navy units to file. Does not work on Aircraft and Helicopter units.
- * TODO: Extend to aircraft and helicopters
- */
- exportToFile() {
- if (!this.#unitDataExport) this.#unitDataExport = new UnitDataFileExport("unit-export-dialog");
- this.#unitDataExport.showForm(Object.values(this.#units));
- }
-
- /** Import ground and navy units from file
- * TODO: extend to support aircraft and helicopters
- */
- importFromFile() {
- if (!this.#unitDataImport) this.#unitDataImport = new UnitDataFileImport("unit-import-dialog");
- this.#unitDataImport.selectFile();
- }
-
/** Spawn a new group of units
*
* @param category Category of the new units