diff --git a/client/package-lock.json b/client/package-lock.json index 05df666f..e6e672a7 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@types/geojson": "^7946.0.10", "@types/leaflet": "^1.9.0", + "@types/svg-injector": "^0.0.29", "cookie-parser": "~1.4.4", "debug": "~2.6.9", "ejs": "^3.1.8", @@ -1865,6 +1866,11 @@ "integrity": "sha512-qrhtM7M41EhH4tZQTNw2/RJkxllBx3reiJpTbgWCM2Dx0U1sZ6LwKp9lfNln9uqE26ZMKUaPEYaD4rzvOWYtZw==", "dev": true }, + "node_modules/@types/svg-injector": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/svg-injector/-/svg-injector-0.0.29.tgz", + "integrity": "sha512-tNvoN0Xk2si6IfxQI/PqInipOuCyXkDZhCh9Vc4aFv/l1DhIBfTFbXRXYUHBAGXaUNMOCHEtg9475O9J+4NXOg==" + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -6989,6 +6995,11 @@ "integrity": "sha512-qrhtM7M41EhH4tZQTNw2/RJkxllBx3reiJpTbgWCM2Dx0U1sZ6LwKp9lfNln9uqE26ZMKUaPEYaD4rzvOWYtZw==", "dev": true }, + "@types/svg-injector": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/svg-injector/-/svg-injector-0.0.29.tgz", + "integrity": "sha512-tNvoN0Xk2si6IfxQI/PqInipOuCyXkDZhCh9Vc4aFv/l1DhIBfTFbXRXYUHBAGXaUNMOCHEtg9475O9J+4NXOg==" + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", diff --git a/client/package.json b/client/package.json index 8fa54c31..f0db50cb 100644 --- a/client/package.json +++ b/client/package.json @@ -12,6 +12,7 @@ "dependencies": { "@types/geojson": "^7946.0.10", "@types/leaflet": "^1.9.0", + "@types/svg-injector": "^0.0.29", "cookie-parser": "~1.4.4", "debug": "~2.6.9", "ejs": "^3.1.8", diff --git a/client/public/stylesheets/atc/atc.css b/client/public/stylesheets/atc/atc.css index c19e19fd..e9fa7441 100644 --- a/client/public/stylesheets/atc/atc.css +++ b/client/public/stylesheets/atc/atc.css @@ -1,4 +1,8 @@ - +.ol-strip-board .ol-dialog-header { + align-items: center; + display:flex; + justify-content: space-between; +} .ol-strip-board-strips { display:flex; @@ -178,6 +182,7 @@ .ol-strip-board-add-flight .ol-auto-suggest[data-has-suggestions] { display:flex; + row-gap: 4px; } .ol-strip-board-add-flight .ol-auto-suggest[data-has-suggestions] a { @@ -192,4 +197,9 @@ [data-board-type="tower"] { right:10px; top:10px; +} + +[data-board-type="tower"] .ol-auto-suggest { + top:30px; + translate:0; } \ No newline at end of file diff --git a/client/src/atc/atc.ts b/client/src/atc/atc.ts index e85c1ee2..1597ca7e 100644 --- a/client/src/atc/atc.ts +++ b/client/src/atc/atc.ts @@ -1,3 +1,5 @@ +import { getMissionData } from ".."; +import { getConnected } from "../server/server"; import { ATCBoard } from "./atcboard"; import { ATCBoardGround } from "./board/ground"; import { ATCBoardTower } from "./board/tower"; @@ -48,6 +50,10 @@ class ATCDataHandler { this.#updateInterval = window.setInterval( () => { + if ( !getConnected() ) { + return; + } + const aBoardIsVisible = this.#atc.getBoards().some( board => board.boardIsVisible() ); if ( aBoardIsVisible ) { @@ -135,7 +141,7 @@ export class ATC { getMissionDateTime() : Date { - return new Date( this.getMissionStartDateTime().getTime() + this.getMissionElapsedSeconds() ); + return new Date( getMissionData().getNowDate() ); } diff --git a/client/src/atc/atcboard.ts b/client/src/atc/atcboard.ts index 79586def..959874ab 100644 --- a/client/src/atc/atcboard.ts +++ b/client/src/atc/atcboard.ts @@ -2,9 +2,10 @@ import { Dropdown } from "../controls/dropdown"; import { zeroAppend } from "../other/utils"; import { ATC } from "./atc"; import { Unit } from "../units/unit"; -import { getUnitsManager } from ".."; +import { getMissionData, getUnitsManager } from ".."; import Sortable from "sortablejs"; import { FlightInterface } from "./atc"; +import { getConnected } from "../server/server"; export interface StripBoardStripInterface { "id": string, @@ -84,6 +85,10 @@ export abstract class ATCBoard { window.setInterval( () => { + + if ( !getConnected() ) { + return; + } this.updateClock(); }, 1000 ); @@ -410,6 +415,10 @@ export abstract class ATCBoard { this.#updateInterval = window.setInterval( () => { + if ( !getConnected() ) { + return; + } + this.update(); }, this.#updateIntervalDelay ); @@ -446,8 +455,12 @@ export abstract class ATCBoard { updateClock() { - const now = this.#atc.getMissionDateTime(); - this.#clockElement.innerText = now.toLocaleTimeString(); + const missionTime = this.#atc.getMissionDateTime().getTime(); + const timeDiff = new Date().getTime() - getMissionData().getUpdateTime(); + + const nowDate = new Date( missionTime + timeDiff ); + + this.#clockElement.innerText = nowDate.toLocaleTimeString(); } diff --git a/client/src/features/featureswitches.ts b/client/src/features/featureswitches.ts index ee237b01..a7cf1c30 100644 --- a/client/src/features/featureswitches.ts +++ b/client/src/features/featureswitches.ts @@ -1,7 +1,7 @@ export interface FeatureSwitchInterface { - "defaultEnabled": boolean, // default on/off state (if allowed by masterSwitch) + "defaultEnabled": boolean, // default on/off state (if allowed by forceState) + "forceState": number, // -1 don't force; 0 force off; 1 force on "label": string, - "masterSwitch": boolean, // on/off regardless of user preference "name": string, "onEnabled"?: CallableFunction, "options"?: object, @@ -13,8 +13,8 @@ class FeatureSwitch { // From config param defaultEnabled; + forceState = -1; label; - masterSwitch; name; onEnabled; removeArtifactsIfDisabled = true; @@ -26,10 +26,10 @@ class FeatureSwitch { constructor(config: FeatureSwitchInterface) { this.defaultEnabled = config.defaultEnabled; - this.label = config.label; - this.masterSwitch = config.masterSwitch; - this.name = config.name; - this.onEnabled = config.onEnabled; + this.forceState = config.forceState; + this.label = config.label; + this.name = config.name; + this.onEnabled = config.onEnabled; this.userPreference = this.getUserPreference(); @@ -47,10 +47,14 @@ class FeatureSwitch { isEnabled() { - if (!this.masterSwitch) { + if ( this.forceState === 0 ) { return false; } + if ( this.forceState === 1 ) { + return true; + } + return this.userPreference; } @@ -62,37 +66,37 @@ export class FeatureSwitches { new FeatureSwitch({ "defaultEnabled": false, + "forceState": -1, "label": "AIC", - "masterSwitch": true, "name": "aic" }), new FeatureSwitch({ "defaultEnabled": false, + "forceState": -1, "label": "AI Formations", - "masterSwitch": true, "name": "ai-formations", "removeArtifactsIfDisabled": false }), new FeatureSwitch({ "defaultEnabled": false, + "forceState": 1, "label": "ATC", - "masterSwitch": true, "name": "atc" }), new FeatureSwitch({ "defaultEnabled": false, + "forceState": -1, "label": "Force show unit control panel", - "masterSwitch": true, "name": "forceShowUnitControlPanel" }), new FeatureSwitch({ "defaultEnabled": true, + "forceState": -1, "label": "Show splash screen", - "masterSwitch": true, "name": "splashScreen" }) diff --git a/client/src/missionhandler/missionhandler.ts b/client/src/missionhandler/missionhandler.ts index 253283c2..53c9f368 100644 --- a/client/src/missionhandler/missionhandler.ts +++ b/client/src/missionhandler/missionhandler.ts @@ -9,8 +9,18 @@ export class MissionHandler #airbases : {[name: string]: Airbase} = {}; #theatre : string = ""; + #airbaseData : { [name: string]: object } = {}; + // Time + #date : any; + #elapsedTime : any; + #startTime : any; + #time : any; + + #updateTime : any; + + constructor() { @@ -77,6 +87,40 @@ export class MissionHandler //this.#airbases[idx].setParkings(["2x big", "5x small"]); } } + + if ("mission" in data) + { + if (data.mission != null && data.mission.theatre != this.#theatre) + { + this.#theatre = data.mission.theatre; + getMap().setTheatre(this.#theatre); + + getInfoPopup().setText("Map set to " + this.#theatre); + } + + if ( "date" in data.mission ) { + this.#date = data.mission.date; + } + + if ( "elapsedTime" in data.mission ) { + this.#elapsedTime = data.mission.elapsedTime; + } + + if ( "startTime" in data.mission ) { + this.#startTime = data.mission.startTime; + } + + if ( "time" in data.mission ) { + this.#time = data.mission.time; + } + + } + + + if ( "time" in data ) { + this.#updateTime = data.time; + } + } getBullseyes() @@ -84,6 +128,41 @@ export class MissionHandler return this.#bullseyes; } + getDate() { + return this.#date; + } + + + getNowDate() { + + const date = this.getDate(); + const time = this.getTime(); + + if ( !date ) { + return new Date(); + } + + let year = date.Year; + let month = date.Month - 1; + + if ( month < 0 ) { + month = 11; + year--; + } + + return new Date( year, month, date.Day, time.h, time.m, time.s ); + } + + + getTime() { + return this.#time; + } + + + getUpdateTime() { + return this.#updateTime; + } + #onAirbaseClick(e: any) { getMap().showAirbaseContextMenu(e, e.sourceTarget); diff --git a/client/views/atc/board.ejs b/client/views/atc/board.ejs index 007b91f2..58d3dfcc 100644 --- a/client/views/atc/board.ejs +++ b/client/views/atc/board.ejs @@ -1,9 +1,10 @@ -