Added ability to draw coalition areas (WIP)

This commit is contained in:
Davide Passoni
2024-07-26 17:00:09 +02:00
parent b993786301
commit 2d90c359f7
575 changed files with 300 additions and 24084 deletions

View File

@@ -259,7 +259,8 @@ export const defaultMapLayers = {};
export const IDLE = "Idle";
export const SPAWN_UNIT = "Spawn unit";
export const CONTEXT_ACTION = "Context action";
export const COALITIONAREA_DRAW_POLYGON = "Draw Coalition Area";
export const COALITIONAREA_DRAW_POLYGON = "Draw Coalition Area polygon";
export const COALITIONAREA_EDIT = "Edit Coalition Area";
export const IADSTypes = ["AAA", "SAM Site", "Radar (EWR)"];
export const IADSDensities: { [key: string]: number } = {

View File

@@ -31,7 +31,6 @@ export class CoalitionArea extends Polygon {
super(latlngs, options);
this.#setColors();
this.#registerCallbacks();
if (
[BLUE_COMMANDER, RED_COMMANDER].includes(
@@ -175,20 +174,4 @@ export class CoalitionArea extends Polygon {
});
}
}
#registerCallbacks() {
this.on("click", (e: any) => {
getApp().getMap().deselectAllCoalitionAreas();
if (!this.getSelected()) {
this.setSelected(true);
}
});
this.on("contextmenu", (e: any) => {
if (!this.getEditing()) {
getApp().getMap().deselectAllCoalitionAreas();
this.setSelected(true);
} else this.setEditing(false);
});
}
}

View File

@@ -1,71 +0,0 @@
import * as L from "leaflet";
export class DCSLayer extends L.TileLayer {
createTile(coords: L.Coords, done: L.DoneCallback) {
let newDone = (error?: Error, tile?: HTMLElement) => {
if (
error === null &&
tile !== undefined &&
!tile.classList.contains("filtered")
) {
// Create a canvas and set its width and height.
var canvas = document.createElement("canvas");
canvas.setAttribute("width", "256px");
canvas.setAttribute("height", "256px");
// Get the canvas drawing context, and draw the image to it.
var context = canvas.getContext("2d");
if (context) {
context.drawImage(
tile as CanvasImageSource,
0,
0,
canvas.width,
canvas.height
);
// Get the canvas image data.
var imageData = context.getImageData(
0,
0,
canvas.width,
canvas.height
);
// Create a function for preserving a specified colour.
var makeTransparent = function (
imageData: ImageData,
color: { r: number; g: number; b: number }
) {
// Get the pixel data from the source.
var data = imageData.data;
// Iterate through all the pixels.
for (var i = 0; i < data.length; i += 4) {
// Check if the current pixel should have preserved transparency. This simply compares whether the color we passed in is equivalent to our pixel data.
var convert =
data[i] > color.r - 5 &&
data[i] < color.r + 5 &&
data[i + 1] > color.g - 5 &&
data[i + 1] < color.g + 5 &&
data[i + 2] > color.b - 5 &&
data[i + 2] < color.b + 5;
// Either preserve the initial transparency or set the transparency to 0.
data[i + 3] = convert ? 100 : data[i + 3];
}
return imageData;
};
// Get the new pixel data and set it to the canvas context.
var newData = makeTransparent(imageData, { r: 26, g: 109, b: 127 });
context.putImageData(newData, 0, 0);
(tile as HTMLImageElement).src = canvas.toDataURL();
tile.classList.add("filtered");
}
} else {
return done(error, tile);
}
};
return super.createTile(coords, newDone);
}
}

View File

@@ -97,3 +97,24 @@
* {
font-weight: 600;
}
.ol-coalitionarea-handle-icon {
background-color: #FFFFFFAA;
width: 100%;
height: 100%;
border-radius: 999px;
}
.ol-coalitionarea-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;
}

View File

