Readded map options, simplified ShortcutManager

This commit is contained in:
Davide Passoni 2024-06-27 14:46:28 +02:00
parent fb96926d1e
commit 222a296b4f
19 changed files with 1363 additions and 476 deletions

View File

@ -1,5 +1,7 @@
import { LatLng, LatLngBounds } from "leaflet";
//import { MapMarkerVisibilityControl } from "../map/map";
import { Context } from "../types/types";
export const DEFAULT_CONTEXT: Context = "default context";
export const UNITS_URI = "units";
export const WEAPONS_URI = "weapons";
@ -154,25 +156,8 @@ export const mapBounds = {
"SinaiMap": { bounds: new LatLngBounds([34.312222, 28.523333], [25.946944, 36.897778]), zoom: 4 },
}
export const mapMirrors = {
"DCS Map mirror 1": "https://maps.dcsolympus.com/maps",
"DCS Map mirror 2": "https://refugees.dcsolympus.com/maps"
}
export const defaultMapLayers = {
"ArcGIS Satellite": {
urlTemplate: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
minZoom: 1,
maxZoom: 19,
attribution: "Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Mapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
},
"OpenStreetMap Mapnik": {
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
minZoom: 1,
maxZoom: 20,
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}
}
export const defaultMapMirrors = {};
export const defaultMapLayers = {};
/* Map constants */
export const IDLE = "Idle";
@ -185,18 +170,6 @@ export const IADSTypes = ["AAA", "SAM Site", "Radar (EWR)"];
export const IADSDensities: { [key: string]: number } = { "AAA": 0.8, "SAM Site": 0.1, "Radar (EWR)": 0.05 };
export const GROUND_UNIT_AIR_DEFENCE_REGEX: RegExp = /(\b(AAA|SAM|MANPADS?|[mM]anpads?)|[sS]tinger\b)/;
export const HIDE_GROUP_MEMBERS = "Hide group members when zoomed out";
export const SHOW_UNIT_LABELS = "Show unit labels (L)";
export const SHOW_UNITS_ENGAGEMENT_RINGS = "Show units threat range rings (Q)";
export const HIDE_UNITS_SHORT_RANGE_RINGS = "Hide short range units threat range rings (R)";
export const SHOW_UNITS_ACQUISITION_RINGS = "Show units detection range rings (E)";
export const FILL_SELECTED_RING = "Fill the threat range rings of selected units (F)";
export const SHOW_UNIT_CONTACTS = "Show selected units contact lines";
export const SHOW_UNIT_PATHS = "Show selected unit paths";
export const SHOW_UNIT_TARGETS = "Show selected unit targets";
export const DCS_LINK_PORT = "DCS Camera link port";
export const DCS_LINK_RATIO = "DCS Camera zoom";
export const MAP_OPTIONS_TOOLTIPS = {
hideGroupMembers: "Hide group members when zoomed out",
hideUnitsShortRangeRings: "Hide short range units threat range rings (R)",
@ -216,7 +189,8 @@ export const MAP_OPTIONS_DEFAULTS = {
showUnitTargets: false,
showUnitLabels: true,
showUnitsEngagementRings: true,
showUnitsAcquisitionRings: true
showUnitsAcquisitionRings: true,
fillSelectedRing: false
}
export const MAP_HIDDEN_TYPES_DEFAULTS = {
@ -298,4 +272,4 @@ export const GROUPING_ZOOM_TRANSITION = 13;
export const MAX_SHOTS_SCATTER = 3;
export const MAX_SHOTS_INTENSITY = 3;
export const SHOTS_SCATTER_DEGREES = 10;
export const SHOTS_SCATTER_DEGREES = 10;

View File

@ -12,7 +12,9 @@ interface CustomEventMap {
"mapStateChanged": CustomEvent<string>,
"mapContextMenu": CustomEvent<any>,
"mapOptionChanged": CustomEvent<any>,
"mapSourceChanged": CustomEvent<string>,
"mapOptionsChanged": CustomEvent<any>, // TODO not very clear, why the two options?
"configLoaded": CustomEvent<any>,
"commandModeOptionsChanged": CustomEvent<any>,
"contactsUpdated": CustomEvent<Unit>,
"activeCoalitionChanged": CustomEvent<any>,

View File

@ -12,7 +12,7 @@ export const EventsContext = createContext({
toggleUnitControlMenuVisible: () => {},
toggleMeasureMenuVisible: () => {},
toggleDrawingMenuVisible: () => {},
toggleOptionsMenuVisible: () => {},
toggleOptionsMenuVisible: () => {}
})
export const EventsProvider = EventsContext.Provider;

View File

@ -1,5 +1,5 @@
import { LatLng } from "leaflet";
import { Coalition } from "./types/types";
import { Coalition, Context } from "./types/types";
class Airbase {
@ -264,7 +264,7 @@ export interface Listener {
export interface ShortcutOptions {
altKey?: boolean;
callback: CallableFunction;
context?: string;
context?: Context;
ctrlKey?: boolean;
name?: string;
shiftKey?: boolean;

View File

@ -1,26 +1,17 @@
import * as L from "leaflet"
import { getApp } from "../olympusapp";
import { BoxSelect } from "./boxselect";
//import { MapContextMenu } from "../contextmenus/mapcontextmenu";
//import { UnitContextMenu } from "../contextmenus/unitcontextmenu";
//import { AirbaseContextMenu } from "../contextmenus/airbasecontextmenu";
//import { Dropdown } from "../controls/dropdown";
import { Airbase } from "../mission/airbase";
import { Unit } from "../unit/unit";
import { bearing, /*createCheckboxOption, createSliderInputOption, createTextInputOption,*/ deg2rad, getGroundElevation, getUnitCategoryByBlueprint, polyContains } from "../other/utils";
import { bearing, deg2rad, getGroundElevation, polyContains } from "../other/utils";
import { DestinationPreviewMarker } from "./markers/destinationpreviewmarker";
import { TemporaryUnitMarker } from "./markers/temporaryunitmarker";
import { ClickableMiniMap } from "./clickableminimap";
import { mapMirrors, defaultMapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, MOVE_UNIT, SHOW_UNIT_CONTACTS, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, SHOW_UNIT_LABELS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, /*MAP_MARKER_CONTROLS,*/ DCS_LINK_PORT, DCS_LINK_RATIO, MAP_OPTIONS_DEFAULTS, MAP_HIDDEN_TYPES_DEFAULTS, SPAWN_UNIT, CONTEXT_ACTION } from "../constants/constants";
import { defaultMapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, MOVE_UNIT, defaultMapMirrors, SPAWN_UNIT, CONTEXT_ACTION, MAP_OPTIONS_DEFAULTS, MAP_HIDDEN_TYPES_DEFAULTS } from "../constants/constants";
import { CoalitionArea } from "./coalitionarea/coalitionarea";
//import { CoalitionAreaContextMenu } from "../contextmenus/coalitionareacontextmenu";
import { DrawingCursor } from "./coalitionarea/drawingcursor";
//import { AirbaseSpawnContextMenu } from "../contextmenus/airbasespawnmenu";
//import { GestureHandling } from "leaflet-gesture-handling";
import { TouchBoxSelect } from "./touchboxselect";
import { DestinationPreviewHandle } from "./markers/destinationpreviewHandle";
import { ContextActionSet } from "../unit/contextactionset";
import { DCSLayer } from "./dcslayer";
import './markers/stylesheets/airbase.css'
import './markers/stylesheets/bullseye.css'
@ -28,10 +19,11 @@ import './markers/stylesheets/units.css'
// Temporary
import './theme.css'
import { Coalition, MapHiddenTypes, MapOptions } from "../types/types";
import { SpawnRequestTable, UnitBlueprint, UnitSpawnTable } from "../interfaces";
import { MapHiddenTypes, MapOptions } from "../types/types";
import { SpawnRequestTable } from "../interfaces";
import { ContextAction } from "../unit/contextaction";
// Touch screen support temporarily disabled
var hasTouchScreen = false;
//if ("maxTouchPoints" in navigator)
// hasTouchScreen = navigator.maxTouchPoints > 0;
@ -43,10 +35,6 @@ else
//L.Map.addInitHook("addHandler", "gestureHandling", GestureHandling);
// TODO would be nice to convert to ts - yes
//require("../../node_modules/leaflet.nauticscale/dist/leaflet.nauticscale.js")
//require("../../node_modules/leaflet-path-drag/dist/index.js")
export class Map extends L.Map {
/* Options */
#options: MapOptions = MAP_OPTIONS_DEFAULTS;
@ -105,6 +93,7 @@ export class Map extends L.Map {
#longPressTimer: number = 0;
#mapLayers: any = defaultMapLayers;
#mapMirrors: any = defaultMapMirrors;
#layerName: string = "";
#cameraOptionsXmlHttp: XMLHttpRequest | null = null;
#bradcastPositionXmlHttp: XMLHttpRequest | null = null;
@ -136,10 +125,8 @@ export class Map extends L.Map {
this.#ID = ID;
this.setLayer("DCS Map mirror 2");
/* Minimap */
var minimapLayer = new L.TileLayer(this.#mapLayers[Object.keys(this.#mapLayers)[0]].urlTemplate, { minZoom: 0, maxZoom: 13 });
var minimapLayer = new L.TileLayer("https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", { minZoom: 0, maxZoom: 13 });
this.#miniMapLayerGroup = new L.LayerGroup([minimapLayer]);
this.#miniMapPolyline = new L.Polyline([], { color: '#202831' });
this.#miniMapPolyline.addTo(this.#miniMapLayerGroup);
@ -218,13 +205,36 @@ export class Map extends L.Map {
document.addEventListener("configLoaded", () => {
let config = getApp().getConfig();
if (config.additionalMaps) {
let additionalMaps = config.additionalMaps;
let layerSet = false;
/* First load the map mirrors */
if (config.mapMirrors) {
let mapMirrors = config.mapMirrors;
this.#mapMirrors = {
...this.#mapMirrors,
...mapMirrors
}
this.setLayerName(Object.keys(mapMirrors)[0]);
}
/* Set the options, and if at least one mirror is available, select the first */
if (Object.keys(this.#mapMirrors).length > 0) {
this.setLayerName(Object.keys(this.#mapMirrors)[0]);
layerSet = true; // Needed because this is async
}
/* Then load the map layers */
if (config.mapLayers) {
let mapLayers = config.mapLayers;
this.#mapLayers = {
...this.#mapLayers,
...additionalMaps
...mapLayers
}
//this.#mapSourceDropdown.setOptions(this.getLayers(), null);
}
/* Append this options, and if no mirror was selected, select the first on (if available). Mirrors have the precedence */
if (!layerSet && Object.keys(this.#mapLayers).length > 0) {
this.setLayerName(Object.keys(this.#mapLayers)[0]);
}
})
@ -253,11 +263,11 @@ export class Map extends L.Map {
}, 1000)
}
setLayer(layerName: string) {
if (this.#layer != null)
this.removeLayer(this.#layer)
setLayerName(layerName: string) {
if (this.#layer)
this.removeLayer(this.#layer);
let theatre = getApp().getMissionManager()?.getTheatre() ?? "Nevada";
let theatre = getApp().getMissionManager()?.getTheatre();
/* Normal or custom layers are handled here */
if (layerName in this.#mapLayers) {
@ -267,16 +277,18 @@ export class Map extends L.Map {
return new L.TileLayer(layer.urlTemplate.replace("{theatre}", theatre.toLowerCase()), layer);
})
this.#layer = new L.LayerGroup(layers);
this.#layer?.addTo(this);
} else {
this.#layer = new L.TileLayer(layerData.urlTemplate, layerData);
this.#layer?.addTo(this);
}
/* DCS core layers are handled here */
} else if (["DCS Map mirror 1", "DCS Map mirror 2"].includes(layerName) ) {
let layerData = this.#mapLayers["ArcGIS Satellite"];
let layers = [new L.TileLayer(layerData.urlTemplate, layerData)];
/* Mirrored layers are handled here */
} else if (Object.keys(this.#mapMirrors).includes(layerName) ) {
let layers: L.TileLayer[] = [];
/* Load the configuration file */
const mirror = mapMirrors[layerName as keyof typeof mapMirrors];
const mirror = this.#mapMirrors[layerName as any];
const request = new Request(mirror + "/config.json");
fetch(request).then((response) => {
if (response.status === 200) {
@ -299,12 +311,16 @@ export class Map extends L.Map {
})
}
this.#layerName = layerName;
document.dispatchEvent(new CustomEvent("mapSourceChanged", {detail: layerName}));
}
getLayerName() {
return this.#layerName;
}
getLayers() {
let layers = ["DCS Map mirror 1", "DCS Map mirror 2"];
layers.push(...Object.keys(this.#mapLayers));
return layers;
return Object.keys(this.#mapLayers);
}
/* State machine */
@ -486,7 +502,7 @@ export class Map extends L.Map {
const boundaries = this.#getMinimapBoundaries();
this.#miniMapPolyline.setLatLngs(boundaries[theatre as keyof typeof boundaries]);
this.setLayer(this.#layerName);
this.setLayerName(this.#layerName);
}
getMiniMapLayerGroup() {
@ -615,21 +631,21 @@ export class Map extends L.Map {
}
increaseCameraZoom() {
const slider = document.querySelector(`label[title="${DCS_LINK_RATIO}"] input`);
if (slider instanceof HTMLInputElement) {
slider.value = String(Math.min(Number(slider.max), Number(slider.value) + 10));
slider.dispatchEvent(new Event('input'));
slider.dispatchEvent(new Event('mouseup'));
}
//const slider = document.querySelector(`label[title="${DCS_LINK_RATIO}"] input`);
//if (slider instanceof HTMLInputElement) {
// slider.value = String(Math.min(Number(slider.max), Number(slider.value) + 10));
// slider.dispatchEvent(new Event('input'));
// slider.dispatchEvent(new Event('mouseup'));
//}
}
decreaseCameraZoom() {
const slider = document.querySelector(`label[title="${DCS_LINK_RATIO}"] input`);
if (slider instanceof HTMLInputElement) {
slider.value = String(Math.max(Number(slider.min), Number(slider.value) - 10));
slider.dispatchEvent(new Event('input'));
slider.dispatchEvent(new Event('mouseup'));
}
//const slider = document.querySelector(`label[title="${DCS_LINK_RATIO}"] input`);
//if (slider instanceof HTMLInputElement) {
// slider.value = String(Math.max(Number(slider.min), Number(slider.value) - 10));
// slider.dispatchEvent(new Event('input'));
// slider.dispatchEvent(new Event('mouseup'));
//}
}
/* Event handlers */

File diff suppressed because it is too large Load Diff

View File

@ -14,39 +14,25 @@ export function getApp() {
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 { Manager } from "./other/manager";
import { ServerManager } from "./server/servermanager";
import { sha256 } from 'js-sha256';
import { BLUE_COMMANDER, FILL_SELECTED_RING, GAME_MASTER, HIDE_UNITS_SHORT_RANGE_RINGS, RED_COMMANDER, SHOW_UNITS_ACQUISITION_RINGS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNIT_LABELS } from "./constants/constants";
import { BLUE_COMMANDER, DEFAULT_CONTEXT, GAME_MASTER, RED_COMMANDER } from "./constants/constants";
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 { UnitListPanel } from "./panels/unitlistpanel";
//import { ContextManager } from "./context/contextmanager";
//import { Context } from "./context/context";
import { Coalition, Context } from "./types/types";
export var VERSION = "{{OLYMPUS_VERSION_NUMBER}}";
export var IP = window.location.toString();
export var connectedToServer = true; // Temporary
export class OlympusApp {
/* Global data */
#activeCoalition: string = "blue";
#activeCoalition: Coalition = "blue";
#latestVersion: string|undefined = undefined;
#config: any = {};
@ -54,54 +40,31 @@ export class OlympusApp {
#map: Map | null = null;
/* Managers */
//#contextManager!: ContextManager;
//#dialogManager!: Manager;
#missionManager: MissionManager | null = null;
//#panelsManager: Manager | null = null;
//#pluginsManager: PluginsManager | null = null;
//#popupsManager: Manager | null = null;
#serverManager: ServerManager | null = null;
#shortcutManager!: ShortcutManager;
//#toolbarsManager: Manager | null = null;
#shortcutManager: ShortcutManager | null = null;
#unitsManager: UnitsManager | null = null;
#weaponsManager: WeaponsManager | null = null;
//#pluginsManager: // TODO
/* Current context */
#context: Context = DEFAULT_CONTEXT;
constructor() {
}
// TODO add checks on null
getDialogManager() {
return null //this.#dialogManager as Manager;
getCurrentContext() {
return this.#context;
}
getMap() {
return this.#map as Map;
}
getCurrentContext() {
return null //this.getContextManager().getCurrentContext() as Context;
}
getContextManager() {
return null // this.#contextManager as ContextManager;
}
getServerManager() {
return this.#serverManager as ServerManager;
}
getPanelsManager() {
return null // this.#panelsManager as Manager;
}
getPopupsManager() {
return null // this.#popupsManager as Manager;
}
getToolbarsManager() {
return null // this.#toolbarsManager as Manager;
}
getShortcutManager() {
return this.#shortcutManager as ShortcutManager;
}
@ -118,15 +81,17 @@ export class OlympusApp {
return this.#missionManager as MissionManager;
}
/* TODO
getPluginsManager() {
return null // 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) {
setActiveCoalition(newActiveCoalition: Coalition) {
if (this.getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER) {
this.#activeCoalition = newActiveCoalition;
document.dispatchEvent(new CustomEvent("activeCoalitionChanged"));
@ -137,7 +102,7 @@ export class OlympusApp {
*
* @returns The active coalition
*/
getActiveCoalition() {
getActiveCoalition(): Coalition {
if (this.getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER)
return this.#activeCoalition;
else {
@ -182,77 +147,22 @@ export class OlympusApp {
return navyUnitDatabase;
}
/** 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.#contextManager = new ContextManager();
//this.#contextManager.add( "olympus", {} );
this.#map = new Map('map-container');
this.#missionManager = new MissionManager();
//this.#panelsManager = new Manager();
//this.#popupsManager = new Manager();
this.#serverManager = new ServerManager();
this.#shortcutManager = new ShortcutManager();
//this.#toolbarsManager = new Manager();
this.#unitsManager = new UnitsManager();
this.#weaponsManager = new WeaponsManager();
// Toolbars
//this.getToolbarsManager().add("primaryToolbar", new PrimaryToolbar("primary-toolbar"))
// .add("commandModeToolbar", new CommandModeToolbar("command-mode-toolbar"));
//
//// 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"))
// .add("unitList", new UnitListPanel("unit-list-panel", "unit-list-panel-content"))
//
//// Popups
//this.getPopupsManager()
// .add("infoPopup", new Popup("info-popup"));
//
//this.#pluginsManager = new PluginsManager();
/* Set the address of the server */
this.getServerManager().setAddress(window.location.href.split('?')[0]);
/* Setup all global events */
this.#setupEvents();
/* Set the splash background image to a random image */
let splashScreen = document.getElementById("splash-screen") as HTMLElement;
let i = Math.round(Math.random() * 7 + 1);
if (splashScreen) {
new Promise((resolve, reject) => {
const image = new Image();
image.addEventListener('load', resolve);
image.addEventListener('error', resolve);
image.src = `/resources/theme/images/splash/${i}.jpg`;
}).then(() => {
splashScreen.style.backgroundImage = `url('/resources/theme/images/splash/${i}.jpg')`;
let loadingScreen = document.getElementById("loading-screen") as HTMLElement;
loadingScreen.classList.add("fade-out");
window.setInterval(() => { loadingScreen.classList.add("hide"); }, 1000);
})
}
/* Check if we are running the latest version */
const request = new Request("https://raw.githubusercontent.com/Pax1601/DCSOlympus/main/version.json");
fetch(request).then((response) => {
@ -285,181 +195,14 @@ export class OlympusApp {
})
}
getConfig() {
return this.#config;
}
#setupEvents() {
/* Generic clicks */
document.addEventListener("click", (ev) => {
if (ev instanceof MouseEvent && ev.target instanceof HTMLElement) {
const target = ev.target;
if (target.classList.contains("olympus-dialog-close")) {
target.closest("div.olympus-dialog")?.classList.add("hide");
}
const triggerElement = target.closest("[data-on-click]");
if (triggerElement instanceof HTMLElement) {
const eventName: string = triggerElement.dataset.onClick || "";
let params = JSON.parse(triggerElement.dataset.onClickParams || "{}");
params._element = triggerElement;
if (eventName) {
document.dispatchEvent(new CustomEvent(eventName, {
detail: params
}));
}
}
}
});
const shortcutManager = this.getShortcutManager();
shortcutManager.addKeyboardShortcut("togglePause", {
"altKey": false,
"callback": () => {
this.getServerManager().setPaused(!this.getServerManager().getPaused());
},
"code": "Space",
"context": "olympus",
"ctrlKey": false
}).addKeyboardShortcut("deselectAll", {
"callback": (ev: KeyboardEvent) => {
this.getUnitsManager().deselectAllUnits();
},
"code": "Escape",
"context": "olympus"
}).addKeyboardShortcut("toggleUnitLabels", {
"altKey": false,
"callback": () => {
const chk = document.querySelector(`label[title="${SHOW_UNIT_LABELS}"] input[type="checkbox"]`);
if (chk instanceof HTMLElement) {
chk.click();
}
},
"code": "KeyL",
"context": "olympus",
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("toggleAcquisitionRings", {
"altKey": false,
"callback": () => {
const chk = document.querySelector(`label[title="${SHOW_UNITS_ACQUISITION_RINGS}"] input[type="checkbox"]`);
if (chk instanceof HTMLElement) {
chk.click();
}
},
"code": "KeyE",
"context": "olympus",
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("toggleEngagementRings", {
"altKey": false,
"callback": () => {
const chk = document.querySelector(`label[title="${SHOW_UNITS_ENGAGEMENT_RINGS}"] input[type="checkbox"]`);
if (chk instanceof HTMLElement) {
chk.click();
}
},
"code": "KeyQ",
"context": "olympus",
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("toggleHideShortEngagementRings", {
"altKey": false,
"callback": () => {
const chk = document.querySelector(`label[title="${HIDE_UNITS_SHORT_RANGE_RINGS}"] input[type="checkbox"]`);
if (chk instanceof HTMLElement) {
chk.click();
}
},
"code": "KeyR",
"context": "olympus",
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("toggleFillEngagementRings", {
"altKey": false,
"callback": () => {
const chk = document.querySelector(`label[title="${FILL_SELECTED_RING}"] input[type="checkbox"]`);
if (chk instanceof HTMLElement) {
chk.click();
}
},
"code": "KeyF",
"context": "olympus",
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("increaseCameraZoom", {
"altKey": true,
"callback": () => {
//this.getMap().increaseCameraZoom();
},
"code": "Equal",
"context": "olympus",
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("decreaseCameraZoom", {
"altKey": true,
"callback": () => {
//this.getMap().decreaseCameraZoom();
},
"code": "Minus",
"context": "olympus",
"ctrlKey": false,
"shiftKey": false
});
["KeyW", "KeyA", "KeyS", "KeyD", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].forEach(code => {
shortcutManager.addKeyboardShortcut(`pan${code}keydown`, {
"altKey": false,
"callback": (ev: KeyboardEvent) => {
//this.getMap().handleMapPanning(ev);
},
"code": code,
"context": "olympus",
"ctrlKey": false,
"event": "keydown"
});
shortcutManager.addKeyboardShortcut(`pan${code}keyup`, {
"callback": (ev: KeyboardEvent) => {
//this.getMap().handleMapPanning(ev);
},
"code": code,
"context": "olympus"
});
});
const digits = ["Digit1", "Digit2", "Digit3", "Digit4", "Digit5", "Digit6", "Digit7", "Digit8", "Digit9"];
digits.forEach(code => {
shortcutManager.addKeyboardShortcut(`hotgroup${code}`, {
"altKey": false,
"callback": (ev: KeyboardEvent) => {
if (ev.ctrlKey && ev.shiftKey)
this.getUnitsManager().selectUnitsByHotgroup(parseInt(ev.code.substring(5)), false); // "Select hotgroup X in addition to any units already selected"
else if (ev.ctrlKey && !ev.shiftKey)
this.getUnitsManager().setHotgroup(parseInt(ev.code.substring(5))); // "These selected units are hotgroup X (forget any previous membership)"
else if (!ev.ctrlKey && ev.shiftKey)
this.getUnitsManager().addToHotgroup(parseInt(ev.code.substring(5))); // "Add (append) these units to hotgroup X (in addition to any existing members)"
else
this.getUnitsManager().selectUnitsByHotgroup(parseInt(ev.code.substring(5))); // "Select hotgroup X, deselect any units not in it."
},
"code": code
});
// Stop hotgroup controls sending the browser to another tab
document.addEventListener("keydown", (ev: KeyboardEvent) => {
if (ev.code === code && ev.ctrlKey === true && ev.altKey === false && ev.shiftKey === false) {
ev.preventDefault();
}
});
});
/* Reload the page, used to mimic a restart of the app */
document.addEventListener("reloadPage", () => {
location.reload();
})
}
getConfig() {
return this.#config;
}
}

View File

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

View File

@ -1,37 +0,0 @@
import { Context } from "../context/context";
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()}.`);
}
if (this.#items.hasOwnProperty(name)) {
throw new Error(`Item with name "${name}" already exists.`);
}
this.#items[name] = item;
return this;
}
get(name: string) {
if (this.#items.hasOwnProperty(name)) {
return this.#items[name];
} else {
return false;
}
}
getAll() {
return this.#items;
}
}

View File

@ -1,13 +1,7 @@
import { LatLng } from 'leaflet';
import { getApp } from '../olympusapp';
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';
import { AirbasesData, BullseyesData, GeneralSettings, MissionData, Radio, ServerRequestOptions, ServerStatus, TACAN } from '../interfaces';
import { zeroAppend } from '../other/utils';
import { SiTheregister } from 'react-icons/si';
export class ServerManager {
#connected: boolean = false;
@ -89,7 +83,6 @@ export class ServerManager {
} else if (xmlHttp.status == 401) {
/* Bad credentials */
console.error("Incorrect username/password");
getApp().setLoginStatus("failed");
errorCallback && errorCallback(xmlHttp.status);
} else {
/* Failure, probably disconnected */

View File

@ -20,7 +20,7 @@ export class ShortcutKeyboard extends Shortcut {
super(config);
document.addEventListener(config.event, (ev: any) => {
if ( typeof config.context === "string" && !getApp().getContextManager().currentContextIs( config.context ) ) {
if ( typeof config.context === "string" && getApp().getCurrentContext() !== config.context ) {
return;
}

View File

@ -1,18 +1,16 @@
import { DEFAULT_CONTEXT } from "../constants/constants";
import { ShortcutKeyboardOptions, ShortcutMouseOptions } from "../interfaces";
import { Manager } from "../other/manager";
import { getApp } from "../olympusapp";
import { ShortcutKeyboard, ShortcutMouse } from "./shortcut";
export class ShortcutManager extends Manager {
export class ShortcutManager {
#items: { [key: string]: any } = {};
#keysBeingHeld: string[] = [];
#keyDownCallbacks: CallableFunction[] = [];
#keyUpCallbacks: CallableFunction[] = [];
constructor() {
super();
document.addEventListener("keydown", (ev: KeyboardEvent) => {
if (this.#keysBeingHeld.indexOf(ev.code) < 0) {
this.#keysBeingHeld.push(ev.code)
@ -25,20 +23,137 @@ export class ShortcutManager extends Manager {
this.#keyUpCallbacks.forEach(callback => callback(ev));
});
}
this.addKeyboardShortcut("togglePause", {
"altKey": false,
"callback": () => {
getApp().getServerManager().setPaused(!getApp().getServerManager().getPaused());
},
"code": "Space",
"context": DEFAULT_CONTEXT,
"ctrlKey": false
}).addKeyboardShortcut("deselectAll", {
"callback": (ev: KeyboardEvent) => {
getApp().getUnitsManager().deselectAllUnits();
},
"code": "Escape",
"context": DEFAULT_CONTEXT
}).addKeyboardShortcut("toggleUnitLabels", {
"altKey": false,
"callback": () => { getApp().getMap().setOption("showUnitLabels", !getApp().getMap().getOptions().showUnitLabels) },
"code": "KeyL",
"context": DEFAULT_CONTEXT,
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("toggleAcquisitionRings", {
"altKey": false,
"callback": () => { getApp().getMap().setOption("showUnitsAcquisitionRings", !getApp().getMap().getOptions().showUnitsAcquisitionRings) },
"code": "KeyE",
"context": DEFAULT_CONTEXT,
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("toggleEngagementRings", {
"altKey": false,
"callback": () => { getApp().getMap().setOption("showUnitsEngagementRings", !getApp().getMap().getOptions().showUnitsEngagementRings) },
"code": "KeyQ",
"context": DEFAULT_CONTEXT,
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("toggleHideShortEngagementRings", {
"altKey": false,
"callback": () => { getApp().getMap().setOption("hideUnitsShortRangeRings", !getApp().getMap().getOptions().hideUnitsShortRangeRings) },
"code": "KeyR",
"context": DEFAULT_CONTEXT,
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("toggleDetectionLines", {
"altKey": false,
"callback": () => { getApp().getMap().setOption("showUnitTargets", !getApp().getMap().getOptions().showUnitTargets) },
"code": "KeyF",
"context": DEFAULT_CONTEXT,
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("toggleGroupMembers", {
"altKey": false,
"callback": () => { getApp().getMap().setOption("hideGroupMembers", !getApp().getMap().getOptions().hideGroupMembers) },
"code": "KeyG",
"context": DEFAULT_CONTEXT,
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("increaseCameraZoom", {
"altKey": true,
"callback": () => {
//getApp().getMap().increaseCameraZoom();
},
"code": "Equal",
"context": DEFAULT_CONTEXT,
"ctrlKey": false,
"shiftKey": false
}).addKeyboardShortcut("decreaseCameraZoom", {
"altKey": true,
"callback": () => {
//getApp().getMap().decreaseCameraZoom();
},
"code": "Minus",
"context": DEFAULT_CONTEXT,
"ctrlKey": false,
"shiftKey": false
});
add(name: string, shortcut: any) {
console.error("ShortcutManager:add() cannot be used. Use addKeyboardShortcut or addMouseShortcut.");
return this;
["KeyW", "KeyA", "KeyS", "KeyD", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].forEach(code => {
this.addKeyboardShortcut(`pan${code}keydown`, {
"altKey": false,
"callback": (ev: KeyboardEvent) => {
//getApp().getMap().handleMapPanning(ev);
},
"code": code,
"context": DEFAULT_CONTEXT,
"ctrlKey": false,
"event": "keydown"
});
this.addKeyboardShortcut(`pan${code}keyup`, {
"callback": (ev: KeyboardEvent) => {
//getApp().getMap().handleMapPanning(ev);
},
"code": code,
"context": DEFAULT_CONTEXT
});
});
const digits = ["Digit1", "Digit2", "Digit3", "Digit4", "Digit5", "Digit6", "Digit7", "Digit8", "Digit9"];
digits.forEach(code => {
this.addKeyboardShortcut(`hotgroup${code}`, {
"altKey": false,
"callback": (ev: KeyboardEvent) => {
if (ev.ctrlKey && ev.shiftKey)
getApp().getUnitsManager().selectUnitsByHotgroup(parseInt(ev.code.substring(5)), false); // "Select hotgroup X in addition to any units already selected"
else if (ev.ctrlKey && !ev.shiftKey)
getApp().getUnitsManager().setHotgroup(parseInt(ev.code.substring(5))); // "These selected units are hotgroup X (forget any previous membership)"
else if (!ev.ctrlKey && ev.shiftKey)
getApp().getUnitsManager().addToHotgroup(parseInt(ev.code.substring(5))); // "Add (append) these units to hotgroup X (in addition to any existing members)"
else
getApp().getUnitsManager().selectUnitsByHotgroup(parseInt(ev.code.substring(5))); // "Select hotgroup X, deselect any units not in it."
},
"code": code
});
// Stop hotgroup controls sending the browser to another tab
document.addEventListener("keydown", (ev: KeyboardEvent) => {
if (ev.code === code && ev.ctrlKey === true && ev.altKey === false && ev.shiftKey === false) {
ev.preventDefault();
}
});
});
}
addKeyboardShortcut(name: string, shortcutKeyboardOptions: ShortcutKeyboardOptions) {
super.add(name, new ShortcutKeyboard(shortcutKeyboardOptions));
this.#items[name] = new ShortcutKeyboard(shortcutKeyboardOptions);
return this;
}
addMouseShortcut(name: string, shortcutMouseOptions: ShortcutMouseOptions) {
super.add(name, new ShortcutMouse(shortcutMouseOptions));
this.#items[name] = new ShortcutMouse(shortcutMouseOptions);
return this;
}

View File

@ -9,7 +9,9 @@ export const StateContext = createContext({
drawingMenuVisible: false,
optionsMenuVisible: false,
mapHiddenTypes: MAP_HIDDEN_TYPES_DEFAULTS,
mapOptions: MAP_OPTIONS_DEFAULTS
mapOptions: MAP_OPTIONS_DEFAULTS,
mapSources: [] as string[],
activeMapSource: ""
})
export const StateProvider = StateContext.Provider;

View File

@ -17,7 +17,8 @@ export type MapOptions = {
showUnitTargets: boolean,
showUnitLabels: boolean,
showUnitsEngagementRings: boolean,
showUnitsAcquisitionRings: boolean
showUnitsAcquisitionRings: boolean,
fillSelectedRing: boolean
}
export type MapHiddenTypes = {
@ -48,4 +49,6 @@ export type MGRS = {
zoneNumber: string
}
export type Coalition = "blue" | "neutral" | "red";
export type Coalition = "blue" | "neutral" | "red";
export type Context = string;

View File

@ -78,9 +78,10 @@ export function Header() {
</div>
<OlLabelToggle toggled={false} leftLabel={"Live"} rightLabel={"Map"} onClick={() => {}}></OlLabelToggle>
<OlStateButton checked={false} icon={faCamera} onClick={() => {}} tooltip="Activate/deactivate camera plugin" />
<OlDropdown label="DCS Sat" className="w-40">
<OlDropdownItem className="w-full">DCS Sat</OlDropdownItem>
<OlDropdownItem className="w-full">DCS Alt</OlDropdownItem>
<OlDropdown label={appState.activeMapSource} className="w-80">
{appState.mapSources.map((source) => {
return <OlDropdownItem className="w-full" onClick={() => getApp().getMap().setLayerName(source)}>{ source }</OlDropdownItem>
})}
</OlDropdown>
</div>
</nav>

View File

@ -1,16 +1,15 @@
import React from "react";
import { Menu } from "./components/menu";
import { faArrowRightLong, faCheckCircle, faDatabase, faExternalLink, faExternalLinkAlt, faFile, faFileAlt, faFileExport, faFileImport, faTimesCircle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faGithub } from "@fortawesome/free-brands-svg-icons";
import { OlCheckbox } from "../components/olcheckbox";
import { OlLabelToggle } from "../components/ollabeltoggle";
import { OlRangeSlider } from "../components/olrangeslider";
import { OlNumberInput } from "../components/olnumberinput";
import { MapOptions } from "../../types/types";
import { getApp } from "../../olympusapp";
export function Options(props: {
open: boolean,
onClose: () => void,
options: MapOptions,
children?: JSX.Element | JSX.Element[],
}) {
return <Menu
@ -19,31 +18,57 @@ export function Options(props: {
showBackButton={false}
onClose={props.onClose}
>
<div className="flex flex-col p-5 gap-2 font-normal text-gray-900 text-gray-800 dark:text-white ">
<div className="group flex flex-row rounded-md justify-content gap-4 p-2 dark:hover:bg-olympus-400 cursor-pointer">
<OlCheckbox checked={true} onChange={() => { }}></OlCheckbox>
<span>Toggle Unit Labels</span>
<div className="flex flex-col p-5 gap-2 font-normal text-gray-800 dark:text-white ">
<div
className="group flex flex-row rounded-md justify-content gap-4 p-2 dark:hover:bg-olympus-400 cursor-pointer"
onClick={() => { getApp().getMap().setOption("showUnitLabels", !props.options.showUnitLabels) }}
>
<OlCheckbox checked={props.options.showUnitLabels} onChange={() => { }}></OlCheckbox>
<span>Show Unit Labels</span>
<kbd className="ml-auto px-2 py-1.5 text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded-lg dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500">L</kbd>
</div>
<div className="group flex flex-row rounded-md justify-content gap-4 p-2 dark:hover:bg-olympus-400 cursor-pointer">
<OlCheckbox checked={true} onChange={() => { }}></OlCheckbox>
<span>Toggle Threat Rings</span>
<div
className="group flex flex-row rounded-md justify-content gap-4 p-2 dark:hover:bg-olympus-400 cursor-pointer"
onClick={() => { getApp().getMap().setOption("showUnitsEngagementRings", !props.options.showUnitsEngagementRings) }}
>
<OlCheckbox checked={props.options.showUnitsEngagementRings} onChange={() => { }}></OlCheckbox>
<span>Show Threat Rings</span>
<kbd className="ml-auto px-2 py-1.5 text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded-lg dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500">Q</kbd>
</div>
<div className="group flex flex-row rounded-md justify-content gap-4 p-2 dark:hover:bg-olympus-400 cursor-pointer">
<OlCheckbox checked={true} onChange={() => { }}></OlCheckbox>
<span>Toggle Detection rings</span>
<div
className="group flex flex-row rounded-md justify-content gap-4 p-2 dark:hover:bg-olympus-400 cursor-pointer"
onClick={() => { getApp().getMap().setOption("showUnitsAcquisitionRings", !props.options.showUnitsAcquisitionRings) }}
>
<OlCheckbox checked={props.options.showUnitsAcquisitionRings} onChange={() => { }}></OlCheckbox>
<span>Show Detection rings</span>
<kbd className="ml-auto px-2 py-1.5 text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded-lg dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500">E</kbd>
</div>
<div className="group flex flex-row rounded-md justify-content gap-4 p-2 dark:hover:bg-olympus-400 cursor-pointer">
<OlCheckbox checked={true} onChange={() => { }}></OlCheckbox>
<span>Toggle Detection lines</span>
<div
className="group flex flex-row rounded-md justify-content gap-4 p-2 dark:hover:bg-olympus-400 cursor-pointer"
onClick={() => { getApp().getMap().setOption("showUnitTargets", !props.options.showUnitTargets) }}
>
<OlCheckbox checked={props.options.showUnitTargets} onChange={() => { }}></OlCheckbox>
<span>Show Detection lines</span>
<kbd className="ml-auto px-2 py-1.5 text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded-lg dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500">F</kbd>
</div>
<div className="group flex flex-row rounded-md justify-content gap-4 p-2 dark:hover:bg-olympus-400 cursor-pointer">
<OlCheckbox checked={true} onChange={() => { }}></OlCheckbox>
<span>Toggle Radar lines</span>
<div
className="group flex flex-row rounded-md justify-content gap-4 p-2 dark:hover:bg-olympus-400 cursor-pointer"
onClick={() => { getApp().getMap().setOption("hideUnitsShortRangeRings", !props.options.hideUnitsShortRangeRings) }}
>
<OlCheckbox checked={props.options.hideUnitsShortRangeRings} onChange={() => { }}></OlCheckbox>
<span>Hide Short range Rings</span>
<kbd className="ml-auto px-2 py-1.5 text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded-lg dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500">R</kbd>
</div>
<div className="group flex flex-row rounded-md justify-content gap-4 p-2 dark:hover:bg-olympus-400 cursor-pointer">
<OlCheckbox checked={true} onChange={() => { }}></OlCheckbox>
<span>Toggle Something Else</span>
<div
className="group flex flex-row rounded-md justify-content gap-4 p-2 dark:hover:bg-olympus-400 cursor-pointer"
onClick={() => { getApp().getMap().setOption("hideGroupMembers", !props.options.hideGroupMembers) }}
>
<OlCheckbox checked={props.options.hideGroupMembers} onChange={() => { }}></OlCheckbox>
<span>Hide Group members</span>
<kbd className="ml-auto px-2 py-1.5 text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded-lg dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500">G</kbd>
</div>
{/*
<hr className="w-auto m-2 my-1 bg-gray-700 border-[1px] dark:border-olympus-500"></hr>
<div className="flex flex-col content-center items-start justify-between p-2 gap-2">
<div className="flex flex-col">
@ -71,7 +96,7 @@ export function Options(props: {
onChange={(ev) => { }}
/>
</div>
</div>
</div> */}
</div>
</Menu>
}

View File

@ -42,6 +42,8 @@ export function UI() {
var [checkingPassword, setCheckingPassword] = useState(false);
var [loginError, setLoginError] = useState(false);
var [commandMode, setCommandMode] = useState(null as null | string);
var [mapSources, setMapSources] = useState([] as string[]);
var [activeMapSource, setActiveMapSource] = useState("");
document.addEventListener("hiddenTypesChanged", (ev) => {
setMapHiddenTypes({ ...getApp().getMap().getHiddenTypes() });
@ -57,6 +59,20 @@ export function UI() {
}
})
document.addEventListener("mapSourceChanged", (ev) => {
var source = (ev as CustomEvent).detail;
if (source !== activeMapSource)
setActiveMapSource(source);
})
document.addEventListener("configLoaded", (ev) => {
let config = getApp().getConfig();
var sources = Object.keys(config.mapMirrors).concat(Object.keys(config.mapLayers));
setMapSources(sources);
setActiveMapSource(sources[0]);
})
function hideAllMenus() {
setMainMenuVisible(false);
setSpawnMenuVisible(false);
@ -111,7 +127,9 @@ export function UI() {
drawingMenuVisible: drawingMenuVisible,
optionsMenuVisible: optionsMenuVisible,
mapOptions: mapOptions,
mapHiddenTypes: mapHiddenTypes
mapHiddenTypes: mapHiddenTypes,
mapSources: mapSources,
activeMapSource: activeMapSource
}}>
<EventsProvider value={
{
@ -157,6 +175,7 @@ export function UI() {
<Options
open={optionsMenuVisible}
onClose={() => setOptionsMenuVisible(false)}
options={mapOptions}
/>
<MiniMapPanel />
<UnitControlMenu />

View File

@ -5,7 +5,7 @@ import { CustomMarker } from '../map/markers/custommarker';
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_UNIT_CONTACTS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, GROUPING_ZOOM_TRANSITION, MAX_SHOTS_SCATTER, SHOTS_SCATTER_DEGREES, GROUND_UNIT_AIR_DEFENCE_REGEX } from '../constants/constants';
import { DLINK, DataIndexes, GAME_MASTER, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, VISUAL, emissionsCountermeasures, reactionsToThreat, states, GROUPING_ZOOM_TRANSITION, MAX_SHOTS_SCATTER, SHOTS_SCATTER_DEGREES, GROUND_UNIT_AIR_DEFENCE_REGEX } from '../constants/constants';
import { DataExtractor } from '../server/dataextractor';
import { groundUnitDatabase } from './databases/groundunitdatabase';
import { navyUnitDatabase } from './databases/navyunitdatabase';
@ -708,7 +708,7 @@ export abstract class Unit extends CustomMarker {
/* Hide the unit if it does not belong to the commanded coalition and it is not detected by a method that can pinpoint its location (RWR does not count) */
(!this.belongsToCommandedCoalition() && (this.#detectionMethods.length == 0 || (this.#detectionMethods.length == 1 && this.#detectionMethods[0] === RWR))) ||
/* Hide the unit if grouping is activated, the unit is not the group leader, it is not selected, and the zoom is higher than the grouping threshold */
(getApp().getMap().getOptions()[HIDE_GROUP_MEMBERS] && !this.#isLeader && !this.getSelected() && this.getCategory() == "GroundUnit" && getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION &&
(getApp().getMap().getOptions().hideGroupMembers && !this.#isLeader && !this.getSelected() && this.getCategory() == "GroundUnit" && getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION &&
(this.belongsToCommandedCoalition() || (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0))));
/* Force dead units to be hidden */
@ -1245,7 +1245,7 @@ export abstract class Unit extends CustomMarker {
}
#drawPath() {
if (this.#activePath != undefined && getApp().getMap().getOptions()[SHOW_UNIT_PATHS]) {
if (this.#activePath != undefined && getApp().getMap().getOptions().showUnitPaths) {
var points: LatLng[] = [];
points.push(new LatLng(this.#position.lat, this.#position.lng));
@ -1289,7 +1289,7 @@ export abstract class Unit extends CustomMarker {
#drawContacts() {
this.#clearContacts();
if (getApp().getMap().getOptions()[SHOW_UNIT_CONTACTS]) {
if (getApp().getMap().getOptions().showUnitContacts) {
for (let index in this.#contacts) {
var contactData = this.#contacts[index];
var contact: Unit | Weapon | null;
@ -1364,12 +1364,12 @@ export abstract class Unit extends CustomMarker {
if (engagementRange !== this.#engagementCircle.getRadius())
this.#engagementCircle.setRadius(engagementRange);
this.#engagementCircle.options.fillOpacity = this.getSelected() && getApp().getMap().getOptions()[FILL_SELECTED_RING] ? 0.3 : 0;
this.#engagementCircle.options.fillOpacity = this.getSelected() && getApp().getMap().getOptions().fillSelectedRing ? 0.3 : 0;
/* Acquisition circles */
var shortAcquisitionRangeCheck = (acquisitionRange > nmToM(3) || !getApp().getMap().getOptions()[HIDE_UNITS_SHORT_RANGE_RINGS]);
var shortAcquisitionRangeCheck = (acquisitionRange > nmToM(3) || !getApp().getMap().getOptions().hideUnitsShortRangeRings);
if (getApp().getMap().getOptions()[SHOW_UNITS_ACQUISITION_RINGS] && shortAcquisitionRangeCheck && (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, IRST, RWR].includes(value)))) {
if (getApp().getMap().getOptions().showUnitsAcquisitionRings && shortAcquisitionRangeCheck && (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, IRST, RWR].includes(value)))) {
if (!getApp().getMap().hasLayer(this.#acquisitionCircle)) {
this.#acquisitionCircle.addTo(getApp().getMap());
switch (this.getCoalition()) {
@ -1393,8 +1393,8 @@ export abstract class Unit extends CustomMarker {
}
/* Engagement circles */
var shortEngagementRangeCheck = (engagementRange > nmToM(3) || !getApp().getMap().getOptions()[HIDE_UNITS_SHORT_RANGE_RINGS]);
if (getApp().getMap().getOptions()[SHOW_UNITS_ENGAGEMENT_RINGS] && shortEngagementRangeCheck && (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, IRST, RWR].includes(value)))) {
var shortEngagementRangeCheck = (engagementRange > nmToM(3) || !getApp().getMap().getOptions().hideUnitsShortRangeRings);
if (getApp().getMap().getOptions().showUnitsEngagementRings && shortEngagementRangeCheck && (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, IRST, RWR].includes(value)))) {
if (!getApp().getMap().hasLayer(this.#engagementCircle)) {
this.#engagementCircle.addTo(getApp().getMap());
switch (this.getCoalition()) {
@ -1428,10 +1428,10 @@ export abstract class Unit extends CustomMarker {
}
#drawTarget() {
if (this.#targetPosition.lat != 0 && this.#targetPosition.lng != 0 && getApp().getMap().getOptions()[SHOW_UNIT_PATHS]) {
if (this.#targetPosition.lat != 0 && this.#targetPosition.lng != 0 && getApp().getMap().getOptions().showUnitPaths) {
this.#drawTargetPosition(this.#targetPosition);
}
else if (this.#targetID != 0 && getApp().getMap().getOptions()[SHOW_UNIT_TARGETS]) {
else if (this.#targetID != 0 && getApp().getMap().getOptions().showUnitTargets) {
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());
@ -1648,7 +1648,7 @@ export class GroundUnit extends Unit {
/* When a unit is a leader of a group, the map is zoomed out and grouping when zoomed out is enabled, check if the unit should be shown as a specific group. This is used to show a SAM battery instead of the group leader */
getDatabaseEntry() {
let unitWhenGrouped: string | undefined | null = null;
if (!this.getSelected() && this.getIsLeader() && getApp().getMap().getOptions()[HIDE_GROUP_MEMBERS] && getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION) {
if (!this.getSelected() && this.getIsLeader() && getApp().getMap().getOptions().hideGroupMembers && getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION) {
unitWhenGrouped = this.getDatabase()?.getByName(this.getName())?.unitWhenGrouped ?? null;
let member = this.getGroupMembers().reduce((prev: Unit | null, unit: Unit, index: number) => {
if (unit.getDatabaseEntry()?.unitWhenGrouped != undefined)
@ -1665,7 +1665,7 @@ export class GroundUnit extends Unit {
/* When we zoom past the grouping limit, grouping is enabled and the unit is a leader, we redraw the unit to apply any possible grouped marker */
checkZoomRedraw(): boolean {
return (this.getIsLeader() && getApp().getMap().getOptions()[HIDE_GROUP_MEMBERS] as boolean &&
return (this.getIsLeader() && getApp().getMap().getOptions().hideGroupMembers as boolean &&
(getApp().getMap().getZoom() >= GROUPING_ZOOM_TRANSITION && getApp().getMap().getPreviousZoom() < GROUPING_ZOOM_TRANSITION ||
getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION && getApp().getMap().getPreviousZoom() >= GROUPING_ZOOM_TRANSITION))
}

View File

@ -1192,9 +1192,6 @@ export class UnitsManager {
*
*/
copy(units: Unit[] | null = null) {
//if (!getApp().getContextManager().getCurrentContext().getAllowUnitCopying())
// return;
if (units === null)
units = this.getSelectedUnits();
@ -1212,9 +1209,6 @@ export class UnitsManager {
* @returns True if units were pasted successfully
*/
paste() {
//if (!getApp().getContextManager().getCurrentContext().getAllowUnitPasting())
// return;
let spawnPoints = 0;
/* If spawns are restricted, check that the user has the necessary spawn points */