diff --git a/Content.Client/Audio/AmbientSoundSystem.cs b/Content.Client/Audio/AmbientSoundSystem.cs index d2cf387c4e..542ebb4bc7 100644 --- a/Content.Client/Audio/AmbientSoundSystem.cs +++ b/Content.Client/Audio/AmbientSoundSystem.cs @@ -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. + /// /// Samples nearby and plays audio. /// @@ -33,11 +37,11 @@ namespace Content.Client.Audio /// /// How many times we can be playing 1 particular sound at once. /// - private int _maxSingleSound = 3; + private int _maxSingleSound = 8; private Dictionary _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(); 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(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(); } - /// - /// Get a list of ambient components in range and determine which ones to start playing. - /// - private void SampleNearby(EntityCoordinates coordinates) + private Dictionary> GetNearbySources(EntityCoordinates coordinates) { - var compsInRange = new List(); + //TODO: Make this produce a hashset of nearby entities again. + var sourceDict = new Dictionary>(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(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(_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; + } + + /// + /// Get a list of ambient components in range and determine which ones to start playing. + /// + 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(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); + } + */ } } } diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs index c8cb4aa451..7a120f79b1 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPressurePumpSystem.cs @@ -8,6 +8,7 @@ using Content.Server.NodeContainer.Nodes; using Content.Shared.Atmos; using Content.Shared.Atmos.Piping; using Content.Shared.Atmos.Piping.Binary.Components; +using Content.Shared.Audio; using Content.Shared.Database; using Content.Shared.Examine; using Content.Shared.Interaction; @@ -20,9 +21,10 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems [UsedImplicitly] public sealed class GasPressurePumpSystem : EntitySystem { - [Dependency] private UserInterfaceSystem _userInterfaceSystem = default!; - [Dependency] private AdminLogSystem _adminLogSystem = default!; + [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; + [Dependency] private readonly AdminLogSystem _adminLogSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; public override void Initialize() { @@ -59,6 +61,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems || !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet)) { appearance?.SetData(PumpVisuals.Enabled, false); + _ambientSoundSystem.SetAmbience(pump.Owner, false); return; } @@ -67,12 +70,14 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems if (MathHelper.CloseToPercent(pump.TargetPressure, outputStartingPressure)) { appearance?.SetData(PumpVisuals.Enabled, false); + _ambientSoundSystem.SetAmbience(pump.Owner, false); return; // No need to pump gas if target has been reached. } if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0) { appearance?.SetData(PumpVisuals.Enabled, true); + _ambientSoundSystem.SetAmbience(pump.Owner, true); // We calculate the necessary moles to transfer using our good ol' friend PV=nRT. var pressureDelta = pump.TargetPressure - outputStartingPressure; diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs index cec4cdab22..d15d245466 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasValveSystem.cs @@ -20,6 +20,8 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems [UsedImplicitly] public sealed class GasValveSystem : EntitySystem { + [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; + public override void Initialize() { base.Initialize(); @@ -68,11 +70,13 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems { inlet.AddAlwaysReachable(outlet); outlet.AddAlwaysReachable(inlet); + _ambientSoundSystem.SetAmbience(component.Owner, true); } else { inlet.RemoveAlwaysReachable(outlet); outlet.RemoveAlwaysReachable(inlet); + _ambientSoundSystem.SetAmbience(component.Owner, false); } } } diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs index 9df988fdd6..daead286ca 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs @@ -6,6 +6,7 @@ using Content.Server.NodeContainer; using Content.Server.NodeContainer.Nodes; using Content.Shared.Atmos.Piping; using Content.Shared.Atmos.Piping.Binary.Components; +using Content.Shared.Audio; using Content.Shared.Database; using Content.Shared.Examine; using Content.Shared.Interaction; @@ -21,8 +22,9 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems { [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; - [Dependency] private UserInterfaceSystem _userInterfaceSystem = default!; - [Dependency] private AdminLogSystem _adminLogSystem = default!; + [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; + [Dependency] private readonly AdminLogSystem _adminLogSystem = default!; + [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; public override void Initialize() { @@ -59,6 +61,8 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems || !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet)) { appearance?.SetData(PumpVisuals.Enabled, false); + _ambientSoundSystem.SetAmbience(pump.Owner, false); + _ambientSoundSystem.SetAmbience(pump.Owner, false); return; } @@ -74,6 +78,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems return; appearance?.SetData(PumpVisuals.Enabled, true); + _ambientSoundSystem.SetAmbience(pump.Owner, true); // We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters. var transferRatio = (float)(pump.TransferRate * (_gameTiming.CurTime - device.LastProcess).TotalSeconds) / inlet.Air.Volume; diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs index 8568b94ff3..e663647bae 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs @@ -9,6 +9,7 @@ using Content.Server.UserInterface; using Content.Shared.Atmos; using Content.Shared.Atmos.Piping; using Content.Shared.Atmos.Piping.Trinary.Components; +using Content.Shared.Audio; using Content.Shared.Database; using Content.Shared.Interaction; using Content.Shared.Popups; @@ -28,6 +29,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems [Dependency] private UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private AdminLogSystem _adminLogSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; public override void Initialize() { @@ -50,7 +52,10 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems component.Enabled = false; if (TryComp(uid, out AppearanceComponent? appearance)) + { appearance.SetData(FilterVisuals.Enabled, false); + _ambientSoundSystem.SetAmbience(component.Owner, false); + } DirtyUI(uid, component); _userInterfaceSystem.TryCloseAll(uid, GasFilterUiKey.Key); @@ -69,6 +74,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems || outletNode.Air.Pressure >= Atmospherics.MaxOutputPressure) // No need to transfer if target is full. { appearance?.SetData(FilterVisuals.Enabled, false); + _ambientSoundSystem.SetAmbience(filter.Owner, false); return; } @@ -78,6 +84,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems if (transferRatio <= 0) { appearance?.SetData(FilterVisuals.Enabled, false); + _ambientSoundSystem.SetAmbience(filter.Owner, false); return; } @@ -94,6 +101,14 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems var target = filterNode.Air.Pressure < Atmospherics.MaxOutputPressure ? filterNode : inletNode; _atmosphereSystem.Merge(target.Air, filteredOut); + if (filteredOut.Pressure != 0f) + { + _ambientSoundSystem.SetAmbience(filter.Owner, true); + } + else + { + _ambientSoundSystem.SetAmbience(filter.Owner, false); + } } _atmosphereSystem.Merge(outletNode.Air, removed); diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs index 30a33e2246..3bdd6081e9 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs @@ -7,6 +7,7 @@ using Content.Server.NodeContainer.Nodes; using Content.Shared.Atmos; using Content.Shared.Atmos.Piping; using Content.Shared.Atmos.Piping.Trinary.Components; +using Content.Shared.Audio; using Content.Shared.Database; using Content.Shared.Interaction; using Content.Shared.Popups; @@ -21,6 +22,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems [Dependency] private UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private AdminLogSystem _adminLogSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; public override void Initialize() { @@ -59,7 +61,10 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems // TODO ATMOS: Cache total moles since it's expensive. if (!mixer.Enabled) + { + _ambientSoundSystem.SetAmbience(mixer.Owner, false); return; + } if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) return; @@ -67,13 +72,18 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems if (!nodeContainer.TryGetNode(mixer.InletOneName, out PipeNode? inletOne) || !nodeContainer.TryGetNode(mixer.InletTwoName, out PipeNode? inletTwo) || !nodeContainer.TryGetNode(mixer.OutletName, out PipeNode? outlet)) + { + _ambientSoundSystem.SetAmbience(mixer.Owner, false); return; + } var outputStartingPressure = outlet.Air.Pressure; if (outputStartingPressure >= mixer.TargetPressure) return; // Target reached, no need to mix. + + var generalTransfer = (mixer.TargetPressure - outputStartingPressure) * outlet.Air.Volume / Atmospherics.R; var transferMolesOne = inletOne.Air.Temperature > 0 ? mixer.InletOneConcentration * generalTransfer / inletOne.Air.Temperature : 0f; @@ -102,7 +112,10 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems return; if (transferMolesOne <= 0 || transferMolesTwo <= 0) + { + _ambientSoundSystem.SetAmbience(mixer.Owner, false); return; + } if (inletOne.Air.TotalMoles < transferMolesOne || inletTwo.Air.TotalMoles < transferMolesTwo) { @@ -125,6 +138,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems var removed = inletTwo.Air.Remove(transferMolesTwo); _atmosphereSystem.Merge(outlet.Air, removed); } + _ambientSoundSystem.SetAmbience(mixer.Owner, true); } private void OnMixerInteractHand(EntityUid uid, GasMixerComponent component, InteractHandEvent args) diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs index 0e61af26c1..cb2407924d 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs @@ -13,6 +13,7 @@ using Content.Shared.Atmos; using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Atmos.Visuals; +using Content.Shared.Audio; using JetBrains.Annotations; using Robust.Shared.Timing; @@ -24,6 +25,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; public override void Initialize() { @@ -52,6 +54,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems || !nodeContainer.TryGetNode(vent.InletName, out PipeNode? pipe)) { appearance?.SetData(VentPumpVisuals.State, VentPumpState.Off); + _ambientSoundSystem.SetAmbience(vent.Owner, false); return; } @@ -61,6 +64,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems if (environment == null) { appearance?.SetData(VentPumpVisuals.State, VentPumpState.Off); + _ambientSoundSystem.SetAmbience(vent.Owner, false); return; } @@ -70,6 +74,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems if (vent.PumpDirection == VentPumpDirection.Releasing && pipe.Air.Pressure > 0) { appearance?.SetData(VentPumpVisuals.State, VentPumpState.Out); + _ambientSoundSystem.SetAmbience(vent.Owner, true); if (environment.Pressure > vent.MaxPressure) return; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs index 98d032aefd..daa93feaf5 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs @@ -14,6 +14,7 @@ using Content.Shared.Atmos; using Content.Shared.Atmos.Piping.Unary.Visuals; using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Piping.Unary.Components; +using Content.Shared.Audio; using JetBrains.Annotations; using Robust.Shared.Timing; @@ -25,6 +26,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!; + [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; public override void Initialize() { @@ -44,6 +46,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems if (scrubber.Welded) { appearance?.SetData(ScrubberVisuals.State, ScrubberState.Welded); + _ambientSoundSystem.SetAmbience(scrubber.Owner, false); return; } @@ -57,8 +60,10 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems || !nodeContainer.TryGetNode(scrubber.OutletName, out PipeNode? outlet)) { appearance?.SetData(ScrubberVisuals.State, ScrubberState.Off); + _ambientSoundSystem.SetAmbience(scrubber.Owner, false); return; } + _ambientSoundSystem.SetAmbience(scrubber.Owner, true); var environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent(scrubber.Owner).Coordinates, true); diff --git a/Content.Shared/Audio/AmbientSoundComponent.cs b/Content.Shared/Audio/AmbientSoundComponent.cs index b5b6b55719..752102bb12 100644 --- a/Content.Shared/Audio/AmbientSoundComponent.cs +++ b/Content.Shared/Audio/AmbientSoundComponent.cs @@ -16,7 +16,7 @@ namespace Content.Shared.Audio [DataField("enabled")] public bool Enabled { get; set; } = true; - [DataField("sound")] + [DataField("sound", required: true)] public SoundSpecifier Sound = default!; /// diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 2e2e4bc5e5..78093a53a4 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -31,13 +31,13 @@ namespace Content.Shared.CCVar /// How long we'll wait until re-sampling nearby objects for ambience. /// public static readonly CVarDef AmbientCooldown = - CVarDef.Create("ambience.cooldown", 0.5f, CVar.REPLICATED | CVar.SERVER); + CVarDef.Create("ambience.cooldown", 0.1f, CVar.REPLICATED | CVar.SERVER); public static readonly CVarDef AmbientRange = CVarDef.Create("ambience.range", 5f, CVar.REPLICATED | CVar.SERVER); public static readonly CVarDef MaxAmbientSources = - CVarDef.Create("ambience.max_sounds", 6, CVar.REPLICATED | CVar.SERVER); + CVarDef.Create("ambience.max_sounds", 64, CVar.REPLICATED | CVar.SERVER); /* * Status diff --git a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs index 1d048ee332..2e97064687 100644 --- a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs +++ b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Audio; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Maps; @@ -16,6 +17,7 @@ namespace Content.Shared.SubFloor [Dependency] protected readonly IMapManager MapManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly TrayScannerSystem _trayScannerSystem = default!; + [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; public override void Initialize() { @@ -181,6 +183,10 @@ namespace Content.Shared.SubFloor appearance.SetData(SubFloorVisuals.Covered, hideComp.IsUnderCover); appearance.SetData(SubFloorVisuals.ScannerRevealed, hideComp.RevealedBy.Count != 0); + if (hideComp.BlockAmbience && hideComp.IsUnderCover) + _ambientSoundSystem.SetAmbience(uid, false); + else if (hideComp.BlockAmbience && !hideComp.IsUnderCover) + _ambientSoundSystem.SetAmbience(uid, true); } } diff --git a/Content.Shared/SubFloor/SubFloorHideComponent.cs b/Content.Shared/SubFloor/SubFloorHideComponent.cs index 22978276ba..c72e2518d0 100644 --- a/Content.Shared/SubFloor/SubFloorHideComponent.cs +++ b/Content.Shared/SubFloor/SubFloorHideComponent.cs @@ -29,6 +29,15 @@ namespace Content.Shared.SubFloor [DataField("blockInteractions")] public bool BlockInteractions { get; set; } = true; + /// + /// Whether this entity's ambience should be disabled when underneath the floor. + /// + /// + /// Useful for cables and piping, gives maint it's distinct noise. + /// + [DataField("blockAmbience")] + public bool BlockAmbience { get; set; } = true; + /// /// When revealed using some scanning tool, what transparency should be used to draw this item? /// diff --git a/Resources/Audio/Ambience/Objects/gas_hiss.ogg b/Resources/Audio/Ambience/Objects/gas_hiss.ogg new file mode 100644 index 0000000000..439b5e18a5 Binary files /dev/null and b/Resources/Audio/Ambience/Objects/gas_hiss.ogg differ diff --git a/Resources/Audio/Ambience/Objects/gas_pump.ogg b/Resources/Audio/Ambience/Objects/gas_pump.ogg new file mode 100644 index 0000000000..f21ee62dd0 Binary files /dev/null and b/Resources/Audio/Ambience/Objects/gas_pump.ogg differ diff --git a/Resources/Audio/Ambience/Objects/gas_vent.ogg b/Resources/Audio/Ambience/Objects/gas_vent.ogg new file mode 100644 index 0000000000..b20d052df3 Binary files /dev/null and b/Resources/Audio/Ambience/Objects/gas_vent.ogg differ diff --git a/Resources/Audio/Ambience/Objects/license.txt b/Resources/Audio/Ambience/Objects/license.txt index f0c29b5b7b..1a671be530 100644 --- a/Resources/Audio/Ambience/Objects/license.txt +++ b/Resources/Audio/Ambience/Objects/license.txt @@ -1 +1,4 @@ -circular_saw.ogg - https://freesound.org/people/derjuli/sounds/448133/ and clipped - CC0-1.0 \ No newline at end of file +circular_saw.ogg - https://freesound.org/people/derjuli/sounds/448133/ and clipped - CC0-1.0 +gas_pump - https://freesound.org/people/karinalarasart/sounds/441419/ - CC0-1.0 +gas_hiss - https://freesound.org/people/geodylabs/sounds/122803/ - CC BY 3.0 +gas_vent - https://freesound.org/people/kyles/sounds/453642/ - CC0-1.0 diff --git a/Resources/Locale/en-US/shell.ftl b/Resources/Locale/en-US/shell.ftl index b912b9dc1b..a3df99418a 100644 --- a/Resources/Locale/en-US/shell.ftl +++ b/Resources/Locale/en-US/shell.ftl @@ -22,6 +22,7 @@ shell-need-between-arguments = Need {$lower} to {$upper} arguments! shell-entity-is-not-mob = Target entity is not a mob! shell-invalid-entity-id = Invalid entity ID. shell-invalid-grid-id = Invalid grid ID. +shell-invalid-map-id = Invalid map ID. shell-invalid-entity-uid = {$uid} is not a valid entity uid shell-entity-uid-must-be-number = EntityUid must be a number. shell-could-not-find-entity = Could not find entity {$entity} @@ -34,3 +35,5 @@ shell-timespan-minutes-must-be-correct = {$span} is not a valid minutes timespan shell-argument-must-be-prototype = Argument {$index} must be a ${prototypeName}! shell-argument-number-must-be-between = Argument {$index} must be a number between {$lower} and {$upper}! shell-argument-station-id-invalid = Argument {$index} must be a valid station id! +shell-argument-map-id-invalid = Argument {$index} must be a valid map id! +shell-argument-number-invalid = Argument {$index} must be a valid number! diff --git a/Resources/Prototypes/Entities/Structures/Machines/gravity_generator.yml b/Resources/Prototypes/Entities/Structures/Machines/gravity_generator.yml index bbcda67664..3cfe172b2c 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/gravity_generator.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/gravity_generator.yml @@ -7,6 +7,7 @@ mode: AlignTileAny components: - type: AmbientSound + volume: -6 range: 7 sound: path: /Audio/Ambience/Objects/gravity_gen_hum.ogg diff --git a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml index 5f65b53f93..9d46ace9d8 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml @@ -7,7 +7,7 @@ components: - type: AmbientOnPowered - type: AmbientSound - volume: -14 + volume: -9 range: 3 sound: path: /Audio/Ambience/Objects/vending_machine_hum.ogg diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml index d62ea2bf0c..4e6d190112 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml @@ -8,6 +8,7 @@ - type: AtmosDevice - type: SubFloorHide blockInteractions: false + blockAmbience: false - type: NodeContainer nodes: inlet: @@ -50,6 +51,12 @@ - type: Construction graph: GasBinary node: pressurepump + - type: AmbientSound + enabled: false + volume: -9 + range: 5 + sound: + path: /Audio/Ambience/Objects/gas_pump.ogg - type: entity parent: GasBinaryBase @@ -82,6 +89,12 @@ - type: Construction graph: GasBinary node: volumepump + - type: AmbientSound + enabled: false + volume: -9 + range: 5 + sound: + path: /Audio/Ambience/Objects/gas_pump.ogg - type: entity parent: GasBinaryBase @@ -107,6 +120,12 @@ - type: Construction graph: GasBinary node: passivegate + - type: AmbientSound + enabled: true + volume: -9 + range: 5 + sound: + path: /Audio/Ambience/Objects/gas_hiss.ogg - type: entity parent: GasBinaryBase @@ -148,6 +167,12 @@ - type: Construction graph: GasBinary node: valve + - type: AmbientSound + enabled: false + volume: -9 + range: 5 + sound: + path: /Audio/Ambience/Objects/gas_hiss.ogg - type: entity parent: GasBinaryBase diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml index 470a3723f7..43543b2896 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml @@ -65,6 +65,12 @@ - type: Tag tags: - Pipe + - type: AmbientSound + enabled: true + volume: -15 + range: 2 + sound: + path: /Audio/Ambience/Objects/gas_hiss.ogg #Note: The PipeDirection of the PipeNode should be the south-facing version, because the entity starts at an angle of 0 (south) diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml index 1d9a8dcd21..99bced0865 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml @@ -8,6 +8,7 @@ - type: AtmosDevice - type: SubFloorHide blockInteractions: false + blockAmbience: false - type: NodeContainer nodes: inlet: @@ -58,6 +59,12 @@ - type: Construction graph: GasTrinary node: filter + - type: AmbientSound + enabled: false + volume: -9 + range: 5 + sound: + path: /Audio/Ambience/Objects/gas_hiss.ogg - type: entity parent: GasFilter @@ -136,6 +143,12 @@ - type: Construction graph: GasTrinary node: mixer + - type: AmbientSound + enabled: false + volume: -9 + range: 5 + sound: + path: /Audio/Ambience/Objects/gas_hiss.ogg - type: entity parent: GasMixer diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml index dc1db9ba7b..9da42ceb0b 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml @@ -8,6 +8,7 @@ - type: AtmosDevice - type: SubFloorHide blockInteractions: false + blockAmbience: false - type: NodeContainer nodes: pipe: @@ -51,6 +52,12 @@ graph: GasUnary node: ventpump - type: VentCritterSpawnLocation + - type: AmbientSound + enabled: false + volume: -12 + range: 5 + sound: + path: /Audio/Ambience/Objects/gas_vent.ogg - type: entity parent: GasUnaryBase @@ -115,6 +122,12 @@ - type: Construction graph: GasUnary node: ventscrubber + - type: AmbientSound + enabled: false + volume: -12 + range: 5 + sound: + path: /Audio/Ambience/Objects/gas_vent.ogg - type: entity parent: GasUnaryBase diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml index c10ec16043..edd52656de 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml @@ -5,7 +5,7 @@ components: - type: Clickable - type: AmbientSound - volume: -10 + volume: -4 range: 14 sound: path: /Audio/Effects/singularity.ogg diff --git a/Resources/Prototypes/Entities/Structures/Power/apc.yml b/Resources/Prototypes/Entities/Structures/Power/apc.yml index 3669d521ad..440dc5e729 100644 --- a/Resources/Prototypes/Entities/Structures/Power/apc.yml +++ b/Resources/Prototypes/Entities/Structures/Power/apc.yml @@ -8,7 +8,7 @@ components: - type: AmbientOnPowered - type: AmbientSound - volume: -15 + volume: -9 range: 2 sound: path: /Audio/Ambience/Objects/hdd_buzz.ogg diff --git a/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml b/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml index 0268d9e61e..0e5a448cce 100644 --- a/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml +++ b/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml @@ -33,6 +33,8 @@ - !type:DoActsBehavior acts: ["Destruction"] - type: SubFloorHide + blockAmbience: false + blockInteractions: false - type: Construction graph: CableTerminal node: cable_terminal diff --git a/Resources/Prototypes/Entities/Structures/Power/cables.yml b/Resources/Prototypes/Entities/Structures/Power/cables.yml index f4fa729601..2ab2c32c02 100644 --- a/Resources/Prototypes/Entities/Structures/Power/cables.yml +++ b/Resources/Prototypes/Entities/Structures/Power/cables.yml @@ -86,6 +86,12 @@ visuals: - type: CableVisualizer base: hvcable_ + - type: AmbientSound + enabled: true + volume: -15 + range: 2 + sound: + path: /Audio/Ambience/Objects/emf_buzz.ogg - type: entity parent: CableBase @@ -132,6 +138,12 @@ visuals: - type: CableVisualizer base: mvcable_ + - type: AmbientSound + enabled: true + volume: -16 + range: 2 + sound: + path: /Audio/Ambience/Objects/emf_buzz.ogg - type: entity parent: CableBase @@ -181,3 +193,9 @@ visuals: - type: CableVisualizer base: lvcable_ + - type: AmbientSound + enabled: true + volume: -17 + range: 2 + sound: + path: /Audio/Ambience/Objects/emf_buzz.ogg diff --git a/Resources/Prototypes/Entities/Structures/Power/smes.yml b/Resources/Prototypes/Entities/Structures/Power/smes.yml index aaeab35969..a5bbabda7f 100644 --- a/Resources/Prototypes/Entities/Structures/Power/smes.yml +++ b/Resources/Prototypes/Entities/Structures/Power/smes.yml @@ -8,6 +8,7 @@ mode: SnapgridCenter components: - type: AmbientSound + volume: -7 range: 3 sound: path: /Audio/Ambience/Objects/periodic_beep.ogg diff --git a/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml b/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml index 707a10a8a4..a8032b9f6d 100644 --- a/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml +++ b/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml @@ -7,7 +7,7 @@ components: - type: AmbientSound range: 4 - volume: -5 + volume: -4 sound: path: /Audio/Effects/shuttle_thruster.ogg - type: Transform @@ -60,7 +60,7 @@ map: ["enum.ThrusterVisualLayers.ThrustingUnshaded"] shader: unshaded offset: 0, 1 - + - type: entity id: DebugThruster parent: BaseThruster