mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Completed frontend controls
This commit is contained in:
22
client/src/@types/unit.d.ts
vendored
22
client/src/@types/unit.d.ts
vendored
@@ -37,9 +37,13 @@ interface TaskData {
|
||||
currentTask: string;
|
||||
activePath: any;
|
||||
targetSpeed: number;
|
||||
targetSpeedType: string;
|
||||
targetAltitude: number;
|
||||
targetAltitudeType: string;
|
||||
isTanker: boolean;
|
||||
isAWACS: boolean;
|
||||
onOff: boolean;
|
||||
followRoads: boolean;
|
||||
}
|
||||
|
||||
interface OptionsData {
|
||||
@@ -51,15 +55,6 @@ interface OptionsData {
|
||||
generalSettings: GeneralSettings;
|
||||
}
|
||||
|
||||
interface UnitData {
|
||||
baseData: BaseData;
|
||||
flightData: FlightData;
|
||||
missionData: MissionData;
|
||||
formationData: FormationData;
|
||||
taskData: TaskData;
|
||||
optionsData: OptionsData;
|
||||
}
|
||||
|
||||
interface TACAN {
|
||||
isOn: boolean;
|
||||
channel: number;
|
||||
@@ -91,4 +86,13 @@ interface UnitIconOptions {
|
||||
showAmmo: boolean,
|
||||
showSummary: boolean,
|
||||
rotateToHeading: boolean
|
||||
}
|
||||
|
||||
interface UnitData {
|
||||
baseData: BaseData;
|
||||
flightData: FlightData;
|
||||
missionData: MissionData;
|
||||
formationData: FormationData;
|
||||
taskData: TaskData;
|
||||
optionsData: OptionsData;
|
||||
}
|
||||
102
client/src/constants/constants.ts
Normal file
102
client/src/constants/constants.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { LatLng, LatLngBounds, TileLayer, tileLayer } from "leaflet";
|
||||
|
||||
export const ROEs: string[] = ["Hold", "Return", "Designated", "Free"];
|
||||
export const reactionsToThreat: string[] = ["None", "Manoeuvre", "Passive", "Evade"];
|
||||
export const emissionsCountermeasures: string[] = ["Silent", "Attack", "Defend", "Free"];
|
||||
|
||||
export const ROEDescriptions: string[] = ["Hold (Never fire)", "Return (Only fire if fired upon)", "Designated (Attack the designated target only)", "Free (Attack anyone)"];
|
||||
export const reactionsToThreatDescriptions: string[] = ["None (No reaction)", "Manoeuvre (no countermeasures)", "Passive (Countermeasures only, no manoeuvre)", "Evade (Countermeasures and manoeuvers)"];
|
||||
export const emissionsCountermeasuresDescriptions: string[] = ["Silent (Radar OFF, no ECM)", "Attack (Radar only for targeting, ECM only if locked)", "Defend (Radar for searching, ECM if locked)", "Always on (Radar and ECM always on)"];
|
||||
|
||||
export const minSpeedValues: { [key: string]: number } = { Aircraft: 100, Helicopter: 0, NavyUnit: 0, GroundUnit: 0 };
|
||||
export const maxSpeedValues: { [key: string]: number } = { Aircraft: 800, Helicopter: 300, NavyUnit: 60, GroundUnit: 60 };
|
||||
export const speedIncrements: { [key: string]: number } = { Aircraft: 25, Helicopter: 10, NavyUnit: 5, GroundUnit: 5 };
|
||||
export const minAltitudeValues: { [key: string]: number } = { Aircraft: 0, Helicopter: 0 };
|
||||
export const maxAltitudeValues: { [key: string]: number } = { Aircraft: 50000, Helicopter: 10000 };
|
||||
export const altitudeIncrements: { [key: string]: number } = { Aircraft: 500, Helicopter: 100 };
|
||||
|
||||
export const minimapBoundaries = [
|
||||
[ // NTTR
|
||||
new LatLng(39.7982463, -119.985425),
|
||||
new LatLng(34.4037128, -119.7806729),
|
||||
new LatLng(34.3483316, -112.4529351),
|
||||
new LatLng(39.7372411, -112.1130805),
|
||||
new LatLng(39.7982463, -119.985425)
|
||||
],
|
||||
[ // Syria
|
||||
new LatLng(37.3630556, 29.2686111),
|
||||
new LatLng(31.8472222, 29.8975),
|
||||
new LatLng(32.1358333, 42.1502778),
|
||||
new LatLng(37.7177778, 42.3716667),
|
||||
new LatLng(37.3630556, 29.2686111)
|
||||
],
|
||||
[ // Caucasus
|
||||
new LatLng(39.6170191, 27.634935),
|
||||
new LatLng(38.8735863, 47.1423108),
|
||||
new LatLng(47.3907982, 49.3101946),
|
||||
new LatLng(48.3955879, 26.7753625),
|
||||
new LatLng(39.6170191, 27.634935)
|
||||
],
|
||||
[ // Persian Gulf
|
||||
new LatLng(32.9355285, 46.5623682),
|
||||
new LatLng(21.729393, 47.572675),
|
||||
new LatLng(21.8501348, 63.9734737),
|
||||
new LatLng(33.131584, 64.7313594),
|
||||
new LatLng(32.9355285, 46.5623682)
|
||||
],
|
||||
[ // Marianas
|
||||
new LatLng(22.09, 135.0572222),
|
||||
new LatLng(10.5777778, 135.7477778),
|
||||
new LatLng(10.7725, 149.3918333),
|
||||
new LatLng(22.5127778, 149.5427778),
|
||||
new LatLng(22.09, 135.0572222)
|
||||
]
|
||||
];
|
||||
|
||||
export const mapBounds = {
|
||||
"Syria": { bounds: new LatLngBounds([31.8472222, 29.8975], [37.7177778, 42.3716667]), zoom: 5 },
|
||||
"MarianaIslands": { 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]), zoom: 5 },
|
||||
"PersianGulf": { bounds: new LatLngBounds([21.729393, 47.572675], [33.131584, 64.7313594]), zoom: 5 },
|
||||
"Caucasus": { bounds: new LatLngBounds([39.6170191, 27.634935], [47.3907982, 49.3101946]), zoom: 4 },
|
||||
// TODO "Falklands"
|
||||
}
|
||||
|
||||
export const layers = {
|
||||
"ArcGIS Satellite": {
|
||||
urlTemplate: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
|
||||
maxZoom: 20,
|
||||
minZoom: 1,
|
||||
attribution: "Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
|
||||
},
|
||||
"USGS Topo": {
|
||||
urlTemplate: 'https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}',
|
||||
minZoom: 1,
|
||||
maxZoom: 20,
|
||||
attribution: 'Tiles courtesy of the <a href="https://usgs.gov/">U.S. Geological Survey</a>'
|
||||
},
|
||||
"OpenStreetMap Mapnik": {
|
||||
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
minZoom: 1,
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
},
|
||||
"OPENVKarte": {
|
||||
urlTemplate: 'https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png',
|
||||
minZoom: 1,
|
||||
maxZoom: 18,
|
||||
attribution: 'Map <a href="https://memomaps.de/">memomaps.de</a> <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, map data © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
},
|
||||
"Esri.DeLorme": {
|
||||
urlTemplate: 'https://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer/tile/{z}/{y}/{x}',
|
||||
minZoom: 1,
|
||||
maxZoom: 11,
|
||||
attribution: 'Tiles © Esri — Copyright: ©2012 DeLorme',
|
||||
},
|
||||
"CyclOSM": {
|
||||
urlTemplate: 'https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png',
|
||||
minZoom: 1,
|
||||
maxZoom: 20,
|
||||
attribution: '<a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> | Map data: © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
export class Control {
|
||||
#container: HTMLElement | null;
|
||||
expectedValue: any = null;
|
||||
|
||||
constructor(ID: string) {
|
||||
this.#container = document.getElementById(ID);
|
||||
@@ -18,4 +19,16 @@ export class Control {
|
||||
getContainer() {
|
||||
return this.#container;
|
||||
}
|
||||
|
||||
setExpectedValue(expectedValue: any) {
|
||||
this.expectedValue = expectedValue;
|
||||
}
|
||||
|
||||
resetExpectedValue() {
|
||||
this.expectedValue = null;
|
||||
}
|
||||
|
||||
checkExpectedValue(value: any) {
|
||||
return this.expectedValue === null || value === this.expectedValue;
|
||||
}
|
||||
}
|
||||
@@ -19,14 +19,12 @@ export class Dropdown {
|
||||
}
|
||||
|
||||
this.#value.addEventListener("click", (ev) => {
|
||||
this.#element.classList.toggle("is-open");
|
||||
this.#options.classList.toggle("scrollbar-visible", this.#options.scrollHeight > this.#options.clientHeight);
|
||||
this.#clip();
|
||||
this.#toggle();
|
||||
});
|
||||
|
||||
document.addEventListener("click", (ev) => {
|
||||
if (!(this.#value.contains(ev.target as Node) || this.#options.contains(ev.target as Node) || this.#element.contains(ev.target as Node))) {
|
||||
this.#element.classList.remove("is-open");
|
||||
this.#close();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -46,12 +44,7 @@ export class Dropdown {
|
||||
|
||||
button.addEventListener("click", (e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
this.#value = document.createElement("div");
|
||||
this.#value.classList.add("ol-ellipsed");
|
||||
this.#value.innerText = option;
|
||||
this.#close();
|
||||
this.#callback(option, e);
|
||||
this.#index = idx;
|
||||
this.selectValue(idx);
|
||||
});
|
||||
return div;
|
||||
}));
|
||||
@@ -113,6 +106,8 @@ export class Dropdown {
|
||||
|
||||
#open() {
|
||||
this.#element.classList.add("is-open");
|
||||
this.#options.classList.toggle("scrollbar-visible", this.#options.scrollHeight > this.#options.clientHeight);
|
||||
this.#clip();
|
||||
}
|
||||
|
||||
#toggle() {
|
||||
|
||||
@@ -5,6 +5,8 @@ import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
import { Dropdown } from "./dropdown";
|
||||
import { Switch } from "./switch";
|
||||
import { Slider } from "./slider";
|
||||
|
||||
export interface SpawnOptions {
|
||||
role: string;
|
||||
@@ -13,24 +15,29 @@ export interface SpawnOptions {
|
||||
coalition: string;
|
||||
loadout: string | null;
|
||||
airbaseName: string | null;
|
||||
altitude: number | null;
|
||||
}
|
||||
|
||||
export class MapContextMenu extends ContextMenu {
|
||||
#coalitionSwitch: Switch;
|
||||
#aircraftRoleDropdown: Dropdown;
|
||||
#aircraftTypeDropdown: Dropdown;
|
||||
#aircraftLoadoutDropdown: Dropdown;
|
||||
#aircrafSpawnAltitudeSlider: Slider;
|
||||
#groundUnitRoleDropdown: Dropdown;
|
||||
#groundUnitTypeDropdown: Dropdown;
|
||||
#spawnOptions: SpawnOptions = { role: "", type: "", latlng: new LatLng(0, 0), loadout: null, coalition: "blue", airbaseName: null };
|
||||
|
||||
#spawnOptions: SpawnOptions = { role: "", type: "", latlng: new LatLng(0, 0), loadout: null, coalition: "blue", airbaseName: null, altitude: 20000 };
|
||||
|
||||
constructor(id: string) {
|
||||
super(id);
|
||||
this.getContainer()?.querySelector("#context-menu-switch")?.addEventListener('click', (e) => this.#onToggleLeftClick(e));
|
||||
this.getContainer()?.querySelector("#context-menu-switch")?.addEventListener('contextmenu', (e) => this.#onToggleRightClick(e));
|
||||
|
||||
this.#coalitionSwitch = new Switch("coalition-switch", this.#onSwitchClick);
|
||||
this.#coalitionSwitch.setValue(false);
|
||||
this.#coalitionSwitch.getContainer()?.addEventListener("contextmenu", (e) => this.#onSwitchRightClick(e));
|
||||
this.#aircraftRoleDropdown = new Dropdown("aircraft-role-options", (role: string) => this.#setAircraftRole(role));
|
||||
this.#aircraftTypeDropdown = new Dropdown("aircraft-type-options", (type: string) => this.#setAircraftType(type));
|
||||
this.#aircraftLoadoutDropdown = new Dropdown("loadout-options", (loadout: string) => this.#setAircraftLoadout(loadout));
|
||||
this.#aircrafSpawnAltitudeSlider = new Slider("aircraft-spawn-altitude-slider", 0, 50000, "ft", (value: number) => {this.#spawnOptions.altitude = value;});
|
||||
this.#groundUnitRoleDropdown = new Dropdown("ground-unit-role-options", (role: string) => this.#setGroundUnitRole(role));
|
||||
this.#groundUnitTypeDropdown = new Dropdown("ground-unit-type-options", (type: string) => this.#setGroundUnitType(type));
|
||||
|
||||
@@ -61,6 +68,10 @@ export class MapContextMenu extends ContextMenu {
|
||||
spawnSmoke(e.detail.color, this.getLatLng());
|
||||
});
|
||||
|
||||
this.#aircrafSpawnAltitudeSlider.setIncrement(500);
|
||||
this.#aircrafSpawnAltitudeSlider.setValue(20000);
|
||||
this.#aircrafSpawnAltitudeSlider.setActive(true);
|
||||
|
||||
this.hide();
|
||||
}
|
||||
|
||||
@@ -102,26 +113,13 @@ export class MapContextMenu extends ContextMenu {
|
||||
this.#spawnOptions.latlng = latlng;
|
||||
}
|
||||
|
||||
#onToggleLeftClick(e: any) {
|
||||
if (this.getContainer() != null) {
|
||||
if (e.srcElement.dataset.activeCoalition == "blue")
|
||||
setActiveCoalition("neutral");
|
||||
else if (e.srcElement.dataset.activeCoalition == "neutral")
|
||||
setActiveCoalition("red");
|
||||
else
|
||||
setActiveCoalition("blue");
|
||||
}
|
||||
#onSwitchClick(value: boolean) {
|
||||
value? setActiveCoalition("red"): setActiveCoalition("blue");
|
||||
}
|
||||
|
||||
#onToggleRightClick(e: any) {
|
||||
if (this.getContainer() != null) {
|
||||
if (e.srcElement.dataset.activeCoalition == "red")
|
||||
setActiveCoalition("neutral");
|
||||
else if (e.srcElement.dataset.activeCoalition == "neutral")
|
||||
setActiveCoalition("blue");
|
||||
else
|
||||
setActiveCoalition("red");
|
||||
}
|
||||
#onSwitchRightClick(e: any) {
|
||||
this.#coalitionSwitch.setValue(undefined);
|
||||
setActiveCoalition("neutral");
|
||||
}
|
||||
|
||||
/********* Aircraft spawn menu *********/
|
||||
|
||||
@@ -2,38 +2,38 @@ import { zeroPad } from "../other/utils";
|
||||
import { Control } from "./control";
|
||||
|
||||
export class Slider extends Control {
|
||||
#callback: CallableFunction;
|
||||
#callback: CallableFunction | null = null;
|
||||
#slider: HTMLInputElement | null = null;
|
||||
#valueText: HTMLElement | null = null;
|
||||
#minValue: number;
|
||||
#maxValue: number;
|
||||
#increment: number;
|
||||
#minValue: number = 0;
|
||||
#maxValue: number = 0;
|
||||
#increment: number = 0;
|
||||
#minMaxValueDiv: HTMLElement | null = null;
|
||||
#unit: string;
|
||||
#unitOfMeasure: string;
|
||||
#dragged: boolean = false;
|
||||
#value: number = 0;
|
||||
|
||||
constructor(ID: string, minValue: number, maxValue: number, unit: string, callback: CallableFunction) {
|
||||
constructor(ID: string, minValue: number, maxValue: number, unitOfMeasure: string, callback: CallableFunction) {
|
||||
super(ID);
|
||||
this.#callback = callback;
|
||||
this.#minValue = minValue;
|
||||
this.#maxValue = maxValue;
|
||||
this.#increment = 1;
|
||||
this.#unit = unit;
|
||||
this.#callback = callback;
|
||||
this.#unitOfMeasure = unitOfMeasure;
|
||||
this.#slider = this.getContainer()?.querySelector("input") as HTMLInputElement;
|
||||
|
||||
if (this.#slider != null) {
|
||||
this.#slider.addEventListener("input", (e: any) => this.#onInput());
|
||||
this.#slider.addEventListener("input", (e: any) => this.#update());
|
||||
this.#slider.addEventListener("mousedown", (e: any) => this.#onStart());
|
||||
this.#slider.addEventListener("mouseup", (e: any) => this.#onFinalize());
|
||||
}
|
||||
|
||||
this.#valueText = this.getContainer()?.querySelector(".ol-slider-value") as HTMLElement;
|
||||
this.#minMaxValueDiv = this.getContainer()?.querySelector(".ol-slider-min-max") as HTMLElement;
|
||||
|
||||
this.setIncrement(1);
|
||||
this.setMinMax(minValue, maxValue);
|
||||
}
|
||||
|
||||
setActive(newActive: boolean) {
|
||||
if (!this.#dragged) {
|
||||
if (!this.getDragged()) {
|
||||
this.getContainer()?.classList.toggle("active", newActive);
|
||||
if (!newActive && this.#valueText != null)
|
||||
this.#valueText.innerText = "Mixed values";
|
||||
@@ -41,27 +41,31 @@ export class Slider extends Control {
|
||||
}
|
||||
|
||||
setMinMax(newMinValue: number, newMaxValue: number) {
|
||||
this.#minValue = newMinValue;
|
||||
this.#maxValue = newMaxValue;
|
||||
this.#updateMax();
|
||||
if (this.#minMaxValueDiv != null) {
|
||||
this.#minMaxValueDiv.setAttribute('data-min-value', `${this.#minValue}${this.#unit}`);
|
||||
this.#minMaxValueDiv.setAttribute('data-max-value', `${this.#maxValue}${this.#unit}`);
|
||||
if (this.#minValue != newMinValue || this.#maxValue != newMaxValue) {
|
||||
this.#minValue = newMinValue;
|
||||
this.#maxValue = newMaxValue;
|
||||
this.#updateMaxValue();
|
||||
|
||||
if (this.#minMaxValueDiv != null) {
|
||||
this.#minMaxValueDiv.setAttribute('data-min-value', `${this.#minValue}${this.#unitOfMeasure}`);
|
||||
this.#minMaxValueDiv.setAttribute('data-max-value', `${this.#maxValue}${this.#unitOfMeasure}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setIncrement(newIncrement: number) {
|
||||
this.#increment = newIncrement;
|
||||
this.#updateMax();
|
||||
if (this.#increment != newIncrement) {
|
||||
this.#increment = newIncrement;
|
||||
this.#updateMaxValue();
|
||||
}
|
||||
}
|
||||
|
||||
setValue(newValue: number) {
|
||||
// Disable value setting if the user is dragging the element
|
||||
if (!this.#dragged) {
|
||||
setValue(newValue: number, ignoreExpectedValue: boolean = true) {
|
||||
if (!this.getDragged() && (ignoreExpectedValue || this.checkExpectedValue(newValue))) {
|
||||
this.#value = newValue;
|
||||
if (this.#slider != null)
|
||||
this.#slider.value = String((newValue - this.#minValue) / (this.#maxValue - this.#minValue) * parseFloat(this.#slider.max));
|
||||
this.#onValue()
|
||||
this.#update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,45 +73,51 @@ export class Slider extends Control {
|
||||
return this.#value;
|
||||
}
|
||||
|
||||
setDragged(newDragged: boolean) {
|
||||
this.#dragged = newDragged;
|
||||
}
|
||||
|
||||
getDragged() {
|
||||
return this.#dragged;
|
||||
}
|
||||
|
||||
#updateMax() {
|
||||
#updateMaxValue() {
|
||||
var oldValue = this.getValue();
|
||||
if (this.#slider != null)
|
||||
this.#slider.max = String((this.#maxValue - this.#minValue) / this.#increment);
|
||||
this.setValue(oldValue);
|
||||
}
|
||||
|
||||
#onValue() {
|
||||
#update() {
|
||||
if (this.#valueText != null && this.#slider != null)
|
||||
{
|
||||
/* Update the text value */
|
||||
var value = this.#minValue + Math.round(parseFloat(this.#slider.value) / parseFloat(this.#slider.max) * (this.#maxValue - this.#minValue));
|
||||
var strValue = String(value);
|
||||
if (value > 1000)
|
||||
strValue = String(Math.floor(value / 1000)) + "," + zeroPad(value - Math.floor(value / 1000) * 1000, 3);
|
||||
this.#valueText.innerText = strValue + " " + this.#unit.toUpperCase();
|
||||
this.#valueText.innerText = `${strValue} ${this.#unitOfMeasure.toUpperCase()}`;
|
||||
|
||||
/* Update the position of the slider */
|
||||
var percentValue = parseFloat(this.#slider.value) / parseFloat(this.#slider.max) * 90 + 5;
|
||||
this.#slider.style.background = 'linear-gradient(to right, var(--accent-light-blue) 5%, var(--accent-light-blue) ' + percentValue + '%, var(--background-grey) ' + percentValue + '%, var(--background-grey) 100%)'
|
||||
this.#slider.style.background = `linear-gradient(to right, var(--accent-light-blue) 5%, var(--accent-light-blue) ${percentValue}%, var(--background-grey) ${percentValue}%, var(--background-grey) 100%)`
|
||||
}
|
||||
this.setActive(true);
|
||||
}
|
||||
|
||||
#onInput() {
|
||||
this.#onValue();
|
||||
}
|
||||
|
||||
#onStart() {
|
||||
this.#dragged = true;
|
||||
this.setDragged(true);
|
||||
}
|
||||
|
||||
#onFinalize() {
|
||||
this.#dragged = false;
|
||||
this.setDragged(false);
|
||||
if (this.#slider != null) {
|
||||
this.#value = this.#minValue + parseFloat(this.#slider.value) / parseFloat(this.#slider.max) * (this.#maxValue - this.#minValue);
|
||||
this.#callback(this.getValue());
|
||||
this.resetExpectedValue();
|
||||
this.setValue(this.#minValue + parseFloat(this.#slider.value) / parseFloat(this.#slider.max) * (this.#maxValue - this.#minValue));
|
||||
if (this.#callback) {
|
||||
this.#callback(this.getValue());
|
||||
this.setExpectedValue(this.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
import { Control } from "./control";
|
||||
|
||||
export class Switch extends Control {
|
||||
#value: boolean = false;
|
||||
constructor(ID: string, initialValue?: boolean) {
|
||||
#value: boolean | undefined = false;
|
||||
#callback: CallableFunction | null = null;
|
||||
|
||||
constructor(ID: string, callback: CallableFunction, initialValue?: boolean) {
|
||||
super(ID);
|
||||
this.getContainer()?.addEventListener('click', (e) => this.#onToggle());
|
||||
this.setValue(initialValue !== undefined? initialValue: true);
|
||||
|
||||
this.#callback = callback;
|
||||
|
||||
/* Add the toggle itself to the document */
|
||||
const container = this.getContainer();
|
||||
if (container != undefined){
|
||||
@@ -14,14 +18,17 @@ export class Switch extends Control {
|
||||
const height = getComputedStyle(container).height;
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("ol-switch-fill");
|
||||
el.style.setProperty("--width", width? width: "0px");
|
||||
el.style.setProperty("--height", height? height: "0px");
|
||||
el.style.setProperty("--width", width? width: "0");
|
||||
el.style.setProperty("--height", height? height: "0");
|
||||
this.getContainer()?.appendChild(el);
|
||||
}
|
||||
}
|
||||
setValue(value: boolean) {
|
||||
this.#value = value;
|
||||
this.getContainer()?.setAttribute("data-value", String(value));
|
||||
|
||||
setValue(newValue: boolean | undefined, ignoreExpectedValue: boolean = true) {
|
||||
if (ignoreExpectedValue || this.checkExpectedValue(newValue)) {
|
||||
this.#value = newValue;
|
||||
this.getContainer()?.setAttribute("data-value", String(newValue));
|
||||
}
|
||||
}
|
||||
|
||||
getValue() {
|
||||
@@ -29,6 +36,11 @@ export class Switch extends Control {
|
||||
}
|
||||
|
||||
#onToggle() {
|
||||
this.resetExpectedValue();
|
||||
this.setValue(!this.getValue());
|
||||
if (this.#callback) {
|
||||
this.#callback(this.getValue());
|
||||
this.setExpectedValue(this.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,8 +98,6 @@ function readConfig(config: any) {
|
||||
}
|
||||
|
||||
function setupEvents() {
|
||||
window.onanimationiteration = console.log;
|
||||
|
||||
/* Generic clicks */
|
||||
document.addEventListener("click", (ev) => {
|
||||
if (ev instanceof MouseEvent && ev.target instanceof HTMLElement) {
|
||||
|
||||
@@ -6,7 +6,7 @@ export class DestinationPreviewMarker extends CustomMarker {
|
||||
this.setIcon(new DivIcon({
|
||||
iconSize: [52, 52],
|
||||
iconAnchor: [26, 26],
|
||||
className: "leaflet-destination-preview"
|
||||
className: "leaflet-destination-preview",
|
||||
}));
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("ol-destination-preview-icon");
|
||||
|
||||
@@ -12,6 +12,7 @@ import { DestinationPreviewMarker } from "./destinationpreviewmarker";
|
||||
import { TemporaryUnitMarker } from "./temporaryunitmarker";
|
||||
import { ClickableMiniMap } from "./clickableminimap";
|
||||
import { SVGInjector } from '@tanem/svg-injector'
|
||||
import { layers as mapLayers, mapBounds, minimapBoundaries } from "../constants/constants";
|
||||
|
||||
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
|
||||
|
||||
@@ -58,10 +59,10 @@ export class Map extends L.Map {
|
||||
super(ID, { doubleClickZoom: false, zoomControl: false, boxZoom: false, boxSelect: true, zoomAnimation: true, maxBoundsViscosity: 1.0, minZoom: 7, keyboard: true, keyboardPanDelta: 0 });
|
||||
this.setView([37.23, -115.8], 10);
|
||||
|
||||
this.setLayer("ArcGIS Satellite");
|
||||
this.setLayer(Object.keys(mapLayers)[0]);
|
||||
|
||||
/* 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(mapLayers[Object.keys(mapLayers)[0] as keyof typeof mapLayers].urlTemplate, { minZoom: 0, maxZoom: 13 });
|
||||
this.#miniMapLayerGroup = new L.LayerGroup([minimapLayer]);
|
||||
var miniMapPolyline = new L.Polyline(this.#getMinimapBoundaries(), { color: '#202831' });
|
||||
miniMapPolyline.addTo(this.#miniMapLayerGroup);
|
||||
@@ -124,59 +125,30 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
setLayer(layerName: string) {
|
||||
if (this.#layer != null) {
|
||||
if (this.#layer != null)
|
||||
this.removeLayer(this.#layer)
|
||||
|
||||
if (layerName in mapLayers){
|
||||
const layerData = mapLayers[layerName as keyof typeof mapLayers];
|
||||
var options: L.TileLayerOptions = {
|
||||
attribution: layerData.attribution,
|
||||
minZoom: layerData.minZoom,
|
||||
maxZoom: layerData.maxZoom
|
||||
};
|
||||
this.#layer = new L.TileLayer(layerData.urlTemplate, options);
|
||||
}
|
||||
|
||||
if (layerName == "ArcGIS Satellite") {
|
||||
this.#layer = L.tileLayer("https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", {
|
||||
attribution: "Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
|
||||
});
|
||||
}
|
||||
else if (layerName == "USGS Topo") {
|
||||
this.#layer = L.tileLayer('https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}', {
|
||||
maxZoom: 20,
|
||||
attribution: 'Tiles courtesy of the <a href="https://usgs.gov/">U.S. Geological Survey</a>'
|
||||
});
|
||||
}
|
||||
else if (layerName == "OpenStreetMap Mapnik") {
|
||||
this.#layer = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
});
|
||||
}
|
||||
else if (layerName == "OPENVKarte") {
|
||||
this.#layer = L.tileLayer('https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png', {
|
||||
maxZoom: 18,
|
||||
attribution: 'Map <a href="https://memomaps.de/">memomaps.de</a> <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, map data © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
});
|
||||
}
|
||||
else if (layerName == "Esri.DeLorme") {
|
||||
this.#layer = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer/tile/{z}/{y}/{x}', {
|
||||
attribution: 'Tiles © Esri — Copyright: ©2012 DeLorme',
|
||||
minZoom: 1,
|
||||
maxZoom: 11
|
||||
});
|
||||
}
|
||||
else if (layerName == "CyclOSM") {
|
||||
this.#layer = L.tileLayer('https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png', {
|
||||
maxZoom: 20,
|
||||
attribution: '<a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> | Map data: © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
});
|
||||
}
|
||||
this.#layer?.addTo(this);
|
||||
}
|
||||
|
||||
getLayers() {
|
||||
return ["ArcGIS Satellite", "USGS Topo", "OpenStreetMap Mapnik", "OPENVKarte", "Esri.DeLorme", "CyclOSM"]
|
||||
return Object.keys(mapLayers);
|
||||
}
|
||||
|
||||
/* State machine */
|
||||
setState(state: string) {
|
||||
this.#state = state;
|
||||
if (this.#state === IDLE) {
|
||||
L.DomUtil.removeClass(this.getContainer(), 'crosshair-cursor-enabled');
|
||||
|
||||
/* Remove all the destination preview markers */
|
||||
this.#destinationPreviewMarkers.forEach((marker: L.Marker) => {
|
||||
this.removeLayer(marker);
|
||||
@@ -188,8 +160,6 @@ export class Map extends L.Map {
|
||||
this.#destinationRotationCenter = null;
|
||||
}
|
||||
else if (this.#state === MOVE_UNIT) {
|
||||
L.DomUtil.addClass(this.getContainer(), 'crosshair-cursor-enabled');
|
||||
|
||||
/* Remove all the exising destination preview markers */
|
||||
this.#destinationPreviewMarkers.forEach((marker: L.Marker) => {
|
||||
this.removeLayer(marker);
|
||||
@@ -199,7 +169,7 @@ export class Map extends L.Map {
|
||||
if (getUnitsManager().getSelectedUnits({ excludeHumans: true }).length > 1 && getUnitsManager().getSelectedUnits({ excludeHumans: true }).length < 20) {
|
||||
/* Create the unit destination preview markers */
|
||||
this.#destinationPreviewMarkers = getUnitsManager().getSelectedUnits({ excludeHumans: true }).map((unit: Unit) => {
|
||||
var marker = new DestinationPreviewMarker(this.getMouseCoordinates());
|
||||
var marker = new DestinationPreviewMarker(this.getMouseCoordinates(), {interactive: false});
|
||||
marker.addTo(this);
|
||||
return marker;
|
||||
})
|
||||
@@ -295,20 +265,9 @@ export class Map extends L.Map {
|
||||
setTheatre(theatre: string) {
|
||||
var bounds = new L.LatLngBounds([-90, -180], [90, 180]);
|
||||
var miniMapZoom = 5;
|
||||
if (theatre == "Syria")
|
||||
bounds = new L.LatLngBounds([31.8472222, 29.8975], [37.7177778, 42.3716667]);
|
||||
else if (theatre == "MarianaIslands")
|
||||
bounds = new L.LatLngBounds([10.5777778, 135.7477778], [22.5127778, 149.5427778]);
|
||||
else if (theatre == "Nevada")
|
||||
bounds = new L.LatLngBounds([34.4037128, -119.7806729], [39.7372411, -112.1130805])
|
||||
else if (theatre == "PersianGulf")
|
||||
bounds = new L.LatLngBounds([21.729393, 47.572675], [33.131584, 64.7313594])
|
||||
else if (theatre == "Falklands") {
|
||||
// TODO
|
||||
}
|
||||
else if (theatre == "Caucasus") {
|
||||
bounds = new L.LatLngBounds([39.6170191, 27.634935], [47.3907982, 49.3101946])
|
||||
miniMapZoom = 4;
|
||||
if (theatre in mapBounds) {
|
||||
bounds = mapBounds[theatre as keyof typeof mapBounds].bounds;
|
||||
miniMapZoom = mapBounds[theatre as keyof typeof mapBounds].zoom;
|
||||
}
|
||||
|
||||
this.setView(bounds.getCenter(), 8);
|
||||
@@ -426,7 +385,7 @@ export class Map extends L.Map {
|
||||
if (!e.originalEvent.ctrlKey) {
|
||||
getUnitsManager().selectedUnitsClearDestinations();
|
||||
}
|
||||
getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, !e.originalEvent.shiftKey, this.#destinationGroupRotation)
|
||||
getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, e.originalEvent.shiftKey, this.#destinationGroupRotation)
|
||||
this.#destinationGroupRotation = 0;
|
||||
this.#destinationRotationCenter = null;
|
||||
this.#computeDestinationRotation = false;
|
||||
@@ -481,48 +440,13 @@ export class Map extends L.Map {
|
||||
|
||||
#getMinimapBoundaries() {
|
||||
/* Draw the limits of the maps in the minimap*/
|
||||
return [[ // NTTR
|
||||
new L.LatLng(39.7982463, -119.985425),
|
||||
new L.LatLng(34.4037128, -119.7806729),
|
||||
new L.LatLng(34.3483316, -112.4529351),
|
||||
new L.LatLng(39.7372411, -112.1130805),
|
||||
new L.LatLng(39.7982463, -119.985425)
|
||||
],
|
||||
[ // Syria
|
||||
new L.LatLng(37.3630556, 29.2686111),
|
||||
new L.LatLng(31.8472222, 29.8975),
|
||||
new L.LatLng(32.1358333, 42.1502778),
|
||||
new L.LatLng(37.7177778, 42.3716667),
|
||||
new L.LatLng(37.3630556, 29.2686111)
|
||||
],
|
||||
[ // Caucasus
|
||||
new L.LatLng(39.6170191, 27.634935),
|
||||
new L.LatLng(38.8735863, 47.1423108),
|
||||
new L.LatLng(47.3907982, 49.3101946),
|
||||
new L.LatLng(48.3955879, 26.7753625),
|
||||
new L.LatLng(39.6170191, 27.634935)
|
||||
],
|
||||
[ // Persian Gulf
|
||||
new L.LatLng(32.9355285, 46.5623682),
|
||||
new L.LatLng(21.729393, 47.572675),
|
||||
new L.LatLng(21.8501348, 63.9734737),
|
||||
new L.LatLng(33.131584, 64.7313594),
|
||||
new L.LatLng(32.9355285, 46.5623682)
|
||||
],
|
||||
[ // Marianas
|
||||
new L.LatLng(22.09, 135.0572222),
|
||||
new L.LatLng(10.5777778, 135.7477778),
|
||||
new L.LatLng(10.7725, 149.3918333),
|
||||
new L.LatLng(22.5127778, 149.5427778),
|
||||
new L.LatLng(22.09, 135.0572222)
|
||||
]
|
||||
];
|
||||
return minimapBoundaries;
|
||||
}
|
||||
|
||||
#updateDestinationPreview(e: any) {
|
||||
Object.values(getUnitsManager().selectedUnitsComputeGroupDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : this.getMouseCoordinates(), this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => {
|
||||
if (idx < this.#destinationPreviewMarkers.length)
|
||||
this.#destinationPreviewMarkers[idx].setLatLng(!e.originalEvent.shiftKey ? latlng : this.getMouseCoordinates());
|
||||
this.#destinationPreviewMarkers[idx].setLatLng(e.originalEvent.shiftKey ? latlng : this.getMouseCoordinates());
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ export class MissionHandler
|
||||
for (let idx in data.airbases)
|
||||
{
|
||||
var airbase = data.airbases[idx]
|
||||
if (this.#airbases[idx] === undefined)
|
||||
if (this.#airbases[idx] === undefined && airbase.callsign != '')
|
||||
{
|
||||
this.#airbases[idx] = new Airbase({
|
||||
position: new LatLng(airbase.latitude, airbase.longitude),
|
||||
@@ -45,7 +45,8 @@ export class MissionHandler
|
||||
}).addTo(getMap());
|
||||
this.#airbases[idx].on('contextmenu', (e) => this.#onAirbaseClick(e));
|
||||
}
|
||||
if (airbase.latitude && airbase.longitude && airbase.coalition)
|
||||
|
||||
if (this.#airbases[idx] != undefined && airbase.latitude && airbase.longitude && airbase.coalition)
|
||||
{
|
||||
this.#airbases[idx].setLatLng(new LatLng(airbase.latitude, airbase.longitude));
|
||||
this.#airbases[idx].setCoalition(airbase.coalition);
|
||||
|
||||
@@ -3,38 +3,21 @@ import { getUnitsManager } from "..";
|
||||
import { Dropdown } from "../controls/dropdown";
|
||||
import { Slider } from "../controls/slider";
|
||||
import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
|
||||
import { Aircraft, GroundUnit, Unit } from "../units/unit";
|
||||
import { UnitDatabase } from "../units/unitdatabase";
|
||||
import { Unit } from "../units/unit";
|
||||
import { Panel } from "./panel";
|
||||
import { Switch } from "../controls/switch";
|
||||
|
||||
const ROEs: string[] = ["Hold", "Return", "Designated", "Free"];
|
||||
const reactionsToThreat: string[] = ["None", "Manoeuvre", "Passive", "Evade"];
|
||||
const emissionsCountermeasures: string[] = ["Silent", "Attack", "Defend", "Free"];
|
||||
|
||||
const ROEDescriptions: string[] = ["Hold (Never fire)", "Return (Only fire if fired upon)", "Designated (Attack the designated target only)", "Free (Attack anyone)"];
|
||||
const reactionsToThreatDescriptions: string[] = ["None (No reaction)", "Manoeuvre (no countermeasures)", "Passive (Countermeasures only, no manoeuvre)", "Evade (Countermeasures and manoeuvers)"];
|
||||
const emissionsCountermeasuresDescriptions: string[] = ["Silent (Radar OFF, no ECM)", "Attack (Radar only for targeting, ECM only if locked)", "Defend (Radar for searching, ECM if locked)", "Always on (Radar and ECM always on)"];
|
||||
|
||||
const minSpeedValues: { [key: string]: number } = { Aircraft: 100, Helicopter: 0, NavyUnit: 0, GroundUnit: 0 };
|
||||
const maxSpeedValues: { [key: string]: number } = { Aircraft: 800, Helicopter: 300, NavyUnit: 60, GroundUnit: 60 };
|
||||
const speedIncrements: { [key: string]: number } = { Aircraft: 25, Helicopter: 10, NavyUnit: 5, GroundUnit: 5 };
|
||||
const minAltitudeValues: { [key: string]: number } = { Aircraft: 0, Helicopter: 0 };
|
||||
const maxAltitudeValues: { [key: string]: number } = { Aircraft: 50000, Helicopter: 10000 };
|
||||
const altitudeIncrements: { [key: string]: number } = { Aircraft: 500, Helicopter: 100 };
|
||||
import { ROEDescriptions, ROEs, altitudeIncrements, emissionsCountermeasures, emissionsCountermeasuresDescriptions, maxAltitudeValues, maxSpeedValues, minAltitudeValues, minSpeedValues, reactionsToThreat, reactionsToThreatDescriptions, speedIncrements } from "../constants/constants";
|
||||
|
||||
export class UnitControlPanel extends Panel {
|
||||
#altitudeSlider: Slider;
|
||||
#altitudeTypeSwitch: Switch;
|
||||
#airspeedSlider: Slider;
|
||||
#airspeedTypeSwitch: Switch;
|
||||
#speedSlider: Slider;
|
||||
#speedTypeSwitch: Switch;
|
||||
#onOffSwitch: Switch;
|
||||
#followRoadsSwitch: Switch;
|
||||
#TACANXYDropdown: Dropdown;
|
||||
#radioDecimalsDropdown: Dropdown;
|
||||
#radioCallsignDropdown: Dropdown;
|
||||
#expectedAltitude: number = -1;
|
||||
#expectedSpeed: number = -1;
|
||||
#optionButtons: { [key: string]: HTMLButtonElement[] } = {}
|
||||
#advancedSettingsDialog: HTMLElement;
|
||||
|
||||
@@ -42,17 +25,11 @@ export class UnitControlPanel extends Panel {
|
||||
super(ID);
|
||||
|
||||
/* Unit control sliders */
|
||||
this.#altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => {
|
||||
this.#expectedAltitude = value;
|
||||
getUnitsManager().selectedUnitsSetAltitude(value * 0.3048);
|
||||
});
|
||||
this.#altitudeTypeSwitch = new Switch("altitude-type-switch");
|
||||
this.#altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => { getUnitsManager().selectedUnitsSetAltitude(value * 0.3048); });
|
||||
this.#altitudeTypeSwitch = new Switch("altitude-type-switch", (value: boolean) => { getUnitsManager().selectedUnitsSetAltitudeType(value? "AGL": "ASL"); });
|
||||
|
||||
this.#airspeedSlider = new Slider("airspeed-slider", 0, 100, "kts", (value: number) => {
|
||||
this.#expectedSpeed = value;
|
||||
getUnitsManager().selectedUnitsSetSpeed(value / 1.94384);
|
||||
});
|
||||
this.#airspeedTypeSwitch = new Switch("airspeed-type-switch");
|
||||
this.#speedSlider = new Slider("speed-slider", 0, 100, "kts", (value: number) => { getUnitsManager().selectedUnitsSetSpeed(value / 1.94384); });
|
||||
this.#speedTypeSwitch = new Switch("speed-type-switch", (value: boolean) => { getUnitsManager().selectedUnitsSetSpeedType(value? "GS": "CAS"); });
|
||||
|
||||
/* Option buttons */
|
||||
this.#optionButtons["ROE"] = ROEs.map((option: string, index: number) => {
|
||||
@@ -72,7 +49,14 @@ export class UnitControlPanel extends Panel {
|
||||
this.getElement().querySelector("#emissions-countermeasures-buttons-container")?.append(...this.#optionButtons["emissionsCountermeasures"]);
|
||||
|
||||
/* On off switch */
|
||||
this.#onOffSwitch = new Switch("on-off-switch");
|
||||
this.#onOffSwitch = new Switch("on-off-switch", (value: boolean) => {
|
||||
getUnitsManager().selectedUnitsSetOnOff(value);
|
||||
});
|
||||
|
||||
/* Follow roads switch */
|
||||
this.#followRoadsSwitch = new Switch("follow-roads-switch", (value: boolean) => {
|
||||
getUnitsManager().selectedUnitsSetFollowRoads(value);
|
||||
});
|
||||
|
||||
/* Advanced settings dialog */
|
||||
this.#advancedSettingsDialog = <HTMLElement> document.querySelector("#advanced-settings-dialog");
|
||||
@@ -98,26 +82,18 @@ export class UnitControlPanel extends Panel {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
// Do this after panel is hidden (make sure there's a reset)
|
||||
hide() {
|
||||
super.hide();
|
||||
|
||||
this.#expectedAltitude = -1;
|
||||
this.#expectedSpeed = -1;
|
||||
show() {
|
||||
super.show();
|
||||
this.#speedTypeSwitch.resetExpectedValue();
|
||||
this.#altitudeTypeSwitch.resetExpectedValue();
|
||||
this.#onOffSwitch.resetExpectedValue();
|
||||
this.#followRoadsSwitch.resetExpectedValue();
|
||||
}
|
||||
|
||||
addButtons() {
|
||||
var units = getUnitsManager().getSelectedUnits();
|
||||
if (units.length < 20) {
|
||||
this.getElement().querySelector("#selected-units-container")?.replaceChildren(...units.map((unit: Unit, index: number) => {
|
||||
let database: UnitDatabase | null;
|
||||
if (unit instanceof Aircraft)
|
||||
database = aircraftDatabase;
|
||||
else if (unit instanceof GroundUnit)
|
||||
database = groundUnitsDatabase;
|
||||
else
|
||||
database = null; // TODO add databases for other unit types
|
||||
|
||||
var button = document.createElement("button");
|
||||
var callsign = unit.getBaseData().unitName || "";
|
||||
var label = unit.getDatabase()?.getByName(unit.getBaseData().name)?.label || unit.getBaseData().name;
|
||||
@@ -150,42 +126,42 @@ export class UnitControlPanel extends Panel {
|
||||
if (element != null && units.length > 0) {
|
||||
/* Toggle visibility of control elements */
|
||||
element.toggleAttribute("data-show-categories-tooltip", selectedUnitsTypes.length > 1);
|
||||
element.toggleAttribute("data-show-airspeed-slider", selectedUnitsTypes.length == 1);
|
||||
element.toggleAttribute("data-show-speed-slider", selectedUnitsTypes.length == 1);
|
||||
element.toggleAttribute("data-show-altitude-slider", selectedUnitsTypes.length == 1 && (selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")));
|
||||
element.toggleAttribute("data-show-roe", true);
|
||||
element.toggleAttribute("data-show-threat", (selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")) && !(selectedUnitsTypes.includes("GroundUnit") || selectedUnitsTypes.includes("NavyUnit")));
|
||||
element.toggleAttribute("data-show-emissions-countermeasures", (selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")) && !(selectedUnitsTypes.includes("GroundUnit") || selectedUnitsTypes.includes("NavyUnit")));
|
||||
element.toggleAttribute("data-show-on-off", (selectedUnitsTypes.includes("GroundUnit") || selectedUnitsTypes.includes("NavyUnit")) && !(selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")));
|
||||
element.toggleAttribute("data-show-follow-roads", (selectedUnitsTypes.length == 1 && selectedUnitsTypes.includes("GroundUnit")));
|
||||
element.toggleAttribute("data-show-advanced-settings-button", units.length == 1);
|
||||
|
||||
/* Flight controls */
|
||||
var targetAltitude = getUnitsManager().getSelectedUnitsTargetAltitude();
|
||||
var targetSpeed = getUnitsManager().getSelectedUnitsTargetSpeed();
|
||||
var targetAltitude: number | undefined = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().targetAltitude});
|
||||
var targetAltitudeType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().targetAltitudeType});
|
||||
var targetSpeed = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().targetSpeed});
|
||||
var targetSpeedType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().targetSpeedType});
|
||||
var onOff = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().onOff});
|
||||
var followRoads = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().followRoads});
|
||||
|
||||
if (selectedUnitsTypes.length == 1) {
|
||||
this.#airspeedSlider.setMinMax(minSpeedValues[selectedUnitsTypes[0]], maxSpeedValues[selectedUnitsTypes[0]]);
|
||||
this.#altitudeTypeSwitch.setValue(targetAltitudeType != undefined? targetAltitudeType == "AGL": undefined, false);
|
||||
this.#speedTypeSwitch.setValue(targetSpeedType != undefined? targetSpeedType == "GS": undefined, false);
|
||||
|
||||
this.#speedSlider.setMinMax(minSpeedValues[selectedUnitsTypes[0]], maxSpeedValues[selectedUnitsTypes[0]]);
|
||||
this.#altitudeSlider.setMinMax(minAltitudeValues[selectedUnitsTypes[0]], maxAltitudeValues[selectedUnitsTypes[0]]);
|
||||
this.#airspeedSlider.setIncrement(speedIncrements[selectedUnitsTypes[0]]);
|
||||
this.#speedSlider.setIncrement(speedIncrements[selectedUnitsTypes[0]]);
|
||||
this.#altitudeSlider.setIncrement(altitudeIncrements[selectedUnitsTypes[0]]);
|
||||
|
||||
this.#airspeedSlider.setActive(targetSpeed != undefined);
|
||||
if (targetSpeed != undefined) {
|
||||
targetSpeed *= 1.94384;
|
||||
if (this.#updateCanSetSpeedSlider(targetSpeed)) {
|
||||
this.#airspeedSlider.setValue(targetSpeed);
|
||||
}
|
||||
}
|
||||
this.#speedSlider.setActive(targetSpeed != undefined);
|
||||
if (targetSpeed != undefined)
|
||||
this.#speedSlider.setValue(targetSpeed * 1.94384, false);
|
||||
|
||||
this.#altitudeSlider.setActive(targetAltitude != undefined);
|
||||
if (targetAltitude != undefined) {
|
||||
targetAltitude /= 0.3048;
|
||||
if (this.#updateCanSetAltitudeSlider(targetAltitude)) {
|
||||
this.#altitudeSlider.setValue(targetAltitude);
|
||||
}
|
||||
}
|
||||
if (targetAltitude != undefined)
|
||||
this.#altitudeSlider.setValue(targetAltitude / 0.3048, false);
|
||||
}
|
||||
else {
|
||||
this.#airspeedSlider.setActive(false);
|
||||
this.#speedSlider.setActive(false);
|
||||
this.#altitudeSlider.setActive(false);
|
||||
}
|
||||
|
||||
@@ -201,27 +177,13 @@ export class UnitControlPanel extends Panel {
|
||||
this.#optionButtons["emissionsCountermeasures"].forEach((button: HTMLButtonElement) => {
|
||||
button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().emissionsCountermeasures === button.value))
|
||||
});
|
||||
|
||||
this.#onOffSwitch.setValue(onOff, false);
|
||||
this.#followRoadsSwitch.setValue(followRoads, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update function will only be allowed to update the sliders once it's matched the expected value for the first time (due to lag of Ajax request) */
|
||||
#updateCanSetAltitudeSlider(altitude: number) {
|
||||
if (this.#expectedAltitude < 0 || altitude === this.#expectedAltitude) {
|
||||
this.#expectedAltitude = -1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#updateCanSetSpeedSlider(altitude: number) {
|
||||
if (this.#expectedSpeed < 0 || altitude === this.#expectedSpeed) {
|
||||
this.#expectedSpeed = -1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#updateAdvancedSettingsDialog(units: Unit[])
|
||||
{
|
||||
if (units.length == 1)
|
||||
|
||||
@@ -16,7 +16,7 @@ export class UnitInfoPanel extends Panel {
|
||||
#latitude: HTMLElement;
|
||||
#longitude: HTMLElement;
|
||||
#loadoutContainer: HTMLElement;
|
||||
#silhouette: HTMLElement;
|
||||
#silhouette: HTMLImageElement;
|
||||
#unitControl: HTMLElement;
|
||||
#unitLabel: HTMLElement;
|
||||
#unitName: HTMLElement;
|
||||
@@ -24,21 +24,21 @@ export class UnitInfoPanel extends Panel {
|
||||
constructor(ID: string) {
|
||||
super(ID);
|
||||
|
||||
this.#altitude = <HTMLElement>(this.getElement().querySelector("#altitude"));
|
||||
this.#currentTask = <HTMLElement>(this.getElement().querySelector("#current-task"));
|
||||
this.#groundSpeed = <HTMLElement>(this.getElement().querySelector("#ground-speed"));
|
||||
this.#fuelBar = <HTMLElement>(this.getElement().querySelector("#fuel-bar"));
|
||||
this.#fuelPercentage = <HTMLElement>(this.getElement().querySelector("#fuel-percentage"));
|
||||
this.#groupName = <HTMLElement>(this.getElement().querySelector("#group-name"));
|
||||
this.#heading = <HTMLElement>(this.getElement().querySelector("#heading"));
|
||||
this.#name = <HTMLElement>(this.getElement().querySelector("#name"));
|
||||
this.#latitude = <HTMLElement>(this.getElement().querySelector("#latitude"));
|
||||
this.#loadoutContainer = <HTMLElement>(this.getElement().querySelector("#loadout-container"));
|
||||
this.#longitude = <HTMLElement>(this.getElement().querySelector("#longitude"));
|
||||
this.#silhouette = <HTMLElement>(this.getElement().querySelector("#loadout-silhouette"));
|
||||
this.#unitControl = <HTMLElement>(this.getElement().querySelector("#unit-control"));
|
||||
this.#unitLabel = <HTMLElement>(this.getElement().querySelector("#unit-label"));
|
||||
this.#unitName = <HTMLElement>(this.getElement().querySelector("#unit-name"));
|
||||
this.#altitude = (this.getElement().querySelector("#altitude")) as HTMLElement;
|
||||
this.#currentTask = (this.getElement().querySelector("#current-task")) as HTMLElement;
|
||||
this.#groundSpeed = (this.getElement().querySelector("#ground-speed")) as HTMLElement;
|
||||
this.#fuelBar = (this.getElement().querySelector("#fuel-bar")) as HTMLElement;
|
||||
this.#fuelPercentage = (this.getElement().querySelector("#fuel-percentage")) as HTMLElement;
|
||||
this.#groupName = (this.getElement().querySelector("#group-name")) as HTMLElement;
|
||||
this.#heading = (this.getElement().querySelector("#heading")) as HTMLElement;
|
||||
this.#name = (this.getElement().querySelector("#name")) as HTMLElement;
|
||||
this.#latitude = (this.getElement().querySelector("#latitude")) as HTMLElement;
|
||||
this.#loadoutContainer = (this.getElement().querySelector("#loadout-container")) as HTMLElement;
|
||||
this.#longitude = (this.getElement().querySelector("#longitude")) as HTMLElement;
|
||||
this.#silhouette = (this.getElement().querySelector("#loadout-silhouette")) as HTMLImageElement;
|
||||
this.#unitControl = (this.getElement().querySelector("#unit-control")) as HTMLElement;
|
||||
this.#unitLabel = (this.getElement().querySelector("#unit-label")) as HTMLElement;
|
||||
this.#unitName = (this.getElement().querySelector("#unit-name")) as HTMLElement;
|
||||
|
||||
document.addEventListener("unitsSelection", (e: CustomEvent<Unit[]>) => this.#onUnitsSelection(e.detail));
|
||||
document.addEventListener("unitsDeselection", (e: CustomEvent<Unit[]>) => this.#onUnitsDeselection(e.detail));
|
||||
@@ -69,18 +69,15 @@ export class UnitInfoPanel extends Panel {
|
||||
this.#currentTask.dataset.currentTask = unit.getTaskData().currentTask !== ""? unit.getTaskData().currentTask: "No task";
|
||||
this.#currentTask.dataset.coalition = unit.getMissionData().coalition;
|
||||
|
||||
this.#silhouette.setAttribute( "style", `--loadout-background-image:url('/images/units/${aircraftDatabase.getByName( baseData.name )?.filename}');` );;
|
||||
|
||||
this.#silhouette.src = `/images/units/${unit.getDatabase()?.getByName(baseData.name)?.filename}`;
|
||||
this.#silhouette.classList.toggle("hide", unit.getDatabase()?.getByName(baseData.name)?.filename == undefined || unit.getDatabase()?.getByName(baseData.name)?.filename == '');
|
||||
|
||||
/* Add the loadout elements */
|
||||
const items = <HTMLElement>this.#loadoutContainer.querySelector( "#loadout-items" );
|
||||
|
||||
|
||||
if ( items ) {
|
||||
|
||||
const ammo = Object.values( unit.getMissionData().ammo );
|
||||
|
||||
if ( ammo.length > 0 ) {
|
||||
|
||||
items.replaceChildren(...Object.values(unit.getMissionData().ammo).map(
|
||||
(ammo: any) => {
|
||||
var el = document.createElement("div");
|
||||
@@ -91,25 +88,28 @@ export class UnitInfoPanel extends Panel {
|
||||
));
|
||||
|
||||
} else {
|
||||
|
||||
items.innerText = "No loadout";
|
||||
|
||||
items.innerText = "No loadout";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#onUnitsSelection(units: Unit[]){
|
||||
if (units.length == 1)
|
||||
{
|
||||
this.show();
|
||||
this.#onUnitUpdate(units[0]);
|
||||
}
|
||||
else
|
||||
this.hide();
|
||||
}
|
||||
|
||||
#onUnitsDeselection(units: Unit[]){
|
||||
if (units.length == 1)
|
||||
{
|
||||
this.show();
|
||||
this.#onUnitUpdate(units[0]);
|
||||
}
|
||||
else
|
||||
this.hide();
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export function setCredentials(newUsername: string, newCredentials: string) {
|
||||
credentials = newCredentials;
|
||||
}
|
||||
|
||||
export function GET(callback: CallableFunction, uri: string, options?: {time?: number}) {
|
||||
export function GET(callback: CallableFunction, uri: string, options?: { time?: number }) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
|
||||
/* Assemble the request options string */
|
||||
@@ -37,15 +37,14 @@ export function GET(callback: CallableFunction, uri: string, options?: {time?: n
|
||||
if (options?.time != undefined)
|
||||
optionsString = `time=${options.time}`;
|
||||
|
||||
|
||||
xmlHttp.open("GET", `${demoEnabled? DEMO_ADDRESS: REST_ADDRESS}/${uri}${optionsString? `?${optionsString}`: ''}`, true);
|
||||
|
||||
xmlHttp.open("GET", `${demoEnabled ? DEMO_ADDRESS : REST_ADDRESS}/${uri}${optionsString ? `?${optionsString}` : ''}`, true);
|
||||
if (credentials)
|
||||
xmlHttp.setRequestHeader("Authorization", "Basic " + credentials);
|
||||
xmlHttp.onload = function (e) {
|
||||
if (xmlHttp.status == 200) {
|
||||
var data = JSON.parse(xmlHttp.responseText);
|
||||
if (uri !== UNITS_URI || (options?.time == 0) || parseInt(data.time) > lastUpdateTime)
|
||||
{
|
||||
if (uri !== UNITS_URI || (options?.time == 0) || parseInt(data.time) > lastUpdateTime) {
|
||||
callback(data);
|
||||
lastUpdateTime = parseInt(data.time);
|
||||
if (isNaN(lastUpdateTime))
|
||||
@@ -66,14 +65,14 @@ export function GET(callback: CallableFunction, uri: string, options?: {time?: n
|
||||
xmlHttp.send(null);
|
||||
}
|
||||
|
||||
export function POST(request: object, callback: CallableFunction){
|
||||
export function POST(request: object, callback: CallableFunction) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("PUT", demoEnabled? DEMO_ADDRESS: REST_ADDRESS);
|
||||
xmlHttp.open("PUT", demoEnabled ? DEMO_ADDRESS : REST_ADDRESS);
|
||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
||||
if (credentials)
|
||||
xmlHttp.setRequestHeader("Authorization", "Basic " + credentials);
|
||||
xmlHttp.onreadystatechange = () => {
|
||||
callback();
|
||||
xmlHttp.onreadystatechange = () => {
|
||||
callback();
|
||||
};
|
||||
xmlHttp.send(JSON.stringify(request));
|
||||
}
|
||||
@@ -113,7 +112,7 @@ export function getMission(callback: CallableFunction) {
|
||||
}
|
||||
|
||||
export function getUnits(callback: CallableFunction, refresh: boolean = false) {
|
||||
GET(callback, `${UNITS_URI}`, {time: refresh? 0: lastUpdateTime});
|
||||
GET(callback, `${UNITS_URI}`, { time: refresh ? 0 : lastUpdateTime });
|
||||
}
|
||||
|
||||
export function addDestination(ID: number, path: any) {
|
||||
@@ -135,7 +134,7 @@ export function spawnGroundUnit(spawnOptions: SpawnOptions) {
|
||||
}
|
||||
|
||||
export function spawnAircraft(spawnOptions: SpawnOptions) {
|
||||
var command = { "type": spawnOptions.type, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition, "payloadName": spawnOptions.loadout != null? spawnOptions.loadout: "", "airbaseName": spawnOptions.airbaseName != null? spawnOptions.airbaseName: ""};
|
||||
var command = { "type": spawnOptions.type, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition, "altitude": spawnOptions.altitude, "payloadName": spawnOptions.loadout != null ? spawnOptions.loadout : "", "airbaseName": spawnOptions.airbaseName != null ? spawnOptions.airbaseName : "" };
|
||||
var data = { "spawnAir": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
@@ -146,12 +145,12 @@ export function attackUnit(ID: number, targetID: number) {
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function followUnit(ID: number, targetID: number, offset: {"x": number, "y": number, "z": number}) {
|
||||
export function followUnit(ID: number, targetID: number, offset: { "x": number, "y": number, "z": number }) {
|
||||
// X: front-rear, positive front
|
||||
// Y: top-bottom, positive bottom
|
||||
// Z: left-right, positive right
|
||||
|
||||
var command = { "ID": ID, "targetID": targetID, "offsetX": offset["x"], "offsetY": offset["y"], "offsetZ": offset["z"]};
|
||||
|
||||
var command = { "ID": ID, "targetID": targetID, "offsetX": offset["x"], "offsetY": offset["y"], "offsetZ": offset["z"] };
|
||||
var data = { "followUnit": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
@@ -162,8 +161,8 @@ export function cloneUnit(ID: number, latlng: L.LatLng) {
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function deleteUnit(ID: number) {
|
||||
var command = { "ID": ID};
|
||||
export function deleteUnit(ID: number, explosion: boolean) {
|
||||
var command = { "ID": ID, "explosion": explosion };
|
||||
var data = { "deleteUnit": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
@@ -175,50 +174,74 @@ export function landAt(ID: number, latlng: L.LatLng) {
|
||||
}
|
||||
|
||||
export function changeSpeed(ID: number, speedChange: string) {
|
||||
var command = {"ID": ID, "change": speedChange}
|
||||
var data = {"changeSpeed": command}
|
||||
var command = { "ID": ID, "change": speedChange }
|
||||
var data = { "changeSpeed": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function setSpeed(ID: number, speed: number) {
|
||||
var command = {"ID": ID, "speed": speed}
|
||||
var data = {"setSpeed": command}
|
||||
var command = { "ID": ID, "speed": speed }
|
||||
var data = { "setSpeed": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function setSpeedType(ID: number, speedType: string) {
|
||||
var command = { "ID": ID, "speedType": speedType }
|
||||
var data = { "setSpeedType": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function changeAltitude(ID: number, altitudeChange: string) {
|
||||
var command = {"ID": ID, "change": altitudeChange}
|
||||
var data = {"changeAltitude": command}
|
||||
var command = { "ID": ID, "change": altitudeChange }
|
||||
var data = { "changeAltitude": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function setAltitudeType(ID: number, altitudeType: string) {
|
||||
var command = { "ID": ID, "altitudeType": altitudeType }
|
||||
var data = { "setAltitudeType": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function setAltitude(ID: number, altitude: number) {
|
||||
var command = {"ID": ID, "altitude": altitude}
|
||||
var data = {"setAltitude": command}
|
||||
var command = { "ID": ID, "altitude": altitude }
|
||||
var data = { "setAltitude": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function createFormation(ID: number, isLeader: boolean, wingmenIDs: number[]) {
|
||||
var command = {"ID": ID, "wingmenIDs": wingmenIDs, "isLeader": isLeader}
|
||||
var data = {"setLeader": command}
|
||||
var command = { "ID": ID, "wingmenIDs": wingmenIDs, "isLeader": isLeader }
|
||||
var data = { "setLeader": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function setROE(ID: number, ROE: string) {
|
||||
var command = {"ID": ID, "ROE": ROE}
|
||||
var data = {"setROE": command}
|
||||
var command = { "ID": ID, "ROE": ROE }
|
||||
var data = { "setROE": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function setReactionToThreat(ID: number, reactionToThreat: string) {
|
||||
var command = {"ID": ID, "reactionToThreat": reactionToThreat}
|
||||
var data = {"setReactionToThreat": command}
|
||||
var command = { "ID": ID, "reactionToThreat": reactionToThreat }
|
||||
var data = { "setReactionToThreat": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function setEmissionsCountermeasures(ID: number, emissionCountermeasure: string) {
|
||||
var command = {"ID": ID, "emissionsCountermeasures": emissionCountermeasure}
|
||||
var data = {"setEmissionsCountermeasures": command}
|
||||
var command = { "ID": ID, "emissionsCountermeasures": emissionCountermeasure }
|
||||
var data = { "setEmissionsCountermeasures": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function setOnOff(ID: number, onOff: boolean) {
|
||||
var command = { "ID": ID, "onOff": onOff }
|
||||
var data = { "setOnOff": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function setFollowRoads(ID: number, followRoads: boolean) {
|
||||
var command = { "ID": ID, "followRoads": followRoads }
|
||||
var data = { "setFollowRoads": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
@@ -228,14 +251,14 @@ export function refuel(ID: number) {
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings)
|
||||
{
|
||||
var command = { "ID": ID,
|
||||
"isTanker": isTanker,
|
||||
"isAWACS": isAWACS,
|
||||
"TACAN": TACAN,
|
||||
"radio": radio,
|
||||
"generalSettings": generalSettings
|
||||
export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings) {
|
||||
var command = {
|
||||
"ID": ID,
|
||||
"isTanker": isTanker,
|
||||
"isAWACS": isAWACS,
|
||||
"TACAN": TACAN,
|
||||
"radio": radio,
|
||||
"generalSettings": generalSettings
|
||||
};
|
||||
|
||||
var data = { "setAdvancedOptions": command };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map } from 'leaflet';
|
||||
import { getMap, getUnitsManager } from '..';
|
||||
import { rad2deg } from '../other/utils';
|
||||
import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, getUnits, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions, followUnit, setEmissionsCountermeasures } from '../server/server';
|
||||
import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, getUnits, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions, followUnit, setEmissionsCountermeasures, setSpeedType, setAltitudeType, setOnOff, setFollowRoads } from '../server/server';
|
||||
import { aircraftDatabase } from './aircraftdatabase';
|
||||
import { groundUnitsDatabase } from './groundunitsdatabase';
|
||||
import { CustomMarker } from '../map/custommarker';
|
||||
@@ -49,9 +49,13 @@ export class Unit extends CustomMarker {
|
||||
currentTask: "",
|
||||
activePath: {},
|
||||
targetSpeed: 0,
|
||||
targetSpeedType: "GS",
|
||||
targetAltitude: 0,
|
||||
targetAltitudeType: "AGL",
|
||||
isTanker: false,
|
||||
isAWACS: false,
|
||||
onOff: true,
|
||||
followRoads: false
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "",
|
||||
@@ -116,8 +120,6 @@ export class Unit extends CustomMarker {
|
||||
|
||||
/* Set the unit data */
|
||||
this.setData(data);
|
||||
|
||||
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
@@ -203,48 +205,19 @@ export class Unit extends CustomMarker {
|
||||
const aliveChanged = (data.baseData != undefined && data.baseData.alive != undefined && this.getBaseData().alive != data.baseData.alive);
|
||||
var updateMarker = (positionChanged || headingChanged || aliveChanged || !getMap().hasLayer(this));
|
||||
|
||||
if (data.baseData != undefined) {
|
||||
for (let key in this.#data.baseData)
|
||||
if (key in data.baseData)
|
||||
//@ts-ignore
|
||||
this.#data.baseData[key] = data.baseData[key];
|
||||
}
|
||||
|
||||
if (data.flightData != undefined) {
|
||||
for (let key in this.#data.flightData)
|
||||
if (key in data.flightData)
|
||||
//@ts-ignore
|
||||
this.#data.flightData[key] = data.flightData[key];
|
||||
}
|
||||
|
||||
if (data.missionData != undefined) {
|
||||
for (let key in this.#data.missionData)
|
||||
if (key in data.missionData)
|
||||
//@ts-ignore
|
||||
this.#data.missionData[key] = data.missionData[key];
|
||||
}
|
||||
|
||||
if (data.formationData != undefined) {
|
||||
for (let key in this.#data.formationData)
|
||||
if (key in data.formationData)
|
||||
//@ts-ignore
|
||||
this.#data.formationData[key] = data.formationData[key];
|
||||
}
|
||||
|
||||
if (data.taskData != undefined) {
|
||||
for (let key in this.#data.taskData)
|
||||
if (key in data.taskData)
|
||||
//@ts-ignore
|
||||
this.#data.taskData[key] = data.taskData[key];
|
||||
}
|
||||
|
||||
if (data.optionsData != undefined) {
|
||||
for (let key in this.#data.optionsData)
|
||||
if (key in data.optionsData)
|
||||
//@ts-ignore
|
||||
this.#data.optionsData[key] = data.optionsData[key];
|
||||
}
|
||||
|
||||
/* Load the data from the received json */
|
||||
Object.keys(this.#data).forEach((key1: string) => {
|
||||
Object.keys(this.#data[key1 as keyof(UnitData)]).forEach((key2: string) => {
|
||||
if (key1 in data && key2 in data[key1]) {
|
||||
var value1 = this.#data[key1 as keyof(UnitData)];
|
||||
var value2 = value1[key2 as keyof typeof value1];
|
||||
if (typeof data[key1][key2] === typeof value2)
|
||||
//@ts-ignore
|
||||
this.#data[key1 as keyof(UnitData)][key2 as keyof typeof struct] = data[key1][key2];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* Fire an event when a unit dies */
|
||||
if (aliveChanged && this.getBaseData().alive == false)
|
||||
document.dispatchEvent(new CustomEvent("unitDeath", { detail: this }));
|
||||
@@ -485,11 +458,21 @@ export class Unit extends CustomMarker {
|
||||
setSpeed(this.ID, speed);
|
||||
}
|
||||
|
||||
setSpeedType(speedType: string) {
|
||||
if (!this.getMissionData().flags.Human)
|
||||
setSpeedType(this.ID, speedType);
|
||||
}
|
||||
|
||||
setAltitude(altitude: number) {
|
||||
if (!this.getMissionData().flags.Human)
|
||||
setAltitude(this.ID, altitude);
|
||||
}
|
||||
|
||||
setAltitudeType(altitudeType: string) {
|
||||
if (!this.getMissionData().flags.Human)
|
||||
setAltitudeType(this.ID, altitudeType);
|
||||
}
|
||||
|
||||
setROE(ROE: string) {
|
||||
if (!this.getMissionData().flags.Human)
|
||||
setROE(this.ID, ROE);
|
||||
@@ -510,9 +493,19 @@ export class Unit extends CustomMarker {
|
||||
setLeader(this.ID, isLeader, wingmenIDs);
|
||||
}
|
||||
|
||||
delete() {
|
||||
setOnOff(onOff: boolean) {
|
||||
if (!this.getMissionData().flags.Human)
|
||||
setOnOff(this.ID, onOff);
|
||||
}
|
||||
|
||||
setFollowRoads(followRoads: boolean) {
|
||||
if (!this.getMissionData().flags.Human)
|
||||
setFollowRoads(this.ID, followRoads);
|
||||
}
|
||||
|
||||
delete(explosion: boolean) {
|
||||
// TODO: add confirmation popup
|
||||
deleteUnit(this.ID);
|
||||
deleteUnit(this.ID, explosion);
|
||||
}
|
||||
|
||||
refuel() {
|
||||
|
||||
@@ -21,7 +21,8 @@ export class UnitsManager {
|
||||
document.addEventListener('unitSelection', (e: CustomEvent) => this.#onUnitSelection(e.detail));
|
||||
document.addEventListener('unitDeselection', (e: CustomEvent) => this.#onUnitDeselection(e.detail));
|
||||
document.addEventListener('keydown', (event) => this.#onKeyDown(event));
|
||||
document.addEventListener('deleteSelectedUnits', () => this.selectedUnitsDelete())
|
||||
document.addEventListener('deleteSelectedUnits', () => this.selectedUnitsDelete());
|
||||
document.addEventListener('explodeSelectedUnits', () => this.selectedUnitsDelete(true));
|
||||
}
|
||||
|
||||
getSelectableAircraft() {
|
||||
@@ -155,25 +156,16 @@ export class UnitsManager {
|
||||
});
|
||||
};
|
||||
|
||||
getSelectedUnitsTargetSpeed() {
|
||||
getSelectedUnitsVariable(variableGetter: CallableFunction) {
|
||||
if (this.getSelectedUnits().length == 0)
|
||||
return undefined;
|
||||
return this.getSelectedUnits().map((unit: Unit) => {
|
||||
return unit.getTaskData().targetSpeed
|
||||
return variableGetter(unit);
|
||||
})?.reduce((a: any, b: any) => {
|
||||
return a == b ? a : undefined
|
||||
});
|
||||
};
|
||||
|
||||
getSelectedUnitsTargetAltitude() {
|
||||
if (this.getSelectedUnits().length == 0)
|
||||
return undefined;
|
||||
return this.getSelectedUnits().map((unit: Unit) => {
|
||||
return unit.getTaskData().targetAltitude
|
||||
})?.reduce((a: any, b: any) => {
|
||||
return a == b ? a : undefined
|
||||
});
|
||||
};
|
||||
|
||||
getSelectedUnitsCoalition() {
|
||||
if (this.getSelectedUnits().length == 0)
|
||||
@@ -261,6 +253,14 @@ export class UnitsManager {
|
||||
this.#showActionMessage(selectedUnits, `setting speed to ${speed * 1.94384} kts`);
|
||||
}
|
||||
|
||||
selectedUnitsSetSpeedType(speedType: string) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setSpeedType(speedType);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `setting speed type to ${speedType}`);
|
||||
}
|
||||
|
||||
selectedUnitsSetAltitude(altitude: number) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true });
|
||||
for (let idx in selectedUnits) {
|
||||
@@ -269,6 +269,14 @@ export class UnitsManager {
|
||||
this.#showActionMessage(selectedUnits, `setting altitude to ${altitude / 0.3048} ft`);
|
||||
}
|
||||
|
||||
selectedUnitsSetAltitudeType(altitudeType: string) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setAltitudeType(altitudeType);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `setting altitude type to ${altitudeType}`);
|
||||
}
|
||||
|
||||
selectedUnitsSetROE(ROE: string) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true });
|
||||
for (let idx in selectedUnits) {
|
||||
@@ -290,7 +298,23 @@ export class UnitsManager {
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setEmissionsCountermeasures(emissionCountermeasure);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `reaction to threat set to ${emissionCountermeasure}`);
|
||||
this.#showActionMessage(selectedUnits, `emissions & countermeasures set to ${emissionCountermeasure}`);
|
||||
}
|
||||
|
||||
selectedUnitsSetOnOff(onOff: boolean) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setOnOff(onOff);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `unit acitve set to ${onOff}`);
|
||||
}
|
||||
|
||||
selectedUnitsSetFollowRoads(followRoads: boolean) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setFollowRoads(followRoads);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `follow roads set to ${followRoads}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -302,10 +326,10 @@ export class UnitsManager {
|
||||
this.#showActionMessage(selectedUnits, `attacking unit ${this.getUnitByID(ID)?.getBaseData().unitName}`);
|
||||
}
|
||||
|
||||
selectedUnitsDelete() {
|
||||
selectedUnitsDelete(explosion: boolean = false) {
|
||||
var selectedUnits = this.getSelectedUnits(); /* Can be applied to humans too */
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].delete();
|
||||
selectedUnits[idx].delete(explosion);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `deleted`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user