import { faCompass } from "@fortawesome/free-regular-svg-icons"; import { faCamera, faDrawPolygon, faFlag, faObjectGroup, faSkull, faTriangleExclamation, faVolumeHigh, faWifi } from "@fortawesome/free-solid-svg-icons"; import React, { useEffect, useRef, useState } from "react"; import { FaCheck, FaRedo, FaSpinner } from "react-icons/fa"; import { FaChevronLeft, FaChevronRight, FaFloppyDisk } from "react-icons/fa6"; import { AudioManagerState, BLUE_COMMANDER, COMMAND_MODE_OPTIONS_DEFAULTS, GAME_MASTER, MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS, RED_COMMANDER, } from "../../constants/constants"; import { AudioManagerStateChangedEvent, CommandModeOptionsChangedEvent, ConfigLoadedEvent, EnabledCommandModesChangedEvent, HiddenTypesChangedEvent, MapOptionsChangedEvent, MapSourceChangedEvent, SessionDataChangedEvent, SessionDataLoadedEvent, SessionDataSavedEvent, } from "../../events"; import { OlympusConfig } from "../../interfaces"; import { getApp, IP, VERSION } from "../../olympusapp"; import { OlDropdown, OlDropdownItem } from "../components/oldropdown"; import { OlExpandingTooltip } from "../components/olexpandingtooltip"; import { olButtonsVisibilityAirbase, olButtonsVisibilityAircraft, olButtonsVisibilityDcs, olButtonsVisibilityGroundunit, olButtonsVisibilityGroundunitSam, olButtonsVisibilityHelicopter, olButtonsVisibilityHuman, olButtonsVisibilityNavyunit, olButtonsVisibilityOlympus, } from "../components/olicons"; import { OlLabelToggle } from "../components/ollabeltoggle"; import { OlLockStateButton, OlRoundStateButton, OlStateButton } from "../components/olstatebutton"; export function Header() { const [mapHiddenTypes, setMapHiddenTypes] = useState(MAP_HIDDEN_TYPES_DEFAULTS); const [mapOptions, setMapOptions] = useState(MAP_OPTIONS_DEFAULTS); const [mapSource, setMapSource] = useState(""); const [mapSources, setMapSources] = useState([] as string[]); const [scrolledLeft, setScrolledLeft] = useState(true); const [scrolledRight, setScrolledRight] = useState(false); const [audioState, setAudioState] = useState(AudioManagerState.STOPPED); const [commandModeOptions, setCommandModeOptions] = useState(COMMAND_MODE_OPTIONS_DEFAULTS); const [savingSessionData, setSavingSessionData] = useState(false); const [latestVersion, setLatestVersion] = useState(""); const [isLatestVersion, setIsLatestVersion] = useState(false); const [isBetaVersion, setIsBetaVersion] = useState(false); const [isDevVersion, setIsDevVersion] = useState(false); const [enabledCommandModes, setEnabledCommandModes] = useState([] as string[]); const [loadingNewCommandMode, setLoadingNewCommandMode] = useState(false); const unitViewTypesFilter = { aircraft: olButtonsVisibilityAircraft, helicopter: olButtonsVisibilityHelicopter, "groundunit-sam": olButtonsVisibilityGroundunitSam, groundunit: olButtonsVisibilityGroundunit, navyunit: olButtonsVisibilityNavyunit, airbase: olButtonsVisibilityAirbase, dead: faSkull, }; useEffect(() => { HiddenTypesChangedEvent.on((hiddenTypes) => setMapHiddenTypes({ ...hiddenTypes })); MapOptionsChangedEvent.on((mapOptions) => { setMapOptions({ ...mapOptions }); }); MapSourceChangedEvent.on((source) => { setMapSource(source); }); ConfigLoadedEvent.on((config: OlympusConfig) => { // Timeout needed to make sure the map configuration has updated window.setTimeout(() => { var sources = Object.keys(getApp().getMap().getMirrors()).concat(getApp().getMap().getLayers()); setMapSources(sources); }, 200); }); CommandModeOptionsChangedEvent.on((commandModeOptions) => { setCommandModeOptions(commandModeOptions); setLoadingNewCommandMode(false); }); SessionDataChangedEvent.on(() => setSavingSessionData(true)); SessionDataSavedEvent.on(() => setSavingSessionData(false)); SessionDataLoadedEvent.on((sessionData) => { sessionData.mapSource && setMapSource(sessionData.mapSource.id); }); EnabledCommandModesChangedEvent.on((enabledCommandModes) => setEnabledCommandModes(enabledCommandModes)); AudioManagerStateChangedEvent.on((state) => setAudioState(state as AudioManagerState)); /* Check if we are running the latest version */ const request = new Request("https://raw.githubusercontent.com/Pax1601/DCSOlympus/main/version.json"); fetch(request) .then((response) => { if (response.status === 200) { return response.json(); } else { throw new Error("Error connecting to Github to retrieve latest version"); } }) .then((res) => { setLatestVersion(res["version"]); if (VERSION === "{{OLYMPUS_VERSION_NUMBER}}") { console.log("OLYMPUS_VERSION_NUMBER is not set. Skipping version check."); setIsDevVersion(true); } else { setIsDevVersion(false); /* Check if the new version is newer than the current one */ /* Extract the version numbers */ const currentVersion = VERSION.replace("v", "").split("."); const newVersion = res["version"].replace("v", "").split("."); setIsBetaVersion(true); setIsLatestVersion(true); /* Compare the version numbers */ for (var i = 0; i < currentVersion.length; i++) { if (parseInt(newVersion[i]) > parseInt(currentVersion[i])) { setIsLatestVersion(false); } } /* Check if this is a beta version checking if this version is newer */ for (var i = 0; i < currentVersion.length; i++) { if (parseInt(newVersion[i]) < parseInt(currentVersion[i])) { setIsBetaVersion(false); } } } }); }, []); /* Initialize the "scroll" position of the element */ var scrollRef = useRef(null); useEffect(() => { if (scrollRef.current) onScroll(scrollRef.current); }); function onScroll(el) { const sl = el.scrollLeft; const sr = el.scrollWidth - el.scrollLeft - el.clientWidth; sl < 1 && !scrolledLeft && setScrolledLeft(true); sl > 1 && scrolledLeft && setScrolledLeft(false); sr < 1 && !scrolledRight && setScrolledRight(true); sr > 1 && scrolledRight && setScrolledRight(false); } function unitTypeFilterClickHandler(event: MouseEvent, entryName: string) { if (event.ctrlKey) { const hiddenTypes = getApp().getMap().getHiddenTypes(); const isAnyTypeHidden = Object.keys(unitViewTypesFilter).some((entryName) => hiddenTypes[entryName] === true); if (isAnyTypeHidden && !mapHiddenTypes[entryName]) { // If we ctrl+click an already displayed unit type, we show every unit type Object.keys(unitViewTypesFilter).forEach((entryName) => { getApp().getMap().setHiddenType(entryName, false); }); return; } Object.entries(unitViewTypesFilter) .map((ut) => ut[0]) .filter((utName) => utName !== entryName) .forEach((utName) => getApp().getMap().setHiddenType(utName, true)); getApp().getMap().setHiddenType(entryName, false); } else { getApp().getMap().setHiddenType(entryName, !mapHiddenTypes[entryName]); } } return (
{ if (scrollRef.current) { if (e.deltaY > 0) (scrollRef.current as HTMLElement).scrollLeft += 100; else (scrollRef.current as HTMLElement).scrollLeft -= 100; } }} > {!scrolledLeft && ( )}
onScroll(ev.target)} ref={scrollRef} > {commandModeOptions.commandMode === GAME_MASTER && (
1 ? ` cursor-pointer hover:bg-olympus-400 ` : "" } `} onClick={() => { if (enabledCommandModes.length > 1) { let blueCommandModeIndex = enabledCommandModes.indexOf(BLUE_COMMANDER); let redCommandModeIndex = enabledCommandModes.indexOf(RED_COMMANDER); if (blueCommandModeIndex >= 0) getApp().getServerManager().setActiveCommandMode(BLUE_COMMANDER); else if (redCommandModeIndex >= 0) getApp().getServerManager().setActiveCommandMode(RED_COMMANDER); setLoadingNewCommandMode(true); } }} > Game Master {enabledCommandModes.length > 1 && ( <> {loadingNewCommandMode ? : } )}
)} {commandModeOptions.commandMode === BLUE_COMMANDER && (
1 ? ` cursor-pointer hover:bg-blue-500 ` : "" } flex h-full rounded-md border-2 border-transparent bg-blue-600 px-4 text-gray-200 `} onClick={() => { if (enabledCommandModes.length > 1) { let gameMasterCommandModeIndex = enabledCommandModes.indexOf(GAME_MASTER); let redCommandModeIndex = enabledCommandModes.indexOf(RED_COMMANDER); if (redCommandModeIndex >= 0) getApp().getServerManager().setActiveCommandMode(RED_COMMANDER); else if (gameMasterCommandModeIndex >= 0) getApp().getServerManager().setActiveCommandMode(GAME_MASTER); setLoadingNewCommandMode(true); } }} > BLUE Commander {enabledCommandModes.length > 1 && ( <> {loadingNewCommandMode ? ( ) : ( )} )}
)} {commandModeOptions.commandMode === RED_COMMANDER && (
1 ? ` cursor-pointer hover:bg-red-500 ` : "" } rounded-md border-2 border-transparent bg-red-600 px-4 text-gray-200 `} onClick={() => { if (enabledCommandModes.length > 1) { let gameMasterCommandModeIndex = enabledCommandModes.indexOf(GAME_MASTER); let blueCommandModeIndex = enabledCommandModes.indexOf(BLUE_COMMANDER); if (gameMasterCommandModeIndex >= 0) getApp().getServerManager().setActiveCommandMode(GAME_MASTER); else if (blueCommandModeIndex >= 0) getApp().getServerManager().setActiveCommandMode(BLUE_COMMANDER); setLoadingNewCommandMode(true); } }} > RED Commander {enabledCommandModes.length > 1 && ( <> {loadingNewCommandMode ? ( ) : ( )} )}
)}
{ getApp().getMap().setOption("protectDCSUnits", !mapOptions.protectDCSUnits); }} tooltip={() => (

By default, Mission Editor units are protected from being commanded or deleted. This option allows you to unlock them, so they can be commanded or deleted like any other unit.{" "}

If units are protected, you will still be able to control them, but a prompt will be shown to require your confirmation.

Once a unit has been commanded, it will be unlocked and will become an Olympus unit, completely abandoning its previuos mission.

} /> )} /> { audioState === AudioManagerState.RUNNING ? getApp().getAudioManager().stop() : getApp().getAudioManager().start(); }} className={ audioState === AudioManagerState.ERROR ? `animate-pulse !border-red-500 !text-red-500` : "" } tooltip={() => (

If this option is enabled, you will be able to access the radio and audio features of DCS Olympus.

For this to work, a SRS Server need to be installed and running on the same machine on which the DCS Olympus server is running.

For security reasons, this feature will only work if a secure connection (i.e., using https) is established with the server. It is also suggested to use Google Chrome for optimal compatibility.{" "}

} /> )} icon={faVolumeHigh} />
{Object.entries({ human: olButtonsVisibilityHuman, olympus: olButtonsVisibilityOlympus, dcs: olButtonsVisibilityDcs, }).map((entry) => { return ( { getApp().getMap().setHiddenType(entry[0], !mapHiddenTypes[entry[0]]); }} checked={!mapHiddenTypes[entry[0]]} icon={entry[1]} tooltip={"Hide/show " + entry[0] + " units"} /> ); })}
getApp().getMap().setHiddenType("blue", !mapHiddenTypes["blue"])} checked={!mapHiddenTypes["blue"]} icon={faFlag} className={"!text-blue-500"} tooltip={"Hide/show blue units"} /> getApp().getMap().setHiddenType("red", !mapHiddenTypes["red"])} checked={!mapHiddenTypes["red"]} icon={faFlag} className={"!text-red-500"} tooltip={"Hide/show red units"} /> getApp().getMap().setHiddenType("neutral", !mapHiddenTypes["neutral"])} checked={!mapHiddenTypes["neutral"]} icon={faFlag} className={"!text-gray-500"} tooltip={"Hide/show neutral units"} />
{Object.entries(unitViewTypesFilter).map((entry) => { return ( unitTypeFilterClickHandler(event, entry[0])} checked={!mapHiddenTypes[entry[0]]} icon={entry[1]} tooltip={() => { return (

Tip: holding ctrl key while clicking will hide other unit categories.

To show all units again, hold ctrl while clicking a displayed unit category.

} /> ); }} /> ); })}
{ getApp().getMap().setOption("showMissionDrawings", !mapOptions.showMissionDrawings); }} tooltip={() => ( )} /> { getApp().getMap().setOption("showMissionNavpoints", !mapOptions.showMissionNavpoints); }} tooltip={() => ( )} /> getApp().getMap().setOption("showUnitsEngagementRings", !mapOptions.showUnitsEngagementRings)} checked={mapOptions.showUnitsEngagementRings} icon={faTriangleExclamation} className={""} tooltip={"Hide/show units engagement rings"} /> getApp().getMap().setOption("showUnitsAcquisitionRings", !mapOptions.showUnitsAcquisitionRings)} checked={mapOptions.showUnitsAcquisitionRings} icon={faWifi} className={""} tooltip={"Hide/show units acquisition rings"} /> getApp().getMap().setOption("clusterGroundUnits", !mapOptions.clusterGroundUnits)} checked={mapOptions.clusterGroundUnits} icon={faObjectGroup} className={""} tooltip={"Enable/disable ground unit clustering"} />
{ getApp() .getMap() .setOption("cameraPluginMode", mapOptions.cameraPluginMode === "live" ? "map" : "live"); }} tooltip={() => ( )} /> { getApp().getMap().setOption("cameraPluginEnabled", !mapOptions.cameraPluginEnabled); }} tooltip={() => ( )} /> {mapSources.map((source) => { return ( getApp().getMap().setLayerName(source)}>
{source}
); })}
{!scrolledRight && ( )}
); }