Merge branch 'v0.1.0' of https://github.com/Pax1601/DCSOlympus into v0.1.0

This commit is contained in:
PeekabooSteam 2023-03-13 17:47:39 +00:00
commit 74ee1f7d44
20 changed files with 458 additions and 300 deletions

View File

@ -535,24 +535,49 @@ class DemoDataGenerator {
};
airbases(req, res){
var ret = {airbases: {}};
var ret = {airbases: {
["0"]: {
callsign: "Neutral",
lat: 37.3,
lng: -115.8,
coalition: "neutral"
},
["1"]: {
callsign: "Red",
lat: 37.3,
lng: -115.75,
coalition: "red"
},
["2"]: {
callsign: "Blue",
lat: 37.3,
lng: -115.7,
coalition: "blue"
}
}};
res.send(JSON.stringify(ret));
};
bullseyes(req, res){
var ret = {bullseyes: {}};
var ret = {bullseyes: {
"0": {
lat: 37.25,
lng: -115.8
},
"1": {
lat: 37.25,
lng: -115.75
},
"2": {
lat: 37.25,
lng: -115.7
}
}};
res.send(JSON.stringify(ret));
};
generateRandomUnitsDemoData(unitsNumber)
{
//var units = {};
//for (let i = 0; i < unitsNumber; i++)
//{
// units[String(i)] = JSON.parse(JSON.stringify(DEMO_UNIT_DATA));
// units[String(i)].flightData.latitude += (Math.random() - 0.5) * 0.3;
// units[String(i)].flightData.longitude += (Math.random() - 0.5) * 0.3;
//}
return {"units": DEMO_UNIT_DATA};
}
}

View File

@ -1,41 +1,44 @@
.airbase-marker-container {
height: 60px;
width: 60px;
left: -30px;
top: -30px;
border: 1px transparent solid;
:root {
--airbase-marker-height: 63px;
--airbase-marker-width: 63px;
}
[data-object|="airbase"] {
align-items: center;
cursor: pointer;
display: flex;
justify-content: center;
position: relative;
}
[data-hide-airbase] #map-container [data-object|="airbase"] {
display: none;
}
/******************************
Marker
******************************/
[data-object|="airbase"] .airbase {
background-color: transparent;
background-repeat: no-repeat;
background-size: cover;
position: absolute;
transform-origin: center;
z-index: 3;
}
.airbase-marker-image {
height: 60px;
width: 60px;
left: 0px;
top: 0px;
display: block;
position: absolute;
filter: drop-shadow(1px 1px 0 white) drop-shadow(1px -1px 0 white) drop-shadow(-1px 1px 0 white) drop-shadow(-1px -1px 0 white);
opacity: 0.8;
/* Airbase */
[data-object|="airbase"] .airbase-marker {
background-image: var(--airbase-marker-neutral-url);
height: var(--airbase-marker-height);
width: var(--airbase-marker-width);
}
.blue.airbase-marker-image {
filter: invert(40%) sepia(94%) saturate(2477%) hue-rotate(197deg) brightness(92%) contrast(91%) drop-shadow(1px 1px #FFFA) drop-shadow(1px -1px #FFFA) drop-shadow(-1px 1px 0px #FFFA) drop-shadow(-1px -1px #FFFA);
[data-object|="airbase"][data-coalition="red"] .airbase-marker {
background-image: var(--airbase-marker-red-url);
}
.red.airbase-marker-image {
filter:invert(32%) sepia(91%) saturate(5128%) hue-rotate(349deg) brightness(97%) contrast(97%) drop-shadow(1px 1px #FFFA) drop-shadow(1px -1px #FFFA) drop-shadow(-1px 1px 0px #FFFA) drop-shadow(-1px -1px #FFFA);
}
.neutral.airbase-marker-image {
filter: invert(71%) sepia(12%) saturate(9%) hue-rotate(319deg) brightness(92%) contrast(96%) drop-shadow(1px 1px #000A) drop-shadow(1px -1px #000A) drop-shadow(-1px 1px 0px #000A) drop-shadow(-1px -1px #000A);
}
.airbase-marker-name {
bottom: -20px;
position: absolute;
text-align: center;
font: 800 14px Arial;
white-space: nowrap;
-webkit-text-fill-color: white;
-webkit-text-stroke: 1px;
}
[data-object|="airbase"][data-coalition="blue"] .airbase-marker {
background-image: var(--airbase-marker-blue-url);
}

View File

@ -1,8 +1,5 @@
#contextmenu {
#map-contextmenu {
position: absolute;
}
#contextmenu {
display: flex;
flex-direction: column;
row-gap: 5px;
@ -38,7 +35,7 @@
margin-right: 10px;
}
#contextmenu>div:nth-child(2){
#map-contextmenu>div:nth-child(2){
display: flex;
flex-direction: row;
justify-content: space-between;
@ -46,22 +43,22 @@
padding-right: 0px;
}
#contextmenu>ul{
#map-contextmenu>ul{
max-height: 200px;
overflow-x: hidden;
overflow-y: auto;
}
#contextmenu .ol-panel {
#map-contextmenu .ol-panel {
width: 100%;
border-radius: var(--border-radius-sm);
}
#contextmenu ul {
#map-contextmenu ul {
margin: 0px;
}
#contextmenu>div:nth-child(n+3){
#map-contextmenu>div:nth-child(n+3){
display: flex;
flex-direction: column;
justify-content: space-between;
@ -69,7 +66,7 @@
row-gap: 5px;
}
#contextmenu .ol-select-container{
#map-contextmenu .ol-select-container{
width: 100%;
flex:0 0 auto;
align-self: stretch;
@ -205,4 +202,24 @@
[data-smoke-color="green"]::before{ background-color: green; }
[data-smoke-color="orange"]::before{ background-color: orange; }
/* Unit context menu */
#unit-contextmenu {
position: absolute;
display: flex;
flex-direction: column;
row-gap: 5px;
width: 150px;
height: fit-content;
z-index: 1000;
}
/* Airbase context menu */
#airbase-contextmenu {
position: absolute;
display: flex;
flex-direction: column;
row-gap: 5px;
width: 230px;
height: fit-content;
z-index: 1000;
}

