mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Fixed radios not working, added mouse coordinates panel, started readding tips
This commit is contained in:
parent
b97713f8cc
commit
fa215142ad
@ -35,16 +35,13 @@ export class AudioManager {
|
||||
#socket: WebSocket | null = null;
|
||||
#guid: string = makeID(22);
|
||||
#SRSClientUnitIDs: number[] = [];
|
||||
#syncInterval: number;
|
||||
|
||||
constructor() {
|
||||
ConfigLoadedEvent.on((config: OlympusConfig) => {
|
||||
config.audio.WSPort ? this.setPort(config.audio.WSPort) : this.setEndpoint(config.audio.WSEndpoint);
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
this.#syncRadioSettings();
|
||||
}, 1000);
|
||||
|
||||
let PTTKeys = ["KeyZ", "KeyX", "KeyC", "KeyV", "KeyB", "KeyN", "KeyM", "KeyK", "KeyL"];
|
||||
PTTKeys.forEach((key, idx) => {
|
||||
getApp()
|
||||
@ -59,6 +56,10 @@ export class AudioManager {
|
||||
}
|
||||
|
||||
start() {
|
||||
this.#syncInterval = window.setInterval(() => {
|
||||
this.#syncRadioSettings();
|
||||
}, 1000);
|
||||
|
||||
this.#running = true;
|
||||
this.#audioContext = new AudioContext({ sampleRate: 16000 });
|
||||
this.#playbackPipeline = new PlaybackPipeline();
|
||||
@ -72,7 +73,9 @@ export class AudioManager {
|
||||
else if (this.#port) this.#socket = new WebSocket(`ws://${wsAddress}:${this.#port}`);
|
||||
else console.error("The audio backend was enabled but no port/endpoint was provided in the configuration");
|
||||
|
||||
this.#socket = new WebSocket(`wss://refugees.dcsolympus.com/audio`); // TODO: remove, used for testing!
|
||||
if (!this.#socket) return;
|
||||
|
||||
//this.#socket = new WebSocket(`wss://refugees.dcsolympus.com/audio`); // TODO: remove, used for testing!
|
||||
|
||||
/* Log the opening of the connection */
|
||||
this.#socket.addEventListener("open", (event) => {
|
||||
@ -98,7 +101,8 @@ export class AudioManager {
|
||||
|
||||
/* Extract the frequency value and play it on the speakers if we are listening to it*/
|
||||
audioPacket.getFrequencies().forEach((frequencyInfo) => {
|
||||
if (sink.getFrequency() === frequencyInfo.frequency && sink.getModulation() === frequencyInfo.modulation) {
|
||||
if (sink.getFrequency() === frequencyInfo.frequency && sink.getModulation() === frequencyInfo.modulation && sink.getTuned()) {
|
||||
sink.setReceiving(true);
|
||||
this.#playbackPipeline.playBuffer(audioPacket.getAudioData().buffer);
|
||||
}
|
||||
});
|
||||
@ -133,6 +137,9 @@ export class AudioManager {
|
||||
this.#sinks.forEach((sink) => sink.disconnect());
|
||||
this.#sources = [];
|
||||
this.#sinks = [];
|
||||
this.#socket?.close();
|
||||
|
||||
window.clearInterval(this.#syncInterval);
|
||||
|
||||
AudioSourcesChangedEvent.dispatch(this.#sources);
|
||||
AudioSinksChangedEvent.dispatch(this.#sinks);
|
||||
|
||||
@ -16,6 +16,8 @@ export class RadioSink extends AudioSink {
|
||||
#ptt = false;
|
||||
#tuned = false;
|
||||
#volume = 0.5;
|
||||
#receiving = false;
|
||||
#clearReceivingTimeout: number;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@ -99,6 +101,20 @@ export class RadioSink extends AudioSink {
|
||||
return this.#volume;
|
||||
}
|
||||
|
||||
setReceiving(receiving) {
|
||||
// Only do it if actually changed
|
||||
if (receiving !== this.#receiving) AudioSinksChangedEvent.dispatch(getApp().getAudioManager().getSinks());
|
||||
if (receiving) {
|
||||
window.clearTimeout(this.#clearReceivingTimeout);
|
||||
this.#clearReceivingTimeout = window.setTimeout(() => this.setReceiving(false), 500);
|
||||
}
|
||||
this.#receiving = receiving;
|
||||
}
|
||||
|
||||
getReceiving() {
|
||||
return this.#receiving;
|
||||
}
|
||||
|
||||
handleEncodedData(encodedAudioChunk: EncodedAudioChunk) {
|
||||
let arrayBuffer = new ArrayBuffer(encodedAudioChunk.byteLength);
|
||||
encodedAudioChunk.copyTo(arrayBuffer);
|
||||
|
||||
@ -5,11 +5,13 @@ import { CommandModeOptions, OlympusConfig, ServerStatus, SpawnRequestTable } fr
|
||||
import { CoalitionCircle } from "./map/coalitionarea/coalitioncircle";
|
||||
import { CoalitionPolygon } from "./map/coalitionarea/coalitionpolygon";
|
||||
import { Airbase } from "./mission/airbase";
|
||||
import { Bullseye } from "./mission/bullseye";
|
||||
import { Shortcut } from "./shortcut/shortcut";
|
||||
import { MapHiddenTypes, MapOptions } from "./types/types";
|
||||
import { ContextAction } from "./unit/contextaction";
|
||||
import { ContextActionSet } from "./unit/contextactionset";
|
||||
import { Unit } from "./unit/unit";
|
||||
import { LatLng } from "leaflet";
|
||||
|
||||
export class BaseOlympusEvent {
|
||||
static on(callback: () => void) {
|
||||
@ -34,7 +36,7 @@ export class BaseUnitEvent {
|
||||
static dispatch(unit: Unit) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { unit } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
console.log(unit)
|
||||
console.log(unit);
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,9 +64,9 @@ export class ConfigLoadedEvent {
|
||||
}
|
||||
|
||||
static dispatch(config: OlympusConfig) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: config}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: config }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
console.log(config)
|
||||
console.log(config);
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +93,7 @@ export class InfoPopupEvent {
|
||||
}
|
||||
|
||||
static dispatch(messages: string[]) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {messages}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { messages } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -104,20 +106,20 @@ export class HideMenuEvent {
|
||||
}
|
||||
|
||||
static dispatch(hidden: boolean) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {hidden}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { hidden } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
|
||||
export class ShortcutsChangedEvent {
|
||||
static on(callback: (shortcuts: {[key: string]: Shortcut}) => void) {
|
||||
static on(callback: (shortcuts: { [key: string]: Shortcut }) => void) {
|
||||
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
||||
callback(ev.detail.shortcuts);
|
||||
});
|
||||
}
|
||||
|
||||
static dispatch(shortcuts: {[key: string]: Shortcut}) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {shortcuts}}));
|
||||
static dispatch(shortcuts: { [key: string]: Shortcut }) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { shortcuts } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -130,7 +132,7 @@ export class ShortcutChangedEvent {
|
||||
}
|
||||
|
||||
static dispatch(shortcut: Shortcut) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {shortcut}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { shortcut } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -143,7 +145,7 @@ export class BindShortcutRequestEvent {
|
||||
}
|
||||
|
||||
static dispatch(shortcut: Shortcut) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {shortcut}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { shortcut } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -157,7 +159,7 @@ export class HiddenTypesChangedEvent {
|
||||
}
|
||||
|
||||
static dispatch(hiddenTypes: MapHiddenTypes) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {hiddenTypes}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { hiddenTypes } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -170,7 +172,7 @@ export class MapOptionsChangedEvent {
|
||||
}
|
||||
|
||||
static dispatch(mapOptions: MapOptions) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {mapOptions}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { mapOptions } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -183,7 +185,7 @@ export class MapSourceChangedEvent {
|
||||
}
|
||||
|
||||
static dispatch(source: string) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {source}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { source } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -235,7 +237,7 @@ export class ContextActionSetChangedEvent {
|
||||
}
|
||||
|
||||
static dispatch(contextActionSet: ContextActionSet | null) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {contextActionSet}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { contextActionSet } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -248,7 +250,7 @@ export class ContextActionChangedEvent {
|
||||
}
|
||||
|
||||
static dispatch(contextAction: ContextAction | null) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {contextAction}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { contextAction } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -258,11 +260,11 @@ export class UnitUpdatedEvent extends BaseUnitEvent {
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { unit } }));
|
||||
// Logging disabled since periodic
|
||||
}
|
||||
};
|
||||
export class UnitSelectedEvent extends BaseUnitEvent {};
|
||||
export class UnitDeselectedEvent extends BaseUnitEvent {};
|
||||
export class UnitDeadEvent extends BaseUnitEvent {};
|
||||
export class SelectionClearedEvent extends BaseOlympusEvent {};
|
||||
}
|
||||
export class UnitSelectedEvent extends BaseUnitEvent {}
|
||||
export class UnitDeselectedEvent extends BaseUnitEvent {}
|
||||
export class UnitDeadEvent extends BaseUnitEvent {}
|
||||
export class SelectionClearedEvent extends BaseOlympusEvent {}
|
||||
|
||||
export class SelectedUnitsChangedEvent {
|
||||
static on(callback: (selectedUnits: Unit[]) => void) {
|
||||
@ -272,9 +274,9 @@ export class SelectedUnitsChangedEvent {
|
||||
}
|
||||
|
||||
static dispatch(selectedUnits: Unit[]) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: selectedUnits}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: selectedUnits }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
console.log(selectedUnits)
|
||||
console.log(selectedUnits);
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,7 +288,7 @@ export class UnitExplosionRequestEvent {
|
||||
}
|
||||
|
||||
static dispatch(units: Unit[]) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {units}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { units } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -299,7 +301,7 @@ export class FormationCreationRequestEvent {
|
||||
}
|
||||
|
||||
static dispatch(leader: Unit, wingmen: Unit[]) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {leader, wingmen}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { leader, wingmen } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -312,7 +314,7 @@ export class MapContextMenuRequestEvent {
|
||||
}
|
||||
|
||||
static dispatch(latlng: L.LatLng) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {latlng}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { latlng } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -325,7 +327,7 @@ export class UnitContextMenuRequestEvent {
|
||||
}
|
||||
|
||||
static dispatch(unit: Unit) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {unit}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { unit } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -338,33 +340,46 @@ export class StarredSpawnContextMenuRequestEvent {
|
||||
}
|
||||
|
||||
static dispatch(latlng: L.LatLng) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {latlng}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { latlng } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
|
||||
export class HotgroupsChangedEvent {
|
||||
static on(callback: (hotgroups: {[key: number]: number}) => void) {
|
||||
static on(callback: (hotgroups: { [key: number]: number }) => void) {
|
||||
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
||||
callback(ev.detail.hotgroups);
|
||||
});
|
||||
}
|
||||
|
||||
static dispatch(hotgroups: {[key: number]: number}) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {hotgroups}}));
|
||||
static dispatch(hotgroups: { [key: number]: number }) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { hotgroups } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
|
||||
export class StarredSpawnsChangedEvent {
|
||||
static on(callback: (starredSpawns: {[key: number]: SpawnRequestTable}) => void) {
|
||||
static on(callback: (starredSpawns: { [key: number]: SpawnRequestTable }) => void) {
|
||||
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
||||
callback(ev.detail.starredSpawns);
|
||||
});
|
||||
}
|
||||
|
||||
static dispatch(starredSpawns: {[key: number]: SpawnRequestTable}) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {starredSpawns}}));
|
||||
static dispatch(starredSpawns: { [key: number]: SpawnRequestTable }) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { starredSpawns } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
|
||||
export class MouseMovedEvent {
|
||||
static on(callback: (latlng: LatLng, elevation: number) => void) {
|
||||
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
||||
callback(ev.detail.latlng, ev.detail.elevation);
|
||||
});
|
||||
}
|
||||
|
||||
static dispatch(latlng: LatLng, elevation?: number) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { latlng, elevation } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -378,7 +393,7 @@ export class CommandModeOptionsChangedEvent {
|
||||
}
|
||||
|
||||
static dispatch(options: CommandModeOptions) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: options}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: options }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
@ -392,9 +407,9 @@ export class AudioSourcesChangedEvent {
|
||||
}
|
||||
|
||||
static dispatch(audioSources: AudioSource[]) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {audioSources}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { audioSources } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
console.log(audioSources)
|
||||
console.log(audioSources);
|
||||
}
|
||||
}
|
||||
|
||||
@ -406,9 +421,9 @@ export class AudioSinksChangedEvent {
|
||||
}
|
||||
|
||||
static dispatch(audioSinks: AudioSink[]) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {audioSinks}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { audioSinks } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
console.log(audioSinks)
|
||||
console.log(audioSinks);
|
||||
}
|
||||
}
|
||||
|
||||
@ -433,7 +448,21 @@ export class AudioManagerStateChangedEvent {
|
||||
}
|
||||
|
||||
static dispatch(state: boolean) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, {detail: {state}}));
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { state } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
|
||||
/************** Mission data events ***************/
|
||||
export class BullseyesDataChanged {
|
||||
static on(callback: (bullseyes: { [name: string]: Bullseye }) => void) {
|
||||
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
||||
callback(ev.detail.bullseyes);
|
||||
});
|
||||
}
|
||||
|
||||
static dispatch(bullseyes: { [name: string]: Bullseye } ) {
|
||||
document.dispatchEvent(new CustomEvent(this.name, { detail: { bullseyes } }));
|
||||
console.log(`Event ${this.name} dispatched`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,6 +51,7 @@ import {
|
||||
MapContextMenuRequestEvent,
|
||||
MapOptionsChangedEvent,
|
||||
MapSourceChangedEvent,
|
||||
MouseMovedEvent,
|
||||
SelectionClearedEvent,
|
||||
StarredSpawnContextMenuRequestEvent,
|
||||
StarredSpawnsChangedEvent,
|
||||
@ -517,9 +518,7 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
getCurrentControls() {
|
||||
const touch = matchMedia("(hover: none)").matches;
|
||||
return [];
|
||||
// TODO, is this a good idea? I never look at the thing
|
||||
//const touch = matchMedia("(hover: none)").matches;
|
||||
//if (getApp().getState() === IDLE) {
|
||||
// return [
|
||||
// {
|
||||
@ -544,7 +543,7 @@ export class Map extends L.Map {
|
||||
// text: "Move map location",
|
||||
// },
|
||||
// ];
|
||||
//} else if (getApp().getState() === SPAWN_UNIT) {
|
||||
//} else if (getApp().getState() === OlympusState.SPAWN_UNIT) {
|
||||
// return [
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB"],
|
||||
@ -812,7 +811,7 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
this.#miniMap = new ClickableMiniMap(this.#miniMapLayerGroup, {
|
||||
position: "topright",
|
||||
position: "bottomright",
|
||||
width: 192 * 1.5,
|
||||
height: 108 * 1.5,
|
||||
//@ts-ignore Needed because some of the inputs are wrong in the original module interface
|
||||
@ -999,7 +998,7 @@ export class Map extends L.Map {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.#contextAction?.getTarget() === ContextActionTarget.POINT && e.originalEvent.button === 2) this.#isRotatingDestination = true;
|
||||
if (this.#contextAction?.getTarget() === ContextActionTarget.POINT && e.originalEvent?.button === 2) this.#isRotatingDestination = true;
|
||||
this.scrollWheelZoom.disable();
|
||||
|
||||
this.#shortPressTimer = window.setTimeout(() => {
|
||||
@ -1043,7 +1042,7 @@ export class Map extends L.Map {
|
||||
/* Do nothing */
|
||||
} else if (getApp().getState() === OlympusState.SPAWN) {
|
||||
if (getApp().getSubState() === SpawnSubState.SPAWN_UNIT) {
|
||||
if (e.originalEvent.button != 2 && this.#spawnRequestTable !== null) {
|
||||
if (e.originalEvent?.button != 2 && this.#spawnRequestTable !== null) {
|
||||
this.#spawnRequestTable.unit.location = pressLocation;
|
||||
getApp()
|
||||
.getUnitsManager()
|
||||
@ -1060,7 +1059,7 @@ export class Map extends L.Map {
|
||||
);
|
||||
}
|
||||
} else if (getApp().getSubState() === SpawnSubState.SPAWN_EFFECT) {
|
||||
if (e.originalEvent.button != 2 && this.#effectRequestTable !== null) {
|
||||
if (e.originalEvent?.button != 2 && this.#effectRequestTable !== null) {
|
||||
if (this.#effectRequestTable.type === "explosion") {
|
||||
if (this.#effectRequestTable.explosionType === "High explosive") getApp().getServerManager().spawnExplosion(50, "normal", pressLocation);
|
||||
else if (this.#effectRequestTable.explosionType === "Napalm") getApp().getServerManager().spawnExplosion(50, "napalm", pressLocation);
|
||||
@ -1099,10 +1098,10 @@ export class Map extends L.Map {
|
||||
}
|
||||
}
|
||||
} else if (getApp().getState() === OlympusState.UNIT_CONTROL) {
|
||||
if (e.type === "touchstart" || e.originalEvent.buttons === 1) {
|
||||
if (e.type === "touchstart" || e.originalEvent?.buttons === 1) {
|
||||
if (this.#contextAction !== null) this.executeContextAction(null, pressLocation, e.originalEvent);
|
||||
else getApp().setState(OlympusState.IDLE);
|
||||
} else if (e.originalEvent.buttons === 2) {
|
||||
} else if (e.originalEvent?.buttons === 2) {
|
||||
this.executeDefaultContextAction(null, pressLocation, e.originalEvent);
|
||||
}
|
||||
} else if (getApp().getState() === OlympusState.JTAC) {
|
||||
@ -1172,7 +1171,7 @@ export class Map extends L.Map {
|
||||
if (e.type === "touchstart") document.dispatchEvent(new CustomEvent("forceboxselect", { detail: e }));
|
||||
else document.dispatchEvent(new CustomEvent("forceboxselect", { detail: e.originalEvent }));
|
||||
} else if (getApp().getState() === OlympusState.UNIT_CONTROL) {
|
||||
if (e.originalEvent.button === 2) {
|
||||
if (e.originalEvent?.button === 2) {
|
||||
if (!this.getContextAction()) {
|
||||
getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.MAP_CONTEXT_MENU);
|
||||
MapContextMenuRequestEvent.dispatch(pressLocation);
|
||||
@ -1198,6 +1197,11 @@ export class Map extends L.Map {
|
||||
this.#lastMousePosition.y = e.originalEvent.y;
|
||||
this.#lastMouseCoordinates = e.latlng;
|
||||
|
||||
MouseMovedEvent.dispatch(e.latlng);
|
||||
getGroundElevation(e.latlng, (elevation) => {
|
||||
MouseMovedEvent.dispatch(e.latlng, elevation);
|
||||
})
|
||||
|
||||
if (this.#currentSpawnMarker) this.#currentSpawnMarker.setLatLng(e.latlng);
|
||||
if (this.#currentEffectMarker) this.#currentEffectMarker.setLatLng(e.latlng);
|
||||
} else {
|
||||
@ -1269,17 +1273,6 @@ export class Map extends L.Map {
|
||||
|
||||
#setSlaveDCSCameraAvailable(newSlaveDCSCameraAvailable: boolean) {
|
||||
this.#slaveDCSCameraAvailable = newSlaveDCSCameraAvailable;
|
||||
let linkButton = document.getElementById("camera-link-control");
|
||||
if (linkButton) {
|
||||
if (!newSlaveDCSCameraAvailable) {
|
||||
//this.setSlaveDCSCamera(false); // Commented to experiment with usability
|
||||
linkButton.classList.add("red");
|
||||
linkButton.title = "Camera link to DCS is not available";
|
||||
} else {
|
||||
linkButton.classList.remove("red");
|
||||
linkButton.title = "Link/Unlink DCS camera with Olympus position";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the camera control plugin is available. Right now this will only change the color of the button, no changes in functionality */
|
||||
|
||||
@ -7,7 +7,7 @@ import { AirbasesData, BullseyesData, CommandModeOptions, DateAndTime, MissionDa
|
||||
import { Coalition } from "../types/types";
|
||||
import { Carrier } from "./carrier";
|
||||
import { NavyUnit } from "../unit/unit";
|
||||
import { CommandModeOptionsChangedEvent, InfoPopupEvent } from "../events";
|
||||
import { BullseyesDataChanged, CommandModeOptionsChangedEvent, InfoPopupEvent } from "../events";
|
||||
|
||||
/** The MissionManager */
|
||||
export class MissionManager {
|
||||
@ -56,6 +56,8 @@ export class MissionManager {
|
||||
this.#bullseyes[idx].setLatLng(new LatLng(bullseye.latitude, bullseye.longitude));
|
||||
this.#bullseyes[idx].setCoalition(bullseye.coalition);
|
||||
}
|
||||
|
||||
BullseyesDataChanged.dispatch(this.#bullseyes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -12,13 +12,15 @@ export const RadioSinkPanel = forwardRef((props: { radio: RadioSink; shortcutKey
|
||||
|
||||
useEffect(() => {
|
||||
if (props.onExpanded) props.onExpanded();
|
||||
}, [expanded])
|
||||
}, [expanded]);
|
||||
|
||||
return (
|
||||
<div
|
||||
data-receiving={props.radio.getReceiving()}
|
||||
className={`
|
||||
flex flex-col content-center justify-between gap-2 rounded-md
|
||||
bg-olympus-200/30 px-4 py-3
|
||||
box-border flex flex-col content-center justify-between gap-2 rounded-md
|
||||
border-2 border-transparent bg-olympus-200/30 px-4 py-3
|
||||
data-[receiving='true']:border-white
|
||||
`}
|
||||
ref={ref}
|
||||
>
|
||||
@ -37,19 +39,22 @@ export const RadioSinkPanel = forwardRef((props: { radio: RadioSink; shortcutKey
|
||||
data-expanded={expanded}
|
||||
/>
|
||||
</div>
|
||||
{props.shortcutKey && (<>
|
||||
<kbd
|
||||
className={`
|
||||
my-auto ml-auto rounded-lg border border-gray-200 bg-gray-100 px-2
|
||||
py-1.5 text-xs font-semibold text-gray-800
|
||||
dark:border-gray-500 dark:bg-gray-600 dark:text-gray-100
|
||||
`}
|
||||
>
|
||||
{props.shortcutKey}
|
||||
</kbd>
|
||||
{props.shortcutKey && (
|
||||
<>
|
||||
<kbd
|
||||
className={`
|
||||
my-auto ml-auto rounded-lg border border-gray-200 bg-gray-100
|
||||
px-2 py-1.5 text-xs font-semibold text-gray-800
|
||||
dark:border-gray-500 dark:bg-gray-600 dark:text-gray-100
|
||||
`}
|
||||
>
|
||||
{props.shortcutKey}
|
||||
</kbd>
|
||||
</>
|
||||
)}
|
||||
<span className="my-auto w-full">{props.radio.getName()} {!expanded && `: ${props.radio.getFrequency()/1e6} MHz ${props.radio.getModulation()? "FM": "AM"}`} {} </span>
|
||||
<span className="my-auto w-full">
|
||||
{props.radio.getName()} {!expanded && `: ${props.radio.getFrequency() / 1e6} MHz ${props.radio.getModulation() ? "FM" : "AM"}`} {}{" "}
|
||||
</span>
|
||||
<div
|
||||
className={`
|
||||
mb-auto ml-auto aspect-square cursor-pointer rounded-md p-2
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { IconDefinition } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faHandPointer, faJetFighter, faMap, IconDefinition } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { AppStateChangedEvent } from "../../events";
|
||||
import { MAP_OPTIONS_DEFAULTS, NO_SUBSTATE, OlympusState, OlympusSubState, SpawnSubState } from "../../constants/constants";
|
||||
import { AppStateChangedEvent, MapOptionsChangedEvent } from "../../events";
|
||||
|
||||
export function ControlsPanel(props: {}) {
|
||||
const [controls, setControls] = useState(
|
||||
@ -14,22 +14,91 @@ export function ControlsPanel(props: {}) {
|
||||
}[]
|
||||
| null
|
||||
);
|
||||
const [appState, setAppState] = useState(OlympusState.NOT_INITIALIZED);
|
||||
const [appSubState, setAppSubState] = useState(NO_SUBSTATE as OlympusSubState);
|
||||
const [mapOptions, setMapOptions] = useState(MAP_OPTIONS_DEFAULTS);
|
||||
|
||||
useEffect(() => {
|
||||
if (getApp() && controls === null) {
|
||||
setControls(getApp().getMap().getCurrentControls());
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
AppStateChangedEvent.on(() => setControls(getApp().getMap().getCurrentControls()));
|
||||
AppStateChangedEvent.on((state, subState) => {
|
||||
setAppState(state);
|
||||
setAppSubState(subState);
|
||||
});
|
||||
MapOptionsChangedEvent.on((mapOptions) => setMapOptions({ ...mapOptions }));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const touch = matchMedia("(hover: none)").matches;
|
||||
let controls: {
|
||||
actions: (string | number | IconDefinition)[];
|
||||
target: IconDefinition;
|
||||
text: string;
|
||||
}[] = [];
|
||||
|
||||
if (appState === OlympusState.IDLE) {
|
||||
controls = [
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: faJetFighter,
|
||||
text: "Select unit",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", 2],
|
||||
target: faMap,
|
||||
text: "Quick spawn menu",
|
||||
},
|
||||
{
|
||||
actions: touch ? [faHandPointer, "Drag"] : ["Shift", "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Box selection",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Move map location",
|
||||
},
|
||||
];
|
||||
} else if (appState === OlympusState.SPAWN) {
|
||||
controls = [
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", 2],
|
||||
target: faMap,
|
||||
text: appSubState === SpawnSubState.NO_SUBSTATE ? "Close spawn menu" : "Return to spawn menu",
|
||||
},
|
||||
{
|
||||
actions: touch ? [faHandPointer, "Drag"] : ["Shift", "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Box selection",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Move map location",
|
||||
},
|
||||
];
|
||||
if (appSubState === SpawnSubState.SPAWN_UNIT) {
|
||||
controls.unshift({
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: faMap,
|
||||
text: "Spawn unit",
|
||||
});
|
||||
} else if (appSubState === SpawnSubState.SPAWN_EFFECT) {
|
||||
controls.unshift({
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: faMap,
|
||||
text: "Spawn effect",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setControls(controls);
|
||||
}, [appState, appSubState]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
absolute bottom-[20px] right-[10px] flex w-80 flex-col items-center
|
||||
justify-between gap-1 p-3 text-sm
|
||||
absolute right-[0px]
|
||||
${mapOptions.showMinimap ? `bottom-[233px]` : `bottom-[65px]`}
|
||||
flex w-[310px] flex-col items-center justify-between gap-1 p-3 text-sm
|
||||
`}
|
||||
>
|
||||
{controls?.map((control) => {
|
||||
@ -53,14 +122,9 @@ export function ControlsPanel(props: {}) {
|
||||
return (
|
||||
<div key={idx} className="flex gap-1">
|
||||
<div>
|
||||
{typeof action === "string" || typeof action === "number" ? (
|
||||
action
|
||||
) : (
|
||||
<FontAwesomeIcon
|
||||
icon={action}
|
||||
className={`my-auto ml-auto`}
|
||||
/>
|
||||
)}
|
||||
{typeof action === "string" || typeof action === "number" ? action : <FontAwesomeIcon icon={action} className={`
|
||||
my-auto ml-auto
|
||||
`} />}
|
||||
</div>
|
||||
{idx < control.actions.length - 1 && typeof control.actions[idx + 1] === "string" && <div>+</div>}
|
||||
{idx < control.actions.length - 1 && typeof control.actions[idx + 1] === "number" && <div>x</div>}
|
||||
@ -68,18 +132,6 @@ export function ControlsPanel(props: {}) {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{/*}
|
||||
<div className="my-auto">on</div>
|
||||
|
||||
<div
|
||||
className={`
|
||||
flex gap-1 rounded-full bg-olympus-500 px-2 py-0.5 text-sm
|
||||
font-bold text-white
|
||||
`}
|
||||
>
|
||||
<FontAwesomeIcon icon={control.target} className="my-auto" />
|
||||
</div>
|
||||
{*/}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
106
frontend/react/src/ui/panels/coordinatespanel.tsx
Normal file
106
frontend/react/src/ui/panels/coordinatespanel.tsx
Normal file
@ -0,0 +1,106 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { OlLocation } from "../components/ollocation";
|
||||
import { LatLng } from "leaflet";
|
||||
import { FaBullseye, FaChevronDown, FaChevronUp, FaJetFighter, FaMountain } from "react-icons/fa6";
|
||||
import { BullseyesDataChanged, MouseMovedEvent } from "../../events";
|
||||
import { bearing, mToFt } from "../../other/utils";
|
||||
import { Bullseye } from "../../mission/bullseye";
|
||||
|
||||
export function CoordinatesPanel(props: {}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [latlng, setLatlng] = useState(new LatLng(0, 0));
|
||||
const [elevation, setElevation] = useState(0);
|
||||
const [bullseyes, setBullseyes] = useState(null as null | { [name: string]: Bullseye });
|
||||
|
||||
useEffect(() => {
|
||||
MouseMovedEvent.on((latlng, elevation) => {
|
||||
setLatlng(latlng);
|
||||
if (elevation) setElevation(elevation);
|
||||
});
|
||||
|
||||
BullseyesDataChanged.on((bullseyes) => setBullseyes(bullseyes));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
absolute bottom-[20px] right-[310px] flex w-[380px] flex-col
|
||||
items-center justify-between gap-2 rounded-lg bg-gray-200 p-3 text-sm
|
||||
backdrop-blur-lg backdrop-grayscale
|
||||
dark:bg-olympus-800/90 dark:text-gray-200
|
||||
`}
|
||||
>
|
||||
{" "}
|
||||
{open && (
|
||||
<>
|
||||
{bullseyes && (
|
||||
<div className="flex w-full items-center justify-start">
|
||||
<div
|
||||
className={`
|
||||
flex min-w-64 max-w-64 items-center justify-between gap-2
|
||||
`}
|
||||
>
|
||||
<div className="flex justify-start gap-2">
|
||||
<span
|
||||
className={`
|
||||
rounded-sm bg-blue-500 px-1 py-1 text-center font-bold
|
||||
text-olympus-700
|
||||
`}
|
||||
>
|
||||
<FaBullseye />
|
||||
</span>
|
||||
{(bullseyes[2].getLatLng().distanceTo(latlng) / 1852).toFixed(0)} /{" "}
|
||||
{bearing(bullseyes[2].getLatLng().lat, bullseyes[2].getLatLng().lng, latlng.lat, latlng.lng).toFixed()}°
|
||||
</div>
|
||||
<div className="flex w-[50%] justify-start gap-2">
|
||||
<span
|
||||
className={`
|
||||
rounded-sm bg-red-500 px-1 py-1 text-center font-bold
|
||||
text-olympus-700
|
||||
`}
|
||||
>
|
||||
<FaBullseye />
|
||||
</span>
|
||||
{(bullseyes[1].getLatLng().distanceTo(latlng) / 1852).toFixed(0)} /{" "}
|
||||
{bearing(bullseyes[1].getLatLng().lat, bullseyes[1].getLatLng().lng, latlng.lat, latlng.lng).toFixed()}°
|
||||
</div>
|
||||
</div>
|
||||
{/*}<div className="flex justify-start gap-2">
|
||||
<span
|
||||
className={`
|
||||
rounded-sm bg-white px-1 py-1 text-center font-bold
|
||||
text-olympus-700
|
||||
`}
|
||||
>
|
||||
<FaJetFighter />
|
||||
</span>
|
||||
<div>
|
||||
{(bullseyes[1].getLatLng().distanceTo(latlng) / 1852).toFixed(0)} /{" "}
|
||||
{bearing(bullseyes[1].getLatLng().lat, bullseyes[1].getLatLng().lng, latlng.lat, latlng.lng).toFixed()}°
|
||||
</div>
|
||||
</div>
|
||||
{*/}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<OlLocation className="!min-w-64 !max-w-64 bg-transparent !p-0" location={latlng} />
|
||||
<span
|
||||
className={`
|
||||
mr-2 rounded-sm bg-white px-1 py-1 text-center font-bold
|
||||
text-olympus-700
|
||||
`}
|
||||
>
|
||||
<FaMountain />
|
||||
</span>
|
||||
<div className="min-w-12">{mToFt(elevation).toFixed()}ft</div>
|
||||
{open ? (
|
||||
<FaChevronDown className="w-10 cursor-pointer" onClick={() => setOpen(!open)} />
|
||||
) : (
|
||||
<FaChevronUp className="w-10 cursor-pointer" onClick={() => setOpen(!open)} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -56,10 +56,11 @@ export function MiniMapPanel(props: {}) {
|
||||
onClick={() => setShowMissionTime(!showMissionTime)}
|
||||
className={`
|
||||
absolute right-[10px]
|
||||
${mapOptions.showMinimap ? `top-[232px]` : `top-[70px]`}
|
||||
${mapOptions.showMinimap ? `bottom-[188px]` : `bottom-[20px]`}
|
||||
flex w-[288px] items-center justify-between
|
||||
${mapOptions.showMinimap ? `rounded-b-lg` : `rounded-lg`}
|
||||
bg-gray-200 p-3 text-sm backdrop-blur-lg backdrop-grayscale
|
||||
${mapOptions.showMinimap ? `rounded-t-lg` : `rounded-lg`}
|
||||
cursor-pointer bg-gray-200 p-3 text-sm backdrop-blur-lg
|
||||
backdrop-grayscale
|
||||
dark:bg-olympus-800/90 dark:text-gray-200
|
||||
`}
|
||||
>
|
||||
@ -94,17 +95,9 @@ export function MiniMapPanel(props: {}) {
|
||||
</>
|
||||
)}
|
||||
{mapOptions.showMinimap ? (
|
||||
<FaChevronUp
|
||||
onClick={() => {
|
||||
getApp().getMap().setOption("showMinimap", false);
|
||||
}}
|
||||
></FaChevronUp>
|
||||
<FaChevronDown onClick={() => getApp().getMap().setOption("showMinimap", false)} />
|
||||
) : (
|
||||
<FaChevronDown
|
||||
onClick={() => {
|
||||
getApp().getMap().setOption("showMinimap", true);
|
||||
}}
|
||||
></FaChevronDown>
|
||||
<FaChevronUp onClick={() => getApp().getMap().setOption("showMinimap", true)} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -37,7 +37,7 @@ export function SideBar() {
|
||||
onClick={() => {
|
||||
getApp().setState(appState !== OlympusState.SPAWN ? OlympusState.SPAWN : OlympusState.IDLE);
|
||||
}}
|
||||
checked={appState === OlympusState.SPAWN && appSubState === SpawnSubState.NO_SUBSTATE}
|
||||
checked={appState === OlympusState.SPAWN}
|
||||
icon={faPlusSquare}
|
||||
tooltip="Hide/show unit spawn menu"
|
||||
></OlStateButton>
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
* {
|
||||
user-select: none; /* Standard syntax */
|
||||
}
|
||||
|
||||
/* width */
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
|
||||
@ -29,6 +29,7 @@ import { GameMasterMenu } from "./panels/gamemastermenu";
|
||||
import { InfoBar } from "./panels/infobar";
|
||||
import { HotGroupBar } from "./panels/hotgroupsbar";
|
||||
import { StarredSpawnContextMenu } from "./contextmenus/starredspawncontextmenu";
|
||||
import { CoordinatesPanel } from "./panels/coordinatespanel";
|
||||
|
||||
export type OlympusUIState = {
|
||||
mainMenuVisible: boolean;
|
||||
@ -65,11 +66,9 @@ export function UI() {
|
||||
<div className="flex h-full w-full flex-row-reverse">
|
||||
{appState === OlympusState.LOGIN && (
|
||||
<>
|
||||
<div
|
||||
className={`
|
||||
<div className={`
|
||||
fixed left-0 top-0 z-30 h-full w-full bg-[#111111]/95
|
||||
`}
|
||||
></div>
|
||||
`}></div>
|
||||
<LoginModal />
|
||||
</>
|
||||
)}
|
||||
@ -78,13 +77,10 @@ export function UI() {
|
||||
<KeybindModal open={appState === OlympusState.OPTIONS && appSubState === OptionsSubstate.KEYBIND} />
|
||||
|
||||
<div id="map-container" className="z-0 h-full w-screen" />
|
||||
<MainMenu open={appState === OlympusState.MAIN_MENU} onClose={() => getApp().setState(OlympusState.IDLE)} />
|
||||
<SpawnMenu
|
||||
open={appState === OlympusState.SPAWN && [SpawnSubState.NO_SUBSTATE, SpawnSubState.SPAWN_UNIT].includes(appSubState as SpawnSubState)}
|
||||
onClose={() => getApp().setState(OlympusState.IDLE)}
|
||||
/>
|
||||
<OptionsMenu open={appState === OlympusState.OPTIONS} onClose={() => getApp().setState(OlympusState.IDLE)} />
|
||||
|
||||
<MainMenu open={appState === OlympusState.MAIN_MENU} onClose={() => getApp().setState(OlympusState.IDLE)} />
|
||||
<SpawnMenu open={appState === OlympusState.SPAWN} onClose={() => getApp().setState(OlympusState.IDLE)} />
|
||||
<OptionsMenu open={appState === OlympusState.OPTIONS} onClose={() => getApp().setState(OlympusState.IDLE)} />
|
||||
<UnitControlMenu
|
||||
open={
|
||||
appState === OlympusState.UNIT_CONTROL &&
|
||||
@ -96,12 +92,10 @@ export function UI() {
|
||||
open={appState === OlympusState.UNIT_CONTROL && appSubState === UnitControlSubState.FORMATION}
|
||||
onClose={() => getApp().setState(OlympusState.IDLE)}
|
||||
/>
|
||||
|
||||
<DrawingMenu open={appState === OlympusState.DRAW} onClose={() => getApp().setState(OlympusState.IDLE)} />
|
||||
<AirbaseMenu open={appState === OlympusState.AIRBASE} onClose={() => getApp().setState(OlympusState.IDLE)} />
|
||||
<AudioMenu open={appState === OlympusState.AUDIO} onClose={() => getApp().setState(OlympusState.IDLE)} />
|
||||
<GameMasterMenu open={appState === OlympusState.GAME_MASTER} onClose={() => getApp().setState(OlympusState.IDLE)} />
|
||||
|
||||
<UnitExplosionMenu
|
||||
open={appState === OlympusState.UNIT_CONTROL && appSubState === UnitControlSubState.UNIT_EXPLOSION_MENU}
|
||||
onClose={() => getApp().setState(OlympusState.IDLE)}
|
||||
@ -110,12 +104,15 @@ export function UI() {
|
||||
|
||||
<MiniMapPanel />
|
||||
<ControlsPanel />
|
||||
<CoordinatesPanel />
|
||||
|
||||
<UnitControlBar />
|
||||
<MapContextMenu />
|
||||
<StarredSpawnContextMenu />
|
||||
<SideBar />
|
||||
<InfoBar />
|
||||
<HotGroupBar />
|
||||
|
||||
<MapContextMenu />
|
||||
<StarredSpawnContextMenu />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -38,8 +38,8 @@ import {
|
||||
OlympusState,
|
||||
JTACSubState,
|
||||
UnitControlSubState,
|
||||
ContextActionType,
|
||||
ContextActions,
|
||||
ContextActionTarget,
|
||||
} from "../constants/constants";
|
||||
import { DataExtractor } from "../server/dataextractor";
|
||||
import { Weapon } from "../weapon/weapon";
|
||||
@ -48,40 +48,17 @@ import { RangeCircle } from "../map/rangecircle";
|
||||
import { Group } from "./group";
|
||||
import { ContextActionSet } from "./contextactionset";
|
||||
import * as turf from "@turf/turf";
|
||||
import {
|
||||
olButtonsContextSimulateFireFight,
|
||||
olButtonsContextFollow,
|
||||
olButtonsContextLandAtPoint,
|
||||
olButtonsContextAttack,
|
||||
olButtonsContextRefuel,
|
||||
} from "../ui/components/olicons";
|
||||
import {
|
||||
faExplosion,
|
||||
faHand,
|
||||
faLocationCrosshairs,
|
||||
faLocationDot,
|
||||
faMapLocation,
|
||||
faPeopleGroup,
|
||||
faPlaneArrival,
|
||||
faQuestionCircle,
|
||||
faRoute,
|
||||
faTrash,
|
||||
faXmarksLines,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { Carrier } from "../mission/carrier";
|
||||
import {
|
||||
ContactsUpdatedEvent,
|
||||
FormationCreationRequestEvent,
|
||||
HiddenTypesChangedEvent,
|
||||
MapOptionsChangedEvent,
|
||||
UnitContextMenuRequestEvent,
|
||||
UnitDeadEvent,
|
||||
UnitDeselectedEvent,
|
||||
UnitExplosionRequestEvent,
|
||||
UnitSelectedEvent,
|
||||
UnitUpdatedEvent,
|
||||
} from "../events";
|
||||
import { ContextAction } from "./contextaction";
|
||||
|
||||
var pathIcon = new Icon({
|
||||
iconUrl: "/vite/images/markers/marker-icon.png",
|
||||
@ -328,7 +305,7 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
constructor(ID: number) {
|
||||
super(new LatLng(0, 0), { riseOnHover: true, keyboard: false, bubblingMouseEvents: false });
|
||||
super(new LatLng(0, 0), { riseOnHover: true, keyboard: false, bubblingMouseEvents: true });
|
||||
|
||||
this.ID = ID;
|
||||
|
||||
@ -370,7 +347,8 @@ export abstract class Unit extends CustomMarker {
|
||||
this.on("mouseup", (e) => this.#onMouseUp(e));
|
||||
this.on("dblclick", (e) => this.#onDoubleClick(e));
|
||||
this.on("mouseover", () => {
|
||||
if (this.belongsToCommandedCoalition()) this.setHighlighted(true);
|
||||
if (this.belongsToCommandedCoalition() && (getApp().getState() === OlympusState.IDLE || getApp().getState() === OlympusState.UNIT_CONTROL))
|
||||
this.setHighlighted(true);
|
||||
});
|
||||
this.on("mouseout", () => this.setHighlighted(false));
|
||||
getApp()
|
||||
@ -1304,56 +1282,61 @@ export abstract class Unit extends CustomMarker {
|
||||
|
||||
/***********************************************/
|
||||
#onMouseUp(e: any) {
|
||||
this.#isMouseDown = false;
|
||||
if (getApp().getState() === OlympusState.IDLE || getApp().getState() === OlympusState.UNIT_CONTROL) {
|
||||
this.#isMouseDown = false;
|
||||
|
||||
if (getApp().getMap().isSelecting()) return;
|
||||
if (getApp().getMap().isSelecting()) return;
|
||||
|
||||
DomEvent.stop(e);
|
||||
DomEvent.preventDefault(e);
|
||||
e.originalEvent.stopImmediatePropagation();
|
||||
DomEvent.stop(e);
|
||||
DomEvent.preventDefault(e);
|
||||
e.originalEvent.stopImmediatePropagation();
|
||||
|
||||
e.originalEvent.stopPropagation();
|
||||
e.originalEvent.stopPropagation();
|
||||
|
||||
window.clearTimeout(this.#longPressTimer);
|
||||
window.clearTimeout(this.#longPressTimer);
|
||||
|
||||
this.#isMouseOnCooldown = true;
|
||||
this.#mouseCooldownTimer = window.setTimeout(() => {
|
||||
this.#isMouseOnCooldown = false;
|
||||
}, 200);
|
||||
this.#isMouseOnCooldown = true;
|
||||
this.#mouseCooldownTimer = window.setTimeout(() => {
|
||||
this.#isMouseOnCooldown = false;
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
#onMouseDown(e: any) {
|
||||
this.#isMouseDown = true;
|
||||
if (getApp().getState() === OlympusState.IDLE || getApp().getState() === OlympusState.UNIT_CONTROL) {
|
||||
this.#isMouseDown = true;
|
||||
|
||||
DomEvent.stop(e);
|
||||
DomEvent.preventDefault(e);
|
||||
e.originalEvent.stopImmediatePropagation();
|
||||
DomEvent.stop(e);
|
||||
DomEvent.preventDefault(e);
|
||||
e.originalEvent.stopImmediatePropagation();
|
||||
|
||||
if (this.#isMouseOnCooldown) {
|
||||
return;
|
||||
if (this.#isMouseOnCooldown) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#shortPressTimer = window.setTimeout(() => {
|
||||
/* If the mouse is no longer being pressed, execute the short press action */
|
||||
if (!this.#isMouseDown) this.#onShortPress(e);
|
||||
}, 200);
|
||||
|
||||
this.#longPressTimer = window.setTimeout(() => {
|
||||
/* If the mouse is still being pressed, execute the long press action */
|
||||
if (this.#isMouseDown) this.#onLongPress(e);
|
||||
}, 350);
|
||||
}
|
||||
|
||||
this.#shortPressTimer = window.setTimeout(() => {
|
||||
/* If the mouse is no longer being pressed, execute the short press action */
|
||||
if (!this.#isMouseDown) this.#onShortPress(e);
|
||||
}, 200);
|
||||
|
||||
this.#longPressTimer = window.setTimeout(() => {
|
||||
/* If the mouse is still being pressed, execute the long press action */
|
||||
if (this.#isMouseDown) this.#onLongPress(e);
|
||||
}, 350);
|
||||
}
|
||||
|
||||
#onShortPress(e: LeafletMouseEvent) {
|
||||
console.log(`Short press on ${this.getUnitName()}`);
|
||||
|
||||
if (getApp().getState() !== OlympusState.UNIT_CONTROL || e.originalEvent.ctrlKey) {
|
||||
if (getApp().getState() === OlympusState.IDLE) {
|
||||
if (!e.originalEvent.ctrlKey) getApp().getUnitsManager().deselectAllUnits();
|
||||
this.setSelected(!this.getSelected());
|
||||
} else if (getApp().getState() === OlympusState.UNIT_CONTROL) {
|
||||
if (getApp().getMap().getContextAction()) getApp().getMap().executeContextAction(this, null, e.originalEvent);
|
||||
else {
|
||||
getApp().getUnitsManager().deselectAllUnits();
|
||||
if (getApp().getMap().getContextAction() && getApp().getMap().getContextAction()?.getTarget() === ContextActionTarget.UNIT) {
|
||||
getApp().getMap().executeContextAction(this, null, e.originalEvent);
|
||||
} else {
|
||||
if (!e.originalEvent.ctrlKey) getApp().getUnitsManager().deselectAllUnits();
|
||||
this.setSelected(!this.getSelected());
|
||||
}
|
||||
} else if (getApp().getState() === OlympusState.JTAC && getApp().getSubState() === JTACSubState.SELECT_TARGET) {
|
||||
@ -1365,23 +1348,30 @@ export abstract class Unit extends CustomMarker {
|
||||
#onLongPress(e: any) {
|
||||
console.log(`Long press on ${this.getUnitName()}`);
|
||||
|
||||
if (e.originalEvent.button === 2 && !getApp().getMap().getContextAction()) {
|
||||
if (getApp().getState() === OlympusState.UNIT_CONTROL && e.originalEvent.button === 2 && !getApp().getMap().getContextAction()) {
|
||||
getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.UNIT_CONTEXT_MENU);
|
||||
UnitContextMenuRequestEvent.dispatch(this);
|
||||
}
|
||||
}
|
||||
|
||||
#onDoubleClick(e: any) {
|
||||
console.log(`Double click on ${this.getUnitName()}`);
|
||||
if (getApp().getState() === OlympusState.IDLE || getApp().getState() === OlympusState.UNIT_CONTROL) {
|
||||
DomEvent.stop(e);
|
||||
DomEvent.preventDefault(e);
|
||||
|
||||
window.clearTimeout(this.#shortPressTimer);
|
||||
window.clearTimeout(this.#longPressTimer);
|
||||
console.log(`Double click on ${this.getUnitName()}`);
|
||||
|
||||
/* Select all matching units in the viewport */
|
||||
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);
|
||||
});
|
||||
window.clearTimeout(this.#shortPressTimer);
|
||||
window.clearTimeout(this.#longPressTimer);
|
||||
|
||||
/* Select all matching units in the viewport */
|
||||
if (getApp().getState() === OlympusState.IDLE || getApp().getState() === OlympusState.UNIT_CONTROL) {
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#updateMarker() {
|
||||
|
||||
@ -76,6 +76,10 @@ module.exports = function (configLocation, viteProxy) {
|
||||
app.use("/api/elevation", elevationRouter);
|
||||
app.use("/api/databases", databasesRouter);
|
||||
app.use("/resources", resourcesRouter);
|
||||
app.use("/express/api/airbases", airbasesRouter);
|
||||
app.use("/express/api/elevation", elevationRouter);
|
||||
app.use("/express/api/databases", databasesRouter);
|
||||
app.use("/express/resources", resourcesRouter);
|
||||
|
||||
/* Set default index */
|
||||
if (!viteProxy) {
|
||||
|
||||
@ -29,6 +29,12 @@ export class SRSHandler {
|
||||
this.decodeData(data);
|
||||
});
|
||||
this.ws.on("close", () => {
|
||||
let CLIENT_DISCONNECT = {
|
||||
Client: this.data,
|
||||
MsgType: 5,
|
||||
Version: SRS_VERSION,
|
||||
};
|
||||
this.tcp.write(`${JSON.stringify(CLIENT_DISCONNECT)}\n`);
|
||||
this.tcp.end();
|
||||
});
|
||||
|
||||
@ -113,6 +119,13 @@ export class SRSHandler {
|
||||
this.data.RadioInfo.radios[idx].freq = setting.frequency;
|
||||
this.data.RadioInfo.radios[idx].modulation = setting.modulation;
|
||||
});
|
||||
|
||||
let RADIO_UPDATE = {
|
||||
Client: this.data,
|
||||
MsgType: 3,
|
||||
Version: SRS_VERSION,
|
||||
};
|
||||
this.tcp.write(`${JSON.stringify(RADIO_UPDATE)}\n`);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user