diff --git a/Content.Client/Audio/ClientAdminSoundSystem.cs b/Content.Client/Audio/ClientAdminSoundSystem.cs deleted file mode 100644 index bc576c7384..0000000000 --- a/Content.Client/Audio/ClientAdminSoundSystem.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Content.Shared.Audio; -using Content.Shared.CCVar; -using Robust.Shared.Audio; -using Robust.Shared.Configuration; -using Robust.Shared.Player; - -namespace Content.Client.Audio; - -public sealed class ClientAdminSoundSystem : SharedAdminSoundSystem -{ - [Dependency] private readonly IConfigurationManager _cfg = default!; - - private bool _adminAudioEnabled = true; - private List _adminAudio = new(1); - - public override void Initialize() - { - base.Initialize(); - SubscribeNetworkEvent(PlayAdminSound); - _cfg.OnValueChanged(CCVars.AdminSoundsEnabled, ToggleAdminSound, true); - } - - public override void Shutdown() - { - base.Shutdown(); - foreach (var stream in _adminAudio) - { - stream?.Stop(); - } - _adminAudio.Clear(); - } - - private void PlayAdminSound(AdminSoundEvent soundEvent) - { - if(!_adminAudioEnabled) return; - - var stream = SoundSystem.Play(soundEvent.Filename, Filter.Local(), soundEvent.AudioParams); - _adminAudio.Add(stream); - } - - private void ToggleAdminSound(bool enabled) - { - _adminAudioEnabled = enabled; - if (_adminAudioEnabled) return; - foreach (var stream in _adminAudio) - { - stream?.Stop(); - } - _adminAudio.Clear(); - } -} diff --git a/Content.Client/Audio/ClientGlobalSoundSystem.cs b/Content.Client/Audio/ClientGlobalSoundSystem.cs new file mode 100644 index 0000000000..c70513a5c9 --- /dev/null +++ b/Content.Client/Audio/ClientGlobalSoundSystem.cs @@ -0,0 +1,113 @@ +using Content.Shared.Audio; +using Content.Shared.CCVar; +using Content.Shared.GameTicking; +using Robust.Shared.Audio; +using Robust.Shared.Configuration; +using Robust.Shared.Player; + +namespace Content.Client.Audio; + +public sealed class ClientGlobalSoundSystem : SharedGlobalSoundSystem +{ + [Dependency] private readonly IConfigurationManager _cfg = default!; + + // Admin music + private bool _adminAudioEnabled = true; + private List _adminAudio = new(1); + + // Event sounds (e.g. nuke timer) + private bool _eventAudioEnabled = true; + private Dictionary _eventAudio = new(1); + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnRoundRestart); + SubscribeNetworkEvent(PlayAdminSound); + _cfg.OnValueChanged(CCVars.AdminSoundsEnabled, ToggleAdminSound, true); + + SubscribeNetworkEvent(PlayStationEventMusic); + SubscribeNetworkEvent(StopStationEventMusic); + _cfg.OnValueChanged(CCVars.EventMusicEnabled, ToggleStationEventMusic, true); + + SubscribeNetworkEvent(PlayGameSound); + } + + private void OnRoundRestart(RoundRestartCleanupEvent ev) + { + ClearAudio(); + } + + public override void Shutdown() + { + base.Shutdown(); + ClearAudio(); + } + + private void ClearAudio() + { + foreach (var stream in _adminAudio) + { + stream?.Stop(); + } + _adminAudio.Clear(); + + foreach (var (_, stream) in _eventAudio) + { + stream?.Stop(); + } + + _eventAudio.Clear(); + } + + private void PlayAdminSound(AdminSoundEvent soundEvent) + { + if(!_adminAudioEnabled) return; + + var stream = SoundSystem.Play(soundEvent.Filename, Filter.Local(), soundEvent.AudioParams); + _adminAudio.Add(stream); + } + + private void PlayStationEventMusic(StationEventMusicEvent soundEvent) + { + // Either the cvar is disabled or it's already playing + if(!_eventAudioEnabled || _eventAudio.ContainsKey(soundEvent.Type)) return; + + var stream = SoundSystem.Play(soundEvent.Filename, Filter.Local(), soundEvent.AudioParams); + _eventAudio.Add(soundEvent.Type, stream); + } + + private void PlayGameSound(GameGlobalSoundEvent soundEvent) + { + SoundSystem.Play(soundEvent.Filename, Filter.Local(), soundEvent.AudioParams); + } + + private void StopStationEventMusic(StopStationEventMusic soundEvent) + { + if (!_eventAudio.TryGetValue(soundEvent.Type, out var stream)) return; + stream?.Stop(); + _eventAudio.Remove(soundEvent.Type); + } + + private void ToggleAdminSound(bool enabled) + { + _adminAudioEnabled = enabled; + if (_adminAudioEnabled) return; + foreach (var stream in _adminAudio) + { + stream?.Stop(); + } + _adminAudio.Clear(); + } + + private void ToggleStationEventMusic(bool enabled) + { + _eventAudioEnabled = enabled; + if (_eventAudioEnabled) return; + foreach (var stream in _eventAudio) + { + stream.Value?.Stop(); + } + _eventAudio.Clear(); + } +} diff --git a/Content.Client/EscapeMenu/UI/Tabs/AudioTab.xaml b/Content.Client/EscapeMenu/UI/Tabs/AudioTab.xaml index 5487cdbe64..e4a8d3ff7a 100644 --- a/Content.Client/EscapeMenu/UI/Tabs/AudioTab.xaml +++ b/Content.Client/EscapeMenu/UI/Tabs/AudioTab.xaml @@ -63,6 +63,7 @@ + diff --git a/Content.Client/EscapeMenu/UI/Tabs/AudioTab.xaml.cs b/Content.Client/EscapeMenu/UI/Tabs/AudioTab.xaml.cs index aca123a424..146f4a632b 100644 --- a/Content.Client/EscapeMenu/UI/Tabs/AudioTab.xaml.cs +++ b/Content.Client/EscapeMenu/UI/Tabs/AudioTab.xaml.cs @@ -22,6 +22,7 @@ namespace Content.Client.EscapeMenu.UI.Tabs IoCManager.InjectDependencies(this); LobbyMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.LobbyMusicEnabled); + EventMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.EventMusicEnabled); AdminSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.AdminSoundsEnabled); StationAmbienceCheckBox.Pressed = _cfg.GetCVar(CCVars.StationAmbienceEnabled); SpaceAmbienceCheckBox.Pressed = _cfg.GetCVar(CCVars.SpaceAmbienceEnabled); @@ -33,6 +34,7 @@ namespace Content.Client.EscapeMenu.UI.Tabs AmbienceVolumeSlider.OnValueChanged += OnAmbienceVolumeSliderChanged; AmbienceSoundsSlider.OnValueChanged += OnAmbienceSoundsSliderChanged; LobbyMusicCheckBox.OnToggled += OnLobbyMusicCheckToggled; + EventMusicCheckBox.OnToggled += OnEventMusicCheckToggled; AdminSoundsCheckBox.OnToggled += OnAdminSoundsCheckToggled; StationAmbienceCheckBox.OnToggled += OnStationAmbienceCheckToggled; SpaceAmbienceCheckBox.OnToggled += OnSpaceAmbienceCheckToggled; @@ -79,11 +81,16 @@ namespace Content.Client.EscapeMenu.UI.Tabs UpdateChanges(); } + private void OnEventMusicCheckToggled(BaseButton.ButtonEventArgs args) + { + UpdateChanges(); + } + private void OnAdminSoundsCheckToggled(BaseButton.ButtonEventArgs args) { UpdateChanges(); } - + private void OnStationAmbienceCheckToggled(BaseButton.ButtonEventArgs args) { UpdateChanges(); @@ -101,6 +108,7 @@ namespace Content.Client.EscapeMenu.UI.Tabs _cfg.SetCVar(CCVars.AmbienceVolume, LV100ToDB(AmbienceVolumeSlider.Value)); _cfg.SetCVar(CCVars.MaxAmbientSources, (int)AmbienceSoundsSlider.Value); _cfg.SetCVar(CCVars.LobbyMusicEnabled, LobbyMusicCheckBox.Pressed); + _cfg.SetCVar(CCVars.EventMusicEnabled, EventMusicCheckBox.Pressed); _cfg.SetCVar(CCVars.AdminSoundsEnabled, AdminSoundsCheckBox.Pressed); _cfg.SetCVar(CCVars.StationAmbienceEnabled, StationAmbienceCheckBox.Pressed); _cfg.SetCVar(CCVars.SpaceAmbienceEnabled, SpaceAmbienceCheckBox.Pressed); @@ -120,6 +128,7 @@ namespace Content.Client.EscapeMenu.UI.Tabs AmbienceVolumeSlider.Value = DBToLV100(_cfg.GetCVar(CCVars.AmbienceVolume)); AmbienceSoundsSlider.Value = _cfg.GetCVar(CCVars.MaxAmbientSources); LobbyMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.LobbyMusicEnabled); + EventMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.EventMusicEnabled); AdminSoundsCheckBox.Pressed = _cfg.GetCVar(CCVars.AdminSoundsEnabled); StationAmbienceCheckBox.Pressed = _cfg.GetCVar(CCVars.StationAmbienceEnabled); SpaceAmbienceCheckBox.Pressed = _cfg.GetCVar(CCVars.SpaceAmbienceEnabled); @@ -149,10 +158,11 @@ namespace Content.Client.EscapeMenu.UI.Tabs Math.Abs(AmbienceVolumeSlider.Value - DBToLV100(_cfg.GetCVar(CCVars.AmbienceVolume))) < 0.01f; var isAmbientSoundsSame = (int)AmbienceSoundsSlider.Value == _cfg.GetCVar(CCVars.MaxAmbientSources); var isLobbySame = LobbyMusicCheckBox.Pressed == _cfg.GetCVar(CCVars.LobbyMusicEnabled); + var isEventSame = EventMusicCheckBox.Pressed == _cfg.GetCVar(CCVars.EventMusicEnabled); var isAdminSoundsSame = AdminSoundsCheckBox.Pressed == _cfg.GetCVar(CCVars.AdminSoundsEnabled); var isStationAmbienceSame = StationAmbienceCheckBox.Pressed == _cfg.GetCVar(CCVars.StationAmbienceEnabled); var isSpaceAmbienceSame = SpaceAmbienceCheckBox.Pressed == _cfg.GetCVar(CCVars.SpaceAmbienceEnabled); - var isEverythingSame = isMasterVolumeSame && isMidiVolumeSame && isAmbientVolumeSame && isAmbientSoundsSame && isLobbySame && isAdminSoundsSame && isStationAmbienceSame && isSpaceAmbienceSame; + var isEverythingSame = isMasterVolumeSame && isMidiVolumeSame && isAmbientVolumeSame && isAmbientSoundsSame && isLobbySame && isEventSame && isAdminSoundsSame && isStationAmbienceSame && isSpaceAmbienceSame; ApplyButton.Disabled = isEverythingSame; ResetButton.Disabled = isEverythingSame; MasterVolumeLabel.Text = diff --git a/Content.Server/Audio/ServerAdminSoundSystem.cs b/Content.Server/Audio/ServerGlobalSoundSystem.cs similarity index 67% rename from Content.Server/Audio/ServerAdminSoundSystem.cs rename to Content.Server/Audio/ServerGlobalSoundSystem.cs index 8d34692ed4..205058d7b5 100644 --- a/Content.Server/Audio/ServerAdminSoundSystem.cs +++ b/Content.Server/Audio/ServerGlobalSoundSystem.cs @@ -1,6 +1,9 @@ using Content.Server.Administration; +using Content.Server.Station.Components; +using Content.Server.Station.Systems; using Content.Shared.Administration; using Content.Shared.Audio; +using Content.Shared.Sound; using Robust.Server.Player; using Robust.Shared.Audio; using Robust.Shared.Console; @@ -8,10 +11,11 @@ using Robust.Shared.Player; namespace Content.Server.Audio; -public sealed class ServerAdminSoundSystem : SharedAdminSoundSystem +public sealed class ServerGlobalSoundSystem : SharedGlobalSoundSystem { [Dependency] private readonly IConsoleHost _conHost = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly StationSystem _stationSystem = default!; public override void Initialize() { @@ -25,12 +29,43 @@ public sealed class ServerAdminSoundSystem : SharedAdminSoundSystem _conHost.UnregisterCommand("playglobalsound"); } - private void PlayGlobal(Filter playerFilter, string filename, AudioParams? audioParams = null) + private void PlayAdminGlobal(Filter playerFilter, string filename, AudioParams? audioParams = null) { var msg = new AdminSoundEvent(filename, audioParams); RaiseNetworkEvent(msg, playerFilter); } + private Filter GetStationAndPvs(EntityUid source) + { + var stationFilter = _stationSystem.GetInStation(source); + stationFilter.AddPlayersByPvs(source, entityManager: EntityManager); + return stationFilter; + } + + public void PlayGlobalOnStation(EntityUid source, string filename, AudioParams? audioParams = null) + { + var msg = new GameGlobalSoundEvent(filename, audioParams); + var filter = GetStationAndPvs(source); + RaiseNetworkEvent(msg, filter); + } + + public void StopStationEventMusic(EntityUid source, StationEventMusicType type) + { + var msg = new StopStationEventMusic(type); + var filter = GetStationAndPvs(source); + RaiseNetworkEvent(msg, filter); + } + + public void DispatchStationEventMusic(EntityUid source, SoundSpecifier sound, StationEventMusicType type) + { + var audio = AudioParams.Default.WithVolume(-8); + var soundFile = sound.GetSound(); + var msg = new StationEventMusicEvent(soundFile, type, audio); + + var filter = GetStationAndPvs(source); + RaiseNetworkEvent(msg, filter); + } + /// /// Command that allows admins to play global sounds. /// @@ -96,6 +131,6 @@ public sealed class ServerAdminSoundSystem : SharedAdminSoundSystem break; } - PlayGlobal(filter, args[0], audio); + PlayAdminGlobal(filter, args[0], audio); } } diff --git a/Content.Server/Nuke/NukeComponent.cs b/Content.Server/Nuke/NukeComponent.cs index 33761a9ad3..1741e83bf9 100644 --- a/Content.Server/Nuke/NukeComponent.cs +++ b/Content.Server/Nuke/NukeComponent.cs @@ -66,6 +66,9 @@ namespace Content.Server.Nuke [DataField("disarmSound")] public SoundSpecifier DisarmSound = new SoundPathSpecifier("/Audio/Misc/notice2.ogg"); + [DataField("armMusic")] + public SoundSpecifier ArmMusic = new SoundPathSpecifier("/Audio/StationEvents/countdown.ogg"); + // These datafields here are duplicates of those in explosive component. But I'm hesitant to use explosive // component, just in case at some point, somehow, when grenade crafting added in someone manages to wire up a // proximity trigger or something to the nuke and set it off prematurely. I want to make sure they MEAN to set of diff --git a/Content.Server/Nuke/NukeSystem.cs b/Content.Server/Nuke/NukeSystem.cs index 174fdf5d28..708b5259ca 100644 --- a/Content.Server/Nuke/NukeSystem.cs +++ b/Content.Server/Nuke/NukeSystem.cs @@ -1,4 +1,5 @@ using Content.Server.AlertLevel; +using Content.Server.Audio; using Content.Server.Chat; using Content.Server.Chat.Managers; using Content.Server.Chat.Systems; @@ -15,6 +16,7 @@ using Content.Shared.Sound; using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.Player; +using Robust.Shared.Timing; namespace Content.Server.Nuke { @@ -26,6 +28,7 @@ namespace Content.Server.Nuke [Dependency] private readonly ExplosionSystem _explosions = default!; [Dependency] private readonly AlertLevelSystem _alertLevel = default!; [Dependency] private readonly StationSystem _stationSystem = default!; + [Dependency] private readonly ServerGlobalSoundSystem _soundSystem = default!; [Dependency] private readonly ChatSystem _chatSystem = default!; public override void Initialize() @@ -218,6 +221,7 @@ namespace Content.Server.Nuke if (nuke.RemainingTime <= nuke.AlertSoundTime && !nuke.PlayedAlertSound) { nuke.AlertAudioStream = SoundSystem.Play(nuke.AlertSound.GetSound(), Filter.Broadcast()); + _soundSystem.StopStationEventMusic(uid, StationEventMusicType.Nuke); nuke.PlayedAlertSound = true; } @@ -335,7 +339,7 @@ namespace Content.Server.Nuke // Otherwise, you could set every station to whatever AlertLevelOnActivate is. if (stationUid != null) { - _alertLevel.SetLevel(stationUid.Value, component.AlertLevelOnActivate, true, true, true, true); + _alertLevel.SetLevel(stationUid.Value, component.AlertLevelOnActivate, false, true, true, true); } // warn a crew @@ -344,8 +348,7 @@ namespace Content.Server.Nuke var sender = Loc.GetString("nuke-component-announcement-sender"); _chatSystem.DispatchStationAnnouncement(uid, announcement, sender, false, Color.Red); - // todo: move it to announcements system - SoundSystem.Play(component.ArmSound.GetSound(), Filter.Broadcast()); + NukeArmedAudio(component); component.Status = NukeStatus.ARMED; UpdateUserInterface(uid, component); @@ -373,8 +376,7 @@ namespace Content.Server.Nuke var sender = Loc.GetString("nuke-component-announcement-sender"); _chatSystem.DispatchStationAnnouncement(uid, announcement, sender, false); - // todo: move it to announcements system - SoundSystem.Play(component.DisarmSound.GetSound(), Filter.Broadcast()); + NukeDisarmedAudio(component); // disable sound and reset it component.PlayedAlertSound = false; @@ -423,6 +425,7 @@ namespace Content.Server.Nuke RaiseLocalEvent(new NukeExplodedEvent()); + _soundSystem.StopStationEventMusic(component.Owner, StationEventMusicType.Nuke); EntityManager.DeleteEntity(uid); } @@ -438,6 +441,18 @@ namespace Content.Server.Nuke UpdateUserInterface(uid, component); } #endregion + + private void NukeArmedAudio(NukeComponent component) + { + _soundSystem.PlayGlobalOnStation(component.Owner, component.ArmSound.GetSound()); + _soundSystem.DispatchStationEventMusic(component.Owner, component.ArmMusic, StationEventMusicType.Nuke); + } + + private void NukeDisarmedAudio(NukeComponent component) + { + _soundSystem.PlayGlobalOnStation(component.Owner, component.DisarmSound.GetSound()); + _soundSystem.StopStationEventMusic(component.Owner, StationEventMusicType.Nuke); + } } public sealed class NukeExplodedEvent : EntityEventArgs {} diff --git a/Content.Server/Station/Systems/StationSystem.cs b/Content.Server/Station/Systems/StationSystem.cs index 0d5e7a2aaa..59acbfcaa2 100644 --- a/Content.Server/Station/Systems/StationSystem.cs +++ b/Content.Server/Station/Systems/StationSystem.cs @@ -158,6 +158,18 @@ public sealed class StationSystem : EntitySystem #endregion Event handlers + public Filter GetInStation(EntityUid source, float range = 32f) + { + var station = GetOwningStation(source); + + if (TryComp(station, out var data)) + { + return GetInStation(data); + } + + return Filter.Empty(); + } + /// /// Retrieves a filter for everything in a particular station or near its member grids. /// diff --git a/Content.Shared/Audio/SharedAdminSoundSystem.cs b/Content.Shared/Audio/SharedAdminSoundSystem.cs deleted file mode 100644 index f26558e690..0000000000 --- a/Content.Shared/Audio/SharedAdminSoundSystem.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Robust.Shared.Audio; -using Robust.Shared.Serialization; - -namespace Content.Shared.Audio; - - -public abstract class SharedAdminSoundSystem : EntitySystem -{ -} - -[Serializable, NetSerializable] -public sealed class AdminSoundEvent : EntityEventArgs -{ - public string Filename; - public AudioParams? AudioParams; - public AdminSoundEvent(string filename, AudioParams? audioParams = null) - { - Filename = filename; - AudioParams = audioParams; - } -} diff --git a/Content.Shared/Audio/SharedGlobalSoundSystem.cs b/Content.Shared/Audio/SharedGlobalSoundSystem.cs new file mode 100644 index 0000000000..7ad07c21aa --- /dev/null +++ b/Content.Shared/Audio/SharedGlobalSoundSystem.cs @@ -0,0 +1,76 @@ +using Content.Shared.CCVar; +using Robust.Shared.Audio; +using Robust.Shared.Serialization; +namespace Content.Shared.Audio; + +/// +/// Handles playing audio to all players globally unless disabled by cvar. Some events are grid-specific. +/// +public abstract class SharedGlobalSoundSystem : EntitySystem +{ +} + +[Virtual] +[Serializable, NetSerializable] +public class GlobalSoundEvent : EntityEventArgs +{ + public string Filename; + public AudioParams? AudioParams; + public GlobalSoundEvent(string filename, AudioParams? audioParams = null) + { + Filename = filename; + AudioParams = audioParams; + } +} + +/// +/// Intended for admin music. Can be disabled by the cvar. +/// +[Serializable, NetSerializable] +public sealed class AdminSoundEvent : GlobalSoundEvent +{ + public AdminSoundEvent(string filename, AudioParams? audioParams = null) : base(filename, audioParams){} +} + +/// +/// Intended for misc sound effects. Can't be disabled by cvar. +/// +[Serializable, NetSerializable] +public sealed class GameGlobalSoundEvent : GlobalSoundEvent +{ + public GameGlobalSoundEvent(string filename, AudioParams? audioParams = null) : base(filename, audioParams){} +} + +public enum StationEventMusicType : byte +{ + Nuke +} + +/// +/// Intended for music triggered by events on a specific station. Can be disabled by the cvar. +/// +[Serializable, NetSerializable] +public sealed class StationEventMusicEvent : GlobalSoundEvent +{ + public StationEventMusicType Type; + + public StationEventMusicEvent(string filename, StationEventMusicType type, AudioParams? audioParams = null) : base( + filename, audioParams) + { + Type = type; + } +} + +/// +/// Attempts to stop a playing stream. +/// +[Serializable, NetSerializable] +public sealed class StopStationEventMusic : EntityEventArgs +{ + public StationEventMusicType Type; + + public StopStationEventMusic(StationEventMusicType type) + { + Type = type; + } +} diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 9d7957e93e..663799359d 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -463,18 +463,21 @@ namespace Content.Shared.CCVar CVarDef.Create("physics.mob_pushing", false, CVar.REPLICATED); /* - * Lobby music + * Music */ public static readonly CVarDef LobbyMusicEnabled = - CVarDef.Create("ambience.lobbymusicenabled", true, CVar.ARCHIVE | CVar.CLIENTONLY); + CVarDef.Create("ambience.lobby_music_enabled", true, CVar.ARCHIVE | CVar.CLIENTONLY); + + public static readonly CVarDef EventMusicEnabled = + CVarDef.Create("ambience.event_music_enabled", true, CVar.ARCHIVE | CVar.CLIENTONLY); /* * Admin sounds */ public static readonly CVarDef AdminSoundsEnabled = - CVarDef.Create("audio.adminsoundsenabled", true, CVar.ARCHIVE | CVar.CLIENTONLY); + CVarDef.Create("audio.admin_sounds_enabled", true, CVar.ARCHIVE | CVar.CLIENTONLY); /* * HUD diff --git a/Resources/Audio/StationEvents/attribution.txt b/Resources/Audio/StationEvents/attribution.txt new file mode 100644 index 0000000000..84a99426d3 --- /dev/null +++ b/Resources/Audio/StationEvents/attribution.txt @@ -0,0 +1 @@ +countdown.ogg is created by qwertyquerty and licensed under CC-BY-SA-3.0. It is taken from https://github.com/BeeStation/BeeStation-Hornet at commit 79b8cc23cfb347cf23ea70fc5f6f39afedf9cde7. diff --git a/Resources/Audio/StationEvents/countdown.ogg b/Resources/Audio/StationEvents/countdown.ogg new file mode 100644 index 0000000000..bd34212158 Binary files /dev/null and b/Resources/Audio/StationEvents/countdown.ogg differ diff --git a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl index e768feca9d..56fa911e9b 100644 --- a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl +++ b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl @@ -16,6 +16,7 @@ ui-options-midi-volume = MIDI (Instrument) Volume: ui-options-ambience-volume = Ambience volume: ui-options-ambience-max-sounds = Ambience simultaneous sounds: ui-options-lobby-music = Lobby & Round-end Music +ui-options-event-music = Event Music ui-options-admin-sounds = Play Admin Sounds ui-options-station-ambience = Station Ambience ui-options-space-ambience = Space Ambience