More work adding back missing functions

This commit is contained in:
Davide Passoni
2024-11-07 10:05:27 +01:00
parent 6d551c51eb
commit bf7115288b
14 changed files with 792 additions and 493 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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();

View File

@@ -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;

View File

@@ -230,7 +230,7 @@ export class MissionManager {
this.refreshSpawnPoints(); this.refreshSpawnPoints();
if (commandModeOptionsChanged) { if (commandModeOptionsChanged) {
CommandModeOptionsChangedEvent.dispatch(); CommandModeOptionsChangedEvent.dispatch(this.#commandModeOptions);
} }
document document

View File

@@ -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 */

View File

@@ -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,
}); });

View File

@@ -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={`

View File

@@ -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>

View File

@@ -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)} />;
})} })}

View File

@@ -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);
}) && ( }) && (

View File

@@ -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

View File

@@ -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 */