View File

@ -1,6 +1,6 @@
@import url("layout.css");
@import url("airbases.css");
@import url("contextmenu.css");
@import url("contextmenus.css");
@import url("units.css");
/* Variables definitions */

View File

@ -0,0 +1,10 @@
<svg width="63" height="63" viewBox="0 0 63 63" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.4998 46.9756C40.0468 46.9756 46.9756 40.0468 46.9756 31.4998C46.9756 22.9528 40.0468 16.024 31.4998 16.024C22.9528 16.024 16.024 22.9528 16.024 31.4998C16.024 40.0468 22.9528 46.9756 31.4998 46.9756ZM31.4998 52.734C43.2271 52.734 52.734 43.2271 52.734 31.4998C52.734 19.7725 43.2271 10.2656 31.4998 10.2656C19.7725 10.2656 10.2656 19.7725 10.2656 31.4998C10.2656 43.2271 19.7725 52.734 31.4998 52.734Z" fill="white"/>
<path d="M35.3291 61L37.3291 61L37.3291 59L37.3291 49.2532L37.3291 47.2532L35.3291 47.2532L27.6709 47.2532L25.6709 47.2532L25.6709 49.2532L25.6709 59L25.6709 61L27.6709 61L35.3291 61Z" fill="#247BE2" stroke="white" stroke-width="4"/>
<path d="M47.2529 35.3291V37.3291H49.2529H58.9998H60.9998V35.3291V27.6709V25.6709L58.9998 25.6709H49.2529H47.2529V27.6709V35.3291Z" fill="#247BE2" stroke="white" stroke-width="4"/>
<path d="M35.3291 15.7471L37.3291 15.7471L37.3291 13.7471L37.3291 4.00023L37.3291 2.00023L35.3291 2.00023L27.6709 2.00023L25.6709 2.00023L25.6709 4.00023L25.6709 13.7471L25.6709 15.7471L27.6709 15.7471L35.3291 15.7471Z" fill="#247BE2" stroke="white" stroke-width="4"/>
<path d="M2 35.3291V37.3291H4H13.7468H15.7468V35.3291L15.7468 27.6709V25.6709L13.7468 25.6709H4H2V27.6709L2 35.3291Z" fill="#247BE2" stroke="white" stroke-width="4"/>
<circle cx="31.5001" cy="31.5001" r="15.4494" fill="white" stroke="#247BE2" stroke-width="6"/>
<line x1="25.7895" y1="23.9132" x2="36.5242" y2="38.6028" stroke="#247BE2" stroke-width="2" stroke-linecap="square"/>
<line x1="38.9357" y1="33.9313" x2="25.9313" y2="33.0643" stroke="#247BE2" stroke-width="2" stroke-linecap="square"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,10 @@
<svg width="63" height="63" viewBox="0 0 63 63" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.4998 46.9756C40.0468 46.9756 46.9756 40.0468 46.9756 31.4998C46.9756 22.9528 40.0468 16.024 31.4998 16.024C22.9528 16.024 16.024 22.9528 16.024 31.4998C16.024 40.0468 22.9528 46.9756 31.4998 46.9756ZM31.4998 52.734C43.2271 52.734 52.734 43.2271 52.734 31.4998C52.734 19.7725 43.2271 10.2656 31.4998 10.2656C19.7725 10.2656 10.2656 19.7725 10.2656 31.4998C10.2656 43.2271 19.7725 52.734 31.4998 52.734Z" fill="white"/>
<path d="M35.3291 61L37.3291 61L37.3291 59L37.3291 49.2532L37.3291 47.2532L35.3291 47.2532L27.6709 47.2532L25.6709 47.2532L25.6709 49.2532L25.6709 59L25.6709 61L27.6709 61L35.3291 61Z" fill="#CFD9E8" stroke="white" stroke-width="4"/>
<path d="M47.2529 35.3291V37.3291H49.2529H58.9998H60.9998V35.3291V27.6709V25.6709L58.9998 25.6709H49.2529H47.2529V27.6709V35.3291Z" fill="#CFD9E8" stroke="white" stroke-width="4"/>
<path d="M35.3291 15.7471L37.3291 15.7471L37.3291 13.7471L37.3291 4.00023L37.3291 2.00023L35.3291 2.00023L27.6709 2.00023L25.6709 2.00023L25.6709 4.00023L25.6709 13.7471L25.6709 15.7471L27.6709 15.7471L35.3291 15.7471Z" fill="#CFD9E8" stroke="white" stroke-width="4"/>
<path d="M2 35.3291V37.3291H4H13.7468H15.7468V35.3291L15.7468 27.6709V25.6709L13.7468 25.6709H4H2V27.6709L2 35.3291Z" fill="#CFD9E8" stroke="white" stroke-width="4"/>
<circle cx="31.5001" cy="31.5001" r="15.4494" fill="white" stroke="#CFD9E8" stroke-width="6"/>
<line x1="25.7895" y1="23.9132" x2="36.5242" y2="38.6028" stroke="#CFD9E8" stroke-width="2" stroke-linecap="square"/>
<line x1="38.9357" y1="33.9313" x2="25.9313" y2="33.0643" stroke="#CFD9E8" stroke-width="2" stroke-linecap="square"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,10 @@
<svg width="63" height="63" viewBox="0 0 63 63" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.4998 46.9756C40.0468 46.9756 46.9756 40.0468 46.9756 31.4998C46.9756 22.9528 40.0468 16.024 31.4998 16.024C22.9528 16.024 16.024 22.9528 16.024 31.4998C16.024 40.0468 22.9528 46.9756 31.4998 46.9756ZM31.4998 52.734C43.2271 52.734 52.734 43.2271 52.734 31.4998C52.734 19.7725 43.2271 10.2656 31.4998 10.2656C19.7725 10.2656 10.2656 19.7725 10.2656 31.4998C10.2656 43.2271 19.7725 52.734 31.4998 52.734Z" fill="white"/>
<path d="M35.3291 61L37.3291 61L37.3291 59L37.3291 49.2532L37.3291 47.2532L35.3291 47.2532L27.6709 47.2532L25.6709 47.2532L25.6709 49.2532L25.6709 59L25.6709 61L27.6709 61L35.3291 61Z" fill="#ff5858" stroke="white" stroke-width="4"/>
<path d="M47.2529 35.3291V37.3291H49.2529H58.9998H60.9998V35.3291V27.6709V25.6709L58.9998 25.6709H49.2529H47.2529V27.6709V35.3291Z" fill="#ff5858" stroke="white" stroke-width="4"/>
<path d="M35.3291 15.7471L37.3291 15.7471L37.3291 13.7471L37.3291 4.00023L37.3291 2.00023L35.3291 2.00023L27.6709 2.00023L25.6709 2.00023L25.6709 4.00023L25.6709 13.7471L25.6709 15.7471L27.6709 15.7471L35.3291 15.7471Z" fill="#ff5858" stroke="white" stroke-width="4"/>
<path d="M2 35.3291V37.3291H4H13.7468H15.7468V35.3291L15.7468 27.6709V25.6709L13.7468 25.6709H4H2V27.6709L2 35.3291Z" fill="#ff5858" stroke="white" stroke-width="4"/>
<circle cx="31.5001" cy="31.5001" r="15.4494" fill="white" stroke="#ff5858" stroke-width="6"/>
<line x1="25.7895" y1="23.9132" x2="36.5242" y2="38.6028" stroke="#ff5858" stroke-width="2" stroke-linecap="square"/>
<line x1="38.9357" y1="33.9313" x2="25.9313" y2="33.0643" stroke="#ff5858" stroke-width="2" stroke-linecap="square"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -206,4 +206,12 @@
--spawn-aircraft-url: url( "/themes/olympus/images/spawn_aircraft.svg" );
--spawn-ground-url: url( "/themes/olympus/images/spawn_ground.svg" );
--spawn-smoke-url: url( "/themes/olympus/images/spawn_smoke.svg" );
/*** Airbase ***/
--airbase-marker-height: 63px;
--airbase-marker-width: 63px;
--airbase-marker-blue-url: url( "/themes/olympus/images/icon_airbase_blue.svg" );
--airbase-marker-neutral-url: url( "/themes/olympus/images/icon_airbase_neutral.svg" );
--airbase-marker-red-url: url( "/themes/olympus/images/icon_airbase_red.svg" );
}

