mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
832 lines
33 KiB
TypeScript
832 lines
33 KiB
TypeScript
import * as L from "leaflet"
|
|
import { getApp } from "..";
|
|
import { BoxSelect } from "./boxselect";
|
|
import { MapContextMenu } from "../contextmenus/mapcontextmenu";
|
|
import { UnitContextMenu } from "../contextmenus/unitcontextmenu";
|
|
import { AirbaseContextMenu } from "../contextmenus/airbasecontextmenu";
|
|
import { Dropdown } from "../controls/dropdown";
|
|
import { Airbase } from "../mission/airbase";
|
|
import { Unit } from "../unit/unit";
|
|
import { bearing, createCheckboxOption } from "../other/utils";
|
|
import { DestinationPreviewMarker } from "./markers/destinationpreviewmarker";
|
|
import { TemporaryUnitMarker } from "./markers/temporaryunitmarker";
|
|
import { ClickableMiniMap } from "./clickableminimap";
|
|
import { SVGInjector } from '@tanem/svg-injector'
|
|
import { mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTooltips, MOVE_UNIT, SHOW_CONTACT_LINES, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, visibilityControlsTypes, SHOW_UNIT_LABELS } from "../constants/constants";
|
|
import { TargetMarker } from "./markers/targetmarker";
|
|
import { CoalitionArea } from "./coalitionarea/coalitionarea";
|
|
import { CoalitionAreaContextMenu } from "../contextmenus/coalitionareacontextmenu";
|
|
import { DrawingCursor } from "./coalitionarea/drawingcursor";
|
|
import { AirbaseSpawnContextMenu } from "../contextmenus/airbasespawnmenu";
|
|
import { Popup } from "../popups/popup";
|
|
import { GestureHandling } from "leaflet-gesture-handling";
|
|
import { TouchBoxSelect } from "./touchboxselect";
|
|
|
|
var hasTouchScreen = false;
|
|
if ("maxTouchPoints" in navigator)
|
|
hasTouchScreen = navigator.maxTouchPoints > 0;
|
|
|
|
if (hasTouchScreen)
|
|
L.Map.addInitHook('addHandler', 'boxSelect', TouchBoxSelect);
|
|
else
|
|
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
|
|
|
|
L.Map.addInitHook("addHandler", "gestureHandling", GestureHandling);
|
|
|
|
// TODO would be nice to convert to ts - yes
|
|
require("../../public/javascripts/leaflet.nauticscale.js")
|
|
require("../../public/javascripts/L.Path.Drag.js")
|
|
|
|
export class Map extends L.Map {
|
|
#ID: string;
|
|
#state: string;
|
|
#layer: L.TileLayer | null = null;
|
|
#preventLeftClick: boolean = false;
|
|
#leftClickTimer: number = 0;
|
|
#deafultPanDelta: number = 100;
|
|
#panInterval: number | null = null;
|
|
#panLeft: boolean = false;
|
|
#panRight: boolean = false;
|
|
#panUp: boolean = false;
|
|
#panDown: boolean = false;
|
|
#lastMousePosition: L.Point = new L.Point(0, 0);
|
|
#shiftKey: boolean = false;
|
|
#ctrlKey: boolean = false;
|
|
#centerUnit: Unit | null = null;
|
|
#miniMap: ClickableMiniMap | null = null;
|
|
#miniMapLayerGroup: L.LayerGroup;
|
|
#temporaryMarkers: TemporaryUnitMarker[] = [];
|
|
#selecting: boolean = false;
|
|
#isZooming: boolean = false;
|
|
|
|
#destinationGroupRotation: number = 0;
|
|
#computeDestinationRotation: boolean = false;
|
|
#destinationRotationCenter: L.LatLng | null = null;
|
|
#coalitionAreas: CoalitionArea[] = [];
|
|
|
|
#targetCursor: TargetMarker = new TargetMarker(new L.LatLng(0, 0), { interactive: false });
|
|
#destinationPreviewCursors: DestinationPreviewMarker[] = [];
|
|
#drawingCursor: DrawingCursor = new DrawingCursor();
|
|
#longPressHandled: boolean = false;
|
|
#longPressTimer: number = 0;
|
|
|
|
#mapContextMenu: MapContextMenu = new MapContextMenu("map-contextmenu");
|
|
#unitContextMenu: UnitContextMenu = new UnitContextMenu("unit-contextmenu");
|
|
#airbaseContextMenu: AirbaseContextMenu = new AirbaseContextMenu("airbase-contextmenu");
|
|
#airbaseSpawnMenu: AirbaseSpawnContextMenu = new AirbaseSpawnContextMenu("airbase-spawn-contextmenu");
|
|
#coalitionAreaContextMenu: CoalitionAreaContextMenu = new CoalitionAreaContextMenu("coalition-area-contextmenu");
|
|
|
|
#mapSourceDropdown: Dropdown;
|
|
#mapVisibilityOptionsDropdown: Dropdown;
|
|
#optionButtons: { [key: string]: HTMLButtonElement[] } = {}
|
|
#visibilityOptions: { [key: string]: boolean } = {}
|
|
#hiddenTypes: string[] = [];
|
|
|
|
/**
|
|
*
|
|
* @param ID - the ID of the HTML element which will contain the context menu
|
|
*/
|
|
constructor(ID: string){
|
|
/* Init the leaflet map */
|
|
super(ID, {
|
|
zoomSnap: 0,
|
|
zoomDelta: 0.25,
|
|
preferCanvas: true,
|
|
doubleClickZoom: false,
|
|
zoomControl: false,
|
|
boxZoom: false,
|
|
//@ts-ignore Needed because the boxSelect option is non-standard
|
|
boxSelect: true,
|
|
zoomAnimation: true,
|
|
maxBoundsViscosity: 1.0,
|
|
minZoom: 7,
|
|
keyboard: true,
|
|
keyboardPanDelta: 0,
|
|
gestureHandling: hasTouchScreen
|
|
});
|
|
this.setView([37.23, -115.8], 10);
|
|
|
|
this.#ID = ID;
|
|
|
|
this.setLayer(Object.keys(mapLayers)[0]);
|
|
|
|
/* Minimap */
|
|
var minimapLayer = new L.TileLayer(mapLayers[Object.keys(mapLayers)[0] as keyof typeof mapLayers].urlTemplate, { minZoom: 0, maxZoom: 13 });
|
|
this.#miniMapLayerGroup = new L.LayerGroup([minimapLayer]);
|
|
var miniMapPolyline = new L.Polyline(this.#getMinimapBoundaries(), { color: '#202831' });
|
|
miniMapPolyline.addTo(this.#miniMapLayerGroup);
|
|
|
|
/* Scale */
|
|
//@ts-ignore TODO more hacking because the module is provided as a pure javascript module only
|
|
L.control.scalenautic({ position: "topright", maxWidth: 300, nautic: true, metric: true, imperial: false }).addTo(this);
|
|
|
|
/* Map source dropdown */
|
|
this.#mapSourceDropdown = new Dropdown("map-type", (layerName: string) => this.setLayer(layerName), this.getLayers());
|
|
|
|
/* Visibility options dropdown */
|
|
this.#mapVisibilityOptionsDropdown = new Dropdown("map-visibility-options", (value: string) => { });
|
|
|
|
/* Init the state machine */
|
|
this.#state = IDLE;
|
|
|
|
/* Register event handles */
|
|
this.on("click", (e: any) => this.#onClick(e));
|
|
this.on("dblclick", (e: any) => this.#onDoubleClick(e));
|
|
this.on("zoomstart", (e: any) => this.#onZoomStart(e));
|
|
this.on("zoomend", (e: any) => this.#onZoomEnd(e));
|
|
this.on("drag", (e: any) => this.centerOnUnit(null));
|
|
this.on("contextmenu", (e: any) => this.#onContextMenu(e));
|
|
this.on('selectionstart', (e: any) => this.#onSelectionStart(e));
|
|
this.on('selectionend', (e: any) => this.#onSelectionEnd(e));
|
|
this.on('mousedown', (e: any) => this.#onMouseDown(e));
|
|
this.on('mouseup', (e: any) => this.#onMouseUp(e));
|
|
this.on('mousemove', (e: any) => this.#onMouseMove(e));
|
|
this.on('keydown', (e: any) => this.#onKeyDown(e));
|
|
this.on('keyup', (e: any) => this.#onKeyUp(e));
|
|
|
|
/* Event listeners */
|
|
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
|
|
const el = ev.detail._element;
|
|
el?.classList.toggle("off");
|
|
this.setHiddenType(ev.detail.coalition, !el?.classList.contains("off"));
|
|
Object.values(getApp().getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
|
|
});
|
|
|
|
document.addEventListener("toggleMarkerVisibility", (ev: CustomEventInit) => {
|
|
const el = ev.detail._element;
|
|
el?.classList.toggle("off");
|
|
ev.detail.types.forEach((type: string) => this.setHiddenType(type, !el?.classList.contains("off")));
|
|
Object.values(getApp().getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
|
|
|
|
if (ev.detail.types.includes("airbase")) {
|
|
Object.values(getApp().getMissionManager().getAirbases()).forEach((airbase: Airbase) => {
|
|
if (el?.classList.contains("off"))
|
|
airbase.removeFrom(this);
|
|
else
|
|
airbase.addTo(this);
|
|
})
|
|
}
|
|
});
|
|
|
|
document.addEventListener("toggleCoalitionAreaDraw", (ev: CustomEventInit) => {
|
|
this.getMapContextMenu().hide();
|
|
this.deselectAllCoalitionAreas();
|
|
if (ev.detail?.type == "polygon") {
|
|
if (this.getState() !== COALITIONAREA_DRAW_POLYGON)
|
|
this.setState(COALITIONAREA_DRAW_POLYGON);
|
|
else
|
|
this.setState(IDLE);
|
|
}
|
|
});
|
|
|
|
document.addEventListener("unitUpdated", (ev: CustomEvent) => {
|
|
if (this.#centerUnit != null && ev.detail == this.#centerUnit)
|
|
this.#panToUnit(this.#centerUnit);
|
|
});
|
|
|
|
document.addEventListener("mapVisibilityOptionsChanged", () => {
|
|
this.getContainer().toggleAttribute("data-hide-labels", !this.getVisibilityOptions()[SHOW_UNIT_LABELS]);
|
|
});
|
|
|
|
/* Pan interval */
|
|
this.#panInterval = window.setInterval(() => {
|
|
if (this.#panUp || this.#panDown || this.#panRight || this.#panLeft)
|
|
this.panBy(new L.Point(((this.#panLeft ? -1 : 0) + (this.#panRight ? 1 : 0)) * this.#deafultPanDelta,
|
|
((this.#panUp ? -1 : 0) + (this.#panDown ? 1 : 0)) * this.#deafultPanDelta));
|
|
}, 20);
|
|
|
|
/* Option buttons */
|
|
this.#optionButtons["visibility"] = visibilityControls.map((option: string, index: number) => {
|
|
var typesArrayString = `"${visibilityControlsTypes[index][0]}"`;
|
|
visibilityControlsTypes[index].forEach((type: string, idx: number) => { if (idx > 0) typesArrayString = `${typesArrayString}, "${type}"` });
|
|
return this.#createOptionButton(option, `visibility/${option.toLowerCase()}.svg`, visibilityControlsTooltips[index], "toggleMarkerVisibility", `{"types": [${typesArrayString}]}`);
|
|
});
|
|
document.querySelector("#unit-visibility-control")?.append(...this.#optionButtons["visibility"]);
|
|
|
|
/* Create the checkboxes to select the advanced visibility options */
|
|
this.addVisibilityOption(SHOW_CONTACT_LINES, false);
|
|
this.addVisibilityOption(HIDE_GROUP_MEMBERS, true);
|
|
this.addVisibilityOption(SHOW_UNIT_PATHS, true);
|
|
this.addVisibilityOption(SHOW_UNIT_TARGETS, true);
|
|
this.addVisibilityOption(SHOW_UNIT_LABELS, true);
|
|
}
|
|
|
|
addVisibilityOption(option: string, defaultValue: boolean) {
|
|
this.#visibilityOptions[option] = defaultValue;
|
|
this.#mapVisibilityOptionsDropdown.addOptionElement(createCheckboxOption(option, option, defaultValue, (ev: any) => { this.#setVisibilityOption(option, ev); }));
|
|
}
|
|
|
|
setLayer(layerName: string) {
|
|
if (this.#layer != null)
|
|
this.removeLayer(this.#layer)
|
|
|
|
if (layerName in mapLayers) {
|
|
const layerData = mapLayers[layerName as keyof typeof mapLayers];
|
|
var options: L.TileLayerOptions = {
|
|
attribution: layerData.attribution,
|
|
minZoom: layerData.minZoom,
|
|
maxZoom: layerData.maxZoom
|
|
};
|
|
this.#layer = new L.TileLayer(layerData.urlTemplate, options);
|
|
}
|
|
|
|
this.#layer?.addTo(this);
|
|
}
|
|
|
|
getLayers() {
|
|
return Object.keys(mapLayers);
|
|
}
|
|
|
|
/* State machine */
|
|
setState(state: string) {
|
|
this.#state = state;
|
|
this.#updateCursor();
|
|
|
|
/* Operations to perform if you are NOT in a state */
|
|
if (this.#state !== COALITIONAREA_DRAW_POLYGON) {
|
|
this.#deselectCoalitionAreas();
|
|
}
|
|
|
|
/* Operations to perform if you ARE in a state */
|
|
if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
|
this.#coalitionAreas.push(new CoalitionArea([]));
|
|
this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
|
|
}
|
|
document.dispatchEvent(new CustomEvent("mapStateChanged"));
|
|
}
|
|
|
|
getState() {
|
|
return this.#state;
|
|
}
|
|
|
|
deselectAllCoalitionAreas() {
|
|
this.#coalitionAreas.forEach((coalitionArea: CoalitionArea) => coalitionArea.setSelected(false));
|
|
}
|
|
|
|
deleteCoalitionArea(coalitionArea: CoalitionArea) {
|
|
if (this.#coalitionAreas.includes(coalitionArea))
|
|
this.#coalitionAreas.splice(this.#coalitionAreas.indexOf(coalitionArea), 1);
|
|
if (this.hasLayer(coalitionArea))
|
|
this.removeLayer(coalitionArea);
|
|
}
|
|
|
|
setHiddenType(key: string, value: boolean) {
|
|
if (value) {
|
|
if (this.#hiddenTypes.includes(key))
|
|
delete this.#hiddenTypes[this.#hiddenTypes.indexOf(key)];
|
|
}
|
|
else {
|
|
this.#hiddenTypes.push(key);
|
|
}
|
|
Object.values(getApp().getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
|
|
}
|
|
|
|
getHiddenTypes() {
|
|
return this.#hiddenTypes;
|
|
}
|
|
|
|
/* Context Menus */
|
|
hideAllContextMenus() {
|
|
this.hideMapContextMenu();
|
|
this.hideUnitContextMenu();
|
|
this.hideAirbaseContextMenu();
|
|
this.hideAirbaseSpawnMenu();
|
|
this.hideCoalitionAreaContextMenu();
|
|
}
|
|
|
|
showMapContextMenu(x: number, y: number, latlng: L.LatLng) {
|
|
this.hideAllContextMenus();
|
|
this.#mapContextMenu.show(x, y, latlng);
|
|
document.dispatchEvent(new CustomEvent("mapContextMenu"));
|
|
}
|
|
|
|
hideMapContextMenu() {
|
|
this.#mapContextMenu.hide();
|
|
document.dispatchEvent(new CustomEvent("mapContextMenu"));
|
|
}
|
|
|
|
getMapContextMenu() {
|
|
return this.#mapContextMenu;
|
|
}
|
|
|
|
showUnitContextMenu(x: number, y: number, latlng: L.LatLng) {
|
|
this.hideAllContextMenus();
|
|
this.#unitContextMenu.show(x, y, latlng);
|
|
}
|
|
|
|
getUnitContextMenu() {
|
|
return this.#unitContextMenu;
|
|
}
|
|
|
|
hideUnitContextMenu() {
|
|
this.#unitContextMenu.hide();
|
|
}
|
|
|
|
showAirbaseContextMenu(x: number, y: number, latlng: L.LatLng, airbase: Airbase) {
|
|
this.hideAllContextMenus();
|
|
this.#airbaseContextMenu.show(x, y, latlng);
|
|
this.#airbaseContextMenu.setAirbase(airbase);
|
|
}
|
|
|
|
getAirbaseContextMenu() {
|
|
return this.#airbaseContextMenu;
|
|
}
|
|
|
|
hideAirbaseContextMenu() {
|
|
this.#airbaseContextMenu.hide();
|
|
}
|
|
|
|
showAirbaseSpawnMenu(x: number, y: number, latlng: L.LatLng, airbase: Airbase) {
|
|
this.hideAllContextMenus();
|
|
this.#airbaseSpawnMenu.show(x, y);
|
|
this.#airbaseSpawnMenu.setAirbase(airbase);
|
|
}
|
|
|
|
getAirbaseSpawnMenu() {
|
|
return this.#airbaseSpawnMenu;
|
|
}
|
|
|
|
hideAirbaseSpawnMenu() {
|
|
this.#airbaseSpawnMenu.hide();
|
|
}
|
|
|
|
showCoalitionAreaContextMenu(x: number, y: number, latlng: L.LatLng, coalitionArea: CoalitionArea) {
|
|
this.hideAllContextMenus();
|
|
this.#coalitionAreaContextMenu.show(x, y, latlng);
|
|
this.#coalitionAreaContextMenu.setCoalitionArea(coalitionArea);
|
|
}
|
|
|
|
getCoalitionAreaContextMenu() {
|
|
return this.#coalitionAreaContextMenu;
|
|
}
|
|
|
|
hideCoalitionAreaContextMenu() {
|
|
this.#coalitionAreaContextMenu.hide();
|
|
}
|
|
|
|
isZooming() {
|
|
return this.#isZooming;
|
|
}
|
|
|
|
/* Mouse coordinates */
|
|
getMousePosition() {
|
|
return this.#lastMousePosition;
|
|
}
|
|
|
|
getMouseCoordinates() {
|
|
return this.containerPointToLatLng(this.#lastMousePosition);
|
|
}
|
|
|
|
/* Spawn from air base */
|
|
spawnFromAirbase(e: any) {
|
|
//this.#aircraftSpawnMenu(e);
|
|
}
|
|
|
|
centerOnUnit(ID: number | null) {
|
|
if (ID != null) {
|
|
this.options.scrollWheelZoom = 'center';
|
|
this.#centerUnit = getApp().getUnitsManager().getUnitByID(ID);
|
|
}
|
|
else {
|
|
this.options.scrollWheelZoom = undefined;
|
|
this.#centerUnit = null;
|
|
}
|
|
}
|
|
|
|
getCenterUnit() {
|
|
return this.#centerUnit;
|
|
}
|
|
|
|
setTheatre(theatre: string) {
|
|
var bounds = new L.LatLngBounds([-90, -180], [90, 180]);
|
|
var miniMapZoom = 5;
|
|
if (theatre in mapBounds) {
|
|
bounds = mapBounds[theatre as keyof typeof mapBounds].bounds;
|
|
miniMapZoom = mapBounds[theatre as keyof typeof mapBounds].zoom;
|
|
}
|
|
|
|
this.setView(bounds.getCenter(), 8);
|
|
//this.setMaxBounds(bounds);
|
|
|
|
if (this.#miniMap)
|
|
this.#miniMap.remove();
|
|
|
|
//@ts-ignore // Needed because some of the inputs are wrong in the original module interface
|
|
this.#miniMap = new ClickableMiniMap(this.#miniMapLayerGroup, { position: "topright", width: 192 * 1.5, height: 108 * 1.5, zoomLevelFixed: miniMapZoom, centerFixed: bounds.getCenter() }).addTo(this);
|
|
this.#miniMap.disableInteractivity();
|
|
this.#miniMap.getMap().on("click", (e: any) => {
|
|
if (this.#miniMap)
|
|
this.setView(e.latlng);
|
|
})
|
|
}
|
|
|
|
getMiniMapLayerGroup() {
|
|
return this.#miniMapLayerGroup;
|
|
}
|
|
|
|
handleMapPanning(e: any) {
|
|
if (e.type === "keyup") {
|
|
switch (e.code) {
|
|
case "KeyA":
|
|
case "ArrowLeft":
|
|
this.#panLeft = false;
|
|
break;
|
|
case "KeyD":
|
|
case "ArrowRight":
|
|
this.#panRight = false;
|
|
break;
|
|
case "KeyW":
|
|
case "ArrowUp":
|
|
this.#panUp = false;
|
|
break;
|
|
case "KeyS":
|
|
case "ArrowDown":
|
|
this.#panDown = false;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch (e.code) {
|
|
case 'KeyA':
|
|
case 'ArrowLeft':
|
|
this.#panLeft = true;
|
|
break;
|
|
case 'KeyD':
|
|
case 'ArrowRight':
|
|
this.#panRight = true;
|
|
break;
|
|
case 'KeyW':
|
|
case 'ArrowUp':
|
|
this.#panUp = true;
|
|
break;
|
|
case 'KeyS':
|
|
case 'ArrowDown':
|
|
this.#panDown = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
addTemporaryMarker(latlng: L.LatLng, name: string, coalition: string, commandHash?: string) {
|
|
var marker = new TemporaryUnitMarker(latlng, name, coalition, commandHash);
|
|
marker.addTo(this);
|
|
this.#temporaryMarkers.push(marker);
|
|
return marker;
|
|
}
|
|
|
|
getSelectedCoalitionArea() {
|
|
return this.#coalitionAreas.find((area: CoalitionArea) => { return area.getSelected() });
|
|
}
|
|
|
|
bringCoalitionAreaToBack(coalitionArea: CoalitionArea) {
|
|
coalitionArea.bringToBack();
|
|
this.#coalitionAreas.splice(this.#coalitionAreas.indexOf(coalitionArea), 1);
|
|
this.#coalitionAreas.unshift(coalitionArea);
|
|
}
|
|
|
|
getVisibilityOptions() {
|
|
return this.#visibilityOptions;
|
|
}
|
|
|
|
/* Event handlers */
|
|
#onClick(e: any) {
|
|
if (!this.#preventLeftClick) {
|
|
this.hideAllContextMenus();
|
|
if (this.#state === IDLE) {
|
|
this.deselectAllCoalitionAreas();
|
|
}
|
|
else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
|
if (this.getSelectedCoalitionArea()?.getEditing()) {
|
|
this.getSelectedCoalitionArea()?.addTemporaryLatLng(e.latlng);
|
|
}
|
|
else {
|
|
this.deselectAllCoalitionAreas();
|
|
}
|
|
}
|
|
else {
|
|
this.setState(IDLE);
|
|
getApp().getUnitsManager().deselectAllUnits();
|
|
}
|
|
}
|
|
}
|
|
|
|
#onDoubleClick(e: any) {
|
|
|
|
}
|
|
|
|
#onContextMenu(e: any) {
|
|
/* A long press will show the point action context menu */
|
|
window.clearInterval(this.#longPressTimer);
|
|
if (this.#longPressHandled) {
|
|
this.#longPressHandled = false;
|
|
return;
|
|
}
|
|
|
|
this.hideMapContextMenu();
|
|
if (this.#state === IDLE) {
|
|
if (this.#state == IDLE) {
|
|
this.showMapContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
|
|
var clickedCoalitionArea = null;
|
|
|
|
/* Coalition areas are ordered in the #coalitionAreas array according to their zindex. Select the upper one */
|
|
for (let coalitionArea of this.#coalitionAreas) {
|
|
if (coalitionArea.getBounds().contains(e.latlng)) {
|
|
if (coalitionArea.getSelected())
|
|
clickedCoalitionArea = coalitionArea;
|
|
else
|
|
this.getMapContextMenu().setCoalitionArea(coalitionArea);
|
|
}
|
|
}
|
|
if (clickedCoalitionArea)
|
|
this.showCoalitionAreaContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng, clickedCoalitionArea);
|
|
}
|
|
}
|
|
else if (this.#state === MOVE_UNIT) {
|
|
if (!e.originalEvent.ctrlKey) {
|
|
getApp().getUnitsManager().selectedUnitsClearDestinations();
|
|
}
|
|
getApp().getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation)
|
|
|
|
this.#destinationGroupRotation = 0;
|
|
this.#destinationRotationCenter = null;
|
|
this.#computeDestinationRotation = false;
|
|
}
|
|
else {
|
|
this.setState(IDLE);
|
|
}
|
|
}
|
|
|
|
#onSelectionStart(e: any) {
|
|
this.#selecting = true;
|
|
this.#updateCursor();
|
|
}
|
|
|
|
#onSelectionEnd(e: any) {
|
|
this.#selecting = false;
|
|
clearTimeout(this.#leftClickTimer);
|
|
this.#preventLeftClick = true;
|
|
this.#leftClickTimer = window.setTimeout(() => {
|
|
this.#preventLeftClick = false;
|
|
}, 200);
|
|
getApp().getUnitsManager().selectFromBounds(e.selectionBounds);
|
|
this.#updateCursor();
|
|
}
|
|
|
|
#onMouseDown(e: any) {
|
|
this.hideAllContextMenus();
|
|
|
|
if (this.#state == MOVE_UNIT) {
|
|
this.#destinationGroupRotation = 0;
|
|
this.#destinationRotationCenter = null;
|
|
this.#computeDestinationRotation = false;
|
|
if (e.originalEvent.button == 2) {
|
|
this.#computeDestinationRotation = true;
|
|
this.#destinationRotationCenter = this.getMouseCoordinates();
|
|
}
|
|
}
|
|
|
|
this.#longPressTimer = window.setTimeout(() => {
|
|
if (e.originalEvent.button != 2 || e.originalEvent.ctrlKey)
|
|
return;
|
|
|
|
this.hideMapContextMenu();
|
|
this.#longPressHandled = true;
|
|
|
|
var options: { [key: string]: { text: string, tooltip: string } } = {};
|
|
const selectedUnits = getApp().getUnitsManager().getSelectedUnits();
|
|
const selectedUnitTypes = getApp().getUnitsManager().getSelectedUnitsCategories();
|
|
|
|
if (selectedUnitTypes.length === 1 && ["Aircraft", "Helicopter"].includes(selectedUnitTypes[0])) {
|
|
if (selectedUnits.every((unit: Unit) => { return unit.canFulfillRole(["CAS", "Strike"]) })) {
|
|
options["bomb"] = { text: "Precision bombing", tooltip: "Precision bombing of a specific point" };
|
|
options["carpet-bomb"] = { text: "Carpet bombing", tooltip: "Carpet bombing close to a point" };
|
|
} else {
|
|
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Selected units can not perform point actions.`);
|
|
}
|
|
}
|
|
else if (selectedUnitTypes.length === 1 && ["GroundUnit", "NavyUnit"].includes(selectedUnitTypes[0])) {
|
|
if (selectedUnits.every((unit: Unit) => { return ["AAA", "Gun Artillery", "Rocket Artillery", "Infantry", "IFV", "Tank", "Cruiser", "Destroyer", "Frigate"].includes(unit.getType()) })) {
|
|
options["fire-at-area"] = { text: "Fire at area", tooltip: "Fire at a large area" };
|
|
options["simulate-fire-fight"] = { text: "Simulate fire fight", tooltip: "Simulate a fire fight by shooting randomly in a certain large area" };
|
|
}
|
|
else
|
|
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Selected units can not perform point actions.`);
|
|
}
|
|
else if(selectedUnitTypes.length > 1) {
|
|
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Multiple unit types selected, no common actions available.`);
|
|
}
|
|
|
|
if (Object.keys(options).length > 0) {
|
|
this.showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
|
|
this.getUnitContextMenu().setOptions(options, (option: string) => {
|
|
this.hideUnitContextMenu();
|
|
if (option === "bomb") {
|
|
getApp().getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
|
|
getApp().getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates());
|
|
}
|
|
else if (option === "carpet-bomb") {
|
|
getApp().getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
|
|
getApp().getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates());
|
|
}
|
|
else if (option === "fire-at-area") {
|
|
getApp().getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
|
|
getApp().getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates());
|
|
}
|
|
else if (option === "simulate-fire-fight") {
|
|
getApp().getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
|
|
getApp().getUnitsManager().selectedUnitsSimulateFireFight(this.getMouseCoordinates());
|
|
}
|
|
});
|
|
}
|
|
}, 150);
|
|
this.#longPressHandled = false;
|
|
}
|
|
|
|
#onMouseUp(e: any) {
|
|
}
|
|
|
|
#onMouseMove(e: any) {
|
|
this.#lastMousePosition.x = e.originalEvent.x;
|
|
this.#lastMousePosition.y = e.originalEvent.y;
|
|
|
|
this.#updateCursor();
|
|
|
|
if (this.#state === MOVE_UNIT) {
|
|
/* Update the position of the destination cursors depeding on mouse rotation */
|
|
if (this.#computeDestinationRotation && this.#destinationRotationCenter != null)
|
|
this.#destinationGroupRotation = -bearing(this.#destinationRotationCenter.lat, this.#destinationRotationCenter.lng, this.getMouseCoordinates().lat, this.getMouseCoordinates().lng);
|
|
this.#updateDestinationCursors();
|
|
}
|
|
else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
|
this.#drawingCursor.setLatLng(e.latlng);
|
|
/* Update the polygon being drawn with the current position of the mouse cursor */
|
|
this.getSelectedCoalitionArea()?.moveActiveVertex(e.latlng);
|
|
}
|
|
}
|
|
|
|
#onKeyDown(e: any) {
|
|
this.#shiftKey = e.originalEvent.shiftKey;
|
|
this.#ctrlKey = e.originalEvent.ctrlKey;
|
|
this.#updateCursor();
|
|
this.#updateDestinationCursors();
|
|
}
|
|
|
|
#onKeyUp(e: any) {
|
|
this.#shiftKey = e.originalEvent.shiftKey;
|
|
this.#ctrlKey = e.originalEvent.ctrlKey;
|
|
this.#updateCursor();
|
|
this.#updateDestinationCursors();
|
|
}
|
|
|
|
#onZoomStart(e: any) {
|
|
if (this.#centerUnit != null)
|
|
this.#panToUnit(this.#centerUnit);
|
|
this.#isZooming = true;
|
|
}
|
|
|
|
#onZoomEnd(e: any) {
|
|
this.#isZooming = false;
|
|
}
|
|
|
|
|
|
#panToUnit(unit: Unit) {
|
|
var unitPosition = new L.LatLng(unit.getPosition().lat, unit.getPosition().lng);
|
|
this.setView(unitPosition, this.getZoom(), { animate: false });
|
|
}
|
|
|
|
#getMinimapBoundaries() {
|
|
/* Draw the limits of the maps in the minimap*/
|
|
return minimapBoundaries;
|
|
}
|
|
|
|
#createOptionButton(value: string, url: string, title: string, callback: string, argument: string) {
|
|
var button = document.createElement("button");
|
|
const img = document.createElement("img");
|
|
img.src = `/resources/theme/images/buttons/${url}`;
|
|
img.onload = () => SVGInjector(img);
|
|
button.title = title;
|
|
button.value = value;
|
|
button.appendChild(img);
|
|
button.setAttribute("data-on-click", callback);
|
|
button.setAttribute("data-on-click-params", argument);
|
|
return button;
|
|
}
|
|
|
|
#deselectCoalitionAreas() {
|
|
this.getSelectedCoalitionArea()?.setSelected(false);
|
|
}
|
|
|
|
/* Cursors */
|
|
#showDefaultCursor() {
|
|
document.getElementById(this.#ID)?.classList.remove("hidden-cursor");
|
|
}
|
|
|
|
#hideDefaultCursor() {
|
|
document.getElementById(this.#ID)?.classList.add("hidden-cursor");
|
|
}
|
|
|
|
#showDestinationCursors() {
|
|
const singleCursor = !this.#shiftKey;
|
|
const selectedUnitsCount = getApp().getUnitsManager().getSelectedUnits({ excludeHumans: false, onlyOnePerGroup: true }).length;
|
|
if (selectedUnitsCount > 0) {
|
|
if (singleCursor && this.#destinationPreviewCursors.length != 1) {
|
|
this.#hideDestinationCursors();
|
|
var marker = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false });
|
|
marker.addTo(this);
|
|
this.#destinationPreviewCursors = [marker];
|
|
}
|
|
else if (!singleCursor) {
|
|
while (this.#destinationPreviewCursors.length > selectedUnitsCount) {
|
|
this.removeLayer(this.#destinationPreviewCursors[0]);
|
|
this.#destinationPreviewCursors.splice(0, 1);
|
|
}
|
|
|
|
while (this.#destinationPreviewCursors.length < selectedUnitsCount) {
|
|
var cursor = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false });
|
|
cursor.addTo(this);
|
|
this.#destinationPreviewCursors.push(cursor);
|
|
}
|
|
|
|
this.#updateDestinationCursors();
|
|
}
|
|
}
|
|
}
|
|
|
|
#updateDestinationCursors() {
|
|
const groupLatLng = this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : this.getMouseCoordinates();
|
|
if (this.#destinationPreviewCursors.length == 1)
|
|
this.#destinationPreviewCursors[0].setLatLng(this.getMouseCoordinates());
|
|
else {
|
|
Object.values(getApp().getUnitsManager().selectedUnitsComputeGroupDestination(groupLatLng, this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => {
|
|
if (idx < this.#destinationPreviewCursors.length)
|
|
this.#destinationPreviewCursors[idx].setLatLng(this.#shiftKey ? latlng : this.getMouseCoordinates());
|
|
})
|
|
};
|
|
}
|
|
|
|
#hideDestinationCursors() {
|
|
/* Remove all the destination cursors */
|
|
this.#destinationPreviewCursors.forEach((marker: L.Marker) => {
|
|
this.removeLayer(marker);
|
|
})
|
|
this.#destinationPreviewCursors = [];
|
|
|
|
/* Reset the variables used to compute the rotation of the group cursors */
|
|
this.#destinationGroupRotation = 0;
|
|
this.#computeDestinationRotation = false;
|
|
this.#destinationRotationCenter = null;
|
|
}
|
|
|
|
#showTargetCursor() {
|
|
this.#hideTargetCursor();
|
|
this.#targetCursor.addTo(this);
|
|
}
|
|
|
|
#hideTargetCursor() {
|
|
this.#targetCursor.setLatLng(new L.LatLng(0, 0));
|
|
this.removeLayer(this.#targetCursor);
|
|
}
|
|
|
|
#showDrawingCursor() {
|
|
this.#hideDefaultCursor();
|
|
if (!this.hasLayer(this.#drawingCursor))
|
|
this.#drawingCursor.addTo(this);
|
|
}
|
|
|
|
#hideDrawingCursor() {
|
|
this.#drawingCursor.setLatLng(new L.LatLng(0, 0));
|
|
if (this.hasLayer(this.#drawingCursor))
|
|
this.#drawingCursor.removeFrom(this);
|
|
}
|
|
|
|
#updateCursor() {
|
|
/* If the ctrl key is being pressed or we are performing an area selection, show the default cursor */
|
|
if (this.#ctrlKey || this.#selecting) {
|
|
/* Hide all non default cursors */
|
|
this.#hideDestinationCursors();
|
|
this.#hideTargetCursor();
|
|
this.#hideDrawingCursor();
|
|
|
|
this.#showDefaultCursor();
|
|
} else {
|
|
/* Hide all the unnecessary cursors depending on the active state */
|
|
if (this.#state !== IDLE) this.#hideDefaultCursor();
|
|
if (this.#state !== MOVE_UNIT) this.#hideDestinationCursors();
|
|
//if (![BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#hideTargetCursor();
|
|
if (this.#state !== COALITIONAREA_DRAW_POLYGON) this.#hideDrawingCursor();
|
|
|
|
/* Show the active cursor depending on the active state */
|
|
if (this.#state === IDLE) this.#showDefaultCursor();
|
|
else if (this.#state === MOVE_UNIT) this.#showDestinationCursors();
|
|
//else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#showTargetCursor();
|
|
else if (this.#state === COALITIONAREA_DRAW_POLYGON) this.#showDrawingCursor();
|
|
}
|
|
}
|
|
|
|
#setVisibilityOption(option: string, ev: any) {
|
|
this.#visibilityOptions[option] = ev.currentTarget.checked;
|
|
document.dispatchEvent(new CustomEvent("mapVisibilityOptionsChanged"));
|
|
}
|
|
}
|
|
|