@@ -3,13 +3,7 @@ import { getApp } from "../olympusapp";
import { BoxSelect } from "./boxselect";
import { Airbase } from "../mission/airbase";
import { Unit } from "../unit/unit";
import {
bearing,
deg2rad,
getGroundElevation,
polyContains,
} from "../other/utils";
import { DestinationPreviewMarker } from "./markers/destinationpreviewmarker";
import { deg2rad, getGroundElevation, polyContains } from "../other/utils";
import { TemporaryUnitMarker } from "./markers/temporaryunitmarker";
import { ClickableMiniMap } from "./clickableminimap";
import {
@@ -23,21 +17,19 @@ import {
CONTEXT_ACTION,
MAP_OPTIONS_DEFAULTS,
MAP_HIDDEN_TYPES_DEFAULTS,
COALITIONAREA_EDIT,
} from "../constants/constants";
import { CoalitionArea } from "./coalitionarea/coalitionarea";
import { TouchBoxSelect } from "./touchboxselect";
import { DestinationPreviewHandle } from "./markers/destinationpreviewHandle";
import "./markers/stylesheets/airbase.css";
import "./markers/stylesheets/bullseye.css";
import "./markers/stylesheets/units.css";
// Temporary
import "./theme.css";
import { MapHiddenTypes, MapOptions } from "../types/types";
import { SpawnRequestTable } from "../interfaces";
import { ContextAction } from "../unit/contextaction";
/* Stylesheets */
import "./markers/stylesheets/airbase.css";
import "./markers/stylesheets/bullseye.css";
import "./markers/stylesheets/units.css";
import "./map.css";
L.Map.addInitHook("addHandler", "boxSelect", BoxSelect);
export class Map extends L.Map {
@@ -45,58 +37,68 @@ export class Map extends L.Map {
#options: MapOptions = MAP_OPTIONS_DEFAULTS;
#hiddenTypes: MapHiddenTypes = MAP_HIDDEN_TYPES_DEFAULTS;
#ID: string;
/* State machine */
#state: string;
/* Map layers */
#theatre: string = "";
#layer: L.TileLayer | L.LayerGroup | null = null;
#layerName: string = "";
#mapLayers: any = defaultMapLayers;
#mapMirrors: any = defaultMapMirrors;
#spawnRequestTable: SpawnRequestTable | null = null;
/* Inputs timers */
#mouseCooldownTimer: number = 0;
#shortPressTimer: number = 0;
#longPressTimer: number = 0;
#preventLeftClick: boolean = false;
#leftClickTimer: number = 0;
#deafultPanDelta: number = 100;
/* Camera keyboard panning control */
defaultPanDelta: 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);
#lastMouseCoordinates: L.LatLng = new L.LatLng(0, 0);
/* Keyboard state */
#isShiftKeyDown: boolean = false;
#shiftKey: boolean = false;
#centerUnit: Unit | null = null;
/* Center on unit target */
#centeredUnit: Unit | null = null;
/* Minimap */
#miniMap: ClickableMiniMap | null = null;
#miniMapLayerGroup: L.LayerGroup;
#miniMapPolyline: L.Polyline;
#temporaryMarkers: TemporaryUnitMarker[] = [];
#isSelecting: boolean = false;
/* Other state controls */
#isMouseOnCooldown: boolean = false;
#isZooming: boolean = false;
#isDragging: boolean = false;
#isMouseDown: boolean = false;
#lastMousePosition: L.Point = new L.Point(0, 0);
#lastMouseCoordinates: L.LatLng = new L.LatLng(0, 0);
#previousZoom: number = 0;
/* Camera control plugin */
#slaveDCSCamera: boolean = false;
#slaveDCSCameraAvailable: boolean = false;
#cameraControlTimer: number = 0;
#cameraControlPort: number = 3003;
#cameraControlMode: string = "map";
#coalitionAreas: CoalitionArea[] = [];
#mapLayers: any = defaultMapLayers;
#mapMirrors: any = defaultMapMirrors;
#layerName: string = "";
#cameraOptionsXmlHttp: XMLHttpRequest | null = null;
#bradcastPositionXmlHttp: XMLHttpRequest | null = null;
#cameraZoomRatio: number = 1.0;
/* Coalition areas drawing */
#coalitionAreas: CoalitionArea[] = [];
/* Unit context actions */
#contextAction: null | ContextAction = null;
#theatre: string = "";
#waitingForDoubleClick: boolean = false;
#doubleClickTimer: number = 0;
#longPressTimer: number = 0;
#isDragging: boolean = false;
/* Unit spawning */
#spawnRequestTable: SpawnRequestTable | null = null;
#temporaryMarkers: TemporaryUnitMarker[] = [];
/**
*
@@ -119,8 +121,6 @@ export class Map extends L.Map {
});
this.setView([37.23, -115.8], 10);
this.#ID = ID;
/* Minimap */
var minimapLayer = new L.TileLayer(
"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
@@ -134,24 +134,28 @@ export class Map extends L.Map {
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("zoom", (e: any) => this.#onZoom(e));
this.on("zoomend", (e: any) => this.#onZoomEnd(e));
this.on("drag", (e: any) => this.centerOnUnit(null));
this.on("dragstart", (e: any) => this.#onDragStart(e));
this.on("drag", (e: any) => this.centerOnUnit(null));
this.on("dragend", (e: any) => this.#onDragEnd(e));
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("dblclick", (e: any) => this.#onDoubleClick(e));
this.on("mouseup", (e: any) => this.#onMouseUp(e));
this.on("mousedown", (e: any) => this.#onMouseDown(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));
this.on("move", (e: any) => {
if (this.#slaveDCSCamera) this.#broadcastPosition();
this.#onMapMove(e);
});
L.DomEvent.on(this.getContainer(), "touchstart", this.#onMouseDown, this);
@@ -259,11 +263,11 @@ export class Map extends L.Map {
this.panBy(
new L.Point(
((this.#panLeft ? -1 : 0) + (this.#panRight ? 1 : 0)) *
this.#deafultPanDelta *
(this.#shiftKey ? 3 : 1),
this.defaultPanDelta *
(this.#isShiftKeyDown ? 3 : 1),
((this.#panUp ? -1 : 0) + (this.#panDown ? 1 : 0)) *
this.#deafultPanDelta *
(this.#shiftKey ? 3 : 1)
this.defaultPanDelta *
(this.#isShiftKeyDown ? 3 : 1)
)
);
}, 20);
@@ -368,6 +372,9 @@ export class Map extends L.Map {
/* Operations to perform if you are NOT in a state */
if (this.#state !== COALITIONAREA_DRAW_POLYGON) {
this.getSelectedCoalitionArea()?.setEditing(false);
}
if (this.#state !== COALITIONAREA_EDIT) {
this.#deselectSelectedCoalitionArea();
}
@@ -416,104 +423,6 @@ export class Map extends L.Map {
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 null; //this.#mapContextMenu;
}
showUnitContextMenu(
x: number | undefined = undefined,
y: number | undefined = undefined,
latlng: L.LatLng | undefined = undefined
) {
//this.hideAllContextMenus();
//this.#unitContextMenu.show(x, y, latlng);
}
getUnitContextMenu() {
return null; //this.#unitContextMenu;
}
hideUnitContextMenu() {
//this.#unitContextMenu.hide();
}
showAirbaseContextMenu(
airbase: Airbase,
x: number | undefined = undefined,
y: number | undefined = undefined,
latlng: L.LatLng | undefined = undefined
) {
//this.hideAllContextMenus();
//this.#airbaseContextMenu.show(x, y, latlng);
//this.#airbaseContextMenu.setAirbase(airbase);
}
getAirbaseContextMenu() {
return null; //this.#airbaseContextMenu;
}
hideAirbaseContextMenu() {
//this.#airbaseContextMenu.hide();
}
showAirbaseSpawnMenu(
airbase: Airbase,
x: number | undefined = undefined,
y: number | undefined = undefined,
latlng: L.LatLng | undefined = undefined
) {
//this.hideAllContextMenus();
//this.#airbaseSpawnMenu.show(x, y);
//this.#airbaseSpawnMenu.setAirbase(airbase);
}
getAirbaseSpawnMenu() {
return null; //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 null; //this.#coalitionAreaContextMenu;
}
hideCoalitionAreaContextMenu() {
//this.#coalitionAreaContextMenu.hide();
}
getMousePosition() {
return this.#lastMousePosition;
}
@@ -525,15 +434,15 @@ export class Map extends L.Map {
centerOnUnit(unit: Unit | null) {
if (unit !== null) {
this.options.scrollWheelZoom = "center";
this.#centerUnit = unit;
this.#centeredUnit = unit;
} else {
this.options.scrollWheelZoom = undefined;
this.#centerUnit = null;
this.#centeredUnit = null;
}
}
getCenteredOnUnit() {
return this.#centerUnit;
return this.#centeredUnit;
}
setTheatre(theatre: string) {
@@ -688,10 +597,6 @@ export class Map extends L.Map {
return false;
}
getMapMarkerVisibilityControls() {
return null; //this.#mapMarkerVisibilityControls;
}
setSlaveDCSCamera(newSlaveDCSCamera: boolean) {
this.#slaveDCSCamera = newSlaveDCSCamera;
let button = document.getElementById("camera-link-control");
@@ -740,75 +645,6 @@ export class Map extends L.Map {
}
/* Event handlers */
#onClick(e: any) {
/* Exit if we were waiting for a doubleclick */
if (this.#waitingForDoubleClick) {
return;
}
/* We'll wait for a doubleclick */
this.#waitingForDoubleClick = true;
this.#doubleClickTimer = window.setTimeout(() => {
/* Still waiting so no doubleclick; do the click action */
if (this.#waitingForDoubleClick) {
if (!this.#preventLeftClick) {
/* Execute the short click action */
if (this.#state === IDLE) {
this.deselectAllCoalitionAreas();
} else if (this.#state === SPAWN_UNIT) {
if (this.#spawnRequestTable !== null) {
const location = e.latlng;
this.#spawnRequestTable.unit.location = e.latlng;
getApp()
.getUnitsManager()
.spawnUnits(
this.#spawnRequestTable.category,
[this.#spawnRequestTable.unit],
this.#spawnRequestTable.coalition,
false,
undefined,
undefined,
(hash) => {
this.addTemporaryMarker(
location,
this.#spawnRequestTable?.unit.unitType ?? "unknown",
this.#spawnRequestTable?.coalition ?? "blue",
hash
);
}
);
}
} else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
if (this.getSelectedCoalitionArea()?.getEditing()) {
this.getSelectedCoalitionArea()?.addTemporaryLatLng(e.latlng);
} else {
this.deselectAllCoalitionAreas();
}
} else if (this.#state === CONTEXT_ACTION) {
this.executeContextAction(null, e.latlng);
} else {
this.setState(IDLE);
getApp().getUnitsManager().deselectAllUnits();
}
}
}
/* No longer waiting for a doubleclick */
this.#waitingForDoubleClick = false;
}, 200);
}
#onDoubleClick(e: any) {
/* Let single clicks work again */
this.#waitingForDoubleClick = false;
clearTimeout(this.#doubleClickTimer);
this.setState(IDLE);
}
#onContextMenu(e: any) {}
#onDragStart(e: any) {
this.#isDragging = true;
}
@@ -818,27 +654,103 @@ export class Map extends L.Map {
}
#onSelectionStart(e: any) {
this.#isSelecting = true;
}
#onSelectionEnd(e: any) {
this.#isSelecting = false;
clearTimeout(this.#leftClickTimer);
this.#preventLeftClick = true;
this.#leftClickTimer = window.setTimeout(() => {
this.#preventLeftClick = false;
}, 200);
getApp().getUnitsManager().selectFromBounds(e.selectionBounds);
document.dispatchEvent(new CustomEvent("mapSelectionEnd"));
}
#onMouseUp(e: any) {
this.#isMouseDown = false;
window.clearTimeout(this.#longPressTimer);
this.#isMouseOnCooldown = true;
this.#mouseCooldownTimer = window.setTimeout(() => {
this.#isMouseOnCooldown = false;
}, 200);
}
#onMouseDown(e: any) {
this.#isMouseDown = true;
if (this.#isMouseOnCooldown) {
return;
}
this.#shortPressTimer = window.setTimeout(() => {
/* If the mouse is no longer being pressed, execute the short press action */
if (!this.#isMouseDown) this.#onShortPress(e);
}, 200);
this.#longPressTimer = window.setTimeout(() => {
if (!this.#isDragging && !this.#isZooming)
/* If the mouse is still being pressed, execute the long press action */
if (this.#isMouseDown) this.#onLongPress(e);
}, 500);
}
#onDoubleClick(e: any) {
console.log(`Double click at ${e.latlng}`);
window.clearTimeout(this.#shortPressTimer);
window.clearTimeout(this.#longPressTimer);
if (this.#state === COALITIONAREA_DRAW_POLYGON) {
this.setState(COALITIONAREA_EDIT);
} else {
this.setState(IDLE);
}
}
#onShortPress(e: any) {
console.log(`Short press at ${e.latlng}`);
const location = new L.LatLng(e.latlng.lat, e.latlng.lng);
/* 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;
getApp()
.getUnitsManager()
.spawnUnits(
this.#spawnRequestTable.category,
[this.#spawnRequestTable.unit],
this.#spawnRequestTable.coalition,
false,
undefined,
undefined,
(hash) => {
this.addTemporaryMarker(
location,
this.#spawnRequestTable?.unit.unitType ?? "unknown",
this.#spawnRequestTable?.coalition ?? "blue",
hash
);
}
);
}
} else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
if (this.getSelectedCoalitionArea()?.getEditing()) {
this.getSelectedCoalitionArea()?.addTemporaryLatLng(location);
} else {
this.deselectAllCoalitionAreas();
}
} else if (this.#state === COALITIONAREA_EDIT) {
this.deselectAllCoalitionAreas();
} else if (this.#state === CONTEXT_ACTION) {
this.executeContextAction(null, e.latlng);
} else {
}
}
#onLongPress(e: any) {
console.log(`Long press at ${e.latlng}`);
if (!this.#isDragging && !this.#isZooming) {
if (this.getState() == IDLE) {
if (e.type === "touchstart")
document.dispatchEvent(
new CustomEvent("mapForceBoxSelect", { detail: e })
@@ -847,36 +759,50 @@ export class Map extends L.Map {
document.dispatchEvent(
new CustomEvent("mapForceBoxSelect", { detail: e.originalEvent })
);
}, 500);
} 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);
break;
}
}
}
}
}
#onMouseMove(e: any) {
window.clearTimeout(this.#longPressTimer);
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);
//this.getSelectedCoalitionArea()?.moveActiveVertex(e.latlng);
}
}
#onMapMove(e: any) {
if (this.#slaveDCSCamera) this.#broadcastPosition();
}
#onKeyDown(e: any) {
this.#shiftKey = e.originalEvent.shiftKey;
this.#isShiftKeyDown = e.originalEvent.shiftKey;
}
#onKeyUp(e: any) {
this.#shiftKey = e.originalEvent.shiftKey;
this.#isShiftKeyDown = e.originalEvent.shiftKey;
}
#onZoomStart(e: any) {
this.#previousZoom = this.getZoom();
if (this.#centerUnit != null) this.#panToUnit(this.#centerUnit);
if (this.#centeredUnit != null) this.#panToUnit(this.#centeredUnit);
this.#isZooming = true;
}
#onZoom(e: any) {
if (this.#centerUnit != null) this.#panToUnit(this.#centerUnit);
if (this.#centeredUnit != null) this.#panToUnit(this.#centeredUnit);
}
#onZoomEnd(e: any) {

View File

@@ -31,7 +31,7 @@ export class SmokeMarker extends CustomMarker {
el.classList.add("ol-smoke-icon");
el.setAttribute("data-color", this.#color);
var img = document.createElement("img");
img.src = "/images/markers/smoke.svg";
img.src = "/vite/images/markers/smoke.svg";
img.onload = () => SVGInjector(img);
el.appendChild(img);
this.getElement()?.appendChild(el);

View File

@@ -288,15 +288,15 @@
}
[data-object|="unit"][data-state="rtb"] .unit-state {
background-image: url("/images/states/rtb.svg");
background-image: url("/vite/images/states/rtb.svg");
}
[data-object|="unit"][data-state="land"] .unit-state {
background-image: url("/images/states/rtb.svg");
background-image: url("/vite/images/states/rtb.svg");
}
[data-object|="unit"][data-state="idle"] .unit-state {
background-image: url("/images/states/idle.svg");
background-image: url("/vite/images/states/idle.svg");
}
[data-object*="groundunit"][data-state="idle"] .unit-state,
@@ -308,59 +308,59 @@
[data-object|="unit"][data-state="bomb-point"] .unit-state,
[data-object|="unit"][data-state="carpet-bombing"] .unit-state,
[data-object|="unit"][data-state="fire-at-area"] .unit-state {
background-image: url("/images/states/attack.svg");
background-image: url("/vite/images/states/attack.svg");
}
[data-object|="unit"][data-state="follow"] .unit-state {
background-image: url("/images/states/follow.svg");
background-image: url("/vite/images/states/follow.svg");
}
[data-object|="unit"][data-state="refuel"] .unit-state {
background-image: url("/images/states/refuel.svg");
background-image: url("/vite/images/states/refuel.svg");
}
[data-object|="unit"][data-state="human"] .unit-state {
background-image: url("/images/states/human.svg");
background-image: url("/vite/images/states/human.svg");
}
[data-object|="unit"][data-state="dcs"] .unit-state {
background-image: url("/images/states/dcs.svg");
background-image: url("/vite/images/states/dcs.svg");
}
[data-object|="unit"][data-state="land-at-point"] .unit-state {
background-image: url("/images/states/land-at-point.svg");
background-image: url("/vite/images/states/land-at-point.svg");
}
[data-object|="unit"][data-state="no-task"] .unit-state {
background-image: url("/images/states/no-task.svg");
background-image: url("/vite/images/states/no-task.svg");
}
[data-object|="unit"][data-state="off"] .unit-state {
background-image: url("/images/states/off.svg");
background-image: url("/vite/images/states/off.svg");
}
[data-object|="unit"][data-state="tanker"] .unit-state {
background-image: url("/images/states/tanker.svg");
background-image: url("/vite/images/states/tanker.svg");
}
[data-object|="unit"][data-state="AWACS"] .unit-state {
background-image: url("/images/states/awacs.svg");
background-image: url("/vite/images/states/awacs.svg");
}
[data-object|="unit"][data-state="miss-on-purpose"] .unit-state {
background-image: url("/images/states/miss-on-purpose.svg");
background-image: url("/vite/images/states/miss-on-purpose.svg");
}
[data-object|="unit"][data-state="scenic-aaa"] .unit-state {
background-image: url("/images/states/scenic-aaa.svg");
background-image: url("/vite/images/states/scenic-aaa.svg");
}
[data-object|="unit"][data-state="simulate-fire-fight"] .unit-state {
background-image: url("/images/states/simulate-fire-fight.svg");
background-image: url("/vite/images/states/simulate-fire-fight.svg");
}
[data-object|="unit"] .unit-health::before {
background-image: url("/images/icons/health.svg");
background-image: url("/vite/images/icons/health.svg");
background-repeat: no-repeat;
background-size: contain;
content: " ";

View File

@@ -67,7 +67,7 @@ export class TemporaryUnitMarker extends CustomMarker {
unitIcon.classList.add("unit-icon");
var img = document.createElement("img");
img.src = `/images/units/${databaseEntry?.markerFile ?? category}.svg`;
img.src = `/vite/images/units/${databaseEntry?.markerFile ?? category}.svg`;
img.onload = () => SVGInjector(img);
unitIcon.appendChild(img);
unitIcon.toggleAttribute("data-rotate-to-heading", false);

View File

@@ -1,149 +0,0 @@
import { Map, Point } from "leaflet";
import { Handler } from "leaflet";
import { Util } from "leaflet";
import { DomUtil } from "leaflet";
import { DomEvent } from "leaflet";
import { LatLngBounds } from "leaflet";
import { Bounds } from "leaflet";
export var TouchBoxSelect = Handler.extend({
initialize: function (map: Map) {
this._map = map;
this._container = map.getContainer();
this._pane = map.getPanes().overlayPane;
this._resetStateTimeout = 0;
this._doubleClicked = false;
map.on("unload", this._destroy, this);
},
addHooks: function () {
DomEvent.on(this._container, "touchstart", this._onMouseDown, this);
},
removeHooks: function () {
DomEvent.off(this._container, "touchstart", this._onMouseDown, this);
},
moved: function () {
return this._moved;
},
_destroy: function () {
DomUtil.remove(this._pane);
delete this._pane;
},
_resetState: function () {
this._resetStateTimeout = 0;
this._moved = false;
},
_clearDeferredResetState: function () {
if (this._resetStateTimeout !== 0) {
clearTimeout(this._resetStateTimeout);
this._resetStateTimeout = 0;
}
},
_onMouseDown: function (e: any) {
if (e.which == 0) {
this._map.fire("selectionstart");
// Clear the deferred resetState if it hasn't executed yet, otherwise it
// will interrupt the interaction and orphan a box element in the container.
this._clearDeferredResetState();
this._resetState();
DomUtil.disableTextSelection();
DomUtil.disableImageDrag();
this._startPoint = this._getMousePosition(e);
//@ts-ignore
DomEvent.on(
document,
{
contextmenu: DomEvent.stop,
touchmove: this._onMouseMove,
touchend: this._onMouseUp,
},
this
);
} else {
return false;
}
},
_onMouseMove: function (e: any) {
if (!this._moved) {
this._moved = true;
this._box = DomUtil.create("div", "leaflet-zoom-box", this._container);
DomUtil.addClass(this._container, "leaflet-crosshair");
}
this._point = this._getMousePosition(e);
var bounds = new Bounds(this._point, this._startPoint),
size = bounds.getSize();
if (bounds.min != undefined) DomUtil.setPosition(this._box, bounds.min);
this._box.style.width = size.x + "px";
this._box.style.height = size.y + "px";
},
_finish: function () {
if (this._moved) {
DomUtil.remove(this._box);
DomUtil.removeClass(this._container, "leaflet-crosshair");
}
DomUtil.enableTextSelection();
DomUtil.enableImageDrag();
//@ts-ignore
DomEvent.off(
document,
{
contextmenu: DomEvent.stop,
touchmove: this._onMouseMove,
touchend: this._onMouseUp,
},
this
);
},
_onMouseUp: function (e: any) {
if (e.which !== 0) {
return;
}
this._finish();
if (!this._moved) {
return;
}
// Postpone to next JS tick so internal click event handling
// still see it as "moved".
window.setTimeout(Util.bind(this._resetState, this), 0);
var bounds = new LatLngBounds(
this._map.containerPointToLatLng(this._startPoint),
this._map.containerPointToLatLng(this._point)
);
this._map.fire("selectionend", { selectionBounds: bounds });
},
_getMousePosition(e: any) {
var scale = DomUtil.getScale(this._container),
offset = scale.boundingClientRect; // left and top values are in page scale (like the event clientX/Y)
return new Point(
// offset.left/top values are in page scale (like clientX/Y),
// whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
(e.touches[0].clientX - offset.left) / scale.x -
this._container.clientLeft,
(e.touches[0].clientY - offset.top) / scale.y - this._container.clientTop
);
},
});

View File

@@ -38,7 +38,7 @@ export class Airbase extends CustomMarker {
el.classList.add("airbase-icon");
el.setAttribute("data-object", "airbase");
var img = document.createElement("img");
img.src = "/images/markers/airbase.svg";
img.src = "/vite/images/markers/airbase.svg";
img.onload = () => SVGInjector(img);
el.appendChild(img);
this.getElement()?.appendChild(el);

View File

@@ -17,7 +17,7 @@ export class Bullseye extends CustomMarker {
el.classList.add("bullseye-icon");
el.setAttribute("data-object", "bullseye");
var img = document.createElement("img");
img.src = "/images/markers/bullseye.svg";
img.src = "/vite/images/markers/bullseye.svg";
img.onload = () => SVGInjector(img);
el.appendChild(img);
this.getElement()?.appendChild(el);

View File

@@ -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)

View File

@@ -175,7 +175,7 @@ export class ServerManager {
}
setAddress(address: string) {
this.#REST_ADDRESS = `${address}olympus`;
this.#REST_ADDRESS = `${address.replace('vite', '')}olympus`;
console.log(`Setting REST address to ${this.#REST_ADDRESS}`);
}

View File

@@ -16,6 +16,7 @@ export function OlStateButton(props: {
icon: IconProp;
tooltip: string;
onClick: () => void;
children?: JSX.Element | JSX.Element[];
}) {
var [hover, setHover] = useState(false);
var buttonRef = useRef(null);
@@ -47,7 +48,10 @@ export function OlStateButton(props: {
setHover(false);
}}
>
<FontAwesomeIcon icon={props.icon} />
<div className="m-auto flex w-fit content-center justify-center gap-2">
<FontAwesomeIcon icon={props.icon} className="m-auto"/>
{props.children}
</div>
</button>
{hover && <OlTooltip buttonRef={buttonRef} content={props.tooltip} />}
</>

View File

@@ -23,7 +23,7 @@ export function OlUnitSummary(props: {
className={`
absolute right-5 top-0 h-full object-cover opacity-10 invert
`}
src={"images/units/" + props.blueprint.filename}
src={"vite/images/units/" + props.blueprint.filename}
alt=""
/>
<div

View File

@@ -45,7 +45,7 @@ export function LoginModal(props: {
`}
>
<img
src="/images/splash/1.jpg"
src="/vite/images/splash/1.jpg"
className={`contents-center w-full object-cover opacity-[7%]`}
></img>
<div
@@ -106,7 +106,7 @@ export function LoginModal(props: {
>
<span className="size-[80px] min-w-14">
<img
src="..\images\olympus-500x500.png"
src="..\vite\images\olympus-500x500.png"
className={`flex w-full`}
></img>
</span>
@@ -332,7 +332,7 @@ export function LoginModal(props: {
>
<Card className="flex">
<img
src="/images/splash/1.jpg"
src="/vite/images/splash/1.jpg"
className={`
h-[40%] max-h-[120px] contents-center w-full rounded-md
object-cover
@@ -362,7 +362,7 @@ export function LoginModal(props: {
</Card>
<Card className="flex">
<img
src="/images/splash/1.jpg"
src="/vite/images/splash/1.jpg"
className={`
h-[40%] max-h-[120px] contents-center w-full rounded-md
object-cover

View File

@@ -0,0 +1,62 @@
import React, { useEffect, useState } from "react";
import { Menu } from "./components/menu";
import { FaQuestionCircle } from "react-icons/fa";
import { getApp } from "../../olympusapp";
import { COALITIONAREA_DRAW_POLYGON, COALITIONAREA_EDIT, IDLE } from "../../constants/constants";
import { OlStateButton } from "../components/olstatebutton";
import { faDrawPolygon } from "@fortawesome/free-solid-svg-icons";
export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
const [drawingPolygon, setDrawingPolygon] = useState(false);
useEffect(() => {
if (props.open && !drawingPolygon) {
getApp().getMap().setState(COALITIONAREA_EDIT);
}
})
return (
<Menu
open={props.open}
title="Draw"
onClose={props.onClose}
canBeHidden={true}
>
<div className="p-4 text-sm text-gray-400">
The draw tool allows you to quickly draw areas on the map and use these
areas to spawn units and activate triggers.
</div>
<div className="mx-6 my-2 flex rounded-lg bg-olympus-400 p-4 text-sm">
<div>
<FaQuestionCircle className="my-4 ml-2 mr-6 text-gray-400" />
</div>
<div className="flex flex-col gap-1">
<div className="text-gray-100">
Use the polygon or paint tool to draw areas on the map.
</div>
<div className="text-gray-400">
After drawing a shape, select it to see the options for spawning
units.
</div>
</div>
</div>
<div className="p-6 text-sm text-gray-400">
<OlStateButton
className="!w-full"
icon={faDrawPolygon}
tooltip={"Add a new polygon"}
checked={drawingPolygon}
onClick={() => {
if (drawingPolygon)
getApp().getMap().setState(COALITIONAREA_EDIT);
else
getApp().getMap().setState(COALITIONAREA_DRAW_POLYGON);
setDrawingPolygon(!drawingPolygon);
}}
>
<div className="text-sm">Add polygon</div>
</OlStateButton>
</div>
</Menu>
);
}

View File

@@ -60,7 +60,7 @@ export function Header() {
`}
>
<img
src="images/icon.png"
src="vite/images/icon.png"
className="my-auto h-10 w-10 rounded-md p-0"
></img>
{!scrolledLeft && (

View File

@@ -35,10 +35,7 @@ import {
import { Coalition } from "../../types/types";
import { ftToM, knotsToMs, mToFt, msToKnots } from "../../other/utils";
export function UnitControlMenu(props: {
open: boolean;
onClose: () => void;
}) {
export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
var [selectedUnits, setSelectedUnits] = useState([] as Unit[]);
var [selectedUnitsData, setSelectedUnitsData] = useState({
@@ -185,7 +182,12 @@ export function UnitControlMenu(props: {
getApp()?.getUnitsManager()?.getSelectedUnitsCategories() ?? [];
return (
<Menu open={props.open} title="Units selected (x)" onClose={props.onClose}>
<Menu
open={props.open}
title="Units selected (x)"
onClose={props.onClose}
canBeHidden={true}
>
{/* Units list */}
<div
className={`

View File

@@ -24,6 +24,7 @@ import { LoginModal } from "./modals/login";
import { sha256 } from "js-sha256";
import { MiniMapPanel } from "./panels/minimappanel";
import { UnitMouseControlBar } from "./panels/unitmousecontrolbar";
import { DrawingMenu } from "./panels/drawingmenu";
export type OlympusState = {
mainMenuVisible: boolean;
@@ -238,6 +239,10 @@ export function UI() {
open={unitControlMenuVisible}
onClose={() => setUnitControlMenuVisible(false)}
/>
<DrawingMenu
open={drawingMenuVisible}
onClose={() => setDrawingMenuVisible(false)}
/>
<div id="map-container" className="h-full w-screen" />
<UnitMouseControlBar />
</div>

View File

@@ -102,8 +102,8 @@ import {
import { FaXmarksLines } from "react-icons/fa6";
var pathIcon = new Icon({
iconUrl: "/images/markers/marker-icon.png",
shadowUrl: "/images/markers/marker-shadow.png",
iconUrl: "/vite/images/markers/marker-icon.png",
shadowUrl: "/vite/images/markers/marker-shadow.png",
iconAnchor: [13, 41],
});
@@ -991,7 +991,7 @@ export abstract class Unit extends CustomMarker {
)
marker = this.getDatabaseEntry()?.markerFile ?? this.getDefaultMarker();
else marker = "aircraft";
img.src = `/images/units/${marker}.svg`;
img.src = `/vite/images/units/${marker}.svg`;
img.onload = () => SVGInjector(img);
unitIcon.appendChild(img);
@@ -1486,9 +1486,6 @@ export abstract class Unit extends CustomMarker {
faExclamation,
() => this.applyFollowOptions("custom", units)
);
//getApp().getMap().getUnitContextMenu().setContextActions(contextActionSet);
getApp().getMap().showUnitContextMenu();
}
applyFollowOptions(formation: string, units: Unit[]) {

View File

@@ -194,7 +194,7 @@ export class Weapon extends CustomMarker {
var unitIcon = document.createElement("div");
unitIcon.classList.add("unit-icon");
var img = document.createElement("img");
img.src = `/images/units/${this.getMarkerCategory()}.svg`;
img.src = `/vite/images/units/${this.getMarkerCategory()}.svg`;
img.onload = () => SVGInjector(img);
unitIcon.appendChild(img);
unitIcon.toggleAttribute(