Ambience enhancements. (#7073)

This commit is contained in:
Moony
2022-03-12 16:20:31 -06:00
committed by GitHub
parent 6d2c5868a3
commit f5c92caef4
29 changed files with 290 additions and 68 deletions

View File

@@ -11,9 +11,13 @@ using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Client.Audio
{
//TODO: This is using a incomplete version of the whole "only play nearest sounds" algo, that breaks down a bit should the ambient sound cap get hit.
//TODO: This'll be fixed when GetEntitiesInRange produces consistent outputs.
/// <summary>
/// Samples nearby <see cref="AmbientSoundComponent"/> and plays audio.
/// </summary>
@@ -33,11 +37,11 @@ namespace Content.Client.Audio
/// <summary>
/// How many times we can be playing 1 particular sound at once.
/// </summary>
private int _maxSingleSound = 3;
private int _maxSingleSound = 8;
private Dictionary<AmbientSoundComponent, (IPlayingAudioStream? Stream, string Sound)> _playingSounds = new();
private const float RangeBuffer = 0.5f;
private const float RangeBuffer = 3f;
public override void Initialize()
{
@@ -56,6 +60,7 @@ namespace Content.Client.Audio
public override void Shutdown()
{
base.Shutdown();
ClearSounds();
var configManager = IoCManager.Resolve<IConfigurationManager>();
configManager.UnsubValueChanged(CCVars.AmbientCooldown, SetCooldown);
configManager.UnsubValueChanged(CCVars.MaxAmbientSources, SetAmbientCount);
@@ -99,22 +104,7 @@ namespace Content.Client.Audio
var coordinates = playerManager.Coordinates;
foreach (var (comp, (stream, _)) in _playingSounds.ToArray())
{
if (!comp.Deleted && comp.Enabled && EntityManager.GetComponent<TransformComponent>(comp.Owner).Coordinates.TryDistance(EntityManager, coordinates, out var range) &&
range <= comp.Range)
{
continue;
}
stream?.Stop();
_playingSounds.Remove(comp);
}
if (_playingSounds.Count >= _maxAmbientCount) return;
SampleNearby(coordinates);
ProcessNearbyAmbience(coordinates);
}
private void ClearSounds()
@@ -127,57 +117,136 @@ namespace Content.Client.Audio
_playingSounds.Clear();
}
/// <summary>
/// Get a list of ambient components in range and determine which ones to start playing.
/// </summary>
private void SampleNearby(EntityCoordinates coordinates)
private Dictionary<string, List<AmbientSoundComponent>> GetNearbySources(EntityCoordinates coordinates)
{
var compsInRange = new List<AmbientSoundComponent>();
//TODO: Make this produce a hashset of nearby entities again.
var sourceDict = new Dictionary<string, List<AmbientSoundComponent>>(16);
foreach (var entity in _lookup.GetEntitiesInRange(coordinates, _maxAmbientRange,
LookupFlags.Approximate | LookupFlags.IncludeAnchored))
foreach (var entity in _lookup.GetEntitiesInRange(coordinates, _maxAmbientRange + RangeBuffer, LookupFlags.IncludeAnchored | LookupFlags.Approximate))
{
if (!EntityManager.TryGetComponent(entity, out AmbientSoundComponent? ambientComp) ||
_playingSounds.ContainsKey(ambientComp) ||
!ambientComp.Enabled ||
// We'll also do this crude distance check because it's what we're doing in the active loop above.
!EntityManager.GetComponent<TransformComponent>(entity).Coordinates.TryDistance(EntityManager, coordinates, out var range) ||
range > ambientComp.Range - RangeBuffer)
range > ambientComp.Range)
{
continue;
}
compsInRange.Add(ambientComp);
var key = ambientComp.Sound.GetSound();
if (!sourceDict.ContainsKey(key))
sourceDict[key] = new List<AmbientSoundComponent>(_maxSingleSound);
sourceDict[key].Add(ambientComp);
}
while (_playingSounds.Count < _maxAmbientCount)
foreach (var (key, val) in sourceDict)
{
if (compsInRange.Count == 0) break;
var comp = _random.PickAndTake(compsInRange);
var sound = comp.Sound.GetSound();
if (PlayingCount(sound) >= _maxSingleSound) continue;
var audioParams = AudioHelpers
.WithVariation(0.01f)
.WithVolume(comp.Volume)
.WithLoop(true)
.WithAttenuation(Attenuation.LinearDistance)
// Randomise start so 2 sources don't increase their volume.
.WithPlayOffset(_random.NextFloat())
.WithMaxDistance(comp.Range);
var stream = SoundSystem.Play(
Filter.Local(),
sound,
comp.Owner,
audioParams);
if (stream == null) continue;
_playingSounds[comp] = (stream, sound);
sourceDict[key] = val.OrderByDescending(x =>
Transform(x.Owner).Coordinates.TryDistance(EntityManager, coordinates, out var dist) ? dist : float.MaxValue).ToList();
}
return sourceDict;
}
/// <summary>
/// Get a list of ambient components in range and determine which ones to start playing.
/// </summary>
private void ProcessNearbyAmbience(EntityCoordinates coordinates)
{
var compsInRange= GetNearbySources(coordinates);
var keys = compsInRange.Keys.ToHashSet();
while (keys.Count != 0)
{
if (_playingSounds.Count >= _maxAmbientCount)
{
/*
// Go through and remove everything from compSet
foreach (var toRemove in keys.SelectMany(key => compsInRange[key]))
{
compSet.Remove(toRemove.Owner);
}
*/
break;
}
foreach (var key in keys)
{
if (_playingSounds.Count >= _maxAmbientCount)
break;
if (compsInRange[key].Count == 0)
{
keys.Remove(key);
continue;
}
var comp = compsInRange[key].Pop();
if (_playingSounds.ContainsKey(comp))
continue;
var sound = comp.Sound.GetSound();
if (PlayingCount(sound) >= _maxSingleSound)
{
keys.Remove(key);
/*foreach (var toRemove in compsInRange[key])
{
Logger.Debug($"removing {toRemove.Owner} from set.");
compSet.Remove(toRemove.Owner);
}*/
compsInRange[key].Clear(); // reduce work later should we overrun the max sounds.
continue;
}
var audioParams = AudioHelpers
.WithVariation(0.01f)
.WithVolume(comp.Volume)
.WithLoop(true)
.WithAttenuation(Attenuation.LinearDistance)
// Randomise start so 2 sources don't increase their volume.
.WithPlayOffset(_random.NextFloat(0.0f, 100.0f))
.WithMaxDistance(comp.Range);
var stream = SoundSystem.Play(
Filter.Local(),
sound,
comp.Owner,
audioParams);
if (stream == null) continue;
_playingSounds[comp] = (stream, sound);
}
}
foreach (var (comp, sound) in _playingSounds)
{
var entity = comp.Owner;
if (!comp.Enabled ||
!EntityManager.GetComponent<TransformComponent>(entity).Coordinates
.TryDistance(EntityManager, coordinates, out var range) ||
range > comp.Range)
{
_playingSounds[comp].Stream?.Stop();
_playingSounds.Remove(comp);
}
}
//TODO: Put this code back in place! Currently not done this way because of GetEntitiesInRange being funny.
/*
foreach (var (comp, sound) in _playingSounds)
{
if (compSet.Contains(comp.Owner)) continue;
Logger.Debug($"Cancelled {comp.Owner}");
_playingSounds[comp].Stream?.Stop();
_playingSounds.Remove(comp);
}
*/
}
}
}