import React, { MutableRefObject, useEffect, useRef, useState } from "react"; import { Menu } from "./components/menu"; import { Unit } from "../../unit/unit"; import { OlLabelToggle } from "../components/ollabeltoggle"; import { OlRangeSlider } from "../components/olrangeslider"; import { getApp } from "../../olympusapp"; import { OlButtonGroup, OlButtonGroupItem } from "../components/olbuttongroup"; import { OlCheckbox } from "../components/olcheckbox"; import { ROEs, emissionsCountermeasures, reactionsToThreat, } from "../../constants/constants"; import { OlToggle } from "../components/oltoggle"; import { OlCoalitionToggle } from "../components/olcoalitiontoggle"; import { olButtonsEmissionsAttack, olButtonsEmissionsDefend, olButtonsEmissionsFree, olButtonsEmissionsSilent, olButtonsIntensity1, olButtonsIntensity2, olButtonsIntensity3, olButtonsRoeDesignated, olButtonsRoeFree, olButtonsRoeHold, olButtonsRoeReturn, olButtonsScatter1, olButtonsScatter2, olButtonsScatter3, olButtonsThreatEvade, olButtonsThreatManoeuvre, olButtonsThreatNone, olButtonsThreatPassive, olButtonsVisibilityAircraft, olButtonsVisibilityDcs, olButtonsVisibilityGroundunit, olButtonsVisibilityGroundunitSam, olButtonsVisibilityHelicopter, olButtonsVisibilityHuman, olButtonsVisibilityNavyunit, olButtonsVisibilityOlympus, } from "../components/olicons"; import { Coalition } from "../../types/types"; import { ftToM, getUnitsByLabel, knotsToMs, mToFt, msToKnots, } from "../../other/utils"; import { FaGasPump, FaQuestionCircle } from "react-icons/fa"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { OlSearchBar } from "../components/olsearchbar"; import { OlDropdown, OlDropdownItem } from "../components/oldropdown"; import { UnitBlueprint } from "../../interfaces"; export function UnitControlMenu(props: { open: boolean; onClose: () => void }) { var [selectedUnits, setSelectedUnits] = useState([] as Unit[]); var [selectedUnitsData, setSelectedUnitsData] = useState({ desiredAltitude: undefined as undefined | number, desiredAltitudeType: undefined as undefined | string, desiredSpeed: undefined as undefined | number, desiredSpeedType: undefined as undefined | string, ROE: undefined as undefined | string, reactionToThreat: undefined as undefined | string, emissionsCountermeasures: undefined as undefined | string, shotsScatter: undefined as undefined | number, shotsIntensity: undefined as undefined | number, operateAs: undefined as undefined | string, followRoads: undefined as undefined | boolean, isActiveAWACS: undefined as undefined | boolean, isActiveTanker: undefined as undefined | boolean, onOff: undefined as undefined | boolean, }); var [selectionFilter, setSelectionFilter] = useState({ control: { human: true, dcs: true, olympus: true, }, blue: { aircraft: true, helicopter: true, "groundunit-sam": true, groundunit: true, navyunit: true, }, neutral: { aircraft: true, helicopter: true, "groundunit-sam": true, groundunit: true, navyunit: true, }, red: { aircraft: true, helicopter: true, "groundunit-sam": true, groundunit: true, navyunit: true, }, }); var [selectionBlueprint, setSelectionBlueprint] = useState( null as null | UnitBlueprint ); const [searchBarRefState, setSearchBarRefState] = useState( null as MutableRefObject | null ); const [filterString, setFilterString] = useState(""); var searchBarRef = useRef(null); useEffect(() => { if (!searchBarRefState) setSearchBarRefState(searchBarRef); if (!props.open && selectionBlueprint !== null) setSelectionBlueprint(null); if (!props.open && filterString !== "") setFilterString(""); }); /* */ const minAltitude = 0; const maxAltitude = getApp() ?.getUnitsManager() ?.getSelectedUnitsCategories() .every((category) => { return category === "Helicopter"; }) ? 20000 : 60000; const altitudeStep = getApp() ?.getUnitsManager() ?.getSelectedUnitsCategories() .every((category) => { return category === "Helicopter"; }) ? 100 : 500; const minSpeed = 0; const maxSpeed = getApp() ?.getUnitsManager() ?.getSelectedUnitsCategories() .every((category) => { return category === "Helicopter"; }) ? 200 : 800; const speedStep = getApp() ?.getUnitsManager() ?.getSelectedUnitsCategories() .every((category) => { return category === "Helicopter"; }) ? 5 : 10; /* When a unit is selected, open the menu */ document.addEventListener("unitsSelection", (ev: CustomEventInit) => { setSelectedUnits(ev.detail as Unit[]); updateData(); }); /* When a unit is deselected, refresh the view */ document.addEventListener("unitDeselection", (ev: CustomEventInit) => { /* TODO add delay to avoid doing it too many times */ updateData(); }); /* When all units are deselected clean the view */ document.addEventListener("clearSelection", () => { setSelectedUnits([]); }); /* Update the current values of the shown data */ function updateData() { const getters = { desiredAltitude: (unit: Unit) => { return Math.round(mToFt(unit.getDesiredAltitude())); }, desiredAltitudeType: (unit: Unit) => { return unit.getDesiredAltitudeType(); }, desiredSpeed: (unit: Unit) => { return Math.round(msToKnots(unit.getDesiredSpeed())); }, desiredSpeedType: (unit: Unit) => { return unit.getDesiredSpeedType(); }, ROE: (unit: Unit) => { return unit.getROE(); }, reactionToThreat: (unit: Unit) => { return unit.getReactionToThreat(); }, emissionsCountermeasures: (unit: Unit) => { return unit.getEmissionsCountermeasures(); }, shotsScatter: (unit: Unit) => { return unit.getShotsScatter(); }, shotsIntensity: (unit: Unit) => { return unit.getShotsIntensity(); }, operateAs: (unit: Unit) => { return unit.getOperateAs(); }, followRoads: (unit: Unit) => { return unit.getFollowRoads(); }, isActiveAWACS: (unit: Unit) => { return unit.getIsActiveAWACS(); }, isActiveTanker: (unit: Unit) => { return unit.getIsActiveTanker(); }, onOff: (unit: Unit) => { return unit.getOnOff(); }, } as { [key in keyof typeof selectedUnitsData]: (unit: Unit) => void }; var updatedData = selectedUnitsData; Object.entries(getters).forEach(([key, getter]) => { updatedData[key] = getApp() ?.getUnitsManager() ?.getSelectedUnitsVariable(getter); }); setSelectedUnitsData(updatedData); } /* Count how many units are selected of each type, divided by coalition */ var unitOccurences = { blue: {}, red: {}, neutral: {}, }; selectedUnits.forEach((unit) => { if (!(unit.getName() in unitOccurences[unit.getCoalition()])) unitOccurences[unit.getCoalition()][unit.getName()] = 1; else unitOccurences[unit.getCoalition()][unit.getName()]++; }); const selectedCategories = getApp()?.getUnitsManager()?.getSelectedUnitsCategories() ?? []; const [ filteredAircraft, filteredHelicopters, filteredAirDefense, filteredGroundUnits, filteredNavyUnits, ] = getUnitsByLabel(filterString); const mergedFilteredUnits = Object.assign( {}, filteredAircraft, filteredHelicopters, filteredAirDefense, filteredGroundUnits, filteredNavyUnits ) as { [key: string]: UnitBlueprint }; return ( 0 ? `Units selected (x${selectedUnits.length})` : `No units selected` } onClose={props.onClose} canBeHidden={true} > <> {selectedUnits.length == 0 && (
Selection tool
The selection tools allows you to select units depending on their category, coalition, and control mode. You can also select units depending on their specific type by using the search input.
Control mode
{Object.entries({ human: ["Human", olButtonsVisibilityHuman], olympus: ["Olympus controlled", olButtonsVisibilityOlympus], dcs: ["From DCS mission", olButtonsVisibilityDcs], }).map((entry) => { return (
{entry[1][0] as string} { selectionFilter["control"][entry[0]] = !selectionFilter["control"][entry[0]]; setSelectionFilter( JSON.parse(JSON.stringify(selectionFilter)) ); }} toggled={selectionFilter["control"][entry[0]]} />
); })}
Types and coalitions
{selectionBlueprint === null && Object.entries({ aircraft: olButtonsVisibilityAircraft, helicopter: olButtonsVisibilityHelicopter, "groundunit-sam": olButtonsVisibilityGroundunitSam, groundunit: olButtonsVisibilityGroundunit, navyunit: olButtonsVisibilityNavyunit, }).map((entry) => { return ( {["blue", "neutral", "red"].map((coalition) => { return ( ); })} ); })}
BLUE NEUTRAL RED
{ selectionFilter[coalition][entry[0]] = !selectionFilter[coalition][entry[0]]; setSelectionFilter( JSON.parse(JSON.stringify(selectionFilter)) ); }} />
value )} onChange={() => { const newValue = !Object.values( selectionFilter["blue"] ).some((value) => value); Object.keys(selectionFilter["blue"]).forEach((key) => { selectionFilter["blue"][key] = newValue; }); setSelectionFilter( JSON.parse(JSON.stringify(selectionFilter)) ); }} /> value )} onChange={() => { const newValue = !Object.values( selectionFilter["neutral"] ).some((value) => value); Object.keys(selectionFilter["neutral"]).forEach( (key) => { selectionFilter["neutral"][key] = newValue; } ); setSelectionFilter( JSON.parse(JSON.stringify(selectionFilter)) ); }} /> value )} onChange={() => { const newValue = !Object.values( selectionFilter["red"] ).some((value) => value); Object.keys(selectionFilter["red"]).forEach((key) => { selectionFilter["red"][key] = newValue; }); setSelectionFilter( JSON.parse(JSON.stringify(selectionFilter)) ); }} />
{ setFilterString(value); selectionBlueprint && setSelectionBlueprint(null); }} text={ selectionBlueprint ? selectionBlueprint.label : filterString } />
{filterString !== "" && Object.keys(mergedFilteredUnits).length > 0 && Object.entries(mergedFilteredUnits).map((entry) => { const blueprint = entry[1]; return ( { setSelectionBlueprint(blueprint); }} > {blueprint.label} ); })} {Object.keys(mergedFilteredUnits).length == 0 && ( No results )}
)} <> {selectedUnits.length > 0 && ( <> {/* Units list */}
{ <> {["blue", "red", "neutral"].map((coalition) => { return Object.keys(unitOccurences[coalition]).map( (name) => { return (
{name} x{unitOccurences[coalition][name]}
); } ); })} }
{ /* Altitude selector */ selectedCategories.every((category) => { return ["Aircraft", "Helicopter"].includes(category); }) && (
Altitude {selectedUnitsData.desiredAltitude !== undefined ? Intl.NumberFormat("en-US").format( selectedUnitsData.desiredAltitude ) + " FT" : "Different values"}
{ selectedUnits.forEach((unit) => { unit.setAltitudeType( selectedUnitsData.desiredAltitudeType === "ASL" ? "AGL" : "ASL" ); setSelectedUnitsData({ ...selectedUnitsData, desiredAltitudeType: selectedUnitsData.desiredAltitudeType === "ASL" ? "AGL" : "ASL", }); }); }} />
{ selectedUnits.forEach((unit) => { unit.setAltitude(ftToM(Number(ev.target.value))); setSelectedUnitsData({ ...selectedUnitsData, desiredAltitude: Number(ev.target.value), }); }); }} value={selectedUnitsData.desiredAltitude} min={minAltitude} max={maxAltitude} step={altitudeStep} />
) } {/* Airspeed selector */}
Speed {selectedUnitsData.desiredSpeed !== undefined ? selectedUnitsData.desiredSpeed + " KTS" : "Different values"}
{ selectedUnits.forEach((unit) => { unit.setSpeedType( selectedUnitsData.desiredSpeedType === "CAS" ? "GS" : "CAS" ); setSelectedUnitsData({ ...selectedUnitsData, desiredSpeedType: selectedUnitsData.desiredSpeedType === "CAS" ? "GS" : "CAS", }); }); }} />
{ selectedUnits.forEach((unit) => { unit.setSpeed(knotsToMs(Number(ev.target.value))); setSelectedUnitsData({ ...selectedUnitsData, desiredSpeed: Number(ev.target.value), }); }); }} value={selectedUnitsData.desiredSpeed} min={minSpeed} max={maxSpeed} step={speedStep} />
Rules of engagement {[ olButtonsRoeHold, olButtonsRoeReturn, olButtonsRoeDesignated, olButtonsRoeFree, ].map((icon, idx) => { return ( { selectedUnits.forEach((unit) => { unit.setROE(ROEs[idx]); setSelectedUnitsData({ ...selectedUnitsData, ROE: ROEs[idx], }); }); }} active={selectedUnitsData.ROE === ROEs[idx]} icon={icon} /> ); })}
{selectedCategories.every((category) => { return ["Aircraft", "Helicopter"].includes(category); }) && ( <> {" "}
Threat reaction {[ olButtonsThreatNone, olButtonsThreatPassive, olButtonsThreatManoeuvre, olButtonsThreatEvade, ].map((icon, idx) => { return ( { selectedUnits.forEach((unit) => { unit.setReactionToThreat( reactionsToThreat[idx] ); setSelectedUnitsData({ ...selectedUnitsData, reactionToThreat: reactionsToThreat[idx], }); }); }} active={ selectedUnitsData.reactionToThreat === reactionsToThreat[idx] } icon={icon} /> ); })}
Radar and ECM {[ olButtonsEmissionsSilent, olButtonsEmissionsDefend, olButtonsEmissionsAttack, olButtonsEmissionsFree, ].map((icon, idx) => { return ( { selectedUnits.forEach((unit) => { unit.setEmissionsCountermeasures( emissionsCountermeasures[idx] ); setSelectedUnitsData({ ...selectedUnitsData, emissionsCountermeasures: emissionsCountermeasures[idx], }); }); }} active={ selectedUnitsData.emissionsCountermeasures === emissionsCountermeasures[idx] } icon={icon} /> ); })}
)} {getApp() ?.getUnitsManager() ?.getSelectedUnitsVariable((unit) => { return unit.isTanker(); }) && (
{" "} Act as tanker{" "} { selectedUnits.forEach((unit) => { unit.setAdvancedOptions( !selectedUnitsData.isActiveTanker, unit.getIsActiveAWACS(), unit.getTACAN(), unit.getRadio(), unit.getGeneralSettings() ); setSelectedUnitsData({ ...selectedUnitsData, isActiveTanker: !selectedUnitsData.isActiveTanker, }); }); }} />
)} {getApp() ?.getUnitsManager() ?.getSelectedUnitsVariable((unit) => { return unit.isAWACS(); }) && (
{" "} Act as AWACS{" "} { selectedUnits.forEach((unit) => { unit.setAdvancedOptions( unit.getIsActiveTanker(), !selectedUnitsData.isActiveAWACS, unit.getTACAN(), unit.getRadio(), unit.getGeneralSettings() ); setSelectedUnitsData({ ...selectedUnitsData, isActiveAWACS: !selectedUnitsData.isActiveAWACS, }); }); }} />
)} {selectedCategories.every((category) => { return ["GroundUnit", "NavyUnit"].includes(category); }) && ( <> {" "}
Shots scatter {[ olButtonsScatter1, olButtonsScatter2, olButtonsScatter3, ].map((icon, idx) => { return ( { selectedUnits.forEach((unit) => { unit.setShotsScatter(idx + 1); setSelectedUnitsData({ ...selectedUnitsData, shotsScatter: idx + 1, }); }); }} active={selectedUnitsData.shotsScatter === idx + 1} icon={icon} /> ); })}
Shots intensity {[ olButtonsIntensity1, olButtonsIntensity2, olButtonsIntensity3, ].map((icon, idx) => { return ( { selectedUnits.forEach((unit) => { unit.setShotsIntensity(idx + 1); setSelectedUnitsData({ ...selectedUnitsData, shotsIntensity: idx + 1, }); }); }} active={ selectedUnitsData.shotsIntensity === idx + 1 } icon={icon} /> ); })}
{" "} Operate as{" "} { selectedUnits.forEach((unit) => { unit.setOperateAs( selectedUnitsData.operateAs === "blue" ? "red" : "blue" ); setSelectedUnitsData({ ...selectedUnitsData, operateAs: selectedUnitsData.operateAs === "blue" ? "red" : "blue", }); }); }} />
{" "} Follow roads{" "} { selectedUnits.forEach((unit) => { unit.setFollowRoads(!selectedUnitsData.followRoads); setSelectedUnitsData({ ...selectedUnitsData, followRoads: !selectedUnitsData.followRoads, }); }); }} />
{" "} Unit active{" "} { selectedUnits.forEach((unit) => { unit.setOnOff(!selectedUnitsData.onOff); setSelectedUnitsData({ ...selectedUnitsData, onOff: !selectedUnitsData.onOff, }); }); }} />
)}
<> {selectedUnits.length === 1 && (
40 && `bg-green-700`} ${ selectedUnits[0].getFuel() > 10 && selectedUnits[0].getFuel() <= 40 && `bg-yellow-700` } ${selectedUnits[0].getFuel() <= 10 && `bg-red-700`} px-2 py-1 text-sm font-bold text-white `} > {selectedUnits[0].getFuel()}%
{selectedUnits[0].getAmmo().map((ammo) => { return (
{ammo.quantity}
{ammo.name}
); })}
)} )}
); }