diff --git a/Content.Client/GameObjects/EntitySystems/AmbienceSystem.cs b/Content.Client/GameObjects/EntitySystems/AmbienceSystem.cs deleted file mode 100644 index 5285ed7cec..0000000000 --- a/Content.Client/GameObjects/EntitySystems/AmbienceSystem.cs +++ /dev/null @@ -1,95 +0,0 @@ -#nullable enable -using Content.Shared.Audio; -using JetBrains.Annotations; -using Robust.Client.GameObjects; -using Content.Shared; -using Robust.Shared.Audio; -using Robust.Shared.Configuration; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Client; -using Robust.Client.State; -using Content.Client.State; - -namespace Content.Client.GameObjects.EntitySystems -{ - [UsedImplicitly] - public class AmbienceSystem : EntitySystem - { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; - [Dependency] private readonly IConfigurationManager _configManager = default!; - [Dependency] private readonly IStateManager _stateManager = default!; - - private AudioSystem _audioSystem = default!; - - private SoundCollectionPrototype _ambientCollection = default!; - - private AudioParams _ambientParams = new(-10f, 1, "Master", 0, 0, AudioMixTarget.Stereo, true, 0f); - - private IPlayingAudioStream? _ambientStream; - - public override void Initialize() - { - base.Initialize(); - - _audioSystem = EntitySystemManager.GetEntitySystem(); - _ambientCollection = _prototypeManager.Index("AmbienceBase"); - - _configManager.OnValueChanged(CCVars.AmbienceBasicEnabled, AmbienceCVarChanged); - - _stateManager.OnStateChanged += StateManagerOnStateChanged; - } - - public override void Shutdown() - { - base.Shutdown(); - - _stateManager.OnStateChanged -= StateManagerOnStateChanged; - } - - private void StateManagerOnStateChanged(StateChangedEventArgs args) - { - if (args.NewState is not GameScreen) - { - EndAmbience(); - } - else if (_configManager.GetCVar(CCVars.AmbienceBasicEnabled)) - { - StartAmbience(); - } - } - - private void AmbienceCVarChanged(bool ambienceEnabled) - { - if (!ambienceEnabled) - { - EndAmbience(); - } - else if (_stateManager.CurrentState is GameScreen) - { - StartAmbience(); - } - } - - private void StartAmbience() - { - EndAmbience(); - var file = _robustRandom.Pick(_ambientCollection.PickFiles); - _ambientStream = _audioSystem.Play(file, _ambientParams); - } - - private void EndAmbience() - { - if (_ambientStream == null) - { - return; - } - _ambientStream.Stop(); - _ambientStream = null; - } - } -} - diff --git a/Content.Client/GameObjects/EntitySystems/BackgroundAudioSystem.cs b/Content.Client/GameObjects/EntitySystems/BackgroundAudioSystem.cs new file mode 100644 index 0000000000..f0cd863d46 --- /dev/null +++ b/Content.Client/GameObjects/EntitySystems/BackgroundAudioSystem.cs @@ -0,0 +1,186 @@ +#nullable enable +using Content.Client.Interfaces; +using Content.Shared.Audio; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Content.Shared; +using Robust.Shared.Audio; +using Robust.Shared.Configuration; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Client; +using Robust.Client.State; +using Content.Client.State; + +namespace Content.Client.GameObjects.EntitySystems +{ + [UsedImplicitly] + public class BackgroundAudioSystem : EntitySystem + { + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + [Dependency] private readonly IConfigurationManager _configManager = default!; + [Dependency] private readonly IStateManager _stateManager = default!; + [Dependency] private readonly IBaseClient _client = default!; + [Dependency] private readonly IClientGameTicker _clientGameTicker = default!; + + private AudioSystem _audioSystem = default!; + + private SoundCollectionPrototype _ambientCollection = default!; + + private AudioParams _ambientParams = new(-10f, 1, "Master", 0, 0, AudioMixTarget.Stereo, true, 0f); + private AudioParams _lobbyParams = new(5f, 1, "Master", 0, 0, AudioMixTarget.Stereo, true, 0f); + + private IPlayingAudioStream? _ambientStream; + private IPlayingAudioStream? _lobbyStream; + + public override void Initialize() + { + base.Initialize(); + + _audioSystem = Get(); + + _ambientCollection = _prototypeManager.Index("AmbienceBase"); + + _configManager.OnValueChanged(CCVars.AmbienceBasicEnabled, AmbienceCVarChanged); + _configManager.OnValueChanged(CCVars.LobbyMusicEnabled, LobbyMusicCVarChanged); + + _stateManager.OnStateChanged += StateManagerOnStateChanged; + + _client.PlayerJoinedServer += OnJoin; + _client.PlayerLeaveServer += OnLeave; + + _clientGameTicker.LobbyStatusUpdated += LobbySongReceived; + } + + public override void Shutdown() + { + base.Shutdown(); + + _stateManager.OnStateChanged -= StateManagerOnStateChanged; + + _client.PlayerJoinedServer -= OnJoin; + _client.PlayerLeaveServer -= OnLeave; + + _clientGameTicker.LobbyStatusUpdated -= LobbySongReceived; + + EndAmbience(); + EndLobbyMusic(); + } + + private void StateManagerOnStateChanged(StateChangedEventArgs args) + { + EndAmbience(); + EndLobbyMusic(); + if (args.NewState is LobbyState && _configManager.GetCVar(CCVars.LobbyMusicEnabled)) + { + StartLobbyMusic(); + } + else if (args.NewState is GameScreen && _configManager.GetCVar(CCVars.AmbienceBasicEnabled)) + { + StartAmbience(); + } + } + + private void OnJoin(object? sender, PlayerEventArgs args) + { + if (_stateManager.CurrentState is LobbyState) + { + EndAmbience(); + if (_configManager.GetCVar(CCVars.LobbyMusicEnabled)) + { + StartLobbyMusic(); + } + } + else + { + EndLobbyMusic(); + if (_configManager.GetCVar(CCVars.AmbienceBasicEnabled)) + { + StartAmbience(); + } + } + } + + private void OnLeave(object? sender, PlayerEventArgs args) + { + EndAmbience(); + EndLobbyMusic(); + } + + private void AmbienceCVarChanged(bool ambienceEnabled) + { + if (!ambienceEnabled) + { + EndAmbience(); + } + else if (_stateManager.CurrentState is GameScreen) + { + StartAmbience(); + } + else + { + EndAmbience(); + } + } + + private void StartAmbience() + { + EndAmbience(); + var file = _robustRandom.Pick(_ambientCollection.PickFiles); + _ambientStream = _audioSystem.Play(file, _ambientParams); + } + + private void EndAmbience() + { + _ambientStream?.Stop(); + _ambientStream = null; + } + + private void LobbyMusicCVarChanged(bool musicEnabled) + { + if (!musicEnabled) + { + EndLobbyMusic(); + } + else if (_stateManager.CurrentState is LobbyState) + { + StartLobbyMusic(); + } + else + { + EndLobbyMusic(); + } + } + + private void LobbySongReceived() + { + if (_lobbyStream != null) //Toggling Ready status fires this method. This check ensures we only start the lobby music if it's not playing. + { + return; + } + if (_stateManager.CurrentState is LobbyState && _configManager.GetCVar(CCVars.LobbyMusicEnabled)) + { + StartLobbyMusic(); + } + } + private void StartLobbyMusic() + { + EndLobbyMusic(); + var file = _clientGameTicker.LobbySong; + if (file == null) // We have not received the lobby song yet. + { + return; + } + _lobbyStream = _audioSystem.Play(file, _lobbyParams); + } + + private void EndLobbyMusic() + { + _lobbyStream?.Stop(); + _lobbyStream = null; + } + } +} diff --git a/Content.Client/GameTicking/ClientGameTicker.cs b/Content.Client/GameTicking/ClientGameTicker.cs index 16450869fe..369b72d3dd 100644 --- a/Content.Client/GameTicking/ClientGameTicker.cs +++ b/Content.Client/GameTicking/ClientGameTicker.cs @@ -24,6 +24,7 @@ namespace Content.Client.GameTicking [ViewVariables] public bool AreWeReady { get; private set; } [ViewVariables] public bool IsGameStarted { get; private set; } + [ViewVariables] public string? LobbySong { get; private set; } [ViewVariables] public bool DisallowedLateJoin { get; private set; } [ViewVariables] public string? ServerInfoBlob { get; private set; } [ViewVariables] public TimeSpan StartTime { get; private set; } @@ -82,6 +83,7 @@ namespace Content.Client.GameTicking StartTime = message.StartTime; IsGameStarted = message.IsRoundStarted; AreWeReady = message.YouAreReady; + LobbySong = message.LobbySong; Paused = message.Paused; if (IsGameStarted) Status.Clear(); diff --git a/Content.Client/Interfaces/IClientGameTicker.cs b/Content.Client/Interfaces/IClientGameTicker.cs index 55fa0c3b98..59ceb4c10b 100644 --- a/Content.Client/Interfaces/IClientGameTicker.cs +++ b/Content.Client/Interfaces/IClientGameTicker.cs @@ -10,6 +10,7 @@ namespace Content.Client.Interfaces bool IsGameStarted { get; } string? ServerInfoBlob { get; } bool AreWeReady { get; } + string? LobbySong { get; } bool DisallowedLateJoin { get; } TimeSpan StartTime { get; } bool Paused { get; } diff --git a/Content.Client/UserInterface/OptionsMenu.Audio.cs b/Content.Client/UserInterface/OptionsMenu.Audio.cs index 19b97421f8..de80cd25ec 100644 --- a/Content.Client/UserInterface/OptionsMenu.Audio.cs +++ b/Content.Client/UserInterface/OptionsMenu.Audio.cs @@ -21,6 +21,7 @@ namespace Content.Client.UserInterface private readonly Label MasterVolumeLabel; private readonly Slider MasterVolumeSlider; private readonly CheckBox AmbienceCheckBox; + private readonly CheckBox LobbyMusicCheckBox; private readonly Button ResetButton; public AudioControl(IConfigurationManager cfg, IClydeAudio clydeAudio) @@ -70,6 +71,10 @@ namespace Content.Client.UserInterface contents.AddChild(AmbienceCheckBox); AmbienceCheckBox.Pressed = _cfg.GetCVar(CCVars.AmbienceBasicEnabled); + LobbyMusicCheckBox = new CheckBox {Text = Loc.GetString("ui-options-lobby-music")}; + contents.AddChild(LobbyMusicCheckBox); + LobbyMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.LobbyMusicEnabled); + ApplyButton = new Button { Text = Loc.GetString("ui-options-apply"), TextAlign = Label.AlignMode.Center, @@ -118,6 +123,7 @@ namespace Content.Client.UserInterface ResetButton.OnPressed += OnResetButtonPressed; MasterVolumeSlider.OnValueChanged += OnMasterVolumeSliderChanged; AmbienceCheckBox.OnToggled += OnAmbienceCheckToggled; + LobbyMusicCheckBox.OnToggled += OnLobbyMusicCheckToggled; AddChild(vBox); @@ -146,10 +152,16 @@ namespace Content.Client.UserInterface UpdateChanges(); } + private void OnLobbyMusicCheckToggled(BaseButton.ButtonEventArgs args) + { + UpdateChanges(); + } + private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args) { _cfg.SetCVar(CVars.AudioMasterVolume, MasterVolumeSlider.Value / 100); _cfg.SetCVar(CCVars.AmbienceBasicEnabled, AmbienceCheckBox.Pressed); + _cfg.SetCVar(CCVars.LobbyMusicEnabled, LobbyMusicCheckBox.Pressed); _cfg.SaveToFile(); UpdateChanges(); } @@ -165,6 +177,7 @@ namespace Content.Client.UserInterface MasterVolumeLabel.Text = Loc.GetString("ui-options-volume-percent", ("volume", MasterVolumeSlider.Value / 100)); AmbienceCheckBox.Pressed = _cfg.GetCVar(CCVars.AmbienceBasicEnabled); + LobbyMusicCheckBox.Pressed = _cfg.GetCVar(CCVars.LobbyMusicEnabled); UpdateChanges(); } @@ -173,7 +186,8 @@ namespace Content.Client.UserInterface var isMasterVolumeSame = System.Math.Abs(MasterVolumeSlider.Value - _cfg.GetCVar(CVars.AudioMasterVolume) * 100) < 0.01f; var isAmbienceSame = AmbienceCheckBox.Pressed == _cfg.GetCVar(CCVars.AmbienceBasicEnabled); - var isEverythingSame = isMasterVolumeSame && isAmbienceSame; + var isLobbySame = LobbyMusicCheckBox.Pressed == _cfg.GetCVar(CCVars.LobbyMusicEnabled); + var isEverythingSame = isMasterVolumeSame && isAmbienceSame && isLobbySame; ApplyButton.Disabled = isEverythingSame; ResetButton.Disabled = isEverythingSame; } diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index f782c5ae06..4c55238a9b 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -24,6 +24,7 @@ using Content.Server.Mobs; using Content.Server.Mobs.Roles; using Content.Server.Players; using Content.Shared; +using Content.Shared.Audio; using Content.Shared.Chat; using Content.Shared.GameTicking; using Content.Shared.Network.NetMessages; @@ -133,6 +134,9 @@ namespace Content.Server.GameTicking private TimeSpan LobbyDuration => TimeSpan.FromSeconds(_configurationManager.GetCVar(CCVars.GameLobbyDuration)); + private SoundCollectionPrototype _lobbyCollection = default!; + [ViewVariables] public string LobbySong { get; private set; } + public override void Initialize() { base.Initialize(); @@ -155,6 +159,9 @@ namespace Content.Server.GameTicking Presets = presets.ToImmutableDictionary(); + _lobbyCollection = _prototypeManager.Index("LobbyMusic"); + LobbySong = _robustRandom.Pick(_lobbyCollection.PickFiles); + _netManager.RegisterNetMessage(nameof(MsgTickerJoinLobby)); _netManager.RegisterNetMessage(nameof(MsgTickerJoinGame)); _netManager.RegisterNetMessage(nameof(MsgTickerLobbyStatus)); @@ -219,6 +226,7 @@ namespace Content.Server.GameTicking RoundNumberMetric.Inc(); RunLevel = GameRunLevel.PreRoundLobby; + LobbySong = _robustRandom.Pick(_lobbyCollection.PickFiles); _resettingCleanup(); _preRoundSetup(); @@ -1042,6 +1050,7 @@ namespace Content.Server.GameTicking msg.StartTime = _roundStartTime; msg.YouAreReady = status == PlayerStatus.Ready; msg.Paused = Paused; + msg.LobbySong = LobbySong; return msg; } diff --git a/Content.Shared/CCVars.cs b/Content.Shared/CCVars.cs index 583b23868c..ef62efb83f 100644 --- a/Content.Shared/CCVars.cs +++ b/Content.Shared/CCVars.cs @@ -183,6 +183,12 @@ namespace Content.Shared public static readonly CVarDef AmbienceBasicEnabled = CVarDef.Create("ambience.basicenabled", true, CVar.ARCHIVE | CVar.CLIENTONLY); + /* + * Lobby music + */ + + public static readonly CVarDef LobbyMusicEnabled = + CVarDef.Create("ambience.lobbymusicenabled", true, CVar.ARCHIVE | CVar.CLIENTONLY); /* * AI diff --git a/Content.Shared/GameTicking/SharedGameTicker.cs b/Content.Shared/GameTicking/SharedGameTicker.cs index b3e2b95dc8..ec62578352 100644 --- a/Content.Shared/GameTicking/SharedGameTicker.cs +++ b/Content.Shared/GameTicking/SharedGameTicker.cs @@ -91,6 +91,7 @@ namespace Content.Shared.GameTicking #endregion public bool IsRoundStarted { get; set; } + public string? LobbySong { get; set; } public bool YouAreReady { get; set; } // UTC. public TimeSpan StartTime { get; set; } @@ -99,6 +100,7 @@ namespace Content.Shared.GameTicking public override void ReadFromBuffer(NetIncomingMessage buffer) { IsRoundStarted = buffer.ReadBoolean(); + LobbySong = buffer.ReadString(); if (IsRoundStarted) { @@ -108,11 +110,13 @@ namespace Content.Shared.GameTicking YouAreReady = buffer.ReadBoolean(); StartTime = new TimeSpan(buffer.ReadInt64()); Paused = buffer.ReadBoolean(); + } public override void WriteToBuffer(NetOutgoingMessage buffer) { buffer.Write(IsRoundStarted); + buffer.Write(LobbySong); if (IsRoundStarted) { @@ -122,6 +126,7 @@ namespace Content.Shared.GameTicking buffer.Write(YouAreReady); buffer.Write(StartTime.Ticks); buffer.Write(Paused); + } } diff --git a/Resources/Audio/Lobby/absconditus.ogg b/Resources/Audio/Lobby/absconditus.ogg new file mode 100644 index 0000000000..1e1b0e6b2f Binary files /dev/null and b/Resources/Audio/Lobby/absconditus.ogg differ diff --git a/Resources/Audio/Lobby/endless_space.ogg b/Resources/Audio/Lobby/endless_space.ogg new file mode 100644 index 0000000000..3ec55a0621 Binary files /dev/null and b/Resources/Audio/Lobby/endless_space.ogg differ diff --git a/Resources/Audio/Lobby/licenses.txt b/Resources/Audio/Lobby/licenses.txt new file mode 100644 index 0000000000..e58743c7c3 --- /dev/null +++ b/Resources/Audio/Lobby/licenses.txt @@ -0,0 +1,7 @@ +thunderdome.ogg (-Sector11) by MashedByMachines is licensed under CC-BY-NC-SA-3.0. It was converted from MP3 to OGG. The source can be found here: https://www.newgrounds.com/audio/listen/312622 + +endless_space.ogg (Endless Space) by SolusLunes is licensed under CC-BY-3.0. It was converted from MP3 to OGG. The source can be found here: https://www.newgrounds.com/audio/listen/67583 + +space_asshole.ogg (Space Asshole) by Chris Remo is used with special permission from the author, under the condition that the project remains non-commercial and open source. At the author's request, here is a link to his bandcamp: https://chrisremo.bandcamp.com/ + +absconditus.ogg (Absconditus) by ZhayTee is licensed under CC-BY-NC-SA-3.0. It was converted from MP3 to OGG. The source can be found here: https://bandcamp.zhaytee.net/track/absconditus \ No newline at end of file diff --git a/Resources/Audio/Lobby/space_asshole.ogg b/Resources/Audio/Lobby/space_asshole.ogg new file mode 100644 index 0000000000..7b302e44b1 Binary files /dev/null and b/Resources/Audio/Lobby/space_asshole.ogg differ diff --git a/Resources/Audio/Lobby/thunderdome.ogg b/Resources/Audio/Lobby/thunderdome.ogg new file mode 100644 index 0000000000..a32f8de69b Binary files /dev/null and b/Resources/Audio/Lobby/thunderdome.ogg differ diff --git a/Resources/Locale/en-US/ui/options.ftl b/Resources/Locale/en-US/ui/options.ftl index 7186d73969..c2490c0320 100644 --- a/Resources/Locale/en-US/ui/options.ftl +++ b/Resources/Locale/en-US/ui/options.ftl @@ -12,6 +12,7 @@ ui-options-reset-all = Reset All ## Audio menu ui-options-master-volume = Master Volume: ui-options-ambient-hum = Ambient Hum +ui-options-lobby-music = Lobby Music ui-options-volume-sliders = Volume Sliders ui-options-volume-percent = { TOSTRING($volume, "P0") } diff --git a/Resources/Prototypes/SoundCollections/lobby.yml b/Resources/Prototypes/SoundCollections/lobby.yml new file mode 100644 index 0000000000..248702970a --- /dev/null +++ b/Resources/Prototypes/SoundCollections/lobby.yml @@ -0,0 +1,7 @@ +- type: soundCollection + id: LobbyMusic + files: + - /Audio/Lobby/thunderdome.ogg + - /Audio/Lobby/absconditus.ogg + - /Audio/Lobby/space_asshole.ogg + - /Audio/Lobby/endless_space.ogg