diff --git a/frontend/react/public/images/markers/path.svg b/frontend/react/public/images/markers/path.svg index 61a883ff..b5c7effa 100644 --- a/frontend/react/public/images/markers/path.svg +++ b/frontend/react/public/images/markers/path.svg @@ -1,8 +1,8 @@ + transform="matrix(0.65082874,0,0,0.65082874,3.8892989,2.018336)"> SVGInjector(img); + + el.appendChild(img); + this.getElement()?.appendChild(el); + } +} diff --git a/frontend/react/src/shortcut/shortcut.ts b/frontend/react/src/shortcut/shortcut.ts index b947ef3e..39b0d951 100644 --- a/frontend/react/src/shortcut/shortcut.ts +++ b/frontend/react/src/shortcut/shortcut.ts @@ -13,14 +13,18 @@ export class Shortcut { this.#id = id; this.#options = options; - AppStateChangedEvent.on((state, subState) => (this.#keydown = false)); - ModalEvent.on((modal) => (this.#modal = modal)) + /* I don't know why I set this, may be a leftover from initial shortcut experiments. + If enabled, it will cause the keydown to be reset when the app state changes, which may + cause shortcuts that cause a state change (like unit selection) to remain stuck. */ + //AppStateChangedEvent.on((state, subState) => (this.#keydown = false)); + + ModalEvent.on((modal) => (this.#modal = modal)); /* On keyup, it is enough to check the code only, not the entire combination */ document.addEventListener("keyup", (ev: any) => { if (this.#modal) return; if (this.#keydown && this.getOptions().code === ev.code) { - console.log(`Keyup for shortcut ${this.#id}`) + console.log(`Keyup for shortcut ${this.#id}`); ev.preventDefault(); this.#keydown = false; this.getOptions().keyUpCallback(ev); @@ -30,7 +34,7 @@ export class Shortcut { /* Forced keyup, in case the window loses focus */ document.addEventListener("blur", (ev: any) => { if (this.#keydown) { - console.log(`Keyup (forced by blur) for shortcut ${this.#id}`) + console.log(`Keyup (forced by blur) for shortcut ${this.#id}`); ev.preventDefault(); this.#keydown = false; this.getOptions().keyUpCallback(ev); @@ -46,10 +50,10 @@ export class Shortcut { (this.getOptions().ctrlKey === undefined || ev.ctrlKey === (this.getOptions().ctrlKey ?? ev.code.indexOf("Control") >= 0)) && (this.getOptions().shiftKey === undefined || ev.shiftKey === (this.getOptions().shiftKey ?? ev.code.indexOf("Shift") >= 0)) ) { - console.log(`Keydown event for shortcut ${this.#id}`) + console.log(`Keydown event for shortcut ${this.#id}`); ev.preventDefault(); this.#keydown = true; - const keyDownCallback = this.getOptions().keyDownCallback + const keyDownCallback = this.getOptions().keyDownCallback; if (keyDownCallback) keyDownCallback(ev); /* Key down event is optional */ } }); @@ -84,4 +88,8 @@ export class Shortcut { ); return actions; } + + #setKeydown(keydown: boolean) { + this.#keydown = keydown; + } } diff --git a/frontend/react/src/unit/unit.ts b/frontend/react/src/unit/unit.ts index a1480dde..92db8a1e 100644 --- a/frontend/react/src/unit/unit.ts +++ b/frontend/react/src/unit/unit.ts @@ -72,15 +72,9 @@ import { ArrowMarker } from "../map/markers/arrowmarker"; import { Spot } from "../mission/spot"; import { SpotEditMarker } from "../map/markers/spoteditmarker"; import { SpotMarker } from "../map/markers/spotmarker"; -import { get } from "http"; const bearingStrings = ["north", "north-east", "east", "south-east", "south", "south-west", "west", "north-west", "north"]; -var pathIcon = new Icon({ - iconUrl: "images/markers/path.svg", - iconAnchor: [20, 43], -}); - /** * Unit class which controls unit behaviour */ @@ -164,7 +158,6 @@ export abstract class Unit extends CustomMarker { #selected: boolean = false; #hidden: boolean = false; #highlighted: boolean = false; - #pathMarkers: Marker[] = []; #pathPolyline: Polyline; #contactsPolylines: Polyline[] = []; #engagementCircle: RangeCircle; @@ -1223,14 +1216,6 @@ export abstract class Unit extends CustomMarker { if (!this.#human) this.#activePath = []; } - updatePathFromMarkers() { - var path: any = []; - this.#pathMarkers.forEach((marker) => { - path[Object.keys(path).length.toString()] = marker.getLatLng(); - }); - getApp().getServerManager().addDestination(this.ID, path); - } - attackUnit(targetID: number) { /* Units can't attack themselves */ if (!this.#human) if (this.ID != targetID) getApp().getServerManager().attackUnit(this.ID, targetID); @@ -1716,37 +1701,8 @@ export abstract class Unit extends CustomMarker { var points: LatLng[] = []; points.push(new LatLng(this.#position.lat, this.#position.lng)); - /* Add markers if missing */ - while (this.#pathMarkers.length < Object.keys(this.#activePath).length) { - var marker = new Marker([0, 0], { - icon: pathIcon, - draggable: true, - }).addTo(getApp().getMap()); - marker.on("dragstart", (event) => { - event.target.options["freeze"] = true; - }); - marker.on("dragend", (event) => { - this.updatePathFromMarkers(); - event.target.options["freeze"] = false; - }); - this.#pathMarkers.push(marker); - } - - /* Remove markers if too many */ - while (this.#pathMarkers.length > Object.keys(this.#activePath).length) { - getApp() - .getMap() - .removeLayer(this.#pathMarkers[this.#pathMarkers.length - 1]); - this.#pathMarkers.splice(this.#pathMarkers.length - 1, 1); - } - - /* Update the position of the existing markers (to avoid creating markers uselessly) */ for (let WP in this.#activePath) { var destination = this.#activePath[WP]; - var frozen = this.#pathMarkers[parseInt(WP)].options["freeze"]; - if (!frozen) { - this.#pathMarkers[parseInt(WP)].setLatLng([destination.lat, destination.lng]); - } points.push(new LatLng(destination.lat, destination.lng)); } @@ -1763,10 +1719,6 @@ export abstract class Unit extends CustomMarker { #clearPath() { if (this.#pathPolyline.getLatLngs().length != 0) { - for (let WP in this.#pathMarkers) { - getApp().getMap().removeLayer(this.#pathMarkers[WP]); - } - this.#pathMarkers = []; this.#pathPolyline.setLatLngs([]); } } diff --git a/frontend/react/src/unit/unitsmanager.ts b/frontend/react/src/unit/unitsmanager.ts index 2798b3ea..c178114d 100644 --- a/frontend/react/src/unit/unitsmanager.ts +++ b/frontend/react/src/unit/unitsmanager.ts @@ -38,6 +38,7 @@ import { } from "../events"; import { UnitDatabase } from "./databases/unitdatabase"; import * as turf from "@turf/turf"; +import { PathMarker } from "../map/markers/pathmarker"; /** The UnitsManager handles the creation, update, and control of units. Data is strictly updated by the server ONLY. This means that any interaction from the user will always and only * result in a command to the server, executed by means of a REST PUT request. Any subsequent change in data will be reflected only when the new data is sent back by the server. This strategy allows @@ -54,6 +55,7 @@ export class UnitsManager { #protectionCallback: (units: Unit[]) => void = (units) => {}; #AWACSReference: Unit | null = null; #clusters: { [key: number]: Unit[] } = {}; + #pathMarkers: PathMarker[] = []; constructor() { this.#unitDatabase = new UnitDatabase(); @@ -303,8 +305,50 @@ export class UnitsManager { } /* Update all the lines of all the selected units. This code is handled by the UnitsManager since, for example, it must be run both when the detected OR the detecting unit is updated */ + let pathMarkersCoordinates: LatLng[] = []; for (let ID in this.#units) { - if (this.#units[ID].getSelected()) this.#units[ID].drawLines(); + if (this.#units[ID].getSelected()) { + this.#units[ID].drawLines(); + const unitPath = this.#units[ID].getActivePath(); + unitPath.forEach((latlng: LatLng) => { + if (pathMarkersCoordinates.every((coord: LatLng) => coord.lat != latlng.lat && coord.lng != latlng.lng)) pathMarkersCoordinates.push(latlng); + }); + } + } + + /* Update the path markers */ + if (this.#pathMarkers.find((pathMarker: PathMarker) => pathMarker.options["freeze"]) === undefined) { + this.#pathMarkers.forEach((pathMarker: PathMarker) => { + if (!pathMarkersCoordinates.some((coord: LatLng) => pathMarker.getLatLng().equals(coord))) { + pathMarker.remove(); + this.#pathMarkers = this.#pathMarkers.filter((marker: PathMarker) => marker !== pathMarker); + } + }); + + pathMarkersCoordinates.forEach((latlng: LatLng) => { + if (!this.#pathMarkers.some((pathMarker: PathMarker) => pathMarker.getLatLng().equals(latlng))) { + const pathMarker = new PathMarker(latlng); + + pathMarker.on("dragstart", (event) => { + event.target.options["freeze"] = true; + event.target.options["originalPosition"] = event.target.getLatLng(); + }); + pathMarker.on("dragend", (event) => { + event.target.options["freeze"] = false; + this.getSelectedUnits().forEach((unit) => { + let path = [...unit.getActivePath()]; + const idx = path.findIndex((coord: LatLng) => coord.equals(event.target.options["originalPosition"])); + if (idx !== -1) { + path[idx] = event.target.getLatLng(); + getApp().getServerManager().addDestination(unit.ID, path); + } + }); + }); + + pathMarker.addTo(getApp().getMap()); + this.#pathMarkers.push(pathMarker); + } + }); } /* Compute the base clusters */