mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Implemented basic Plugin handling
This commit is contained in:
parent
ad06117b78
commit
588228c050
@ -29,17 +29,8 @@ declare global {
|
||||
listener: (this: Document, ev: CustomEventMap[K]) => void): void;
|
||||
dispatchEvent<K extends keyof CustomEventMap>(ev: CustomEventMap[K]): void;
|
||||
}
|
||||
|
||||
function getOlympusPlugin(): OlympusPlugin;
|
||||
}
|
||||
|
||||
export interface ConfigParameters {
|
||||
port: number;
|
||||
address: string;
|
||||
}
|
||||
|
||||
export interface ContextMenuOption {
|
||||
tooltip: string;
|
||||
src: string;
|
||||
callback: CallableFunction;
|
||||
}
|
||||
|
||||
export { };
|
||||
export { };
|
||||
267
client/@types/olympus.d.ts
vendored
Normal file
267
client/@types/olympus.d.ts
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
interface OlympusPlugin {
|
||||
getName: () => string;
|
||||
initialize: (any) => boolean;
|
||||
}
|
||||
|
||||
declare global {
|
||||
function getOlympusPlugin(): OlympusPlugin;
|
||||
}
|
||||
|
||||
interface ConfigurationOptions {
|
||||
port: number;
|
||||
address: string;
|
||||
}
|
||||
|
||||
interface ContextMenuOption {
|
||||
tooltip: string;
|
||||
src: string;
|
||||
callback: CallableFunction;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
interface AirbaseOptions {
|
||||
name: string,
|
||||
position: L.LatLng
|
||||
}
|
||||
|
||||
interface AirbaseChartData {
|
||||
elevation: string,
|
||||
ICAO: string,
|
||||
TACAN: string,
|
||||
runways: AirbaseChartRunwayData[]
|
||||
}
|
||||
|
||||
interface AirbaseChartRunwayData {
|
||||
headings: AirbaseChartRunwayHeadingData[],
|
||||
length: string
|
||||
}
|
||||
|
||||
interface AirbaseChartRunwayHeadingData {
|
||||
[index: string]: {
|
||||
magHeading: string,
|
||||
ILS: string
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
callback: CallableFunction;
|
||||
name?: string
|
||||
}
|
||||
|
||||
interface ShortcutOptions {
|
||||
altKey?: boolean;
|
||||
callback: CallableFunction;
|
||||
ctrlKey?: boolean;
|
||||
name?: string;
|
||||
shiftKey?: boolean;
|
||||
}
|
||||
|
||||
interface KeyboardShortcutOptions extends ShortcutOptions {
|
||||
code: string;
|
||||
event?: "keydown" | "keyup";
|
||||
}
|
||||
|
||||
interface MouseShortcutOptions extends ShortcutOptions {
|
||||
button: number;
|
||||
event: "mousedown" | "mouseup";
|
||||
}
|
||||
|
||||
interface Manager {
|
||||
add: CallableFunction;
|
||||
}
|
||||
@ -10,6 +10,7 @@ var indexRouter = require('./routes/index');
|
||||
var uikitRouter = require('./routes/uikit');
|
||||
var usersRouter = require('./routes/users');
|
||||
var resourcesRouter = require('./routes/resources');
|
||||
var pluginsRouter = require('./routes/plugins');
|
||||
|
||||
var app = express();
|
||||
|
||||
@ -22,6 +23,7 @@ app.use(express.static(path.join(__dirname, 'public')));
|
||||
app.use('/', indexRouter);
|
||||
app.use('/api/atc', atcRouter);
|
||||
app.use('/api/airbases', airbasesRouter);
|
||||
app.use('/plugins', pluginsRouter)
|
||||
app.use('/users', usersRouter);
|
||||
app.use('/uikit', uikitRouter);
|
||||
app.use('/resources', resourcesRouter);
|
||||
|
||||
@ -444,7 +444,7 @@ class DemoDataGenerator {
|
||||
|
||||
var auth = req.get("Authorization");
|
||||
if (auth) {
|
||||
var username = atob(auth.replace("Basic ", "")).split(":")[0];
|
||||
var username = Buffer.from(auth.replace("Basic ", ""), 'base64').toString('binary').split(":")[0];
|
||||
switch (username) {
|
||||
case "admin":
|
||||
ret.mission.commandModeOptions.commandMode = "Game master";
|
||||
|
||||
5
client/plugins/controltips/copy.bat
Normal file
5
client/plugins/controltips/copy.bat
Normal file
@ -0,0 +1,5 @@
|
||||
mkdir .\\..\\..\\public\\plugins\\controltipsplugin
|
||||
|
||||
copy .\\index.js .\\..\\..\\public\\plugins\\controltipsplugin\\index.js
|
||||
copy .\\plugin.json .\\..\\..\\public\\plugins\\controltipsplugin\\plugin.json
|
||||
copy .\\style.css .\\..\\..\\public\\plugins\\controltipsplugin\\style.css
|
||||
252
client/plugins/controltips/index.js
Normal file
252
client/plugins/controltips/index.js
Normal file
@ -0,0 +1,252 @@
|
||||
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||||
"use strict";
|
||||
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
||||
if (kind === "m") throw new TypeError("Private method is not writable");
|
||||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
||||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
||||
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
||||
};
|
||||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
||||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
||||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
||||
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
||||
};
|
||||
var _ControlTips_instances, _ControlTips_element, _ControlTips_app, _ControlTips_shortcutManager, _ControlTips_cursorIsHoveringOverUnit, _ControlTips_cursorIsHoveringOverAirbase, _ControlTips_updateTips;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ControlTips = void 0;
|
||||
class ControlTips {
|
||||
constructor() {
|
||||
_ControlTips_instances.add(this);
|
||||
_ControlTips_element.set(this, void 0);
|
||||
_ControlTips_app.set(this, void 0);
|
||||
_ControlTips_shortcutManager.set(this, void 0);
|
||||
_ControlTips_cursorIsHoveringOverUnit.set(this, false);
|
||||
_ControlTips_cursorIsHoveringOverAirbase.set(this, false);
|
||||
__classPrivateFieldSet(this, _ControlTips_element, document.createElement("div"), "f");
|
||||
__classPrivateFieldGet(this, _ControlTips_element, "f").id = "control-tips-panel";
|
||||
document.body.appendChild(__classPrivateFieldGet(this, _ControlTips_element, "f"));
|
||||
console.log("HELLO");
|
||||
}
|
||||
getName() {
|
||||
return "Control Tips Plugin";
|
||||
}
|
||||
initialize(app) {
|
||||
__classPrivateFieldSet(this, _ControlTips_app, app, "f");
|
||||
__classPrivateFieldSet(this, _ControlTips_shortcutManager, __classPrivateFieldGet(this, _ControlTips_app, "f").getShortcutManager(), "f");
|
||||
__classPrivateFieldGet(this, _ControlTips_shortcutManager, "f").onKeyDown(() => {
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
});
|
||||
__classPrivateFieldGet(this, _ControlTips_shortcutManager, "f").onKeyUp(() => {
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("airbaseMouseover", (ev) => {
|
||||
__classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverAirbase, true, "f");
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("airbaseMouseout", (ev) => {
|
||||
__classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverAirbase, false, "f");
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
});
|
||||
//document.addEventListener("unitDeselection", (ev: CustomEvent) => {
|
||||
// this.#updateTips();
|
||||
//});
|
||||
document.addEventListener("unitMouseover", (ev) => {
|
||||
__classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverUnit, true, "f");
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("unitMouseout", (ev) => {
|
||||
__classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverUnit, false, "f");
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
});
|
||||
//document.addEventListener("unitSelection", (ev: CustomEvent) => {
|
||||
// this.#updateTips()
|
||||
//});
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
return true;
|
||||
}
|
||||
getElement() {
|
||||
return __classPrivateFieldGet(this, _ControlTips_element, "f");
|
||||
}
|
||||
toggle(bool) {
|
||||
this.getElement().classList.toggle("hide", bool);
|
||||
}
|
||||
}
|
||||
exports.ControlTips = ControlTips;
|
||||
_ControlTips_element = new WeakMap(), _ControlTips_app = new WeakMap(), _ControlTips_shortcutManager = new WeakMap(), _ControlTips_cursorIsHoveringOverUnit = new WeakMap(), _ControlTips_cursorIsHoveringOverAirbase = new WeakMap(), _ControlTips_instances = new WeakSet(), _ControlTips_updateTips = function _ControlTips_updateTips() {
|
||||
const combos = [
|
||||
{
|
||||
"keys": [],
|
||||
"tips": [
|
||||
{
|
||||
"key": `SHIFT`,
|
||||
"action": `Box select`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false,
|
||||
"showIfUnitSelected": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse1`,
|
||||
"action": `Deselect`,
|
||||
"showIfUnitSelected": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse1+drag`,
|
||||
"action": `Move map`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false,
|
||||
"showIfUnitSelected": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Spawn menu`,
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Quick options`,
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Airbase menu`,
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": true,
|
||||
"showIfHoveringOverUnit": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Set first waypoint`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfUnitSelected": true,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": "CTRL+Mouse2",
|
||||
"action": "Add waypoint",
|
||||
"showIfUnitSelected": true,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2 (hold)`,
|
||||
"action": `Point operations`,
|
||||
"showIfUnitSelected": true,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": "CTRL",
|
||||
"action": " Pin tool",
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": "CTRL+Mouse2",
|
||||
"action": " Airbase menu",
|
||||
"showIfUnitSelected": true,
|
||||
"showIfHoveringOverAirbase": true,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": `Delete`,
|
||||
"action": `Delete unit`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfUnitSelected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"keys": ["ControlLeft"],
|
||||
"tips": [
|
||||
{
|
||||
"key": `Mouse1`,
|
||||
"action": "Toggle pin",
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse1`,
|
||||
"action": "Toggle selection",
|
||||
"showIfUnitSelected": true,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Add waypoint`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfUnitSelected": true,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Airbase menu`,
|
||||
"showIfHoveringOverAirbase": true,
|
||||
"showIfUnitSelected": true,
|
||||
"unitsMustBeControlled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"keys": ["ShiftLeft"],
|
||||
"tips": [
|
||||
{
|
||||
"key": `mouse1+drag`,
|
||||
"action": "Box select"
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
const currentCombo = combos.find((combo) => __classPrivateFieldGet(this, _ControlTips_shortcutManager, "f").keyComboMatches(combo.keys)) || combos[0];
|
||||
const element = this.getElement();
|
||||
element.innerHTML = "";
|
||||
let numSelectedUnits = 0;
|
||||
let unitSelectionContainsControlled = false;
|
||||
if (__classPrivateFieldGet(this, _ControlTips_app, "f").getUnitsManager()) {
|
||||
let selectedUnits = Object.values(__classPrivateFieldGet(this, _ControlTips_app, "f").getUnitsManager().getSelectedUnits());
|
||||
numSelectedUnits = selectedUnits.length;
|
||||
unitSelectionContainsControlled = selectedUnits.some((unit) => unit.getControlled());
|
||||
}
|
||||
currentCombo.tips.forEach((tip) => {
|
||||
if (numSelectedUnits > 0) {
|
||||
if (tip.showIfUnitSelected === false) {
|
||||
return;
|
||||
}
|
||||
if (tip.unitsMustBeControlled === true && unitSelectionContainsControlled === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (numSelectedUnits === 0 && tip.showIfUnitSelected === true) {
|
||||
return;
|
||||
}
|
||||
if (typeof tip.showIfHoveringOverAirbase === "boolean") {
|
||||
if (tip.showIfHoveringOverAirbase !== __classPrivateFieldGet(this, _ControlTips_cursorIsHoveringOverAirbase, "f")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (typeof tip.showIfHoveringOverUnit === "boolean") {
|
||||
if (tip.showIfHoveringOverUnit !== __classPrivateFieldGet(this, _ControlTips_cursorIsHoveringOverUnit, "f")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
element.innerHTML += `<div><span class="key">${tip.key}</span><span class="action">${tip.action}</span></div>`;
|
||||
});
|
||||
};
|
||||
|
||||
},{}],2:[function(require,module,exports){
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const controltips_1 = require("./controltips");
|
||||
globalThis.getOlympusPlugin = () => {
|
||||
return new controltips_1.ControlTips();
|
||||
};
|
||||
|
||||
},{"./controltips":1}]},{},[2]);
|
||||
162
client/plugins/controltips/package-lock.json
generated
Normal file
162
client/plugins/controltips/package-lock.json
generated
Normal file
@ -0,0 +1,162 @@
|
||||
{
|
||||
"name": "ControlTipsPlugin",
|
||||
"version": "v0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"any-promise": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
||||
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
|
||||
},
|
||||
"convert-source-map": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||
},
|
||||
"error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
||||
"requires": {
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
|
||||
},
|
||||
"is-utf8": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
|
||||
"integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q=="
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
|
||||
},
|
||||
"parse-json": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
|
||||
"integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==",
|
||||
"requires": {
|
||||
"error-ex": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-bom": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
|
||||
"integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==",
|
||||
"requires": {
|
||||
"is-utf8": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
|
||||
},
|
||||
"through2": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
||||
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
|
||||
"requires": {
|
||||
"readable-stream": "~2.3.6",
|
||||
"xtend": "~4.0.1"
|
||||
}
|
||||
},
|
||||
"tsconfig": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-5.0.3.tgz",
|
||||
"integrity": "sha512-Cq65A3kVp6BbsUgg9DRHafaGmbMb9EhAc7fjWvudNWKjkbWrt43FnrtZt6awshH1R0ocfF2Z0uxock3lVqEgOg==",
|
||||
"requires": {
|
||||
"any-promise": "^1.3.0",
|
||||
"parse-json": "^2.2.0",
|
||||
"strip-bom": "^2.0.0",
|
||||
"strip-json-comments": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"tsify": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/tsify/-/tsify-5.0.4.tgz",
|
||||
"integrity": "sha512-XAZtQ5OMPsJFclkZ9xMZWkSNyMhMxEPsz3D2zu79yoKorH9j/DT4xCloJeXk5+cDhosEibu4bseMVjyPOAyLJA==",
|
||||
"requires": {
|
||||
"convert-source-map": "^1.1.0",
|
||||
"fs.realpath": "^1.0.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"semver": "^6.1.0",
|
||||
"through2": "^2.0.0",
|
||||
"tsconfig": "^5.0.3"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
10
client/plugins/controltips/package.json
Normal file
10
client/plugins/controltips/package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "ControlTipsPlugin",
|
||||
"version": "v0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "browserify ./src/index.ts -p [ tsify --noImplicitAny] > index.js && copy.bat"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {}
|
||||
}
|
||||
6
client/plugins/controltips/plugin.json
Normal file
6
client/plugins/controltips/plugin.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Control Tip Plugin",
|
||||
"version": "0.0.1",
|
||||
"description": "This plugin shows useful control tips on the right side of the screen. The tips change dynamically depending on what the user does",
|
||||
"author": "Peekaboo"
|
||||
}
|
||||
@ -1,80 +1,79 @@
|
||||
import { OlympusApp } from "../olympusapp";
|
||||
import { ShortcutManager } from "../shortcut/shortcutmanager";
|
||||
import { Unit } from "../unit/unit";
|
||||
export class ControlTipsPlugin implements OlympusPlugin {
|
||||
#element: HTMLElement;
|
||||
#app: any;
|
||||
#shortcutManager: any;
|
||||
#cursorIsHoveringOverUnit: boolean = false;
|
||||
#cursorIsHoveringOverAirbase: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this.#element = document.createElement("div");
|
||||
this.#element.id = "control-tips-panel";
|
||||
document.body.appendChild(this.#element);
|
||||
|
||||
console.log("HELLO")
|
||||
}
|
||||
|
||||
export class ControlTips {
|
||||
getName() {
|
||||
return "Control Tips Plugin"
|
||||
}
|
||||
|
||||
#element:HTMLElement;
|
||||
#cursorIsHoveringOverUnit:boolean = false;
|
||||
#cursorIsHoveringOverAirbase:boolean = false;
|
||||
#olympusApp:OlympusApp;
|
||||
#shortcutManager:ShortcutManager;
|
||||
initialize(app: any) {
|
||||
this.#app = app;
|
||||
|
||||
constructor( ID:string, olympusApp:OlympusApp ) {
|
||||
this.#shortcutManager = this.#app.getShortcutManager();
|
||||
|
||||
this.#element = <HTMLElement>document.getElementById( ID );
|
||||
|
||||
this.#olympusApp = olympusApp;
|
||||
|
||||
this.#shortcutManager = this.#olympusApp.getShortcutManager();
|
||||
|
||||
this.#shortcutManager.onKeyDown( () => {
|
||||
this.#updateTips()
|
||||
this.#shortcutManager.onKeyDown(() => {
|
||||
this.#updateTips()
|
||||
});
|
||||
|
||||
this.#shortcutManager.onKeyUp( () => {
|
||||
this.#updateTips()
|
||||
this.#shortcutManager.onKeyUp(() => {
|
||||
this.#updateTips()
|
||||
});
|
||||
|
||||
document.addEventListener( "airbaseMouseover", ( ev:CustomEventInit ) => {
|
||||
document.addEventListener("airbaseMouseover", (ev: CustomEventInit) => {
|
||||
this.#cursorIsHoveringOverAirbase = true;
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener( "airbaseMouseout", ( ev:CustomEventInit ) => {
|
||||
document.addEventListener("airbaseMouseout", (ev: CustomEventInit) => {
|
||||
this.#cursorIsHoveringOverAirbase = false;
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener( "unitDeselection", ( ev:CustomEvent ) => {
|
||||
this.#updateTips();
|
||||
});
|
||||
//document.addEventListener("unitDeselection", (ev: CustomEvent) => {
|
||||
// this.#updateTips();
|
||||
//});
|
||||
|
||||
document.addEventListener( "unitMouseover", ( ev:CustomEventInit ) => {
|
||||
document.addEventListener("unitMouseover", (ev: CustomEventInit) => {
|
||||
this.#cursorIsHoveringOverUnit = true;
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener( "unitMouseout", ( ev:CustomEventInit ) => {
|
||||
document.addEventListener("unitMouseout", (ev: CustomEventInit) => {
|
||||
this.#cursorIsHoveringOverUnit = false;
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener( "unitSelection", ( ev:CustomEvent ) => {
|
||||
this.#updateTips()
|
||||
});
|
||||
//document.addEventListener("unitSelection", (ev: CustomEvent) => {
|
||||
// this.#updateTips()
|
||||
//});
|
||||
|
||||
this.#updateTips();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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" ) );
|
||||
toggle(bool?: boolean) {
|
||||
this.getElement().classList.toggle("hide", bool);
|
||||
}
|
||||
|
||||
#updateTips() {
|
||||
|
||||
const combos:Array<object> = [
|
||||
const combos: Array<object> = [
|
||||
{
|
||||
"keys": [],
|
||||
"tips": [
|
||||
@ -164,7 +163,7 @@ export class ControlTips {
|
||||
]
|
||||
},
|
||||
{
|
||||
"keys": [ "ControlLeft" ],
|
||||
"keys": ["ControlLeft"],
|
||||
"tips": [
|
||||
{
|
||||
"key": `Mouse1`,
|
||||
@ -197,7 +196,7 @@ export class ControlTips {
|
||||
]
|
||||
},
|
||||
{
|
||||
"keys": [ "ShiftLeft" ],
|
||||
"keys": ["ShiftLeft"],
|
||||
"tips": [
|
||||
{
|
||||
"key": `mouse1+drag`,
|
||||
@ -207,48 +206,44 @@ export class ControlTips {
|
||||
}
|
||||
];
|
||||
|
||||
const currentCombo:any = combos.find( (combo:any) => this.#shortcutManager.keyComboMatches( combo.keys ) ) || combos[0];
|
||||
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() );
|
||||
if (this.#app.getUnitsManager()) {
|
||||
let selectedUnits = Object.values(this.#app.getUnitsManager().getSelectedUnits());
|
||||
numSelectedUnits = selectedUnits.length;
|
||||
unitSelectionContainsControlled = selectedUnits.some( (unit:Unit) => unit.getControlled() );
|
||||
unitSelectionContainsControlled = selectedUnits.some((unit: any) => unit.getControlled());
|
||||
}
|
||||
|
||||
|
||||
currentCombo.tips.forEach( ( tip:any ) => {
|
||||
|
||||
if ( numSelectedUnits > 0 ) {
|
||||
if ( tip.showIfUnitSelected === false ) {
|
||||
currentCombo.tips.forEach((tip: any) => {
|
||||
if (numSelectedUnits > 0) {
|
||||
if (tip.showIfUnitSelected === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( tip.unitsMustBeControlled === true && unitSelectionContainsControlled === false ) {
|
||||
if (tip.unitsMustBeControlled === true && unitSelectionContainsControlled === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( numSelectedUnits === 0 && tip.showIfUnitSelected === true ) {
|
||||
if (numSelectedUnits === 0 && tip.showIfUnitSelected === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( typeof tip.showIfHoveringOverAirbase === "boolean" ) {
|
||||
if ( tip.showIfHoveringOverAirbase !== this.#cursorIsHoveringOverAirbase ) {
|
||||
if (typeof tip.showIfHoveringOverAirbase === "boolean") {
|
||||
if (tip.showIfHoveringOverAirbase !== this.#cursorIsHoveringOverAirbase) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( typeof tip.showIfHoveringOverUnit === "boolean" ) {
|
||||
if ( tip.showIfHoveringOverUnit !== this.#cursorIsHoveringOverUnit ) {
|
||||
if (typeof tip.showIfHoveringOverUnit === "boolean") {
|
||||
if (tip.showIfHoveringOverUnit !== this.#cursorIsHoveringOverUnit) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -256,8 +251,5 @@ export class ControlTips {
|
||||
element.innerHTML += `<div><span class="key">${tip.key}</span><span class="action">${tip.action}</span></div>`
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
5
client/plugins/controltips/src/index.ts
Normal file
5
client/plugins/controltips/src/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { ControlTipsPlugin } from "./controltips";
|
||||
|
||||
globalThis.getOlympusPlugin = () => {
|
||||
return new ControlTipsPlugin();
|
||||
}
|
||||
@ -11,23 +11,23 @@
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
#control-tips-panel > * {
|
||||
#control-tips-panel>* {
|
||||
align-items: center;
|
||||
align-self: end;
|
||||
background-color: var( --background-steel );
|
||||
background-color: var(--background-steel);
|
||||
border-radius: var(--border-radius-md);
|
||||
color: white;
|
||||
column-gap: 8px;
|
||||
display:flex;
|
||||
display: flex;
|
||||
justify-items: right;
|
||||
opacity: .9;
|
||||
padding:5px;
|
||||
width:fit-content;
|
||||
padding: 5px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#control-tips-panel > * > .key {
|
||||
background-color: var( --background-grey );
|
||||
border-radius: var( --border-radius-sm );
|
||||
#control-tips-panel>*>.key {
|
||||
background-color: var(--background-grey);
|
||||
border-radius: var(--border-radius-sm);
|
||||
color: white;
|
||||
padding:1px 4px;
|
||||
padding: 1px 4px;
|
||||
}
|
||||
104
client/plugins/controltips/tsconfig.json
Normal file
104
client/plugins/controltips/tsconfig.json
Normal file
@ -0,0 +1,104 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
/* Language and Environment */
|
||||
"target": "ES2017", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
/* Modules */
|
||||
"module": "commonjs", /* Specify what module code is generated. */
|
||||
"rootDirs": ["./src", "../../@types"], /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"../../@types"
|
||||
], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
"types": [
|
||||
"olympus"
|
||||
], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
/* JavaScript Support */
|
||||
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": [
|
||||
"src/*.ts",
|
||||
"../../@types/*.d.ts"
|
||||
]
|
||||
}
|
||||
252
client/public/plugins/controltipsplugin/index.js
Normal file
252
client/public/plugins/controltipsplugin/index.js
Normal file
@ -0,0 +1,252 @@
|
||||
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||||
"use strict";
|
||||
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
||||
if (kind === "m") throw new TypeError("Private method is not writable");
|
||||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
||||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
||||
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
||||
};
|
||||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
||||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
||||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
||||
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
||||
};
|
||||
var _ControlTips_instances, _ControlTips_element, _ControlTips_app, _ControlTips_shortcutManager, _ControlTips_cursorIsHoveringOverUnit, _ControlTips_cursorIsHoveringOverAirbase, _ControlTips_updateTips;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ControlTips = void 0;
|
||||
class ControlTips {
|
||||
constructor() {
|
||||
_ControlTips_instances.add(this);
|
||||
_ControlTips_element.set(this, void 0);
|
||||
_ControlTips_app.set(this, void 0);
|
||||
_ControlTips_shortcutManager.set(this, void 0);
|
||||
_ControlTips_cursorIsHoveringOverUnit.set(this, false);
|
||||
_ControlTips_cursorIsHoveringOverAirbase.set(this, false);
|
||||
__classPrivateFieldSet(this, _ControlTips_element, document.createElement("div"), "f");
|
||||
__classPrivateFieldGet(this, _ControlTips_element, "f").id = "control-tips-panel";
|
||||
document.body.appendChild(__classPrivateFieldGet(this, _ControlTips_element, "f"));
|
||||
console.log("HELLO");
|
||||
}
|
||||
getName() {
|
||||
return "Control Tips Plugin";
|
||||
}
|
||||
initialize(app) {
|
||||
__classPrivateFieldSet(this, _ControlTips_app, app, "f");
|
||||
__classPrivateFieldSet(this, _ControlTips_shortcutManager, __classPrivateFieldGet(this, _ControlTips_app, "f").getShortcutManager(), "f");
|
||||
__classPrivateFieldGet(this, _ControlTips_shortcutManager, "f").onKeyDown(() => {
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
});
|
||||
__classPrivateFieldGet(this, _ControlTips_shortcutManager, "f").onKeyUp(() => {
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("airbaseMouseover", (ev) => {
|
||||
__classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverAirbase, true, "f");
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("airbaseMouseout", (ev) => {
|
||||
__classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverAirbase, false, "f");
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
});
|
||||
//document.addEventListener("unitDeselection", (ev: CustomEvent) => {
|
||||
// this.#updateTips();
|
||||
//});
|
||||
document.addEventListener("unitMouseover", (ev) => {
|
||||
__classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverUnit, true, "f");
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("unitMouseout", (ev) => {
|
||||
__classPrivateFieldSet(this, _ControlTips_cursorIsHoveringOverUnit, false, "f");
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
});
|
||||
//document.addEventListener("unitSelection", (ev: CustomEvent) => {
|
||||
// this.#updateTips()
|
||||
//});
|
||||
__classPrivateFieldGet(this, _ControlTips_instances, "m", _ControlTips_updateTips).call(this);
|
||||
return true;
|
||||
}
|
||||
getElement() {
|
||||
return __classPrivateFieldGet(this, _ControlTips_element, "f");
|
||||
}
|
||||
toggle(bool) {
|
||||
this.getElement().classList.toggle("hide", bool);
|
||||
}
|
||||
}
|
||||
exports.ControlTips = ControlTips;
|
||||
_ControlTips_element = new WeakMap(), _ControlTips_app = new WeakMap(), _ControlTips_shortcutManager = new WeakMap(), _ControlTips_cursorIsHoveringOverUnit = new WeakMap(), _ControlTips_cursorIsHoveringOverAirbase = new WeakMap(), _ControlTips_instances = new WeakSet(), _ControlTips_updateTips = function _ControlTips_updateTips() {
|
||||
const combos = [
|
||||
{
|
||||
"keys": [],
|
||||
"tips": [
|
||||
{
|
||||
"key": `SHIFT`,
|
||||
"action": `Box select`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false,
|
||||
"showIfUnitSelected": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse1`,
|
||||
"action": `Deselect`,
|
||||
"showIfUnitSelected": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse1+drag`,
|
||||
"action": `Move map`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false,
|
||||
"showIfUnitSelected": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Spawn menu`,
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Quick options`,
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Airbase menu`,
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": true,
|
||||
"showIfHoveringOverUnit": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Set first waypoint`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfUnitSelected": true,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": "CTRL+Mouse2",
|
||||
"action": "Add waypoint",
|
||||
"showIfUnitSelected": true,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2 (hold)`,
|
||||
"action": `Point operations`,
|
||||
"showIfUnitSelected": true,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": "CTRL",
|
||||
"action": " Pin tool",
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": "CTRL+Mouse2",
|
||||
"action": " Airbase menu",
|
||||
"showIfUnitSelected": true,
|
||||
"showIfHoveringOverAirbase": true,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": `Delete`,
|
||||
"action": `Delete unit`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfUnitSelected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"keys": ["ControlLeft"],
|
||||
"tips": [
|
||||
{
|
||||
"key": `Mouse1`,
|
||||
"action": "Toggle pin",
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse1`,
|
||||
"action": "Toggle selection",
|
||||
"showIfUnitSelected": true,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Add waypoint`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfUnitSelected": true,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Airbase menu`,
|
||||
"showIfHoveringOverAirbase": true,
|
||||
"showIfUnitSelected": true,
|
||||
"unitsMustBeControlled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"keys": ["ShiftLeft"],
|
||||
"tips": [
|
||||
{
|
||||
"key": `mouse1+drag`,
|
||||
"action": "Box select"
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
const currentCombo = combos.find((combo) => __classPrivateFieldGet(this, _ControlTips_shortcutManager, "f").keyComboMatches(combo.keys)) || combos[0];
|
||||
const element = this.getElement();
|
||||
element.innerHTML = "";
|
||||
let numSelectedUnits = 0;
|
||||
let unitSelectionContainsControlled = false;
|
||||
if (__classPrivateFieldGet(this, _ControlTips_app, "f").getUnitsManager()) {
|
||||
let selectedUnits = Object.values(__classPrivateFieldGet(this, _ControlTips_app, "f").getUnitsManager().getSelectedUnits());
|
||||
numSelectedUnits = selectedUnits.length;
|
||||
unitSelectionContainsControlled = selectedUnits.some((unit) => unit.getControlled());
|
||||
}
|
||||
currentCombo.tips.forEach((tip) => {
|
||||
if (numSelectedUnits > 0) {
|
||||
if (tip.showIfUnitSelected === false) {
|
||||
return;
|
||||
}
|
||||
if (tip.unitsMustBeControlled === true && unitSelectionContainsControlled === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (numSelectedUnits === 0 && tip.showIfUnitSelected === true) {
|
||||
return;
|
||||
}
|
||||
if (typeof tip.showIfHoveringOverAirbase === "boolean") {
|
||||
if (tip.showIfHoveringOverAirbase !== __classPrivateFieldGet(this, _ControlTips_cursorIsHoveringOverAirbase, "f")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (typeof tip.showIfHoveringOverUnit === "boolean") {
|
||||
if (tip.showIfHoveringOverUnit !== __classPrivateFieldGet(this, _ControlTips_cursorIsHoveringOverUnit, "f")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
element.innerHTML += `<div><span class="key">${tip.key}</span><span class="action">${tip.action}</span></div>`;
|
||||
});
|
||||
};
|
||||
|
||||
},{}],2:[function(require,module,exports){
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const controltips_1 = require("./controltips");
|
||||
globalThis.getOlympusPlugin = () => {
|
||||
return new controltips_1.ControlTips();
|
||||
};
|
||||
|
||||
},{"./controltips":1}]},{},[2]);
|
||||
6
client/public/plugins/controltipsplugin/plugin.json
Normal file
6
client/public/plugins/controltipsplugin/plugin.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Control Tip Plugin",
|
||||
"version": "0.0.1",
|
||||
"description": "This plugin shows useful control tips on the right side of the screen. The tips change dynamically depending on what the user does",
|
||||
"author": "Peekaboo"
|
||||
}
|
||||
33
client/public/plugins/controltipsplugin/style.css
Normal file
33
client/public/plugins/controltipsplugin/style.css
Normal file
@ -0,0 +1,33 @@
|
||||
#control-tips-panel {
|
||||
align-self: center;
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
font-size: 13px;
|
||||
justify-self: flex-end;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
row-gap: 20px;
|
||||
text-align: right;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
#control-tips-panel>* {
|
||||
align-items: center;
|
||||
align-self: end;
|
||||
background-color: var(--background-steel);
|
||||
border-radius: var(--border-radius-md);
|
||||
color: white;
|
||||
column-gap: 8px;
|
||||
display: flex;
|
||||
justify-items: right;
|
||||
opacity: .9;
|
||||
padding: 5px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#control-tips-panel>*>.key {
|
||||
background-color: var(--background-grey);
|
||||
border-radius: var(--border-radius-sm);
|
||||
color: white;
|
||||
padding: 1px 4px;
|
||||
}
|
||||
@ -60,11 +60,6 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.leaflet-container img.leaflet-tile {
|
||||
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
|
||||
mix-blend-mode: plus-lighter;
|
||||
}
|
||||
|
||||
.leaflet-container.leaflet-touch-zoom {
|
||||
-ms-touch-action: pan-x pan-y;
|
||||
touch-action: pan-x pan-y;
|
||||
@ -651,7 +646,7 @@ svg.leaflet-image-layer.leaflet-interactive path {
|
||||
}
|
||||
|
||||
/* Printing */
|
||||
|
||||
|
||||
@media print {
|
||||
/* Prevent printers from removing background-images of controls. */
|
||||
.leaflet-control {
|
||||
|
||||
@ -1,82 +1,65 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
|
||||
const bodyParser = require('body-parser');
|
||||
app.use(bodyParser.urlencoded({ extended: false}));
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(bodyParser.json());
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Flight:
|
||||
"name"
|
||||
"take-off time"
|
||||
"priority"
|
||||
"status"
|
||||
|
||||
//*/
|
||||
|
||||
function uuidv4() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Flight( name, boardId, unitId ) {
|
||||
function Flight(name, boardId, unitId) {
|
||||
this.assignedAltitude = 0;
|
||||
this.assignedSpeed = 0;
|
||||
this.id = uuidv4();
|
||||
this.boardId = boardId;
|
||||
this.name = name;
|
||||
this.status = "unknown";
|
||||
this.takeoffTime = -1;
|
||||
this.unitId = parseInt( unitId );
|
||||
this.assignedSpeed = 0;
|
||||
this.id = uuidv4();
|
||||
this.boardId = boardId;
|
||||
this.name = name;
|
||||
this.status = "unknown";
|
||||
this.takeoffTime = -1;
|
||||
this.unitId = parseInt(unitId);
|
||||
}
|
||||
|
||||
Flight.prototype.getData = function() {
|
||||
Flight.prototype.getData = function () {
|
||||
return {
|
||||
"assignedAltitude" : this.assignedAltitude,
|
||||
"assignedSpeed" : this.assignedSpeed,
|
||||
"id" : this.id,
|
||||
"boardId" : this.boardId,
|
||||
"name" : this.name,
|
||||
"status" : this.status,
|
||||
"takeoffTime" : this.takeoffTime,
|
||||
"unitId" : this.unitId
|
||||
"assignedAltitude": this.assignedAltitude,
|
||||
"assignedSpeed": this.assignedSpeed,
|
||||
"id": this.id,
|
||||
"boardId": this.boardId,
|
||||
"name": this.name,
|
||||
"status": this.status,
|
||||
"takeoffTime": this.takeoffTime,
|
||||
"unitId": this.unitId
|
||||
};
|
||||
}
|
||||
|
||||
Flight.prototype.setAssignedAltitude = function (assignedAltitude) {
|
||||
|
||||
Flight.prototype.setAssignedAltitude = function( assignedAltitude ) {
|
||||
|
||||
if ( isNaN( assignedAltitude ) ) {
|
||||
if (isNaN(assignedAltitude)) {
|
||||
return "Altitude must be a number"
|
||||
}
|
||||
|
||||
this.assignedAltitude = parseInt( assignedAltitude );
|
||||
this.assignedAltitude = parseInt(assignedAltitude);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
Flight.prototype.setAssignedSpeed = function (assignedSpeed) {
|
||||
|
||||
Flight.prototype.setAssignedSpeed = function( assignedSpeed ) {
|
||||
|
||||
if ( isNaN( assignedSpeed ) ) {
|
||||
if (isNaN(assignedSpeed)) {
|
||||
return "Speed must be a number"
|
||||
}
|
||||
|
||||
this.assignedSpeed = parseInt( assignedSpeed );
|
||||
this.assignedSpeed = parseInt(assignedSpeed);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
Flight.prototype.setOrder = function( order ) {
|
||||
Flight.prototype.setOrder = function (order) {
|
||||
|
||||
this.order = order;
|
||||
|
||||
@ -84,10 +67,9 @@ Flight.prototype.setOrder = function( order ) {
|
||||
|
||||
}
|
||||
|
||||
Flight.prototype.setStatus = function (status) {
|
||||
|
||||
Flight.prototype.setStatus = function( status ) {
|
||||
|
||||
if ( [ "unknown", "checkedin", "readytotaxi", "clearedtotaxi", "halted", "terminated" ].indexOf( status ) < 0 ) {
|
||||
if (["unknown", "checkedin", "readytotaxi", "clearedtotaxi", "halted", "terminated"].indexOf(status) < 0) {
|
||||
return "Invalid status";
|
||||
}
|
||||
|
||||
@ -97,197 +79,186 @@ Flight.prototype.setStatus = function( status ) {
|
||||
|
||||
}
|
||||
|
||||
Flight.prototype.setTakeoffTime = function (takeoffTime) {
|
||||
|
||||
Flight.prototype.setTakeoffTime = function( takeoffTime ) {
|
||||
|
||||
if ( takeoffTime === "" || takeoffTime === -1 ) {
|
||||
if (takeoffTime === "" || takeoffTime === -1) {
|
||||
this.takeoffTime = -1;
|
||||
}
|
||||
|
||||
if ( isNaN( takeoffTime ) ) {
|
||||
if (isNaN(takeoffTime)) {
|
||||
return "Invalid takeoff time"
|
||||
}
|
||||
|
||||
this.takeoffTime = parseInt( takeoffTime );
|
||||
this.takeoffTime = parseInt(takeoffTime);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function ATCDataHandler( data ) {
|
||||
function ATCDataHandler(data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
ATCDataHandler.prototype.addFlight = function( flight ) {
|
||||
ATCDataHandler.prototype.addFlight = function (flight) {
|
||||
|
||||
if ( flight instanceof Flight === false ) {
|
||||
throw new Error( "Given flight is not an instance of Flight" );
|
||||
if (flight instanceof Flight === false) {
|
||||
throw new Error("Given flight is not an instance of Flight");
|
||||
}
|
||||
|
||||
this.data.flights[ flight.id ] = flight;
|
||||
this.data.flights[flight.id] = flight;
|
||||
|
||||
}
|
||||
|
||||
|
||||
ATCDataHandler.prototype.deleteFlight = function( flightId ) {
|
||||
delete this.data.flights[ flightId ];
|
||||
ATCDataHandler.prototype.deleteFlight = function (flightId) {
|
||||
delete this.data.flights[flightId];
|
||||
}
|
||||
|
||||
|
||||
ATCDataHandler.prototype.getFlight = function( flightId ) {
|
||||
return this.data.flights[ flightId ] || false;
|
||||
ATCDataHandler.prototype.getFlight = function (flightId) {
|
||||
return this.data.flights[flightId] || false;
|
||||
}
|
||||
|
||||
|
||||
ATCDataHandler.prototype.getFlights = function() {
|
||||
ATCDataHandler.prototype.getFlights = function () {
|
||||
return this.data.flights;
|
||||
}
|
||||
|
||||
|
||||
const dataHandler = new ATCDataHandler( {
|
||||
const dataHandler = new ATCDataHandler({
|
||||
"flights": {}
|
||||
} );
|
||||
|
||||
|
||||
});
|
||||
|
||||
/**************************************************************************************************************/
|
||||
// Endpoints
|
||||
/**************************************************************************************************************/
|
||||
app.get("/flight", (req, res) => {
|
||||
|
||||
let flights = Object.values(dataHandler.getFlights());
|
||||
|
||||
app.get( "/flight", ( req, res ) => {
|
||||
if (flights && req.query.boardId) {
|
||||
|
||||
let flights = Object.values( dataHandler.getFlights() );
|
||||
|
||||
if ( flights && req.query.boardId ) {
|
||||
|
||||
flights = flights.reduce( ( acc, flight ) => {
|
||||
if ( flight.boardId === req.query.boardId ) {
|
||||
acc[ flight.id ] = flight;
|
||||
flights = flights.reduce((acc, flight) => {
|
||||
if (flight.boardId === req.query.boardId) {
|
||||
acc[flight.id] = flight;
|
||||
}
|
||||
return acc;
|
||||
}, {} );
|
||||
}, {});
|
||||
|
||||
}
|
||||
|
||||
res.json( flights );
|
||||
res.json(flights);
|
||||
|
||||
});
|
||||
|
||||
|
||||
app.patch( "/flight/:flightId", ( req, res ) => {
|
||||
app.patch("/flight/:flightId", (req, res) => {
|
||||
|
||||
const flightId = req.params.flightId;
|
||||
const flight = dataHandler.getFlight( flightId );
|
||||
const flight = dataHandler.getFlight(flightId);
|
||||
|
||||
if ( !flight ) {
|
||||
res.status( 400 ).send( `Unrecognised flight ID (given: "${req.params.flightId}")` );
|
||||
if (!flight) {
|
||||
res.status(400).send(`Unrecognised flight ID (given: "${req.params.flightId}")`);
|
||||
}
|
||||
|
||||
if ( req.body.hasOwnProperty( "assignedAltitude" ) ) {
|
||||
if (req.body.hasOwnProperty("assignedAltitude")) {
|
||||
|
||||
const altitudeChangeSuccess = flight.setAssignedAltitude( req.body.assignedAltitude );
|
||||
const altitudeChangeSuccess = flight.setAssignedAltitude(req.body.assignedAltitude);
|
||||
|
||||
if ( altitudeChangeSuccess !== true ) {
|
||||
res.status( 400 ).send( altitudeChangeSuccess );
|
||||
if (altitudeChangeSuccess !== true) {
|
||||
res.status(400).send(altitudeChangeSuccess);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( req.body.hasOwnProperty( "assignedSpeed" ) ) {
|
||||
if (req.body.hasOwnProperty("assignedSpeed")) {
|
||||
|
||||
const speedChangeSuccess = flight.setAssignedSpeed( req.body.assignedSpeed );
|
||||
const speedChangeSuccess = flight.setAssignedSpeed(req.body.assignedSpeed);
|
||||
|
||||
if ( speedChangeSuccess !== true ) {
|
||||
res.status( 400 ).send( speedChangeSuccess );
|
||||
if (speedChangeSuccess !== true) {
|
||||
res.status(400).send(speedChangeSuccess);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( req.body.status ) {
|
||||
if (req.body.status) {
|
||||
|
||||
const statusChangeSuccess = flight.setStatus( req.body.status );
|
||||
const statusChangeSuccess = flight.setStatus(req.body.status);
|
||||
|
||||
if ( statusChangeSuccess !== true ) {
|
||||
res.status( 400 ).send( statusChangeSuccess );
|
||||
if (statusChangeSuccess !== true) {
|
||||
res.status(400).send(statusChangeSuccess);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( req.body.hasOwnProperty( "takeoffTime" ) ) {
|
||||
if (req.body.hasOwnProperty("takeoffTime")) {
|
||||
|
||||
const takeoffChangeSuccess = flight.setTakeoffTime( req.body.takeoffTime );
|
||||
const takeoffChangeSuccess = flight.setTakeoffTime(req.body.takeoffTime);
|
||||
|
||||
if ( takeoffChangeSuccess !== true ) {
|
||||
res.status( 400 ).send( takeoffChangeSuccess );
|
||||
if (takeoffChangeSuccess !== true) {
|
||||
res.status(400).send(takeoffChangeSuccess);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
res.json( flight.getData() );
|
||||
res.json(flight.getData());
|
||||
|
||||
});
|
||||
|
||||
|
||||
app.post( "/flight/order", ( req, res ) => {
|
||||
app.post("/flight/order", (req, res) => {
|
||||
|
||||
if ( !req.body.boardId ) {
|
||||
res.status( 400 ).send( "Invalid/missing boardId" );
|
||||
if (!req.body.boardId) {
|
||||
res.status(400).send("Invalid/missing boardId");
|
||||
}
|
||||
|
||||
if ( !req.body.order || !Array.isArray( req.body.order ) ) {
|
||||
res.status( 400 ).send( "Invalid/missing boardId" );
|
||||
if (!req.body.order || !Array.isArray(req.body.order)) {
|
||||
res.status(400).send("Invalid/missing boardId");
|
||||
}
|
||||
|
||||
req.body.order.forEach( ( flightId, i ) => {
|
||||
req.body.order.forEach((flightId, i) => {
|
||||
|
||||
dataHandler.getFlight( flightId ).setOrder( i );
|
||||
dataHandler.getFlight(flightId).setOrder(i);
|
||||
|
||||
});
|
||||
|
||||
res.send( "" );
|
||||
res.send("");
|
||||
|
||||
});
|
||||
|
||||
|
||||
app.post( "/flight", ( req, res ) => {
|
||||
app.post("/flight", (req, res) => {
|
||||
|
||||
if ( !req.body.boardId ) {
|
||||
res.status( 400 ).send( "Invalid/missing boardId" );
|
||||
if (!req.body.boardId) {
|
||||
res.status(400).send("Invalid/missing boardId");
|
||||
}
|
||||
|
||||
if ( !req.body.name ) {
|
||||
res.status( 400 ).send( "Invalid/missing flight name" );
|
||||
if (!req.body.name) {
|
||||
res.status(400).send("Invalid/missing flight name");
|
||||
}
|
||||
|
||||
if ( !req.body.unitId || isNaN( req.body.unitId ) ) {
|
||||
res.status( 400 ).send( "Invalid/missing unitId" );
|
||||
if (!req.body.unitId || isNaN(req.body.unitId)) {
|
||||
res.status(400).send("Invalid/missing unitId");
|
||||
}
|
||||
|
||||
const flight = new Flight( req.body.name, req.body.boardId, req.body.unitId );
|
||||
const flight = new Flight(req.body.name, req.body.boardId, req.body.unitId);
|
||||
|
||||
dataHandler.addFlight( flight );
|
||||
dataHandler.addFlight(flight);
|
||||
|
||||
res.status( 201 );
|
||||
res.status(201);
|
||||
|
||||
res.json( flight.getData() );
|
||||
res.json(flight.getData());
|
||||
|
||||
});
|
||||
|
||||
|
||||
app.delete( "/flight/:flightId", ( req, res ) => {
|
||||
app.delete("/flight/:flightId", (req, res) => {
|
||||
|
||||
const flight = dataHandler.getFlight( req.params.flightId );
|
||||
const flight = dataHandler.getFlight(req.params.flightId);
|
||||
|
||||
if ( !flight ) {
|
||||
res.status( 400 ).send( `Unrecognised flight ID (given: "${req.params.flightId}")` );
|
||||
if (!flight) {
|
||||
res.status(400).send(`Unrecognised flight ID (given: "${req.params.flightId}")`);
|
||||
}
|
||||
|
||||
dataHandler.deleteFlight( req.params.flightId );
|
||||
dataHandler.deleteFlight(req.params.flightId);
|
||||
|
||||
res.status( 204 ).send( "" );
|
||||
res.status(204).send("");
|
||||
|
||||
});
|
||||
|
||||
|
||||
23
client/routes/plugins.js
Normal file
23
client/routes/plugins.js
Normal file
@ -0,0 +1,23 @@
|
||||
const express = require('express');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const pluginsDirectory = "./public/plugins"
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
function listDirectories(source) {
|
||||
const directories = fs.readdirSync(source, { withFileTypes: true })
|
||||
.filter(dirent => dirent.isDirectory())
|
||||
.map(dirent => dirent.name);
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
router.get('/list', function (req, res) {
|
||||
var directories = listDirectories(pluginsDirectory);
|
||||
console.log(directories)
|
||||
res.send(directories.filter(directory => fs.existsSync(path.join(pluginsDirectory, directory))));
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@ -1,6 +1,7 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
// TODO should be user selectable or at least configurable from configuration file
|
||||
var theme = "olympus";
|
||||
|
||||
router.get('/theme/*', function (req, res, next) {
|
||||
|
||||
52
client/src/@types/server.d.ts
vendored
52
client/src/@types/server.d.ts
vendored
@ -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;
|
||||
}
|
||||
104
client/src/@types/unit.d.ts
vendored
104
client/src/@types/unit.d.ts
vendored
@ -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;
|
||||
}
|
||||
43
client/src/@types/unitdatabase.d.ts
vendored
43
client/src/@types/unitdatabase.d.ts
vendored
@ -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
151
client/src/app.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
@ -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 © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
|
||||
attribution: "Tiles © Esri — 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}',
|
||||
|
||||
@ -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" );
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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"));
|
||||
|
||||
@ -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));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
@ -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 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 = "";
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@ -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() );
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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 );
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { Ammo } from "../@types/unit";
|
||||
import { aircraftDatabase } from "../unit/databases/aircraftdatabase";
|
||||
import { Unit } from "../unit/unit";
|
||||
import { Panel } from "./panel";
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN } from "../@types/unit";
|
||||
|
||||
export class DataExtractor {
|
||||
#seekPosition = 0;
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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: ""}]);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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}`);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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())
|
||||
{
|
||||
|
||||
@ -23,19 +23,22 @@
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
/* Modules */
|
||||
"module": "commonjs", /* Specify what module code is generated. */
|
||||
"rootDir": "./src", /* Specify the root folder within your source files. */
|
||||
"rootDirs": ["./src", "./@types"], /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
"./node_modules/@types",
|
||||
"./@types"
|
||||
], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
"types": [
|
||||
"leaflet",
|
||||
"geojson",
|
||||
"node",
|
||||
"formatcoords"
|
||||
"formatcoords",
|
||||
"olympus",
|
||||
"dom"
|
||||
], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
@ -100,6 +103,7 @@
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
"src/**/*.ts",
|
||||
"@types/*.d.ts"
|
||||
]
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
<div class="ol-panel aic-panel" id="aic-control-panel" data-feature-switch="aic">
|
||||
<div class="olympus-button" id="toggle-aic-button"></div>
|
||||
<div class="olympus-button" id="aic-help-button"></div>
|
||||
</div>
|
||||
|
||||
<div id="aic-help" class="olympus-dialog hide" data-feature-switch="aic">
|
||||
<div class="olympus-dialog-close">×</div>
|
||||
<div class="olympus-dialog-header">AIC Help</div>
|
||||
<div class="olympus-dialog-content">
|
||||
<p>How to be a good AIC and get people to do stuff good, too.</p>
|
||||
<div
|
||||
style="align-items: center; background:black; color:white; display:flex; height:250px; justify-content: center; justify-self: center; width:450px;">
|
||||
<div>[DCS with Volvo video]</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="aic-teleprompt" data-feature-switch="aic"></div>
|
||||
|
||||
<div id="aic-callsign-panel" class="aic-panel" data-feature-switch="aic">
|
||||
|
||||
<div class="aic-panel">
|
||||
<h2>My callsign</h2>
|
||||
<div>Magic</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="aic-toolbox" class="aic-panel" data-feature-switch="aic">
|
||||
|
||||
<div id="aic-control-type" class="aic-toolbox-panel">
|
||||
<h2>Control</h2>
|
||||
<div>
|
||||
<input type="radio" name="control-type" id="control-type-broadcast" value="broadcast" checked="checked" />
|
||||
<label for="control-type-broadcast">Broadcast</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" name="control-type" id="control-type-tactical" value="tactical" />
|
||||
<label for="control-type-tactical">Tactical</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="aic-formation-panel" class="aic-toolbox-panel">
|
||||
|
||||
<h2>Formations</h2>
|
||||
|
||||
<div id="aic-formation-list"></div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -1,5 +0,0 @@
|
||||
<form class="ol-strip-board-add-flight">
|
||||
<div class="ol-auto-suggest"></div>
|
||||
<input type="text" name="unitName" placeholder="Flight search" />
|
||||
<button class="add-flight-by-click" title="Add unit via click"><img src="/resources/theme/images/icons/crosshairs-solid.svg" /></button>
|
||||
</form>
|
||||
@ -1,11 +0,0 @@
|
||||
<%- include('board.ejs', {
|
||||
"boardId": "strip-board-tower",
|
||||
"boardType": "tower",
|
||||
"headers": [ "Flight", "a. Alt", "alt", "a. Speed", "Speed" ]
|
||||
}) %>
|
||||
|
||||
<%- include('board.ejs', {
|
||||
"boardId": "strip-board-ground",
|
||||
"boardType": "ground",
|
||||
"headers": [ "Flight", "Status", "T/O Time", "TTG" ]
|
||||
}) %>
|
||||
@ -1,22 +0,0 @@
|
||||
<div id="<%= boardId %>" class="ol-panel ol-dialog ol-strip-board ol-draggable hide" data-board-type="<%= boardType %>" data-feature-switch="atc">
|
||||
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h3><%= boardType %></h3>
|
||||
<%- include('addflight.ejs') %>
|
||||
<div class="ol-strip-board-clock"></div>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<div class="ol-strip-board-headers">
|
||||
<div><!-- handles --></div>
|
||||
<% headers.forEach( header => { %>
|
||||
<div><%= header %></div>
|
||||
<% }); %>
|
||||
<div><!-- delete --></div>
|
||||
</div>
|
||||
<div class="ol-strip-board-strips ol-sortable"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -1,13 +0,0 @@
|
||||
<div id="unit-data-table" class="ol-panel ol-dialog scrollable" oncontextmenu="return false;">
|
||||
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h4>Unit list</h4>
|
||||
</div>
|
||||
|
||||
<div id="unit-list" class="ol-dialog-content">
|
||||
<!-- Here the list of units is shown -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -23,9 +23,6 @@
|
||||
</div>
|
||||
|
||||
<!-- Panels -->
|
||||
|
||||
<%- include('other/controltips.ejs') %>
|
||||
|
||||
<%- include('panels/unitcontrol.ejs') %>
|
||||
<%- include('panels/unitinfo.ejs') %>
|
||||
<%- include('panels/mouseinfo.ejs') %>
|
||||
|
||||
@ -1 +0,0 @@
|
||||
<div id="control-tips-panel"></div>
|
||||
Loading…
x
Reference in New Issue
Block a user