diff --git a/.gitignore b/.gitignore index 12d0171d..f58d9ee8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ Output node_modules /client/TODO.txt /client/public/javascripts/bundle.js -!client/bin \ No newline at end of file +!client/bin +/client/public/plugins diff --git a/client/plugins/controltips/index.js b/client/plugins/controltips/index.js index 90ee53e6..37ad45af 100644 --- a/client/plugins/controltips/index.js +++ b/client/plugins/controltips/index.js @@ -11,68 +11,72 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function ( if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; -var _ControlTips_instances, _ControlTips_element, _ControlTips_app, _ControlTips_shortcutManager, _ControlTips_cursorIsHoveringOverUnit, _ControlTips_cursorIsHoveringOverAirbase, _ControlTips_updateTips; +var _ControlTipsPlugin_instances, _ControlTipsPlugin_element, _ControlTipsPlugin_app, _ControlTipsPlugin_shortcutManager, _ControlTipsPlugin_cursorIsHoveringOverUnit, _ControlTipsPlugin_cursorIsHoveringOverAirbase, _ControlTipsPlugin_updateTips; Object.defineProperty(exports, "__esModule", { value: true }); -exports.ControlTips = void 0; -class ControlTips { +exports.ControlTipsPlugin = void 0; +const SHOW_CONTROL_TIPS = "Show control tips"; +class ControlTipsPlugin { constructor() { - _ControlTips_instances.add(this); - _ControlTips_element.set(this, void 0); - _ControlTips_app.set(this, void 0); - _ControlTips_shortcutManager.set(this, void 0); - _ControlTips_cursorIsHoveringOverUnit.set(this, false); - _ControlTips_cursorIsHoveringOverAirbase.set(this, false); - __classPrivateFieldSet(this, _ControlTips_element, document.createElement("div"), "f"); - __classPrivateFieldGet(this, _ControlTips_element, "f").id = "control-tips-panel"; - document.body.appendChild(__classPrivateFieldGet(this, _ControlTips_element, "f")); - console.log("HELLO"); + _ControlTipsPlugin_instances.add(this); + _ControlTipsPlugin_element.set(this, void 0); + _ControlTipsPlugin_app.set(this, void 0); + _ControlTipsPlugin_shortcutManager.set(this, void 0); + _ControlTipsPlugin_cursorIsHoveringOverUnit.set(this, false); + _ControlTipsPlugin_cursorIsHoveringOverAirbase.set(this, false); + __classPrivateFieldSet(this, _ControlTipsPlugin_element, document.createElement("div"), "f"); + __classPrivateFieldGet(this, _ControlTipsPlugin_element, "f").id = "control-tips-panel"; + document.body.appendChild(__classPrivateFieldGet(this, _ControlTipsPlugin_element, "f")); } getName() { return "Control Tips Plugin"; } initialize(app) { - __classPrivateFieldSet(this, _ControlTips_app, app, "f"); - __classPrivateFieldSet(this, _ControlTips_shortcutManager, __classPrivateFieldGet(this, _ControlTips_app, "f").getShortcutManager(), "f"); - __classPrivateFieldGet(this, _ControlTips_shortcutManager, "f").onKeyDown(() => { - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); + __classPrivateFieldSet(this, _ControlTipsPlugin_app, app, "f"); + __classPrivateFieldSet(this, _ControlTipsPlugin_shortcutManager, __classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getShortcutManager(), "f"); + __classPrivateFieldGet(this, _ControlTipsPlugin_shortcutManager, "f").onKeyDown(() => { + __classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this); }); - __classPrivateFieldGet(this, _ControlTips_shortcutManager, "f").onKeyUp(() => { - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); + __classPrivateFieldGet(this, _ControlTipsPlugin_shortcutManager, "f").onKeyUp(() => { + __classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this); }); document.addEventListener("airbaseMouseover", (ev) => { - __classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverAirbase, true, "f"); - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); + __classPrivateFieldSet(this, _ControlTipsPlugin_cursorIsHoveringOverAirbase, true, "f"); + __classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this); }); document.addEventListener("airbaseMouseout", (ev) => { - __classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverAirbase, false, "f"); - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); + __classPrivateFieldSet(this, _ControlTipsPlugin_cursorIsHoveringOverAirbase, false, "f"); + __classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this); + }); + document.addEventListener("unitDeselection", (ev) => { + __classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this); }); - //document.addEventListener("unitDeselection", (ev: CustomEvent) => { - // this.#updateTips(); - //}); document.addEventListener("unitMouseover", (ev) => { - __classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverUnit, true, "f"); - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); + __classPrivateFieldSet(this, _ControlTipsPlugin_cursorIsHoveringOverUnit, true, "f"); + __classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this); }); document.addEventListener("unitMouseout", (ev) => { - __classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverUnit, false, "f"); - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); + __classPrivateFieldSet(this, _ControlTipsPlugin_cursorIsHoveringOverUnit, false, "f"); + __classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this); }); - //document.addEventListener("unitSelection", (ev: CustomEvent) => { - // this.#updateTips() - //}); - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); + document.addEventListener("unitSelection", (ev) => { + __classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this); + }); + document.addEventListener("mapVisibilityOptionsChanged", () => { + this.toggle(!__classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getMap().getVisibilityOptions()[SHOW_CONTROL_TIPS]); + }); + __classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this); + __classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getMap().addVisibilityOption(SHOW_CONTROL_TIPS, true); return true; } getElement() { - return __classPrivateFieldGet(this, _ControlTips_element, "f"); + return __classPrivateFieldGet(this, _ControlTipsPlugin_element, "f"); } toggle(bool) { this.getElement().classList.toggle("hide", bool); } } -exports.ControlTips = ControlTips; -_ControlTips_element = new WeakMap(), _ControlTips_app = new WeakMap(), _ControlTips_shortcutManager = new WeakMap(), _ControlTips_cursorIsHoveringOverUnit = new WeakMap(), _ControlTips_cursorIsHoveringOverAirbase = new WeakMap(), _ControlTips_instances = new WeakSet(), _ControlTips_updateTips = function _ControlTips_updateTips() { +exports.ControlTipsPlugin = ControlTipsPlugin; +_ControlTipsPlugin_element = new WeakMap(), _ControlTipsPlugin_app = new WeakMap(), _ControlTipsPlugin_shortcutManager = new WeakMap(), _ControlTipsPlugin_cursorIsHoveringOverUnit = new WeakMap(), _ControlTipsPlugin_cursorIsHoveringOverAirbase = new WeakMap(), _ControlTipsPlugin_instances = new WeakSet(), _ControlTipsPlugin_updateTips = function _ControlTipsPlugin_updateTips() { const combos = [ { "keys": [], @@ -205,13 +209,13 @@ _ControlTips_element = new WeakMap(), _ControlTips_app = new WeakMap(), _Control ] } ]; - const currentCombo = combos.find((combo) => __classPrivateFieldGet(this, _ControlTips_shortcutManager, "f").keyComboMatches(combo.keys)) || combos[0]; + const currentCombo = combos.find((combo) => __classPrivateFieldGet(this, _ControlTipsPlugin_shortcutManager, "f").keyComboMatches(combo.keys)) || combos[0]; const element = this.getElement(); element.innerHTML = ""; let numSelectedUnits = 0; let unitSelectionContainsControlled = false; - if (__classPrivateFieldGet(this, _ControlTips_app, "f").getUnitsManager()) { - let selectedUnits = Object.values(__classPrivateFieldGet(this, _ControlTips_app, "f").getUnitsManager().getSelectedUnits()); + if (__classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getUnitsManager()) { + let selectedUnits = Object.values(__classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getUnitsManager().getSelectedUnits()); numSelectedUnits = selectedUnits.length; unitSelectionContainsControlled = selectedUnits.some((unit) => unit.getControlled()); } @@ -228,12 +232,12 @@ _ControlTips_element = new WeakMap(), _ControlTips_app = new WeakMap(), _Control return; } if (typeof tip.showIfHoveringOverAirbase === "boolean") { - if (tip.showIfHoveringOverAirbase !== __classPrivateFieldGet(this, _ControlTips_cursorIsHoveringOverAirbase, "f")) { + if (tip.showIfHoveringOverAirbase !== __classPrivateFieldGet(this, _ControlTipsPlugin_cursorIsHoveringOverAirbase, "f")) { return; } } if (typeof tip.showIfHoveringOverUnit === "boolean") { - if (tip.showIfHoveringOverUnit !== __classPrivateFieldGet(this, _ControlTips_cursorIsHoveringOverUnit, "f")) { + if (tip.showIfHoveringOverUnit !== __classPrivateFieldGet(this, _ControlTipsPlugin_cursorIsHoveringOverUnit, "f")) { return; } } @@ -246,7 +250,7 @@ _ControlTips_element = new WeakMap(), _ControlTips_app = new WeakMap(), _Control Object.defineProperty(exports, "__esModule", { value: true }); const controltips_1 = require("./controltips"); globalThis.getOlympusPlugin = () => { - return new controltips_1.ControlTips(); + return new controltips_1.ControlTipsPlugin(); }; },{"./controltips":1}]},{},[2]); diff --git a/client/plugins/controltips/src/controltips.ts b/client/plugins/controltips/src/controltipsplugin.ts similarity index 94% rename from client/plugins/controltips/src/controltips.ts rename to client/plugins/controltips/src/controltipsplugin.ts index 9e885247..2eeba8ff 100644 --- a/client/plugins/controltips/src/controltips.ts +++ b/client/plugins/controltips/src/controltipsplugin.ts @@ -1,3 +1,5 @@ +const SHOW_CONTROL_TIPS = "Show control tips" + export class ControlTipsPlugin implements OlympusPlugin { #element: HTMLElement; #app: any; @@ -9,8 +11,6 @@ export class ControlTipsPlugin implements OlympusPlugin { this.#element = document.createElement("div"); this.#element.id = "control-tips-panel"; document.body.appendChild(this.#element); - - console.log("HELLO") } getName() { @@ -40,9 +40,9 @@ export class ControlTipsPlugin implements OlympusPlugin { this.#updateTips(); }); - //document.addEventListener("unitDeselection", (ev: CustomEvent) => { - // this.#updateTips(); - //}); + document.addEventListener("unitDeselection", (ev: CustomEvent) => { + this.#updateTips(); + }); document.addEventListener("unitMouseover", (ev: CustomEventInit) => { this.#cursorIsHoveringOverUnit = true; @@ -54,12 +54,18 @@ export class ControlTipsPlugin implements OlympusPlugin { this.#updateTips(); }); - //document.addEventListener("unitSelection", (ev: CustomEvent) => { - // this.#updateTips() - //}); + document.addEventListener("unitSelection", (ev: CustomEvent) => { + this.#updateTips() + }); + + document.addEventListener("mapVisibilityOptionsChanged", () => { + this.toggle( !this.#app.getMap().getVisibilityOptions()[SHOW_CONTROL_TIPS] ); + }); this.#updateTips(); + this.#app.getMap().addVisibilityOption(SHOW_CONTROL_TIPS, true); + return true; } @@ -72,7 +78,6 @@ export class ControlTipsPlugin implements OlympusPlugin { } #updateTips() { - const combos: Array = [ { "keys": [], @@ -249,7 +254,6 @@ export class ControlTipsPlugin implements OlympusPlugin { } element.innerHTML += `
${tip.key}${tip.action}
` - }); } } \ No newline at end of file diff --git a/client/plugins/controltips/src/index.ts b/client/plugins/controltips/src/index.ts index d6372eb6..425907e0 100644 --- a/client/plugins/controltips/src/index.ts +++ b/client/plugins/controltips/src/index.ts @@ -1,4 +1,4 @@ -import { ControlTipsPlugin } from "./controltips"; +import { ControlTipsPlugin } from "./controltipsplugin"; globalThis.getOlympusPlugin = () => { return new ControlTipsPlugin(); diff --git a/client/public/plugins/controltipsplugin/index.js b/client/public/plugins/controltipsplugin/index.js deleted file mode 100644 index 90ee53e6..00000000 --- a/client/public/plugins/controltipsplugin/index.js +++ /dev/null @@ -1,252 +0,0 @@ -(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i { - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); - }); - __classPrivateFieldGet(this, _ControlTips_shortcutManager, "f").onKeyUp(() => { - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); - }); - document.addEventListener("airbaseMouseover", (ev) => { - __classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverAirbase, true, "f"); - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); - }); - document.addEventListener("airbaseMouseout", (ev) => { - __classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverAirbase, false, "f"); - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); - }); - //document.addEventListener("unitDeselection", (ev: CustomEvent) => { - // this.#updateTips(); - //}); - document.addEventListener("unitMouseover", (ev) => { - __classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverUnit, true, "f"); - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); - }); - document.addEventListener("unitMouseout", (ev) => { - __classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverUnit, false, "f"); - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); - }); - //document.addEventListener("unitSelection", (ev: CustomEvent) => { - // this.#updateTips() - //}); - __classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this); - return true; - } - getElement() { - return __classPrivateFieldGet(this, _ControlTips_element, "f"); - } - toggle(bool) { - this.getElement().classList.toggle("hide", bool); - } -} -exports.ControlTips = ControlTips; -_ControlTips_element = new WeakMap(), _ControlTips_app = new WeakMap(), _ControlTips_shortcutManager = new WeakMap(), _ControlTips_cursorIsHoveringOverUnit = new WeakMap(), _ControlTips_cursorIsHoveringOverAirbase = new WeakMap(), _ControlTips_instances = new WeakSet(), _ControlTips_updateTips = function _ControlTips_updateTips() { - const combos = [ - { - "keys": [], - "tips": [ - { - "key": `SHIFT`, - "action": `Box select`, - "showIfHoveringOverAirbase": false, - "showIfHoveringOverUnit": false, - "showIfUnitSelected": false - }, - { - "key": `Mouse1`, - "action": `Deselect`, - "showIfUnitSelected": true - }, - { - "key": `Mouse1+drag`, - "action": `Move map`, - "showIfHoveringOverAirbase": false, - "showIfHoveringOverUnit": false, - "showIfUnitSelected": false - }, - { - "key": `Mouse2`, - "action": `Spawn menu`, - "showIfUnitSelected": false, - "showIfHoveringOverAirbase": false, - "showIfHoveringOverUnit": false - }, - { - "key": `Mouse2`, - "action": `Quick options`, - "showIfUnitSelected": false, - "showIfHoveringOverAirbase": false, - "showIfHoveringOverUnit": true - }, - { - "key": `Mouse2`, - "action": `Airbase menu`, - "showIfUnitSelected": false, - "showIfHoveringOverAirbase": true, - "showIfHoveringOverUnit": false - }, - { - "key": `Mouse2`, - "action": `Set first waypoint`, - "showIfHoveringOverAirbase": false, - "showIfUnitSelected": true, - "unitsMustBeControlled": true - }, - { - "key": "CTRL+Mouse2", - "action": "Add waypoint", - "showIfUnitSelected": true, - "showIfHoveringOverAirbase": false, - "unitsMustBeControlled": true - }, - { - "key": `Mouse2 (hold)`, - "action": `Point operations`, - "showIfUnitSelected": true, - "showIfHoveringOverAirbase": false, - "showIfHoveringOverUnit": false, - "unitsMustBeControlled": true - }, - { - "key": "CTRL", - "action": " Pin tool", - "showIfUnitSelected": false, - "showIfHoveringOverAirbase": false, - "showIfHoveringOverUnit": false, - "unitsMustBeControlled": true - }, - { - "key": "CTRL+Mouse2", - "action": " Airbase menu", - "showIfUnitSelected": true, - "showIfHoveringOverAirbase": true, - "unitsMustBeControlled": true - }, - { - "key": `Delete`, - "action": `Delete unit`, - "showIfHoveringOverAirbase": false, - "showIfUnitSelected": true - } - ] - }, - { - "keys": ["ControlLeft"], - "tips": [ - { - "key": `Mouse1`, - "action": "Toggle pin", - "showIfUnitSelected": false, - "showIfHoveringOverAirbase": false, - "showIfHoveringOverUnit": false - }, - { - "key": `Mouse1`, - "action": "Toggle selection", - "showIfUnitSelected": true, - "showIfHoveringOverAirbase": false, - "showIfHoveringOverUnit": true - }, - { - "key": `Mouse2`, - "action": `Add waypoint`, - "showIfHoveringOverAirbase": false, - "showIfUnitSelected": true, - "unitsMustBeControlled": true - }, - { - "key": `Mouse2`, - "action": `Airbase menu`, - "showIfHoveringOverAirbase": true, - "showIfUnitSelected": true, - "unitsMustBeControlled": true - } - ] - }, - { - "keys": ["ShiftLeft"], - "tips": [ - { - "key": `mouse1+drag`, - "action": "Box select" - } - ] - } - ]; - const currentCombo = combos.find((combo) => __classPrivateFieldGet(this, _ControlTips_shortcutManager, "f").keyComboMatches(combo.keys)) || combos[0]; - const element = this.getElement(); - element.innerHTML = ""; - let numSelectedUnits = 0; - let unitSelectionContainsControlled = false; - if (__classPrivateFieldGet(this, _ControlTips_app, "f").getUnitsManager()) { - let selectedUnits = Object.values(__classPrivateFieldGet(this, _ControlTips_app, "f").getUnitsManager().getSelectedUnits()); - numSelectedUnits = selectedUnits.length; - unitSelectionContainsControlled = selectedUnits.some((unit) => unit.getControlled()); - } - currentCombo.tips.forEach((tip) => { - if (numSelectedUnits > 0) { - if (tip.showIfUnitSelected === false) { - return; - } - if (tip.unitsMustBeControlled === true && unitSelectionContainsControlled === false) { - return; - } - } - if (numSelectedUnits === 0 && tip.showIfUnitSelected === true) { - return; - } - if (typeof tip.showIfHoveringOverAirbase === "boolean") { - if (tip.showIfHoveringOverAirbase !== __classPrivateFieldGet(this, _ControlTips_cursorIsHoveringOverAirbase, "f")) { - return; - } - } - if (typeof tip.showIfHoveringOverUnit === "boolean") { - if (tip.showIfHoveringOverUnit !== __classPrivateFieldGet(this, _ControlTips_cursorIsHoveringOverUnit, "f")) { - return; - } - } - element.innerHTML += `
${tip.key}${tip.action}
`; - }); -}; - -},{}],2:[function(require,module,exports){ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const controltips_1 = require("./controltips"); -globalThis.getOlympusPlugin = () => { - return new controltips_1.ControlTips(); -}; - -},{"./controltips":1}]},{},[2]); diff --git a/client/public/plugins/controltipsplugin/plugin.json b/client/public/plugins/controltipsplugin/plugin.json deleted file mode 100644 index 77a1f817..00000000 --- a/client/public/plugins/controltipsplugin/plugin.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Control Tip Plugin", - "version": "0.0.1", - "description": "This plugin shows useful control tips on the right side of the screen. The tips change dynamically depending on what the user does", - "author": "Peekaboo" -} \ No newline at end of file diff --git a/client/public/plugins/controltipsplugin/style.css b/client/public/plugins/controltipsplugin/style.css deleted file mode 100644 index 0ab48856..00000000 --- a/client/public/plugins/controltipsplugin/style.css +++ /dev/null @@ -1,33 +0,0 @@ -#control-tips-panel { - align-self: center; - display: flex; - flex-flow: column wrap; - font-size: 13px; - justify-self: flex-end; - position: absolute; - right: 10px; - row-gap: 20px; - text-align: right; - z-index: 999; -} - -#control-tips-panel>* { - align-items: center; - align-self: end; - background-color: var(--background-steel); - border-radius: var(--border-radius-md); - color: white; - column-gap: 8px; - display: flex; - justify-items: right; - opacity: .9; - padding: 5px; - width: fit-content; -} - -#control-tips-panel>*>.key { - background-color: var(--background-grey); - border-radius: var(--border-radius-sm); - color: white; - padding: 1px 4px; -} \ No newline at end of file diff --git a/client/public/stylesheets/olympus.css b/client/public/stylesheets/olympus.css index eab3e9ba..7e51ec7a 100644 --- a/client/public/stylesheets/olympus.css +++ b/client/public/stylesheets/olympus.css @@ -1,7 +1,6 @@ @import url("layout/layout.css"); @import url("style/style.css"); -@import url("other/controltips.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 08b8350d..b4866edd 100644 --- a/client/public/stylesheets/style/style.css +++ b/client/public/stylesheets/style/style.css @@ -653,17 +653,6 @@ nav.ol-panel> :last-child { stroke: var(--background-steel) !important; } -#atc-navbar-control { - align-items: center; - display: flex; - flex-direction: column; -} - -#atc-navbar-control button svg { - height: 24px; - width: 24px; -} - #roe-buttons-container button, #reaction-to-threat-buttons-container button, #emissions-countermeasures-buttons-container button { diff --git a/client/src/app.ts b/client/src/app.ts index 3010a884..0f7c611f 100644 --- a/client/src/app.ts +++ b/client/src/app.ts @@ -17,6 +17,9 @@ import { WeaponsManager } from "./weapon/weaponsmanager"; import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "./constants/constants"; import { Manager } from "./other/manager"; +import { ShortcutKeyboard } from "./shortcut/shortcut"; +import { getPaused, setCredentials, setPaused, startUpdate, toggleDemoEnabled } from "./server/server"; +import { SVGInjector } from "@tanem/svg-injector"; export class OlympusApp { /* Global data */ @@ -36,11 +39,11 @@ export class OlympusApp { #shortcutManager: ShortcutManager | null = null; /* UI Toolbars */ - #primaryToolbar: PrimaryToolbar| null = null; - #commandModeToolbar: CommandModeToolbar| null = null; + #primaryToolbar: PrimaryToolbar | null = null; + #commandModeToolbar: CommandModeToolbar | null = null; constructor() { - + } getMap() { @@ -122,7 +125,7 @@ export class OlympusApp { this.#unitsManager = new UnitsManager(); this.#weaponsManager = new WeaponsManager(); this.#missionManager = new MissionManager(); - + this.#shortcutManager = new ShortcutManager(); this.#panelsManager = new Manager(); @@ -136,16 +139,130 @@ export class OlympusApp { .add("mouseInfo", new MouseInfoPanel("mouse-info-panel")) .add("log", new LogPanel("log-panel")) .add("serverStatus", new ServerStatusPanel("server-status-panel")) - .add("unitControl", new UnitControlPanel("unit-control-panel")) + .add("unitControl", new UnitControlPanel("unit-control-panel")) .add("unitInfo", new UnitInfoPanel("unit-info-panel")) - + // Popups this.getPopupsManager().add("infoPopup", new Popup("info-popup")); // Toolbars this.getToolbarsManager().add("primaryToolbar", new PrimaryToolbar("primary-toolbar")) - .add("commandModeToolbar", new PrimaryToolbar("command-mode-toolbar")); + .add("commandModeToolbar", new PrimaryToolbar("command-mode-toolbar")); this.#pluginsManager = new PluginsManager(); + + this.#setupEvents(); + } + + #setupEvents() { + /* Generic clicks */ + document.addEventListener("click", (ev) => { + if (ev instanceof MouseEvent && ev.target instanceof HTMLElement) { + const target = ev.target; + + if (target.classList.contains("olympus-dialog-close")) { + target.closest("div.olympus-dialog")?.classList.add("hide"); + } + + const triggerElement = target.closest("[data-on-click]"); + + if (triggerElement instanceof HTMLElement) { + const eventName: string = triggerElement.dataset.onClick || ""; + let params = JSON.parse(triggerElement.dataset.onClickParams || "{}"); + params._element = triggerElement; + + if (eventName) { + document.dispatchEvent(new CustomEvent(eventName, { + detail: params + })); + } + } + } + }); + + const shortcutManager = this.getShortcutManager(); + shortcutManager.add("toggleDemo", new ShortcutKeyboard({ + "callback": () => { + toggleDemoEnabled(); + }, + "code": "KeyT" + })).add("togglePause", new ShortcutKeyboard({ + "altKey": false, + "callback": () => { + setPaused(!getPaused()); + }, + "code": "Space", + "ctrlKey": false + })); + + ["KeyW", "KeyA", "KeyS", "KeyD", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].forEach(code => { + shortcutManager.add(`pan${code}keydown`, new ShortcutKeyboard({ + "altKey": false, + "callback": (ev: KeyboardEvent) => { + this.getMap().handleMapPanning(ev); + }, + "code": code, + "ctrlKey": false, + "event": "keydown" + })); + }); + + ["KeyW", "KeyA", "KeyS", "KeyD", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].forEach(code => { + shortcutManager.add(`pan${code}keyup`, new ShortcutKeyboard({ + "callback": (ev: KeyboardEvent) => { + this.getMap().handleMapPanning(ev); + }, + "code": code + })); + }); + + ["Digit1", "Digit2", "Digit3", "Digit4", "Digit5", "Digit6", "Digit7", "Digit8", "Digit9"].forEach(code => { + shortcutManager.add(`hotgroup${code}`, new ShortcutKeyboard({ + "callback": (ev: KeyboardEvent) => { + if (ev.ctrlKey && ev.shiftKey) + this.getUnitsManager().selectedUnitsAddToHotgroup(parseInt(ev.code.substring(5))); + else if (ev.ctrlKey && !ev.shiftKey) + this.getUnitsManager().selectedUnitsSetHotgroup(parseInt(ev.code.substring(5))); + else + this.getUnitsManager().selectUnitsByHotgroup(parseInt(ev.code.substring(5))); + }, + "code": code + })); + }); + + // TODO: move from here in dedicated class + document.addEventListener("closeDialog", (ev: CustomEventInit) => { + ev.detail._element.closest(".ol-dialog").classList.add("hide"); + }); + + /* Try and connect with the Olympus REST server */ + document.addEventListener("tryConnection", () => { + const form = document.querySelector("#splash-content")?.querySelector("#authentication-form"); + const username = (form?.querySelector("#username") as HTMLInputElement).value; + const password = (form?.querySelector("#password") as HTMLInputElement).value; + + /* Update the user credentials */ + setCredentials(username, password); + + /* Start periodically requesting updates */ + startUpdate(); + + this.setLoginStatus("connecting"); + }) + + /* Reload the page, used to mimic a restart of the app */ + document.addEventListener("reloadPage", () => { + location.reload(); + }) + + /* Inject the svgs with the corresponding svg code. This allows to dynamically manipulate the svg, like changing colors */ + document.querySelectorAll("[inject-svg]").forEach((el: Element) => { + var img = el as HTMLImageElement; + var isLoaded = img.complete; + if (isLoaded) + SVGInjector(img); + else + img.addEventListener("load", () => { SVGInjector(img); }); + }) } } \ No newline at end of file diff --git a/client/src/constants/constants.ts b/client/src/constants/constants.ts index 20bbbeeb..608dcd73 100644 --- a/client/src/constants/constants.ts +++ b/client/src/constants/constants.ts @@ -153,7 +153,6 @@ export const IADSDensities: {[key: string]: number}= {"AAA": 0.8, "MANPADS": 0.3 export const SHOW_CONTACT_LINES = "Show unit contact lines"; export const HIDE_GROUP_MEMBERS = "Hide group members when zoomed out"; -export const SHOW_CONTROL_TIPS = "Show control tips"; export const SHOW_UNIT_LABELS = "Show unit labels"; export const SHOW_UNIT_PATHS = "Show unit paths"; export const SHOW_UNIT_TARGETS = "Show unit targets"; diff --git a/client/src/controls/dropdown.ts b/client/src/controls/dropdown.ts index f203691f..c7823856 100644 --- a/client/src/controls/dropdown.ts +++ b/client/src/controls/dropdown.ts @@ -68,6 +68,10 @@ export class Dropdown { return this.#options.children; } + addOptionElement(optionElement: HTMLElement) { + this.#options.appendChild(optionElement); + } + selectText(text: string) { const index = [].slice.call(this.#options.children).findIndex((opt: Element) => opt.querySelector("button")?.innerText === text); if (index > -1) { diff --git a/client/src/index.ts b/client/src/index.ts index 23136d15..8d7d5cba 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -1,156 +1,30 @@ -import { getConfig, getPaused, setAddress, setCredentials, setPaused, startUpdate, toggleDemoEnabled } from "./server/server"; -import { SVGInjector } from "@tanem/svg-injector"; +import { getConfig, setAddress } from "./server/server"; import { OlympusApp } from "./app"; -import { ShortcutKeyboard } from "./shortcut/shortcut"; var app: OlympusApp; function setup() { /* Load the config file from the app server*/ - getConfig((config: ConfigurationOptions) => readConfig(config)); + getConfig((config: ConfigurationOptions) => { + if (config && config.address != undefined && config.port != undefined) { + const address = config.address; + const port = config.port; + if (typeof address === 'string' && typeof port == 'number') { + setAddress(address == "*" ? window.location.hostname : address, port); - app = new OlympusApp(); - app.start(); - - /* Setup event handlers */ - setupEvents(app); + /* If the configuration file was successfully loaded, start the app */ + app = new OlympusApp(); + app.start(); + } + } + else { + throw new Error('Could not read configuration file'); + } + }); } export function getApp() { return app; } -/** Loads the configuration parameters - * - * @param config ConfigParameters, defines the address and port of the Olympus REST server - */ -function readConfig(config: ConfigurationOptions) { - if (config && config.address != undefined && config.port != undefined) { - const address = config.address; - const port = config.port; - if (typeof address === 'string' && typeof port == 'number') - setAddress(address == "*" ? window.location.hostname : address, port); - } - else { - throw new Error('Could not read configuration file'); - } -} - -function setupEvents(app: OlympusApp) { - /* Generic clicks */ - document.addEventListener("click", (ev) => { - if (ev instanceof MouseEvent && ev.target instanceof HTMLElement) { - const target = ev.target; - - if (target.classList.contains("olympus-dialog-close")) { - target.closest("div.olympus-dialog")?.classList.add("hide"); - } - - const triggerElement = target.closest("[data-on-click]"); - - if (triggerElement instanceof HTMLElement) { - const eventName: string = triggerElement.dataset.onClick || ""; - let params = JSON.parse(triggerElement.dataset.onClickParams || "{}"); - params._element = triggerElement; - - if (eventName) { - document.dispatchEvent(new CustomEvent(eventName, { - detail: params - })); - } - } - } - }); - - const shortcutManager = app.getShortcutManager(); - shortcutManager.add("toggleDemo", new ShortcutKeyboard({ - "callback": () => { - toggleDemoEnabled(); - }, - "code": "KeyT" - }) - ) - .add("togglePause", new ShortcutKeyboard({ - "altKey": false, - "callback": () => { - setPaused(!getPaused()); - }, - "code": "Space", - "ctrlKey": false - }) - ); - - ["KeyW", "KeyA", "KeyS", "KeyD", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].forEach(code => { - shortcutManager.add(`pan${code}keydown`, new ShortcutKeyboard({ - "altKey": false, - "callback": (ev: KeyboardEvent) => { - getApp().getMap().handleMapPanning(ev); - }, - "code": code, - "ctrlKey": false, - "event": "keydown" - })); - }); - - ["KeyW", "KeyA", "KeyS", "KeyD", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].forEach(code => { - shortcutManager.add(`pan${code}keyup`, new ShortcutKeyboard({ - "callback": (ev: KeyboardEvent) => { - getApp().getMap().handleMapPanning(ev); - }, - "code": code - })); - }); - - ["Digit1", "Digit2", "Digit3", "Digit4", "Digit5", "Digit6", "Digit7", "Digit8", "Digit9"].forEach(code => { - shortcutManager.add(`hotgroup${code}`, new ShortcutKeyboard({ - "callback": (ev: KeyboardEvent) => { - if (ev.ctrlKey && ev.shiftKey) - getApp().getUnitsManager().selectedUnitsAddToHotgroup(parseInt(ev.code.substring(5))); - else if (ev.ctrlKey && !ev.shiftKey) - getApp().getUnitsManager().selectedUnitsSetHotgroup(parseInt(ev.code.substring(5))); - else - getApp().getUnitsManager().selectUnitsByHotgroup(parseInt(ev.code.substring(5))); - }, - "code": code - })); - }); - - // TODO: move from here in dedicated class - document.addEventListener("closeDialog", (ev: CustomEventInit) => { - ev.detail._element.closest(".ol-dialog").classList.add("hide"); - }); - - /* Try and connect with the Olympus REST server */ - document.addEventListener("tryConnection", () => { - const form = document.querySelector("#splash-content")?.querySelector("#authentication-form"); - const username = (form?.querySelector("#username") as HTMLInputElement).value; - const password = (form?.querySelector("#password") as HTMLInputElement).value; - - /* Update the user credentials */ - setCredentials(username, password); - - /* Start periodically requesting updates */ - startUpdate(); - - getApp().setLoginStatus("connecting"); - }) - - /* Reload the page, used to mimic a restart of the app */ - document.addEventListener("reloadPage", () => { - location.reload(); - }) - - /* Inject the svgs with the corresponding svg code. This allows to dynamically manipulate the svg, like changing colors */ - document.querySelectorAll("[inject-svg]").forEach((el: Element) => { - var img = el as HTMLImageElement; - var isLoaded = img.complete; - if (isLoaded) - SVGInjector(img); - else - img.addEventListener("load", () => { - SVGInjector(img); - }); - }) -} - window.onload = setup; \ No newline at end of file diff --git a/client/src/map/map.ts b/client/src/map/map.ts index 6dc77a01..479bae5e 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -12,7 +12,7 @@ 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, SHOW_CONTROL_TIPS } 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 "./markers/targetmarker"; import { CoalitionArea } from "./coalitionarea/coalitionarea"; import { CoalitionAreaContextMenu } from "../contextmenus/coalitionareacontextmenu"; @@ -161,7 +161,6 @@ export class Map extends L.Map { document.addEventListener("mapVisibilityOptionsChanged", () => { this.getContainer().toggleAttribute("data-hide-labels", !this.getVisibilityOptions()[SHOW_UNIT_LABELS]); - // TODO this.getControlTips().toggle( !this.getVisibilityOptions()[SHOW_CONTROL_TIPS] ); }); /* Pan interval */ @@ -180,20 +179,16 @@ export class Map extends L.Map { document.querySelector("#unit-visibility-control")?.append(...this.#optionButtons["visibility"]); /* Create the checkboxes to select the advanced visibility options */ - this.#visibilityOptions[SHOW_CONTACT_LINES] = false; - this.#visibilityOptions[HIDE_GROUP_MEMBERS] = true; - this.#visibilityOptions[SHOW_UNIT_PATHS] = true; - this.#visibilityOptions[SHOW_UNIT_TARGETS] = true; - this.#visibilityOptions[SHOW_UNIT_LABELS] = true; - - // Manual until we use the App approach - this.#visibilityOptions[SHOW_CONTROL_TIPS] = JSON.parse( localStorage.getItem( "featureSwitches" ) || "{}" )?.controlTips || true; + this.addVisibilityOption(SHOW_CONTACT_LINES, false); + this.addVisibilityOption(HIDE_GROUP_MEMBERS, true); + this.addVisibilityOption(SHOW_UNIT_PATHS, true); + this.addVisibilityOption(SHOW_UNIT_TARGETS, true); + this.addVisibilityOption(SHOW_UNIT_LABELS, true); + } - this.#mapVisibilityOptionsDropdown.setOptionsElements(Object.keys(this.#visibilityOptions).map((option: string) => { - return createCheckboxOption(option, option, this.#visibilityOptions[option], (ev: any) => { - this.#setVisibilityOption(option, ev); - }); - })); + addVisibilityOption(option: string, defaultValue: boolean) { + this.#visibilityOptions[option] = defaultValue; + this.#mapVisibilityOptionsDropdown.addOptionElement(createCheckboxOption(option, option, defaultValue, (ev: any) => { this.#setVisibilityOption(option, ev); })); } setLayer(layerName: string) { diff --git a/client/src/plugin/pluginmanager.ts b/client/src/plugin/pluginmanager.ts index 3179786b..38119b69 100644 --- a/client/src/plugin/pluginmanager.ts +++ b/client/src/plugin/pluginmanager.ts @@ -2,6 +2,11 @@ import path from "path"; import { Manager } from "../other/manager"; import { getApp } from ".."; +/** The plugins manager is responsible for loading and initializing all the plugins. Plugins are located in the public/plugins folder. + * Each plugin must be comprised of a single folder containing a index.js file. Each plugin must set the globalThis.getOlympusPlugin variable to + * return a valid class implementing the OlympusPlugin interface. + */ + export class PluginsManager extends Manager { constructor() { super(); @@ -36,15 +41,28 @@ export class PluginsManager extends Manager { document.getElementsByTagName("head")[0].appendChild(link); /* Evaluate the plugin javascript */ - eval(xhr.response); - const plugin = globalThis.getOlympusPlugin() as OlympusPlugin; - console.log(plugin.getName() + " loaded correctly"); - - if (plugin.initialize(getApp())) { - console.log(plugin.getName() + " initialized correctly"); - this.add(pluginName, plugin); + var plugin: OlympusPlugin | null = null; + try { + eval(xhr.response); + plugin = globalThis.getOlympusPlugin() as OlympusPlugin; + console.log(plugin.getName() + " loaded correctly"); + } catch (error: any) { + console.log("An error occured while loading a plugin from " + pluginName); + console.log(error); + } + + /* If the plugin was loaded, try to initialize it */ + if (plugin != null) { + try { + if (plugin.initialize(getApp())) { + console.log(plugin.getName() + " initialized correctly"); + this.add(pluginName, plugin); + } + } catch (error: any) { + console.log("An error occured while initializing a plugin from " + pluginName); + console.log(error); + } } - } else { console.error(`Error retrieving plugin from ${pluginName}`) }