| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- using System.Linq;
- using Content.Client.GameTicking.Managers;
- using Content.Client.Lobby;
- using Content.Shared.Audio.Events;
- using Content.Shared.CCVar;
- using Content.Shared.GameTicking;
- using Robust.Client;
- using Robust.Client.ResourceManagement;
- using Robust.Client.State;
- using Robust.Shared.Audio;
- using Robust.Shared.Audio.Systems;
- using Robust.Shared.Player;
- using Robust.Shared.Timing;
- using Robust.Shared.Utility;
- namespace Content.Client.Audio;
- // Part of ContentAudioSystem that is responsible for lobby music playing/stopping and round-end sound-effect.
- public sealed partial class ContentAudioSystem
- {
- [Dependency] private readonly IBaseClient _client = default!;
- [Dependency] private readonly ClientGameTicker _gameTicker = default!;
- [Dependency] private readonly IResourceCache _resourceCache = default!;
- private readonly AudioParams _lobbySoundtrackParams = new(-5f, 1, 0, 0, 0, false, 0f);
- private readonly AudioParams _roundEndSoundEffectParams = new(-5f, 1, 0, 0, 0, false, 0f);
- /// <summary>
- /// EntityUid of lobby restart sound component.
- /// </summary>
- private EntityUid? _lobbyRoundRestartAudioStream;
- /// <summary>
- /// Shuffled list of soundtrack file-names.
- /// </summary>
- private string[]? _lobbyPlaylist;
- /// <summary>
- /// Short info about lobby soundtrack currently playing. Is null if soundtrack is not playing.
- /// </summary>
- private LobbySoundtrackInfo? _lobbySoundtrackInfo;
- private Action<LobbySoundtrackChangedEvent>? _lobbySoundtrackChanged;
- /// <summary>
- /// Event for subscription on lobby soundtrack changes.
- /// </summary>
- public event Action<LobbySoundtrackChangedEvent>? LobbySoundtrackChanged
- {
- add
- {
- if (value != null)
- {
- if (_lobbySoundtrackInfo != null)
- {
- value(new LobbySoundtrackChangedEvent(_lobbySoundtrackInfo.Filename));
- }
- _lobbySoundtrackChanged += value;
- }
- }
- remove => _lobbySoundtrackChanged -= value;
- }
- /// <summary>
- /// Initializes subscriptions that are related to lobby music.
- /// </summary>
- private void InitializeLobbyMusic()
- {
- Subs.CVar(_configManager, CCVars.LobbyMusicEnabled, LobbyMusicCVarChanged);
- Subs.CVar(_configManager, CCVars.LobbyMusicVolume, LobbyMusicVolumeCVarChanged);
- _state.OnStateChanged += StateManagerOnStateChanged;
- _client.PlayerLeaveServer += OnLeave;
- SubscribeNetworkEvent<LobbyMusicStopEvent>(OnLobbySongStopped);
- SubscribeNetworkEvent<LobbyPlaylistChangedEvent>(OnLobbySongChanged);
- }
- private void OnLobbySongStopped(LobbyMusicStopEvent ev)
- {
- EndLobbyMusic();
- }
- private void StateManagerOnStateChanged(StateChangedEventArgs args)
- {
- switch (args.NewState)
- {
- case LobbyState:
- StartLobbyMusic();
- break;
- default:
- EndLobbyMusic();
- break;
- }
- }
- private void OnLeave(object? sender, PlayerEventArgs args)
- {
- EndLobbyMusic();
- }
- private void LobbyMusicVolumeCVarChanged(float volume)
- {
- if (_lobbySoundtrackInfo != null)
- {
- _audio.SetVolume(
- _lobbySoundtrackInfo.MusicStreamEntityUid,
- _lobbySoundtrackParams.Volume + SharedAudioSystem.GainToVolume(_configManager.GetCVar(CCVars.LobbyMusicVolume))
- );
- }
- }
- private void LobbyMusicCVarChanged(bool musicEnabled)
- {
- if (musicEnabled && _state.CurrentState is LobbyState)
- {
- StartLobbyMusic();
- }
- else
- {
- EndLobbyMusic();
- }
- }
- private void OnLobbySongChanged(LobbyPlaylistChangedEvent playlistChangedEvent)
- {
- var playlist = playlistChangedEvent.Playlist;
- //playlist is already playing, no need to restart it
- if (_lobbySoundtrackInfo != null
- && _lobbyPlaylist != null
- && _lobbyPlaylist.SequenceEqual(playlist)
- )
- {
- return;
- }
- EndLobbyMusic();
- StartLobbyMusic(playlistChangedEvent.Playlist);
- }
- /// <summary>
- /// Re-starts playing lobby music from playlist, last sent from server. if there is currently none - does nothing.
- /// </summary>
- private void StartLobbyMusic()
- {
- if (_lobbyPlaylist == null || _lobbyPlaylist.Length == 0)
- {
- return;
- }
- StartLobbyMusic(_lobbyPlaylist);
- }
- /// <summary>
- /// Starts playing lobby music from playlist. If playlist is empty, or lobby music setting is turned off - does nothing.
- /// </summary>
- /// <param name="playlist">Array of soundtrack filenames for lobby playlist.</param>
- private void StartLobbyMusic(string[] playlist)
- {
- if (_lobbySoundtrackInfo != null || !_configManager.GetCVar(CCVars.LobbyMusicEnabled))
- return;
- _lobbyPlaylist = playlist;
- if (_lobbyPlaylist.Length == 0)
- {
- return;
- }
- PlaySoundtrack(playlist[0]);
- }
- private void PlaySoundtrack(string soundtrackFilename)
- {
- if (!_resourceCache.TryGetResource(new ResPath(soundtrackFilename), out AudioResource? audio))
- {
- return;
- }
- var playResult = _audio.PlayGlobal(
- soundtrackFilename,
- Filter.Local(),
- false,
- _lobbySoundtrackParams.WithVolume(_lobbySoundtrackParams.Volume + SharedAudioSystem.GainToVolume(_configManager.GetCVar(CCVars.LobbyMusicVolume)))
- );
- if (playResult == null)
- {
- _sawmill.Warning(
- $"Tried to play lobby soundtrack '{{Filename}}' using {nameof(SharedAudioSystem)}.{nameof(SharedAudioSystem.PlayGlobal)} but it returned default value of EntityUid!",
- soundtrackFilename);
- return;
- }
- var nextTrackOn = _timing.CurTime + audio.AudioStream.Length;
- _lobbySoundtrackInfo = new LobbySoundtrackInfo(soundtrackFilename, nextTrackOn, playResult.Value.Entity);
- var lobbySongChangedEvent = new LobbySoundtrackChangedEvent(soundtrackFilename);
- _lobbySoundtrackChanged?.Invoke(lobbySongChangedEvent);
- }
- private void EndLobbyMusic()
- {
- if (_lobbySoundtrackInfo == null)
- {
- return;
- }
- _audio.Stop(_lobbySoundtrackInfo.MusicStreamEntityUid);
- _lobbySoundtrackInfo = null;
- var lobbySongChangedEvent = new LobbySoundtrackChangedEvent();
- _lobbySoundtrackChanged?.Invoke(lobbySongChangedEvent);
- }
- private void PlayRestartSound(RoundRestartCleanupEvent ev)
- {
- if (!_configManager.GetCVar(CCVars.RestartSoundsEnabled))
- return;
- var file = _gameTicker.RestartSound;
- if (ResolvedSoundSpecifier.IsNullOrEmpty(file))
- {
- return;
- }
- _lobbyRoundRestartAudioStream = _audio.PlayGlobal(
- file,
- Filter.Local(),
- false,
- _roundEndSoundEffectParams.WithVolume(_roundEndSoundEffectParams.Volume + SharedAudioSystem.GainToVolume(_configManager.GetCVar(CCVars.LobbyMusicVolume)))
- )?.Entity;
- }
- private void ShutdownLobbyMusic()
- {
- _state.OnStateChanged -= StateManagerOnStateChanged;
- _client.PlayerLeaveServer -= OnLeave;
- EndLobbyMusic();
- }
- private void UpdateLobbyMusic()
- {
- if (
- _lobbySoundtrackInfo != null
- && _timing.CurTime >= _lobbySoundtrackInfo.NextTrackOn
- && _lobbyPlaylist?.Length > 0
- )
- {
- var nextSoundtrackFilename = GetNextSoundtrackFromPlaylist(_lobbySoundtrackInfo.Filename, _lobbyPlaylist);
- PlaySoundtrack(nextSoundtrackFilename);
- }
- }
- private static string GetNextSoundtrackFromPlaylist(string currentSoundtrackFilename, string[] playlist)
- {
- var indexOfCurrent = Array.IndexOf(playlist, currentSoundtrackFilename);
- var nextTrackIndex = indexOfCurrent + 1;
- if (nextTrackIndex > playlist.Length - 1)
- {
- nextTrackIndex = 0;
- }
- return playlist[nextTrackIndex];
- }
- /// <summary> Container for lobby soundtrack information. </summary>
- /// <param name="Filename">Soundtrack filename.</param>
- /// <param name="NextTrackOn">Time (based on <see cref="IGameTiming.CurTime"/>) when this track is going to finish playing and next track have to be started.</param>
- /// <param name="MusicStreamEntityUid">
- /// EntityUid of launched soundtrack (from <see cref="SharedAudioSystem.PlayGlobal(string,Robust.Shared.Player.Filter,bool,System.Nullable{Robust.Shared.Audio.AudioParams})"/>).
- /// </param>
- private sealed record LobbySoundtrackInfo(string Filename, TimeSpan NextTrackOn, EntityUid MusicStreamEntityUid);
- }
- /// <summary>
- /// Event of changing lobby soundtrack (or stopping lobby music - will pass null for <paramref name="SoundtrackFilename"/> in that case).
- /// Is used by <see cref="ContentAudioSystem.LobbySoundtrackChanged"/> and <see cref="LobbyState.UpdateLobbySoundtrackInfo"/>.
- /// </summary>
- /// <param name="SoundtrackFilename">Filename of newly set soundtrack, or null if soundtrack playback is stopped.</param>
- public sealed record LobbySoundtrackChangedEvent(string? SoundtrackFilename = null);
|