(WiP) Unit spawn menu

This commit is contained in:
dpassoni
2023-03-09 17:55:35 +01:00
parent 70c73aa3a9
commit 406e515fa6
14 changed files with 707 additions and 408 deletions

View File

@@ -1,41 +1,63 @@
import { LatLng } from "leaflet";
import { getActiveCoalition, setActiveCoalition } from "..";
import { ContextMenuOption } from "../@types/dom";
import { ClickEvent } from "../map/map";
import { spawnAircraft } from "../server/server";
import { aircraftDatabase } from "../units/aircraftdatabase";
import { Dropdown } from "./dropdown";
export interface SpawnOptions {
role: string;
type: string;
latlng: LatLng;
coalition: string;
loadout: string | null;
airbaseName: string | null;
}
export class ContextMenu {
#container: HTMLElement | null;
#latlng: LatLng = new LatLng(0, 0);
#aircraftRoleDropdown: Dropdown;
#aircraftTypeDropdown: Dropdown;
#aircraftLoadoutDropdown: Dropdown;
//#unitsNumberDropdown: Dropdown;
#spawnOptions: SpawnOptions = {role: "", type: "", latlng: this.#latlng, loadout: null, coalition: "blue", airbaseName: null};
constructor(id: string,) {
this.#container = document.getElementById(id);
this.#container?.querySelector("#switch")?.addEventListener('change', (e) => this.#onSwitch(e))
this.#container?.querySelector("#context-menu-switch")?.addEventListener('change', (e) => this.#onSwitch(e));
this.#aircraftRoleDropdown = new Dropdown("role-options", (role: string) => this.#setAircraftRole(role), aircraftDatabase.getRoles());
this.#aircraftTypeDropdown = new Dropdown("aircraft-options", (type: string) => this.#setAircraftType(type));
this.#aircraftLoadoutDropdown = new Dropdown("loadout-options", (loadout: string) => this.#setAircraftLoadout(loadout));
//this.#unitsNumberDropdown = new Dropdown("#units-options", this.#setAircraftType, [""]);
document.addEventListener("contextMenuShow", (e: any) => {
this.#container?.querySelector("#aircraft-spawn-menu")?.classList.toggle("hide", e.detail.unitType !== "aircraft");
})
document.addEventListener("contextMenuDeployAircraft", () => {
this.hide();
this.#spawnOptions.coalition = getActiveCoalition();
if (this.#spawnOptions)
spawnAircraft(this.#spawnOptions);
})
this.hide();
}
show(x: number, y: number, title: string, options: ContextMenuOption[], showCoalition: boolean) {
show(x: number, y: number, latlng: LatLng) {
this.#spawnOptions.latlng = latlng;
this.#container?.classList.toggle("hide", false);
this.#container?.querySelector("#list")?.replaceChildren(...options.map((option: ContextMenuOption) => {
var li = document.createElement("li");
var button = document.createElement("button");
button.textContent = option.tooltip;
li.appendChild(button);
button.addEventListener("click", (e: MouseEvent) => option.callback((e.target as HTMLButtonElement).innerText));
return button;
}));
this.#container?.querySelector("#switch")?.classList.toggle("hide", !showCoalition);
if (this.#container != null && options.length >= 1) {
var titleDiv = this.#container.querySelector("#title");
if (titleDiv)
titleDiv.textContent = title;
if (x - this.#container.offsetWidth / 2 + this.#container.offsetWidth < window.innerWidth)
this.#container.style.left = x - this.#container.offsetWidth / 2 + "px";
if (this.#container != null) {
if (x + this.#container.offsetWidth < window.innerWidth)
this.#container.style.left = x + "px";
else
this.#container.style.left = window.innerWidth - this.#container.offsetWidth + "px";
if (y - 20 + this.#container.offsetHeight < window.innerHeight)
this.#container.style.top = y - 20 + "px";
if (y + this.#container.offsetHeight < window.innerHeight)
this.#container.style.top = y + "px";
else
this.#container.style.top = window.innerHeight - this.#container.offsetHeight + "px";
}
@@ -47,10 +69,43 @@ export class ContextMenu {
#onSwitch(e: any) {
if (this.#container != null) {
if (e.currentTarget.checked)
if (e.srcElement.checked)
setActiveCoalition("red");
else
setActiveCoalition("blue");
}
}
#setAircraftRole(role: string)
{
if (this.#spawnOptions != null)
{
this.#spawnOptions.role = role;
this.#aircraftTypeDropdown.setOptions(aircraftDatabase.getLabelsByRole(role));
}
}
#setAircraftType(type: string)
{
if (this.#spawnOptions != null)
{
this.#spawnOptions.type = type;
this.#aircraftLoadoutDropdown.setOptions(aircraftDatabase.getLoadoutNamesByRole(type, this.#spawnOptions.role));
}
}
#setAircraftLoadout(loadoutName: string)
{
if (this.#spawnOptions != null)
{
var loadout = aircraftDatabase.getLoadoutsByName(this.#spawnOptions.type, loadoutName);
if (loadout)
this.#spawnOptions.loadout = loadout.code;
}
}
#setUnitsNumber(unitsNumber: string)
{
}
}

View File

@@ -0,0 +1,32 @@
export class Dropdown {
#element: HTMLElement;
#options: HTMLElement;
#value: HTMLElement;
#callback: CallableFunction;
constructor(ID: string, callback: CallableFunction, options: string[] | null = null)
{
this.#element = <HTMLElement>document.getElementById(ID);
var element = this.#element;
this.#options = <HTMLElement>this.#element.querySelector(".ol-select-options");
this.#value = <HTMLElement>this.#element.querySelector(".ol-select-value");
this.#callback = callback;
if (options != null)
this.setOptions(options);
}
setOptions(options: string[])
{
this.#options.replaceChildren(...options.map((option: string) => {
var div = document.createElement("div");
var button = document.createElement("button");
button.textContent = option;
div.appendChild(button);
button.addEventListener("click", (e: MouseEvent) => {
this.#value.innerText = option;
this.#callback(option);
});
return div;
}));
}
}

View File

@@ -248,6 +248,7 @@ export function getConnectionStatusPanel() {
export function setActiveCoalition(newActiveCoalition: string) {
activeCoalition = newActiveCoalition;
document.querySelectorAll('[data-coalition]').forEach((element: any) => {element.setAttribute("data-coalition", activeCoalition)});
}
export function getActiveCoalition() {

View File

@@ -5,6 +5,7 @@ import { aircraftDatabase } from "../units/aircraftdatabase";
import { unitTypes } from "../units/unittypes";
import { BoxSelect } from "./boxselect";
import { ContextMenuOption } from "../@types/dom";
import { SpawnOptions } from "../controls/contextmenu";
export const IDLE = "IDLE";
export const MOVE_UNIT = "MOVE_UNIT";
@@ -116,10 +117,10 @@ export class Map extends L.Map {
}
/* Context Menu */
showContextMenu(e: ClickEvent | SpawnEvent, title: string, options: ContextMenuOption[], showCoalition: boolean = false) {
var x = e.x;
var y = e.y;
getContextMenu()?.show(x, y, title, options, showCoalition);
showContextMenu(e: any, spawnOptions: SpawnOptions | null = null) {
var x = e.originalEvent.x;
var y = e.originalEvent.y;
getContextMenu()?.show(x, y, e.latlng);
document.dispatchEvent(new CustomEvent("mapContextMenu"));
}
@@ -139,7 +140,7 @@ export class Map extends L.Map {
/* Spawn from air base */
spawnFromAirbase(e: SpawnEvent)
{
this.#aircraftSpawnMenu(e);
//this.#aircraftSpawnMenu(e);
}
/* Event handlers */
@@ -164,15 +165,8 @@ export class Map extends L.Map {
#onContextMenu(e: any) {
this.hideContextMenu();
if (this.#state === IDLE) {
var spawnEvent: SpawnEvent = {x: e.originalEvent.x, y: e.originalEvent.y, latlng: e.latlng, airbaseName: null, coalitionID: null};
if (this.#state == IDLE) {
var options = [
{ "tooltip": "Spawn air unit", "src": "spawnAir.png", "callback": () => this.#aircraftSpawnMenu(spawnEvent) },
{ "tooltip": "Spawn ground unit", "src": "spawnGround.png", "callback": () => this.#groundUnitSpawnMenu(spawnEvent) },
{ "tooltip": "Smoke", "src": "spawnSmoke.png", "callback": () => this.#smokeSpawnMenu(spawnEvent) },
//{ "tooltip": "Explosion", "src": "spawnExplosion.png", "callback": () => this.#explosionSpawnMenu(e) }
]
this.showContextMenu(spawnEvent, "Action", options, false);
this.showContextMenu(e);
}
}
else if (this.#state === MOVE_UNIT) {
@@ -215,99 +209,4 @@ export class Map extends L.Map {
this.#lastMousePosition.x = e.originalEvent.x;
this.#lastMousePosition.y = e.originalEvent.y;
}
/* Spawning menus */
#aircraftSpawnMenu(e: SpawnEvent) {
var options = [
{ 'coalition': true, 'tooltip': 'CAP', 'src': 'spawnCAP.png', 'callback': () => this.#selectAircraft(e, "cap") },
{ 'coalition': true, 'tooltip': 'CAS', 'src': 'spawnCAS.png', 'callback': () => this.#selectAircraft(e, "cas") },
{ 'coalition': true, 'tooltip': 'Strike', 'src': 'spawnStrike.png', 'callback': () => this.#selectAircraft(e, "strike") },
{ 'coalition': true, 'tooltip': 'Recce', 'src': 'spawnStrike.png', 'callback': () => this.#selectAircraft(e, "reconnaissance") },
{ 'coalition': true, 'tooltip': 'Tanker', 'src': 'spawnTanker.png', 'callback': () => this.#selectAircraft(e, "tanker") },
{ 'coalition': true, 'tooltip': 'AWACS', 'src': 'spawnAWACS.png', 'callback': () => this.#selectAircraft(e, "awacs") },
{ 'coalition': true, 'tooltip': 'Drone', 'src': 'spawnDrone.png', 'callback': () => this.#selectAircraft(e, "drone") },
{ 'coalition': true, 'tooltip': 'Transport', 'src': 'spawnTransport.png', 'callback': () => this.#selectAircraft(e, "transport") },
]
if (e.airbaseName != null)
this.showContextMenu(e, "Spawn at " + e.airbaseName, options, true);
else
this.showContextMenu(e, "Spawn air unit", options, true);
}
#groundUnitSpawnMenu(e: SpawnEvent) {
var options = [
{'coalition': true, 'tooltip': 'Howitzer', 'src': 'spawnHowitzer.png', 'callback': () => this.#selectGroundUnit(e, "Howitzers")},
{'coalition': true, 'tooltip': 'SAM', 'src': 'spawnSAM.png', 'callback': () => this.#selectGroundUnit(e, "SAM")},
{'coalition': true, 'tooltip': 'IFV', 'src': 'spawnIFV.png', 'callback': () => this.#selectGroundUnit(e, "IFV")},
{'coalition': true, 'tooltip': 'Tank', 'src': 'spawnTank.png', 'callback': () => this.#selectGroundUnit(e, "Tanks")},
{'coalition': true, 'tooltip': 'MLRS', 'src': 'spawnMLRS.png', 'callback': () => this.#selectGroundUnit(e, "MLRS")},
{'coalition': true, 'tooltip': 'Radar', 'src': 'spawnRadar.png', 'callback': () => this.#selectGroundUnit(e, "Radar")},
{'coalition': true, 'tooltip': 'Unarmed', 'src': 'spawnUnarmed.png', 'callback': () => this.#selectGroundUnit(e, "Unarmed")}
]
this.showContextMenu(e, "Spawn ground unit", options, true);
}
#smokeSpawnMenu(e: SpawnEvent) {
this.hideContextMenu();
var options = [
{'tooltip': 'Red smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('red', e.latlng)}, 'tint': 'red'},
{'tooltip': 'White smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('white', e.latlng)}, 'tint': 'white'},
{'tooltip': 'Blue smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('blue', e.latlng)}, 'tint': 'blue'},
{'tooltip': 'Green smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('green', e.latlng)}, 'tint': 'green'},
{'tooltip': 'Orange smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('orange', e.latlng)}, 'tint': 'orange'},
]
this.showContextMenu(e, "Spawn smoke", options, false);
}
#explosionSpawnMenu(e: SpawnEvent) {
}
/* Show unit selection for air units */
#selectAircraft(e: SpawnEvent, role: string) {
this.hideContextMenu();
var options = aircraftDatabase.getLabelsByRole(role);
this.showContextMenu(e, "Select aircraft",
options.map((option: string) => {
return {tooltip: option, src: "", callback: (label: string) => {
this.hideContextMenu();
var name = aircraftDatabase.getNameByLabel(label);
if (name != null)
this.#unitSelectPayload(e, name, role);
}}}), true);
}
/* Show weapon selection for air units */
#unitSelectPayload(e: SpawnEvent, unitType: string, role: string) {
this.hideContextMenu();
var options = aircraftDatabase.getLoadoutNamesByRole(unitType, role);
//options = payloadNames[unitType]
if (options != undefined && options.length > 0) {
options.sort();
this.showContextMenu({x: e.x, y: e.y, latlng: e.latlng}, "Select loadout",
options.map((option: string) => {
return {tooltip: option, src: "", callback: (loadoutName: string) => {
this.hideContextMenu();
var loadout = aircraftDatabase.getLoadoutsByName(unitType, loadoutName);
spawnAircraft(unitType, e.latlng, getActiveCoalition(), loadout != null? loadout.code: "", e.airbaseName);
}}}), true);
}
else {
spawnAircraft(unitType, e.latlng, getActiveCoalition());
}
}
/* Show unit selection for ground units */
#selectGroundUnit(e: any, group: string)
{
this.hideContextMenu();
var options = unitTypes.vehicles[group];
options.sort();
this.showContextMenu(e, "Select ground unit",
options.map((option: string) => {
return {tooltip: option, src: "", callback: (unitType: string) => {
this.hideContextMenu();
spawnGroundUnit(unitType, e.latlng, getActiveCoalition());
}}}), true);
}
}

View File

@@ -84,9 +84,9 @@ export class MissionHandler
else
options = ["Spawn unit"];
getMap().showContextMenu(e.originalEvent, e.sourceTarget.getName(),
options.map((option) => {return {tooltip: option, src: "", callback: (label: string) => {this.#onAirbaseOptionSelection(e, label)}}}, false)
)
//getMap().showContextMenu(e.originalEvent, e.sourceTarget.getName(),
// options.map((option) => {return {tooltip: option, src: "", callback: (label: string) => {this.#onAirbaseOptionSelection(e, label)}}}, false)
//)
}
#onAirbaseOptionSelection(e: any, option: string) {

View File

@@ -1,5 +1,6 @@
import * as L from 'leaflet'
import { setConnected } from '..';
import { SpawnOptions } from '../controls/contextmenu';
/* Edit here to change server address */
const REST_ADDRESS = "http://localhost:30000/olympus";
@@ -69,8 +70,8 @@ export function spawnGroundUnit(type: string, latlng: L.LatLng, coalition: strin
POST(data, () => { });
}
export function spawnAircraft(type: string, latlng: L.LatLng, coalition: string, payloadName: string | null = null, airbaseName: string | null = null) {
var command = { "type": type, "location": latlng, "coalition": coalition, "payloadName": payloadName != null? payloadName: "", "airbaseName": airbaseName != null? airbaseName: ""};
export function spawnAircraft(spawnOptions: SpawnOptions) {
var command = { "type": spawnOptions.type, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition, "payloadName": spawnOptions.loadout != null? spawnOptions.loadout: "", "airbaseName": spawnOptions.airbaseName != null? spawnOptions.airbaseName: ""};
var data = { "spawnAir": command }
POST(data, () => { });
}

View File

@@ -290,7 +290,7 @@ export class Unit extends Marker {
'Attack',
'Follow'
]
getMap().showContextMenu(e.originalEvent, "Action: " + this.getData().unitName, options.map((option: string) => {return {tooltip: option, src: "", callback: (action: string) => this.#executeAction(action)}}));
//getMap().showContextMenu(e.originalEvent, "Action: " + this.getData().unitName, options.map((option: string) => {return {tooltip: option, src: "", callback: (action: string) => this.#executeAction(action)}}));
}
#executeAction(action: string) {

View File

@@ -6,6 +6,24 @@ export class UnitDatabase {
}
getRoles()
{
var roles: string[] = [];
for (let unit in this.units)
{
for (let loadout of this.units[unit].loadouts)
{
for (let role of loadout.roles)
{
role = role.toUpperCase();
if (role !== "" && !roles.includes(role))
roles.push(role);
}
}
}
return roles;
}
getLabelsByRole(role: string)
{
var units = [];
@@ -13,7 +31,7 @@ export class UnitDatabase {
{
for (let loadout of this.units[unit].loadouts)
{
if (loadout.roles.includes(role))
if (loadout.roles.includes(role) || loadout.roles.includes(role.toLowerCase()))
{
units.push(this.units[unit].label)
break;
@@ -28,7 +46,7 @@ export class UnitDatabase {
var loadouts = [];
for (let loadout of this.units[unit].loadouts)
{
if (loadout.roles.includes(role) || loadout.roles.includes(""))
if (loadout.roles.includes(role) || loadout.roles.includes(role.toLowerCase()) || loadout.roles.includes(""))
{
loadouts.push(loadout.name)
}