283 lines
9.3 KiB
C#
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);
|