diff --git a/client/public/stylesheets/aic/aic.css b/client/public/stylesheets/aic/aic.css deleted file mode 100644 index ea972d71..00000000 --- a/client/public/stylesheets/aic/aic.css +++ /dev/null @@ -1,194 +0,0 @@ -/**************************************/ - -.olympus-dialog { - align-self: center; - background:white; - border-radius: 10px; - display: flex; - flex-direction: column; - justify-self: center; - padding:10px; - position:absolute; - width:fit-content; - z-index: 9999; -} - -.olympus-dialog-close { - cursor:pointer; - position:absolute; - right:10px; - top:5px; -} - -.olympus-dialog-header { - font-weight:bold; -} - - -/**************************************/ - - -/***** AIC *****/ - -.aic-panel { - z-index: 9999; -} - -#aic-control-panel { - bottom:30px; - position: absolute; - left:30px; -} - - -#aic-control-panel .olympus-button img { - max-width: 32px; -} - - -#aic-toolbox, #aic-callsign-panel { - align-items: flex-start; - align-self: center; - flex-direction: column; - row-gap: 10px; - display:none; - position:absolute; -} - -.aic-panel { - background:#eaeaea; - border-bottom-right-radius: 10px; - border-top-right-radius: 10px; - justify-self: left; - padding:5px 10px; -} - -.aic-enabled #aic-toolbox, .aic-enabled #aic-callsign-panel { - display:flex; -} - -.aic-enabled #aic-callsign-panel { - align-self: auto; - top: 100px; -} - -.aic-panel h2 { - font-size:90%; - margin:0; - padding:0; - text-align: center; -} - -#aic-callsign-display { - text-align: center; -} - -#aic-formation-list { - display:flex; - flex-direction: column; - justify-content: center; -} - -#aic-formation-list > div { - align-items: center; - cursor: pointer; - display:flex; - flex-direction: column; - justify-content: center; - margin-top:10px; - position:relative; -} - -#aic-formation-list .aic-formation-image img { - border: 1px solid #ccc; - border-radius: 10px; - max-width: 50px; -} - -#aic-formation-list .aic-formation-name { - font-size:90%; -} - -#aic-formation-list .aic-formation-descriptor { - background:white; - border-radius: 10px; - left:100px; - padding:5px; - position:absolute; - width: max-content; -} - -#aic-teleprompt { - background-color: white; - border:2px solid black; - border-radius: 10px; - bottom: 50px; - color: black; - display: none; - justify-content: center; - justify-self: center; - padding: 10px; - position: absolute; - width: fit-content; - z-index: 9999; -} - -.aic-enabled #aic-teleprompt { - display:flex; -} - -#aic-descriptor { - display:flex; - flex-direction: row; -} - -#aic-descriptor .aic-descriptor-section { - display:flex; - flex-direction: column; - margin:0 10px; -} - -#aic-descriptor .aic-descriptor-section-label { - background-color:#eaeaea; - border-top-left-radius: 10px; - border-top-right-radius: 10px; - padding:.25em; - text-align: center; -} - -#aic-descriptor .aic-descriptor-phrase { - border-bottom: 1px solid #ccc; - display:flex; - flex-direction: row; - margin-bottom:5px; - padding-bottom:2px; -} - -#aic-descriptor .aic-descriptor-phrase:last-of-type { - margin-bottom: 0; -} - -#aic-descriptor .aic-descriptor-components .aic-descriptor-component { - margin:0 5px; - text-align: center; -} - -#aic-descriptor .aic-descriptor-component-label { - display:none; -} - -#aic-descriptor .aic-descriptor-component-value:after { - content:","; - margin-right:5px; -} - -#aic-descriptor .aic-descriptor-component:last-of-type .aic-descriptor-component-value:after { - content:"; "; -} - -#aic-descriptor .aic-descriptor-section:last-of-type .aic-descriptor-component:last-of-type .aic-descriptor-component-value:after { - content:"."; -} - - -/**************************************/ \ No newline at end of file diff --git a/client/public/stylesheets/atc/atc.css b/client/public/stylesheets/atc/atc.css deleted file mode 100644 index e9fa7441..00000000 --- a/client/public/stylesheets/atc/atc.css +++ /dev/null @@ -1,205 +0,0 @@ -.ol-strip-board .ol-dialog-header { - align-items: center; - display:flex; - justify-content: space-between; -} - -.ol-strip-board-strips { - display:flex; - flex-direction: column; - row-gap: 4px; -} - -.ol-strip-board-strip { - align-items: center; - border-radius: var( --border-radius-sm ); - column-gap: 4px; - display:flex; - flex-flow: row nowrap; - row-gap:4px; -} - -.ol-strip-board-strip[data-flight-status="checkedin"] { - background-color: #ffffff2A; -} - -.ol-strip-board-strip[data-flight-status="readytotaxi"] { - background-color: #ffff0063; -} - -.ol-strip-board-strip[data-flight-status="clearedtotaxi"] { - background-color: #00ff0030; -} - -.ol-strip-board-strip[data-flight-status="halted"] { - background-color: #FF000040; -} - -.ol-strip-board-strip[data-flight-status="terminated"] { - background-color: black; -} - -.ol-strip-board-headers { - column-gap: 4px; - display:flex; - flex-flow:row nowrap; - text-align: center; -} - -.ol-strip-board-headers > *, .ol-strip-board-strip > [data-point] { - padding: 4px; - text-overflow: ellipsis; - white-space: nowrap; - width:80px; -} - -.ol-strip-board-strip input[type="text"] { - appearance: none; - background-color: transparent; - border:1px solid #ffffff30; - border-radius: var( --border-radius-sm ); - color:white; - font-size:12px; - font-weight:normal; - outline:none; - padding: 4px 0; - text-align: center; - width:100%; -} - -.ol-strip-board-strip[data-time-warning="level-1"] [data-point="timeToGo"] { - border:1px solid #cc0000; -} - -.ol-strip-board-headers :nth-child(1) { - width:12px; -} - -.ol-strip-board-headers :nth-child(2), -.ol-strip-board-strip :nth-child(2), -[data-board-type="ground"] .ol-strip-board-headers :nth-child(3), -[data-board-type="ground"] .ol-strip-board-strip :nth-child(3) { - width:130px; -} - -[data-board-type="ground"] .ol-strip-board-strip :nth-child(5) { - text-align: center; -} - - - -.ol-strip-board-headers :last-child, -.ol-strip-board-strip :last-child { - width:20px; -} - - - - - - -[data-board-type="tower"] .ol-strip-board-strip > * { - text-align: center; -} - -[data-board-type="tower"] .ol-strip-board-strip a { - color:white; -} - -[data-board-type="tower"] .ol-strip-board-strip > :nth-child(2) { - text-align: left; -} - -[data-board-type="tower"] .ol-strip-board-strip :nth-child(3) input, -[data-board-type="tower"] .ol-strip-board-strip :nth-child(5) input { - width:30px; -} - -[data-board-type="tower"] .ol-strip-board-strip :nth-child(3) { - font-size:10px; -} - - -[data-altitude-assigned] [data-point="assignedAltitude"] input, -[data-speed-assigned] [data-point="assignedSpeed"] input { - background-color:#ffffffbb; - color: black; - font-weight: var( --font-weight-bolder ); -} - -[data-warning-altitude] [data-point="altitude"], -[data-warning-speed] [data-point="speed"] { - background:#cc0000; - border-radius: var( --border-radius-sm ); -} - - - -.ol-strip-board-strip > [data-point="name"] { - text-overflow: ellipsis; - overflow:hidden; -} - -.ol-strip-board-strip .ol-select-value { - opacity: .85; -} - - -.ol-strip-board-add-flight { - display:flex; - flex-flow: row nowrap; - position:relative; -} - - -.ol-strip-board-add-flight > * { - border:none; - outline: none; - padding:4px 8px; -} - -.add-flight-by-click img { - filter:invert(); - height: 12px; -} - -.ol-strip-board-add-flight input { - border-radius: var( --border-radius-sm ); -} - -.ol-strip-board-add-flight .ol-auto-suggest { - background:white; - border-radius: var(--border-radius-sm ); - color:black; - display:none; - flex-direction: column; - left:0; - margin:0; - position:absolute; - translate:0 -100%; - top:0; -} - -.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 { - cursor: pointer; -} - - -[data-board-type="ground"] { - bottom:20px; -} - -[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/public/stylesheets/atc/unitdatatable.css b/client/public/stylesheets/atc/unitdatatable.css deleted file mode 100644 index e16e1bdd..00000000 --- a/client/public/stylesheets/atc/unitdatatable.css +++ /dev/null @@ -1,27 +0,0 @@ -#unit-list { - display:flex; - flex-direction: column; - font-size:13px; - height: 250px; - width:fit-content; -} - -#unit-list > div { - display:flex; - flex-direction: row; - flex-wrap: nowrap; -} - -#unit-list > div > div { - text-overflow: ellipsis; - white-space: nowrap; - width:100px; -} - -#unit-list > div:first-of-type { - text-align: center; -} - -#unit-list > div > div:nth-of-type( 4 ) { - text-align: center; -} \ No newline at end of file diff --git a/client/public/stylesheets/olympus.css b/client/public/stylesheets/olympus.css index 0ecc64ee..7e51ec7a 100644 --- a/client/public/stylesheets/olympus.css +++ b/client/public/stylesheets/olympus.css @@ -1,9 +1,6 @@ @import url("layout/layout.css"); @import url("style/style.css"); -@import url("atc/atc.css"); -@import url("atc/unitdatatable.css"); - @import url("panels/connectionstatus.css"); @import url("panels/serverstatus.css"); @import url("panels/mouseinfo.css"); diff --git a/client/public/stylesheets/style/style.css b/client/public/stylesheets/style/style.css index 4ac1eb50..f013aa57 100644 --- a/client/public/stylesheets/style/style.css +++ b/client/public/stylesheets/style/style.css @@ -1,4 +1,3 @@ - * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; @@ -7,7 +6,7 @@ html * { font-family: 'Open Sans', sans-serif !important; - user-select: none; + user-select: none; } body { @@ -117,7 +116,8 @@ form>div { .ol-panel { background-color: var(--background-steel); - border-radius: var(--border-radius-md);; + border-radius: var(--border-radius-md); + ; box-shadow: 0px 2px 5px #000A; color: white; font-size: 12px; @@ -963,16 +963,16 @@ nav.ol-panel> :last-child { #command-mode-phase.game-commenced::after { content: "Spawn restrictions are being enforced"; - font-size: 10px; + font-size: 10px; } #command-mode-phase.no-restrictions::after { content: "No spawn restrictions"; - font-size: 10px; + font-size: 10px; } #command-mode-toolbar { - min-width: fit-content ; + min-width: fit-content; } #command-mode-toolbar .ol-button { @@ -990,17 +990,17 @@ nav.ol-panel> :last-child { } #command-mode-settings-dialog>.ol-dialog-content { - display: flex; - flex-direction: column; - flex-wrap: nowrap; - margin-bottom: 10px; - margin-top: 10px; - row-gap: 10px; + display: flex; + flex-direction: column; + flex-wrap: nowrap; + margin-bottom: 10px; + margin-top: 10px; + row-gap: 10px; width: 100%; } #command-mode-settings-dialog>.ol-dialog-content .ol-group { - justify-content: space-between; + justify-content: space-between; } #command-mode-settings-dialog h4 { @@ -1292,11 +1292,11 @@ input[type=number]::-webkit-outer-spin-button { position: relative; transition: background-color 0.2s; height: var(--height); - width: var(--width); + width: var(--width); } .ol-switch-fill::after { - aspect-ratio : 1 / 1; + aspect-ratio: 1 / 1; background-clip: content-box; background-color: #ffffff; border-radius: 999px; @@ -1310,15 +1310,15 @@ input[type=number]::-webkit-outer-spin-button { } .ol-switch-fill::before { - align-items: center; - box-sizing: border-box; - color: white; - display: flex; - font-size: 11px; - height: 100%; - padding: 0px 7px; - position: absolute; - transition: transform 0.2s; + align-items: center; + box-sizing: border-box; + color: white; + display: flex; + font-size: 11px; + height: 100%; + padding: 0px 7px; + position: absolute; + transition: transform 0.2s; } .ol-switch[data-value="false"]>.ol-switch-fill::before { @@ -1326,11 +1326,11 @@ input[type=number]::-webkit-outer-spin-button { } .ol-switch[data-value="true"]>.ol-switch-fill::after { - transform: translateX(calc(var(--width) - var(--height))); + transform: translateX(calc(var(--width) - var(--height))); } .ol-switch[data-value="undefined"]>.ol-switch-fill::after { - transform: translateX(calc((var(--width) - var(--height)) * 0.5)); + transform: translateX(calc((var(--width) - var(--height)) * 0.5)); } .ol-contexmenu-panel { @@ -1338,15 +1338,15 @@ input[type=number]::-webkit-outer-spin-button { } .ol-coalition-switch[data-value="false"]>.ol-switch-fill { - background-color: var(--primary-blue); + background-color: var(--primary-blue); } .ol-coalition-switch[data-value="true"]>.ol-switch-fill { - background-color: var(--primary-red); + background-color: var(--primary-red); } .ol-coalition-switch[data-value="undefined"]>.ol-switch-fill { - background-color: var(--primary-neutral); + background-color: var(--primary-neutral); } .ol-context-menu>ul { @@ -1412,4 +1412,4 @@ input[type=number]::-webkit-outer-spin-button { .ol-log-entry { border-bottom: 1px solid #FFFFFF44; -} \ No newline at end of file +} diff --git a/client/src/aic/aic.ts b/client/src/aic/aic.ts deleted file mode 100644 index 2aaf8a1c..00000000 --- a/client/src/aic/aic.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { ToggleableFeature } from "../features/toggleablefeature"; -import { AICFormation_Azimuth } from "./aicformation/azimuth"; -import { AICFormation_Range } from "./aicformation/range"; -import { AICFormation_Single } from "./aicformation/single"; -import { AICFormationDescriptorSection } from "./aicformationdescriptorsection"; - - -export class AIC extends ToggleableFeature { - - #formations = [ - - new AICFormation_Single(), - new AICFormation_Range(), - new AICFormation_Azimuth() - - ]; - - - constructor() { - - super( false ); - - this.onStatusUpdate(); - - // This feels kind of dirty - let $aicFormationList = document.getElementById( "aic-formation-list" ); - - if ( $aicFormationList ) { - - this.getFormations().forEach( formation => { - - // Image - let $imageDiv = document.createElement( "div" ); - $imageDiv.classList.add( "aic-formation-image" ); - - let $img = document.createElement( "img" ); - $img.src = "images/formations/" + formation.icon; - - $imageDiv.appendChild( $img ); - - // Name - let $nameDiv = document.createElement( "div" ); - $nameDiv.classList.add( "aic-formation-name" ); - $nameDiv.innerText = formation.label; - - // Wrapper - let $wrapperDiv = document.createElement( "div" ); - $wrapperDiv.dataset.formationName = formation.name; - $wrapperDiv.appendChild( $imageDiv ) - $wrapperDiv.appendChild( $nameDiv ); - $wrapperDiv.addEventListener( "click", ( ev ) => { - - const controlTypeInput = document.querySelector( "input[type='radio'][name='control-type']:checked" ); - - let controlTypeValue:any = ( controlTypeInput instanceof HTMLInputElement && [ "broadcast", "tactical" ].indexOf( controlTypeInput.value ) > -1 ) ? controlTypeInput.value : "broadcast"; - - // TODO: make this not an "any" - const output:any = formation.getDescriptor({ - "aicCallsign" : "Magic", - "bullseyeName" : "Bullseye", - "control" : controlTypeValue, - "numGroups" : formation.numGroups - }); - - this.updateTeleprompt( output ); - - }); - - // Add to DOM - $aicFormationList?.appendChild( $wrapperDiv ); - - }); - - } - - } - - - getFormations() { - return this.#formations; - } - - - onStatusUpdate() { - - // Update the DOM - document.body.classList.toggle( "aic-enabled", this.getStatus() ); - - } - - - toggleHelp() { - document.getElementById( "aic-help" )?.classList.toggle( "hide" ); - } - -//* - updateTeleprompt( descriptor:T[] ) { - - let $teleprompt = document.getElementById( "aic-teleprompt" ); - - if ( $teleprompt instanceof HTMLElement ) { - - // Clean slate - while ( $teleprompt.childNodes.length > 0 ) { - $teleprompt.childNodes[0].remove(); - } - - function newDiv() { - return document.createElement( "div" ); - } - - // Wrapper - let $descriptor = newDiv(); - $descriptor.id = "aic-descriptor"; - - for ( const section of descriptor ) { - - if ( section.omitSection ) { - continue; - } - - let $section = newDiv(); - $section.classList.add( "aic-descriptor-section" ); - - let $sectionLabel = newDiv(); - $sectionLabel.classList.add( "aic-descriptor-section-label" ); - $sectionLabel.innerText = section.label; - $section.appendChild( $sectionLabel ); - - - for ( const phrase of section.getPhrases() ) { - - let $phrase = newDiv(); - $phrase.classList.add( "aic-descriptor-phrase" ); - - for ( const component of phrase.getComponents() ) { - - let $component = newDiv(); - $component.classList.add( "aic-descriptor-component" ); - - let $componentLabel = newDiv(); - $componentLabel.classList.add( "aic-descriptor-component-label" ); - $componentLabel.innerText = component.label; - - let $componentValue = newDiv(); - $componentValue.classList.add( "aic-descriptor-component-value" ); - $componentValue.innerText = component.value; - - $component.appendChild( $componentLabel ); - $component.appendChild( $componentValue ); - - $phrase.appendChild( $component ); - - } - - $section.appendChild( $phrase ); - - } - - $descriptor.appendChild( $section ); - - } - - $teleprompt.appendChild( $descriptor ); - - } - - - } -//*/ - -} \ No newline at end of file diff --git a/client/src/aic/aicformation.ts b/client/src/aic/aicformation.ts deleted file mode 100644 index 588f4326..00000000 --- a/client/src/aic/aicformation.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { AICFormationContextDataInterface, AICFormationDescriptor } from "./aicformationdescriptor"; -import { AICFormationDescriptorPhrase } from "./aicformationdescriptorphrase"; -import { AICFormationDescriptorSection } from "./aicformationdescriptorsection"; - -export interface AICFormationInterface { - "icon" : string, - "label" : string, - "name" : string, - "numGroups" : number, - "summary" : string, - "unitBreakdown" : string[] -} - - - -export abstract class AICFormation { - - "icon" = ""; - "label" = ""; - "name" = ""; - "numGroups" = 1; - "summary" = ""; - "unitBreakdown":string[] = [] - - - constructor() { - - this.unitBreakdown = []; - } - - - addToDescriptorPhrase( section: AICFormationDescriptorSection, phrase: AICFormationDescriptorPhrase, contextData: AICFormationContextDataInterface ) { - return phrase; - } - - - getDescriptor( contextData: AICFormationContextDataInterface ) { - - return new AICFormationDescriptor().generate( this, contextData ); - - } - - - hasUnitBreakdown() { - return this.unitBreakdown.length > 0; - } - - - showFormationNameInDescriptor() { - return true; - } - - -} \ No newline at end of file diff --git a/client/src/aic/aicformation/azimuth.ts b/client/src/aic/aicformation/azimuth.ts deleted file mode 100644 index 7db676ae..00000000 --- a/client/src/aic/aicformation/azimuth.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { AICFormation, AICFormationInterface } from "../aicformation"; -import { AICFormationContextDataInterface } from "../aicformationdescriptor"; -import { AICFormationDescriptorSection } from "../aicformationdescriptorsection"; -import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent"; -import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase"; - -export class AICFormation_Azimuth extends AICFormation implements AICFormationInterface { - - "icon" = "azimuth.png"; - "label" = "Azimuth"; - "name" = "azimuth"; - "numGroups" = 2; - "summary" = "Two contacts, side-by-side in a line perpedicular to the perspective."; - "unitBreakdown" = [ " group", " group" ]; - - constructor() { - - super(); - - } - - addToDescriptorPhrase( section: AICFormationDescriptorSection, phrase: AICFormationDescriptorPhrase, contextData: AICFormationContextDataInterface ) { - - switch ( section.name ) { - - case "formation": - - phrase.addComponent( new AICFormationDescriptorComponent( "" ) ); - phrase.addComponent( new AICFormationDescriptorComponent( "track " ) ); - - } - - return phrase; - - - } - -} \ No newline at end of file diff --git a/client/src/aic/aicformation/range.ts b/client/src/aic/aicformation/range.ts deleted file mode 100644 index 08c00cd5..00000000 --- a/client/src/aic/aicformation/range.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { AICFormation, AICFormationInterface } from "../aicformation"; -import { AICFormationContextDataInterface } from "../aicformationdescriptor"; -import { AICFormationDescriptorSection } from "../aicformationdescriptorsection"; -import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent"; -import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase"; - -export class AICFormation_Range extends AICFormation implements AICFormationInterface { - - "icon" = "range.png"; - "label" = "Range"; - "name" = "range"; - "numGroups" = 2; - "summary" = "Two contacts, one behind the other"; - "unitBreakdown" = [ "Lead group", "Trail group" ]; - - constructor() { - - super(); - - } - - addToDescriptorPhrase( section: AICFormationDescriptorSection, phrase: AICFormationDescriptorPhrase, contextData: AICFormationContextDataInterface ) { - - switch ( section.name ) { - - case "formation": - - phrase.addComponent( new AICFormationDescriptorComponent( "" ) ); - phrase.addComponent( new AICFormationDescriptorComponent( "track " ) ); - - - } - - return phrase; - - } - -} \ No newline at end of file diff --git a/client/src/aic/aicformation/single.ts b/client/src/aic/aicformation/single.ts deleted file mode 100644 index 1e2aa851..00000000 --- a/client/src/aic/aicformation/single.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { AICFormation, AICFormationInterface } from "../aicformation"; -import { AICFormationContextDataInterface, AICFormationDescriptor } from "../aicformationdescriptor"; - -export class AICFormation_Single extends AICFormation implements AICFormationInterface { - - "icon" = "single.png"; - "label" = "Single"; - "name" = "single"; - "numGroups" = 1; - "summary" = "One contact on its own"; - "unitBreakdown": string[] = []; - - constructor() { - - super(); - - } - - - showFormationNameInDescriptor() { - return false; - } - -} \ No newline at end of file diff --git a/client/src/aic/aicformationdescriptor.ts b/client/src/aic/aicformationdescriptor.ts deleted file mode 100644 index c7a5a563..00000000 --- a/client/src/aic/aicformationdescriptor.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { AICFormation } from "./aicformation"; -import { AICFormationDescriptorSection } from "./aicformationdescriptorsection"; -import { AICFormationDescriptorSection_Formation } from "./aicformationdescriptorsection/formation"; -import { AICFormationDescriptorSection_Unit } from "./aicformationdescriptorsection/unit"; -import { AICFormationDescriptorSection_NumGroups } from "./aicformationdescriptorsection/numgroups"; -import { AICFormationDescriptorSection_Who } from "./aicformationdescriptorsection/who"; - - -export interface AICFormationContextDataInterface { - "aicCallsign" : string, - "bullseyeName" : string, - "control" : "broadcast" | "tactical", - "numGroups" : number -} - - -export class AICFormationDescriptor { - - #sections:AICFormationDescriptorSection[] = [ - new AICFormationDescriptorSection_Who(), - new AICFormationDescriptorSection_NumGroups(), - new AICFormationDescriptorSection_Formation(), - new AICFormationDescriptorSection_Unit() - ] - - constructor() { - } - - - addSection( section:AICFormationDescriptorSection ) { - this.#sections.push( section ); - } - - - getSections() { - return this.#sections; - } - - - generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) { - - let output:object[] = []; - - for ( const section of this.#sections ) { - output.push( - section.generate( formation, contextData ) - ); - } - - return output; - - } - - -} \ No newline at end of file diff --git a/client/src/aic/aicformationdescriptorcomponent.ts b/client/src/aic/aicformationdescriptorcomponent.ts deleted file mode 100644 index da194576..00000000 --- a/client/src/aic/aicformationdescriptorcomponent.ts +++ /dev/null @@ -1,18 +0,0 @@ -interface ComponentInterface { - "label" : string; - "value" : string; -} - -export class AICFormationDescriptorComponent implements ComponentInterface { - - label = "(not set)"; - value = "(not set)"; - - constructor( value:any, label?:string ) { - - this.label = label || "(not set)"; - this.value = value; - - } - -} \ No newline at end of file diff --git a/client/src/aic/aicformationdescriptorcomponent/distance.ts b/client/src/aic/aicformationdescriptorcomponent/distance.ts deleted file mode 100644 index 2a3766f3..00000000 --- a/client/src/aic/aicformationdescriptorcomponent/distance.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent"; - -export abstract class AICFormactionDescriptorComponent_Distance extends AICFormationDescriptorComponent { - - constructor( value:string, label?:string ) { - super( value, label ); - } - -} \ No newline at end of file diff --git a/client/src/aic/aicformationdescriptorcomponent/distance/range.ts b/client/src/aic/aicformationdescriptorcomponent/distance/range.ts deleted file mode 100644 index ab8a58d3..00000000 --- a/client/src/aic/aicformationdescriptorcomponent/distance/range.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { AICFormactionDescriptorComponent_Distance } from "../distance"; - -export class AICFormationDescriptorComponent_Distance_Range extends AICFormactionDescriptorComponent_Distance { - - constructor( value:string, label?:string ) { - super( value, label ); - } - -} \ No newline at end of file diff --git a/client/src/aic/aicformationdescriptorphrase.ts b/client/src/aic/aicformationdescriptorphrase.ts deleted file mode 100644 index b2a519a0..00000000 --- a/client/src/aic/aicformationdescriptorphrase.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { AICFormation } from "./aicformation"; -import { AICFormationContextDataInterface } from "./aicformationdescriptor"; -import { AICFormationDescriptorComponent } from "./aicformationdescriptorcomponent"; - -export interface AICFormationDescriptorPhraseInterface { - "generate" : CallableFunction, - "label" : string, - "name" : string -} - -export class AICFormationDescriptorPhrase { - - #components : AICFormationDescriptorComponent[] = []; - label = ""; - name = ""; - - constructor() { - } - - - addComponent( component:AICFormationDescriptorComponent ) { - this.#components.push( component ); - return this; - } - - - - getComponents() { - return this.#components; - } - - - generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) { - - return this; - - } - - -} \ No newline at end of file diff --git a/client/src/aic/aicformationdescriptorsection.ts b/client/src/aic/aicformationdescriptorsection.ts deleted file mode 100644 index bcd2f3c3..00000000 --- a/client/src/aic/aicformationdescriptorsection.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { AICFormation } from "./aicformation"; -import { AICFormationContextDataInterface } from "./aicformationdescriptor"; -import { AICFormationDescriptorPhrase } from "./aicformationdescriptorphrase"; - -export interface AICFormationDescriptorSectionInterface { - "generate" : CallableFunction, - "label" : string, - "name" : string, - "omitSection" : boolean -} - -export abstract class AICFormationDescriptorSection { - - #phrases : AICFormationDescriptorPhrase[] = []; - label = ""; - name = ""; - omitSection = false; - - constructor() { - } - - - addPhrase( phrase:AICFormationDescriptorPhrase ) { - this.#phrases.push( phrase ); - } - - - generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) { - - return this; - - } - - - getPhrases() { - return this.#phrases; - } - - -} \ No newline at end of file diff --git a/client/src/aic/aicformationdescriptorsection/formation.ts b/client/src/aic/aicformationdescriptorsection/formation.ts deleted file mode 100644 index c77cb4be..00000000 --- a/client/src/aic/aicformationdescriptorsection/formation.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { AICFormation } from "../aicformation"; -import { AICFormationContextDataInterface } from "../aicformationdescriptor"; -import { AICFormationDescriptorSection } from "../aicformationdescriptorsection"; -import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent"; -import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase"; - -export class AICFormationDescriptorSection_Formation extends AICFormationDescriptorSection { - - label = "Formation"; - name = "formation"; - - constructor() { - - super(); - - } - - - generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) { - - if ( !formation.showFormationNameInDescriptor() ) { - this.omitSection = true; - return this; - } - - let phrase = new AICFormationDescriptorPhrase(); - - phrase.addComponent( new AICFormationDescriptorComponent( formation.label, "Formation" ) ); - - phrase = formation.addToDescriptorPhrase( this, phrase, contextData ); - - this.addPhrase( phrase ); - - - return this; - - } - -} \ No newline at end of file diff --git a/client/src/aic/aicformationdescriptorsection/numgroups.ts b/client/src/aic/aicformationdescriptorsection/numgroups.ts deleted file mode 100644 index 81b92a86..00000000 --- a/client/src/aic/aicformationdescriptorsection/numgroups.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { AICFormation } from "../aicformation"; -import { AICFormationContextDataInterface } from "../aicformationdescriptor"; -import { AICFormationDescriptorSection } from "../aicformationdescriptorsection"; -import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent"; -import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase"; - -export class AICFormationDescriptorSection_NumGroups extends AICFormationDescriptorSection { - - label = "Groups"; - name = "numgroups"; - - constructor() { - - super(); - - } - - - generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) { - - let value = "Single group"; - - if ( contextData.numGroups > 1 ) { - value = contextData.numGroups + " groups"; - } - - let phrase = new AICFormationDescriptorPhrase(); - phrase.addComponent( new AICFormationDescriptorComponent( value, "Number of groups" ) ); - this.addPhrase( phrase ); - - return this; - - } - -} \ No newline at end of file diff --git a/client/src/aic/aicformationdescriptorsection/unit.ts b/client/src/aic/aicformationdescriptorsection/unit.ts deleted file mode 100644 index 749fe625..00000000 --- a/client/src/aic/aicformationdescriptorsection/unit.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { AICFormation } from "../aicformation"; -import { AICFormationContextDataInterface } from "../aicformationdescriptor"; -import { AICFormationDescriptorSection } from "../aicformationdescriptorsection"; -import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent"; -import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase"; - -interface addUnitInformationInterface { - omitTrack?: boolean -} - -export class AICFormationDescriptorSection_Unit extends AICFormationDescriptorSection { - - label = "Unit"; - name = "unit"; - - constructor() { - - super(); - - } - - - addUnitInformation( formation:AICFormation, contextData: AICFormationContextDataInterface, phrase: AICFormationDescriptorPhrase, options?:addUnitInformationInterface ) { - - options = options || {}; - - const originPoint = ( contextData.control === "broadcast" ) ? contextData.bullseyeName : "BRAA"; - - phrase.addComponent( new AICFormationDescriptorComponent( originPoint, "Bearing origin point" ) ); - phrase.addComponent( new AICFormationDescriptorComponent( "", "Bearing" ) ); - phrase.addComponent( new AICFormationDescriptorComponent( "", "Range" ) ); - phrase.addComponent( new AICFormationDescriptorComponent( "", "Altitude" ) ); - - if ( contextData.control === "broadcast" ) { - if ( !options.hasOwnProperty( "omitTrack" ) || options.omitTrack !== true ) { - phrase.addComponent( new AICFormationDescriptorComponent( "track ", "Tracking" ) ); - } - } else { - phrase.addComponent( new AICFormationDescriptorComponent( "[hot|flanking [left|right]|beam |cold]", "Azimuth" ) ); - } - - return phrase; - - } - - - generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) { - - if ( formation.hasUnitBreakdown() ) { - - for ( const [ i, unitRef ] of formation.unitBreakdown.entries() ) { - - let phrase = new AICFormationDescriptorPhrase(); - - phrase.addComponent( new AICFormationDescriptorComponent( unitRef, "Unit reference" ) ); - - if ( i === 0 ) { - this.addUnitInformation( formation, contextData, phrase, { "omitTrack": true } ); - } else { - phrase.addComponent( new AICFormationDescriptorComponent( "" ) ); - } - - phrase.addComponent( new AICFormationDescriptorComponent( "hostile" ) ); - - this.addPhrase( phrase ); - - - } - - } else { - - this.addPhrase( - this.addUnitInformation( formation, contextData, new AICFormationDescriptorPhrase() ) - ); - - } - - return this; - - } - - -} \ No newline at end of file diff --git a/client/src/aic/aicformationdescriptorsection/who.ts b/client/src/aic/aicformationdescriptorsection/who.ts deleted file mode 100644 index 244b0558..00000000 --- a/client/src/aic/aicformationdescriptorsection/who.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { AICFormation } from "../aicformation"; -import { AICFormationContextDataInterface } from "../aicformationdescriptor"; -import { AICFormationDescriptorSection } from "../aicformationdescriptorsection"; -import { AICFormationDescriptorComponent } from "../aicformationdescriptorcomponent"; -import { AICFormationDescriptorPhrase } from "../aicformationdescriptorphrase"; - -export class AICFormationDescriptorSection_Who extends AICFormationDescriptorSection { - - label = "Who"; - name = "who"; - - constructor() { - - super(); - - } - - - generate( formation:AICFormation, contextData: AICFormationContextDataInterface ) { - - let phrase = new AICFormationDescriptorPhrase(); - - if ( contextData.control === "tactical" ) { - phrase.addComponent( new AICFormationDescriptorComponent( "", "Their callsign" ) ); - } - - phrase.addComponent( new AICFormationDescriptorComponent( contextData.aicCallsign, "Your callsign" ) ); - - this.addPhrase( phrase ); - - return this; - - } - -} \ No newline at end of file diff --git a/client/src/atc/atc.ts b/client/src/atc/atc.ts deleted file mode 100644 index 2e5241af..00000000 --- a/client/src/atc/atc.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { getMissionHandler } from ".."; -import { convertDateAndTimeToDate } from "../other/utils"; -import { getConnected } from "../server/server"; -import { ATCBoard } from "./atcboard"; -import { ATCBoardGround } from "./board/ground"; -import { ATCBoardTower } from "./board/tower"; - -export interface FlightInterface { - assignedSpeed: any; - assignedAltitude : any; - id : string; - boardId : string; - name : string; - order : number; - status : "unknown"; - takeoffTime : number; - unitId : number; -} - - -class ATCDataHandler { - - #atc:ATC; - #flights:{[key:string]: FlightInterface} = {}; - - #updateInterval:number|undefined = undefined; - #updateIntervalDelay:number = 2500; // Wait between unit update requests - - - constructor( atc:ATC ) { - - this.#atc = atc; - - } - - - getFlights( boardId:string ) { - - return Object.values( this.#flights ).reduce( ( acc:{[key:string]: FlightInterface}, flight ) => { - - if ( flight.boardId === boardId ) { - acc[ flight.id ] = flight; - } - - return acc; - }, {} ); - } - - - startUpdates() { - - this.#updateInterval = window.setInterval( () => { - - if ( !getConnected() ) { - return; - } - - const aBoardIsVisible = this.#atc.getBoards().some( board => board.boardIsVisible() ); - - if ( aBoardIsVisible ) { - - fetch( '/api/atc/flight', { - method: 'GET', - headers: { - 'Accept': '*/*', - 'Content-Type': 'application/json' - } - }) - .then( response => response.json() ) - .then( data => { - this.setFlights( data ); - }); - - } - - }, this.#updateIntervalDelay ); - - } - - - setFlights( flights:{[key:string]: any} ) { - - this.#flights = flights; - - } - - - stopUpdates() { - - clearInterval( this.#updateInterval ); - - } - -} - - - - -export class ATC { - - #boards:ATCBoard[] = []; - #dataHandler:ATCDataHandler; - - #initDate:Date = new Date(); - - constructor() { - - this.#dataHandler = new ATCDataHandler( this ); - - this.lookForBoards(); - - } - - - addBoard( board:T ) { - - board.startUpdates(); - - this.#boards.push( board ); - - } - - - getBoards() { - return this.#boards; - } - - - getDataHandler() { - return this.#dataHandler; - } - - - getMissionElapsedSeconds() : number { - return new Date().getTime() - this.#initDate.getTime(); - } - - - getMissionStartDateTime() : Date { - return new Date( 1990, 3, 1, 18, 0, 0 ); - } - - - getMissionDate() : Date { - return convertDateAndTimeToDate(getMissionHandler().getDateAndTime()); - } - - lookForBoards() { - - document.querySelectorAll( ".ol-strip-board" ).forEach( board => { - - if ( board instanceof HTMLElement ) { - - switch ( board.dataset.boardType ) { - - case "ground": - this.addBoard( new ATCBoardGround( this, board ) ); - return; - - case "tower": - this.addBoard( new ATCBoardTower( this, board ) ); - return; - - default: - console.warn( "Unknown board type for ATC board, got: " + board.dataset.boardType ); - - } - - } - - }); - - } - - startUpdates() { - - this.#dataHandler.startUpdates(); - - } - - - stopUpdates() { - - this.#dataHandler.stopUpdates(); - - } - -} \ No newline at end of file diff --git a/client/src/atc/atcboard.ts b/client/src/atc/atcboard.ts deleted file mode 100644 index 1c4f31b3..00000000 --- a/client/src/atc/atcboard.ts +++ /dev/null @@ -1,527 +0,0 @@ -import { Dropdown } from "../controls/dropdown"; -import { zeroAppend } from "../other/utils"; -import { ATC } from "./atc"; -import { Unit } from "../unit/unit"; -import { getMissionHandler, getUnitsManager } from ".."; -import Sortable from "sortablejs"; -import { FlightInterface } from "./atc"; -import { getConnected } from "../server/server"; - -export interface StripBoardStripInterface { - "id": string, - "element": HTMLElement, - "dropdowns": {[key:string]: Dropdown}, - "isDeleted"?: boolean, - "unitId": number -} - -export abstract class ATCBoard { - - #atc:ATC; - #boardId:string = ""; - #templates: {[key:string]: string} = {}; - - - // Elements - #boardElement:HTMLElement; - #clockElement:HTMLElement; - #stripBoardElement:HTMLElement; - - // Content - #isAddFlightByClickEnabled:boolean = false; - #strips:{[key:string]: StripBoardStripInterface} = {}; - #unitIdsBeingMonitored:number[] = []; - - // Update timing - #updateInterval:number|undefined = undefined; - #updateIntervalDelay:number = 1000; - - - constructor( atc:ATC, boardElement:HTMLElement, options?:{[key:string]: any} ) { - - options = options || {}; - - this.#atc = atc; - this.#boardElement = boardElement; - this.#stripBoardElement = this.getBoardElement().querySelector( ".ol-strip-board-strips" ); - this.#clockElement = this.getBoardElement().querySelector( ".ol-strip-board-clock" ); - - - new MutationObserver( () => { - if ( this.boardIsVisible() ) { - this.startUpdates(); - } else { - this.stopUpdates(); - } - }).observe( this.getBoardElement(), { - "attributes": true, - "childList": false, - "subtree": false - }); - - - new Sortable( this.getStripBoardElement(), { - "handle": ".handle", - "onUpdate": ev => { - - const order = [].slice.call( this.getStripBoardElement().children ).map( ( strip:HTMLElement ) => { - return strip.dataset.flightId - }); - - fetch( '/api/atc/flight/order', { - method: 'POST', - headers: { - 'Accept': '*/*', - 'Content-Type': 'application/json' - }, - "body": JSON.stringify({ - "boardId" : this.getBoardId(), - "order" : order - }) - }); - - } - }); - - - window.setInterval( () => { - - if ( !getConnected() ) { - return; - } - this.updateClock(); - }, 1000 ); - - - if ( this.#boardElement.classList.contains( "ol-draggable" ) ) { - - let options:any = {}; - - let handle = this.#boardElement.querySelector( ".handle" ); - - if ( handle instanceof HTMLElement ) { - options.handle = handle; - } - - } - - - this.#setupAddFlight(); - - // this.#_setupDemoData(); - - } - - - addFlight( unit:Unit ) { - - const baseData = unit.getData(); - - const unitCanBeAdded = () => { - - if ( unit.getCategory() !== "Aircraft" ) { - return false; - } - - if ( baseData.controlled === true ) { - // return false; - } - - if ( this.#unitIdsBeingMonitored.includes( unit.ID ) ) { - return false; - } - - return true; - } - - if ( !unitCanBeAdded() ) { - return; - } - - this.#unitIdsBeingMonitored.push( unit.ID ); - - return fetch( '/api/atc/flight/', { - method: 'POST', - headers: { - 'Accept': '*/*', - 'Content-Type': 'application/json' - }, - "body": JSON.stringify({ - "boardId" : this.getBoardId(), - "name" : baseData.unitName, - "unitId" : unit.ID - }) - }); - - } - - - addStrip( strip:StripBoardStripInterface ) { - - this.#strips[ strip.id ] = strip; - - strip.element.querySelectorAll( "button.deleteFlight" ).forEach( btn => { - btn.addEventListener( "click", ev => { - ev.preventDefault(); - this.deleteFlight( strip.id ); - }); - }); - - } - - - boardIsVisible() { - return ( !this.getBoardElement().classList.contains( "hide" ) ); - } - - - calculateTimeToGo( fromTimestamp:number, toTimestamp:number ) { - - let timestamp = ( toTimestamp - fromTimestamp ) / 1000; - - const hasElapsed = ( timestamp < 0 ) ? true : false; - - if ( hasElapsed ) { - timestamp = -( timestamp ); - } - - const hours = ( timestamp < 3600 ) ? "00" : zeroAppend( Math.floor( timestamp / 3600 ), 2 ); - const rMinutes = timestamp % 3600; - - const minutes = ( timestamp < 60 ) ? "00" : zeroAppend( Math.floor( rMinutes / 60 ), 2 ); - const seconds = zeroAppend( Math.floor( rMinutes % 60 ), 2 ); - - return { - "elapsedMarker": ( hasElapsed ) ? "+" : "-", - "hasElapsed": hasElapsed, - "hours": hours, - "minutes": minutes, - "seconds": seconds, - "time": `${hours}:${minutes}:${seconds}`, - "totalSeconds": timestamp - }; - - } - - - deleteStrip( flightId:string ) { - - if ( this.#strips.hasOwnProperty( flightId ) ) { - - this.#strips[ flightId ].element.remove(); - this.#strips[ flightId ].isDeleted = true; - - window.setTimeout( () => { - delete this.#strips[ flightId ]; - }, 10000 ); - - } - - } - - - deleteFlight( flightId:string ) { - - this.deleteStrip( flightId ); - - fetch( '/api/atc/flight/' + flightId, { - method: 'DELETE', - headers: { - 'Accept': '*/*', - 'Content-Type': 'application/json' - }, - "body": JSON.stringify({ - "boardId": this.getBoardId() - }) - }); - - } - - - getATC() { - return this.#atc; - } - - - getBoardElement() { - return this.#boardElement; - } - - - getBoardId(): string { - return this.getBoardElement().id; - } - - - getStripBoardElement() { - return this.#stripBoardElement; - } - - - getStrips() { - return this.#strips; - } - - - getStrip( id:string ) { - return this.#strips[ id ] || false; - } - - - getTemplate( key:string ) { - - return this.#templates[ key ] || false; - - } - - - getUnitIdsBeingMonitored() { - - return this.#unitIdsBeingMonitored; - - } - - - setTemplates( templates:{[key:string]: string} ) { - this.#templates = templates; - } - - - #setupAddFlight() { - - const toggleIsAddFlightByClickEnabled = () => { - this.#isAddFlightByClickEnabled = ( !this.#isAddFlightByClickEnabled ); - this.getBoardElement().classList.toggle( "add-flight-by-click", this.#isAddFlightByClickEnabled ); - } - - - document.addEventListener( "unitSelection", ( ev:CustomEventInit ) => { - - if ( this.#isAddFlightByClickEnabled !== true ) { - return; - } - - this.addFlight( ev.detail ); - - toggleIsAddFlightByClickEnabled(); - - }); - - - const form = this.getBoardElement().querySelector( "form.ol-strip-board-add-flight" ); - const suggestions = form.querySelector( ".ol-auto-suggest" ); - const unitName = form.querySelector( "input[name='unitName']" ); - - const toggleSuggestions = ( bool:boolean ) => { - suggestions.toggleAttribute( "data-has-suggestions", bool ); - } - - let searchTimeout:number|null; - - unitName.addEventListener( "keyup", ev => { - - if ( searchTimeout ) { - clearTimeout( searchTimeout ); - } - - const resetSuggestions = () => { - suggestions.innerHTML = ""; - toggleSuggestions( false ); - } - - resetSuggestions(); - - searchTimeout = window.setTimeout( () => { - - const searchString = unitName.value.toLowerCase(); - - if ( searchString === "" ) { - return; - } - - const units = getUnitsManager().getSelectableAircraft(); - const unitIdsBeingMonitored = this.getUnitIdsBeingMonitored(); - - const results = Object.keys( units ).reduce( ( acc:Unit[], unitId:any ) => { - - const unit = units[ unitId ]; - const baseData = unit.getData(); - - if ( !unitIdsBeingMonitored.includes( parseInt( unitId ) ) && baseData.unitName.toLowerCase().indexOf( searchString ) > -1 ) { - acc.push( unit ); - } - - return acc; - - }, [] ); - - toggleSuggestions( results.length > 0 ); - - results.forEach( unit => { - - const baseData = unit.getData(); - - const a = document.createElement( "a" ); - a.innerText = baseData.unitName; - - a.addEventListener( "click", ev => { - this.addFlight( unit ); - resetSuggestions(); - unitName.value = ""; - }); - - suggestions.appendChild( a ); - - }); - - - - }, 1000 ); - - - }); - - form.querySelectorAll( ".add-flight-by-click" ).forEach( el => { - el.addEventListener( "click", ev => { - ev.preventDefault(); - toggleIsAddFlightByClickEnabled(); - }); - }); - - } - - - sortFlights( flights:FlightInterface[] ) { - - flights.sort( ( a, b ) => { - - const aVal = a.order; - const bVal = b.order; - - return ( aVal > bVal ) ? 1 : -1; - - }); - - return flights; - - } - - - startUpdates() { - - if ( !this.boardIsVisible() ) { - return; - } - - this.#updateInterval = window.setInterval( () => { - - if ( !getConnected() ) { - return; - } - - this.update(); - - }, this.#updateIntervalDelay ); - - } - - - stopUpdates() { - - clearInterval( this.#updateInterval ); - - } - - - timestampToLocaleTime( timestamp:number ) { - - return ( timestamp === -1 ) ? "-" : new Date( timestamp ).toLocaleTimeString(); - - } - - - timeToGo( timestamp:number ) { - - const timeData = this.calculateTimeToGo( this.getATC().getMissionDate().getTime(), timestamp ); - - return ( timestamp === -1 ) ? "-" : timeData.elapsedMarker + timeData.time; - - } - - protected update() { - console.warn( "No custom update method defined." ); - } - - - updateClock() { - - const missionTime = this.#atc.getMissionDate().getTime(); - const timeDiff = new Date().getTime() - getMissionHandler().getDateAndTime().elapsedTime; - - const nowDate = new Date( missionTime + timeDiff ); - - this.#clockElement.innerText = nowDate.toLocaleTimeString(); - - } - - - updateFlight( flightId:string, reqBody:object ) { - - return fetch( '/api/atc/flight/' + flightId, { - method: 'PATCH', - headers: { - 'Accept': '*/*', - 'Content-Type': 'application/json' - }, - "body": JSON.stringify( reqBody ) - }); - - } - - - #_setupDemoData() { - - fetch( '/api/atc/flight/', { - method: 'POST', - headers: { - 'Accept': '*/*', - 'Content-Type': 'application/json' - }, - "body": JSON.stringify({ - "boardId" : this.getBoardId(), - "name" : this.getBoardId() + " 1", - "unitId" : 1 - }) - }); - - - // fetch( '/api/atc/flight/', { - // method: 'POST', - // headers: { - // 'Accept': '*/*', - // 'Content-Type': 'application/json' - // }, - // "body": JSON.stringify({ - // "boardId" : this.getBoardId(), - // "name" : this.getBoardId() + " 2", - // "unitId" : 2 - // }) - // }); - - // fetch( '/api/atc/flight/', { - // method: 'POST', - // headers: { - // 'Accept': '*/*', - // 'Content-Type': 'application/json' - // }, - // "body": JSON.stringify({ - // "boardId" : this.getBoardId(), - // "name" : this.getBoardId() + " 3", - // "unitId" : 9 - // }) - // }); - - } - - -} \ No newline at end of file diff --git a/client/src/atc/board/ground.ts b/client/src/atc/board/ground.ts deleted file mode 100644 index 4e5dba3d..00000000 --- a/client/src/atc/board/ground.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { Dropdown } from "../../controls/dropdown"; -import { ATC } from "../atc"; -import { ATCBoard } from "../atcboard"; - - -export class ATCBoardGround extends ATCBoard { - - constructor( atc:ATC, element:HTMLElement ) { - - super( atc, element ); - - } - - - update() { - - const flights = this.sortFlights( Object.values( this.getATC().getDataHandler().getFlights( this.getBoardId() ) ) ); - const stripBoard = this.getStripBoardElement(); - - const missionTime = this.getATC().getMissionDate().getTime(); - - for( const strip of stripBoard.children ) { - strip.toggleAttribute( "data-updating", true ); - } - - - flights.forEach( flight => { - - let strip = this.getStrip( flight.id ); - - if ( !strip ) { - - const template = `
-
-
${flight.name}
- -
-
${flight.status}
-
-
- -
- -
${this.timeToGo( flight.takeoffTime )}
- - -
`; - - stripBoard.insertAdjacentHTML( "beforeend", template ); - - - strip = { - "id": flight.id, - "element": stripBoard.lastElementChild, - "dropdowns": {}, - "unitId": -1 - }; - - strip.element.querySelectorAll( ".ol-select" ).forEach( select => { - - switch( select.getAttribute( "data-point" ) ) { - - case "status": - - strip.dropdowns.status = new Dropdown( select.id, ( value:string, ev:MouseEvent ) => { - - fetch( '/api/atc/flight/' + flight.id, { - method: 'PATCH', - headers: { - 'Accept': '*/*', - 'Content-Type': 'application/json' - }, - "body": JSON.stringify({ - "status": value - }) - }); - - }, [ - "unknown", "checkedin", "readytotaxi", "clearedtotaxi", "halted", "terminated" - ]); - - break; - - } - - }); - - strip.element.querySelectorAll( `input[type="text"]` ).forEach( input => { - - if ( input instanceof HTMLInputElement ) { - - input.addEventListener( "blur", ( ev ) => { - - const target = ev.target; - - if ( target instanceof HTMLInputElement ) { - - if ( /^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/.test( target.value ) ) { - target.value += ":00"; - } - - const value = target.value; - - if ( value === target.dataset.previousValue ) { - return; - - } else if ( value === "" ) { - - this.#updateTakeoffTime( flight.id, -1 ); - - } else if ( /^([0-1]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/.test( value ) ) { - - let [ hours, minutes, seconds ] = value.split( ":" ).map( str => parseInt( str ) ); - - const missionStart = this.getATC().getMissionStartDateTime(); - - this.#updateTakeoffTime( flight.id, new Date( - missionStart.getFullYear(), - missionStart.getMonth(), - missionStart.getDate(), - hours, - minutes, - seconds - ).getTime() ); - - } else { - - target.value === target.dataset.previousValue - - } - - } - - }); - - } - - }); - - this.addStrip( strip ); - - } else { - - if ( flight.status !== strip.element.getAttribute( "data-flight-status" ) ) { - strip.element.setAttribute( "data-flight-status", flight.status ); - strip.dropdowns.status.selectText( flight.status ); - } - - strip.element.querySelectorAll( `input[name="takeoffTime"]:not(:focus)` ).forEach( el => { - if ( el instanceof HTMLInputElement ) { - el.value = this.timestampToLocaleTime( flight.takeoffTime ); - el.dataset.previousValue = el.value; - } - }); - - strip.element.querySelectorAll( `[data-point="timeToGo"]` ).forEach( el => { - - if ( flight.takeoffTime > 0 && this.calculateTimeToGo( missionTime, flight.takeoffTime ).totalSeconds <= 120 ) { - strip.element.setAttribute( "data-time-warning", "level-1" ); - } - - if ( el instanceof HTMLElement ) { - el.innerText = this.timeToGo( flight.takeoffTime ); - } - - }); - - - } - - strip.element.toggleAttribute( "data-updating", false ); - - }); - - - stripBoard.querySelectorAll( `[data-updating]` ).forEach( strip => { - this.deleteStrip( strip.getAttribute( "data-flight-id" ) || "" ); - }); - - } - - - - - #updateTakeoffTime = function( flightId:string, time:number ) { - - fetch( '/api/atc/flight/' + flightId, { - method: 'PATCH', - headers: { - 'Accept': '*/*', - 'Content-Type': 'application/json' - }, - "body": JSON.stringify({ - "takeoffTime": time - }) - }); - - } - -} \ No newline at end of file diff --git a/client/src/atc/board/tower.ts b/client/src/atc/board/tower.ts deleted file mode 100644 index 9afcf7db..00000000 --- a/client/src/atc/board/tower.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { getUnitsManager } from "../.."; -import { Dropdown } from "../../controls/dropdown"; -import { mToFt, msToKnots } from "../../other/utils"; -import { ATC } from "../atc"; -import { ATCBoard } from "../atcboard"; - - -export class ATCBoardTower extends ATCBoard { - - constructor( atc:ATC, element:HTMLElement ) { - - super( atc, element ); - - } - - - update() { - - const flights = this.sortFlights( Object.values( this.getATC().getDataHandler().getFlights( this.getBoardId() ) ) ); - const missionTime = this.getATC().getMissionDate().getTime(); - const selectableUnits = getUnitsManager().getSelectableAircraft(); - const stripBoard = this.getStripBoardElement(); - - for( const strip of stripBoard.children ) { - strip.toggleAttribute( "data-updating", true ); - } - - - flights.forEach( flight => { - - let strip = this.getStrip( flight.id ); - - if ( strip.isDeleted === true ) { - return; - } - - const flightData = { - latitude: -1, - longitude: -1, - altitude: -1, - heading: -1, - speed: -1, - ...( selectableUnits.hasOwnProperty( flight.unitId ) ? selectableUnits[flight.unitId].getData() : {} ) - }; - - if ( !strip ) { - - const template = `
-
- - -
000
-
-
- -
-
-
- - -
`; - - stripBoard.insertAdjacentHTML( "beforeend", template ); - - - strip = { - "id": flight.id, - "element": stripBoard.lastElementChild, - "dropdowns": {}, - "unitId": flight.unitId - }; - - - strip.element.querySelectorAll( `input[type="text"]` ).forEach( input => { - - if ( input instanceof HTMLInputElement ) { - - switch ( input.name ) { - - case "assignedAltitude": - - input.addEventListener( "change", ( ev ) => { - - let val = parseInt( input.value.replace( /[^\d]/g, "" ) ); - - if ( isNaN( val ) || val < 0 || val > 40 ) { - val = 0; - } - - this.updateFlight( flight.id, { - "assignedAltitude": val - }); - - }); - - break; - - case "assignedSpeed": - - input.addEventListener( "change", ( ev ) => { - - let val = parseInt( input.value.replace( /[^\d]/g, "" ) ); - - if ( isNaN( val ) || val < 0 || val > 750 ) { - val = 0; - } - - this.updateFlight( flight.id, { - "assignedSpeed": val - }); - - }); - - break; - - } - - } - - }); - - strip.element.querySelectorAll( ".select-unit" ).forEach( el => { - - el.addEventListener( "click", ev => { - ev.preventDefault(); - getUnitsManager().selectUnit( flight.unitId ); - }); - - }); - - this.addStrip( strip ); - - } else { - - // - // Altitude - // - - let assignedAltitude = strip.element.querySelector( `input[name="assignedAltitude"]`); - - if ( !assignedAltitude.matches( ":focus" ) && assignedAltitude.value !== flight.assignedAltitude ) { - assignedAltitude.value = flight.assignedAltitude; - } - - flightData.altitude = Math.floor( mToFt(flightData.altitude) ); - - strip.element.querySelectorAll( `[data-point="altitude"]` ).forEach( el => { - if ( el instanceof HTMLElement ) { - el.innerText = "" + flightData.altitude; - } - }); - - const altitudeDelta = ( flight.assignedAltitude === 0 ) ? 0 : ( flight.assignedAltitude * 1000 ) - flightData.altitude; - - strip.element.toggleAttribute( "data-altitude-assigned", ( flight.assignedAltitude > 0 ) ); - strip.element.toggleAttribute( "data-warning-altitude", ( altitudeDelta >= 300 || altitudeDelta <= -300 ) ); - - - // - // Speed - // - - let assignedSpeed = strip.element.querySelector( `input[name="assignedSpeed"]`); - - if ( !assignedSpeed.matches( ":focus" ) && assignedSpeed.value !== flight.assignedSpeed ) { - assignedSpeed.value = flight.assignedSpeed; - } - - flightData.speed = Math.floor( msToKnots(flightData.speed) ); - - strip.element.querySelectorAll( `[data-point="speed"]` ).forEach( el => { - if ( el instanceof HTMLElement ) { - el.innerText = "" + flightData.speed; - } - }); - - const speedDelta = ( flight.assignedSpeed === 0 ) ? 0 : flight.assignedSpeed - flightData.speed; - - strip.element.toggleAttribute( "data-speed-assigned", ( flight.assignedSpeed > 0 ) ); - strip.element.toggleAttribute( "data-warning-speed", ( speedDelta >= 25 || speedDelta <= -25 ) ); - - - - } - - strip.element.toggleAttribute( "data-updating", false ); - - }); - - stripBoard.querySelectorAll( `[data-updating]` ).forEach( strip => { - this.deleteStrip( strip.getAttribute( "data-flight-id" ) || "" ); - }); - - } - -} \ No newline at end of file diff --git a/client/src/atc/unitdatatable.ts b/client/src/atc/unitdatatable.ts deleted file mode 100644 index fe461273..00000000 --- a/client/src/atc/unitdatatable.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { getUnitsManager } from ".."; -import { Panel } from "../panels/panel"; -import { Unit } from "../unit/unit"; - -export class UnitDataTable extends Panel { - /** - * - * @param ID - the ID of the HTML element which will contain the context menu - */ - constructor(ID: string){ - super(ID); - this.hide(); - } - - update() { - var units = getUnitsManager().getUnits(); - - const unitsArray = Object.values(units).sort((a: Unit, b: Unit) => { - const aVal = a.getUnitName()?.toLowerCase(); - const bVal = b.getUnitName()?.toLowerCase(); - - if (aVal > bVal) { - return 1; - } else if (bVal > aVal) { - return -1; - } else { - return 0; - } - }); - - - function addRow(parentEl: HTMLElement, columns: string[]) { - const rowDiv = document.createElement("div"); - - for (const item of columns) { - - const div = document.createElement("div"); - div.innerText = item; - rowDiv.appendChild(div); - - } - parentEl.appendChild(rowDiv); - } - - const el = this.getElement().querySelector("#unit-list"); - - if (el) { - - el.innerHTML = ""; - - addRow(el, ["Callsign", "Name", "Category", "AI/Human"]) - - for (const unit of unitsArray) { - - const dataset = [unit.getUnitName(), unit.getName(), unit.getCategory(), (unit.getControlled()) ? "AI" : "Human"]; - - addRow(el, dataset); - } - } - } -} \ No newline at end of file diff --git a/client/src/contextmenus/airbasespawnmenu.ts b/client/src/contextmenus/airbasespawnmenu.ts index f7c240ec..4266a733 100644 --- a/client/src/contextmenus/airbasespawnmenu.ts +++ b/client/src/contextmenus/airbasespawnmenu.ts @@ -35,6 +35,9 @@ export class AirbaseSpawnContextMenu extends ContextMenu { this.#aircraftSpawnMenu.getContainer().addEventListener("resize", () => this.clip()); this.#helicopterSpawnMenu.getContainer().addEventListener("resize", () => this.clip()); + this.#aircraftSpawnMenu.getContainer().addEventListener("hide", () => this.hide()); + this.#helicopterSpawnMenu.getContainer().addEventListener("hide", () => this.hide()); + this.hide(); } diff --git a/client/src/contextmenus/coalitionareacontextmenu.ts b/client/src/contextmenus/coalitionareacontextmenu.ts index d153cd82..94a88e36 100644 --- a/client/src/contextmenus/coalitionareacontextmenu.ts +++ b/client/src/contextmenus/coalitionareacontextmenu.ts @@ -1,12 +1,12 @@ import { LatLng } from "leaflet"; import { getMap, getMissionHandler, getUnitsManager } from ".."; import { GAME_MASTER, IADSTypes } from "../constants/constants"; -import { CoalitionArea } from "../map/coalitionarea"; +import { CoalitionArea } from "../map/coalitionarea/coalitionarea"; import { ContextMenu } from "./contextmenu"; import { Dropdown } from "../controls/dropdown"; import { Slider } from "../controls/slider"; import { Switch } from "../controls/switch"; -import { groundUnitDatabase } from "../unit/groundunitdatabase"; +import { groundUnitDatabase } from "../unit/databases/groundunitdatabase"; import { createCheckboxOption, getCheckboxOptions } from "../other/utils"; /** This context menu allows the user to edit or delete a CoalitionArea. Moreover, it allows the user to create a IADS automatically using the CoalitionArea as bounds. */ diff --git a/client/src/contextmenus/mapcontextmenu.ts b/client/src/contextmenus/mapcontextmenu.ts index 90780d12..b35c1705 100644 --- a/client/src/contextmenus/mapcontextmenu.ts +++ b/client/src/contextmenus/mapcontextmenu.ts @@ -4,10 +4,10 @@ import { spawnExplosion, spawnSmoke } from "../server/server"; import { ContextMenu } from "./contextmenu"; import { Switch } from "../controls/switch"; import { GAME_MASTER } from "../constants/constants"; -import { CoalitionArea } from "../map/coalitionarea"; +import { CoalitionArea } from "../map/coalitionarea/coalitionarea"; import { AircraftSpawnMenu, GroundUnitSpawnMenu, HelicopterSpawnMenu, NavyUnitSpawnMenu } from "../controls/unitspawnmenu"; import { Airbase } from "../mission/airbase"; -import { SmokeMarker } from "../map/smokemarker"; +import { SmokeMarker } from "../map/markers/smokemarker"; /** The MapContextMenu is the main contextmenu shown to the user whenever it rightclicks on the map. It is the primary interaction method for the user. * It allows to spawn units, create explosions and smoke, and edit CoalitionAreas. @@ -75,6 +75,11 @@ export class MapContextMenu extends ContextMenu { this.#groundUnitSpawnMenu.getContainer().addEventListener("resize", () => this.clip()); this.#navyUnitSpawnMenu.getContainer().addEventListener("resize", () => this.clip()); + this.#aircraftSpawnMenu.getContainer().addEventListener("hide", () => this.hide()); + this.#helicopterSpawnMenu.getContainer().addEventListener("hide", () => this.hide()); + this.#groundUnitSpawnMenu.getContainer().addEventListener("hide", () => this.hide()); + this.#navyUnitSpawnMenu.getContainer().addEventListener("hide", () => this.hide()); + this.hide(); } diff --git a/client/src/controls/unitspawnmenu.ts b/client/src/controls/unitspawnmenu.ts index 5a6a64cc..250169a6 100644 --- a/client/src/controls/unitspawnmenu.ts +++ b/client/src/controls/unitspawnmenu.ts @@ -1,16 +1,16 @@ import { LatLng } from "leaflet"; import { Dropdown } from "./dropdown"; import { Slider } from "./slider"; -import { UnitDatabase } from "../unit/unitdatabase"; +import { UnitDatabase } from "../unit/databases/unitdatabase"; import { getActiveCoalition, getMap, getMissionHandler, getUnitsManager } from ".."; import { GAME_MASTER } from "../constants/constants"; import { UnitSpawnOptions } from "../@types/unitdatabase"; import { Airbase } from "../mission/airbase"; import { ftToM } from "../other/utils"; -import { aircraftDatabase } from "../unit/aircraftdatabase"; -import { helicopterDatabase } from "../unit/helicopterdatabase"; -import { groundUnitDatabase } from "../unit/groundunitdatabase"; -import { navyUnitDatabase } from "../unit/navyunitdatabase"; +import { aircraftDatabase } from "../unit/databases/aircraftdatabase"; +import { helicopterDatabase } from "../unit/databases/helicopterdatabase"; +import { groundUnitDatabase } from "../unit/databases/groundunitdatabase"; +import { navyUnitDatabase } from "../unit/databases/navyunitdatabase"; export class UnitSpawnMenu { #container: HTMLElement; @@ -418,7 +418,7 @@ export class AircraftSpawnMenu extends UnitSpawnMenu { getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition(), res.commandHash); }); - getMap().getMapContextMenu().hide(); + this.getContainer().dispatchEvent(new Event("hide")); } } } @@ -456,7 +456,7 @@ export class HelicopterSpawnMenu extends UnitSpawnMenu { getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition(), res.commandHash); }); - getMap().getMapContextMenu().hide(); + this.getContainer().dispatchEvent(new Event("hide")); } } } @@ -493,7 +493,7 @@ export class GroundUnitSpawnMenu extends UnitSpawnMenu { getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition(), res.commandHash); }); - getMap().getMapContextMenu().hide(); + this.getContainer().dispatchEvent(new Event("hide")); } } } @@ -530,7 +530,7 @@ export class NavyUnitSpawnMenu extends UnitSpawnMenu { getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition(), res.commandHash); }); - getMap().getMapContextMenu().hide(); + this.getContainer().dispatchEvent(new Event("hide")); } } } \ No newline at end of file diff --git a/client/src/index.ts b/client/src/index.ts index 1802e300..a3b33b38 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -15,6 +15,8 @@ import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "./constants/constant import { ServerStatusPanel } from "./panels/serverstatuspanel"; import { WeaponsManager } from "./weapon/weaponsmanager"; import { ConfigParameters } from "./@types/dom"; +import { CommandModeToolbar } from "./toolbars/commandmodetoolbar"; +import { PrimaryToolbar } from "./toolbars/primaryToolbar"; /* Global data */ var activeCoalition: string = "blue"; @@ -36,10 +38,15 @@ var mouseInfoPanel: MouseInfoPanel; var logPanel: LogPanel; var hotgroupPanel: HotgroupPanel; +/* UI Toolbars */ +var primaryToolbar: PrimaryToolbar; +var commandModeToolbar: CommandModeToolbar; + /* Popups */ var infoPopup: Popup; function setup() { + /* Initialize base functionalitites */ map = new Map('map-container'); @@ -56,6 +63,10 @@ function setup() { hotgroupPanel = new HotgroupPanel("hotgroup-panel"); logPanel = new LogPanel("log-panel"); + /* Toolbars */ + primaryToolbar = new PrimaryToolbar("primary-toolbar"); + commandModeToolbar = new CommandModeToolbar("command-mode-toolbar"); + /* Popups */ infoPopup = new Popup("info-popup"); diff --git a/client/src/map/coalitionarea.ts b/client/src/map/coalitionarea/coalitionarea.ts similarity index 97% rename from client/src/map/coalitionarea.ts rename to client/src/map/coalitionarea/coalitionarea.ts index fe98daa9..fb3682c6 100644 --- a/client/src/map/coalitionarea.ts +++ b/client/src/map/coalitionarea/coalitionarea.ts @@ -1,8 +1,8 @@ import { DomUtil, LatLng, LatLngExpression, Map, Point, Polygon, PolylineOptions } from "leaflet"; -import { getMap, getMissionHandler, getUnitsManager } from ".."; +import { getMap, getMissionHandler, getUnitsManager } from "../.."; import { CoalitionAreaHandle } from "./coalitionareahandle"; import { CoalitionAreaMiddleHandle } from "./coalitionareamiddlehandle"; -import { BLUE_COMMANDER, RED_COMMANDER } from "../constants/constants"; +import { BLUE_COMMANDER, RED_COMMANDER } from "../../constants/constants"; export class CoalitionArea extends Polygon { #coalition: string = "blue"; diff --git a/client/src/map/coalitionareahandle.ts b/client/src/map/coalitionarea/coalitionareahandle.ts similarity index 90% rename from client/src/map/coalitionareahandle.ts rename to client/src/map/coalitionarea/coalitionareahandle.ts index 6dceaaa8..26421c85 100644 --- a/client/src/map/coalitionareahandle.ts +++ b/client/src/map/coalitionarea/coalitionareahandle.ts @@ -1,5 +1,5 @@ import { DivIcon, LatLng } from "leaflet"; -import { CustomMarker } from "./custommarker"; +import { CustomMarker } from "../markers/custommarker"; export class CoalitionAreaHandle extends CustomMarker { constructor(latlng: LatLng) { diff --git a/client/src/map/coalitionareamiddlehandle.ts b/client/src/map/coalitionarea/coalitionareamiddlehandle.ts similarity index 91% rename from client/src/map/coalitionareamiddlehandle.ts rename to client/src/map/coalitionarea/coalitionareamiddlehandle.ts index b268ec9b..06cd4a57 100644 --- a/client/src/map/coalitionareamiddlehandle.ts +++ b/client/src/map/coalitionarea/coalitionareamiddlehandle.ts @@ -1,5 +1,5 @@ import { DivIcon, LatLng } from "leaflet"; -import { CustomMarker } from "./custommarker"; +import { CustomMarker } from "../markers/custommarker"; export class CoalitionAreaMiddleHandle extends CustomMarker { constructor(latlng: LatLng) { diff --git a/client/src/map/drawingcursor.ts b/client/src/map/coalitionarea/drawingcursor.ts similarity index 90% rename from client/src/map/drawingcursor.ts rename to client/src/map/coalitionarea/drawingcursor.ts index 2758dc1a..e4497952 100644 --- a/client/src/map/drawingcursor.ts +++ b/client/src/map/coalitionarea/drawingcursor.ts @@ -1,5 +1,5 @@ import { DivIcon, LatLng } from "leaflet"; -import { CustomMarker } from "./custommarker"; +import { CustomMarker } from "../markers/custommarker"; export class DrawingCursor extends CustomMarker { constructor() { diff --git a/client/src/map/map.ts b/client/src/map/map.ts index f9ea77ae..351f0382 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -8,15 +8,15 @@ import { Dropdown } from "../controls/dropdown"; import { Airbase } from "../mission/airbase"; import { Unit } from "../unit/unit"; import { bearing, createCheckboxOption } from "../other/utils"; -import { DestinationPreviewMarker } from "./destinationpreviewmarker"; -import { TemporaryUnitMarker } from "./temporaryunitmarker"; +import { DestinationPreviewMarker } from "./markers/destinationpreviewmarker"; +import { TemporaryUnitMarker } from "./markers/temporaryunitmarker"; import { ClickableMiniMap } from "./clickableminimap"; import { SVGInjector } from '@tanem/svg-injector' import { layers as mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTooltips, MOVE_UNIT, SHOW_CONTACT_LINES, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, visibilityControlsTypes, SHOW_UNIT_LABELS } from "../constants/constants"; -import { TargetMarker } from "./targetmarker"; -import { CoalitionArea } from "./coalitionarea"; +import { TargetMarker } from "./markers/targetmarker"; +import { CoalitionArea } from "./coalitionarea/coalitionarea"; import { CoalitionAreaContextMenu } from "../contextmenus/coalitionareacontextmenu"; -import { DrawingCursor } from "./drawingcursor"; +import { DrawingCursor } from "./coalitionarea/drawingcursor"; import { AirbaseSpawnContextMenu } from "../contextmenus/airbasespawnmenu"; L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect); diff --git a/client/src/map/custommarker.ts b/client/src/map/markers/custommarker.ts similarity index 100% rename from client/src/map/custommarker.ts rename to client/src/map/markers/custommarker.ts diff --git a/client/src/map/destinationpreviewmarker.ts b/client/src/map/markers/destinationpreviewmarker.ts similarity index 100% rename from client/src/map/destinationpreviewmarker.ts rename to client/src/map/markers/destinationpreviewmarker.ts diff --git a/client/src/map/smokemarker.ts b/client/src/map/markers/smokemarker.ts similarity index 97% rename from client/src/map/smokemarker.ts rename to client/src/map/markers/smokemarker.ts index 678912c2..39cf9f82 100644 --- a/client/src/map/smokemarker.ts +++ b/client/src/map/markers/smokemarker.ts @@ -1,7 +1,7 @@ import { DivIcon, LatLngExpression, MarkerOptions } from "leaflet"; import { CustomMarker } from "./custommarker"; import { SVGInjector } from "@tanem/svg-injector"; -import { getMap } from ".."; +import { getMap } from "../.."; export class SmokeMarker extends CustomMarker { #color: string; diff --git a/client/src/map/targetmarker.ts b/client/src/map/markers/targetmarker.ts similarity index 100% rename from client/src/map/targetmarker.ts rename to client/src/map/markers/targetmarker.ts diff --git a/client/src/map/temporaryunitmarker.ts b/client/src/map/markers/temporaryunitmarker.ts similarity index 95% rename from client/src/map/temporaryunitmarker.ts rename to client/src/map/markers/temporaryunitmarker.ts index 97f93d4f..0f8e55ee 100644 --- a/client/src/map/temporaryunitmarker.ts +++ b/client/src/map/markers/temporaryunitmarker.ts @@ -1,9 +1,9 @@ import { CustomMarker } from "./custommarker"; import { DivIcon, LatLng } from "leaflet"; import { SVGInjector } from "@tanem/svg-injector"; -import { getMarkerCategoryByName, getUnitDatabaseByCategory } from "../other/utils"; -import { isCommandExecuted } from "../server/server"; -import { getMap } from ".."; +import { getMarkerCategoryByName, getUnitDatabaseByCategory } from "../../other/utils"; +import { isCommandExecuted } from "../../server/server"; +import { getMap } from "../.."; export class TemporaryUnitMarker extends CustomMarker { #name: string; diff --git a/client/src/mission/airbase.ts b/client/src/mission/airbase.ts index e1477acd..0534803e 100644 --- a/client/src/mission/airbase.ts +++ b/client/src/mission/airbase.ts @@ -1,5 +1,5 @@ import { DivIcon } from 'leaflet'; -import { CustomMarker } from '../map/custommarker'; +import { CustomMarker } from '../map/markers/custommarker'; import { SVGInjector } from '@tanem/svg-injector'; export interface AirbaseOptions { diff --git a/client/src/mission/bullseye.ts b/client/src/mission/bullseye.ts index b50b1a7b..ccadfd77 100644 --- a/client/src/mission/bullseye.ts +++ b/client/src/mission/bullseye.ts @@ -1,5 +1,5 @@ import { DivIcon } from "leaflet"; -import { CustomMarker } from "../map/custommarker"; +import { CustomMarker } from "../map/markers/custommarker"; import { SVGInjector } from "@tanem/svg-injector"; export class Bullseye extends CustomMarker { diff --git a/client/src/mission/missionhandler.ts b/client/src/mission/missionhandler.ts index ed44828b..fbe8935b 100644 --- a/client/src/mission/missionhandler.ts +++ b/client/src/mission/missionhandler.ts @@ -5,11 +5,11 @@ import { Bullseye } from "./bullseye"; import { BLUE_COMMANDER, GAME_MASTER, NONE, RED_COMMANDER } from "../constants/constants"; import { refreshAll, setCommandModeOptions } from "../server/server"; import { Dropdown } from "../controls/dropdown"; -import { groundUnitDatabase } from "../unit/groundunitdatabase"; +import { groundUnitDatabase } from "../unit/databases/groundunitdatabase"; import { createCheckboxOption, getCheckboxOptions } from "../other/utils"; -import { aircraftDatabase } from "../unit/aircraftdatabase"; -import { helicopterDatabase } from "../unit/helicopterdatabase"; -import { navyUnitDatabase } from "../unit/navyunitdatabase"; +import { aircraftDatabase } from "../unit/databases/aircraftdatabase"; +import { helicopterDatabase } from "../unit/databases/helicopterdatabase"; +import { navyUnitDatabase } from "../unit/databases/navyunitdatabase"; export class MissionManager { #bullseyes: { [name: string]: Bullseye } = {}; diff --git a/client/src/other/utils.ts b/client/src/other/utils.ts index 7dba5e31..4d46d202 100644 --- a/client/src/other/utils.ts +++ b/client/src/other/utils.ts @@ -1,14 +1,14 @@ import { LatLng, Point, Polygon } from "leaflet"; import * as turf from "@turf/turf"; -import { UnitDatabase } from "../unit/unitdatabase"; -import { aircraftDatabase } from "../unit/aircraftdatabase"; -import { helicopterDatabase } from "../unit/helicopterdatabase"; -import { groundUnitDatabase } from "../unit/groundunitdatabase"; +import { UnitDatabase } from "../unit/databases/unitdatabase"; +import { aircraftDatabase } from "../unit/databases/aircraftdatabase"; +import { helicopterDatabase } from "../unit/databases/helicopterdatabase"; +import { groundUnitDatabase } from "../unit/databases/groundunitdatabase"; import { Buffer } from "buffer"; import { ROEs, emissionsCountermeasures, reactionsToThreat, states } from "../constants/constants"; import { Dropdown } from "../controls/dropdown"; import { UnitBlueprint } from "../@types/unitdatabase"; -import { navyUnitDatabase } from "../unit/navyunitdatabase"; +import { navyUnitDatabase } from "../unit/databases/navyunitdatabase"; export function bearing(lat1: number, lon1: number, lat2: number, lon2: number) { const φ1 = deg2rad(lat1); // φ, λ in radians diff --git a/client/src/panels/logpanel.ts b/client/src/panels/logpanel.ts index 4a03e19f..a0a38b7c 100644 --- a/client/src/panels/logpanel.ts +++ b/client/src/panels/logpanel.ts @@ -19,6 +19,7 @@ export class LogPanel extends Panel { this.#open = !this.#open; this.#queuedMessages = 0; this.#updateHeader(); + this.#calculateHeight(); if (this.#scrolledDown) this.#scrollDown(); @@ -89,6 +90,9 @@ export class LogPanel extends Panel { #calculateHeight() { const mouseInfoPanel = getMouseInfoPanel(); - this.getElement().style.height = `${mouseInfoPanel.getElement().offsetTop - this.getElement().offsetTop - 10}px`; + if (this.#open) + this.getElement().style.height = `${mouseInfoPanel.getElement().offsetTop - this.getElement().offsetTop - 10}px`; + else + this.getElement().style.height = "fit-content"; } } \ No newline at end of file diff --git a/client/src/panels/unitcontrolpanel.ts b/client/src/panels/unitcontrolpanel.ts index 631caa35..19694e7b 100644 --- a/client/src/panels/unitcontrolpanel.ts +++ b/client/src/panels/unitcontrolpanel.ts @@ -2,7 +2,7 @@ import { SVGInjector } from "@tanem/svg-injector"; import { getUnitsManager } from ".."; import { Dropdown } from "../controls/dropdown"; import { Slider } from "../controls/slider"; -import { aircraftDatabase } from "../unit/aircraftdatabase"; +import { aircraftDatabase } from "../unit/databases/aircraftdatabase"; import { Unit } from "../unit/unit"; import { Panel } from "./panel"; import { Switch } from "../controls/switch"; diff --git a/client/src/panels/unitinfopanel.ts b/client/src/panels/unitinfopanel.ts index b4367d75..99b8005b 100644 --- a/client/src/panels/unitinfopanel.ts +++ b/client/src/panels/unitinfopanel.ts @@ -1,5 +1,5 @@ import { Ammo } from "../@types/unit"; -import { aircraftDatabase } from "../unit/aircraftdatabase"; +import { aircraftDatabase } from "../unit/databases/aircraftdatabase"; import { Unit } from "../unit/unit"; import { Panel } from "./panel"; diff --git a/client/src/toolbars/commandmodetoolbar.ts b/client/src/toolbars/commandmodetoolbar.ts new file mode 100644 index 00000000..a551ee84 --- /dev/null +++ b/client/src/toolbars/commandmodetoolbar.ts @@ -0,0 +1,5 @@ +import { Toolbar } from "./toolbar"; + +export class CommandModeToolbar extends Toolbar { + // TODO move here all code about the command mode toolbar +} \ No newline at end of file diff --git a/client/src/toolbars/primarytoolbar.ts b/client/src/toolbars/primarytoolbar.ts new file mode 100644 index 00000000..241cbb38 --- /dev/null +++ b/client/src/toolbars/primarytoolbar.ts @@ -0,0 +1,13 @@ +import { Dropdown } from "../controls/dropdown"; +import { Toolbar } from "./toolbar"; + +export class PrimaryToolbar extends Toolbar { + constructor(ID: string) { + super(ID); + + // TODO move here all code about primary toolbar + + /* The content of the dropdown is entirely defined in the .ejs file */ + new Dropdown("app-icon", () => { }); + } +} \ No newline at end of file diff --git a/client/src/toolbars/toolbar.ts b/client/src/toolbars/toolbar.ts new file mode 100644 index 00000000..9ddd329e --- /dev/null +++ b/client/src/toolbars/toolbar.ts @@ -0,0 +1,38 @@ +export class Toolbar { + #element: HTMLElement + #visible: boolean = true; + + /** + * + * @param ID - the ID of the HTML element which will contain the context menu + */ + constructor(ID: string){ + this.#element = document.getElementById(ID) as HTMLElement; + } + + show() { + this.#element.classList.toggle("hide", false); + this.#visible = true; + } + + hide() { + this.#element.classList.toggle("hide", true); + this.#visible = false; + } + + toggle() { + // Simple way to track if currently visible + if (this.#visible) + this.hide(); + else + this.show(); + } + + getElement() { + return this.#element; + } + + getVisible(){ + return this.#visible; + } +} \ No newline at end of file diff --git a/client/src/unit/aircraftdatabase.ts b/client/src/unit/databases/aircraftdatabase.ts similarity index 91% rename from client/src/unit/aircraftdatabase.ts rename to client/src/unit/databases/aircraftdatabase.ts index d3f4e5cb..31d072cb 100644 --- a/client/src/unit/aircraftdatabase.ts +++ b/client/src/unit/databases/aircraftdatabase.ts @@ -1,5 +1,5 @@ -import { getMissionHandler } from ".."; -import { GAME_MASTER } from "../constants/constants"; +import { getMissionHandler } from "../.."; +import { GAME_MASTER } from "../../constants/constants"; import { UnitDatabase } from "./unitdatabase" export class AircraftDatabase extends UnitDatabase { diff --git a/client/src/unit/groundunitdatabase.ts b/client/src/unit/databases/groundunitdatabase.ts similarity index 91% rename from client/src/unit/groundunitdatabase.ts rename to client/src/unit/databases/groundunitdatabase.ts index 2a8d7c5f..e34c6313 100644 --- a/client/src/unit/groundunitdatabase.ts +++ b/client/src/unit/databases/groundunitdatabase.ts @@ -1,5 +1,5 @@ -import { getMissionHandler} from ".."; -import { GAME_MASTER } from "../constants/constants"; +import { getMissionHandler } from "../.."; +import { GAME_MASTER } from "../../constants/constants"; import { UnitDatabase } from "./unitdatabase" export class GroundUnitDatabase extends UnitDatabase { diff --git a/client/src/unit/helicopterdatabase.ts b/client/src/unit/databases/helicopterdatabase.ts similarity index 91% rename from client/src/unit/helicopterdatabase.ts rename to client/src/unit/databases/helicopterdatabase.ts index 031e9ad8..a39de1c8 100644 --- a/client/src/unit/helicopterdatabase.ts +++ b/client/src/unit/databases/helicopterdatabase.ts @@ -1,5 +1,5 @@ -import { getMissionHandler } from ".."; -import { GAME_MASTER } from "../constants/constants"; +import { getMissionHandler } from "../.."; +import { GAME_MASTER } from "../../constants/constants"; import { UnitDatabase } from "./unitdatabase" export class HelicopterDatabase extends UnitDatabase { diff --git a/client/src/unit/navyunitdatabase.ts b/client/src/unit/databases/navyunitdatabase.ts similarity index 91% rename from client/src/unit/navyunitdatabase.ts rename to client/src/unit/databases/navyunitdatabase.ts index 5f113943..c45f07c2 100644 --- a/client/src/unit/navyunitdatabase.ts +++ b/client/src/unit/databases/navyunitdatabase.ts @@ -1,5 +1,5 @@ -import { getMissionHandler } from ".."; -import { GAME_MASTER } from "../constants/constants"; +import { getMissionHandler } from "../.."; +import { GAME_MASTER } from "../../constants/constants"; import { UnitDatabase } from "./unitdatabase" export class NavyUnitDatabase extends UnitDatabase { diff --git a/client/src/unit/unitdatabase.ts b/client/src/unit/databases/unitdatabase.ts similarity index 97% rename from client/src/unit/unitdatabase.ts rename to client/src/unit/databases/unitdatabase.ts index 8e681559..5265e922 100644 --- a/client/src/unit/unitdatabase.ts +++ b/client/src/unit/databases/unitdatabase.ts @@ -1,7 +1,7 @@ import { LatLng } from "leaflet"; -import { getMissionHandler, getUnitsManager } from ".."; -import { GAME_MASTER } from "../constants/constants"; -import { UnitBlueprint } from "../@types/unitdatabase"; +import { getMissionHandler, getUnitsManager } from "../.."; +import { GAME_MASTER } from "../../constants/constants"; +import { UnitBlueprint } from "../../@types/unitdatabase"; export class UnitDatabase { blueprints: { [key: string]: UnitBlueprint } = {}; diff --git a/client/src/unit/unit.ts b/client/src/unit/unit.ts index b8626525..88a1f4e0 100644 --- a/client/src/unit/unit.ts +++ b/client/src/unit/unit.ts @@ -2,15 +2,15 @@ import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map, Point } fro import { getMap, getMissionHandler, getUnitsManager, getWeaponsManager } from '..'; import { enumToCoalition, enumToEmissioNCountermeasure, getMarkerCategoryByName, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg, bearing, deg2rad, ftToM } from '../other/utils'; import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions, followUnit, setEmissionsCountermeasures, setSpeedType, setAltitudeType, setOnOff, setFollowRoads, bombPoint, carpetBomb, bombBuilding, fireAtArea } from '../server/server'; -import { CustomMarker } from '../map/custommarker'; +import { CustomMarker } from '../map/markers/custommarker'; import { SVGInjector } from '@tanem/svg-injector'; -import { UnitDatabase } from './unitdatabase'; -import { TargetMarker } from '../map/targetmarker'; +import { UnitDatabase } from './databases/unitdatabase'; +import { TargetMarker } from '../map/markers/targetmarker'; import { DLINK, DataIndexes, GAME_MASTER, HIDE_GROUP_MEMBERS, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, SHOW_CONTACT_LINES, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants'; import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN, ObjectIconOptions, UnitData } from '../@types/unit'; import { DataExtractor } from '../server/dataextractor'; -import { groundUnitDatabase } from './groundunitdatabase'; -import { navyUnitDatabase } from './navyunitdatabase'; +import { groundUnitDatabase } from './databases/groundunitdatabase'; +import { navyUnitDatabase } from './databases/navyunitdatabase'; import { Weapon } from '../weapon/weapon'; import { LoadoutBlueprint } from '../@types/unitdatabase'; diff --git a/client/src/unit/unitsmanager.ts b/client/src/unit/unitsmanager.ts index 10d16abe..efdf78cc 100644 --- a/client/src/unit/unitsmanager.ts +++ b/client/src/unit/unitsmanager.ts @@ -3,16 +3,16 @@ import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler, getUnitsMana import { Unit } from "./unit"; import { cloneUnits, deleteUnit, spawnAircrafts, spawnGroundUnits, spawnHelicopters, spawnNavyUnits } from "../server/server"; import { bearingAndDistanceToLatLng, deg2rad, getUnitDatabaseByCategory, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polyContains, polygonArea, randomPointInPoly, randomUnitBlueprint } from "../other/utils"; -import { CoalitionArea } from "../map/coalitionarea"; -import { groundUnitDatabase } from "./groundunitdatabase"; +import { CoalitionArea } from "../map/coalitionarea/coalitionarea"; +import { groundUnitDatabase } from "./databases/groundunitdatabase"; import { DataIndexes, GAME_MASTER, IADSDensities, IDLE, MOVE_UNIT } from "../constants/constants"; import { DataExtractor } from "../server/dataextractor"; import { Contact, UnitData } from "../@types/unit"; import { citiesDatabase } from "./citiesDatabase"; -import { aircraftDatabase } from "./aircraftdatabase"; -import { helicopterDatabase } from "./helicopterdatabase"; -import { navyUnitDatabase } from "./navyunitdatabase"; -import { TemporaryUnitMarker } from "../map/temporaryunitmarker"; +import { aircraftDatabase } from "./databases/aircraftdatabase"; +import { helicopterDatabase } from "./databases/helicopterdatabase"; +import { navyUnitDatabase } from "./databases/navyunitdatabase"; +import { TemporaryUnitMarker } from "../map/markers/temporaryunitmarker"; /** The UnitsManager handles the creation, update, and control of units. Data is strictly updated by the server ONLY. This means that any interaction from the user will always and only * result in a command to the server, executed by means of a REST PUT request. Any subsequent change in data will be reflected only when the new data is sent back by the server. This strategy allows @@ -595,11 +595,7 @@ export class UnitsManager { var unit = selectedUnits[idx]; units.push({ ID: unit.ID, location: unit.getPosition() }); } - cloneUnits(units, true, 0 /* No spawn points, we delete the original units */, () => { - units.forEach((unit: any) => { - deleteUnit(unit.ID, false, false); - }); - }); + cloneUnits(units, true, 0 /* No spawn points, we delete the original units */); } /***********************************************/ diff --git a/client/src/weapon/weapon.ts b/client/src/weapon/weapon.ts index 1ee6b946..42dbcd85 100644 --- a/client/src/weapon/weapon.ts +++ b/client/src/weapon/weapon.ts @@ -1,7 +1,7 @@ import { LatLng, DivIcon, Map } from 'leaflet'; import { getMap, getMissionHandler, getUnitsManager } from '..'; import { enumToCoalition, mToFt, msToKnots, rad2deg } from '../other/utils'; -import { CustomMarker } from '../map/custommarker'; +import { CustomMarker } from '../map/markers/custommarker'; import { SVGInjector } from '@tanem/svg-injector'; import { DLINK, DataIndexes, GAME_MASTER, IRST, OPTIC, RADAR, VISUAL } from '../constants/constants'; import { ObjectIconOptions } from '../@types/unit'; diff --git a/client/views/index.ejs b/client/views/index.ejs index 7a2d64d9..2c08cc48 100644 --- a/client/views/index.ejs +++ b/client/views/index.ejs @@ -22,11 +22,6 @@ <%- include('toolbars/commandmode.ejs') %> - - <%- include('aic/aic.ejs') %> - <%- include('atc/atc.ejs') %> - <%- include('atc/unitdatatable.ejs') %> - <%- include('panels/unitcontrol.ejs') %> <%- include('panels/unitinfo.ejs') %>