mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Added airbase info and spawn menu
This commit is contained in:
@@ -7,12 +7,14 @@ export const EventsContext = createContext({
|
|||||||
setMeasureMenuVisible: (e: boolean) => {},
|
setMeasureMenuVisible: (e: boolean) => {},
|
||||||
setDrawingMenuVisible: (e: boolean) => {},
|
setDrawingMenuVisible: (e: boolean) => {},
|
||||||
setOptionsMenuVisible: (e: boolean) => {},
|
setOptionsMenuVisible: (e: boolean) => {},
|
||||||
|
setAirbaseMenuVisible: (e: boolean) => {},
|
||||||
toggleMainMenuVisible: () => {},
|
toggleMainMenuVisible: () => {},
|
||||||
toggleSpawnMenuVisible: () => {},
|
toggleSpawnMenuVisible: () => {},
|
||||||
toggleUnitControlMenuVisible: () => {},
|
toggleUnitControlMenuVisible: () => {},
|
||||||
toggleMeasureMenuVisible: () => {},
|
toggleMeasureMenuVisible: () => {},
|
||||||
toggleDrawingMenuVisible: () => {},
|
toggleDrawingMenuVisible: () => {},
|
||||||
toggleOptionsMenuVisible: () => {},
|
toggleOptionsMenuVisible: () => {},
|
||||||
|
toggleAirbaseMenuVisible: () => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const EventsProvider = EventsContext.Provider;
|
export const EventsProvider = EventsContext.Provider;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export class Airbase extends CustomMarker {
|
|||||||
runways: [],
|
runways: [],
|
||||||
};
|
};
|
||||||
#coalition: string = "";
|
#coalition: string = "";
|
||||||
#hasChartDataBeenSet: boolean = false;
|
|
||||||
#properties: string[] = [];
|
#properties: string[] = [];
|
||||||
#parkings: string[] = [];
|
#parkings: string[] = [];
|
||||||
|
|
||||||
@@ -22,10 +21,6 @@ export class Airbase extends CustomMarker {
|
|||||||
this.#name = options.name;
|
this.#name = options.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
chartDataHasBeenSet() {
|
|
||||||
return this.#hasChartDataBeenSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
createIcon() {
|
createIcon() {
|
||||||
var icon = new DivIcon({
|
var icon = new DivIcon({
|
||||||
className: "leaflet-airbase-marker",
|
className: "leaflet-airbase-marker",
|
||||||
@@ -43,10 +38,10 @@ export class Airbase extends CustomMarker {
|
|||||||
el.appendChild(img);
|
el.appendChild(img);
|
||||||
this.getElement()?.appendChild(el);
|
this.getElement()?.appendChild(el);
|
||||||
el.addEventListener("mouseover", (ev) => {
|
el.addEventListener("mouseover", (ev) => {
|
||||||
document.dispatchEvent(new CustomEvent("airbaseMouseover", { detail: this }));
|
document.dispatchEvent(new CustomEvent("airbasemouseover", { detail: this }));
|
||||||
});
|
});
|
||||||
el.addEventListener("mouseout", (ev) => {
|
el.addEventListener("mouseout", (ev) => {
|
||||||
document.dispatchEvent(new CustomEvent("airbaseMouseout", { detail: this }));
|
document.dispatchEvent(new CustomEvent("airbasemouseout", { detail: this }));
|
||||||
});
|
});
|
||||||
el.dataset.coalition = this.#coalition;
|
el.dataset.coalition = this.#coalition;
|
||||||
}
|
}
|
||||||
@@ -73,7 +68,6 @@ export class Airbase extends CustomMarker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setChartData(chartData: AirbaseChartData) {
|
setChartData(chartData: AirbaseChartData) {
|
||||||
this.#hasChartDataBeenSet = true;
|
|
||||||
this.#chartData = chartData;
|
this.#chartData = chartData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export class MissionManager {
|
|||||||
position: new LatLng(airbase.latitude, airbase.longitude),
|
position: new LatLng(airbase.latitude, airbase.longitude),
|
||||||
name: airbase.callsign,
|
name: airbase.callsign,
|
||||||
}).addTo(getApp().getMap());
|
}).addTo(getApp().getMap());
|
||||||
this.#airbases[airbase.callsign].on("contextmenu", (e) => this.#onAirbaseClick(e));
|
this.#airbases[airbase.callsign].on("click", (e) => this.#onAirbaseClick(e));
|
||||||
this.#loadAirbaseChartData(airbase.callsign);
|
this.#loadAirbaseChartData(airbase.callsign);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +316,9 @@ export class MissionManager {
|
|||||||
if (requestRefresh) getApp().getServerManager().refreshAll();
|
if (requestRefresh) getApp().getServerManager().refreshAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
#onAirbaseClick(e: any) {}
|
#onAirbaseClick(ev: any) {
|
||||||
|
document.dispatchEvent(new CustomEvent("airbaseclick", { detail: ev.target }));
|
||||||
|
}
|
||||||
|
|
||||||
#loadAirbaseChartData(callsign: string) {
|
#loadAirbaseChartData(callsign: string) {
|
||||||
if (!this.#theatre) {
|
if (!this.#theatre) {
|
||||||
@@ -324,7 +326,7 @@ export class MissionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.open("GET", `api/airbases/${this.#theatre.toLowerCase()}/${callsign}`, true);
|
xhr.open("GET", window.location.href.split("?")[0].replace("vite/", "") + `api/airbases/${this.#theatre.toLowerCase()}/${callsign}`, true);
|
||||||
xhr.responseType = "json";
|
xhr.responseType = "json";
|
||||||
xhr.onload = () => {
|
xhr.onload = () => {
|
||||||
var status = xhr.status;
|
var status = xhr.status;
|
||||||
|
|||||||
@@ -142,8 +142,6 @@ export class ServerManager {
|
|||||||
|
|
||||||
setAddress(address: string) {
|
setAddress(address: string) {
|
||||||
this.#REST_ADDRESS = `${address.replace("vite/", "").replace("vite", "")}olympus`;
|
this.#REST_ADDRESS = `${address.replace("vite/", "").replace("vite", "")}olympus`;
|
||||||
// TODO: TEMPORARY FOR DEBUGGING
|
|
||||||
// this.#REST_ADDRESS = `https://refugees.dcsolympus.com/olympus`;
|
|
||||||
|
|
||||||
console.log(`Setting REST address to ${this.#REST_ADDRESS}`);
|
console.log(`Setting REST address to ${this.#REST_ADDRESS}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export const StateContext = createContext({
|
|||||||
measureMenuVisible: false,
|
measureMenuVisible: false,
|
||||||
drawingMenuVisible: false,
|
drawingMenuVisible: false,
|
||||||
optionsMenuVisible: false,
|
optionsMenuVisible: false,
|
||||||
|
airbaseMenuVisible: false,
|
||||||
mapHiddenTypes: MAP_HIDDEN_TYPES_DEFAULTS,
|
mapHiddenTypes: MAP_HIDDEN_TYPES_DEFAULTS,
|
||||||
mapOptions: MAP_OPTIONS_DEFAULTS,
|
mapOptions: MAP_OPTIONS_DEFAULTS,
|
||||||
mapSources: [] as string[],
|
mapSources: [] as string[],
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from "react";
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faArrowCircleDown } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowCircleDown } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
export function OlAccordion(props: { title: string; children?: JSX.Element | JSX.Element[]; showArrows?: boolean }) {
|
export function OlAccordion(props: { title: string; children?: JSX.Element | JSX.Element[]; showArrows?: boolean; className?: string }) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [scrolledUp, setScrolledUp] = useState(true);
|
const [scrolledUp, setScrolledUp] = useState(true);
|
||||||
const [scrolledDown, setScrolledDown] = useState(false);
|
const [scrolledDown, setScrolledDown] = useState(false);
|
||||||
@@ -31,6 +31,7 @@ export function OlAccordion(props: { title: string; children?: JSX.Element | JSX
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => setOpen(!open)}
|
onClick={() => setOpen(!open)}
|
||||||
className={`
|
className={`
|
||||||
|
${props.className ?? ""}
|
||||||
flex w-full items-center justify-between gap-3 border-gray-200 py-2
|
flex w-full items-center justify-between gap-3 border-gray-200 py-2
|
||||||
text-gray-700
|
text-gray-700
|
||||||
dark:border-gray-700 dark:text-white
|
dark:border-gray-700 dark:text-white
|
||||||
|
|||||||
141
frontend/react/src/ui/panels/airbasemenu.tsx
Normal file
141
frontend/react/src/ui/panels/airbasemenu.tsx
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Menu } from "./components/menu";
|
||||||
|
import { OlCheckbox } from "../components/olcheckbox";
|
||||||
|
import { OlRangeSlider } from "../components/olrangeslider";
|
||||||
|
import { OlNumberInput } from "../components/olnumberinput";
|
||||||
|
import { Coalition, MapOptions } from "../../types/types";
|
||||||
|
import { getApp } from "../../olympusapp";
|
||||||
|
import { Airbase } from "../../mission/airbase";
|
||||||
|
import { FaArrowLeft, FaCompass } from "react-icons/fa6";
|
||||||
|
import { getUnitsByLabel } from "../../other/utils";
|
||||||
|
import { UnitBlueprint } from "../../interfaces";
|
||||||
|
import { IDLE } from "../../constants/constants";
|
||||||
|
import { OlSearchBar } from "../components/olsearchbar";
|
||||||
|
import { OlAccordion } from "../components/olaccordion";
|
||||||
|
import { OlUnitEntryList } from "../components/olunitlistentry";
|
||||||
|
import { olButtonsVisibilityAircraft, olButtonsVisibilityHelicopter } from "../components/olicons";
|
||||||
|
import { UnitSpawnMenu } from "./unitspawnmenu";
|
||||||
|
|
||||||
|
export function AirbaseMenu(props: { open: boolean; onClose: () => void; airbase: Airbase | null; children?: JSX.Element | JSX.Element[] }) {
|
||||||
|
const [blueprint, setBlueprint] = useState(null as null | UnitBlueprint);
|
||||||
|
const [filterString, setFilterString] = useState("");
|
||||||
|
|
||||||
|
const [filteredAircraft, filteredHelicopters, filteredAirDefense, filteredGroundUnits, filteredNavyUnits] = getUnitsByLabel(filterString);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu title={props.airbase?.getName() ?? "No airbase selected"} open={props.open} onClose={props.onClose} showBackButton={false} canBeHidden={true}>
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
flex flex-col gap-2 font-normal text-gray-800
|
||||||
|
dark:text-white
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-coalition={props.airbase?.getCoalition()}
|
||||||
|
className={`
|
||||||
|
flex flex-col content-center justify-between gap-2 border-l-4
|
||||||
|
bg-olympus-200/30 py-3 pl-4 pr-5
|
||||||
|
data-[coalition='blue']:border-blue-500
|
||||||
|
data-[coalition='neutral']:border-gray-500
|
||||||
|
data-[coalition='red']:border-red-500
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<div className="flex w-full justify-between">
|
||||||
|
<span className="text-gray-400">ICAO name</span>
|
||||||
|
<span>{props.airbase?.getChartData().ICAO !== "" ? props.airbase?.getChartData().ICAO : "N/A"}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full justify-between">
|
||||||
|
<span className="text-gray-400">TACAN</span>
|
||||||
|
<span>{props.airbase?.getChartData().TACAN !== "" ? props.airbase?.getChartData().TACAN : "None"}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full justify-between">
|
||||||
|
<span className="text-gray-400">Elevation</span>
|
||||||
|
<span>{props.airbase?.getChartData().elevation !== "" ? props.airbase?.getChartData().elevation : "N/A"}ft</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<OlAccordion title={`Runways`} className="!p-0 !text-gray-400">
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{props.airbase?.getChartData().runways.map((runway) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{Object.keys(runway.headings[0]).map((runwayName) => {
|
||||||
|
return (
|
||||||
|
<div className="flex w-full justify-between">
|
||||||
|
<span>
|
||||||
|
{" "}
|
||||||
|
<span className="text-gray-400">RWY</span> {runwayName}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`
|
||||||
|
flex gap-1 rounded-full bg-olympus-200/30 px-2
|
||||||
|
py-1
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<FaCompass className={`my-auto text-gray-400`} /> {runway.headings[0][runwayName].magHeading}°{" "}
|
||||||
|
<span className={`text-gray-400`}>ILS</span>{" "}
|
||||||
|
{runway.headings[0][runwayName].ILS !== "" ? runway.headings[0][runwayName].ILS + "MHz" : "None"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</OlAccordion>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 flex gap-2 px-5 text-white bold">
|
||||||
|
{blueprint && (
|
||||||
|
<FaArrowLeft
|
||||||
|
className={`
|
||||||
|
my-auto h-8 w-8 cursor-pointer rounded-md p-2
|
||||||
|
dark:text-gray-500 dark:hover:bg-gray-700 dark:hover:text-white
|
||||||
|
`}
|
||||||
|
onClick={() => setBlueprint(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span className="my-auto">Spawn units at airbase</span>
|
||||||
|
</div>
|
||||||
|
<>
|
||||||
|
{blueprint === null && (
|
||||||
|
<div className="p-5">
|
||||||
|
<OlSearchBar onChange={(value) => setFilterString(value)} text={filterString} />
|
||||||
|
<OlAccordion title={`Aircraft`}>
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
flex max-h-80 flex-col gap-1 overflow-y-scroll no-scrollbar
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{Object.entries(filteredAircraft).map((entry) => {
|
||||||
|
return <OlUnitEntryList key={entry[0]} icon={olButtonsVisibilityAircraft} blueprint={entry[1]} onClick={() => setBlueprint(entry[1])} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</OlAccordion>
|
||||||
|
<OlAccordion title={`Helicopters`}>
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
flex max-h-80 flex-col gap-1 overflow-y-scroll no-scrollbar
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{Object.entries(filteredHelicopters).map((entry) => {
|
||||||
|
return <OlUnitEntryList key={entry[0]} icon={olButtonsVisibilityHelicopter} blueprint={entry[1]} onClick={() => setBlueprint(entry[1])} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</OlAccordion>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!(blueprint === null) && (
|
||||||
|
<UnitSpawnMenu
|
||||||
|
blueprint={blueprint}
|
||||||
|
spawnAtLocation={false}
|
||||||
|
airbase={props.airbase}
|
||||||
|
coalition={(props.airbase?.getCoalition() ?? "blue") as Coalition}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
</div>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { OlStateButton } from "../components/olstatebutton";
|
import { OlStateButton } from "../components/olstatebutton";
|
||||||
import { faGamepad, faRuler, faPencil, faEllipsisV, faCog, faQuestionCircle, faPlusSquare, faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons";
|
import { faGamepad, faRuler, faPencil, faEllipsisV, faCog, faQuestionCircle, faPlusSquare, faMagnifyingGlass, faPlaneDeparture } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { EventsConsumer } from "../../eventscontext";
|
import { EventsConsumer } from "../../eventscontext";
|
||||||
import { StateConsumer } from "../../statecontext";
|
import { StateConsumer } from "../../statecontext";
|
||||||
import { IDLE } from "../../constants/constants";
|
import { IDLE } from "../../constants/constants";
|
||||||
@@ -52,6 +52,12 @@ export function SideBar() {
|
|||||||
icon={faPencil}
|
icon={faPencil}
|
||||||
tooltip="Hide/show drawing menu"
|
tooltip="Hide/show drawing menu"
|
||||||
></OlStateButton>
|
></OlStateButton>
|
||||||
|
<OlStateButton
|
||||||
|
onClick={events.toggleAirbaseMenuVisible}
|
||||||
|
checked={appState.airbaseMenuVisible}
|
||||||
|
icon={faPlaneDeparture}
|
||||||
|
tooltip="Hide/show airbase menu"
|
||||||
|
></OlStateButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-16 flex-wrap content-end justify-center p-4">
|
<div className="flex w-16 flex-wrap content-end justify-center p-4">
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Menu } from "./components/menu";
|
import { Menu } from "./components/menu";
|
||||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
|
||||||
import { OlSearchBar } from "../components/olsearchbar";
|
import { OlSearchBar } from "../components/olsearchbar";
|
||||||
import { OlAccordion } from "../components/olaccordion";
|
import { OlAccordion } from "../components/olaccordion";
|
||||||
import { getApp } from "../../olympusapp";
|
import { getApp } from "../../olympusapp";
|
||||||
@@ -18,8 +16,6 @@ import {
|
|||||||
import { IDLE, SPAWN_UNIT } from "../../constants/constants";
|
import { IDLE, SPAWN_UNIT } from "../../constants/constants";
|
||||||
import { getUnitsByLabel } from "../../other/utils";
|
import { getUnitsByLabel } from "../../other/utils";
|
||||||
|
|
||||||
library.add(faPlus);
|
|
||||||
|
|
||||||
export function SpawnMenu(props: { open: boolean; onClose: () => void; children?: JSX.Element | JSX.Element[] }) {
|
export function SpawnMenu(props: { open: boolean; onClose: () => void; children?: JSX.Element | JSX.Element[] }) {
|
||||||
const [blueprint, setBlueprint] = useState(null as null | UnitBlueprint);
|
const [blueprint, setBlueprint] = useState(null as null | UnitBlueprint);
|
||||||
const [filterString, setFilterString] = useState("");
|
const [filterString, setFilterString] = useState("");
|
||||||
@@ -107,7 +103,7 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!(blueprint === null) && <UnitSpawnMenu blueprint={blueprint} />}
|
{!(blueprint === null) && <UnitSpawnMenu blueprint={blueprint} spawnAtLocation={true} />}
|
||||||
</>
|
</>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ import { getApp } from "../../olympusapp";
|
|||||||
import { IDLE, SPAWN_UNIT } from "../../constants/constants";
|
import { IDLE, SPAWN_UNIT } from "../../constants/constants";
|
||||||
import { ftToM, getUnitCategoryByBlueprint } from "../../other/utils";
|
import { ftToM, getUnitCategoryByBlueprint } from "../../other/utils";
|
||||||
import { LatLng } from "leaflet";
|
import { LatLng } from "leaflet";
|
||||||
|
import { Airbase } from "../../mission/airbase";
|
||||||
|
|
||||||
export function UnitSpawnMenu(props: { blueprint: UnitBlueprint }) {
|
export function UnitSpawnMenu(props: { blueprint: UnitBlueprint; spawnAtLocation: boolean; airbase?: Airbase | null; coalition?: Coalition }) {
|
||||||
/* Compute the min and max values depending on the unit type */
|
/* Compute the min and max values depending on the unit type */
|
||||||
const minNumber = 1;
|
const minNumber = 1;
|
||||||
const maxNumber = 4;
|
const maxNumber = 4;
|
||||||
@@ -30,31 +31,60 @@ export function UnitSpawnMenu(props: { blueprint: UnitBlueprint }) {
|
|||||||
|
|
||||||
/* When the menu is opened show the unit preview on the map as a cursor */
|
/* When the menu is opened show the unit preview on the map as a cursor */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.blueprint !== null) {
|
if (props.coalition && props.coalition !== spawnCoalition) {
|
||||||
getApp()
|
setSpawnCoalition(props.coalition);
|
||||||
?.getMap()
|
}
|
||||||
?.setState(SPAWN_UNIT, {
|
if (props.spawnAtLocation) {
|
||||||
spawnRequestTable: {
|
if (props.blueprint !== null) {
|
||||||
category: getUnitCategoryByBlueprint(props.blueprint),
|
getApp()
|
||||||
unit: {
|
?.getMap()
|
||||||
unitType: props.blueprint.name,
|
?.setState(SPAWN_UNIT, {
|
||||||
location: new LatLng(0, 0), // This will be filled when the user clicks on the map to spawn the unit
|
spawnRequestTable: {
|
||||||
skill: "High",
|
category: getUnitCategoryByBlueprint(props.blueprint),
|
||||||
liveryID: "",
|
unit: {
|
||||||
altitude: ftToM(spawnAltitude),
|
unitType: props.blueprint.name,
|
||||||
loadout:
|
location: new LatLng(0, 0), // This will be filled when the user clicks on the map to spawn the unit
|
||||||
props.blueprint.loadouts?.find((loadout) => {
|
skill: "High",
|
||||||
return loadout.name === spawnLoadoutName;
|
liveryID: "",
|
||||||
})?.code ?? "",
|
altitude: ftToM(spawnAltitude),
|
||||||
|
loadout:
|
||||||
|
props.blueprint.loadouts?.find((loadout) => {
|
||||||
|
return loadout.name === spawnLoadoutName;
|
||||||
|
})?.code ?? "",
|
||||||
|
},
|
||||||
|
coalition: spawnCoalition,
|
||||||
},
|
},
|
||||||
coalition: spawnCoalition,
|
});
|
||||||
},
|
} else {
|
||||||
});
|
if (getApp()?.getMap()?.getState() === SPAWN_UNIT) getApp().getMap().setState(IDLE);
|
||||||
} else {
|
}
|
||||||
if (getApp()?.getMap()?.getState() === SPAWN_UNIT) getApp().getMap().setState(IDLE);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function spawnAtAirbase() {
|
||||||
|
getApp()
|
||||||
|
.getUnitsManager()
|
||||||
|
.spawnUnits(
|
||||||
|
getUnitCategoryByBlueprint(props.blueprint),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
unitType: props.blueprint.name,
|
||||||
|
location: new LatLng(0, 0), // Not relevant spawning at airbase
|
||||||
|
skill: "High",
|
||||||
|
liveryID: "",
|
||||||
|
altitude: 0,
|
||||||
|
loadout:
|
||||||
|
props.blueprint.loadouts?.find((loadout) => {
|
||||||
|
return loadout.name === spawnLoadoutName;
|
||||||
|
})?.code ?? "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
props.coalition,
|
||||||
|
false,
|
||||||
|
props.airbase?.getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/* Get a list of all the roles */
|
/* Get a list of all the roles */
|
||||||
const roles: string[] = [];
|
const roles: string[] = [];
|
||||||
(props.blueprint as UnitBlueprint).loadouts?.forEach((loadout) => {
|
(props.blueprint as UnitBlueprint).loadouts?.forEach((loadout) => {
|
||||||
@@ -87,14 +117,16 @@ export function UnitSpawnMenu(props: { blueprint: UnitBlueprint }) {
|
|||||||
inline-flex w-full flex-row content-center justify-between
|
inline-flex w-full flex-row content-center justify-between
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<OlCoalitionToggle
|
{!props.coalition && (
|
||||||
coalition={spawnCoalition}
|
<OlCoalitionToggle
|
||||||
onClick={() => {
|
coalition={spawnCoalition}
|
||||||
spawnCoalition === "blue" && setSpawnCoalition("neutral");
|
onClick={() => {
|
||||||
spawnCoalition === "neutral" && setSpawnCoalition("red");
|
spawnCoalition === "blue" && setSpawnCoalition("neutral");
|
||||||
spawnCoalition === "red" && setSpawnCoalition("blue");
|
spawnCoalition === "neutral" && setSpawnCoalition("red");
|
||||||
}}
|
spawnCoalition === "red" && setSpawnCoalition("blue");
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<OlNumberInput
|
<OlNumberInput
|
||||||
value={spawnNumber}
|
value={spawnNumber}
|
||||||
min={minNumber}
|
min={minNumber}
|
||||||
@@ -203,14 +235,14 @@ export function UnitSpawnMenu(props: { blueprint: UnitBlueprint }) {
|
|||||||
</OlDropdown>
|
</OlDropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
{spawnLoadout && spawnLoadout.items.length > 0 && (
|
||||||
className={`
|
<div
|
||||||
flex h-fit flex-col gap-1 p-4
|
className={`
|
||||||
dark:bg-olympus-200/30
|
flex h-fit flex-col gap-1 p-4
|
||||||
`}
|
dark:bg-olympus-200/30
|
||||||
>
|
`}
|
||||||
{spawnLoadout &&
|
>
|
||||||
spawnLoadout.items.map((item) => {
|
{spawnLoadout.items.map((item) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex content-center gap-2">
|
<div className="flex content-center gap-2">
|
||||||
<div
|
<div
|
||||||
@@ -233,7 +265,25 @@ export function UnitSpawnMenu(props: { blueprint: UnitBlueprint }) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
{!props.spawnAtLocation && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`
|
||||||
|
m-2 rounded-lg bg-blue-700 px-5 py-2.5 text-sm font-medium
|
||||||
|
text-white
|
||||||
|
dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
|
||||||
|
focus:outline-none focus:ring-4 focus:ring-blue-300
|
||||||
|
hover:bg-blue-800
|
||||||
|
`}
|
||||||
|
onClick={() => {
|
||||||
|
spawnAtAirbase();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Spawn
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,16 +18,19 @@ import { sha256 } from "js-sha256";
|
|||||||
import { MiniMapPanel } from "./panels/minimappanel";
|
import { MiniMapPanel } from "./panels/minimappanel";
|
||||||
import { UnitMouseControlBar } from "./panels/unitmousecontrolbar";
|
import { UnitMouseControlBar } from "./panels/unitmousecontrolbar";
|
||||||
import { DrawingMenu } from "./panels/drawingmenu";
|
import { DrawingMenu } from "./panels/drawingmenu";
|
||||||
import { ControlsPanel } from "./panels/controls";
|
import { ControlsPanel } from "./panels/controlspanel";
|
||||||
import { MapContextMenu } from "./contextmenus/mapcontextmenu";
|
import { MapContextMenu } from "./contextmenus/mapcontextmenu";
|
||||||
|
import { AirbaseMenu } from "./panels/airbasemenu";
|
||||||
|
import { Airbase } from "../mission/airbase";
|
||||||
|
|
||||||
export type OlympusState = {
|
export type OlympusUIState = {
|
||||||
mainMenuVisible: boolean;
|
mainMenuVisible: boolean;
|
||||||
spawnMenuVisible: boolean;
|
spawnMenuVisible: boolean;
|
||||||
unitControlMenuVisible: boolean;
|
unitControlMenuVisible: boolean;
|
||||||
measureMenuVisible: boolean;
|
measureMenuVisible: boolean;
|
||||||
drawingMenuVisible: boolean;
|
drawingMenuVisible: boolean;
|
||||||
optionsMenuVisible: boolean;
|
optionsMenuVisible: boolean;
|
||||||
|
airbaseMenuVisible: boolean;
|
||||||
mapHiddenTypes: MapHiddenTypes;
|
mapHiddenTypes: MapHiddenTypes;
|
||||||
mapOptions: MapOptions;
|
mapOptions: MapOptions;
|
||||||
};
|
};
|
||||||
@@ -40,6 +43,7 @@ export function UI() {
|
|||||||
const [measureMenuVisible, setMeasureMenuVisible] = useState(false);
|
const [measureMenuVisible, setMeasureMenuVisible] = useState(false);
|
||||||
const [drawingMenuVisible, setDrawingMenuVisible] = useState(false);
|
const [drawingMenuVisible, setDrawingMenuVisible] = useState(false);
|
||||||
const [optionsMenuVisible, setOptionsMenuVisible] = useState(false);
|
const [optionsMenuVisible, setOptionsMenuVisible] = useState(false);
|
||||||
|
const [airbaseMenuVisible, setAirbaseMenuVisible] = useState(false);
|
||||||
const [mapHiddenTypes, setMapHiddenTypes] = useState(MAP_HIDDEN_TYPES_DEFAULTS);
|
const [mapHiddenTypes, setMapHiddenTypes] = useState(MAP_HIDDEN_TYPES_DEFAULTS);
|
||||||
const [mapOptions, setMapOptions] = useState(MAP_OPTIONS_DEFAULTS);
|
const [mapOptions, setMapOptions] = useState(MAP_OPTIONS_DEFAULTS);
|
||||||
const [checkingPassword, setCheckingPassword] = useState(false);
|
const [checkingPassword, setCheckingPassword] = useState(false);
|
||||||
@@ -48,6 +52,7 @@ export function UI() {
|
|||||||
const [mapSources, setMapSources] = useState([] as string[]);
|
const [mapSources, setMapSources] = useState([] as string[]);
|
||||||
const [activeMapSource, setActiveMapSource] = useState("");
|
const [activeMapSource, setActiveMapSource] = useState("");
|
||||||
const [mapState, setMapState] = useState(IDLE);
|
const [mapState, setMapState] = useState(IDLE);
|
||||||
|
const [airbase, setAirbase] = useState(null as null | Airbase);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.addEventListener("hiddenTypesChanged", (ev) => {
|
document.addEventListener("hiddenTypesChanged", (ev) => {
|
||||||
@@ -75,6 +80,13 @@ export function UI() {
|
|||||||
setMapSources(sources);
|
setMapSources(sources);
|
||||||
setActiveMapSource(sources[0]);
|
setActiveMapSource(sources[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener("airbaseclick", (ev) => {
|
||||||
|
hideAllMenus();
|
||||||
|
getApp().getMap().setState(IDLE);
|
||||||
|
setAirbase((ev as CustomEvent).detail);
|
||||||
|
setAirbaseMenuVisible(true);
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function hideAllMenus() {
|
function hideAllMenus() {
|
||||||
@@ -84,6 +96,7 @@ export function UI() {
|
|||||||
setMeasureMenuVisible(false);
|
setMeasureMenuVisible(false);
|
||||||
setDrawingMenuVisible(false);
|
setDrawingMenuVisible(false);
|
||||||
setOptionsMenuVisible(false);
|
setOptionsMenuVisible(false);
|
||||||
|
setAirbaseMenuVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkPassword(password: string) {
|
function checkPassword(password: string) {
|
||||||
@@ -139,6 +152,7 @@ export function UI() {
|
|||||||
measureMenuVisible: measureMenuVisible,
|
measureMenuVisible: measureMenuVisible,
|
||||||
drawingMenuVisible: drawingMenuVisible,
|
drawingMenuVisible: drawingMenuVisible,
|
||||||
optionsMenuVisible: optionsMenuVisible,
|
optionsMenuVisible: optionsMenuVisible,
|
||||||
|
airbaseMenuVisible: airbaseMenuVisible,
|
||||||
mapOptions: mapOptions,
|
mapOptions: mapOptions,
|
||||||
mapHiddenTypes: mapHiddenTypes,
|
mapHiddenTypes: mapHiddenTypes,
|
||||||
mapSources: mapSources,
|
mapSources: mapSources,
|
||||||
@@ -154,6 +168,7 @@ export function UI() {
|
|||||||
setDrawingMenuVisible: setDrawingMenuVisible,
|
setDrawingMenuVisible: setDrawingMenuVisible,
|
||||||
setMeasureMenuVisible: setMeasureMenuVisible,
|
setMeasureMenuVisible: setMeasureMenuVisible,
|
||||||
setOptionsMenuVisible: setOptionsMenuVisible,
|
setOptionsMenuVisible: setOptionsMenuVisible,
|
||||||
|
setAirbaseMenuVisible: setAirbaseMenuVisible,
|
||||||
toggleMainMenuVisible: () => {
|
toggleMainMenuVisible: () => {
|
||||||
hideAllMenus();
|
hideAllMenus();
|
||||||
setMainMenuVisible(!mainMenuVisible);
|
setMainMenuVisible(!mainMenuVisible);
|
||||||
@@ -178,6 +193,10 @@ export function UI() {
|
|||||||
hideAllMenus();
|
hideAllMenus();
|
||||||
setOptionsMenuVisible(!optionsMenuVisible);
|
setOptionsMenuVisible(!optionsMenuVisible);
|
||||||
},
|
},
|
||||||
|
toggleAirbaseMenuVisible: () => {
|
||||||
|
hideAllMenus();
|
||||||
|
setAirbaseMenuVisible(!airbaseMenuVisible);
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Header />
|
<Header />
|
||||||
@@ -211,6 +230,7 @@ export function UI() {
|
|||||||
<Options open={optionsMenuVisible} onClose={() => setOptionsMenuVisible(false)} options={mapOptions} />
|
<Options open={optionsMenuVisible} onClose={() => setOptionsMenuVisible(false)} options={mapOptions} />
|
||||||
<UnitControlMenu open={unitControlMenuVisible} onClose={() => setUnitControlMenuVisible(false)} />
|
<UnitControlMenu open={unitControlMenuVisible} onClose={() => setUnitControlMenuVisible(false)} />
|
||||||
<DrawingMenu open={drawingMenuVisible} onClose={() => setDrawingMenuVisible(false)} />
|
<DrawingMenu open={drawingMenuVisible} onClose={() => setDrawingMenuVisible(false)} />
|
||||||
|
<AirbaseMenu open={airbaseMenuVisible} onClose={() => setAirbaseMenuVisible(false)} airbase={airbase}/>
|
||||||
|
|
||||||
<MiniMapPanel />
|
<MiniMapPanel />
|
||||||
<ControlsPanel />
|
<ControlsPanel />
|
||||||
|
|||||||
Reference in New Issue
Block a user