mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
More work adding back missing functions
This commit is contained in:
@@ -4,7 +4,6 @@
|
|||||||
"coalition": "blue",
|
"coalition": "blue",
|
||||||
"era": "Late Cold War",
|
"era": "Late Cold War",
|
||||||
"category": "aircraft",
|
"category": "aircraft",
|
||||||
"category": "aircraft",
|
|
||||||
"label": "A-10C Warthog 2",
|
"label": "A-10C Warthog 2",
|
||||||
"shortLabel": "A10",
|
"shortLabel": "A10",
|
||||||
"loadouts": [
|
"loadouts": [
|
||||||
@@ -2427,7 +2426,6 @@
|
|||||||
"name": "A-20G",
|
"name": "A-20G",
|
||||||
"coalition": "blue",
|
"coalition": "blue",
|
||||||
"category": "aircraft",
|
"category": "aircraft",
|
||||||
"category": "aircraft",
|
|
||||||
"label": "A-20G Havoc",
|
"label": "A-20G Havoc",
|
||||||
"era": "WW2",
|
"era": "WW2",
|
||||||
"shortLabel": "A20",
|
"shortLabel": "A20",
|
||||||
@@ -2487,7 +2485,6 @@
|
|||||||
"name": "A-50",
|
"name": "A-50",
|
||||||
"coalition": "red",
|
"coalition": "red",
|
||||||
"category": "aircraft",
|
"category": "aircraft",
|
||||||
"category": "aircraft",
|
|
||||||
"label": "A-50 Mainstay",
|
"label": "A-50 Mainstay",
|
||||||
"era": "Late Cold War",
|
"era": "Late Cold War",
|
||||||
"shortLabel": "A50",
|
"shortLabel": "A50",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -102,35 +102,41 @@ export const shotsIntensityDescriptions: string[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const minSpeedValues: { [key: string]: number } = {
|
export const minSpeedValues: { [key: string]: number } = {
|
||||||
Aircraft: 100,
|
aircraft: 100,
|
||||||
Helicopter: 0,
|
helicopter: 0,
|
||||||
NavyUnit: 0,
|
navyunit: 0,
|
||||||
GroundUnit: 0,
|
groundunit: 0,
|
||||||
};
|
};
|
||||||
export const maxSpeedValues: { [key: string]: number } = {
|
export const maxSpeedValues: { [key: string]: number } = {
|
||||||
Aircraft: 800,
|
aircraft: 800,
|
||||||
Helicopter: 300,
|
helicopter: 300,
|
||||||
NavyUnit: 60,
|
navyunit: 60,
|
||||||
GroundUnit: 60,
|
groundunit: 60,
|
||||||
};
|
};
|
||||||
export const speedIncrements: { [key: string]: number } = {
|
export const speedIncrements: { [key: string]: number } = {
|
||||||
Aircraft: 25,
|
aircraft: 25,
|
||||||
Helicopter: 10,
|
helicopter: 10,
|
||||||
NavyUnit: 5,
|
navyunit: 5,
|
||||||
GroundUnit: 5,
|
groundunit: 5,
|
||||||
};
|
};
|
||||||
export const minAltitudeValues: { [key: string]: number } = {
|
export const minAltitudeValues: { [key: string]: number } = {
|
||||||
Aircraft: 0,
|
aircraft: 0,
|
||||||
Helicopter: 0,
|
helicopter: 0,
|
||||||
};
|
};
|
||||||
export const maxAltitudeValues: { [key: string]: number } = {
|
export const maxAltitudeValues: { [key: string]: number } = {
|
||||||
Aircraft: 50000,
|
aircraft: 50000,
|
||||||
Helicopter: 10000,
|
helicopter: 10000,
|
||||||
};
|
};
|
||||||
export const altitudeIncrements: { [key: string]: number } = {
|
export const altitudeIncrements: { [key: string]: number } = {
|
||||||
Aircraft: 500,
|
aircraft: 500,
|
||||||
Helicopter: 100,
|
helicopter: 100,
|
||||||
};
|
};
|
||||||
|
export const groupUnitCount: { [key: string]: number } = {
|
||||||
|
aircraft: 4,
|
||||||
|
helicopter: 4,
|
||||||
|
navyunit: 20,
|
||||||
|
groundunit: 20,
|
||||||
|
}
|
||||||
|
|
||||||
export const minimapBoundaries = {
|
export const minimapBoundaries = {
|
||||||
Nevada: [
|
Nevada: [
|
||||||
@@ -283,7 +289,6 @@ export const IADSDensities: { [key: string]: number } = {
|
|||||||
"SAM Site": 0.1,
|
"SAM Site": 0.1,
|
||||||
"Radar (EWR)": 0.05,
|
"Radar (EWR)": 0.05,
|
||||||
};
|
};
|
||||||
export const GROUND_UNIT_AIR_DEFENCE_REGEX: RegExp = /(\b(AAA|SAM|MANPADS?|[mM]anpads?)|[sS]tinger\b)/;
|
|
||||||
|
|
||||||
export const MAP_OPTIONS_DEFAULTS = {
|
export const MAP_OPTIONS_DEFAULTS = {
|
||||||
hideGroupMembers: true,
|
hideGroupMembers: true,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { AudioSink } from "./audio/audiosink";
|
import { AudioSink } from "./audio/audiosink";
|
||||||
import { AudioSource } from "./audio/audiosource";
|
import { AudioSource } from "./audio/audiosource";
|
||||||
import { OlympusState, OlympusSubState } from "./constants/constants";
|
import { OlympusState, OlympusSubState } from "./constants/constants";
|
||||||
import { OlympusConfig, ServerStatus } from "./interfaces";
|
import { CommandModeOptions, OlympusConfig, ServerStatus } from "./interfaces";
|
||||||
import { CoalitionCircle } from "./map/coalitionarea/coalitioncircle";
|
import { CoalitionCircle } from "./map/coalitionarea/coalitioncircle";
|
||||||
import { CoalitionPolygon } from "./map/coalitionarea/coalitionpolygon";
|
import { CoalitionPolygon } from "./map/coalitionarea/coalitionpolygon";
|
||||||
import { Airbase } from "./mission/airbase";
|
import { Airbase } from "./mission/airbase";
|
||||||
@@ -220,23 +220,20 @@ export class SelectedUnitsChangedEvent {
|
|||||||
|
|
||||||
/************** Command mode events ***************/
|
/************** Command mode events ***************/
|
||||||
export class CommandModeOptionsChangedEvent {
|
export class CommandModeOptionsChangedEvent {
|
||||||
/* TODO: add command mode options */
|
static on(callback: (options: CommandModeOptions) => void) {
|
||||||
static on(callback: () => void) {
|
|
||||||
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
||||||
callback();
|
callback(ev.detail);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static dispatch() {
|
static dispatch(options: CommandModeOptions) {
|
||||||
document.dispatchEvent(new CustomEvent(this.name));
|
document.dispatchEvent(new CustomEvent(this.name, {detail: options}));
|
||||||
console.log(`Event ${this.name} dispatched`);
|
console.log(`Event ${this.name} dispatched`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/************** Audio backend events ***************/
|
/************** Audio backend events ***************/
|
||||||
/* TODO: split into two events for single source changed */
|
|
||||||
export class AudioSourcesChangedEvent {
|
export class AudioSourcesChangedEvent {
|
||||||
/* TODO add audio sources */
|
|
||||||
static on(callback: (audioSources: AudioSource[]) => void) {
|
static on(callback: (audioSources: AudioSource[]) => void) {
|
||||||
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
||||||
callback(ev.detail);
|
callback(ev.detail);
|
||||||
@@ -250,7 +247,6 @@ export class AudioSourcesChangedEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: split into two events for single sink changed */
|
|
||||||
export class AudioSinksChangedEvent {
|
export class AudioSinksChangedEvent {
|
||||||
static on(callback: (audioSinks: AudioSink[]) => void) {
|
static on(callback: (audioSinks: AudioSink[]) => void) {
|
||||||
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
||||||
@@ -266,7 +262,6 @@ export class AudioSinksChangedEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class SRSClientsChangedEvent {
|
export class SRSClientsChangedEvent {
|
||||||
/* TODO add clients */
|
|
||||||
static on(callback: () => void) {
|
static on(callback: () => void) {
|
||||||
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
document.addEventListener(this.name, (ev: CustomEventInit) => {
|
||||||
callback();
|
callback();
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import {
|
|||||||
HiddenTypesChangedEvent,
|
HiddenTypesChangedEvent,
|
||||||
MapOptionsChangedEvent,
|
MapOptionsChangedEvent,
|
||||||
MapSourceChangedEvent,
|
MapSourceChangedEvent,
|
||||||
|
UnitUpdatedEvent,
|
||||||
} from "../events";
|
} from "../events";
|
||||||
import { ContextActionSet } from "../unit/contextactionset";
|
import { ContextActionSet } from "../unit/contextactionset";
|
||||||
|
|
||||||
@@ -72,7 +73,6 @@ export class Map extends L.Map {
|
|||||||
#selecting: boolean = false;
|
#selecting: boolean = false;
|
||||||
|
|
||||||
/* Camera keyboard panning control */
|
/* Camera keyboard panning control */
|
||||||
// TODO add back
|
|
||||||
defaultPanDelta: number = 100;
|
defaultPanDelta: number = 100;
|
||||||
#panInterval: number | null = null;
|
#panInterval: number | null = null;
|
||||||
#panLeft: boolean = false;
|
#panLeft: boolean = false;
|
||||||
@@ -199,10 +199,10 @@ export class Map extends L.Map {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//document.addEventListener("unitUpdated", (ev: CustomEvent) => {
|
UnitUpdatedEvent.on((unit) => {
|
||||||
// if (this.#centerUnit != null && ev.detail == this.#centerUnit)
|
if (this.#centeredUnit != null && unit == this.#centeredUnit)
|
||||||
// this.#panToUnit(this.#centerUnit);
|
this.#panToUnit(this.#centeredUnit);
|
||||||
//});
|
})
|
||||||
|
|
||||||
MapOptionsChangedEvent.on((options) => {
|
MapOptionsChangedEvent.on((options) => {
|
||||||
this.getContainer().toggleAttribute("data-hide-labels", !options.showUnitLabels);
|
this.getContainer().toggleAttribute("data-hide-labels", !options.showUnitLabels);
|
||||||
@@ -610,6 +610,7 @@ export class Map extends L.Map {
|
|||||||
if (unit !== null) {
|
if (unit !== null) {
|
||||||
this.options.scrollWheelZoom = "center";
|
this.options.scrollWheelZoom = "center";
|
||||||
this.#centeredUnit = unit;
|
this.#centeredUnit = unit;
|
||||||
|
this.#panToUnit(unit);
|
||||||
} else {
|
} else {
|
||||||
this.options.scrollWheelZoom = undefined;
|
this.options.scrollWheelZoom = undefined;
|
||||||
this.#centeredUnit = null;
|
this.#centeredUnit = null;
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ export class MissionManager {
|
|||||||
this.refreshSpawnPoints();
|
this.refreshSpawnPoints();
|
||||||
|
|
||||||
if (commandModeOptionsChanged) {
|
if (commandModeOptionsChanged) {
|
||||||
CommandModeOptionsChangedEvent.dispatch();
|
CommandModeOptionsChangedEvent.dispatch(this.#commandModeOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
document
|
document
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import { OlympusConfig } from "./interfaces";
|
|||||||
|
|
||||||
export var VERSION = "{{OLYMPUS_VERSION_NUMBER}}";
|
export var VERSION = "{{OLYMPUS_VERSION_NUMBER}}";
|
||||||
export var IP = window.location.toString();
|
export var IP = window.location.toString();
|
||||||
export var connectedToServer = true; // TODO Temporary
|
|
||||||
|
|
||||||
export class OlympusApp {
|
export class OlympusApp {
|
||||||
/* Global data */
|
/* Global data */
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { ShortcutKeyboardOptions, ShortcutMouseOptions } from "../interfaces";
|
|||||||
import { getApp } from "../olympusapp";
|
import { getApp } from "../olympusapp";
|
||||||
import { ShortcutKeyboard, ShortcutMouse } from "./shortcut";
|
import { ShortcutKeyboard, ShortcutMouse } from "./shortcut";
|
||||||
|
|
||||||
const DEFAULT_CONTEXT = "Default context"; // TODO remove context
|
|
||||||
|
|
||||||
export class ShortcutManager {
|
export class ShortcutManager {
|
||||||
#items: { [key: string]: any } = {};
|
#items: { [key: string]: any } = {};
|
||||||
#keysBeingHeld: string[] = [];
|
#keysBeingHeld: string[] = [];
|
||||||
@@ -138,7 +136,7 @@ export class ShortcutManager {
|
|||||||
this.addKeyboardShortcut(`pan${code}keydown`, {
|
this.addKeyboardShortcut(`pan${code}keydown`, {
|
||||||
altKey: false,
|
altKey: false,
|
||||||
callback: (ev: KeyboardEvent) => {
|
callback: (ev: KeyboardEvent) => {
|
||||||
//getApp().getMap().handleMapPanning(ev);
|
getApp().getMap().handleMapPanning(ev);
|
||||||
},
|
},
|
||||||
code: code,
|
code: code,
|
||||||
ctrlKey: false,
|
ctrlKey: false,
|
||||||
@@ -147,7 +145,7 @@ export class ShortcutManager {
|
|||||||
|
|
||||||
this.addKeyboardShortcut(`pan${code}keyup`, {
|
this.addKeyboardShortcut(`pan${code}keyup`, {
|
||||||
callback: (ev: KeyboardEvent) => {
|
callback: (ev: KeyboardEvent) => {
|
||||||
//getApp().getMap().handleMapPanning(ev);
|
getApp().getMap().handleMapPanning(ev);
|
||||||
},
|
},
|
||||||
code: code,
|
code: code,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { UnitBlueprint } from "../../interfaces";
|
|||||||
import { faArrowRight } from "@fortawesome/free-solid-svg-icons/faArrowRight";
|
import { faArrowRight } from "@fortawesome/free-solid-svg-icons/faArrowRight";
|
||||||
|
|
||||||
export function OlUnitListEntry(props: { icon: IconProp; blueprint: UnitBlueprint; onClick: () => void }) {
|
export function OlUnitListEntry(props: { icon: IconProp; blueprint: UnitBlueprint; onClick: () => void }) {
|
||||||
|
const pillString = !["aircraft", "helicopter"].includes(props.blueprint.category) ? props.blueprint.type : props.blueprint.abilities;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={props.onClick}
|
onClick={props.onClick}
|
||||||
@@ -16,18 +17,16 @@ export function OlUnitListEntry(props: { icon: IconProp; blueprint: UnitBlueprin
|
|||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={props.icon} className="text-sm"></FontAwesomeIcon>
|
<FontAwesomeIcon icon={props.icon} className="text-sm"></FontAwesomeIcon>
|
||||||
<div className="flex-1 px-2 text-left font-normal">{props.blueprint.label}</div>
|
<div className="flex-1 px-2 text-left font-normal">{props.blueprint.label}</div>
|
||||||
<div
|
{pillString && (
|
||||||
className={`
|
<div
|
||||||
rounded-full bg-olympus-800 px-2 py-0.5 text-xs font-bold
|
className={`
|
||||||
dark:text-olympus-50
|
rounded-full bg-olympus-800 px-2 py-0.5 text-xs font-bold
|
||||||
`}
|
dark:text-olympus-50
|
||||||
>
|
`}
|
||||||
{props.blueprint.era === "WW2"
|
>
|
||||||
? "WW2"
|
{!["aircraft", "helicopter"].includes(props.blueprint.category) ? props.blueprint.type : props.blueprint.abilities}
|
||||||
: props.blueprint.era.split(" ").map((word) => {
|
</div>
|
||||||
return word.charAt(0).toLocaleUpperCase();
|
)}
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faArrowRight}
|
icon={faArrowRight}
|
||||||
className={`
|
className={`
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { faSkull, faCamera, faFlag, faLink, faUnlink, faBars, faVolumeHigh } fro
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { OlDropdownItem, OlDropdown } from "../components/oldropdown";
|
import { OlDropdownItem, OlDropdown } from "../components/oldropdown";
|
||||||
import { OlLabelToggle } from "../components/ollabeltoggle";
|
import { OlLabelToggle } from "../components/ollabeltoggle";
|
||||||
import { getApp, IP, connectedToServer } from "../../olympusapp";
|
import { getApp, IP } from "../../olympusapp";
|
||||||
import {
|
import {
|
||||||
olButtonsVisibilityAirbase,
|
olButtonsVisibilityAirbase,
|
||||||
olButtonsVisibilityAircraft,
|
olButtonsVisibilityAircraft,
|
||||||
@@ -108,15 +108,6 @@ export function Header() {
|
|||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{IP}
|
{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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -42,24 +42,33 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
|
|||||||
const [types, setTypes] = useState({ groundunit: [] as string[], navyunit: [] as string[] });
|
const [types, setTypes] = useState({ groundunit: [] as string[], navyunit: [] as string[] });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedRole)
|
if (selectedRole) setBlueprints(getApp()?.getUnitsManager().getDatabase().getByRole(selectedRole));
|
||||||
setBlueprints(getApp()?.getUnitsManager().getDatabase().getByRole(selectedRole));
|
else if (selectedType) setBlueprints(getApp()?.getUnitsManager().getDatabase().getByType(selectedType));
|
||||||
else if (selectedType)
|
else setBlueprints(getApp()?.getUnitsManager().getDatabase().getBlueprints());
|
||||||
setBlueprints(getApp()?.getUnitsManager().getDatabase().getByType(selectedType));
|
|
||||||
else
|
|
||||||
setBlueprints(getApp()?.getUnitsManager().getDatabase().getBlueprints())
|
|
||||||
}, [selectedRole, selectedType, openAccordion]);
|
}, [selectedRole, selectedType, openAccordion]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
UnitDatabaseLoadedEvent.on(() => {
|
UnitDatabaseLoadedEvent.on(() => {
|
||||||
setRoles({
|
setRoles({
|
||||||
aircraft: getApp()?.getUnitsManager().getDatabase().getRoles((unit) => unit.category === "aircraft"),
|
aircraft: getApp()
|
||||||
helicopter: getApp()?.getUnitsManager().getDatabase().getRoles((unit) => unit.category === "helicopter"),
|
?.getUnitsManager()
|
||||||
|
.getDatabase()
|
||||||
|
.getRoles((unit) => unit.category === "aircraft"),
|
||||||
|
helicopter: getApp()
|
||||||
|
?.getUnitsManager()
|
||||||
|
.getDatabase()
|
||||||
|
.getRoles((unit) => unit.category === "helicopter"),
|
||||||
});
|
});
|
||||||
|
|
||||||
setTypes({
|
setTypes({
|
||||||
groundunit: getApp()?.getUnitsManager().getDatabase().getTypes((unit) => unit.category === "groundunit"),
|
groundunit: getApp()
|
||||||
navyunit: getApp()?.getUnitsManager().getDatabase().getTypes((unit) => unit.category === "navyunit")
|
?.getUnitsManager()
|
||||||
|
.getDatabase()
|
||||||
|
.getTypes((unit) => unit.category === "groundunit"),
|
||||||
|
navyunit: getApp()
|
||||||
|
?.getUnitsManager()
|
||||||
|
.getDatabase()
|
||||||
|
.getTypes((unit) => unit.category === "navyunit"),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
@@ -193,7 +202,7 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
|
|||||||
</div>
|
</div>
|
||||||
</OlAccordion>
|
</OlAccordion>
|
||||||
<OlAccordion
|
<OlAccordion
|
||||||
title={`Surfact to Air Missiles (SAM sites)`}
|
title={`Surface to Air Missiles (SAM sites)`}
|
||||||
open={openAccordion == CategoryAccordion.SAM}
|
open={openAccordion == CategoryAccordion.SAM}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOpenAccordion(openAccordion === CategoryAccordion.SAM ? CategoryAccordion.NONE : CategoryAccordion.SAM);
|
setOpenAccordion(openAccordion === CategoryAccordion.SAM ? CategoryAccordion.NONE : CategoryAccordion.SAM);
|
||||||
@@ -208,7 +217,7 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
|
|||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{filteredBlueprints
|
{filteredBlueprints
|
||||||
.filter((blueprint) => blueprint.category === "groundunit")
|
.filter((blueprint) => blueprint.category === "groundunit" && blueprint.type === "SAM Site")
|
||||||
.map((entry) => {
|
.map((entry) => {
|
||||||
return <OlUnitListEntry key={entry.name} icon={olButtonsVisibilityGroundunitSam} blueprint={entry} onClick={() => setBlueprint(entry)} />;
|
return <OlUnitListEntry key={entry.name} icon={olButtonsVisibilityGroundunitSam} blueprint={entry} onClick={() => setBlueprint(entry)} />;
|
||||||
})}
|
})}
|
||||||
@@ -230,7 +239,7 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
|
|||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{filteredBlueprints
|
{filteredBlueprints
|
||||||
.filter((blueprint) => blueprint.category === "groundunit")
|
.filter((blueprint) => blueprint.canAAA)
|
||||||
.map((entry) => {
|
.map((entry) => {
|
||||||
return <OlUnitListEntry key={entry.name} icon={olButtonsVisibilityGroundunitSam} blueprint={entry} onClick={() => setBlueprint(entry)} />;
|
return <OlUnitListEntry key={entry.name} icon={olButtonsVisibilityGroundunitSam} blueprint={entry} onClick={() => setBlueprint(entry)} />;
|
||||||
})}
|
})}
|
||||||
@@ -248,9 +257,7 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
|
|||||||
<div className="mb-2 flex flex-wrap gap-1">
|
<div className="mb-2 flex flex-wrap gap-1">
|
||||||
{types.groundunit
|
{types.groundunit
|
||||||
.sort()
|
.sort()
|
||||||
.filter((type) => {
|
.filter((type) => type !== "SAM Site")
|
||||||
return type !== "AAA" && type !== "SAM Site";
|
|
||||||
})
|
|
||||||
.map((type) => {
|
.map((type) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -278,7 +285,7 @@ export function SpawnMenu(props: { open: boolean; onClose: () => void; children?
|
|||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{filteredBlueprints
|
{filteredBlueprints
|
||||||
.filter((blueprint) => blueprint.category === "groundunit")
|
.filter((blueprint) => blueprint.category === "groundunit" && blueprint.type !== "SAM Site")
|
||||||
.map((entry) => {
|
.map((entry) => {
|
||||||
return <OlUnitListEntry key={entry.name} icon={olButtonsVisibilityGroundunit} blueprint={entry} onClick={() => setBlueprint(entry)} />;
|
return <OlUnitListEntry key={entry.name} icon={olButtonsVisibilityGroundunit} blueprint={entry} onClick={() => setBlueprint(entry)} />;
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { OlRangeSlider } from "../components/olrangeslider";
|
|||||||
import { getApp } from "../../olympusapp";
|
import { getApp } from "../../olympusapp";
|
||||||
import { OlButtonGroup, OlButtonGroupItem } from "../components/olbuttongroup";
|
import { OlButtonGroup, OlButtonGroupItem } from "../components/olbuttongroup";
|
||||||
import { OlCheckbox } from "../components/olcheckbox";
|
import { OlCheckbox } from "../components/olcheckbox";
|
||||||
import { ROEs, emissionsCountermeasures, reactionsToThreat } from "../../constants/constants";
|
import { ROEs, altitudeIncrements, emissionsCountermeasures, maxAltitudeValues, minAltitudeValues, reactionsToThreat, speedIncrements } from "../../constants/constants";
|
||||||
import { OlToggle } from "../components/oltoggle";
|
import { OlToggle } from "../components/oltoggle";
|
||||||
import { OlCoalitionToggle } from "../components/olcoalitiontoggle";
|
import { OlCoalitionToggle } from "../components/olcoalitiontoggle";
|
||||||
import {
|
import {
|
||||||
@@ -202,26 +202,28 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
|
|||||||
return category === "Helicopter";
|
return category === "Helicopter";
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: use constants
|
/* Speed/altitude increments */
|
||||||
const minAltitude = 0;
|
const minAltitude = 0;
|
||||||
const minSpeed = 0;
|
const minSpeed = 0;
|
||||||
|
|
||||||
let maxAltitude = 60000;
|
let maxAltitude = maxAltitudeValues.aircraft;
|
||||||
let maxSpeed = 800;
|
let maxSpeed = minAltitudeValues.aircraft;
|
||||||
|
let speedStep = speedIncrements.aircraft;
|
||||||
let altitudeStep = 500;
|
let altitudeStep = altitudeIncrements.aircraft;
|
||||||
let speedStep = 10;
|
|
||||||
|
|
||||||
if (everyUnitIsHelicopter) {
|
if (everyUnitIsHelicopter) {
|
||||||
maxAltitude = 20000;
|
maxAltitude = maxAltitudeValues.helicopter;
|
||||||
maxSpeed = 200;
|
maxSpeed = minAltitudeValues.helicopter;
|
||||||
speedStep = 5;
|
speedStep = speedIncrements.helicopter;
|
||||||
altitudeStep = 100;
|
altitudeStep = altitudeIncrements.helicopter;
|
||||||
}
|
}
|
||||||
|
else if (everyUnitIsGround) {
|
||||||
if (everyUnitIsGround || everyUnitIsNavy) {
|
maxSpeed = minAltitudeValues.groundunit;
|
||||||
maxSpeed = 60;
|
speedStep = speedIncrements.groundunit;
|
||||||
speedStep = 1;
|
}
|
||||||
|
else if (everyUnitIsNavy) {
|
||||||
|
maxSpeed = minAltitudeValues.navyunit;
|
||||||
|
speedStep = speedIncrements.navyunit;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -654,7 +656,6 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* ============== Rules of Engagement END ============== */}
|
{/* ============== Rules of Engagement END ============== */}
|
||||||
|
|
||||||
{selectedCategories.every((category) => {
|
{selectedCategories.every((category) => {
|
||||||
return ["Aircraft", "Helicopter"].includes(category);
|
return ["Aircraft", "Helicopter"].includes(category);
|
||||||
}) && (
|
}) && (
|
||||||
|
|||||||
@@ -11,15 +11,15 @@ import { getApp } from "../../olympusapp";
|
|||||||
import { ftToM } from "../../other/utils";
|
import { ftToM } from "../../other/utils";
|
||||||
import { LatLng } from "leaflet";
|
import { LatLng } from "leaflet";
|
||||||
import { Airbase } from "../../mission/airbase";
|
import { Airbase } from "../../mission/airbase";
|
||||||
import { OlympusState, SpawnSubState } from "../../constants/constants";
|
import { altitudeIncrements, groupUnitCount, maxAltitudeValues, minAltitudeValues, OlympusState, SpawnSubState } from "../../constants/constants";
|
||||||
|
|
||||||
export function UnitSpawnMenu(props: { blueprint: UnitBlueprint; spawnAtLocation: boolean; airbase?: Airbase | null; coalition?: Coalition }) {
|
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 = groupUnitCount[props.blueprint.category];
|
||||||
const minAltitude = 0;
|
const minAltitude = minAltitudeValues[props.blueprint.category];
|
||||||
const maxAltitude = 30000;
|
const maxAltitude = maxAltitudeValues[props.blueprint.category];
|
||||||
const altitudeStep = 500;
|
const altitudeStep = altitudeIncrements[props.blueprint.category];
|
||||||
|
|
||||||
/* State initialization */
|
/* State initialization */
|
||||||
const [spawnCoalition, setSpawnCoalition] = useState("blue" as Coalition);
|
const [spawnCoalition, setSpawnCoalition] = useState("blue" as Coalition);
|
||||||
@@ -53,7 +53,7 @@ export function UnitSpawnMenu(props: { blueprint: UnitBlueprint; spawnAtLocation
|
|||||||
},
|
},
|
||||||
coalition: spawnCoalition,
|
coalition: spawnCoalition,
|
||||||
});
|
});
|
||||||
getApp().setState(OlympusState.SPAWN, SpawnSubState.SPAWN_UNIT)
|
getApp().setState(OlympusState.SPAWN, SpawnSubState.SPAWN_UNIT);
|
||||||
} else {
|
} else {
|
||||||
if (getApp().getState() === OlympusState.SPAWN) getApp().setState(OlympusState.IDLE);
|
if (getApp().getState() === OlympusState.SPAWN) getApp().setState(OlympusState.IDLE);
|
||||||
}
|
}
|
||||||
@@ -141,98 +141,102 @@ export function UnitSpawnMenu(props: { blueprint: UnitBlueprint; spawnAtLocation
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
{["aircraft", "helicopter"].includes(props.blueprint.category) && (
|
||||||
<div
|
<>
|
||||||
className={`
|
<div>
|
||||||
flex flex-row content-center items-center justify-between
|
<div
|
||||||
`}
|
|
||||||
>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<span
|
|
||||||
className={`
|
className={`
|
||||||
font-normal
|
flex flex-row content-center items-center justify-between
|
||||||
dark:text-white
|
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
Altitude
|
<div className="flex flex-col">
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className={`
|
|
||||||
font-bold
|
|
||||||
dark:text-blue-500
|
|
||||||
`}
|
|
||||||
>{`${Intl.NumberFormat("en-US").format(spawnAltitude)} FT`}</span>
|
|
||||||
</div>
|
|
||||||
<OlLabelToggle toggled={spawnAltitudeType} leftLabel={"AGL"} rightLabel={"ASL"} onClick={() => setSpawnAltitudeType(!spawnAltitudeType)} />
|
|
||||||
</div>
|
|
||||||
<OlRangeSlider
|
|
||||||
onChange={(ev) => setSpawnAltitude(Number(ev.target.value))}
|
|
||||||
value={spawnAltitude}
|
|
||||||
min={minAltitude}
|
|
||||||
max={maxAltitude}
|
|
||||||
step={altitudeStep}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="flex flex-row content-center justify-between">
|
|
||||||
<span
|
|
||||||
className={`
|
|
||||||
h-8 font-normal
|
|
||||||
dark:text-white
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
Role
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<OlDropdown label={spawnRole} className="w-full">
|
|
||||||
{roles.map((role) => {
|
|
||||||
return (
|
|
||||||
<OlDropdownItem
|
|
||||||
onClick={() => {
|
|
||||||
setSpawnRole(role);
|
|
||||||
setSpawnLoadout("");
|
|
||||||
}}
|
|
||||||
className={`w-full`}
|
|
||||||
>
|
|
||||||
{role}
|
|
||||||
</OlDropdownItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</OlDropdown>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="flex flex-row content-center justify-between">
|
|
||||||
<span
|
|
||||||
className={`
|
|
||||||
h-8 font-normal
|
|
||||||
dark:text-white
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
Weapons
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<OlDropdown label={spawnLoadoutName} className={`w-full w-max-full`}>
|
|
||||||
{loadouts.map((loadout) => {
|
|
||||||
return (
|
|
||||||
<OlDropdownItem
|
|
||||||
onClick={() => {
|
|
||||||
setSpawnLoadout(loadout.name);
|
|
||||||
}}
|
|
||||||
className={`w-full`}
|
|
||||||
>
|
|
||||||
<span
|
<span
|
||||||
className={`
|
className={`
|
||||||
w-full overflow-hidden text-ellipsis text-nowrap text-left
|
font-normal
|
||||||
w-max-full
|
dark:text-white
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{loadout.name}
|
Altitude
|
||||||
</span>
|
</span>
|
||||||
</OlDropdownItem>
|
<span
|
||||||
);
|
className={`
|
||||||
})}
|
font-bold
|
||||||
</OlDropdown>
|
dark:text-blue-500
|
||||||
</div>
|
`}
|
||||||
|
>{`${Intl.NumberFormat("en-US").format(spawnAltitude)} FT`}</span>
|
||||||
|
</div>
|
||||||
|
<OlLabelToggle toggled={spawnAltitudeType} leftLabel={"AGL"} rightLabel={"ASL"} onClick={() => setSpawnAltitudeType(!spawnAltitudeType)} />
|
||||||
|
</div>
|
||||||
|
<OlRangeSlider
|
||||||
|
onChange={(ev) => setSpawnAltitude(Number(ev.target.value))}
|
||||||
|
value={spawnAltitude}
|
||||||
|
min={minAltitude}
|
||||||
|
max={maxAltitude}
|
||||||
|
step={altitudeStep}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex flex-row content-center justify-between">
|
||||||
|
<span
|
||||||
|
className={`
|
||||||
|
h-8 font-normal
|
||||||
|
dark:text-white
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
Role
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<OlDropdown label={spawnRole} className="w-full">
|
||||||
|
{roles.map((role) => {
|
||||||
|
return (
|
||||||
|
<OlDropdownItem
|
||||||
|
onClick={() => {
|
||||||
|
setSpawnRole(role);
|
||||||
|
setSpawnLoadout("");
|
||||||
|
}}
|
||||||
|
className={`w-full`}
|
||||||
|
>
|
||||||
|
{role}
|
||||||
|
</OlDropdownItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</OlDropdown>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex flex-row content-center justify-between">
|
||||||
|
<span
|
||||||
|
className={`
|
||||||
|
h-8 font-normal
|
||||||
|
dark:text-white
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
Weapons
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<OlDropdown label={spawnLoadoutName} className={`w-full w-max-full`}>
|
||||||
|
{loadouts.map((loadout) => {
|
||||||
|
return (
|
||||||
|
<OlDropdownItem
|
||||||
|
onClick={() => {
|
||||||
|
setSpawnLoadout(loadout.name);
|
||||||
|
}}
|
||||||
|
className={`w-full`}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={`
|
||||||
|
w-full overflow-hidden text-ellipsis text-nowrap
|
||||||
|
text-left w-max-full
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{loadout.name}
|
||||||
|
</span>
|
||||||
|
</OlDropdownItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</OlDropdown>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{spawnLoadout && spawnLoadout.items.length > 0 && (
|
{spawnLoadout && spawnLoadout.items.length > 0 && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -599,8 +599,7 @@ export abstract class Unit extends CustomMarker {
|
|||||||
const blueprint = getApp().getUnitsManager().getDatabase().getByName(this.#name);
|
const blueprint = getApp().getUnitsManager().getDatabase().getByName(this.#name);
|
||||||
this.#blueprint = blueprint ?? null;
|
this.#blueprint = blueprint ?? null;
|
||||||
/* Refresh the marker */
|
/* Refresh the marker */
|
||||||
this.remove();
|
this.#redrawMarker();
|
||||||
this.addTo(getApp().getMap());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update the blueprint to use when the unit is grouped */
|
/* Update the blueprint to use when the unit is grouped */
|
||||||
|
|||||||
Reference in New Issue
Block a user