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:
174
client/src/contextmenus/airbasecontextmenu.ts
Normal file
174
client/src/contextmenus/airbasecontextmenu.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import { getMap, getMissionHandler, getUnitsManager, setActiveCoalition } from "..";
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @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();
|
||||
})
|
||||
|
||||
document.addEventListener("contextMenuLandAirbase", (e: any) => {
|
||||
if (this.#airbase)
|
||||
getUnitsManager().selectedUnitsLandAt(this.#airbase.getLatLng());
|
||||
this.hide();
|
||||
})
|
||||
}
|
||||
|
||||
/** 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.#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();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param airbaseName The name of the airbase
|
||||
*/
|
||||
#setName(airbaseName: string) {
|
||||
var nameDiv = <HTMLElement>this.getContainer()?.querySelector("#airbase-name");
|
||||
if (nameDiv != null)
|
||||
nameDiv.innerText = airbaseName;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @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;
|
||||
return div;
|
||||
}),);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @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;
|
||||
return div;
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param coalition Coalition to which the airbase belongs
|
||||
*/
|
||||
#setCoalition(coalition: string) {
|
||||
(this.getContainer()?.querySelector("#spawn-airbase-aircraft-button") as HTMLElement).dataset.coalition = coalition;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param showSpawnButton If true, the spawn button will be visibile
|
||||
*/
|
||||
#showSpawnButton(showSpawnButton: boolean) {
|
||||
this.getContainer()?.querySelector("#spawn-airbase-aircraft-button")?.classList.toggle("hide", !showSpawnButton);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param showLandButton If true, the land button will be visible
|
||||
*/
|
||||
#showLandButton(showLandButton: boolean) {
|
||||
this.getContainer()?.querySelector("#land-here-button")?.classList.toggle("hide", !showLandButton);
|
||||
}
|
||||
|
||||
/** 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().showAirbaseSpawnMenu(this.getX(), this.getY(), this.getLatLng(), this.#airbase);
|
||||
}
|
||||
}
|
||||
|
||||
/** @todo needs commenting
|
||||
*
|
||||
*/
|
||||
#setAirbaseData() {
|
||||
if (this.#airbase && this.getContainer()) {
|
||||
dataPointMap(this.getContainer() as HTMLElement, {
|
||||
"coalition": this.#airbase.getCoalition(),
|
||||
"airbaseName": this.#airbase.getName()
|
||||
});
|
||||
|
||||
dataPointMap( this.getContainer() as HTMLElement, this.#airbase.getChartData() );
|
||||
|
||||
const runwaysContainer = this.getContainer()?.querySelector( "#airbase-runways" ) as HTMLElement;
|
||||
runwaysContainer.innerHTML = "";
|
||||
|
||||
if ( runwaysContainer instanceof HTMLElement ) {
|
||||
const runways = this.#airbase.getChartData().runways;
|
||||
|
||||
if ( runways.length === 0 ) {
|
||||
runwaysContainer.innerText = "No data";
|
||||
} else {
|
||||
runways.forEach( runway => {
|
||||
let runwayDiv = document.createElement( "div" );
|
||||
runwayDiv.classList.add( "runway" );
|
||||
|
||||
runway.headings.forEach( headings => {
|
||||
for ( const [ heading, data ] of Object.entries( headings ) ) {
|
||||
|
||||
let headingDiv = document.createElement( "div" );
|
||||
headingDiv.classList.add( "heading" );
|
||||
|
||||
let abbr = document.createElement( "abbr" );
|
||||
abbr.title = `Mag heading: ${data.magHeading}`;
|
||||
abbr.innerText = heading;
|
||||
|
||||
headingDiv.appendChild( abbr );
|
||||
runwayDiv.appendChild( headingDiv );
|
||||
|
||||
if ( data.ILS ) {
|
||||
let ilsDiv = document.createElement( "div" );
|
||||
ilsDiv.classList.add( "ils" );
|
||||
|
||||
abbr = document.createElement( "abbr" );
|
||||
abbr.title = data.ILS;
|
||||
abbr.innerText = "ILS";
|
||||
|
||||
ilsDiv.appendChild( abbr );
|
||||
headingDiv.appendChild( ilsDiv );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
runwaysContainer.appendChild( runwayDiv );
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
159
client/src/contextmenus/coalitionareacontextmenu.ts
Normal file
159
client/src/contextmenus/coalitionareacontextmenu.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { getMap, getMissionHandler, getUnitsManager } from "..";
|
||||
import { GAME_MASTER, IADSTypes } from "../constants/constants";
|
||||
import { CoalitionArea } from "../map/coalitionarea";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
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;
|
||||
#iadsDensitySlider: Slider;
|
||||
#iadsDistributionSlider: Slider;
|
||||
#iadsTypesDropdown: Dropdown;
|
||||
#iadsErasDropdown: Dropdown;
|
||||
#iadsRangesDropdown: Dropdown;
|
||||
|
||||
/**
|
||||
*
|
||||
* @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);
|
||||
this.#iadsDistributionSlider.setIncrement(5);
|
||||
this.#iadsDistributionSlider.setValue(50);
|
||||
this.#iadsDistributionSlider.setActive(true);
|
||||
|
||||
document.addEventListener("coalitionAreaContextMenuShow", (e: any) => {
|
||||
if (this.getVisibleSubMenu() !== e.detail.type)
|
||||
this.#showSubMenu(e.detail.type);
|
||||
else
|
||||
this.#hideSubMenus();
|
||||
});
|
||||
|
||||
document.addEventListener("coalitionAreaBringToBack", (e: any) => {
|
||||
if (this.#coalitionArea)
|
||||
getMap().bringCoalitionAreaToBack(this.#coalitionArea);
|
||||
getMap().hideCoalitionAreaContextMenu();
|
||||
});
|
||||
|
||||
document.addEventListener("coalitionAreaDelete", (e: any) => {
|
||||
if (this.#coalitionArea)
|
||||
getMap().deleteCoalitionArea(this.#coalitionArea);
|
||||
getMap().hideCoalitionAreaContextMenu();
|
||||
});
|
||||
|
||||
document.addEventListener("contextMenuCreateIads", (e: any) => {
|
||||
const area = this.getCoalitionArea();
|
||||
if (area)
|
||||
getUnitsManager().createIADS(area, getCheckboxOptions(this.#iadsTypesDropdown), getCheckboxOptions(this.#iadsErasDropdown), getCheckboxOptions(this.#iadsRangesDropdown), this.#iadsDensitySlider.getValue(), this.#iadsDistributionSlider.getValue());
|
||||
})
|
||||
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);
|
||||
|
||||
/* Create the checkboxes to select the unit roles */
|
||||
this.#iadsTypesDropdown.setOptionsElements(IADSTypes.map((role: string) => {
|
||||
return createCheckboxOption(role, `Add ${role}s to the IADS` );
|
||||
}));
|
||||
|
||||
|
||||
/* Create the checkboxes to select the unit periods */
|
||||
this.#iadsErasDropdown.setOptionsElements(groundUnitDatabase.getEras().map((era: string) => {
|
||||
return createCheckboxOption(era, `Add ${era} era units to the IADS`);
|
||||
}));
|
||||
|
||||
/* Create the checkboxes to select the unit ranges */
|
||||
this.#iadsRangesDropdown.setOptionsElements(groundUnitDatabase.getRanges().map((range: string) => {
|
||||
return createCheckboxOption(range, `Add ${range} units to the IADS`);
|
||||
}));
|
||||
|
||||
if (getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER)
|
||||
this.#coalitionSwitch.hide()
|
||||
}
|
||||
|
||||
/** 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) => {
|
||||
element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition())
|
||||
});
|
||||
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");
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => {
|
||||
element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
105
client/src/contextmenus/contextmenu.ts
Normal file
105
client/src/contextmenus/contextmenu.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
|
||||
/** Hide the contextmenu
|
||||
*
|
||||
*/
|
||||
hide() {
|
||||
this.#container?.classList.toggle("hide", 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;
|
||||
}
|
||||
|
||||
/** 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)
|
||||
this.#container.style.left = this.#x + "px";
|
||||
else
|
||||
this.#container.style.left = window.innerWidth - this.#container.offsetWidth - 10 + "px";
|
||||
|
||||
if (this.#y + this.#container.offsetHeight < window.innerHeight)
|
||||
this.#container.style.top = this.#y + "px";
|
||||
else
|
||||
this.#container.style.top = window.innerHeight - this.#container.offsetHeight - 10 + "px";
|
||||
}
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
}
|
||||
215
client/src/contextmenus/mapcontextmenu.ts
Normal file
215
client/src/contextmenus/mapcontextmenu.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { getActiveCoalition, getMap, getMissionHandler, setActiveCoalition } from "..";
|
||||
import { spawnExplosion, spawnSmoke } from "../server/server";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
import { Switch } from "../controls/switch";
|
||||
import { GAME_MASTER } from "../constants/constants";
|
||||
import { CoalitionArea } from "../map/coalitionarea";
|
||||
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: AircraftSpawnMenu;
|
||||
#helicopterSpawnMenu: HelicopterSpawnMenu;
|
||||
#groundUnitSpawnMenu: GroundUnitSpawnMenu;
|
||||
#navyUnitSpawnMenu: NavyUnitSpawnMenu;
|
||||
#coalitionArea: CoalitionArea | null = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @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());
|
||||
|
||||
/* 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);
|
||||
});
|
||||
|
||||
document.addEventListener("contextMenuDeploySmoke", (e: any) => {
|
||||
this.hide();
|
||||
spawnSmoke(e.detail.color, this.getLatLng());
|
||||
});
|
||||
|
||||
document.addEventListener("contextMenuExplosion", (e: any) => {
|
||||
this.hide();
|
||||
spawnExplosion(e.detail.strength, this.getLatLng());
|
||||
});
|
||||
|
||||
document.addEventListener("editCoalitionArea", (e: any) => {
|
||||
this.hide();
|
||||
if (this.#coalitionArea) {
|
||||
getMap().deselectAllCoalitionAreas();
|
||||
this.#coalitionArea.setSelected(true);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("commandModeOptionsChanged", (e: any) => {
|
||||
//this.#refreshOptions();
|
||||
});
|
||||
|
||||
this.#aircraftSpawnMenu.getContainer().addEventListener("resize", () => this.clip());
|
||||
this.#helicopterSpawnMenu.getContainer().addEventListener("resize", () => this.clip());
|
||||
this.#groundUnitSpawnMenu.getContainer().addEventListener("resize", () => this.clip());
|
||||
this.#navyUnitSpawnMenu.getContainer().addEventListener("resize", () => this.clip());
|
||||
|
||||
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.#aircraftSpawnMenu.setLatLng(latlng);
|
||||
this.#helicopterSpawnMenu.setLatLng(latlng);
|
||||
this.#groundUnitSpawnMenu.setLatLng(latlng);
|
||||
this.#navyUnitSpawnMenu.setLatLng(latlng);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/** 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", "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");
|
||||
this.getContainer()?.querySelector("#aircraft-spawn-button")?.classList.toggle("is-open", type === "aircraft");
|
||||
this.getContainer()?.querySelector("#helicopter-spawn-menu")?.classList.toggle("hide", type !== "helicopter");
|
||||
this.getContainer()?.querySelector("#helicopter-spawn-button")?.classList.toggle("is-open", type === "helicopter");
|
||||
this.getContainer()?.querySelector("#groundunit-spawn-menu")?.classList.toggle("hide", type !== "groundunit");
|
||||
this.getContainer()?.querySelector("#groundunit-spawn-button")?.classList.toggle("is-open", type === "groundunit");
|
||||
this.getContainer()?.querySelector("#navyunit-spawn-menu")?.classList.toggle("hide", type !== "navyunit");
|
||||
this.getContainer()?.querySelector("#navyunit-spawn-button")?.classList.toggle("is-open", type === "navyunit");
|
||||
this.getContainer()?.querySelector("#smoke-spawn-menu")?.classList.toggle("hide", type !== "smoke");
|
||||
this.getContainer()?.querySelector("#smoke-spawn-button")?.classList.toggle("is-open", type === "smoke");
|
||||
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.#aircraftSpawnMenu.reset();
|
||||
this.#aircraftSpawnMenu.setCountries();
|
||||
this.#helicopterSpawnMenu.reset();
|
||||
this.#helicopterSpawnMenu.setCountries();
|
||||
this.#groundUnitSpawnMenu.reset();
|
||||
this.#groundUnitSpawnMenu.setCountries();
|
||||
this.#navyUnitSpawnMenu.reset();
|
||||
this.#navyUnitSpawnMenu.setCountries();
|
||||
|
||||
this.setVisibleSubMenu(type);
|
||||
this.clip();
|
||||
}
|
||||
|
||||
/** 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);
|
||||
this.getContainer()?.querySelector("#helicopter-spawn-button")?.classList.toggle("is-open", false);
|
||||
this.getContainer()?.querySelector("#groundunit-spawn-menu")?.classList.toggle("hide", true);
|
||||
this.getContainer()?.querySelector("#groundunit-spawn-button")?.classList.toggle("is-open", false);
|
||||
this.getContainer()?.querySelector("#navyunit-spawn-menu")?.classList.toggle("hide", true);
|
||||
this.getContainer()?.querySelector("#navyunit-spawn-button")?.classList.toggle("is-open", false);
|
||||
this.getContainer()?.querySelector("#smoke-spawn-menu")?.classList.toggle("hide", true);
|
||||
this.getContainer()?.querySelector("#smoke-spawn-button")?.classList.toggle("is-open", false);
|
||||
this.getContainer()?.querySelector("#explosion-menu")?.classList.toggle("hide", true);
|
||||
this.getContainer()?.querySelector("#explosion-spawn-button")?.classList.toggle("is-open", false);
|
||||
|
||||
this.#aircraftSpawnMenu.reset();
|
||||
this.#helicopterSpawnMenu.reset();
|
||||
this.#groundUnitSpawnMenu.reset();
|
||||
this.#navyUnitSpawnMenu.reset();
|
||||
|
||||
this.setVisibleSubMenu(null);
|
||||
this.clip();
|
||||
}
|
||||
|
||||
/** 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");
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
|
||||
this.#aircraftSpawnMenu.setCountries();
|
||||
this.#helicopterSpawnMenu.setCountries();
|
||||
this.#groundUnitSpawnMenu.setCountries();
|
||||
this.#navyUnitSpawnMenu.setCountries();
|
||||
}
|
||||
|
||||
/** 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()) });
|
||||
this.#aircraftSpawnMenu.setCountries();
|
||||
this.#helicopterSpawnMenu.setCountries();
|
||||
this.#groundUnitSpawnMenu.setCountries();
|
||||
this.#navyUnitSpawnMenu.setCountries();
|
||||
}
|
||||
}
|
||||
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);
|
||||
}));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user