diff --git a/Content.Client/Audio/BackgroundAudioSystem.cs b/Content.Client/Audio/BackgroundAudioSystem.cs index d71eef1b88..03b69c336a 100644 --- a/Content.Client/Audio/BackgroundAudioSystem.cs +++ b/Content.Client/Audio/BackgroundAudioSystem.cs @@ -1,11 +1,13 @@ -using Content.Client.GameTicking.Managers; using System.Threading; using Content.Client.Gameplay; +using Content.Client.GameTicking.Managers; using Content.Client.Lobby; using Content.Shared.CCVar; using JetBrains.Annotations; using Robust.Client; +using Robust.Client.GameObjects; using Robust.Client.Player; +using Robust.Client.ResourceManagement; using Robust.Client.State; using Robust.Shared.Audio; using Robust.Shared.Configuration; @@ -13,311 +15,291 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; -using Robust.Client.GameObjects; -using Robust.Client.ResourceManagement; using Timer = Robust.Shared.Timing.Timer; -namespace Content.Client.Audio +namespace Content.Client.Audio; + +[UsedImplicitly] +public sealed class BackgroundAudioSystem : EntitySystem { - [UsedImplicitly] - public sealed class BackgroundAudioSystem : EntitySystem + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly IBaseClient _client = default!; + [Dependency] private readonly IConfigurationManager _configManager = default!; + [Dependency] private readonly ClientGameTicker _gameTicker = default!; + [Dependency] private readonly IPlayerManager _playMan = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + [Dependency] private readonly IStateManager _stateManager = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + private readonly AudioParams _ambientParams = new(-10f, 1, "Master", 0, 0, 0, true, 0f); + private readonly AudioParams _lobbyParams = new(-5f, 1, "Master", 0, 0, 0, true, 0f); + + private IPlayingAudioStream? _ambientStream; + private IPlayingAudioStream? _lobbyStream; + + /// + /// What is currently playing. + /// + private SoundCollectionPrototype? _playingCollection; + + /// + /// What the ambience has been set to. + /// + private SoundCollectionPrototype? _currentCollection; + private CancellationTokenSource _timerCancelTokenSource = new(); + + private SoundCollectionPrototype _spaceAmbience = default!; + private SoundCollectionPrototype _stationAmbience = default!; + + public override void Initialize() { - [Dependency] private readonly IBaseClient _client = default!; - [Dependency] private readonly IConfigurationManager _configManager = default!; - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IPlayerManager _playMan = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; - [Dependency] private readonly IStateManager _stateManager = default!; - [Dependency] private readonly ClientGameTicker _gameTicker = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; + base.Initialize(); - private readonly AudioParams _ambientParams = new(-10f, 1, "Master", 0, 0, 0, true, 0f); - private readonly AudioParams _lobbyParams = new(-5f, 1, "Master", 0, 0, 0, true, 0f); + _stationAmbience = _prototypeManager.Index("StationAmbienceBase"); + _spaceAmbience = _prototypeManager.Index("SpaceAmbienceBase"); + _currentCollection = _stationAmbience; - private IPlayingAudioStream? _ambientStream; - private IPlayingAudioStream? _lobbyStream; + // TODO: Ideally audio loading streamed better / we have more robust audio but this is quite annoying + var cache = IoCManager.Resolve(); - /// - /// What is currently playing. - /// - private SoundCollectionPrototype? _playingCollection; - - /// - /// What the ambience has been set to. - /// - private SoundCollectionPrototype? _currentCollection; - private CancellationTokenSource _timerCancelTokenSource = new(); - - private SoundCollectionPrototype _spaceAmbience = default!; - private SoundCollectionPrototype _stationAmbience = default!; - - public override void Initialize() + foreach (var audio in _spaceAmbience.PickFiles) { - base.Initialize(); - - _stationAmbience = _prototypeManager.Index("StationAmbienceBase"); - _spaceAmbience = _prototypeManager.Index("SpaceAmbienceBase"); - _currentCollection = _stationAmbience; - - // TOOD: Ideally audio loading streamed better / we have more robust audio but this is quite annoying - var cache = IoCManager.Resolve(); - - foreach (var audio in _spaceAmbience.PickFiles) - { - cache.GetResource(audio.ToString()); - } - - _configManager.OnValueChanged(CCVars.AmbienceVolume, AmbienceCVarChanged); - _configManager.OnValueChanged(CCVars.LobbyMusicEnabled, LobbyMusicCVarChanged); - _configManager.OnValueChanged(CCVars.StationAmbienceEnabled, StationAmbienceCVarChanged); - _configManager.OnValueChanged(CCVars.SpaceAmbienceEnabled, SpaceAmbienceCVarChanged); - - SubscribeLocalEvent(OnPlayerAttached); - SubscribeLocalEvent(EntParentChanged); - SubscribeLocalEvent(OnPlayerDetached); - - _stateManager.OnStateChanged += StateManagerOnStateChanged; - - _client.PlayerJoinedServer += OnJoin; - _client.PlayerLeaveServer += OnLeave; - - _gameTicker.LobbyStatusUpdated += LobbySongReceived; + cache.GetResource(audio.ToString()); } - private void OnPlayerAttached(PlayerAttachedEvent ev) + _configManager.OnValueChanged(CCVars.AmbienceVolume, AmbienceCVarChanged); + _configManager.OnValueChanged(CCVars.LobbyMusicEnabled, LobbyMusicCVarChanged); + _configManager.OnValueChanged(CCVars.StationAmbienceEnabled, StationAmbienceCVarChanged); + _configManager.OnValueChanged(CCVars.SpaceAmbienceEnabled, SpaceAmbienceCVarChanged); + + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(EntParentChanged); + SubscribeLocalEvent(OnPlayerDetached); + + _stateManager.OnStateChanged += StateManagerOnStateChanged; + + _client.PlayerLeaveServer += OnLeave; + + _gameTicker.LobbyStatusUpdated += LobbySongReceived; + } + + private void OnPlayerAttached(PlayerAttachedEvent ev) + { + if (!TryComp(ev.Entity, out var xform)) + return; + + CheckAmbience(xform); + } + + private void OnPlayerDetached(PlayerDetachedEvent ev) + { + EndAmbience(); + } + + public override void Shutdown() + { + base.Shutdown(); + + _configManager.UnsubValueChanged(CCVars.AmbienceVolume, AmbienceCVarChanged); + _configManager.UnsubValueChanged(CCVars.LobbyMusicEnabled, LobbyMusicCVarChanged); + _configManager.UnsubValueChanged(CCVars.StationAmbienceEnabled, StationAmbienceCVarChanged); + _configManager.UnsubValueChanged(CCVars.SpaceAmbienceEnabled, SpaceAmbienceCVarChanged); + + _stateManager.OnStateChanged -= StateManagerOnStateChanged; + + _client.PlayerLeaveServer -= OnLeave; + + _gameTicker.LobbyStatusUpdated -= LobbySongReceived; + + EndAmbience(); + EndLobbyMusic(); + } + + private void CheckAmbience(TransformComponent xform) + { + if (xform.GridUid != null) + ChangeAmbience(_stationAmbience); + else + ChangeAmbience(_spaceAmbience); + } + + private void EntParentChanged(ref EntParentChangedMessage message) + { + if (_playMan.LocalPlayer is null + || _playMan.LocalPlayer.ControlledEntity != message.Entity + || !_timing.IsFirstTimePredicted) + return; + + // Check if we traversed to grid. + CheckAmbience(message.Transform); + } + + private void ChangeAmbience(SoundCollectionPrototype newAmbience) + { + if (_currentCollection == newAmbience) + return; + + _timerCancelTokenSource.Cancel(); + _currentCollection = newAmbience; + _timerCancelTokenSource = new CancellationTokenSource(); + Timer.Spawn(1500, () => { - if (!TryComp(ev.Entity, out var xform)) + // If we traverse a few times then don't interrupt an existing song. + // If we are not in gameplay, don't call StartAmbience because of player movement + if (_playingCollection == _currentCollection || _stateManager.CurrentState is not GameplayState) return; + StartAmbience(); + }, _timerCancelTokenSource.Token); + } - CheckAmbience(xform); - } - - private void OnPlayerDetached(PlayerDetachedEvent ev) + private void StateManagerOnStateChanged(StateChangedEventArgs args) + { + switch (args.NewState) { - EndAmbience(); - } - - public override void Shutdown() - { - base.Shutdown(); - - _configManager.UnsubValueChanged(CCVars.AmbienceVolume, AmbienceCVarChanged); - _configManager.UnsubValueChanged(CCVars.LobbyMusicEnabled, LobbyMusicCVarChanged); - _configManager.UnsubValueChanged(CCVars.StationAmbienceEnabled, StationAmbienceCVarChanged); - _configManager.UnsubValueChanged(CCVars.SpaceAmbienceEnabled, SpaceAmbienceCVarChanged); - - _stateManager.OnStateChanged -= StateManagerOnStateChanged; - - _client.PlayerJoinedServer -= OnJoin; - _client.PlayerLeaveServer -= OnLeave; - - _gameTicker.LobbyStatusUpdated -= LobbySongReceived; - - EndAmbience(); - EndLobbyMusic(); - } - - private void CheckAmbience(TransformComponent xform) - { - if (xform.GridUid != null) - { - if (_currentCollection == _stationAmbience) - return; - ChangeAmbience(_stationAmbience); - } - else - { - ChangeAmbience(_spaceAmbience); - } - } - - private void EntParentChanged(ref EntParentChangedMessage message) - { - if(_playMan.LocalPlayer is null || _playMan.LocalPlayer.ControlledEntity != message.Entity || - !_timing.IsFirstTimePredicted) - return; - - // Check if we traversed to grid. - CheckAmbience(message.Transform); - } - - private void ChangeAmbience(SoundCollectionPrototype newAmbience) - { - if (_currentCollection == newAmbience) - return; - _timerCancelTokenSource.Cancel(); - _currentCollection = newAmbience; - _timerCancelTokenSource = new(); - Timer.Spawn(1500, () => - { - // If we traverse a few times then don't interrupt an existing song. - // If we are not in gameplay, don't call StartAmbience because of player movement - if (_playingCollection == _currentCollection || _stateManager.CurrentState is not GameplayState) - return; - StartAmbience(); - }, _timerCancelTokenSource.Token); - } - - private void StateManagerOnStateChanged(StateChangedEventArgs args) - { - EndAmbience(); - - if (args.NewState is LobbyState) - { - StartLobbyMusic(); - return; - } - else if (args.NewState is GameplayState) - { - StartAmbience(); - } - - EndLobbyMusic(); - } - - private void OnJoin(object? sender, PlayerEventArgs args) - { - if (_stateManager.CurrentState is LobbyState) - { + case LobbyState: EndAmbience(); StartLobbyMusic(); - } - else - { + break; + case GameplayState: EndLobbyMusic(); StartAmbience(); - } - } - - private void OnLeave(object? sender, PlayerEventArgs args) - { - EndAmbience(); - EndLobbyMusic(); - } - - private void AmbienceCVarChanged(float volume) - { - if (_stateManager.CurrentState is GameplayState) - { - StartAmbience(); - } - else - { + break; + default: EndAmbience(); - } - } - - private void StartAmbience() - { - EndAmbience(); - if (_currentCollection == null || !CanPlayCollection(_currentCollection)) - return; - _playingCollection = _currentCollection; - var file = _robustRandom.Pick(_currentCollection.PickFiles).ToString(); - _ambientStream = _audio.PlayGlobal(file, Filter.Local(), - _ambientParams.WithVolume(_ambientParams.Volume + _configManager.GetCVar(CCVars.AmbienceVolume))); - } - - private void EndAmbience() - { - _playingCollection = null; - _ambientStream?.Stop(); - _ambientStream = null; - } - - private bool CanPlayCollection(SoundCollectionPrototype collection) - { - if (collection.ID == _spaceAmbience.ID) - return _configManager.GetCVar(CCVars.SpaceAmbienceEnabled); - if (collection.ID == _stationAmbience.ID) - return _configManager.GetCVar(CCVars.StationAmbienceEnabled); - - return true; - } - - private void StationAmbienceCVarChanged(bool enabled) - { - if (_currentCollection == null) - return; - - if (enabled && _stateManager.CurrentState is GameplayState && _currentCollection.ID == _stationAmbience.ID) - { - StartAmbience(); - } - else if(_currentCollection.ID == _stationAmbience.ID) - { - EndAmbience(); - } - } - - private void SpaceAmbienceCVarChanged(bool enabled) - { - if (_currentCollection == null) - return; - - if (enabled && _stateManager.CurrentState is GameplayState && _currentCollection.ID == _spaceAmbience.ID) - { - StartAmbience(); - } - else if(_currentCollection.ID == _spaceAmbience.ID) - { - EndAmbience(); - } - } - - 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) - { - StartLobbyMusic(); - } - } - - public void RestartLobbyMusic() - { - EndLobbyMusic(); - StartLobbyMusic(); - } - - public void StartLobbyMusic() - { - if (_lobbyStream != null || !_configManager.GetCVar(CCVars.LobbyMusicEnabled)) - return; - - var file = _gameTicker.LobbySong; - if (file == null) // We have not received the lobby song yet. - { - return; - } - - _lobbyStream = _audio.PlayGlobal(file, Filter.Local(), _lobbyParams); - } - - private void EndLobbyMusic() - { - _lobbyStream?.Stop(); - _lobbyStream = null; + break; } } -} + + private void OnLeave(object? sender, PlayerEventArgs args) + { + EndAmbience(); + EndLobbyMusic(); + } + + private void AmbienceCVarChanged(float volume) + { + if (_stateManager.CurrentState is GameplayState) + { + StartAmbience(); + } + else + { + EndAmbience(); + } + } + + private void StartAmbience() + { + EndAmbience(); + if (_currentCollection == null || !CanPlayCollection(_currentCollection)) + return; + _playingCollection = _currentCollection; + var file = _robustRandom.Pick(_currentCollection.PickFiles).ToString(); + _ambientStream = _audio.PlayGlobal(file, Filter.Local(), + _ambientParams.WithVolume(_ambientParams.Volume + _configManager.GetCVar(CCVars.AmbienceVolume))); + } + + private void EndAmbience() + { + _playingCollection = null; + _ambientStream?.Stop(); + _ambientStream = null; + } + + private bool CanPlayCollection(SoundCollectionPrototype collection) + { + if (collection.ID == _spaceAmbience.ID) + return _configManager.GetCVar(CCVars.SpaceAmbienceEnabled); + if (collection.ID == _stationAmbience.ID) + return _configManager.GetCVar(CCVars.StationAmbienceEnabled); + + return true; + } + + private void StationAmbienceCVarChanged(bool enabled) + { + if (_currentCollection == null) + return; + + if (enabled && _stateManager.CurrentState is GameplayState && _currentCollection.ID == _stationAmbience.ID) + { + StartAmbience(); + } + else if (_currentCollection.ID == _stationAmbience.ID) + { + EndAmbience(); + } + } + + private void SpaceAmbienceCVarChanged(bool enabled) + { + if (_currentCollection == null) + return; + + if (enabled && _stateManager.CurrentState is GameplayState && _currentCollection.ID == _spaceAmbience.ID) + { + StartAmbience(); + } + else if (_currentCollection.ID == _spaceAmbience.ID) + { + EndAmbience(); + } + } + + 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) + { + StartLobbyMusic(); + } + } + + public void RestartLobbyMusic() + { + EndLobbyMusic(); + StartLobbyMusic(); + } + + public void StartLobbyMusic() + { + if (_lobbyStream != null || !_configManager.GetCVar(CCVars.LobbyMusicEnabled)) + return; + + var file = _gameTicker.LobbySong; + if (file == null) // We have not received the lobby song yet. + { + return; + } + + _lobbyStream = _audio.PlayGlobal(file, Filter.Local(), _lobbyParams); + } + + private void EndLobbyMusic() + { + _lobbyStream?.Stop(); + _lobbyStream = null; + } +} \ No newline at end of file