mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Resolved conflicts
This commit is contained in:
@@ -2,6 +2,7 @@ var express = require('express');
|
||||
var path = require('path');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var logger = require('morgan');
|
||||
var fs = require('fs');
|
||||
|
||||
var atcRouter = require('./routes/api/atc');
|
||||
var indexRouter = require('./routes/index');
|
||||
@@ -23,12 +24,14 @@ app.use('/uikit', uikitRouter);
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
let rawdata = fs.readFileSync('../olympus.json');
|
||||
let config = JSON.parse(rawdata);
|
||||
app.get('/config', (req, res) => res.send(config));
|
||||
|
||||
module.exports = app;
|
||||
|
||||
const DemoDataGenerator = require('./demo.js');
|
||||
|
||||
var demoDataGenerator = new DemoDataGenerator(10);
|
||||
|
||||
app.get('/demo/units', (req, res) => demoDataGenerator.units(req, res));
|
||||
app.get('/demo/logs', (req, res) => demoDataGenerator.logs(req, res));
|
||||
app.get('/demo/bullseyes', (req, res) => demoDataGenerator.bullseyes(req, res));
|
||||
|
||||
@@ -625,6 +625,7 @@ class DemoDataGenerator {
|
||||
|
||||
units(req, res){
|
||||
var ret = this.demoUnits;
|
||||
ret.time = Date.now();
|
||||
res.send(JSON.stringify(ret));
|
||||
};
|
||||
|
||||
|
||||
4
client/package-lock.json
generated
4
client/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "DCSOlympus",
|
||||
"version": "0.1.0-alpha",
|
||||
"version": "0.1.1-alpha",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "DCSOlympus",
|
||||
"version": "0.1.0-alpha",
|
||||
"version": "0.1.1-alpha",
|
||||
"dependencies": {
|
||||
"@types/geojson": "^7946.0.10",
|
||||
"@types/leaflet": "^1.9.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "DCSOlympus",
|
||||
"node-main": "./bin/www",
|
||||
"main": "http://localhost:3000",
|
||||
"version": "0.1.0-alpha",
|
||||
"version": "0.1.1-alpha",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"copy": "copy .\\node_modules\\leaflet\\dist\\leaflet.css .\\public\\stylesheets\\leaflet.css",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
position: absolute;
|
||||
row-gap: 5px;
|
||||
width: 230px;
|
||||
z-index: 1000;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#aircraft-spawn-menu {
|
||||
|
||||
@@ -77,6 +77,23 @@ form > div {
|
||||
}
|
||||
|
||||
|
||||
.ol-scrollable::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.ol-scrollable::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
.ol-scrollable::-webkit-scrollbar-thumb {
|
||||
background-color: white;
|
||||
border-radius: 100px;
|
||||
opacity: 0.8;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
||||
.ol-panel {
|
||||
background-color: var(--background-steel);
|
||||
border-radius: 15px;
|
||||
@@ -146,8 +163,9 @@ form > div {
|
||||
}
|
||||
|
||||
.ol-select>.ol-select-options {
|
||||
position: absolute;
|
||||
border-radius: var( --border-radius-md );
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
max-height: 0;
|
||||
translate: 0 -2px;
|
||||
z-index: 1000;
|
||||
@@ -164,9 +182,15 @@ form > div {
|
||||
overflow-y: auto;
|
||||
padding: 8px 0;
|
||||
min-width: 100%;
|
||||
z-index:9999;
|
||||
}
|
||||
|
||||
|
||||
.ol-select.is-open[data-position="top"] > .ol-select-options {
|
||||
top:0;
|
||||
translate:0 -100%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.ol-select>.ol-select-options > div {
|
||||
@@ -214,22 +238,6 @@ form > div {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.ol-select>.ol-select-options::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.ol-select>.ol-select-options::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
.ol-select>.ol-select-options::-webkit-scrollbar-thumb {
|
||||
background-color: white;
|
||||
border-radius: 100px;
|
||||
opacity: 0.8;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
||||
.ol-panel-list {
|
||||
border-radius: var(--border-radius-sm);
|
||||
@@ -663,101 +671,93 @@ body[data-hide-navyunit] #unit-visibility-control-navyunit {
|
||||
}
|
||||
|
||||
#roe-buttons-container button::before, #reaction-to-threat-buttons-container button::before {
|
||||
background-position:center;
|
||||
background-repeat: no-repeat;
|
||||
background-size:16px 16px;
|
||||
content: "";
|
||||
display:block;
|
||||
height:16px;
|
||||
width:16px;
|
||||
height:24px;
|
||||
width:24px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#roe-buttons-container button[title="Free"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_free_light.svg");
|
||||
}
|
||||
|
||||
#roe-buttons-container button[title="Designated free"]::before {
|
||||
#roe-buttons-container button[title="Hold"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_stop_light.svg");
|
||||
}
|
||||
|
||||
#roe-buttons-container button[title="Designated"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_stop_light.svg");
|
||||
#roe-buttons-container button[title="Hold"].selected::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_stop_dark.svg");
|
||||
}
|
||||
|
||||
/**/
|
||||
|
||||
#roe-buttons-container button[title="Return"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_defend_light.svg");
|
||||
}
|
||||
|
||||
#roe-buttons-container button[title="Return"].selected::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_defend_dark.svg");
|
||||
}
|
||||
|
||||
/**/
|
||||
|
||||
#roe-buttons-container button[title="Designated"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_target_light.svg");
|
||||
}
|
||||
|
||||
#roe-buttons-container button[title="Hold"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_actions_nothing_light.svg");
|
||||
#roe-buttons-container button[title="Designated"].selected::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_target_dark.svg");
|
||||
}
|
||||
|
||||
/**/
|
||||
|
||||
#roe-buttons-container button[title="Free"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_free_light.svg");
|
||||
}
|
||||
|
||||
#roe-buttons-container button[title="Free"].selected::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_free_dark.svg");
|
||||
}
|
||||
|
||||
#roe-buttons-container button[title="Designated free"].selected::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_stop_dark.svg");
|
||||
}
|
||||
|
||||
#roe-buttons-container button[title="Designated"].selected::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_stop_dark.svg");
|
||||
}
|
||||
|
||||
#roe-buttons-container button[title="Return"].selected::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_target_dark.svg");
|
||||
}
|
||||
|
||||
#roe-buttons-container button[title="Hold"].selected::before {
|
||||
background-image: url( "/themes/olympus/images/icons_actions_nothing_dark.svg");
|
||||
}
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
|
||||
#reaction-to-threat-buttons-container button[title="None"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_actions_nothing_light.svg");
|
||||
background-image: url( "/themes/olympus/images/icons_threat_nothing_light.svg");
|
||||
}
|
||||
|
||||
#reaction-to-threat-buttons-container button[title="Passive"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_stop_light.svg");
|
||||
}
|
||||
|
||||
#reaction-to-threat-buttons-container button[title="Evade"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_stop_light.svg");
|
||||
}
|
||||
|
||||
#reaction-to-threat-buttons-container button[title="Escape"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_threat_retreat_light.svg");
|
||||
}
|
||||
|
||||
#reaction-to-threat-buttons-container button[title="Abort"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_stop_light.svg");
|
||||
}
|
||||
#reaction-to-threat-buttons-container button[title="None"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_actions_nothing_light.svg");
|
||||
}
|
||||
|
||||
|
||||
#reaction-to-threat-buttons-container button[title="None"].selected::before {
|
||||
background-image: url( "/themes/olympus/images/icons_actions_nothing_dark.svg");
|
||||
background-image: url( "/themes/olympus/images/icons_threat_nothing_dark.svg");
|
||||
}
|
||||
|
||||
|
||||
/**/
|
||||
|
||||
|
||||
#reaction-to-threat-buttons-container button[title="Passive"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_threat_cms_light.svg");
|
||||
}
|
||||
|
||||
#reaction-to-threat-buttons-container button[title="Passive"].selected::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_stop_dark.svg");
|
||||
background-image: url( "/themes/olympus/images/icons_threat_cms_dark.svg");
|
||||
}
|
||||
|
||||
|
||||
/**/
|
||||
|
||||
|
||||
#reaction-to-threat-buttons-container button[title="Evade"]::before {
|
||||
background-image: url( "/themes/olympus/images/icons_threat_defend_light.svg");
|
||||
}
|
||||
|
||||
#reaction-to-threat-buttons-container button[title="Evade"].selected::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_stop_dark.svg");
|
||||
background-image: url( "/themes/olympus/images/icons_threat_defend_dark.svg");
|
||||
}
|
||||
|
||||
#reaction-to-threat-buttons-container button[title="Escape"].selected::before {
|
||||
background-image: url( "/themes/olympus/images/icons_threat_retreat_dark.svg");
|
||||
}
|
||||
|
||||
#reaction-to-threat-buttons-container button[title="Abort"].selected::before {
|
||||
background-image: url( "/themes/olympus/images/icons_roe_stop_dark.svg");
|
||||
}
|
||||
/****************************************************************************************/
|
||||
|
||||
|
||||
#splash-screen {
|
||||
|
||||
@@ -12,11 +12,17 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#unit-control-panel h3 {
|
||||
margin-bottom:8px;
|
||||
}
|
||||
|
||||
#unit-control-panel #selected-units-container {
|
||||
align-items: left;
|
||||
border-radius: var( --border-radius-md );
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
max-height: 136px;
|
||||
overflow-y:auto;
|
||||
row-gap: 4px;
|
||||
}
|
||||
|
||||
@@ -85,5 +91,10 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
|
||||
#unit-control-panel h4 {
|
||||
margin-bottom:8px;
|
||||
margin-top:20px;
|
||||
}
|
||||
|
||||
|
||||
#unit-control-panel #threat,
|
||||
#unit-control-panel #roe {
|
||||
margin-top:12px;
|
||||
}
|
||||
@@ -58,7 +58,6 @@
|
||||
display:flex;
|
||||
flex-flow: column nowrap;
|
||||
row-gap: 8px;
|
||||
text-align: center;
|
||||
width:45%;
|
||||
}
|
||||
|
||||
@@ -67,23 +66,23 @@
|
||||
align-items: center;
|
||||
column-gap: 8px;
|
||||
display:flex;
|
||||
justify-content: flex-end;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#loadout-items > *::before {
|
||||
align-items: center;
|
||||
background-color: var( --secondary-light-grey );
|
||||
border-radius: 50%;
|
||||
border-radius: var( --border-radius-sm );
|
||||
content: attr( data-qty );
|
||||
display:flex;
|
||||
font-weight: var( --font-weight-bolder );
|
||||
justify-content: center;
|
||||
height:20px;
|
||||
width:20px;
|
||||
padding:1px 4px;
|
||||
}
|
||||
|
||||
#loadout-items > *::after {
|
||||
content: attr( data-item );
|
||||
width:52px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,36 +8,61 @@ export class Dropdown {
|
||||
|
||||
constructor(ID: string, callback: CallableFunction, options: string[] | null = null)
|
||||
{
|
||||
this.#element = <HTMLElement>document.getElementById(ID);
|
||||
this.#options = <HTMLElement>this.#element.querySelector(".ol-select-options");
|
||||
this.#value = <HTMLElement>this.#element.querySelector(".ol-select-value");
|
||||
this.#element = <HTMLElement>document.getElementById(ID);
|
||||
this.#options = <HTMLElement>this.#element.querySelector(".ol-select-options");
|
||||
this.#value = <HTMLElement>this.#element.querySelector(".ol-select-value");
|
||||
this.#defaultValue = this.#value.innerText;
|
||||
this.#callback = callback;
|
||||
this.#callback = callback;
|
||||
|
||||
if (options != null) {
|
||||
this.setOptions(options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Do open/close toggle
|
||||
this.#element.addEventListener("click", ev => {
|
||||
|
||||
if ( ev.target instanceof HTMLElement && ev.target.nodeName !== "A" ) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
this.#value.addEventListener( "click", ev => {
|
||||
|
||||
ev.stopPropagation();
|
||||
this.#element.classList.toggle("is-open");
|
||||
this.#element.classList.toggle( "is-open" );
|
||||
this.#clip();
|
||||
|
||||
});
|
||||
|
||||
// Autoclose on mouseleave
|
||||
this.#element.addEventListener("mouseleave", ev => {
|
||||
this.#element.classList.remove("is-open");
|
||||
this.#close();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
#clip() {
|
||||
|
||||
const options = this.#options;
|
||||
const bounds = options.getBoundingClientRect();
|
||||
|
||||
this.#element.dataset.position = ( bounds.bottom > window.innerHeight ) ? "top" : "";
|
||||
|
||||
}
|
||||
|
||||
|
||||
#close() {
|
||||
this.#element.classList.remove( "is-open" );
|
||||
this.#element.dataset.position = "";
|
||||
}
|
||||
|
||||
|
||||
#open() {
|
||||
this.#element.classList.add( "is-open" );
|
||||
}
|
||||
|
||||
|
||||
#toggle() {
|
||||
|
||||
if ( this.#element.classList.contains( "is-open" ) ) {
|
||||
this.#close();
|
||||
} else {
|
||||
this.#open();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
setOptions(optionsList: string[])
|
||||
{
|
||||
this.#optionsList = optionsList;
|
||||
@@ -47,7 +72,9 @@ export class Dropdown {
|
||||
button.textContent = option;
|
||||
div.appendChild(button);
|
||||
button.addEventListener("click", (e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
this.#value.innerText = option;
|
||||
this.#close();
|
||||
this.#callback( option, e );
|
||||
});
|
||||
return div;
|
||||
|
||||
@@ -168,7 +168,7 @@ export class MapContextMenu extends ContextMenu {
|
||||
/********* Ground unit spawn menu *********/
|
||||
#setGroundUnitRole(role: string) {
|
||||
this.#spawnOptions.role = role;
|
||||
this.#resetGroundUnitRole();
|
||||
this.#resetGroundUnitType();
|
||||
this.#groundUnitTypeDropdown.setOptions(groundUnitsDatabase.getByRole(role).map((blueprint) => { return blueprint.label }));
|
||||
this.#groundUnitTypeDropdown.selectValue(0);
|
||||
this.clip();
|
||||
|
||||
@@ -75,7 +75,7 @@ export class Slider {
|
||||
{
|
||||
this.#value = newValue;
|
||||
if (this.#slider != null)
|
||||
this.#slider.value = String((newValue - this.#minValue) / (this.#maxValue - this.#minValue) * 100);
|
||||
this.#slider.value = String((newValue - this.#minValue) / (this.#maxValue - this.#minValue) * parseFloat(this.#slider.max));
|
||||
this.#onValue()
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ export class Slider {
|
||||
this.#dragged = false;
|
||||
if (this.#slider != null)
|
||||
{
|
||||
this.#value = this.#minValue + parseFloat(this.#slider.value) / 100 * (this.#maxValue - this.#minValue);
|
||||
this.#value = this.#minValue + parseFloat(this.#slider.value) / parseFloat(this.#slider.max) * (this.#maxValue - this.#minValue);
|
||||
this.#callback(this.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,9 @@ import { AIC } from "./aic/aic";
|
||||
import { ATC } from "./atc/atc";
|
||||
import { FeatureSwitches } from "./featureswitches";
|
||||
import { LogPanel } from "./panels/logpanel";
|
||||
import { getAirbases, getBullseye as getBullseyes, getMission, getUnits, toggleDemoEnabled } from "./server/server";
|
||||
import { getAirbases, getBullseye as getBullseyes, getConfig, getMission, getUnits, setAddress, toggleDemoEnabled } from "./server/server";
|
||||
import { UnitDataTable } from "./units/unitdatatable";
|
||||
import { keyEventWasInInput } from "./other/utils";
|
||||
|
||||
var map: Map;
|
||||
|
||||
@@ -69,16 +70,33 @@ function setup() {
|
||||
/* Setup event handlers */
|
||||
setupEvents();
|
||||
|
||||
/* On the first connection, force request of full data */
|
||||
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
|
||||
getBullseyes((data: BullseyesData) => getMissionData()?.update(data));
|
||||
getMission((data: any) => {getMissionData()?.update(data)});
|
||||
getUnits((data: UnitsData) => getUnitsManager()?.update(data), true /* Does a full refresh */);
|
||||
|
||||
/* Start periodically requesting updates */
|
||||
startPeriodicUpdate();
|
||||
getConfig(readConfig)
|
||||
}
|
||||
|
||||
function readConfig(config: any)
|
||||
{
|
||||
if (config && config["server"] != undefined && config["server"]["address"] != undefined && config["server"]["port"] != undefined)
|
||||
{
|
||||
const address = config["server"]["address"];
|
||||
const port = config["server"]["port"];
|
||||
if ((typeof address === 'string' || address instanceof String) && typeof port == 'number')
|
||||
{
|
||||
setAddress(window.location.hostname, <number>port);
|
||||
}
|
||||
|
||||
/* On the first connection, force request of full data */
|
||||
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
|
||||
getBullseyes((data: BullseyesData) => getMissionData()?.update(data));
|
||||
getMission((data: any) => {getMissionData()?.update(data)});
|
||||
getUnits((data: UnitsData) => getUnitsManager()?.update(data), true /* Does a full refresh */);
|
||||
|
||||
/* Start periodically requesting updates */
|
||||
startPeriodicUpdate();
|
||||
}
|
||||
else {
|
||||
throw new Error('Could not read configuration file!');
|
||||
}
|
||||
}
|
||||
|
||||
function startPeriodicUpdate() {
|
||||
requestUpdate();
|
||||
@@ -124,14 +142,16 @@ function checkSessionHash(newSessionHash: string) {
|
||||
function setupEvents() {
|
||||
/* Generic clicks */
|
||||
document.addEventListener("click", (ev) => {
|
||||
|
||||
if (ev instanceof PointerEvent && ev.target instanceof HTMLElement) {
|
||||
if (ev instanceof MouseEvent && ev.target instanceof HTMLElement) {
|
||||
|
||||
const target = ev.target;
|
||||
|
||||
if (target.classList.contains("olympus-dialog-close")) {
|
||||
target.closest("div.olympus-dialog")?.classList.add("hide");
|
||||
}
|
||||
|
||||
const triggerElement = target.closest("[data-on-click]");
|
||||
|
||||
if (triggerElement instanceof HTMLElement) {
|
||||
const eventName: string = triggerElement.dataset.onClick || "";
|
||||
let params = JSON.parse(triggerElement.dataset.onClickParams || "{}");
|
||||
@@ -148,23 +168,22 @@ function setupEvents() {
|
||||
|
||||
/* Keyup events */
|
||||
document.addEventListener("keyup", ev => {
|
||||
|
||||
if ( keyEventWasInInput( ev ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ev.code) {
|
||||
|
||||
case "KeyL":
|
||||
document.body.toggleAttribute("data-hide-labels");
|
||||
break;
|
||||
|
||||
case "KeyD":
|
||||
toggleDemoEnabled();
|
||||
break;
|
||||
|
||||
case "Minus": // For Veltro's italian layout keyboard, which lacks a quote
|
||||
case "Quote":
|
||||
unitDataTable.toggle();
|
||||
break
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
@@ -26,6 +26,27 @@ export function ConvertDDToDMS(D: number, lng: boolean) {
|
||||
}
|
||||
|
||||
|
||||
export function dataPointMap( container:HTMLElement, data:any) {
|
||||
|
||||
Object.keys( data ).forEach( ( key ) => {
|
||||
|
||||
const val = "" + data[ key ]; // Ensure a string
|
||||
|
||||
container.querySelectorAll( `[data-point="${key}"]`).forEach( el => {
|
||||
|
||||
// We could probably have options here
|
||||
if ( el instanceof HTMLInputElement ) {
|
||||
el.value = val;
|
||||
} else if ( el instanceof HTMLElement ) {
|
||||
el.innerText = val;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
export function deg2rad(deg: number) {
|
||||
var pi = Math.PI;
|
||||
return deg * (pi / 180);
|
||||
@@ -48,6 +69,15 @@ export function distance(lat1: number, lon1: number, lat2: number, lon2: number)
|
||||
}
|
||||
|
||||
|
||||
export function keyEventWasInInput( event:KeyboardEvent ) {
|
||||
|
||||
const target = event.target;
|
||||
|
||||
return ( target instanceof HTMLElement && ( [ "INPUT", "TEXTAREA" ].includes( target.nodeName ) ) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
export function rad2deg(rad: number) {
|
||||
var pi = Math.PI;
|
||||
return rad / (pi / 180);
|
||||
|
||||
@@ -36,10 +36,17 @@ export class MouseInfoPanel extends Panel {
|
||||
var el = <HTMLElement>this.getElement().querySelector(`#bullseye-${idx}`);
|
||||
|
||||
if ( el != null ) {
|
||||
|
||||
var dist = distance(bullseyes[idx].latitude, bullseyes[idx].longitude, mousePosition.lat, mousePosition.lng);
|
||||
var bear = bearing(bullseyes[idx].latitude, bullseyes[idx].longitude, mousePosition.lat, mousePosition.lng);
|
||||
|
||||
el.dataset.bearing = zeroAppend(Math.floor(bear), 3);
|
||||
let bng = zeroAppend(Math.floor(bear), 3);
|
||||
|
||||
if ( bng === "000" ) {
|
||||
bng = "360";
|
||||
}
|
||||
|
||||
el.dataset.bearing = bng;
|
||||
el.dataset.distance = zeroAppend(Math.floor(dist*0.000539957), 3);
|
||||
el.dataset.distanceUnits = "NM";
|
||||
}
|
||||
|
||||
@@ -11,9 +11,13 @@ export class Panel {
|
||||
this.#visible = true;
|
||||
}
|
||||
|
||||
protected onHide() {}
|
||||
|
||||
hide() {
|
||||
this.#element.classList.toggle("hide", true);
|
||||
this.#visible = false;
|
||||
|
||||
this.onHide();
|
||||
}
|
||||
|
||||
toggle() {
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { getUnitsManager } from "..";
|
||||
import { Slider } from "../controls/slider";
|
||||
import { dataPointMap } from "../other/utils";
|
||||
import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
|
||||
import { Aircraft, GroundUnit, Unit } from "../units/unit";
|
||||
import { UnitDatabase } from "../units/unitdatabase";
|
||||
import { UnitsManager } from "../units/unitsmanager";
|
||||
import { Panel } from "./panel";
|
||||
|
||||
const ROEs: string[] = ["Free", "Designated free", "Designated", "Return", "Hold"];
|
||||
const reactionsToThreat: string[] = ["None", "Passive", "Evade", "Escape", "Abort"];
|
||||
// const ROEs: string[] = ["Free", "Designated free", "Designated", "Return", "Hold"]; // Full list
|
||||
// const reactionsToThreat: string[] = ["None", "Passive", "Evade", "Escape", "Abort"]; // Full list
|
||||
|
||||
const ROEs: string[] = [ "Hold", "Return", "Designated", "Free" ];
|
||||
const reactionsToThreat: string[] = [ "None", "Passive", "Evade" ];
|
||||
|
||||
const minSpeedValues: { [key: string]: number } = { Aircraft: 100, Helicopter: 0, NavyUnit: 0, GroundUnit: 0 };
|
||||
const maxSpeedValues: { [key: string]: number } = { Aircraft: 800, Helicopter: 300, NavyUnit: 60, GroundUnit: 60 };
|
||||
const speedIncrements: { [key: string]: number } = { Aircraft: 25, Helicopter: 10, NavyUnit: 5, GroundUnit: 5 };
|
||||
@@ -19,14 +23,23 @@ const altitudeIncrements: { [key: string]: number } = { Aircraft: 2500, Helicopt
|
||||
export class UnitControlPanel extends Panel {
|
||||
#altitudeSlider: Slider;
|
||||
#airspeedSlider: Slider;
|
||||
#expectedAltitude:number = -1;
|
||||
#expectedSpeed: number = -1;
|
||||
#optionButtons: { [key: string]: HTMLButtonElement[] } = {}
|
||||
|
||||
constructor(ID: string) {
|
||||
super(ID);
|
||||
|
||||
/* Unit control sliders */
|
||||
this.#altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => getUnitsManager().selectedUnitsSetAltitude(value * 0.3048));
|
||||
this.#airspeedSlider = new Slider("airspeed-slider", 0, 100, "kts", (value: number) => getUnitsManager().selectedUnitsSetSpeed(value / 1.94384));
|
||||
this.#altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => {
|
||||
this.#expectedAltitude = value;
|
||||
getUnitsManager().selectedUnitsSetAltitude(value * 0.3048)
|
||||
});
|
||||
|
||||
this.#airspeedSlider = new Slider("airspeed-slider", 0, 100, "kts", (value: number) => {
|
||||
this.#expectedSpeed = value;
|
||||
getUnitsManager().selectedUnitsSetSpeed(value / 1.94384)
|
||||
});
|
||||
|
||||
/* Option buttons */
|
||||
this.#optionButtons["ROE"] = ROEs.map((option: string, index: number) => {
|
||||
@@ -55,6 +68,39 @@ export class UnitControlPanel extends Panel {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
|
||||
// Do this after panel is hidden (make sure there's a reset)
|
||||
protected onHide() {
|
||||
this.#expectedAltitude = -1;
|
||||
this.#expectedSpeed = -1;
|
||||
}
|
||||
|
||||
|
||||
// Update function will only be allowed to update the sliders once it's matched the expected value for the first time (due to lag of Ajax request)
|
||||
#updateCanSetAltitudeSlider( altitude:number ) {
|
||||
|
||||
if ( this.#expectedAltitude < 0 || altitude === this.#expectedAltitude ) {
|
||||
this.#expectedAltitude = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#updateCanSetSpeedSlider( altitude:number ) {
|
||||
|
||||
if ( this.#expectedSpeed < 0 || altitude === this.#expectedSpeed ) {
|
||||
this.#expectedSpeed = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
update() {
|
||||
var units = getUnitsManager().getSelectedUnits();
|
||||
if (this.getElement() != null && units.length > 0) {
|
||||
@@ -69,23 +115,13 @@ export class UnitControlPanel extends Panel {
|
||||
else
|
||||
database = null; // TODO add databases for other unit types
|
||||
|
||||
if (index === 0) {
|
||||
this.getElement().querySelectorAll(`[data-object|="unit"]`).forEach(marker => {
|
||||
marker.setAttribute("data-coalition", unit.getMissionData().coalition);
|
||||
const shortLabel = <HTMLElement>marker.querySelector(".unit-short-label");
|
||||
if (shortLabel)
|
||||
shortLabel.innerText = database?.getByName(unit.getBaseData().name)?.shortLabel || "";
|
||||
});
|
||||
}
|
||||
console.log( unit.getBaseData() );
|
||||
|
||||
var button = document.createElement("button");
|
||||
const unitName = <HTMLInputElement>this.getElement().querySelector("#unit-name");
|
||||
var callsign = aircraftDatabase.getByName(unit.getBaseData().unitName)?.label || "";
|
||||
var callsign = unit.getBaseData().unitName || "";
|
||||
|
||||
button.innerText = "";
|
||||
button.setAttribute("data-short-label", database?.getByName(unit.getBaseData().name)?.shortLabel || "");
|
||||
button.setAttribute("data-callsign", callsign);
|
||||
unitName.value = callsign;
|
||||
|
||||
button.setAttribute("data-coalition", unit.getMissionData().coalition);
|
||||
button.classList.add("pill", "highlight-coalition")
|
||||
@@ -104,6 +140,7 @@ export class UnitControlPanel extends Panel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#showFlightControlSliders(units: Unit[])
|
||||
{
|
||||
if (getUnitsManager().getSelectedUnitsType() !== undefined)
|
||||
@@ -132,12 +169,24 @@ export class UnitControlPanel extends Panel {
|
||||
this.#altitudeSlider.setIncrement(altitudeIncrements[unitsType]);
|
||||
|
||||
this.#airspeedSlider.setActive(targetSpeed != undefined);
|
||||
if (targetSpeed != undefined)
|
||||
this.#airspeedSlider.setValue(targetSpeed * 1.94384);
|
||||
if (targetSpeed != undefined) {
|
||||
|
||||
targetSpeed *= 1.94384;
|
||||
|
||||
if ( this.#updateCanSetSpeedSlider( targetSpeed ) ) {
|
||||
this.#airspeedSlider.setValue( targetSpeed );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.#altitudeSlider.setActive(targetAltitude != undefined);
|
||||
if (targetAltitude != undefined)
|
||||
this.#altitudeSlider.setValue(targetAltitude / 0.3048);
|
||||
if (targetAltitude != undefined) {
|
||||
targetAltitude /= 0.3048;
|
||||
|
||||
if ( this.#updateCanSetAltitudeSlider( targetAltitude ) ) {
|
||||
this.#altitudeSlider.setValue( targetAltitude );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.#airspeedSlider.setActive(false);
|
||||
|
||||
@@ -2,9 +2,8 @@ import * as L from 'leaflet'
|
||||
import { setConnected } from '..';
|
||||
import { SpawnOptions } from '../controls/mapcontextmenu';
|
||||
|
||||
/* Edit here to change server address */
|
||||
const REST_ADDRESS = "http://localhost:30000/olympus";
|
||||
const DEMO_ADDRESS = "http://localhost:3000/demo";
|
||||
var REST_ADDRESS = "http://localhost:30000/olympus";
|
||||
var DEMO_ADDRESS = window.location.href + "demo";
|
||||
const UNITS_URI = "units";
|
||||
const LOGS_URI = "logs";
|
||||
const AIRBASES_URI = "airbases";
|
||||
@@ -24,9 +23,14 @@ export function GET(callback: CallableFunction, uri: string){
|
||||
xmlHttp.open("GET", `${demoEnabled? DEMO_ADDRESS: REST_ADDRESS}/${uri}`, true);
|
||||
xmlHttp.onload = function (e) {
|
||||
var data = JSON.parse(xmlHttp.responseText);
|
||||
callback(data);
|
||||
lastUpdateTime = parseInt(data.time);
|
||||
setConnected(true);
|
||||
if (parseInt(data.time) > lastUpdateTime)
|
||||
{
|
||||
callback(data);
|
||||
lastUpdateTime = parseInt(data.time);
|
||||
if (isNaN(lastUpdateTime))
|
||||
lastUpdateTime = 0;
|
||||
setConnected(true);
|
||||
}
|
||||
};
|
||||
xmlHttp.onerror = function () {
|
||||
console.error("An error occurred during the XMLHttpRequest");
|
||||
@@ -45,6 +49,24 @@ export function POST(request: object, callback: CallableFunction){
|
||||
xhr.send(JSON.stringify(request));
|
||||
}
|
||||
|
||||
export function getConfig(callback: CallableFunction) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("GET", window.location.href + "config", true);
|
||||
xmlHttp.onload = function (e) {
|
||||
var data = JSON.parse(xmlHttp.responseText);
|
||||
callback(data);
|
||||
};
|
||||
xmlHttp.onerror = function () {
|
||||
console.error("An error occurred during the XMLHttpRequest, could not retrieve configuration file");
|
||||
};
|
||||
xmlHttp.send(null);
|
||||
}
|
||||
|
||||
export function setAddress(address: string, port: number) {
|
||||
REST_ADDRESS = `http://${address}:${port}/olympus`
|
||||
console.log(`Setting REST address to ${REST_ADDRESS}`)
|
||||
}
|
||||
|
||||
export function getAirbases(callback: CallableFunction) {
|
||||
GET(callback, AIRBASES_URI);
|
||||
}
|
||||
|
||||
@@ -573,6 +573,15 @@ export class GroundUnit extends Unit {
|
||||
super(ID, data);
|
||||
}
|
||||
|
||||
getMarkerHTML() {
|
||||
var role = groundUnitsDatabase.getByName(this.getBaseData().name)?.loadouts[0].roles[0];
|
||||
return `<div class="unit" data-object="unit-${this.getMarkerCategory()}" data-coalition="${this.getMissionData().coalition}">
|
||||
<div class="unit-selected-spotlight"></div>
|
||||
<div class="unit-marker"></div>
|
||||
<div class="unit-short-label">${role?.substring(0, 1)?.toUpperCase() || ""}</div>
|
||||
</div>`
|
||||
}
|
||||
|
||||
getMarkerCategory()
|
||||
{
|
||||
// TODO this is very messy
|
||||
|
||||
@@ -3,11 +3,13 @@ import { getMap, getUnitDataTable } from "..";
|
||||
import { Unit } from "./unit";
|
||||
import { cloneUnit } from "../server/server";
|
||||
import { IDLE, MOVE_UNIT } from "../map/map";
|
||||
import { keyEventWasInInput } from "../other/utils";
|
||||
|
||||
export class UnitsManager {
|
||||
#units: { [ID: number]: Unit };
|
||||
#copiedUnits: Unit[];
|
||||
#selectionEventDisabled: boolean = false;
|
||||
#pasteDisabled: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this.#units = {};
|
||||
@@ -330,16 +332,21 @@ export class UnitsManager {
|
||||
|
||||
pasteUnits()
|
||||
{
|
||||
for (let idx in this.#copiedUnits)
|
||||
if (!this.#pasteDisabled)
|
||||
{
|
||||
var unit = this.#copiedUnits[idx];
|
||||
cloneUnit(unit.ID, getMap().getMouseCoordinates());
|
||||
for (let idx in this.#copiedUnits)
|
||||
{
|
||||
var unit = this.#copiedUnits[idx];
|
||||
cloneUnit(unit.ID, getMap().getMouseCoordinates());
|
||||
}
|
||||
this.#pasteDisabled = true;
|
||||
setTimeout(() => this.#pasteDisabled = false, 250);
|
||||
}
|
||||
}
|
||||
|
||||
#onKeyDown(event: KeyboardEvent)
|
||||
{
|
||||
if (event.key === "Delete")
|
||||
if ( !keyEventWasInInput( event ) && event.key === "Delete")
|
||||
{
|
||||
this.selectedUnitsDelete();
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;600;700;800&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z4L2TC3YX0"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'G-Z4L2TC3YX0');
|
||||
</script>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z4L2TC3YX0"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'G-Z4L2TC3YX0');
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="ol-select-options">
|
||||
<div id="olympus-toolbar-summary">
|
||||
<h3>Olympus</h3>
|
||||
<div class="accent-green app-version-number">v0.1.0</div>
|
||||
<div class="accent-green app-version-number">v0.1.1</div>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://www.discord.com" target="_blank">Discord</a>
|
||||
|
||||
@@ -982,6 +982,42 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="example">
|
||||
|
||||
<div class="ol-select">
|
||||
<div class="ol-select-value">
|
||||
The selected value goes here
|
||||
</div>
|
||||
<div class="ol-select-options">
|
||||
<div>
|
||||
<button>Option 1</button>
|
||||
</div>
|
||||
<div>
|
||||
<button>Option 2</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="example">
|
||||
|
||||
<div class="ol-select" data-position="top">
|
||||
<div class="ol-select-value">
|
||||
Options go up
|
||||
</div>
|
||||
<div class="ol-select-options">
|
||||
<div>
|
||||
<button>Option 1</button>
|
||||
</div>
|
||||
<div>
|
||||
<button>Option 2</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -991,7 +1027,7 @@
|
||||
<div class="content-body">
|
||||
<div class="example">
|
||||
|
||||
<div id="airbase-contextmenu" class="ol-panel">
|
||||
<div id="airbase-contextmenu" class="ol-panel" style="position:relative;">
|
||||
<h3 id="airbase-name">Al Alhambra</h3>
|
||||
<dl id="airbase-properties" class="ol-data-grid">
|
||||
<dt>Runway 1</dt>
|
||||
@@ -1048,6 +1084,46 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div id="fuel-percentage" data-percentage="45"></div>
|
||||
<div id="fuel-display">
|
||||
<div id="fuel-bar" class="highlight-coalition" data-coalition="blue" style="width:0%;"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="example">
|
||||
|
||||
<div id="unit-info-panel" class="ol-panel" style="position:relative;">
|
||||
|
||||
<div class="ol-panel-board">
|
||||
|
||||
<div id="general" class="panel-section">
|
||||
<h3 class="unit-name">Olympus 1-1</h3>
|
||||
<div class="ol-group">
|
||||
<div class="unit-label">Name</div>
|
||||
<div class="unit-control">AI Controlled</div>
|
||||
</div>
|
||||
<div id="current-task" class="pill highlight-coalition" data-coalition="blue" data-current-task="Awaiting tasking"></div>
|
||||
</div>
|
||||
|
||||
<div id="loadout-container" class="panel-section">
|
||||
|
||||
<div id="loadout">
|
||||
<div id="loadout-silhouette" style="--loadout-background-image:url('/images/units/f-15.png');"></div>
|
||||
<div id="loadout-items">
|
||||
<div data-qty="1150" data-item="30mm AP"></div>
|
||||
<div data-qty="2" data-item="AIM-9M"></div>
|
||||
<div data-qty="6" data-item="Mk-82"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="fuel-percentage" data-percentage="45"></div>
|
||||
<div id="fuel-display">
|
||||
<div id="fuel-bar" class="highlight-coalition" data-coalition="blue" style="width:0%;"></div>
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
<div id="unit-control-panel" class="ol-panel ol-panel-padding-lg">
|
||||
|
||||
<h3>Selected Units</h3>
|
||||
|
||||
<div id="unit-selection">
|
||||
|
||||
<div id="unit-identification">
|
||||
<div data-object="unit-aircraft">
|
||||
<div class="unit-marker"></div>
|
||||
<div class="unit-short-label"></div>
|
||||
</div>
|
||||
|
||||
<input id="unit-name" value="" readonly disabled />
|
||||
|
||||
<!-- <button id="edit-unit-name" data-on-click="editUnitName"></button> -->
|
||||
</div>
|
||||
|
||||
<div id="selected-units-container" class="ol-scroll">
|
||||
<div id="selected-units-container" class="ol-scrollable">
|
||||
<!-- This is where all the unit selection buttons will be shown-->
|
||||
<!-- <button class="pill highlight-coalition" data-coalition="blue" data-short-label="18">Olympus 1-1</button> -->
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div id="flight-data">
|
||||
<h4>Flight controls</h4>
|
||||
<div class="slider-container flight-control-slider" id="airspeed-slider">
|
||||
@@ -52,9 +45,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Reaction to threat</h4>
|
||||
<div id="reaction-to-threat-buttons-container" class="ol-group ol-button-box">
|
||||
<!-- This is where the reaction to threat buttons will be shown -->
|
||||
<div id="threat">
|
||||
<h4>Reaction to threat</h4>
|
||||
<div id="reaction-to-threat-buttons-container" class="ol-group ol-button-box">
|
||||
<!-- This is where the reaction to threat buttons will be shown -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
Reference in New Issue
Block a user