Ambience enhancements. (#7073)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user