diff --git a/frontend/react/src/map/coalitionarea/coalitionareahandle.ts b/frontend/react/src/map/coalitionarea/coalitionareahandle.ts index ea86b808..1612b63b 100644 --- a/frontend/react/src/map/coalitionarea/coalitionareahandle.ts +++ b/frontend/react/src/map/coalitionarea/coalitionareahandle.ts @@ -4,6 +4,10 @@ import { CustomMarker } from "../markers/custommarker"; export class CoalitionAreaHandle extends CustomMarker { constructor(latlng: LatLng) { super(latlng, { interactive: true, draggable: true }); + + this.on("add", (e) => { + this.getElement()?.addEventListener("touchstart", (e) => e.stopPropagation()); + }) } createIcon() { diff --git a/frontend/react/src/map/coalitionarea/coalitioncircle.ts b/frontend/react/src/map/coalitionarea/coalitioncircle.ts index 590d30b8..f7916267 100644 --- a/frontend/react/src/map/coalitionarea/coalitioncircle.ts +++ b/frontend/react/src/map/coalitionarea/coalitioncircle.ts @@ -49,6 +49,10 @@ export class CoalitionCircle extends Circle { this.#setRadiusHandle(); this.#drawLabel(); }); + + this.on("remove", () => { + this.#label.removeFrom(this._map); + }); } setCoalition(coalition: Coalition) { diff --git a/frontend/react/src/map/coalitionarea/coalitionpolygon.ts b/frontend/react/src/map/coalitionarea/coalitionpolygon.ts index 81f1912f..eff13d35 100644 --- a/frontend/react/src/map/coalitionarea/coalitionpolygon.ts +++ b/frontend/react/src/map/coalitionarea/coalitionpolygon.ts @@ -6,7 +6,7 @@ import { Polygon, PolylineOptions, DivIcon, - Marker, + Marker } from "leaflet"; import { getApp } from "../../olympusapp"; import { CoalitionAreaHandle } from "./coalitionareahandle"; @@ -57,6 +57,10 @@ export class CoalitionPolygon extends Polygon { this.#setMiddleHandles(); this.#drawLabel(); }); + + this.on("remove", () => { + this.#label.removeFrom(this._map); + }); } setCoalition(coalition: Coalition) { diff --git a/frontend/react/src/map/coalitionarea/draggablepath.ts b/frontend/react/src/map/coalitionarea/draggablepath.ts new file mode 100644 index 00000000..676870db --- /dev/null +++ b/frontend/react/src/map/coalitionarea/draggablepath.ts @@ -0,0 +1,179 @@ +import * as L from "leaflet"; + +export const initDraggablePath = () => { + //@ts-ignore + L.PathDraggable = L.Draggable.extend({ + initialize: function (path) { + this._path = path; + + this._canvas = path._map.getRenderer(path) instanceof L.Canvas; + + var element = this._canvas + ? this._path._map.getRenderer(this._path)._container + : this._path._path; + + //@ts-ignore + L.Draggable.prototype.initialize.call(this, element, element, true); + }, + + _updatePosition: function () { + var e = { originalEvent: this._lastEvent }; + + this.fire("drag", e); + }, + + _onDown: function (e) { + var first = e.touches ? e.touches[0] : e; + + this._startPoint = new L.Point(first.clientX, first.clientY); + + if ( + this._canvas && + !this._path._containsPoint( + this._path._map.mouseEventToLayerPoint(first) + ) + ) { + return; + } + + //@ts-ignore + L.Draggable.prototype._onDown.call(this, e); + e.stopPropagation(); + }, + }); + + //@ts-ignore + L.Handler.PathDrag = L.Handler.extend({ + initialize: function (path) { + this._path = path; + }, + + getEvents: function () { + return { + dragstart: this._onDragStart, + + drag: this._onDrag, + + dragend: this._onDragEnd, + }; + }, + + addHooks: function () { + if (!this._draggable) { + //@ts-ignore + this._draggable = new L.PathDraggable(this._path); + } + + this._draggable.on(this.getEvents(), this).enable(); + + L.DomUtil.addClass(this._draggable._element, "leaflet-path-draggable"); + }, + + removeHooks: function () { + this._draggable.off(this.getEvents(), this).disable(); + + L.DomUtil.removeClass(this._draggable._element, "leaflet-path-draggable"); + }, + + moved: function () { + return this._draggable && this._draggable._moved; + }, + + _onDragStart: function () { + this._startPoint = this._draggable._startPoint; + + this._path.closePopup().fire("movestart").fire("dragstart"); + }, + + _onDrag: function (e) { + var path = this._path, + event = + e.originalEvent.touches && e.originalEvent.touches.length === 1 + ? e.originalEvent.touches[0] + : e.originalEvent, + newPoint = L.point(event.clientX, event.clientY), + latlng = path._map.layerPointToLatLng(newPoint); + + this._offset = newPoint.subtract(this._startPoint); + + this._startPoint = newPoint; + + this._path.eachLatLng(this.updateLatLng, this); + + path.redraw(); + + e.latlng = latlng; + + e.offset = this._offset; + + path.fire("drag", e); + + e.latlng = this._path.getCenter + ? this._path.getCenter() + : this._path.getLatLng(); + + path.fire("move", e); + }, + + _onDragEnd: function (e) { + if (this._path._bounds) this.resetBounds(); + + this._path.fire("moveend").fire("dragend", e); + }, + + latLngToLayerPoint: function (latlng) { + // Same as map.latLngToLayerPoint, but without the round(). + + var projectedPoint = this._path._map.project(L.latLng(latlng)); + + return projectedPoint._subtract(this._path._map.getPixelOrigin()); + }, + + updateLatLng: function (latlng) { + var oldPoint = this.latLngToLayerPoint(latlng); + + oldPoint._add(this._offset); + + var newLatLng = this._path._map.layerPointToLatLng(oldPoint); + + latlng.lat = newLatLng.lat; + + latlng.lng = newLatLng.lng; + }, + + resetBounds: function () { + //@ts-ignore + this._path._bounds = new L.LatLngBounds(); + + this._path.eachLatLng(function (latlng) { + this._bounds.extend(latlng); + }); + }, + }); + + L.Path.include({ + eachLatLng: function (callback, context) { + context = context || this; + + var loop = function (latlngs) { + for (var i = 0; i < latlngs.length; i++) { + if (L.Util.isArray(latlngs[i])) loop(latlngs[i]); + else callback.call(context, latlngs[i]); + } + }; + + loop(this.getLatLngs ? this.getLatLngs() : [this.getLatLng()]); + }, + }); + + L.Path.addInitHook(function () { + //@ts-ignore + this.dragging = new L.Handler.PathDrag(this); + + if (this.options.draggable) { + this.once("add", function () { + this.dragging.enable(); + }); + } + }); +}; diff --git a/frontend/react/src/map/map.ts b/frontend/react/src/map/map.ts index 83b66e71..ed313f1a 100644 --- a/frontend/react/src/map/map.ts +++ b/frontend/react/src/map/map.ts @@ -3,13 +3,7 @@ import { getApp } from "../olympusapp"; import { BoxSelect } from "./boxselect"; import { Airbase } from "../mission/airbase"; import { Unit } from "../unit/unit"; -import { - areaContains, - circleContains, - deg2rad, - getGroundElevation, - polyContains, -} from "../other/utils"; +import { areaContains, deg2rad, getFunctionArguments, getGroundElevation } from "../other/utils"; import { TemporaryUnitMarker } from "./markers/temporaryunitmarker"; import { ClickableMiniMap } from "./clickableminimap"; import { @@ -38,12 +32,14 @@ import "./markers/stylesheets/units.css"; import "./map.css"; import { CoalitionCircle } from "./coalitionarea/coalitioncircle"; -import "leaflet-path-drag"; -import { faLeftLong } from "@fortawesome/free-solid-svg-icons"; +import { initDraggablePath } from "./coalitionarea/draggablepath"; +import { faDrawPolygon, faJetFighter, faMap } from "@fortawesome/free-solid-svg-icons"; /* Register the handler for the box selection */ L.Map.addInitHook("addHandler", "boxSelect", BoxSelect); +initDraggablePath(); + export class Map extends L.Map { /* Options */ #options: MapOptions = MAP_OPTIONS_DEFAULTS; @@ -413,63 +409,83 @@ export class Map extends L.Map { return [ { actions: ["Tap"], + target: faJetFighter, text: "Select unit", }, { actions: ["Shift", "Drag"], + target: faMap, + text: "Box selection", + }, + { + actions: ["Press", "Drag"], + target: faMap, text: "Box selection", }, { actions: ["Drag"], + target: faMap, text: "Move map location", }, - { - actions: ["Long tap", "Drag"], - text: "Box selection", - }, ]; } else if (this.#state === SPAWN_UNIT) { return [ { actions: ["Tap"], + target: faMap, text: "Spawn unit", }, { actions: ["Double tap"], + target: faMap, text: "Exit spawn mode", }, { actions: ["Drag"], + target: faMap, text: "Move map location", }, ]; } else if (this.#state === CONTEXT_ACTION) { - return [ - { - actions: ["Tap"], - text: this.#contextAction?.getLabel() ?? "", - }, + let controls = [ { actions: ["Double tap"], - text: "Exit action mode", + target: faMap, + text: "Deselect units", }, { actions: ["Drag"], + target: faMap, text: "Move map location", }, ]; + + if (this.#contextAction) { + /* TODO: I don't like this approach, it relies on the arguments names of the callback. We should find a better method */ + const args = getFunctionArguments(this.#contextAction.getCallback()); + controls.push({ + actions: ["Tap"], + target: args.includes("targetUnit")? faJetFighter: faMap, + text: this.#contextAction?.getLabel() ?? "", + }); + } + + return controls; } else if (this.#state === COALITIONAREA_EDIT) { return [ { actions: ["Tap"], + target: faDrawPolygon, text: "Select shape", }, { actions: ["Double tap"], + target: faMap, text: "Exit drawing mode", }, { actions: ["Drag"], + target: faMap, text: "Move map location", }, ]; @@ -477,14 +493,17 @@ export class Map extends L.Map { return [ { actions: ["Tap"], + target: faMap, text: "Add vertex to polygon", }, { actions: ["Double tap"], + target: faMap, text: "Finalize polygon", }, { actions: ["Drag"], + target: faMap, text: "Move map location", }, ]; @@ -492,10 +511,12 @@ export class Map extends L.Map { return [ { actions: ["Tap"], + target: faMap, text: "Add circle", }, { actions: ["Drag"], + target: faMap, text: "Move map location", }, ]; @@ -801,7 +822,8 @@ export class Map extends L.Map { this.#longPressTimer = window.setTimeout(() => { /* If the mouse is still being pressed, execute the long press action */ - if (this.#isMouseDown) this.#onLongPress(e); + if (this.#isMouseDown && !this.#isDragging && !this.#isZooming) + this.#onLongPress(e); }, 500); } @@ -822,15 +844,20 @@ export class Map extends L.Map { } #onShortPress(e: any) { - console.log(`Short press at ${e.latlng}`); + let touchLocation: L.LatLng; + if (e.type === "touchstart") + touchLocation = this.containerPointToLatLng( + this.mouseEventToContainerPoint(e.touches[0]) + ); + else touchLocation = new L.LatLng(e.latlng.lat, e.latlng.lng); - const location = new L.LatLng(e.latlng.lat, e.latlng.lng); + console.log(`Short press at ${touchLocation}`); /* Execute the short click action */ if (this.#state === IDLE) { } else if (this.#state === SPAWN_UNIT) { if (this.#spawnRequestTable !== null) { - this.#spawnRequestTable.unit.location = location; + this.#spawnRequestTable.unit.location = touchLocation; getApp() .getUnitsManager() .spawnUnits( @@ -842,7 +869,7 @@ export class Map extends L.Map { undefined, (hash) => { this.addTemporaryMarker( - location, + touchLocation, this.#spawnRequestTable?.unit.unitType ?? "unknown", this.#spawnRequestTable?.coalition ?? "blue", hash @@ -853,7 +880,7 @@ export class Map extends L.Map { } else if (this.#state === COALITIONAREA_DRAW_POLYGON) { const selectedArea = this.getSelectedCoalitionArea(); if (selectedArea && selectedArea instanceof CoalitionPolygon) { - selectedArea.addTemporaryLatLng(location); + selectedArea.addTemporaryLatLng(touchLocation); } } else if (this.#state === COALITIONAREA_DRAW_CIRCLE) { const selectedArea = this.getSelectedCoalitionArea(); @@ -862,13 +889,13 @@ export class Map extends L.Map { selectedArea.getLatLng().lat == 0 && selectedArea.getLatLng().lng == 0 ) - selectedArea.setLatLng(location); + selectedArea.setLatLng(touchLocation); this.setState(COALITIONAREA_EDIT); } } else if (this.#state == COALITIONAREA_EDIT) { this.deselectAllCoalitionAreas(); for (let idx = 0; idx < this.#coalitionAreas.length; idx++) { - if (areaContains(e.latlng, this.#coalitionAreas[idx])) { + if (areaContains(touchLocation, this.#coalitionAreas[idx])) { this.#coalitionAreas[idx].setSelected(true); document.dispatchEvent( new CustomEvent("coalitionAreaSelected", { @@ -879,18 +906,24 @@ export class Map extends L.Map { } } } else if (this.#state === CONTEXT_ACTION) { - this.executeContextAction(null, e.latlng); + this.executeContextAction(null, touchLocation); } else { } } #onLongPress(e: any) { - console.log(`Long press at ${e.latlng}`); + let touchLocation: L.LatLng; + if (e.type === "touchstart") + touchLocation = this.containerPointToLatLng( + this.mouseEventToContainerPoint(e.touches[0]) + ); + else touchLocation = new L.LatLng(e.latlng.lat, e.latlng.lng); - this.deselectAllCoalitionAreas(); + console.log(`Long press at ${touchLocation}`); if (!this.#isDragging && !this.#isZooming) { - if (this.#state == IDLE) { + this.deselectAllCoalitionAreas(); + if (this.#state === IDLE) { if (e.type === "touchstart") document.dispatchEvent( new CustomEvent("mapForceBoxSelect", { detail: e }) @@ -908,7 +941,7 @@ export class Map extends L.Map { this.#lastMousePosition.x = e.originalEvent.x; this.#lastMousePosition.y = e.originalEvent.y; - this.#lastMouseCoordinates = this.mouseEventToLatLng(e.originalEvent); + this.#lastMouseCoordinates = e.latlng; } #onMapMove(e: any) { diff --git a/frontend/react/src/other/utils.ts b/frontend/react/src/other/utils.ts index 850d157d..3ec5ba96 100644 --- a/frontend/react/src/other/utils.ts +++ b/frontend/react/src/other/utils.ts @@ -1,9 +1,7 @@ import { Circle, LatLng, Polygon } from "leaflet"; import * as turf from "@turf/turf"; import { UnitDatabase } from "../unit/databases/unitdatabase"; -import { - aircraftDatabase, -} from "../unit/databases/aircraftdatabase"; +import { aircraftDatabase } from "../unit/databases/aircraftdatabase"; import { helicopterDatabase } from "../unit/databases/helicopterdatabase"; import { groundUnitDatabase } from "../unit/databases/groundunitdatabase"; import { @@ -317,33 +315,52 @@ export function nmToFt(nm: number) { } export function areaContains(latlng: LatLng, area: Polygon | Circle) { - if (area instanceof Polygon) - return polyContains(latlng, area); - else - return circleContains(latlng, area); + if (area instanceof Polygon) return polyContains(latlng, area); + else return circleContains(latlng, area); } export function polyContains(latlng: LatLng, polygon: Polygon) { - let coordinates = [(polygon.getLatLngs()[0] as LatLng[]).map((latlng) => {return [latlng.lng, latlng.lat]} )]; - coordinates[0].push([polygon.getLatLngs()[0][0].lng, polygon.getLatLngs()[0][0].lat]) + let coordinates = [ + (polygon.getLatLngs()[0] as LatLng[]).map((latlng) => { + return [latlng.lng, latlng.lat]; + }), + ]; + coordinates[0].push([ + polygon.getLatLngs()[0][0].lng, + polygon.getLatLngs()[0][0].lat, + ]); const poly = turf.polygon(coordinates); return turf.inside(turf.point([latlng.lng, latlng.lat]), poly); } export function circleContains(latlng: LatLng, circle: Circle) { - const poly = turf.circle(turf.point([circle.getLatLng().lng, circle.getLatLng().lat]), circle.getRadius() / 1000, 100, 'kilometers'); + const poly = turf.circle( + turf.point([circle.getLatLng().lng, circle.getLatLng().lat]), + circle.getRadius() / 1000, + 100, + "kilometers" + ); return turf.inside(turf.point([latlng.lng, latlng.lat]), poly); } export function polyCenter(polygon: Polygon) { - let coordinates = [(polygon.getLatLngs()[0] as LatLng[]).map((latlng) => {return [latlng.lng, latlng.lat]} )]; - coordinates[0].push([polygon.getLatLngs()[0][0].lng, polygon.getLatLngs()[0][0].lat]) + let coordinates = [ + (polygon.getLatLngs()[0] as LatLng[]).map((latlng) => { + return [latlng.lng, latlng.lat]; + }), + ]; + coordinates[0].push([ + polygon.getLatLngs()[0][0].lng, + polygon.getLatLngs()[0][0].lat, + ]); const poly = turf.polygon(coordinates); const center = turf.center(featureCollection([poly])); - return new LatLng(center.geometry.coordinates[1], center.geometry.coordinates[0]); + 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(); @@ -354,8 +371,15 @@ export function randomPointInPoly(polygon: Polygon): LatLng { var lat = y_min + Math.random() * (y_max - y_min); var lng = x_min + Math.random() * (x_max - x_min); - let coordinates = [(polygon.getLatLngs()[0] as LatLng[]).map((latlng) => {return [latlng.lng, latlng.lat]} )]; - coordinates[0].push([polygon.getLatLngs()[0][0].lng, polygon.getLatLngs()[0][0].lat]) + let coordinates = [ + (polygon.getLatLngs()[0] as LatLng[]).map((latlng) => { + return [latlng.lng, latlng.lat]; + }), + ]; + coordinates[0].push([ + polygon.getLatLngs()[0][0].lng, + polygon.getLatLngs()[0][0].lat, + ]); const poly = turf.polygon(coordinates); var inside = turf.inside(turf.point([lng, lat]), poly); @@ -565,3 +589,15 @@ export function getWikipediaEntry(search: string, callback: CallableFunction) { }; xhr.send(); } + +export function getFunctionArguments(func) { + var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm; + var ARGUMENT_NAMES = /([^\s,]+)/g; + + var fnStr = func.toString().replace(STRIP_COMMENTS, ""); + var result = fnStr + .slice(fnStr.indexOf("(") + 1, fnStr.indexOf(")")) + .match(ARGUMENT_NAMES); + if (result === null) result = []; + return result; +} diff --git a/frontend/react/src/ui/panels/controls.tsx b/frontend/react/src/ui/panels/controls.tsx index e8899222..1579512f 100644 --- a/frontend/react/src/ui/panels/controls.tsx +++ b/frontend/react/src/ui/panels/controls.tsx @@ -1,9 +1,18 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { getApp } from "../../olympusapp"; +import { IconDefinition } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; export function ControlsPanel(props: {}) { const [controls, setControls] = useState( - [] as { actions: string[]; text: string }[] + [] as { actions: string[]; target: IconDefinition, text: string }[] ); + + useEffect(() => { + if (getApp() && controls.length === 0) { + setControls(getApp().getMap().getCurrentControls()); + } + }) + document.addEventListener("mapStateChanged", (ev) => { setControls(getApp().getMap().getCurrentControls()); }); @@ -11,7 +20,7 @@ export function ControlsPanel(props: {}) { return (
@@ -19,29 +28,32 @@ export function ControlsPanel(props: {}) { return (
-
{control.text}
+
{control.text}
+
{control.actions.map((action, idx) => { return ( <> + {
+
}
{action}
- {idx !== control.actions.length - 1 &&
+
} + ); })}
+
); })} diff --git a/frontend/react/src/ui/panels/drawingmenu.tsx b/frontend/react/src/ui/panels/drawingmenu.tsx index 2fc60648..23dbe7bd 100644 --- a/frontend/react/src/ui/panels/drawingmenu.tsx +++ b/frontend/react/src/ui/panels/drawingmenu.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import { Menu } from "./components/menu"; -import { FaQuestionCircle, FaRegCircle } from "react-icons/fa"; +import { FaQuestionCircle, FaRegCircle, FaTrash } from "react-icons/fa"; import { getApp } from "../../olympusapp"; import { COALITIONAREA_DRAW_CIRCLE, @@ -62,6 +62,19 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) { setAreaCoalition(activeCoalitionArea?.getCoalition()); }); + useEffect(() => { + if (!props.open) { + if ( + [ + COALITIONAREA_EDIT, + COALITIONAREA_DRAW_CIRCLE, + COALITIONAREA_DRAW_POLYGON, + ].includes(getApp()?.getMap()?.getState()) + ) + getApp().getMap().setState(IDLE); + } + }); + document.addEventListener("mapStateChanged", (event: any) => { if ( drawingPolygon && @@ -147,7 +160,8 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) { Click on the map to add a new circle.
- You can drag the circle to move it and you can use the handle to set the radius. + You can drag the circle to move it and you can use the handle to + set the radius.
@@ -197,11 +211,21 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) { text-gray-200 `} > -
Area label
+
+ Area label +
{ + getApp().getMap().deleteCoalitionArea(activeCoalitionArea); + setActiveCoalitionArea(null); + }}> + +
+
{ + if (scrollRef.current) { + onScroll(scrollRef.current); + } + }); + + function onScroll(el) { + const sl = el.scrollLeft; const sr = - ev.target.scrollWidth - ev.target.scrollLeft - ev.target.clientWidth; + el.scrollWidth - el.scrollLeft - el.clientWidth; sl < 1 && !scrolledLeft && setScrolledLeft(true); sl > 1 && scrolledLeft && setScrolledLeft(false); @@ -77,7 +85,8 @@ export function Header() { my-2 flex w-full items-center gap-3 overflow-x-scroll no-scrollbar `} - onScroll={(ev) => onScroll(ev)} + onScroll={(ev) => onScroll(ev.target)} + ref={scrollRef} >
{ + if (scrollRef.current) { + onScroll(scrollRef.current); + } + }); + /* When a unit is selected, open the menu */ document.addEventListener("unitsSelection", (ev: CustomEventInit) => { setOpen(true); @@ -61,10 +69,10 @@ export function UnitMouseControlBar(props: {}) { setActiveContextAction(null); } - function onScroll(ev) { - const sl = ev.target.scrollLeft; + function onScroll(el) { + const sl = el.scrollLeft; const sr = - ev.target.scrollWidth - ev.target.scrollLeft - ev.target.clientWidth; + el.scrollWidth - el.scrollLeft - el.clientWidth; sl < 1 && !scrolledLeft && setScrolledLeft(true); sl > 1 && scrolledLeft && setScrolledLeft(false); @@ -96,7 +104,8 @@ export function UnitMouseControlBar(props: {}) { )}
onScroll(ev)} + onScroll={(ev) => onScroll(ev.target)} + ref={scrollRef} > {Object.values(contextActionsSet.getContextActions()).map( (contextAction) => { diff --git a/frontend/react/src/unit/unit.ts b/frontend/react/src/unit/unit.ts index c88c3a81..1b7bf65e 100644 --- a/frontend/react/src/unit/unit.ts +++ b/frontend/react/src/unit/unit.ts @@ -892,7 +892,7 @@ export abstract class Unit extends CustomMarker { contextActionSet.addContextAction( this, "move", - "Move", + "Set destination", "Click on the map to move the units there", faLocationDot, (units: Unit[], _, targetPosition) => { @@ -905,7 +905,7 @@ export abstract class Unit extends CustomMarker { contextActionSet.addContextAction( this, "path", - "Path", + "Append destination", "Click on the map to add a destination to the path", faRoute, (units: Unit[], _, targetPosition) => { @@ -2190,7 +2190,7 @@ export abstract class AirUnit extends Unit { contextActionSet.addContextAction( this, "refuel", - "Refuel", + "Refuel at tanker", "Refuel units at the nearest AAR Tanker. If no tanker is available the unit will RTB", olStatesRefuel, (units: Unit[]) => { @@ -2220,7 +2220,7 @@ export abstract class AirUnit extends Unit { ( units: Unit[], targetUnit: Unit | null, - targetPosition: LatLng | null + _ ) => { if (targetUnit) getApp().getUnitsManager().attackUnit(targetUnit.ID, units); @@ -2235,7 +2235,7 @@ export abstract class AirUnit extends Unit { ( units: Unit[], targetUnit: Unit | null, - targetPosition: LatLng | null + _ ) => { if (targetUnit) targetUnit.showFollowOptions(units); } @@ -2245,12 +2245,12 @@ export abstract class AirUnit extends Unit { contextActionSet.addContextAction( this, "bomb", - "Precision bombing", + "Precision bomb location", "Click on a point to execute a precision bombing attack", faLocationCrosshairs, ( units: Unit[], - targetUnit: Unit | null, + _, targetPosition: LatLng | null ) => { if (targetPosition) @@ -2260,12 +2260,12 @@ export abstract class AirUnit extends Unit { contextActionSet.addContextAction( this, "carpet-bomb", - "Carpet bombing", + "Carpet bomb location", "Click on a point to execute a carpet bombing attack", faXmarksLines, ( units: Unit[], - targetUnit: Unit | null, + _, targetPosition: LatLng | null ) => { if (targetPosition) @@ -2311,12 +2311,12 @@ export class Helicopter extends AirUnit { contextActionSet.addContextAction( this, "land-at-point", - "Land here", + "Land at location", "Click on a point to land there", olIconsLandAtPoint, ( units: Unit[], - targetUnit: Unit | null, + _, targetPosition: LatLng | null ) => { if (targetPosition) @@ -2372,8 +2372,8 @@ export class GroundUnit extends Unit { faPeopleGroup, ( units: Unit[], - targetUnit: Unit | null, - targetPosition: LatLng | null + _1, + _2 ) => { getApp().getUnitsManager().createGroup(units); }, @@ -2426,7 +2426,7 @@ export class GroundUnit extends Unit { ( units: Unit[], targetUnit: Unit | null, - targetPosition: LatLng | null + _ ) => { if (targetUnit) getApp().getUnitsManager().attackUnit(targetUnit.ID, units); @@ -2443,7 +2443,7 @@ export class GroundUnit extends Unit { faLocationCrosshairs, ( units: Unit[], - targetUnit: Unit | null, + _, targetPosition: LatLng | null ) => { if (targetPosition) @@ -2458,7 +2458,7 @@ export class GroundUnit extends Unit { olButtonsContextSimulateFireFight, ( units: Unit[], - targetUnit: Unit | null, + _, targetPosition: LatLng | null ) => { if (targetPosition) @@ -2577,8 +2577,8 @@ export class NavyUnit extends Unit { faQuestionCircle, ( units: Unit[], - targetUnit: Unit | null, - targetPosition: LatLng | null + _1, + _2 ) => { getApp().getUnitsManager().createGroup(units); }, @@ -2606,7 +2606,7 @@ export class NavyUnit extends Unit { ( units: Unit[], targetUnit: Unit | null, - targetPosition: LatLng | null + _ ) => { if (targetUnit) getApp().getUnitsManager().attackUnit(targetUnit.ID, units); @@ -2622,7 +2622,7 @@ export class NavyUnit extends Unit { faQuestionCircle, ( units: Unit[], - targetUnit: Unit | null, + _, targetPosition: LatLng | null ) => { if (targetPosition) diff --git a/frontend/react/src/unit/unitsmanager.ts b/frontend/react/src/unit/unitsmanager.ts index 71e138fc..33ac3d71 100644 --- a/frontend/react/src/unit/unitsmanager.ts +++ b/frontend/react/src/unit/unitsmanager.ts @@ -20,6 +20,7 @@ import { import { CoalitionPolygon } from "../map/coalitionarea/coalitionpolygon"; import { groundUnitDatabase } from "./databases/groundunitdatabase"; import { + CONTEXT_ACTION, DELETE_CYCLE_TIME, DELETE_SLOW_THRESHOLD, DataIndexes, @@ -1953,6 +1954,7 @@ export class UnitsManager { #onUnitSelection(unit: Unit) { if (this.getSelectedUnits().length > 0) { + getApp().getMap().setState(CONTEXT_ACTION); /* Disable the firing of the selection event for a certain amount of time. This avoids firing many events if many units are selected */ if (!this.#selectionEventDisabled) { window.setTimeout(() => {