Files
tbd-station-14/Content.Client/Audio/ContentAudioSystem.LobbyMusic.cs
nikthechampiongr a5840b925b Fix RA0032 (#32514)
2024-09-29 02:25:21 +02:00

283 lines
9.3 KiB
C#

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 (string.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);