Implemented basic Plugin handling

This commit is contained in:
Pax1601
2023-09-15 17:05:26 +02:00
parent ad06117b78
commit 588228c050
75 changed files with 1920 additions and 1657 deletions

View File

@@ -1,45 +0,0 @@
interface CustomEventMap {
"unitSelection": CustomEvent<Unit>,
"unitDeselection": CustomEvent<Unit>,
"unitsSelection": CustomEvent<Unit[]>,
"unitsDeselection": CustomEvent<Unit[]>,
"clearSelection": CustomEvent<>,
"unitCreation": CustomEvent<Unit>,
"unitDeletion": CustomEvent<Unit>,
"unitDeath": CustomEvent<Unit>,
"unitUpdated": CustomEvent<Unit>,
"unitMoveCommand": CustomEvent<Unit>,
"unitAttackCommand": CustomEvent<Unit>,
"unitLandCommand": CustomEvent<Unit>,
"unitSetAltitudeCommand": CustomEvent<Unit>,
"unitSetSpeedCommand": CustomEvent<Unit>,
"unitSetOption": CustomEvent<Unit>,
"groupCreation": CustomEvent<Unit[]>,
"groupDeletion": CustomEvent<Unit[]>,
"mapStateChanged": CustomEvent<string>,
"mapContextMenu": CustomEvent<>,
"mapVisibilityOptionsChanged": CustomEvent<>,
"commandModeOptionsChanged": CustomEvent<>,
"contactsUpdated": CustomEvent<Unit>,
}
declare global {
interface Document {
addEventListener<K extends keyof CustomEventMap>(type: K,
listener: (this: Document, ev: CustomEventMap[K]) => void): void;
dispatchEvent<K extends keyof CustomEventMap>(ev: CustomEventMap[K]): void;
}
}
export interface ConfigParameters {
port: number;
address: string;
}
export interface ContextMenuOption {
tooltip: string;
src: string;
callback: CallableFunction;
}
export { };

View File

@@ -1,52 +0,0 @@
interface AirbasesData {
airbases: {[key: string]: any},
sessionHash: string;
time: number;
}
interface BullseyesData {
bullseyes: {[key: string]: {latitude: number, longitude: number, coalition: string}},
sessionHash: string;
time: number;
}
interface MissionData {
mission: {
theatre: string,
dateAndTime: DateAndTime;
commandModeOptions: CommandModeOptions;
coalitions: {red: string[], blue: string[]} = {};
}
time: number;
sessionHash: string;
}
interface CommandModeOptions {
commandMode: string;
restrictSpawns: boolean;
restrictToCoalition: boolean;
setupTime: number;
spawnPoints: {
red: number,
blue: number
},
eras: string[]
}
interface DateAndTime {
date: {Year: number, Month: number, Day: number};
time: {h: number, m: number, s: number};
elapsedTime: number;
startTime: number;
}
interface LogData {
logs: {[key: string]: string},
sessionHash: string;
time: number;
}
interface ServerRequestOptions {
time?: number;
commandHash?: string;
}

View File

@@ -1,104 +0,0 @@
import { LatLng } from "leaflet"
interface UnitSpawnTable {
unitType: string,
location: latlng,
altitude?: number,
loadout?: string,
liveryID: string
}
interface ObjectIconOptions {
showState: boolean,
showVvi: boolean,
showHotgroup: boolean,
showUnitIcon: boolean,
showShortLabel: boolean,
showFuel: boolean,
showAmmo: boolean,
showSummary: boolean,
showCallsign: boolean,
rotateToHeading: boolean
}
interface GeneralSettings {
prohibitJettison: boolean;
prohibitAA: boolean;
prohibitAG: boolean;
prohibitAfterburner: boolean;
prohibitAirWpn: boolean;
}
interface TACAN {
isOn: boolean;
channel: number;
XY: string;
callsign: string;
}
interface Radio {
frequency: number;
callsign: number;
callsignNumber: number;
}
interface Ammo {
quantity: number,
name: string,
guidance: number,
category: number,
missileCategory: number
}
interface Contact {
ID: number,
detectionMethod: number
}
interface Offset {
x: number,
y: number,
z: number
}
interface UnitData {
category: string,
ID: number;
alive: boolean;
human: boolean;
controlled: boolean;
coalition: string;
country: number;
name: string;
unitName: string;
groupName: string;
state: string;
task: string;
hasTask: boolean;
position: LatLng;
speed: number;
heading: number;
isTanker: boolean;
isAWACS: boolean;
onOff: boolean;
followRoads: boolean;
fuel: number;
desiredSpeed: number;
desiredSpeedType: string;
desiredAltitude: number;
desiredAltitudeType: string;
leaderID: number;
formationOffset: Offset;
targetID: number;
targetPosition: LatLng;
ROE: string;
reactionToThreat: string;
emissionsCountermeasures: string;
TACAN: TACAN;
radio: Radio;
generalSettings: GeneralSettings;
ammo: Ammo[];
contacts: Contact[];
activePath: LatLng[];
isLeader: boolean;
}

View File

@@ -1,43 +0,0 @@
import { LatLng } from "leaflet";
import { Airbase } from "../mission/airbase";
interface LoadoutItemBlueprint {
name: string;
quantity: number;
effectiveAgainst?: string;
}
interface LoadoutBlueprint {
fuel: number;
items: LoadoutItemBlueprint[];
roles: string[];
code: string;
name: string;
}
interface UnitBlueprint {
name: string;
coalition: string;
era: string;
label: string;
shortLabel: string;
type?: string;
range?: string;
loadouts?: LoadoutBlueprint[];
filename?: string;
liveries?: {[key: string]: {name: string, countries: string[]}};
cost?: number;
}
interface UnitSpawnOptions {
roleType: string;
name: string;
latlng: LatLng;
coalition: string;
count: number;
country: string;
loadout: LoadoutBlueprint | undefined;
airbase: Airbase | undefined;
liveryID: string | undefined;
altitude: number | undefined;
}

151
client/src/app.ts Normal file
View File

@@ -0,0 +1,151 @@
import { Map } from "./map/map";
import { MissionManager } from "./mission/missionmanager";
import { ConnectionStatusPanel } from "./panels/connectionstatuspanel";
import { HotgroupPanel } from "./panels/hotgrouppanel";
import { LogPanel } from "./panels/logpanel";
import { MouseInfoPanel } from "./panels/mouseinfopanel";
import { ServerStatusPanel } from "./panels/serverstatuspanel";
import { UnitControlPanel } from "./panels/unitcontrolpanel";
import { UnitInfoPanel } from "./panels/unitinfopanel";
import { PluginsManager } from "./plugin/pluginmanager";
import { Popup } from "./popups/popup";
import { ShortcutManager } from "./shortcut/shortcutmanager";
import { CommandModeToolbar } from "./toolbars/commandmodetoolbar";
import { PrimaryToolbar } from "./toolbars/primarytoolbar";
import { UnitsManager } from "./unit/unitsmanager";
import { WeaponsManager } from "./weapon/weaponsmanager";
import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "./constants/constants";
import { Manager } from "./other/manager";
export class OlympusApp {
/* Global data */
#activeCoalition: string = "blue";
/* Main leaflet map, extended by custom methods */
#map: Map | null = null;
/* Managers */
#unitsManager: UnitsManager | null = null;
#weaponsManager: WeaponsManager | null = null;
#missionManager: MissionManager | null = null;
#pluginsManager: PluginsManager | null = null;
#panelsManager: Manager | null = null;
#popupsManager: Manager | null = null;
#toolbarsManager: Manager | null = null;
#shortcutManager: ShortcutManager | null = null;
/* UI Toolbars */
#primaryToolbar: PrimaryToolbar| null = null;
#commandModeToolbar: CommandModeToolbar| null = null;
constructor() {
}
getMap() {
return this.#map as Map;
}
getPanelsManager() {
return this.#panelsManager as Manager;
}
getPopupsManager() {
return this.#popupsManager as Manager;
}
getToolbarsManager() {
return this.#toolbarsManager as Manager;
}
getShortcutManager() {
return this.#shortcutManager as ShortcutManager;
}
getUnitsManager() {
return this.#unitsManager as UnitsManager;
}
getWeaponsManager() {
return this.#weaponsManager as WeaponsManager;
}
getMissionManager() {
return this.#missionManager as MissionManager;
}
getPluginsManager() {
return this.#pluginsManager as PluginsManager;
}
/** Set the active coalition, i.e. the currently controlled coalition. A game master can change the active coalition, while a commander is bound to his/her coalition
*
* @param newActiveCoalition
*/
setActiveCoalition(newActiveCoalition: string) {
if (this.getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER)
this.#activeCoalition = newActiveCoalition;
}
/**
*
* @returns The active coalition
*/
getActiveCoalition() {
if (this.getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER)
return this.#activeCoalition;
else {
if (this.getMissionManager().getCommandModeOptions().commandMode == BLUE_COMMANDER)
return "blue";
else if (this.getMissionManager().getCommandModeOptions().commandMode == RED_COMMANDER)
return "red";
else
return "neutral";
}
}
/** Set a message in the login splash screen
*
* @param status The message to show in the login splash screen
*/
setLoginStatus(status: string) {
const el = document.querySelector("#login-status") as HTMLElement;
if (el)
el.dataset["status"] = status;
}
start() {
/* Initialize base functionalitites */
this.#map = new Map('map-container');
this.#unitsManager = new UnitsManager();
this.#weaponsManager = new WeaponsManager();
this.#missionManager = new MissionManager();
this.#shortcutManager = new ShortcutManager();
this.#panelsManager = new Manager();
this.#popupsManager = new Manager();
this.#toolbarsManager = new Manager();
// Panels
this.getPanelsManager()
.add("connectionStatus", new ConnectionStatusPanel("connection-status-panel"))
.add("hotgroup", new HotgroupPanel("hotgroup-panel"))
.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("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"));
this.#pluginsManager = new PluginsManager();
}
}

View File

