Completed advanced settings panel for tanker and AWACS

Reformatted files with larger width, reordered unused icons
This commit is contained in:
Pax1601
2024-08-07 15:32:39 +02:00
parent bc5049992a
commit 224dc5a688
158 changed files with 5813 additions and 6404 deletions

View File

@@ -43,18 +43,8 @@ export const states: string[] = [
"land-at-point",
];
export const ROEs: string[] = ["free", "designated", "", "return", "hold"];
export const reactionsToThreat: string[] = [
"none",
"manoeuvre",
"passive",
"evade",
];
export const emissionsCountermeasures: string[] = [
"silent",
"attack",
"defend",
"free",
];
export const reactionsToThreat: string[] = ["none", "manoeuvre", "passive", "evade"];
export const emissionsCountermeasures: string[] = ["silent", "attack", "defend", "free"];
export const ERAS = [
{
@@ -217,17 +207,11 @@ export const mapBounds = {
zoom: 5,
},
MarianaIslands: {
bounds: new LatLngBounds(
[10.5777778, 135.7477778],
[22.5127778, 149.5427778]
),
bounds: new LatLngBounds([10.5777778, 135.7477778], [22.5127778, 149.5427778]),
zoom: 5,
},
Nevada: {
bounds: new LatLngBounds(
[34.4037128, -119.7806729],
[39.7372411, -112.1130805]
),
bounds: new LatLngBounds([34.4037128, -119.7806729], [39.7372411, -112.1130805]),
zoom: 5,
},
PersianGulf: {
@@ -239,10 +223,7 @@ export const mapBounds = {
zoom: 4,
},
Falklands: {
bounds: new LatLngBounds(
[-49.097217, -79.418267],
[-56.874517, -43.316433]
),
bounds: new LatLngBounds([-49.097217, -79.418267], [-56.874517, -43.316433]),
zoom: 3,
},
Normandy: { bounds: new LatLngBounds([50.44, -3.29], [48.12, 3.7]), zoom: 5 },
@@ -269,8 +250,7 @@ export const IADSDensities: { [key: string]: number } = {
"SAM Site": 0.1,
"Radar (EWR)": 0.05,
};
export const GROUND_UNIT_AIR_DEFENCE_REGEX: RegExp =
/(\b(AAA|SAM|MANPADS?|[mM]anpads?)|[sS]tinger\b)/;
export const GROUND_UNIT_AIR_DEFENCE_REGEX: RegExp = /(\b(AAA|SAM|MANPADS?|[mM]anpads?)|[sS]tinger\b)/;
export const MAP_OPTIONS_TOOLTIPS = {
hideGroupMembers: "Hide group members when zoomed out",
@@ -293,7 +273,7 @@ export const MAP_OPTIONS_DEFAULTS = {
showUnitsEngagementRings: true,
showUnitsAcquisitionRings: true,
fillSelectedRing: false,
showMinimap: false
showMinimap: false,
} as MapOptions;
export const MAP_HIDDEN_TYPES_DEFAULTS = {

View File

@@ -27,10 +27,7 @@ interface CustomEventMap {
declare global {
interface Document {
addEventListener<K extends keyof CustomEventMap>(
type: K,
listener: (this: Document, ev: CustomEventMap[K]) => void
): void;
addEventListener<K extends keyof CustomEventMap>(type: K, listener: (this: Document, ev: CustomEventMap[K]) => void): void;
dispatchEvent<K extends keyof CustomEventMap>(ev: CustomEventMap[K]): void;
}

View File

@@ -53,10 +53,7 @@ export var BoxSelect = Handler.extend({
},
_onMouseDown: function (e: any) {
if (
(e.which == 1 && e.button == 0 && (e.shiftKey || this._forceBoxSelect)) ||
(e.type === "touchstart" && this._forceBoxSelect)
) {
if ((e.which == 1 && e.button == 0 && (e.shiftKey || this._forceBoxSelect)) || (e.type === "touchstart" && this._forceBoxSelect)) {
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.
@@ -67,10 +64,8 @@ export var BoxSelect = Handler.extend({
DomUtil.disableImageDrag();
this._map.dragging.disable();
if (e.type === "touchstart")
this._startPoint = this._map.mouseEventToContainerPoint(e.touches[0]);
else
this._startPoint = this._map.mouseEventToContainerPoint(e);
if (e.type === "touchstart") this._startPoint = this._map.mouseEventToContainerPoint(e.touches[0]);
else this._startPoint = this._map.mouseEventToContainerPoint(e);
DomEvent.on(
//@ts-ignore
@@ -101,10 +96,8 @@ export var BoxSelect = Handler.extend({
this._map.fire("boxzoomstart");
}
if (e.type === "touchmove")
this._point = this._map.mouseEventToContainerPoint(e.touches[0]);
else
this._point = this._map.mouseEventToContainerPoint(e);
if (e.type === "touchmove") this._point = this._map.mouseEventToContainerPoint(e.touches[0]);
else this._point = this._map.mouseEventToContainerPoint(e);
var bounds = new Bounds(this._point, this._startPoint),
size = bounds.getSize();
@@ -154,10 +147,7 @@ export var BoxSelect = Handler.extend({
// 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)
);
var bounds = new LatLngBounds(this._map.containerPointToLatLng(this._startPoint), this._map.containerPointToLatLng(this._point));
this._forceBoxSelect = false;
this._map.fire("selectionend", { selectionBounds: bounds });

View File

@@ -1,5 +0,0 @@
const CoalitionArea = Base => class extends Base {
}
export CoalitionArea;

View File

@@ -7,7 +7,7 @@ export class CoalitionAreaHandle extends CustomMarker {
this.on("add", (e) => {
this.getElement()?.addEventListener("touchstart", (e) => e.stopPropagation());
})
});
}
createIcon() {

View File

@@ -1,12 +1,4 @@
import {
LatLngExpression,
Map,
Circle,
DivIcon,
Marker,
CircleOptions,
LatLng,
} from "leaflet";
import { LatLngExpression, Map, Circle, DivIcon, Marker, CircleOptions, LatLng } from "leaflet";
import { getApp } from "../../olympusapp";
import { CoalitionAreaHandle } from "./coalitionareahandle";
import { BLUE_COMMANDER, RED_COMMANDER } from "../../constants/constants";
@@ -38,11 +30,7 @@ export class CoalitionCircle extends Circle {
this.#labelText = `Circle ${totalAreas}`;
if (
[BLUE_COMMANDER, RED_COMMANDER].includes(
getApp().getMissionManager().getCommandModeOptions().commandMode
)
)
if ([BLUE_COMMANDER, RED_COMMANDER].includes(getApp().getMissionManager().getCommandModeOptions().commandMode))
this.setCoalition(getApp().getMissionManager().getCommandedCoalition());
this.on("drag", () => {
@@ -129,14 +117,8 @@ export class CoalitionCircle extends Circle {
if (this.#radiusHandle) this.#radiusHandle.removeFrom(getApp().getMap());
if (this.#selected) {
const dest = turf.destination(
turf.point([this.getLatLng().lng, this.getLatLng().lat]),
this.getRadius() / 1000,
0
);
this.#radiusHandle = new CoalitionAreaHandle(
new LatLng(dest.geometry.coordinates[1], dest.geometry.coordinates[0])
);
const dest = turf.destination(turf.point([this.getLatLng().lng, this.getLatLng().lat]), this.getRadius() / 1000, 0);
this.#radiusHandle = new CoalitionAreaHandle(new LatLng(dest.geometry.coordinates[1], dest.geometry.coordinates[0]));
this.#radiusHandle.addTo(getApp().getMap());
this.#radiusHandle.on("drag", (e: any) => {
this.setRadius(this.getLatLng().distanceTo(e.latlng));
@@ -156,11 +138,6 @@ export class CoalitionCircle extends Circle {
}),
interactive: false,
}).addTo(this._map);
this.#label
.getElement()
?.classList.add(
`ol-coalitionarea-label`,
`${this.#selected ? "selected" : `${this.#coalition}`}`
);
this.#label.getElement()?.classList.add(`ol-coalitionarea-label`, `${this.#selected ? "selected" : `${this.#coalition}`}`);
}
}

View File

@@ -1,13 +1,4 @@
import {
LatLng,
LatLngExpression,
Map,
Point,
Polygon,
PolylineOptions,
DivIcon,
Marker
} from "leaflet";
import { LatLng, LatLngExpression, Map, Point, Polygon, PolylineOptions, DivIcon, Marker } from "leaflet";
import { getApp } from "../../olympusapp";
import { CoalitionAreaHandle } from "./coalitionareahandle";
import { CoalitionAreaMiddleHandle } from "./coalitionareamiddlehandle";
@@ -27,10 +18,7 @@ export class CoalitionPolygon extends Polygon {
#labelText: string;
#label: Marker;
constructor(
latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][],
options?: PolylineOptions
) {
constructor(latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][], options?: PolylineOptions) {
if (options === undefined) options = {};
totalAreas++;
@@ -45,11 +33,7 @@ export class CoalitionPolygon extends Polygon {
this.#labelText = `Polygon ${totalAreas}`;
if (
[BLUE_COMMANDER, RED_COMMANDER].includes(
getApp().getMissionManager().getCommandModeOptions().commandMode
)
)
if ([BLUE_COMMANDER, RED_COMMANDER].includes(getApp().getMissionManager().getCommandModeOptions().commandMode))
this.setCoalition(getApp().getMissionManager().getCommandedCoalition());
this.on("drag", () => {
@@ -130,17 +114,11 @@ export class CoalitionPolygon extends Polygon {
onRemove(map: Map): this {
super.onRemove(map);
this.#handles
.concat(this.#middleHandles)
.forEach((handle: CoalitionAreaHandle | CoalitionAreaMiddleHandle) =>
handle.removeFrom(getApp().getMap())
);
this.#handles.concat(this.#middleHandles).forEach((handle: CoalitionAreaHandle | CoalitionAreaMiddleHandle) => handle.removeFrom(getApp().getMap()));
return this;
}
setLatLngs(
latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][]
) {
setLatLngs(latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][]) {
super.setLatLngs(latlngs);
this.#drawLabel();
return this;
@@ -158,9 +136,7 @@ export class CoalitionPolygon extends Polygon {
}
#setHandles() {
this.#handles.forEach((handle: CoalitionAreaHandle) =>
handle.removeFrom(getApp().getMap())
);
this.#handles.forEach((handle: CoalitionAreaHandle) => handle.removeFrom(getApp().getMap()));
this.#handles = [];
if (this.getSelected()) {
var latlngs = this.getLatLngs()[0] as LatLng[];
@@ -181,9 +157,7 @@ export class CoalitionPolygon extends Polygon {
}
#setMiddleHandles() {
this.#middleHandles.forEach((handle: CoalitionAreaMiddleHandle) =>
handle.removeFrom(getApp().getMap())
);
this.#middleHandles.forEach((handle: CoalitionAreaMiddleHandle) => handle.removeFrom(getApp().getMap()));
this.#middleHandles = [];
var latlngs = this.getLatLngs()[0] as LatLng[];
if (this.getSelected() && latlngs.length >= 2) {
@@ -193,13 +167,8 @@ export class CoalitionPolygon extends Polygon {
if (lastLatLng != null) {
const handle1Point = getApp().getMap().latLngToLayerPoint(latlng);
const handle2Point = getApp().getMap().latLngToLayerPoint(lastLatLng);
const middlePoint = new Point(
(handle1Point.x + handle2Point.x) / 2,
(handle1Point.y + handle2Point.y) / 2
);
const middleLatLng = getApp()
.getMap()
.layerPointToLatLng(middlePoint);
const middlePoint = new Point((handle1Point.x + handle2Point.x) / 2, (handle1Point.y + handle2Point.y) / 2);
const middleLatLng = getApp().getMap().layerPointToLatLng(middlePoint);
const middleHandle = new CoalitionAreaMiddleHandle(middleLatLng);
middleHandle.addTo(getApp().getMap());
@@ -228,12 +197,7 @@ export class CoalitionPolygon extends Polygon {
}),
interactive: false,
}).addTo(this._map);
this.#label
.getElement()
?.classList.add(
`ol-coalitionarea-label`,
`${this.#selected ? "selected" : `${this.#coalition}`}`
);
this.#label.getElement()?.classList.add(`ol-coalitionarea-label`, `${this.#selected ? "selected" : `${this.#coalition}`}`);
}
}
}

View File

@@ -1,6 +1,4 @@
import * as L from "leaflet";
export const initDraggablePath = () => {
export const initDraggablePath = (L) => {
//@ts-ignore
L.PathDraggable = L.Draggable.extend({
initialize: function (path) {
@@ -8,9 +6,7 @@ export const initDraggablePath = () => {
this._canvas = path._map.getRenderer(path) instanceof L.Canvas;
var element = this._canvas
? this._path._map.getRenderer(this._path)._container
: this._path._path;
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);
@@ -27,12 +23,7 @@ export const initDraggablePath = () => {
this._startPoint = new L.Point(first.clientX, first.clientY);
if (
this._canvas &&
!this._path._containsPoint(
this._path._map.mouseEventToLayerPoint(first)
)
) {
if (this._canvas && !this._path._containsPoint(this._path._map.mouseEventToLayerPoint(first))) {
return;
}
@@ -87,10 +78,7 @@ export const initDraggablePath = () => {
_onDrag: function (e) {
var path = this._path,
event =
e.originalEvent.touches && e.originalEvent.touches.length === 1
? e.originalEvent.touches[0]
: e.originalEvent,
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);
@@ -108,9 +96,7 @@ export const initDraggablePath = () => {
path.fire("drag", e);
e.latlng = this._path.getCenter
? this._path.getCenter()
: this._path.getLatLng();
e.latlng = this._path.getCenter ? this._path.getCenter() : this._path.getLatLng();
path.fire("move", e);
},

View File

@@ -3,12 +3,7 @@ import { getApp } from "../olympusapp";
import { BoxSelect } from "./boxselect";
import { Airbase } from "../mission/airbase";
import { Unit } from "../unit/unit";
import {
areaContains,
deg2rad,
getFunctionArguments,
getGroundElevation,
} from "../other/utils";
import { areaContains, deg2rad, getFunctionArguments, getGroundElevation } from "../other/utils";
import { TemporaryUnitMarker } from "./markers/temporaryunitmarker";
import { ClickableMiniMap } from "./clickableminimap";
import {
@@ -38,18 +33,12 @@ import "./map.css";
import { CoalitionCircle } from "./coalitionarea/coalitioncircle";
import { initDraggablePath } from "./coalitionarea/draggablepath";
import {
faComputerMouse,
faDrawPolygon,
faHandPointer,
faJetFighter,
faMap,
} from "@fortawesome/free-solid-svg-icons";
import { faComputerMouse, faDrawPolygon, faHandPointer, faJetFighter, faMap } from "@fortawesome/free-solid-svg-icons";
/* Register the handler for the box selection */
L.Map.addInitHook("addHandler", "boxSelect", BoxSelect);
initDraggablePath();
initDraggablePath(L);
export class Map extends L.Map {
/* Options */
@@ -141,10 +130,10 @@ export class Map extends L.Map {
this.setView([37.23, -115.8], 10);
/* Minimap */
var minimapLayer = new L.TileLayer(
"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
{ minZoom: 0, maxZoom: 13 }
);
var minimapLayer = new L.TileLayer("https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", {
minZoom: 0,
maxZoom: 13,
});
this.#miniMapLayerGroup = new L.LayerGroup([minimapLayer]);
this.#miniMapPolyline = new L.Polyline([], { color: "#202831" });
this.#miniMapPolyline.addTo(this.#miniMapLayerGroup);
@@ -181,15 +170,11 @@ export class Map extends L.Map {
/* Event listeners */
document.addEventListener("hiddenTypesChanged", (ev: CustomEventInit) => {
Object.values(getApp().getUnitsManager().getUnits()).forEach(
(unit: Unit) => unit.updateVisibility()
);
Object.values(getApp().getMissionManager().getAirbases()).forEach(
(airbase: Airbase) => {
if (this.getHiddenTypes().airbase) airbase.removeFrom(this);
else airbase.addTo(this);
}
);
Object.values(getApp().getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
Object.values(getApp().getMissionManager().getAirbases()).forEach((airbase: Airbase) => {
if (this.getHiddenTypes().airbase) airbase.removeFrom(this);
else airbase.addTo(this);
});
});
//document.addEventListener("unitUpdated", (ev: CustomEvent) => {
@@ -198,10 +183,7 @@ export class Map extends L.Map {
//});
document.addEventListener("mapOptionsChanged", () => {
this.getContainer().toggleAttribute(
"data-hide-labels",
!this.getOptions().showUnitLabels
);
this.getContainer().toggleAttribute("data-hide-labels", !this.getOptions().showUnitLabels);
//this.#cameraControlPort = this.getOptions()[DCS_LINK_PORT] as number;
//this.#cameraZoomRatio = 50 / (20 + (this.getOptions()[DCS_LINK_RATIO] as number));
@@ -263,12 +245,8 @@ export class Map extends L.Map {
if (this.#panUp || this.#panDown || this.#panRight || this.#panLeft)
this.panBy(
new L.Point(
((this.#panLeft ? -1 : 0) + (this.#panRight ? 1 : 0)) *
this.defaultPanDelta *
(this.#isShiftKeyDown ? 3 : 1),
((this.#panUp ? -1 : 0) + (this.#panDown ? 1 : 0)) *
this.defaultPanDelta *
(this.#isShiftKeyDown ? 3 : 1)
((this.#panLeft ? -1 : 0) + (this.#panRight ? 1 : 0)) * this.defaultPanDelta * (this.#isShiftKeyDown ? 3 : 1),
((this.#panUp ? -1 : 0) + (this.#panDown ? 1 : 0)) * this.defaultPanDelta * (this.#isShiftKeyDown ? 3 : 1)
)
);
}, 20);
@@ -289,10 +267,7 @@ export class Map extends L.Map {
const layerData = this.#mapLayers[layerName];
if (layerData instanceof Array) {
let layers = layerData.map((layer: any) => {
return new L.TileLayer(
layer.urlTemplate.replace("{theatre}", theatre.toLowerCase()),
layer
);
return new L.TileLayer(layer.urlTemplate.replace("{theatre}", theatre.toLowerCase()), layer);
});
this.#layer = new L.LayerGroup(layers);
this.#layer?.addTo(this);
@@ -306,13 +281,10 @@ export class Map extends L.Map {
let layers: L.TileLayer[] = [];
layers.push(
new L.TileLayer(
"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
{
minZoom: 1,
maxZoom: 19,
}
)
new L.TileLayer("https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", {
minZoom: 1,
maxZoom: 19,
})
);
/* Load the configuration file */
@@ -348,9 +320,7 @@ export class Map extends L.Map {
}
this.#layerName = layerName;
document.dispatchEvent(
new CustomEvent("mapSourceChanged", { detail: layerName })
);
document.dispatchEvent(new CustomEvent("mapSourceChanged", { detail: layerName }));
}
getLayerName() {
@@ -372,11 +342,7 @@ export class Map extends L.Map {
console.log(`Switching from state ${this.#state} to ${state}`);
/* Operations to perform when leaving a state */
if (
this.#state === COALITIONAREA_DRAW_POLYGON ||
this.#state === COALITIONAREA_DRAW_CIRCLE
)
this.getSelectedCoalitionArea()?.setEditing(false);
if (this.#state === COALITIONAREA_DRAW_POLYGON || this.#state === COALITIONAREA_DRAW_CIRCLE) this.getSelectedCoalitionArea()?.setEditing(false);
this.#state = state;
@@ -400,15 +366,11 @@ export class Map extends L.Map {
this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
} else if (this.#state === COALITIONAREA_DRAW_CIRCLE) {
getApp().getUnitsManager().deselectAllUnits();
this.#coalitionAreas.push(
new CoalitionCircle(new L.LatLng(0, 0), { radius: 1000 })
);
this.#coalitionAreas.push(new CoalitionCircle(new L.LatLng(0, 0), { radius: 1000 }));
this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
}
document.dispatchEvent(
new CustomEvent("mapStateChanged", { detail: this.#state })
);
document.dispatchEvent(new CustomEvent("mapStateChanged", { detail: this.#state }));
}
getState() {
@@ -449,10 +411,7 @@ export class Map extends L.Map {
text: "Spawn unit",
},
{
actions: [
touch ? faHandPointer : faComputerMouse,
touch ? faHandPointer : faComputerMouse,
],
actions: [touch ? faHandPointer : faComputerMouse, touch ? faHandPointer : faComputerMouse],
target: faMap,
text: "Exit spawn mode",
},
@@ -465,10 +424,7 @@ export class Map extends L.Map {
} else if (this.#state === CONTEXT_ACTION) {
let controls = [
{
actions: [
touch ? faHandPointer : faComputerMouse,
touch ? faHandPointer : faComputerMouse,
],
actions: [touch ? faHandPointer : faComputerMouse, touch ? faHandPointer : faComputerMouse],
target: faMap,
text: "Deselect units",
},
@@ -498,10 +454,7 @@ export class Map extends L.Map {
text: "Select shape",
},
{
actions: [
touch ? faHandPointer : faComputerMouse,
touch ? faHandPointer : faComputerMouse,
],
actions: [touch ? faHandPointer : faComputerMouse, touch ? faHandPointer : faComputerMouse],
target: faMap,
text: "Exit drawing mode",
},
@@ -519,10 +472,7 @@ export class Map extends L.Map {
text: "Add vertex to polygon",
},
{
actions: [
touch ? faHandPointer : faComputerMouse,
touch ? faHandPointer : faComputerMouse,
],
actions: [touch ? faHandPointer : faComputerMouse, touch ? faHandPointer : faComputerMouse],
target: faMap,
text: "Finalize polygon",
},
@@ -557,18 +507,11 @@ export class Map extends L.Map {
})
);
this.#coalitionAreas.forEach(
(coalitionArea: CoalitionPolygon | CoalitionCircle) =>
coalitionArea.setSelected(false)
);
this.#coalitionAreas.forEach((coalitionArea: CoalitionPolygon | CoalitionCircle) => coalitionArea.setSelected(false));
}
deleteCoalitionArea(coalitionArea: CoalitionPolygon | CoalitionCircle) {
if (this.#coalitionAreas.includes(coalitionArea))
this.#coalitionAreas.splice(
this.#coalitionAreas.indexOf(coalitionArea),
1
);
if (this.#coalitionAreas.includes(coalitionArea)) this.#coalitionAreas.splice(this.#coalitionAreas.indexOf(coalitionArea), 1);
if (this.hasLayer(coalitionArea)) this.removeLayer(coalitionArea);
}
@@ -617,9 +560,7 @@ export class Map extends L.Map {
this.updateMinimap();
const boundaries = this.#getMinimapBoundaries();
this.#miniMapPolyline.setLatLngs(
boundaries[theatre as keyof typeof boundaries]
);
this.#miniMapPolyline.setLatLngs(boundaries[theatre as keyof typeof boundaries]);
this.setLayerName(this.#layerName);
}
@@ -697,12 +638,7 @@ export class Map extends L.Map {
}
}
addTemporaryMarker(
latlng: L.LatLng,
name: string,
coalition: string,
commandHash?: string
) {
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);
@@ -710,11 +646,9 @@ export class Map extends L.Map {
}
getSelectedCoalitionArea() {
const coalitionArea = this.#coalitionAreas.find(
(coalitionArea: CoalitionPolygon | CoalitionCircle) => {
return coalitionArea.getSelected();
}
);
const coalitionArea = this.#coalitionAreas.find((coalitionArea: CoalitionPolygon | CoalitionCircle) => {
return coalitionArea.getSelected();
});
return coalitionArea ?? null;
}
@@ -794,10 +728,7 @@ export class Map extends L.Map {
//}
}
executeContextAction(
targetUnit: Unit | null,
targetPosition: L.LatLng | null
) {
executeContextAction(targetUnit: Unit | null, targetPosition: L.LatLng | null) {
this.#contextAction?.executeCallback(targetUnit, targetPosition);
}
@@ -847,8 +778,7 @@ 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.#isDragging && !this.#isZooming)
this.#onLongPress(e);
if (this.#isMouseDown && !this.#isDragging && !this.#isZooming) this.#onLongPress(e);
}, 500);
}
@@ -858,10 +788,7 @@ export class Map extends L.Map {
window.clearTimeout(this.#shortPressTimer);
window.clearTimeout(this.#longPressTimer);
if (
this.#state === COALITIONAREA_DRAW_POLYGON ||
this.#state === COALITIONAREA_DRAW_CIRCLE
) {
if (this.#state === COALITIONAREA_DRAW_POLYGON || this.#state === COALITIONAREA_DRAW_CIRCLE) {
this.setState(COALITIONAREA_EDIT);
} else {
this.setState(IDLE);
@@ -870,10 +797,7 @@ export class Map extends L.Map {
#onShortPress(e: any) {
let touchLocation: L.LatLng;
if (e.type === "touchstart")
touchLocation = this.containerPointToLatLng(
this.mouseEventToContainerPoint(e.touches[0])
);
if (e.type === "touchstart") touchLocation = this.containerPointToLatLng(this.mouseEventToContainerPoint(e.touches[0]));
else touchLocation = new L.LatLng(e.latlng.lat, e.latlng.lng);
console.log(`Short press at ${touchLocation}`);
@@ -893,12 +817,7 @@ export class Map extends L.Map {
undefined,
undefined,
(hash) => {
this.addTemporaryMarker(
touchLocation,
this.#spawnRequestTable?.unit.unitType ?? "unknown",
this.#spawnRequestTable?.coalition ?? "blue",
hash
);
this.addTemporaryMarker(touchLocation, this.#spawnRequestTable?.unit.unitType ?? "unknown", this.#spawnRequestTable?.coalition ?? "blue", hash);
}
);
}
@@ -910,11 +829,7 @@ export class Map extends L.Map {
} else if (this.#state === COALITIONAREA_DRAW_CIRCLE) {
const selectedArea = this.getSelectedCoalitionArea();
if (selectedArea && selectedArea instanceof CoalitionCircle) {
if (
selectedArea.getLatLng().lat == 0 &&
selectedArea.getLatLng().lng == 0
)
selectedArea.setLatLng(touchLocation);
if (selectedArea.getLatLng().lat == 0 && selectedArea.getLatLng().lng == 0) selectedArea.setLatLng(touchLocation);
this.setState(COALITIONAREA_EDIT);
}
} else if (this.#state == COALITIONAREA_EDIT) {
@@ -938,10 +853,7 @@ export class Map extends L.Map {
#onLongPress(e: any) {
let touchLocation: L.LatLng;
if (e.type === "touchstart")
touchLocation = this.containerPointToLatLng(
this.mouseEventToContainerPoint(e.touches[0])
);
if (e.type === "touchstart") touchLocation = this.containerPointToLatLng(this.mouseEventToContainerPoint(e.touches[0]));
else touchLocation = new L.LatLng(e.latlng.lat, e.latlng.lng);
console.log(`Long press at ${touchLocation}`);
@@ -949,14 +861,8 @@ export class Map extends L.Map {
if (!this.#isDragging && !this.#isZooming) {
this.deselectAllCoalitionAreas();
if (this.#state === IDLE) {
if (e.type === "touchstart")
document.dispatchEvent(
new CustomEvent("mapForceBoxSelect", { detail: e })
);
else
document.dispatchEvent(
new CustomEvent("mapForceBoxSelect", { detail: e.originalEvent })
);
if (e.type === "touchstart") document.dispatchEvent(new CustomEvent("mapForceBoxSelect", { detail: e }));
else document.dispatchEvent(new CustomEvent("mapForceBoxSelect", { detail: e.originalEvent }));
}
}
}
@@ -996,11 +902,7 @@ export class Map extends L.Map {
}
#broadcastPosition() {
if (
this.#bradcastPositionXmlHttp?.readyState !== 4 &&
this.#bradcastPositionXmlHttp !== null
)
return;
if (this.#bradcastPositionXmlHttp?.readyState !== 4 && this.#bradcastPositionXmlHttp !== null) return;
getGroundElevation(this.getCenter(), (response: string) => {
var groundElevation: number | null = null;
@@ -1009,18 +911,12 @@ export class Map extends L.Map {
this.#bradcastPositionXmlHttp = new XMLHttpRequest();
/* Using 127.0.0.1 instead of localhost because the LuaSocket version used in DCS only listens to IPv4. This avoids the lag caused by the
browser if it first tries to send the request on the IPv6 address for localhost */
this.#bradcastPositionXmlHttp.open(
"POST",
`http://127.0.0.1:${this.#cameraControlPort}`
);
this.#bradcastPositionXmlHttp.open("POST", `http://127.0.0.1:${this.#cameraControlPort}`);
const C = 40075016.686;
let mpp =
(C * Math.cos(deg2rad(this.getCenter().lat))) /
Math.pow(2, this.getZoom() + 8);
let mpp = (C * Math.cos(deg2rad(this.getCenter().lat))) / Math.pow(2, this.getZoom() + 8);
let d = mpp * 1920;
let alt =
(((d / 2) * 1) / Math.tan(deg2rad(40))) * this.#cameraZoomRatio;
let alt = (((d / 2) * 1) / Math.tan(deg2rad(40))) * this.#cameraZoomRatio;
alt = Math.min(alt, 50000);
this.#bradcastPositionXmlHttp.send(
JSON.stringify({
@@ -1038,10 +934,7 @@ export class Map extends L.Map {
/* */
#panToUnit(unit: Unit) {
var unitPosition = new L.LatLng(
unit.getPosition().lat,
unit.getPosition().lng
);
var unitPosition = new L.LatLng(unit.getPosition().lat, unit.getPosition().lng);
this.setView(unitPosition, this.getZoom(), { animate: false });
}
@@ -1067,23 +960,15 @@ export class Map extends L.Map {
/* Check if the camera control plugin is available. Right now this will only change the color of the button, no changes in functionality */
#checkCameraPort() {
if (this.#cameraOptionsXmlHttp?.readyState !== 4)
this.#cameraOptionsXmlHttp?.abort();
if (this.#cameraOptionsXmlHttp?.readyState !== 4) this.#cameraOptionsXmlHttp?.abort();
this.#cameraOptionsXmlHttp = new XMLHttpRequest();
/* Using 127.0.0.1 instead of localhost because the LuaSocket version used in DCS only listens to IPv4. This avoids the lag caused by the
browser if it first tries to send the request on the IPv6 address for localhost */
this.#cameraOptionsXmlHttp.open(
"OPTIONS",
`http://127.0.0.1:${this.#cameraControlPort}`
);
this.#cameraOptionsXmlHttp.open("OPTIONS", `http://127.0.0.1:${this.#cameraControlPort}`);
this.#cameraOptionsXmlHttp.onload = (res: any) => {
if (
this.#cameraOptionsXmlHttp !== null &&
this.#cameraOptionsXmlHttp.status == 204
)
this.#setSlaveDCSCameraAvailable(true);
if (this.#cameraOptionsXmlHttp !== null && this.#cameraOptionsXmlHttp.status == 204) this.#setSlaveDCSCameraAvailable(true);
else this.#setSlaveDCSCameraAvailable(false);
};
this.#cameraOptionsXmlHttp.onerror = (res: any) => {

View File

@@ -6,11 +6,7 @@ import { getApp } from "../../olympusapp";
export class SmokeMarker extends CustomMarker {
#color: string;
constructor(
latlng: LatLngExpression,
color: string,
options?: MarkerOptions
) {
constructor(latlng: LatLngExpression, color: string, options?: MarkerOptions) {
super(latlng, options);
this.setZIndexOffset(9999);
this.#color = color;

View File

@@ -1,10 +1,7 @@
import { CustomMarker } from "./custommarker";
import { DivIcon, LatLng } from "leaflet";
import { SVGInjector } from "@tanem/svg-injector";
import {
getMarkerCategoryByName,
getUnitDatabaseByCategory,
} from "../../other/utils";
import { getMarkerCategoryByName, getUnitDatabaseByCategory } from "../../other/utils";
import { getApp } from "../../olympusapp";
export class TemporaryUnitMarker extends CustomMarker {
@@ -13,12 +10,7 @@ export class TemporaryUnitMarker extends CustomMarker {
#commandHash: string | undefined = undefined;
#timer: number = 0;
constructor(
latlng: LatLng,
name: string,
coalition: string,
commandHash?: string
) {
constructor(latlng: LatLng, name: string, coalition: string, commandHash?: string) {
super(latlng, { interactive: false });
this.#name = name;
this.#coalition = coalition;
@@ -45,9 +37,7 @@ export class TemporaryUnitMarker extends CustomMarker {
createIcon() {
const category = getMarkerCategoryByName(this.#name);
const databaseEntry = getUnitDatabaseByCategory(category)?.getByName(
this.#name
);
const databaseEntry = getUnitDatabaseByCategory(category)?.getByName(this.#name);
/* Set the icon */
var icon = new DivIcon({

View File

@@ -26,14 +26,8 @@ export class RangeCircle extends Circle {
let pathBegun = false;
let dtheta = (Math.PI * 2) / 120;
for (let theta = 0; theta <= Math.PI * 2; theta += dtheta) {
let p1 = new Point(
p.x + r * Math.cos(theta),
p.y / s + r * Math.sin(theta)
);
let p2 = new Point(
p.x + r * Math.cos(theta + dtheta),
p.y / s + r * Math.sin(theta + dtheta)
);
let p1 = new Point(p.x + r * Math.cos(theta), p.y / s + r * Math.sin(theta));
let p2 = new Point(p.x + r * Math.cos(theta + dtheta), p.y / s + r * Math.sin(theta + dtheta));
let l1 = this._map.layerPointToLatLng(p1);
let l2 = this._map.layerPointToLatLng(p2);
let line = new Polyline([l1, l2]);

View File

@@ -43,23 +43,17 @@ export class Airbase extends CustomMarker {
el.appendChild(img);
this.getElement()?.appendChild(el);
el.addEventListener("mouseover", (ev) => {
document.dispatchEvent(
new CustomEvent("airbaseMouseover", { detail: this })
);
document.dispatchEvent(new CustomEvent("airbaseMouseover", { detail: this }));
});
el.addEventListener("mouseout", (ev) => {
document.dispatchEvent(
new CustomEvent("airbaseMouseout", { detail: this })
);
document.dispatchEvent(new CustomEvent("airbaseMouseout", { detail: this }));
});
el.dataset.coalition = this.#coalition;
}
setCoalition(coalition: string) {
this.#coalition = coalition;
(<HTMLElement>(
this.getElement()?.querySelector(".airbase-icon")
)).dataset.coalition = this.#coalition;
(<HTMLElement>this.getElement()?.querySelector(".airbase-icon")).dataset.coalition = this.#coalition;
}
getChartData() {

View File

@@ -25,9 +25,7 @@ export class Bullseye extends CustomMarker {
setCoalition(coalition: string) {
this.#coalition = coalition;
(<HTMLElement>(
this.getElement()?.querySelector(".bullseye-icon")
)).dataset.coalition = this.#coalition;
(<HTMLElement>this.getElement()?.querySelector(".bullseye-icon")).dataset.coalition = this.#coalition;
}
getCoalition() {

View File

@@ -2,13 +2,7 @@ import { LatLng } from "leaflet";
import { getApp } from "../olympusapp";
import { Airbase } from "./airbase";
import { Bullseye } from "./bullseye";
import {
BLUE_COMMANDER,
ERAS,
GAME_MASTER,
NONE,
RED_COMMANDER,
} from "../constants/constants";
import { BLUE_COMMANDER, ERAS, GAME_MASTER, NONE, RED_COMMANDER } from "../constants/constants";
//import { Dropdown } from "../controls/dropdown";
import { groundUnitDatabase } from "../unit/databases/groundunitdatabase";
//import { createCheckboxOption, getCheckboxOptions } from "../other/utils";
@@ -16,13 +10,7 @@ import { aircraftDatabase } from "../unit/databases/aircraftdatabase";
import { helicopterDatabase } from "../unit/databases/helicopterdatabase";
import { navyUnitDatabase } from "../unit/databases/navyunitdatabase";
//import { Popup } from "../popups/popup";
import {
AirbasesData,
BullseyesData,
CommandModeOptions,
DateAndTime,
MissionData,
} from "../interfaces";
import { AirbasesData, BullseyesData, CommandModeOptions, DateAndTime, MissionData } from "../interfaces";
import { Coalition } from "../types/types";
/** The MissionManager */
@@ -53,18 +41,11 @@ export class MissionManager {
#coalitions: { red: string[]; blue: string[] } = { red: [], blue: [] };
constructor() {
document.addEventListener("applycommandModeOptions", () =>
this.#applycommandModeOptions()
);
document.addEventListener("showCommandModeDialog", () =>
this.showCommandModeDialog()
);
document.addEventListener(
"toggleSpawnRestrictions",
(ev: CustomEventInit) => {
this.#toggleSpawnRestrictions(ev.detail._element.checked);
}
);
document.addEventListener("applycommandModeOptions", () => this.#applycommandModeOptions());
document.addEventListener("showCommandModeDialog", () => this.showCommandModeDialog());
document.addEventListener("toggleSpawnRestrictions", (ev: CustomEventInit) => {
this.#toggleSpawnRestrictions(ev.detail._element.checked);
});
/* command-mode settings dialog */
//this.#commandModeDialog = document.querySelector("#command-mode-settings-dialog") as HTMLElement;
@@ -76,27 +57,19 @@ export class MissionManager {
* @param object <BulleyesData>
*/
updateBullseyes(data: BullseyesData) {
const commandMode = getApp()
.getMissionManager()
.getCommandModeOptions().commandMode;
const commandMode = getApp().getMissionManager().getCommandModeOptions().commandMode;
for (let idx in data.bullseyes) {
const bullseye = data.bullseyes[idx];
// Prevent Red and Blue coalitions seeing each other's bulleye(s)
if (
(bullseye.coalition === "red" && commandMode === BLUE_COMMANDER) ||
(bullseye.coalition === "blue" && commandMode === RED_COMMANDER)
) {
if ((bullseye.coalition === "red" && commandMode === BLUE_COMMANDER) || (bullseye.coalition === "blue" && commandMode === RED_COMMANDER)) {
continue;
}
if (!(idx in this.#bullseyes))
this.#bullseyes[idx] = new Bullseye([0, 0]).addTo(getApp().getMap());
if (!(idx in this.#bullseyes)) this.#bullseyes[idx] = new Bullseye([0, 0]).addTo(getApp().getMap());
if (bullseye.latitude && bullseye.longitude && bullseye.coalition) {
this.#bullseyes[idx].setLatLng(
new LatLng(bullseye.latitude, bullseye.longitude)
);
this.#bullseyes[idx].setLatLng(new LatLng(bullseye.latitude, bullseye.longitude));
this.#bullseyes[idx].setCoalition(bullseye.coalition);
}
}
@@ -109,29 +82,17 @@ export class MissionManager {
updateAirbases(data: AirbasesData) {
for (let idx in data.airbases) {
var airbase = data.airbases[idx];
if (
this.#airbases[airbase.callsign] === undefined &&
airbase.callsign != ""
) {
if (this.#airbases[airbase.callsign] === undefined && airbase.callsign != "") {
this.#airbases[airbase.callsign] = new Airbase({
position: new LatLng(airbase.latitude, airbase.longitude),
name: airbase.callsign,
}).addTo(getApp().getMap());
this.#airbases[airbase.callsign].on("contextmenu", (e) =>
this.#onAirbaseClick(e)
);
this.#airbases[airbase.callsign].on("contextmenu", (e) => this.#onAirbaseClick(e));
this.#loadAirbaseChartData(airbase.callsign);
}
if (
this.#airbases[airbase.callsign] != undefined &&
airbase.latitude &&
airbase.longitude &&
airbase.coalition
) {
this.#airbases[airbase.callsign].setLatLng(
new LatLng(airbase.latitude, airbase.longitude)
);
if (this.#airbases[airbase.callsign] != undefined && airbase.latitude && airbase.longitude && airbase.coalition) {
this.#airbases[airbase.callsign].setLatLng(new LatLng(airbase.latitude, airbase.longitude));
this.#airbases[airbase.callsign].setCoalition(airbase.coalition);
}
}
@@ -159,23 +120,15 @@ export class MissionManager {
/* Set the command mode options */
this.#setcommandModeOptions(data.mission.commandModeOptions);
this.#remainingSetupTime =
this.getCommandModeOptions().setupTime -
this.getDateAndTime().elapsedTime;
var commandModePhaseEl = document.querySelector(
"#command-mode-phase"
) as HTMLElement;
this.#remainingSetupTime = this.getCommandModeOptions().setupTime - this.getDateAndTime().elapsedTime;
var commandModePhaseEl = document.querySelector("#command-mode-phase") as HTMLElement;
if (commandModePhaseEl) {
if (this.#remainingSetupTime > 0) {
var remainingTime = `-${new Date(this.#remainingSetupTime * 1000).toISOString().substring(14, 19)}`;
commandModePhaseEl.dataset.remainingTime = remainingTime;
}
commandModePhaseEl.classList.toggle(
"setup-phase",
this.#remainingSetupTime > 0 &&
this.getCommandModeOptions().restrictSpawns
);
commandModePhaseEl.classList.toggle("setup-phase", this.#remainingSetupTime > 0 && this.getCommandModeOptions().restrictSpawns);
//commandModePhaseEl.classList.toggle("game-commenced", this.#remainingSetupTime <= 0 || !this.getCommandModeOptions().restrictSpawns);
//commandModePhaseEl.classList.toggle("no-restrictions", !this.getCommandModeOptions().restrictSpawns);
}
@@ -239,24 +192,15 @@ export class MissionManager {
}
getAvailableSpawnPoints() {
if (this.getCommandModeOptions().commandMode === GAME_MASTER)
return Infinity;
else if (this.getCommandModeOptions().commandMode === BLUE_COMMANDER)
return (
this.getCommandModeOptions().spawnPoints.blue - this.#spentSpawnPoint
);
else if (this.getCommandModeOptions().commandMode === RED_COMMANDER)
return (
this.getCommandModeOptions().spawnPoints.red - this.#spentSpawnPoint
);
if (this.getCommandModeOptions().commandMode === GAME_MASTER) return Infinity;
else if (this.getCommandModeOptions().commandMode === BLUE_COMMANDER) return this.getCommandModeOptions().spawnPoints.blue - this.#spentSpawnPoint;
else if (this.getCommandModeOptions().commandMode === RED_COMMANDER) return this.getCommandModeOptions().spawnPoints.red - this.#spentSpawnPoint;
else return 0;
}
getCommandedCoalition() {
if (this.getCommandModeOptions().commandMode === BLUE_COMMANDER)
return "blue" as Coalition;
else if (this.getCommandModeOptions().commandMode === RED_COMMANDER)
return "red" as Coalition;
if (this.getCommandModeOptions().commandMode === BLUE_COMMANDER) return "blue" as Coalition;
else if (this.getCommandModeOptions().commandMode === RED_COMMANDER) return "red" as Coalition;
else return "all" as Coalition;
}
@@ -335,41 +279,27 @@ export class MissionManager {
#setcommandModeOptions(commandModeOptions: CommandModeOptions) {
/* Refresh all the data if we have exited the NONE state */
var requestRefresh = false;
if (
this.#commandModeOptions.commandMode === NONE &&
commandModeOptions.commandMode !== NONE
)
requestRefresh = true;
if (this.#commandModeOptions.commandMode === NONE && commandModeOptions.commandMode !== NONE) requestRefresh = true;
/* Refresh the page if we have lost Game Master priviledges */
if (
this.#commandModeOptions.commandMode === GAME_MASTER &&
commandModeOptions.commandMode !== GAME_MASTER
)
location.reload();
if (this.#commandModeOptions.commandMode === GAME_MASTER && commandModeOptions.commandMode !== GAME_MASTER) location.reload();
/* Check if any option has changed */
var commandModeOptionsChanged =
!commandModeOptions.eras.every((value: string, idx: number) => {
return value === this.getCommandModeOptions().eras[idx];
}) ||
commandModeOptions.spawnPoints.red !==
this.getCommandModeOptions().spawnPoints.red ||
commandModeOptions.spawnPoints.blue !==
this.getCommandModeOptions().spawnPoints.blue ||
commandModeOptions.restrictSpawns !==
this.getCommandModeOptions().restrictSpawns ||
commandModeOptions.restrictToCoalition !==
this.getCommandModeOptions().restrictToCoalition;
commandModeOptions.spawnPoints.red !== this.getCommandModeOptions().spawnPoints.red ||
commandModeOptions.spawnPoints.blue !== this.getCommandModeOptions().spawnPoints.blue ||
commandModeOptions.restrictSpawns !== this.getCommandModeOptions().restrictSpawns ||
commandModeOptions.restrictToCoalition !== this.getCommandModeOptions().restrictToCoalition;
this.#commandModeOptions = commandModeOptions;
this.setSpentSpawnPoints(0);
this.refreshSpawnPoints();
if (commandModeOptionsChanged) {
document.dispatchEvent(
new CustomEvent("commandModeOptionsChanged", { detail: this })
);
document.dispatchEvent(new CustomEvent("commandModeOptionsChanged", { detail: this }));
document.getElementById("command-mode-toolbar")?.classList.remove("hide");
const el = document.getElementById("command-mode");
if (el) {
@@ -380,24 +310,13 @@ export class MissionManager {
document
.querySelector("#spawn-points-container")
?.classList.toggle(
"hide",
this.getCommandModeOptions().commandMode === GAME_MASTER ||
!this.getCommandModeOptions().restrictSpawns
);
document
.querySelector("#command-mode-settings-button")
?.classList.toggle(
"hide",
this.getCommandModeOptions().commandMode !== GAME_MASTER
);
?.classList.toggle("hide", this.getCommandModeOptions().commandMode === GAME_MASTER || !this.getCommandModeOptions().restrictSpawns);
document.querySelector("#command-mode-settings-button")?.classList.toggle("hide", this.getCommandModeOptions().commandMode !== GAME_MASTER);
if (requestRefresh) getApp().getServerManager().refreshAll();
}
#onAirbaseClick(e: any) {
}
#onAirbaseClick(e: any) {}
#loadAirbaseChartData(callsign: string) {
if (!this.#theatre) {
@@ -405,11 +324,7 @@ export class MissionManager {
}
var xhr = new XMLHttpRequest();
xhr.open(
"GET",
`api/airbases/${this.#theatre.toLowerCase()}/${callsign}`,
true
);
xhr.open("GET", `api/airbases/${this.#theatre.toLowerCase()}/${callsign}`, true);
xhr.responseType = "json";
xhr.onload = () => {
var status = xhr.status;

View File

@@ -19,12 +19,7 @@ import { UnitsManager } from "./unit/unitsmanager";
import { WeaponsManager } from "./weapon/weaponsmanager";
import { ServerManager } from "./server/servermanager";
import {
BLUE_COMMANDER,
DEFAULT_CONTEXT,
GAME_MASTER,
RED_COMMANDER,
} from "./constants/constants";
import { BLUE_COMMANDER, DEFAULT_CONTEXT, GAME_MASTER, RED_COMMANDER } from "./constants/constants";
import { aircraftDatabase } from "./unit/databases/aircraftdatabase";
import { helicopterDatabase } from "./unit/databases/helicopterdatabase";
import { groundUnitDatabase } from "./unit/databases/groundunitdatabase";
@@ -96,10 +91,7 @@ export class OlympusApp {
* @param newActiveCoalition
*/
setActiveCoalition(newActiveCoalition: Coalition) {
if (
this.getMissionManager().getCommandModeOptions().commandMode ==
GAME_MASTER
) {
if (this.getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER) {
this.#activeCoalition = newActiveCoalition;
document.dispatchEvent(new CustomEvent("activeCoalitionChanged"));
}
@@ -110,22 +102,10 @@ export class OlympusApp {
* @returns The active coalition
*/
getActiveCoalition(): Coalition {
if (
this.getMissionManager().getCommandModeOptions().commandMode ==
GAME_MASTER
)
return this.#activeCoalition;
if (this.getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER) return this.#activeCoalition;
else {
if (
this.getMissionManager().getCommandModeOptions().commandMode ==
BLUE_COMMANDER
)
return "blue";
else if (
this.getMissionManager().getCommandModeOptions().commandMode ==
RED_COMMANDER
)
return "red";
if (this.getMissionManager().getCommandModeOptions().commandMode == BLUE_COMMANDER) return "blue";
else if (this.getMissionManager().getCommandModeOptions().commandMode == RED_COMMANDER) return "red";
else return "neutral";
}
}
@@ -173,46 +153,32 @@ export class OlympusApp {
this.#weaponsManager = new WeaponsManager();
/* Set the address of the server */
this.getServerManager().setAddress(
window.location.href.split("?")[0].replace("vite/", "")
);
this.getServerManager().setAddress(window.location.href.split("?")[0].replace("vite/", ""));
/* Setup all global events */
this.#setupEvents();
/* Check if we are running the latest version */
const request = new Request(
"https://raw.githubusercontent.com/Pax1601/DCSOlympus/main/version.json"
);
const request = new Request("https://raw.githubusercontent.com/Pax1601/DCSOlympus/main/version.json");
fetch(request)
.then((response) => {
if (response.status === 200) {
return response.json();
} else {
throw new Error(
"Error connecting to Github to retrieve latest version"
);
throw new Error("Error connecting to Github to retrieve latest version");
}
})
.then((res) => {
this.#latestVersion = res["version"];
const latestVersionSpan = document.getElementById(
"latest-version"
) as HTMLElement;
const latestVersionSpan = document.getElementById("latest-version") as HTMLElement;
if (latestVersionSpan) {
latestVersionSpan.innerHTML = this.#latestVersion ?? "Unknown";
latestVersionSpan.classList.toggle(
"new-version",
this.#latestVersion !== VERSION
);
latestVersionSpan.classList.toggle("new-version", this.#latestVersion !== VERSION);
}
});
/* Load the config file from the server */
const configRequest = new Request(
window.location.href.split("?")[0].replace("vite/", "") +
"resources/config"
);
const configRequest = new Request(window.location.href.split("?")[0].replace("vite/", "") + "resources/config");
fetch(configRequest)
.then((response) => {
if (response.status === 200) {

View File

@@ -4,12 +4,7 @@ import { UnitDatabase } from "../unit/databases/unitdatabase";
import { aircraftDatabase } from "../unit/databases/aircraftdatabase";
import { helicopterDatabase } from "../unit/databases/helicopterdatabase";
import { groundUnitDatabase } from "../unit/databases/groundunitdatabase";
import {
ROEs,
emissionsCountermeasures,
reactionsToThreat,
states,
} from "../constants/constants";
import { ROEs, emissionsCountermeasures, reactionsToThreat, states } from "../constants/constants";
import { navyUnitDatabase } from "../unit/databases/navyunitdatabase";
import { DateAndTime, UnitBlueprint } from "../interfaces";
import { Converter } from "usng";
@@ -17,41 +12,27 @@ import { MGRS } from "../types/types";
import { getApp } from "../olympusapp";
import { featureCollection } from "turf";
export function bearing(
lat1: number,
lon1: number,
lat2: number,
lon2: number
) {
export function bearing(lat1: number, lon1: number, lat2: number, lon2: number) {
const φ1 = deg2rad(lat1); // φ, λ in radians
const φ2 = deg2rad(lat2);
const λ1 = deg2rad(lon1); // φ, λ in radians
const λ2 = deg2rad(lon2);
const y = Math.sin(λ2 - λ1) * Math.cos(φ2);
const x =
Math.cos(φ1) * Math.sin(φ2) -
Math.sin(φ1) * Math.cos(φ2) * Math.cos(λ2 - λ1);
const x = Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(λ2 - λ1);
const θ = Math.atan2(y, x);
const brng = (rad2deg(θ) + 360) % 360; // in degrees
return brng;
}
export function distance(
lat1: number,
lon1: number,
lat2: number,
lon2: number
) {
export function distance(lat1: number, lon1: number, lat2: number, lon2: number) {
const R = 6371e3; // metres
const φ1 = deg2rad(lat1); // φ, λ in radians
const φ2 = deg2rad(lat2);
const Δφ = deg2rad(lat2 - lat1);
const Δλ = deg2rad(lon2 - lon1);
const a =
Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const d = R * c; // in metres
@@ -59,25 +40,12 @@ export function distance(
return d;
}
export function bearingAndDistanceToLatLng(
lat: number,
lon: number,
brng: number,
dist: number
) {
export function bearingAndDistanceToLatLng(lat: number, lon: number, brng: number, dist: number) {
const R = 6371e3; // metres
const φ1 = deg2rad(lat); // φ, λ in radians
const λ1 = deg2rad(lon);
const φ2 = Math.asin(
Math.sin(φ1) * Math.cos(dist / R) +
Math.cos(φ1) * Math.sin(dist / R) * Math.cos(brng)
);
const λ2 =
λ1 +
Math.atan2(
Math.sin(brng) * Math.sin(dist / R) * Math.cos(φ1),
Math.cos(dist / R) - Math.sin(φ1) * Math.sin(φ2)
);
const φ2 = Math.asin(Math.sin(φ1) * Math.cos(dist / R) + Math.cos(φ1) * Math.sin(dist / R) * Math.cos(brng));
const λ2 = λ1 + Math.atan2(Math.sin(brng) * Math.sin(dist / R) * Math.cos(φ1), Math.cos(dist / R) - Math.sin(φ1) * Math.sin(φ2));
return new LatLng(rad2deg(φ2), rad2deg(λ2));
}
@@ -89,30 +57,8 @@ export function ConvertDDToDMS(D: number, lng: boolean) {
var sec = (0 | (((D * 60) % 1) * 6000)) / 100;
var dec = Math.round((sec - Math.floor(sec)) * 100);
var sec = Math.floor(sec);
if (lng)
return (
dir +
zeroPad(deg, 3) +
"°" +
zeroPad(min, 2) +
"'" +
zeroPad(sec, 2) +
"." +
zeroPad(dec, 2) +
'"'
);
else
return (
dir +
zeroPad(deg, 2) +
"°" +
zeroPad(min, 2) +
"'" +
zeroPad(sec, 2) +
"." +
zeroPad(dec, 2) +
'"'
);
if (lng) return dir + zeroPad(deg, 3) + "°" + zeroPad(min, 2) + "'" + zeroPad(sec, 2) + "." + zeroPad(dec, 2) + '"';
else return dir + zeroPad(deg, 2) + "°" + zeroPad(min, 2) + "'" + zeroPad(sec, 2) + "." + zeroPad(dec, 2) + '"';
}
export function dataPointMap(container: HTMLElement, data: any) {
@@ -149,10 +95,7 @@ export function generateUUIDv4() {
export function keyEventWasInInput(event: KeyboardEvent) {
const target = event.target;
return (
target instanceof HTMLElement &&
["INPUT", "TEXTAREA"].includes(target.nodeName)
);
return target instanceof HTMLElement && ["INPUT", "TEXTAREA"].includes(target.nodeName);
}
export function reciprocalHeading(heading: number): number {
@@ -167,11 +110,7 @@ export function reciprocalHeading(heading: number): number {
* @param decimal <boolean> whether this is a decimal number or not
*
* */
export const zeroAppend = function (
num: number,
places: number,
decimal: boolean = false
) {
export const zeroAppend = function (num: number, places: number, decimal: boolean = false) {
var string = decimal ? num.toFixed(2) : String(num);
while (string.length < places) {
string = "0" + string;
@@ -213,8 +152,7 @@ export function editDistance(s1: string, s2: string) {
else {
if (j > 0) {
var newValue = costs[j - 1];
if (s1.charAt(i - 1) != s2.charAt(j - 1))
newValue = Math.min(Math.min(newValue, lastValue), costs[j]) + 1;
if (s1.charAt(i - 1) != s2.charAt(j - 1)) newValue = Math.min(Math.min(newValue, lastValue), costs[j]) + 1;
costs[j - 1] = lastValue;
lastValue = newValue;
}
@@ -225,16 +163,9 @@ export function editDistance(s1: string, s2: string) {
return costs[s2.length];
}
export function latLngToMGRS(
lat: number,
lng: number,
precision: number = 4
): MGRS | false {
export function latLngToMGRS(lat: number, lng: number, precision: number = 4): MGRS | false {
if (precision < 0 || precision > 6) {
console.error(
"latLngToMGRS: precision must be a number >= 0 and <= 6. Given precision: " +
precision
);
console.error("latLngToMGRS: precision must be a number >= 0 and <= 6. Given precision: " + precision);
return false;
}
const mgrs = new Converter({}).LLtoMGRS(lat, lng, precision);
@@ -261,10 +192,7 @@ export function latLngToUTM(lat: number, lng: number) {
return new Converter({}).LLtoUTM(lat, lng);
}
export function latLngToMercator(
lat: number,
lng: number
): { x: number; y: number } {
export function latLngToMercator(lat: number, lng: number): { x: number; y: number } {
var rMajor = 6378137; //Equatorial Radius, WGS84
var shift = Math.PI * rMajor;
var x = (lng * shift) / 180;
@@ -279,9 +207,7 @@ export function mercatorToLatLng(x: number, y: number) {
var shift = Math.PI * rMajor;
var lng = (x / shift) * 180.0;
var lat = (y / shift) * 180.0;
lat =
(180 / Math.PI) *
(2 * Math.atan(Math.exp((lat * Math.PI) / 180.0)) - Math.PI / 2.0);
lat = (180 / Math.PI) * (2 * Math.atan(Math.exp((lat * Math.PI) / 180.0)) - Math.PI / 2.0);
return { lng: lng, lat: lat };
}
@@ -325,21 +251,13 @@ export function polyContains(latlng: LatLng, polygon: Polygon) {
return [latlng.lng, latlng.lat];
}),
];
coordinates[0].push([
polygon.getLatLngs()[0][0].lng,
polygon.getLatLngs()[0][0].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);
}
@@ -349,16 +267,10 @@ export function polyCenter(polygon: Polygon) {
return [latlng.lng, latlng.lat];
}),
];
coordinates[0].push([
polygon.getLatLngs()[0][0].lng,
polygon.getLatLngs()[0][0].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 {
@@ -376,10 +288,7 @@ export function randomPointInPoly(polygon: Polygon): LatLng {
return [latlng.lng, latlng.lat];
}),
];
coordinates[0].push([
polygon.getLatLngs()[0][0].lng,
polygon.getLatLngs()[0][0].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);
@@ -410,9 +319,7 @@ export function randomUnitBlueprint(
/* If a specific type or role is provided, use only the blueprints of that type or role */
if (options.type && options.role) {
console.error(
"Can't create random unit if both type and role are provided. Either create by type or by role."
);
console.error("Can't create random unit if both type and role are provided. Either create by type or by role.");
return null;
}
@@ -439,18 +346,14 @@ export function randomUnitBlueprint(
/* Keep only the units that have an era included in the requested values */
if (options.eras) {
unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => {
return unitBlueprint.era
? options.eras?.includes(unitBlueprint.era)
: true;
return unitBlueprint.era ? options.eras?.includes(unitBlueprint.era) : true;
});
}
/* Keep only the units that have the correct coalition, if selected */
if (options.coalition) {
unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => {
return unitBlueprint.coalition && unitBlueprint.coalition !== ""
? options.coalition === unitBlueprint.coalition
: true;
return unitBlueprint.coalition && unitBlueprint.coalition !== "" ? options.coalition === unitBlueprint.coalition : true;
});
}
@@ -463,8 +366,7 @@ export function getMarkerCategoryByName(name: string) {
else if (helicopterDatabase.getByName(name) != null) return "helicopter";
else if (groundUnitDatabase.getByName(name) != null) {
var type = groundUnitDatabase.getByName(name)?.type ?? "";
if (/\bAAA|SAM\b/.test(type) || /\bmanpad|stinger\b/i.test(type))
return "groundunit-sam";
if (/\bAAA|SAM\b/.test(type) || /\bmanpad|stinger\b/i.test(type)) return "groundunit-sam";
else return "groundunit-other";
} else if (navyUnitDatabase.getByName(name) != null) return "navyunit";
else return "aircraft"; // TODO add other unit types
@@ -473,8 +375,7 @@ export function getMarkerCategoryByName(name: string) {
export function getUnitDatabaseByCategory(category: string) {
if (category.toLowerCase() == "aircraft") return aircraftDatabase;
else if (category.toLowerCase() == "helicopter") return helicopterDatabase;
else if (category.toLowerCase().includes("groundunit"))
return groundUnitDatabase;
else if (category.toLowerCase().includes("groundunit")) return groundUnitDatabase;
else if (category.toLowerCase().includes("navyunit")) return navyUnitDatabase;
else return null;
}
@@ -502,14 +403,12 @@ export function enumToROE(ROE: number) {
}
export function enumToReactionToThreat(reactionToThreat: number) {
if (reactionToThreat < reactionsToThreat.length)
return reactionsToThreat[reactionToThreat];
if (reactionToThreat < reactionsToThreat.length) return reactionsToThreat[reactionToThreat];
else return reactionsToThreat[0];
}
export function enumToEmissioNCountermeasure(emissionCountermeasure: number) {
if (emissionCountermeasure < emissionsCountermeasures.length)
return emissionsCountermeasures[emissionCountermeasure];
if (emissionCountermeasure < emissionsCountermeasures.length) return emissionsCountermeasures[emissionCountermeasure];
else return emissionsCountermeasures[0];
}
@@ -595,25 +494,16 @@ export function getFunctionArguments(func) {
var ARGUMENT_NAMES = /([^\s,]+)/g;
var fnStr = func.toString().replace(STRIP_COMMENTS, "");
var result = fnStr
.slice(fnStr.indexOf("(") + 1, fnStr.indexOf(")"))
.match(ARGUMENT_NAMES);
var result = fnStr.slice(fnStr.indexOf("(") + 1, fnStr.indexOf(")")).match(ARGUMENT_NAMES);
if (result === null) result = [];
return result;
}
export function filterBlueprintsByLabel(
blueprints: { [key: string]: UnitBlueprint },
filterString: string
) {
export function filterBlueprintsByLabel(blueprints: { [key: string]: UnitBlueprint }, filterString: string) {
var filteredBlueprints: { [key: string]: UnitBlueprint } = {};
if (blueprints) {
Object.entries(blueprints).forEach(([key, value]) => {
if (
value.enabled &&
(filterString === "" || value.label.includes(filterString))
)
filteredBlueprints[key] = value;
if (value.enabled && (filterString === "" || value.label.includes(filterString))) filteredBlueprints[key] = value;
});
}
return filteredBlueprints;
@@ -621,35 +511,24 @@ export function filterBlueprintsByLabel(
export function getUnitsByLabel(filterString: string) {
/* Filter aircrafts, helicopters, and navyunits */
const filteredAircraft = filterBlueprintsByLabel(
getApp()?.getAircraftDatabase()?.blueprints,
filterString
);
const filteredHelicopters = filterBlueprintsByLabel(
getApp()?.getHelicopterDatabase()?.blueprints,
filterString
);
const filteredNavyUnits = filterBlueprintsByLabel(
getApp()?.getNavyUnitDatabase()?.blueprints,
filterString
);
const filteredAircraft = filterBlueprintsByLabel(getApp()?.getAircraftDatabase()?.blueprints, filterString);
const filteredHelicopters = filterBlueprintsByLabel(getApp()?.getHelicopterDatabase()?.blueprints, filterString);
const filteredNavyUnits = filterBlueprintsByLabel(getApp()?.getNavyUnitDatabase()?.blueprints, filterString);
/* Split ground units between air defence and all others */
var filteredAirDefense: { [key: string]: UnitBlueprint } = {};
var filteredGroundUnits: { [key: string]: UnitBlueprint } = {};
Object.keys(getApp()?.getGroundUnitDatabase()?.blueprints ?? {}).forEach(
(key) => {
var blueprint = getApp()?.getGroundUnitDatabase()?.blueprints[key];
var type = blueprint.label;
if (/\bAAA|SAM\b/.test(type) || /\bmanpad|stinger\b/i.test(type)) {
filteredAirDefense[key] = blueprint;
} else {
filteredGroundUnits[key] = blueprint;
}
Object.keys(getApp()?.getGroundUnitDatabase()?.blueprints ?? {}).forEach((key) => {
var blueprint = getApp()?.getGroundUnitDatabase()?.blueprints[key];
var type = blueprint.label;
if (/\bAAA|SAM\b/.test(type) || /\bmanpad|stinger\b/i.test(type)) {
filteredAirDefense[key] = blueprint;
} else {
filteredGroundUnits[key] = blueprint;
}
);
});
filteredAirDefense = filterBlueprintsByLabel(filteredAirDefense, filterString);
filteredGroundUnits = filterBlueprintsByLabel(filteredGroundUnits, filterString);
return [filteredAircraft, filteredHelicopters, filteredAirDefense, filteredGroundUnits, filteredNavyUnits]
}
return [filteredAircraft, filteredHelicopters, filteredAirDefense, filteredGroundUnits, filteredNavyUnits];
}

View File

@@ -1,12 +1,5 @@
import { LatLng } from "leaflet";
import {
Ammo,
Contact,
GeneralSettings,
Offset,
Radio,
TACAN,
} from "../interfaces";
import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN } from "../interfaces";
export class DataExtractor {
#seekPosition = 0;
@@ -65,11 +58,7 @@ export class DataExtractor {
}
extractLatLng() {
return new LatLng(
this.extractFloat64(),
this.extractFloat64(),
this.extractFloat64()
);
return new LatLng(this.extractFloat64(), this.extractFloat64(), this.extractFloat64());
}
extractFromBitmask(bitmask: number, position: number) {
@@ -78,10 +67,7 @@ export class DataExtractor {
extractString(length?: number) {
if (length === undefined) length = this.extractUInt16();
var stringBuffer = this.#buffer.slice(
this.#seekPosition,
this.#seekPosition + length
);
var stringBuffer = this.#buffer.slice(this.#seekPosition, this.#seekPosition + length);
var view = new Int8Array(stringBuffer);
var stringLength = length;
view.every((value: number, idx: number) => {

View File

@@ -13,16 +13,7 @@ import {
emissionsCountermeasures,
reactionsToThreat,
} from "../constants/constants";
import {
AirbasesData,
BullseyesData,
GeneralSettings,
MissionData,
Radio,
ServerRequestOptions,
ServerStatus,
TACAN,
} from "../interfaces";
import { AirbasesData, BullseyesData, GeneralSettings, MissionData, Radio, ServerRequestOptions, ServerStatus, TACAN } from "../interfaces";
export class ServerManager {
#connected: boolean = false;
@@ -67,11 +58,7 @@ export class ServerManager {
/* If a request on this uri is still pending (meaning it's not done or did not yet fail), skip the request, to avoid clogging the TCP workers */
/* If we are forcing the request we don't care if one already exists, just send it. CAREFUL: this makes sense only for low frequency requests, like refreshes, when we
are reasonably confident any previous request will be done before we make a new one on the same URI. */
if (
uri in this.#requests &&
this.#requests[uri].readyState !== 4 &&
!force
) {
if (uri in this.#requests && this.#requests[uri].readyState !== 4 && !force) {
console.warn(`GET request on ${uri} URI still pending, skipping...`);
return;
}
@@ -81,33 +68,22 @@ export class ServerManager {
/* Assemble the request options string */
var optionsString = "";
if (options?.time != undefined) optionsString = `time=${options.time}`;
if (options?.commandHash != undefined)
optionsString = `commandHash=${options.commandHash}`;
if (options?.commandHash != undefined) optionsString = `commandHash=${options.commandHash}`;
/* On the connection */
xmlHttp.open(
"GET",
`${this.#REST_ADDRESS}/${uri}${optionsString ? `?${optionsString}` : ""}`,
true
);
xmlHttp.open("GET", `${this.#REST_ADDRESS}/${uri}${optionsString ? `?${optionsString}` : ""}`, true);
/* If provided, set the credentials */
if (this.#username && this.#password)
xmlHttp.setRequestHeader(
"Authorization",
"Basic " + btoa(`${this.#username}:${this.#password}`)
);
if (this.#username && this.#password) xmlHttp.setRequestHeader("Authorization", "Basic " + btoa(`${this.#username}:${this.#password}`));
/* If specified, set the response type */
if (responseType)
xmlHttp.responseType = responseType as XMLHttpRequestResponseType;
if (responseType) xmlHttp.responseType = responseType as XMLHttpRequestResponseType;
xmlHttp.onload = (e) => {
if (xmlHttp.status == 200) {
/* Success */
this.setConnected(true);
if (xmlHttp.responseType == "arraybuffer")
this.#lastUpdateTimes[uri] = callback(xmlHttp.response);
if (xmlHttp.responseType == "arraybuffer") this.#lastUpdateTimes[uri] = callback(xmlHttp.response);
else {
const result = JSON.parse(xmlHttp.responseText);
this.#lastUpdateTimes[uri] = callback(result);
@@ -141,11 +117,7 @@ export class ServerManager {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("PUT", this.#REST_ADDRESS);
xmlHttp.setRequestHeader("Content-Type", "application/json");
if (this.#username && this.#password)
xmlHttp.setRequestHeader(
"Authorization",
"Basic " + btoa(`${this.#username}:${this.#password}`)
);
if (this.#username && this.#password) xmlHttp.setRequestHeader("Authorization", "Basic " + btoa(`${this.#username}:${this.#password}`));
xmlHttp.onload = (res: any) => {
var res = JSON.parse(xmlHttp.responseText);
callback(res.commandHash);
@@ -157,99 +129,47 @@ export class ServerManager {
getConfig(callback: CallableFunction) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open(
"GET",
window.location.href.split("?")[0].replace("vite/", "") + "config",
true
);
xmlHttp.open("GET", window.location.href.split("?")[0].replace("vite/", "") + "config", true);
xmlHttp.onload = function (e) {
var data = JSON.parse(xmlHttp.responseText);
callback(data);
};
xmlHttp.onerror = function () {
console.error(
"An error occurred during the XMLHttpRequest, could not retrieve configuration file"
);
console.error("An error occurred during the XMLHttpRequest, could not retrieve configuration file");
};
xmlHttp.send(null);
}
setAddress(address: string) {
this.#REST_ADDRESS = `${address.replace('vite/', '')}olympus`;
this.#REST_ADDRESS = `${address.replace("vite/", "")}olympus`;
console.log(`Setting REST address to ${this.#REST_ADDRESS}`);
}
getAirbases(
callback: CallableFunction,
errorCallback: CallableFunction = () => {}
) {
getAirbases(callback: CallableFunction, errorCallback: CallableFunction = () => {}) {
this.GET(callback, errorCallback, AIRBASES_URI);
}
getBullseye(
callback: CallableFunction,
errorCallback: CallableFunction = () => {}
) {
getBullseye(callback: CallableFunction, errorCallback: CallableFunction = () => {}) {
this.GET(callback, errorCallback, BULLSEYE_URI);
}
getLogs(
callback: CallableFunction,
refresh: boolean = false,
errorCallback: CallableFunction = () => {}
) {
this.GET(
callback,
errorCallback,
LOGS_URI,
{ time: refresh ? 0 : this.#lastUpdateTimes[LOGS_URI] },
"text",
refresh
);
getLogs(callback: CallableFunction, refresh: boolean = false, errorCallback: CallableFunction = () => {}) {
this.GET(callback, errorCallback, LOGS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[LOGS_URI] }, "text", refresh);
}
getMission(
callback: CallableFunction,
errorCallback: CallableFunction = () => {}
) {
getMission(callback: CallableFunction, errorCallback: CallableFunction = () => {}) {
this.GET(callback, errorCallback, MISSION_URI);
}
getUnits(
callback: CallableFunction,
refresh: boolean = false,
errorCallback: CallableFunction = () => {}
) {
this.GET(
callback,
errorCallback,
UNITS_URI,
{ time: refresh ? 0 : this.#lastUpdateTimes[UNITS_URI] },
"arraybuffer",
refresh
);
getUnits(callback: CallableFunction, refresh: boolean = false, errorCallback: CallableFunction = () => {}) {
this.GET(callback, errorCallback, UNITS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[UNITS_URI] }, "arraybuffer", refresh);
}
getWeapons(
callback: CallableFunction,
refresh: boolean = false,
errorCallback: CallableFunction = () => {}
) {
this.GET(
callback,
errorCallback,
WEAPONS_URI,
{ time: refresh ? 0 : this.#lastUpdateTimes[WEAPONS_URI] },
"arraybuffer",
refresh
);
getWeapons(callback: CallableFunction, refresh: boolean = false, errorCallback: CallableFunction = () => {}) {
this.GET(callback, errorCallback, WEAPONS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[WEAPONS_URI] }, "arraybuffer", refresh);
}
isCommandExecuted(
callback: CallableFunction,
commandHash: string,
errorCallback: CallableFunction = () => {}
) {
isCommandExecuted(callback: CallableFunction, commandHash: string, errorCallback: CallableFunction = () => {}) {
this.GET(callback, errorCallback, COMMANDS_URI, {
commandHash: commandHash,
});
@@ -261,22 +181,13 @@ export class ServerManager {
this.PUT(data, callback);
}
spawnSmoke(
color: string,
latlng: LatLng,
callback: CallableFunction = () => {}
) {
spawnSmoke(color: string, latlng: LatLng, callback: CallableFunction = () => {}) {
var command = { color: color, location: latlng };
var data = { smoke: command };
this.PUT(data, callback);
}
spawnExplosion(
intensity: number,
explosionType: string,
latlng: LatLng,
callback: CallableFunction = () => {}
) {
spawnExplosion(intensity: number, explosionType: string, latlng: LatLng, callback: CallableFunction = () => {}) {
var command = {
explosionType: explosionType,
intensity: intensity,
@@ -328,14 +239,7 @@ export class ServerManager {
this.PUT(data, callback);
}
spawnGroundUnits(
units: any,
coalition: string,
country: string,
immediate: boolean,
spawnPoints: number,
callback: CallableFunction = () => {}
) {
spawnGroundUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) {
var command = {
units: units,
coalition: coalition,
@@ -347,14 +251,7 @@ export class ServerManager {
this.PUT(data, callback);
}
spawnNavyUnits(
units: any,
coalition: string,
country: string,
immediate: boolean,
spawnPoints: number,
callback: CallableFunction = () => {}
) {
spawnNavyUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) {
var command = {
units: units,
coalition: coalition,
@@ -366,22 +263,13 @@ export class ServerManager {
this.PUT(data, callback);
}
attackUnit(
ID: number,
targetID: number,
callback: CallableFunction = () => {}
) {
attackUnit(ID: number, targetID: number, callback: CallableFunction = () => {}) {
var command = { ID: ID, targetID: targetID };
var data = { attackUnit: command };
this.PUT(data, callback);
}
followUnit(
ID: number,
targetID: number,
offset: { x: number; y: number; z: number },
callback: CallableFunction = () => {}
) {
followUnit(ID: number, targetID: number, offset: { x: number; y: number; z: number }, callback: CallableFunction = () => {}) {
// X: front-rear, positive front
// Y: top-bottom, positive bottom
// Z: left-right, positive right
@@ -397,12 +285,7 @@ export class ServerManager {
this.PUT(data, callback);
}
cloneUnits(
units: { ID: number; location: LatLng }[],
deleteOriginal: boolean,
spawnPoints: number,
callback: CallableFunction = () => {}
) {
cloneUnits(units: { ID: number; location: LatLng }[], deleteOriginal: boolean, spawnPoints: number, callback: CallableFunction = () => {}) {
var command = {
units: units,
deleteOriginal: deleteOriginal,
@@ -412,13 +295,7 @@ export class ServerManager {
this.PUT(data, callback);
}
deleteUnit(
ID: number,
explosion: boolean,
explosionType: string,
immediate: boolean,
callback: CallableFunction = () => {}
) {
deleteUnit(ID: number, explosion: boolean, explosionType: string, immediate: boolean, callback: CallableFunction = () => {}) {
var command = {
ID: ID,
explosion: explosion,
@@ -435,11 +312,7 @@ export class ServerManager {
this.PUT(data, callback);
}
changeSpeed(
ID: number,
speedChange: string,
callback: CallableFunction = () => {}
) {
changeSpeed(ID: number, speedChange: string, callback: CallableFunction = () => {}) {
var command = { ID: ID, change: speedChange };
var data = { changeSpeed: command };
this.PUT(data, callback);
@@ -451,52 +324,31 @@ export class ServerManager {
this.PUT(data, callback);
}
setSpeedType(
ID: number,
speedType: string,
callback: CallableFunction = () => {}
) {
setSpeedType(ID: number, speedType: string, callback: CallableFunction = () => {}) {
var command = { ID: ID, speedType: speedType };
var data = { setSpeedType: command };
this.PUT(data, callback);
}
changeAltitude(
ID: number,
altitudeChange: string,
callback: CallableFunction = () => {}
) {
changeAltitude(ID: number, altitudeChange: string, callback: CallableFunction = () => {}) {
var command = { ID: ID, change: altitudeChange };
var data = { changeAltitude: command };
this.PUT(data, callback);
}
setAltitudeType(
ID: number,
altitudeType: string,
callback: CallableFunction = () => {}
) {
setAltitudeType(ID: number, altitudeType: string, callback: CallableFunction = () => {}) {
var command = { ID: ID, altitudeType: altitudeType };
var data = { setAltitudeType: command };
this.PUT(data, callback);
}
setAltitude(
ID: number,
altitude: number,
callback: CallableFunction = () => {}
) {
setAltitude(ID: number, altitude: number, callback: CallableFunction = () => {}) {
var command = { ID: ID, altitude: altitude };
var data = { setAltitude: command };
this.PUT(data, callback);
}
createFormation(
ID: number,
isLeader: boolean,
wingmenIDs: number[],
callback: CallableFunction = () => {}
) {
createFormation(ID: number, isLeader: boolean, wingmenIDs: number[], callback: CallableFunction = () => {}) {
var command = { ID: ID, wingmenIDs: wingmenIDs, isLeader: isLeader };
var data = { setLeader: command };
this.PUT(data, callback);
@@ -508,11 +360,7 @@ export class ServerManager {
this.PUT(data, callback);
}
setReactionToThreat(
ID: number,
reactionToThreat: string,
callback: CallableFunction = () => {}
) {
setReactionToThreat(ID: number, reactionToThreat: string, callback: CallableFunction = () => {}) {
var command = {
ID: ID,
reactionToThreat: reactionsToThreat.indexOf(reactionToThreat),
@@ -521,16 +369,10 @@ export class ServerManager {
this.PUT(data, callback);
}
setEmissionsCountermeasures(
ID: number,
emissionCountermeasure: string,
callback: CallableFunction = () => {}
) {
setEmissionsCountermeasures(ID: number, emissionCountermeasure: string, callback: CallableFunction = () => {}) {
var command = {
ID: ID,
emissionsCountermeasures: emissionsCountermeasures.indexOf(
emissionCountermeasure
),
emissionsCountermeasures: emissionsCountermeasures.indexOf(emissionCountermeasure),
};
var data = { setEmissionsCountermeasures: command };
this.PUT(data, callback);
@@ -542,21 +384,13 @@ export class ServerManager {
this.PUT(data, callback);
}
setFollowRoads(
ID: number,
followRoads: boolean,
callback: CallableFunction = () => {}
) {
setFollowRoads(ID: number, followRoads: boolean, callback: CallableFunction = () => {}) {
var command = { ID: ID, followRoads: followRoads };
var data = { setFollowRoads: command };
this.PUT(data, callback);
}
setOperateAs(
ID: number,
operateAs: number,
callback: CallableFunction = () => {}
) {
setOperateAs(ID: number, operateAs: number, callback: CallableFunction = () => {}) {
var command = { ID: ID, operateAs: operateAs };
var data = { setOperateAs: command };
this.PUT(data, callback);
@@ -574,94 +408,57 @@ export class ServerManager {
this.PUT(data, callback);
}
carpetBomb(
ID: number,
latlng: LatLng,
callback: CallableFunction = () => {}
) {
carpetBomb(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
var command = { ID: ID, location: latlng };
var data = { carpetBomb: command };
this.PUT(data, callback);
}
bombBuilding(
ID: number,
latlng: LatLng,
callback: CallableFunction = () => {}
) {
bombBuilding(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
var command = { ID: ID, location: latlng };
var data = { bombBuilding: command };
this.PUT(data, callback);
}
fireAtArea(
ID: number,
latlng: LatLng,
callback: CallableFunction = () => {}
) {
fireAtArea(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
var command = { ID: ID, location: latlng };
var data = { fireAtArea: command };
this.PUT(data, callback);
}
simulateFireFight(
ID: number,
latlng: LatLng,
altitude: number,
callback: CallableFunction = () => {}
) {
simulateFireFight(ID: number, latlng: LatLng, altitude: number, callback: CallableFunction = () => {}) {
var command = { ID: ID, location: latlng, altitude: altitude };
var data = { simulateFireFight: command };
this.PUT(data, callback);
}
// TODO: Remove coalition
scenicAAA(
ID: number,
coalition: string,
callback: CallableFunction = () => {}
) {
scenicAAA(ID: number, coalition: string, callback: CallableFunction = () => {}) {
var command = { ID: ID, coalition: coalition };
var data = { scenicAAA: command };
this.PUT(data, callback);
}
// TODO: Remove coalition
missOnPurpose(
ID: number,
coalition: string,
callback: CallableFunction = () => {}
) {
missOnPurpose(ID: number, coalition: string, callback: CallableFunction = () => {}) {
var command = { ID: ID, coalition: coalition };
var data = { missOnPurpose: command };
this.PUT(data, callback);
}
landAtPoint(
ID: number,
latlng: LatLng,
callback: CallableFunction = () => {}
) {
landAtPoint(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
var command = { ID: ID, location: latlng };
var data = { landAtPoint: command };
this.PUT(data, callback);
}
setShotsScatter(
ID: number,
shotsScatter: number,
callback: CallableFunction = () => {}
) {
setShotsScatter(ID: number, shotsScatter: number, callback: CallableFunction = () => {}) {
var command = { ID: ID, shotsScatter: shotsScatter };
var data = { setShotsScatter: command };
this.PUT(data, callback);
}
setShotsIntensity(
ID: number,
shotsIntensity: number,
callback: CallableFunction = () => {}
) {
setShotsIntensity(ID: number, shotsIntensity: number, callback: CallableFunction = () => {}) {
var command = { ID: ID, shotsIntensity: shotsIntensity };
var data = { setShotsIntensity: command };
this.PUT(data, callback);
@@ -735,11 +532,7 @@ export class ServerManager {
this.#intervals.push(
window.setInterval(() => {
if (
!this.getPaused() &&
getApp().getMissionManager().getCommandModeOptions().commandMode !=
NONE
) {
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
this.getAirbases((data: AirbasesData) => {
this.checkSessionHash(data.sessionHash);
getApp().getMissionManager()?.updateAirbases(data);
@@ -751,11 +544,7 @@ export class ServerManager {
this.#intervals.push(
window.setInterval(() => {
if (
!this.getPaused() &&
getApp().getMissionManager().getCommandModeOptions().commandMode !=
NONE
) {
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
this.getBullseye((data: BullseyesData) => {
this.checkSessionHash(data.sessionHash);
getApp().getMissionManager()?.updateBullseyes(data);
@@ -767,11 +556,7 @@ export class ServerManager {
this.#intervals.push(
window.setInterval(() => {
if (
!this.getPaused() &&
getApp().getMissionManager().getCommandModeOptions().commandMode !=
NONE
) {
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
this.getLogs((data: any) => {
this.checkSessionHash(data.sessionHash);
//(getApp().getPanelsManager().get("log") as LogPanel).appendLogs(data.logs)
@@ -783,11 +568,7 @@ export class ServerManager {
this.#intervals.push(
window.setInterval(() => {
if (
!this.getPaused() &&
getApp().getMissionManager().getCommandModeOptions().commandMode !=
NONE
) {
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
this.getUnits((buffer: ArrayBuffer) => {
var time = getApp().getUnitsManager()?.update(buffer);
return time;
@@ -798,11 +579,7 @@ export class ServerManager {
this.#intervals.push(
window.setInterval(() => {
if (
!this.getPaused() &&
getApp().getMissionManager().getCommandModeOptions().commandMode !=
NONE
) {
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
this.getWeapons((buffer: ArrayBuffer) => {
var time = getApp().getWeaponsManager()?.update(buffer);
return time;
@@ -814,11 +591,7 @@ export class ServerManager {
this.#intervals.push(
window.setInterval(
() => {
if (
!this.getPaused() &&
getApp().getMissionManager().getCommandModeOptions().commandMode !=
NONE
) {
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
this.getUnits((buffer: ArrayBuffer) => {
var time = getApp().getUnitsManager()?.update(buffer);
return time;
@@ -832,11 +605,8 @@ export class ServerManager {
// Mission clock and elapsed time
this.#intervals.push(
window.setInterval(() => {
const elapsedMissionTime = getApp()
.getMissionManager()
.getDateAndTime().elapsedTime;
this.#serverIsPaused =
elapsedMissionTime === this.#previousMissionElapsedTime;
const elapsedMissionTime = getApp().getMissionManager().getDateAndTime().elapsedTime;
this.#serverIsPaused = elapsedMissionTime === this.#previousMissionElapsedTime;
this.#previousMissionElapsedTime = elapsedMissionTime;
document.dispatchEvent(
@@ -844,8 +614,7 @@ export class ServerManager {
detail: {
frameRate: getApp().getMissionManager().getFrameRate(),
load: getApp().getMissionManager().getLoad(),
elapsedTime: getApp().getMissionManager().getDateAndTime()
.elapsedTime,
elapsedTime: getApp().getMissionManager().getDateAndTime().elapsedTime,
missionTime: getApp().getMissionManager().getDateAndTime().time,
connected: this.getConnected(),
paused: this.getPaused(),
@@ -857,11 +626,7 @@ export class ServerManager {
this.#intervals.push(
window.setInterval(() => {
if (
!this.getPaused() &&
getApp().getMissionManager().getCommandModeOptions().commandMode !=
NONE
) {
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
this.getWeapons((buffer: ArrayBuffer) => {
var time = getApp().getWeaponsManager()?.update(buffer);
return time;

View File

@@ -1,9 +1,5 @@
import { getApp } from "../olympusapp";
import {
ShortcutKeyboardOptions,
ShortcutMouseOptions,
ShortcutOptions,
} from "../interfaces";
import { ShortcutKeyboardOptions, ShortcutMouseOptions, ShortcutOptions } from "../interfaces";
import { keyEventWasInInput } from "../other/utils";
export abstract class Shortcut {
@@ -24,10 +20,7 @@ export class ShortcutKeyboard extends Shortcut {
super(config);
document.addEventListener(config.event, (ev: any) => {
if (
typeof config.context === "string" &&
getApp().getCurrentContext() !== config.context
) {
if (typeof config.context === "string" && getApp().getCurrentContext() !== config.context) {
return;
}
@@ -40,15 +33,9 @@ export class ShortcutKeyboard extends Shortcut {
}
if (
(typeof config.altKey !== "boolean" ||
(typeof config.altKey === "boolean" &&
ev.altKey === config.altKey)) &&
(typeof config.ctrlKey !== "boolean" ||
(typeof config.ctrlKey === "boolean" &&
ev.ctrlKey === config.ctrlKey)) &&
(typeof config.shiftKey !== "boolean" ||
(typeof config.shiftKey === "boolean" &&
ev.shiftKey === config.shiftKey))
(typeof config.altKey !== "boolean" || (typeof config.altKey === "boolean" && ev.altKey === config.altKey)) &&
(typeof config.ctrlKey !== "boolean" || (typeof config.ctrlKey === "boolean" && ev.ctrlKey === config.ctrlKey)) &&
(typeof config.shiftKey !== "boolean" || (typeof config.shiftKey === "boolean" && ev.shiftKey === config.shiftKey))
) {
config.callback(ev);
}

View File

@@ -18,18 +18,14 @@ export class ShortcutManager {
});
document.addEventListener("keyup", (ev: KeyboardEvent) => {
this.#keysBeingHeld = this.#keysBeingHeld.filter(
(held) => held !== ev.code
);
this.#keysBeingHeld = this.#keysBeingHeld.filter((held) => held !== ev.code);
this.#keyUpCallbacks.forEach((callback) => callback(ev));
});
this.addKeyboardShortcut("togglePause", {
altKey: false,
callback: () => {
getApp()
.getServerManager()
.setPaused(!getApp().getServerManager().getPaused());
getApp().getServerManager().setPaused(!getApp().getServerManager().getPaused());
},
code: "Space",
context: DEFAULT_CONTEXT,
@@ -45,12 +41,7 @@ export class ShortcutManager {
.addKeyboardShortcut("toggleUnitLabels", {
altKey: false,
callback: () => {
getApp()
.getMap()
.setOption(
"showUnitLabels",
!getApp().getMap().getOptions().showUnitLabels
);
getApp().getMap().setOption("showUnitLabels", !getApp().getMap().getOptions().showUnitLabels);
},
code: "KeyL",
context: DEFAULT_CONTEXT,
@@ -60,12 +51,7 @@ export class ShortcutManager {
.addKeyboardShortcut("toggleAcquisitionRings", {
altKey: false,
callback: () => {
getApp()
.getMap()
.setOption(
"showUnitsAcquisitionRings",
!getApp().getMap().getOptions().showUnitsAcquisitionRings
);
getApp().getMap().setOption("showUnitsAcquisitionRings", !getApp().getMap().getOptions().showUnitsAcquisitionRings);
},
code: "KeyE",
context: DEFAULT_CONTEXT,
@@ -75,12 +61,7 @@ export class ShortcutManager {
.addKeyboardShortcut("toggleEngagementRings", {
altKey: false,
callback: () => {
getApp()
.getMap()
.setOption(
"showUnitsEngagementRings",
!getApp().getMap().getOptions().showUnitsEngagementRings
);
getApp().getMap().setOption("showUnitsEngagementRings", !getApp().getMap().getOptions().showUnitsEngagementRings);
},
code: "KeyQ",
context: DEFAULT_CONTEXT,
@@ -90,12 +71,7 @@ export class ShortcutManager {
.addKeyboardShortcut("toggleHideShortEngagementRings", {
altKey: false,
callback: () => {
getApp()
.getMap()
.setOption(
"hideUnitsShortRangeRings",
!getApp().getMap().getOptions().hideUnitsShortRangeRings
);
getApp().getMap().setOption("hideUnitsShortRangeRings", !getApp().getMap().getOptions().hideUnitsShortRangeRings);
},
code: "KeyR",
context: DEFAULT_CONTEXT,
@@ -105,12 +81,7 @@ export class ShortcutManager {
.addKeyboardShortcut("toggleDetectionLines", {
altKey: false,
callback: () => {
getApp()
.getMap()
.setOption(
"showUnitTargets",
!getApp().getMap().getOptions().showUnitTargets
);
getApp().getMap().setOption("showUnitTargets", !getApp().getMap().getOptions().showUnitTargets);
},
code: "KeyF",
context: DEFAULT_CONTEXT,
@@ -120,12 +91,7 @@ export class ShortcutManager {
.addKeyboardShortcut("toggleGroupMembers", {
altKey: false,
callback: () => {
getApp()
.getMap()
.setOption(
"hideGroupMembers",
!getApp().getMap().getOptions().hideGroupMembers
);
getApp().getMap().setOption("hideGroupMembers", !getApp().getMap().getOptions().hideGroupMembers);
},
code: "KeyG",
context: DEFAULT_CONTEXT,
@@ -153,16 +119,7 @@ export class ShortcutManager {
shiftKey: false,
});
[
"KeyW",
"KeyA",
"KeyS",
"KeyD",
"ArrowLeft",
"ArrowRight",
"ArrowUp",
"ArrowDown",
].forEach((code) => {
["KeyW", "KeyA", "KeyS", "KeyD", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].forEach((code) => {
this.addKeyboardShortcut(`pan${code}keydown`, {
altKey: false,
callback: (ev: KeyboardEvent) => {
@@ -183,17 +140,7 @@ export class ShortcutManager {
});
});
const digits = [
"Digit1",
"Digit2",
"Digit3",
"Digit4",
"Digit5",
"Digit6",
"Digit7",
"Digit8",
"Digit9",
];
const digits = ["Digit1", "Digit2", "Digit3", "Digit4", "Digit5", "Digit6", "Digit7", "Digit8", "Digit9"];
digits.forEach((code) => {
this.addKeyboardShortcut(`hotgroup${code}`, {
@@ -202,15 +149,18 @@ export class ShortcutManager {
if (ev.ctrlKey && ev.shiftKey)
getApp()
.getUnitsManager()
.selectUnitsByHotgroup(parseInt(ev.code.substring(5)), false); // "Select hotgroup X in addition to any units already selected"
.selectUnitsByHotgroup(parseInt(ev.code.substring(5)), false);
// "Select hotgroup X in addition to any units already selected"
else if (ev.ctrlKey && !ev.shiftKey)
getApp()
.getUnitsManager()
.setHotgroup(parseInt(ev.code.substring(5))); // "These selected units are hotgroup X (forget any previous membership)"
.setHotgroup(parseInt(ev.code.substring(5)));
// "These selected units are hotgroup X (forget any previous membership)"
else if (!ev.ctrlKey && ev.shiftKey)
getApp()
.getUnitsManager()
.addToHotgroup(parseInt(ev.code.substring(5))); // "Add (append) these units to hotgroup X (in addition to any existing members)"
.addToHotgroup(parseInt(ev.code.substring(5)));
// "Add (append) these units to hotgroup X (in addition to any existing members)"
else
getApp()
.getUnitsManager()
@@ -221,22 +171,14 @@ export class ShortcutManager {
// Stop hotgroup controls sending the browser to another tab
document.addEventListener("keydown", (ev: KeyboardEvent) => {
if (
ev.code === code &&
ev.ctrlKey === true &&
ev.altKey === false &&
ev.shiftKey === false
) {
if (ev.code === code && ev.ctrlKey === true && ev.altKey === false && ev.shiftKey === false) {
ev.preventDefault();
}
});
});
}
addKeyboardShortcut(
name: string,
shortcutKeyboardOptions: ShortcutKeyboardOptions
) {
addKeyboardShortcut(name: string, shortcutKeyboardOptions: ShortcutKeyboardOptions) {
this.#items[name] = new ShortcutKeyboard(shortcutKeyboardOptions);
return this;
}

View File

@@ -1,8 +1,5 @@
import { createContext } from "react";
import {
MAP_HIDDEN_TYPES_DEFAULTS,
MAP_OPTIONS_DEFAULTS,
} from "./constants/constants";
import { MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS } from "./constants/constants";
export const StateContext = createContext({
mainMenuVisible: false,
@@ -15,7 +12,7 @@ export const StateContext = createContext({
mapOptions: MAP_OPTIONS_DEFAULTS,
mapSources: [] as string[],
activeMapSource: "",
mapBoxSelection: false
mapBoxSelection: false,
});
export const StateProvider = StateContext.Provider;

View File

@@ -2,31 +2,21 @@ import React, { useEffect, useRef, useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faArrowCircleDown } from "@fortawesome/free-solid-svg-icons";
export function OlAccordion(props: {
title: string;
children?: JSX.Element | JSX.Element[];
showArrows?: boolean;
}) {
var [open, setOpen] = useState(false);
var [scrolledUp, setScrolledUp] = useState(true);
var [scrolledDown, setScrolledDown] = useState(false);
export function OlAccordion(props: { title: string; children?: JSX.Element | JSX.Element[]; showArrows?: boolean }) {
const [open, setOpen] = useState(false);
const [scrolledUp, setScrolledUp] = useState(true);
const [scrolledDown, setScrolledDown] = useState(false);
var contentRef = useRef(null);
useEffect(() => {
contentRef.current &&
(contentRef.current as HTMLElement).children[0]?.addEventListener(
"scroll",
(e: any) => {
if (e.target.clientHeight < e.target.scrollHeight) {
setScrolledDown(
e.target.scrollTop ===
e.target.scrollHeight - e.target.offsetHeight
);
setScrolledUp(e.target.scrollTop === 0);
}
(contentRef.current as HTMLElement).children[0]?.addEventListener("scroll", (e: any) => {
if (e.target.clientHeight < e.target.scrollHeight) {
setScrolledDown(e.target.scrollTop === e.target.scrollHeight - e.target.offsetHeight);
setScrolledUp(e.target.scrollTop === 0);
}
);
});
});
return (
@@ -61,13 +51,7 @@ export function OlAccordion(props: {
fill="none"
viewBox="0 0 10 6"
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M9 5 5 1 1 5"
/>
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5 5 1 1 5" />
</svg>
</button>
</h3>

View File

@@ -2,19 +2,11 @@ import { IconProp } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React from "react";
export function OlButtonGroup(props: {
children?: JSX.Element | JSX.Element[];
}) {
return (
<div className="inline-flex rounded-md shadow-sm">{props.children}</div>
);
export function OlButtonGroup(props: { children?: JSX.Element | JSX.Element[] }) {
return <div className="inline-flex rounded-md shadow-sm">{props.children}</div>;
}
export function OlButtonGroupItem(props: {
icon: IconProp;
active: boolean;
onClick: () => void;
}) {
export function OlButtonGroupItem(props: { icon: IconProp; active: boolean; onClick: () => void }) {
return (
<button
onClick={props.onClick}

View File

@@ -1,14 +1,5 @@
import React from "react";
import {
faSkull,
faCamera,
faFlag,
faLink,
faUnlink,
faAngleDoubleRight,
faExclamationCircle,
faInfoCircle,
} from "@fortawesome/free-solid-svg-icons";
import { faSkull, faCamera, faFlag, faLink, faUnlink, faAngleDoubleRight, faExclamationCircle, faInfoCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
// Error message callout, only to be used for error messages
@@ -21,12 +12,7 @@ export function ErrorCallout(props: { title?: string; description?: string }) {
dark:bg-gray-800 dark:text-red-500
`}
>
{props.title && (
<FontAwesomeIcon
className="mt-1"
icon={faExclamationCircle}
></FontAwesomeIcon>
)}
{props.title && <FontAwesomeIcon className="mt-1" icon={faExclamationCircle}></FontAwesomeIcon>}
<div
className={`
flex flex-col content-center items-start gap-2 text-pretty font-bold
@@ -55,9 +41,7 @@ export function InfoCallout(props: { title?: string; description?: string }) {
dark:bg-gray-800 dark:text-blue-400
`}
>
{props.title && (
<FontAwesomeIcon className="mt-1" icon={faInfoCircle}></FontAwesomeIcon>
)}
{props.title && <FontAwesomeIcon className="mt-1" icon={faInfoCircle}></FontAwesomeIcon>}
<div
className={`
flex flex-col content-center items-start gap-2 text-pretty font-medium
@@ -88,10 +72,7 @@ export function CommandCallout(props: { coalition?: string }) {
dark:bg-gray-800 dark:text-gray-400
`}
>
<FontAwesomeIcon
className="mt-1"
icon={faAngleDoubleRight}
></FontAwesomeIcon>
<FontAwesomeIcon className="mt-1" icon={faAngleDoubleRight}></FontAwesomeIcon>
<div
className={`
content-center items-start gap-2 whitespace-break-spaces text-pretty
@@ -100,13 +81,9 @@ export function CommandCallout(props: { coalition?: string }) {
>
You are playing as
{props.coalition == "blue" ? (
<div className="inline-block font-bold text-blue-500">
BLUE COMMANDER
</div>
<div className="inline-block font-bold text-blue-500">BLUE COMMANDER</div>
) : (
<div className="inline-block font-bold text-red-500">
RED COMMANDER
</div>
<div className="inline-block font-bold text-red-500">RED COMMANDER</div>
)}
</div>
</div>

View File

@@ -1,11 +1,6 @@
import React, { ChangeEvent } from "react";
export function OlCheckbox(props: {
checked: boolean;
className?: string;
disabled?: boolean;
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
}) {
export function OlCheckbox(props: { checked: boolean; className?: string; disabled?: boolean; onChange: (e: ChangeEvent<HTMLInputElement>) => void }) {
return (
<input
onChange={props.onChange}

View File

@@ -1,15 +1,9 @@
import React from "react";
import { Coalition } from "../../types/types";
export function OlCoalitionToggle(props: {
coalition: Coalition | undefined;
onClick: () => void;
}) {
export function OlCoalitionToggle(props: { coalition: Coalition | undefined; onClick: () => void }) {
return (
<div
className="inline-flex cursor-pointer items-center"
onClick={props.onClick}
>
<div className="inline-flex cursor-pointer items-center" onClick={props.onClick}>
<button className="peer sr-only" />
<div
data-flash={props.coalition === undefined}
@@ -39,9 +33,7 @@ export function OlCoalitionToggle(props: {
data-[flash='true']:after:animate-pulse
`}
>
{props.coalition
? `${props.coalition[0].toLocaleUpperCase() + props.coalition.substring(1)}`
: "Diff. values"}
{props.coalition ? `${props.coalition[0].toLocaleUpperCase() + props.coalition.substring(1)}` : "Diff. values"}
</span>
</div>
);

View File

@@ -11,11 +11,9 @@ export function OlDropdown(props: {
buttonRef?: MutableRefObject<null> | null;
open?: boolean;
}) {
var [open, setOpen] =
props.open !== undefined ? [props.open, () => {}] : useState(false);
const [open, setOpen] = props.open !== undefined ? [props.open, () => {}] : useState(false);
var contentRef = useRef(null);
var buttonRef =
props.buttonRef !== undefined ? props.buttonRef : useRef(null);
var buttonRef = props.buttonRef !== undefined ? props.buttonRef : useRef(null);
function setPosition(content: HTMLDivElement, button: HTMLButtonElement) {
/* Reset the position of the content */
@@ -24,7 +22,7 @@ export function OlDropdown(props: {
content.style.height = "";
/* Get the position and size of the button and the content elements */
var [cxl, cyt, cxr, cyb, cw, ch] = [
let [cxl, cyt, cxr, cyb, cw, ch] = [
content.getBoundingClientRect().x,
content.getBoundingClientRect().y,
content.getBoundingClientRect().x + content.clientWidth,
@@ -32,7 +30,7 @@ export function OlDropdown(props: {
content.clientWidth,
content.clientHeight,
];
var [bxl, byt, bxr, byb, bbw, bh] = [
let [bxl, byt, bxr, byb, bbw, bh] = [
button.getBoundingClientRect().x,
button.getBoundingClientRect().y,
button.getBoundingClientRect().x + button.clientWidth,
@@ -81,11 +79,7 @@ export function OlDropdown(props: {
/* Register click events to automatically close the dropdown when clicked anywhere outside of it */
document.addEventListener("click", function (event) {
const target = event.target;
if (
target &&
!content.contains(target as HTMLElement) &&
!button.contains(target as HTMLElement)
) {
if (target && !content.contains(target as HTMLElement) && !button.contains(target as HTMLElement)) {
setOpen(false);
}
});
@@ -93,9 +87,7 @@ export function OlDropdown(props: {
});
return (
<div
className={props.className ?? ""}
>
<div className={props.className ?? ""}>
{props.buttonRef === undefined && (
<button
ref={buttonRef}
@@ -111,12 +103,8 @@ export function OlDropdown(props: {
`}
type="button"
>
{props.leftIcon && (
<FontAwesomeIcon icon={props.leftIcon} className={`mr-3`} />
)}
<span className="overflow-hidden text-ellipsis text-nowrap">
{props.label ?? ""}
</span>
{props.leftIcon && <FontAwesomeIcon icon={props.leftIcon} className={`mr-3`} />}
<span className="overflow-hidden text-ellipsis text-nowrap">{props.label ?? ""}</span>
<svg
className={`
ml-auto ms-3 h-2.5 w-2.5 flex-none transition-transform
@@ -127,13 +115,7 @@ export function OlDropdown(props: {
fill="none"
viewBox="0 0 10 6"
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="m1 1 4 4 4-4"
/>
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="m1 1 4 4 4-4" />
</svg>
</button>
)}
@@ -142,7 +124,7 @@ export function OlDropdown(props: {
ref={contentRef}
data-open={open}
className={`
absolute divide-y divide-gray-100 overflow-y-scroll no-scrollbar
absolute z-40 divide-y divide-gray-100 overflow-y-scroll no-scrollbar
rounded-lg bg-white p-2 shadow
dark:bg-gray-700
data-[open='false']:hidden

File diff suppressed because one or more lines are too long

View File

@@ -1,18 +1,13 @@
import React from "react";
export function OlLabelToggle(props: {
toggled: boolean | undefined;
leftLabel: string;
rightLabel: string;
onClick: () => void;
}) {
export function OlLabelToggle(props: { toggled: boolean | undefined; leftLabel: string; rightLabel: string; onClick: () => void }) {
return (
<button
onClick={props.onClick}
className={`
relative flex h-10 w-[120px] flex-none cursor-pointer select-none
flex-row justify-between rounded-md border px-1 py-[5px] text-sm
contents-center
flex-row content-center justify-between rounded-md border px-1 py-[5px]
text-sm
dark:border-gray-600 dark:border-transparent dark:bg-gray-700
dark:hover:bg-gray-600 dark:focus:ring-blue-800
focus:outline-none focus:ring-2 focus:ring-blue-300
@@ -31,7 +26,7 @@ export function OlLabelToggle(props: {
<span
data-active={!(props.toggled ?? false)}
className={`
my-auto pl-3 font-normal transition-colors z-ui-1
absolute left-[17px] top-[8px] font-normal transition-colors
dark:data-[active='false']:text-gray-400
dark:data-[active='true']:text-white
`}
@@ -41,7 +36,7 @@ export function OlLabelToggle(props: {
<span
data-active={props.toggled ?? false}
className={`
my-auto pr-3.5 font-normal transition-colors z-ui-1
absolute right-[17px] top-[8px] font-normal transition-colors
dark:data-[active='false']:text-gray-400
dark:data-[active='true']:text-white
`}

View File

@@ -1,15 +1,23 @@
import React, { ChangeEvent, useEffect, useId } from "react";
import React, { ChangeEvent } from "react";
import { zeroAppend } from "../../other/utils";
export function OlNumberInput(props: {
value: number;
min: number;
max: number;
minLength?: number;
className?: string;
onDecrease: () => void;
onIncrease: () => void;
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
}) {
return (
<div className="min-w-32">
<div
className={`
${props.className ?? ""}
min-w-32
`}
>
<div className="relative flex max-w-[8rem] items-center">
<button
type="button"
@@ -31,13 +39,7 @@ export function OlNumberInput(props: {
fill="none"
viewBox="0 0 18 2"
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M1 1h16"
/>
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M1 1h16" />
</svg>
</button>
<input
@@ -53,7 +55,7 @@ export function OlNumberInput(props: {
dark:focus:ring-blue-700
focus:border-blue-700 focus:ring-blue-500
`}
value={props.value}
value={zeroAppend(props.value, props.minLength ?? 0)}
/>
<button
type="button"
@@ -75,13 +77,7 @@ export function OlNumberInput(props: {
fill="none"
viewBox="0 0 18 18"
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M9 1v16M1 9h16"
/>
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 1v16M1 9h16" />
</svg>
</button>
</div>

View File

@@ -2,10 +2,7 @@ import { faMultiply, faSearch } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React, { useId, useRef } from "react";
export function OlSearchBar(props: {
onChange: (e: string) => void;
text: string;
}) {
export function OlSearchBar(props: { onChange: (e: string) => void; text: string }) {
const searchId = useId();
const inputRef = useRef(null);

View File

@@ -1,11 +1,5 @@
import { IconProp } from "@fortawesome/fontawesome-svg-core";
import {
faExternalLink,
faLock,
faLockOpen,
faUnlock,
faUnlockAlt,
} from "@fortawesome/free-solid-svg-icons";
import { faExternalLink, faLock, faLockOpen, faUnlock, faUnlockAlt } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React, { useRef, useState } from "react";
import { OlTooltip } from "./oltooltip";
@@ -18,7 +12,7 @@ export function OlStateButton(props: {
onClick: () => void;
children?: JSX.Element | JSX.Element[];
}) {
var [hover, setHover] = useState(false);
const [hover, setHover] = useState(false);
var buttonRef = useRef(null);
const className =
@@ -49,8 +43,8 @@ export function OlStateButton(props: {
}}
>
<div className="m-auto flex w-fit content-center justify-center gap-2">
<FontAwesomeIcon icon={props.icon} className="m-auto"/>
{props.children}
<FontAwesomeIcon icon={props.icon} className="m-auto" />
{props.children}
</div>
</button>
{hover && <OlTooltip buttonRef={buttonRef} content={props.tooltip} />}
@@ -58,14 +52,8 @@ export function OlStateButton(props: {
);
}
export function OlRoundStateButton(props: {
className?: string;
checked: boolean;
icon: IconProp;
tooltip: string;
onClick: () => void;
}) {
var [hover, setHover] = useState(false);
export function OlRoundStateButton(props: { className?: string; checked: boolean; icon: IconProp; tooltip: string; onClick: () => void }) {
const [hover, setHover] = useState(false);
var buttonRef = useRef(null);
const className =
@@ -106,13 +94,8 @@ export function OlRoundStateButton(props: {
);
}
export function OlLockStateButton(props: {
className?: string;
checked: boolean;
tooltip: string;
onClick: () => void;
}) {
var [hover, setHover] = useState(false);
export function OlLockStateButton(props: { className?: string; checked: boolean; tooltip: string; onClick: () => void }) {
const [hover, setHover] = useState(false);
var buttonRef = useRef(null);
const className =
@@ -143,10 +126,7 @@ export function OlLockStateButton(props: {
setHover(false);
}}
>
<FontAwesomeIcon
className="pt-[3px]"
icon={props.checked == true ? faUnlockAlt : faLock}
/>
<FontAwesomeIcon className="pt-[3px]" icon={props.checked == true ? faUnlockAlt : faLock} />
</button>
{hover && <OlTooltip buttonRef={buttonRef} content={props.tooltip} />}
</>

View File

@@ -0,0 +1,28 @@
import React, { ChangeEvent } from "react";
export function OlStringInput(props: { value: string; className?: string; onChange: (e: ChangeEvent<HTMLInputElement>) => void }) {
return (
<div
className={`
${props.className ?? ""}
min-w-32
`}
>
<div className="relative flex max-w-[8rem] items-center">
<input
type="text"
onChange={props.onChange}
className={`
block h-10 w-full rounded-md border-[2px] bg-gray-50 py-2.5
text-center text-sm text-gray-900
dark:border-gray-700 dark:bg-olympus-600 dark:text-white
dark:placeholder-gray-400 dark:focus:border-blue-700
dark:focus:ring-blue-700
focus:border-blue-700 focus:ring-blue-500
`}
value={props.value}
/>
</div>
</div>
);
}

View File

@@ -1,14 +1,8 @@
import React from "react";
export function OlToggle(props: {
toggled: boolean | undefined;
onClick: () => void;
}) {
export function OlToggle(props: { toggled: boolean | undefined; onClick: () => void }) {
return (
<div
className="inline-flex cursor-pointer items-center"
onClick={props.onClick}
>
<div className="inline-flex cursor-pointer items-center" onClick={props.onClick}>
<button className="peer sr-only" />
<div
data-flash={props.toggled === undefined}

View File

@@ -1,9 +1,6 @@
import React, { useEffect, useRef, useState } from "react";
export function OlTooltip(props: {
content: string;
buttonRef: React.MutableRefObject<null>;
}) {
export function OlTooltip(props: { content: string; buttonRef: React.MutableRefObject<null> }) {
var contentRef = useRef(null);
function setPosition(content: HTMLDivElement, button: HTMLButtonElement) {
@@ -13,7 +10,7 @@ export function OlTooltip(props: {
content.style.height = "";
/* Get the position and size of the button and the content elements */
var [cxl, cyt, cxr, cyb, cw, ch] = [
let [cxl, cyt, cxr, cyb, cw, ch] = [
content.getBoundingClientRect().x,
content.getBoundingClientRect().y,
content.getBoundingClientRect().x + content.clientWidth,
@@ -21,7 +18,7 @@ export function OlTooltip(props: {
content.clientWidth,
content.clientHeight,
];
var [bxl, byt, bxr, byb, bbw, bh] = [
let [bxl, byt, bxr, byb, bbw, bh] = [
button.getBoundingClientRect().x,
button.getBoundingClientRect().y,
button.getBoundingClientRect().x + button.clientWidth,
@@ -73,8 +70,8 @@ export function OlTooltip(props: {
<div
ref={contentRef}
className={`
absolute whitespace-nowrap z-ui-4 rounded-lg bg-gray-900 px-3 py-2
text-sm font-medium text-white shadow-sm
absolute whitespace-nowrap rounded-lg bg-gray-900 px-3 py-2 text-sm
font-medium text-white shadow-sm
dark:bg-gray-700
`}
>

View File

@@ -2,19 +2,10 @@ import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { IconProp } from "@fortawesome/fontawesome-svg-core";
import { UnitBlueprint } from "../../interfaces";
import {
faArrowRightLong,
faCaretRight,
faCircleArrowRight,
faLongArrowAltRight,
} from "@fortawesome/free-solid-svg-icons";
import { faArrowRightLong, faCaretRight, faCircleArrowRight, faLongArrowAltRight } from "@fortawesome/free-solid-svg-icons";
import { faArrowRight } from "@fortawesome/free-solid-svg-icons/faArrowRight";
export function OlUnitEntryList(props: {
icon: IconProp;
blueprint: UnitBlueprint;
onClick: () => void;
}) {
export function OlUnitEntryList(props: { icon: IconProp; blueprint: UnitBlueprint; onClick: () => void }) {
return (
<div
onClick={props.onClick}
@@ -25,9 +16,7 @@ export function OlUnitEntryList(props: {
`}
>
<FontAwesomeIcon icon={props.icon} className="text-sm"></FontAwesomeIcon>
<div className="flex-1 px-2 text-left font-normal">
{props.blueprint.label}
</div>
<div className="flex-1 px-2 text-left font-normal">{props.blueprint.label}</div>
<div
className={`
rounded-full bg-olympus-800 px-2 py-0.5 text-xs font-bold

View File

@@ -2,10 +2,7 @@ import React from "react";
import { UnitBlueprint } from "../../interfaces";
import { Coalition } from "../../types/types";
export function OlUnitSummary(props: {
blueprint: UnitBlueprint;
coalition: Coalition;
}) {
export function OlUnitSummary(props: { blueprint: UnitBlueprint; coalition: Coalition }) {
return (
<div
data-coalition={props.coalition}

View File

@@ -1,17 +1,8 @@
import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faArrowRight,
faCheckCircle,
faExternalLink,
faLink,
faUnlink,
} from "@fortawesome/free-solid-svg-icons";
import { faArrowRight, faCheckCircle, faExternalLink, faLink, faUnlink } from "@fortawesome/free-solid-svg-icons";
export function Card(props: {
children?: JSX.Element | JSX.Element[];
className?: string;
}) {
export function Card(props: { children?: JSX.Element | JSX.Element[]; className?: string }) {
return (
<div
className={`

View File

@@ -1,15 +1,11 @@
import React from "react";
export function Modal(props: {
grayout?: boolean;
children?: JSX.Element | JSX.Element[];
className?: string;
}) {
export function Modal(props: { grayout?: boolean; children?: JSX.Element | JSX.Element[]; className?: string }) {
return (
<div
className={`
${props.className}
fixed left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] z-ui-4
fixed left-[50%] top-[50%] z-40 translate-x-[-50%] translate-y-[-50%]
rounded-xl border-[1px] border-solid border-gray-700 drop-shadow-md
`}
>

View File

@@ -1,27 +1,10 @@
import React, { useState, version } from "react";
import React, { useState } from "react";
import { Modal } from "./components/modal";
import { Card } from "./components/card";
import {
ErrorCallout,
InfoCallout,
CommandCallout,
} from "../../ui/components/olcallout";
import { ErrorCallout } from "../../ui/components/olcallout";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faArrowRight,
faCheckCircle,
faDatabase,
faExclamation,
faExclamationCircle,
faExternalLink,
faLink,
faServer,
faSitemap,
faUnlink,
faWindowMaximize,
} from "@fortawesome/free-solid-svg-icons";
import { VERSION, connectedToServer } from "../../olympusapp";
import { faFirefoxBrowser } from "@fortawesome/free-brands-svg-icons";
import { faArrowRight, faCheckCircle, faExternalLink } from "@fortawesome/free-solid-svg-icons";
import { VERSION } from "../../olympusapp";
export function LoginModal(props: {
checkingPassword: boolean;
@@ -38,7 +21,7 @@ export function LoginModal(props: {
<Modal
className={`
inline-flex h-[75%] max-h-[530px] w-[80%] max-w-[1100px] overflow-y-auto
scroll-smooth bg-white z-ui-5
scroll-smooth bg-white
dark:bg-olympus-800
max-md:h-full max-md:max-h-full max-md:w-full max-md:rounded-none
max-md:border-none
@@ -46,7 +29,9 @@ export function LoginModal(props: {
>
<img
src="/vite/images/splash/1.jpg"
className={`contents-center w-full object-cover opacity-[7%]`}
className={`
contents-center w-full object-cover opacity-[7%]
`}
></img>
<div
className={`
@@ -107,7 +92,9 @@ export function LoginModal(props: {
<span className="size-[80px] min-w-14">
<img
src="..\vite\images\olympus-500x500.png"
className={`flex w-full`}
className={`
flex w-full
`}
></img>
</span>
<div className={`flex flex-col items-start gap-1`}>
@@ -126,10 +113,7 @@ export function LoginModal(props: {
dark:text-green-400
`}
>
<FontAwesomeIcon
icon={faCheckCircle}
className={`my-auto`}
/>
<FontAwesomeIcon icon={faCheckCircle} className={`my-auto`} />
Version {VERSION}
</div>
</div>
@@ -149,9 +133,7 @@ export function LoginModal(props: {
</label>
<input
type="password"
onChange={(ev) =>
setPassword(ev.currentTarget.value)
}
onChange={(ev) => setPassword(ev.currentTarget.value)}
className={`
block w-full max-w-80 rounded-lg border
border-gray-300 bg-gray-50 p-2.5 text-sm
@@ -182,10 +164,7 @@ export function LoginModal(props: {
`}
>
Login
<FontAwesomeIcon
className={`my-auto`}
icon={faArrowRight}
/>
<FontAwesomeIcon className={`my-auto`} icon={faArrowRight} />
</button>
{/*
<button type="button" className="flex content-center items-center gap-2 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium text-sm px-5 py-2.5 me-2 mb-2 dark:bg-gray-800 dark:hover:bg-gray-700 focus:outline-none dark:focus:ring-blue-800 rounded-sm dark:border-gray-600 border-[1px] dark:text-gray-400">
@@ -213,9 +192,7 @@ export function LoginModal(props: {
<input
type="text"
autoComplete="username"
onChange={(ev) =>
setDisplayName(ev.currentTarget.value)
}
onChange={(ev) => setDisplayName(ev.currentTarget.value)}
className={`
block w-full max-w-80 rounded-lg border
border-gray-300 bg-gray-50 p-2.5 text-sm
@@ -246,10 +223,7 @@ export function LoginModal(props: {
`}
>
Continue
<FontAwesomeIcon
className={`my-auto`}
icon={faArrowRight}
/>
<FontAwesomeIcon className={`my-auto`} icon={faArrowRight} />
</button>
<button
type="button"
@@ -344,10 +318,7 @@ export function LoginModal(props: {
`}
>
YouTube Video Guide
<FontAwesomeIcon
className={`my-auto text-xs text-gray-400`}
icon={faExternalLink}
/>
<FontAwesomeIcon className={`my-auto text-xs text-gray-400`} icon={faExternalLink} />
</div>
<div
className={`
@@ -355,9 +326,7 @@ export function LoginModal(props: {
dark:text-gray-400
`}
>
Check out our official video tutorial on how to get started with
Olympus - so you can immediately start controlling the
battlefield.
Check out our official video tutorial on how to get started with Olympus - so you can immediately start controlling the battlefield.
</div>
</Card>
<Card className="flex">
@@ -374,10 +343,7 @@ export function LoginModal(props: {
`}
>
Wiki Guide
<FontAwesomeIcon
className={`my-auto text-xs text-gray-400`}
icon={faExternalLink}
/>
<FontAwesomeIcon className={`my-auto text-xs text-gray-400`} icon={faExternalLink} />
</div>
<div
className={`
@@ -396,13 +362,9 @@ export function LoginModal(props: {
max-lg:flex-col
`}
>
DCS Olympus (the "MATERIAL" or "Software") is provided completely free
to users subject to the terms of the CC BY-NC-SA 4.0 Licence except
where such terms conflict with this disclaimer, in which case, the
terms of this disclaimer shall prevail. Any party making use of the
Software in any manner agrees to be bound by the terms set out in the
disclaimer. THIS MATERIAL IS NOT MADE OR SUPPORTED BY EAGLE DYNAMICS
SA.
DCS Olympus (the "MATERIAL" or "Software") is provided completely free to users subject to the terms of the CC BY-NC-SA 4.0 Licence except where such
terms conflict with this disclaimer, in which case, the terms of this disclaimer shall prevail. Any party making use of the Software in any manner
agrees to be bound by the terms set out in the disclaimer. THIS MATERIAL IS NOT MADE OR SUPPORTED BY EAGLE DYNAMICS SA.
</div>
</div>
</Modal>

View File

@@ -20,8 +20,8 @@ export function Menu(props: {
<div
data-open={props.open}
className={`
absolute left-16 right-0 top-[58px] bg-transparent z-ui-3
pointer-events-none h-[calc(100vh-58px)] transition-transform
pointer-events-none absolute left-16 right-0 top-[58px] z-10
h-[calc(100vh-58px)] bg-transparent transition-transform
data-[open='false']:-translate-x-full
sm:w-[400px]
`}
@@ -71,7 +71,7 @@ export function Menu(props: {
{props.canBeHidden == true && (
<div
className={`
flex h-8 justify-center z-ui-4 pointer-events-auto backdrop-blur-lg
pointer-events-auto flex h-8 justify-center backdrop-blur-lg
backdrop-grayscale
dark:bg-olympus-800/90
`}
@@ -80,7 +80,11 @@ export function Menu(props: {
{hide ? (
<FaChevronUp className="mx-auto my-auto text-gray-400" />
) : (
<FaChevronDown className="mx-auto my-auto text-gray-400" />
<FaChevronDown
className={`
mx-auto my-auto text-gray-400
`}
/>
)}
</div>
)}

View File

@@ -24,8 +24,8 @@ export function ControlsPanel(props: {}) {
return (
<div
className={`
absolute bottom-[20px] right-[10px] w-80 z-ui-0 flex flex-col
items-center justify-between gap-1 p-3 text-sm
absolute bottom-[20px] right-[10px] flex w-80 flex-col items-center
justify-between gap-1 p-3 text-sm
`}
>
{controls.map((control) => {
@@ -37,9 +37,7 @@ export function ControlsPanel(props: {}) {
dark:bg-olympus-800/90 dark:text-gray-200
`}
>
<div className="my-auto overflow-hidden text-nowrap">
{control.text}
</div>
<div className="my-auto overflow-hidden text-nowrap">{control.text}</div>
<div
className={`
ml-auto flex gap-1 rounded-full bg-olympus-500 px-2 py-0.5
@@ -55,7 +53,9 @@ export function ControlsPanel(props: {}) {
) : (
<FontAwesomeIcon
icon={action}
className={`my-auto ml-auto`}
className={`
my-auto ml-auto
`}
/>
)}
</div>

View File

@@ -2,12 +2,7 @@ import React, { useEffect, useState } from "react";
import { Menu } from "./components/menu";
import { FaQuestionCircle, FaRegCircle, FaTrash } from "react-icons/fa";
import { getApp } from "../../olympusapp";
import {
COALITIONAREA_DRAW_CIRCLE,
COALITIONAREA_DRAW_POLYGON,
COALITIONAREA_EDIT,
IDLE,
} from "../../constants/constants";
import { COALITIONAREA_DRAW_CIRCLE, COALITIONAREA_DRAW_POLYGON, COALITIONAREA_EDIT, IDLE } from "../../constants/constants";
import { OlStateButton } from "../components/olstatebutton";
import { faDrawPolygon } from "@fortawesome/free-solid-svg-icons";
import { faCircle } from "@fortawesome/free-regular-svg-icons";
@@ -22,14 +17,11 @@ import { CoalitionCircle } from "../../map/coalitionarea/coalitioncircle";
export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
const [drawingPolygon, setDrawingPolygon] = useState(false);
const [drawingCircle, setDrawingCircle] = useState(false);
const [activeCoalitionArea, setActiveCoalitionArea] = useState(
null as null | CoalitionPolygon | CoalitionCircle
);
const [activeCoalitionArea, setActiveCoalitionArea] = useState(null as null | CoalitionPolygon | CoalitionCircle);
const [areaCoalition, setAreaCoalition] = useState("blue" as Coalition);
const [IADSDensity, setIADSDensity] = useState(50);
const [IADSDistribution, setIADSDistribution] = useState(50);
const [forceCoalitionAppropriateUnits, setForceCoalitionApproriateUnits] =
useState(false);
const [forceCoalitionAppropriateUnits, setForceCoalitionApproriateUnits] = useState(false);
const [typesSelection, setTypesSelection] = useState({});
const [erasSelection, setErasSelection] = useState({});
@@ -37,55 +29,30 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
useEffect(() => {
/* If we are not in polygon drawing mode, force the draw polygon button off */
if (
drawingPolygon &&
getApp().getMap().getState() !== COALITIONAREA_DRAW_POLYGON
)
setDrawingPolygon(false);
if (drawingPolygon && getApp().getMap().getState() !== COALITIONAREA_DRAW_POLYGON) setDrawingPolygon(false);
/* If we are not in circle drawing mode, force the draw circle button off */
if (
drawingCircle &&
getApp().getMap().getState() !== COALITIONAREA_DRAW_CIRCLE
)
setDrawingCircle(false);
if (drawingCircle && getApp().getMap().getState() !== COALITIONAREA_DRAW_CIRCLE) setDrawingCircle(false);
/* If we are not in any drawing mode, force the map in edit mode */
if (props.open && !drawingPolygon && !drawingCircle)
getApp().getMap().setState(COALITIONAREA_EDIT);
if (props.open && !drawingPolygon && !drawingCircle) getApp().getMap().setState(COALITIONAREA_EDIT);
/* Align the state of the coalition toggle to the coalition of the area */
if (
activeCoalitionArea &&
activeCoalitionArea?.getCoalition() !== areaCoalition
)
setAreaCoalition(activeCoalitionArea?.getCoalition());
if (activeCoalitionArea && activeCoalitionArea?.getCoalition() !== areaCoalition) setAreaCoalition(activeCoalitionArea?.getCoalition());
});
useEffect(() => {
if (!props.open) {
if (
[
COALITIONAREA_EDIT,
COALITIONAREA_DRAW_CIRCLE,
COALITIONAREA_DRAW_POLYGON,
].includes(getApp()?.getMap()?.getState())
)
if ([COALITIONAREA_EDIT, COALITIONAREA_DRAW_CIRCLE, COALITIONAREA_DRAW_POLYGON].includes(getApp()?.getMap()?.getState()))
getApp().getMap().setState(IDLE);
}
});
document.addEventListener("mapStateChanged", (event: any) => {
if (
drawingPolygon &&
getApp().getMap().getState() !== COALITIONAREA_DRAW_POLYGON
)
setDrawingPolygon(false);
if (drawingPolygon && getApp().getMap().getState() !== COALITIONAREA_DRAW_POLYGON) setDrawingPolygon(false);
if (getApp().getMap().getState() == COALITIONAREA_EDIT) {
setActiveCoalitionArea(
getApp().getMap().getSelectedCoalitionArea() ?? null
);
setActiveCoalitionArea(getApp().getMap().getSelectedCoalitionArea() ?? null);
}
});
@@ -109,21 +76,15 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
{activeCoalitionArea === null && !drawingPolygon && !drawingCircle && (
<>
<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.
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 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 circle 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. Click on a shape to select it.
</div>
<div className="text-gray-100">Use the polygon or circle 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. Click on a shape to select it.</div>
</div>
</div>
</>
@@ -137,12 +98,9 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
<FaQuestionCircle className="my-4 ml-2 mr-6 text-gray-400" />
</div>
<div className="flex flex-col gap-1">
<div className="text-gray-100">
Click on the map to add vertices to the polygon.
</div>
<div className="text-gray-100">Click on the map to add vertices to the polygon.</div>
<div className="text-gray-400">
When you are done, double click on the map to finalize the
polygon. Vertices can be dragged or added to adjust the shape.
When you are done, double click on the map to finalize the polygon. Vertices can be dragged or added to adjust the shape.
</div>
</div>
</div>
@@ -156,13 +114,8 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
<FaQuestionCircle className="my-4 ml-2 mr-6 text-gray-400" />
</div>
<div className="flex flex-col gap-1">
<div className="text-gray-100">
Click on the map to add a new circle.
</div>
<div className="text-gray-400">
You can drag the circle to move it and you can use the handle to
set the radius.
</div>
<div className="text-gray-100">Click on the map to add a new circle.</div>
<div className="text-gray-400">You can drag the circle to move it and you can use the handle to set the radius.</div>
</div>
</div>
)}
@@ -177,8 +130,7 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
tooltip={"Add a new polygon"}
checked={drawingPolygon}
onClick={() => {
if (drawingPolygon)
getApp().getMap().setState(COALITIONAREA_EDIT);
if (drawingPolygon) getApp().getMap().setState(COALITIONAREA_EDIT);
else getApp().getMap().setState(COALITIONAREA_DRAW_POLYGON);
setDrawingPolygon(!drawingPolygon);
}}
@@ -191,8 +143,7 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
tooltip={"Add a new circle"}
checked={drawingCircle}
onClick={() => {
if (drawingCircle)
getApp().getMap().setState(COALITIONAREA_EDIT);
if (drawingCircle) getApp().getMap().setState(COALITIONAREA_EDIT);
else getApp().getMap().setState(COALITIONAREA_DRAW_CIRCLE);
setDrawingCircle(!drawingCircle);
}}
@@ -213,13 +164,14 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
>
<div className="my-auto flex justify-between text-md">
Area label
<div className="rounded-md bg-red-800 p-2" onClick={() => {
getApp().getMap().deleteCoalitionArea(activeCoalitionArea);
setActiveCoalitionArea(null);
}}>
<FaTrash
className={`text-gray-50`}
></FaTrash>
<div
className="rounded-md bg-red-800 p-2"
onClick={() => {
getApp().getMap().deleteCoalitionArea(activeCoalitionArea);
setActiveCoalitionArea(null);
}}
>
<FaTrash className={`text-gray-50`}></FaTrash>
</div>
</div>
<input
@@ -233,9 +185,7 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
focus:border-blue-500 focus:ring-blue-500
`}
placeholder={activeCoalitionArea.getLabelText()}
onInput={(ev) =>
activeCoalitionArea.setLabelText(ev.currentTarget.value)
}
onInput={(ev) => activeCoalitionArea.setLabelText(ev.currentTarget.value)}
></input>
</div>
<div
@@ -262,9 +212,7 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
bg-olympus-600 p-5
`}
>
<div className="border-b-2 border-b-olympus-100 pb-4 text-gray-300">
Automatic IADS generation
</div>
<div className="border-b-2 border-b-olympus-100 pb-4 text-gray-300">Automatic IADS generation</div>
<OlDropdown className="" label="Units types">
{getApp()
.getGroundUnitDatabase()
@@ -272,9 +220,7 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
.map((type) => {
if (!(type in typesSelection)) {
typesSelection[type] = true;
setTypesSelection(
JSON.parse(JSON.stringify(typesSelection))
);
setTypesSelection(JSON.parse(JSON.stringify(typesSelection)));
}
return (
@@ -283,9 +229,7 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
checked={typesSelection[type]}
onChange={(ev) => {
typesSelection[type] = ev.currentTarget.checked;
setTypesSelection(
JSON.parse(JSON.stringify(typesSelection))
);
setTypesSelection(JSON.parse(JSON.stringify(typesSelection)));
}}
/>
{type}
@@ -300,9 +244,7 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
.map((era) => {
if (!(era in erasSelection)) {
erasSelection[era] = true;
setErasSelection(
JSON.parse(JSON.stringify(erasSelection))
);
setErasSelection(JSON.parse(JSON.stringify(erasSelection)));
}
return (
@@ -311,9 +253,7 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
checked={erasSelection[era]}
onChange={(ev) => {
erasSelection[era] = ev.currentTarget.checked;
setErasSelection(
JSON.parse(JSON.stringify(erasSelection))
);
setErasSelection(JSON.parse(JSON.stringify(erasSelection)));
}}
/>
{era}
@@ -325,9 +265,7 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
{["Short range", "Medium range", "Long range"].map((range) => {
if (!(range in rangesSelection)) {
rangesSelection[range] = true;
setRangesSelection(
JSON.parse(JSON.stringify(rangesSelection))
);
setRangesSelection(JSON.parse(JSON.stringify(rangesSelection)));
}
return (
@@ -336,9 +274,7 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
checked={rangesSelection[range]}
onChange={(ev) => {
rangesSelection[range] = ev.currentTarget.checked;
setErasSelection(
JSON.parse(JSON.stringify(rangesSelection))
);
setErasSelection(JSON.parse(JSON.stringify(rangesSelection)));
}}
/>
{range}
@@ -388,9 +324,7 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
<OlCheckbox
checked={forceCoalitionAppropriateUnits}
onChange={() => {
setForceCoalitionApproriateUnits(
!forceCoalitionAppropriateUnits
);
setForceCoalitionApproriateUnits(!forceCoalitionAppropriateUnits);
}}
/>
Force coalition appropriate units

View File

@@ -1,17 +1,6 @@
import React, { useEffect, useRef, useState } from "react";
import {
OlRoundStateButton,
OlStateButton,
OlLockStateButton,
} from "../components/olstatebutton";
import {
faSkull,
faCamera,
faFlag,
faLink,
faUnlink,
faBars,
} from "@fortawesome/free-solid-svg-icons";
import { OlRoundStateButton, OlStateButton, OlLockStateButton } from "../components/olstatebutton";
import { faSkull, faCamera, faFlag, faLink, faUnlink, faBars } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { EventsConsumer } from "../../eventscontext";
import { StateConsumer } from "../../statecontext";
@@ -61,20 +50,22 @@ export function Header() {
{() => (
<nav
className={`
flex w-full gap-4 border-gray-200 bg-gray-300 px-3
drop-shadow-md z-ui-4 align-center
z-10 flex w-full gap-4 border-gray-200 bg-gray-300 px-3
drop-shadow-md align-center
dark:border-gray-800 dark:bg-olympus-900
`}
>
<img
src="vite/images/icon.png"
className="my-auto h-10 w-10 rounded-md p-0"
className={`
my-auto h-10 w-10 rounded-md p-0
`}
></img>
{!scrolledLeft && (
<FaChevronLeft
className={`
absolute left-14 h-full w-6 rounded-lg px-2 py-3.5
text-gray-200 z-ui-1
text-gray-200
dark:bg-olympus-900
`}
/>
@@ -124,11 +115,7 @@ export function Header() {
</div>
</div>
<div>
<OlLockStateButton
checked={false}
onClick={() => {}}
tooltip="Lock/unlock protected units (from scripted mission)"
/>
<OlLockStateButton checked={false} onClick={() => {}} tooltip="Lock/unlock protected units (from scripted mission)" />
</div>
<div className={`h-8 w-0 border-l-[2px] border-gray-700`}></div>
<div
@@ -145,12 +132,7 @@ export function Header() {
<OlRoundStateButton
key={entry[0]}
onClick={() => {
getApp()
.getMap()
.setHiddenType(
entry[0],
!appState.mapHiddenTypes[entry[0]]
);
getApp().getMap().setHiddenType(entry[0], !appState.mapHiddenTypes[entry[0]]);
}}
checked={!appState.mapHiddenTypes[entry[0]]}
icon={entry[1]}
@@ -166,36 +148,21 @@ export function Header() {
`}
>
<OlRoundStateButton
onClick={() =>
getApp()
.getMap()
.setHiddenType("blue", !appState.mapHiddenTypes["blue"])
}
onClick={() => getApp().getMap().setHiddenType("blue", !appState.mapHiddenTypes["blue"])}
checked={!appState.mapHiddenTypes["blue"]}
icon={faFlag}
className={"!text-blue-500"}
tooltip={"Hide/show blue units"}
/>
<OlRoundStateButton
onClick={() =>
getApp()
.getMap()
.setHiddenType("red", !appState.mapHiddenTypes["red"])
}
onClick={() => getApp().getMap().setHiddenType("red", !appState.mapHiddenTypes["red"])}
checked={!appState.mapHiddenTypes["red"]}
icon={faFlag}
className={"!text-red-500"}
tooltip={"Hide/show red units"}
/>
<OlRoundStateButton
onClick={() =>
getApp()
.getMap()
.setHiddenType(
"neutral",
!appState.mapHiddenTypes["neutral"]
)
}
onClick={() => getApp().getMap().setHiddenType("neutral", !appState.mapHiddenTypes["neutral"])}
checked={!appState.mapHiddenTypes["neutral"]}
icon={faFlag}
className={"!text-gray-500"}
@@ -221,12 +188,7 @@ export function Header() {
<OlRoundStateButton
key={entry[0]}
onClick={() => {
getApp()
.getMap()
.setHiddenType(
entry[0],
!appState.mapHiddenTypes[entry[0]]
);
getApp().getMap().setHiddenType(entry[0], !appState.mapHiddenTypes[entry[0]]);
}}
checked={!appState.mapHiddenTypes[entry[0]]}
icon={entry[1]}
@@ -236,25 +198,12 @@ export function Header() {
})}
</div>
<OlLabelToggle
toggled={false}
leftLabel={"Live"}
rightLabel={"Map"}
onClick={() => {}}
></OlLabelToggle>
<OlStateButton
checked={false}
icon={faCamera}
onClick={() => {}}
tooltip="Activate/deactivate camera plugin"
/>
<OlLabelToggle toggled={false} leftLabel={"Live"} rightLabel={"Map"} onClick={() => {}}></OlLabelToggle>
<OlStateButton checked={false} icon={faCamera} onClick={() => {}} tooltip="Activate/deactivate camera plugin" />
<OlDropdown label={appState.activeMapSource} className="w-60">
{appState.mapSources.map((source) => {
return (
<OlDropdownItem
key={source}
onClick={() => getApp().getMap().setLayerName(source)}
>
<OlDropdownItem key={source} onClick={() => getApp().getMap().setLayerName(source)}>
<div className="truncate">{source}</div>
</OlDropdownItem>
);
@@ -265,7 +214,7 @@ export function Header() {
<FaChevronRight
className={`
absolute right-0 h-full w-6 rounded-lg px-2 py-3.5
text-gray-200 z-ui-1
text-gray-200
dark:bg-olympus-900
`}
/>

View File

@@ -16,18 +16,9 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { VERSION } from "../../olympusapp";
import { faGithub } from "@fortawesome/free-brands-svg-icons";
export function MainMenu(props: {
open: boolean;
onClose: () => void;
children?: JSX.Element | JSX.Element[];
}) {
export function MainMenu(props: { open: boolean; onClose: () => void; children?: JSX.Element | JSX.Element[] }) {
return (
<Menu
title="Main Menu"
open={props.open}
showBackButton={false}
onClose={props.onClose}
>
<Menu title="Main Menu" open={props.open} showBackButton={false} onClose={props.onClose}>
<div
className={`
flex flex-col gap-1 p-5 font-normal text-gray-900
@@ -45,10 +36,7 @@ export function MainMenu(props: {
<FontAwesomeIcon icon={faCheckCircle} className={`my-auto`} />
Olympus Version {VERSION}
</div>
<div className="text-sm text-gray-400">
You can use the Olympus Manager to update port, passwords or other
settings.
</div>
<div className="text-sm text-gray-400">You can use the Olympus Manager to update port, passwords or other settings.</div>
</div>
<div

View File

@@ -5,18 +5,18 @@ import { getApp } from "../../olympusapp";
import { FaChevronDown, FaChevronUp } from "react-icons/fa6";
export function MiniMapPanel(props: {}) {
var [frameRate, setFrameRate] = useState(0);
var [load, setLoad] = useState(0);
var [elapsedTime, setElapsedTime] = useState(0);
var [missionTime, setMissionTime] = useState({
const [frameRate, setFrameRate] = useState(0);
const [load, setLoad] = useState(0);
const [elapsedTime, setElapsedTime] = useState(0);
const [missionTime, setMissionTime] = useState({
h: 0,
m: 0,
s: 0,
} as DateAndTime["time"]);
var [connected, setConnected] = useState(false);
var [paused, setPaused] = useState(false);
var [showMissionTime, setShowMissionTime] = useState(false);
var [showMinimap, setShowMinimap] = useState(false);
const [connected, setConnected] = useState(false);
const [paused, setPaused] = useState(false);
const [showMissionTime, setShowMissionTime] = useState(false);
const [showMinimap, setShowMinimap] = useState(false);
document.addEventListener("serverStatusUpdated", (ev) => {
const detail = (ev as CustomEvent).detail;
@@ -73,7 +73,7 @@ export function MiniMapPanel(props: {}) {
className={`
absolute right-[10px]
${showMinimap ? `top-[232px]` : `top-[70px]`}
w-[288px] z-ui-0 flex items-center justify-between
flex w-[288px] items-center justify-between
${showMinimap ? `rounded-b-lg` : `rounded-lg`}
bg-gray-200 p-3 text-sm backdrop-blur-lg backdrop-grayscale
dark:bg-olympus-800/90 dark:text-gray-200

View File

@@ -6,19 +6,9 @@ import { OlNumberInput } from "../components/olnumberinput";
import { MapOptions } from "../../types/types";
import { getApp } from "../../olympusapp";
export function Options(props: {
open: boolean;
onClose: () => void;
options: MapOptions;
children?: JSX.Element | JSX.Element[];
}) {
export function Options(props: { open: boolean; onClose: () => void; options: MapOptions; children?: JSX.Element | JSX.Element[] }) {
return (
<Menu
title="User preferences"
open={props.open}
showBackButton={false}
onClose={props.onClose}
>
<Menu title="User preferences" open={props.open} showBackButton={false} onClose={props.onClose}>
<div
className={`
flex flex-col gap-2 p-5 font-normal text-gray-800
@@ -32,15 +22,10 @@ export function Options(props: {
dark:hover:bg-olympus-400
`}
onClick={() => {
getApp()
.getMap()
.setOption("showUnitLabels", !props.options.showUnitLabels);
getApp().getMap().setOption("showUnitLabels", !props.options.showUnitLabels);
}}
>
<OlCheckbox
checked={props.options.showUnitLabels}
onChange={() => {}}
></OlCheckbox>
<OlCheckbox checked={props.options.showUnitLabels} onChange={() => {}}></OlCheckbox>
<span>Show Unit Labels</span>
<kbd
className={`
@@ -59,18 +44,10 @@ export function Options(props: {
dark:hover:bg-olympus-400
`}
onClick={() => {
getApp()
.getMap()
.setOption(
"showUnitsEngagementRings",
!props.options.showUnitsEngagementRings
);
getApp().getMap().setOption("showUnitsEngagementRings", !props.options.showUnitsEngagementRings);
}}
>
<OlCheckbox
checked={props.options.showUnitsEngagementRings}
onChange={() => {}}
></OlCheckbox>
<OlCheckbox checked={props.options.showUnitsEngagementRings} onChange={() => {}}></OlCheckbox>
<span>Show Threat Rings</span>
<kbd
className={`
@@ -89,18 +66,10 @@ export function Options(props: {
dark:hover:bg-olympus-400
`}
onClick={() => {
getApp()
.getMap()
.setOption(
"showUnitsAcquisitionRings",
!props.options.showUnitsAcquisitionRings
);
getApp().getMap().setOption("showUnitsAcquisitionRings", !props.options.showUnitsAcquisitionRings);
}}
>
<OlCheckbox
checked={props.options.showUnitsAcquisitionRings}
onChange={() => {}}
></OlCheckbox>
<OlCheckbox checked={props.options.showUnitsAcquisitionRings} onChange={() => {}}></OlCheckbox>
<span>Show Detection rings</span>
<kbd
className={`
@@ -119,15 +88,10 @@ export function Options(props: {
dark:hover:bg-olympus-400
`}
onClick={() => {
getApp()
.getMap()
.setOption("showUnitTargets", !props.options.showUnitTargets);
getApp().getMap().setOption("showUnitTargets", !props.options.showUnitTargets);
}}
>
<OlCheckbox
checked={props.options.showUnitTargets}
onChange={() => {}}
></OlCheckbox>
<OlCheckbox checked={props.options.showUnitTargets} onChange={() => {}}></OlCheckbox>
<span>Show Detection lines</span>
<kbd
className={`
@@ -146,18 +110,10 @@ export function Options(props: {
dark:hover:bg-olympus-400
`}
onClick={() => {
getApp()
.getMap()
.setOption(
"hideUnitsShortRangeRings",
!props.options.hideUnitsShortRangeRings
);
getApp().getMap().setOption("hideUnitsShortRangeRings", !props.options.hideUnitsShortRangeRings);
}}
>
<OlCheckbox
checked={props.options.hideUnitsShortRangeRings}
onChange={() => {}}
></OlCheckbox>
<OlCheckbox checked={props.options.hideUnitsShortRangeRings} onChange={() => {}}></OlCheckbox>
<span>Hide Short range Rings</span>
<kbd
className={`
@@ -176,15 +132,10 @@ export function Options(props: {
dark:hover:bg-olympus-400
`}
onClick={() => {
getApp()
.getMap()
.setOption("hideGroupMembers", !props.options.hideGroupMembers);
getApp().getMap().setOption("hideGroupMembers", !props.options.hideGroupMembers);
}}
>
<OlCheckbox
checked={props.options.hideGroupMembers}
onChange={() => {}}
></OlCheckbox>
<OlCheckbox checked={props.options.hideGroupMembers} onChange={() => {}}></OlCheckbox>
<span>Hide Group members</span>
<kbd
className={`
@@ -203,15 +154,10 @@ export function Options(props: {
dark:hover:bg-olympus-400
`}
onClick={() => {
getApp()
.getMap()
.setOption("showMinimap", !props.options.showMinimap);
getApp().getMap().setOption("showMinimap", !props.options.showMinimap);
}}
>
<OlCheckbox
checked={props.options.showMinimap}
onChange={() => {}}
></OlCheckbox>
<OlCheckbox checked={props.options.showMinimap} onChange={() => {}}></OlCheckbox>
<span>Show minimap</span>
<kbd
className={`

View File

@@ -1,14 +1,6 @@
import React, { useState } from "react";
import { OlStateButton } from "../components/olstatebutton";
import {
faGamepad,
faRuler,
faPencil,
faEllipsisV,
faCog,
faQuestionCircle,
faPlusSquare
} from "@fortawesome/free-solid-svg-icons";
import { faGamepad, faRuler, faPencil, faEllipsisV, faCog, faQuestionCircle, faPlusSquare } from "@fortawesome/free-solid-svg-icons";
import { EventsConsumer } from "../../eventscontext";
import { StateConsumer } from "../../statecontext";
@@ -20,7 +12,7 @@ export function SideBar() {
{(events) => (
<nav
className={`
flex flex-col z-ui-4 h-full bg-gray-300
absolute left-0 z-20 flex h-full flex-col bg-gray-300
dark:bg-olympus-900
`}
>
@@ -52,12 +44,7 @@ export function SideBar() {
icon={faGamepad}
tooltip=""
></OlStateButton>
<OlStateButton
onClick={events.toggleMeasureMenuVisible}
checked={appState.measureMenuVisible}
icon={faRuler}
tooltip=""
></OlStateButton>
<OlStateButton onClick={events.toggleMeasureMenuVisible} checked={appState.measureMenuVisible} icon={faRuler} tooltip=""></OlStateButton>
<OlStateButton
onClick={events.toggleDrawingMenuVisible}
checked={appState.drawingMenuVisible}
@@ -73,9 +60,7 @@ export function SideBar() {
`}
>
<OlStateButton
onClick={() =>
window.open("https://github.com/Pax1601/DCSOlympus/wiki")
}
onClick={() => window.open("https://github.com/Pax1601/DCSOlympus/wiki")}
checked={false}
icon={faQuestionCircle}
tooltip="Open user guide on separate window"

View File

@@ -20,26 +20,15 @@ import { getUnitsByLabel } from "../../other/utils";
library.add(faPlus);
export function SpawnMenu(props: {
open: boolean;
onClose: () => void;
children?: JSX.Element | JSX.Element[];
}) {
var [blueprint, setBlueprint] = useState(null as null | UnitBlueprint);
var [filterString, setFilterString] = useState("");
export function SpawnMenu(props: { open: boolean; onClose: () => void; children?: JSX.Element | JSX.Element[] }) {
const [blueprint, setBlueprint] = useState(null as null | UnitBlueprint);
const [filterString, setFilterString] = useState("");
const [
filteredAircraft,
filteredHelicopters,
filteredAirDefense,
filteredGroundUnits,
filteredNavyUnits,
] = getUnitsByLabel(filterString);
const [filteredAircraft, filteredHelicopters, filteredAirDefense, filteredGroundUnits, filteredNavyUnits] = getUnitsByLabel(filterString);
useEffect(() => {
if (!props.open) {
if (getApp()?.getMap()?.getState() === SPAWN_UNIT)
getApp().getMap().setState(IDLE);
if (getApp()?.getMap()?.getState() === SPAWN_UNIT) getApp().getMap().setState(IDLE);
if (blueprint !== null) setBlueprint(null);
}
});
@@ -58,10 +47,7 @@ export function SpawnMenu(props: {
<>
{blueprint === null && (
<div className="p-5">
<OlSearchBar
onChange={(value) => setFilterString(value)}
text={filterString}
/>
<OlSearchBar onChange={(value) => setFilterString(value)} text={filterString} />
<OlAccordion title={`Aircraft`}>
<div
className={`
@@ -69,14 +55,7 @@ export function SpawnMenu(props: {
`}
>
{Object.entries(filteredAircraft).map((entry) => {
return (
<OlUnitEntryList
key={entry[0]}
icon={olButtonsVisibilityAircraft}
blueprint={entry[1]}
onClick={() => setBlueprint(entry[1])}
/>
);
return <OlUnitEntryList key={entry[0]} icon={olButtonsVisibilityAircraft} blueprint={entry[1]} onClick={() => setBlueprint(entry[1])} />;
})}
</div>
</OlAccordion>
@@ -87,14 +66,7 @@ export function SpawnMenu(props: {
`}
>
{Object.entries(filteredHelicopters).map((entry) => {
return (
<OlUnitEntryList
key={entry[0]}
icon={olButtonsVisibilityHelicopter}
blueprint={entry[1]}
onClick={() => setBlueprint(entry[1])}
/>
);
return <OlUnitEntryList key={entry[0]} icon={olButtonsVisibilityHelicopter} blueprint={entry[1]} onClick={() => setBlueprint(entry[1])} />;
})}
</div>
</OlAccordion>
@@ -105,14 +77,7 @@ export function SpawnMenu(props: {
`}
>
{Object.entries(filteredAirDefense).map((entry) => {
return (
<OlUnitEntryList
key={entry[0]}
icon={olButtonsVisibilityGroundunitSam}
blueprint={entry[1]}
onClick={() => setBlueprint(entry[1])}
/>
);
return <OlUnitEntryList key={entry[0]} icon={olButtonsVisibilityGroundunitSam} blueprint={entry[1]} onClick={() => setBlueprint(entry[1])} />;
})}
</div>
</OlAccordion>
@@ -123,14 +88,7 @@ export function SpawnMenu(props: {
`}
>
{Object.entries(filteredGroundUnits).map((entry) => {
return (
<OlUnitEntryList
key={entry[0]}
icon={olButtonsVisibilityGroundunit}
blueprint={entry[1]}
onClick={() => setBlueprint(entry[1])}
/>
);
return <OlUnitEntryList key={entry[0]} icon={olButtonsVisibilityGroundunit} blueprint={entry[1]} onClick={() => setBlueprint(entry[1])} />;
})}
</div>
</OlAccordion>
@@ -141,14 +99,7 @@ export function SpawnMenu(props: {
`}
>
{Object.entries(filteredNavyUnits).map((entry) => {
return (
<OlUnitEntryList
key={entry[0]}
icon={olButtonsVisibilityNavyunit}
blueprint={entry[1]}
onClick={() => setBlueprint(entry[1])}
/>
);
return <OlUnitEntryList key={entry[0]} icon={olButtonsVisibilityNavyunit} blueprint={entry[1]} onClick={() => setBlueprint(entry[1])} />;
})}
</div>
</OlAccordion>

File diff suppressed because it is too large Load Diff

View File

@@ -9,14 +9,10 @@ import { FaInfoCircle } from "react-icons/fa";
import { FaChevronLeft, FaChevronRight } from "react-icons/fa6";
export function UnitMouseControlBar(props: {}) {
var [open, setOpen] = useState(false);
var [selectedUnits, setSelectedUnits] = useState([] as Unit[]);
var [contextActionsSet, setContextActionsSet] = useState(
new ContextActionSet()
);
var [activeContextAction, setActiveContextAction] = useState(
null as null | ContextAction
);
const [open, setOpen] = useState(false);
const [selectedUnits, setSelectedUnits] = useState([] as Unit[]);
const [contextActionsSet, setContextActionsSet] = useState(new ContextActionSet());
const [activeContextAction, setActiveContextAction] = useState(null as null | ContextAction);
const [scrolledLeft, setScrolledLeft] = useState(true);
const [scrolledRight, setScrolledRight] = useState(false);
@@ -87,7 +83,7 @@ export function UnitMouseControlBar(props: {}) {
<div
className={`
absolute left-[50%] top-16 flex max-w-[80%]
translate-x-[calc(-50%+2rem)] gap-2 rounded-md bg-gray-200 z-ui-2
translate-x-[calc(-50%+2rem)] gap-2 rounded-md bg-gray-200
dark:bg-olympus-900
`}
>
@@ -100,40 +96,34 @@ export function UnitMouseControlBar(props: {}) {
`}
/>
)}
<div
className="flex gap-2 overflow-x-auto no-scrollbar p-2"
onScroll={(ev) => onScroll(ev.target)}
ref={scrollRef}
>
{Object.values(contextActionsSet.getContextActions()).map(
(contextAction) => {
return (
<OlStateButton
checked={contextAction === activeContextAction}
icon={contextAction.getIcon()}
tooltip={contextAction.getLabel()}
onClick={() => {
if (contextAction.getOptions().executeImmediately) {
setActiveContextAction(null);
contextAction.executeCallback(null, null);
<div className="flex gap-2 overflow-x-auto no-scrollbar p-2" onScroll={(ev) => onScroll(ev.target)} ref={scrollRef}>
{Object.values(contextActionsSet.getContextActions()).map((contextAction) => {
return (
<OlStateButton
checked={contextAction === activeContextAction}
icon={contextAction.getIcon()}
tooltip={contextAction.getLabel()}
onClick={() => {
if (contextAction.getOptions().executeImmediately) {
setActiveContextAction(null);
contextAction.executeCallback(null, null);
} else {
if (activeContextAction != contextAction) {
setActiveContextAction(contextAction);
getApp().getMap().setState(CONTEXT_ACTION, {
contextAction: contextAction,
});
} else {
if (activeContextAction != contextAction) {
setActiveContextAction(contextAction);
getApp().getMap().setState(CONTEXT_ACTION, {
contextAction: contextAction,
});
} else {
setActiveContextAction(null);
getApp().getMap().setState(CONTEXT_ACTION, {
contextAction: null,
});
}
setActiveContextAction(null);
getApp().getMap().setState(CONTEXT_ACTION, {
contextAction: null,
});
}
}}
/>
);
}
)}
}
}}
/>
);
})}
</div>
{!scrolledRight && (
<FaChevronRight
@@ -148,9 +138,9 @@ export function UnitMouseControlBar(props: {}) {
{activeContextAction && (
<div
className={`
absolute left-[50%] top-32 flex translate-x-[calc(-50%+2rem)]
items-center gap-2 rounded-md bg-gray-200 p-4 z-ui-1
min-w-[300px]
absolute left-[50%] top-32 flex min-w-[300px]
translate-x-[calc(-50%+2rem)] items-center gap-2 rounded-md
bg-gray-200 p-4
dark:bg-olympus-800
`}
>

View File

@@ -21,14 +21,12 @@ export function UnitSpawnMenu(props: { blueprint: UnitBlueprint }) {
const altitudeStep = 500;
/* State initialization */
var [spawnCoalition, setSpawnCoalition] = useState("blue" as Coalition);
var [spawnNumber, setSpawnNumber] = useState(1);
var [spawnRole, setSpawnRole] = useState("");
var [spawnLoadoutName, setSpawnLoadout] = useState("");
var [spawnAltitude, setSpawnAltitude] = useState(
(maxAltitude - minAltitude) / 2
);
var [spawnAltitudeType, setSpawnAltitudeType] = useState(false);
const [spawnCoalition, setSpawnCoalition] = useState("blue" as Coalition);
const [spawnNumber, setSpawnNumber] = useState(1);
const [spawnRole, setSpawnRole] = useState("");
const [spawnLoadoutName, setSpawnLoadout] = useState("");
const [spawnAltitude, setSpawnAltitude] = useState((maxAltitude - minAltitude) / 2);
const [spawnAltitudeType, setSpawnAltitudeType] = useState(false);
/* When the menu is opened show the unit preview on the map as a cursor */
useEffect(() => {
@@ -53,8 +51,7 @@ export function UnitSpawnMenu(props: { blueprint: UnitBlueprint }) {
},
});
} else {
if (getApp()?.getMap()?.getState() === SPAWN_UNIT)
getApp().getMap().setState(IDLE);
if (getApp()?.getMap()?.getState() === SPAWN_UNIT) getApp().getMap().setState(IDLE);
}
});
@@ -76,9 +73,7 @@ export function UnitSpawnMenu(props: { blueprint: UnitBlueprint }) {
});
/* Initialize the loadout */
spawnLoadoutName === "" &&
loadouts.length > 0 &&
setSpawnLoadout(loadouts[0].name);
spawnLoadoutName === "" && loadouts.length > 0 && setSpawnLoadout(loadouts[0].name);
const spawnLoadout = props.blueprint.loadouts?.find((loadout) => {
return loadout.name === spawnLoadoutName;
});
@@ -111,13 +106,7 @@ export function UnitSpawnMenu(props: { blueprint: UnitBlueprint }) {
setSpawnNumber(Math.min(maxNumber, spawnNumber + 1));
}}
onChange={(ev) => {
!isNaN(Number(ev.target.value)) &&
setSpawnNumber(
Math.max(
minNumber,
Math.min(maxNumber, Number(ev.target.value))
)
);
!isNaN(Number(ev.target.value)) && setSpawnNumber(Math.max(minNumber, Math.min(maxNumber, Number(ev.target.value))));
}}
/>
</div>
@@ -143,12 +132,7 @@ export function UnitSpawnMenu(props: { blueprint: UnitBlueprint }) {
`}
>{`${Intl.NumberFormat("en-US").format(spawnAltitude)} FT`}</span>
</div>
<OlLabelToggle
toggled={spawnAltitudeType}
leftLabel={"AGL"}
rightLabel={"ASL"}
onClick={() => setSpawnAltitudeType(!spawnAltitudeType)}
/>
<OlLabelToggle toggled={spawnAltitudeType} leftLabel={"AGL"} rightLabel={"ASL"} onClick={() => setSpawnAltitudeType(!spawnAltitudeType)} />
</div>
<OlRangeSlider
onChange={(ev) => setSpawnAltitude(Number(ev.target.value))}

View File

@@ -11,14 +11,7 @@ import { MainMenu } from "./panels/mainmenu";
import { SideBar } from "./panels/sidebar";
import { Options } from "./panels/options";
import { MapHiddenTypes, MapOptions } from "../types/types";
import {
BLUE_COMMANDER,
GAME_MASTER,
IDLE,
MAP_HIDDEN_TYPES_DEFAULTS,
MAP_OPTIONS_DEFAULTS,
RED_COMMANDER,
} from "../constants/constants";
import { BLUE_COMMANDER, GAME_MASTER, IDLE, MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS, RED_COMMANDER } from "../constants/constants";
import { getApp, setupApp } from "../olympusapp";
import { LoginModal } from "./modals/login";
import { sha256 } from "js-sha256";
@@ -39,22 +32,22 @@ export type OlympusState = {
};
export function UI() {
var [loginModalVisible, setLoginModalVisible] = useState(true);
var [mainMenuVisible, setMainMenuVisible] = useState(false);
var [spawnMenuVisible, setSpawnMenuVisible] = useState(false);
var [unitControlMenuVisible, setUnitControlMenuVisible] = useState(false);
var [measureMenuVisible, setMeasureMenuVisible] = useState(false);
var [drawingMenuVisible, setDrawingMenuVisible] = useState(false);
var [optionsMenuVisible, setOptionsMenuVisible] = useState(false);
var [mapHiddenTypes, setMapHiddenTypes] = useState(MAP_HIDDEN_TYPES_DEFAULTS);
var [mapOptions, setMapOptions] = useState(MAP_OPTIONS_DEFAULTS);
var [checkingPassword, setCheckingPassword] = useState(false);
var [loginError, setLoginError] = useState(false);
var [commandMode, setCommandMode] = useState(null as null | string);
var [mapSources, setMapSources] = useState([] as string[]);
var [activeMapSource, setActiveMapSource] = useState("");
var [mapBoxSelection, setMapBoxSelection] = useState(false);
var [mapState, setMapState] = useState(IDLE);
const [loginModalVisible, setLoginModalVisible] = useState(true);
const [mainMenuVisible, setMainMenuVisible] = useState(false);
const [spawnMenuVisible, setSpawnMenuVisible] = useState(false);
const [unitControlMenuVisible, setUnitControlMenuVisible] = useState(false);
const [measureMenuVisible, setMeasureMenuVisible] = useState(false);
const [drawingMenuVisible, setDrawingMenuVisible] = useState(false);
const [optionsMenuVisible, setOptionsMenuVisible] = useState(false);
const [mapHiddenTypes, setMapHiddenTypes] = useState(MAP_HIDDEN_TYPES_DEFAULTS);
const [mapOptions, setMapOptions] = useState(MAP_OPTIONS_DEFAULTS);
const [checkingPassword, setCheckingPassword] = useState(false);
const [loginError, setLoginError] = useState(false);
const [commandMode, setCommandMode] = useState(null as null | string);
const [mapSources, setMapSources] = useState([] as string[]);
const [activeMapSource, setActiveMapSource] = useState("");
const [mapBoxSelection, setMapBoxSelection] = useState(false);
const [mapState, setMapState] = useState(IDLE);
document.addEventListener("hiddenTypesChanged", (ev) => {
setMapHiddenTypes({ ...getApp().getMap().getHiddenTypes() });
@@ -65,10 +58,9 @@ export function UI() {
});
document.addEventListener("mapStateChanged", (ev) => {
if ((ev as CustomEvent).detail === IDLE && mapState !== IDLE)
hideAllMenus();
setMapState(String((ev as CustomEvent).detail))
if ((ev as CustomEvent).detail === IDLE && mapState !== IDLE) hideAllMenus();
setMapState(String((ev as CustomEvent).detail));
});
document.addEventListener("mapSourceChanged", (ev) => {
@@ -78,9 +70,7 @@ export function UI() {
document.addEventListener("configLoaded", (ev) => {
let config = getApp().getConfig();
var sources = Object.keys(config.mapMirrors).concat(
Object.keys(config.mapLayers)
);
var sources = Object.keys(config.mapMirrors).concat(Object.keys(config.mapLayers));
setMapSources(sources);
setActiveMapSource(sources[0]);
});
@@ -112,9 +102,7 @@ export function UI() {
(response) => {
const commandMode = response.mission.commandModeOptions.commandMode;
try {
[GAME_MASTER, BLUE_COMMANDER, RED_COMMANDER].includes(commandMode)
? setCommandMode(commandMode)
: setLoginError(true);
[GAME_MASTER, BLUE_COMMANDER, RED_COMMANDER].includes(commandMode) ? setCommandMode(commandMode) : setLoginError(true);
} catch {
setLoginError(true);
}
@@ -197,14 +185,18 @@ export function UI() {
},
}}
>
<div className="absolute left-0 top-0 flex h-full w-full flex-col">
<div
className={`
absolute left-0 top-0 flex h-full w-full flex-col
`}
>
<Header />
<div className="flex h-full">
<div className="flex justify-reverse h-full">
{loginModalVisible && (
<>
<div
className={`
fixed left-0 top-0 h-full w-full z-ui-5 bg-[#111111]/95
fixed left-0 top-0 z-30 h-full w-full bg-[#111111]/95
`}
></div>
<LoginModal
@@ -223,32 +215,17 @@ export function UI() {
/>
</>
)}
<SideBar />
<MainMenu
open={mainMenuVisible}
onClose={() => setMainMenuVisible(false)}
/>
<SpawnMenu
open={spawnMenuVisible}
onClose={() => setSpawnMenuVisible(false)}
/>
<Options
open={optionsMenuVisible}
onClose={() => setOptionsMenuVisible(false)}
options={mapOptions}
/>
<div id="map-container" className="z-0 h-full w-screen" />
<MainMenu open={mainMenuVisible} onClose={() => setMainMenuVisible(false)} />
<SpawnMenu open={spawnMenuVisible} onClose={() => setSpawnMenuVisible(false)} />
<Options open={optionsMenuVisible} onClose={() => setOptionsMenuVisible(false)} options={mapOptions} />
<MiniMapPanel />
<ControlsPanel />
<UnitControlMenu
open={unitControlMenuVisible}
onClose={() => setUnitControlMenuVisible(false)}
/>
<DrawingMenu
open={drawingMenuVisible}
onClose={() => setDrawingMenuVisible(false)}
/>
<div id="map-container" className="h-full w-screen" />
<UnitControlMenu open={unitControlMenuVisible} onClose={() => setUnitControlMenuVisible(false)} />
<DrawingMenu open={drawingMenuVisible} onClose={() => setDrawingMenuVisible(false)} />
<UnitMouseControlBar />
<SideBar />
</div>
</div>
</EventsProvider>

View File

@@ -6,11 +6,7 @@ export interface ContextActionOptions {
executeImmediately?: boolean;
}
export type ContextActionCallback = (
units: Unit[],
targetUnit: Unit | null,
targetPosition: LatLng | null
) => void;
export type ContextActionCallback = (units: Unit[], targetUnit: Unit | null, targetPosition: LatLng | null) => void;
export class ContextAction {
#id: string = "";
@@ -21,14 +17,7 @@ export class ContextAction {
#icon: IconDefinition;
#options: ContextActionOptions;
constructor(
id: string,
label: string,
description: string,
icon: IconDefinition,
callback: ContextActionCallback,
options: ContextActionOptions
) {
constructor(id: string, label: string, description: string, icon: IconDefinition, callback: ContextActionCallback, options: ContextActionOptions) {
this.#id = id;
this.#label = label;
this.#description = description;

View File

@@ -1,8 +1,4 @@
import {
ContextAction,
ContextActionCallback,
ContextActionOptions,
} from "./contextaction";
import { ContextAction, ContextActionCallback, ContextActionOptions } from "./contextaction";
import { Unit } from "./unit";
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
@@ -21,14 +17,7 @@ export class ContextActionSet {
options = options || {};
if (!(id in this.#contextActions)) {
this.#contextActions[id] = new ContextAction(
id,
label,
description,
icon,
callback,
options
);
this.#contextActions[id] = new ContextAction(id, label, description, icon, callback, options);
}
this.#contextActions[id].addUnit(unit);
}

View File

@@ -4,10 +4,7 @@ import { UnitDatabase } from "./unitdatabase";
export class AircraftDatabase extends UnitDatabase {
constructor() {
super(
window.location.href.split("?")[0].replace("vite/", "") +
"api/databases/units/aircraftdatabase"
);
super(window.location.href.split("?")[0].replace("vite/", "") + "api/databases/units/aircraftdatabase");
}
getCategory() {
@@ -15,11 +12,7 @@ export class AircraftDatabase extends UnitDatabase {
}
getSpawnPointsByName(name: string) {
if (
getApp().getMissionManager().getCommandModeOptions().commandMode ==
GAME_MASTER ||
!getApp().getMissionManager().getCommandModeOptions().restrictSpawns
)
if (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER || !getApp().getMissionManager().getCommandModeOptions().restrictSpawns)
return 0;
const blueprint = this.getByName(name);

View File

@@ -4,18 +4,11 @@ import { UnitDatabase } from "./unitdatabase";
export class GroundUnitDatabase extends UnitDatabase {
constructor() {
super(
window.location.href.split("?")[0].replace("vite/", "") +
"api/databases/units/groundunitdatabase"
);
super(window.location.href.split("?")[0].replace("vite/", "") + "api/databases/units/groundunitdatabase");
}
getSpawnPointsByName(name: string) {
if (
getApp().getMissionManager().getCommandModeOptions().commandMode ==
GAME_MASTER ||
!getApp().getMissionManager().getCommandModeOptions().restrictSpawns
)
if (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER || !getApp().getMissionManager().getCommandModeOptions().restrictSpawns)
return 0;
const blueprint = this.getByName(name);

View File

@@ -4,18 +4,11 @@ import { UnitDatabase } from "./unitdatabase";
export class HelicopterDatabase extends UnitDatabase {
constructor() {
super(
window.location.href.split("?")[0].replace("vite/", "") +
"api/databases/units/helicopterdatabase"
);
super(window.location.href.split("?")[0].replace("vite/", "") + "api/databases/units/helicopterdatabase");
}
getSpawnPointsByName(name: string) {
if (
getApp().getMissionManager().getCommandModeOptions().commandMode ==
GAME_MASTER ||
!getApp().getMissionManager().getCommandModeOptions().restrictSpawns
)
if (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER || !getApp().getMissionManager().getCommandModeOptions().restrictSpawns)
return 0;
const blueprint = this.getByName(name);

View File

@@ -4,18 +4,11 @@ import { UnitDatabase } from "./unitdatabase";
export class NavyUnitDatabase extends UnitDatabase {
constructor() {
super(
window.location.href.split("?")[0].replace("vite/", "") +
"api/databases/units/navyunitdatabase"
);
super(window.location.href.split("?")[0].replace("vite/", "") + "api/databases/units/navyunitdatabase");
}
getSpawnPointsByName(name: string) {
if (
getApp().getMissionManager().getCommandModeOptions().commandMode ==
GAME_MASTER ||
!getApp().getMissionManager().getCommandModeOptions().restrictSpawns
)
if (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER || !getApp().getMissionManager().getCommandModeOptions().restrictSpawns)
return 0;
const blueprint = this.getByName(name);

View File

@@ -1,7 +1,7 @@
import { LatLng } from "leaflet";
import { getApp } from "../../olympusapp";
import { GAME_MASTER } from "../../constants/constants";
import { UnitBlueprint } from "../../interfaces";
import { LoadoutBlueprint, UnitBlueprint } from "../../interfaces";
export abstract class UnitDatabase {
blueprints: { [key: string]: UnitBlueprint } = {};
@@ -49,15 +49,13 @@ export abstract class UnitDatabase {
getBlueprints(includeDisabled: boolean = false) {
if (
getApp().getMissionManager().getCommandModeOptions().commandMode ==
GAME_MASTER ||
getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER ||
!getApp().getMissionManager().getCommandModeOptions().restrictSpawns
) {
var filteredBlueprints: { [key: string]: UnitBlueprint } = {};
for (let unit in this.blueprints) {
const blueprint = this.blueprints[unit];
if (blueprint.enabled || includeDisabled)
filteredBlueprints[unit] = blueprint;
if (blueprint.enabled || includeDisabled) filteredBlueprints[unit] = blueprint;
}
return filteredBlueprints;
} else {
@@ -66,16 +64,10 @@ export abstract class UnitDatabase {
const blueprint = this.blueprints[unit];
if (
(blueprint.enabled || includeDisabled) &&
this.getSpawnPointsByName(blueprint.name) <=
getApp().getMissionManager().getAvailableSpawnPoints() &&
getApp()
.getMissionManager()
.getCommandModeOptions()
.eras.includes(blueprint.era) &&
(!getApp().getMissionManager().getCommandModeOptions()
.restrictToCoalition ||
blueprint.coalition ===
getApp().getMissionManager().getCommandedCoalition() ||
this.getSpawnPointsByName(blueprint.name) <= getApp().getMissionManager().getAvailableSpawnPoints() &&
getApp().getMissionManager().getCommandModeOptions().eras.includes(blueprint.era) &&
(!getApp().getMissionManager().getCommandModeOptions().restrictToCoalition ||
blueprint.coalition === getApp().getMissionManager().getCommandedCoalition() ||
blueprint.coalition === undefined)
) {
filteredBlueprints[unit] = blueprint;
@@ -107,11 +99,7 @@ export abstract class UnitDatabase {
var filteredBlueprints = this.getBlueprints();
var types: string[] = [];
for (let unit in filteredBlueprints) {
if (
typeof unitFilter === "function" &&
!unitFilter(filteredBlueprints[unit])
)
continue;
if (typeof unitFilter === "function" && !unitFilter(filteredBlueprints[unit])) continue;
var type = filteredBlueprints[unit].type;
if (type && type !== "" && !types.includes(type)) types.push(type);
}
@@ -132,7 +120,7 @@ export abstract class UnitDatabase {
/* Get all blueprints by range */
getByRange(range: string) {
var filteredBlueprints = this.getBlueprints();
var unitswithrange = [];
var unitswithrange: UnitBlueprint[] = [];
var minRange = 0;
var maxRange = 0;
@@ -161,7 +149,7 @@ export abstract class UnitDatabase {
/* Get all blueprints by type */
getByType(type: string) {
var filteredBlueprints = this.getBlueprints();
var units = [];
var units: UnitBlueprint[] = [];
for (let unit in filteredBlueprints) {
if (filteredBlueprints[unit].type === type) {
units.push(filteredBlueprints[unit]);
@@ -173,15 +161,12 @@ export abstract class UnitDatabase {
/* Get all blueprints by role */
getByRole(role: string) {
var filteredBlueprints = this.getBlueprints();
var units = [];
var units: UnitBlueprint[] = [];
for (let unit in filteredBlueprints) {
var loadouts = filteredBlueprints[unit].loadouts;
if (loadouts) {
for (let loadout of loadouts) {
if (
loadout.roles.includes(role) ||
loadout.roles.includes(role.toLowerCase())
) {
if (loadout.roles.includes(role) || loadout.roles.includes(role.toLowerCase())) {
units.push(filteredBlueprints[unit]);
break;
}
@@ -194,7 +179,7 @@ export abstract class UnitDatabase {
/* Get the names of all the loadouts for a specific unit and for a specific role */
getLoadoutNamesByRole(name: string, role: string) {
var filteredBlueprints = this.getBlueprints();
var loadoutsByRole = [];
var loadoutsByRole: string[] = [];
var loadouts = filteredBlueprints[name].loadouts;
if (loadouts) {
for (let loadout of loadouts) {

View File

@@ -8,8 +8,7 @@ export class Group {
this.#name = name;
document.addEventListener("unitDeath", (e: any) => {
if (this.#members.includes(e.detail))
this.getLeader()?.onGroupChanged(e.detail);
if (this.#members.includes(e.detail)) this.getLeader()?.onGroupChanged(e.detail);
});
}

View File

@@ -15,11 +15,9 @@ export class UnitDataFileExport extends UnitDataFile {
//this.dialog = new Dialog(elementId);
//this.#element = this.dialog.getElement();
this.#element
.querySelector(".start-transfer")
?.addEventListener("click", (ev: MouseEventInit) => {
this.#doExport();
});
this.#element.querySelector(".start-transfer")?.addEventListener("click", (ev: MouseEventInit) => {
this.#doExport();
});
}
/**
@@ -65,9 +63,7 @@ export class UnitDataFileExport extends UnitDataFile {
let selectedUnits: Unit[] = [];
this.#element
.querySelectorAll(
`input[type="checkbox"][name="category-coalition-selection"]:checked`
)
.querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`)
.forEach(<HTMLInputElement>(checkbox: HTMLInputElement) => {
if (checkbox instanceof HTMLInputElement) {
const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition"
@@ -83,8 +79,7 @@ export class UnitDataFileExport extends UnitDataFile {
var unitsToExport: { [key: string]: any } = {};
selectedUnits.forEach((unit: Unit) => {
var data: any = unit.getData();
if (unit.getGroupName() in unitsToExport)
unitsToExport[unit.getGroupName()].push(data);
if (unit.getGroupName() in unitsToExport) unitsToExport[unit.getGroupName()].push(data);
else unitsToExport[unit.getGroupName()] = [data];
});

File diff suppressed because it is too large Load Diff

View File

@@ -19,15 +19,7 @@ import {
} from "../other/utils";
import { CoalitionPolygon } from "../map/coalitionarea/coalitionpolygon";
import { groundUnitDatabase } from "./databases/groundunitdatabase";
import {
CONTEXT_ACTION,
DELETE_CYCLE_TIME,
DELETE_SLOW_THRESHOLD,
DataIndexes,
GAME_MASTER,
IADSDensities,
IDLE,
} from "../constants/constants";
import { CONTEXT_ACTION, DELETE_CYCLE_TIME, DELETE_SLOW_THRESHOLD, DataIndexes, GAME_MASTER, IADSDensities, IDLE } from "../constants/constants";
import { DataExtractor } from "../server/dataextractor";
import { citiesDatabase } from "./databases/citiesdatabase";
import { aircraftDatabase } from "./databases/aircraftdatabase";
@@ -36,12 +28,7 @@ import { navyUnitDatabase } from "./databases/navyunitdatabase";
import { TemporaryUnitMarker } from "../map/markers/temporaryunitmarker";
//import { Popup } from "../popups/popup";
//import { HotgroupPanel } from "../panels/hotgrouppanel";
import {
Contact,
UnitBlueprint,
UnitData,
UnitSpawnTable,
} from "../interfaces";
import { Contact, UnitBlueprint, UnitData, UnitSpawnTable } from "../interfaces";
//import { Dialog } from "../dialog/dialog";
import { Group } from "./group";
import { UnitDataFileExport } from "./importexport/unitdatafileexport";
@@ -68,18 +55,14 @@ export class UnitsManager {
this.#units = {};
document.addEventListener("commandModeOptionsChanged", () => {
Object.values(this.#units).forEach((unit: Unit) =>
unit.updateVisibility()
);
Object.values(this.#units).forEach((unit: Unit) => unit.updateVisibility());
});
document.addEventListener("contactsUpdated", (e: CustomEvent) => {
document.addEventListener("contactsUpdated", (e) => {
this.#requestDetectionUpdate = true;
});
document.addEventListener("copy", () => this.copy());
document.addEventListener("deleteSelectedUnits", () => this.delete());
document.addEventListener("explodeSelectedUnits", (e: any) =>
this.delete(true, e.detail.type)
);
document.addEventListener("explodeSelectedUnits", (e: any) => this.delete(true, e.detail.type));
document.addEventListener("exportToFile", () => this.exportToFile());
document.addEventListener("importFromFile", () => this.importFromFile());
document.addEventListener("keyup", (event) => this.#onKeyUp(event));
@@ -90,18 +73,11 @@ export class UnitsManager {
document.addEventListener("selectedUnitsChangeSpeed", (e: any) => {
this.changeSpeed(e.detail.type);
});
document.addEventListener("unitDeselection", (e: CustomEvent) =>
this.#onUnitDeselection(e.detail)
);
document.addEventListener("unitSelection", (e: CustomEvent) =>
this.#onUnitSelection(e.detail)
);
document.addEventListener(
"toggleMarkerProtection",
(ev: CustomEventInit) => {
this.#showNumberOfSelectedProtectedUnits();
}
);
document.addEventListener("unitDeselection", (e) => this.#onUnitDeselection((e as CustomEvent).detail));
document.addEventListener("unitSelection", (e) => this.#onUnitSelection((e as CustomEvent).detail));
document.addEventListener("toggleMarkerProtection", (e) => {
this.#showNumberOfSelectedProtectedUnits();
});
//this.#slowDeleteDialog = new Dialog("slow-delete-dialog");
}
@@ -171,11 +147,7 @@ export class UnitsManager {
else if (map.getIsUnitProtected(unit)) data.dcsProtected.push(unit);
else data.dcsUnprotected.push(unit);
});
data.controllable = [].concat(
data.dcsUnprotected,
data.human,
data.olympus
);
data.controllable = [].concat(data.dcsUnprotected, data.human, data.olympus);
return data;
}
@@ -185,15 +157,10 @@ export class UnitsManager {
*/
showProtectedUnitsPopup(numOfProtectedUnits: number) {
if (numOfProtectedUnits < 1) return;
const messageText =
numOfProtectedUnits === 1
? `Unit is protected`
: `All selected units are protected`;
const messageText = numOfProtectedUnits === 1 ? `Unit is protected` : `All selected units are protected`;
//(getApp().getPopupsManager().get("infoPopup") as Popup).setText(messageText);
// Cheap way for now until we use more locks
let lock = <HTMLElement>(
document.querySelector("#unit-visibility-control button.lock")
);
let lock = <HTMLElement>document.querySelector("#unit-visibility-control button.lock");
lock.classList.add("prompt");
setTimeout(() => lock.classList.remove("prompt"), 4000);
}
@@ -238,8 +205,7 @@ export class UnitsManager {
if (groupName !== "") {
/* If the group does not yet exist, create it */
if (!(groupName in this.#groups))
this.#groups[groupName] = new Group(groupName);
if (!(groupName in this.#groups)) this.#groups[groupName] = new Group(groupName);
/* If the unit was not assigned to a group yet, assign it */
if (unit.getGroup() === null) this.#groups[groupName].addMember(unit);
@@ -249,16 +215,11 @@ export class UnitsManager {
/* If we are not in Game Master mode, visibility of units by the user is determined by the detections of the units themselves. This is performed here.
This operation is computationally expensive, therefore it is only performed when #requestDetectionUpdate is true. This happens whenever a change in the detectionUpdates is detected
*/
if (
this.#requestDetectionUpdate &&
getApp().getMissionManager().getCommandModeOptions().commandMode !=
GAME_MASTER
) {
if (this.#requestDetectionUpdate && getApp().getMissionManager().getCommandModeOptions().commandMode != GAME_MASTER) {
/* Create a dictionary of empty detection methods arrays */
var detectionMethods: { [key: string]: number[] } = {};
for (let ID in this.#units) detectionMethods[ID] = [];
for (let ID in getApp().getWeaponsManager().getWeapons())
detectionMethods[ID] = [];
for (let ID in getApp().getWeaponsManager().getWeapons()) detectionMethods[ID] = [];
/* Fill the array with the detection methods */
for (let ID in this.#units) {
@@ -267,10 +228,7 @@ export class UnitsManager {
const contacts = unit.getContacts();
contacts.forEach((contact: Contact) => {
const contactID = contact.ID;
if (
contactID in detectionMethods &&
!detectionMethods[contactID].includes(contact.detectionMethod)
)
if (contactID in detectionMethods && !detectionMethods[contactID].includes(contact.detectionMethod))
detectionMethods[contactID]?.push(contact.detectionMethod);
});
}
@@ -320,10 +278,7 @@ export class UnitsManager {
this.deselectAllUnits();
for (let ID in this.#units) {
if (this.#units[ID].getHidden() == false) {
var latlng = new LatLng(
this.#units[ID].getPosition().lat,
this.#units[ID].getPosition().lng
);
var latlng = new LatLng(this.#units[ID].getPosition().lat, this.#units[ID].getPosition().lng);
if (bounds.contains(latlng)) {
this.#units[ID].setSelected(true);
}
@@ -340,9 +295,7 @@ export class UnitsManager {
this.deselectAllUnits();
}
this.getUnitsByHotgroup(hotgroup).forEach((unit: Unit) =>
unit.setSelected(true)
);
this.getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setSelected(true));
}
/** Get all the currently selected units
@@ -350,12 +303,7 @@ export class UnitsManager {
* @param options Selection options
* @returns Array of selected units
*/
getSelectedUnits(options?: {
excludeHumans?: boolean;
excludeProtected?: boolean;
onlyOnePerGroup?: boolean;
showProtectionReminder?: boolean;
}) {
getSelectedUnits(options?: { excludeHumans?: boolean; excludeProtected?: boolean; onlyOnePerGroup?: boolean; showProtectionReminder?: boolean }) {
let selectedUnits: Unit[] = [];
let numProtectedUnits = 0;
for (const [ID, unit] of Object.entries(this.#units)) {
@@ -363,10 +311,7 @@ export class UnitsManager {
if (options) {
if (options.excludeHumans && unit.getHuman()) continue;
if (
options.excludeProtected === true &&
this.#unitIsProtected(unit)
) {
if (options.excludeProtected === true && this.#unitIsProtected(unit)) {
numProtectedUnits++;
continue;
}
@@ -375,23 +320,13 @@ export class UnitsManager {
}
}
if (options) {
if (
options.showProtectionReminder === true &&
numProtectedUnits > selectedUnits.length &&
selectedUnits.length === 0
)
if (options.showProtectionReminder === true && numProtectedUnits > selectedUnits.length && selectedUnits.length === 0)
this.showProtectedUnitsPopup(numProtectedUnits);
if (options.onlyOnePerGroup) {
var temp: Unit[] = [];
for (let unit of selectedUnits) {
if (
!temp.some(
(otherUnit: Unit) =>
unit.getGroupName() == otherUnit.getGroupName()
)
)
temp.push(unit);
if (!temp.some((otherUnit: Unit) => unit.getGroupName() == otherUnit.getGroupName())) temp.push(unit);
}
selectedUnits = temp;
}
@@ -466,11 +401,7 @@ export class UnitsManager {
this.#units[idx].getCoalition() != unit.getCoalition()
) {
this.#units[idx].getContacts().forEach((contact: Contact) => {
if (
contact.ID == unit.ID &&
!detectionMethods.includes(contact.detectionMethod)
)
detectionMethods.push(contact.detectionMethod);
if (contact.ID == unit.ID && !detectionMethods.includes(contact.detectionMethod)) detectionMethods.push(contact.detectionMethod);
});
}
}
@@ -485,12 +416,7 @@ export class UnitsManager {
* @param rotation Rotation in radians by which the formation will be rigidly rotated. E.g. a ( V ) formation will look like this ( < ) if rotated pi/4 radians (90 degrees)
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
addDestination(
latlng: L.LatLng,
mantainRelativePosition: boolean,
rotation: number,
units: Unit[] | null = null
) {
addDestination(latlng: L.LatLng, mantainRelativePosition: boolean, rotation: number, units: Unit[] | null = null) {
if (units === null)
units = this.getSelectedUnits({
excludeHumans: true,
@@ -509,8 +435,7 @@ export class UnitsManager {
/* Compute the destination for each unit. If mantainRelativePosition is true, compute the destination so to hold the relative positions */
var unitDestinations: { [key: number]: LatLng } = {};
if (mantainRelativePosition)
unitDestinations = this.computeGroupDestination(latlng, rotation);
if (mantainRelativePosition) unitDestinations = this.computeGroupDestination(latlng, rotation);
else
units.forEach((unit: Unit) => {
unitDestinations[unit.ID] = latlng;
@@ -523,8 +448,7 @@ export class UnitsManager {
if (leader && leader.getSelected()) leader.addDestination(latlng);
else unit.addDestination(latlng);
} else {
if (unit.ID in unitDestinations)
unit.addDestination(unitDestinations[unit.ID]);
if (unit.ID in unitDestinations) unit.addDestination(unitDestinations[unit.ID]);
}
});
this.#showActionMessage(units, " new destination added");
@@ -790,10 +714,7 @@ export class UnitsManager {
units = segregatedUnits.controllable;
units.forEach((unit: Unit) => unit.setReactionToThreat(reactionToThreat));
this.#showActionMessage(
units,
`reaction to threat set to ${reactionToThreat}`
);
this.#showActionMessage(units, `reaction to threat set to ${reactionToThreat}`);
}
/** Set a specific emissions & countermeasures to all the selected units
@@ -801,10 +722,7 @@ export class UnitsManager {
* @param emissionCountermeasure Value to set, see constants for acceptable values
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
setEmissionsCountermeasures(
emissionCountermeasure: string,
units: Unit[] | null = null
) {
setEmissionsCountermeasures(emissionCountermeasure: string, units: Unit[] | null = null) {
if (units === null)
units = this.getSelectedUnits({
excludeHumans: true,
@@ -821,13 +739,8 @@ export class UnitsManager {
units = segregatedUnits.controllable;
units.forEach((unit: Unit) =>
unit.setEmissionsCountermeasures(emissionCountermeasure)
);
this.#showActionMessage(
units,
`emissions & countermeasures set to ${emissionCountermeasure}`
);
units.forEach((unit: Unit) => unit.setEmissionsCountermeasures(emissionCountermeasure));
this.#showActionMessage(units, `emissions & countermeasures set to ${emissionCountermeasure}`);
}
/** Turn selected units on or off, only works on ground and navy units
@@ -932,10 +845,7 @@ export class UnitsManager {
units = segregatedUnits.controllable;
units.forEach((unit: Unit) => unit.attackUnit(ID));
this.#showActionMessage(
units,
`attacking unit ${this.getUnitByID(ID)?.getUnitName()}`
);
this.#showActionMessage(units, `attacking unit ${this.getUnitByID(ID)?.getUnitName()}`);
}
/** Instruct units to refuel at the nearest tanker, if possible. Else units will RTB
@@ -957,10 +867,7 @@ export class UnitsManager {
}
segregatedUnits.controllable.forEach((unit: Unit) => unit.refuel());
this.#showActionMessage(
segregatedUnits.controllable,
`sent to nearest tanker`
);
this.#showActionMessage(segregatedUnits.controllable, `sent to nearest tanker`);
}
/** Instruct the selected units to follow another unit in a formation. Only works for aircrafts and helicopters.
@@ -970,12 +877,7 @@ export class UnitsManager {
* @param formation Optional parameter, defines a predefined formation type. Values are: "trail", "echelon-lh", "echelon-rh", "line-abreast-lh", "line-abreast-rh", "front", "diamond"
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
followUnit(
ID: number,
offset?: { x: number; y: number; z: number },
formation?: string,
units: Unit[] | null = null
) {
followUnit(ID: number, offset?: { x: number; y: number; z: number }, formation?: string, units: Unit[] | null = null) {
if (units === null)
units = this.getSelectedUnits({
excludeHumans: true,
@@ -1062,10 +964,7 @@ export class UnitsManager {
count++;
}
});
this.#showActionMessage(
units,
`following unit ${this.getUnitByID(ID)?.getUnitName()}`
);
this.#showActionMessage(units, `following unit ${this.getUnitByID(ID)?.getUnitName()}`);
}
/** Instruct the selected units to perform precision bombing of specific coordinates
@@ -1173,13 +1072,9 @@ export class UnitsManager {
try {
groundElevation = parseFloat(response);
} catch {
console.warn(
"Simulate fire fight: could not retrieve ground elevation"
);
console.warn("Simulate fire fight: could not retrieve ground elevation");
}
units?.forEach((unit: Unit) =>
unit.simulateFireFight(latlng, groundElevation)
);
units?.forEach((unit: Unit) => unit.simulateFireFight(latlng, groundElevation));
});
this.#showActionMessage(units, `unit simulating fire fight`);
}
@@ -1346,16 +1241,8 @@ export class UnitsManager {
if (this.getUnitsCategories(units).length == 1) {
var unitsData: { ID: number; location: LatLng }[] = [];
units.forEach((unit: Unit) =>
unitsData.push({ ID: unit.ID, location: unit.getPosition() })
);
getApp()
.getServerManager()
.cloneUnits(
unitsData,
true,
0 /* No spawn points, we delete the original units */
);
units.forEach((unit: Unit) => unitsData.push({ ID: unit.ID, location: unit.getPosition() }));
getApp().getServerManager().cloneUnits(unitsData, true, 0 /* No spawn points, we delete the original units */);
this.#showActionMessage(units, `created a group`);
} else {
//(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Groups can only be created from units of the same category`);
@@ -1368,9 +1255,7 @@ export class UnitsManager {
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
*/
setHotgroup(hotgroup: number, units: Unit[] | null = null) {
this.getUnitsByHotgroup(hotgroup).forEach((unit: Unit) =>
unit.setHotgroup(null)
);
this.getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setHotgroup(null));
this.addToHotgroup(hotgroup);
}
@@ -1392,11 +1277,7 @@ export class UnitsManager {
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
* @returns
*/
delete(
explosion: boolean = false,
explosionType: string = "",
units: Unit[] | null = null
) {
delete(explosion: boolean = false, explosionType: string = "", units: Unit[] | null = null) {
if (units === null)
units = this.getSelectedUnits({
excludeProtected: true,
@@ -1417,21 +1298,13 @@ export class UnitsManager {
if (
selectionContainsAHuman &&
!confirm(
"Your selection includes a human player. Deleting humans causes their vehicle to crash.\n\nAre you sure you want to do this?"
)
!confirm("Your selection includes a human player. Deleting humans causes their vehicle to crash.\n\nAre you sure you want to do this?")
) {
return;
}
const doDelete = (
explosion = false,
explosionType = "",
immediate = false
) => {
units?.forEach((unit: Unit) =>
unit.delete(explosion, explosionType, immediate)
);
const doDelete = (explosion = false, explosionType = "", immediate = false) => {
units?.forEach((unit: Unit) => unit.delete(explosion, explosionType, immediate));
this.#showActionMessage(units as Unit[], `deleted`);
};
@@ -1453,11 +1326,7 @@ export class UnitsManager {
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
* @returns Array of positions for each unit, in order
*/
computeGroupDestination(
latlng: LatLng,
rotation: number,
units: Unit[] | null = null
) {
computeGroupDestination(latlng: LatLng, rotation: number, units: Unit[] | null = null) {
if (units === null)
units = this.getSelectedUnits({
excludeHumans: true,
@@ -1479,10 +1348,7 @@ export class UnitsManager {
var len = units.length;
var center = { x: 0, y: 0 };
units.forEach((unit: Unit) => {
var mercator = latLngToMercator(
unit.getPosition().lat,
unit.getPosition().lng
);
var mercator = latLngToMercator(unit.getPosition().lat, unit.getPosition().lng);
center.x += mercator.x / len;
center.y += mercator.y / len;
});
@@ -1490,10 +1356,7 @@ export class UnitsManager {
/* Compute the distances from the center of the group */
var unitDestinations: { [key: number]: LatLng } = {};
units.forEach((unit: Unit) => {
var mercator = latLngToMercator(
unit.getPosition().lat,
unit.getPosition().lng
);
var mercator = latLngToMercator(unit.getPosition().lat, unit.getPosition().lng);
var distancesFromCenter = {
dx: mercator.x - center.x,
dy: mercator.y - center.y,
@@ -1504,12 +1367,8 @@ export class UnitsManager {
dx: 0,
dy: 0,
};
rotatedDistancesFromCenter.dx =
distancesFromCenter.dx * Math.cos(deg2rad(rotation)) -
distancesFromCenter.dy * Math.sin(deg2rad(rotation));
rotatedDistancesFromCenter.dy =
distancesFromCenter.dx * Math.sin(deg2rad(rotation)) +
distancesFromCenter.dy * Math.cos(deg2rad(rotation));
rotatedDistancesFromCenter.dx = distancesFromCenter.dx * Math.cos(deg2rad(rotation)) - distancesFromCenter.dy * Math.sin(deg2rad(rotation));
rotatedDistancesFromCenter.dy = distancesFromCenter.dx * Math.sin(deg2rad(rotation)) + distancesFromCenter.dy * Math.cos(deg2rad(rotation));
/* Compute the final position of the unit */
var destMercator = latLngToMercator(latlng.lat, latlng.lng); // Convert destination point to mercator
@@ -1552,28 +1411,18 @@ export class UnitsManager {
let spawnPoints = 0;
/* If spawns are restricted, check that the user has the necessary spawn points */
if (
getApp().getMissionManager().getCommandModeOptions().commandMode !=
GAME_MASTER
) {
if (
getApp().getMissionManager().getCommandModeOptions().restrictSpawns &&
getApp().getMissionManager().getRemainingSetupTime() < 0
) {
if (getApp().getMissionManager().getCommandModeOptions().commandMode != GAME_MASTER) {
if (getApp().getMissionManager().getCommandModeOptions().restrictSpawns && getApp().getMissionManager().getRemainingSetupTime() < 0) {
//(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Units can be pasted only during SETUP phase`);
return false;
}
this.#copiedUnits.forEach((unit: UnitData) => {
let unitSpawnPoints = getUnitDatabaseByCategory(
unit.category
)?.getSpawnPointsByName(unit.name);
let unitSpawnPoints = getUnitDatabaseByCategory(unit.category)?.getSpawnPointsByName(unit.name);
if (unitSpawnPoints !== undefined) spawnPoints += unitSpawnPoints;
});
if (
spawnPoints > getApp().getMissionManager().getAvailableSpawnPoints()
) {
if (spawnPoints > getApp().getMissionManager().getAvailableSpawnPoints()) {
//(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Not enough spawn points available!");
return false;
}
@@ -1603,18 +1452,10 @@ export class UnitsManager {
let markers: TemporaryUnitMarker[] = [];
groups[groupName].forEach((unit: UnitData) => {
var position = new LatLng(
getApp().getMap().getMouseCoordinates().lat +
unit.position.lat -
avgLat,
getApp().getMap().getMouseCoordinates().lng +
unit.position.lng -
avgLng
);
markers.push(
getApp()
.getMap()
.addTemporaryMarker(position, unit.name, unit.coalition)
getApp().getMap().getMouseCoordinates().lat + unit.position.lat - avgLat,
getApp().getMap().getMouseCoordinates().lng + unit.position.lng - avgLng
);
markers.push(getApp().getMap().addTemporaryMarker(position, unit.name, unit.coalition));
units.push({ ID: unit.ID, location: position });
});
@@ -1667,29 +1508,18 @@ export class UnitsManager {
Object.keys(airbases).forEach((airbaseName: string) => {
var airbase = airbases[airbaseName];
/* Check if the city is inside the coalition area */
if (
areaContains(
new LatLng(airbase.getLatLng().lat, airbase.getLatLng().lng),
coalitionArea
)
) {
if (areaContains(new LatLng(airbase.getLatLng().lat, airbase.getLatLng().lng), coalitionArea)) {
/* Arbitrary formula to obtain a number of units */
var pointsNumber = 2 + (10 * density) / 100;
for (let i = 0; i < pointsNumber; i++) {
/* Place the unit nearby the airbase, depending on the distribution parameter */
var bearing = Math.random() * 360;
var distance = Math.random() * distribution * 100;
const latlng = bearingAndDistanceToLatLng(
airbase.getLatLng().lat,
airbase.getLatLng().lng,
bearing,
distance
);
const latlng = bearingAndDistanceToLatLng(airbase.getLatLng().lat, airbase.getLatLng().lng, bearing, distance);
/* Make sure the unit is still inside the coalition area */
if (areaContains(latlng, coalitionArea)) {
const type =
activeTypes[Math.floor(Math.random() * activeTypes.length)];
const type = activeTypes[Math.floor(Math.random() * activeTypes.length)];
if (Math.random() < IADSDensities[type]) {
/* Get a random blueprint depending on the selected parameters and spawn the unit */
let unitBlueprint: UnitBlueprint | null;
@@ -1729,74 +1559,65 @@ export class UnitsManager {
}
});
citiesDatabase.forEach(
(city: { lat: number; lng: number; pop: number }) => {
/* Check if the city is inside the coalition area */
if (areaContains(new LatLng(city.lat, city.lng), coalitionArea)) {
/* Arbitrary formula to obtain a number of units depending on the city population */
var pointsNumber = 2 + (Math.pow(city.pop, 0.15) * density) / 100;
for (let i = 0; i < pointsNumber; i++) {
/* Place the unit nearby the city, depending on the distribution parameter */
var bearing = Math.random() * 360;
var distance = Math.random() * distribution * 100;
const latlng = bearingAndDistanceToLatLng(
city.lat,
city.lng,
bearing,
distance
);
citiesDatabase.forEach((city: { lat: number; lng: number; pop: number }) => {
/* Check if the city is inside the coalition area */
if (areaContains(new LatLng(city.lat, city.lng), coalitionArea)) {
/* Arbitrary formula to obtain a number of units depending on the city population */
var pointsNumber = 2 + (Math.pow(city.pop, 0.15) * density) / 100;
for (let i = 0; i < pointsNumber; i++) {
/* Place the unit nearby the city, depending on the distribution parameter */
var bearing = Math.random() * 360;
var distance = Math.random() * distribution * 100;
const latlng = bearingAndDistanceToLatLng(city.lat, city.lng, bearing, distance);
/* Make sure the unit is still inside the coalition area */
if (areaContains(latlng, coalitionArea)) {
const type =
activeTypes[Math.floor(Math.random() * activeTypes.length)];
if (Math.random() < IADSDensities[type]) {
/* Get a random blueprint depending on the selected parameters and spawn the unit */
let unitBlueprint: UnitBlueprint | null;
if (forceCoalition)
unitBlueprint = randomUnitBlueprint(groundUnitDatabase, {
type: type,
eras: activeEras,
ranges: activeRanges,
coalition: coalitionArea.getCoalition(),
});
else
unitBlueprint = randomUnitBlueprint(groundUnitDatabase, {
type: type,
eras: activeEras,
ranges: activeRanges,
});
/* Make sure the unit is still inside the coalition area */
if (areaContains(latlng, coalitionArea)) {
const type = activeTypes[Math.floor(Math.random() * activeTypes.length)];
if (Math.random() < IADSDensities[type]) {
/* Get a random blueprint depending on the selected parameters and spawn the unit */
let unitBlueprint: UnitBlueprint | null;
if (forceCoalition)
unitBlueprint = randomUnitBlueprint(groundUnitDatabase, {
type: type,
eras: activeEras,
ranges: activeRanges,
coalition: coalitionArea.getCoalition(),
});
else
unitBlueprint = randomUnitBlueprint(groundUnitDatabase, {
type: type,
eras: activeEras,
ranges: activeRanges,
});
if (unitBlueprint)
this.spawnUnits(
"GroundUnit",
[
{
unitType: unitBlueprint.name,
location: latlng,
liveryID: "",
skill: "High",
},
],
coalitionArea.getCoalition(),
false,
"",
""
);
}
if (unitBlueprint)
this.spawnUnits(
"GroundUnit",
[
{
unitType: unitBlueprint.name,
location: latlng,
liveryID: "",
skill: "High",
},
],
coalitionArea.getCoalition(),
false,
"",
""
);
}
}
}
}
);
});
}
/** Export all the ground and navy units to file. Does not work on Aircraft and Helicopter units.
* TODO: Extend to aircraft and helicopters
*/
exportToFile() {
if (!this.#unitDataExport)
this.#unitDataExport = new UnitDataFileExport("unit-export-dialog");
if (!this.#unitDataExport) this.#unitDataExport = new UnitDataFileExport("unit-export-dialog");
this.#unitDataExport.showForm(Object.values(this.#units));
}
@@ -1804,8 +1625,7 @@ export class UnitsManager {
* TODO: extend to support aircraft and helicopters
*/
importFromFile() {
if (!this.#unitDataImport)
this.#unitDataImport = new UnitDataFileImport("unit-import-dialog");
if (!this.#unitDataImport) this.#unitDataImport = new UnitDataFileImport("unit-import-dialog");
this.#unitDataImport.selectFile();
}
@@ -1834,8 +1654,7 @@ export class UnitsManager {
var spawnsRestricted =
getApp().getMissionManager().getCommandModeOptions().restrictSpawns &&
getApp().getMissionManager().getRemainingSetupTime() < 0 &&
getApp().getMissionManager().getCommandModeOptions().commandMode !==
GAME_MASTER;
getApp().getMissionManager().getCommandModeOptions().commandMode !== GAME_MASTER;
if (category === "Aircraft") {
if (airbase == "" && spawnsRestricted) {
@@ -1845,18 +1664,7 @@ export class UnitsManager {
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => {
return points + aircraftDatabase.getSpawnPointsByName(unit.unitType);
}, 0);
spawnFunction = () =>
getApp()
.getServerManager()
.spawnAircrafts(
units,
coalition,
airbase,
country,
immediate,
spawnPoints,
callback
);
spawnFunction = () => getApp().getServerManager().spawnAircrafts(units, coalition, airbase, country, immediate, spawnPoints, callback);
} else if (category === "Helicopter") {
if (airbase == "" && spawnsRestricted) {
//(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Helicopters can be air spawned during the SETUP phase only");
@@ -1865,18 +1673,7 @@ export class UnitsManager {
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => {
return points + helicopterDatabase.getSpawnPointsByName(unit.unitType);
}, 0);
spawnFunction = () =>
getApp()
.getServerManager()
.spawnHelicopters(
units,
coalition,
airbase,
country,
immediate,
spawnPoints,
callback
);
spawnFunction = () => getApp().getServerManager().spawnHelicopters(units, coalition, airbase, country, immediate, spawnPoints, callback);
} else if (category === "GroundUnit") {
if (spawnsRestricted) {
//(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Ground units can be spawned during the SETUP phase only");
@@ -1885,17 +1682,7 @@ export class UnitsManager {
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => {
return points + groundUnitDatabase.getSpawnPointsByName(unit.unitType);
}, 0);
spawnFunction = () =>
getApp()
.getServerManager()
.spawnGroundUnits(
units,
coalition,
country,
immediate,
spawnPoints,
callback
);
spawnFunction = () => getApp().getServerManager().spawnGroundUnits(units, coalition, country, immediate, spawnPoints, callback);
} else if (category === "NavyUnit") {
if (spawnsRestricted) {
//(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Navy units can be spawned during the SETUP phase only");
@@ -1904,17 +1691,7 @@ export class UnitsManager {
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => {
return points + navyUnitDatabase.getSpawnPointsByName(unit.unitType);
}, 0);
spawnFunction = () =>
getApp()
.getServerManager()
.spawnNavyUnits(
units,
coalition,
country,
immediate,
spawnPoints,
callback
);
spawnFunction = () => getApp().getServerManager().spawnNavyUnits(units, coalition, country, immediate, spawnPoints, callback);
}
if (spawnPoints <= getApp().getMissionManager().getAvailableSpawnPoints()) {
@@ -2033,9 +1810,7 @@ export class UnitsManager {
const map = getApp().getMap();
const units = this.getSelectedUnits();
const numSelectedUnits = units.length;
const numProtectedUnits = units.filter((unit: Unit) =>
map.getIsUnitProtected(unit)
).length;
const numProtectedUnits = units.filter((unit: Unit) => map.getIsUnitProtected(unit)).length;
//if (numProtectedUnits === 1 && numSelectedUnits === numProtectedUnits)
//(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Notice: unit is protected`);

View File

@@ -1,23 +1,9 @@
import { LatLng, DivIcon, Map } from "leaflet";
import { getApp } from "../olympusapp";
import {
enumToCoalition,
mToFt,
msToKnots,
rad2deg,
zeroAppend,
} from "../other/utils";
import { enumToCoalition, mToFt, msToKnots, rad2deg, zeroAppend } from "../other/utils";
import { CustomMarker } from "../map/markers/custommarker";
import { SVGInjector } from "@tanem/svg-injector";
import {
DLINK,
DataIndexes,
GAME_MASTER,
IRST,
OPTIC,
RADAR,
VISUAL,
} from "../constants/constants";
import { DLINK, DataIndexes, GAME_MASTER, IRST, OPTIC, RADAR, VISUAL } from "../constants/constants";
import { DataExtractor } from "../server/dataextractor";
import { ObjectIconOptions } from "../interfaces";
@@ -153,8 +139,7 @@ export class Weapon extends CustomMarker {
belongsToCommandedCoalition() {
if (
getApp().getMissionManager().getCommandModeOptions().commandMode !==
GAME_MASTER &&
getApp().getMissionManager().getCommandModeOptions().commandMode !== GAME_MASTER &&
getApp().getMissionManager().getCommandedCoalition() !== this.#coalition
)
return false;
@@ -197,10 +182,7 @@ export class Weapon extends CustomMarker {
img.src = `/vite/images/units/${this.getMarkerCategory()}.svg`;
img.onload = () => SVGInjector(img);
unitIcon.appendChild(img);
unitIcon.toggleAttribute(
"data-rotate-to-heading",
this.getIconOptions().rotateToHeading
);
unitIcon.toggleAttribute("data-rotate-to-heading", this.getIconOptions().rotateToHeading);
el.append(unitIcon);
}
@@ -211,10 +193,7 @@ export class Weapon extends CustomMarker {
updateVisibility() {
const hiddenUnits = getApp().getMap().getHiddenTypes();
var hidden =
hiddenUnits[this.getMarkerCategory()] ||
hiddenUnits[this.#coalition] ||
(!this.belongsToCommandedCoalition() &&
this.#detectionMethods.length == 0);
hiddenUnits[this.getMarkerCategory()] || hiddenUnits[this.#coalition] || (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0);
this.setHidden(hidden || !this.#alive);
}
@@ -244,12 +223,7 @@ export class Weapon extends CustomMarker {
setDetectionMethods(newDetectionMethods: number[]) {
if (!this.belongsToCommandedCoalition()) {
/* Check if the detection methods of this unit have changed */
if (
this.#detectionMethods.length !== newDetectionMethods.length ||
this.getDetectionMethods().some(
(value) => !newDetectionMethods.includes(value)
)
) {
if (this.#detectionMethods.length !== newDetectionMethods.length || this.getDetectionMethods().some((value) => !newDetectionMethods.includes(value))) {
/* Force a redraw of the unit to reflect the new status of the detection methods */
this.setHidden(true);
this.#detectionMethods = newDetectionMethods;
@@ -273,53 +247,35 @@ export class Weapon extends CustomMarker {
/* Draw the marker */
if (!this.getHidden()) {
if (
this.getLatLng().lat !== this.#position.lat ||
this.getLatLng().lng !== this.#position.lng
) {
if (this.getLatLng().lat !== this.#position.lat || this.getLatLng().lng !== this.#position.lng) {
this.setLatLng(new LatLng(this.#position.lat, this.#position.lng));
}
var element = this.getElement();
if (element != null) {
/* Draw the velocity vector */
element
.querySelector(".unit-vvi")
?.setAttribute("style", `height: ${15 + this.#speed / 5}px;`);
element.querySelector(".unit-vvi")?.setAttribute("style", `height: ${15 + this.#speed / 5}px;`);
/* Set dead/alive flag */
element
.querySelector(".unit")
?.toggleAttribute("data-is-dead", !this.#alive);
element.querySelector(".unit")?.toggleAttribute("data-is-dead", !this.#alive);
/* Set altitude and speed */
if (element.querySelector(".unit-altitude"))
(<HTMLElement>element.querySelector(".unit-altitude")).innerText =
"FL" +
zeroAppend(
Math.floor(mToFt(this.#position.alt as number) / 100),
3
);
(<HTMLElement>element.querySelector(".unit-altitude")).innerText = "FL" + zeroAppend(Math.floor(mToFt(this.#position.alt as number) / 100), 3);
if (element.querySelector(".unit-speed"))
(<HTMLElement>element.querySelector(".unit-speed")).innerText =
String(Math.floor(msToKnots(this.#speed))) + "GS";
(<HTMLElement>element.querySelector(".unit-speed")).innerText = String(Math.floor(msToKnots(this.#speed))) + "GS";
/* Rotate elements according to heading */
element.querySelectorAll("[data-rotate-to-heading]").forEach((el) => {
const headingDeg = rad2deg(this.#heading);
let currentStyle = el.getAttribute("style") || "";
el.setAttribute(
"style",
currentStyle + `transform:rotate(${headingDeg}deg);`
);
el.setAttribute("style", currentStyle + `transform:rotate(${headingDeg}deg);`);
});
}
/* Set vertical offset for altitude stacking */
var pos = getApp().getMap().latLngToLayerPoint(this.getLatLng()).round();
this.setZIndexOffset(
1000 + Math.floor(this.#position.alt as number) - pos.y
);
this.setZIndexOffset(1000 + Math.floor(this.#position.alt as number) - pos.y);
}
}
}
@@ -334,12 +290,7 @@ export class Missile extends Weapon {
}
getMarkerCategory() {
if (
this.belongsToCommandedCoalition() ||
this.getDetectionMethods().includes(VISUAL) ||
this.getDetectionMethods().includes(OPTIC)
)
return "missile";
if (this.belongsToCommandedCoalition() || this.getDetectionMethods().includes(VISUAL) || this.getDetectionMethods().includes(OPTIC)) return "missile";
else return "aircraft";
}
@@ -348,35 +299,20 @@ export class Missile extends Weapon {
showState: false,
showVvi:
!this.belongsToCommandedCoalition() &&
!this.getDetectionMethods().some((value) =>
[VISUAL, OPTIC].includes(value)
) &&
this.getDetectionMethods().some((value) =>
[RADAR, IRST, DLINK].includes(value)
),
!this.getDetectionMethods().some((value) => [VISUAL, OPTIC].includes(value)) &&
this.getDetectionMethods().some((value) => [RADAR, IRST, DLINK].includes(value)),
showHealth: false,
showHotgroup: false,
showUnitIcon:
this.belongsToCommandedCoalition() ||
this.getDetectionMethods().some((value) =>
[VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value)
),
showUnitIcon: this.belongsToCommandedCoalition() || this.getDetectionMethods().some((value) => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value)),
showShortLabel: false,
showFuel: false,
showAmmo: false,
showSummary:
!this.belongsToCommandedCoalition() &&
!this.getDetectionMethods().some((value) =>
[VISUAL, OPTIC].includes(value)
) &&
this.getDetectionMethods().some((value) =>
[RADAR, IRST, DLINK].includes(value)
),
!this.getDetectionMethods().some((value) => [VISUAL, OPTIC].includes(value)) &&
this.getDetectionMethods().some((value) => [RADAR, IRST, DLINK].includes(value)),
showCallsign: false,
rotateToHeading:
this.belongsToCommandedCoalition() ||
this.getDetectionMethods().includes(VISUAL) ||
this.getDetectionMethods().includes(OPTIC),
rotateToHeading: this.belongsToCommandedCoalition() || this.getDetectionMethods().includes(VISUAL) || this.getDetectionMethods().includes(OPTIC),
};
}
}
@@ -391,12 +327,7 @@ export class Bomb extends Weapon {
}
getMarkerCategory() {
if (
this.belongsToCommandedCoalition() ||
this.getDetectionMethods().includes(VISUAL) ||
this.getDetectionMethods().includes(OPTIC)
)
return "bomb";
if (this.belongsToCommandedCoalition() || this.getDetectionMethods().includes(VISUAL) || this.getDetectionMethods().includes(OPTIC)) return "bomb";
else return "aircraft";
}
@@ -405,35 +336,20 @@ export class Bomb extends Weapon {
showState: false,
showVvi:
!this.belongsToCommandedCoalition() &&
!this.getDetectionMethods().some((value) =>
[VISUAL, OPTIC].includes(value)
) &&
this.getDetectionMethods().some((value) =>
[RADAR, IRST, DLINK].includes(value)
),
!this.getDetectionMethods().some((value) => [VISUAL, OPTIC].includes(value)) &&
this.getDetectionMethods().some((value) => [RADAR, IRST, DLINK].includes(value)),
showHealth: false,
showHotgroup: false,
showUnitIcon:
this.belongsToCommandedCoalition() ||
this.getDetectionMethods().some((value) =>
[VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value)
),
showUnitIcon: this.belongsToCommandedCoalition() || this.getDetectionMethods().some((value) => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value)),
showShortLabel: false,
showFuel: false,
showAmmo: false,
showSummary:
!this.belongsToCommandedCoalition() &&
!this.getDetectionMethods().some((value) =>
[VISUAL, OPTIC].includes(value)
) &&
this.getDetectionMethods().some((value) =>
[RADAR, IRST, DLINK].includes(value)
),
!this.getDetectionMethods().some((value) => [VISUAL, OPTIC].includes(value)) &&
this.getDetectionMethods().some((value) => [RADAR, IRST, DLINK].includes(value)),
showCallsign: false,
rotateToHeading:
this.belongsToCommandedCoalition() ||
this.getDetectionMethods().includes(VISUAL) ||
this.getDetectionMethods().includes(OPTIC),
rotateToHeading: this.belongsToCommandedCoalition() || this.getDetectionMethods().includes(VISUAL) || this.getDetectionMethods().includes(OPTIC),
};
}
}

View File

@@ -12,9 +12,7 @@ export class WeaponsManager {
this.#weapons = {};
document.addEventListener("commandModeOptionsChanged", () => {
Object.values(this.#weapons).forEach((weapon: Weapon) =>
weapon.updateVisibility()
);
Object.values(this.#weapons).forEach((weapon: Weapon) => weapon.updateVisibility());
});
}
@@ -96,18 +94,9 @@ export class WeaponsManager {
var detectionMethods: number[] = [];
var units = getApp().getUnitsManager().getUnits();
for (let idx in units) {
if (
units[idx].getAlive() &&
units[idx].getIsLeader() &&
units[idx].getCoalition() !== "neutral" &&
units[idx].getCoalition() != weapon.getCoalition()
) {
if (units[idx].getAlive() && units[idx].getIsLeader() && units[idx].getCoalition() !== "neutral" && units[idx].getCoalition() != weapon.getCoalition()) {
units[idx].getContacts().forEach((contact: Contact) => {
if (
contact.ID == weapon.ID &&
!detectionMethods.includes(contact.detectionMethod)
)
detectionMethods.push(contact.detectionMethod);
if (contact.ID == weapon.ID && !detectionMethods.includes(contact.detectionMethod)) detectionMethods.push(contact.detectionMethod);
});
}
}