View File

@ -0,0 +1,9 @@
import { ContextMenu } from "./contextmenu";
export class AirbaseContextMenu extends ContextMenu {
constructor(id: string)
{
super(id);
}
}

View File

@ -1,74 +1,16 @@
import { LatLng } from "leaflet";
import { getActiveCoalition, setActiveCoalition } from "..";
import { ContextMenuOption } from "../@types/dom";
import { ClickEvent } from "../map/map";
import { spawnAircraft, spawnGroundUnit } from "../server/server";
import { aircraftDatabase } from "../units/aircraftdatabase";
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
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;
#groundUnitRoleDropdown: Dropdown;
#groundUnitTypeDropdown: Dropdown;
#spawnOptions: SpawnOptions = {role: "", type: "", latlng: this.#latlng, loadout: null, coalition: "blue", airbaseName: null};
constructor(id: string,) {
constructor(id: string) {
this.#container = document.getElementById(id);
this.#container?.querySelector("#context-menu-switch")?.addEventListener('change', (e) => this.#onSwitch(e));
this.#aircraftRoleDropdown = new Dropdown("aircraft-role-options", (role: string) => this.#setAircraftRole(role));
this.#aircraftTypeDropdown = new Dropdown("aircraft-type-options", (type: string) => this.#setAircraftType(type));
this.#aircraftLoadoutDropdown = new Dropdown("loadout-options", (loadout: string) => this.#setAircraftLoadout(loadout));
this.#groundUnitRoleDropdown = new Dropdown("ground-unit-role-options", (role: string) => this.#setGroundUnitRole(role));
this.#groundUnitTypeDropdown = new Dropdown("ground-unit-type-options", (type: string) => this.#setGroundUnitType(type));
document.addEventListener("contextMenuShow", (e: any) => {
this.#container?.querySelector("#aircraft-spawn-menu")?.classList.toggle("hide", e.detail.type !== "aircraft");
this.#container?.querySelector("#aircraft-spawn-button")?.classList.toggle("is-open", e.detail.type === "aircraft");
this.#container?.querySelector("#ground-unit-spawn-menu")?.classList.toggle("hide", e.detail.type !== "ground-unit");
this.#container?.querySelector("#ground-unit-spawn-button")?.classList.toggle("is-open", e.detail.type === "ground-unit");
this.#container?.querySelector("#smoke-spawn-menu")?.classList.toggle("hide", e.detail.type !== "smoke");
this.#container?.querySelector("#smoke-spawn-button")?.classList.toggle("is-open", e.detail.type === "smoke");
this.#resetAircraftRole();
this.#resetAircraftType();
this.#resetGroundUnitRole();
this.#resetGroundUnitType();
})
document.addEventListener("contextMenuDeployAircraft", () => {
this.hide();
this.#spawnOptions.coalition = getActiveCoalition();
if (this.#spawnOptions)
spawnAircraft(this.#spawnOptions);
})
document.addEventListener("contextMenuDeployGroundUnit", () => {
this.hide();
this.#spawnOptions.coalition = getActiveCoalition();
if (this.#spawnOptions)
spawnGroundUnit(this.#spawnOptions);
})
this.hide();
}
show(x: number, y: number, latlng: LatLng) {
this.#spawnOptions.latlng = latlng;
this.#latlng = latlng;
this.#container?.classList.toggle("hide", false);
if (this.#container != null) {
if (x + this.#container.offsetWidth < window.innerWidth)
@ -87,117 +29,13 @@ export class ContextMenu {
this.#container?.classList.toggle("hide", true);
}
#onSwitch(e: any) {
if (this.#container != null) {
if (e.srcElement.checked)
setActiveCoalition("red");
else
setActiveCoalition("blue");
}
}
/********* Aircraft spawn menu *********/
#setAircraftRole(role: string)
getContainer()
{
if (this.#spawnOptions != null)
{
this.#spawnOptions.role = role;
this.#resetAircraftRole();
this.#aircraftTypeDropdown.setOptions(aircraftDatabase.getLabelsByRole(role));
this.#aircraftTypeDropdown.selectValue(0);
}
return this.#container;
}
#resetAircraftRole() {
(<HTMLButtonElement>this.#container?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
(<HTMLButtonElement>this.#container?.querySelector("#loadout-list")).replaceChildren();
this.#aircraftRoleDropdown.reset();
this.#aircraftTypeDropdown.reset();
this.#aircraftRoleDropdown.setOptions(aircraftDatabase.getRoles());
}
#setAircraftType(label: string)
getLatLng()
{
if (this.#spawnOptions != null)
{
this.#resetAircraftType();
var type = aircraftDatabase.getNameByLabel(label);
if (type != null)
{
this.#spawnOptions.type = type;
this.#aircraftLoadoutDropdown.setOptions(aircraftDatabase.getLoadoutNamesByRole(type, this.#spawnOptions.role));
this.#aircraftLoadoutDropdown.selectValue(0);
var image = (<HTMLImageElement>this.#container?.querySelector("#unit-image"));
image.src = `images/units/${aircraftDatabase.getByLabel(label)?.filename}`;
image.classList.toggle("hide", false);
}
}
}
#resetAircraftType() {
(<HTMLButtonElement>this.#container?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
(<HTMLButtonElement>this.#container?.querySelector("#loadout-list")).replaceChildren();
this.#aircraftLoadoutDropdown.reset();
(<HTMLImageElement>this.#container?.querySelector("#unit-image")).classList.toggle("hide", true);
}
#setAircraftLoadout(loadoutName: string)
{
if (this.#spawnOptions != null)
{
var loadout = aircraftDatabase.getLoadoutsByName(this.#spawnOptions.type, loadoutName);
if (loadout)
{
this.#spawnOptions.loadout = loadout.code;
(<HTMLButtonElement>this.#container?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false;
var items = loadout.items.map((item: any) => {return `${item.quantity}x ${item.name}`;});
items.length == 0? items.push("Empty loadout"): "";
(<HTMLButtonElement>this.#container?.querySelector("#loadout-list")).replaceChildren(
...items.map((item: any) => {
var div = document.createElement('div');
div.innerText = item;
return div;
})
)
}
}
}
/********* Ground unit spawn menu *********/
#setGroundUnitRole(role: string)
{
if (this.#spawnOptions != null)
{
this.#spawnOptions.role = role;
this.#resetGroundUnitRole();
this.#groundUnitTypeDropdown.setOptions(groundUnitsDatabase.getLabelsByRole(role));
this.#groundUnitTypeDropdown.selectValue(0);
}
}
#resetGroundUnitRole() {
(<HTMLButtonElement>this.#container?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
(<HTMLButtonElement>this.#container?.querySelector("#loadout-list")).replaceChildren();
this.#groundUnitRoleDropdown.reset();
this.#groundUnitTypeDropdown.reset();
this.#groundUnitRoleDropdown.setOptions(groundUnitsDatabase.getRoles());
}
#setGroundUnitType(label: string)
{
if (this.#spawnOptions != null)
{
this.#resetGroundUnitType();
var type = groundUnitsDatabase.getNameByLabel(label);
if (type != null)
{
this.#spawnOptions.type = type;
(<HTMLButtonElement>this.#container?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false;
}
}
}
#resetGroundUnitType() {
(<HTMLButtonElement>this.#container?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
return this.#latlng;
}
}

View File

@ -0,0 +1,185 @@
import { LatLng } from "leaflet";
import { getActiveCoalition, setActiveCoalition } from "..";
import { spawnAircraft, spawnGroundUnit } from "../server/server";
import { aircraftDatabase } from "../units/aircraftdatabase";
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
import { ContextMenu } from "./contextmenu";
import { Dropdown } from "./dropdown";
export interface SpawnOptions {
role: string;
type: string;
latlng: LatLng;
coalition: string;
loadout: string | null;
airbaseName: string | null;
}
export class MapContextMenu extends ContextMenu {
#aircraftRoleDropdown: Dropdown;
#aircraftTypeDropdown: Dropdown;
#aircraftLoadoutDropdown: Dropdown;
#groundUnitRoleDropdown: Dropdown;
#groundUnitTypeDropdown: Dropdown;
#spawnOptions: SpawnOptions = {role: "", type: "", latlng: new LatLng(0, 0), loadout: null, coalition: "blue", airbaseName: null};
constructor(id: string) {
super(id);
this.getContainer()?.querySelector("#context-menu-switch")?.addEventListener('change', (e) => this.#onSwitch(e));
this.#aircraftRoleDropdown = new Dropdown("aircraft-role-options", (role: string) => this.#setAircraftRole(role));
this.#aircraftTypeDropdown = new Dropdown("aircraft-type-options", (type: string) => this.#setAircraftType(type));
this.#aircraftLoadoutDropdown = new Dropdown("loadout-options", (loadout: string) => this.#setAircraftLoadout(loadout));
this.#groundUnitRoleDropdown = new Dropdown("ground-unit-role-options", (role: string) => this.#setGroundUnitRole(role));
this.#groundUnitTypeDropdown = new Dropdown("ground-unit-type-options", (type: string) => this.#setGroundUnitType(type));
document.addEventListener("contextMenuShow", (e: any) => {
this.getContainer()?.querySelector("#aircraft-spawn-menu")?.classList.toggle("hide", e.detail.type !== "aircraft");
this.getContainer()?.querySelector("#aircraft-spawn-button")?.classList.toggle("is-open", e.detail.type === "aircraft");
this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.classList.toggle("hide", e.detail.type !== "ground-unit");
this.getContainer()?.querySelector("#ground-unit-spawn-button")?.classList.toggle("is-open", e.detail.type === "ground-unit");
this.getContainer()?.querySelector("#smoke-spawn-menu")?.classList.toggle("hide", e.detail.type !== "smoke");
this.getContainer()?.querySelector("#smoke-spawn-button")?.classList.toggle("is-open", e.detail.type === "smoke");
this.#resetAircraftRole();
this.#resetAircraftType();
this.#resetGroundUnitRole();
this.#resetGroundUnitType();
})
document.addEventListener("contextMenuDeployAircraft", () => {
this.hide();
this.#spawnOptions.coalition = getActiveCoalition();
if (this.#spawnOptions)
spawnAircraft(this.#spawnOptions);
})
document.addEventListener("contextMenuDeployGroundUnit", () => {
this.hide();
this.#spawnOptions.coalition = getActiveCoalition();
if (this.#spawnOptions)
spawnGroundUnit(this.#spawnOptions);
})
this.hide();
}
show(x: number, y: number, latlng: LatLng) {
super.show(x, y, latlng);
this.#spawnOptions.latlng = latlng;
}
#onSwitch(e: any) {
if (this.getContainer() != null) {
if (e.srcElement.checked)
setActiveCoalition("red");
else
setActiveCoalition("blue");
}
}
/********* Aircraft spawn menu *********/
#setAircraftRole(role: string)
{
if (this.#spawnOptions != null)
{
this.#spawnOptions.role = role;
this.#resetAircraftRole();
this.#aircraftTypeDropdown.setOptions(aircraftDatabase.getLabelsByRole(role));
this.#aircraftTypeDropdown.selectValue(0);
}
}
#resetAircraftRole() {
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
(<HTMLButtonElement>this.getContainer()?.querySelector("#loadout-list")).replaceChildren();
this.#aircraftRoleDropdown.reset();
this.#aircraftTypeDropdown.reset();
this.#aircraftRoleDropdown.setOptions(aircraftDatabase.getRoles());
}
#setAircraftType(label: string)
{
if (this.#spawnOptions != null)
{
this.#resetAircraftType();
var type = aircraftDatabase.getNameByLabel(label);
if (type != null)
{
this.#spawnOptions.type = type;
this.#aircraftLoadoutDropdown.setOptions(aircraftDatabase.getLoadoutNamesByRole(type, this.#spawnOptions.role));
this.#aircraftLoadoutDropdown.selectValue(0);
var image = (<HTMLImageElement>this.getContainer()?.querySelector("#unit-image"));
image.src = `images/units/${aircraftDatabase.getByLabel(label)?.filename}`;
image.classList.toggle("hide", false);
}
}
}
#resetAircraftType() {
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
(<HTMLButtonElement>this.getContainer()?.querySelector("#loadout-list")).replaceChildren();
this.#aircraftLoadoutDropdown.reset();
(<HTMLImageElement>this.getContainer()?.querySelector("#unit-image")).classList.toggle("hide", true);
}
#setAircraftLoadout(loadoutName: string)
{
if (this.#spawnOptions != null)
{
var loadout = aircraftDatabase.getLoadoutsByName(this.#spawnOptions.type, loadoutName);
if (loadout)
{
this.#spawnOptions.loadout = loadout.code;
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false;
var items = loadout.items.map((item: any) => {return `${item.quantity}x ${item.name}`;});
items.length == 0? items.push("Empty loadout"): "";
(<HTMLButtonElement>this.getContainer()?.querySelector("#loadout-list")).replaceChildren(
...items.map((item: any) => {
var div = document.createElement('div');
div.innerText = item;
return div;
})
)
}
}
}
/********* Ground unit spawn menu *********/
#setGroundUnitRole(role: string)
{
if (this.#spawnOptions != null)
{
this.#spawnOptions.role = role;
this.#resetGroundUnitRole();
this.#groundUnitTypeDropdown.setOptions(groundUnitsDatabase.getLabelsByRole(role));
this.#groundUnitTypeDropdown.selectValue(0);
}
}
#resetGroundUnitRole() {
(<HTMLButtonElement>this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
(<HTMLButtonElement>this.getContainer()?.querySelector("#loadout-list")).replaceChildren();
this.#groundUnitRoleDropdown.reset();
this.#groundUnitTypeDropdown.reset();
this.#groundUnitRoleDropdown.setOptions(groundUnitsDatabase.getRoles());
}
#setGroundUnitType(label: string)
{
if (this.#spawnOptions != null)
{
this.#resetGroundUnitType();
var type = groundUnitsDatabase.getNameByLabel(label);
if (type != null)
{
this.#spawnOptions.type = type;
(<HTMLButtonElement>this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false;
}
}
}
#resetGroundUnitType() {
(<HTMLButtonElement>this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
}
}

View File

@ -0,0 +1,18 @@
import { ContextMenu } from "./contextmenu";
export class UnitContextMenu extends ContextMenu {
constructor(id: string) {
super(id);
}
setOptions(options: string[], callback: CallableFunction)
{
this.getContainer()?.replaceChildren(...options.map((option: string) =>
{
var button = document.createElement("button");
button.innerText = option;
button.addEventListener("click", () => callback(option));
return (button);
}));
}
}

View File

@ -1,7 +1,6 @@
import { Map } from "./map/map"
import { UnitsManager } from "./units/unitsmanager";
import { UnitInfoPanel } from "./panels/unitinfopanel";
import { ContextMenu } from "./controls/contextmenu";
import { ConnectionStatusPanel } from "./panels/connectionstatuspanel";
import { MissionHandler } from "./missionhandler/missionhandler";
import { UnitControlPanel } from "./panels/unitcontrolpanel";
@ -13,7 +12,6 @@ import { LogPanel } from "./panels/logpanel";
import { getAirbases, getBulllseye as getBulllseyes, getUnits, toggleDemoEnabled } from "./server/server";
var map: Map;
var contextMenu: ContextMenu;
var unitsManager: UnitsManager;
var missionHandler: MissionHandler;
@ -43,9 +41,6 @@ function setup() {
unitsManager = new UnitsManager();
missionHandler = new MissionHandler();
/* Context menus */
contextMenu = new ContextMenu("contextmenu");
/* Panels */
unitInfoPanel = new UnitInfoPanel("unit-info-panel");
unitControlPanel = new UnitControlPanel("unit-control-panel");
@ -204,10 +199,6 @@ export function getMissionData() {
return missionHandler;
}
export function getContextMenu() {
return contextMenu;
}
export function getUnitsManager() {
return unitsManager;
}

View File

@ -1,24 +1,15 @@
import * as L from "leaflet"
import { getContextMenu, getUnitsManager } from "..";
import { getUnitsManager } from "..";
import { BoxSelect } from "./boxselect";
import { SpawnOptions } from "../controls/contextmenu";
import { MapContextMenu, SpawnOptions } from "../controls/mapcontextmenu";
import { UnitContextMenu } from "../controls/unitcontextmenu";
import { AirbaseContextMenu } from "../controls/airbasecontextmenu";
export const IDLE = "IDLE";
export const MOVE_UNIT = "MOVE_UNIT";
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
export interface ClickEvent {
x: number;
y: number;
latlng: L.LatLng;
}
export interface SpawnEvent extends ClickEvent {
airbaseName: string | null;
coalitionID: number | null;
}
export class Map extends L.Map {
#state: string;
#layer: L.TileLayer | null = null;
@ -26,6 +17,10 @@ export class Map extends L.Map {
#leftClickTimer: number = 0;
#lastMousePosition: L.Point = new L.Point(0, 0);
#mapContextMenu: MapContextMenu = new MapContextMenu("map-contextmenu");
#unitContextMenu: UnitContextMenu = new UnitContextMenu("unit-contextmenu");
#airbaseContextMenu: AirbaseContextMenu = new AirbaseContextMenu("airbase-contextmenu");
constructor(ID: string) {
/* Init the leaflet map */
//@ts-ignore
@ -36,7 +31,6 @@ export class Map extends L.Map {
/* Init the state machine */
this.#state = IDLE;
/* Register event handles */
this.on("click", (e: any) => this.#onClick(e));
@ -112,19 +106,62 @@ export class Map extends L.Map {
return this.#state;
}
/* Context Menu */
showContextMenu(e: any, spawnOptions: SpawnOptions | null = null) {
/* Context Menus */
hideAllContextMenus()
{
this.hideMapContextMenu();
this.hideUnitContextMenu();
this.hideAirbaseContextMenu();
}
showMapContextMenu(e: any) {
this.hideAllContextMenus();
var x = e.originalEvent.x;
var y = e.originalEvent.y;
getContextMenu()?.show(x, y, e.latlng);
this.#mapContextMenu.show(x, y, e.latlng);
document.dispatchEvent(new CustomEvent("mapContextMenu"));
}
hideContextMenu() {
getContextMenu()?.hide();
hideMapContextMenu() {
this.#mapContextMenu.hide();
document.dispatchEvent(new CustomEvent("mapContextMenu"));
}
getMapContextMenu(){
return this.#mapContextMenu;
}
showUnitContextMenu(e: any) {
this.hideAllContextMenus();
var x = e.originalEvent.x;
var y = e.originalEvent.y;
this.#unitContextMenu.show(x, y, e.latlng);
}
getUnitContextMenu(){
return this.#unitContextMenu;
}
hideUnitContextMenu() {
this.#unitContextMenu.hide();
}
showAirbaseContextMenu(e: any) {
this.hideAllContextMenus();
var x = e.originalEvent.x;
var y = e.originalEvent.y;
this.#airbaseContextMenu.show(x, y, e.latlng);
}
getAirbaseContextMenu(){
return this.#airbaseContextMenu;
}
hideAirbaseContextMenu() {
this.#airbaseContextMenu.hide();
}
/* Mouse coordinates */
getMousePosition() {
return this.#lastMousePosition;
}
@ -134,7 +171,7 @@ export class Map extends L.Map {
}
/* Spawn from air base */
spawnFromAirbase(e: SpawnEvent)
spawnFromAirbase(e: any)
{
//this.#aircraftSpawnMenu(e);
}
@ -142,14 +179,13 @@ export class Map extends L.Map {
/* Event handlers */
#onClick(e: any) {
if (!this.#preventLeftClick) {
this.hideContextMenu();
this.hideAllContextMenus();
if (this.#state === IDLE) {
}
else if (this.#state === MOVE_UNIT) {
this.setState(IDLE);
getUnitsManager().deselectAllUnits();
this.hideContextMenu();
}
}
}
@ -159,10 +195,10 @@ export class Map extends L.Map {
}
#onContextMenu(e: any) {
this.hideContextMenu();
this.hideMapContextMenu();
if (this.#state === IDLE) {
if (this.#state == IDLE) {
this.showContextMenu(e);
this.showMapContextMenu(e);
}
}
else if (this.#state === MOVE_UNIT) {

View File

@ -10,41 +10,28 @@ export interface AirbaseOptions
export class Airbase extends L.Marker
{
#name: string = "";
#coalitionID: number = -1;
#coalition: string = "";
constructor(options: AirbaseOptions)
{
super(options.position, { riseOnHover: true });
this.#name = options.name;
var icon = new L.DivIcon({
html: `<table class="airbase-marker-container" id="container">
<tr>
<td>
<img class="airbase-marker-image" id="icon" src="${options.src}">
<div class="airbase-marker-name" id="name">${options.name}</div>
</td>
</tr>
</table>`,
className: 'airbase-marker'}); // Set the marker, className must be set to avoid white square
html: ` <div class="airbase" data-object="airbase" data-coalition="red">
<div class="airbase-marker"> </div>
</div>`,
className: 'leaflet-airbase-marker',
iconSize: [63, 63]
}); // Set the marker, className must be set to avoid white square
this.setIcon(icon);
}
setCoalitionID(coalitionID: number)
setCoalition(coalition: string)
{
this.#coalitionID = coalitionID;
var element = this.getElement();
if (element != null)
{
var img = element.querySelector("#icon");
if (img != null)
{
img.classList.toggle("blue", this.#coalitionID == 2);
img.classList.toggle("red", this.#coalitionID == 1);
img.classList.toggle("neutral", this.#coalitionID == 0);
}
}
this.#coalition = coalition;
this.getElement()?.setAttribute("data-coalition", this.#coalition);
}
getName()
@ -52,8 +39,8 @@ export class Airbase extends L.Marker
return this.#name;
}
getCoalitionID()
getCoalition()
{
return this.#coalitionID;
return this.#coalition;
}
}

View File

@ -1,6 +1,5 @@
import { Marker, LatLng, Icon } from "leaflet";
import { getMap, getUnitsManager } from "..";
import { SpawnEvent } from "../map/map";
import { Airbase } from "./airbase";
var bullseyeIcons = [
@ -71,7 +70,8 @@ export class MissionHandler
}
else
{
this.#airbasesMarkers[idx].setCoalitionID(airbase.coalition);
this.#airbasesMarkers[idx].setLatLng(new LatLng(airbase.lat, airbase.lng));
this.#airbasesMarkers[idx].setCoalition(airbase.coalition);
}
}
}
@ -84,20 +84,21 @@ export class MissionHandler
else
options = ["Spawn unit"];
getMap().showAirbaseContextMenu(e);
//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) {
if (option === "Spawn unit") {
var spawnEvent: SpawnEvent = {x: e.originalEvent.x, y: e.originalEvent.y, latlng: e.latlng, airbaseName: e.sourceTarget.getName(), coalitionID: e.sourceTarget.getCoalitionID()};
getMap().spawnFromAirbase(spawnEvent);
}
else if (option === "Land here")
{
getMap().hideContextMenu();
getUnitsManager().selectedUnitsLandAt(e.latlng);
}
//if (option === "Spawn unit") {
// var spawnEvent: SpawnEvent = {x: e.originalEvent.x, y: e.originalEvent.y, latlng: e.latlng, airbaseName: e.sourceTarget.getName(), coalitionID: e.sourceTarget.getCoalitionID()};
// getMap().spawnFromAirbase(spawnEvent);
//}
//else if (option === "Land here")
//{
// getMap().hideContextMenu();
// getUnitsManager().selectedUnitsLandAt(e.latlng);
//}
}
}

View File

@ -1,6 +1,6 @@
import * as L from 'leaflet'
import { setConnected } from '..';
import { SpawnOptions } from '../controls/contextmenu';
import { SpawnOptions } from '../controls/mapcontextmenu';
/* Edit here to change server address */
const REST_ADDRESS = "http://localhost:30000/olympus";

View File

@ -91,7 +91,7 @@ export class Unit extends Marker {
var icon = new DivIcon({
html: html,
className: 'ol-unit-marker',
className: 'leaflet-unit-marker',
iconAnchor: [0, 0]
});
this.setIcon(icon);
@ -338,14 +338,16 @@ export class Unit extends Marker {
#onContextMenu(e: any) {
var options = [
'Attack',
'Follow'
'Attack'
]
//getMap().showContextMenu(e.originalEvent, "Action: " + this.getData().unitName, options.map((option: string) => {return {tooltip: option, src: "", callback: (action: string) => this.#executeAction(action)}}));
getMap().showUnitContextMenu(e);
getMap().getUnitContextMenu().setOptions(options, (option: string) => {
getMap().hideUnitContextMenu();
this.#executeAction(option);
});
}
#executeAction(action: string) {
getMap().hideContextMenu();
if (action === "Attack")
getUnitsManager().selectedUnitsAttackUnit(this.ID);
}

View File

@ -1,4 +1,4 @@
<div id="contextmenu">
<div id="map-contextmenu">
<div id="active-coalition-label" data-active-coalition="blue"></div>
<div class="ol-panel">
<label id="context-menu-switch" class="toggle" for="context-menu-toggle">
@ -73,4 +73,12 @@
<button class="smoke-button" title="" data-smoke-color="green" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "type": "green" }'>Green smoke</button>
<button class="smoke-button" title="" data-smoke-color="orange" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "type": "orange" }'>Orange smoke</button>
</div>
</div>
<div id="unit-contextmenu" class="ol-panel">
<!-- Here the available unit options will be shown -->
</div>
<div id="airbase-contextmenu" class="ol-panel">
</div>

View File

@ -25,7 +25,7 @@
<%- include('aic.ejs') %>
<%- include('atc.ejs') %>
<%- include('contextmenu.ejs') %>
<%- include('contextmenus.ejs') %>
<%- include('unitcontrolpanel.ejs') %>
<%- include('unitinfopanel.ejs') %>
<%- include('mouseinfopanel.ejs') %>