* Revert "- fix: YAML linter fixes. (#598)" This reverts commit012bf3c357. * Revert "Automatic changelog update" This reverts commitcf1c3a9af5. * Revert "[Fix] Base Layer Prototype (#597)" This reverts commitb000423999. * Revert "Modules update (#596)" This reverts commit00fbdead77. * Revert "Automatic changelog update" This reverts commit0d7a12b2a2. * Revert "Fixes (#593)" This reverts commit943c77031c. * Revert "minor loadout fixes (#594)" This reverts commit143c010a89. * Revert "Update DryDock.yml (#595)" This reverts commit4cd0100ac7. * Revert "Automatic changelog update" This reverts commit08eadc690f. * Revert "fix: Maximum message size (#591)" This reverts commit343f3612eb. * Revert "Черри пики 7 (#592)" This reverts commit3f97bdce2f. * Revert "Automatic changelog update" This reverts commit0678eca250. * Revert "Рандомфиксы (#590)" This reverts commit2b9e5e2437. * Revert "Нижнее бельё в лодауты (#580)" This reverts commite01a47b089. * Revert "add lathe sounds (#588)" This reverts commitc80a2985f2. * Revert "Добавил параметр группы для некоторых реагентов (#585)" This reverts commit713b16bb98. * Revert "add hrp ++++ aspect (#587)" This reverts commita6a69cc60f. * Revert "Новые амбиенты и пару песен (#586)" This reverts commit48c86bd846. * Revert "Сообщения в ПДА 2 (#583)" This reverts commitcced3cc98b. * Revert "Automatic changelog update" This reverts commitabf435b11d. * Revert "Chem stuff and more (#584)" This reverts commit3608960f5c. * Revert "JobRequiremet refactor (#579)" This reverts commit9a9c9598e0. * Revert "Revert "Reapply "Нижнее бельё в лодауты""" This reverts commit44447d573f. * Revert "Reapply "Нижнее бельё в лодауты"" This reverts commit0c4d082ad3. * Revert "Revert "Нижнее бельё в лодауты"" This reverts commit56473c5492. * Revert "Нижнее бельё в лодауты" This reverts commitd1cb0cb364. * Revert "DryDock and WhiteMoose update (#578)" This reverts commit14755808af. * Revert "Automatic changelog update" This reverts commit0133f82722. * Revert "Fixes (#576)" This reverts commitb7cc49896c. * Revert "порт системы регенерации солюшена цинки (#574)" This reverts commita22cf3d50b. * Revert "Воровские перчатки (#573)" This reverts commitbb7140f3d4. * Revert "mood resprite (#572)" This reverts commit4db96dc569. * Revert "fix missing letter (#571)" This reverts commit94ea756794. * Revert "Сообщения в ПДА (#564)" This reverts commitd023d29e54. * Revert "- fix: No visible aghost." This reverts commit27e7f25f7e. * Revert "- tweak: Nerf cult shield." This reverts commit6a384246b8.
266 lines
8.2 KiB
C#
266 lines
8.2 KiB
C#
using System.Linq;
|
|
using Content.Client.Gameplay;
|
|
using Content.Shared.Audio;
|
|
using Content.Shared.CCVar;
|
|
using Content.Shared.GameTicking;
|
|
using Content.Shared.Random;
|
|
using Robust.Client.GameObjects;
|
|
using Robust.Client.Player;
|
|
using Robust.Client.ResourceManagement;
|
|
using Robust.Client.State;
|
|
using Robust.Shared.Audio;
|
|
using Robust.Shared.Audio.Components;
|
|
using Robust.Shared.Audio.Systems;
|
|
using Robust.Shared.Configuration;
|
|
using Robust.Shared.Player;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Random;
|
|
using Robust.Shared.Timing;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Client.Audio;
|
|
|
|
public sealed partial class ContentAudioSystem
|
|
{
|
|
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
|
[Dependency] private readonly IGameTiming _timing = default!;
|
|
[Dependency] private readonly IPlayerManager _player = default!;
|
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
|
[Dependency] private readonly IRobustRandom _random = default!;
|
|
[Dependency] private readonly IStateManager _state = default!;
|
|
[Dependency] private readonly RulesSystem _rules = default!;
|
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
|
|
private readonly TimeSpan _minAmbienceTime = TimeSpan.FromSeconds(30);
|
|
private readonly TimeSpan _maxAmbienceTime = TimeSpan.FromSeconds(60);
|
|
|
|
private const float AmbientMusicFadeTime = 10f;
|
|
private static float _volumeSlider;
|
|
|
|
// Don't need to worry about this being serializable or pauseable as it doesn't affect the sim.
|
|
private TimeSpan _nextAudio;
|
|
|
|
private EntityUid? _ambientMusicStream;
|
|
private AmbientMusicPrototype? _musicProto;
|
|
|
|
/// <summary>
|
|
/// If we find a better ambient music proto can we interrupt this one.
|
|
/// </summary>
|
|
private bool _interruptable;
|
|
|
|
/// <summary>
|
|
/// Track what ambient sounds we've played. This is so they all get played an even
|
|
/// number of times.
|
|
/// When we get to the end of the list we'll re-shuffle
|
|
/// </summary>
|
|
private readonly Dictionary<string, List<ResPath>> _ambientSounds = new();
|
|
|
|
private ISawmill _sawmill = default!;
|
|
|
|
private void InitializeAmbientMusic()
|
|
{
|
|
Subs.CVar(_configManager, CCVars.AmbientMusicVolume, AmbienceCVarChanged, true);
|
|
_sawmill = IoCManager.Resolve<ILogManager>().GetSawmill("audio.ambience");
|
|
|
|
// Reset audio
|
|
_nextAudio = TimeSpan.MaxValue;
|
|
|
|
SetupAmbientSounds();
|
|
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnProtoReload);
|
|
_state.OnStateChanged += OnStateChange;
|
|
// On round end summary OR lobby cut audio.
|
|
SubscribeNetworkEvent<RoundEndMessageEvent>(OnRoundEndMessage);
|
|
}
|
|
|
|
private void AmbienceCVarChanged(float obj)
|
|
{
|
|
_volumeSlider = SharedAudioSystem.GainToVolume(obj);
|
|
|
|
if (_ambientMusicStream != null && _musicProto != null)
|
|
{
|
|
_audio.SetVolume(_ambientMusicStream, _musicProto.Sound.Params.Volume + _volumeSlider);
|
|
}
|
|
}
|
|
|
|
private void ShutdownAmbientMusic()
|
|
{
|
|
_state.OnStateChanged -= OnStateChange;
|
|
_ambientMusicStream = _audio.Stop(_ambientMusicStream);
|
|
}
|
|
|
|
private void OnProtoReload(PrototypesReloadedEventArgs obj)
|
|
{
|
|
if (obj.WasModified<AmbientMusicPrototype>() || obj.WasModified<RulesPrototype>())
|
|
SetupAmbientSounds();
|
|
}
|
|
|
|
private void OnStateChange(StateChangedEventArgs obj)
|
|
{
|
|
if (obj.NewState is not GameplayState)
|
|
return;
|
|
|
|
// If they go to game then reset the ambience timer.
|
|
_nextAudio = _timing.CurTime + _random.Next(_minAmbienceTime, _maxAmbienceTime);
|
|
}
|
|
|
|
private void SetupAmbientSounds()
|
|
{
|
|
_ambientSounds.Clear();
|
|
foreach (var ambience in _proto.EnumeratePrototypes<AmbientMusicPrototype>())
|
|
{
|
|
var tracks = _ambientSounds.GetOrNew(ambience.ID);
|
|
RefreshTracks(ambience.Sound, tracks, null);
|
|
_random.Shuffle(tracks);
|
|
}
|
|
}
|
|
|
|
private void OnRoundEndMessage(RoundEndMessageEvent ev)
|
|
{
|
|
// If scoreboard shows then just stop the music
|
|
_ambientMusicStream = _audio.Stop(_ambientMusicStream);
|
|
_nextAudio = TimeSpan.FromMinutes(3);
|
|
}
|
|
|
|
private void RefreshTracks(SoundSpecifier sound, List<ResPath> tracks, ResPath? lastPlayed)
|
|
{
|
|
DebugTools.Assert(tracks.Count == 0);
|
|
|
|
switch (sound)
|
|
{
|
|
case SoundCollectionSpecifier collection:
|
|
if (collection.Collection == null)
|
|
break;
|
|
|
|
var slothCud = _proto.Index<SoundCollectionPrototype>(collection.Collection);
|
|
tracks.AddRange(slothCud.PickFiles);
|
|
break;
|
|
case SoundPathSpecifier path:
|
|
tracks.Add(path.Path);
|
|
break;
|
|
}
|
|
|
|
// Just so the same track doesn't play twice
|
|
if (tracks.Count > 1 && tracks[^1] == lastPlayed)
|
|
{
|
|
(tracks[0], tracks[^1]) = (tracks[^1], tracks[0]);
|
|
}
|
|
}
|
|
|
|
private void UpdateAmbientMusic()
|
|
{
|
|
// Update still runs in lobby so just ignore it.
|
|
if (_state.CurrentState is not GameplayState)
|
|
{
|
|
_ambientMusicStream = Audio.Stop(_ambientMusicStream);
|
|
_musicProto = null;
|
|
return;
|
|
}
|
|
|
|
bool? isDone = null;
|
|
|
|
if (TryComp(_ambientMusicStream, out AudioComponent? audioComp))
|
|
{
|
|
isDone = !audioComp.Playing;
|
|
}
|
|
|
|
if (_interruptable)
|
|
{
|
|
var player = _player.LocalSession?.AttachedEntity;
|
|
|
|
if (player == null || _musicProto == null || !_rules.IsTrue(player.Value, _proto.Index<RulesPrototype>(_musicProto.Rules)))
|
|
{
|
|
FadeOut(_ambientMusicStream, duration: AmbientMusicFadeTime);
|
|
_musicProto = null;
|
|
_interruptable = false;
|
|
isDone = true;
|
|
}
|
|
}
|
|
|
|
// Still running existing ambience
|
|
if (isDone == false)
|
|
return;
|
|
|
|
// If ambience finished reset the CD (this also means if we have long ambience it won't clip)
|
|
if (isDone == true)
|
|
{
|
|
// Also don't need to worry about rounding here as it doesn't affect the sim
|
|
_nextAudio = _timing.CurTime + _random.Next(_minAmbienceTime, _maxAmbienceTime);
|
|
}
|
|
|
|
_ambientMusicStream = null;
|
|
|
|
if (_nextAudio > _timing.CurTime)
|
|
return;
|
|
|
|
_musicProto = GetAmbience();
|
|
|
|
if (_musicProto == null)
|
|
{
|
|
_interruptable = false;
|
|
return;
|
|
}
|
|
|
|
_interruptable = _musicProto.Interruptable;
|
|
var tracks = _ambientSounds[_musicProto.ID];
|
|
|
|
var track = tracks[^1];
|
|
tracks.RemoveAt(tracks.Count - 1);
|
|
|
|
var strim = _audio.PlayGlobal(
|
|
track.ToString(),
|
|
Filter.Local(),
|
|
false,
|
|
AudioParams.Default.WithVolume(_musicProto.Sound.Params.Volume + _volumeSlider));
|
|
|
|
_ambientMusicStream = strim.Value.Entity;
|
|
|
|
if (_musicProto.FadeIn)
|
|
{
|
|
FadeIn(_ambientMusicStream, strim.Value.Component, AmbientMusicFadeTime);
|
|
}
|
|
|
|
// Refresh the list
|
|
if (tracks.Count == 0)
|
|
{
|
|
RefreshTracks(_musicProto.Sound, tracks, track);
|
|
}
|
|
}
|
|
|
|
private AmbientMusicPrototype? GetAmbience()
|
|
{
|
|
var player = _player.LocalEntity;
|
|
|
|
if (player == null)
|
|
return null;
|
|
|
|
var ev = new PlayAmbientMusicEvent();
|
|
RaiseLocalEvent(ref ev);
|
|
|
|
if (ev.Cancelled)
|
|
return null;
|
|
|
|
var ambiences = _proto.EnumeratePrototypes<AmbientMusicPrototype>().ToList();
|
|
ambiences.Sort((x, y) => y.Priority.CompareTo(x.Priority));
|
|
|
|
foreach (var amb in ambiences)
|
|
{
|
|
if (!_rules.IsTrue(player.Value, _proto.Index<RulesPrototype>(amb.Rules)))
|
|
continue;
|
|
|
|
return amb;
|
|
}
|
|
|
|
_sawmill.Warning($"Unable to find fallback ambience track");
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fades out the current ambient music temporarily.
|
|
/// </summary>
|
|
public void DisableAmbientMusic()
|
|
{
|
|
FadeOut(_ambientMusicStream);
|
|
_ambientMusicStream = null;
|
|
}
|
|
}
|