@@ -106,7 +106,7 @@ export const layers = {
urlTemplate: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
maxZoom: 20,
minZoom: 1,
attribution: "Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
attribution: "Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, GetApp().getMap()ping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
},
"USGS Topo": {
urlTemplate: 'https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}',

View File

@@ -1,4 +1,4 @@
import { getMap, getMissionHandler, getUnitsManager, setActiveCoalition } from "..";
import { getApp } from "..";
import { GAME_MASTER } from "../constants/constants";
import { Airbase } from "../mission/airbase";
import { dataPointMap } from "../other/utils";
@@ -23,7 +23,7 @@ export class AirbaseContextMenu extends ContextMenu {
document.addEventListener("contextMenuLandAirbase", (e: any) => {
if (this.#airbase)
getUnitsManager().selectedUnitsLandAt(this.#airbase.getLatLng());
getApp().getUnitsManager().selectedUnitsLandAt(this.#airbase.getLatLng());
this.hide();
})
}
@@ -39,8 +39,8 @@ export class AirbaseContextMenu extends ContextMenu {
this.#setProperties(this.#airbase.getProperties());
this.#setParkings(this.#airbase.getParkings());
this.#setCoalition(this.#airbase.getCoalition());
this.#showLandButton(getUnitsManager().getSelectedUnitsCategories().length == 1 && ["Aircraft", "Helicopter"].includes(getUnitsManager().getSelectedUnitsCategories()[0]) && (getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getCoalition()}) === this.#airbase.getCoalition() || this.#airbase.getCoalition() === "neutral"))
this.#showSpawnButton(getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || this.#airbase.getCoalition() == getMissionHandler().getCommandedCoalition());
this.#showLandButton(getApp().getUnitsManager().getSelectedUnitsCategories().length == 1 && ["Aircraft", "Helicopter"].includes(getApp().getUnitsManager().getSelectedUnitsCategories()[0]) && (getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getCoalition()}) === this.#airbase.getCoalition() || this.#airbase.getCoalition() === "neutral"))
this.#showSpawnButton(getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER || this.#airbase.getCoalition() == getApp().getMissionManager().getCommandedCoalition());
this.#setAirbaseData();
this.clip();
@@ -109,8 +109,8 @@ export class AirbaseContextMenu extends ContextMenu {
*/
#showSpawnMenu() {
if (this.#airbase != null) {
setActiveCoalition(this.#airbase.getCoalition());
getMap().showAirbaseSpawnMenu(this.getX(), this.getY(), this.getLatLng(), this.#airbase);
getApp().setActiveCoalition(this.#airbase.getCoalition());
getApp().getMap().showAirbaseSpawnMenu(this.getX(), this.getY(), this.getLatLng(), this.#airbase);
}
}
@@ -135,11 +135,11 @@ export class AirbaseContextMenu extends ContextMenu {
if ( runways.length === 0 ) {
runwaysContainer.innerText = "No data";
} else {
runways.forEach( runway => {
runways.forEach( (runway: AirbaseChartRunwayData) => {
let runwayDiv = document.createElement( "div" );
runwayDiv.classList.add( "runway" );
runway.headings.forEach( headings => {
runway.headings.forEach( (headings: AirbaseChartRunwayHeadingData) => {
for ( const [ heading, data ] of Object.entries( headings ) ) {
let headingDiv = document.createElement( "div" );

View File

@@ -1,8 +1,8 @@
import { LatLng } from "leaflet";
import { getActiveCoalition } from "..";
import { ContextMenu } from "./contextmenu";
import { AircraftSpawnMenu, HelicopterSpawnMenu } from "../controls/unitspawnmenu";
import { Airbase } from "../mission/airbase";
import { getApp } from "..";
/** This context menu is shown when the user wants to spawn a new aircraft or helicopter from the ground at an airbase.
* It is shown by clicking on the "spawn" button of a AirbaseContextMenu. */
@@ -54,7 +54,7 @@ export class AirbaseSpawnContextMenu extends ContextMenu {
this.#aircraftSpawnMenu.setCountries();
this.#helicopterSpawnMenu.setCountries();
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getApp().getActiveCoalition()) });
}
/** Sets the airbase at which the new unit will be spawned

View File

@@ -1,5 +1,5 @@
import { LatLng } from "leaflet";
import { getMap, getMissionHandler, getUnitsManager } from "..";
import { getApp } from "..";
import { GAME_MASTER, IADSTypes } from "../constants/constants";
import { CoalitionArea } from "../map/coalitionarea/coalitionarea";
import { ContextMenu } from "./contextmenu";
@@ -54,20 +54,20 @@ export class CoalitionAreaContextMenu extends ContextMenu {
document.addEventListener("coalitionAreaBringToBack", (e: any) => {
if (this.#coalitionArea)
getMap().bringCoalitionAreaToBack(this.#coalitionArea);
getMap().hideCoalitionAreaContextMenu();
getApp().getMap().bringCoalitionAreaToBack(this.#coalitionArea);
getApp().getMap().hideCoalitionAreaContextMenu();
});
document.addEventListener("coalitionAreaDelete", (e: any) => {
if (this.#coalitionArea)
getMap().deleteCoalitionArea(this.#coalitionArea);
getMap().hideCoalitionAreaContextMenu();
getApp().getMap().deleteCoalitionArea(this.#coalitionArea);
getApp().getMap().hideCoalitionAreaContextMenu();
});
document.addEventListener("contextMenuCreateIads", (e: any) => {
const area = this.getCoalitionArea();
if (area)
getUnitsManager().createIADS(area, getCheckboxOptions(this.#iadsTypesDropdown), getCheckboxOptions(this.#iadsErasDropdown), getCheckboxOptions(this.#iadsRangesDropdown), this.#iadsDensitySlider.getValue(), this.#iadsDistributionSlider.getValue());
getApp().getUnitsManager().createIADS(area, getCheckboxOptions(this.#iadsTypesDropdown), getCheckboxOptions(this.#iadsErasDropdown), getCheckboxOptions(this.#iadsRangesDropdown), this.#iadsDensitySlider.getValue(), this.#iadsDistributionSlider.getValue());
})
this.hide();
}
@@ -97,7 +97,7 @@ export class CoalitionAreaContextMenu extends ContextMenu {
return createCheckboxOption(range, `Add ${range} units to the IADS`);
}));
if (getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER)
if (getApp().getMissionManager().getCommandModeOptions().commandMode !== GAME_MASTER)
this.#coalitionSwitch.hide()
}
@@ -149,7 +149,7 @@ export class CoalitionAreaContextMenu extends ContextMenu {
* @param value Switch position (false: blue, true: red)
*/
#onSwitchClick(value: boolean) {
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER) {
if (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER) {
this.getCoalitionArea()?.setCoalition(value ? "red" : "blue");
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => {
element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition())

View File

@@ -1,5 +1,5 @@
import { LatLng } from "leaflet";
import { getActiveCoalition, getMap, getMissionHandler, setActiveCoalition } from "..";
import { getApp } from "..";
import { spawnExplosion, spawnSmoke } from "../server/server";
import { ContextMenu } from "./contextmenu";
import { Switch } from "../controls/switch";
@@ -50,7 +50,7 @@ export class MapContextMenu extends ContextMenu {
this.hide();
spawnSmoke(e.detail.color, this.getLatLng());
var marker = new SmokeMarker(this.getLatLng(), e.detail.color);
marker.addTo(getMap());
marker.addTo(getApp().getMap());
});
document.addEventListener("contextMenuExplosion", (e: any) => {
@@ -61,7 +61,7 @@ export class MapContextMenu extends ContextMenu {
document.addEventListener("editCoalitionArea", (e: any) => {
this.hide();
if (this.#coalitionArea) {
getMap().deselectAllCoalitionAreas();
getApp().getMap().deselectAllCoalitionAreas();
this.#coalitionArea.setSelected(true);
}
});
@@ -103,13 +103,13 @@ export class MapContextMenu extends ContextMenu {
this.#navyUnitSpawnMenu.setCountries();
/* Only a Game Master can choose the coalition of a new unit */
if (getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER)
if (getApp().getMissionManager().getCommandModeOptions().commandMode !== GAME_MASTER)
this.#coalitionSwitch.hide()
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
if (getActiveCoalition() == "blue")
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getApp().getActiveCoalition()) });
if (getApp().getActiveCoalition() == "blue")
this.#coalitionSwitch.setValue(false);
else if (getActiveCoalition() == "red")
else if (getApp().getActiveCoalition() == "red")
this.#coalitionSwitch.setValue(true);
else
this.#coalitionSwitch.setValue(undefined);
@@ -199,8 +199,8 @@ export class MapContextMenu extends ContextMenu {
* @param value Switch position (false: "blue", true: "red")
*/
#onSwitchClick(value: boolean) {
value ? setActiveCoalition("red") : setActiveCoalition("blue");
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
value ? getApp().setActiveCoalition("red") : getApp().setActiveCoalition("blue");
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getApp().getActiveCoalition()) });
this.#aircraftSpawnMenu.setCountries();
this.#helicopterSpawnMenu.setCountries();
this.#groundUnitSpawnMenu.setCountries();
@@ -212,8 +212,8 @@ export class MapContextMenu extends ContextMenu {
*/
#onSwitchRightClick() {
this.#coalitionSwitch.setValue(undefined);
setActiveCoalition("neutral");
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
getApp().setActiveCoalition("neutral");
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getApp().getActiveCoalition()) });
this.#aircraftSpawnMenu.setCountries();
this.#helicopterSpawnMenu.setCountries();
this.#groundUnitSpawnMenu.setCountries();

View File

@@ -2,16 +2,14 @@ import { LatLng } from "leaflet";
import { Dropdown } from "./dropdown";
import { Slider } from "./slider";
import { UnitDatabase } from "../unit/databases/unitdatabase";
import { getActiveCoalition, getMap, getMissionHandler, getUnitsManager } from "..";
import { getApp } 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/databases/aircraftdatabase";
import { helicopterDatabase } from "../unit/databases/helicopterdatabase";
import { groundUnitDatabase } from "../unit/databases/groundunitdatabase";
import { navyUnitDatabase } from "../unit/databases/navyunitdatabase";
import { UnitSpawnTable } from "../@types/unit";
export class UnitSpawnMenu {
#container: HTMLElement;
@@ -209,8 +207,8 @@ export class UnitSpawnMenu {
}
setCountries() {
var coalitions = getMissionHandler().getCoalitions();
var countries = Object.values(coalitions[getActiveCoalition() as keyof typeof coalitions]);
var coalitions = getApp().getMissionManager().getCoalitions();
var countries = Object.values(coalitions[getApp().getActiveCoalition() as keyof typeof coalitions]);
this.#unitCountryDropdown.setOptionsElements(this.#createCountryButtons(this.#unitCountryDropdown, countries, (country: string) => { this.#setUnitCountry(country) }));
if (countries.length > 0 && !countries.includes(this.#spawnOptions.country)) {
@@ -377,11 +375,11 @@ export class UnitSpawnMenu {
}
#computeSpawnPoints() {
if (getMissionHandler() && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) {
if (getApp().getMissionManager() && getApp().getMissionManager().getCommandModeOptions().commandMode !== GAME_MASTER) {
var unitCount = parseInt(this.#unitCountDropdown.getValue());
var unitSpawnPoints = unitCount * this.#unitDatabase.getSpawnPointsByLabel(this.#unitLabelDropdown.getValue());
this.#deployUnitButtonEl.dataset.points = `${unitSpawnPoints}`;
this.#deployUnitButtonEl.disabled = unitSpawnPoints >= getMissionHandler().getAvailableSpawnPoints();
this.#deployUnitButtonEl.disabled = unitSpawnPoints >= getApp().getMissionManager().getAvailableSpawnPoints();
}
}
}
@@ -400,7 +398,7 @@ export class AircraftSpawnMenu extends UnitSpawnMenu {
}
deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number) {
spawnOptions.coalition = getActiveCoalition();
spawnOptions.coalition = getApp().getActiveCoalition();
if (spawnOptions) {
var unitTable: UnitSpawnTable = {
unitType: spawnOptions.name,
@@ -414,9 +412,9 @@ export class AircraftSpawnMenu extends UnitSpawnMenu {
units.push(unitTable);
}
getUnitsManager().spawnUnits("Aircraft", units, getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => {
getApp().getUnitsManager().spawnUnits("Aircraft", units, getApp().getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => {
if (res.commandHash !== undefined)
getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition(), res.commandHash);
getApp().getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getApp().getActiveCoalition(), res.commandHash);
});
this.getContainer().dispatchEvent(new Event("hide"));
@@ -438,7 +436,7 @@ export class HelicopterSpawnMenu extends UnitSpawnMenu {
}
deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number) {
spawnOptions.coalition = getActiveCoalition();
spawnOptions.coalition = getApp().getActiveCoalition();
if (spawnOptions) {
var unitTable: UnitSpawnTable = {
unitType: spawnOptions.name,
@@ -452,9 +450,9 @@ export class HelicopterSpawnMenu extends UnitSpawnMenu {
units.push(unitTable);
}
getUnitsManager().spawnUnits("Helicopter", units, getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => {
getApp().getUnitsManager().spawnUnits("Helicopter", units, getApp().getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => {
if (res.commandHash !== undefined)
getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition(), res.commandHash);
getApp().getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getApp().getActiveCoalition(), res.commandHash);
});
this.getContainer().dispatchEvent(new Event("hide"));
@@ -476,7 +474,7 @@ export class GroundUnitSpawnMenu extends UnitSpawnMenu {
}
deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number) {
spawnOptions.coalition = getActiveCoalition();
spawnOptions.coalition = getApp().getActiveCoalition();
if (spawnOptions) {
var unitTable: UnitSpawnTable = {
unitType: spawnOptions.name,
@@ -490,9 +488,9 @@ export class GroundUnitSpawnMenu extends UnitSpawnMenu {
unitTable.location.lat += i > 0? 0.0001: 0;
}
getUnitsManager().spawnUnits("GroundUnit", units, getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => {
getApp().getUnitsManager().spawnUnits("GroundUnit", units, getApp().getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => {
if (res.commandHash !== undefined)
getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition(), res.commandHash);
getApp().getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getApp().getActiveCoalition(), res.commandHash);
});
this.getContainer().dispatchEvent(new Event("hide"));
@@ -514,7 +512,7 @@ export class NavyUnitSpawnMenu extends UnitSpawnMenu {
}
deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number) {
spawnOptions.coalition = getActiveCoalition();
spawnOptions.coalition = getApp().getActiveCoalition();
if (spawnOptions) {
var unitTable: UnitSpawnTable = {
unitType: spawnOptions.name,
@@ -528,9 +526,9 @@ export class NavyUnitSpawnMenu extends UnitSpawnMenu {
unitTable.location.lat += i > 0? 0.0001: 0;
}
getUnitsManager().spawnUnits("NavyUnit", units, getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => {
getApp().getUnitsManager().spawnUnits("NavyUnit", units, getApp().getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => {
if (res.commandHash !== undefined)
getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getActiveCoalition(), res.commandHash);
getApp().getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getApp().getActiveCoalition(), res.commandHash);
});
this.getContainer().dispatchEvent(new Event("hide"));

View File

@@ -1,172 +0,0 @@
export interface FeatureSwitchInterface {
"defaultEnabled": boolean, // default on/off state (if allowed by forceState)
"forceState": number, // -1 don't force; 0 force off; 1 force on
"label": string,
"name": string,
"onEnabled"?: CallableFunction,
"options"?: object,
"removeArtifactsIfDisabled"?: boolean
}
class FeatureSwitch {
// From config param
defaultEnabled;
forceState = -1;
label;
name;
onEnabled;
removeArtifactsIfDisabled = true;
// Self-set
userPreference;
constructor(config: FeatureSwitchInterface) {
this.defaultEnabled = config.defaultEnabled;
this.forceState = config.forceState;
this.label = config.label;
this.name = config.name;
this.onEnabled = config.onEnabled;
this.userPreference = this.getUserPreference();
}
getUserPreference() {
let preferences = JSON.parse(localStorage.getItem("featureSwitches") || "{}");
return (preferences.hasOwnProperty(this.name)) ? preferences[this.name] : this.defaultEnabled;
}
isEnabled():boolean {
if ( this.forceState === 0 ) {
return false;
}
if ( this.forceState === 1 ) {
return true;
}
return this.userPreference;
}
}
export class FeatureSwitches {
#featureSwitches: FeatureSwitch[] = [
new FeatureSwitch({
"defaultEnabled": false,
"forceState": -1,
"label": "AIC",
"name": "aic"
}),
new FeatureSwitch({
"defaultEnabled": false,
"forceState": -1,
"label": "AI Formations",
"name": "ai-formations",
"removeArtifactsIfDisabled": false
}),
new FeatureSwitch({
"defaultEnabled": false,
"forceState": -1,
"label": "ATC",
"name": "atc"
}),
new FeatureSwitch({
"defaultEnabled": false,
"forceState": -1,
"label": "Control tips",
"name": "controlTips"
}),
new FeatureSwitch({
"defaultEnabled": false,
"forceState": -1,
"label": "Force show unit control panel",
"name": "forceShowUnitControlPanel"
}),
new FeatureSwitch({
"defaultEnabled": true,
"forceState": -1,
"label": "Show splash screen",
"name": "splashScreen"
})
];
constructor() {
this.#testSwitches();
this.savePreferences();
}
getSwitch(switchName: string) {
return this.#featureSwitches.find(featureSwitch => featureSwitch.name === switchName);
}
#testSwitches() {
for (const featureSwitch of this.#featureSwitches) {
if (featureSwitch.isEnabled()) {
if (typeof featureSwitch.onEnabled === "function") {
featureSwitch.onEnabled();
}
} else {
document.querySelectorAll("[data-feature-switch='" + featureSwitch.name + "']").forEach(el => {
if (featureSwitch.removeArtifactsIfDisabled === false) {
el.remove();
} else {
el.classList.add("hide");
}
});
}
document.body.classList.toggle("feature-" + featureSwitch.name, featureSwitch.isEnabled());
}
}
savePreferences() {
let preferences: any = {};
for (const featureSwitch of this.#featureSwitches) {
preferences[featureSwitch.name] = featureSwitch.isEnabled();
}
localStorage.setItem("featureSwitches", JSON.stringify(preferences));
}
savePreference( featureSwitchName:string, value:boolean ) {
const preferences = JSON.parse( localStorage.getItem( "featureSwitches" ) || "{}" );
if ( preferences.hasOwnProperty( featureSwitchName ) ) {
preferences[ featureSwitchName ] = value;
}
localStorage.setItem("featureSwitches", JSON.stringify(preferences));
}
}

View File

@@ -1,35 +0,0 @@
export abstract class ToggleableFeature {
#status: boolean = false;
constructor(defaultStatus: boolean) {
this.#status = defaultStatus;
this.onStatusUpdate();
}
getStatus(): boolean {
return this.#status;
}
protected onStatusUpdate() { }
toggleStatus(force?: boolean): void {
if (force) {
this.#status = force;
} else {
this.#status = !this.#status;
}
this.onStatusUpdate();
}
}

View File

@@ -1,114 +1,30 @@
import { Map } from "./map/map"
import { UnitsManager } from "./unit/unitsmanager";
import { UnitInfoPanel } from "./panels/unitinfopanel";
import { ConnectionStatusPanel } from "./panels/connectionstatuspanel";
import { MissionManager } from "./mission/missionmanager";
import { UnitControlPanel } from "./panels/unitcontrolpanel";
import { MouseInfoPanel } from "./panels/mouseinfopanel";
import { LogPanel } from "./panels/logpanel";
import { getConfig, getPaused, setAddress, setCredentials, setPaused, startUpdate, toggleDemoEnabled } from "./server/server";
import { Popup } from "./popups/popup";
import { HotgroupPanel } from "./panels/hotgrouppanel";
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 { ConfigParameters } from "./@types/dom";
import { IndexApp } from "./indexapp";
import { FeatureSwitches } from "./features/featureswitches";
import { PrimaryToolbar } from "./toolbars/primarytoolbar";
import { CommandModeToolbar } from "./toolbars/commandmodetoolbar";
import { OlympusApp } from "./olympusapp";
import { OlympusApp } from "./app";
import { ShortcutKeyboard } from "./shortcut/shortcut";
/* Global data */
var activeCoalition: string = "blue";
/* Main leaflet map, extended by custom methods */
var map: Map;
/* Managers */
var unitsManager: UnitsManager;
var weaponsManager: WeaponsManager;
var missionManager: MissionManager;
/* UI Panels */
var unitInfoPanel: UnitInfoPanel;
var connectionStatusPanel: ConnectionStatusPanel;
var serverStatusPanel: ServerStatusPanel;
var unitControlPanel: UnitControlPanel;
var mouseInfoPanel: MouseInfoPanel;
var logPanel: LogPanel;
var hotgroupPanel: HotgroupPanel;
/* UI Toolbars */
var primaryToolbar: PrimaryToolbar;
var commandModeToolbar: CommandModeToolbar;
/* Popups */
var infoPopup: Popup;
var app: OlympusApp;
function setup() {
/* Initialize base functionalitites */
map = new Map('map-container');
unitsManager = new UnitsManager();
weaponsManager = new WeaponsManager();
missionManager = new MissionManager();
/* Panels */
unitInfoPanel = new UnitInfoPanel("unit-info-panel");
unitControlPanel = new UnitControlPanel("unit-control-panel");
connectionStatusPanel = new ConnectionStatusPanel("connection-status-panel");
serverStatusPanel = new ServerStatusPanel("server-status-panel");
mouseInfoPanel = new MouseInfoPanel("mouse-info-panel");
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");
/* Load the config file from the app server*/
getConfig((config: ConfigParameters) => readConfig(config));
getConfig((config: ConfigurationOptions) => readConfig(config));
/*
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": new FeatureSwitches(),
"map": map,
"panels": {
"connectionStatus": connectionStatusPanel,
"hotgroup": hotgroupPanel,
"infoPopup": infoPopup,
"log": logPanel,
"mouseInfo": mouseInfoPanel,
"serverStatus": serverStatusPanel,
"unitControl": unitControlPanel,
"unitInfo": unitInfoPanel
},
"unitsManager": unitsManager
});
app = new OlympusApp();
app.start();
/* Setup event handlers */
setupEvents( indexApp );
indexApp.start();
setupEvents(app);
}
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: ConfigParameters) {
function readConfig(config: ConfigurationOptions) {
if (config && config.address != undefined && config.port != undefined) {
const address = config.address;
const port = config.port;
@@ -120,8 +36,7 @@ function readConfig(config: ConfigParameters) {
}
}
function setupEvents( indexApp:OlympusApp ) {
function setupEvents(app: OlympusApp) {
/* Generic clicks */
document.addEventListener("click", (ev) => {
if (ev instanceof MouseEvent && ev.target instanceof HTMLElement) {
@@ -147,18 +62,15 @@ function setupEvents( indexApp:OlympusApp ) {
}
});
const shortcutManager = indexApp.getShortcutManager();
shortcutManager.add( "toggleDemo", new ShortcutKeyboard({
"callback": () => {
toggleDemoEnabled();
},
"code": "KeyT"
})
const shortcutManager = app.getShortcutManager();
shortcutManager.add("toggleDemo", new ShortcutKeyboard({
"callback": () => {
toggleDemoEnabled();
},
"code": "KeyT"
})
)
.add( "togglePause", new ShortcutKeyboard({
.add("togglePause", new ShortcutKeyboard({
"altKey": false,
"callback": () => {
setPaused(!getPaused());
@@ -166,45 +78,43 @@ function setupEvents( indexApp:OlympusApp ) {
"code": "Space",
"ctrlKey": false
})
);
);
[ "KeyW", "KeyA", "KeyS", "KeyD", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown" ].forEach( code => {
shortcutManager.add( `pan${code}keydown`, new ShortcutKeyboard({
["KeyW", "KeyA", "KeyS", "KeyD", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].forEach(code => {
shortcutManager.add(`pan${code}keydown`, new ShortcutKeyboard({
"altKey": false,
"callback": ( ev:KeyboardEvent ) => {
getMap().handleMapPanning(ev);
"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 ) => {
getMap().handleMapPanning(ev);
["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 ) => {
["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)
getUnitsManager().selectedUnitsAddToHotgroup(parseInt(ev.code.substring(5)));
getApp().getUnitsManager().selectedUnitsAddToHotgroup(parseInt(ev.code.substring(5)));
else if (ev.ctrlKey && !ev.shiftKey)
getUnitsManager().selectedUnitsSetHotgroup(parseInt(ev.code.substring(5)));
getApp().getUnitsManager().selectedUnitsSetHotgroup(parseInt(ev.code.substring(5)));
else
getUnitsManager().selectUnitsByHotgroup(parseInt(ev.code.substring(5)));
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");
@@ -222,7 +132,7 @@ function setupEvents( indexApp:OlympusApp ) {
/* Start periodically requesting updates */
startUpdate();
setLoginStatus("connecting");
getApp().setLoginStatus("connecting");
})
/* Reload the page, used to mimic a restart of the app */
@@ -234,7 +144,7 @@ function setupEvents( indexApp:OlympusApp ) {
document.querySelectorAll("[inject-svg]").forEach((el: Element) => {
var img = el as HTMLImageElement;
var isLoaded = img.complete;
if (isLoaded)
if (isLoaded)
SVGInjector(img);
else
img.addEventListener("load", () => {
@@ -243,90 +153,4 @@ function setupEvents( indexApp:OlympusApp ) {
})
}
/* Getters */
export function getMap() {
return map;
}
export function getUnitsManager() {
return unitsManager;
}
export function getWeaponsManager() {
return weaponsManager;
}
export function getMissionHandler() {
return missionManager;
}
export function getUnitInfoPanel() {
return unitInfoPanel;
}
export function getUnitControlPanel() {
return unitControlPanel;
}
export function getMouseInfoPanel() {
return mouseInfoPanel;
}
export function getLogPanel() {
return logPanel;
}
export function getConnectionStatusPanel() {
return connectionStatusPanel;
}
export function getServerStatusPanel() {
return serverStatusPanel;
}
export function getHotgroupPanel() {
return hotgroupPanel;
}
export function getInfoPopup() {
return infoPopup;
}
/** Set the active coalition, i.e. the currently controlled coalition. A game master can change the active coalition, while a commander is bound to his/her coalition
*
* @param newActiveCoalition
*/
export function setActiveCoalition(newActiveCoalition: string) {
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER)
activeCoalition = newActiveCoalition;
}
/**
*
* @returns The active coalition
*/
export function getActiveCoalition() {
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER)
return activeCoalition;
else {
if (getMissionHandler().getCommandModeOptions().commandMode == BLUE_COMMANDER)
return "blue";
else if (getMissionHandler().getCommandModeOptions().commandMode == RED_COMMANDER)
return "red";
else
return "neutral";
}
}
/** Set a message in the login splash screen
*
* @param status The message to show in the login splash screen
*/
export function setLoginStatus(status: string) {
const el = document.querySelector("#login-status") as HTMLElement;
if (el)
el.dataset["status"] = status;
}
window.onload = setup;
window.onload = setup;

View File

@@ -1,66 +0,0 @@
import { FeatureSwitches } from "./features/featureswitches";
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 { Popup } from "./popups/popup";
import { ControlTips } from "./shortcut/controltips";
import { UnitsManager } from "./unit/unitsmanager";
export interface IIndexApp extends IOlympusApp {
"featureSwitches": FeatureSwitches,
"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 {
constructor( config:IIndexApp ) {
super( config );
// Panels
this.getPanelsManager()
.add( "connectionStatus", config.panels.connectionStatus )
.add( "hotgroup", config.panels.hotgroup )
.add( "log", config.panels.log )
.add( "mouseInfo", config.panels.mouseInfo )
.add( "serverStatus", config.panels.serverStatus )
.add( "unitControl", config.panels.unitControl )
.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 );
});
}
start() {
super.start();
new ControlTips( "control-tips-panel", this );
}
}

View File

@@ -1,5 +1,5 @@
import { DomUtil, LatLng, LatLngExpression, Map, Point, Polygon, PolylineOptions } from "leaflet";
import { getMap, getMissionHandler, getUnitsManager } from "../..";
import { getApp } from "../..";
import { CoalitionAreaHandle } from "./coalitionareahandle";
import { CoalitionAreaMiddleHandle } from "./coalitionareamiddlehandle";
import { BLUE_COMMANDER, RED_COMMANDER } from "../../constants/constants";
@@ -23,8 +23,8 @@ export class CoalitionArea extends Polygon {
this.#setColors();
this.#registerCallbacks();
if ([BLUE_COMMANDER, RED_COMMANDER].includes(getMissionHandler().getCommandModeOptions().commandMode))
this.setCoalition(getMissionHandler().getCommandedCoalition());
if ([BLUE_COMMANDER, RED_COMMANDER].includes(getApp().getMissionManager().getCommandModeOptions().commandMode))
this.setCoalition(getApp().getMissionManager().getCommandedCoalition());
}
setCoalition(coalition: string) {
@@ -61,7 +61,7 @@ export class CoalitionArea extends Polygon {
/* Remove areas with less than 2 vertexes */
if (latlngs.length <= 2)
getMap().deleteCoalitionArea(this);
getApp().getMap().deleteCoalitionArea(this);
}
getEditing() {
@@ -89,7 +89,7 @@ export class CoalitionArea extends Polygon {
onRemove(map: Map): this {
super.onRemove(map);
this.#handles.concat(this.#middleHandles).forEach((handle: CoalitionAreaHandle | CoalitionAreaMiddleHandle) => handle.removeFrom(getMap()));
this.#handles.concat(this.#middleHandles).forEach((handle: CoalitionAreaHandle | CoalitionAreaMiddleHandle) => handle.removeFrom(getApp().getMap()));
return this;
}
@@ -99,14 +99,14 @@ export class CoalitionArea extends Polygon {
}
#setHandles() {
this.#handles.forEach((handle: CoalitionAreaHandle) => handle.removeFrom(getMap()));
this.#handles.forEach((handle: CoalitionAreaHandle) => handle.removeFrom(getApp().getMap()));
this.#handles = [];
if (this.getSelected()) {
var latlngs = this.getLatLngs()[0] as LatLng[];
latlngs.forEach((latlng: LatLng, idx: number) => {
/* Add the polygon vertex handle (for moving the vertex) */
const handle = new CoalitionAreaHandle(latlng);
handle.addTo(getMap());
handle.addTo(getApp().getMap());
handle.on("drag", (e: any) => {
var latlngs = this.getLatLngs()[0] as LatLng[];
latlngs[idx] = e.target.getLatLng();
@@ -120,7 +120,7 @@ export class CoalitionArea extends Polygon {
}
#setMiddleHandles() {
this.#middleHandles.forEach((handle: CoalitionAreaMiddleHandle) => handle.removeFrom(getMap()));
this.#middleHandles.forEach((handle: CoalitionAreaMiddleHandle) => handle.removeFrom(getApp().getMap()));
this.#middleHandles = [];
var latlngs = this.getLatLngs()[0] as LatLng[];
if (this.getSelected() && latlngs.length >= 2) {
@@ -128,13 +128,13 @@ export class CoalitionArea extends Polygon {
latlngs.concat([latlngs[0]]).forEach((latlng: LatLng, idx: number) => {
/* Add the polygon middle point handle (for adding new vertexes) */
if (lastLatLng != null) {
const handle1Point = getMap().latLngToLayerPoint(latlng);
const handle2Point = getMap().latLngToLayerPoint(lastLatLng);
const handle1Point = getApp().getMap().latLngToLayerPoint(latlng);
const handle2Point = getApp().getMap().latLngToLayerPoint(lastLatLng);
const middlePoint = new Point((handle1Point.x + handle2Point.x) / 2, (handle1Point.y + handle2Point.y) / 2);
const middleLatLng = getMap().layerPointToLatLng(middlePoint);
const middleLatLng = getApp().getMap().layerPointToLatLng(middlePoint);
const middleHandle = new CoalitionAreaMiddleHandle(middleLatLng);
middleHandle.addTo(getMap());
middleHandle.addTo(getApp().getMap());
middleHandle.on("click", (e: any) => {
this.#activeIndex = idx - 1;
this.addTemporaryLatLng(middleLatLng);
@@ -148,7 +148,7 @@ export class CoalitionArea extends Polygon {
#registerCallbacks() {
this.on("click", (e: any) => {
getMap().deselectAllCoalitionAreas();
getApp().getMap().deselectAllCoalitionAreas();
if (!this.getSelected()) {
this.setSelected(true);
}
@@ -156,7 +156,7 @@ export class CoalitionArea extends Polygon {
this.on("contextmenu", (e: any) => {
if (!this.getEditing()) {
getMap().deselectAllCoalitionAreas();
getApp().getMap().deselectAllCoalitionAreas();
this.setSelected(true);
}
else

View File

@@ -1,5 +1,5 @@
import * as L from "leaflet"
import { getInfoPopup, getMissionHandler, getUnitsManager } from "..";
import { getApp } from "..";
import { BoxSelect } from "./boxselect";
import { MapContextMenu } from "../contextmenus/mapcontextmenu";
import { UnitContextMenu } from "../contextmenus/unitcontextmenu";
@@ -18,7 +18,7 @@ import { CoalitionArea } from "./coalitionarea/coalitionarea";
import { CoalitionAreaContextMenu } from "../contextmenus/coalitionareacontextmenu";
import { DrawingCursor } from "./coalitionarea/drawingcursor";
import { AirbaseSpawnContextMenu } from "../contextmenus/airbasespawnmenu";
import { OlympusApp } from "../olympusapp";
import { Popup } from "../popups/popup";
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
@@ -71,8 +71,6 @@ export class Map extends L.Map {
#visibilityOptions: { [key: string]: boolean } = {}
#hiddenTypes: string[] = [];
#olympusApp!:OlympusApp;
/**
*
* @param ID - the ID of the HTML element which will contain the context menu
@@ -126,17 +124,17 @@ export class Map extends L.Map {
const el = ev.detail._element;
el?.classList.toggle("off");
this.setHiddenType(ev.detail.coalition, !el?.classList.contains("off"));
Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
Object.values(getApp().getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
});
document.addEventListener("toggleMarkerVisibility", (ev: CustomEventInit) => {
const el = ev.detail._element;
el?.classList.toggle("off");
ev.detail.types.forEach((type: string) => this.setHiddenType(type, !el?.classList.contains("off")));
Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
Object.values(getApp().getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
if (ev.detail.types.includes("airbase")) {
Object.values(getMissionHandler().getAirbases()).forEach((airbase: Airbase) => {
Object.values(getApp().getMissionManager().getAirbases()).forEach((airbase: Airbase) => {
if (el?.classList.contains("off"))
airbase.removeFrom(this);
else
@@ -163,7 +161,7 @@ export class Map extends L.Map {
document.addEventListener("mapVisibilityOptionsChanged", () => {
this.getContainer().toggleAttribute("data-hide-labels", !this.getVisibilityOptions()[SHOW_UNIT_LABELS]);
this.getOlympusApp().getControlTips().toggle( !this.getVisibilityOptions()[SHOW_CONTROL_TIPS] );
// TODO this.getControlTips().toggle( !this.getVisibilityOptions()[SHOW_CONTROL_TIPS] );
});
/* Pan interval */
@@ -188,7 +186,7 @@ export class Map extends L.Map {
this.#visibilityOptions[SHOW_UNIT_TARGETS] = true;
this.#visibilityOptions[SHOW_UNIT_LABELS] = true;
// Manual until we use the OlympusApp approach
// Manual until we use the App approach
this.#visibilityOptions[SHOW_CONTROL_TIPS] = JSON.parse( localStorage.getItem( "featureSwitches" ) || "{}" )?.controlTips || true;
this.#mapVisibilityOptionsDropdown.setOptionsElements(Object.keys(this.#visibilityOptions).map((option: string) => {
@@ -260,7 +258,7 @@ export class Map extends L.Map {
else {
this.#hiddenTypes.push(key);
}
Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
Object.values(getApp().getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
}
getHiddenTypes() {
@@ -367,7 +365,7 @@ export class Map extends L.Map {
centerOnUnit(ID: number | null) {
if (ID != null) {
this.options.scrollWheelZoom = 'center';
this.#centerUnit = getUnitsManager().getUnitByID(ID);
this.#centerUnit = getApp().getUnitsManager().getUnitByID(ID);
}
else {
this.options.scrollWheelZoom = undefined;
@@ -487,7 +485,7 @@ export class Map extends L.Map {
}
else {
this.setState(IDLE);
getUnitsManager().deselectAllUnits();
getApp().getUnitsManager().deselectAllUnits();
}
}
}
@@ -525,9 +523,9 @@ export class Map extends L.Map {
}
else if (this.#state === MOVE_UNIT) {
if (!e.originalEvent.ctrlKey) {
getUnitsManager().selectedUnitsClearDestinations();
getApp().getUnitsManager().selectedUnitsClearDestinations();
}
getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation)
getApp().getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation)
this.#destinationGroupRotation = 0;
this.#destinationRotationCenter = null;
@@ -550,7 +548,7 @@ export class Map extends L.Map {
this.#leftClickTimer = window.setTimeout(() => {
this.#preventLeftClick = false;
}, 200);
getUnitsManager().selectFromBounds(e.selectionBounds);
getApp().getUnitsManager().selectFromBounds(e.selectionBounds);
this.#updateCursor();
}
@@ -575,25 +573,25 @@ export class Map extends L.Map {
this.#longPressHandled = true;
var options: { [key: string]: { text: string, tooltip: string } } = {};
const selectedUnits = getUnitsManager().getSelectedUnits();
const selectedUnitTypes = getUnitsManager().getSelectedUnitsCategories();
const selectedUnits = getApp().getUnitsManager().getSelectedUnits();
const selectedUnitTypes = getApp().getUnitsManager().getSelectedUnitsCategories();
if (selectedUnitTypes.length === 1 && ["Aircraft", "Helicopter"].includes(selectedUnitTypes[0])) {
if (selectedUnits.every((unit: Unit) => { return unit.canFulfillRole(["CAS", "Strike"]) })) {
options["bomb"] = { text: "Precision bombing", tooltip: "Precision bombing of a specific point" };
options["carpet-bomb"] = { text: "Carpet bombing", tooltip: "Carpet bombing close to a point" };
} else {
getInfoPopup().setText(`Selected units can not perform point actions.`);
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Selected units can not perform point actions.`);
}
}
else if (selectedUnitTypes.length === 1 && ["GroundUnit", "NavyUnit"].includes(selectedUnitTypes[0])) {
if (selectedUnits.every((unit: Unit) => { return ["Gun Artillery", "Rocket Artillery", "Infantry", "IFV", "Tank", "Cruiser", "Destroyer", "Frigate"].includes(unit.getType()) }))
options["fire-at-area"] = { text: "Fire at area", tooltip: "Fire at a large area" };
else
getInfoPopup().setText(`Selected units can not perform point actions.`);
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Selected units can not perform point actions.`);
}
else if(selectedUnitTypes.length > 1) {
getInfoPopup().setText(`Multiple unit types selected, no common actions available.`);
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Multiple unit types selected, no common actions available.`);
}
if (Object.keys(options).length > 0) {
@@ -601,16 +599,16 @@ export class Map extends L.Map {
this.getUnitContextMenu().setOptions(options, (option: string) => {
this.hideUnitContextMenu();
if (option === "bomb") {
getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates());
getApp().getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
getApp().getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates());
}
else if (option === "carpet-bomb") {
getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates());
getApp().getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
getApp().getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates());
}
else if (option === "fire-at-area") {
getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates());
getApp().getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
getApp().getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates());
}
});
}
@@ -703,7 +701,7 @@ export class Map extends L.Map {
#showDestinationCursors() {
const singleCursor = !this.#shiftKey;
const selectedUnitsCount = getUnitsManager().getSelectedUnits({ excludeHumans: false, onlyOnePerGroup: true }).length;
const selectedUnitsCount = getApp().getUnitsManager().getSelectedUnits({ excludeHumans: false, onlyOnePerGroup: true }).length;
if (selectedUnitsCount > 0) {
if (singleCursor && this.#destinationPreviewCursors.length != 1) {
this.#hideDestinationCursors();
@@ -733,7 +731,7 @@ export class Map extends L.Map {
if (this.#destinationPreviewCursors.length == 1)
this.#destinationPreviewCursors[0].setLatLng(this.getMouseCoordinates());
else {
Object.values(getUnitsManager().selectedUnitsComputeGroupDestination(groupLatLng, this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => {
Object.values(getApp().getUnitsManager().selectedUnitsComputeGroupDestination(groupLatLng, this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => {
if (idx < this.#destinationPreviewCursors.length)
this.#destinationPreviewCursors[idx].setLatLng(this.#shiftKey ? latlng : this.getMouseCoordinates());
})
@@ -803,15 +801,5 @@ export class Map extends L.Map {
this.#visibilityOptions[option] = ev.currentTarget.checked;
document.dispatchEvent(new CustomEvent("mapVisibilityOptionsChanged"));
}
getOlympusApp() {
return this.#olympusApp;
}
setOlympusApp( olympusApp:OlympusApp ) {
this.#olympusApp = olympusApp;
}
}

View File

@@ -1,7 +1,7 @@
import { DivIcon, LatLngExpression, MarkerOptions } from "leaflet";
import { CustomMarker } from "./custommarker";
import { SVGInjector } from "@tanem/svg-injector";
import { getMap } from "../..";
import { getApp } from "../..";
export class SmokeMarker extends CustomMarker {
#color: string;
@@ -10,7 +10,7 @@ export class SmokeMarker extends CustomMarker {
super(latlng, options);
this.setZIndexOffset(9999);
this.#color = color;
window.setTimeout(() => { this.removeFrom(getMap()); }, 300000) /* Remove the smoke after 5 minutes */
window.setTimeout(() => { this.removeFrom(getApp().getMap()); }, 300000) /* Remove the smoke after 5 minutes */
}
createIcon() {

View File

@@ -3,7 +3,7 @@ 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 { getApp } from "../..";
export class TemporaryUnitMarker extends CustomMarker {
#name: string;
@@ -27,7 +27,7 @@ export class TemporaryUnitMarker extends CustomMarker {
if (this.#commandHash !== undefined) {
isCommandExecuted((res: any) => {
if (res.commandExecuted) {
this.removeFrom(getMap());
this.removeFrom(getApp().getMap());
window.clearInterval(this.#timer);
}
}, this.#commandHash)

View File

@@ -2,30 +2,6 @@ import { DivIcon } from 'leaflet';
import { CustomMarker } from '../map/markers/custommarker';
import { SVGInjector } from '@tanem/svg-injector';
export interface AirbaseOptions {
name: string,
position: L.LatLng
}
export interface AirbaseChartData {
elevation: string,
ICAO: string,
TACAN: string,
runways: AirbaseChartRunwayData[]
}
export interface AirbaseChartRunwayData {
"headings": AirbaseChartRunwayHeadingData[],
"length": string
}
export interface AirbaseChartRunwayHeadingData {
[index: string]: {
"magHeading": string,
"ILS": string
}
}
export class Airbase extends CustomMarker {
#name: string = "";

View File

@@ -1,5 +1,5 @@
import { LatLng } from "leaflet";
import { getInfoPopup, getMap } from "..";
import { getApp } from "..";
import { Airbase } from "./airbase";
import { Bullseye } from "./bullseye";
import { BLUE_COMMANDER, GAME_MASTER, NONE, RED_COMMANDER } from "../constants/constants";
@@ -10,6 +10,7 @@ import { createCheckboxOption, getCheckboxOptions } from "../other/utils";
import { aircraftDatabase } from "../unit/databases/aircraftdatabase";
import { helicopterDatabase } from "../unit/databases/helicopterdatabase";
import { navyUnitDatabase } from "../unit/databases/navyunitdatabase";
import { Popup } from "../popups/popup";
/** The MissionManager */
export class MissionManager {
@@ -38,7 +39,7 @@ export class MissionManager {
for (let idx in data.bullseyes) {
const bullseye = data.bullseyes[idx];
if (!(idx in this.#bullseyes))
this.#bullseyes[idx] = new Bullseye([0, 0]).addTo(getMap());
this.#bullseyes[idx] = new Bullseye([0, 0]).addTo(getApp().getMap());
if (bullseye.latitude && bullseye.longitude && bullseye.coalition) {
this.#bullseyes[idx].setLatLng(new LatLng(bullseye.latitude, bullseye.longitude));
@@ -54,7 +55,7 @@ export class MissionManager {
this.#airbases[airbase.callsign] = new Airbase({
position: new LatLng(airbase.latitude, airbase.longitude),
name: airbase.callsign
}).addTo(getMap());
}).addTo(getApp().getMap());
this.#airbases[airbase.callsign].on('contextmenu', (e) => this.#onAirbaseClick(e));
this.#loadAirbaseChartData(airbase.callsign);
}
@@ -72,8 +73,8 @@ export class MissionManager {
/* Set the mission theatre */
if (data.mission.theatre != this.#theatre) {
this.#theatre = data.mission.theatre;
getMap().setTheatre(this.#theatre);
getInfoPopup().setText("Map set to " + this.#theatre);
getApp().getMap().setTheatre(this.#theatre);
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Map set to " + this.#theatre);
}
/* Set the date and time data */
@@ -232,7 +233,7 @@ export class MissionManager {
}
#onAirbaseClick(e: any) {
getMap().showAirbaseContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng, e.sourceTarget);
getApp().getMap().showAirbaseContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng, e.sourceTarget);
}
#loadAirbaseChartData(callsign: string) {

View File

@@ -1,70 +0,0 @@
import { FeatureSwitches } from "./features/featureswitches";
import { Map } from "./map/map";
import { PanelsManager } from "./panels/panelsmanager";
import { ControlTips } from "./shortcut/controltips";
import { ShortcutManager } from "./shortcut/shortcutmanager";
import { UnitsManager } from "./unit/unitsmanager";
export interface IOlympusApp {
featureSwitches: FeatureSwitches;
map: Map,
unitsManager: UnitsManager;
}
export abstract class OlympusApp {
#controlTips: ControlTips;
#featureSwitches: FeatureSwitches;
#map: Map;
#panelsManager: PanelsManager = new PanelsManager( this );
#shortcutManager: ShortcutManager = new ShortcutManager( this );
#unitsManager: UnitsManager;
constructor( config:IOlympusApp ) {
this.#controlTips = new ControlTips( "control-tips-panel", this );
this.#featureSwitches = config.featureSwitches;
this.#map = config.map;
this.#unitsManager = config.unitsManager;
this.getMap().setOlympusApp( this );
}
getControlTips() {
return this.#controlTips;
}
getFeatureSwitches() {
return this.#featureSwitches;
}
getMap() {
return this.#map;
}
getPanelsManager() {
return this.#panelsManager;
}
getShortcutManager() {
return this.#shortcutManager;
}
getUnitsManager() {
return this.#unitsManager;
}
getWeaponsManager() {
return this.getWeaponsManager;
}
start() {
// Start the app
}
}

View File

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

View File

@@ -1,54 +1,33 @@
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;
export class Manager {
#items: { [key: string]: any } = {};
constructor() {
}
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()}.` );
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.` );
if (this.#items.hasOwnProperty(name)) {
throw new Error(`Item with name "${name}" already exists.`);
}
this.#items[ name ] = item;
this.#items[name] = item;
return this;
}
get( name:string ) {
if ( this.#items.hasOwnProperty( name ) ) {
return this.#items[ name ];
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

@@ -7,7 +7,6 @@ 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/databases/navyunitdatabase";
export function bearing(lat1: number, lon1: number, lat2: number, lon2: number) {

View File

@@ -1,4 +1,4 @@
import { getUnitsManager } from "..";
import { getApp } from "..";
import { Unit } from "../unit/unit";
import { Panel } from "./panel";
@@ -15,7 +15,7 @@ export class HotgroupPanel extends Panel {
refreshHotgroups() {
for (let hotgroup = 1; hotgroup <= 9; hotgroup++){
this.removeHotgroup(hotgroup);
if (getUnitsManager().getUnitsByHotgroup(hotgroup).length > 0)
if (getApp().getUnitsManager().getUnitsByHotgroup(hotgroup).length > 0)
this.addHotgroup(hotgroup);
}
@@ -32,7 +32,7 @@ export class HotgroupPanel extends Panel {
// Hotgroup unit count
var countDiv = document.createElement("div");
countDiv.innerText = `x${getUnitsManager().getUnitsByHotgroup(hotgroup).length}`;
countDiv.innerText = `x${getApp().getUnitsManager().getUnitsByHotgroup(hotgroup).length}`;
var el = document.createElement("div");
el.appendChild(hotgroupDiv);
@@ -43,15 +43,15 @@ export class HotgroupPanel extends Panel {
this.getElement().appendChild(el);
el.addEventListener("click", () => {
getUnitsManager().selectUnitsByHotgroup(hotgroup);
getApp().getUnitsManager().selectUnitsByHotgroup(hotgroup);
});
el.addEventListener("mouseover", () => {
getUnitsManager().getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setHighlighted(true));
getApp().getUnitsManager().getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setHighlighted(true));
});
el.addEventListener("mouseout", () => {
getUnitsManager().getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setHighlighted(false));
getApp().getUnitsManager().getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setHighlighted(false));
});
}

View File

@@ -1,4 +1,5 @@
import { getMouseInfoPanel } from "..";
import { getApp } from "..";
import { MouseInfoPanel } from "./mouseinfopanel";
import { Panel } from "./panel";
export class LogPanel extends Panel {
@@ -37,7 +38,7 @@ export class LogPanel extends Panel {
});
const mouseInfoPanel = getMouseInfoPanel();
const mouseInfoPanel = getApp().getPanelsManager().get("mouseInfo") as MouseInfoPanel;
new ResizeObserver(() => this.#calculateHeight()).observe(mouseInfoPanel.getElement())
}
@@ -89,7 +90,7 @@ export class LogPanel extends Panel {
}
#calculateHeight() {
const mouseInfoPanel = getMouseInfoPanel();
const mouseInfoPanel = getApp().getPanelsManager().get("mouseInfo");
if (this.#open)
this.getElement().style.height = `${mouseInfoPanel.getElement().offsetTop - this.getElement().offsetTop - 10}px`;
else

View File

@@ -1,5 +1,5 @@
import { Icon, LatLng, Marker, Polyline } from "leaflet";
import { getMap, getMissionHandler, getUnitsManager } from "..";
import { getApp } from "..";
import { distance, bearing, zeroAppend, mToNm, nmToFt } from "../other/utils";
import { Unit } from "../unit/unit";
import { Panel } from "./panel";
@@ -22,19 +22,19 @@ export class MouseInfoPanel extends Panel {
this.#measureBox.classList.add("ol-measure-box", "hide");
document.body.appendChild(this.#measureBox);
getMap()?.on("click", (e: any) => this.#onMapClick(e));
getMap()?.on('zoom', (e: any) => this.#onZoom(e));
getMap()?.on('mousemove', (e: any) => this.#onMouseMove(e));
getApp().getMap()?.on("click", (e: any) => this.#onMapClick(e));
getApp().getMap()?.on('zoom', (e: any) => this.#onZoom(e));
getApp().getMap()?.on('mousemove', (e: any) => this.#onMouseMove(e));
document.addEventListener('unitsSelection', (e: CustomEvent<Unit[]>) => this.#update());
document.addEventListener('clearSelection', () => this.#update());
}
#update() {
const mousePosition = getMap().getMouseCoordinates();
const mousePosition = getApp().getMap().getMouseCoordinates();
var selectedUnitPosition = null;
var selectedUnits = getUnitsManager().getSelectedUnits();
var selectedUnits = getApp().getUnitsManager().getSelectedUnits();
if (selectedUnits && selectedUnits.length == 1)
selectedUnitPosition = new LatLng(selectedUnits[0].getPosition().lat, selectedUnits[0].getPosition().lng);
@@ -44,7 +44,7 @@ export class MouseInfoPanel extends Panel {
this.getElement().querySelector(`#measuring-tool`)?.classList.toggle("hide", this.#measurePoint === null && selectedUnitPosition === null);
var bullseyes = getMissionHandler().getBullseyes();
var bullseyes = getApp().getMissionManager().getBullseyes();
for (let idx in bullseyes)
this.#drawMeasure(null, `bullseye-${idx}`, bullseyes[idx].getLatLng(), mousePosition);
@@ -61,19 +61,19 @@ export class MouseInfoPanel extends Panel {
this.#measureBox.classList.toggle("hide", false);
this.#measurePoint = e.latlng;
this.#measureMarker.setLatLng(e.latlng);
this.#measureMarker.addTo(getMap());
if (!getMap().hasLayer(this.#measureLine))
this.#measureLine.addTo(getMap());
this.#measureMarker.addTo(getApp().getMap());
if (!getApp().getMap().hasLayer(this.#measureLine))
this.#measureLine.addTo(getApp().getMap());
}
else {
this.#measureBox.classList.toggle("hide", true);
this.#measurePoint = null;
if (getMap().hasLayer(this.#measureMarker))
getMap().removeLayer(this.#measureMarker);
if (getApp().getMap().hasLayer(this.#measureMarker))
getApp().getMap().removeLayer(this.#measureMarker);
this.#measureLine.setLatLngs([]);
if (getMap().hasLayer(this.#measureLine))
getMap().removeLayer(this.#measureLine);
if (getApp().getMap().hasLayer(this.#measureLine))
getApp().getMap().removeLayer(this.#measureLine);
}
}
@@ -81,15 +81,15 @@ export class MouseInfoPanel extends Panel {
}
#drawMeasureLine() {
var mouseLatLng = getMap().containerPointToLatLng(getMap().getMousePosition());
var mouseLatLng = getApp().getMap().containerPointToLatLng(getApp().getMap().getMousePosition());
if (this.#measurePoint != null) {
var points = [this.#measurePoint, mouseLatLng];
this.#measureLine.setLatLngs(points);
var dist = distance(this.#measurePoint.lat, this.#measurePoint.lng, mouseLatLng.lat, mouseLatLng.lng);
var bear = bearing(this.#measurePoint.lat, this.#measurePoint.lng, mouseLatLng.lat, mouseLatLng.lng);
var startXY = getMap().latLngToContainerPoint(this.#measurePoint);
var dx = (getMap().getMousePosition().x - startXY.x);
var dy = (getMap().getMousePosition().y - startXY.y);
var startXY = getApp().getMap().latLngToContainerPoint(this.#measurePoint);
var dx = (getApp().getMap().getMousePosition().x - startXY.x);
var dy = (getApp().getMap().getMousePosition().y - startXY.y);
var angle = Math.atan2(dy, dx);
if (angle > Math.PI / 2)
@@ -108,8 +108,8 @@ export class MouseInfoPanel extends Panel {
let data = [`${bng}°`, `${str} ${unit}`];
this.#measureBox.innerText = data.join(" / ");
this.#measureBox.style.left = (getMap().getMousePosition().x + startXY.x) / 2 - this.#measureBox.offsetWidth / 2 + "px";
this.#measureBox.style.top = (getMap().getMousePosition().y + startXY.y) / 2 - this.#measureBox.offsetHeight / 2 + "px";
this.#measureBox.style.left = (getApp().getMap().getMousePosition().x + startXY.x) / 2 - this.#measureBox.offsetWidth / 2 + "px";
this.#measureBox.style.top = (getApp().getMap().getMousePosition().y + startXY.y) / 2 - this.#measureBox.offsetHeight / 2 + "px";
this.#measureBox.style.rotate = angle + "rad";
}
}

View File

@@ -1,35 +1,31 @@
import { OlympusApp } from "../olympusapp";
import { PanelEventsManager } from "./paneleventsmanager";
export abstract class Panel {
#element: HTMLElement
#eventsManager!: PanelEventsManager;
#olympusApp!: OlympusApp;
constructor(ID: string, olympusApp?:OlympusApp ) {
constructor(ID: string) {
this.#element = <HTMLElement>document.getElementById(ID);
if ( olympusApp ) {
this.setOlympusApp( olympusApp );
}
this.#eventsManager = new PanelEventsManager();
}
show() {
this.#element.classList.toggle("hide", false);
this.getEventsManager()?.trigger( "show", {} );
this.getEventsManager()?.trigger("show", {});
}
hide() {
this.#element.classList.toggle("hide", true);
this.getEventsManager()?.trigger( "hide", {} );
this.getEventsManager()?.trigger("hide", {});
}
toggle() {
// Simple way to track if currently visible
if (this.getVisible())
this.hide();
else
else
this.show();
}
@@ -37,21 +33,11 @@ export abstract class Panel {
return this.#element;
}
getVisible(){
return (!this.getElement().classList.contains( "hide" ) );
getVisible() {
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

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

View File

@@ -1,17 +0,0 @@
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

@@ -1,5 +1,5 @@
import { SVGInjector } from "@tanem/svg-injector";
import { getUnitsManager } from "..";
import { getApp } from "..";
import { Dropdown } from "../controls/dropdown";
import { Slider } from "../controls/slider";
import { aircraftDatabase } from "../unit/databases/aircraftdatabase";
@@ -8,7 +8,6 @@ import { Panel } from "./panel";
import { Switch } from "../controls/switch";
import { ROEDescriptions, ROEs, altitudeIncrements, emissionsCountermeasures, emissionsCountermeasuresDescriptions, maxAltitudeValues, maxSpeedValues, minAltitudeValues, minSpeedValues, reactionsToThreat, reactionsToThreatDescriptions, speedIncrements } from "../constants/constants";
import { ftToM, knotsToMs, mToFt, msToKnots } from "../other/utils";
import { GeneralSettings, Radio, TACAN } from "../@types/unit";
export class UnitControlPanel extends Panel {
#altitudeSlider: Slider;
@@ -33,24 +32,24 @@ export class UnitControlPanel extends Panel {
super(ID);
/* Unit control sliders */
this.#altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => { getUnitsManager().selectedUnitsSetAltitude(ftToM(value)); });
this.#altitudeTypeSwitch = new Switch("altitude-type-switch", (value: boolean) => { getUnitsManager().selectedUnitsSetAltitudeType(value? "ASL": "AGL"); });
this.#altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => { getApp().getUnitsManager().selectedUnitsSetAltitude(ftToM(value)); });
this.#altitudeTypeSwitch = new Switch("altitude-type-switch", (value: boolean) => { getApp().getUnitsManager().selectedUnitsSetAltitudeType(value? "ASL": "AGL"); });
this.#speedSlider = new Slider("speed-slider", 0, 100, "kts", (value: number) => { getUnitsManager().selectedUnitsSetSpeed(knotsToMs(value)); });
this.#speedTypeSwitch = new Switch("speed-type-switch", (value: boolean) => { getUnitsManager().selectedUnitsSetSpeedType(value? "CAS": "GS"); });
this.#speedSlider = new Slider("speed-slider", 0, 100, "kts", (value: number) => { getApp().getUnitsManager().selectedUnitsSetSpeed(knotsToMs(value)); });
this.#speedTypeSwitch = new Switch("speed-type-switch", (value: boolean) => { getApp().getUnitsManager().selectedUnitsSetSpeedType(value? "CAS": "GS"); });
/* Option buttons */
// Reversing the ROEs so that the least "aggressive" option is always on the left
this.#optionButtons["ROE"] = ROEs.slice(0).reverse().map((option: string, index: number) => {
return this.#createOptionButton(option, `roe/${option.toLowerCase()}.svg`, ROEDescriptions.slice(0).reverse()[index], () => { getUnitsManager().selectedUnitsSetROE(option); });
return this.#createOptionButton(option, `roe/${option.toLowerCase()}.svg`, ROEDescriptions.slice(0).reverse()[index], () => { getApp().getUnitsManager().selectedUnitsSetROE(option); });
}).filter((button: HTMLButtonElement, index: number) => {return ROEs[index] !== "";});
this.#optionButtons["reactionToThreat"] = reactionsToThreat.map((option: string, index: number) => {
return this.#createOptionButton(option, `threat/${option.toLowerCase()}.svg`, reactionsToThreatDescriptions[index],() => { getUnitsManager().selectedUnitsSetReactionToThreat(option); });
return this.#createOptionButton(option, `threat/${option.toLowerCase()}.svg`, reactionsToThreatDescriptions[index],() => { getApp().getUnitsManager().selectedUnitsSetReactionToThreat(option); });
});
this.#optionButtons["emissionsCountermeasures"] = emissionsCountermeasures.map((option: string, index: number) => {
return this.#createOptionButton(option, `emissions/${option.toLowerCase()}.svg`, emissionsCountermeasuresDescriptions[index],() => { getUnitsManager().selectedUnitsSetEmissionsCountermeasures(option); });
return this.#createOptionButton(option, `emissions/${option.toLowerCase()}.svg`, emissionsCountermeasuresDescriptions[index],() => { getApp().getUnitsManager().selectedUnitsSetEmissionsCountermeasures(option); });
});
this.getElement().querySelector("#roe-buttons-container")?.append(...this.#optionButtons["ROE"]);
@@ -59,12 +58,12 @@ export class UnitControlPanel extends Panel {
/* On off switch */
this.#onOffSwitch = new Switch("on-off-switch", (value: boolean) => {
getUnitsManager().selectedUnitsSetOnOff(value);
getApp().getUnitsManager().selectedUnitsSetOnOff(value);
});
/* Follow roads switch */
this.#followRoadsSwitch = new Switch("follow-roads-switch", (value: boolean) => {
getUnitsManager().selectedUnitsSetFollowRoads(value);
getApp().getUnitsManager().selectedUnitsSetFollowRoads(value);
});
/* Advanced settings dialog */
@@ -84,7 +83,7 @@ export class UnitControlPanel extends Panel {
document.addEventListener("clearSelection", () => { this.hide() });
document.addEventListener("applyAdvancedSettings", () => {this.#applyAdvancedSettings();})
document.addEventListener("showAdvancedSettings", () => {
this.#updateAdvancedSettingsDialog(getUnitsManager().getSelectedUnits());
this.#updateAdvancedSettingsDialog(getApp().getUnitsManager().getSelectedUnits());
this.#advancedSettingsDialog.classList.remove("hide");
});
@@ -108,8 +107,8 @@ export class UnitControlPanel extends Panel {
}
addButtons() {
this.#units = getUnitsManager().getSelectedUnits();
this.#selectedUnitsTypes = getUnitsManager().getSelectedUnitsCategories();
this.#units = getApp().getUnitsManager().getSelectedUnits();
this.#selectedUnitsTypes = getApp().getUnitsManager().getSelectedUnitsCategories();
if (this.#units.length < 20) {
this.getElement().querySelector("#selected-units-container")?.replaceChildren(...this.#units.map((unit: Unit, index: number) => {
@@ -127,12 +126,12 @@ export class UnitControlPanel extends Panel {
button.addEventListener("click", ( ev:MouseEventInit ) => {
// Ctrl-click deselection
if ( ev.ctrlKey === true && ev.shiftKey === false && ev.altKey === false ) {
getUnitsManager().deselectUnit( unit.ID );
getApp().getUnitsManager().deselectUnit( unit.ID );
button.remove();
// Deselect all
} else {
getUnitsManager().deselectAllUnits();
getUnitsManager().selectUnit(unit.ID, true);
getApp().getUnitsManager().deselectAllUnits();
getApp().getUnitsManager().selectUnit(unit.ID, true);
}
});
return (button);
@@ -161,12 +160,12 @@ export class UnitControlPanel extends Panel {
if (this.#selectedUnitsTypes.length == 1) {
/* Flight controls */
var desiredAltitude = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitude()});
var desiredAltitudeType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitudeType()});
var desiredSpeed = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeed()});
var desiredSpeedType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeedType()});
var onOff = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getOnOff()});
var followRoads = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getFollowRoads()});
var desiredAltitude = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitude()});
var desiredAltitudeType = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitudeType()});
var desiredSpeed = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeed()});
var desiredSpeedType = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeedType()});
var onOff = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getOnOff()});
var followRoads = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getFollowRoads()});
this.#altitudeTypeSwitch.setValue(desiredAltitudeType != undefined? desiredAltitudeType == "ASL": undefined, false);
this.#speedTypeSwitch.setValue(desiredSpeedType != undefined? desiredSpeedType == "CAS": undefined, false);
@@ -328,7 +327,7 @@ export class UnitControlPanel extends Panel {
}
/* Send command and close */
var units = getUnitsManager().getSelectedUnits();
var units = getApp().getUnitsManager().getSelectedUnits();
if (units.length > 0)
units[0].setAdvancedOptions(isTanker, isAWACS, TACAN, radio, generalSettings);

View File

@@ -1,4 +1,3 @@
import { Ammo } from "../@types/unit";
import { aircraftDatabase } from "../unit/databases/aircraftdatabase";
import { Unit } from "../unit/unit";
import { Panel } from "./panel";

View File

@@ -1,36 +0,0 @@
import { OlympusApp } from "../olympusapp";
const templateParser = require( "ejs" );
export abstract class Plugin {
#olympusApp!:OlympusApp;
protected name = "";
#templateParser:any;
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;
this.#templateParser = templateParser;
}
getName() {
return this.name;
}
getOlympusApp() {
return this.#olympusApp;
}
getTemplateParser() {
return this.#templateParser;
}
}

View File

@@ -1,13 +1,55 @@
import { OlympusApp } from "../olympusapp";
import path from "path";
import { Manager } from "../other/manager";
import { getApp } from "..";
export class PluginsManager extends Manager {
constructor() {
super();
export class PluginManager extends Manager {
constructor( olympusApp:OlympusApp ) {
super( olympusApp );
var xhr = new XMLHttpRequest();
xhr.open('GET', "/plugins/list", true);
xhr.responseType = 'json';
xhr.onload = () => {
var status = xhr.status;
if (status === 200) {
this.#loadPlugins(xhr.response);
} else {
console.error(`Error retrieving plugins`)
}
};
xhr.send();
}
#loadPlugins(pluginsFolders: string[]) {
pluginsFolders.forEach((pluginName: string) => {
var xhr = new XMLHttpRequest();
xhr.open('GET', path.join("/plugins", pluginName, "index.js"), true);
xhr.responseType = 'text';
xhr.onload = () => {
var status = xhr.status;
if (status === 200) {
/* Inject the plugin style */
var link = document.createElement("link");
link.href = path.join("/plugins", pluginName, "style.css");
link.type = "text/css";
link.rel = "stylesheet";
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);
}
} else {
console.error(`Error retrieving plugin from ${pluginName}`)
}
};
xhr.send();
})
}
}

View File

@@ -1,5 +1,4 @@
import { LatLng } from "leaflet";
import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN } from "../@types/unit";
export class DataExtractor {
#seekPosition = 0;

View File

@@ -1,7 +1,10 @@
import { LatLng } from 'leaflet';
import { getConnectionStatusPanel, getInfoPopup, getLogPanel, getMissionHandler, getServerStatusPanel, getUnitsManager, getWeaponsManager, setLoginStatus } from '..';
import { GeneralSettings, Radio, TACAN } from '../@types/unit';
import { getApp } from '..';
import { AIRBASES_URI, BULLSEYE_URI, COMMANDS_URI, LOGS_URI, MISSION_URI, NONE, ROEs, UNITS_URI, WEAPONS_URI, emissionsCountermeasures, reactionsToThreat } from '../constants/constants';
import { ServerStatusPanel } from '../panels/serverstatuspanel';
import { LogPanel } from '../panels/logpanel';
import { Popup } from '../popups/popup';
import { ConnectionStatusPanel } from '../panels/connectionstatuspanel';
var connected: boolean = false;
var paused: boolean = false;
@@ -64,12 +67,12 @@ export function GET(callback: CallableFunction, uri: string, options?: ServerReq
lastUpdateTimes[uri] = callback(result);
if (result.frameRate !== undefined && result.load !== undefined)
getServerStatusPanel().update(result.frameRate, result.load);
(getApp().getPanelsManager().get("serverStatus") as ServerStatusPanel).update(result.frameRate, result.load);
}
} else if (xmlHttp.status == 401) {
/* Bad credentials */
console.error("Incorrect username/password");
setLoginStatus("failed");
getApp().setLoginStatus("failed");
} else {
/* Failure, probably disconnected */
setConnected(false);
@@ -351,74 +354,74 @@ export function startUpdate() {
if (!getPaused()) {
getMission((data: MissionData) => {
checkSessionHash(data.sessionHash);
getMissionHandler()?.updateMission(data);
getApp().getMissionManager()?.updateMission(data);
return data.time;
});
}
}, 1000);
window.setInterval(() => {
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE) {
if (!getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
getAirbases((data: AirbasesData) => {
checkSessionHash(data.sessionHash);
getMissionHandler()?.updateAirbases(data);
getApp().getMissionManager()?.updateAirbases(data);
return data.time;
});
}
}, 10000);
window.setInterval(() => {
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE){
if (!getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE){
getBullseye((data: BullseyesData) => {
checkSessionHash(data.sessionHash);
getMissionHandler()?.updateBullseyes(data);
getApp().getMissionManager()?.updateBullseyes(data);
return data.time;
});
}
}, 10000);
window.setInterval(() => {
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE) {
if (!getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
getLogs((data: any) => {
checkSessionHash(data.sessionHash);
getLogPanel().appendLogs(data.logs)
(getApp().getPanelsManager().get("log") as LogPanel).appendLogs(data.logs)
return data.time;
});
}
}, 1000);
window.setInterval(() => {
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE) {
if (!getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
getUnits((buffer: ArrayBuffer) => {
var time = getUnitsManager()?.update(buffer);
var time = getApp().getUnitsManager()?.update(buffer);
return time;
}, false);
}
}, 250);
window.setInterval(() => {
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE) {
if (!getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
getWeapons((buffer: ArrayBuffer) => {
var time = getWeaponsManager()?.update(buffer);
var time = getApp().getWeaponsManager()?.update(buffer);
return time;
}, false);
}
}, 250);
window.setInterval(() => {
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE) {
if (!getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
getUnits((buffer: ArrayBuffer) => {
var time = getUnitsManager()?.update(buffer);
var time = getApp().getUnitsManager()?.update(buffer);
return time;
}, true);
getConnectionStatusPanel()?.update(getConnected());
(getApp().getPanelsManager().get("connectionStatus") as ConnectionStatusPanel).update(getConnected());
}
}, 5000);
window.setInterval(() => {
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE) {
if (!getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
getWeapons((buffer: ArrayBuffer) => {
var time = getWeaponsManager()?.update(buffer);
var time = getApp().getWeaponsManager()?.update(buffer);
return time;
}, true);
}
@@ -428,29 +431,29 @@ export function startUpdate() {
export function refreshAll() {
getAirbases((data: AirbasesData) => {
checkSessionHash(data.sessionHash);
getMissionHandler()?.updateAirbases(data);
getApp().getMissionManager()?.updateAirbases(data);
return data.time;
});
getBullseye((data: BullseyesData) => {
checkSessionHash(data.sessionHash);
getMissionHandler()?.updateBullseyes(data);
getApp().getMissionManager()?.updateBullseyes(data);
return data.time;
});
getLogs((data: any) => {
checkSessionHash(data.sessionHash);
getLogPanel().appendLogs(data.logs)
(getApp().getPanelsManager().get("log") as LogPanel).appendLogs(data.logs)
return data.time;
});
getWeapons((buffer: ArrayBuffer) => {
var time = getWeaponsManager()?.update(buffer);
var time = getApp().getWeaponsManager()?.update(buffer);
return time;
}, true);
getUnits((buffer: ArrayBuffer) => {
var time = getUnitsManager()?.update(buffer);
var time = getApp().getUnitsManager()?.update(buffer);
return time;
}, true);
}
@@ -466,7 +469,7 @@ export function checkSessionHash(newSessionHash: string) {
export function setConnected(newConnected: boolean) {
if (connected != newConnected)
newConnected ? getInfoPopup().setText("Connected to DCS Olympus server") : getInfoPopup().setText("Disconnected from DCS Olympus server");
newConnected ? (getApp().getPopupsManager().get("infoPopup") as Popup).setText("Connected to DCS Olympus server") : (getApp().getPopupsManager().get("infoPopup") as Popup).setText("Disconnected from DCS Olympus server");
connected = newConnected;
if (connected) {
@@ -481,7 +484,7 @@ export function getConnected() {
export function setPaused(newPaused: boolean) {
paused = newPaused;
paused ? getInfoPopup().setText("View paused") : getInfoPopup().setText("View unpaused");
paused ? (getApp().getPopupsManager().get("infoPopup") as Popup).setText("View paused") : (getApp().getPopupsManager().get("infoPopup") as Popup).setText("View unpaused");
}
export function getPaused() {

View File

@@ -1,263 +0,0 @@
import { OlympusApp } from "../olympusapp";
import { ShortcutManager } from "../shortcut/shortcutmanager";
import { Unit } from "../unit/unit";
export class ControlTips {
#element:HTMLElement;
#cursorIsHoveringOverUnit:boolean = false;
#cursorIsHoveringOverAirbase:boolean = false;
#olympusApp:OlympusApp;
#shortcutManager:ShortcutManager;
constructor( ID:string, olympusApp:OlympusApp ) {
this.#element = <HTMLElement>document.getElementById( ID );
this.#olympusApp = olympusApp;
this.#shortcutManager = this.#olympusApp.getShortcutManager();
this.#shortcutManager.onKeyDown( () => {
this.#updateTips()
});
this.#shortcutManager.onKeyUp( () => {
this.#updateTips()
});
document.addEventListener( "airbaseMouseover", ( ev:CustomEventInit ) => {
this.#cursorIsHoveringOverAirbase = true;
this.#updateTips();
});
document.addEventListener( "airbaseMouseout", ( ev:CustomEventInit ) => {
this.#cursorIsHoveringOverAirbase = false;
this.#updateTips();
});
document.addEventListener( "unitDeselection", ( ev:CustomEvent ) => {
this.#updateTips();
});
document.addEventListener( "unitMouseover", ( ev:CustomEventInit ) => {
this.#cursorIsHoveringOverUnit = true;
this.#updateTips();
});
document.addEventListener( "unitMouseout", ( ev:CustomEventInit ) => {
this.#cursorIsHoveringOverUnit = false;
this.#updateTips();
});
document.addEventListener( "unitSelection", ( ev:CustomEvent ) => {
this.#updateTips()
});
this.#updateTips();
}
getElement() {
return this.#element;
}
#getOlympusApp() {
return this.#olympusApp;
}
toggle( bool?:boolean ) {
this.getElement().classList.toggle( "hide", bool );
this.#olympusApp.getFeatureSwitches().savePreference( "controlTips", !this.getElement().classList.contains( "hide" ) );
}
#updateTips() {
const combos:Array<object> = [
{
"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:any = combos.find( (combo:any) => this.#shortcutManager.keyComboMatches( combo.keys ) ) || combos[0];
const element = this.getElement();
element.innerHTML = "";
const a = this.#getOlympusApp();
let numSelectedUnits = 0;
let unitSelectionContainsControlled = false;
if ( this.#getOlympusApp().getUnitsManager() ) {
let selectedUnits = Object.values( this.#getOlympusApp().getUnitsManager().getSelectedUnits() );
numSelectedUnits = selectedUnits.length;
unitSelectionContainsControlled = selectedUnits.some( (unit:Unit) => unit.getControlled() );
}
currentCombo.tips.forEach( ( tip:any ) => {
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 !== this.#cursorIsHoveringOverAirbase ) {
return;
}
}
if ( typeof tip.showIfHoveringOverUnit === "boolean" ) {
if ( tip.showIfHoveringOverUnit !== this.#cursorIsHoveringOverUnit ) {
return;
}
}
element.innerHTML += `<div><span class="key">${tip.key}</span><span class="action">${tip.action}</span></div>`
});
}
}

View File

@@ -1,70 +1,42 @@
import { keyEventWasInInput } from "../other/utils";
interface IShortcut {
altKey?:boolean;
callback:CallableFunction;
ctrlKey?:boolean;
name?:string;
shiftKey?:boolean;
}
interface IShortcutKeyboard extends IShortcut {
code:string;
event?:"keydown"|"keyup";
}
interface IShortcutMouse extends IShortcut {
button:number;
event:"mousedown"|"mouseup";
}
export abstract class Shortcut {
#config: ShortcutOptions
#config:IShortcut
constructor( config:IShortcut ) {
constructor(config: ShortcutOptions) {
this.#config = config;
}
getConfig() {
return this.#config;
}
}
export class ShortcutKeyboard extends Shortcut {
constructor( config:IShortcutKeyboard ) {
constructor(config: KeyboardShortcutOptions) {
config.event = config.event || "keyup";
super( config );
super(config);
document.addEventListener( config.event, ( ev:any ) => {
if ( ev instanceof KeyboardEvent === false || keyEventWasInInput( ev )) {
document.addEventListener(config.event, (ev: any) => {
if (ev instanceof KeyboardEvent === false || keyEventWasInInput(ev)) {
return;
}
if ( config.code !== ev.code ) {
if (config.code !== ev.code) {
return;
}
if ( ( ( typeof config.altKey !== "boolean" ) || ( typeof config.altKey === "boolean" && ev.altKey === config.altKey ) )
&& ( ( typeof config.ctrlKey !== "boolean" ) || ( typeof config.ctrlKey === "boolean" && ev.ctrlKey === config.ctrlKey ) )
&& ( ( typeof config.shiftKey !== "boolean" ) || ( typeof config.shiftKey === "boolean" && ev.shiftKey === config.shiftKey ) ) ) {
config.callback( ev );
if (((typeof config.altKey !== "boolean") || (typeof config.altKey === "boolean" && ev.altKey === config.altKey))
&& ((typeof config.ctrlKey !== "boolean") || (typeof config.ctrlKey === "boolean" && ev.ctrlKey === config.ctrlKey))
&& ((typeof config.shiftKey !== "boolean") || (typeof config.shiftKey === "boolean" && ev.shiftKey === config.shiftKey))) {
config.callback(ev);
}
});
}
}
export class ShortcutMouse extends Shortcut {
constructor( config:IShortcutMouse ) {
super( config );
constructor(config: MouseShortcutOptions) {
super(config);
}
}

View File

@@ -1,33 +1,32 @@
import { OlympusApp } from "../olympusapp";
import { Manager } from "../other/manager";
import { Shortcut } from "./shortcut";
export class ShortcutManager extends Manager {
#keysBeingHeld:string[] = [];
#keyDownCallbacks:CallableFunction[] = [];
#keyUpCallbacks:CallableFunction[] = [];
#keysBeingHeld: string[] = [];
#keyDownCallbacks: CallableFunction[] = [];
#keyUpCallbacks: CallableFunction[] = [];
constructor( olympusApp:OlympusApp ) {
super( olympusApp );
constructor() {
document.addEventListener( "keydown", ( ev:KeyboardEvent ) => {
if ( this.#keysBeingHeld.indexOf( ev.code ) < 0 ) {
this.#keysBeingHeld.push( ev.code )
super();
document.addEventListener("keydown", (ev: KeyboardEvent) => {
if (this.#keysBeingHeld.indexOf(ev.code) < 0) {
this.#keysBeingHeld.push(ev.code)
}
this.#keyDownCallbacks.forEach( callback => callback( ev ) );
this.#keyDownCallbacks.forEach(callback => callback(ev));
});
document.addEventListener( "keyup", ( ev:KeyboardEvent ) => {
this.#keysBeingHeld = this.#keysBeingHeld.filter( held => held !== ev.code );
this.#keyUpCallbacks.forEach( callback => callback( ev ) );
document.addEventListener("keyup", (ev: KeyboardEvent) => {
this.#keysBeingHeld = this.#keysBeingHeld.filter(held => held !== ev.code);
this.#keyUpCallbacks.forEach(callback => callback(ev));
});
}
add( name:string, shortcut:Shortcut ) {
super.add( name, shortcut );
add(name: string, shortcut: Shortcut) {
super.add(name, shortcut);
return this;
}
@@ -35,24 +34,20 @@ export class ShortcutManager extends Manager {
return this.#keysBeingHeld;
}
keyComboMatches( combo:string[] ) {
keyComboMatches(combo: string[]) {
const heldKeys = this.getKeysBeingHeld();
if ( combo.length !== heldKeys.length ) {
if (combo.length !== heldKeys.length) {
return false;
}
return combo.every( key => heldKeys.indexOf( key ) > -1 );
return combo.every(key => heldKeys.indexOf(key) > -1);
}
onKeyDown( callback:CallableFunction ) {
this.#keyDownCallbacks.push( callback );
onKeyDown(callback: CallableFunction) {
this.#keyDownCallbacks.push(callback);
}
onKeyUp( callback:CallableFunction ) {
this.#keyUpCallbacks.push( callback );
onKeyUp(callback: CallableFunction) {
this.#keyUpCallbacks.push(callback);
}
}

View File

@@ -1,4 +1,4 @@
import { getMissionHandler } from "../..";
import { getApp } from "../..";
import { GAME_MASTER } from "../../constants/constants";
import { UnitDatabase } from "./unitdatabase"
@@ -12,7 +12,7 @@ export class AircraftDatabase extends UnitDatabase {
}
getSpawnPointsByName(name: string) {
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || !getMissionHandler().getCommandModeOptions().restrictSpawns)
if (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER || !getApp().getMissionManager().getCommandModeOptions().restrictSpawns)
return 0;
const blueprint = this.getByName(name);

View File

@@ -1,4 +1,4 @@
import { getMissionHandler } from "../..";
import { getApp } from "../..";
import { GAME_MASTER } from "../../constants/constants";
import { UnitDatabase } from "./unitdatabase"
@@ -8,7 +8,7 @@ export class GroundUnitDatabase extends UnitDatabase {
}
getSpawnPointsByName(name: string) {
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || !getMissionHandler().getCommandModeOptions().restrictSpawns)
if (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER || !getApp().getMissionManager().getCommandModeOptions().restrictSpawns)
return 0;
const blueprint = this.getByName(name);

View File

@@ -1,4 +1,4 @@
import { getMissionHandler } from "../..";
import { getApp } from "../..";
import { GAME_MASTER } from "../../constants/constants";
import { UnitDatabase } from "./unitdatabase"
@@ -8,7 +8,7 @@ export class HelicopterDatabase extends UnitDatabase {
}
getSpawnPointsByName(name: string) {
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || !getMissionHandler().getCommandModeOptions().restrictSpawns)
if (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER || !getApp().getMissionManager().getCommandModeOptions().restrictSpawns)
return 0;
const blueprint = this.getByName(name);

View File

@@ -1,4 +1,4 @@
import { getMissionHandler } from "../..";
import { getApp } from "../..";
import { GAME_MASTER } from "../../constants/constants";
import { UnitDatabase } from "./unitdatabase"
@@ -8,7 +8,7 @@ export class NavyUnitDatabase extends UnitDatabase {
}
getSpawnPointsByName(name: string) {
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || !getMissionHandler().getCommandModeOptions().restrictSpawns)
if (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER || !getApp().getMissionManager().getCommandModeOptions().restrictSpawns)
return 0;
const blueprint = this.getByName(name);

View File

@@ -1,7 +1,6 @@
import { LatLng } from "leaflet";
import { getMissionHandler, getUnitsManager } from "../..";
import { getApp } from "../..";
import { GAME_MASTER } from "../../constants/constants";
import { UnitBlueprint } from "../../@types/unitdatabase";
export class UnitDatabase {
blueprints: { [key: string]: UnitBlueprint } = {};
@@ -44,15 +43,15 @@ export class UnitDatabase {
}
getBlueprints() {
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || !getMissionHandler().getCommandModeOptions().restrictSpawns)
if (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER || !getApp().getMissionManager().getCommandModeOptions().restrictSpawns)
return this.blueprints;
else {
var filteredBlueprints: { [key: string]: UnitBlueprint } = {};
for (let unit in this.blueprints) {
const blueprint = this.blueprints[unit];
if (this.getSpawnPointsByName(blueprint.name) <= getMissionHandler().getAvailableSpawnPoints() &&
getMissionHandler().getCommandModeOptions().eras.includes(blueprint.era) &&
(!getMissionHandler().getCommandModeOptions().restrictToCoalition || blueprint.coalition === getMissionHandler().getCommandedCoalition() || blueprint.coalition === undefined)) {
if (this.getSpawnPointsByName(blueprint.name) <= getApp().getMissionManager().getAvailableSpawnPoints() &&
getApp().getMissionManager().getCommandModeOptions().eras.includes(blueprint.era) &&
(!getApp().getMissionManager().getCommandModeOptions().restrictToCoalition || blueprint.coalition === getApp().getMissionManager().getCommandedCoalition() || blueprint.coalition === undefined)) {
filteredBlueprints[unit] = blueprint;
}
}
@@ -201,7 +200,7 @@ export class UnitDatabase {
var row = Math.floor(idx / gridSize);
var col = idx - row * gridSize;
var location = new LatLng(initialPosition.lat + col * step, initialPosition.lng + row * step)
getUnitsManager().spawnUnits(this.getCategory(), [{unitType: unitBlueprint.name, location: location, altitude: 1000, loadout: "", liveryID: ""}]);
getApp().getUnitsManager().spawnUnits(this.getCategory(), [{unitType: unitBlueprint.name, location: location, altitude: 1000, loadout: "", liveryID: ""}]);
})
}

View File

@@ -1,5 +1,5 @@
import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map, Point } from 'leaflet';
import { getMap, getMissionHandler, getUnitsManager, getWeaponsManager } from '..';
import { getApp } 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/markers/custommarker';
@@ -7,12 +7,10 @@ import { SVGInjector } from '@tanem/svg-injector';
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 './databases/groundunitdatabase';
import { navyUnitDatabase } from './databases/navyunitdatabase';
import { Weapon } from '../weapon/weapon';
import { LoadoutBlueprint } from '../@types/unitdatabase';
var pathIcon = new Icon({
iconUrl: '/resources/theme/images/markers/marker-icon.png',
@@ -147,7 +145,7 @@ export class Unit extends CustomMarker {
this.#selectable = true;
this.#pathPolyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1 });
this.#pathPolyline.addTo(getMap());
this.#pathPolyline.addTo(getApp().getMap());
this.#contactsPolylines = [];
this.#targetPositionMarker = new TargetMarker(new LatLng(0, 0));
this.#targetPositionPolyline = new Polyline([], { color: '#FF0000', weight: 3, opacity: 0.5, smoothFactor: 1 });
@@ -165,7 +163,7 @@ export class Unit extends CustomMarker {
this.setHighlighted(false);
document.dispatchEvent(new CustomEvent("unitMouseout", { detail: this }));
});
getMap().on("zoomend", () => { this.#onZoom(); })
getApp().getMap().on("zoomend", () => { this.#onZoom(); })
/* Deselect units if they are hidden */
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
@@ -190,7 +188,7 @@ export class Unit extends CustomMarker {
/********************** Unit data *************************/
setData(dataExtractor: DataExtractor) {
var updateMarker = !getMap().hasLayer(this);
var updateMarker = !getApp().getMap().hasLayer(this);
var datumIndex = 0;
while (datumIndex != DataIndexes.endOfData) {
@@ -243,7 +241,7 @@ export class Unit extends CustomMarker {
if (updateMarker)
this.#updateMarker();
if (this.getSelected() || getMap().getCenterUnit() === this)
if (this.getSelected() || getApp().getMap().getCenterUnit() === this)
document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this }));
}
@@ -342,7 +340,7 @@ export class Unit extends CustomMarker {
}
this.getElement()?.querySelector(`.unit`)?.toggleAttribute("data-is-selected", selected);
if (this.getCategory() === "GroundUnit" && getMap().getZoom() < 13) {
if (this.getCategory() === "GroundUnit" && getApp().getMap().getZoom() < 13) {
if (this.#isLeader)
this.getGroupMembers().forEach((unit: Unit) => unit.setSelected(selected));
else
@@ -393,11 +391,11 @@ export class Unit extends CustomMarker {
}
getGroupMembers() {
return Object.values(getUnitsManager().getUnits()).filter((unit: Unit) => { return unit != this && unit.#groupName === this.#groupName; });
return Object.values(getApp().getUnitsManager().getUnits()).filter((unit: Unit) => { return unit != this && unit.#groupName === this.#groupName; });
}
belongsToCommandedCoalition() {
if (getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER && getMissionHandler().getCommandedCoalition() !== this.#coalition)
if (getApp().getMissionManager().getCommandModeOptions().commandMode !== GAME_MASTER && getApp().getMissionManager().getCommandedCoalition() !== this.#coalition)
return false;
return true;
}
@@ -523,13 +521,13 @@ export class Unit extends CustomMarker {
/********************** Visibility *************************/
updateVisibility() {
const hiddenUnits = getMap().getHiddenTypes();
const hiddenUnits = getApp().getMap().getHiddenTypes();
var hidden = ((this.#human && hiddenUnits.includes("human")) ||
(this.#controlled == false && hiddenUnits.includes("dcs")) ||
(hiddenUnits.includes(this.getMarkerCategory())) ||
(hiddenUnits.includes(this.#coalition)) ||
(!this.belongsToCommandedCoalition() && (this.#detectionMethods.length == 0 || (this.#detectionMethods.length == 1 && this.#detectionMethods[0] === RWR))) ||
(getMap().getVisibilityOptions()[HIDE_GROUP_MEMBERS] && !this.#isLeader && this.getCategory() == "GroundUnit" && getMap().getZoom() < 13 && (this.belongsToCommandedCoalition() || (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0)))) &&
(getApp().getMap().getVisibilityOptions()[HIDE_GROUP_MEMBERS] && !this.#isLeader && this.getCategory() == "GroundUnit" && getApp().getMap().getZoom() < 13 && (this.belongsToCommandedCoalition() || (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0)))) &&
!(this.getSelected());
this.setHidden(hidden || !this.#alive);
@@ -539,16 +537,16 @@ export class Unit extends CustomMarker {
this.#hidden = hidden;
/* Add the marker if not present */
if (!getMap().hasLayer(this) && !this.getHidden()) {
if (getMap().isZooming())
this.once("zoomend", () => { this.addTo(getMap()) })
if (!getApp().getMap().hasLayer(this) && !this.getHidden()) {
if (getApp().getMap().isZooming())
this.once("zoomend", () => { this.addTo(getApp().getMap()) })
else
this.addTo(getMap());
this.addTo(getApp().getMap());
}
/* Hide the marker if necessary*/
if (getMap().hasLayer(this) && this.getHidden()) {
getMap().removeLayer(this);
if (getApp().getMap().hasLayer(this) && this.getHidden()) {
getApp().getMap().removeLayer(this);
}
}
@@ -573,7 +571,7 @@ export class Unit extends CustomMarker {
}
getLeader() {
return getUnitsManager().getUnitByID(this.#leaderID);
return getApp().getUnitsManager().getUnitByID(this.#leaderID);
}
canFulfillRole(roles: string | string[]) {
@@ -591,7 +589,7 @@ export class Unit extends CustomMarker {
isInViewport() {
const mapBounds = getMap().getBounds();
const mapBounds = getApp().getMap().getBounds();
const unitPos = this.getPosition();
return (unitPos.lng > mapBounds.getWest()
@@ -739,9 +737,9 @@ export class Unit extends CustomMarker {
/***********************************************/
#onClick(e: any) {
if (!this.#preventClick) {
if (getMap().getState() === IDLE || getMap().getState() === MOVE_UNIT || e.originalEvent.ctrlKey) {
if (getApp().getMap().getState() === IDLE || getApp().getMap().getState() === MOVE_UNIT || e.originalEvent.ctrlKey) {
if (!e.originalEvent.ctrlKey)
getUnitsManager().deselectAllUnits();
getApp().getUnitsManager().deselectAllUnits();
this.setSelected(!this.getSelected());
const detail = { "detail": { "unit": this } };
@@ -756,7 +754,7 @@ export class Unit extends CustomMarker {
}
#onDoubleClick(e: any) {
const unitsManager = getUnitsManager();
const unitsManager = getApp().getUnitsManager();
Object.values(unitsManager.getUnits()).forEach((unit: Unit) => {
if (unit.getAlive() === true && unit.getName() === this.getName() && unit.isInViewport())
unitsManager.selectUnit(unit.ID, false);
@@ -768,14 +766,14 @@ export class Unit extends CustomMarker {
#onContextMenu(e: any) {
var options: { [key: string]: { text: string, tooltip: string } } = {};
const selectedUnits = getUnitsManager().getSelectedUnits();
const selectedUnitTypes = getUnitsManager().getSelectedUnitsCategories();
const selectedUnits = getApp().getUnitsManager().getSelectedUnits();
const selectedUnitTypes = getApp().getUnitsManager().getSelectedUnitsCategories();
options["center-map"] = { text: "Center map", tooltip: "Center the map on the unit and follow it" };
if (selectedUnits.length > 0 && !(selectedUnits.length == 1 && (selectedUnits.includes(this)))) {
options["attack"] = { text: "Attack", tooltip: "Attack the unit using A/A or A/G weapons" };
if (getUnitsManager().getSelectedUnitsCategories().length == 1 && getUnitsManager().getSelectedUnitsCategories()[0] === "Aircraft")
if (getApp().getUnitsManager().getSelectedUnitsCategories().length == 1 && getApp().getUnitsManager().getSelectedUnitsCategories()[0] === "Aircraft")
options["follow"] = { text: "Follow", tooltip: "Follow the unit at a user defined distance and position" };;
}
else if ((selectedUnits.length > 0 && (selectedUnits.includes(this))) || selectedUnits.length == 0) {
@@ -784,13 +782,13 @@ export class Unit extends CustomMarker {
}
}
if (selectedUnitTypes.length === 1 && ["NavyUnit", "GroundUnit"].includes(selectedUnitTypes[0]) && getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getCoalition()}) !== undefined)
if (selectedUnitTypes.length === 1 && ["NavyUnit", "GroundUnit"].includes(selectedUnitTypes[0]) && getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getCoalition()}) !== undefined)
options["group"] = { text: "Create group", tooltip: "Create a group from the selected units." };
if (Object.keys(options).length > 0) {
getMap().showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
getMap().getUnitContextMenu().setOptions(options, (option: string) => {
getMap().hideUnitContextMenu();
getApp().getMap().showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
getApp().getMap().getUnitContextMenu().setOptions(options, (option: string) => {
getApp().getMap().hideUnitContextMenu();
this.#executeAction(e, option);
});
}
@@ -798,13 +796,13 @@ export class Unit extends CustomMarker {
#executeAction(e: any, action: string) {
if (action === "center-map")
getMap().centerOnUnit(this.ID);
getApp().getMap().centerOnUnit(this.ID);
if (action === "attack")
getUnitsManager().selectedUnitsAttackUnit(this.ID);
getApp().getUnitsManager().selectedUnitsAttackUnit(this.ID);
else if (action === "refuel")
getUnitsManager().selectedUnitsRefuel();
getApp().getUnitsManager().selectedUnitsRefuel();
else if (action === "group")
getUnitsManager().selectedUnitsCreateGroup();
getApp().getUnitsManager().selectedUnitsCreateGroup();
else if (action === "follow")
this.#showFollowOptions(e);
}
@@ -823,12 +821,12 @@ export class Unit extends CustomMarker {
'custom': { text: "Custom", tooltip: "Set a custom formation position" },
}
getMap().getUnitContextMenu().setOptions(options, (option: string) => {
getMap().hideUnitContextMenu();
getApp().getMap().getUnitContextMenu().setOptions(options, (option: string) => {
getApp().getMap().hideUnitContextMenu();
this.#applyFollowOptions(option);
});
getMap().showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
getApp().getMap().showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
}
#applyFollowOptions(action: string) {
@@ -856,12 +854,12 @@ export class Unit extends CustomMarker {
var y = upDown;
var z = distance * Math.sin(angleRad);
getUnitsManager().selectedUnitsFollowUnit(this.ID, { "x": x, "y": y, "z": z });
getApp().getUnitsManager().selectedUnitsFollowUnit(this.ID, { "x": x, "y": y, "z": z });
}
});
}
else {
getUnitsManager().selectedUnitsFollowUnit(this.ID, undefined, action);
getApp().getUnitsManager().selectedUnitsFollowUnit(this.ID, undefined, action);
}
}
@@ -879,7 +877,7 @@ export class Unit extends CustomMarker {
this.#miniMapMarker.setStyle({ color: "#ff5858" });
else
this.#miniMapMarker.setStyle({ color: "#247be2" });
this.#miniMapMarker.addTo(getMap().getMiniMapLayerGroup());
this.#miniMapMarker.addTo(getApp().getMap().getMiniMapLayerGroup());
this.#miniMapMarker.bringToBack();
}
else {
@@ -890,8 +888,8 @@ export class Unit extends CustomMarker {
}
}
else {
if (this.#miniMapMarker != null && getMap().getMiniMapLayerGroup().hasLayer(this.#miniMapMarker)) {
getMap().getMiniMapLayerGroup().removeLayer(this.#miniMapMarker);
if (this.#miniMapMarker != null && getApp().getMap().getMiniMapLayerGroup().hasLayer(this.#miniMapMarker)) {
getApp().getMap().getMiniMapLayerGroup().removeLayer(this.#miniMapMarker);
this.#miniMapMarker = null;
}
}
@@ -976,25 +974,25 @@ export class Unit extends CustomMarker {
}
/* Set vertical offset for altitude stacking */
var pos = getMap().latLngToLayerPoint(this.getLatLng()).round();
var pos = getApp().getMap().latLngToLayerPoint(this.getLatLng()).round();
this.setZIndexOffset(1000 + Math.floor(this.#position.alt as number) - pos.y + (this.#highlighted || this.#selected ? 5000 : 0));
}
}
#drawPath() {
if (this.#activePath != undefined && getMap().getVisibilityOptions()[SHOW_UNIT_PATHS]) {
if (this.#activePath != undefined && getApp().getMap().getVisibilityOptions()[SHOW_UNIT_PATHS]) {
var points = [];
points.push(new LatLng(this.#position.lat, this.#position.lng));
/* Add markers if missing */
while (this.#pathMarkers.length < Object.keys(this.#activePath).length) {
var marker = new Marker([0, 0], { icon: pathIcon }).addTo(getMap());
var marker = new Marker([0, 0], { icon: pathIcon }).addTo(getApp().getMap());
this.#pathMarkers.push(marker);
}
/* Remove markers if too many */
while (this.#pathMarkers.length > Object.keys(this.#activePath).length) {
getMap().removeLayer(this.#pathMarkers[this.#pathMarkers.length - 1]);
getApp().getMap().removeLayer(this.#pathMarkers[this.#pathMarkers.length - 1]);
this.#pathMarkers.splice(this.#pathMarkers.length - 1, 1)
}
@@ -1016,7 +1014,7 @@ export class Unit extends CustomMarker {
#clearPath() {
for (let WP in this.#pathMarkers) {
getMap().removeLayer(this.#pathMarkers[WP]);
getApp().getMap().removeLayer(this.#pathMarkers[WP]);
}
this.#pathMarkers = [];
this.#pathPolyline.setLatLngs([]);
@@ -1024,25 +1022,25 @@ export class Unit extends CustomMarker {
#drawContacts() {
this.#clearContacts();
if (getMap().getVisibilityOptions()[SHOW_CONTACT_LINES]) {
if (getApp().getMap().getVisibilityOptions()[SHOW_CONTACT_LINES]) {
for (let index in this.#contacts) {
var contactData = this.#contacts[index];
var contact: Unit | Weapon | null;
if (contactData.ID in getUnitsManager().getUnits())
contact = getUnitsManager().getUnitByID(contactData.ID);
if (contactData.ID in getApp().getUnitsManager().getUnits())
contact = getApp().getUnitsManager().getUnitByID(contactData.ID);
else
contact = getWeaponsManager().getWeaponByID(contactData.ID);
contact = getApp().getWeaponsManager().getWeaponByID(contactData.ID);
if (contact != null && contact.getAlive()) {
var startLatLng = new LatLng(this.#position.lat, this.#position.lng);
var endLatLng: LatLng;
if (contactData.detectionMethod === RWR) {
var bearingToContact = bearing(this.#position.lat, this.#position.lng, contact.getPosition().lat, contact.getPosition().lng);
var startXY = getMap().latLngToContainerPoint(startLatLng);
var startXY = getApp().getMap().latLngToContainerPoint(startLatLng);
var endX = startXY.x + 80 * Math.sin(deg2rad(bearingToContact));
var endY = startXY.y - 80 * Math.cos(deg2rad(bearingToContact));
endLatLng = getMap().containerPointToLatLng(new Point(endX, endY));
endLatLng = getApp().getMap().containerPointToLatLng(new Point(endX, endY));
}
else
endLatLng = new LatLng(contact.getPosition().lat, contact.getPosition().lng);
@@ -1057,7 +1055,7 @@ export class Unit extends CustomMarker {
else
color = "#FFFFFF";
var contactPolyline = new Polyline([startLatLng, endLatLng], { color: color, weight: 3, opacity: 1, smoothFactor: 1, dashArray: "4, 8" });
contactPolyline.addTo(getMap());
contactPolyline.addTo(getApp().getMap());
this.#contactsPolylines.push(contactPolyline)
}
}
@@ -1066,17 +1064,17 @@ export class Unit extends CustomMarker {
#clearContacts() {
for (let index in this.#contactsPolylines) {
getMap().removeLayer(this.#contactsPolylines[index])
getApp().getMap().removeLayer(this.#contactsPolylines[index])
}
}
#drawTarget() {
if (this.#targetPosition.lat != 0 && this.#targetPosition.lng != 0 && getMap().getVisibilityOptions()[SHOW_UNIT_PATHS]) {
if (this.#targetPosition.lat != 0 && this.#targetPosition.lng != 0 && getApp().getMap().getVisibilityOptions()[SHOW_UNIT_PATHS]) {
this.#drawTargetPosition(this.#targetPosition);
}
else if (this.#targetID != 0 && getMap().getVisibilityOptions()[SHOW_UNIT_TARGETS]) {
const target = getUnitsManager().getUnitByID(this.#targetID);
if (target && (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || (this.belongsToCommandedCoalition() && getUnitsManager().getUnitDetectedMethods(target).some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))))) {
else if (this.#targetID != 0 && getApp().getMap().getVisibilityOptions()[SHOW_UNIT_TARGETS]) {
const target = getApp().getUnitsManager().getUnitByID(this.#targetID);
if (target && (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER || (this.belongsToCommandedCoalition() && getApp().getUnitsManager().getUnitDetectedMethods(target).some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))))) {
this.#drawTargetPosition(target.getPosition());
}
}
@@ -1085,20 +1083,20 @@ export class Unit extends CustomMarker {
}
#drawTargetPosition(targetPosition: LatLng) {
if (!getMap().hasLayer(this.#targetPositionMarker))
this.#targetPositionMarker.addTo(getMap());
if (!getMap().hasLayer(this.#targetPositionPolyline))
this.#targetPositionPolyline.addTo(getMap());
if (!getApp().getMap().hasLayer(this.#targetPositionMarker))
this.#targetPositionMarker.addTo(getApp().getMap());
if (!getApp().getMap().hasLayer(this.#targetPositionPolyline))
this.#targetPositionPolyline.addTo(getApp().getMap());
this.#targetPositionMarker.setLatLng(new LatLng(targetPosition.lat, targetPosition.lng));
this.#targetPositionPolyline.setLatLngs([new LatLng(this.#position.lat, this.#position.lng), new LatLng(targetPosition.lat, targetPosition.lng)])
}
#clearTarget() {
if (getMap().hasLayer(this.#targetPositionMarker))
this.#targetPositionMarker.removeFrom(getMap());
if (getApp().getMap().hasLayer(this.#targetPositionMarker))
this.#targetPositionMarker.removeFrom(getApp().getMap());
if (getMap().hasLayer(this.#targetPositionPolyline))
this.#targetPositionPolyline.removeFrom(getMap());
if (getApp().getMap().hasLayer(this.#targetPositionPolyline))
this.#targetPositionPolyline.removeFrom(getApp().getMap());
}
#onZoom() {

View File

@@ -1,18 +1,19 @@
import { LatLng, LatLngBounds } from "leaflet";
import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler, getUnitsManager, getWeaponsManager } from "..";
import { getApp } from "..";
import { Unit } from "./unit";
import { cloneUnits, deleteUnit, spawnAircrafts, spawnGroundUnits, spawnHelicopters, spawnNavyUnits } from "../server/server";
import { cloneUnits, 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/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, UnitSpawnTable } from "../@types/unit";
import { citiesDatabase } from "./citiesDatabase";
import { aircraftDatabase } from "./databases/aircraftdatabase";
import { helicopterDatabase } from "./databases/helicopterdatabase";
import { navyUnitDatabase } from "./databases/navyunitdatabase";
import { TemporaryUnitMarker } from "../map/markers/temporaryunitmarker";
import { Popup } from "../popups/popup";
import { HotgroupPanel } from "../panels/hotgrouppanel";
/** 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
@@ -124,12 +125,12 @@ export class UnitsManager {
/* If we are not in Game Master mode, visibility of units by the user is determined by the detections of the units themselves. This is performed here.
This operation is computationally expensive, therefore it is only performed when #requestDetectionUpdate is true. This happens whenever a change in the detectionUpdates is detected
*/
if (this.#requestDetectionUpdate && getMissionHandler().getCommandModeOptions().commandMode != GAME_MASTER) {
if (this.#requestDetectionUpdate && getApp().getMissionManager().getCommandModeOptions().commandMode != GAME_MASTER) {
/* Create a dictionary of empty detection methods arrays */
var detectionMethods: { [key: string]: number[] } = {};
for (let ID in this.#units)
detectionMethods[ID] = [];
for (let ID in getWeaponsManager().getWeapons())
for (let ID in getApp().getWeaponsManager().getWeapons())
detectionMethods[ID] = [];
/* Fill the array with the detection methods */
@@ -152,8 +153,8 @@ export class UnitsManager {
}
/* Set the detection methods for every weapon (weapons must be detected too) */
for (let ID in getWeaponsManager().getWeapons()) {
const weapon = getWeaponsManager().getWeaponByID(parseInt(ID));
for (let ID in getApp().getWeaponsManager().getWeapons()) {
const weapon = getApp().getWeaponsManager().getWeaponByID(parseInt(ID));
weapon?.setDetectionMethods(detectionMethods[ID]);
}
@@ -636,7 +637,7 @@ export class UnitsManager {
}
cloneUnits(units, true, 0 /* No spawn points, we delete the original units */);
} else {
getInfoPopup().setText(`Groups can only be created from units of the same category`);
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Groups can only be created from units of the same category`);
}
}
@@ -659,7 +660,7 @@ export class UnitsManager {
selectedUnits[idx].setHotgroup(hotgroup);
}
this.#showActionMessage(selectedUnits, `added to hotgroup ${hotgroup}`);
getHotgroupPanel().refreshHotgroups();
(getApp().getPanelsManager().get("hotgroup") as HotgroupPanel).refreshHotgroups();
}
/** Delete the selected units
@@ -730,7 +731,7 @@ export class UnitsManager {
selectedUnitsCopy() {
/* A JSON is used to deepcopy the units, creating a "snapshot" of their properties at the time of the copy */
this.#copiedUnits = JSON.parse(JSON.stringify(this.getSelectedUnits().map((unit: Unit) => { return unit.getData() }))); /* Can be applied to humans too */
getInfoPopup().setText(`${this.#copiedUnits.length} units copied`);
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`${this.#copiedUnits.length} units copied`);
}
/*********************** Unit manipulation functions ************************/
@@ -742,9 +743,9 @@ export class UnitsManager {
let spawnPoints = 0;
/* If spawns are restricted, check that the user has the necessary spawn points */
if (getMissionHandler().getCommandModeOptions().commandMode != GAME_MASTER) {
if (getMissionHandler().getCommandModeOptions().restrictSpawns && getMissionHandler().getRemainingSetupTime() < 0) {
getInfoPopup().setText(`Units can be pasted only during SETUP phase`);
if (getApp().getMissionManager().getCommandModeOptions().commandMode != GAME_MASTER) {
if (getApp().getMissionManager().getCommandModeOptions().restrictSpawns && getApp().getMissionManager().getRemainingSetupTime() < 0) {
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Units can be pasted only during SETUP phase`);
return false;
}
@@ -754,8 +755,8 @@ export class UnitsManager {
spawnPoints += unitSpawnPoints;
})
if (spawnPoints > getMissionHandler().getAvailableSpawnPoints()) {
getInfoPopup().setText("Not enough spawn points available!");
if (spawnPoints > getApp().getMissionManager().getAvailableSpawnPoints()) {
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Not enough spawn points available!");
return false;
}
}
@@ -784,8 +785,8 @@ export class UnitsManager {
var units: { ID: number, location: LatLng }[] = [];
let markers: TemporaryUnitMarker[] = [];
groups[groupName].forEach((unit: UnitData) => {
var position = new LatLng(getMap().getMouseCoordinates().lat + unit.position.lat - avgLat, getMap().getMouseCoordinates().lng + unit.position.lng - avgLng);
markers.push(getMap().addTemporaryMarker(position, unit.name, unit.coalition));
var position = new LatLng(getApp().getMap().getMouseCoordinates().lat + unit.position.lat - avgLat, getApp().getMap().getMouseCoordinates().lng + unit.position.lng - avgLng);
markers.push(getApp().getMap().addTemporaryMarker(position, unit.name, unit.coalition));
units.push({ ID: unit.ID, location: position });
});
@@ -797,10 +798,10 @@ export class UnitsManager {
}
});
}
getInfoPopup().setText(`${this.#copiedUnits.length} units pasted`);
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`${this.#copiedUnits.length} units pasted`);
}
else {
getInfoPopup().setText("No units copied!");
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("No units copied!");
}
}
@@ -888,7 +889,7 @@ export class UnitsManager {
var units = aliveUnits.map((unit: UnitData) => {
return { unitType: unit.name, location: unit.position, liveryID: "" }
});
getUnitsManager().spawnUnits(groups[groupName][0].category, units, groups[groupName][0].coalition, true);
getApp().getUnitsManager().spawnUnits(groups[groupName][0].category, units, groups[groupName][0].coalition, true);
}
}
};
@@ -911,18 +912,18 @@ export class UnitsManager {
spawnUnits(category: string, units: UnitSpawnTable[], coalition: string = "blue", immediate: boolean = true, airbase: string = "", country: string = "", callback: CallableFunction = () => {}) {
var spawnPoints = 0;
var spawnFunction = () => {};
var spawnsRestricted = getMissionHandler().getCommandModeOptions().restrictSpawns && getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER;
var spawnsRestricted = getApp().getMissionManager().getCommandModeOptions().restrictSpawns && getApp().getMissionManager().getRemainingSetupTime() < 0 && getApp().getMissionManager().getCommandModeOptions().commandMode !== GAME_MASTER;
if (category === "Aircraft") {
if (airbase == "" && spawnsRestricted) {
getInfoPopup().setText("Aircrafts can be air spawned during the SETUP phase only");
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Aircrafts can be air spawned during the SETUP phase only");
return false;
}
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => {return points + aircraftDatabase.getSpawnPointsByName(unit.unitType)}, 0);
spawnFunction = () => spawnAircrafts(units, coalition, airbase, country, immediate, spawnPoints, callback);
} else if (category === "Helicopter") {
if (airbase == "" && spawnsRestricted) {
getInfoPopup().setText("Helicopters can be air spawned during the SETUP phase only");
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Helicopters can be air spawned during the SETUP phase only");
return false;
}
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => {return points + helicopterDatabase.getSpawnPointsByName(unit.unitType)}, 0);
@@ -930,7 +931,7 @@ export class UnitsManager {
} else if (category === "GroundUnit") {
if (spawnsRestricted) {
getInfoPopup().setText("Ground units can be spawned during the SETUP phase only");
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Ground units can be spawned during the SETUP phase only");
return false;
}
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => {return points + groundUnitDatabase.getSpawnPointsByName(unit.unitType)}, 0);
@@ -938,19 +939,19 @@ export class UnitsManager {
} else if (category === "NavyUnit") {
if (spawnsRestricted) {
getInfoPopup().setText("Navy units can be spawned during the SETUP phase only");
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Navy units can be spawned during the SETUP phase only");
return false;
}
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => {return points + navyUnitDatabase.getSpawnPointsByName(unit.unitType)}, 0);
spawnFunction = () => spawnNavyUnits(units, coalition, country, immediate, spawnPoints, callback);
}
if (spawnPoints <= getMissionHandler().getAvailableSpawnPoints()) {
getMissionHandler().setSpentSpawnPoints(spawnPoints);
if (spawnPoints <= getApp().getMissionManager().getAvailableSpawnPoints()) {
getApp().getMissionManager().setSpentSpawnPoints(spawnPoints);
spawnFunction();
return true;
} else {
getInfoPopup().setText("Not enough spawn points available!");
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Not enough spawn points available!");
return false;
}
}
@@ -969,7 +970,7 @@ export class UnitsManager {
if (this.getSelectedUnits().length > 0) {
/* Disable the firing of the selection event for a certain amount of time. This avoids firing many events if many units are selected */
if (!this.#selectionEventDisabled) {
getMap().setState(MOVE_UNIT);
getApp().getMap().setState(MOVE_UNIT);
window.setTimeout(() => {
document.dispatchEvent(new CustomEvent("unitsSelection", { detail: this.getSelectedUnits() }));
this.#selectionEventDisabled = false;
@@ -978,14 +979,14 @@ export class UnitsManager {
}
}
else {
getMap().setState(IDLE);
getApp().getMap().setState(IDLE);
document.dispatchEvent(new CustomEvent("clearSelection"));
}
}
#onUnitDeselection(unit: Unit) {
if (this.getSelectedUnits().length == 0) {
getMap().setState(IDLE);
getApp().getMap().setState(IDLE);
document.dispatchEvent(new CustomEvent("clearSelection"));
}
else
@@ -994,8 +995,8 @@ export class UnitsManager {
#showActionMessage(units: Unit[], message: string) {
if (units.length == 1)
getInfoPopup().setText(`${units[0].getUnitName()} ${message}`);
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`${units[0].getUnitName()} ${message}`);
else if (units.length > 1)
getInfoPopup().setText(`${units[0].getUnitName()} and ${units.length - 1} other units ${message}`);
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`${units[0].getUnitName()} and ${units.length - 1} other units ${message}`);
}
}

View File

@@ -1,10 +1,9 @@
import { LatLng, DivIcon, Map } from 'leaflet';
import { getMap, getMissionHandler, getUnitsManager } from '..';
import { getApp } from '..';
import { enumToCoalition, mToFt, msToKnots, rad2deg } from '../other/utils';
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';
import { DataExtractor } from '../server/dataextractor';
export class Weapon extends CustomMarker {
@@ -58,7 +57,7 @@ export class Weapon extends CustomMarker {
/********************** Unit data *************************/
setData(dataExtractor: DataExtractor) {
var updateMarker = !getMap().hasLayer(this);
var updateMarker = !getApp().getMap().hasLayer(this);
var datumIndex = 0;
while (datumIndex != DataIndexes.endOfData) {
@@ -116,7 +115,7 @@ export class Weapon extends CustomMarker {
}
belongsToCommandedCoalition() {
if (getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER && getMissionHandler().getCommandedCoalition() !== this.#coalition)
if (getApp().getMissionManager().getCommandModeOptions().commandMode !== GAME_MASTER && getApp().getMissionManager().getCommandedCoalition() !== this.#coalition)
return false;
return true;
}
@@ -166,7 +165,7 @@ export class Weapon extends CustomMarker {
/********************** Visibility *************************/
updateVisibility() {
const hiddenUnits = getMap().getHiddenTypes();
const hiddenUnits = getApp().getMap().getHiddenTypes();
var hidden = (hiddenUnits.includes(this.getMarkerCategory())) ||
(hiddenUnits.includes(this.#coalition)) ||
(!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0);
@@ -178,16 +177,16 @@ export class Weapon extends CustomMarker {
this.#hidden = hidden;
/* Add the marker if not present */
if (!getMap().hasLayer(this) && !this.getHidden()) {
if (getMap().isZooming())
this.once("zoomend", () => {this.addTo(getMap())})
if (!getApp().getMap().hasLayer(this) && !this.getHidden()) {
if (getApp().getMap().isZooming())
this.once("zoomend", () => {this.addTo(getApp().getMap())})
else
this.addTo(getMap());
this.addTo(getApp().getMap());
}
/* Hide the marker if necessary*/
if (getMap().hasLayer(this) && this.getHidden()) {
getMap().removeLayer(this);
if (getApp().getMap().hasLayer(this) && this.getHidden()) {
getApp().getMap().removeLayer(this);
}
}
@@ -250,7 +249,7 @@ export class Weapon extends CustomMarker {
}
/* Set vertical offset for altitude stacking */
var pos = getMap().latLngToLayerPoint(this.getLatLng()).round();
var pos = getApp().getMap().latLngToLayerPoint(this.getLatLng()).round();
this.setZIndexOffset(1000 + Math.floor(this.#position.alt as number) - pos.y);
}
}

View File

@@ -1,8 +1,7 @@
import { getMissionHandler, getUnitsManager } from "..";
import { getApp } from "..";
import { Weapon } from "./weapon";
import { DataIndexes, GAME_MASTER } from "../constants/constants";
import { DataExtractor } from "../server/dataextractor";
import { Contact } from "../@types/unit";
/** The WeaponsManager handles the creation and update of weapons. Data is strictly updated by the server ONLY. */
export class WeaponsManager {
@@ -93,7 +92,7 @@ export class WeaponsManager {
*/
getWeaponDetectedMethods(weapon: Weapon) {
var detectionMethods: number[] = [];
var units = getUnitsManager().getUnits();
var units = getApp().getUnitsManager().getUnits();
for (let idx in units) {
if (units[idx].getAlive() && units[idx].getIsLeader() && units[idx].getCoalition() !== "neutral" && units[idx].getCoalition() != weapon.getCoalition())
{