mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
More work on units spawn menu and started to add documentation
This commit is contained in:
@@ -3,8 +3,12 @@ import { Panel } from "../panels/panel";
|
||||
import { Unit } from "../unit/unit";
|
||||
|
||||
export class UnitDataTable extends Panel {
|
||||
constructor(id: string) {
|
||||
super(id);
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID);
|
||||
this.hide();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
import { getMap, getMissionHandler, getUnitsManager, setActiveCoalition } from "..";
|
||||
import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "../constants/constants";
|
||||
import { GAME_MASTER } from "../constants/constants";
|
||||
import { Airbase } from "../mission/airbase";
|
||||
import { dataPointMap } from "../other/utils";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
|
||||
/** This context menu is shown to the user when the airbase marker is right clicked on the map.
|
||||
* It allows the user to inspect information about the airbase, as well as allowing to spawn units from the airbase itself and land units on it. */
|
||||
export class AirbaseContextMenu extends ContextMenu {
|
||||
#airbase: Airbase | null = null;
|
||||
|
||||
constructor(id: string) {
|
||||
super(id);
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID);
|
||||
|
||||
document.addEventListener("contextMenuSpawnAirbase", (e: any) => {
|
||||
this.showSpawnMenu();
|
||||
this.#showSpawnMenu();
|
||||
})
|
||||
|
||||
document.addEventListener("contextMenuLandAirbase", (e: any) => {
|
||||
@@ -20,27 +27,39 @@ export class AirbaseContextMenu extends ContextMenu {
|
||||
})
|
||||
}
|
||||
|
||||
/** Sets the airbase for which data will be shown in the context menu
|
||||
*
|
||||
* @param airbase The airbase for which data will be shown in the context menu. Note: the airbase must be present in the public/databases/airbases/<theatre>.json database.
|
||||
*/
|
||||
setAirbase(airbase: Airbase) {
|
||||
this.#airbase = airbase;
|
||||
this.setName(this.#airbase.getName());
|
||||
this.setProperties(this.#airbase.getProperties());
|
||||
this.setParkings(this.#airbase.getParkings());
|
||||
this.setCoalition(this.#airbase.getCoalition());
|
||||
this.enableLandButton(getUnitsManager().getSelectedUnitsTypes().length == 1 && ["Aircraft", "Helicopter"].includes(getUnitsManager().getSelectedUnitsTypes()[0]) && (getUnitsManager().getSelectedUnitsCoalition() === this.#airbase.getCoalition() || this.#airbase.getCoalition() === "neutral"))
|
||||
this.enableSpawnButton(getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || this.#airbase.getCoalition() == getMissionHandler().getCommandedCoalition());
|
||||
|
||||
|
||||
this.#setName(this.#airbase.getName());
|
||||
this.#setProperties(this.#airbase.getProperties());
|
||||
this.#setParkings(this.#airbase.getParkings());
|
||||
this.#setCoalition(this.#airbase.getCoalition());
|
||||
this.#showLandButton(getUnitsManager().getSelectedUnitsTypes().length == 1 && ["Aircraft", "Helicopter"].includes(getUnitsManager().getSelectedUnitsTypes()[0]) && (getUnitsManager().getSelectedUnitsCoalition() === this.#airbase.getCoalition() || this.#airbase.getCoalition() === "neutral"))
|
||||
this.#showSpawnButton(getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || this.#airbase.getCoalition() == getMissionHandler().getCommandedCoalition());
|
||||
this.#setAirbaseData();
|
||||
|
||||
this.clip();
|
||||
}
|
||||
|
||||
setName(airbaseName: string) {
|
||||
/**
|
||||
*
|
||||
* @param airbaseName The name of the airbase
|
||||
*/
|
||||
#setName(airbaseName: string) {
|
||||
var nameDiv = <HTMLElement>this.getContainer()?.querySelector("#airbase-name");
|
||||
if (nameDiv != null)
|
||||
nameDiv.innerText = airbaseName;
|
||||
}
|
||||
|
||||
setProperties(airbaseProperties: string[]) {
|
||||
/**
|
||||
*
|
||||
* @param airbaseProperties The properties of the airbase
|
||||
*/
|
||||
#setProperties(airbaseProperties: string[]) {
|
||||
this.getContainer()?.querySelector("#airbase-properties")?.replaceChildren(...airbaseProperties.map((property: string) => {
|
||||
var div = document.createElement("div");
|
||||
div.innerText = property;
|
||||
@@ -48,7 +67,11 @@ export class AirbaseContextMenu extends ContextMenu {
|
||||
}),);
|
||||
}
|
||||
|
||||
setParkings(airbaseParkings: string[]) {
|
||||
/**
|
||||
*
|
||||
* @param airbaseParkings List of available parkings at the airbase
|
||||
*/
|
||||
#setParkings(airbaseParkings: string[]) {
|
||||
this.getContainer()?.querySelector("#airbase-parking")?.replaceChildren(...airbaseParkings.map((parking: string) => {
|
||||
var div = document.createElement("div");
|
||||
div.innerText = parking;
|
||||
@@ -56,31 +79,43 @@ export class AirbaseContextMenu extends ContextMenu {
|
||||
}));
|
||||
}
|
||||
|
||||
setCoalition(coalition: string) {
|
||||
(<HTMLElement>this.getContainer()?.querySelector("#spawn-airbase-aircraft-button")).dataset.coalition = coalition;
|
||||
/**
|
||||
*
|
||||
* @param coalition Coalition to which the airbase belongs
|
||||
*/
|
||||
#setCoalition(coalition: string) {
|
||||
(this.getContainer()?.querySelector("#spawn-airbase-aircraft-button") as HTMLElement).dataset.coalition = coalition;
|
||||
}
|
||||
|
||||
enableSpawnButton(enableSpawnButton: boolean) {
|
||||
this.getContainer()?.querySelector("#spawn-airbase-aircraft-button")?.classList.toggle("hide", !enableSpawnButton);
|
||||
/**
|
||||
*
|
||||
* @param showSpawnButton If true, the spawn button will be visibile
|
||||
*/
|
||||
#showSpawnButton(showSpawnButton: boolean) {
|
||||
this.getContainer()?.querySelector("#spawn-airbase-aircraft-button")?.classList.toggle("hide", !showSpawnButton);
|
||||
}
|
||||
|
||||
enableLandButton(enableLandButton: boolean) {
|
||||
this.getContainer()?.querySelector("#land-here-button")?.classList.toggle("hide", !enableLandButton);
|
||||
/**
|
||||
*
|
||||
* @param showLandButton If true, the land button will be visible
|
||||
*/
|
||||
#showLandButton(showLandButton: boolean) {
|
||||
this.getContainer()?.querySelector("#land-here-button")?.classList.toggle("hide", !showLandButton);
|
||||
}
|
||||
|
||||
showSpawnMenu() {
|
||||
/** Shows the spawn context menu which allows the user to select a unit to ground spawn at the airbase
|
||||
*
|
||||
*/
|
||||
#showSpawnMenu() {
|
||||
if (this.#airbase != null) {
|
||||
setActiveCoalition(this.#airbase.getCoalition());
|
||||
getMap().showMapContextMenu(this.getX(), this.getY(), this.getLatLng());
|
||||
getMap().getMapContextMenu().hideUpperBar();
|
||||
getMap().getMapContextMenu().hideLowerBar();
|
||||
getMap().getMapContextMenu().hideAltitudeSlider();
|
||||
getMap().getMapContextMenu().showSubMenu("aircraft");
|
||||
getMap().getMapContextMenu().setAirbase(this.#airbase);
|
||||
getMap().getMapContextMenu().setLatLng(this.#airbase.getLatLng());
|
||||
getMap().showAirbaseSpawnMenu(this.getX(), this.getY(), this.getLatLng(), this.#airbase);
|
||||
}
|
||||
}
|
||||
|
||||
/** @todo needs commenting
|
||||
*
|
||||
*/
|
||||
#setAirbaseData() {
|
||||
if (this.#airbase && this.getContainer()) {
|
||||
dataPointMap(this.getContainer() as HTMLElement, {
|
||||
102
client/src/contextmenus/airbasespawnmenu.ts
Normal file
102
client/src/contextmenus/airbasespawnmenu.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { getActiveCoalition } from "..";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
import { AircraftSpawnMenu, HelicopterSpawnMenu } from "../controls/unitspawnmenu";
|
||||
import { Airbase } from "../mission/airbase";
|
||||
|
||||
/** This context menu is shown when the user wants to spawn a new aircraft or helicopter from the ground at an airbase.
|
||||
* It is shown by clicking on the "spawn" button of a AirbaseContextMenu. */
|
||||
export class AirbaseSpawnContextMenu extends ContextMenu {
|
||||
#aircraftSpawnMenu: AircraftSpawnMenu;
|
||||
#helicopterSpawnMenu: HelicopterSpawnMenu;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID);
|
||||
|
||||
/* Create the spawn menus for the different unit types */
|
||||
this.#aircraftSpawnMenu = new AircraftSpawnMenu("airbase-aircraft-spawn-menu");
|
||||
this.#helicopterSpawnMenu = new HelicopterSpawnMenu("airbase-helicopter-spawn-menu");
|
||||
|
||||
this.#aircraftSpawnMenu.getAltitudeSlider().hide();
|
||||
this.#helicopterSpawnMenu.getAltitudeSlider().hide();
|
||||
|
||||
/* Event listeners */
|
||||
document.addEventListener("mapContextMenuShow", (e: any) => {
|
||||
if (this.getVisibleSubMenu() !== e.detail.type)
|
||||
this.#showSubMenu(e.detail.type);
|
||||
else
|
||||
this.#hideSubMenus();
|
||||
});
|
||||
|
||||
this.#aircraftSpawnMenu.getContainer().addEventListener("resize", () => this.clip());
|
||||
this.#helicopterSpawnMenu.getContainer().addEventListener("resize", () => this.clip());
|
||||
|
||||
this.hide();
|
||||
}
|
||||
|
||||
/** Show the context menu
|
||||
*
|
||||
* @param x X screen coordinate of the top left corner of the context menu
|
||||
* @param y Y screen coordinate of the top left corner of the context menu
|
||||
*/
|
||||
show(x: number, y: number) {
|
||||
super.show(x, y, new LatLng(0, 0));
|
||||
|
||||
this.#aircraftSpawnMenu.setAirbase(undefined);
|
||||
this.#helicopterSpawnMenu.setAirbase(undefined);
|
||||
this.#aircraftSpawnMenu.setCountries();
|
||||
this.#helicopterSpawnMenu.setCountries();
|
||||
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
|
||||
}
|
||||
|
||||
/** Sets the airbase at which the new unit will be spawned
|
||||
*
|
||||
* @param airbase The airbase at which the new unit will be spawned. Note: if the airbase has no suitable parking spots, the airplane may be spawned on the runway, or spawning may fail.
|
||||
*/
|
||||
setAirbase(airbase: Airbase) {
|
||||
this.#aircraftSpawnMenu.setAirbase(airbase);
|
||||
this.#helicopterSpawnMenu.setAirbase(airbase);
|
||||
}
|
||||
|
||||
/** Shows the submenu depending on unit selection
|
||||
*
|
||||
* @param type Submenu type, either "aircraft" or "helicopter"
|
||||
*/
|
||||
#showSubMenu(type: string) {
|
||||
this.getContainer()?.querySelector("#airbase-aircraft-spawn-menu")?.classList.toggle("hide", type !== "aircraft");
|
||||
this.getContainer()?.querySelector("#airbase-aircraft-spawn-button")?.classList.toggle("is-open", type === "aircraft");
|
||||
this.getContainer()?.querySelector("#airbase-helicopter-spawn-menu")?.classList.toggle("hide", type !== "helicopter");
|
||||
this.getContainer()?.querySelector("#airbase-helicopter-spawn-button")?.classList.toggle("is-open", type === "helicopter");
|
||||
|
||||
(this.getContainer()?.querySelectorAll(".deploy-unit-button"))?.forEach((element: Node) => { (element as HTMLButtonElement).disabled = true; })
|
||||
|
||||
this.#aircraftSpawnMenu.reset();
|
||||
this.#aircraftSpawnMenu.setCountries();
|
||||
this.#helicopterSpawnMenu.reset();
|
||||
this.#helicopterSpawnMenu.setCountries();
|
||||
|
||||
this.setVisibleSubMenu(type);
|
||||
this.clip();
|
||||
}
|
||||
|
||||
/** Hide all the open submenus
|
||||
*
|
||||
*/
|
||||
#hideSubMenus() {
|
||||
this.getContainer()?.querySelector("#airbase-aircraft-spawn-menu")?.classList.toggle("hide", true);
|
||||
this.getContainer()?.querySelector("#airbase-aircraft-spawn-button")?.classList.toggle("is-open", false);
|
||||
this.getContainer()?.querySelector("#airbase-helicopter-spawn-menu")?.classList.toggle("hide", true);
|
||||
this.getContainer()?.querySelector("#airbase-helicopter-spawn-button")?.classList.toggle("is-open", false);
|
||||
|
||||
this.#aircraftSpawnMenu.reset();
|
||||
this.#helicopterSpawnMenu.reset();
|
||||
|
||||
this.setVisibleSubMenu(null);
|
||||
this.clip();
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,13 @@ import { getMap, getMissionHandler, getUnitsManager } from "..";
|
||||
import { GAME_MASTER, IADSTypes } from "../constants/constants";
|
||||
import { CoalitionArea } from "../map/coalitionarea";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
import { Dropdown } from "./dropdown";
|
||||
import { Slider } from "./slider";
|
||||
import { Switch } from "./switch";
|
||||
import { Dropdown } from "../controls/dropdown";
|
||||
import { Slider } from "../controls/slider";
|
||||
import { Switch } from "../controls/switch";
|
||||
import { groundUnitDatabase } from "../unit/groundunitdatabase";
|
||||
import { createCheckboxOption, getCheckboxOptions } from "../other/utils";
|
||||
|
||||
/** This context menu allows the user to edit or delete a CoalitionArea. Moreover, it allows the user to create a IADS automatically using the CoalitionArea as bounds. */
|
||||
export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
#coalitionSwitch: Switch;
|
||||
#coalitionArea: CoalitionArea | null = null;
|
||||
@@ -18,16 +19,25 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
#iadsErasDropdown: Dropdown;
|
||||
#iadsRangesDropdown: Dropdown;
|
||||
|
||||
constructor(id: string) {
|
||||
super(id);
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID);
|
||||
|
||||
/* Create the coalition switch */
|
||||
this.#coalitionSwitch = new Switch("coalition-area-switch", (value: boolean) => this.#onSwitchClick(value));
|
||||
this.#coalitionSwitch.setValue(false);
|
||||
|
||||
/* Create the controls of the IADS creation submenu */
|
||||
this.#iadsTypesDropdown = new Dropdown("iads-units-type-options", () => { });
|
||||
this.#iadsErasDropdown = new Dropdown("iads-era-options", () => {});
|
||||
this.#iadsRangesDropdown = new Dropdown("iads-range-options", () => {});
|
||||
this.#iadsDensitySlider = new Slider("iads-density-slider", 5, 100, "%", (value: number) => { });
|
||||
this.#iadsDistributionSlider = new Slider("iads-distribution-slider", 5, 100, "%", (value: number) => { });
|
||||
|
||||
/* Set the default parameters of the sliders */
|
||||
this.#iadsDensitySlider.setIncrement(5);
|
||||
this.#iadsDensitySlider.setValue(50);
|
||||
this.#iadsDensitySlider.setActive(true);
|
||||
@@ -37,9 +47,9 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
|
||||
document.addEventListener("coalitionAreaContextMenuShow", (e: any) => {
|
||||
if (this.getVisibleSubMenu() !== e.detail.type)
|
||||
this.showSubMenu(e.detail.type);
|
||||
this.#showSubMenu(e.detail.type);
|
||||
else
|
||||
this.hideSubMenus();
|
||||
this.#hideSubMenus();
|
||||
});
|
||||
|
||||
document.addEventListener("coalitionAreaBringToBack", (e: any) => {
|
||||
@@ -62,6 +72,12 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x X screen coordinate of the top left corner of the context menu
|
||||
* @param y Y screen coordinate of the top left corner of the context menu
|
||||
* @param latlng Leaflet latlng object of the mouse click
|
||||
*/
|
||||
show(x: number, y: number, latlng: LatLng) {
|
||||
super.show(x, y, latlng);
|
||||
|
||||
@@ -85,26 +101,10 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
this.#coalitionSwitch.hide()
|
||||
}
|
||||
|
||||
showSubMenu(type: string) {
|
||||
this.getContainer()?.querySelector("#iads-menu")?.classList.toggle("hide", type !== "iads");
|
||||
this.getContainer()?.querySelector("#iads-button")?.classList.toggle("is-open", type === "iads");
|
||||
this.clip();
|
||||
|
||||
this.setVisibleSubMenu(type);
|
||||
}
|
||||
|
||||
hideSubMenus() {
|
||||
this.getContainer()?.querySelector("#iads-menu")?.classList.toggle("hide", true);
|
||||
this.getContainer()?.querySelector("#iads-button")?.classList.toggle("is-open", false);
|
||||
this.clip();
|
||||
|
||||
this.setVisibleSubMenu(null);
|
||||
}
|
||||
|
||||
getCoalitionArea() {
|
||||
return this.#coalitionArea;
|
||||
}
|
||||
|
||||
/** Set the CoalitionArea object the user will be able to edit using this menu
|
||||
*
|
||||
* @param coalitionArea The CoalitionArea object to edit
|
||||
*/
|
||||
setCoalitionArea(coalitionArea: CoalitionArea) {
|
||||
this.#coalitionArea = coalitionArea;
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => {
|
||||
@@ -113,6 +113,41 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
this.#coalitionSwitch.setValue(this.getCoalitionArea()?.getCoalition() === "red");
|
||||
}
|
||||
|
||||
/** Get the CoalitionArea object the contextmenu is editing
|
||||
*
|
||||
* @returns The CoalitionArea the contextmenu is editing
|
||||
*/
|
||||
getCoalitionArea() {
|
||||
return this.#coalitionArea;
|
||||
}
|
||||
|
||||
/** Show a submenu of the contextmenu
|
||||
*
|
||||
* @param type The submenu type, currently only "iads"
|
||||
*/
|
||||
#showSubMenu(type: string) {
|
||||
this.getContainer()?.querySelector("#iads-menu")?.classList.toggle("hide", type !== "iads");
|
||||
this.getContainer()?.querySelector("#iads-button")?.classList.toggle("is-open", type === "iads");
|
||||
this.clip();
|
||||
|
||||
this.setVisibleSubMenu(type);
|
||||
}
|
||||
|
||||
/** Hide all submenus
|
||||
*
|
||||
*/
|
||||
#hideSubMenus() {
|
||||
this.getContainer()?.querySelector("#iads-menu")?.classList.toggle("hide", true);
|
||||
this.getContainer()?.querySelector("#iads-button")?.classList.toggle("is-open", false);
|
||||
this.clip();
|
||||
|
||||
this.setVisibleSubMenu(null);
|
||||
}
|
||||
|
||||
/** Callback event called when the coalition switch is clicked to change the coalition of the CoalitionArea
|
||||
*
|
||||
* @param value Switch position (false: blue, true: red)
|
||||
*/
|
||||
#onSwitchClick(value: boolean) {
|
||||
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER) {
|
||||
this.getCoalitionArea()?.setCoalition(value ? "red" : "blue");
|
||||
@@ -1,52 +1,78 @@
|
||||
import { LatLng } from "leaflet";
|
||||
|
||||
/** Base class for map contextmenus. By default it is empty and requires to be extended. */
|
||||
export class ContextMenu {
|
||||
#container: HTMLElement | null;
|
||||
#latlng: LatLng = new LatLng(0, 0);
|
||||
#x: number = 0;
|
||||
#y: number = 0;
|
||||
#visibleSubMenu: string | null = null;
|
||||
#hidden: boolean = true;
|
||||
|
||||
constructor(id: string) {
|
||||
this.#container = document.getElementById(id);
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
this.#container = document.getElementById(ID);
|
||||
this.hide();
|
||||
}
|
||||
|
||||
/** Show the contextmenu on top of the map, usually at the location where the user has clicked on it.
|
||||
*
|
||||
* @param x X screen coordinate of the top left corner of the context menu
|
||||
* @param y Y screen coordinate of the top left corner of the context menu
|
||||
* @param latlng Leaflet latlng object of the mouse click
|
||||
*/
|
||||
show(x: number, y: number, latlng: LatLng) {
|
||||
this.#latlng = latlng;
|
||||
this.#container?.classList.toggle("hide", false);
|
||||
this.#x = x;
|
||||
this.#y = y;
|
||||
this.clip();
|
||||
this.#hidden = false;
|
||||
}
|
||||
|
||||
/** Hide the contextmenu
|
||||
*
|
||||
*/
|
||||
hide() {
|
||||
this.#container?.classList.toggle("hide", true);
|
||||
this.#hidden = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The HTMLElement that contains the contextmenu
|
||||
*/
|
||||
getContainer() {
|
||||
return this.#container;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The Leaflet latlng object associated to the click that caused the contextmenu to be shown
|
||||
*/
|
||||
getLatLng() {
|
||||
return this.#latlng;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The x coordinate of the top left corner of the menu
|
||||
*/
|
||||
getX() {
|
||||
return this.#x;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The y coordinate of the top left corner of the menu
|
||||
*/
|
||||
getY() {
|
||||
return this.#y;
|
||||
}
|
||||
|
||||
getHidden() {
|
||||
return this.#hidden;
|
||||
}
|
||||
|
||||
/** Clips the contextmenu, meaning it moves it on the screen to make sure it does not overflow the window.
|
||||
*
|
||||
*/
|
||||
clip() {
|
||||
if (this.#container != null) {
|
||||
if (this.#x + this.#container.offsetWidth < window.innerWidth)
|
||||
@@ -61,10 +87,18 @@ export class ContextMenu {
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets the currently visible submenu
|
||||
*
|
||||
* @param menu The name of the currently visibile submenu, or null if no submenu is visible
|
||||
*/
|
||||
setVisibleSubMenu(menu: string | null) {
|
||||
this.#visibleSubMenu = menu;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The name of the currently visible submenu
|
||||
*/
|
||||
getVisibleSubMenu() {
|
||||
return this.#visibleSubMenu;
|
||||
}
|
||||
@@ -1,46 +1,48 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { getActiveCoalition, getMap, getMissionHandler, getUnitsManager, setActiveCoalition } from "..";
|
||||
import { getActiveCoalition, getMap, getMissionHandler, setActiveCoalition } from "..";
|
||||
import { spawnExplosion, spawnSmoke } from "../server/server";
|
||||
import { aircraftDatabase } from "../unit/aircraftdatabase";
|
||||
import { groundUnitDatabase } from "../unit/groundunitdatabase";
|
||||
import { helicopterDatabase } from "../unit/helicopterdatabase";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
import { Dropdown } from "./dropdown";
|
||||
import { Switch } from "./switch";
|
||||
import { Slider } from "./slider";
|
||||
import { ftToM } from "../other/utils";
|
||||
import { Switch } from "../controls/switch";
|
||||
import { GAME_MASTER } from "../constants/constants";
|
||||
import { navyUnitDatabase } from "../unit/navyunitdatabase";
|
||||
import { CoalitionArea } from "../map/coalitionarea";
|
||||
import { UnitSpawnMenu } from "./unitspawnmenu";
|
||||
import { AircraftSpawnMenu, GroundUnitSpawnMenu, HelicopterSpawnMenu, NavyUnitSpawnMenu } from "../controls/unitspawnmenu";
|
||||
import { Airbase } from "../mission/airbase";
|
||||
|
||||
/** The MapContextMenu is the main contextmenu shown to the user whenever it rightclicks on the map. It is the primary interaction method for the user.
|
||||
* It allows to spawn units, create explosions and smoke, and edit CoalitionAreas.
|
||||
*/
|
||||
export class MapContextMenu extends ContextMenu {
|
||||
#coalitionSwitch: Switch;
|
||||
#aircraftSpawnMenu: UnitSpawnMenu;
|
||||
#helicopterSpawnMenu: UnitSpawnMenu;
|
||||
#groundUnitSpawnMenu: UnitSpawnMenu;
|
||||
#navyUnitSpawnMenu: UnitSpawnMenu;
|
||||
|
||||
#aircraftSpawnMenu: AircraftSpawnMenu;
|
||||
#helicopterSpawnMenu: HelicopterSpawnMenu;
|
||||
#groundUnitSpawnMenu: GroundUnitSpawnMenu;
|
||||
#navyUnitSpawnMenu: NavyUnitSpawnMenu;
|
||||
#coalitionArea: CoalitionArea | null = null;
|
||||
|
||||
constructor(id: string) {
|
||||
super(id);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID);
|
||||
|
||||
/* Create the coalition switch */
|
||||
this.#coalitionSwitch = new Switch("coalition-switch", (value: boolean) => this.#onSwitchClick(value));
|
||||
this.#coalitionSwitch.setValue(false);
|
||||
this.#coalitionSwitch.getContainer()?.addEventListener("contextmenu", (e) => this.#onSwitchRightClick(e));
|
||||
this.#coalitionSwitch.getContainer()?.addEventListener("contextmenu", (e) => this.#onSwitchRightClick());
|
||||
|
||||
this.#aircraftSpawnMenu = new UnitSpawnMenu("aircraft-spawn-menu", aircraftDatabase);
|
||||
this.#helicopterSpawnMenu = new UnitSpawnMenu("helicopter-spawn-menu", helicopterDatabase);
|
||||
this.#groundUnitSpawnMenu = new UnitSpawnMenu("groundunit-spawn-menu", groundUnitDatabase, {orderByRole: false, maxUnitCount: 20, showLoadout: false, showAltitudeSlider: false});
|
||||
this.#navyUnitSpawnMenu = new UnitSpawnMenu("navyunit-spawn-menu", navyUnitDatabase, {orderByRole: false, maxUnitCount: 4, showLoadout: false, showAltitudeSlider: false});
|
||||
/* Create the spawn menus for the different unit types */
|
||||
this.#aircraftSpawnMenu = new AircraftSpawnMenu("aircraft-spawn-menu");
|
||||
this.#helicopterSpawnMenu = new HelicopterSpawnMenu("helicopter-spawn-menu");
|
||||
this.#groundUnitSpawnMenu = new GroundUnitSpawnMenu("groundunit-spawn-menu");
|
||||
this.#navyUnitSpawnMenu = new NavyUnitSpawnMenu("navyunit-spawn-menu");
|
||||
|
||||
/* Event listeners */
|
||||
document.addEventListener("mapContextMenuShow", (e: any) => {
|
||||
if (this.getVisibleSubMenu() !== e.detail.type)
|
||||
this.showSubMenu(e.detail.type);
|
||||
else
|
||||
this.hideSubMenus(e.detail.type);
|
||||
this.#showSubMenu(e.detail.type);
|
||||
else
|
||||
this.#hideSubMenus(e.detail.type);
|
||||
});
|
||||
|
||||
document.addEventListener("contextMenuDeploySmoke", (e: any) => {
|
||||
@@ -52,7 +54,7 @@ export class MapContextMenu extends ContextMenu {
|
||||
this.hide();
|
||||
spawnExplosion(e.detail.strength, this.getLatLng());
|
||||
});
|
||||
|
||||
|
||||
document.addEventListener("editCoalitionArea", (e: any) => {
|
||||
this.hide();
|
||||
if (this.#coalitionArea) {
|
||||
@@ -65,14 +67,6 @@ export class MapContextMenu extends ContextMenu {
|
||||
//this.#refreshOptions();
|
||||
});
|
||||
|
||||
document.addEventListener("toggleAdvancedOptions", (e: any) => {
|
||||
if (e.detail.type === "aircraft")
|
||||
document.querySelector("#aircraft-advanced-options")?.classList.toggle("hide");
|
||||
else if (e.detail.type === "helicopter")
|
||||
document.querySelector("#helicopter-advanced-options")?.classList.toggle("hide");
|
||||
this.clip();
|
||||
});
|
||||
|
||||
this.#aircraftSpawnMenu.getContainer().addEventListener("resize", () => this.clip());
|
||||
this.#helicopterSpawnMenu.getContainer().addEventListener("resize", () => this.clip());
|
||||
this.#groundUnitSpawnMenu.getContainer().addEventListener("resize", () => this.clip());
|
||||
@@ -81,42 +75,59 @@ export class MapContextMenu extends ContextMenu {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
/** Show the contextmenu on top of the map, usually at the location where the user has clicked on it.
|
||||
*
|
||||
* @param x X screen coordinate of the top left corner of the context menu
|
||||
* @param y Y screen coordinate of the top left corner of the context menu
|
||||
* @param latlng Leaflet latlng object of the mouse click
|
||||
*/
|
||||
show(x: number, y: number, latlng: LatLng) {
|
||||
super.show(x, y, latlng);
|
||||
this.showUpperBar();
|
||||
|
||||
this.showAltitudeSlider();
|
||||
|
||||
this.#aircraftSpawnMenu.setAirbase(undefined);
|
||||
this.#aircraftSpawnMenu.setLatLng(latlng);
|
||||
this.#helicopterSpawnMenu.setAirbase(undefined);
|
||||
this.#helicopterSpawnMenu.setLatLng(latlng);
|
||||
this.#groundUnitSpawnMenu.setLatLng(latlng);
|
||||
this.#navyUnitSpawnMenu.setLatLng(latlng);
|
||||
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
|
||||
if (getActiveCoalition() == "blue")
|
||||
this.#coalitionSwitch.setValue(false);
|
||||
else if (getActiveCoalition() == "red")
|
||||
this.#coalitionSwitch.setValue(true);
|
||||
else
|
||||
this.#coalitionSwitch.setValue(undefined);
|
||||
|
||||
if (getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER)
|
||||
this.#coalitionSwitch.hide()
|
||||
|
||||
this.getContainer()?.querySelector("#coalition-area-button")?.classList.toggle("hide", true);
|
||||
|
||||
this.#aircraftSpawnMenu.setCountries();
|
||||
this.#helicopterSpawnMenu.setCountries();
|
||||
this.#groundUnitSpawnMenu.setCountries();
|
||||
this.#navyUnitSpawnMenu.setCountries();
|
||||
|
||||
/* Only a Game Master can choose the coalition of a new unit */
|
||||
if (getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) {
|
||||
this.#coalitionSwitch.hide()
|
||||
} else {
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
|
||||
if (getActiveCoalition() == "blue")
|
||||
this.#coalitionSwitch.setValue(false);
|
||||
else if (getActiveCoalition() == "red")
|
||||
this.#coalitionSwitch.setValue(true);
|
||||
else
|
||||
this.#coalitionSwitch.setValue(undefined);
|
||||
}
|
||||
|
||||
/* Hide the coalition area button. It will be visible if a coalition area is set */
|
||||
this.getContainer()?.querySelector("#coalition-area-button")?.classList.toggle("hide", true);
|
||||
}
|
||||
|
||||
showSubMenu(type: string) {
|
||||
/** If the user rightclicked on a CoalitionArea, it will be given the ability to edit it.
|
||||
*
|
||||
* @param coalitionArea The CoalitionArea the user can edit
|
||||
*/
|
||||
setCoalitionArea(coalitionArea: CoalitionArea) {
|
||||
this.#coalitionArea = coalitionArea;
|
||||
this.getContainer()?.querySelector("#coalition-area-button")?.classList.toggle("hide", false);
|
||||
}
|
||||
|
||||
/** Shows the submenu depending on unit selection
|
||||
*
|
||||
* @param type Submenu type, either "aircraft", "helicopter", "groundunit", "navyunit", "smoke", or "explosion"
|
||||
*/
|
||||
#showSubMenu(type: string) {
|
||||
if (type === "more")
|
||||
this.getContainer()?.querySelector("#more-options-button-bar")?.classList.toggle("hide");
|
||||
else if (["aircraft", "groundunit"].includes(type))
|
||||
else if (["aircraft", "helicopter", "groundunit"].includes(type))
|
||||
this.getContainer()?.querySelector("#more-options-button-bar")?.classList.toggle("hide", true);
|
||||
|
||||
this.getContainer()?.querySelector("#aircraft-spawn-menu")?.classList.toggle("hide", type !== "aircraft");
|
||||
@@ -132,7 +143,7 @@ export class MapContextMenu extends ContextMenu {
|
||||
this.getContainer()?.querySelector("#explosion-menu")?.classList.toggle("hide", type !== "explosion");
|
||||
this.getContainer()?.querySelector("#explosion-spawn-button")?.classList.toggle("is-open", type === "explosion");
|
||||
|
||||
(this.getContainer()?.querySelectorAll(".deploy-unit-button"))?.forEach((element: Node) => {(element as HTMLButtonElement).disabled = true;})
|
||||
(this.getContainer()?.querySelectorAll(".deploy-unit-button"))?.forEach((element: Node) => { (element as HTMLButtonElement).disabled = true; })
|
||||
|
||||
this.#aircraftSpawnMenu.reset();
|
||||
this.#aircraftSpawnMenu.setCountries();
|
||||
@@ -142,13 +153,18 @@ export class MapContextMenu extends ContextMenu {
|
||||
this.#groundUnitSpawnMenu.setCountries();
|
||||
this.#navyUnitSpawnMenu.reset();
|
||||
this.#navyUnitSpawnMenu.setCountries();
|
||||
|
||||
|
||||
this.setVisibleSubMenu(type);
|
||||
this.clip();
|
||||
}
|
||||
|
||||
hideSubMenus(type: string) {
|
||||
this.getContainer()?.querySelector("#more-options-button-bar")?.classList.toggle("hide", ["aircraft", "groundunit"].includes(type));
|
||||
/** Hide all the submenus
|
||||
*
|
||||
* @param type The type of the currenlt open submenu.
|
||||
*/
|
||||
#hideSubMenus(type: string) {
|
||||
/* Close the lower options bar if the currently open submenu does not required it */
|
||||
this.getContainer()?.querySelector("#more-options-button-bar")?.classList.toggle("hide", ["aircraft", "helicopter", "groundunit"].includes(type));
|
||||
this.getContainer()?.querySelector("#aircraft-spawn-menu")?.classList.toggle("hide", true);
|
||||
this.getContainer()?.querySelector("#aircraft-spawn-button")?.classList.toggle("is-open", false);
|
||||
this.getContainer()?.querySelector("#helicopter-spawn-menu")?.classList.toggle("hide", true);
|
||||
@@ -171,49 +187,12 @@ export class MapContextMenu extends ContextMenu {
|
||||
this.clip();
|
||||
}
|
||||
|
||||
showUpperBar() {
|
||||
this.getContainer()?.querySelector(".upper-bar")?.classList.toggle("hide", false);
|
||||
}
|
||||
|
||||
hideUpperBar() {
|
||||
this.getContainer()?.querySelector(".upper-bar")?.classList.toggle("hide", true);
|
||||
}
|
||||
|
||||
showLowerBar() {
|
||||
this.getContainer()?.querySelector("#more-options-button-bar")?.classList.toggle("hide", false);
|
||||
}
|
||||
|
||||
hideLowerBar() {
|
||||
this.getContainer()?.querySelector("#more-optionsbutton-bar")?.classList.toggle("hide", true);
|
||||
}
|
||||
|
||||
showAltitudeSlider() {
|
||||
this.getContainer()?.querySelector("#aircraft-spawn-altitude-slider")?.classList.toggle("hide", false);
|
||||
}
|
||||
|
||||
hideAltitudeSlider() {
|
||||
this.getContainer()?.querySelector("#aircraft-spawn-altitude-slider")?.classList.toggle("hide", true);
|
||||
}
|
||||
|
||||
setAirbase(airbase: Airbase) {
|
||||
this.#aircraftSpawnMenu.setAirbase(airbase);
|
||||
this.#helicopterSpawnMenu.setAirbase(airbase);
|
||||
}
|
||||
|
||||
setLatLng(latlng: LatLng) {
|
||||
this.#aircraftSpawnMenu.setLatLng(latlng);
|
||||
this.#helicopterSpawnMenu.setLatLng(latlng);
|
||||
this.#groundUnitSpawnMenu.setLatLng(latlng);
|
||||
this.#navyUnitSpawnMenu.setLatLng(latlng);
|
||||
}
|
||||
|
||||
setCoalitionArea(coalitionArea: CoalitionArea) {
|
||||
this.#coalitionArea = coalitionArea;
|
||||
this.getContainer()?.querySelector("#coalition-area-button")?.classList.toggle("hide", false);
|
||||
}
|
||||
|
||||
/** Callback called when the user left clicks on the coalition switch
|
||||
*
|
||||
* @param value Switch position (false: "blue", true: "red")
|
||||
*/
|
||||
#onSwitchClick(value: boolean) {
|
||||
value? setActiveCoalition("red"): setActiveCoalition("blue");
|
||||
value ? setActiveCoalition("red") : setActiveCoalition("blue");
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
|
||||
this.#aircraftSpawnMenu.setCountries();
|
||||
this.#helicopterSpawnMenu.setCountries();
|
||||
@@ -221,7 +200,10 @@ export class MapContextMenu extends ContextMenu {
|
||||
this.#navyUnitSpawnMenu.setCountries();
|
||||
}
|
||||
|
||||
#onSwitchRightClick(e: any) {
|
||||
/** Callback called when the user rightclicks on the coalition switch. This will select the "neutral" coalition.
|
||||
*
|
||||
*/
|
||||
#onSwitchRightClick() {
|
||||
this.#coalitionSwitch.setValue(undefined);
|
||||
setActiveCoalition("neutral");
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
|
||||
32
client/src/contextmenus/unitcontextmenu.ts
Normal file
32
client/src/contextmenus/unitcontextmenu.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { deg2rad, ftToM } from "../other/utils";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
|
||||
/** The UnitContextMenu is shown when the user rightclicks on a unit. It dynamically presents the user with possible actions to perform on the unit. */
|
||||
export class UnitContextMenu extends ContextMenu {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID);
|
||||
}
|
||||
|
||||
/** Set the options that will be presented to the user in the contextmenu
|
||||
*
|
||||
* @param options Dictionary element containing the text and tooltip of the options shown in the menu
|
||||
* @param callback Callback that will be called when the user clicks on one of the options
|
||||
*/
|
||||
setOptions(options: { [key: string]: {text: string, tooltip: string }}, callback: CallableFunction) {
|
||||
this.getContainer()?.replaceChildren(...Object.keys(options).map((key: string, idx: number) => {
|
||||
const option = options[key];
|
||||
var button = document.createElement("button");
|
||||
var el = document.createElement("div");
|
||||
el.title = option.tooltip;
|
||||
el.innerText = option.text;
|
||||
el.id = key;
|
||||
button.addEventListener("click", () => callback(key));
|
||||
button.appendChild(el);
|
||||
return (button);
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,11 @@ export class Control {
|
||||
#container: HTMLElement | null;
|
||||
expectedValue: any = null;
|
||||
|
||||
constructor(ID: string) {
|
||||
this.#container = document.getElementById(ID);
|
||||
constructor(container: string | null, options?: any) {
|
||||
if (typeof container === "string")
|
||||
this.#container = document.getElementById(container);
|
||||
else
|
||||
this.#container = this.createElement(options);
|
||||
}
|
||||
|
||||
show() {
|
||||
@@ -31,4 +34,8 @@ export class Control {
|
||||
checkExpectedValue(value: any) {
|
||||
return this.expectedValue === null || value === this.expectedValue;
|
||||
}
|
||||
|
||||
createElement(options?: any): HTMLElement | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
export class Dropdown {
|
||||
#element: HTMLElement;
|
||||
#container: HTMLElement;
|
||||
#options: HTMLElement;
|
||||
#value: HTMLElement;
|
||||
#callback: CallableFunction;
|
||||
@@ -7,14 +7,14 @@ export class Dropdown {
|
||||
#optionsList: string[] = [];
|
||||
#index: number = 0;
|
||||
|
||||
constructor(element: string | HTMLElement, callback: CallableFunction, options: string[] | null = null) {
|
||||
if (typeof element === 'string')
|
||||
this.#element = document.getElementById(element) as HTMLElement;
|
||||
constructor(ID: string | null, callback: CallableFunction, options: string[] | null = null, defaultText?: string) {
|
||||
if (ID === null)
|
||||
this.#container = this.#createElement(defaultText);
|
||||
else
|
||||
this.#element = element;
|
||||
this.#container = document.getElementById(ID) as HTMLElement;
|
||||
|
||||
this.#options = this.#element.querySelector(".ol-select-options") as HTMLElement;
|
||||
this.#value = this.#element.querySelector(".ol-select-value") as HTMLElement;
|
||||
this.#options = this.#container.querySelector(".ol-select-options") as HTMLElement;
|
||||
this.#value = this.#container.querySelector(".ol-select-value") as HTMLElement;
|
||||
this.#defaultValue = this.#value.innerText;
|
||||
this.#callback = callback;
|
||||
|
||||
@@ -25,7 +25,7 @@ export class Dropdown {
|
||||
this.#value.addEventListener("click", (ev) => { 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))) {
|
||||
if (!(this.#value.contains(ev.target as Node) || this.#options.contains(ev.target as Node) || this.#container.contains(ev.target as Node))) {
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
@@ -33,6 +33,10 @@ export class Dropdown {
|
||||
this.#options.classList.add("ol-scrollable");
|
||||
}
|
||||
|
||||
getContainer() {
|
||||
return this.#container;
|
||||
}
|
||||
|
||||
setOptions(optionsList: string[], sortAlphabetically: boolean = true) {
|
||||
this.#optionsList = optionsList.sort();
|
||||
if (this.#optionsList.length == 0) {
|
||||
@@ -120,21 +124,44 @@ export class Dropdown {
|
||||
clip() {
|
||||
const options = this.#options;
|
||||
const bounds = options.getBoundingClientRect();
|
||||
this.#element.dataset.position = (bounds.bottom > window.innerHeight) ? "top" : "";
|
||||
this.#container.dataset.position = (bounds.bottom > window.innerHeight) ? "top" : "";
|
||||
}
|
||||
|
||||
close() {
|
||||
this.#element.classList.remove("is-open");
|
||||
this.#element.dataset.position = "";
|
||||
this.#container.classList.remove("is-open");
|
||||
this.#container.dataset.position = "";
|
||||
}
|
||||
|
||||
open() {
|
||||
this.#element.classList.add("is-open");
|
||||
this.#container.classList.add("is-open");
|
||||
this.#options.classList.toggle("scrollbar-visible", this.#options.scrollHeight > this.#options.clientHeight);
|
||||
this.clip();
|
||||
}
|
||||
|
||||
show() {
|
||||
this.#container.classList.add("show");
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.#container.classList.add("hide");
|
||||
}
|
||||
|
||||
#toggle() {
|
||||
this.#element.classList.contains("is-open")? this.close(): this.open();
|
||||
this.#container.classList.contains("is-open")? this.close(): this.open();
|
||||
}
|
||||
|
||||
#createElement(defaultText: string | undefined) {
|
||||
var div = document.createElement("div");
|
||||
div.classList.add("ol-select");
|
||||
|
||||
var value = document.createElement("div");
|
||||
value.classList.add("ol-select-value");
|
||||
value.innerText = defaultText? defaultText: "";
|
||||
|
||||
var options = document.createElement("div");
|
||||
options.classList.add("ol-select-options");
|
||||
|
||||
div.append(value, options);
|
||||
return div;
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,9 @@ export class Slider extends Control {
|
||||
#dragged: boolean = false;
|
||||
#value: number = 0;
|
||||
|
||||
constructor(ID: string, minValue: number, maxValue: number, unitOfMeasure: string, callback: CallableFunction) {
|
||||
super(ID);
|
||||
constructor(ID: string | null, minValue: number, maxValue: number, unitOfMeasure: string, callback: CallableFunction, options?: any) {
|
||||
super(ID, options);
|
||||
|
||||
this.#callback = callback;
|
||||
this.#unitOfMeasure = unitOfMeasure;
|
||||
this.#slider = this.getContainer()?.querySelector("input") as HTMLInputElement;
|
||||
@@ -120,4 +121,31 @@ export class Slider extends Control {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createElement(options?: any): HTMLElement | null {
|
||||
var containerEl = document.createElement("div");
|
||||
containerEl.classList.add("ol-slider-container", "flight-control-ol-slider");
|
||||
|
||||
var dl = document.createElement("dl");
|
||||
dl.classList.add("ol-data-grid");
|
||||
|
||||
var dt = document.createElement("dt");
|
||||
dt.innerText = (options !== undefined && options.title !== undefined)? options.title: "";
|
||||
|
||||
var dd = document.createElement("dd");
|
||||
var sliderEl = document.createElement("div");
|
||||
sliderEl.classList.add("ol-slider-value");
|
||||
dd.append(sliderEl);
|
||||
dl.append(dt, dd);
|
||||
|
||||
var input = document.createElement("input") as HTMLInputElement;
|
||||
input.type = "range";
|
||||
input.min = "0";
|
||||
input.max = "100";
|
||||
input.value = "0"
|
||||
input.classList.add("ol-slider");
|
||||
containerEl.append(dl, input);
|
||||
|
||||
return containerEl;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ export class Switch extends Control {
|
||||
#value: boolean | undefined = false;
|
||||
#callback: CallableFunction | null = null;
|
||||
|
||||
// TODO: allow for null ID so that the element is created automatically
|
||||
constructor(ID: string, callback: CallableFunction, initialValue?: boolean) {
|
||||
super(ID);
|
||||
this.getContainer()?.addEventListener('click', (e) => this.#onToggle());
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import { deg2rad, ftToM } from "../other/utils";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
|
||||
export class UnitContextMenu extends ContextMenu {
|
||||
#customFormationCallback: CallableFunction | null = null;
|
||||
|
||||
constructor(id: string) {
|
||||
super(id);
|
||||
|
||||
document.addEventListener("applyCustomFormation", () => {
|
||||
var dialog = document.getElementById("custom-formation-dialog");
|
||||
if (dialog) {
|
||||
dialog.classList.add("hide");
|
||||
var clock = 1;
|
||||
while (clock < 8) {
|
||||
if ((<HTMLInputElement>dialog.querySelector(`#formation-${clock}`)).checked)
|
||||
break
|
||||
clock++;
|
||||
}
|
||||
var angleDeg = 360 - (clock - 1) * 45;
|
||||
var angleRad = deg2rad(angleDeg);
|
||||
var distance = ftToM(parseInt((<HTMLInputElement>dialog.querySelector(`#distance`)?.querySelector("input")).value));
|
||||
var upDown = ftToM(parseInt((<HTMLInputElement>dialog.querySelector(`#up-down`)?.querySelector("input")).value));
|
||||
|
||||
// X: front-rear, positive front
|
||||
// Y: top-bottom, positive top
|
||||
// Z: left-right, positive right
|
||||
|
||||
var x = distance * Math.cos(angleRad);
|
||||
var y = upDown;
|
||||
var z = distance * Math.sin(angleRad);
|
||||
|
||||
if (this.#customFormationCallback)
|
||||
this.#customFormationCallback({ "x": x, "y": y, "z": z })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setCustomFormationCallback(callback: CallableFunction) {
|
||||
this.#customFormationCallback = callback;
|
||||
}
|
||||
|
||||
setOptions(options: { [key: string]: {text: string, tooltip: string }}, callback: CallableFunction) {
|
||||
this.getContainer()?.replaceChildren(...Object.keys(options).map((key: string, idx: number) => {
|
||||
const option = options[key];
|
||||
var button = document.createElement("button");
|
||||
var el = document.createElement("div");
|
||||
el.title = option.tooltip;
|
||||
el.innerText = option.text;
|
||||
el.id = key;
|
||||
button.addEventListener("click", () => callback(key));
|
||||
button.appendChild(el);
|
||||
return (button);
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -6,88 +6,74 @@ import { getActiveCoalition, getMap, getMissionHandler, getUnitsManager } from "
|
||||
import { GAME_MASTER } from "../constants/constants";
|
||||
import { UnitSpawnOptions } from "../@types/unitdatabase";
|
||||
import { Airbase } from "../mission/airbase";
|
||||
import { ftToM } from "../other/utils";
|
||||
import { aircraftDatabase } from "../unit/aircraftdatabase";
|
||||
import { helicopterDatabase } from "../unit/helicopterdatabase";
|
||||
import { groundUnitDatabase } from "../unit/groundunitdatabase";
|
||||
import { navyUnitDatabase } from "../unit/navyunitdatabase";
|
||||
|
||||
export class UnitSpawnMenu {
|
||||
#container: HTMLElement;
|
||||
#unitDatabase: UnitDatabase;
|
||||
#countryCodes: any;
|
||||
#orderByRole: boolean;
|
||||
#spawnOptions: UnitSpawnOptions = {
|
||||
roleType: "",
|
||||
name: "",
|
||||
latlng: new LatLng(0, 0),
|
||||
coalition: "blue",
|
||||
count: 1,
|
||||
country: "",
|
||||
loadout: undefined,
|
||||
airbase: undefined,
|
||||
liveryID: undefined,
|
||||
altitude: undefined
|
||||
};
|
||||
|
||||
/* Controls */
|
||||
#unitRoleTypeDropdown: Dropdown;
|
||||
#unitLabelDropdown: Dropdown;
|
||||
#unitCountDropdown: Dropdown;
|
||||
#unitLoadoutDropdown: Dropdown | null = null;
|
||||
#unitLoadoutDropdown: Dropdown;
|
||||
#unitCountryDropdown: Dropdown;
|
||||
#unitLiveryDropdown: Dropdown;
|
||||
//#unitSpawnAltitudeSlider: Slider;
|
||||
#unitSpawnAltitudeSlider: Slider;
|
||||
|
||||
#unitRoleTypeDropdownEl: HTMLDivElement;
|
||||
#unitLabelDropdownEl: HTMLDivElement;
|
||||
#unitLoadoutDropdownEl: HTMLDivElement | null = null;
|
||||
#unitCountDropdownEl: HTMLDivElement;
|
||||
#unitCountryDropdownEl: HTMLDivElement;
|
||||
#unitLiveryDropdownEl: HTMLDivElement;
|
||||
/* HTML Elements */
|
||||
#deployUnitButtonEl: HTMLButtonElement;
|
||||
#unitLoadoutPreviewEl: HTMLDivElement | null = null;
|
||||
#unitImageEl: HTMLImageElement | null = null;
|
||||
#unitLoadoutListEl: HTMLDivElement | null = null;
|
||||
#unitLoadoutPreviewEl: HTMLDivElement;
|
||||
#unitImageEl: HTMLImageElement;
|
||||
#unitLoadoutListEl: HTMLDivElement;
|
||||
|
||||
#container: HTMLElement;
|
||||
#options = {orderByRole: true, maxUnitCount: 4, showLoadout: true, showAltitudeSlider: true};
|
||||
#spawnOptions: UnitSpawnOptions = { roleType: "", name: "", latlng: new LatLng(0, 0), coalition: "blue", count: 1, country: "", loadout: undefined, airbase: undefined, liveryID: undefined, altitude: undefined };
|
||||
#unitDatabase: UnitDatabase;
|
||||
#countryCodes: any;
|
||||
|
||||
constructor(id: string, unitDatabase: UnitDatabase, options?: {orderByRole: boolean, maxUnitCount: number, showLoadout: boolean, showAltitudeSlider: boolean}) {
|
||||
this.#container = document.getElementById(id) as HTMLElement;
|
||||
constructor(ID: string, unitDatabase: UnitDatabase, orderByRole: boolean) {
|
||||
this.#container = document.getElementById(ID) as HTMLElement;
|
||||
this.#unitDatabase = unitDatabase;
|
||||
this.#orderByRole = orderByRole;
|
||||
|
||||
if (options !== undefined)
|
||||
this.#options = options;
|
||||
|
||||
/* Create the HTML elements for the dropdowns */
|
||||
if (this.#options.orderByRole)
|
||||
this.#unitRoleTypeDropdownEl = this.#addDropdown("Unit role");
|
||||
else
|
||||
this.#unitRoleTypeDropdownEl = this.#addDropdown("Unit type");
|
||||
this.#unitLabelDropdownEl = this.#addDropdown("Unit label");
|
||||
if (this.#options.showLoadout)
|
||||
this.#unitLoadoutDropdownEl = this.#addDropdown("Unit loadout");
|
||||
this.#unitCountDropdownEl = this.#addDropdown("Unit count");
|
||||
this.#unitCountryDropdownEl = this.#addDropdown("Unit country");
|
||||
this.#unitLiveryDropdownEl = this.#addDropdown("Unit livery");
|
||||
/* Create the dropdowns and the altitude slider */
|
||||
this.#unitRoleTypeDropdown = new Dropdown(null, (roleType: string) => this.#setUnitRoleType(roleType), undefined, "Unit type");
|
||||
this.#unitLabelDropdown = new Dropdown(null, (label: string) => this.#setUnitLabel(label), undefined, "Unit label");
|
||||
this.#unitLoadoutDropdown = new Dropdown(null, (loadout: string) => this.#setUnitLoadout(loadout), undefined, "Unit loadout");
|
||||
this.#unitCountDropdown = new Dropdown(null, (count: string) => this.#setUnitCount(count), undefined, "Unit count");
|
||||
this.#unitCountryDropdown = new Dropdown(null, () => { /* Custom button implementation */ }, undefined, "Unit country");
|
||||
this.#unitLiveryDropdown = new Dropdown(null, (livery: string) => this.#setUnitLivery(livery), undefined, "Unit livery");
|
||||
this.#unitSpawnAltitudeSlider = new Slider(null, 0, 1000, "ft", (value: number) => { this.#spawnOptions.altitude = ftToM(value); }, { title: "Spawn altitude" });
|
||||
|
||||
/* The unit label and unit count are in the same "row" for clarity and compactness */
|
||||
var unitLabelCountContainerEl = document.createElement("div");
|
||||
unitLabelCountContainerEl.classList.add("unit-label-count-container");
|
||||
var divider = document.createElement("div");
|
||||
divider.innerText = "x";
|
||||
unitLabelCountContainerEl.append(this.#unitLabelDropdownEl, divider, this.#unitCountDropdownEl);
|
||||
|
||||
/* Create the dropdowns and the altitude slider */
|
||||
this.#unitRoleTypeDropdown = new Dropdown(this.#unitRoleTypeDropdownEl, (roleType: string) => this.#setUnitRoleType(roleType));
|
||||
this.#unitLabelDropdown = new Dropdown(this.#unitLabelDropdownEl, (label: string) => this.#setUnitLabel(label));
|
||||
if (this.#unitLoadoutDropdownEl !== null)
|
||||
this.#unitLoadoutDropdown = new Dropdown(this.#unitLoadoutDropdownEl, (loadout: string) => this.#setUnitLoadout(loadout));
|
||||
this.#unitCountDropdown = new Dropdown(this.#unitCountDropdownEl, (count: string) => this.#setUnitCount(count));
|
||||
this.#unitCountryDropdown = new Dropdown(this.#unitCountryDropdownEl, () => { /* Custom button implementation */ });
|
||||
this.#unitLiveryDropdown = new Dropdown(this.#unitLiveryDropdownEl, (livery: string) => this.#setUnitLivery(livery));
|
||||
//this.#unitSpawnAltitudeSlider = new Slider("unit-spawn-altitude-slider", 0, 50000, "ft", (value: number) => {this.#spawnOptions.altitude = ftToM(value);});
|
||||
|
||||
var countOptions: string[] = [];
|
||||
for (let i = 1; i <= this.#options.maxUnitCount; i++)
|
||||
countOptions.push(i.toString());
|
||||
this.#unitCountDropdown.setOptions(countOptions);
|
||||
this.#unitCountDropdown.selectValue(0);
|
||||
|
||||
//this.#unitSpawnAltitudeSlider.setIncrement(500);
|
||||
//this.#unitSpawnAltitudeSlider.setValue(20000);
|
||||
//this.#unitSpawnAltitudeSlider.setActive(true);
|
||||
unitLabelCountContainerEl.append(this.#unitLabelDropdown.getContainer(), divider, this.#unitCountDropdown.getContainer());
|
||||
|
||||
/* Create the unit image and loadout elements */
|
||||
if (this.#options.showLoadout) {
|
||||
this.#unitLoadoutPreviewEl = document.createElement("div");
|
||||
this.#unitLoadoutPreviewEl.classList.add("unit-loadout-preview");
|
||||
this.#unitImageEl = document.createElement("img");
|
||||
this.#unitImageEl.classList.add("unit-image", "hide");
|
||||
this.#unitLoadoutListEl = document.createElement("div");
|
||||
this.#unitLoadoutListEl.classList.add("unit-loadout-list");
|
||||
this.#unitLoadoutPreviewEl.append(this.#unitImageEl, this.#unitLoadoutListEl);
|
||||
}
|
||||
this.#unitLoadoutPreviewEl = document.createElement("div");
|
||||
this.#unitLoadoutPreviewEl.classList.add("unit-loadout-preview");
|
||||
this.#unitImageEl = document.createElement("img");
|
||||
this.#unitImageEl.classList.add("unit-image", "hide");
|
||||
this.#unitLoadoutListEl = document.createElement("div");
|
||||
this.#unitLoadoutListEl.classList.add("unit-loadout-list");
|
||||
this.#unitLoadoutPreviewEl.append(this.#unitImageEl, this.#unitLoadoutListEl);
|
||||
|
||||
/* Create the divider and the advanced options collapsible div */
|
||||
var advancedOptionsDiv = document.createElement("div");
|
||||
@@ -98,10 +84,9 @@ export class UnitSpawnMenu {
|
||||
advancedOptionsText.innerText = "Advanced options";
|
||||
var advancedOptionsHr = document.createElement("hr");
|
||||
advancedOptionsToggle.append(advancedOptionsText, advancedOptionsHr);
|
||||
advancedOptionsToggle.addEventListener("click", () => {advancedOptionsDiv.classList.toggle("hide")});
|
||||
advancedOptionsDiv.append(this.#unitCountryDropdownEl, this.#unitLiveryDropdownEl);
|
||||
if (this.#unitLoadoutPreviewEl !== null)
|
||||
advancedOptionsDiv.append(this.#unitLoadoutPreviewEl)
|
||||
advancedOptionsToggle.addEventListener("click", () => { advancedOptionsDiv.classList.toggle("hide") });
|
||||
advancedOptionsDiv.append(this.#unitCountryDropdown.getContainer(), this.#unitLiveryDropdown.getContainer(),
|
||||
this.#unitLoadoutPreviewEl, this.#unitSpawnAltitudeSlider.getContainer() as HTMLElement);
|
||||
|
||||
/* Create the unit deploy button */
|
||||
this.#deployUnitButtonEl = document.createElement("button");
|
||||
@@ -112,40 +97,38 @@ export class UnitSpawnMenu {
|
||||
this.#deployUnitButtonEl.addEventListener("click", () => { this.#deployUnits(); });
|
||||
|
||||
/* Assemble all components */
|
||||
this.#container.append(this.#unitRoleTypeDropdownEl, unitLabelCountContainerEl)
|
||||
if (this.#unitLoadoutDropdownEl !== null)
|
||||
this.#container.append(this.#unitLoadoutDropdownEl)
|
||||
this.#container.append(advancedOptionsToggle, advancedOptionsDiv, this.#deployUnitButtonEl);
|
||||
this.#container.append(this.#unitRoleTypeDropdown.getContainer(), unitLabelCountContainerEl, this.#unitLoadoutDropdown.getContainer(),
|
||||
advancedOptionsToggle, advancedOptionsDiv, this.#deployUnitButtonEl);
|
||||
|
||||
/* Load the country codes from the public folder */
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'images/countries/codes.json', true);
|
||||
xhr.responseType = 'json';
|
||||
xhr.onload = () => {
|
||||
var status = xhr.status;
|
||||
if (status === 200) {
|
||||
var status = xhr.status;
|
||||
if (status === 200)
|
||||
this.#countryCodes = xhr.response;
|
||||
} else {
|
||||
console.error(`Error retrieving country codes`)
|
||||
}
|
||||
else
|
||||
console.error(`Error retrieving country codes`)
|
||||
};
|
||||
xhr.send();
|
||||
|
||||
/* Event listeners */
|
||||
this.#container.addEventListener("unitRoleTypeChanged", () => {
|
||||
this.#unitLabelDropdown.reset();
|
||||
if (this.#unitLoadoutListEl !== null)
|
||||
this.#unitLoadoutListEl.replaceChildren();
|
||||
this.#unitLoadoutListEl.replaceChildren();
|
||||
if (this.#unitLoadoutDropdown !== null)
|
||||
this.#unitLoadoutDropdown.reset();
|
||||
if (this.#unitImageEl !== null)
|
||||
this.#unitImageEl.classList.toggle("hide", true);
|
||||
this.#unitLiveryDropdown.reset();
|
||||
|
||||
if (this.#options.orderByRole)
|
||||
|
||||
if (this.#orderByRole)
|
||||
this.#unitLabelDropdown.setOptions(this.#unitDatabase.getByRole(this.#spawnOptions.roleType).map((blueprint) => { return blueprint.label }));
|
||||
else
|
||||
this.#unitLabelDropdown.setOptions(this.#unitDatabase.getByType(this.#spawnOptions.roleType).map((blueprint) => { return blueprint.label }));
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
this.#computeSpawnPoints();
|
||||
})
|
||||
|
||||
@@ -160,9 +143,9 @@ export class UnitSpawnMenu {
|
||||
}
|
||||
this.#setUnitLiveryOptions();
|
||||
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
this.#computeSpawnPoints();
|
||||
})
|
||||
})
|
||||
|
||||
this.#container.addEventListener("unitLoadoutChanged", () => {
|
||||
this.#deployUnitButtonEl.disabled = false;
|
||||
@@ -177,17 +160,17 @@ export class UnitSpawnMenu {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
})
|
||||
|
||||
this.#container.addEventListener("unitCountChanged", () => {
|
||||
this.#computeSpawnPoints();
|
||||
})
|
||||
})
|
||||
|
||||
this.#container.addEventListener("unitCountryChanged", () => {
|
||||
this.#setUnitLiveryOptions();
|
||||
})
|
||||
})
|
||||
|
||||
this.#container.addEventListener("unitLiveryChanged", () => {
|
||||
|
||||
@@ -203,7 +186,7 @@ export class UnitSpawnMenu {
|
||||
this.#unitRoleTypeDropdown.reset();
|
||||
this.#unitLabelDropdown.reset();
|
||||
this.#unitLiveryDropdown.reset();
|
||||
if (this.#options.orderByRole)
|
||||
if (this.#orderByRole)
|
||||
this.#unitRoleTypeDropdown.setOptions(this.#unitDatabase.getRoles());
|
||||
else
|
||||
this.#unitRoleTypeDropdown.setOptions(this.#unitDatabase.getTypes());
|
||||
@@ -216,13 +199,13 @@ export class UnitSpawnMenu {
|
||||
this.#unitImageEl.classList.toggle("hide", true);
|
||||
|
||||
this.setCountries();
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
}
|
||||
|
||||
setCountries() {
|
||||
var coalitions = getMissionHandler().getCoalitions();
|
||||
var countries = Object.values(coalitions[getActiveCoalition() as keyof typeof coalitions]);
|
||||
this.#unitCountryDropdown.setOptionsElements(this.#createCountryButtons(this.#unitCountryDropdown, countries, (country: string) => {this.#setUnitCountry(country)}));
|
||||
this.#unitCountryDropdown.setOptionsElements(this.#createCountryButtons(this.#unitCountryDropdown, countries, (country: string) => { this.#setUnitCountry(country) }));
|
||||
|
||||
if (countries.length > 0 && !countries.includes(this.#spawnOptions.country)) {
|
||||
this.#unitCountryDropdown.forceValue(countries[0]);
|
||||
@@ -245,6 +228,47 @@ export class UnitSpawnMenu {
|
||||
this.#spawnOptions.latlng = latlng;
|
||||
}
|
||||
|
||||
setMaxUnitCount(maxUnitCount: number) {
|
||||
/* Create the unit count options */
|
||||
var countOptions: string[] = [];
|
||||
for (let i = 1; i <= maxUnitCount; i++)
|
||||
countOptions.push(i.toString());
|
||||
this.#unitCountDropdown.setOptions(countOptions);
|
||||
this.#unitCountDropdown.selectValue(0);
|
||||
}
|
||||
|
||||
getRoleTypeDrodown() {
|
||||
return this.#unitRoleTypeDropdown;
|
||||
}
|
||||
|
||||
getLabelDropdown() {
|
||||
return this.#unitLabelDropdown;
|
||||
}
|
||||
|
||||
getCountDropdown() {
|
||||
return this.#unitCountDropdown;
|
||||
}
|
||||
|
||||
getLoadoutDropdown() {
|
||||
return this.#unitLoadoutDropdown;
|
||||
}
|
||||
|
||||
getCountryDropdown() {
|
||||
return this.#unitCountDropdown;
|
||||
}
|
||||
|
||||
getLiveryDropdown() {
|
||||
return this.#unitLiveryDropdown;
|
||||
}
|
||||
|
||||
getLoadoutPreview() {
|
||||
return this.#unitLoadoutPreviewEl;
|
||||
}
|
||||
|
||||
getAltitudeSlider() {
|
||||
return this.#unitSpawnAltitudeSlider;
|
||||
}
|
||||
|
||||
#setUnitRoleType(roleType: string) {
|
||||
this.#spawnOptions.roleType = roleType;
|
||||
this.#container.dispatchEvent(new Event("unitRoleTypeChanged"));
|
||||
@@ -252,16 +276,16 @@ export class UnitSpawnMenu {
|
||||
|
||||
#setUnitLabel(label: string) {
|
||||
var name = this.#unitDatabase.getByLabel(label)?.name || null;
|
||||
if (name != null)
|
||||
if (name != null)
|
||||
this.#spawnOptions.name = name;
|
||||
this.#container.dispatchEvent(new Event("unitLabelChanged"));
|
||||
}
|
||||
|
||||
#setUnitLoadout(loadoutName: string) {
|
||||
var loadout = this.#unitDatabase.getLoadoutByName(this.#spawnOptions.name, loadoutName);
|
||||
if (loadout)
|
||||
this.#spawnOptions.loadout = loadout;
|
||||
this.#container.dispatchEvent(new Event("unitLoadoutChanged"));
|
||||
if (loadout)
|
||||
this.#spawnOptions.loadout = loadout;
|
||||
this.#container.dispatchEvent(new Event("unitLoadoutChanged"));
|
||||
}
|
||||
|
||||
#setUnitCount(count: string) {
|
||||
@@ -284,28 +308,13 @@ export class UnitSpawnMenu {
|
||||
this.#container.dispatchEvent(new Event("unitLiveryChanged"));
|
||||
}
|
||||
|
||||
#addDropdown(defaultText: string) {
|
||||
var div = document.createElement("div");
|
||||
div.classList.add("ol-select");
|
||||
|
||||
var value = document.createElement("div");
|
||||
value.classList.add("ol-select-value");
|
||||
value.innerText = defaultText;
|
||||
|
||||
var options = document.createElement("div");
|
||||
options.classList.add("ol-select-options");
|
||||
|
||||
div.append(value, options);
|
||||
return div;
|
||||
}
|
||||
|
||||
#setUnitLiveryOptions() {
|
||||
if (this.#spawnOptions.name !== "" && this.#spawnOptions.country !== "") {
|
||||
var liveries = this.#unitDatabase.getLiveryNamesByName(this.#spawnOptions.name);
|
||||
var countryLiveries: string[] = ["Default"];
|
||||
liveries.forEach((livery: any) => {
|
||||
var nationLiveryCodes = this.#countryCodes[this.#spawnOptions.country].liveryCodes;
|
||||
if (livery.countries.some((country: string) => {return nationLiveryCodes.includes(country)}))
|
||||
if (livery.countries.some((country: string) => { return nationLiveryCodes.includes(country) }))
|
||||
countryLiveries.push(livery.name);
|
||||
});
|
||||
this.#unitLiveryDropdown.setOptions(countryLiveries);
|
||||
@@ -317,17 +326,17 @@ export class UnitSpawnMenu {
|
||||
this.#spawnOptions.coalition = getActiveCoalition();
|
||||
if (this.#spawnOptions) {
|
||||
var unitTable = {
|
||||
unitType: this.#spawnOptions.name,
|
||||
location: this.#spawnOptions.latlng,
|
||||
altitude: this.#spawnOptions.altitude,
|
||||
loadout: this.#spawnOptions.loadout,
|
||||
unitType: this.#spawnOptions.name,
|
||||
location: this.#spawnOptions.latlng,
|
||||
altitude: this.#spawnOptions.altitude,
|
||||
loadout: this.#spawnOptions.loadout,
|
||||
liveryID: this.#spawnOptions.liveryID
|
||||
};
|
||||
var units = [];
|
||||
for (let i = 1; i < parseInt(this.#unitCountDropdown.getValue()) + 1; i++) {
|
||||
units.push(unitTable);
|
||||
}
|
||||
if (getUnitsManager().spawnUnits("Unit", units, getActiveCoalition(), false, this.#spawnOptions.airbase? this.#spawnOptions.airbase.getName(): "", this.#spawnOptions.country)) {
|
||||
if (getUnitsManager().spawnUnits("Unit", units, getActiveCoalition(), false, this.#spawnOptions.airbase ? this.#spawnOptions.airbase.getName() : "", this.#spawnOptions.country)) {
|
||||
getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition());
|
||||
getMap().getMapContextMenu().hide();
|
||||
}
|
||||
@@ -339,11 +348,11 @@ export class UnitSpawnMenu {
|
||||
var el = document.createElement("div");
|
||||
|
||||
var formattedCountry = "";
|
||||
if (this.#countryCodes[country] !== undefined && this.#countryCodes[country].displayName !== undefined)
|
||||
if (this.#countryCodes[country] !== undefined && this.#countryCodes[country].displayName !== undefined)
|
||||
formattedCountry = this.#countryCodes[country].displayName;
|
||||
else
|
||||
formattedCountry = country.charAt(0).toUpperCase() + country.slice(1).toLowerCase();
|
||||
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.classList.add("country-dropdown-element");
|
||||
el.appendChild(button);
|
||||
@@ -371,11 +380,67 @@ export class UnitSpawnMenu {
|
||||
}
|
||||
|
||||
#computeSpawnPoints() {
|
||||
if (getMissionHandler() && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER){
|
||||
if (getMissionHandler() && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) {
|
||||
var unitCount = parseInt(this.#unitCountDropdown.getValue());
|
||||
var unitSpawnPoints = unitCount * this.#unitDatabase.getSpawnPointsByLabel(this.#unitLabelDropdown.getValue());
|
||||
this.#deployUnitButtonEl.dataset.points = `${unitSpawnPoints}`;
|
||||
this.#deployUnitButtonEl.disabled = unitSpawnPoints >= getMissionHandler().getAvailableSpawnPoints();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class AircraftSpawnMenu extends UnitSpawnMenu {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID, aircraftDatabase, true);
|
||||
this.setMaxUnitCount(4);
|
||||
this.getAltitudeSlider().setMinMax(0, 50000);
|
||||
this.getAltitudeSlider().setIncrement(500);
|
||||
this.getAltitudeSlider().setValue(20000);
|
||||
}
|
||||
}
|
||||
|
||||
export class HelicopterSpawnMenu extends UnitSpawnMenu {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID, helicopterDatabase, true);
|
||||
this.setMaxUnitCount(4);
|
||||
this.getAltitudeSlider().setMinMax(0, 10000);
|
||||
this.getAltitudeSlider().setIncrement(100);
|
||||
this.getAltitudeSlider().setValue(5000);
|
||||
}
|
||||
}
|
||||
|
||||
export class GroundUnitSpawnMenu extends UnitSpawnMenu {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID, groundUnitDatabase, false);
|
||||
this.setMaxUnitCount(20);
|
||||
this.getAltitudeSlider().hide();
|
||||
this.getLoadoutDropdown().hide();
|
||||
this.getLoadoutPreview().classList.add("hide");
|
||||
}
|
||||
}
|
||||
|
||||
export class NavyUnitSpawnMenu extends UnitSpawnMenu {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID, navyUnitDatabase, false);
|
||||
this.setMaxUnitCount(4);
|
||||
this.getAltitudeSlider().hide();
|
||||
this.getLoadoutDropdown().hide();
|
||||
this.getLoadoutPreview().classList.add("hide");
|
||||
}
|
||||
}
|
||||
@@ -231,7 +231,6 @@ export function getWeaponsManager() {
|
||||
return weaponsManager;
|
||||
}
|
||||
|
||||
|
||||
export function getMissionHandler() {
|
||||
return missionHandler;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as L from "leaflet"
|
||||
import { getMissionHandler, getUnitsManager } from "..";
|
||||
import { BoxSelect } from "./boxselect";
|
||||
import { MapContextMenu } from "../controls/mapcontextmenu";
|
||||
import { UnitContextMenu } from "../controls/unitcontextmenu";
|
||||
import { AirbaseContextMenu } from "../controls/airbasecontextmenu";
|
||||
import { MapContextMenu } from "../contextmenus/mapcontextmenu";
|
||||
import { UnitContextMenu } from "../contextmenus/unitcontextmenu";
|
||||
import { AirbaseContextMenu } from "../contextmenus/airbasecontextmenu";
|
||||
import { Dropdown } from "../controls/dropdown";
|
||||
import { Airbase } from "../mission/airbase";
|
||||
import { Unit } from "../unit/unit";
|
||||
@@ -15,8 +15,9 @@ import { SVGInjector } from '@tanem/svg-injector'
|
||||
import { layers as mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTootlips, FIRE_AT_AREA, MOVE_UNIT, CARPET_BOMBING, BOMBING, SHOW_CONTACT_LINES, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, visibilityControlsTypes } from "../constants/constants";
|
||||
import { TargetMarker } from "./targetmarker";
|
||||
import { CoalitionArea } from "./coalitionarea";
|
||||
import { CoalitionAreaContextMenu } from "../controls/coalitionareacontextmenu";
|
||||
import { CoalitionAreaContextMenu } from "../contextmenus/coalitionareacontextmenu";
|
||||
import { DrawingCursor } from "./drawingcursor";
|
||||
import { AirbaseSpawnContextMenu } from "../contextmenus/airbasespawnmenu";
|
||||
|
||||
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
|
||||
|
||||
@@ -58,6 +59,7 @@ export class Map extends L.Map {
|
||||
#mapContextMenu: MapContextMenu = new MapContextMenu("map-contextmenu");
|
||||
#unitContextMenu: UnitContextMenu = new UnitContextMenu("unit-contextmenu");
|
||||
#airbaseContextMenu: AirbaseContextMenu = new AirbaseContextMenu("airbase-contextmenu");
|
||||
#airbaseSpawnMenu: AirbaseSpawnContextMenu = new AirbaseSpawnContextMenu("airbase-spawn-contextmenu");
|
||||
#coalitionAreaContextMenu: CoalitionAreaContextMenu = new CoalitionAreaContextMenu("coalition-area-contextmenu");
|
||||
|
||||
#mapSourceDropdown: Dropdown;
|
||||
@@ -65,7 +67,11 @@ export class Map extends L.Map {
|
||||
#optionButtons: { [key: string]: HTMLButtonElement[] } = {}
|
||||
#visibiityOptions: { [key: string]: boolean } = {}
|
||||
|
||||
constructor(ID: string) {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
/* Init the leaflet map */
|
||||
//@ts-ignore Needed because the boxSelect option is non-standard
|
||||
super(ID, { zoomSnap: 0, zoomDelta: 0.25, preferCanvas: true, doubleClickZoom: false, zoomControl: false, boxZoom: false, boxSelect: true, zoomAnimation: true, maxBoundsViscosity: 1.0, minZoom: 7, keyboard: true, keyboardPanDelta: 0 });
|
||||
@@ -237,6 +243,7 @@ export class Map extends L.Map {
|
||||
this.hideMapContextMenu();
|
||||
this.hideUnitContextMenu();
|
||||
this.hideAirbaseContextMenu();
|
||||
this.hideAirbaseSpawnMenu();
|
||||
this.hideCoalitionAreaContextMenu();
|
||||
}
|
||||
|
||||
@@ -282,6 +289,20 @@ export class Map extends L.Map {
|
||||
this.#airbaseContextMenu.hide();
|
||||
}
|
||||
|
||||
showAirbaseSpawnMenu(x: number, y: number, latlng: L.LatLng, airbase: Airbase) {
|
||||
this.hideAllContextMenus();
|
||||
this.#airbaseSpawnMenu.show(x, y);
|
||||
this.#airbaseSpawnMenu.setAirbase(airbase);
|
||||
}
|
||||
|
||||
getAirbaseSpawnMenu() {
|
||||
return this.#airbaseSpawnMenu;
|
||||
}
|
||||
|
||||
hideAirbaseSpawnMenu() {
|
||||
this.#airbaseSpawnMenu.hide();
|
||||
}
|
||||
|
||||
showCoalitionAreaContextMenu(x: number, y: number, latlng: L.LatLng, coalitionArea: CoalitionArea) {
|
||||
this.hideAllContextMenus();
|
||||
this.#coalitionAreaContextMenu.show(x, y, latlng);
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { Panel } from "./panel";
|
||||
|
||||
export class ConnectionStatusPanel extends Panel {
|
||||
constructor(ID: string) {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,11 @@ import { Unit } from "../unit/unit";
|
||||
import { Panel } from "./panel";
|
||||
|
||||
export class HotgroupPanel extends Panel {
|
||||
constructor(ID: string) {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID);
|
||||
document.addEventListener("unitDeath", () => this.refreshHotgroups());
|
||||
}
|
||||
|
||||
@@ -7,7 +7,11 @@ export class LogPanel extends Panel {
|
||||
#scrolledDown: boolean = true;
|
||||
#logs: {[key: string]: string} = {};
|
||||
|
||||
constructor(ID: string) {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID);
|
||||
|
||||
document.addEventListener("toggleLogPanel", () => {
|
||||
|
||||
@@ -12,7 +12,11 @@ export class MouseInfoPanel extends Panel {
|
||||
#measureLine: Polyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1, interactive: false });
|
||||
#measureBox: HTMLElement;
|
||||
|
||||
constructor(ID: string) {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID);
|
||||
|
||||
this.#measureIcon = new Icon({ iconUrl: 'resources/theme/images/icons/pin.svg', iconAnchor: [16, 32] });
|
||||
@@ -124,9 +128,9 @@ export class MouseInfoPanel extends Panel {
|
||||
this.#drawMeasureLine();
|
||||
}
|
||||
|
||||
#drawMeasure(imgId: string | null, textId: string, value: LatLng | null, mousePosition: LatLng) {
|
||||
var el = this.getElement().querySelector(`#${textId}`) as HTMLElement;
|
||||
var img = imgId != null ? this.getElement().querySelector(`#${imgId}`) as HTMLElement : null;
|
||||
#drawMeasure(imgID: string | null, textID: string, value: LatLng | null, mousePosition: LatLng) {
|
||||
var el = this.getElement().querySelector(`#${textID}`) as HTMLElement;
|
||||
var img = imgID != null ? this.getElement().querySelector(`#${imgID}`) as HTMLElement : null;
|
||||
if (value) {
|
||||
if (el != null) {
|
||||
el.classList.remove("hide");
|
||||
@@ -156,9 +160,9 @@ export class MouseInfoPanel extends Panel {
|
||||
}
|
||||
}
|
||||
|
||||
#drawCoordinates(imgId: string, textId: string, value: string) {
|
||||
const el = this.getElement().querySelector(`#${textId}`) as HTMLElement;
|
||||
const img = this.getElement().querySelector(`#${imgId}`) as HTMLElement;
|
||||
#drawCoordinates(imgID: string, textID: string, value: string) {
|
||||
const el = this.getElement().querySelector(`#${textID}`) as HTMLElement;
|
||||
const img = this.getElement().querySelector(`#${imgID}`) as HTMLElement;
|
||||
if (img && el) {
|
||||
el.dataset.value = value.substring(1);
|
||||
img.dataset.label = value[0];
|
||||
|
||||
@@ -2,7 +2,11 @@ export class Panel {
|
||||
#element: HTMLElement
|
||||
#visible: boolean = true;
|
||||
|
||||
constructor(ID: string) {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
this.#element = <HTMLElement>document.getElementById(ID);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { Panel } from "./panel";
|
||||
|
||||
export class ServerStatusPanel extends Panel {
|
||||
constructor(ID: string) {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,11 @@ export class UnitControlPanel extends Panel {
|
||||
#units: Unit[] = [];
|
||||
#selectedUnitsTypes: string[] = [];
|
||||
|
||||
constructor(ID: string) {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID);
|
||||
|
||||
/* Unit control sliders */
|
||||
|
||||
@@ -22,7 +22,11 @@ export class UnitInfoPanel extends Panel {
|
||||
#unitLabel: HTMLElement;
|
||||
#unitName: HTMLElement;
|
||||
|
||||
constructor(ID: string) {
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
super(ID);
|
||||
|
||||
this.#altitude = (this.getElement().querySelector("#altitude")) as HTMLElement;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map, Point } from 'leaflet';
|
||||
import { getMap, getMissionHandler, getUnitsManager, getWeaponsManager } from '..';
|
||||
import { enumToCoalition, enumToEmissioNCountermeasure, getMarkerCategoryByName, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg, bearing, deg2rad } from '../other/utils';
|
||||
import { enumToCoalition, enumToEmissioNCountermeasure, getMarkerCategoryByName, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg, bearing, deg2rad, ftToM } from '../other/utils';
|
||||
import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions, followUnit, setEmissionsCountermeasures, setSpeedType, setAltitudeType, setOnOff, setFollowRoads, bombPoint, carpetBomb, bombBuilding, fireAtArea } from '../server/server';
|
||||
import { CustomMarker } from '../map/custommarker';
|
||||
import { SVGInjector } from '@tanem/svg-injector';
|
||||
@@ -818,9 +818,31 @@ export class Unit extends CustomMarker {
|
||||
#applyFollowOptions(action: string) {
|
||||
if (action === "custom") {
|
||||
document.getElementById("custom-formation-dialog")?.classList.remove("hide");
|
||||
getMap().getUnitContextMenu().setCustomFormationCallback((offset: { x: number, y: number, z: number }) => {
|
||||
getUnitsManager().selectedUnitsFollowUnit(this.ID, offset);
|
||||
})
|
||||
document.addEventListener("applyCustomFormation", () => {
|
||||
var dialog = document.getElementById("custom-formation-dialog");
|
||||
if (dialog) {
|
||||
dialog.classList.add("hide");
|
||||
var clock = 1;
|
||||
while (clock < 8) {
|
||||
if ((<HTMLInputElement>dialog.querySelector(`#formation-${clock}`)).checked)
|
||||
break
|
||||
clock++;
|
||||
}
|
||||
var angleDeg = 360 - (clock - 1) * 45;
|
||||
var angleRad = deg2rad(angleDeg);
|
||||
var distance = ftToM(parseInt((<HTMLInputElement>dialog.querySelector(`#distance`)?.querySelector("input")).value));
|
||||
var upDown = ftToM(parseInt((<HTMLInputElement>dialog.querySelector(`#up-down`)?.querySelector("input")).value));
|
||||
|
||||
// X: front-rear, positive front
|
||||
// Y: top-bottom, positive top
|
||||
// Z: left-right, positive right
|
||||
var x = distance * Math.cos(angleRad);
|
||||
var y = upDown;
|
||||
var z = distance * Math.sin(angleRad);
|
||||
|
||||
getUnitsManager().selectedUnitsFollowUnit(this.ID, { "x": x, "y": y, "z": z });
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
getUnitsManager().selectedUnitsFollowUnit(this.ID, undefined, action);
|
||||
|
||||
Reference in New Issue
Block a user