Resolved conflicts

This commit is contained in:
PeekabooSteam
2023-04-09 09:54:21 +01:00
44 changed files with 3106 additions and 478 deletions

View File

@@ -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));

View File

@@ -625,6 +625,7 @@ class DemoDataGenerator {
units(req, res){
var ret = this.demoUnits;
ret.time = Date.now();
res.send(JSON.stringify(ret));
};

View File

@@ -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",

View File

@@ -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",

View File

@@ -5,7 +5,7 @@
position: absolute;
row-gap: 5px;
width: 230px;
z-index: 1000;
z-index: 9999;
}
#aircraft-spawn-menu {

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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());
}
}

View File

@@ -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
}
});
/*

View File

@@ -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);

View File

@@ -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";
}

View File

@@ -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() {

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 />