diff --git a/frontend/react/src/audio/audiomanager.ts b/frontend/react/src/audio/audiomanager.ts index d52d4b1b..12602523 100644 --- a/frontend/react/src/audio/audiomanager.ts +++ b/frontend/react/src/audio/audiomanager.ts @@ -1,4 +1,4 @@ -import { AudioMessageType, OlympusState } from "../constants/constants"; +import { AudioMessageType, BLUE_COMMANDER, GAME_MASTER, OlympusState, RED_COMMANDER } from "../constants/constants"; import { MicrophoneSource } from "./microphonesource"; import { RadioSink } from "./radiosink"; import { getApp } from "../olympusapp"; @@ -11,17 +11,20 @@ import { Unit } from "../unit/unit"; import { UnitSink } from "./unitsink"; import { AudioPacket, MessageType } from "./audiopacket"; import { + AudioManagerCoalitionChangedEvent, AudioManagerDevicesChangedEvent, AudioManagerInputChangedEvent, AudioManagerOutputChangedEvent, AudioManagerStateChangedEvent, AudioSinksChangedEvent, AudioSourcesChangedEvent, + CommandModeOptionsChangedEvent, ConfigLoadedEvent, SRSClientsChangedEvent, } from "../events"; -import { OlympusConfig } from "../interfaces"; +import { CommandModeOptions, OlympusConfig } from "../interfaces"; import { TextToSpeechSource } from "./texttospeechsource"; +import { Coalition } from "../types/types"; export class AudioManager { #audioContext: AudioContext; @@ -46,12 +49,23 @@ export class AudioManager { #syncInterval: number; #speechRecognition: boolean = true; #internalTextToSpeechSource: TextToSpeechSource; + #coalition: Coalition = "blue"; + #commandMode: string = BLUE_COMMANDER; constructor() { ConfigLoadedEvent.on((config: OlympusConfig) => { config.audio.WSPort ? this.setPort(config.audio.WSPort) : this.setEndpoint(config.audio.WSEndpoint); }); + CommandModeOptionsChangedEvent.on((options: CommandModeOptions) => { + if (options.commandMode === BLUE_COMMANDER) { + this.setCoalition("blue"); + } else if (options.commandMode === RED_COMMANDER) { + this.setCoalition("red"); + } + this.#commandMode = options.commandMode; + }); + let PTTKeys = ["KeyZ", "KeyX", "KeyC", "KeyV", "KeyB", "KeyN", "KeyM", "KeyComma", "KeyDot"]; PTTKeys.forEach((key, idx) => { getApp() @@ -117,7 +131,7 @@ export class AudioManager { if (sink.getFrequency() === frequencyInfo.frequency && sink.getModulation() === frequencyInfo.modulation && sink.getTuned()) { sink.setReceiving(true); - sink.setTransmittingUnit(getApp().getUnitsManager().getUnitByID(audioPacket.getUnitID()) ?? undefined) + sink.setTransmittingUnit(getApp().getUnitsManager().getUnitByID(audioPacket.getUnitID()) ?? undefined); /* Make a copy of the array buffer for the playback pipeline to use */ var dst = new ArrayBuffer(audioPacket.getAudioData().buffer.byteLength); @@ -150,7 +164,7 @@ export class AudioManager { let newRadio = this.addRadio(); newRadio?.setFrequency(options.frequency); newRadio?.setModulation(options.modulation); - newRadio?.setPan(options.pan) + newRadio?.setPan(options.pan); }); } else { /* Add two default radios and connect to the microphone*/ @@ -346,12 +360,23 @@ export class AudioManager { return this.#internalTextToSpeechSource; } + setCoalition(coalition: Coalition) { + if (this.#commandMode === GAME_MASTER) { + this.#coalition = coalition; + AudioManagerCoalitionChangedEvent.dispatch(coalition); + } + } + + getCoalition() { + return this.#coalition; + } + #syncRadioSettings() { /* Send the radio settings of each radio to the SRS backend */ let message = { type: "Settings update", guid: this.#guid, - coalition: 2, // TODO + coalition: this.#coalition, settings: this.#sinks .filter((sink) => sink instanceof RadioSink) .map((radio) => { diff --git a/frontend/react/src/events.ts b/frontend/react/src/events.ts index c65b9bda..2ceccc57 100644 --- a/frontend/react/src/events.ts +++ b/frontend/react/src/events.ts @@ -7,7 +7,7 @@ import { CoalitionPolygon } from "./map/coalitionarea/coalitionpolygon"; import { Airbase } from "./mission/airbase"; import { Bullseye } from "./mission/bullseye"; import { Shortcut } from "./shortcut/shortcut"; -import { MapHiddenTypes, MapOptions } from "./types/types"; +import { Coalition, MapHiddenTypes, MapOptions } from "./types/types"; import { ContextAction } from "./unit/contextaction"; import { ContextActionSet } from "./unit/contextactionset"; import { Unit } from "./unit/unit"; @@ -749,6 +749,23 @@ export class AudioManagerOutputChangedEvent { } } +export class AudioManagerCoalitionChangedEvent { + static on(callback: (coalition: Coalition) => void, singleShot = false) { + document.addEventListener( + this.name, + (ev: CustomEventInit) => { + callback(ev.detail.coalition); + }, + { once: singleShot } + ); + } + + static dispatch(coalition: Coalition) { + document.dispatchEvent(new CustomEvent(this.name, { detail: { coalition } })); + console.log(`Event ${this.name} dispatched`); + } +} + /************** Mission data events ***************/ export class BullseyesDataChangedEvent { static on(callback: (bullseyes: { [name: string]: Bullseye }) => void, singleShot = false) { diff --git a/frontend/react/src/mission/missionmanager.ts b/frontend/react/src/mission/missionmanager.ts index 692eee09..44ebfb7c 100644 --- a/frontend/react/src/mission/missionmanager.ts +++ b/frontend/react/src/mission/missionmanager.ts @@ -6,7 +6,7 @@ import { BLUE_COMMANDER, GAME_MASTER, NONE, RED_COMMANDER } from "../constants/c import { AirbasesData, BullseyesData, CommandModeOptions, DateAndTime, MissionData } from "../interfaces"; import { Coalition } from "../types/types"; import { Carrier } from "./carrier"; -import { AirbaseSelectedEvent, AppStateChangedEvent, BullseyesDataChangedEvent, CommandModeOptionsChangedEvent, InfoPopupEvent, MissionDataChangedEvent } from "../events"; +import { AirbaseSelectedEvent, AppStateChangedEvent, BullseyesDataChangedEvent, CommandModeOptionsChangedEvent, MissionDataChangedEvent } from "../events"; /** The MissionManager */ export class MissionManager { diff --git a/frontend/react/src/ui/contextmenus/spawncontextmenu.tsx b/frontend/react/src/ui/contextmenus/spawncontextmenu.tsx index b9462086..ab8a3eed 100644 --- a/frontend/react/src/ui/contextmenus/spawncontextmenu.tsx +++ b/frontend/react/src/ui/contextmenus/spawncontextmenu.tsx @@ -28,7 +28,6 @@ import { OlDropdownItem } from "../components/oldropdown"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { OlCoalitionToggle } from "../components/olcoalitiontoggle"; import { Coalition } from "../../types/types"; -import { CompactEffectSpawnMenu } from "../panels/compacteffectspawnmenu"; enum CategoryGroup { NONE, diff --git a/frontend/react/src/ui/panels/audiomenu.tsx b/frontend/react/src/ui/panels/audiomenu.tsx index d3380fe4..e9f37c71 100644 --- a/frontend/react/src/ui/panels/audiomenu.tsx +++ b/frontend/react/src/ui/panels/audiomenu.tsx @@ -12,15 +12,20 @@ import { UnitSink } from "../../audio/unitsink"; import { FaMinus, FaVolumeHigh } from "react-icons/fa6"; import { getRandomColor } from "../../other/utils"; import { + AudioManagerCoalitionChangedEvent, AudioManagerDevicesChangedEvent, AudioManagerInputChangedEvent, AudioManagerOutputChangedEvent, AudioManagerStateChangedEvent, AudioSinksChangedEvent, AudioSourcesChangedEvent, + CommandModeOptionsChangedEvent, ShortcutsChangedEvent, } from "../../events"; import { OlDropdown, OlDropdownItem } from "../components/oldropdown"; +import { OlCoalitionToggle } from "../components/olcoalitiontoggle"; +import { Coalition } from "../../types/types"; +import { GAME_MASTER, NONE } from "../../constants/constants"; export function AudioMenu(props: { open: boolean; onClose: () => void; children?: JSX.Element | JSX.Element[] }) { const [devices, setDevices] = useState([] as MediaDeviceInfo[]); @@ -32,6 +37,8 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children? const [shortcuts, setShortcuts] = useState({}); const [input, setInput] = useState(undefined as undefined | MediaDeviceInfo); const [output, setOutput] = useState(undefined as undefined | MediaDeviceInfo); + const [coalition, setCoalition] = useState("blue" as Coalition); + const [commandMode, setCommandMode] = useState(NONE as string); /* Preallocate 128 references for the source and sink panels. If the number of references changes, React will give an error */ const sourceRefs = Array(128) @@ -77,6 +84,8 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children? AudioManagerDevicesChangedEvent.on((devices) => setDevices([...devices])); AudioManagerInputChangedEvent.on((input) => setInput(input)); AudioManagerOutputChangedEvent.on((output) => setOutput(output)); + AudioManagerCoalitionChangedEvent.on((coalition) => setCoalition(coalition)); + CommandModeOptionsChangedEvent.on((options) => setCommandMode(options.commandMode)); }, []); /* When the sinks or sources change, use the count state to force a rerender to update the connection lines */ @@ -135,6 +144,7 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children? )} > +