mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
818 lines
26 KiB
TypeScript
818 lines
26 KiB
TypeScript
import { decimalToRGBA } from "../../other/utils";
|
|
import { getApp } from "../../olympusapp";
|
|
import { CommandModeOptionsChangedEvent, DrawingsInitEvent, DrawingsUpdatedEvent, MapOptionsChangedEvent, SessionDataLoadedEvent } from "../../events";
|
|
import { MapOptions } from "../../types/types";
|
|
import { Circle, DivIcon, Layer, LayerGroup, layerGroup, Marker, Polygon, Polyline } from "leaflet";
|
|
import { NavpointMarker } from "../markers/navpointmarker";
|
|
import { BLUE_COMMANDER, GAME_MASTER, NONE, RED_COMMANDER } from "../../constants/constants";
|
|
|
|
export abstract class DCSDrawing {
|
|
#name: string;
|
|
#parent: DCSDrawingsContainer;
|
|
#weight: number;
|
|
|
|
constructor(drawingData, parent: DCSDrawingsContainer) {
|
|
this.#name = drawingData["name"];
|
|
this.#parent = parent;
|
|
this.setWeight(drawingData);
|
|
}
|
|
|
|
getName() {
|
|
return this.#name;
|
|
}
|
|
|
|
getParent() {
|
|
return this.#parent;
|
|
}
|
|
|
|
toJSON() {
|
|
return {
|
|
name: this.#name,
|
|
opacity: this.getOpacity(),
|
|
visibility: this.getVisibility(),
|
|
};
|
|
}
|
|
|
|
setWeight(drawingData) {
|
|
if (!drawingData.thickness) {
|
|
return;
|
|
}
|
|
|
|
this.#weight = drawingData.thickness * 0.5;
|
|
|
|
if (this.#weight === 0) {
|
|
this.#weight = 0.1;
|
|
}
|
|
|
|
if (this.#weight > 1) {
|
|
this.#weight = 1;
|
|
}
|
|
}
|
|
|
|
getWeight() {
|
|
return this.#weight;
|
|
}
|
|
|
|
abstract getLayer(): Layer;
|
|
abstract setOpacity(opacity: number): void;
|
|
abstract getOpacity(): number;
|
|
abstract setVisibility(visibility: boolean): void;
|
|
abstract getVisibility(): boolean;
|
|
}
|
|
|
|
export class DCSEmptyLayer extends DCSDrawing {
|
|
getLayer() {
|
|
return layerGroup();
|
|
}
|
|
|
|
setOpacity(opacity: number): void {
|
|
//Do nothing
|
|
}
|
|
|
|
setVisibility(visibility: boolean): void {
|
|
//Do nothing
|
|
}
|
|
|
|
getOpacity(): number {
|
|
return 1;
|
|
}
|
|
|
|
getVisibility(): boolean {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export class DCSPolygon extends DCSDrawing {
|
|
#polygon: Polygon | Circle;
|
|
|
|
constructor(drawingData, parent) {
|
|
super(drawingData, parent);
|
|
|
|
const polygonMode = drawingData["polygonMode"];
|
|
let dashArray: number[] | string = [];
|
|
|
|
switch (drawingData.style) {
|
|
case "dash":
|
|
dashArray = [5];
|
|
case "dot":
|
|
dashArray = [2];
|
|
case "dotdash":
|
|
dashArray = "2, 5, 5, 5";
|
|
}
|
|
|
|
switch (polygonMode) {
|
|
case "circle":
|
|
// Example circle:
|
|
/*
|
|
colorString: 4278190335
|
|
fillColorString: 4278190127
|
|
lat: 27.65469131156049
|
|
layer: "Blue"
|
|
layerName: "Blue"
|
|
lng: 54.33075915954884
|
|
name: "SA11-2 + SA6-3"
|
|
points: {0: {…}, x: 166867.07767244, y: -187576.93134045}
|
|
polygonMode: "circle"
|
|
primitiveType: "Polygon"
|
|
radius: 36651.296128911
|
|
style: "dash"
|
|
thickness: 16
|
|
visible: true*/
|
|
|
|
this.#polygon = new Circle([drawingData.lat, drawingData.lng], {
|
|
radius: Math.round(drawingData.radius),
|
|
color: `${decimalToRGBA(drawingData.colorString)}`,
|
|
fillColor: `${decimalToRGBA(drawingData.fillColorString)}`,
|
|
opacity: 1,
|
|
fillOpacity: 1,
|
|
weight: this.getWeight(),
|
|
dashArray: dashArray,
|
|
});
|
|
break;
|
|
|
|
case "arrow":
|
|
let weight = this.getWeight();
|
|
|
|
if (!weight || weight < 1) {
|
|
weight = 1;
|
|
}
|
|
|
|
|
|
const arrowBounds = [
|
|
[drawingData.points["1"].lat, drawingData.points["1"].lng],
|
|
[drawingData.points["2"].lat, drawingData.points["2"].lng],
|
|
[drawingData.points["3"].lat, drawingData.points["3"].lng],
|
|
[drawingData.points["4"].lat, drawingData.points["4"].lng],
|
|
[drawingData.points["5"].lat, drawingData.points["5"].lng],
|
|
[drawingData.points["6"].lat, drawingData.points["6"].lng],
|
|
[drawingData.points["7"].lat, drawingData.points["7"].lng],
|
|
[drawingData.points["8"].lat, drawingData.points["8"].lng],
|
|
];
|
|
|
|
this.#polygon = new Polygon(arrowBounds, {
|
|
color: `${decimalToRGBA(drawingData.colorString)}`,
|
|
fillColor: `${decimalToRGBA(drawingData.fillColorString)}`,
|
|
opacity: 1,
|
|
fillOpacity: 1,
|
|
weight: weight,
|
|
dashArray,
|
|
});
|
|
break;
|
|
|
|
case "rect":
|
|
/** Rectangle Example:
|
|
* {
|
|
"angle": 68.579040048342,
|
|
"colorString": 255,
|
|
"fillColorString": 4294901888,
|
|
"height": 11100,
|
|
"lat": 27.5547706075188,
|
|
"layer": "Author",
|
|
"layerName": "Author",
|
|
"lng": 57.22438242806247,
|
|
"mapX": 152970.68262179,
|
|
"mapY": 97907.892121675,
|
|
"name": "FLOT BUFFER EAST",
|
|
"points": {
|
|
"1": {
|
|
"lat": 27.417344649833286,
|
|
"lng": 57.34472624501578
|
|
},
|
|
"2": {
|
|
"lat": 27.38096510320196,
|
|
"lng": 57.24010993680159
|
|
},
|
|
"3": {
|
|
"lat": 27.69209116201148,
|
|
"lng": 57.1037392116416
|
|
},
|
|
"4": {
|
|
"lat": 27.728570135811577,
|
|
"lng": 57.20860735951096
|
|
}
|
|
},
|
|
"polygonMode": "rect",
|
|
"primitiveType": "Polygon",
|
|
"style": "dot",
|
|
"thickness": 16,
|
|
"visible": true,
|
|
"width": 37000
|
|
}
|
|
*/
|
|
const bounds = [
|
|
[drawingData.points["1"].lat, drawingData.points["1"].lng],
|
|
[drawingData.points["2"].lat, drawingData.points["2"].lng],
|
|
[drawingData.points["3"].lat, drawingData.points["3"].lng],
|
|
[drawingData.points["4"].lat, drawingData.points["4"].lng],
|
|
];
|
|
|
|
this.#polygon = new Polygon(bounds, {
|
|
color: `${decimalToRGBA(drawingData.colorString)}`,
|
|
fillColor: `${decimalToRGBA(drawingData.fillColorString)}`,
|
|
opacity: 1,
|
|
fillOpacity: 1,
|
|
weight: this.getWeight(),
|
|
dashArray: dashArray,
|
|
});
|
|
break;
|
|
|
|
case "oval":
|
|
/**
|
|
* Example:
|
|
* {
|
|
"angle": 270,
|
|
"colorString": 255,
|
|
"fillColorString": 4278190080,
|
|
"lat": 25.032272009407105,
|
|
"layer": "Blue",
|
|
"layerName": "Blue",
|
|
"lng": 55.36597899137401,
|
|
"mapX": -125416.92956726,
|
|
"mapY": -89103.936896595,
|
|
"name": "AM OTP",
|
|
"points": {
|
|
"1": {
|
|
"lat": 25.03332743167039,
|
|
"lng": 55.34689257576858
|
|
},
|
|
"2": {
|
|
"lat": 25.034529398092356,
|
|
"lng": 55.348849087588164
|
|
},
|
|
...
|
|
"24": {
|
|
"lat": 25.032053589358366,
|
|
"lng": 55.34623694782629
|
|
}
|
|
},
|
|
"polygonMode": "oval",
|
|
"primitiveType": "Polygon",
|
|
"r1": 1992.4714734497,
|
|
"r2": 541.99904672895,
|
|
"style": "dot",
|
|
"thickness": 10,
|
|
"visible": true
|
|
}
|
|
*/
|
|
const points: [number, number][] = Object.values(drawingData.points as Record<string, { lat: number; lng: number }>).map((p) => [p.lat, p.lng]);
|
|
|
|
this.#polygon = new Polygon(points, {
|
|
color: `${decimalToRGBA(drawingData.colorString)}`,
|
|
fillColor: `${decimalToRGBA(drawingData.fillColorString)}`,
|
|
opacity: 1,
|
|
fillOpacity: 1,
|
|
weight: this.getWeight(),
|
|
dashArray: dashArray,
|
|
});
|
|
|
|
case "free":
|
|
const freePolypoints: [number, number][] = Object.values(drawingData.points as Record<string, { lat: number; lng: number }>).map((p) => [p.lat, p.lng]);
|
|
|
|
this.#polygon = new Polygon(freePolypoints, {
|
|
color: `${decimalToRGBA(drawingData.colorString)}`,
|
|
fillColor: `${decimalToRGBA(drawingData.fillColorString)}`,
|
|
opacity: 1,
|
|
fillOpacity: 1,
|
|
weight: this.getWeight(),
|
|
dashArray: dashArray,
|
|
});
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
this.setVisibility(true);
|
|
}
|
|
|
|
getLayer() {
|
|
return this.#polygon;
|
|
}
|
|
|
|
setOpacity(opacity: number): void {
|
|
if (opacity === this.#polygon.options.fillOpacity && opacity === this.#polygon.options.opacity) return;
|
|
|
|
this.#polygon.options.fillOpacity = opacity;
|
|
this.#polygon.options.opacity = opacity;
|
|
this.#polygon.redraw();
|
|
|
|
getApp().getDrawingsManager().requestUpdateEventDispatch();
|
|
}
|
|
|
|
setVisibility(visibility: boolean): void {
|
|
if (visibility && !this.getParent().getLayerGroup().hasLayer(this.#polygon)) this.#polygon.addTo(this.getParent().getLayerGroup());
|
|
//@ts-ignore Leaflet typings are wrong
|
|
if (!visibility && this.getParent().getLayerGroup().hasLayer(this.#polygon)) this.#polygon.removeFrom(this.getParent().getLayerGroup());
|
|
|
|
if (visibility && !this.getParent().getVisibility()) this.getParent().setVisibility(true);
|
|
|
|
getApp().getDrawingsManager().requestUpdateEventDispatch();
|
|
}
|
|
|
|
getOpacity(): number {
|
|
return this.#polygon.options.opacity ?? 1;
|
|
}
|
|
|
|
getVisibility(): boolean {
|
|
return this.getParent().getLayerGroup().hasLayer(this.#polygon);
|
|
}
|
|
}
|
|
|
|
export class DCSLine extends DCSDrawing {
|
|
#line: Polyline;
|
|
|
|
constructor(drawingData, parent) {
|
|
super(drawingData, parent);
|
|
|
|
const points: [number, number][] = Object.values(drawingData.points as Record<string, { lat: number; lng: number }>).map((p) => [p.lat, p.lng]);
|
|
const dashArray = drawingData.style === "dot" ? "5" : drawingData.style === "dot2" ? "10" : undefined;
|
|
|
|
this.#line = new Polyline(points, {
|
|
color: `${decimalToRGBA(drawingData.colorString)}`,
|
|
weight: this.getWeight(),
|
|
dashArray: dashArray,
|
|
});
|
|
|
|
this.setVisibility(true);
|
|
}
|
|
|
|
getLayer() {
|
|
return this.#line;
|
|
}
|
|
|
|
setOpacity(opacity: number): void {
|
|
if (opacity === this.#line.options.opacity) return;
|
|
|
|
this.#line.options.opacity = opacity;
|
|
this.#line.redraw();
|
|
|
|
getApp().getDrawingsManager().requestUpdateEventDispatch();
|
|
}
|
|
|
|
setVisibility(visibility: boolean): void {
|
|
if (visibility && !this.getParent().getLayerGroup().hasLayer(this.#line)) this.#line.addTo(this.getParent().getLayerGroup());
|
|
//@ts-ignore Leaflet typings are wrong
|
|
if (!visibility && this.getParent().getLayerGroup().hasLayer(this.#line)) this.#line.removeFrom(this.getParent().getLayerGroup());
|
|
|
|
if (visibility && !this.getParent().getVisibility()) this.getParent().setVisibility(true);
|
|
|
|
getApp().getDrawingsManager().requestUpdateEventDispatch();
|
|
}
|
|
|
|
getOpacity(): number {
|
|
return this.#line.options.opacity ?? 1;
|
|
}
|
|
|
|
getVisibility(): boolean {
|
|
return this.getParent().getLayerGroup().hasLayer(this.#line);
|
|
}
|
|
}
|
|
|
|
export class DCSTextBox extends DCSDrawing {
|
|
#marker: Marker;
|
|
|
|
constructor(drawingData, parent) {
|
|
super(drawingData, parent);
|
|
|
|
/* Example textbox "ABC625":
|
|
angle: 0
|
|
borderThickness: 1
|
|
colorString: 4294967295
|
|
fillColorString: 8421504
|
|
font: "DejaVuLGCSansCondensed.ttf"
|
|
fontSize: 10
|
|
layer: "Common"
|
|
layerName: "Common"
|
|
mapX: -261708.68309463
|
|
mapY: -217863.03743212
|
|
name: "ABC625"
|
|
primitiveType: "TextBox"
|
|
text: "ABC625"
|
|
visible: true
|
|
*/
|
|
const customIcon = new DivIcon({
|
|
html: `
|
|
<div style="
|
|
border: ${drawingData.borderThickness}px solid ${decimalToRGBA(drawingData.colorString)};
|
|
background-color: ${decimalToRGBA(drawingData.fillColorString)};
|
|
color: ${decimalToRGBA(drawingData.colorString)};
|
|
padding: 5px;
|
|
font-family: Arial, sans-serif;
|
|
font-size: ${drawingData.fontSize - 1}px;
|
|
text-align: center;
|
|
width: max-content;
|
|
transform: rotate(${drawingData.angle}deg);
|
|
transform-origin: center;
|
|
opacity: 100%;
|
|
">
|
|
${drawingData.text || drawingData.name}
|
|
</div>
|
|
`,
|
|
// iconSize: [100, 50], // Dimensioni del box
|
|
iconAnchor: [50, 25], // Punto di ancoraggio al centro
|
|
className: "",
|
|
});
|
|
|
|
this.#marker = new Marker([drawingData.lat, drawingData.lng], { icon: customIcon });
|
|
|
|
this.setVisibility(true);
|
|
}
|
|
|
|
getLayer() {
|
|
return this.#marker;
|
|
}
|
|
|
|
setOpacity(opacity: number): void {
|
|
if (opacity === this.#marker.options.opacity) return;
|
|
|
|
this.#marker.options.opacity = opacity;
|
|
|
|
/* Hack to force marker redraw */
|
|
const originalVisibility = this.getVisibility();
|
|
this.setVisibility(false);
|
|
this.setVisibility(originalVisibility);
|
|
|
|
getApp().getDrawingsManager().requestUpdateEventDispatch();
|
|
}
|
|
|
|
setVisibility(visibility: boolean): void {
|
|
if (visibility && !this.getParent().getLayerGroup().hasLayer(this.#marker)) this.#marker.addTo(this.getParent().getLayerGroup());
|
|
//@ts-ignore Leaflet typings are wrong
|
|
if (!visibility && this.getParent().getLayerGroup().hasLayer(this.#marker)) this.#marker.removeFrom(this.getParent().getLayerGroup());
|
|
|
|
if (visibility && !this.getParent().getVisibility()) this.getParent().setVisibility(true);
|
|
|
|
getApp().getDrawingsManager().requestUpdateEventDispatch();
|
|
}
|
|
|
|
getOpacity(): number {
|
|
return this.#marker.options.opacity ?? 1;
|
|
}
|
|
|
|
getVisibility(): boolean {
|
|
return this.getParent().getLayerGroup().hasLayer(this.#marker);
|
|
}
|
|
}
|
|
|
|
export class DCSNavpoint extends DCSDrawing {
|
|
#point: NavpointMarker;
|
|
|
|
constructor(drawingData, parent) {
|
|
super(drawingData, parent);
|
|
|
|
this.#point = new NavpointMarker([drawingData.lat, drawingData.lng], drawingData.callsignStr, drawingData.comment);
|
|
|
|
this.setVisibility(true);
|
|
}
|
|
|
|
getLayer() {
|
|
return this.#point;
|
|
}
|
|
|
|
getLabelLayer() {
|
|
return this.#point;
|
|
}
|
|
|
|
setOpacity(opacity: number): void {
|
|
if (opacity === this.#point.options.opacity) return;
|
|
|
|
this.#point.options.opacity = opacity;
|
|
|
|
/* Hack to force marker redraw */
|
|
const originalVisibility = this.getVisibility();
|
|
this.setVisibility(false);
|
|
this.setVisibility(originalVisibility);
|
|
|
|
getApp().getDrawingsManager().requestUpdateEventDispatch();
|
|
}
|
|
|
|
setVisibility(visibility: boolean): void {
|
|
if (visibility && !this.getParent().getLayerGroup().hasLayer(this.#point)) this.#point.addTo(this.getParent().getLayerGroup());
|
|
//@ts-ignore Leaflet typings are wrong
|
|
if (!visibility && this.getParent().getLayerGroup().hasLayer(this.#point)) this.#point.removeFrom(this.getParent().getLayerGroup());
|
|
|
|
if (visibility && !this.getParent().getVisibility()) this.getParent().setVisibility(true);
|
|
|
|
getApp().getDrawingsManager().requestUpdateEventDispatch();
|
|
}
|
|
|
|
getOpacity(): number {
|
|
return this.#point.options.opacity ?? 1;
|
|
}
|
|
|
|
getVisibility(): boolean {
|
|
return this.getParent().getLayerGroup().hasLayer(this.#point);
|
|
}
|
|
}
|
|
|
|
export class DCSDrawingsContainer {
|
|
#drawings: DCSDrawing[] = [];
|
|
#subContainers: DCSDrawingsContainer[] = [];
|
|
#name: string;
|
|
#opacity: number = 1;
|
|
#visibility: boolean = true;
|
|
#layerGroup: LayerGroup = new LayerGroup();
|
|
#parent: LayerGroup | DCSDrawingsContainer;
|
|
#guid: string = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
|
|
|
constructor(name: string, parent: LayerGroup | DCSDrawingsContainer) {
|
|
this.#name = name;
|
|
this.#parent = parent;
|
|
this.#layerGroup.addTo(parent instanceof DCSDrawingsContainer ? parent.getLayerGroup() : parent);
|
|
}
|
|
|
|
getGuid() {
|
|
return this.#guid;
|
|
}
|
|
|
|
initFromData(drawingsData) {
|
|
let hasContainers = false;
|
|
Object.keys(drawingsData).forEach((layerName: string) => {
|
|
const layerIsAContainer = drawingsData[layerName]["name"] === undefined && drawingsData[layerName]["callsignStr"] === undefined;
|
|
const layerIsNotEmpty = Object.keys(drawingsData[layerName]).length > 0;
|
|
if (layerIsAContainer && layerIsNotEmpty) {
|
|
const newContainer = new DCSDrawingsContainer(layerName, this);
|
|
this.addSubContainer(newContainer);
|
|
newContainer.initFromData(drawingsData[layerName]);
|
|
hasContainers = true;
|
|
}
|
|
});
|
|
const othersContainer = new DCSDrawingsContainer("Others", this);
|
|
if (hasContainers) this.addSubContainer(othersContainer);
|
|
|
|
Object.keys(drawingsData).forEach((layerName: string) => {
|
|
const primitiveType = drawingsData[layerName]["primitiveType"];
|
|
const isANavpoint = !!drawingsData[layerName]['callsignStr'];
|
|
|
|
// Possible primitives:
|
|
// "Line","TextBox","Polygon","Icon"
|
|
|
|
// Possible polygon modes:
|
|
// "arrow","circle","rect","oval","free"
|
|
|
|
// Possible Line modes:
|
|
// 'segments', 'free', 'segment'
|
|
|
|
let newDrawing = new DCSEmptyLayer(drawingsData[layerName], othersContainer) as DCSDrawing;
|
|
|
|
if (isANavpoint) {
|
|
newDrawing = new DCSNavpoint(drawingsData[layerName], othersContainer);
|
|
if (hasContainers) othersContainer.addDrawing(newDrawing);
|
|
else this.addDrawing(newDrawing);
|
|
if (othersContainer.getDrawings().length === 0) this.removeSubContainer(othersContainer); // Remove empty container
|
|
return;
|
|
}
|
|
|
|
switch (primitiveType) {
|
|
case "Polygon":
|
|
newDrawing = new DCSPolygon(drawingsData[layerName], othersContainer);
|
|
break;
|
|
case "TextBox":
|
|
newDrawing = new DCSTextBox(drawingsData[layerName], othersContainer);
|
|
break;
|
|
case "Line":
|
|
newDrawing = new DCSLine(drawingsData[layerName], othersContainer);
|
|
break;
|
|
case "Icon":
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (hasContainers) othersContainer.addDrawing(newDrawing);
|
|
else this.addDrawing(newDrawing);
|
|
});
|
|
|
|
if (othersContainer.getDrawings().length === 0) {
|
|
this.removeSubContainer(othersContainer); // Remove empty container
|
|
// FIXME: it's not working for main containers.
|
|
}
|
|
}
|
|
|
|
getLayerGroup() {
|
|
return this.#layerGroup;
|
|
}
|
|
|
|
getName() {
|
|
return this.#name;
|
|
}
|
|
|
|
addDrawing(drawing: DCSDrawing) {
|
|
this.#drawings.push(drawing);
|
|
}
|
|
|
|
addSubContainer(container: DCSDrawingsContainer) {
|
|
this.#subContainers.push(container);
|
|
}
|
|
|
|
removeSubContainer(container: DCSDrawingsContainer) {
|
|
const index = this.#subContainers.indexOf(container);
|
|
if (index !== -1) this.#subContainers.splice(index, 1);
|
|
}
|
|
|
|
getDrawings() {
|
|
return this.#drawings;
|
|
}
|
|
|
|
getSubContainers() {
|
|
return this.#subContainers;
|
|
}
|
|
|
|
setOpacity(opacity: number) {
|
|
if (opacity === this.#opacity) return;
|
|
|
|
this.#opacity = opacity;
|
|
this.#drawings.forEach((drawing) => drawing.setOpacity(opacity));
|
|
this.#subContainers.forEach((container) => container.setOpacity(opacity));
|
|
|
|
getApp().getDrawingsManager().requestUpdateEventDispatch();
|
|
}
|
|
|
|
setVisibility(visibility: boolean, force: boolean = false) {
|
|
this.#visibility = visibility;
|
|
|
|
if (force) {
|
|
this.#subContainers.forEach((container) => container.setVisibility(visibility, force));
|
|
this.#drawings.forEach((drawing) => drawing.setVisibility(visibility));
|
|
}
|
|
if (visibility && this.#parent instanceof DCSDrawingsContainer && !this.#parent.getVisibility()) this.#parent.setVisibility(true);
|
|
|
|
if (this.#parent instanceof DCSDrawingsContainer) {
|
|
if (visibility && !this.#parent.getLayerGroup().hasLayer(this.#layerGroup)) this.#layerGroup.addTo(this.#parent.getLayerGroup());
|
|
//@ts-ignore Leaflet typings are wrong
|
|
if (!visibility && this.#parent.getLayerGroup().hasLayer(this.#layerGroup)) this.#layerGroup.removeFrom(this.#parent.getLayerGroup());
|
|
} else {
|
|
if (visibility && !this.#parent.hasLayer(this.#layerGroup)) this.#layerGroup.addTo(this.#parent);
|
|
//@ts-ignore Leaflet typings are wrong
|
|
if (!visibility && this.#parent.hasLayer(this.#layerGroup)) this.#layerGroup.removeFrom(this.#parent);
|
|
}
|
|
|
|
getApp().getDrawingsManager().requestUpdateEventDispatch();
|
|
}
|
|
|
|
getOpacity() {
|
|
return this.#opacity;
|
|
}
|
|
|
|
getVisibility() {
|
|
return this.#visibility;
|
|
}
|
|
|
|
toJSON() {
|
|
let JSON = { drawings: {}, containers: {}, guid: this.#guid, name: this.#name, opacity: this.#opacity, visibility: this.#visibility };
|
|
this.#drawings.forEach((drawing) => {
|
|
JSON["drawings"][drawing.getName()] = drawing.toJSON();
|
|
});
|
|
this.#subContainers.forEach((container) => {
|
|
JSON["containers"][container.getName()] = container.toJSON();
|
|
});
|
|
return JSON;
|
|
}
|
|
|
|
fromJSON(JSON) {
|
|
this.#subContainers.forEach((container) => {
|
|
if (JSON["containers"][container.getName()]) container.fromJSON(JSON["containers"][container.getName()]);
|
|
});
|
|
this.#drawings.forEach((drawing) => {
|
|
if (JSON["drawings"][drawing.getName()]) {
|
|
drawing.setOpacity(JSON["drawings"][drawing.getName()].opacity);
|
|
drawing.setVisibility(JSON["drawings"][drawing.getName()].visibility);
|
|
}
|
|
});
|
|
this.setOpacity(JSON["opacity"]);
|
|
this.setVisibility(JSON["visibility"]);
|
|
}
|
|
|
|
hasSearchString(searchString) {
|
|
if (this.getName().toLowerCase().includes(searchString.toLowerCase())) return true;
|
|
if (
|
|
this.getDrawings().some((drawing) =>
|
|
drawing
|
|
.getName()
|
|
?.toLowerCase()
|
|
.includes(searchString.toLowerCase() ?? false)
|
|
)
|
|
)
|
|
return true;
|
|
if (this.getSubContainers().some((container) => container.hasSearchString(searchString))) return true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export class DrawingsManager {
|
|
#drawingsContainer: DCSDrawingsContainer;
|
|
#navpointsContainer: DCSDrawingsContainer;
|
|
#updateEventRequested: boolean = false;
|
|
#sessionDataDrawings = {};
|
|
#sessionDataNavpoints = {};
|
|
#initialized: boolean = false;
|
|
#hiddenContainers: Record<string, { parent: DCSDrawingsContainer, container: DCSDrawingsContainer }> = {};
|
|
|
|
constructor() {
|
|
const drawingsLayerGroup = new LayerGroup();
|
|
drawingsLayerGroup.addTo(getApp().getMap());
|
|
this.#drawingsContainer = new DCSDrawingsContainer("Mission drawings", drawingsLayerGroup);
|
|
this.#navpointsContainer = new DCSDrawingsContainer("Navpoints", drawingsLayerGroup);
|
|
|
|
MapOptionsChangedEvent.on((mapOptions: MapOptions) => {
|
|
this.#drawingsContainer.setVisibility(mapOptions.showMissionDrawings);
|
|
this.#navpointsContainer.setVisibility(mapOptions.showMissionNavpoints);
|
|
});
|
|
|
|
SessionDataLoadedEvent.on((sessionData) => {
|
|
this.#sessionDataDrawings = sessionData.drawings ?? {};
|
|
this.#sessionDataNavpoints = sessionData.navpoints ?? {};
|
|
if (this.#initialized) {
|
|
if (this.#sessionDataDrawings["Mission drawings"]) this.#drawingsContainer.fromJSON(this.#sessionDataDrawings["Mission drawings"]);
|
|
if (this.#sessionDataNavpoints["Navpoints"]) this.#navpointsContainer.fromJSON(this.#sessionDataNavpoints["Navpoints"]);
|
|
}
|
|
this.#drawingsContainer.setVisibility(getApp().getMap().getOptions().showMissionDrawings);
|
|
this.#navpointsContainer.setVisibility(getApp().getMap().getOptions().showMissionNavpoints);
|
|
});
|
|
|
|
CommandModeOptionsChangedEvent.on((commandOptions) => {
|
|
if (commandOptions.commandMode !== GAME_MASTER) {
|
|
this.restrictCoalitionLayers(commandOptions.commandMode)
|
|
}
|
|
this.restoreHiddenLayers(commandOptions.commandMode);
|
|
})
|
|
}
|
|
|
|
initDrawings(data: { drawings: Record<string, Record<string, any>> }): boolean {
|
|
if (data && data.drawings) {
|
|
if (data.drawings.navpoints) {
|
|
this.#navpointsContainer.initFromData(data.drawings.navpoints);
|
|
delete data.drawings.navpoints;
|
|
}
|
|
this.#drawingsContainer.initFromData(data.drawings);
|
|
if (this.#sessionDataDrawings["Mission drawings"]) this.#drawingsContainer.fromJSON(this.#sessionDataDrawings["Mission drawings"]);
|
|
this.#drawingsContainer.setVisibility(getApp().getMap().getOptions().showMissionDrawings);
|
|
DrawingsInitEvent.dispatch(this.#drawingsContainer, this.#navpointsContainer);
|
|
this.#initialized = true;
|
|
return true;
|
|
} else {
|
|
console.error("Error while initializing drawings, empty data");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private restrictContainer(containerName: string, targetContainer: any, hiddenKey: string) {
|
|
const container = targetContainer.getSubContainers().find(c => c.getName().toLowerCase() === containerName.toLowerCase());
|
|
if (container) {
|
|
this.#hiddenContainers[hiddenKey] = {
|
|
parent: container['#parent'],
|
|
container: container
|
|
};
|
|
container.setVisibility(false);
|
|
targetContainer.removeSubContainer(container);
|
|
}
|
|
}
|
|
|
|
restrictCoalitionLayers(playerRole: string) {
|
|
if (playerRole === RED_COMMANDER) {
|
|
this.restrictContainer('Blue', this.#drawingsContainer, 'blue_drawings');
|
|
this.restrictContainer('blue', this.#navpointsContainer, 'blue_navpoints');
|
|
} else {
|
|
this.restrictContainer('Red', this.#drawingsContainer, 'red_drawings');
|
|
this.restrictContainer('red', this.#navpointsContainer, 'red_navpoints');
|
|
}
|
|
}
|
|
|
|
private restoreContainer(key: string, targetContainer: any) {
|
|
if (this.#hiddenContainers[key]) {
|
|
const container = this.#hiddenContainers[key].container;
|
|
targetContainer.addSubContainer(container);
|
|
container.setVisibility(true);
|
|
}
|
|
}
|
|
|
|
restoreHiddenLayers(playerRole: string) {
|
|
const roleContainers: Record<string, string[]> = {
|
|
[RED_COMMANDER]: ['red_drawings', 'red_navpoints'],
|
|
[BLUE_COMMANDER]: ['blue_drawings', 'blue_navpoints'],
|
|
[GAME_MASTER]: ['red_drawings', 'red_navpoints', 'blue_drawings', 'blue_navpoints']
|
|
};
|
|
|
|
roleContainers[playerRole]?.forEach((key) => {
|
|
const targetContainer = key.includes('drawings') ? this.#drawingsContainer : this.#navpointsContainer;
|
|
this.restoreContainer(key, targetContainer);
|
|
});
|
|
}
|
|
|
|
getDrawingsContainer() {
|
|
return this.#drawingsContainer;
|
|
}
|
|
|
|
getNavpointsContainer() {
|
|
return this.#navpointsContainer;
|
|
}
|
|
|
|
requestUpdateEventDispatch() {
|
|
if (this.#updateEventRequested) return;
|
|
this.#updateEventRequested = true;
|
|
window.setTimeout(() => {
|
|
this.#updateEventRequested = false;
|
|
DrawingsUpdatedEvent.dispatch();
|
|
}, 100);
|
|
}
|
|
}
|