mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Refactored app state
This commit is contained in:
parent
4946807d88
commit
14c0a2f1e8
@ -3,6 +3,11 @@ import { Context, MapOptions } from "../types/types";
|
||||
|
||||
export const DEFAULT_CONTEXT: Context = "default context";
|
||||
|
||||
export enum OlympusEvent {
|
||||
STATE_CHANGED = "State changed",
|
||||
UNITS_SELECTED = "Unit selected"
|
||||
}
|
||||
|
||||
export const UNITS_URI = "units";
|
||||
export const WEAPONS_URI = "weapons";
|
||||
export const LOGS_URI = "logs";
|
||||
@ -236,18 +241,48 @@ export const mapBounds = {
|
||||
export const defaultMapMirrors = {};
|
||||
export const defaultMapLayers = {};
|
||||
|
||||
/* Map constants */
|
||||
export const NOT_INITIALIZED = "Not initialized";
|
||||
export const IDLE = "Idle";
|
||||
export const SPAWN_UNIT = "Spawn unit";
|
||||
export const SPAWN_EFFECT = "Spawn effect";
|
||||
export const CONTEXT_ACTION = "Context action";
|
||||
export const COALITIONAREA_DRAW_POLYGON = "Draw Coalition Area polygon";
|
||||
export const COALITIONAREA_DRAW_CIRCLE = "Draw Coalition Area circle";
|
||||
export const COALITIONAREA_EDIT = "Edit Coalition Area";
|
||||
export const SELECT_JTAC_TARGET = "Select JTAC target"
|
||||
export const SELECT_JTAC_ECHO = "Select JTAC echo point"
|
||||
export const SELECT_JTAC_IP = "Select JTAC IP"
|
||||
export enum OlympusState {
|
||||
NOT_INITIALIZED = "Not initialized",
|
||||
LOGIN = "Login",
|
||||
IDLE = "Idle",
|
||||
MAIN_MENU = "Main menu",
|
||||
UNIT_CONTROL = "Unit control",
|
||||
SPAWN = "Spawn",
|
||||
DRAW = "Draw",
|
||||
JTAC = "JTAC",
|
||||
OPTIONS = "Options",
|
||||
AUDIO = "Audio",
|
||||
AIRBASE = "Airbase"
|
||||
}
|
||||
|
||||
export const NO_SUBSTATE = "No substate";
|
||||
|
||||
export enum UnitControlSubState {
|
||||
NO_SUBSTATE = "No substate",
|
||||
FORMATION = "Formation"
|
||||
}
|
||||
|
||||
export enum DrawSubState {
|
||||
NO_SUBSTATE = "No substate",
|
||||
DRAW_POLYGON = "Polygon",
|
||||
DRAW_CIRCLE = "Circle",
|
||||
EDIT = "Edit"
|
||||
}
|
||||
|
||||
export enum JTACSubState {
|
||||
NO_SUBSTATE = "No substate",
|
||||
SELECT_ECHO_POINT = "ECHO",
|
||||
SELECT_IP = "IP",
|
||||
SELECT_TARGET = "Target"
|
||||
}
|
||||
|
||||
export enum SpawnSubState {
|
||||
NO_SUBSTATE = "No substate",
|
||||
SPAWN_UNIT = "Unit",
|
||||
SPAWN_EFFECT = "Effect"
|
||||
}
|
||||
|
||||
export type OlympusSubState = DrawSubState | JTACSubState | SpawnSubState | string;
|
||||
|
||||
export const IADSTypes = ["AAA", "SAM Site", "Radar (EWR)"];
|
||||
export const IADSDensities: { [key: string]: number } = {
|
||||
|
||||
45
frontend/react/src/dom.d.ts
vendored
45
frontend/react/src/dom.d.ts
vendored
@ -1,45 +0,0 @@
|
||||
import { ServerStatus } from "./interfaces";
|
||||
import { CoalitionPolygon } from "./map/coalitionarea/coalitionpolygon";
|
||||
import { Unit } from "./unit/unit";
|
||||
|
||||
interface CustomEventMap {
|
||||
// TODO add missing events and remove unused
|
||||
unitSelection: CustomEvent<Unit>;
|
||||
unitDeselection: CustomEvent<Unit>;
|
||||
unitsSelection: CustomEvent<Unit[]>;
|
||||
unitsDeselection: CustomEvent<Unit[]>;
|
||||
clearSelection: CustomEvent<any>;
|
||||
unitDeath: CustomEvent<Unit>;
|
||||
unitUpdated: CustomEvent<Unit>;
|
||||
mapStateChanged: CustomEvent<string>;
|
||||
mapOptionChanged: CustomEvent<any>;
|
||||
mapSourceChanged: CustomEvent<string>;
|
||||
mapOptionsChanged: CustomEvent<any>; // TODO not very clear, why the two options?
|
||||
mapSelectionEnd: CustomEvent<any>;
|
||||
configLoaded: CustomEvent<any>;
|
||||
commandModeOptionsChanged: CustomEvent<any>;
|
||||
contactsUpdated: CustomEvent<Unit>;
|
||||
activeCoalitionChanged: CustomEvent<any>;
|
||||
serverStatusUpdated: CustomEvent<ServerStatus>;
|
||||
mapForceBoxSelect: CustomEvent<any>;
|
||||
coalitionAreaSelected: CustomEvent<CoalitionPolygon>;
|
||||
showMapContextMenu: CustomEvent<any>;
|
||||
hideMapContextMenu: CustomEvent<any>;
|
||||
showUnitContextMenu: CustomEvent<any>;
|
||||
hideUnitContextMenu: CustomEvent<any>;
|
||||
audioSourcesUpdated: CustomEvent<any>;
|
||||
audioSinksUpdated: CustomEvent<any>;
|
||||
audioManagerStateChanged: CustomEvent<any>;
|
||||
SRSClientsUpdated: CustomEvent<any>;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Document {
|
||||
addEventListener<K extends keyof CustomEventMap>(type: K, listener: (this: Document, ev: CustomEventMap[K]) => void): void;
|
||||
dispatchEvent<K extends keyof CustomEventMap>(ev: CustomEventMap[K]): void;
|
||||
}
|
||||
|
||||
//function getOlympusPlugin(): OlympusPlugin;
|
||||
}
|
||||
|
||||
export {};
|
||||
@ -1,25 +0,0 @@
|
||||
import { createContext } from "react";
|
||||
|
||||
export const EventsContext = createContext({
|
||||
setMainMenuVisible: (e: boolean) => {},
|
||||
setSpawnMenuVisible: (e: boolean) => {},
|
||||
setUnitControlMenuVisible: (e: boolean) => {},
|
||||
setMeasureMenuVisible: (e: boolean) => {},
|
||||
setDrawingMenuVisible: (e: boolean) => {},
|
||||
setOptionsMenuVisible: (e: boolean) => {},
|
||||
setAirbaseMenuVisible: (e: boolean) => {},
|
||||
setAudioMenuVisible: (e: boolean) => {},
|
||||
setJTACMenuVisible: (e: boolean) => {},
|
||||
toggleMainMenuVisible: () => {},
|
||||
toggleSpawnMenuVisible: () => {},
|
||||
toggleUnitControlMenuVisible: () => {},
|
||||
toggleMeasureMenuVisible: () => {},
|
||||
toggleDrawingMenuVisible: () => {},
|
||||
toggleOptionsMenuVisible: () => {},
|
||||
toggleAirbaseMenuVisible: () => {},
|
||||
toggleAudioMenuVisible: () => {},
|
||||
toggleJTACMenuVisible: () => {},
|
||||
});
|
||||
|
||||
export const EventsProvider = EventsContext.Provider;
|
||||
export const EventsConsumer = EventsContext.Consumer;
|
||||
@ -59,6 +59,14 @@ export class CoalitionCircle extends Circle {
|
||||
this.#drawLabel();
|
||||
this.setOpacity(selected ? 1 : 0.5);
|
||||
|
||||
if (selected) {
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("coalitionAreaSelected", {
|
||||
detail: this,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
//@ts-ignore draggable option added by leaflet-path-drag
|
||||
selected ? this.dragging.enable() : this.dragging.disable();
|
||||
}
|
||||
|
||||
@ -70,6 +70,14 @@ export class CoalitionPolygon extends Polygon {
|
||||
this.setEditing(false);
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("coalitionAreaSelected", {
|
||||
detail: this,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
//@ts-ignore draggable option added by leaflet-path-drag
|
||||
selected ? this.dragging.enable() : this.dragging.disable();
|
||||
}
|
||||
|
||||
@ -10,20 +10,15 @@ import {
|
||||
defaultMapLayers,
|
||||
mapBounds,
|
||||
minimapBoundaries,
|
||||
IDLE,
|
||||
COALITIONAREA_DRAW_POLYGON,
|
||||
defaultMapMirrors,
|
||||
SPAWN_UNIT,
|
||||
CONTEXT_ACTION,
|
||||
MAP_OPTIONS_DEFAULTS,
|
||||
MAP_HIDDEN_TYPES_DEFAULTS,
|
||||
COALITIONAREA_EDIT,
|
||||
COALITIONAREA_DRAW_CIRCLE,
|
||||
NOT_INITIALIZED,
|
||||
SPAWN_EFFECT,
|
||||
SELECT_JTAC_TARGET,
|
||||
SELECT_JTAC_ECHO,
|
||||
SELECT_JTAC_IP,
|
||||
OlympusState,
|
||||
OlympusSubState,
|
||||
NO_SUBSTATE,
|
||||
SpawnSubState,
|
||||
DrawSubState,
|
||||
JTACSubState,
|
||||
} from "../constants/constants";
|
||||
import { CoalitionPolygon } from "./coalitionarea/coalitionpolygon";
|
||||
import { MapHiddenTypes, MapOptions } from "../types/types";
|
||||
@ -53,9 +48,6 @@ export class Map extends L.Map {
|
||||
#options: MapOptions = MAP_OPTIONS_DEFAULTS;
|
||||
#hiddenTypes: MapHiddenTypes = MAP_HIDDEN_TYPES_DEFAULTS;
|
||||
|
||||
/* State machine */
|
||||
#state: string = NOT_INITIALIZED;
|
||||
|
||||
/* Map layers */
|
||||
#theatre: string = "";
|
||||
#layer: L.TileLayer | L.LayerGroup | null = null;
|
||||
@ -158,9 +150,6 @@ export class Map extends L.Map {
|
||||
this.#miniMapPolyline = new L.Polyline([], { color: "#202831" });
|
||||
this.#miniMapPolyline.addTo(this.#miniMapLayerGroup);
|
||||
|
||||
/* Init the state machine */
|
||||
setTimeout(() => this.setState(IDLE), 100);
|
||||
|
||||
/* Register event handles */
|
||||
this.on("zoomstart", (e: any) => this.#onZoomStart(e));
|
||||
this.on("zoom", (e: any) => this.#onZoom(e));
|
||||
@ -192,6 +181,10 @@ export class Map extends L.Map {
|
||||
L.DomEvent.on(this.getContainer(), "touchend", this.#onMouseUp, this);
|
||||
|
||||
/* Event listeners */
|
||||
document.addEventListener("appStateChanged", (ev: CustomEventInit) => {
|
||||
this.#onStateChanged(ev.detail.state, ev.detail.subState);
|
||||
});
|
||||
|
||||
document.addEventListener("hiddenTypesChanged", (ev: CustomEventInit) => {
|
||||
Object.values(getApp().getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
|
||||
Object.values(getApp().getMissionManager().getAirbases()).forEach((airbase: Airbase) => {
|
||||
@ -274,8 +267,10 @@ export class Map extends L.Map {
|
||||
document.dispatchEvent(new CustomEvent("selectJTACECHO", { detail: this.#ECHOPoint?.getLatLng() }));
|
||||
event.target.options["freeze"] = false;
|
||||
});
|
||||
this.#ECHOPoint.on("click", (event) => {
|
||||
getApp().setState(OlympusState.JTAC)
|
||||
})
|
||||
} else this.#ECHOPoint.setLatLng(ev.detail);
|
||||
|
||||
});
|
||||
|
||||
document.addEventListener("selectJTACIP", (ev: CustomEventInit) => {
|
||||
@ -289,6 +284,9 @@ export class Map extends L.Map {
|
||||
document.dispatchEvent(new CustomEvent("selectJTACIP", { detail: this.#IPPoint?.getLatLng() }));
|
||||
event.target.options["freeze"] = false;
|
||||
});
|
||||
this.#IPPoint.on("click", (event) => {
|
||||
getApp().setState(OlympusState.JTAC)
|
||||
})
|
||||
} else this.#IPPoint.setLatLng(ev.detail);
|
||||
|
||||
this.#drawIPToTargetLine();
|
||||
@ -303,9 +301,12 @@ export class Map extends L.Map {
|
||||
event.target.options["freeze"] = true;
|
||||
});
|
||||
this.#targetPoint.on("dragend", (event) => {
|
||||
document.dispatchEvent(new CustomEvent("selectJTACTarget", { detail: {location: this.#targetPoint?.getLatLng() }}));
|
||||
document.dispatchEvent(new CustomEvent("selectJTACTarget", { detail: { location: this.#targetPoint?.getLatLng() } }));
|
||||
event.target.options["freeze"] = false;
|
||||
});
|
||||
this.#targetPoint.on("click", (event) => {
|
||||
getApp().setState(OlympusState.JTAC)
|
||||
})
|
||||
} else this.#targetPoint.setLatLng(ev.detail.location);
|
||||
} else {
|
||||
this.#targetPoint?.removeFrom(this);
|
||||
@ -405,276 +406,280 @@ export class Map extends L.Map {
|
||||
return Object.keys(this.#mapLayers);
|
||||
}
|
||||
|
||||
/* State machine */
|
||||
setState(
|
||||
state: string,
|
||||
options?: {
|
||||
spawnRequestTable?: SpawnRequestTable;
|
||||
effectRequestTable?: EffectRequestTable;
|
||||
contextAction?: ContextAction | null;
|
||||
defaultContextAction?: ContextAction | null;
|
||||
}
|
||||
) {
|
||||
console.log(`Switching from state ${this.#state} to ${state}`);
|
||||
setSpawnRequestTable(spawnRequestTable: SpawnRequestTable) {
|
||||
this.#spawnRequestTable = spawnRequestTable;
|
||||
}
|
||||
|
||||
setEffectRequestTable(effectRequestTable: EffectRequestTable) {
|
||||
this.#effectRequestTable = effectRequestTable;
|
||||
}
|
||||
|
||||
setContextAction(contextAction: ContextAction | null) {
|
||||
this.#contextAction = contextAction;
|
||||
}
|
||||
|
||||
setDefaultContextAction(defaultContextAction: ContextAction | null) {
|
||||
this.#defaultContextAction = defaultContextAction;
|
||||
}
|
||||
|
||||
#onStateChanged(state: OlympusState, subState: OlympusSubState) {
|
||||
/* Operations to perform when leaving a state */
|
||||
if (this.#state === COALITIONAREA_DRAW_POLYGON || this.#state === COALITIONAREA_DRAW_CIRCLE) this.getSelectedCoalitionArea()?.setEditing(false);
|
||||
this.getSelectedCoalitionArea()?.setEditing(false);
|
||||
this.#currentSpawnMarker?.removeFrom(this);
|
||||
this.#currentSpawnMarker = null;
|
||||
|
||||
this.#state = state;
|
||||
if (state !== OlympusState.UNIT_CONTROL) {
|
||||
getApp().getUnitsManager().deselectAllUnits();
|
||||
}
|
||||
|
||||
if (state !== OlympusState.DRAW || (state === OlympusState.DRAW && subState !== DrawSubState.EDIT)) {
|
||||
this.deselectAllCoalitionAreas();
|
||||
}
|
||||
|
||||
/* Operations to perform when entering a state */
|
||||
if (this.#state === IDLE) {
|
||||
if (state === OlympusState.IDLE) {
|
||||
getApp().getUnitsManager()?.deselectAllUnits();
|
||||
this.deselectAllCoalitionAreas();
|
||||
} else if (this.#state === SPAWN_UNIT) {
|
||||
this.deselectAllCoalitionAreas();
|
||||
this.#spawnRequestTable = options?.spawnRequestTable ?? null;
|
||||
console.log(`Spawn request table:`);
|
||||
console.log(this.#spawnRequestTable);
|
||||
this.#currentSpawnMarker = new TemporaryUnitMarker(
|
||||
new L.LatLng(0, 0),
|
||||
this.#spawnRequestTable?.unit.unitType ?? "",
|
||||
this.#spawnRequestTable?.coalition ?? "neutral"
|
||||
);
|
||||
this.#currentSpawnMarker.addTo(this);
|
||||
} else if (this.#state === SPAWN_EFFECT) {
|
||||
this.deselectAllCoalitionAreas();
|
||||
this.#effectRequestTable = options?.effectRequestTable ?? null;
|
||||
console.log(`Effect request table:`);
|
||||
console.log(this.#effectRequestTable);
|
||||
//this.#currentEffectMarker = new TemporaryUnitMarker(new L.LatLng(0, 0), this.#spawnRequestTable?.unit.unitType ?? "", this.#spawnRequestTable?.coalition ?? "neutral")
|
||||
//this.#currentEffectMarker.addTo(this);
|
||||
} else if (this.#state === CONTEXT_ACTION) {
|
||||
this.deselectAllCoalitionAreas();
|
||||
this.#contextAction = options?.contextAction ?? null;
|
||||
this.#defaultContextAction = options?.defaultContextAction ?? null;
|
||||
} else if (state === OlympusState.SPAWN) {
|
||||
if (subState === SpawnSubState.SPAWN_UNIT) {
|
||||
console.log(`Spawn request table:`);
|
||||
console.log(this.#spawnRequestTable);
|
||||
this.#currentSpawnMarker = new TemporaryUnitMarker(
|
||||
new L.LatLng(0, 0),
|
||||
this.#spawnRequestTable?.unit.unitType ?? "",
|
||||
this.#spawnRequestTable?.coalition ?? "neutral"
|
||||
);
|
||||
this.#currentSpawnMarker.addTo(this);
|
||||
} else if (subState === SpawnSubState.SPAWN_EFFECT) {
|
||||
console.log(`Effect request table:`);
|
||||
console.log(this.#effectRequestTable);
|
||||
// TODO
|
||||
//this.#currentEffectMarker = new TemporaryUnitMarker(new L.LatLng(0, 0), this.#spawnRequestTable?.unit.unitType ?? "", this.#spawnRequestTable?.coalition ?? "neutral")
|
||||
//this.#currentEffectMarker.addTo(this);
|
||||
}
|
||||
} else if (state === OlympusState.UNIT_CONTROL) {
|
||||
console.log(`Context action:`);
|
||||
console.log(this.#contextAction);
|
||||
console.log(`Default context action callback:`);
|
||||
console.log(this.#defaultContextAction);
|
||||
} else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
||||
getApp().getUnitsManager().deselectAllUnits();
|
||||
this.#coalitionAreas.push(new CoalitionPolygon([]));
|
||||
this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
|
||||
} else if (this.#state === COALITIONAREA_DRAW_CIRCLE) {
|
||||
getApp().getUnitsManager().deselectAllUnits();
|
||||
this.#coalitionAreas.push(new CoalitionCircle(new L.LatLng(0, 0), { radius: 1000 }));
|
||||
this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
|
||||
} else if (state === OlympusState.DRAW) {
|
||||
if (subState == DrawSubState.DRAW_POLYGON) {
|
||||
this.#coalitionAreas.push(new CoalitionPolygon([]));
|
||||
this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
|
||||
this.#coalitionAreas[this.#coalitionAreas.length - 1].setSelected(true);
|
||||
} else if (subState === DrawSubState.DRAW_CIRCLE) {
|
||||
this.#coalitionAreas.push(new CoalitionCircle(new L.LatLng(0, 0), { radius: 1000 }));
|
||||
this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
|
||||
this.#coalitionAreas[this.#coalitionAreas.length - 1].setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
document.dispatchEvent(new CustomEvent("mapStateChanged", { detail: this.#state }));
|
||||
}
|
||||
|
||||
getState() {
|
||||
return this.#state;
|
||||
}
|
||||
|
||||
getCurrentControls() {
|
||||
const touch = matchMedia("(hover: none)").matches;
|
||||
if (this.#state === IDLE) {
|
||||
return [
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: faJetFighter,
|
||||
text: "Select unit",
|
||||
},
|
||||
touch
|
||||
? {
|
||||
actions: [faHandPointer, "Drag"],
|
||||
target: faMap,
|
||||
text: "Box selection",
|
||||
}
|
||||
: {
|
||||
actions: ["Shift", "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Box selection",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Move map location",
|
||||
},
|
||||
];
|
||||
} else if (this.#state === SPAWN_UNIT) {
|
||||
return [
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: faMap,
|
||||
text: "Spawn unit",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", 2],
|
||||
target: faMap,
|
||||
text: "Exit spawn mode",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Move map location",
|
||||
},
|
||||
];
|
||||
} else if (this.#state === SPAWN_EFFECT) {
|
||||
return [
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: faMap,
|
||||
text: "Spawn effect",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", 2],
|
||||
target: faMap,
|
||||
text: "Exit spawn mode",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Move map location",
|
||||
},
|
||||
];
|
||||
} else if (this.#state === CONTEXT_ACTION) {
|
||||
let controls = [
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: faMap,
|
||||
text: "Deselect units",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Move map location",
|
||||
},
|
||||
];
|
||||
|
||||
if (this.#contextAction) {
|
||||
controls.push({
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: this.#contextAction.getTarget() === "unit" ? faJetFighter : faMap,
|
||||
text: this.#contextAction?.getLabel() ?? "",
|
||||
});
|
||||
}
|
||||
|
||||
if (!touch && this.#defaultContextAction) {
|
||||
controls.push({
|
||||
actions: ["RMB"],
|
||||
target: faMap,
|
||||
text: this.#defaultContextAction?.getLabel() ?? "",
|
||||
});
|
||||
controls.push({
|
||||
actions: ["RMB", "hold"],
|
||||
target: faMap,
|
||||
text: "Open context menu",
|
||||
});
|
||||
}
|
||||
|
||||
return controls;
|
||||
} else if (this.#state === COALITIONAREA_EDIT) {
|
||||
return [
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: faDrawPolygon,
|
||||
text: "Select shape",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", 2],
|
||||
target: faMap,
|
||||
text: "Exit drawing mode",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Move map location",
|
||||
},
|
||||
];
|
||||
} else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
||||
return [
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: faMap,
|
||||
text: "Add vertex to polygon",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", 2],
|
||||
target: faMap,
|
||||
text: "Finalize polygon",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Move map location",
|
||||
},
|
||||
];
|
||||
} else if (this.#state === COALITIONAREA_DRAW_CIRCLE) {
|
||||
return [
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: faMap,
|
||||
text: "Add circle",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Move map location",
|
||||
},
|
||||
];
|
||||
} else if (this.#state === SELECT_JTAC_TARGET) {
|
||||
return [
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: faMap,
|
||||
text: "Set unit/location as target",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", 2],
|
||||
target: faMap,
|
||||
text: "Exit selection mode",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Move map location",
|
||||
},
|
||||
];
|
||||
} else if (this.#state === SELECT_JTAC_ECHO) {
|
||||
return [
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: faMap,
|
||||
text: "Set location as ECHO point",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", 2],
|
||||
target: faMap,
|
||||
text: "Exit selection mode",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Move map location",
|
||||
},
|
||||
];
|
||||
} else if (this.#state === SELECT_JTAC_IP) {
|
||||
return [
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB"],
|
||||
target: faMap,
|
||||
text: "Set location as IP point",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", 2],
|
||||
target: faMap,
|
||||
text: "Exit selection mode",
|
||||
},
|
||||
{
|
||||
actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
target: faMap,
|
||||
text: "Move map location",
|
||||
},
|
||||
];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
// TODO
|
||||
//if (getApp().getState() === IDLE) {
|
||||
// return [
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB"],
|
||||
// target: faJetFighter,
|
||||
// text: "Select unit",
|
||||
// },
|
||||
// touch
|
||||
// ? {
|
||||
// actions: [faHandPointer, "Drag"],
|
||||
// target: faMap,
|
||||
// text: "Box selection",
|
||||
// }
|
||||
// : {
|
||||
// actions: ["Shift", "LMB", "Drag"],
|
||||
// target: faMap,
|
||||
// text: "Box selection",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
// target: faMap,
|
||||
// text: "Move map location",
|
||||
// },
|
||||
// ];
|
||||
//} else if (getApp().getState() === SPAWN_UNIT) {
|
||||
// return [
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB"],
|
||||
// target: faMap,
|
||||
// text: "Spawn unit",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", 2],
|
||||
// target: faMap,
|
||||
// text: "Exit spawn mode",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
// target: faMap,
|
||||
// text: "Move map location",
|
||||
// },
|
||||
// ];
|
||||
//} else if (getApp().getState() === SPAWN_EFFECT) {
|
||||
// return [
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB"],
|
||||
// target: faMap,
|
||||
// text: "Spawn effect",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", 2],
|
||||
// target: faMap,
|
||||
// text: "Exit spawn mode",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
// target: faMap,
|
||||
// text: "Move map location",
|
||||
// },
|
||||
// ];
|
||||
//} else if (getApp().getState() === CONTEXT_ACTION) {
|
||||
// let controls = [
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB"],
|
||||
// target: faMap,
|
||||
// text: "Deselect units",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
// target: faMap,
|
||||
// text: "Move map location",
|
||||
// },
|
||||
// ];
|
||||
//
|
||||
// if (this.#contextAction) {
|
||||
// controls.push({
|
||||
// actions: [touch ? faHandPointer : "LMB"],
|
||||
// target: this.#contextAction.getTarget() === "unit" ? faJetFighter : faMap,
|
||||
// text: this.#contextAction?.getLabel() ?? "",
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (!touch && this.#defaultContextAction) {
|
||||
// controls.push({
|
||||
// actions: ["RMB"],
|
||||
// target: faMap,
|
||||
// text: this.#defaultContextAction?.getLabel() ?? "",
|
||||
// });
|
||||
// controls.push({
|
||||
// actions: ["RMB", "hold"],
|
||||
// target: faMap,
|
||||
// text: "Open context menu",
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// return controls;
|
||||
//} else if (getApp().getState() === COALITIONAREA_EDIT) {
|
||||
// return [
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB"],
|
||||
// target: faDrawPolygon,
|
||||
// text: "Select shape",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", 2],
|
||||
// target: faMap,
|
||||
// text: "Exit drawing mode",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
// target: faMap,
|
||||
// text: "Move map location",
|
||||
// },
|
||||
// ];
|
||||
//} else if (getApp().getState() === COALITIONAREA_DRAW_POLYGON) {
|
||||
// return [
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB"],
|
||||
// target: faMap,
|
||||
// text: "Add vertex to polygon",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", 2],
|
||||
// target: faMap,
|
||||
// text: "Finalize polygon",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
// target: faMap,
|
||||
// text: "Move map location",
|
||||
// },
|
||||
// ];
|
||||
//} else if (getApp().getState() === COALITIONAREA_DRAW_CIRCLE) {
|
||||
// return [
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB"],
|
||||
// target: faMap,
|
||||
// text: "Add circle",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
// target: faMap,
|
||||
// text: "Move map location",
|
||||
// },
|
||||
// ];
|
||||
//} else if (getApp().getState() === SELECT_JTAC_TARGET) {
|
||||
// return [
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB"],
|
||||
// target: faMap,
|
||||
// text: "Set unit/location as target",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", 2],
|
||||
// target: faMap,
|
||||
// text: "Exit selection mode",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
// target: faMap,
|
||||
// text: "Move map location",
|
||||
// },
|
||||
// ];
|
||||
//} else if (getApp().getState() === SELECT_JTAC_ECHO) {
|
||||
// return [
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB"],
|
||||
// target: faMap,
|
||||
// text: "Set location as ECHO point",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", 2],
|
||||
// target: faMap,
|
||||
// text: "Exit selection mode",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
// target: faMap,
|
||||
// text: "Move map location",
|
||||
// },
|
||||
// ];
|
||||
//} else if (getApp().getState() === SELECT_JTAC_IP) {
|
||||
// return [
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB"],
|
||||
// target: faMap,
|
||||
// text: "Set location as IP point",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", 2],
|
||||
// target: faMap,
|
||||
// text: "Exit selection mode",
|
||||
// },
|
||||
// {
|
||||
// actions: [touch ? faHandPointer : "LMB", "Drag"],
|
||||
// target: faMap,
|
||||
// text: "Move map location",
|
||||
// },
|
||||
// ];
|
||||
//} else {
|
||||
// return [];
|
||||
//}
|
||||
}
|
||||
|
||||
deselectAllCoalitionAreas() {
|
||||
@ -972,10 +977,10 @@ export class Map extends L.Map {
|
||||
window.clearTimeout(this.#shortPressTimer);
|
||||
window.clearTimeout(this.#longPressTimer);
|
||||
|
||||
if (this.#state === COALITIONAREA_DRAW_POLYGON || this.#state === COALITIONAREA_DRAW_CIRCLE) {
|
||||
this.setState(COALITIONAREA_EDIT);
|
||||
if (getApp().getSubState() !== NO_SUBSTATE) {
|
||||
getApp().setState(getApp().getState(), NO_SUBSTATE);
|
||||
} else {
|
||||
this.setState(IDLE);
|
||||
getApp().setState(OlympusState.IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -990,68 +995,69 @@ export class Map extends L.Map {
|
||||
document.dispatchEvent(new CustomEvent("hideUnitContextMenu"));
|
||||
|
||||
/* Execute the short click action */
|
||||
if (this.#state === IDLE) {
|
||||
} else if (this.#state === SPAWN_UNIT) {
|
||||
if (e.originalEvent.button != 2 && this.#spawnRequestTable !== null) {
|
||||
this.#spawnRequestTable.unit.location = pressLocation;
|
||||
getApp()
|
||||
.getUnitsManager()
|
||||
.spawnUnits(
|
||||
this.#spawnRequestTable.category,
|
||||
[this.#spawnRequestTable.unit],
|
||||
this.#spawnRequestTable.coalition,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
(hash) => {
|
||||
this.addTemporaryMarker(pressLocation, this.#spawnRequestTable?.unit.unitType ?? "unknown", this.#spawnRequestTable?.coalition ?? "blue", hash);
|
||||
}
|
||||
);
|
||||
}
|
||||
} else if (this.#state === SPAWN_EFFECT) {
|
||||
if (e.originalEvent.button != 2 && this.#effectRequestTable !== null) {
|
||||
getApp().getServerManager().spawnExplosion(50, "normal", pressLocation);
|
||||
}
|
||||
} else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
||||
const selectedArea = this.getSelectedCoalitionArea();
|
||||
if (selectedArea && selectedArea instanceof CoalitionPolygon) {
|
||||
selectedArea.addTemporaryLatLng(pressLocation);
|
||||
}
|
||||
} else if (this.#state === COALITIONAREA_DRAW_CIRCLE) {
|
||||
const selectedArea = this.getSelectedCoalitionArea();
|
||||
if (selectedArea && selectedArea instanceof CoalitionCircle) {
|
||||
if (selectedArea.getLatLng().lat == 0 && selectedArea.getLatLng().lng == 0) selectedArea.setLatLng(pressLocation);
|
||||
this.setState(COALITIONAREA_EDIT);
|
||||
}
|
||||
} else if (this.#state == COALITIONAREA_EDIT) {
|
||||
this.deselectAllCoalitionAreas();
|
||||
for (let idx = 0; idx < this.#coalitionAreas.length; idx++) {
|
||||
if (areaContains(pressLocation, this.#coalitionAreas[idx])) {
|
||||
this.#coalitionAreas[idx].setSelected(true);
|
||||
document.dispatchEvent(
|
||||
new CustomEvent("coalitionAreaSelected", {
|
||||
detail: this.#coalitionAreas[idx],
|
||||
})
|
||||
);
|
||||
break;
|
||||
if (getApp().getState() === OlympusState.IDLE) {
|
||||
/* Do nothing */
|
||||
} else if (getApp().getState() === OlympusState.SPAWN) {
|
||||
if (getApp().getSubState() === SpawnSubState.SPAWN_UNIT) {
|
||||
if (e.originalEvent.button != 2 && this.#spawnRequestTable !== null) {
|
||||
this.#spawnRequestTable.unit.location = pressLocation;
|
||||
getApp()
|
||||
.getUnitsManager()
|
||||
.spawnUnits(
|
||||
this.#spawnRequestTable.category,
|
||||
[this.#spawnRequestTable.unit],
|
||||
this.#spawnRequestTable.coalition,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
(hash) => {
|
||||
this.addTemporaryMarker(pressLocation, this.#spawnRequestTable?.unit.unitType ?? "unknown", this.#spawnRequestTable?.coalition ?? "blue", hash);
|
||||
}
|
||||
);
|
||||
}
|
||||
} else if (getApp().getSubState() === SpawnSubState.SPAWN_EFFECT) {
|
||||
if (e.originalEvent.button != 2 && this.#effectRequestTable !== null) {
|
||||
getApp().getServerManager().spawnExplosion(50, "normal", pressLocation);
|
||||
}
|
||||
}
|
||||
} else if (this.#state === CONTEXT_ACTION) {
|
||||
} else if (getApp().getState() === OlympusState.DRAW) {
|
||||
if (getApp().getSubState() === DrawSubState.DRAW_POLYGON) {
|
||||
const selectedArea = this.getSelectedCoalitionArea();
|
||||
if (selectedArea && selectedArea instanceof CoalitionPolygon) {
|
||||
selectedArea.addTemporaryLatLng(pressLocation);
|
||||
}
|
||||
} else if (getApp().getSubState() === DrawSubState.DRAW_CIRCLE) {
|
||||
const selectedArea = this.getSelectedCoalitionArea();
|
||||
if (selectedArea && selectedArea instanceof CoalitionCircle) {
|
||||
if (selectedArea.getLatLng().lat == 0 && selectedArea.getLatLng().lng == 0) selectedArea.setLatLng(pressLocation);
|
||||
getApp().setState(OlympusState.DRAW, DrawSubState.EDIT);
|
||||
}
|
||||
} else if (getApp().getSubState() == DrawSubState.NO_SUBSTATE) {
|
||||
this.deselectAllCoalitionAreas();
|
||||
for (let idx = 0; idx < this.#coalitionAreas.length; idx++) {
|
||||
if (areaContains(pressLocation, this.#coalitionAreas[idx])) {
|
||||
this.#coalitionAreas[idx].setSelected(true);
|
||||
getApp().setState(OlympusState.DRAW, DrawSubState.EDIT)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (getApp().getState() === OlympusState.UNIT_CONTROL) {
|
||||
if (e.type === "touchstart" || e.originalEvent.buttons === 1) {
|
||||
if (this.#contextAction !== null) this.executeContextAction(null, pressLocation);
|
||||
else this.setState(IDLE);
|
||||
else getApp().setState(OlympusState.IDLE);
|
||||
} else if (e.originalEvent.buttons === 2) {
|
||||
if (this.#defaultContextAction !== null) this.executeDefaultContextAction(null, pressLocation);
|
||||
}
|
||||
} else if (this.#state === SELECT_JTAC_TARGET) {
|
||||
document.dispatchEvent(new CustomEvent("selectJTACTarget", { detail: { location: pressLocation } }));
|
||||
this.setState(IDLE);
|
||||
} else if (this.#state === SELECT_JTAC_ECHO) {
|
||||
document.dispatchEvent(new CustomEvent("selectJTACECHO", { detail: pressLocation }));
|
||||
this.setState(IDLE);
|
||||
} else if (this.#state === SELECT_JTAC_IP) {
|
||||
document.dispatchEvent(new CustomEvent("selectJTACIP", { detail: pressLocation }));
|
||||
this.setState(IDLE);
|
||||
} else if (getApp().getState() === OlympusState.JTAC) {
|
||||
if (getApp().getSubState() === JTACSubState.SELECT_TARGET) {
|
||||
document.dispatchEvent(new CustomEvent("selectJTACTarget", { detail: { location: pressLocation } }));
|
||||
} else if (getApp().getSubState() === JTACSubState.SELECT_ECHO_POINT) {
|
||||
document.dispatchEvent(new CustomEvent("selectJTACECHO", { detail: pressLocation }));
|
||||
} else if (getApp().getSubState() === JTACSubState.SELECT_IP) {
|
||||
document.dispatchEvent(new CustomEvent("selectJTACIP", { detail: pressLocation }));
|
||||
}
|
||||
getApp().setState(OlympusState.JTAC);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
@ -1065,10 +1071,10 @@ export class Map extends L.Map {
|
||||
|
||||
if (!this.#isDragging && !this.#isZooming) {
|
||||
this.deselectAllCoalitionAreas();
|
||||
if (this.#state === IDLE) {
|
||||
if (getApp().getState() === OlympusState.IDLE) {
|
||||
if (e.type === "touchstart") document.dispatchEvent(new CustomEvent("mapForceBoxSelect", { detail: e }));
|
||||
else document.dispatchEvent(new CustomEvent("mapForceBoxSelect", { detail: e.originalEvent }));
|
||||
} else if (this.#state === CONTEXT_ACTION) {
|
||||
} else if (getApp().getState() === OlympusState.UNIT_CONTROL) {
|
||||
if (e.originalEvent.button === 2) {
|
||||
document.dispatchEvent(new CustomEvent("showMapContextMenu", { detail: e }));
|
||||
} else {
|
||||
|
||||
@ -3,7 +3,7 @@ import { CustomMarker } from "../map/markers/custommarker";
|
||||
import { SVGInjector } from "@tanem/svg-injector";
|
||||
import { AirbaseChartData, AirbaseOptions } from "../interfaces";
|
||||
import { getApp } from "../olympusapp";
|
||||
import { IDLE } from "../constants/constants";
|
||||
import { OlympusState } from "../constants/constants";
|
||||
|
||||
export class Airbase extends CustomMarker {
|
||||
#name: string = "";
|
||||
@ -25,8 +25,9 @@ export class Airbase extends CustomMarker {
|
||||
this.#img = document.createElement("img");
|
||||
|
||||
this.addEventListener("click", (ev) => {
|
||||
if (getApp().getMap().getState() === IDLE) {
|
||||
document.dispatchEvent(new CustomEvent("airbaseClick", { detail: ev.target }));
|
||||
if (getApp().getState() === OlympusState.IDLE) {
|
||||
getApp().setState(OlympusState.AIRBASE)
|
||||
// TODO: document.dispatchEvent(new CustomEvent("airbaseClick", { detail: ev.target }));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -18,24 +18,32 @@ import { ShortcutManager } from "./shortcut/shortcutmanager";
|
||||
import { UnitsManager } from "./unit/unitsmanager";
|
||||
import { WeaponsManager } from "./weapon/weaponsmanager";
|
||||
import { ServerManager } from "./server/servermanager";
|
||||
import { AudioManager } from "./audio/audiomanager";
|
||||
|
||||
import { BLUE_COMMANDER, DEFAULT_CONTEXT, GAME_MASTER, RED_COMMANDER } from "./constants/constants";
|
||||
import { DEFAULT_CONTEXT, NO_SUBSTATE, OlympusEvent, OlympusState, OlympusSubState } from "./constants/constants";
|
||||
import { aircraftDatabase } from "./unit/databases/aircraftdatabase";
|
||||
import { helicopterDatabase } from "./unit/databases/helicopterdatabase";
|
||||
import { groundUnitDatabase } from "./unit/databases/groundunitdatabase";
|
||||
import { navyUnitDatabase } from "./unit/databases/navyunitdatabase";
|
||||
import { Coalition, Context } from "./types/types";
|
||||
import { AudioManager } from "./audio/audiomanager";
|
||||
import { Unit } from "./unit/unit";
|
||||
|
||||
export var VERSION = "{{OLYMPUS_VERSION_NUMBER}}";
|
||||
export var IP = window.location.toString();
|
||||
export var connectedToServer = true; // TODO Temporary
|
||||
|
||||
|
||||
export class OlympusApp {
|
||||
/* Global data */
|
||||
#activeCoalition: Coalition = "blue";
|
||||
#latestVersion: string | undefined = undefined;
|
||||
#config: any = {};
|
||||
#state: OlympusState = OlympusState.NOT_INITIALIZED;
|
||||
#subState: OlympusSubState = NO_SUBSTATE;
|
||||
|
||||
#events = {
|
||||
[OlympusEvent.STATE_CHANGED]: [] as ((state: OlympusState, subState: OlympusSubState) => void)[],
|
||||
[OlympusEvent.UNITS_SELECTED]: [] as ((units: Unit[]) => void)[]
|
||||
};
|
||||
|
||||
/* Main leaflet map, extended by custom methods */
|
||||
#map: Map | null = null;
|
||||
@ -92,30 +100,6 @@ export class OlympusApp {
|
||||
}
|
||||
*/
|
||||
|
||||
/** 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: Coalition) {
|
||||
if (this.getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER) {
|
||||
this.#activeCoalition = newActiveCoalition;
|
||||
document.dispatchEvent(new CustomEvent("activeCoalitionChanged"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The active coalition
|
||||
*/
|
||||
getActiveCoalition(): Coalition {
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The aircraft database
|
||||
@ -163,9 +147,6 @@ export class OlympusApp {
|
||||
this.getServerManager().setAddress(window.location.href.split("?")[0].replace("vite/", ""));
|
||||
this.getAudioManager().setAddress(window.location.href.split("?")[0].replace("vite/", ""));
|
||||
|
||||
/* Setup all global events */
|
||||
this.#setupEvents();
|
||||
|
||||
/* Check if we are running the latest version */
|
||||
const request = new Request("https://raw.githubusercontent.com/Pax1601/DCSOlympus/main/version.json");
|
||||
fetch(request)
|
||||
@ -198,17 +179,39 @@ export class OlympusApp {
|
||||
.then((res) => {
|
||||
this.#config = res;
|
||||
document.dispatchEvent(new CustomEvent("configLoaded"));
|
||||
this.setState(OlympusState.LOGIN);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
getConfig() {
|
||||
return this.#config;
|
||||
}
|
||||
|
||||
#setupEvents() {
|
||||
/* Reload the page, used to mimic a restart of the app */
|
||||
document.addEventListener("reloadPage", () => {
|
||||
location.reload();
|
||||
});
|
||||
setState(state: OlympusState, subState: OlympusSubState = NO_SUBSTATE) {
|
||||
this.#state = state;
|
||||
this.#subState = subState;
|
||||
|
||||
console.log(`App state set to ${state}, substate ${subState}`)
|
||||
this.dispatchEvent(OlympusEvent.STATE_CHANGED, state, subState)
|
||||
}
|
||||
|
||||
getState() {
|
||||
return this.#state;
|
||||
}
|
||||
|
||||
getSubState() {
|
||||
return this.#subState;
|
||||
}
|
||||
|
||||
registerEventCallback(event: OlympusEvent, callback: any) {
|
||||
this.#events[event].push(callback)
|
||||
}
|
||||
|
||||
dispatchEvent(event: OlympusEvent, ...args) {
|
||||
console.log(`Dispatching event ${event}. Arguments: ${args}`)
|
||||
this.#events[event].forEach((event) => {
|
||||
event(args);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -499,40 +499,16 @@ export function getFunctionArguments(func) {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function filterBlueprintsByLabel(blueprints: { [key: string]: UnitBlueprint }, filterString: string) {
|
||||
var filteredBlueprints: { [key: string]: UnitBlueprint } = {};
|
||||
export function filterBlueprintsByLabel(blueprints: UnitBlueprint[], filterString: string) {
|
||||
var filteredBlueprints: UnitBlueprint[] = [];
|
||||
if (blueprints) {
|
||||
Object.entries(blueprints).forEach(([key, value]) => {
|
||||
if (value.enabled && (filterString === "" || value.label.toLowerCase().includes(filterString.toLowerCase()))) filteredBlueprints[key] = value;
|
||||
blueprints.forEach((blueprint) => {
|
||||
if (blueprint.enabled && (filterString === "" || blueprint.label.toLowerCase().includes(filterString.toLowerCase()))) filteredBlueprints.push(blueprint);
|
||||
});
|
||||
}
|
||||
return filteredBlueprints;
|
||||
}
|
||||
|
||||
export function getUnitsByLabel(filterString: string) {
|
||||
/* Filter aircrafts, helicopters, and navyunits */
|
||||
const filteredAircraft = filterBlueprintsByLabel(getApp()?.getAircraftDatabase()?.blueprints, filterString);
|
||||
const filteredHelicopters = filterBlueprintsByLabel(getApp()?.getHelicopterDatabase()?.blueprints, filterString);
|
||||
const filteredNavyUnits = filterBlueprintsByLabel(getApp()?.getNavyUnitDatabase()?.blueprints, filterString);
|
||||
|
||||
/* Split ground units between air defence and all others */
|
||||
var filteredAirDefense: { [key: string]: UnitBlueprint } = {};
|
||||
var filteredGroundUnits: { [key: string]: UnitBlueprint } = {};
|
||||
Object.keys(getApp()?.getGroundUnitDatabase()?.blueprints ?? {}).forEach((key) => {
|
||||
var blueprint = getApp()?.getGroundUnitDatabase()?.blueprints[key];
|
||||
var type = blueprint.label;
|
||||
if (/\bAAA|SAM\b/.test(type) || /\bmanpad|stinger\b/i.test(type)) {
|
||||
filteredAirDefense[key] = blueprint;
|
||||
} else {
|
||||
filteredGroundUnits[key] = blueprint;
|
||||
}
|
||||
});
|
||||
filteredAirDefense = filterBlueprintsByLabel(filteredAirDefense, filterString);
|
||||
filteredGroundUnits = filterBlueprintsByLabel(filteredGroundUnits, filterString);
|
||||
|
||||
return [filteredAircraft, filteredHelicopters, filteredAirDefense, filteredGroundUnits, filteredNavyUnits];
|
||||
}
|
||||
|
||||
export function makeID(length) {
|
||||
let result = "";
|
||||
const characters =
|
||||
|
||||
@ -1,21 +1,13 @@
|
||||
import { createContext } from "react";
|
||||
import { IDLE, MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS } from "./constants/constants";
|
||||
import { MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS, NO_SUBSTATE, OlympusState, OlympusSubState } from "./constants/constants";
|
||||
|
||||
export const StateContext = createContext({
|
||||
mainMenuVisible: false,
|
||||
spawnMenuVisible: false,
|
||||
unitControlMenuVisible: false,
|
||||
measureMenuVisible: false,
|
||||
drawingMenuVisible: false,
|
||||
optionsMenuVisible: false,
|
||||
airbaseMenuVisible: false,
|
||||
audioMenuVisible: false,
|
||||
JTACMenuVisible: false,
|
||||
appState: OlympusState.NOT_INITIALIZED as OlympusState,
|
||||
appSubState: NO_SUBSTATE as OlympusSubState,
|
||||
mapHiddenTypes: MAP_HIDDEN_TYPES_DEFAULTS,
|
||||
mapOptions: MAP_OPTIONS_DEFAULTS,
|
||||
mapSources: [] as string[],
|
||||
activeMapSource: "",
|
||||
mapState: IDLE
|
||||
activeMapSource: ""
|
||||
});
|
||||
|
||||
export const StateProvider = StateContext.Provider;
|
||||
|
||||
@ -2,8 +2,7 @@ import React, { useEffect, useRef, useState } from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faArrowCircleDown } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
export function OlAccordion(props: { title: string; children?: JSX.Element | JSX.Element[]; showArrows?: boolean; className?: string }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
export function OlAccordion(props: { title: string; open: boolean, onClick: () => void; children?: JSX.Element | JSX.Element[]; showArrows?: boolean; className?: string }) {
|
||||
const [scrolledUp, setScrolledUp] = useState(true);
|
||||
const [scrolledDown, setScrolledDown] = useState(false);
|
||||
|
||||
@ -29,7 +28,7 @@ export function OlAccordion(props: { title: string; children?: JSX.Element | JSX
|
||||
<h3>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(!open)}
|
||||
onClick={() => props.onClick()}
|
||||
className={`
|
||||
${props.className ?? ""}
|
||||
flex w-full items-center justify-between gap-3 border-gray-200 py-2
|
||||
@ -56,7 +55,7 @@ export function OlAccordion(props: { title: string; children?: JSX.Element | JSX
|
||||
</svg>
|
||||
</button>
|
||||
</h3>
|
||||
<div className={open ? "" : "hidden"}>
|
||||
<div className={props.open ? "" : "hidden"}>
|
||||
{props.showArrows && (
|
||||
<div className="rotate-180">
|
||||
{!scrolledUp && (
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Unit } from "../../unit/unit";
|
||||
import { ContextActionSet } from "../../unit/contextactionset";
|
||||
import { OlStateButton } from "../components/olstatebutton";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { ContextAction } from "../../unit/contextaction";
|
||||
import { CONTEXT_ACTION, CONTEXT_ACTION_COLORS } from "../../constants/constants";
|
||||
import { CONTEXT_ACTION_COLORS } from "../../constants/constants";
|
||||
import { OlDropdownItem } from "../components/oldropdown";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { LatLng } from "leaflet";
|
||||
|
||||
@ -3,7 +3,6 @@ import { Menu } from "./components/menu";
|
||||
import { Coalition } from "../../types/types";
|
||||
import { Airbase } from "../../mission/airbase";
|
||||
import { FaArrowLeft, FaCompass } from "react-icons/fa6";
|
||||
import { getUnitsByLabel } from "../../other/utils";
|
||||
import { UnitBlueprint } from "../../interfaces";
|
||||
import { OlSearchBar } from "../components/olsearchbar";
|
||||
import { OlAccordion } from "../components/olaccordion";
|
||||
@ -15,7 +14,7 @@ export function AirbaseMenu(props: { open: boolean; onClose: () => void; airbase
|
||||
const [blueprint, setBlueprint] = useState(null as null | UnitBlueprint);
|
||||
const [filterString, setFilterString] = useState("");
|
||||
|
||||
const [filteredAircraft, filteredHelicopters, _1, _2, _3] = getUnitsByLabel(filterString);
|
||||
const [filteredAircraft, filteredHelicopters] = [{}, {}] // TODOgetUnitsByLabel(filterString);
|
||||
|
||||
return (
|
||||
<Menu title={props.airbase?.getName() ?? "No airbase selected"} open={props.open} onClose={props.onClose} showBackButton={false} canBeHidden={true}>
|
||||
@ -47,7 +46,9 @@ export function AirbaseMenu(props: { open: boolean; onClose: () => void; airbase
|
||||
<span className="text-gray-400">Elevation</span>
|
||||
<span>{props.airbase?.getChartData().elevation !== "" ? props.airbase?.getChartData().elevation : "N/A"}ft</span>
|
||||
</div>
|
||||
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
<OlAccordion title={`Runways`} className="!p-0 !text-gray-400">
|
||||
<div className="flex flex-col gap-2">
|
||||
{props.airbase?.getChartData().runways.map((runway, idx) => {
|
||||
|
||||
@ -19,7 +19,7 @@ export function ControlsPanel(props: {}) {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("mapStateChanged", (ev) => {
|
||||
document.addEventListener("appStateChanged", (ev) => {
|
||||
setControls(getApp().getMap().getCurrentControls());
|
||||
});
|
||||
}, []);
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Menu } from "./components/menu";
|
||||
import { FaQuestionCircle, FaRegCircle, FaTrash } from "react-icons/fa";
|
||||
import { FaQuestionCircle, FaTrash } from "react-icons/fa";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { COALITIONAREA_DRAW_CIRCLE, COALITIONAREA_DRAW_POLYGON, COALITIONAREA_EDIT, IDLE } from "../../constants/constants";
|
||||
import { OlStateButton } from "../components/olstatebutton";
|
||||
import { faDrawPolygon } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faCircle } from "@fortawesome/free-regular-svg-icons";
|
||||
@ -13,10 +12,11 @@ import { OlCheckbox } from "../components/olcheckbox";
|
||||
import { Coalition } from "../../types/types";
|
||||
import { OlRangeSlider } from "../components/olrangeslider";
|
||||
import { CoalitionCircle } from "../../map/coalitionarea/coalitioncircle";
|
||||
import { DrawSubState, NO_SUBSTATE, OlympusState } from "../../constants/constants";
|
||||
|
||||
export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
|
||||
const [drawingPolygon, setDrawingPolygon] = useState(false);
|
||||
const [drawingCircle, setDrawingCircle] = useState(false);
|
||||
const [appState, setAppState] = useState(OlympusState.NOT_INITIALIZED);
|
||||
const [appSubState, setAppSubState] = useState(NO_SUBSTATE);
|
||||
const [activeCoalitionArea, setActiveCoalitionArea] = useState(null as null | CoalitionPolygon | CoalitionCircle);
|
||||
const [areaCoalition, setAreaCoalition] = useState("blue" as Coalition);
|
||||
const [IADSDensity, setIADSDensity] = useState(50);
|
||||
@ -27,37 +27,32 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
|
||||
const [erasSelection, setErasSelection] = useState({});
|
||||
const [rangesSelection, setRangesSelection] = useState({});
|
||||
|
||||
const [showPolygonTip, setShowPolygonTip] = useState(true);
|
||||
const [showCircleTip, setShowCircleTip] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (getApp()) {
|
||||
/* If we are not in polygon drawing mode, force the draw polygon button off */
|
||||
if (drawingPolygon && getApp().getMap().getState() !== COALITIONAREA_DRAW_POLYGON) setDrawingPolygon(false);
|
||||
|
||||
/* If we are not in circle drawing mode, force the draw circle button off */
|
||||
if (drawingCircle && getApp().getMap().getState() !== COALITIONAREA_DRAW_CIRCLE) setDrawingCircle(false);
|
||||
|
||||
/* If we are not in any drawing mode, force the map in edit mode */
|
||||
if (props.open && !drawingPolygon && !drawingCircle) getApp().getMap().setState(COALITIONAREA_EDIT);
|
||||
|
||||
/* Align the state of the coalition toggle to the coalition of the area */
|
||||
if (activeCoalitionArea && activeCoalitionArea?.getCoalition() !== areaCoalition) setAreaCoalition(activeCoalitionArea?.getCoalition());
|
||||
// TODO
|
||||
///* If we are not in polygon drawing mode, force the draw polygon button off */
|
||||
//if (drawingPolygon && getApp().getState() !== COALITIONAREA_DRAW_POLYGON) setDrawingPolygon(false);
|
||||
//
|
||||
///* If we are not in circle drawing mode, force the draw circle button off */
|
||||
//if (drawingCircle && getApp().getState() !== COALITIONAREA_DRAW_CIRCLE) setDrawingCircle(false);
|
||||
//
|
||||
///* If we are not in any drawing mode, force the map in edit mode */
|
||||
//if (props.open && !drawingPolygon && !drawingCircle) getApp().getMap().setState(COALITIONAREA_EDIT);
|
||||
//
|
||||
///* Align the state of the coalition toggle to the coalition of the area */
|
||||
//if (activeCoalitionArea && activeCoalitionArea?.getCoalition() !== areaCoalition) setAreaCoalition(activeCoalitionArea?.getCoalition());
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("mapStateChanged", (event: any) => {
|
||||
if (drawingPolygon && getApp().getMap().getState() !== COALITIONAREA_DRAW_POLYGON) setDrawingPolygon(false);
|
||||
|
||||
if (getApp().getMap().getState() == COALITIONAREA_EDIT) {
|
||||
setActiveCoalitionArea(getApp().getMap().getSelectedCoalitionArea() ?? null);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("coalitionAreaSelected", (event: any) => {
|
||||
setActiveCoalitionArea(event.detail);
|
||||
});
|
||||
|
||||
document.addEventListener("appStateChanged", (ev: CustomEventInit) => {
|
||||
setAppState(ev.detail.state);
|
||||
setAppSubState(ev.detail.subState);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@ -68,71 +63,20 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
|
||||
canBeHidden={true}
|
||||
showBackButton={activeCoalitionArea !== null}
|
||||
onBack={() => {
|
||||
setActiveCoalitionArea(null);
|
||||
getApp().getMap().deselectAllCoalitionAreas();
|
||||
getApp().setState(OlympusState.DRAW, DrawSubState.NO_SUBSTATE)
|
||||
}}
|
||||
>
|
||||
<>
|
||||
{activeCoalitionArea === null && !drawingPolygon && !drawingCircle && (
|
||||
<>
|
||||
<div className="p-4 text-sm text-gray-400">
|
||||
The draw tool allows you to quickly draw areas on the map and use these areas to spawn units and activate triggers.
|
||||
</div>
|
||||
<div className="mx-6 flex rounded-lg bg-olympus-400 p-4 text-sm">
|
||||
<div className="my-auto">
|
||||
<FaQuestionCircle className="my-auto ml-2 mr-6 text-gray-400" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="text-gray-100">Use the polygon or circle tool to draw areas on the map.</div>
|
||||
<div className="text-gray-400">After drawing a shape, select it to see the options for spawning units. Click on a shape to select it.</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
<>
|
||||
{activeCoalitionArea === null && drawingPolygon && (
|
||||
<div className="mx-6 flex rounded-lg bg-olympus-400 p-4 text-sm">
|
||||
<div>
|
||||
<FaQuestionCircle className="my-4 ml-2 mr-6 text-gray-400" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="text-gray-100">Click on the map to add vertices to the polygon.</div>
|
||||
<div className="text-gray-400">
|
||||
When you are done, double click on the map to finalize the polygon. Vertices can be dragged or added to adjust the shape.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
<>
|
||||
{activeCoalitionArea === null && drawingCircle && (
|
||||
<div className="mx-6 flex rounded-lg bg-olympus-400 p-4 text-sm">
|
||||
<div>
|
||||
<FaQuestionCircle className="my-4 ml-2 mr-6 text-gray-400" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="text-gray-100">Click on the map to add a new circle.</div>
|
||||
<div className="text-gray-400">You can drag the circle to move it and you can use the handle to set the radius.</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
<>
|
||||
{activeCoalitionArea === null && (
|
||||
{appState === OlympusState.DRAW && appSubState !== DrawSubState.EDIT && (
|
||||
<div className="flex flex-col gap-2 p-6 text-sm text-gray-400">
|
||||
<OlStateButton
|
||||
className="!w-full"
|
||||
icon={faDrawPolygon}
|
||||
tooltip={"Add a new polygon"}
|
||||
checked={drawingPolygon}
|
||||
checked={appSubState === DrawSubState.DRAW_POLYGON}
|
||||
onClick={() => {
|
||||
if (drawingPolygon) getApp().getMap().setState(COALITIONAREA_EDIT);
|
||||
else getApp().getMap().setState(COALITIONAREA_DRAW_POLYGON);
|
||||
setDrawingPolygon(!drawingPolygon);
|
||||
if (appSubState === DrawSubState.DRAW_POLYGON) getApp().setState(OlympusState.DRAW, DrawSubState.EDIT);
|
||||
else getApp().setState(OlympusState.DRAW, DrawSubState.DRAW_POLYGON);
|
||||
}}
|
||||
>
|
||||
<div className="text-sm">Add polygon</div>
|
||||
@ -141,11 +85,10 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
|
||||
className="!w-full"
|
||||
icon={faCircle}
|
||||
tooltip={"Add a new circle"}
|
||||
checked={drawingCircle}
|
||||
checked={appSubState === DrawSubState.DRAW_CIRCLE}
|
||||
onClick={() => {
|
||||
if (drawingCircle) getApp().getMap().setState(COALITIONAREA_EDIT);
|
||||
else getApp().getMap().setState(COALITIONAREA_DRAW_CIRCLE);
|
||||
setDrawingCircle(!drawingCircle);
|
||||
if (appSubState === DrawSubState.DRAW_CIRCLE) getApp().setState(OlympusState.DRAW, DrawSubState.EDIT);
|
||||
else getApp().setState(OlympusState.DRAW, DrawSubState.DRAW_CIRCLE);
|
||||
}}
|
||||
>
|
||||
<div className="text-sm">Add circle</div>
|
||||
@ -154,7 +97,7 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
|
||||
)}
|
||||
</>
|
||||
<div>
|
||||
{activeCoalitionArea !== null && (
|
||||
{activeCoalitionArea !== null && appSubState === DrawSubState.EDIT && (
|
||||
<div className={`flex flex-col gap-4 py-4`}>
|
||||
<div
|
||||
className={`
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { OlDropdown, OlDropdownItem } from "../components/oldropdown";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { IDLE, SPAWN_EFFECT } from "../../constants/constants";
|
||||
|
||||
export function EffectSpawnMenu(props: { effect: string }) {
|
||||
const [explosionType, setExplosionType] = useState("High explosive");
|
||||
|
||||
/* When the menu is opened show the unit preview on the map as a cursor */
|
||||
useEffect(() => {
|
||||
if (props.effect !== null) {
|
||||
getApp()
|
||||
?.getMap()
|
||||
?.setState(SPAWN_EFFECT, {
|
||||
effectRequestTable: {
|
||||
type: props.effect,
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (getApp().getMap().getState() === SPAWN_EFFECT) getApp().getMap().setState(IDLE);
|
||||
}
|
||||
// TODO
|
||||
//if (props.effect !== null) {
|
||||
// getApp()
|
||||
// ?.getMap()
|
||||
// ?.setState(SPAWN_EFFECT, {
|
||||
// effectRequestTable: {
|
||||
// type: props.effect,
|
||||
// }
|
||||
// });
|
||||
//} else {
|
||||
// if (getApp().getState() === SPAWN_EFFECT) getApp().setState(OlympusState.IDLE);
|
||||
//}
|
||||
|
||||
});
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@ import React, { useEffect, useRef, useState } from "react";
|
||||
import { OlRoundStateButton, OlStateButton, OlLockStateButton } from "../components/olstatebutton";
|
||||
import { faSkull, faCamera, faFlag, faLink, faUnlink, faBars, faVolumeHigh } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { EventsConsumer } from "../../eventscontext";
|
||||
import { StateConsumer } from "../../statecontext";
|
||||
import { OlDropdownItem, OlDropdown } from "../components/oldropdown";
|
||||
import { OlLabelToggle } from "../components/ollabeltoggle";
|
||||
@ -45,192 +44,192 @@ export function Header() {
|
||||
return (
|
||||
<StateConsumer>
|
||||
{(appState) => (
|
||||
<EventsConsumer>
|
||||
{() => (
|
||||
<nav
|
||||
<nav
|
||||
className={`
|
||||
z-10 flex w-full gap-4 border-gray-200 bg-gray-300 px-3
|
||||
drop-shadow-md align-center
|
||||
dark:border-gray-800 dark:bg-olympus-900
|
||||
`}
|
||||
>
|
||||
<img
|
||||
src="images/icon.png"
|
||||
className={`my-auto h-10 w-10 rounded-md p-0`}
|
||||
></img>
|
||||
{!scrolledLeft && (
|
||||
<FaChevronLeft
|
||||
className={`
|
||||
z-10 flex w-full gap-4 border-gray-200 bg-gray-300 px-3
|
||||
drop-shadow-md align-center
|
||||
dark:border-gray-800 dark:bg-olympus-900
|
||||
absolute left-14 h-full w-6 rounded-lg px-2 py-3.5 text-gray-200
|
||||
dark:bg-olympus-900
|
||||
`}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`
|
||||
my-2 flex w-full items-center gap-3 overflow-x-scroll no-scrollbar
|
||||
`}
|
||||
onScroll={(ev) => onScroll(ev.target)}
|
||||
ref={scrollRef}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
mr-auto hidden flex-none flex-row items-center justify-start
|
||||
gap-6
|
||||
lg:flex
|
||||
`}
|
||||
>
|
||||
<img src="images/icon.png" className={`
|
||||
my-auto h-10 w-10 rounded-md p-0
|
||||
`}></img>
|
||||
{!scrolledLeft && (
|
||||
<FaChevronLeft
|
||||
className={`
|
||||
absolute left-14 h-full w-6 rounded-lg px-2 py-3.5
|
||||
text-gray-200
|
||||
dark:bg-olympus-900
|
||||
`}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`
|
||||
my-2 flex w-full items-center gap-3 overflow-x-scroll
|
||||
no-scrollbar
|
||||
`}
|
||||
onScroll={(ev) => onScroll(ev.target)}
|
||||
ref={scrollRef}
|
||||
>
|
||||
<div className="flex flex-col items-start">
|
||||
<div
|
||||
className={`
|
||||
mr-auto hidden flex-none flex-row items-center justify-start
|
||||
gap-6
|
||||
lg:flex
|
||||
pt-1 text-xs text-gray-800
|
||||
dark:text-gray-400
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-col items-start">
|
||||
<div
|
||||
className={`
|
||||
pt-1 text-xs text-gray-800
|
||||
dark:text-gray-400
|
||||
`}
|
||||
>
|
||||
Connected to
|
||||
</div>
|
||||
<div
|
||||
className={`
|
||||
flex items-center justify-center gap-2 text-sm
|
||||
font-extrabold text-gray-800
|
||||
dark:text-gray-200
|
||||
`}
|
||||
>
|
||||
{IP}
|
||||
<FontAwesomeIcon
|
||||
icon={connectedToServer ? faLink : faUnlink}
|
||||
data-connected={connectedToServer}
|
||||
className={`
|
||||
py-auto text-green-400
|
||||
dark:text-red-500
|
||||
data-[connected='true']:dark:text-green-400
|
||||
`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
Connected to
|
||||
</div>
|
||||
<div
|
||||
className={`
|
||||
flex h-fit flex-row items-center justify-start gap-1
|
||||
flex items-center justify-center gap-2 text-sm
|
||||
font-extrabold text-gray-800
|
||||
dark:text-gray-200
|
||||
`}
|
||||
>
|
||||
<OlLockStateButton checked={!appState.mapOptions.protectDCSUnits} onClick={() => {getApp().getMap().setOption("protectDCSUnits", !appState.mapOptions.protectDCSUnits)}} tooltip="Lock/unlock protected units (from scripted mission)" />
|
||||
<OlRoundStateButton
|
||||
checked={audioEnabled}
|
||||
onClick={() => {
|
||||
audioEnabled ? getApp().getAudioManager().stop() : getApp().getAudioManager().start();
|
||||
setAudioEnabled(!audioEnabled);
|
||||
}}
|
||||
tooltip="Enable/disable audio and radio backend"
|
||||
icon={faVolumeHigh}
|
||||
{IP}
|
||||
<FontAwesomeIcon
|
||||
icon={connectedToServer ? faLink : faUnlink}
|
||||
data-connected={connectedToServer}
|
||||
className={`
|
||||
py-auto text-green-400
|
||||
dark:text-red-500
|
||||
data-[connected='true']:dark:text-green-400
|
||||
`}
|
||||
/>
|
||||
</div>
|
||||
<div className={`h-8 w-0 border-l-[2px] border-gray-700`}></div>
|
||||
<div
|
||||
className={`
|
||||
flex h-fit flex-row items-center justify-start gap-1
|
||||
`}
|
||||
>
|
||||
{Object.entries({
|
||||
human: olButtonsVisibilityHuman,
|
||||
olympus: olButtonsVisibilityOlympus,
|
||||
dcs: olButtonsVisibilityDcs,
|
||||
}).map((entry) => {
|
||||
return (
|
||||
<OlRoundStateButton
|
||||
key={entry[0]}
|
||||
onClick={() => {
|
||||
getApp().getMap().setHiddenType(entry[0], !appState.mapHiddenTypes[entry[0]]);
|
||||
}}
|
||||
checked={!appState.mapHiddenTypes[entry[0]]}
|
||||
icon={entry[1]}
|
||||
tooltip={"Hide/show " + entry[0] + " units"}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className={`h-8 w-0 border-l-[2px] border-gray-700`}></div>
|
||||
<div
|
||||
className={`
|
||||
flex h-fit flex-row items-center justify-start gap-1
|
||||
`}
|
||||
>
|
||||
<OlRoundStateButton
|
||||
onClick={() => getApp().getMap().setHiddenType("blue", !appState.mapHiddenTypes["blue"])}
|
||||
checked={!appState.mapHiddenTypes["blue"]}
|
||||
icon={faFlag}
|
||||
className={"!text-blue-500"}
|
||||
tooltip={"Hide/show blue units"}
|
||||
/>
|
||||
<OlRoundStateButton
|
||||
onClick={() => getApp().getMap().setHiddenType("red", !appState.mapHiddenTypes["red"])}
|
||||
checked={!appState.mapHiddenTypes["red"]}
|
||||
icon={faFlag}
|
||||
className={"!text-red-500"}
|
||||
tooltip={"Hide/show red units"}
|
||||
/>
|
||||
<OlRoundStateButton
|
||||
onClick={() => getApp().getMap().setHiddenType("neutral", !appState.mapHiddenTypes["neutral"])}
|
||||
checked={!appState.mapHiddenTypes["neutral"]}
|
||||
icon={faFlag}
|
||||
className={"!text-gray-500"}
|
||||
tooltip={"Hide/show neutral units"}
|
||||
/>
|
||||
</div>
|
||||
<div className={`h-8 w-0 border-l-[2px] border-gray-700`}></div>
|
||||
<div
|
||||
className={`
|
||||
flex h-fit flex-row items-center justify-start gap-1
|
||||
`}
|
||||
>
|
||||
{Object.entries({
|
||||
aircraft: olButtonsVisibilityAircraft,
|
||||
helicopter: olButtonsVisibilityHelicopter,
|
||||
"groundunit-sam": olButtonsVisibilityGroundunitSam,
|
||||
groundunit: olButtonsVisibilityGroundunit,
|
||||
navyunit: olButtonsVisibilityNavyunit,
|
||||
airbase: olButtonsVisibilityAirbase,
|
||||
dead: faSkull,
|
||||
}).map((entry) => {
|
||||
return (
|
||||
<OlRoundStateButton
|
||||
key={entry[0]}
|
||||
onClick={() => {
|
||||
getApp().getMap().setHiddenType(entry[0], !appState.mapHiddenTypes[entry[0]]);
|
||||
}}
|
||||
checked={!appState.mapHiddenTypes[entry[0]]}
|
||||
icon={entry[1]}
|
||||
tooltip={"Hide/show " + entry[0] + " units"}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<OlLabelToggle toggled={false} leftLabel={"Live"} rightLabel={"Map"} onClick={() => {}}></OlLabelToggle>
|
||||
<OlStateButton checked={false} icon={faCamera} onClick={() => {}} tooltip="Activate/deactivate camera plugin" />
|
||||
<OlDropdown label={appState.activeMapSource} className="w-60">
|
||||
{appState.mapSources.map((source) => {
|
||||
return (
|
||||
<OlDropdownItem key={source} onClick={() => getApp().getMap().setLayerName(source)}>
|
||||
<div className="truncate">{source}</div>
|
||||
</OlDropdownItem>
|
||||
);
|
||||
})}
|
||||
</OlDropdown>
|
||||
</div>
|
||||
{!scrolledRight && (
|
||||
<FaChevronRight
|
||||
className={`
|
||||
absolute right-0 h-full w-6 rounded-lg px-2 py-3.5
|
||||
text-gray-200
|
||||
dark:bg-olympus-900
|
||||
</div>
|
||||
<div
|
||||
className={`
|
||||
flex h-fit flex-row items-center justify-start gap-1
|
||||
`}
|
||||
/>
|
||||
)}
|
||||
</nav>
|
||||
>
|
||||
<OlLockStateButton
|
||||
checked={!appState.mapOptions.protectDCSUnits}
|
||||
onClick={() => {
|
||||
getApp().getMap().setOption("protectDCSUnits", !appState.mapOptions.protectDCSUnits);
|
||||
}}
|
||||
tooltip="Lock/unlock protected units (from scripted mission)"
|
||||
/>
|
||||
<OlRoundStateButton
|
||||
checked={audioEnabled}
|
||||
onClick={() => {
|
||||
audioEnabled ? getApp().getAudioManager().stop() : getApp().getAudioManager().start();
|
||||
setAudioEnabled(!audioEnabled);
|
||||
}}
|
||||
tooltip="Enable/disable audio and radio backend"
|
||||
icon={faVolumeHigh}
|
||||
/>
|
||||
</div>
|
||||
<div className={`h-8 w-0 border-l-[2px] border-gray-700`}></div>
|
||||
<div
|
||||
className={`
|
||||
flex h-fit flex-row items-center justify-start gap-1
|
||||
`}
|
||||
>
|
||||
{Object.entries({
|
||||
human: olButtonsVisibilityHuman,
|
||||
olympus: olButtonsVisibilityOlympus,
|
||||
dcs: olButtonsVisibilityDcs,
|
||||
}).map((entry) => {
|
||||
return (
|
||||
<OlRoundStateButton
|
||||
key={entry[0]}
|
||||
onClick={() => {
|
||||
getApp().getMap().setHiddenType(entry[0], !appState.mapHiddenTypes[entry[0]]);
|
||||
}}
|
||||
checked={!appState.mapHiddenTypes[entry[0]]}
|
||||
icon={entry[1]}
|
||||
tooltip={"Hide/show " + entry[0] + " units"}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className={`h-8 w-0 border-l-[2px] border-gray-700`}></div>
|
||||
<div
|
||||
className={`
|
||||
flex h-fit flex-row items-center justify-start gap-1
|
||||
`}
|
||||
>
|
||||
<OlRoundStateButton
|
||||
onClick={() => getApp().getMap().setHiddenType("blue", !appState.mapHiddenTypes["blue"])}
|
||||
checked={!appState.mapHiddenTypes["blue"]}
|
||||
icon={faFlag}
|
||||
className={"!text-blue-500"}
|
||||
tooltip={"Hide/show blue units"}
|
||||
/>
|
||||
<OlRoundStateButton
|
||||
onClick={() => getApp().getMap().setHiddenType("red", !appState.mapHiddenTypes["red"])}
|
||||
checked={!appState.mapHiddenTypes["red"]}
|
||||
icon={faFlag}
|
||||
className={"!text-red-500"}
|
||||
tooltip={"Hide/show red units"}
|
||||
/>
|
||||
<OlRoundStateButton
|
||||
onClick={() => getApp().getMap().setHiddenType("neutral", !appState.mapHiddenTypes["neutral"])}
|
||||
checked={!appState.mapHiddenTypes["neutral"]}
|
||||
icon={faFlag}
|
||||
className={"!text-gray-500"}
|
||||
tooltip={"Hide/show neutral units"}
|
||||
/>
|
||||
</div>
|
||||
<div className={`h-8 w-0 border-l-[2px] border-gray-700`}></div>
|
||||
<div
|
||||
className={`
|
||||
flex h-fit flex-row items-center justify-start gap-1
|
||||
`}
|
||||
>
|
||||
{Object.entries({
|
||||
aircraft: olButtonsVisibilityAircraft,
|
||||
helicopter: olButtonsVisibilityHelicopter,
|
||||
"groundunit-sam": olButtonsVisibilityGroundunitSam,
|
||||
groundunit: olButtonsVisibilityGroundunit,
|
||||
navyunit: olButtonsVisibilityNavyunit,
|
||||
airbase: olButtonsVisibilityAirbase,
|
||||
dead: faSkull,
|
||||
}).map((entry) => {
|
||||
return (
|
||||
<OlRoundStateButton
|
||||
key={entry[0]}
|
||||
onClick={() => {
|
||||
getApp().getMap().setHiddenType(entry[0], !appState.mapHiddenTypes[entry[0]]);
|
||||
}}
|
||||
checked={!appState.mapHiddenTypes[entry[0]]}
|
||||
icon={entry[1]}
|
||||
tooltip={"Hide/show " + entry[0] + " units"}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<OlLabelToggle toggled={false} leftLabel={"Live"} rightLabel={"Map"} onClick={() => {}}></OlLabelToggle>
|
||||
<OlStateButton checked={false} icon={faCamera} onClick={() => {}} tooltip="Activate/deactivate camera plugin" />
|
||||
<OlDropdown label={appState.activeMapSource} className="w-60">
|
||||
{appState.mapSources.map((source) => {
|
||||
return (
|
||||
<OlDropdownItem key={source} onClick={() => getApp().getMap().setLayerName(source)}>
|
||||
<div className="truncate">{source}</div>
|
||||
</OlDropdownItem>
|
||||
);
|
||||
})}
|
||||
</OlDropdown>
|
||||
</div>
|
||||
{!scrolledRight && (
|
||||
<FaChevronRight
|
||||
className={`
|
||||
absolute right-0 h-full w-6 rounded-lg px-2 py-3.5 text-gray-200
|
||||
dark:bg-olympus-900
|
||||
`}
|
||||
/>
|
||||
)}
|
||||
</EventsConsumer>
|
||||
</nav>
|
||||
)}
|
||||
</StateConsumer>
|
||||
);
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Menu } from "./components/menu";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { IDLE, SELECT_JTAC_ECHO, SELECT_JTAC_IP, SELECT_JTAC_TARGET } from "../../constants/constants";
|
||||
import { LatLng } from "leaflet";
|
||||
import { Unit } from "../../unit/unit";
|
||||
import { OlDropdown, OlDropdownItem } from "../components/oldropdown";
|
||||
@ -10,6 +9,7 @@ import { ConvertDDToDMS, latLngToMGRS, mToFt, zeroAppend } from "../../other/uti
|
||||
import { FaMousePointer } from "react-icons/fa";
|
||||
import { OlLocation } from "../components/ollocation";
|
||||
import { FaBullseye } from "react-icons/fa6";
|
||||
import { JTACSubState, OlympusState } from "../../constants/constants";
|
||||
|
||||
export function JTACMenu(props: { open: boolean; onClose: () => void; children?: JSX.Element | JSX.Element[] }) {
|
||||
const [referenceSystem, setReferenceSystem] = useState("LatLngDec");
|
||||
@ -17,7 +17,6 @@ export function JTACMenu(props: { open: boolean; onClose: () => void; children?:
|
||||
const [targetUnit, setTargetUnit] = useState(null as null | Unit);
|
||||
const [IP, setIP] = useState(null as null | LatLng);
|
||||
const [ECHO, setECHO] = useState(null as null | LatLng);
|
||||
const [mapState, setMapState] = useState(IDLE);
|
||||
const [callsign, setCallsign] = useState("Eyeball");
|
||||
const [humanUnits, setHumanUnits] = useState([] as Unit[]);
|
||||
const [attacker, setAttacker] = useState(null as null | Unit);
|
||||
@ -40,9 +39,8 @@ export function JTACMenu(props: { open: boolean; onClose: () => void; children?:
|
||||
setIP(ev.detail);
|
||||
});
|
||||
|
||||
document.addEventListener("mapStateChanged", (ev: CustomEventInit) => {
|
||||
setMapState(ev.detail);
|
||||
if (ev.detail === SELECT_JTAC_TARGET) {
|
||||
document.addEventListener("appStateChanged", (ev: CustomEventInit) => {
|
||||
if (ev.detail.subState === JTACSubState.SELECT_TARGET) {
|
||||
setTargetLocation(null);
|
||||
setTargetUnit(null);
|
||||
}
|
||||
@ -124,7 +122,7 @@ export function JTACMenu(props: { open: boolean; onClose: () => void; children?:
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
getApp().getMap().setState(SELECT_JTAC_ECHO);
|
||||
getApp().setState(OlympusState.JTAC, JTACSubState.SELECT_ECHO_POINT);
|
||||
}}
|
||||
className={`
|
||||
rounded-r-md bg-blue-700 px-3 py-2.5 text-md font-medium
|
||||
@ -161,7 +159,7 @@ export function JTACMenu(props: { open: boolean; onClose: () => void; children?:
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
getApp().getMap().setState(SELECT_JTAC_IP);
|
||||
getApp().setState(OlympusState.JTAC, JTACSubState.SELECT_IP);
|
||||
}}
|
||||
className={`
|
||||
rounded-r-lg bg-blue-700 px-3 py-2.5 text-md font-medium
|
||||
@ -198,7 +196,7 @@ export function JTACMenu(props: { open: boolean; onClose: () => void; children?:
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
getApp().getMap().setState(SELECT_JTAC_TARGET);
|
||||
getApp().setState(OlympusState.JTAC, JTACSubState.SELECT_TARGET);
|
||||
}}
|
||||
className={`
|
||||
rounded-r-lg bg-blue-700 px-3 py-2.5 text-md font-medium
|
||||
|
||||
@ -1,94 +1,98 @@
|
||||
import React from "react";
|
||||
import { OlStateButton } from "../components/olstatebutton";
|
||||
import { faGamepad, faRuler, faPencil, faEllipsisV, faCog, faQuestionCircle, faPlusSquare, faMagnifyingGlass, faRadio, faVolumeHigh, faJ } from "@fortawesome/free-solid-svg-icons";
|
||||
import { EventsConsumer } from "../../eventscontext";
|
||||
import {
|
||||
faGamepad,
|
||||
faRuler,
|
||||
faPencil,
|
||||
faEllipsisV,
|
||||
faCog,
|
||||
faQuestionCircle,
|
||||
faPlusSquare,
|
||||
faMagnifyingGlass,
|
||||
faVolumeHigh,
|
||||
faJ,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { StateConsumer } from "../../statecontext";
|
||||
import { CONTEXT_ACTION, IDLE } from "../../constants/constants";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { OlympusState } from "../../constants/constants";
|
||||
|
||||
export function SideBar() {
|
||||
return (
|
||||
<StateConsumer>
|
||||
{(appState) => (
|
||||
<EventsConsumer>
|
||||
{(events) => (
|
||||
<nav
|
||||
className={`
|
||||
z-20 flex h-full flex-col bg-gray-300
|
||||
dark:bg-olympus-900
|
||||
`}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
<nav
|
||||
className={`
|
||||
z-20 flex h-full flex-col bg-gray-300
|
||||
dark:bg-olympus-900
|
||||
`}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
w-16 flex-1 flex-wrap items-center justify-center p-4
|
||||
`}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col items-center justify-center gap-2.5
|
||||
`}
|
||||
>
|
||||
<OlStateButton
|
||||
onClick={events.toggleMainMenuVisible}
|
||||
checked={appState.mainMenuVisible}
|
||||
icon={faEllipsisV}
|
||||
tooltip="Hide/show main menu"
|
||||
></OlStateButton>
|
||||
<OlStateButton
|
||||
onClick={events.toggleSpawnMenuVisible}
|
||||
checked={appState.spawnMenuVisible}
|
||||
icon={faPlusSquare}
|
||||
tooltip="Hide/show unit spawn menu"
|
||||
></OlStateButton>
|
||||
<OlStateButton
|
||||
onClick={events.toggleUnitControlMenuVisible}
|
||||
checked={appState.unitControlMenuVisible}
|
||||
icon={appState.mapState !== CONTEXT_ACTION? faMagnifyingGlass: faGamepad}
|
||||
tooltip="Hide/show selection tool and unit control menu"
|
||||
></OlStateButton>
|
||||
<OlStateButton onClick={events.toggleMeasureMenuVisible} checked={appState.measureMenuVisible} icon={faRuler} tooltip="NOT IMPLEMENTED"></OlStateButton>
|
||||
<OlStateButton
|
||||
onClick={events.toggleDrawingMenuVisible}
|
||||
checked={appState.drawingMenuVisible}
|
||||
icon={faPencil}
|
||||
tooltip="Hide/show drawing menu"
|
||||
></OlStateButton>
|
||||
<OlStateButton
|
||||
onClick={events.toggleAudioMenuVisible}
|
||||
checked={appState.audioMenuVisible}
|
||||
icon={faVolumeHigh}
|
||||
tooltip="Hide/show audio menu"
|
||||
></OlStateButton>
|
||||
<OlStateButton
|
||||
onClick={events.toggleJTACMenuVisible}
|
||||
checked={appState.JTACMenuVisible}
|
||||
icon={faJ}
|
||||
tooltip="Hide/show JTAC menu"
|
||||
></OlStateButton>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-16 flex-wrap content-end justify-center p-4">
|
||||
<div
|
||||
className={`
|
||||
>
|
||||
<OlStateButton
|
||||
onClick={() => {getApp().setState(appState.appState !== OlympusState.MAIN_MENU? OlympusState.MAIN_MENU: OlympusState.IDLE)}}
|
||||
checked={appState.appState === OlympusState.MAIN_MENU}
|
||||
icon={faEllipsisV}
|
||||
tooltip="Hide/show main menu"
|
||||
></OlStateButton>
|
||||
<OlStateButton
|
||||
onClick={() => {getApp().setState(appState.appState !== OlympusState.SPAWN? OlympusState.SPAWN: OlympusState.IDLE)}}
|
||||
checked={appState.appState === OlympusState.SPAWN}
|
||||
icon={faPlusSquare}
|
||||
tooltip="Hide/show unit spawn menu"
|
||||
></OlStateButton>
|
||||
<OlStateButton
|
||||
onClick={() => {getApp().setState(appState.appState !== OlympusState.UNIT_CONTROL? OlympusState.UNIT_CONTROL: OlympusState.IDLE)}}
|
||||
checked={appState.appState === OlympusState.UNIT_CONTROL}
|
||||
icon={faGamepad}
|
||||
tooltip="Hide/show selection tool and unit control menu"
|
||||
></OlStateButton>
|
||||
<OlStateButton
|
||||
onClick={() => {getApp().setState(appState.appState !== OlympusState.DRAW? OlympusState.DRAW: OlympusState.IDLE)}}
|
||||
checked={appState.appState === OlympusState.DRAW}
|
||||
icon={faPencil}
|
||||
tooltip="Hide/show drawing menu"
|
||||
></OlStateButton>
|
||||
<OlStateButton
|
||||
onClick={() => {getApp().setState(appState.appState !== OlympusState.AUDIO? OlympusState.AUDIO: OlympusState.IDLE)}}
|
||||
checked={appState.appState === OlympusState.AUDIO}
|
||||
icon={faVolumeHigh}
|
||||
tooltip="Hide/show audio menu"
|
||||
></OlStateButton>
|
||||
<OlStateButton
|
||||
onClick={() => {getApp().setState(appState.appState !== OlympusState.JTAC? OlympusState.JTAC: OlympusState.IDLE)}}
|
||||
checked={appState.appState === OlympusState.JTAC}
|
||||
icon={faJ} tooltip="Hide/show JTAC menu"></OlStateButton>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-16 flex-wrap content-end justify-center p-4">
|
||||
<div
|
||||
className={`
|
||||
flex flex-col items-center justify-center gap-2.5
|
||||
`}
|
||||
>
|
||||
<OlStateButton
|
||||
onClick={() => window.open("https://github.com/Pax1601/DCSOlympus/wiki")}
|
||||
checked={false}
|
||||
icon={faQuestionCircle}
|
||||
tooltip="Open user guide on separate window"
|
||||
></OlStateButton>
|
||||
<OlStateButton
|
||||
onClick={events.toggleOptionsMenuVisible}
|
||||
checked={appState.optionsMenuVisible}
|
||||
icon={faCog}
|
||||
tooltip="Hide/show settings menu"
|
||||
></OlStateButton>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
)}
|
||||
</EventsConsumer>
|
||||
>
|
||||
<OlStateButton
|
||||
onClick={() => window.open("https://github.com/Pax1601/DCSOlympus/wiki")}
|
||||
checked={false}
|
||||
icon={faQuestionCircle}
|
||||
tooltip="Open user guide on separate window"
|
||||
></OlStateButton>
|
||||
<OlStateButton
|
||||
onClick={() => {getApp().setState(appState.appState !== OlympusState.OPTIONS? OlympusState.OPTIONS: OlympusState.IDLE)}}
|
||||
checked={appState.appState === OlympusState.OPTIONS}
|
||||
icon={faCog}
|
||||
tooltip="Hide/show settings menu"
|
||||
></OlStateButton>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
)}
|
||||
</StateConsumer>
|
||||
);
|
||||
|
||||
@ -13,29 +13,68 @@ import {
|
||||
olButtonsVisibilityHelicopter,
|
||||
olButtonsVisibilityNavyunit,
|
||||
} from "../components/olicons";
|
||||
import { IDLE, SPAWN_EFFECT, SPAWN_UNIT } from "../../constants/constants";
|
||||
import { getUnitsByLabel } from "../../other/utils";
|
||||
import { faExplosion, faSmog } from "@fortawesome/free-solid-svg-icons";
|
||||
import { OlEffectListEntry } from "../components/oleffectlistentry";
|
||||
import { EffectSpawnMenu } from "./effectspawnmenu";
|
||||
import { NO_SUBSTATE, OlympusState, SpawnSubState } from "../../constants/constants";
|
||||
import { aircraftDatabase } from "../../unit/databases/aircraftdatabase";
|
||||
import { navyUnitDatabase } from "../../unit/databases/navyunitdatabase";
|
||||
import { filterBlueprintsByLabel } from "../../other/utils";
|
||||
import { helicopterDatabase } from "../../unit/databases/helicopterdatabase";
|
||||
import { groundUnitDatabase } from "../../unit/databases/groundunitdatabase";
|
||||
|
||||
enum Accordion {
|
||||
NONE,
|
||||
AIRCRAFT,
|
||||
HELICOPTER,
|
||||
SAM,
|
||||
AAA,
|
||||
GROUND_UNIT,
|
||||
NAVY_UNIT,
|
||||
EFFECT,
|
||||
}
|
||||
|
||||
export function SpawnMenu(props: { open: boolean; onClose: () => void; children?: JSX.Element | JSX.Element[] }) {
|
||||
const [openAccordion, setOpenAccordion] = useState(Accordion.NONE);
|
||||
const [blueprint, setBlueprint] = useState(null as null | UnitBlueprint);
|
||||
const [effect, setEffect] = useState(null as null | string);
|
||||
const [filterString, setFilterString] = useState("");
|
||||
const [selectedRole, setSelectedRole] = useState(null as null | string);
|
||||
const [selectedType, setSelectedType] = useState(null as null | string);
|
||||
|
||||
const [filteredAircraft, filteredHelicopters, filteredAirDefense, filteredGroundUnits, filteredNavyUnits] = getUnitsByLabel(filterString);
|
||||
const filteredAircraft = getApp()
|
||||
? filterBlueprintsByLabel(selectedRole ? aircraftDatabase.getByRole(selectedRole) : Object.values(aircraftDatabase.getBlueprints()), filterString)
|
||||
: ({} as { [key: string]: UnitBlueprint });
|
||||
const filteredHelicopters = getApp()
|
||||
? filterBlueprintsByLabel(selectedRole ? helicopterDatabase.getByRole(selectedRole) : Object.values(helicopterDatabase.getBlueprints()), filterString)
|
||||
: ({} as { [key: string]: UnitBlueprint });
|
||||
const filteredSAMs = getApp() ? filterBlueprintsByLabel(groundUnitDatabase.getByType("SAM Site"), filterString) : ({} as { [key: string]: UnitBlueprint });
|
||||
const filteredAAA = getApp() ? filterBlueprintsByLabel(groundUnitDatabase.getByType("AAA"), filterString) : ({} as { [key: string]: UnitBlueprint });
|
||||
const filteredGroundUnits = getApp()
|
||||
? filterBlueprintsByLabel(selectedType ? groundUnitDatabase.getByType(selectedType) : Object.values(groundUnitDatabase.getBlueprints()), filterString)
|
||||
: ({} as { [key: string]: UnitBlueprint });
|
||||
const filteredNavyUnits = getApp()
|
||||
? filterBlueprintsByLabel(selectedType ? navyUnitDatabase.getByType(selectedType) : Object.values(navyUnitDatabase.getBlueprints()), filterString)
|
||||
: ({} as { [key: string]: UnitBlueprint });
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.open && getApp()) {
|
||||
if (getApp().getMap().getState() === SPAWN_UNIT) getApp().getMap().setState(IDLE);
|
||||
else if (getApp().getMap().getState() === SPAWN_EFFECT) getApp().getMap().setState(IDLE);
|
||||
|
||||
if (!props.open) {
|
||||
if (blueprint !== null) setBlueprint(null);
|
||||
if (effect !== null) setEffect(null);
|
||||
if (filterString !== "") setFilterString("");
|
||||
if (openAccordion !== Accordion.NONE) setOpenAccordion(Accordion.NONE);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("appStateChanged", (ev: CustomEventInit) => {
|
||||
if (ev.detail.subState === NO_SUBSTATE) {
|
||||
setBlueprint(null);
|
||||
setEffect(null);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Menu
|
||||
{...props}
|
||||
@ -43,7 +82,7 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
|
||||
showBackButton={blueprint !== null || effect !== null}
|
||||
canBeHidden={true}
|
||||
onBack={() => {
|
||||
getApp().getMap().setState(IDLE);
|
||||
getApp().setState(OlympusState.SPAWN);
|
||||
setBlueprint(null);
|
||||
setEffect(null);
|
||||
}}
|
||||
@ -52,10 +91,43 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
|
||||
{blueprint === null && effect === null && (
|
||||
<div className="p-5">
|
||||
<OlSearchBar onChange={(value) => setFilterString(value)} text={filterString} />
|
||||
<OlAccordion title={`Aircraft`}>
|
||||
<OlAccordion
|
||||
title={`Aircraft`}
|
||||
open={openAccordion == Accordion.AIRCRAFT}
|
||||
onClick={() => {
|
||||
setOpenAccordion(openAccordion === Accordion.AIRCRAFT ? Accordion.NONE : Accordion.AIRCRAFT);
|
||||
setSelectedRole(null);
|
||||
setSelectedType(null);
|
||||
}}
|
||||
>
|
||||
<div className="mb-2 flex flex-wrap gap-1">
|
||||
{aircraftDatabase
|
||||
.getRoles()
|
||||
.sort()
|
||||
.map((role) => {
|
||||
return (
|
||||
<div
|
||||
key={role}
|
||||
data-selected={selectedRole === role}
|
||||
className={`
|
||||
cursor-pointer rounded-full bg-olympus-800 px-2 py-0.5
|
||||
text-xs font-bold text-olympus-50
|
||||
data-[selected='true']:bg-blue-500
|
||||
data-[selected='true']:text-gray-200
|
||||
`}
|
||||
onClick={() => {
|
||||
selectedRole === role ? setSelectedRole(null) : setSelectedRole(role);
|
||||
}}
|
||||
>
|
||||
{role}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className={`
|
||||
flex max-h-80 flex-col gap-1 overflow-y-scroll no-scrollbar
|
||||
flex max-h-[450px] flex-col gap-1 overflow-y-scroll
|
||||
no-scrollbar
|
||||
`}
|
||||
>
|
||||
{Object.entries(filteredAircraft).map((entry) => {
|
||||
@ -63,10 +135,43 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title={`Helicopters`}>
|
||||
<OlAccordion
|
||||
title={`Helicopters`}
|
||||
open={openAccordion == Accordion.HELICOPTER}
|
||||
onClick={() => {
|
||||
setOpenAccordion(openAccordion === Accordion.HELICOPTER ? Accordion.NONE : Accordion.HELICOPTER);
|
||||
setSelectedRole(null);
|
||||
setSelectedType(null);
|
||||
}}
|
||||
>
|
||||
<div className="mb-2 flex flex-wrap gap-1">
|
||||
{helicopterDatabase
|
||||
.getRoles()
|
||||
.sort()
|
||||
.map((role) => {
|
||||
return (
|
||||
<div
|
||||
key={role}
|
||||
data-selected={selectedRole === role}
|
||||
className={`
|
||||
cursor-pointer rounded-full bg-olympus-800 px-2 py-0.5
|
||||
text-xs font-bold text-olympus-50
|
||||
data-[selected='true']:bg-blue-500
|
||||
data-[selected='true']:text-gray-200
|
||||
`}
|
||||
onClick={() => {
|
||||
selectedRole === role ? setSelectedRole(null) : setSelectedRole(role);
|
||||
}}
|
||||
>
|
||||
{role}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className={`
|
||||
flex max-h-80 flex-col gap-1 overflow-y-scroll no-scrollbar
|
||||
flex max-h-[450px] flex-col gap-1 overflow-y-scroll
|
||||
no-scrollbar
|
||||
`}
|
||||
>
|
||||
{Object.entries(filteredHelicopters).map((entry) => {
|
||||
@ -74,21 +179,86 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title={`SAM & AAA`}>
|
||||
<OlAccordion
|
||||
title={`Surfact to Air Missiles (SAM sites)`}
|
||||
open={openAccordion == Accordion.SAM}
|
||||
onClick={() => {
|
||||
setOpenAccordion(openAccordion === Accordion.SAM ? Accordion.NONE : Accordion.SAM);
|
||||
setSelectedRole(null);
|
||||
setSelectedType(null);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
flex max-h-80 flex-col gap-1 overflow-y-scroll no-scrollbar
|
||||
flex max-h-[450px] flex-col gap-1 overflow-y-scroll
|
||||
no-scrollbar
|
||||
`}
|
||||
>
|
||||
{Object.entries(filteredAirDefense).map((entry) => {
|
||||
{Object.entries(filteredSAMs).map((entry) => {
|
||||
return <OlUnitListEntry key={entry[0]} icon={olButtonsVisibilityGroundunitSam} blueprint={entry[1]} onClick={() => setBlueprint(entry[1])} />;
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title={`Ground Units`}>
|
||||
<OlAccordion
|
||||
title={`Anti Aircraft Artillery (AAA)`}
|
||||
open={openAccordion == Accordion.AAA}
|
||||
onClick={() => {
|
||||
setOpenAccordion(openAccordion === Accordion.AAA ? Accordion.NONE : Accordion.AAA);
|
||||
setSelectedRole(null);
|
||||
setSelectedType(null);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
flex max-h-80 flex-col gap-1 overflow-y-scroll no-scrollbar
|
||||
flex max-h-[450px] flex-col gap-1 overflow-y-scroll
|
||||
no-scrollbar
|
||||
`}
|
||||
>
|
||||
{Object.entries(filteredAAA).map((entry) => {
|
||||
return <OlUnitListEntry key={entry[0]} icon={olButtonsVisibilityGroundunitSam} blueprint={entry[1]} onClick={() => setBlueprint(entry[1])} />;
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion
|
||||
title={`Ground Units`}
|
||||
open={openAccordion == Accordion.GROUND_UNIT}
|
||||
onClick={() => {
|
||||
setOpenAccordion(openAccordion === Accordion.GROUND_UNIT ? Accordion.NONE : Accordion.GROUND_UNIT);
|
||||
setSelectedRole(null);
|
||||
setSelectedType(null);
|
||||
}}
|
||||
>
|
||||
<div className="mb-2 flex flex-wrap gap-1">
|
||||
{groundUnitDatabase
|
||||
.getTypes()
|
||||
.sort()
|
||||
.filter((type) => {
|
||||
return type !== "AAA" && type !== "SAM Site";
|
||||
})
|
||||
.map((type) => {
|
||||
return (
|
||||
<div
|
||||
key={type}
|
||||
data-selected={selectedType === type}
|
||||
className={`
|
||||
cursor-pointer rounded-full bg-olympus-800 px-2 py-0.5
|
||||
text-xs font-bold text-olympus-50
|
||||
data-[selected='true']:bg-blue-500
|
||||
data-[selected='true']:text-gray-200
|
||||
`}
|
||||
onClick={() => {
|
||||
selectedType === type ? setSelectedType(null) : setSelectedType(type);
|
||||
}}
|
||||
>
|
||||
{type}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className={`
|
||||
flex max-h-[450px] flex-col gap-1 overflow-y-scroll
|
||||
no-scrollbar
|
||||
`}
|
||||
>
|
||||
{Object.entries(filteredGroundUnits).map((entry) => {
|
||||
@ -96,10 +266,43 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title={`Ships and submarines`}>
|
||||
<OlAccordion
|
||||
title={`Ships and submarines`}
|
||||
open={openAccordion == Accordion.NAVY_UNIT}
|
||||
onClick={() => {
|
||||
setOpenAccordion(openAccordion === Accordion.NAVY_UNIT ? Accordion.NONE : Accordion.NAVY_UNIT);
|
||||
setSelectedRole(null);
|
||||
setSelectedType(null);
|
||||
}}
|
||||
>
|
||||
<div className="mb-2 flex flex-wrap gap-1">
|
||||
{navyUnitDatabase
|
||||
.getTypes()
|
||||
.sort()
|
||||
.map((type) => {
|
||||
return (
|
||||
<div
|
||||
key={type}
|
||||
data-selected={selectedType === type}
|
||||
className={`
|
||||
cursor-pointer rounded-full bg-olympus-800 px-2 py-0.5
|
||||
text-xs font-bold text-olympus-50
|
||||
data-[selected='true']:bg-blue-500
|
||||
data-[selected='true']:text-gray-200
|
||||
`}
|
||||
onClick={() => {
|
||||
selectedType === type ? setSelectedType(null) : setSelectedType(type);
|
||||
}}
|
||||
>
|
||||
{type}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className={`
|
||||
flex max-h-80 flex-col gap-1 overflow-y-scroll no-scrollbar
|
||||
flex max-h-[450px] flex-col gap-1 overflow-y-scroll
|
||||
no-scrollbar
|
||||
`}
|
||||
>
|
||||
{Object.entries(filteredNavyUnits).map((entry) => {
|
||||
@ -107,10 +310,19 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
|
||||
})}
|
||||
</div>
|
||||
</OlAccordion>
|
||||
<OlAccordion title="Effects (smokes, explosions etc)">
|
||||
<OlAccordion
|
||||
title="Effects (smokes, explosions etc)"
|
||||
open={openAccordion == Accordion.EFFECT}
|
||||
onClick={() => {
|
||||
setOpenAccordion(openAccordion === Accordion.EFFECT ? Accordion.NONE : Accordion.EFFECT);
|
||||
setSelectedRole(null);
|
||||
setSelectedType(null);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
flex max-h-80 flex-col gap-1 overflow-y-scroll no-scrollbar
|
||||
flex max-h-[450px] flex-col gap-1 overflow-y-scroll
|
||||
no-scrollbar
|
||||
`}
|
||||
>
|
||||
<OlEffectListEntry
|
||||
|
||||
@ -38,7 +38,7 @@ import {
|
||||
olButtonsVisibilityOlympus,
|
||||
} from "../components/olicons";
|
||||
import { Coalition } from "../../types/types";
|
||||
import { ftToM, getUnitsByLabel, knotsToMs, mToFt, msToKnots } from "../../other/utils";
|
||||
import { ftToM, knotsToMs, mToFt, msToKnots } from "../../other/utils";
|
||||
import { FaCog, FaGasPump, FaSignal, FaTag } from "react-icons/fa";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { OlSearchBar } from "../components/olsearchbar";
|
||||
@ -236,7 +236,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
|
||||
|
||||
const selectedCategories = getApp()?.getUnitsManager()?.getSelectedUnitsCategories() ?? [];
|
||||
|
||||
const [filteredAircraft, filteredHelicopters, filteredAirDefense, filteredGroundUnits, filteredNavyUnits] = getUnitsByLabel(filterString);
|
||||
const [filteredAircraft, filteredHelicopters, filteredAirDefense, filteredGroundUnits, filteredNavyUnits] = [{}, {}, {}, {}, {}] // TODOgetUnitsByLabel(filterString);
|
||||
|
||||
const mergedFilteredUnits = Object.assign({}, filteredAircraft, filteredHelicopters, filteredAirDefense, filteredGroundUnits, filteredNavyUnits) as {
|
||||
[key: string]: UnitBlueprint;
|
||||
|
||||
@ -4,9 +4,10 @@ import { ContextActionSet } from "../../unit/contextactionset";
|
||||
import { OlStateButton } from "../components/olstatebutton";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { ContextAction } from "../../unit/contextaction";
|
||||
import { CONTEXT_ACTION, CONTEXT_ACTION_COLORS } from "../../constants/constants";
|
||||
import { CONTEXT_ACTION_COLORS } from "../../constants/constants";
|
||||
import { FaInfoCircle } from "react-icons/fa";
|
||||
import { FaChevronLeft, FaChevronRight } from "react-icons/fa6";
|
||||
import { OlympusState } from "../../constants/constants";
|
||||
|
||||
export function UnitMouseControlBar(props: {}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
@ -41,8 +42,8 @@ export function UnitMouseControlBar(props: {}) {
|
||||
});
|
||||
|
||||
/* Deselect the context action when exiting state */
|
||||
document.addEventListener("mapStateChanged", (ev) => {
|
||||
setOpen((ev as CustomEvent).detail === CONTEXT_ACTION);
|
||||
document.addEventListener("appStateChanged", (ev) => {
|
||||
setOpen((ev as CustomEvent).detail.state === OlympusState.UNIT_CONTROL);
|
||||
});
|
||||
}, []);
|
||||
|
||||
@ -123,16 +124,12 @@ export function UnitMouseControlBar(props: {}) {
|
||||
} else {
|
||||
if (activeContextAction !== contextAction) {
|
||||
setActiveContextAction(contextAction);
|
||||
getApp().getMap().setState(CONTEXT_ACTION, {
|
||||
contextAction: contextAction,
|
||||
defaultContextAction: contextActionsSet.getDefaultContextAction(),
|
||||
});
|
||||
getApp().getMap().setContextAction(contextAction);
|
||||
getApp().getMap().setDefaultContextAction(contextActionsSet.getDefaultContextAction());
|
||||
} else {
|
||||
setActiveContextAction(null);
|
||||
getApp().getMap().setState(CONTEXT_ACTION, {
|
||||
contextAction: null,
|
||||
defaultContextAction: contextActionsSet.getDefaultContextAction(),
|
||||
});
|
||||
getApp().getMap().setContextAction(null);
|
||||
getApp().getMap().setDefaultContextAction(null);
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
@ -8,10 +8,10 @@ import { OlDropdownItem, OlDropdown } from "../components/oldropdown";
|
||||
import { LoadoutBlueprint, UnitBlueprint } from "../../interfaces";
|
||||
import { Coalition } from "../../types/types";
|
||||
import { getApp } from "../../olympusapp";
|
||||
import { IDLE, SPAWN_UNIT } from "../../constants/constants";
|
||||
import { ftToM, getUnitCategoryByBlueprint } from "../../other/utils";
|
||||
import { LatLng } from "leaflet";
|
||||
import { Airbase } from "../../mission/airbase";
|
||||
import { OlympusState, SpawnSubState } from "../../constants/constants";
|
||||
|
||||
export function UnitSpawnMenu(props: { blueprint: UnitBlueprint; spawnAtLocation: boolean; airbase?: Airbase | null; coalition?: Coalition }) {
|
||||
/* Compute the min and max values depending on the unit type */
|
||||
@ -38,25 +38,24 @@ export function UnitSpawnMenu(props: { blueprint: UnitBlueprint; spawnAtLocation
|
||||
if (props.blueprint !== null) {
|
||||
getApp()
|
||||
?.getMap()
|
||||
?.setState(SPAWN_UNIT, {
|
||||
spawnRequestTable: {
|
||||
category: getUnitCategoryByBlueprint(props.blueprint),
|
||||
unit: {
|
||||
unitType: props.blueprint.name,
|
||||
location: new LatLng(0, 0), // This will be filled when the user clicks on the map to spawn the unit
|
||||
skill: "High",
|
||||
liveryID: "",
|
||||
altitude: ftToM(spawnAltitude),
|
||||
loadout:
|
||||
props.blueprint.loadouts?.find((loadout) => {
|
||||
return loadout.name === spawnLoadoutName;
|
||||
})?.code ?? "",
|
||||
},
|
||||
coalition: spawnCoalition,
|
||||
?.setSpawnRequestTable({
|
||||
category: getUnitCategoryByBlueprint(props.blueprint),
|
||||
unit: {
|
||||
unitType: props.blueprint.name,
|
||||
location: new LatLng(0, 0), // This will be filled when the user clicks on the map to spawn the unit
|
||||
skill: "High",
|
||||
liveryID: "",
|
||||
altitude: ftToM(spawnAltitude),
|
||||
loadout:
|
||||
props.blueprint.loadouts?.find((loadout) => {
|
||||
return loadout.name === spawnLoadoutName;
|
||||
})?.code ?? "",
|
||||
},
|
||||
coalition: spawnCoalition,
|
||||
});
|
||||
getApp().setState(OlympusState.SPAWN, SpawnSubState.SPAWN_UNIT)
|
||||
} else {
|
||||
if (getApp().getMap().getState() === SPAWN_UNIT) getApp().getMap().setState(IDLE);
|
||||
if (getApp().getState() === OlympusState.SPAWN) getApp().setState(OlympusState.IDLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import "./ui.css";
|
||||
|
||||
import { EventsProvider } from "../eventscontext";
|
||||
import { StateProvider } from "../statecontext";
|
||||
|
||||
import { Header } from "./panels/header";
|
||||
@ -11,7 +10,7 @@ import { MainMenu } from "./panels/mainmenu";
|
||||
import { SideBar } from "./panels/sidebar";
|
||||
import { OptionsMenu } from "./panels/optionsmenu";
|
||||
import { MapHiddenTypes, MapOptions } from "../types/types";
|
||||
import { BLUE_COMMANDER, CONTEXT_ACTION, GAME_MASTER, IDLE, MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS, RED_COMMANDER } from "../constants/constants";
|
||||
import { BLUE_COMMANDER, GAME_MASTER, MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS, NO_SUBSTATE, OlympusEvent, OlympusState, OlympusSubState, RED_COMMANDER, UnitControlSubState } from "../constants/constants";
|
||||
import { getApp, setupApp } from "../olympusapp";
|
||||
import { LoginModal } from "./modals/login";
|
||||
import { sha256 } from "js-sha256";
|
||||
@ -42,35 +41,34 @@ export type OlympusUIState = {
|
||||
};
|
||||
|
||||
export function UI() {
|
||||
const [loginModalVisible, setLoginModalVisible] = useState(true);
|
||||
const [mainMenuVisible, setMainMenuVisible] = useState(false);
|
||||
const [spawnMenuVisible, setSpawnMenuVisible] = useState(false);
|
||||
const [unitControlMenuVisible, setUnitControlMenuVisible] = useState(false);
|
||||
const [measureMenuVisible, setMeasureMenuVisible] = useState(false);
|
||||
const [drawingMenuVisible, setDrawingMenuVisible] = useState(false);
|
||||
const [audioMenuVisible, setAudioMenuVisible] = useState(false);
|
||||
const [optionsMenuVisible, setOptionsMenuVisible] = useState(false);
|
||||
const [airbaseMenuVisible, setAirbaseMenuVisible] = useState(false);
|
||||
const [formationMenuVisible, setFormationMenuVisible] = useState(false);
|
||||
const [unitExplosionMenuVisible, setUnitExplosionMenuVisible] = useState(false);
|
||||
const [JTACMenuVisible, setJTACMenuVisible] = useState(false);
|
||||
const [appState, setAppState] = useState(OlympusState.NOT_INITIALIZED);
|
||||
const [appSubState, setAppSubState] = useState(NO_SUBSTATE as OlympusSubState);
|
||||
|
||||
const [mapHiddenTypes, setMapHiddenTypes] = useState(MAP_HIDDEN_TYPES_DEFAULTS);
|
||||
const [mapOptions, setMapOptions] = useState(MAP_OPTIONS_DEFAULTS);
|
||||
const [mapSources, setMapSources] = useState([] as string[]);
|
||||
const [activeMapSource, setActiveMapSource] = useState("");
|
||||
|
||||
const [checkingPassword, setCheckingPassword] = useState(false);
|
||||
const [loginError, setLoginError] = useState(false);
|
||||
const [commandMode, setCommandMode] = useState(null as null | string);
|
||||
const [mapSources, setMapSources] = useState([] as string[]);
|
||||
const [activeMapSource, setActiveMapSource] = useState("");
|
||||
const [mapState, setMapState] = useState(IDLE);
|
||||
|
||||
const [airbase, setAirbase] = useState(null as null | Airbase);
|
||||
|
||||
const [formationLeader, setFormationLeader] = useState(null as null | Unit);
|
||||
const [formationWingmen, setFormationWingmen] = useState(null as null | Unit[]);
|
||||
const [protectionPromptVisible, setProtectionPromptVisible] = useState(false);
|
||||
const [protectionCallback, setProtectionCallback] = useState(null as any);
|
||||
const [protectionUnits, setProtectionUnits] = useState([] as Unit[]);
|
||||
|
||||
const [unitExplosionUnits, setUnitExplosionUnits] = useState([] as Unit[]);
|
||||
|
||||
useEffect(() => {
|
||||
getApp()?.registerEventCallback(OlympusEvent.STATE_CHANGED, (state, subState) => {
|
||||
setAppState(state);
|
||||
setAppSubState(subState);
|
||||
})
|
||||
|
||||
document.addEventListener("hiddenTypesChanged", (ev) => {
|
||||
setMapHiddenTypes({ ...getApp().getMap().getHiddenTypes() });
|
||||
});
|
||||
@ -79,15 +77,6 @@ export function UI() {
|
||||
setMapOptions({ ...getApp().getMap().getOptions() });
|
||||
});
|
||||
|
||||
document.addEventListener("mapStateChanged", (ev: CustomEventInit) => {
|
||||
if (ev.detail === IDLE || ev.detail === CONTEXT_ACTION || mapState !== IDLE) {
|
||||
hideAllMenus();
|
||||
}
|
||||
|
||||
if (ev.detail === CONTEXT_ACTION && window.innerWidth > 1000) setUnitControlMenuVisible(true);
|
||||
setMapState(ev.detail);
|
||||
});
|
||||
|
||||
document.addEventListener("mapSourceChanged", (ev) => {
|
||||
var source = (ev as CustomEvent).detail;
|
||||
setActiveMapSource(source);
|
||||
@ -99,20 +88,7 @@ export function UI() {
|
||||
setMapSources(sources);
|
||||
setActiveMapSource(sources[0]);
|
||||
});
|
||||
|
||||
document.addEventListener("airbaseClick", (ev) => {
|
||||
hideAllMenus();
|
||||
getApp().getMap().setState(IDLE);
|
||||
setAirbase((ev as CustomEvent).detail);
|
||||
setAirbaseMenuVisible(true);
|
||||
});
|
||||
|
||||
document.addEventListener("showFormationMenu", (ev) => {
|
||||
setFormationMenuVisible(true);
|
||||
setFormationLeader((ev as CustomEvent).detail.leader);
|
||||
setFormationWingmen((ev as CustomEvent).detail.wingmen);
|
||||
});
|
||||
|
||||
|
||||
document.addEventListener("showProtectionPrompt", (ev: CustomEventInit) => {
|
||||
setProtectionPromptVisible(true);
|
||||
setProtectionCallback(() => {
|
||||
@ -120,27 +96,8 @@ export function UI() {
|
||||
});
|
||||
setProtectionUnits(ev.detail.units);
|
||||
});
|
||||
|
||||
document.addEventListener("showUnitExplosionMenu", (ev) => {
|
||||
setUnitExplosionMenuVisible(true);
|
||||
setUnitExplosionUnits((ev as CustomEvent).detail.units);
|
||||
});
|
||||
}, []);
|
||||
|
||||
function hideAllMenus() {
|
||||
setMainMenuVisible(false);
|
||||
setSpawnMenuVisible(false);
|
||||
setUnitControlMenuVisible(false);
|
||||
setMeasureMenuVisible(false);
|
||||
setDrawingMenuVisible(false);
|
||||
setOptionsMenuVisible(false);
|
||||
setAirbaseMenuVisible(false);
|
||||
setAudioMenuVisible(false);
|
||||
setFormationMenuVisible(false);
|
||||
setUnitExplosionMenuVisible(false);
|
||||
setJTACMenuVisible(false);
|
||||
}
|
||||
|
||||
function checkPassword(password: string) {
|
||||
setCheckingPassword(true);
|
||||
var hash = sha256.create();
|
||||
@ -167,7 +124,7 @@ export function UI() {
|
||||
function connect(username: string) {
|
||||
getApp().getServerManager().setUsername(username);
|
||||
getApp().getServerManager().startUpdate();
|
||||
setLoginModalVisible(false);
|
||||
getApp().setState(OlympusState.IDLE);
|
||||
}
|
||||
|
||||
return (
|
||||
@ -180,74 +137,18 @@ export function UI() {
|
||||
>
|
||||
<StateProvider
|
||||
value={{
|
||||
mainMenuVisible: mainMenuVisible,
|
||||
spawnMenuVisible: spawnMenuVisible,
|
||||
unitControlMenuVisible: unitControlMenuVisible,
|
||||
measureMenuVisible: measureMenuVisible,
|
||||
drawingMenuVisible: drawingMenuVisible,
|
||||
optionsMenuVisible: optionsMenuVisible,
|
||||
airbaseMenuVisible: airbaseMenuVisible,
|
||||
audioMenuVisible: audioMenuVisible,
|
||||
JTACMenuVisible: JTACMenuVisible,
|
||||
mapOptions: mapOptions,
|
||||
mapHiddenTypes: mapHiddenTypes,
|
||||
mapSources: mapSources,
|
||||
activeMapSource: activeMapSource,
|
||||
mapState: mapState,
|
||||
appState,
|
||||
appSubState,
|
||||
mapOptions,
|
||||
mapHiddenTypes,
|
||||
mapSources,
|
||||
activeMapSource,
|
||||
}}
|
||||
>
|
||||
<EventsProvider
|
||||
value={{
|
||||
setMainMenuVisible: setMainMenuVisible,
|
||||
setSpawnMenuVisible: setSpawnMenuVisible,
|
||||
setUnitControlMenuVisible: setUnitControlMenuVisible,
|
||||
setDrawingMenuVisible: setDrawingMenuVisible,
|
||||
setMeasureMenuVisible: setMeasureMenuVisible,
|
||||
setOptionsMenuVisible: setOptionsMenuVisible,
|
||||
setAirbaseMenuVisible: setAirbaseMenuVisible,
|
||||
setJTACMenuVisible: setJTACMenuVisible,
|
||||
setAudioMenuVisible: setAudioMenuVisible,
|
||||
toggleMainMenuVisible: () => {
|
||||
hideAllMenus();
|
||||
setMainMenuVisible(!mainMenuVisible);
|
||||
},
|
||||
toggleSpawnMenuVisible: () => {
|
||||
hideAllMenus();
|
||||
setSpawnMenuVisible(!spawnMenuVisible);
|
||||
},
|
||||
toggleUnitControlMenuVisible: () => {
|
||||
hideAllMenus();
|
||||
setUnitControlMenuVisible(!unitControlMenuVisible);
|
||||
},
|
||||
toggleMeasureMenuVisible: () => {
|
||||
hideAllMenus();
|
||||
setMeasureMenuVisible(!measureMenuVisible);
|
||||
},
|
||||
toggleDrawingMenuVisible: () => {
|
||||
hideAllMenus();
|
||||
setDrawingMenuVisible(!drawingMenuVisible);
|
||||
},
|
||||
toggleOptionsMenuVisible: () => {
|
||||
hideAllMenus();
|
||||
setOptionsMenuVisible(!optionsMenuVisible);
|
||||
},
|
||||
toggleAirbaseMenuVisible: () => {
|
||||
hideAllMenus();
|
||||
setAirbaseMenuVisible(!airbaseMenuVisible);
|
||||
},
|
||||
toggleAudioMenuVisible: () => {
|
||||
hideAllMenus();
|
||||
setAudioMenuVisible(!audioMenuVisible);
|
||||
},
|
||||
toggleJTACMenuVisible: () => {
|
||||
hideAllMenus();
|
||||
setJTACMenuVisible(!JTACMenuVisible);
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
||||
<Header />
|
||||
<div className="flex h-full w-full flex-row-reverse">
|
||||
{loginModalVisible && (
|
||||
{appState === OlympusState.LOGIN && (
|
||||
<>
|
||||
<div
|
||||
className={`
|
||||
@ -290,16 +191,19 @@ export function UI() {
|
||||
</>
|
||||
)}
|
||||
<div id="map-container" className="z-0 h-full w-screen" />
|
||||
<MainMenu open={mainMenuVisible} onClose={() => setMainMenuVisible(false)} />
|
||||
<SpawnMenu open={spawnMenuVisible} onClose={() => setSpawnMenuVisible(false)} />
|
||||
<OptionsMenu open={optionsMenuVisible} onClose={() => setOptionsMenuVisible(false)} options={mapOptions} />
|
||||
<UnitControlMenu open={unitControlMenuVisible} onClose={() => setUnitControlMenuVisible(false)} />
|
||||
<DrawingMenu open={drawingMenuVisible} onClose={() => setDrawingMenuVisible(false)} />
|
||||
<AirbaseMenu open={airbaseMenuVisible} onClose={() => setAirbaseMenuVisible(false)} airbase={airbase} />
|
||||
<AudioMenu open={audioMenuVisible} onClose={() => setAudioMenuVisible(false)} />
|
||||
<FormationMenu open={formationMenuVisible} leader={formationLeader} wingmen={formationWingmen} onClose={() => setFormationMenuVisible(false)} />
|
||||
<UnitExplosionMenu open={unitExplosionMenuVisible} units={unitExplosionUnits} onClose={() => setUnitExplosionMenuVisible(false)} />
|
||||
<JTACMenu open={JTACMenuVisible} onClose={() => setJTACMenuVisible(false)} />
|
||||
<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)} options={mapOptions} />
|
||||
|
||||
<UnitControlMenu open={appState === OlympusState.UNIT_CONTROL && appSubState !== UnitControlSubState.FORMATION} onClose={() => getApp().setState(OlympusState.IDLE)} />
|
||||
<FormationMenu open={appState === OlympusState.UNIT_CONTROL && appSubState === UnitControlSubState.FORMATION} leader={formationLeader} wingmen={formationWingmen} 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)} airbase={airbase} />
|
||||
<AudioMenu open={appState === OlympusState.AUDIO} onClose={() => getApp().setState(OlympusState.IDLE)} />
|
||||
|
||||
{/* TODO} <UnitExplosionMenu open={appState === OlympusState.MAIN_MENU} units={unitExplosionUnits} onClose={() => getApp().setState(OlympusState.IDLE)} /> {*/}
|
||||
<JTACMenu open={appState === OlympusState.JTAC} onClose={() => getApp().setState(OlympusState.IDLE)} />
|
||||
|
||||
<MiniMapPanel />
|
||||
<ControlsPanel />
|
||||
@ -307,7 +211,6 @@ export function UI() {
|
||||
<MapContextMenu />
|
||||
<SideBar />
|
||||
</div>
|
||||
</EventsProvider>
|
||||
</StateProvider>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -48,6 +48,8 @@ export abstract class UnitDatabase {
|
||||
}
|
||||
|
||||
getBlueprints(includeDisabled: boolean = false) {
|
||||
if (!getApp()) return {};
|
||||
|
||||
if (
|
||||
getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER ||
|
||||
!getApp().getMissionManager().getCommandModeOptions().restrictSpawns
|
||||
|
||||
@ -25,7 +25,6 @@ import {
|
||||
DLINK,
|
||||
DataIndexes,
|
||||
GAME_MASTER,
|
||||
IDLE,
|
||||
IRST,
|
||||
OPTIC,
|
||||
RADAR,
|
||||
@ -38,10 +37,10 @@ import {
|
||||
GROUPING_ZOOM_TRANSITION,
|
||||
MAX_SHOTS_SCATTER,
|
||||
SHOTS_SCATTER_DEGREES,
|
||||
CONTEXT_ACTION,
|
||||
SELECT_JTAC_TARGET,
|
||||
ContextActionColors,
|
||||
CONTEXT_ACTION_COLORS,
|
||||
OlympusState,
|
||||
JTACSubState,
|
||||
} from "../constants/constants";
|
||||
import { DataExtractor } from "../server/dataextractor";
|
||||
import { groundUnitDatabase } from "./databases/groundunitdatabase";
|
||||
@ -1384,18 +1383,18 @@ export abstract class Unit extends CustomMarker {
|
||||
#onShortPress(e: LeafletMouseEvent) {
|
||||
console.log(`Short press on ${this.getUnitName()}`);
|
||||
|
||||
if (getApp().getMap().getState() === IDLE || e.originalEvent.ctrlKey) {
|
||||
if (getApp().getState() === OlympusState.IDLE || e.originalEvent.ctrlKey) {
|
||||
if (!e.originalEvent.ctrlKey) getApp().getUnitsManager().deselectAllUnits();
|
||||
this.setSelected(!this.getSelected());
|
||||
} else if (getApp().getMap().getState() === CONTEXT_ACTION) {
|
||||
} else if (getApp().getState() === OlympusState.UNIT_CONTROL) {
|
||||
if (getApp().getMap().getContextAction()) getApp().getMap().executeContextAction(this, null);
|
||||
else {
|
||||
getApp().getUnitsManager().deselectAllUnits();
|
||||
this.setSelected(!this.getSelected());
|
||||
}
|
||||
} else if (getApp().getMap().getState() === SELECT_JTAC_TARGET) {
|
||||
} else if (getApp().getState() === OlympusState.JTAC && getApp().getSubState() === JTACSubState.SELECT_TARGET) {
|
||||
document.dispatchEvent(new CustomEvent("selectJTACTarget", { detail: { unit: this } }));
|
||||
getApp().getMap().setState(IDLE);
|
||||
getApp().setState(OlympusState.IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ import {
|
||||
} from "../other/utils";
|
||||
import { CoalitionPolygon } from "../map/coalitionarea/coalitionpolygon";
|
||||
import { groundUnitDatabase } from "./databases/groundunitdatabase";
|
||||
import { CONTEXT_ACTION, DELETE_CYCLE_TIME, DELETE_SLOW_THRESHOLD, DataIndexes, GAME_MASTER, IADSDensities, IDLE } from "../constants/constants";
|
||||
import { DELETE_CYCLE_TIME, DELETE_SLOW_THRESHOLD, DataIndexes, GAME_MASTER, IADSDensities, OlympusState } from "../constants/constants";
|
||||
import { DataExtractor } from "../server/dataextractor";
|
||||
import { citiesDatabase } from "./databases/citiesdatabase";
|
||||
import { aircraftDatabase } from "./databases/aircraftdatabase";
|
||||
@ -1461,10 +1461,9 @@ export class UnitsManager {
|
||||
|
||||
let newContextActionSet = new ContextActionSet();
|
||||
this.getSelectedUnits().forEach((unit) => unit.appendContextActions(newContextActionSet));
|
||||
getApp().getMap().setState(CONTEXT_ACTION, {
|
||||
contextAction: null,
|
||||
defaultContextAction: newContextActionSet.getDefaultContextAction(),
|
||||
});
|
||||
getApp().getMap().setContextAction(null);
|
||||
getApp().getMap().setDefaultContextAction(newContextActionSet.getDefaultContextAction());
|
||||
getApp().setState(OlympusState.UNIT_CONTROL);
|
||||
|
||||
this.#selectionEventDisabled = false;
|
||||
this.#showNumberOfSelectedProtectedUnits();
|
||||
@ -1472,14 +1471,15 @@ export class UnitsManager {
|
||||
this.#selectionEventDisabled = true;
|
||||
}
|
||||
} else {
|
||||
getApp().getMap().setState(IDLE);
|
||||
getApp().setState(OlympusState.IDLE);
|
||||
document.dispatchEvent(new CustomEvent("clearSelection"));
|
||||
}
|
||||
}
|
||||
|
||||
#onUnitDeselection(unit: Unit) {
|
||||
if (this.getSelectedUnits().length == 0) {
|
||||
getApp().getMap().setState(IDLE);
|
||||
if (getApp().getState() === OlympusState.UNIT_CONTROL)
|
||||
getApp().setState(OlympusState.IDLE);
|
||||
document.dispatchEvent(new CustomEvent("clearSelection"));
|
||||
} else {
|
||||
/* Disable the firing of the selection event for a certain amount of time. This avoids firing many events if many units are selected */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user