First commit

This commit is contained in:
PeekabooSteam 2023-09-03 12:08:35 +01:00
parent a338e5fa26
commit 4d863bb894
22 changed files with 421 additions and 18 deletions

View File

@ -4,7 +4,7 @@ import { Unit } from "../unit/unit";
export class UnitDataTable extends Panel {
constructor(id: string) {
super(id);
super( id );
this.hide();
}

View File

@ -19,6 +19,7 @@ import { SVGInjector } from "@tanem/svg-injector";
import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "./constants/constants";
import { ServerStatusPanel } from "./panels/serverstatuspanel";
import { WeaponsManager } from "./weapon/weaponsmanager";
import { IndexApp } from "./indexapp";
var map: Map;
@ -90,6 +91,32 @@ function setup() {
/* Load the config file */
getConfig(readConfig);
/*
This is done like this for now as a way to make it work in the new and old world.
Over time/at some point, we'll need to start migrating the pre-existing code to an "app" format
*/
const indexApp = new IndexApp({
"featureSwitches": featureSwitches,
"map": map,
"missionHandler": missionHandler,
"panels": {
"connectionStatus": connectionStatusPanel,
"hotgroup": hotgroupPanel,
"infoPopup": infoPopup,
"log": logPanel,
"mouseInfo": mouseInfoPanel,
"serverStatus": serverStatusPanel,
"unitControl": unitControlPanel,
"unitInfo": unitInfoPanel
},
"unitDataTable": unitDataTable,
"unitsManager": unitsManager
});
indexApp.start();
}
function readConfig(config: any) {

78
client/src/indexapp.ts Normal file
View File

@ -0,0 +1,78 @@
import { FeatureSwitches } from "./features/featureswitches";
import { Map } from "./map/map";
import { MissionHandler } from "./mission/missionhandler";
import { IOlympusApp, OlympusApp } from "./olympusapp";
import { ConnectionStatusPanel } from "./panels/connectionstatuspanel";
import { HotgroupPanel } from "./panels/hotgrouppanel";
import { LogPanel } from "./panels/logpanel";
import { MouseInfoPanel } from "./panels/mouseinfopanel";
import { Panel } from "./panels/panel";
import { ServerStatusPanel } from "./panels/serverstatuspanel";
import { UnitControlPanel } from "./panels/unitcontrolpanel";
import { UnitInfoPanel } from "./panels/unitinfopanel";
import { PluginManager } from "./plugin/pluginmanager";
import { PluginHelloWorld } from "./plugins/helloworld/pluginhelloworld";
import { Popup } from "./popups/popup";
import { UnitsManager } from "./unit/unitsmanager";
export interface IIndexApp extends IOlympusApp {
"featureSwitches": FeatureSwitches,
"map": Map,
"missionHandler": MissionHandler,
"panels": IIndexAppPanels,
"unitsManager": UnitsManager
}
export interface IIndexAppPanels {
"connectionStatus": ConnectionStatusPanel,
"hotgroup": HotgroupPanel,
"infoPopup": Popup,
"log": LogPanel,
"mouseInfo": MouseInfoPanel,
"serverStatus": ServerStatusPanel,
"unitControl": UnitControlPanel,
"unitInfo": UnitInfoPanel
}
export class IndexApp extends OlympusApp {
#pluginManager!: PluginManager;
constructor( config:IIndexApp ) {
super( config );
// this.setMap( config.map );
// Panels
this.getPanelsManager().add( "connectionStatus", config.panels.connectionStatus );
this.getPanelsManager().add( "hotgroup", config.panels.hotgroup );
this.getPanelsManager().add( "log", config.panels.log );
this.getPanelsManager().add( "mouseInfo", config.panels.mouseInfo );
this.getPanelsManager().add( "serverStatus", config.panels.serverStatus );
this.getPanelsManager().add( "unitControl", config.panels.unitControl );
this.getPanelsManager().add( "unitInfo", config.panels.unitInfo );
// Popup
this.getPanelsManager().add( "unitPopup", config.panels.infoPopup );
// Retrofitting
Object.values( this.getPanelsManager().getAll() ).forEach( ( panel:Panel ) => {
panel.setOlympusApp( this );
});
// Plugins
this.#pluginManager = new PluginManager( this );
// Manual loading for now
this.#pluginManager.add( "helloWorld", new PluginHelloWorld( this ) );
}
start() {
super.start();
}
}

View File

@ -12,7 +12,7 @@ import { DestinationPreviewMarker } from "./destinationpreviewmarker";
import { TemporaryUnitMarker } from "./temporaryunitmarker";
import { ClickableMiniMap } from "./clickableminimap";
import { SVGInjector } from '@tanem/svg-injector'
import { layers as mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTootlips, MOVE_UNIT, SHOW_CONTACT_LINES, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, visibilityControlsTypes, SHOW_UNIT_LABELS } from "../constants/constants";
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 { CoalitionAreaContextMenu } from "../controls/coalitionareacontextmenu";
@ -20,7 +20,7 @@ import { DrawingCursor } from "./drawingcursor";
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
// TODO would be nice to convert to ts
// TODO would be nice to convert to ts - yes
require("../../public/javascripts/leaflet.nauticscale.js")
require("../../public/javascripts/L.Path.Drag.js")

67
client/src/olympusapp.ts Normal file
View File

@ -0,0 +1,67 @@
import { UnitDataTable } from "./atc/unitdatatable";
import { FeatureSwitches } from "./features/featureswitches";
import { Map } from "./map/map";
import { MissionHandler } from "./mission/missionhandler";
import { PanelsManager } from "./panels/panelsmanager";
import { UnitsManager } from "./unit/unitsmanager";
export interface IOlympusApp {
featureSwitches: FeatureSwitches;
missionHandler: MissionHandler;
unitDataTable: UnitDataTable;
unitsManager: UnitsManager;
}
export abstract class OlympusApp {
#featureSwitches: FeatureSwitches;
#map!: Map;
#missionHandler: MissionHandler;
#panelsManager: PanelsManager = new PanelsManager( this );
#unitDataTable: UnitDataTable;
#unitsManager: UnitsManager;
constructor( config:IOlympusApp ) {
this.#featureSwitches = config.featureSwitches;
this.#missionHandler = config.missionHandler;
this.#unitDataTable = config.unitDataTable;
this.#unitsManager = config.unitsManager;
}
getFeatureSwitches() {
return this.#featureSwitches;
}
getMap() {
return this.#map;
}
getPanelsManager() {
return this.#panelsManager;
}
getUnitDataTable() {
return this.#unitDataTable;
}
getUnitsManager() {
return this.#unitsManager;
}
getWeaponsManager() {
return this.getWeaponsManager;
}
setMap( map:Map ) {
this.#map = map;
}
start() {
// Start the app
}
}

View File

@ -0,0 +1,10 @@
import { OlympusApp } from "../olympusapp";
import { Manager } from "./manager";
export abstract class EventsManager extends Manager {
constructor( olympusApp:OlympusApp ) {
super( olympusApp );
}
}

View File

@ -0,0 +1,52 @@
import { OlympusApp } from "../olympusapp";
export interface IManager {
add:CallableFunction;
}
export abstract class Manager {
#items: {[key:string]: any } = {};
#olympusApp: OlympusApp;
constructor( olympusApp:OlympusApp ) {
this.#olympusApp = olympusApp;
}
add( name:string, item:any ) {
const regex = new RegExp( "^[a-z][a-z0-9]{2,}$", "i" );
if ( regex.test( name ) === false ) {
throw new Error( `Item name "${name}" does not match regex: ${regex.toString()}.` );
}
if ( this.#items.hasOwnProperty( name ) ) {
throw new Error( `Item with name "${name}" already exists.` );
}
this.#items[ name ] = item;
}
get( name:string ) {
if ( this.#items.hasOwnProperty( name ) ) {
return this.#items[ name ];
} else {
return false;
}
}
getAll() {
return this.#items;
}
getOlympusApp() {
return this.#olympusApp;
}
}

View File

@ -2,7 +2,7 @@ import { Panel } from "./panel";
export class ConnectionStatusPanel extends Panel {
constructor(ID: string) {
super(ID);
super( ID );
}
update(connected: boolean) {

View File

@ -4,7 +4,7 @@ import { Panel } from "./panel";
export class HotgroupPanel extends Panel {
constructor(ID: string) {
super(ID);
super( ID );
document.addEventListener("unitDeath", () => this.refreshHotgroups());
}

View File

@ -8,7 +8,7 @@ export class LogPanel extends Panel {
#logs: {[key: string]: string} = {};
constructor(ID: string) {
super(ID);
super( ID );
document.addEventListener("toggleLogPanel", () => {
this.getElement().classList.toggle("open");

View File

@ -13,7 +13,7 @@ export class MouseInfoPanel extends Panel {
#measureBox: HTMLElement;
constructor(ID: string) {
super(ID);
super( ID );
this.#measureIcon = new Icon({ iconUrl: 'resources/theme/images/icons/pin.svg', iconAnchor: [16, 32] });
this.#measureMarker = new Marker([0, 0], { icon: this.#measureIcon, interactive: false });

View File

@ -1,6 +1,11 @@
export class Panel {
import { OlympusApp } from "../olympusapp";
import { PanelEventsManager } from "./paneleventsmanager";
export abstract class Panel {
#element: HTMLElement
#visible: boolean = true;
#eventsManager!: PanelEventsManager;
#olympusApp!: OlympusApp;
constructor(ID: string) {
this.#element = <HTMLElement>document.getElementById(ID);
@ -8,17 +13,17 @@ export class Panel {
show() {
this.#element.classList.toggle("hide", false);
this.#visible = true;
this.getEventsManager()?.trigger( "show", {} );
}
hide() {
this.#element.classList.toggle("hide", true);
this.#visible = false;
this.getEventsManager()?.trigger( "hide", {} );
}
toggle() {
// Simple way to track if currently visible
if (this.#visible)
if (this.getVisible())
this.hide();
else
this.show();
@ -29,6 +34,20 @@ export class Panel {
}
getVisible(){
return this.#visible;
return (!this.getElement().classList.contains( "hide" ) );
}
getEventsManager() {
return this.#eventsManager;
}
getOlympusApp() {
return this.#olympusApp;
}
setOlympusApp( olympusApp:OlympusApp ) {
this.#olympusApp = olympusApp;
this.#eventsManager = new PanelEventsManager( this.getOlympusApp() );
}
}

View File

@ -0,0 +1,50 @@
import { OlympusApp } from "../olympusapp";
import { EventsManager } from "../other/eventsmanager";
interface IListener {
callback: CallableFunction;
name?: string
}
export class PanelEventsManager extends EventsManager {
constructor( olympusApp:OlympusApp ) {
super( olympusApp );
this.add( "hide", [] );
this.add( "show", [] );
}
on( eventName:string, listener:IListener ) {
const event = this.get( eventName );
if ( !event ) {
throw new Error( `Event name "${eventName}" is not valid.` );
}
this.get( eventName ).push({
"callback": listener.callback
});
}
trigger( eventName:string, contextData:object ) {
const listeners = this.get( eventName );
if ( listeners ) {
listeners.forEach( ( listener:IListener ) => {
listener.callback( contextData );
});
}
}
}

View File

@ -0,0 +1,17 @@
import { OlympusApp } from "../olympusapp";
import { Manager } from "../other/manager";
import { Panel } from "./panel";
export class PanelsManager extends Manager {
#panels: { [key:string]: Panel } = {}
constructor( olympusApp:OlympusApp ) {
super( olympusApp );
}
get( name:string ): Panel {
return super.get( name );
}
}

View File

@ -2,7 +2,7 @@ import { Panel } from "./panel";
export class ServerStatusPanel extends Panel {
constructor(ID: string) {
super(ID);
super( ID );
}
update(frameRate: number, load: number) {

View File

@ -26,7 +26,7 @@ export class UnitControlPanel extends Panel {
#selectedUnitsTypes: string[] = [];
constructor(ID: string) {
super(ID);
super( ID );
/* Unit control sliders */
this.#altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => { getUnitsManager().selectedUnitsSetAltitude(ftToM(value)); });

View File

@ -1,6 +1,4 @@
import { getUnitsManager } from "..";
import { Ammo } from "../@types/unit";
import { ConvertDDToDMS, rad2deg } from "../other/utils";
import { aircraftDatabase } from "../unit/aircraftdatabase";
import { Unit } from "../unit/unit";
import { Panel } from "./panel";
@ -23,7 +21,7 @@ export class UnitInfoPanel extends Panel {
#unitName: HTMLElement;
constructor(ID: string) {
super(ID);
super( ID );
this.#altitude = (this.getElement().querySelector("#altitude")) as HTMLElement;
this.#currentTask = (this.getElement().querySelector("#current-task")) as HTMLElement;

View File

@ -0,0 +1,32 @@
import { OlympusApp } from "../olympusapp";
export interface PluginInterface {
}
export abstract class Plugin {
#olympusApp!:OlympusApp;
protected name = "";
constructor( olympusApp:OlympusApp, pluginName:string ) {
const regex = "^[a-zA-Z][a-zA-Z\d]{4,}"
if ( new RegExp( regex ).test( pluginName ) === false ) {
throw new Error( `Plugin names must match regex: ${regex}` );
}
this.name = pluginName;
this.#olympusApp = olympusApp;
}
getName() {
return this.name;
}
getOlympusApp() {
return this.#olympusApp;
}
}

View File

@ -0,0 +1,13 @@
import { OlympusApp } from "../olympusapp";
import { Manager } from "../other/manager";
export class PluginManager extends Manager {
constructor( olympusApp:OlympusApp ) {
super( olympusApp );
}
}

View File

@ -0,0 +1,6 @@
{
"author": "J. R. Hartley",
"exportedClassName": "PluginHelloWorld",
"name": "Hello World",
"version": "1.2.3"
}

View File

@ -0,0 +1,29 @@
import { OlympusApp } from "../../olympusapp";
import { Plugin, PluginInterface } from "../../plugin/plugin";
export class PluginHelloWorld extends Plugin implements PluginInterface {
constructor( olympusApp:OlympusApp ) {
super( olympusApp, "HelloWorld" );
const panel = this.getOlympusApp().getPanelsManager().get( "unitControl" );
const em = panel.getEventsManager();
em.on( "show", {
"callback": () => {
console.log( "Showing unit control panel" );
}
});
em.on( "hide", {
"callback": () => {
console.log( "Hiding unit control panel" );
}
});
//const tpl = new ejs
}
}

View File

@ -1,6 +1,11 @@
import { Panel } from "../panels/panel";
export class Popup extends Panel {
constructor( elementId:string ) {
super( elementId );
}
#fadeTime: number = 2000; // Milliseconds
#hideTimer: number | undefined = undefined;
#visibilityTimer: number | undefined = undefined;