mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Reorganized client source code
Removed many useless and old classes Started transition to new CSS Added URIs for specific REST requests
This commit is contained in:
6
client/src/@types/dom.d.ts
vendored
6
client/src/@types/dom.d.ts
vendored
@@ -26,4 +26,10 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ContextMenuOption {
|
||||
tooltip: string;
|
||||
src: string;
|
||||
callback: CallableFunction;
|
||||
}
|
||||
|
||||
export { };
|
||||
19
client/src/@types/unitdatabase.d.ts
vendored
Normal file
19
client/src/@types/unitdatabase.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
interface LoadoutItemBlueprint {
|
||||
name: string;
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
interface LoadoutBlueprint {
|
||||
fuel: number;
|
||||
items: LoadoutItemBlueprint[];
|
||||
roles: string[];
|
||||
code: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface UnitBlueprint {
|
||||
name: string;
|
||||
label: string;
|
||||
shortLabel: string;
|
||||
loadouts: LoadoutBlueprint[];
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
export class Button {
|
||||
#container: HTMLElement | null;
|
||||
#srcs: string[];
|
||||
#callback: CallableFunction;
|
||||
#img: any;
|
||||
#state: number = 0;
|
||||
|
||||
constructor(ID: string, srcs: string[], callback: CallableFunction) {
|
||||
this.#container = document.getElementById(ID);
|
||||
this.#srcs = srcs;
|
||||
this.#callback = callback;
|
||||
if (this.#container != null) {
|
||||
this.#img = document.createElement("img");
|
||||
this.#img.src = this.#srcs[this.#state];
|
||||
this.#container.appendChild(this.#img);
|
||||
this.#container.addEventListener("click", () => this.#onClick());
|
||||
}
|
||||
}
|
||||
|
||||
setState(state: number) {
|
||||
if (state < this.#srcs.length) {
|
||||
this.#state = state;
|
||||
this.#img.src = this.#srcs[this.#state];
|
||||
}
|
||||
}
|
||||
|
||||
getState() {
|
||||
return this.#state;
|
||||
}
|
||||
|
||||
#onClick() {
|
||||
if (this.#img != null) {
|
||||
this.setState(this.#state < this.#srcs.length - 1 ? this.#state + 1 : 0);
|
||||
if (this.#callback)
|
||||
this.#callback(this.#state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +1,33 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { getActiveCoalition, setActiveCoalition } from "..";
|
||||
import { ContextMenuOption } from "../@types/dom";
|
||||
|
||||
export class ContextMenu {
|
||||
#container: HTMLElement | null;
|
||||
#display: string;
|
||||
|
||||
constructor(id: string,) {
|
||||
this.#container = document.getElementById(id);
|
||||
this.#display = '';
|
||||
if (this.#container != null) {
|
||||
this.#container.querySelector("#coalition-switch")?.addEventListener('change', (e) => this.#onSwitch(e))
|
||||
this.#display = this.#container.style.display;
|
||||
this.hide();
|
||||
}
|
||||
this.#container?.querySelector("#switch")?.addEventListener('change', (e) => this.#onSwitch(e))
|
||||
this.hide();
|
||||
}
|
||||
|
||||
show(x: number, y: number, title: string, options: any, callback: CallableFunction, showCoalition: boolean) {
|
||||
/* Hide to remove buttons, if present */
|
||||
this.hide();
|
||||
show(x: number, y: number, title: string, options: ContextMenuOption[], showCoalition: boolean) {
|
||||
this.#container?.classList.toggle("hide", false);
|
||||
|
||||
this.#container?.querySelector("#list")?.replaceChildren(...options.map((option: ContextMenuOption) => {
|
||||
var li = document.createElement("li");
|
||||
var button = document.createElement("button");
|
||||
button.textContent = option.tooltip;
|
||||
li.appendChild(button);
|
||||
button.addEventListener("click", (e: MouseEvent) => option.callback((e.target as HTMLButtonElement).innerText));
|
||||
return button;
|
||||
}));
|
||||
|
||||
this.#container?.querySelector("#switch")?.classList.toggle("hide", !showCoalition);
|
||||
|
||||
if (this.#container != null && options.length >= 1) {
|
||||
var titleDiv = this.#container.querySelector("#ol-selection-scroll-top-bar")?.querySelector(".ol-selection-scroll-title");
|
||||
var titleDiv = this.#container.querySelector("#title");
|
||||
if (titleDiv)
|
||||
titleDiv.innerHTML = title;
|
||||
this.#container.style.display = this.#display;
|
||||
|
||||
var scroll = this.#container.querySelector(".ol-selection-scroll");
|
||||
if (scroll != null)
|
||||
{
|
||||
for (let optionID in options) {
|
||||
var node = document.createElement("div");
|
||||
node.classList.add("ol-selection-scroll-element");
|
||||
if (typeof options[optionID] === 'string' || options[optionID] instanceof String){
|
||||
node.appendChild(document.createTextNode(options[optionID]));
|
||||
node.addEventListener('click', () => callback(options[optionID]));
|
||||
}
|
||||
else {
|
||||
node.appendChild(document.createTextNode(options[optionID].tooltip));
|
||||
node.addEventListener('click', () => options[optionID].callback());
|
||||
}
|
||||
scroll.appendChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide the coalition switch if required */
|
||||
var switchContainer = <HTMLElement>this.#container.querySelector("#ol-selection-scroll-top-bar")?.querySelector("#coalition-switch-container");
|
||||
if (showCoalition == false) {
|
||||
switchContainer.style.display = "none";
|
||||
document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--neutral-coalition-color"));
|
||||
}
|
||||
else {
|
||||
switchContainer.style.display = "block";
|
||||
if (getActiveCoalition() == "blue")
|
||||
document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--blue-coalition-color"));
|
||||
else
|
||||
document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--red-coalition-color"));
|
||||
}
|
||||
titleDiv.textContent = title;
|
||||
|
||||
if (x - this.#container.offsetWidth / 2 + this.#container.offsetWidth < window.innerWidth)
|
||||
this.#container.style.left = x - this.#container.offsetWidth / 2 + "px";
|
||||
@@ -66,34 +38,19 @@ export class ContextMenu {
|
||||
this.#container.style.top = y - 20 + "px";
|
||||
else
|
||||
this.#container.style.top = window.innerHeight - this.#container.offsetHeight + "px";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (this.#container != null) {
|
||||
this.#container.style.display = "none";
|
||||
var buttons = this.#container.querySelectorAll(".ol-selection-scroll-element");
|
||||
var scroll = this.#container.querySelector(".ol-selection-scroll");
|
||||
if (scroll != null)
|
||||
{
|
||||
for (let child of buttons) {
|
||||
scroll.removeChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.#container?.classList.toggle("hide", true);
|
||||
}
|
||||
|
||||
#onSwitch(e: any) {
|
||||
if (this.#container != null) {
|
||||
if (e.currentTarget.checked) {
|
||||
document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--red-coalition-color"));
|
||||
if (e.currentTarget.checked)
|
||||
setActiveCoalition("red");
|
||||
}
|
||||
else {
|
||||
document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--blue-coalition-color"));
|
||||
else
|
||||
setActiveCoalition("blue");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
export class Dropdown {
|
||||
#container: HTMLElement | null;
|
||||
#options: string[];
|
||||
#open?: boolean;
|
||||
#content?: HTMLElement;
|
||||
#callback?: CallableFunction;
|
||||
|
||||
constructor(ID: string, options: string[], callback: CallableFunction) {
|
||||
this.#container = document.getElementById(ID);
|
||||
this.#options = options;
|
||||
this.#callback = callback;
|
||||
this.close()
|
||||
this.#container?.addEventListener("click", () => {
|
||||
this.#open ? this.close() : this.open();
|
||||
})
|
||||
if (this.#container != null && this.#options.length > 0)
|
||||
this.#container.innerHTML = this.#options[0];
|
||||
}
|
||||
|
||||
open() {
|
||||
if (this.#container != null) {
|
||||
this.#open = true;
|
||||
this.#container.classList.add("ol-dropdown-open");
|
||||
this.#container.classList.remove("ol-dropdown-closed");
|
||||
this.#content = document.createElement("div");
|
||||
this.#content.classList.add("ol-dropdown-content");
|
||||
this.#content.style.width = (this.#container.offsetWidth - this.#container.offsetHeight) + "px";
|
||||
|
||||
this.#content.style.left = this.#container.offsetLeft + "px";
|
||||
this.#content.style.top = this.#container.offsetTop + this.#container.offsetHeight + "px";
|
||||
document.body.appendChild(this.#content);
|
||||
|
||||
var height = 2;
|
||||
for (let optionID in this.#options) {
|
||||
var node = document.createElement("div");
|
||||
node.classList.add("ol-dropdown-element");
|
||||
node.appendChild(document.createTextNode(this.#options[optionID]));
|
||||
this.#content.appendChild(node);
|
||||
height += node.offsetHeight + 2;
|
||||
node.addEventListener('click', () => {
|
||||
this.close();
|
||||
if (this.#container != null)
|
||||
this.#container.innerHTML = this.#options[optionID];
|
||||
if (this.#callback != null)
|
||||
this.#callback(this.#options[optionID])
|
||||
})
|
||||
}
|
||||
this.#content.style.height = height + "px";
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.#container != null) {
|
||||
this.#open = false;
|
||||
this.#container?.classList.remove("ol-dropdown-open");
|
||||
this.#container?.classList.add("ol-dropdown-closed");
|
||||
if (this.#content != null)
|
||||
document.body.removeChild(this.#content);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ export class Slider {
|
||||
{
|
||||
this.#container.classList.toggle("active", newActive);
|
||||
if (!newActive && this.#value != null)
|
||||
this.#value.innerHTML = "Mixed values"
|
||||
this.#value.innerText = "Mixed values";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,19 +3,14 @@ import { getDataFromDCS } from "./server/server"
|
||||
import { UnitsManager } from "./units/unitsmanager";
|
||||
import { UnitInfoPanel } from "./panels/unitinfopanel";
|
||||
import { ContextMenu } from "./controls/contextmenu";
|
||||
import { Dropdown } from "./controls/dropdown";
|
||||
import { ConnectionStatusPanel } from "./panels/connectionstatuspanel";
|
||||
import { MissionData } from "./missiondata/missiondata";
|
||||
import { UnitControlPanel } from "./panels/unitcontrolpanel";
|
||||
import { MouseInfoPanel } from "./panels/mouseinfopanel";
|
||||
import { Slider } from "./controls/slider";
|
||||
import { AIC } from "./aic/aic";
|
||||
|
||||
import { VisibilityControlPanel } from "./panels/visibilitycontrolpanel";
|
||||
import { ATC } from "./atc/ATC";
|
||||
import { FeatureSwitches } from "./FeatureSwitches";
|
||||
import { LogPanel } from "./panels/logpanel";
|
||||
import { Button } from "./controls/button";
|
||||
|
||||
var map: Map;
|
||||
var contextMenu: ContextMenu;
|
||||
@@ -23,24 +18,18 @@ var contextMenu: ContextMenu;
|
||||
var unitsManager: UnitsManager;
|
||||
var missionData: MissionData;
|
||||
|
||||
var aic: AIC;
|
||||
var atc: ATC;
|
||||
|
||||
var unitInfoPanel: UnitInfoPanel;
|
||||
var connectionStatusPanel: ConnectionStatusPanel;
|
||||
var unitControlPanel: UnitControlPanel;
|
||||
var mouseInfoPanel: MouseInfoPanel;
|
||||
var visibilityControlPanel: VisibilityControlPanel;
|
||||
var logPanel: LogPanel;
|
||||
|
||||
var mapSourceDropdown: Dropdown;
|
||||
|
||||
var aic: AIC;
|
||||
var aicToggleButton: Button;
|
||||
var aicHelpButton: Button;
|
||||
|
||||
var atc: ATC;
|
||||
var atcToggleButton: Button;
|
||||
|
||||
var connected: boolean;
|
||||
var activeCoalition: string;
|
||||
var connected: boolean = false;
|
||||
var activeCoalition: string = "blue";
|
||||
var refreshData: boolean = true;
|
||||
|
||||
var featureSwitches;
|
||||
|
||||
@@ -53,86 +42,57 @@ function setup() {
|
||||
unitsManager = new UnitsManager();
|
||||
missionData = new MissionData();
|
||||
|
||||
contextMenu = new ContextMenu("selection-scroll");
|
||||
contextMenu = new ContextMenu("contextmenu");
|
||||
|
||||
unitInfoPanel = new UnitInfoPanel("unit-info-panel");
|
||||
unitControlPanel = new UnitControlPanel("unit-control-panel");
|
||||
mapSourceDropdown = new Dropdown("map-source-dropdown", map.getLayers(), (option: string) => map.setLayer(option));
|
||||
connectionStatusPanel = new ConnectionStatusPanel("connection-status-panel");
|
||||
mouseInfoPanel = new MouseInfoPanel("mouse-info-panel");
|
||||
visibilityControlPanel = new VisibilityControlPanel("visibility-control-panel");
|
||||
logPanel = new LogPanel("log-panel");
|
||||
//logPanel = new LogPanel("log-panel");
|
||||
|
||||
missionData = new MissionData();
|
||||
|
||||
/* AIC */
|
||||
|
||||
let aicFeatureSwitch = featureSwitches.getSwitch( "aic" );
|
||||
|
||||
if ( aicFeatureSwitch?.isEnabled() ) {
|
||||
aic = new AIC();
|
||||
|
||||
aicToggleButton = new Button( "toggle-aic-button", ["images/buttons/radar.svg"], () => {
|
||||
aic.toggleStatus();
|
||||
});
|
||||
|
||||
aicHelpButton = new Button( "aic-help-button", [ "images/buttons/question-mark.svg" ], () => {
|
||||
aic.toggleHelp();
|
||||
});
|
||||
// TODO: add back buttons
|
||||
}
|
||||
|
||||
|
||||
/* Generic clicks */
|
||||
|
||||
document.addEventListener( "click", ( ev ) => {
|
||||
|
||||
if ( ev instanceof PointerEvent && ev.target instanceof HTMLElement ) {
|
||||
|
||||
if ( ev.target.classList.contains( "olympus-dialog-close" ) ) {
|
||||
ev.target.closest( "div.olympus-dialog" )?.classList.add( "hide" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
/*** ATC ***/
|
||||
|
||||
/* ATC */
|
||||
let atcFeatureSwitch = featureSwitches.getSwitch( "atc" );
|
||||
|
||||
if ( atcFeatureSwitch?.isEnabled() ) {
|
||||
|
||||
atc = new ATC();
|
||||
|
||||
atcToggleButton = new Button( "atc-toggle-button", [ "images/buttons/atc.svg" ], () => {
|
||||
atc.toggleStatus();
|
||||
} );
|
||||
|
||||
// TODO: add back buttons
|
||||
}
|
||||
|
||||
mapSourceDropdown = new Dropdown("map-source-dropdown", map.getLayers(), (option: string) => map.setLayer(option));
|
||||
|
||||
/* Default values */
|
||||
activeCoalition = "blue";
|
||||
connected = false;
|
||||
|
||||
/* On the first connection, force request of full data */
|
||||
requestUpdate();
|
||||
refreshData = false;
|
||||
}
|
||||
|
||||
function requestUpdate() {
|
||||
getDataFromDCS(update);
|
||||
|
||||
getDataFromDCS(refreshData, update);
|
||||
|
||||
/* Main update rate = 250ms is minimum time, equal to server update time. */
|
||||
setTimeout(() => requestUpdate(), getConnected() ? 250 : 1000);
|
||||
|
||||
connectionStatusPanel.update(getConnected());
|
||||
getConnectionStatusPanel()?.update(getConnected());
|
||||
}
|
||||
|
||||
export function update(data: ServerData) {
|
||||
unitsManager.update(data);
|
||||
missionData.update(data);
|
||||
logPanel.update(data);
|
||||
getUnitsManager()?.update(data);
|
||||
getMissionData()?.update(data);
|
||||
getLogPanel()?.update(data);
|
||||
}
|
||||
|
||||
export function getMap() {
|
||||
@@ -163,6 +123,14 @@ export function getMouseInfoPanel() {
|
||||
return mouseInfoPanel;
|
||||
}
|
||||
|
||||
export function getLogPanel() {
|
||||
return logPanel;
|
||||
}
|
||||
|
||||
export function getConnectionStatusPanel() {
|
||||
return connectionStatusPanel;
|
||||
}
|
||||
|
||||
export function setActiveCoalition(newActiveCoalition: string) {
|
||||
activeCoalition = newActiveCoalition;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { bearing, distance, zeroAppend } from "../other/utils";
|
||||
import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||
import { unitTypes } from "../units/unittypes";
|
||||
import { BoxSelect } from "./boxselect";
|
||||
import { ContextMenuOption } from "../@types/dom";
|
||||
|
||||
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
|
||||
|
||||
@@ -126,16 +127,16 @@ export class Map extends L.Map {
|
||||
return this.#state;
|
||||
}
|
||||
|
||||
/* Selection scroll */
|
||||
showContextMenu(e: ClickEvent | SpawnEvent, title: string, options: any, callback: CallableFunction, showCoalition: boolean = false) {
|
||||
/* Context Menu */
|
||||
showContextMenu(e: ClickEvent | SpawnEvent, title: string, options: ContextMenuOption[], showCoalition: boolean = false) {
|
||||
var x = e.x;
|
||||
var y = e.y;
|
||||
getContextMenu().show(x, y, title, options, callback, showCoalition);
|
||||
getContextMenu()?.show(x, y, title, options, showCoalition);
|
||||
document.dispatchEvent(new CustomEvent("mapContextMenu"));
|
||||
}
|
||||
|
||||
hideContextMenu() {
|
||||
getContextMenu().hide();
|
||||
getContextMenu()?.hide();
|
||||
document.dispatchEvent(new CustomEvent("mapContextMenu"));
|
||||
}
|
||||
|
||||
@@ -195,7 +196,7 @@ export class Map extends L.Map {
|
||||
{ "tooltip": "Smoke", "src": "spawnSmoke.png", "callback": () => this.#smokeSpawnMenu(spawnEvent) },
|
||||
//{ "tooltip": "Explosion", "src": "spawnExplosion.png", "callback": () => this.#explosionSpawnMenu(e) }
|
||||
]
|
||||
this.showContextMenu(spawnEvent, "Action", options, () => {}, false);
|
||||
this.showContextMenu(spawnEvent, "Action", options, false);
|
||||
}
|
||||
}
|
||||
else if (this.#state === "MOVE_UNIT") {
|
||||
@@ -240,7 +241,7 @@ export class Map extends L.Map {
|
||||
{
|
||||
selectedUnitPosition = new L.LatLng(selectedUnits[0].getFlightData().latitude, selectedUnits[0].getFlightData().longitude);
|
||||
}
|
||||
getMouseInfoPanel().update(<L.LatLng>e.latlng, this.#measurePoint, selectedUnitPosition);
|
||||
getMouseInfoPanel()?.update(<L.LatLng>e.latlng, this.#measurePoint, selectedUnitPosition);
|
||||
|
||||
this.#lastMousePosition.x = e.originalEvent.x;
|
||||
this.#lastMousePosition.y = e.originalEvent.y;
|
||||
@@ -272,9 +273,9 @@ export class Map extends L.Map {
|
||||
{ 'coalition': true, 'tooltip': 'Transport', 'src': 'spawnTransport.png', 'callback': () => this.#selectAircraft(e, "transport") },
|
||||
]
|
||||
if (e.airbaseName != null)
|
||||
this.showContextMenu(e, "Spawn at " + e.airbaseName, options, () => {}, true);
|
||||
this.showContextMenu(e, "Spawn at " + e.airbaseName, options, true);
|
||||
else
|
||||
this.showContextMenu(e, "Spawn air unit", options, () => {}, true);
|
||||
this.showContextMenu(e, "Spawn air unit", options, true);
|
||||
}
|
||||
|
||||
#groundUnitSpawnMenu(e: SpawnEvent) {
|
||||
@@ -287,7 +288,7 @@ export class Map extends L.Map {
|
||||
{'coalition': true, 'tooltip': 'Radar', 'src': 'spawnRadar.png', 'callback': () => this.#selectGroundUnit(e, "Radar")},
|
||||
{'coalition': true, 'tooltip': 'Unarmed', 'src': 'spawnUnarmed.png', 'callback': () => this.#selectGroundUnit(e, "Unarmed")}
|
||||
]
|
||||
this.showContextMenu(e, "Spawn ground unit", options, () => {}, true);
|
||||
this.showContextMenu(e, "Spawn ground unit", options, true);
|
||||
}
|
||||
|
||||
#smokeSpawnMenu(e: SpawnEvent) {
|
||||
@@ -299,7 +300,7 @@ export class Map extends L.Map {
|
||||
{'tooltip': 'Green smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('green', e.latlng)}, 'tint': 'green'},
|
||||
{'tooltip': 'Orange smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('orange', e.latlng)}, 'tint': 'orange'},
|
||||
]
|
||||
this.showContextMenu(e, "Spawn smoke", options, () => {}, false);
|
||||
this.showContextMenu(e, "Spawn smoke", options, false);
|
||||
}
|
||||
|
||||
#explosionSpawnMenu(e: SpawnEvent) {
|
||||
@@ -310,12 +311,14 @@ export class Map extends L.Map {
|
||||
#selectAircraft(e: SpawnEvent, role: string) {
|
||||
this.hideContextMenu();
|
||||
var options = aircraftDatabase.getLabelsByRole(role);
|
||||
this.showContextMenu(e, "Select aircraft", options, (label: string) => {
|
||||
this.hideContextMenu();
|
||||
var name = aircraftDatabase.getNameByLabel(label);
|
||||
if (name != null)
|
||||
this.#unitSelectPayload(e, name, role);
|
||||
}, true);
|
||||
this.showContextMenu(e, "Select aircraft",
|
||||
options.map((option: string) => {
|
||||
return {tooltip: option, src: "", callback: (label: string) => {
|
||||
this.hideContextMenu();
|
||||
var name = aircraftDatabase.getNameByLabel(label);
|
||||
if (name != null)
|
||||
this.#unitSelectPayload(e, name, role);
|
||||
}}}), true);
|
||||
}
|
||||
|
||||
/* Show weapon selection for air units */
|
||||
@@ -325,11 +328,13 @@ export class Map extends L.Map {
|
||||
//options = payloadNames[unitType]
|
||||
if (options != undefined && options.length > 0) {
|
||||
options.sort();
|
||||
this.showContextMenu({x: e.x, y: e.y, latlng: e.latlng}, "Select loadout", options, (loadoutName: string) => {
|
||||
this.hideContextMenu();
|
||||
var loadout = aircraftDatabase.getLoadoutsByName(unitType, loadoutName);
|
||||
spawnAircraft(unitType, e.latlng, getActiveCoalition(), loadout != null? loadout.code: "", e.airbaseName);
|
||||
}, true);
|
||||
this.showContextMenu({x: e.x, y: e.y, latlng: e.latlng}, "Select loadout",
|
||||
options.map((option: string) => {
|
||||
return {tooltip: option, src: "", callback: (loadoutName: string) => {
|
||||
this.hideContextMenu();
|
||||
var loadout = aircraftDatabase.getLoadoutsByName(unitType, loadoutName);
|
||||
spawnAircraft(unitType, e.latlng, getActiveCoalition(), loadout != null? loadout.code: "", e.airbaseName);
|
||||
}}}), true);
|
||||
}
|
||||
else {
|
||||
spawnAircraft(unitType, e.latlng, getActiveCoalition());
|
||||
@@ -342,10 +347,12 @@ export class Map extends L.Map {
|
||||
this.hideContextMenu();
|
||||
var options = unitTypes.vehicles[group];
|
||||
options.sort();
|
||||
this.showContextMenu(e, "Select ground unit", options, (unitType: string) => {
|
||||
this.hideContextMenu();
|
||||
spawnGroundUnit(unitType, e.latlng, getActiveCoalition());
|
||||
}, true);
|
||||
this.showContextMenu(e, "Select ground unit",
|
||||
options.map((option: string) => {
|
||||
return {tooltip: option, src: "", callback: (unitType: string) => {
|
||||
this.hideContextMenu();
|
||||
spawnGroundUnit(unitType, e.latlng, getActiveCoalition());
|
||||
}}}), true);
|
||||
}
|
||||
|
||||
#drawMeasureLine()
|
||||
|
||||
@@ -7,7 +7,7 @@ export interface AirbaseOptions
|
||||
src: string
|
||||
}
|
||||
|
||||
export class AirbaseMarker extends L.Marker
|
||||
export class Airbase extends L.Marker
|
||||
{
|
||||
#name: string = "";
|
||||
#coalitionID: number = -1;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Marker, LatLng, Icon } from "leaflet";
|
||||
import { getMap, getUnitsManager } from "..";
|
||||
import { SpawnEvent } from "../map/map";
|
||||
import { AirbaseMarker } from "./airbasemarker";
|
||||
import { Airbase } from "./airbase";
|
||||
|
||||
var bullseyeIcons = [
|
||||
new Icon({ iconUrl: 'images/bullseye0.png', iconAnchor: [30, 30]}),
|
||||
@@ -14,7 +14,7 @@ export class MissionData
|
||||
#bullseyes : any; //TODO declare interface
|
||||
#bullseyeMarkers: any;
|
||||
#airbases : any; //TODO declare interface
|
||||
#airbasesMarkers: {[name: string]: AirbaseMarker};
|
||||
#airbasesMarkers: {[name: string]: Airbase};
|
||||
|
||||
constructor()
|
||||
{
|
||||
@@ -59,7 +59,7 @@ export class MissionData
|
||||
var airbase = this.#airbases[idx]
|
||||
if (this.#airbasesMarkers[idx] === undefined)
|
||||
{
|
||||
this.#airbasesMarkers[idx] = new AirbaseMarker({
|
||||
this.#airbasesMarkers[idx] = new Airbase({
|
||||
position: new LatLng(airbase.lat, airbase.lng),
|
||||
name: airbase.callsign,
|
||||
src: "images/airbase.png"}).addTo(getMap());
|
||||
@@ -79,8 +79,10 @@ export class MissionData
|
||||
options = ["Spawn unit", "Land here"];
|
||||
else
|
||||
options = ["Spawn unit"];
|
||||
getMap().showContextMenu(e.originalEvent, e.sourceTarget.getName(), options, (option: string) => this.#onAirbaseOptionSelection(e, option), false);
|
||||
|
||||
|
||||
getMap().showContextMenu(e.originalEvent, e.sourceTarget.getName(),
|
||||
options.map((option) => {return {tooltip: option, src: "", callback: (label: string) => {this.#onAirbaseOptionSelection(e, label)}}}, false)
|
||||
)
|
||||
}
|
||||
|
||||
#onAirbaseOptionSelection(e: any, option: string) {
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
export class Panel {
|
||||
#element: HTMLElement
|
||||
#display: string;
|
||||
|
||||
constructor(ID: string) {
|
||||
this.#element = <HTMLElement>document.getElementById(ID);
|
||||
this.#display = '';
|
||||
this.#display = this.#element.style.display;
|
||||
}
|
||||
|
||||
show() {
|
||||
this.#element.style.display = this.#display;
|
||||
this.#element.classList.toggle("hide", false);
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.#element.style.display = "none";
|
||||
this.#element.classList.toggle("hide", true);
|
||||
}
|
||||
|
||||
getElement() {
|
||||
|
||||
0
client/src/panels/toppanel.ts
Normal file
0
client/src/panels/toppanel.ts
Normal file
@@ -3,66 +3,42 @@ import { Slider } from "../controls/slider";
|
||||
import { Aircraft, AirUnit, GroundUnit, Helicopter, NavyUnit, Unit } from "../units/unit";
|
||||
import { Panel } from "./panel";
|
||||
|
||||
interface Button {
|
||||
id: string,
|
||||
value: string,
|
||||
element: null | HTMLElement
|
||||
}
|
||||
var ROEs: string[] = ["Free", "Designated free", "Designated", "Return", "Hold"];
|
||||
var reactionsToThreat: string[] = [ "None", "Passive", "Evade", "Escape", "Abort"];
|
||||
var minSpeedValues: {[key: string]: number} = {Aircraft: 100, Helicopter: 0, NavyUnit: 0, GroundUnit: 0};
|
||||
var maxSpeedValues: {[key: string]: number} = {Aircraft: 800, Helicopter: 300, NavyUnit: 60, GroundUnit: 60};
|
||||
var minAltitudeValues: {[key: string]: number} = {Aircraft: 500, Helicopter: 0, NavyUnit: 0, GroundUnit: 0};
|
||||
var maxAltitudeValues: {[key: string]: number} = {Aircraft: 50000, Helicopter: 10000, NavyUnit: 60, GroundUnit: 60};
|
||||
|
||||
export class UnitControlPanel extends Panel {
|
||||
#altitudeSlider: Slider;
|
||||
#airspeedSlider: Slider;
|
||||
#formationCreationContainer: HTMLElement;
|
||||
#ROEButtonsContainer: HTMLElement;
|
||||
#reactionToThreatButtonsContainer: HTMLElement;
|
||||
#selectedUnitsContainer: HTMLElement;
|
||||
#ROEButtons: Button[] = [
|
||||
{id: "#free", value: "Free", element: null},
|
||||
{id: "#designated-free", value: "Designated free", element: null},
|
||||
{id: "#designated", value: "Designated", element: null},
|
||||
{id: "#return", value: "Return", element: null},
|
||||
{id: "#hold", value: "Hold", element: null}
|
||||
]
|
||||
#reactionToThreatButtons: Button[] = [
|
||||
{id: "#none", value: "None", element: null},
|
||||
{id: "#passive", value: "Passive", element: null},
|
||||
{id: "#evade", value: "Evade", element: null},
|
||||
{id: "#escape", value: "Escape", element: null},
|
||||
{id: "#abort", value: "Abort", element: null}
|
||||
]
|
||||
|
||||
#optionButtons: {[key: string]: HTMLButtonElement[]} = {}
|
||||
|
||||
constructor(ID: string) {
|
||||
super(ID);
|
||||
|
||||
/* Selected units container */
|
||||
this.#selectedUnitsContainer = <HTMLElement>(this.getElement().querySelector("#selected-units-container"));
|
||||
|
||||
/* Unit control sliders */
|
||||
this.#altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => getUnitsManager().selectedUnitsSetAltitude(value * 0.3048));
|
||||
this.#airspeedSlider = new Slider("airspeed-slider", 0, 100, "kts", (value: number) => getUnitsManager().selectedUnitsSetSpeed(value / 1.94384));
|
||||
|
||||
/* Formation control buttons */
|
||||
this.#formationCreationContainer = <HTMLElement>(this.getElement().querySelector("#formation-creation-container"));
|
||||
//var createButton = <HTMLElement>this.#formationCreationContainer.querySelector("#create-formation");
|
||||
//createButton?.addEventListener("click", () => getUnitsManager().selectedUnitsCreateFormation());
|
||||
//var undoButton = <HTMLElement>this.#formationCreationContainer.querySelector("#undo-formation");
|
||||
//undoButton?.addEventListener("click", () => getUnitsManager().selectedUnitsUndoFormation());
|
||||
|
||||
/* ROE buttons */
|
||||
this.#ROEButtonsContainer = <HTMLElement>(this.getElement().querySelector("#roe-buttons-container"));
|
||||
for (let button of this.#ROEButtons)
|
||||
{
|
||||
button.element = <HTMLElement>(this.#ROEButtonsContainer.querySelector(button.id));
|
||||
button.element?.addEventListener("click", () => getUnitsManager().selectedUnitsSetROE(button.value));
|
||||
}
|
||||
|
||||
/* Reaction to threat buttons */
|
||||
this.#reactionToThreatButtonsContainer = <HTMLElement>(this.getElement().querySelector("#reaction-to-threat-buttons-container"));
|
||||
for (let button of this.#reactionToThreatButtons)
|
||||
{
|
||||
button.element = <HTMLElement>(this.#reactionToThreatButtonsContainer.querySelector(button.id));
|
||||
button.element?.addEventListener("click", () => getUnitsManager().selectedUnitsSetReactionToThreat(button.value));
|
||||
}
|
||||
/* Option buttons */
|
||||
this.#optionButtons["ROE"] = ROEs.map((option: string) => {
|
||||
var button = document.createElement("button");
|
||||
button.innerText = option;
|
||||
button.addEventListener("click", () => getUnitsManager().selectedUnitsSetROE(button.value));
|
||||
return button;
|
||||
})
|
||||
|
||||
this.#optionButtons["reactionToThreat"] = reactionsToThreat.map((option: string) => {
|
||||
var button = document.createElement("button");
|
||||
button.innerText = option;
|
||||
button.addEventListener("click", () => getUnitsManager().selectedUnitsSetROE(button.value));
|
||||
return button;
|
||||
})
|
||||
|
||||
this.getElement().querySelector("#roe-buttons-container")?.append(...this.#optionButtons["ROE"]);
|
||||
this.getElement().querySelector("#reaction-to-threat-buttons-container")?.append(...this.#optionButtons["reactionToThreat"]);
|
||||
|
||||
this.hide();
|
||||
}
|
||||
@@ -70,16 +46,22 @@ export class UnitControlPanel extends Panel {
|
||||
update(units: Unit[]) {
|
||||
if (this.getElement() != null)
|
||||
{
|
||||
this.#addUnitsButtons(units);
|
||||
//this.#showFormationButtons(units);
|
||||
|
||||
this.#showFlightControlSliders(units);
|
||||
this.getElement().querySelector("#selected-units-container")?.replaceChildren(...units.map((unit: Unit) =>
|
||||
{
|
||||
var button = document.createElement("button");
|
||||
button.innerText = unit.getData().unitName;
|
||||
button.addEventListener("click", () => getUnitsManager().selectUnit(unit.ID, true));
|
||||
return (button);
|
||||
}));
|
||||
|
||||
for (let button of this.#ROEButtons)
|
||||
button.element?.classList.toggle("white", this.#getROE(units) === button.value);
|
||||
|
||||
for (let button of this.#reactionToThreatButtons)
|
||||
button.element?.classList.toggle("white", this.#getReactionToThreat(units) === button.value);
|
||||
this.#optionButtons["ROE"].forEach((button: HTMLButtonElement) => {
|
||||
button.classList.toggle("active", units.every((unit: Unit) => unit.getOptionsData().ROE === button.value))
|
||||
});
|
||||
|
||||
this.#optionButtons["reactionToThreat"].forEach((button: HTMLButtonElement) => {
|
||||
button.classList.toggle("active", units.every((unit: Unit) => unit.getOptionsData().reactionToThreat === button.value))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,263 +70,18 @@ export class UnitControlPanel extends Panel {
|
||||
this.#airspeedSlider.show();
|
||||
this.#altitudeSlider.show();
|
||||
|
||||
if (this.#checkAllUnitsAircraft(units))
|
||||
{
|
||||
this.#airspeedSlider.setMinMax(100, 600);
|
||||
this.#altitudeSlider.setMinMax(0, 50000);
|
||||
}
|
||||
else if (this.#checkAllUnitsHelicopter(units))
|
||||
{
|
||||
this.#airspeedSlider.setMinMax(0, 200);
|
||||
this.#altitudeSlider.setMinMax(0, 10000);
|
||||
}
|
||||
else if (this.#checkAllUnitsGroundUnit(units))
|
||||
{
|
||||
this.#airspeedSlider.setMinMax(0, 60);
|
||||
this.#altitudeSlider.hide();
|
||||
}
|
||||
else if (this.#checkAllUnitsNavyUnit(units))
|
||||
{
|
||||
this.#airspeedSlider.setMinMax(0, 60);
|
||||
this.#altitudeSlider.hide();
|
||||
}
|
||||
else {
|
||||
this.#airspeedSlider.hide();
|
||||
this.#altitudeSlider.hide();
|
||||
}
|
||||
var unitsType = getUnitsManager().getSelectedUnitsType();
|
||||
var targetAltitude = getUnitsManager().getSelectedUnitsTargetAltitude();
|
||||
var targetSpeed = getUnitsManager().getSelectedUnitsTargetSpeed();
|
||||
|
||||
var targetSpeed = this.#getTargetAirspeed(units);
|
||||
if (targetSpeed != null)
|
||||
{
|
||||
this.#airspeedSlider.setActive(true);
|
||||
this.#airspeedSlider.setValue(targetSpeed * 1.94384);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.#airspeedSlider.setActive(false);
|
||||
}
|
||||
if (["GroundUnit", "NavyUnit"].includes(unitsType))
|
||||
this.#altitudeSlider.hide()
|
||||
|
||||
var targetAltitude = this.#getTargetAltitude(units);
|
||||
if (targetAltitude != null)
|
||||
{
|
||||
this.#altitudeSlider.setActive(true);
|
||||
this.#altitudeSlider.setValue(targetAltitude / 0.3048);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.#altitudeSlider.setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
#addUnitsButtons(units: Unit[])
|
||||
{
|
||||
/* Remove any pre-existing unit button */
|
||||
var elements = this.#selectedUnitsContainer.getElementsByClassName("js-unit-container");
|
||||
while (elements.length > 0)
|
||||
this.#selectedUnitsContainer.removeChild(elements[0])
|
||||
|
||||
/* Create all the units buttons */
|
||||
for (let unit of units)
|
||||
{
|
||||
this.#addUnitButton(unit, this.#selectedUnitsContainer);
|
||||
if (unit.getFormationData().isLeader)
|
||||
for (let wingman of unit.getWingmen())
|
||||
this.#addUnitButton(wingman, this.#selectedUnitsContainer);
|
||||
}
|
||||
}
|
||||
|
||||
#addUnitButton(unit: Unit, container: HTMLElement)
|
||||
{
|
||||
var el = document.createElement("div");
|
||||
|
||||
/* Unit name (actually type, but DCS calls it name for some reason) */
|
||||
var nameDiv = document.createElement("div");
|
||||
nameDiv.classList.add("ol-rounded-container-small");
|
||||
if (unit.getData().name.length >= 7)
|
||||
nameDiv.innerHTML = `${unit.getData().name.substring(0, 4)} ...`;
|
||||
else
|
||||
nameDiv.innerHTML = `${unit.getData().name}`;
|
||||
|
||||
/* Unit icon */
|
||||
var icon = document.createElement("img");
|
||||
if (unit.getFormationData().isLeader)
|
||||
icon.src = "images/icons/formation.png"
|
||||
else if (unit.getFormationData().isWingman)
|
||||
{
|
||||
var wingmen = unit.getLeader()?.getWingmen();
|
||||
if (wingmen && wingmen.lastIndexOf(unit) == wingmen.length - 1)
|
||||
icon.src = "images/icons/formation-end.svg"
|
||||
else
|
||||
icon.src = "images/icons/formation-middle.svg"
|
||||
}
|
||||
|
||||
else
|
||||
icon.src = "images/icons/singleton.png"
|
||||
|
||||
el.innerHTML = unit.getData().unitName;
|
||||
|
||||
el.prepend(nameDiv);
|
||||
|
||||
/* Show the icon only for air units */
|
||||
if ((unit instanceof AirUnit))
|
||||
el.append(icon);
|
||||
|
||||
el.classList.add("ol-rounded-container", "js-unit-container");
|
||||
|
||||
if (!unit.getSelected())
|
||||
el.classList.add("not-selected")
|
||||
|
||||
/* Set background color */
|
||||
el.classList.toggle("red", unit.getMissionData().coalition === "red");
|
||||
icon.classList.toggle("red", unit.getMissionData().coalition === "red");
|
||||
el.classList.toggle("blue", unit.getMissionData().coalition === "blue");
|
||||
icon.classList.toggle("blue", unit.getMissionData().coalition === "blue");
|
||||
el.classList.toggle("neutral", unit.getMissionData().coalition === "neutral");
|
||||
icon.classList.toggle("neutral", unit.getMissionData().coalition === "neutral");
|
||||
|
||||
el.addEventListener("click", () => getUnitsManager().selectUnit(unit.ID));
|
||||
container.appendChild(el);
|
||||
}
|
||||
|
||||
#showFormationButtons(units: Unit[])
|
||||
{
|
||||
var createButton = <HTMLElement>this.#formationCreationContainer.querySelector("#create-formation");
|
||||
var undoButton = <HTMLElement>this.#formationCreationContainer.querySelector("#undo-formation");
|
||||
if (createButton && undoButton && this.#checkAllUnitsAir(units))
|
||||
{
|
||||
if (!this.#checkUnitsAlreadyInFormation(units))
|
||||
{
|
||||
createButton.style.display = '';
|
||||
undoButton.style.display = 'none';
|
||||
}
|
||||
else if (this.#checkUnitsAlreadyInFormation(units) && this.#checkAllUnitsSameFormation(units))
|
||||
{
|
||||
createButton.style.display = 'none';
|
||||
undoButton.style.display = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
createButton.style.display = 'none';
|
||||
undoButton.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#checkAllUnitsAir(units: Unit[])
|
||||
{
|
||||
for (let unit of units)
|
||||
if (!(unit instanceof AirUnit))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
#checkAllUnitsAircraft(units: Unit[])
|
||||
{
|
||||
for (let unit of units)
|
||||
if (!(unit instanceof Aircraft))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
#checkAllUnitsHelicopter(units: Unit[])
|
||||
{
|
||||
for (let unit of units)
|
||||
if (!(unit instanceof Helicopter))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
#checkAllUnitsGroundUnit(units: Unit[])
|
||||
{
|
||||
for (let unit of units)
|
||||
if (!(unit instanceof GroundUnit))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
#checkAllUnitsNavyUnit(units: Unit[])
|
||||
{
|
||||
for (let unit of units)
|
||||
if (!(unit instanceof NavyUnit))
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
#checkAllUnitsSameFormation(units: Unit[])
|
||||
{
|
||||
var leaderFound = false;
|
||||
for (let unit of units)
|
||||
{
|
||||
if (unit.getFormationData().isLeader)
|
||||
{
|
||||
if (leaderFound)
|
||||
return false
|
||||
else
|
||||
leaderFound = true;
|
||||
}
|
||||
if (!unit.getFormationData().isLeader)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
#checkUnitsAlreadyInFormation(units: Unit[])
|
||||
{
|
||||
for (let unit of units)
|
||||
if (unit.getFormationData().isLeader)
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
#getTargetAirspeed(units: Unit[])
|
||||
{
|
||||
var airspeed = null;
|
||||
for (let unit of units)
|
||||
{
|
||||
if (unit.getTaskData().targetSpeed != airspeed && airspeed != null)
|
||||
return null
|
||||
else
|
||||
airspeed = unit.getTaskData().targetSpeed;
|
||||
}
|
||||
return airspeed;
|
||||
}
|
||||
|
||||
#getTargetAltitude(units: Unit[])
|
||||
{
|
||||
var altitude = null;
|
||||
for (let unit of units)
|
||||
{
|
||||
if (unit.getTaskData().targetAltitude != altitude && altitude != null)
|
||||
return null
|
||||
else
|
||||
altitude = unit.getTaskData().targetAltitude;
|
||||
}
|
||||
return altitude;
|
||||
}
|
||||
|
||||
#getROE(units: Unit[])
|
||||
{
|
||||
var ROE = null;
|
||||
for (let unit of units)
|
||||
{
|
||||
if (unit.getOptionsData().ROE !== ROE && ROE != null)
|
||||
return null
|
||||
else
|
||||
ROE = unit.getOptionsData().ROE;
|
||||
}
|
||||
return ROE;
|
||||
}
|
||||
|
||||
#getReactionToThreat(units: Unit[])
|
||||
{
|
||||
var reactionToThreat = null;
|
||||
for (let unit of units)
|
||||
{
|
||||
if (unit.getOptionsData().reactionToThreat !== reactionToThreat && reactionToThreat != null)
|
||||
return null
|
||||
else
|
||||
reactionToThreat = unit.getOptionsData().reactionToThreat;
|
||||
}
|
||||
return reactionToThreat;
|
||||
this.#airspeedSlider.setMinMax(minSpeedValues[unitsType], maxSpeedValues[unitsType]);
|
||||
this.#altitudeSlider.setMinMax(minAltitudeValues[unitsType], maxAltitudeValues[unitsType]);
|
||||
this.#airspeedSlider.setActive(targetSpeed != undefined);
|
||||
this.#airspeedSlider.setValue(targetSpeed * 1.94384);
|
||||
this.#altitudeSlider.setActive(targetAltitude != undefined);
|
||||
this.#altitudeSlider.setValue(targetAltitude / 0.3048);
|
||||
}
|
||||
}
|
||||
@@ -36,16 +36,16 @@ export class UnitInfoPanel extends Panel {
|
||||
update(unit: Unit) {
|
||||
if (this.getElement() != null) {
|
||||
/* Set the unit info */
|
||||
this.#unitName.innerHTML = unit.getData().unitName;
|
||||
this.#groupName.innerHTML = unit.getData().groupName;
|
||||
this.#name.innerHTML = unit.getData().name;
|
||||
this.#heading.innerHTML = String(Math.floor(rad2deg(unit.getFlightData().heading)) + " °");
|
||||
this.#altitude.innerHTML = String(Math.floor(unit.getFlightData().altitude / 0.3048) + " ft");
|
||||
this.#groundSpeed.innerHTML = String(Math.floor(unit.getFlightData().speed * 1.94384) + " kts");
|
||||
this.#fuel.innerHTML = String(unit.getMissionData().fuel + "%");
|
||||
this.#latitude.innerHTML = ConvertDDToDMS(unit.getFlightData().latitude, false);
|
||||
this.#longitude.innerHTML = ConvertDDToDMS(unit.getFlightData().longitude, true);
|
||||
this.#task.innerHTML = unit.getTaskData().currentTask !== ""? unit.getTaskData().currentTask: "No task";
|
||||
this.#unitName.innerText = unit.getData().unitName;
|
||||
this.#groupName.innerText = unit.getData().groupName;
|
||||
this.#name.innerText = unit.getData().name;
|
||||
this.#heading.innerText = String(Math.floor(rad2deg(unit.getFlightData().heading)) + " °");
|
||||
this.#altitude.innerText = String(Math.floor(unit.getFlightData().altitude / 0.3048) + " ft");
|
||||
this.#groundSpeed.innerText = String(Math.floor(unit.getFlightData().speed * 1.94384) + " kts");
|
||||
this.#fuel.innerText = String(unit.getMissionData().fuel + "%");
|
||||
this.#latitude.innerText = ConvertDDToDMS(unit.getFlightData().latitude, false);
|
||||
this.#longitude.innerText = ConvertDDToDMS(unit.getFlightData().longitude, true);
|
||||
this.#task.innerText = unit.getTaskData().currentTask !== ""? unit.getTaskData().currentTask: "No task";
|
||||
|
||||
/* Set the class of the task container */
|
||||
this.#task.classList.toggle("red", unit.getMissionData().coalition === "red");
|
||||
@@ -69,7 +69,7 @@ export class UnitInfoPanel extends Panel {
|
||||
var amount = ammo.count;
|
||||
var el = document.createElement("div")
|
||||
el.classList.add("js-loadout-element", "ol-rectangular-container-dark")
|
||||
el.innerHTML = amount + "x" + displayName;
|
||||
el.innerText = amount + "x" + displayName;
|
||||
this.#loadoutContainer.appendChild(el);
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import { AirUnit, GroundUnit, NavyUnit, Weapon } from "../units/unit";
|
||||
|
||||
export class VisibilityControlPanel {
|
||||
#element: HTMLElement
|
||||
|
||||
constructor(ID: string) {
|
||||
this.#element = <HTMLElement>document.getElementById(ID);
|
||||
|
||||
if (this.#element != null)
|
||||
{
|
||||
var airVisibilityCheckbox = this.#element.querySelector("#air-visibility");
|
||||
var groundVisibilityCheckbox = this.#element.querySelector("#ground-visibility");
|
||||
var navyVisibilityCheckbox = this.#element.querySelector("#navy-visibility");
|
||||
var weaponVisibilityCheckbox = this.#element.querySelector("#weapon-visibility");
|
||||
|
||||
airVisibilityCheckbox?.addEventListener("change", () => this.#onChange());
|
||||
groundVisibilityCheckbox?.addEventListener("change", () => this.#onChange());
|
||||
navyVisibilityCheckbox?.addEventListener("change", () => this.#onChange());
|
||||
weaponVisibilityCheckbox?.addEventListener("change", () => this.#onChange());
|
||||
|
||||
var fullVisibilitySelection = this.#element.querySelector("#full-visibility");
|
||||
var partialVisibilitySelection = this.#element.querySelector("#partial-visibility");
|
||||
var minimalVisibilitySelection = this.#element.querySelector("#minimal-visibility");
|
||||
|
||||
fullVisibilitySelection?.addEventListener("change", () => this.#onChange());
|
||||
partialVisibilitySelection?.addEventListener("change", () => this.#onChange());
|
||||
minimalVisibilitySelection?.addEventListener("change", () => this.#onChange());
|
||||
|
||||
var uncontrolledVisibilityCheckbox = this.#element.querySelector("#uncontrolled-visibility");
|
||||
uncontrolledVisibilityCheckbox?.addEventListener("change", () => this.#onChange());
|
||||
}
|
||||
}
|
||||
|
||||
#onChange(){
|
||||
if (this.#element != null)
|
||||
{
|
||||
var fullVisibilitySelection = <HTMLInputElement> this.#element.querySelector("#full-visibility");
|
||||
var partialVisibilitySelection = <HTMLInputElement> this.#element.querySelector("#partial-visibility");
|
||||
var minimalVisibilitySelection = <HTMLInputElement> this.#element.querySelector("#minimal-visibility");
|
||||
|
||||
var activeVisibility = "";
|
||||
if (fullVisibilitySelection.checked)
|
||||
activeVisibility = "full";
|
||||
else if (partialVisibilitySelection.checked)
|
||||
activeVisibility = "partial";
|
||||
else if (minimalVisibilitySelection.checked)
|
||||
activeVisibility = "minimal";
|
||||
|
||||
var uncontrolledVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#uncontrolled-visibility");
|
||||
var uncontrolledVisibility = !uncontrolledVisibilityCheckbox.checked;
|
||||
|
||||
//var airVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#air-visibility");
|
||||
//if (airVisibilityCheckbox.checked)
|
||||
// AirUnit.setVisibility({human: "full", AI: activeVisibility, uncontrolled: uncontrolledVisibility? activeVisibility: "hidden", dead: "hidden"});
|
||||
//else
|
||||
// AirUnit.setVisibility({human: "hidden", AI: "hidden", uncontrolled: "hidden", dead: "hidden"});
|
||||
//
|
||||
//var groundVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#ground-visibility");
|
||||
//if (groundVisibilityCheckbox.checked)
|
||||
// GroundUnit.setVisibility({human: activeVisibility, AI: activeVisibility, uncontrolled: uncontrolledVisibility? activeVisibility: "hidden", dead: "hidden"});
|
||||
//else
|
||||
// GroundUnit.setVisibility({human: "hidden", AI: "hidden", uncontrolled: "hidden", dead: "hidden"});
|
||||
//
|
||||
//var navyVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#navy-visibility");
|
||||
//if (navyVisibilityCheckbox.checked)
|
||||
// NavyUnit.setVisibility({human: activeVisibility, AI: activeVisibility, uncontrolled: uncontrolledVisibility? activeVisibility: "hidden", dead: "hidden"});
|
||||
//else
|
||||
// NavyUnit.setVisibility({human: "hidden", AI: "hidden", uncontrolled: "hidden", dead: "hidden"});
|
||||
//
|
||||
//var weaponVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#weapon-visibility");
|
||||
//if (weaponVisibilityCheckbox.checked)
|
||||
// Weapon.setVisibility({human: activeVisibility, AI: activeVisibility, uncontrolled: uncontrolledVisibility? activeVisibility: "hidden", dead: "hidden"});
|
||||
//else
|
||||
// Weapon.setVisibility({human: "hidden", AI: "hidden", uncontrolled: "hidden", dead: "hidden"});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
import * as L from 'leaflet'
|
||||
import { getUnitsManager, setConnected } from '..';
|
||||
import { ConvertDDToDMS } from '../other/utils';
|
||||
import { setConnected } from '..';
|
||||
|
||||
/* Edit here to change server address */
|
||||
var RESTaddress = "http://localhost:30000/restdemo";
|
||||
const RESTaddress = "http://localhost:30000/olympus";
|
||||
const UNITS_URI = "units";
|
||||
const FULL_UPDATE_URI = "full";
|
||||
const PARTIAL_UPDATE_URI = "partial";
|
||||
const LOGS_URI = "logs";
|
||||
const AIRBASES_URI = "airbases";
|
||||
const BULLSEYE_URI = "bullseye";
|
||||
|
||||
export function getDataFromDCS(callback: CallableFunction) {
|
||||
export function getDataFromDCS(refresh: boolean, callback: CallableFunction) {
|
||||
/* Request the updated unit data from the server */
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("GET", RESTaddress, true);
|
||||
@@ -20,7 +25,9 @@ export function getDataFromDCS(callback: CallableFunction) {
|
||||
console.error("An error occurred during the XMLHttpRequest");
|
||||
setConnected(false);
|
||||
};
|
||||
xmlHttp.send(null);
|
||||
|
||||
var request = { "refresh": refresh }
|
||||
xmlHttp.send(JSON.stringify(request));
|
||||
}
|
||||
|
||||
export function addDestination(ID: number, path: any) {
|
||||
|
||||
@@ -49,37 +49,28 @@ export class Unit extends Marker {
|
||||
this.on('contextmenu', (e) => this.#onContextMenu(e));
|
||||
|
||||
var icon = new DivIcon({
|
||||
html: `<div class="unit"
|
||||
data-coalition=${this.getMissionData().coalition}
|
||||
data-pilot=${this.getMissionData().flags.human? "human": "ai"}>
|
||||
<div class="unit-spotlight">
|
||||
<div class="unit-selected-border">
|
||||
<div class="unit-vvi">
|
||||
<div class="unit-vvi-heading"></div>
|
||||
</div>
|
||||
<div class="unit-id">${aircraftDatabase.getShortLabelByName(this.getData().name)}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="unit-hotgroup">
|
||||
<div class="unit-hotgroup-id"></div>
|
||||
</div>
|
||||
<div class="unit-fuel">
|
||||
<div class="unit-fuel-level"></div>
|
||||
</div>
|
||||
<div class="unit-ammo">
|
||||
<div data-ammo-type="fox-1"></div>
|
||||
<div data-ammo-type="fox-2"></div>
|
||||
<div data-ammo-type="fox-3"></div>
|
||||
<div data-ammo-type="other"></div>
|
||||
</div>
|
||||
<div class="unit-summary">
|
||||
<div class="unit-callsign">${this.getData().unitName}</div>
|
||||
<div class="unit-heading"></div>
|
||||
<div class="unit-altitude"></div>
|
||||
</div>
|
||||
</div>`,
|
||||
html: ` <svg class="unit" data-coalition="${this.getMissionData().coalition}" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle class="unit-spotlight" />
|
||||
<rect class="unit-vvi" style="transform:rotate( calc( var( --unit-marker-air-vvi-rotation-offset ) + 090deg ) ); height:calc( ( var( --unit-marker-air-height ) / 2 ) + 25px );" />
|
||||
<rect class="unit-hotgroup"></rect>
|
||||
<text x="74" y="27" class="unit-hotgroup-id">3</text>
|
||||
<rect class="unit-selected-border" />
|
||||
<rect class="unit-marker" />
|
||||
<text x="50%" y="54px" class="unit-short-label">${aircraftDatabase.getShortLabelByName(this.getData().name)}</text>
|
||||
<rect class="unit-fuel" />
|
||||
<rect class="unit-fuel-level" />
|
||||
<circle class="unit-ammo unit-ammo-fox-1" />
|
||||
<circle class="unit-ammo unit-ammo-fox-2" />
|
||||
<circle class="unit-ammo unit-ammo-fox-3" />
|
||||
<circle class="unit-ammo unit-ammo-other" />
|
||||
<g class="unit-summary">
|
||||
<text class="unit-callsign" x="1" y="46">${this.getData().unitName}</text>
|
||||
<text class="unit-heading" x="20" y="60"></text>
|
||||
<text class="unit-altitude" x="46" y="60"></text>
|
||||
</g>
|
||||
</svg>`,
|
||||
className: 'ol-unit-marker',
|
||||
iconAnchor: [30, 30]
|
||||
iconAnchor: [60, 60]
|
||||
});
|
||||
this.setIcon(icon);
|
||||
|
||||
@@ -88,19 +79,19 @@ export class Unit extends Marker {
|
||||
this.#targetsPolylines = [];
|
||||
}
|
||||
|
||||
update(response: UnitData) {
|
||||
setData(data: UnitData) {
|
||||
var updateMarker = true;
|
||||
//if (this.#data.flightData.latitude != response.flightData.latitude ||
|
||||
// this.#data.flightData.longitude != response.flightData.longitude ||
|
||||
// this.#data.alive != response.alive ||
|
||||
//if (this.getFlightData().latitude != response.flightData.latitude ||
|
||||
// this.getFlightData().longitude != response.flightData.longitude ||
|
||||
// this.getData().alive != response.alive ||
|
||||
// this.#forceUpdate ||
|
||||
// !getMap().hasLayer(this.#marker))
|
||||
// updateMarker = true;
|
||||
|
||||
this.#data = response;
|
||||
this.#data = data;
|
||||
|
||||
/* Dead units can't be selected */
|
||||
this.setSelected(this.getSelected() && this.#data.alive)
|
||||
this.setSelected(this.getSelected() && this.getData().alive)
|
||||
|
||||
if (updateMarker)
|
||||
this.#updateMarker();
|
||||
@@ -114,9 +105,33 @@ export class Unit extends Marker {
|
||||
this.#clearPath();
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.#data;
|
||||
}
|
||||
|
||||
getFlightData() {
|
||||
return this.getData().flightData;
|
||||
}
|
||||
|
||||
getTaskData() {
|
||||
return this.getData().taskData;
|
||||
}
|
||||
|
||||
getMissionData() {
|
||||
return this.getData().missionData;
|
||||
}
|
||||
|
||||
getFormationData() {
|
||||
return this.getData().formationData;
|
||||
}
|
||||
|
||||
getOptionsData() {
|
||||
return this.getData().optionsData;
|
||||
}
|
||||
|
||||
setSelected(selected: boolean) {
|
||||
/* Only alive units can be selected. Some units are not selectable (weapons) */
|
||||
if ((this.#data.alive || !selected) && this.#selectable && this.#selected != selected) {
|
||||
if ((this.getData().alive || !selected) && this.#selectable && this.#selected != selected) {
|
||||
this.#selected = selected;
|
||||
this.getElement()?.querySelector(".unit")?.setAttribute("data-is-selected", String(this.getSelected()));
|
||||
document.dispatchEvent(new CustomEvent("unitSelection", { detail: this }));
|
||||
@@ -137,8 +152,8 @@ export class Unit extends Marker {
|
||||
|
||||
addDestination(latlng: L.LatLng) {
|
||||
var path: any = {};
|
||||
if (this.#data.taskData.activePath != null) {
|
||||
path = this.#data.taskData.activePath;
|
||||
if (this.getTaskData().activePath != null) {
|
||||
path = this.getTaskData().activePath;
|
||||
path[(Object.keys(path).length + 1).toString()] = latlng;
|
||||
}
|
||||
else {
|
||||
@@ -148,7 +163,7 @@ export class Unit extends Marker {
|
||||
}
|
||||
|
||||
clearDestinations() {
|
||||
this.#data.taskData.activePath = null;
|
||||
this.getTaskData().activePath = null;
|
||||
}
|
||||
|
||||
getHidden() {
|
||||
@@ -156,7 +171,7 @@ export class Unit extends Marker {
|
||||
}
|
||||
|
||||
getLeader() {
|
||||
return getUnitsManager().getUnitByID(this.#data.formationData.leaderID);
|
||||
return getUnitsManager().getUnitByID(this.getFormationData().leaderID);
|
||||
}
|
||||
|
||||
getFormation() {
|
||||
@@ -165,8 +180,8 @@ export class Unit extends Marker {
|
||||
|
||||
getWingmen() {
|
||||
var wingmen: Unit[] = [];
|
||||
if (this.#data.formationData.wingmenIDs != null) {
|
||||
for (let ID of this.#data.formationData.wingmenIDs) {
|
||||
if (this.getFormationData().wingmenIDs != null) {
|
||||
for (let ID of this.getFormationData().wingmenIDs) {
|
||||
var unit = getUnitsManager().getUnitByID(ID)
|
||||
if (unit)
|
||||
wingmen.push(unit);
|
||||
@@ -179,30 +194,6 @@ export class Unit extends Marker {
|
||||
this.#forceUpdate = true;
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.#data;
|
||||
}
|
||||
|
||||
getFlightData() {
|
||||
return this.#data.flightData;
|
||||
}
|
||||
|
||||
getTaskData() {
|
||||
return this.#data.taskData;
|
||||
}
|
||||
|
||||
getMissionData() {
|
||||
return this.#data.missionData;
|
||||
}
|
||||
|
||||
getFormationData() {
|
||||
return this.#data.formationData;
|
||||
}
|
||||
|
||||
getOptionsData() {
|
||||
return this.#data.optionsData;
|
||||
}
|
||||
|
||||
attackUnit(targetID: number) {
|
||||
/* Call DCS attackUnit function */
|
||||
if (this.ID != targetID) {
|
||||
@@ -241,20 +232,14 @@ export class Unit extends Marker {
|
||||
setReactionToThreat(this.ID, reactionToThreat);
|
||||
}
|
||||
|
||||
delete() {
|
||||
deleteUnit(this.ID);
|
||||
}
|
||||
|
||||
/*
|
||||
setformation(formation)
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
setLeader(isLeader: boolean, wingmenIDs: number[] = []) {
|
||||
setLeader(this.ID, isLeader, wingmenIDs);
|
||||
}
|
||||
|
||||
delete() {
|
||||
deleteUnit(this.ID);
|
||||
}
|
||||
|
||||
#onClick(e: any) {
|
||||
this.#timer = setTimeout(() => {
|
||||
if (!this.#preventClick) {
|
||||
@@ -279,8 +264,7 @@ export class Unit extends Marker {
|
||||
'Attack',
|
||||
'Follow'
|
||||
]
|
||||
|
||||
getMap().showContextMenu(e.originalEvent, "Action: " + this.#data.unitName, options, (action: string) => this.#executeAction(action));
|
||||
getMap().showContextMenu(e.originalEvent, "Action: " + this.getData().unitName, options.map((option: string) => {return {tooltip: option, src: "", callback: (action: string) => this.#executeAction(action)}}));
|
||||
}
|
||||
|
||||
#executeAction(action: string) {
|
||||
@@ -300,49 +284,48 @@ export class Unit extends Marker {
|
||||
getMap().removeLayer(this);
|
||||
}
|
||||
else {
|
||||
this.setLatLng(new LatLng(this.#data.flightData.latitude, this.#data.flightData.longitude));
|
||||
this.setLatLng(new LatLng(this.getFlightData().latitude, this.getFlightData().longitude));
|
||||
var element = this.getElement();
|
||||
if (element != null)
|
||||
{
|
||||
element.querySelector(".unit-vvi-heading")?.setAttribute("style",`transform: rotate(${rad2deg(this.getFlightData().heading)}deg); width: ${15 + this.getFlightData().speed / 5}px`);
|
||||
if (element != null) {
|
||||
element.querySelector(".unit-vvi")?.setAttribute("style", `transform:rotate( calc( var( --unit-marker-air-vvi-rotation-offset ) + ${rad2deg(this.getFlightData().heading)}deg ) ); height:calc( ( var( --unit-marker-air-height ) / 2 ) + ${this.getFlightData().speed / 5}px );`);
|
||||
element.querySelector(".unit")?.setAttribute("data-fuel-level", "20");
|
||||
element.querySelector(".unit")?.setAttribute("data-has-fox-1", "true");
|
||||
|
||||
|
||||
var unitHeadingDiv = element.querySelector(".unit-heading");
|
||||
if (unitHeadingDiv != null)
|
||||
unitHeadingDiv.innerHTML = String(Math.floor(rad2deg(this.getFlightData().heading)));
|
||||
|
||||
|
||||
var unitAltitudeDiv = element.querySelector(".unit-altitude");
|
||||
if (unitAltitudeDiv != null)
|
||||
unitAltitudeDiv.innerHTML = String(Math.floor(this.getFlightData().altitude / 0.3048 / 1000));
|
||||
}
|
||||
var pos = getMap().latLngToLayerPoint(this.getLatLng()).round();
|
||||
this.setZIndexOffset(Math.floor(this.getFlightData().altitude) - pos.y);
|
||||
this.setZIndexOffset(Math.floor(this.getFlightData().altitude) - pos.y);
|
||||
}
|
||||
|
||||
this.#forceUpdate = false;
|
||||
}
|
||||
|
||||
#drawPath() {
|
||||
if (this.#data.taskData.activePath != null) {
|
||||
if (this.getTaskData().activePath != null) {
|
||||
var points = [];
|
||||
points.push(new LatLng(this.#data.flightData.latitude, this.#data.flightData.longitude));
|
||||
points.push(new LatLng(this.getFlightData().latitude, this.getFlightData().longitude));
|
||||
|
||||
/* Add markers if missing */
|
||||
while (this.#pathMarkers.length < Object.keys(this.#data.taskData.activePath).length) {
|
||||
while (this.#pathMarkers.length < Object.keys(this.getTaskData().activePath).length) {
|
||||
var marker = new Marker([0, 0], { icon: pathIcon }).addTo(getMap());
|
||||
this.#pathMarkers.push(marker);
|
||||
}
|
||||
|
||||
/* Remove markers if too many */
|
||||
while (this.#pathMarkers.length > Object.keys(this.#data.taskData.activePath).length) {
|
||||
while (this.#pathMarkers.length > Object.keys(this.getTaskData().activePath).length) {
|
||||
getMap().removeLayer(this.#pathMarkers[this.#pathMarkers.length - 1]);
|
||||
this.#pathMarkers.splice(this.#pathMarkers.length - 1, 1)
|
||||
}
|
||||
|
||||
/* Update the position of the existing markers (to avoid creating markers uselessly) */
|
||||
for (let WP in this.#data.taskData.activePath) {
|
||||
var destination = this.#data.taskData.activePath[WP];
|
||||
for (let WP in this.getTaskData().activePath) {
|
||||
var destination = this.getTaskData().activePath[WP];
|
||||
this.#pathMarkers[parseInt(WP) - 1].setLatLng([destination.lat, destination.lng]);
|
||||
points.push(new LatLng(destination.lat, destination.lng));
|
||||
this.#pathPolyline.setLatLngs(points);
|
||||
@@ -364,7 +347,7 @@ export class Unit extends Marker {
|
||||
var targetData = this.getMissionData().targets[typeIndex][index];
|
||||
var target = getUnitsManager().getUnitByID(targetData.object["id_"])
|
||||
if (target != null) {
|
||||
var startLatLng = new LatLng(this.#data.flightData.latitude, this.#data.flightData.longitude)
|
||||
var startLatLng = new LatLng(this.getFlightData().latitude, this.getFlightData().longitude)
|
||||
var endLatLng = new LatLng(target.getFlightData().latitude, target.getFlightData().longitude)
|
||||
|
||||
var color;
|
||||
@@ -393,7 +376,7 @@ export class Unit extends Marker {
|
||||
|
||||
export class AirUnit extends Unit {
|
||||
getHidden() {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
export interface LoadoutItemBlueprint {
|
||||
name: string;
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
export interface LoadoutBlueprint {
|
||||
fuel: number;
|
||||
items: LoadoutItemBlueprint[];
|
||||
roles: string[];
|
||||
code: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface UnitBlueprint {
|
||||
name: string;
|
||||
label: string;
|
||||
shortLabel: string;
|
||||
loadouts: LoadoutBlueprint[];
|
||||
}
|
||||
|
||||
export class UnitDatabase {
|
||||
units: {[key: string]: UnitBlueprint} = {};
|
||||
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
import * as L from 'leaflet'
|
||||
import { getMap } from '..'
|
||||
import { rad2deg } from '../other/utils'
|
||||
|
||||
export interface MarkerOptions {
|
||||
unitName: string,
|
||||
name: string,
|
||||
human: boolean,
|
||||
coalition: string,
|
||||
AI: boolean
|
||||
}
|
||||
|
||||
export interface MarkerData {
|
||||
heading: number,
|
||||
speed: number,
|
||||
altitude: number,
|
||||
alive: boolean
|
||||
}
|
||||
|
||||
export class UnitMarker extends L.Marker {
|
||||
#options: MarkerOptions;
|
||||
#data: MarkerData;
|
||||
#selected: boolean = false
|
||||
|
||||
constructor(options: MarkerOptions) {
|
||||
super(new L.LatLng(0, 0), { riseOnHover: true });
|
||||
|
||||
this.#options = options;
|
||||
this.#data = {heading: 0, speed: 0, altitude: 0, alive: true};
|
||||
|
||||
var icon = new L.DivIcon({
|
||||
html: `<div class="unit"
|
||||
data-coalition=${this.#options.coalition}
|
||||
data-pilot=${this.#options.human? "human": "ai"}>
|
||||
<div class="unit-spotlight">
|
||||
<div class="unit-selected-border">
|
||||
<div class="unit-vvi">
|
||||
<div class="unit-vvi-heading"></div>
|
||||
</div>
|
||||
<div class="unit-id">${this.#options.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="unit-hotgroup">
|
||||
<div class="unit-hotgroup-id"></div>
|
||||
</div>
|
||||
<div class="unit-fuel">
|
||||
<div class="unit-fuel-level"></div>
|
||||
</div>
|
||||
<div class="unit-ammo">
|
||||
<div data-ammo-type="fox-1"></div>
|
||||
<div data-ammo-type="fox-2"></div>
|
||||
<div data-ammo-type="fox-3"></div>
|
||||
<div data-ammo-type="other"></div>
|
||||
</div>
|
||||
<div class="unit-summary">
|
||||
<div class="unit-callsign">${this.#options.unitName}</div>
|
||||
<div class="unit-heading"></div>
|
||||
<div class="unit-altitude"></div>
|
||||
</div>
|
||||
</div>`,
|
||||
className: 'ol-unit-marker',
|
||||
iconAnchor: [30, 30]
|
||||
});
|
||||
this.setIcon(icon);
|
||||
}
|
||||
|
||||
onAdd(map: L.Map): this {
|
||||
super.onAdd(map);
|
||||
this.addEventListener('mouseover', function (e: any) { e.target?.setHovered(true); });
|
||||
this.addEventListener('mouseout', function (e: any) { e.target?.setHovered(false); });
|
||||
return this
|
||||
}
|
||||
|
||||
draw(data: MarkerData) {
|
||||
this.#data;
|
||||
|
||||
var element = this.getElement();
|
||||
if (element != null)
|
||||
{
|
||||
element.querySelector(".unit-vvi-heading")?.setAttribute("style",`transform: rotate(${rad2deg(data.heading)}deg); width: ${15 + data.speed / 5}px`);
|
||||
element.querySelector(".unit")?.setAttribute("data-fuel-level", "20");
|
||||
element.querySelector(".unit")?.setAttribute("data-has-fox-1", "true");
|
||||
|
||||
var unitHeadingDiv = element.querySelector(".unit-heading");
|
||||
if (unitHeadingDiv != null)
|
||||
unitHeadingDiv.innerHTML = String(Math.floor(rad2deg(data.heading)));
|
||||
|
||||
var unitAltitudeDiv = element.querySelector(".unit-altitude");
|
||||
if (unitAltitudeDiv != null)
|
||||
unitAltitudeDiv.innerHTML = String(Math.floor(data.altitude / 0.3048 / 1000));
|
||||
}
|
||||
var pos = getMap().latLngToLayerPoint(this.getLatLng()).round();
|
||||
this.setZIndexOffset(Math.floor(data.altitude) - pos.y);
|
||||
}
|
||||
|
||||
setSelected(selected: boolean) {
|
||||
this.#selected = selected;
|
||||
this.getElement()?.querySelector(".unit")?.setAttribute("data-is-selected", String(this.getSelected()));
|
||||
}
|
||||
|
||||
getSelected() {
|
||||
return this.#selected;
|
||||
}
|
||||
|
||||
setHovered(hovered: boolean) {
|
||||
this.getElement()?.querySelector("#icon")?.classList.toggle("ol-unit-marker-hovered", hovered && this.#data.alive);
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.#data;
|
||||
}
|
||||
|
||||
getOptions() {
|
||||
return this.#options;
|
||||
}
|
||||
}
|
||||
|
||||
export class AirUnitMarker extends UnitMarker {
|
||||
|
||||
}
|
||||
|
||||
export class AircraftMarker extends AirUnitMarker {
|
||||
|
||||
}
|
||||
|
||||
export class HelicopterMarker extends AirUnitMarker {
|
||||
|
||||
}
|
||||
|
||||
export class GroundUnitMarker extends UnitMarker {
|
||||
|
||||
}
|
||||
|
||||
export class NavyUnitMarker extends UnitMarker {
|
||||
|
||||
}
|
||||
|
||||
export class WeaponMarker extends UnitMarker {
|
||||
|
||||
}
|
||||
|
||||
export class BombMarker extends WeaponMarker {
|
||||
|
||||
}
|
||||
|
||||
export class MissileMarker extends WeaponMarker {
|
||||
|
||||
}
|
||||
@@ -17,37 +17,10 @@ export class UnitsManager {
|
||||
document.addEventListener('keydown', (event) => this.#onKeyDown(event));
|
||||
}
|
||||
|
||||
#updateUnitControlPanel() {
|
||||
/* Update the unit control panel */
|
||||
if (this.getSelectedUnits().length > 0) {
|
||||
getUnitControlPanel().show();
|
||||
getUnitControlPanel().update(this.getSelectedLeaders().concat(this.getSelectedSingletons()));
|
||||
}
|
||||
else {
|
||||
getUnitControlPanel().hide();
|
||||
}
|
||||
}
|
||||
|
||||
#onKeyDown(event: KeyboardEvent)
|
||||
{
|
||||
if (event.key === "Delete")
|
||||
{
|
||||
this.selectedUnitsDelete();
|
||||
}
|
||||
}
|
||||
|
||||
getUnits() {
|
||||
return this.#units;
|
||||
}
|
||||
|
||||
addUnit(ID: number, data: UnitData) {
|
||||
/* The name of the unit category is exactly the same as the constructor name */
|
||||
var constructor = Unit.getConstructor(data.category);
|
||||
if (constructor != undefined) {
|
||||
this.#units[ID] = new constructor(ID, data);
|
||||
}
|
||||
}
|
||||
|
||||
getUnitByID(ID: number) {
|
||||
if (ID in this.#units)
|
||||
return this.#units[ID];
|
||||
@@ -55,21 +28,17 @@ export class UnitsManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
removeUnit(ID: number) {
|
||||
|
||||
}
|
||||
|
||||
deselectAllUnits() {
|
||||
for (let ID in this.#units) {
|
||||
this.#units[ID].setSelected(false);
|
||||
addUnit(ID: number, data: UnitData) {
|
||||
/* The name of the unit category is exactly the same as the constructor name */
|
||||
var constructor = Unit.getConstructor(data.category);
|
||||
if (constructor != undefined) {
|
||||
this.#units[ID] = new constructor(ID, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
removeUnit(ID: number) {
|
||||
|
||||
selectUnit(ID: number, deselectAllUnits: boolean = true)
|
||||
{
|
||||
if (deselectAllUnits)
|
||||
this.deselectAllUnits();
|
||||
this.#units[ID]?.setSelected(true);
|
||||
}
|
||||
|
||||
update(data: ServerData) {
|
||||
@@ -78,16 +47,16 @@ export class UnitsManager {
|
||||
if (!(ID in this.#units)) {
|
||||
this.addUnit(parseInt(ID), data.units[ID]);
|
||||
}
|
||||
this.#units[parseInt(ID)].update(data.units[ID]);
|
||||
this.#units[parseInt(ID)].setData(data.units[ID]);
|
||||
}
|
||||
|
||||
/* Update the unit info panel */
|
||||
if (this.getSelectedUnits().length == 1) {
|
||||
getUnitInfoPanel().show();
|
||||
getUnitInfoPanel().update(this.getSelectedUnits()[0]);
|
||||
getUnitInfoPanel()?.show();
|
||||
getUnitInfoPanel()?.update(this.getSelectedUnits()[0]);
|
||||
}
|
||||
else {
|
||||
getUnitInfoPanel().hide();
|
||||
getUnitInfoPanel()?.hide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +66,13 @@ export class UnitsManager {
|
||||
}
|
||||
}
|
||||
|
||||
selectUnit(ID: number, deselectAllUnits: boolean = true)
|
||||
{
|
||||
if (deselectAllUnits)
|
||||
this.deselectAllUnits();
|
||||
this.#units[ID]?.setSelected(true);
|
||||
}
|
||||
|
||||
onUnitSelection() {
|
||||
if (this.getSelectedUnits().length > 0) {
|
||||
getMap().setState("MOVE_UNIT");
|
||||
@@ -135,6 +111,12 @@ export class UnitsManager {
|
||||
return selectedUnits;
|
||||
}
|
||||
|
||||
deselectAllUnits() {
|
||||
for (let ID in this.#units) {
|
||||
this.#units[ID].setSelected(false);
|
||||
}
|
||||
}
|
||||
|
||||
getSelectedLeaders() {
|
||||
var leaders: Unit[] = [];
|
||||
for (let idx in this.getSelectedUnits())
|
||||
@@ -163,14 +145,34 @@ export class UnitsManager {
|
||||
return singletons;
|
||||
}
|
||||
|
||||
getSelectedUnitsType () {
|
||||
return this.getSelectedUnits().map((unit: Unit) => {
|
||||
return unit.constructor.name
|
||||
}).reduce((a: any, b: any) => {
|
||||
return a == b? a: undefined
|
||||
});
|
||||
};
|
||||
|
||||
getSelectedUnitsTargetSpeed () {
|
||||
return this.getSelectedUnits().map((unit: Unit) => {
|
||||
return unit.getTaskData().targetSpeed
|
||||
}).reduce((a: any, b: any) => {
|
||||
return a == b? a: undefined
|
||||
});
|
||||
};
|
||||
|
||||
getSelectedUnitsTargetAltitude () {
|
||||
return this.getSelectedUnits().map((unit: Unit) => {
|
||||
return unit.getTaskData().targetAltitude
|
||||
}).reduce((a: any, b: any) => {
|
||||
return a == b? a: undefined
|
||||
});
|
||||
};
|
||||
|
||||
selectedUnitsAddDestination(latlng: L.LatLng) {
|
||||
var selectedUnits = this.getSelectedUnits();
|
||||
for (let idx in selectedUnits) {
|
||||
var commandedUnit = selectedUnits[idx];
|
||||
//if (selectedUnits[idx].wingman)
|
||||
//{
|
||||
// commandedUnit = this.getLeader(selectedUnits[idx].ID);
|
||||
//}
|
||||
commandedUnit.addDestination(latlng);
|
||||
}
|
||||
}
|
||||
@@ -179,10 +181,6 @@ export class UnitsManager {
|
||||
var selectedUnits = this.getSelectedUnits();
|
||||
for (let idx in selectedUnits) {
|
||||
var commandedUnit = selectedUnits[idx];
|
||||
//if (selectedUnits[idx].wingman)
|
||||
//{
|
||||
// commandedUnit = this.getLeader(selectedUnits[idx].ID);
|
||||
//}
|
||||
commandedUnit.clearDestinations();
|
||||
}
|
||||
}
|
||||
@@ -327,6 +325,15 @@ export class UnitsManager {
|
||||
setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail
|
||||
}
|
||||
|
||||
selectedUnitsDelete()
|
||||
{
|
||||
var selectedUnits = this.getSelectedUnits();
|
||||
for (let idx in selectedUnits)
|
||||
{
|
||||
selectedUnits[idx].delete();
|
||||
}
|
||||
}
|
||||
|
||||
copyUnits()
|
||||
{
|
||||
this.#copiedUnits = this.getSelectedUnits();
|
||||
@@ -341,12 +348,22 @@ export class UnitsManager {
|
||||
}
|
||||
}
|
||||
|
||||
selectedUnitsDelete()
|
||||
#updateUnitControlPanel() {
|
||||
/* Update the unit control panel */
|
||||
if (this.getSelectedUnits().length > 0) {
|
||||
getUnitControlPanel()?.show();
|
||||
getUnitControlPanel()?.update(this.getSelectedLeaders().concat(this.getSelectedSingletons()));
|
||||
}
|
||||
else {
|
||||
getUnitControlPanel()?.hide();
|
||||
}
|
||||
}
|
||||
|
||||
#onKeyDown(event: KeyboardEvent)
|
||||
{
|
||||
var selectedUnits = this.getSelectedUnits();
|
||||
for (let idx in selectedUnits)
|
||||
if (event.key === "Delete")
|
||||
{
|
||||
selectedUnits[idx].delete();
|
||||
this.selectedUnitsDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user