diff --git a/frontend/react/package.json b/frontend/react/package.json index 1f1eee0d..bd80335f 100644 --- a/frontend/react/package.json +++ b/frontend/react/package.json @@ -11,6 +11,7 @@ "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-brands-svg-icons": "^6.5.2", + "@fortawesome/free-regular-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", "@tanem/svg-injector": "^10.1.68", diff --git a/frontend/react/src/map/coalitionarea/coalitionarea.ts b/frontend/react/src/map/coalitionarea/coalitionpolygon.ts similarity index 80% rename from frontend/react/src/map/coalitionarea/coalitionarea.ts rename to frontend/react/src/map/coalitionarea/coalitionpolygon.ts index 4d20bfdd..4f20c766 100644 --- a/frontend/react/src/map/coalitionarea/coalitionarea.ts +++ b/frontend/react/src/map/coalitionarea/coalitionpolygon.ts @@ -1,24 +1,31 @@ import { - DomUtil, LatLng, LatLngExpression, Map, Point, Polygon, PolylineOptions, + DivIcon, + Marker } from "leaflet"; import { getApp } from "../../olympusapp"; import { CoalitionAreaHandle } from "./coalitionareahandle"; import { CoalitionAreaMiddleHandle } from "./coalitionareamiddlehandle"; import { BLUE_COMMANDER, RED_COMMANDER } from "../../constants/constants"; +import { Coalition } from "../../types/types"; +import { polyCenter } from "../../other/utils"; -export class CoalitionArea extends Polygon { - #coalition: string = "blue"; +let totalAreas = 0; + +export class CoalitionPolygon extends Polygon { + #coalition: Coalition = "blue"; #selected: boolean = true; #editing: boolean = true; #handles: CoalitionAreaHandle[] = []; #middleHandles: CoalitionAreaMiddleHandle[] = []; #activeIndex: number = 0; + #labelText: string; + #label: Marker; constructor( latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][], @@ -26,12 +33,16 @@ export class CoalitionArea extends Polygon { ) { if (options === undefined) options = {}; + totalAreas++; + options.bubblingMouseEvents = false; options.interactive = false; super(latlngs, options); this.#setColors(); + this.#labelText = `Polygon ${totalAreas}` + if ( [BLUE_COMMANDER, RED_COMMANDER].includes( getApp().getMissionManager().getCommandModeOptions().commandMode @@ -40,7 +51,7 @@ export class CoalitionArea extends Polygon { this.setCoalition(getApp().getMissionManager().getCommandedCoalition()); } - setCoalition(coalition: string) { + setCoalition(coalition: Coalition) { this.#coalition = coalition; this.#setColors(); } @@ -53,6 +64,7 @@ export class CoalitionArea extends Polygon { this.#selected = selected; this.#setColors(); this.#setHandles(); + this.#drawLabel(); this.setOpacity(selected ? 1 : 0.5); if (!this.getSelected() && this.getEditing()) { /* Remove the vertex we were working on */ @@ -88,16 +100,18 @@ export class CoalitionArea extends Polygon { this.#setHandles(); } - moveActiveVertex(latlng: LatLng) { - var latlngs = this.getLatLngs()[0] as LatLng[]; - latlngs[this.#activeIndex] = latlng; - this.setLatLngs(latlngs); - this.#setHandles(); - } - setOpacity(opacity: number) { this.setStyle({ opacity: opacity, fillOpacity: opacity * 0.25 }); } + + getLabelText() { + return this.#labelText; + } + + setLabelText(labelText: string) { + this.#labelText = labelText; + this.#drawLabel(); + } onRemove(map: Map): this { super.onRemove(map); @@ -109,6 +123,12 @@ export class CoalitionArea extends Polygon { return this; } + setLatLngs(latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][]){ + super.setLatLngs(latlngs); + this.#drawLabel(); + return this; + } + #setColors() { const coalitionColor = this.getCoalition() === "blue" ? "#247be2" : "#ff5858"; @@ -165,6 +185,7 @@ export class CoalitionArea extends Polygon { const middleHandle = new CoalitionAreaMiddleHandle(middleLatLng); middleHandle.addTo(getApp().getMap()); middleHandle.on("click", (e: any) => { + getApp().getMap().preventClicks(); this.#activeIndex = idx - 1; this.addTemporaryLatLng(middleLatLng); }); @@ -174,4 +195,19 @@ export class CoalitionArea extends Polygon { }); } } + + #drawLabel() { + if (this.#label) { + this.#label.removeFrom(this._map); + } + this.#label = new Marker(polyCenter(this), { + icon: new DivIcon({ + className: 'label', + html: this.#labelText, + iconSize: [100, 40] + }), + interactive: false + }).addTo(this._map); + this.#label.getElement()?.classList.add(`ol-coalitionarea-label`, `${this.#selected? "selected": `${this.#coalition}`}`); + } } diff --git a/frontend/react/src/map/map.css b/frontend/react/src/map/map.css index fd06d5c4..0639722e 100644 --- a/frontend/react/src/map/map.css +++ b/frontend/react/src/map/map.css @@ -105,16 +105,28 @@ border-radius: 999px; } -.ol-coalitionarea-handle-icon { +.ol-coalitionarea-middle-handle-icon { background-color: #FFFFFFAA; width: 100%; height: 100%; border-radius: 999px; } -.ol-coalitionarea-middle-handle-icon { - background-color: #FFFFFFAA; - width: 100%; - height: 100%; - border-radius: 999px; +.ol-coalitionarea-label { + font-weight: 700; + font-size: 16px; +} + +.ol-coalitionarea-label.selected { + color: white; + /* 1 pixel black shadow to left, top, right and bottom */ + text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; +} + +.ol-coalitionarea-label.blue { + color: #0f3764; +} + +.ol-coalitionarea-label.red { + color: #461818; } \ No newline at end of file diff --git a/frontend/react/src/map/map.ts b/frontend/react/src/map/map.ts index 4db0e406..9ab49843 100644 --- a/frontend/react/src/map/map.ts +++ b/frontend/react/src/map/map.ts @@ -19,7 +19,7 @@ import { MAP_HIDDEN_TYPES_DEFAULTS, COALITIONAREA_EDIT, } from "../constants/constants"; -import { CoalitionArea } from "./coalitionarea/coalitionarea"; +import { CoalitionPolygon } from "./coalitionarea/coalitionpolygon"; import { MapHiddenTypes, MapOptions } from "../types/types"; import { SpawnRequestTable } from "../interfaces"; import { ContextAction } from "../unit/contextaction"; @@ -30,6 +30,7 @@ import "./markers/stylesheets/bullseye.css"; import "./markers/stylesheets/units.css"; import "./map.css"; +/* Register the handler for the box selection */ L.Map.addInitHook("addHandler", "boxSelect", BoxSelect); export class Map extends L.Map { @@ -91,7 +92,7 @@ export class Map extends L.Map { #cameraZoomRatio: number = 1.0; /* Coalition areas drawing */ - #coalitionAreas: CoalitionArea[] = []; + #coalitionPolygons: CoalitionPolygon[] = []; /* Unit context actions */ #contextAction: null | ContextAction = null; @@ -102,7 +103,7 @@ export class Map extends L.Map { /** * - * @param ID - the ID of the HTML element which will contain the context menu + * @param ID - the ID of the HTML element which will contain the map */ constructor(ID: string) { /* Init the leaflet map */ @@ -111,7 +112,7 @@ export class Map extends L.Map { doubleClickZoom: false, zoomControl: false, boxZoom: false, - //@ts-ignore Needed because the boxSelect option is non-standard + //@ts-ignore Needed because the boxSelect option is non-standard and unsuppoerted boxSelect: true, zoomAnimation: true, maxBoundsViscosity: 1.0, @@ -154,10 +155,9 @@ export class Map extends L.Map { this.on("keydown", (e: any) => this.#onKeyDown(e)); this.on("keyup", (e: any) => this.#onKeyUp(e)); - this.on("move", (e: any) => { - this.#onMapMove(e); - }); + this.on("move", (e: any) => this.#onMapMove(e)); + /* Custom touch events for touchscreen support */ L.DomEvent.on(this.getContainer(), "touchstart", this.#onMouseDown, this); L.DomEvent.on(this.getContainer(), "touchend", this.#onMouseUp, this); @@ -177,7 +177,6 @@ export class Map extends L.Map { document.addEventListener( "toggleCoalitionAreaDraw", (ev: CustomEventInit) => { - //this.getMapContextMenu().hide(); this.deselectAllCoalitionAreas(); if (ev.detail?.type == "polygon") { if (this.getState() !== COALITIONAREA_DRAW_POLYGON) @@ -246,15 +245,11 @@ export class Map extends L.Map { }); document.addEventListener("toggleCameraLinkStatus", () => { - // if (this.#slaveDCSCameraAvailable) { // Commented to experiment with usability this.setSlaveDCSCamera(!this.#slaveDCSCamera); - // } }); document.addEventListener("slewCameraToPosition", () => { - // if (this.#slaveDCSCameraAvailable) { // Commented to experiment with usability this.#broadcastPosition(); - // } }); /* Pan interval */ @@ -368,26 +363,30 @@ export class Map extends L.Map { contextAction?: ContextAction | null; } ) { - this.#state = state; + console.log(`Switching from state ${this.#state} to ${state}`); - /* Operations to perform if you are NOT in a state */ - if (this.#state !== COALITIONAREA_DRAW_POLYGON) { + /* Operations to perform when leaving a state */ + if (this.#state === COALITIONAREA_DRAW_POLYGON) { this.getSelectedCoalitionArea()?.setEditing(false); } - if (this.#state !== COALITIONAREA_EDIT) { - this.#deselectSelectedCoalitionArea(); - } - /* Operations to perform if you ARE in a state */ + this.#state = state; + + /* Operations to perform when entering a state */ if (this.#state === IDLE) { getApp().getUnitsManager().deselectAllUnits(); + this.deselectAllCoalitionAreas(); } else if (this.#state === SPAWN_UNIT) { this.#spawnRequestTable = options?.spawnRequestTable ?? null; + console.log(`Spawn request table:`); + console.log(this.#spawnRequestTable); } else if (this.#state === CONTEXT_ACTION) { this.#contextAction = options?.contextAction ?? null; + console.log(`Context action:`); + console.log(this.#contextAction); } else if (this.#state === COALITIONAREA_DRAW_POLYGON) { - this.#coalitionAreas.push(new CoalitionArea([])); - this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this); + this.#coalitionPolygons.push(new CoalitionPolygon([])); + this.#coalitionPolygons[this.#coalitionPolygons.length - 1].addTo(this); } document.dispatchEvent( @@ -400,15 +399,15 @@ export class Map extends L.Map { } deselectAllCoalitionAreas() { - this.#coalitionAreas.forEach((coalitionArea: CoalitionArea) => - coalitionArea.setSelected(false) + this.#coalitionPolygons.forEach((coalitionPolygon: CoalitionPolygon) => + coalitionPolygon.setSelected(false) ); } - deleteCoalitionArea(coalitionArea: CoalitionArea) { - if (this.#coalitionAreas.includes(coalitionArea)) - this.#coalitionAreas.splice( - this.#coalitionAreas.indexOf(coalitionArea), + deleteCoalitionArea(coalitionArea: CoalitionPolygon) { + if (this.#coalitionPolygons.includes(coalitionArea)) + this.#coalitionPolygons.splice( + this.#coalitionPolygons.indexOf(coalitionArea), 1 ); if (this.hasLayer(coalitionArea)) this.removeLayer(coalitionArea); @@ -551,15 +550,15 @@ export class Map extends L.Map { } getSelectedCoalitionArea() { - return this.#coalitionAreas.find((area: CoalitionArea) => { - return area.getSelected(); + return this.#coalitionPolygons.find((coalitionPolygon: CoalitionPolygon) => { + return coalitionPolygon.getSelected(); }); } - bringCoalitionAreaToBack(coalitionArea: CoalitionArea) { + bringCoalitionAreaToBack(coalitionArea: CoalitionPolygon) { coalitionArea.bringToBack(); - this.#coalitionAreas.splice(this.#coalitionAreas.indexOf(coalitionArea), 1); - this.#coalitionAreas.unshift(coalitionArea); + this.#coalitionPolygons.splice(this.#coalitionPolygons.indexOf(coalitionArea), 1); + this.#coalitionPolygons.unshift(coalitionArea); } setOption(key, value) { @@ -644,6 +643,12 @@ export class Map extends L.Map { this.#contextAction?.executeCallback(targetUnit, targetPosition); } + preventClicks() { + console.log("Preventing clicks on map") + window.clearTimeout(this.#shortPressTimer); + window.clearTimeout(this.#longPressTimer); + } + /* Event handlers */ #onDragStart(e: any) { this.#isDragging = true; @@ -653,8 +658,7 @@ export class Map extends L.Map { this.#isDragging = false; } - #onSelectionStart(e: any) { - } + #onSelectionStart(e: any) {} #onSelectionEnd(e: any) { getApp().getUnitsManager().selectFromBounds(e.selectionBounds); @@ -709,7 +713,6 @@ export class Map extends L.Map { /* Execute the short click action */ if (this.#state === IDLE) { - this.deselectAllCoalitionAreas(); } else if (this.#state === SPAWN_UNIT) { if (this.#spawnRequestTable !== null) { this.#spawnRequestTable.unit.location = location; @@ -733,11 +736,9 @@ export class Map extends L.Map { ); } } else if (this.#state === COALITIONAREA_DRAW_POLYGON) { - if (this.getSelectedCoalitionArea()?.getEditing()) { - this.getSelectedCoalitionArea()?.addTemporaryLatLng(location); - } else { - this.deselectAllCoalitionAreas(); - } + this.getSelectedCoalitionArea()?.getEditing() + ? this.getSelectedCoalitionArea()?.addTemporaryLatLng(location) + : this.deselectAllCoalitionAreas(); } else if (this.#state === COALITIONAREA_EDIT) { this.deselectAllCoalitionAreas(); } else if (this.#state === CONTEXT_ACTION) { @@ -750,7 +751,7 @@ export class Map extends L.Map { console.log(`Long press at ${e.latlng}`); if (!this.#isDragging && !this.#isZooming) { - if (this.getState() == IDLE) { + if (this.#state == IDLE) { if (e.type === "touchstart") document.dispatchEvent( new CustomEvent("mapForceBoxSelect", { detail: e }) @@ -759,10 +760,10 @@ export class Map extends L.Map { document.dispatchEvent( new CustomEvent("mapForceBoxSelect", { detail: e.originalEvent }) ); - } else if (this.getState() == COALITIONAREA_EDIT) { - for (let idx = 0; idx < this.#coalitionAreas.length; idx++) { - if (polyContains(e.latlng, this.#coalitionAreas[idx])) { - this.#coalitionAreas[idx].setSelected(true); + } else if (this.#state == COALITIONAREA_EDIT) { + for (let idx = 0; idx < this.#coalitionPolygons.length; idx++) { + if (polyContains(e.latlng, this.#coalitionPolygons[idx])) { + this.#coalitionPolygons[idx].setSelected(true); break; } } @@ -776,11 +777,6 @@ export class Map extends L.Map { this.#lastMousePosition.x = e.originalEvent.x; this.#lastMousePosition.y = e.originalEvent.y; this.#lastMouseCoordinates = this.mouseEventToLatLng(e.originalEvent); - - if (this.#state === COALITIONAREA_DRAW_POLYGON && e.latlng !== undefined) { - /* Update the polygon being drawn with the current position of the mouse cursor */ - //this.getSelectedCoalitionArea()?.moveActiveVertex(e.latlng); - } } #onMapMove(e: any) { @@ -864,10 +860,6 @@ export class Map extends L.Map { return minimapBoundaries; } - #deselectSelectedCoalitionArea() { - this.getSelectedCoalitionArea()?.setSelected(false); - } - #setSlaveDCSCameraAvailable(newSlaveDCSCameraAvailable: boolean) { this.#slaveDCSCameraAvailable = newSlaveDCSCameraAvailable; let linkButton = document.getElementById("camera-link-control"); diff --git a/frontend/react/src/map/rangecircle.ts b/frontend/react/src/map/rangecircle.ts index ed76937a..d9e81c34 100644 --- a/frontend/react/src/map/rangecircle.ts +++ b/frontend/react/src/map/rangecircle.ts @@ -1,5 +1,4 @@ -// @ts-nocheck -// This is a horrible hack. But it is needed at the moment to ovveride a default behaviour of Leaflet. TODO please fix me the proper way. +// @ts-nocheck <-- This is a horrible hack. But it is needed at the moment to ovveride a default behaviour of Leaflet. TODO please fix me the proper way. import { Circle, Point, Polyline } from "leaflet"; diff --git a/frontend/react/src/mission/missionmanager.ts b/frontend/react/src/mission/missionmanager.ts index 0fed3c8c..91418f36 100644 --- a/frontend/react/src/mission/missionmanager.ts +++ b/frontend/react/src/mission/missionmanager.ts @@ -23,6 +23,7 @@ import { DateAndTime, MissionData, } from "../interfaces"; +import { Coalition } from "../types/types"; /** The MissionManager */ export class MissionManager { @@ -253,10 +254,10 @@ export class MissionManager { getCommandedCoalition() { if (this.getCommandModeOptions().commandMode === BLUE_COMMANDER) - return "blue"; + return "blue" as Coalition; else if (this.getCommandModeOptions().commandMode === RED_COMMANDER) - return "red"; - else return "all"; + return "red" as Coalition; + else return "all" as Coalition; } refreshSpawnPoints() { @@ -395,14 +396,7 @@ export class MissionManager { } #onAirbaseClick(e: any) { - getApp() - .getMap() - .showAirbaseContextMenu( - e.sourceTarget, - e.originalEvent.x, - e.originalEvent.y, - e.latlng - ); + } #loadAirbaseChartData(callsign: string) { diff --git a/frontend/react/src/olympusapp.ts b/frontend/react/src/olympusapp.ts index ca55ac8c..2449aa0c 100644 --- a/frontend/react/src/olympusapp.ts +++ b/frontend/react/src/olympusapp.ts @@ -210,7 +210,7 @@ export class OlympusApp { /* Load the config file from the server */ const configRequest = new Request( - window.location.href.split("?")[0].replace("/vite", "/") + + window.location.href.split("?")[0].replace("vite/", "") + "resources/config" ); fetch(configRequest) diff --git a/frontend/react/src/other/utils.ts b/frontend/react/src/other/utils.ts index 02e8868e..05cc5e02 100644 --- a/frontend/react/src/other/utils.ts +++ b/frontend/react/src/other/utils.ts @@ -1,15 +1,12 @@ -import { LatLng, Point, Polygon } from "leaflet"; +import { LatLng, Polygon } from "leaflet"; import * as turf from "@turf/turf"; import { UnitDatabase } from "../unit/databases/unitdatabase"; import { - AircraftDatabase, aircraftDatabase, } from "../unit/databases/aircraftdatabase"; import { helicopterDatabase } from "../unit/databases/helicopterdatabase"; import { groundUnitDatabase } from "../unit/databases/groundunitdatabase"; -//import { Buffer } from "buffer"; import { - GROUND_UNIT_AIR_DEFENCE_REGEX, ROEs, emissionsCountermeasures, reactionsToThreat, @@ -290,12 +287,6 @@ export function mercatorToLatLng(x: number, y: number) { return { lng: lng, lat: lat }; } -export function createDivWithClass(className: string) { - var el = document.createElement("div"); - el.classList.add(className); - return el; -} - export function knotsToMs(knots: number) { return knots / 1.94384; } @@ -325,10 +316,17 @@ export function nmToFt(nm: number) { } export function polyContains(latlng: LatLng, polygon: Polygon) { - var poly = polygon.toGeoJSON(); + const poly = polygon.toGeoJSON(); return turf.inside(turf.point([latlng.lng, latlng.lat]), poly); } +export function polyCenter(polygon: Polygon) { + const poly = polygon.toGeoJSON(); + const center = turf.center(poly); + return new LatLng(center.geometry.coordinates[1], center.geometry.coordinates[0]); +} + + export function randomPointInPoly(polygon: Polygon): LatLng { var bounds = polygon.getBounds(); var x_min = bounds.getEast(); @@ -450,10 +448,6 @@ export function getUnitCategoryByBlueprint(blueprint: UnitBlueprint) { return "unknown"; } -export function base64ToBytes(base64: string) { - //return Buffer.from(base64, 'base64').buffer; -} - export function enumToState(state: number) { if (state < states.length) return states[state]; else return states[0]; diff --git a/frontend/react/src/server/servermanager.ts b/frontend/react/src/server/servermanager.ts index 8540e013..fd492075 100644 --- a/frontend/react/src/server/servermanager.ts +++ b/frontend/react/src/server/servermanager.ts @@ -175,7 +175,7 @@ export class ServerManager { } setAddress(address: string) { - this.#REST_ADDRESS = `${address.replace('vite', '')}olympus`; + this.#REST_ADDRESS = `${address.replace('vite/', '')}olympus`; console.log(`Setting REST address to ${this.#REST_ADDRESS}`); } diff --git a/frontend/react/src/types/types.ts b/frontend/react/src/types/types.ts index da3a7319..11f7ddad 100644 --- a/frontend/react/src/types/types.ts +++ b/frontend/react/src/types/types.ts @@ -50,6 +50,6 @@ export type MGRS = { zoneNumber: string; }; -export type Coalition = "blue" | "neutral" | "red"; +export type Coalition = "blue" | "neutral" | "red" | "all"; export type Context = string; diff --git a/frontend/react/src/ui/panels/drawingmenu.tsx b/frontend/react/src/ui/panels/drawingmenu.tsx index 8f4085c9..856baaca 100644 --- a/frontend/react/src/ui/panels/drawingmenu.tsx +++ b/frontend/react/src/ui/panels/drawingmenu.tsx @@ -1,19 +1,67 @@ import React, { useEffect, useState } from "react"; import { Menu } from "./components/menu"; -import { FaQuestionCircle } from "react-icons/fa"; +import { FaQuestionCircle, FaRegCircle } from "react-icons/fa"; import { getApp } from "../../olympusapp"; -import { COALITIONAREA_DRAW_POLYGON, COALITIONAREA_EDIT, IDLE } from "../../constants/constants"; +import { + COALITIONAREA_DRAW_POLYGON, + COALITIONAREA_EDIT, + IDLE, +} from "../../constants/constants"; import { OlStateButton } from "../components/olstatebutton"; import { faDrawPolygon } from "@fortawesome/free-solid-svg-icons"; +import { faCircle } from "@fortawesome/free-regular-svg-icons"; +import { CoalitionPolygon } from "../../map/coalitionarea/coalitionpolygon"; +import { OlCoalitionToggle } from "../components/olcoalitiontoggle"; +import { OlDropdown, OlDropdownItem } from "../components/oldropdown"; +import { OlCheckbox } from "../components/olcheckbox"; +import { Coalition } from "../../types/types"; +import { OlRangeSlider } from "../components/olrangeslider"; export function DrawingMenu(props: { open: boolean; onClose: () => void }) { const [drawingPolygon, setDrawingPolygon] = useState(false); + const [drawingCircle, setDrawingCircle] = useState(false); + const [activeCoalitionArea, setActiveCoalitionArea] = useState( + null as null | CoalitionPolygon + ); + const [areaCoalition, setAreaCoalition] = useState("blue" as Coalition); + const [IADSDensity, setIADSDensity] = useState(50); + const [IADSDistribution, setIADSDistribution] = useState(50); + const [forceCoalitionAppropriateUnits, setForceCoalitionApproriateUnits] = useState(false); useEffect(() => { - if (props.open && !drawingPolygon) { + if ( + drawingPolygon && + getApp().getMap().getState() !== COALITIONAREA_DRAW_POLYGON + ) + setDrawingPolygon(false); + + if (props.open && !drawingPolygon) getApp().getMap().setState(COALITIONAREA_EDIT); + + if ( + activeCoalitionArea && + activeCoalitionArea?.getCoalition() !== areaCoalition + ) + setAreaCoalition(activeCoalitionArea?.getCoalition()); + }); + + document.addEventListener("mapStateChanged", (event: any) => { + if ( + drawingPolygon && + getApp().getMap().getState() !== COALITIONAREA_DRAW_POLYGON + ) + setDrawingPolygon(false); + + if ( + [COALITIONAREA_DRAW_POLYGON, COALITIONAREA_EDIT].includes( + getApp().getMap().getState() + ) + ) { + setActiveCoalitionArea( + getApp().getMap().getSelectedCoalitionArea() ?? null + ); } - }) + }); return ( void }) { The draw tool allows you to quickly draw areas on the map and use these areas to spawn units and activate triggers. -
+
@@ -40,22 +88,168 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
-
- { - if (drawingPolygon) - getApp().getMap().setState(COALITIONAREA_EDIT); - else - getApp().getMap().setState(COALITIONAREA_DRAW_POLYGON); - setDrawingPolygon(!drawingPolygon); - }} - > -
Add polygon
-
+ <> + {activeCoalitionArea === null && ( +
+ { + if (drawingPolygon) + getApp().getMap().setState(COALITIONAREA_EDIT); + else getApp().getMap().setState(COALITIONAREA_DRAW_POLYGON); + setDrawingPolygon(!drawingPolygon); + }} + > +
Add polygon
+
+ {}} + > +
Add circle (WIP)
+
+
+ )} + +
+ {activeCoalitionArea !== null && ( +
+
+
Area label
+ + activeCoalitionArea.setLabelText(ev.currentTarget.value) + } + > +
+
+
Coalition:
+ { + let newCoalition = ""; + if (areaCoalition === "blue") newCoalition = "neutral"; + else if (areaCoalition === "neutral") newCoalition = "red"; + else if (areaCoalition === "red") newCoalition = "blue"; + setAreaCoalition(newCoalition as Coalition); + activeCoalitionArea.setCoalition(newCoalition as Coalition); + }} + > +
+
+
+ Automatic IADS generation +
+ + {getApp() + .getGroundUnitDatabase() + .getTypes() + .map((era) => { + return ( + + {}} /> + {era} + + ); + })} + + + {getApp() + .getGroundUnitDatabase() + .getEras() + .map((era) => { + return ( + + {}} /> + {era} + + ); + })} + + + {["Short range", "Medium range", "Long range"].map((era) => { + return ( + + {}} /> + {era} + + ); + })} + +
+
+
IADS Density
+
+ 50% +
+
+ {}}> +
+
+
+
IADS Distribution
+
+ 50% +
+
+ {setIADSDistribution(Number(ev.target.value))}}> +
+
+ { + setForceCoalitionApproriateUnits(!forceCoalitionAppropriateUnits); + }} /> + Force coalition appropriate units +
+ +
+
+ )}
); diff --git a/frontend/react/src/ui/panels/spawnmenu.tsx b/frontend/react/src/ui/panels/spawnmenu.tsx index 45dcd593..9771d87c 100644 --- a/frontend/react/src/ui/panels/spawnmenu.tsx +++ b/frontend/react/src/ui/panels/spawnmenu.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { Menu } from "./components/menu"; import { faPlus } from "@fortawesome/free-solid-svg-icons"; import { library } from "@fortawesome/fontawesome-svg-core"; @@ -75,12 +75,13 @@ export function SpawnMenu(props: { filteredAirDefense = filterUnits(filteredAirDefense, filterString); filteredGroundUnits = filterUnits(filteredGroundUnits, filterString); - if (!props.open) { - if (getApp()?.getMap()?.getState() === SPAWN_UNIT) - getApp().getMap().setState(IDLE); - if (blueprint !== null) - setBlueprint(null); - } + useEffect(() => { + if (!props.open) { + if (getApp()?.getMap()?.getState() === SPAWN_UNIT) + getApp().getMap().setState(IDLE); + if (blueprint !== null) setBlueprint(null); + } + }); return ( setFilterString(ev.target.value)} /> -
+
{Object.keys(filteredAircraft).map((key) => { const blueprint = getApp().getAircraftDatabase().blueprints[key]; @@ -116,9 +119,11 @@ export function SpawnMenu(props: {
-
+
{Object.keys(filteredHelicopters).map((key) => { const blueprint = getApp().getHelicopterDatabase().blueprints[key]; @@ -134,9 +139,11 @@ export function SpawnMenu(props: {
-
+
{Object.keys(filteredAirDefense).map((key) => { const blueprint = getApp().getGroundUnitDatabase().blueprints[key]; @@ -152,9 +159,11 @@ export function SpawnMenu(props: {
-
+
{Object.keys(filteredGroundUnits).map((key) => { const blueprint = getApp().getGroundUnitDatabase().blueprints[key]; @@ -170,9 +179,11 @@ export function SpawnMenu(props: {
-
+
{Object.keys(filteredNavyUnits).map((key) => { const blueprint = getApp().getNavyUnitDatabase().blueprints[key]; diff --git a/frontend/react/src/unit/databases/unitdatabase.ts b/frontend/react/src/unit/databases/unitdatabase.ts index 46ad2bd2..d8a49d9e 100644 --- a/frontend/react/src/unit/databases/unitdatabase.ts +++ b/frontend/react/src/unit/databases/unitdatabase.ts @@ -118,7 +118,7 @@ export abstract class UnitDatabase { return types; } - /* Returns a list of all possible periods in a database */ + /* Returns a list of all possible eras in a database */ getEras() { var filteredBlueprints = this.getBlueprints(); var eras: string[] = []; diff --git a/frontend/react/src/unit/unitsmanager.ts b/frontend/react/src/unit/unitsmanager.ts index 729b2642..5fba4387 100644 --- a/frontend/react/src/unit/unitsmanager.ts +++ b/frontend/react/src/unit/unitsmanager.ts @@ -16,7 +16,7 @@ import { randomPointInPoly, randomUnitBlueprint, } from "../other/utils"; -import { CoalitionArea } from "../map/coalitionarea/coalitionarea"; +import { CoalitionPolygon } from "../map/coalitionarea/coalitionpolygon"; import { groundUnitDatabase } from "./databases/groundunitdatabase"; import { DELETE_CYCLE_TIME, @@ -24,7 +24,7 @@ import { DataIndexes, GAME_MASTER, IADSDensities, - IDLE + IDLE, } from "../constants/constants"; import { DataExtractor } from "../server/dataextractor"; import { citiesDatabase } from "./databases/citiesdatabase"; @@ -1642,7 +1642,7 @@ export class UnitsManager { * @param distribution Value between 0 and 100, controls how "scattered" the units will be */ createIADS( - coalitionArea: CoalitionArea, + coalitionArea: CoalitionPolygon, types: { [key: string]: boolean }, eras: { [key: string]: boolean }, ranges: { [key: string]: boolean },