From 465a1fb5bd91bcb7ed17cfc8c9e3b342e5997e89 Mon Sep 17 00:00:00 2001
From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
Date: Sun, 30 Nov 2025 11:32:26 -0500
Subject: [PATCH] Allow content to override ProcessStream and GetOcclusion in
AudioSystem (#6268)
* Add optional override methods for ProcessStream and GetOcclusion
* Mark override events as static
* Change audio actions to non-static
---
Robust.Client/Audio/AudioSystem.cs | 38 ++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/Robust.Client/Audio/AudioSystem.cs b/Robust.Client/Audio/AudioSystem.cs
index 3492e5b96..acf412008 100644
--- a/Robust.Client/Audio/AudioSystem.cs
+++ b/Robust.Client/Audio/AudioSystem.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
+using JetBrains.Annotations;
using OpenTK.Audio.OpenAL;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
@@ -45,6 +46,26 @@ public sealed partial class AudioSystem : SharedAudioSystem
[Dependency] private readonly SharedTransformSystem _xformSys = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
+ ///
+ /// An optional method that, if provided, will override the behavior of .
+ /// Contains the same parameters in the same order as the method it overrides.
+ ///
+ ///
+ /// This event only supports a single invocation target.
+ ///
+ [PublicAPI]
+ public event Action? ProcessStreamOverride;
+
+ ///
+ /// An optional method that, if provided, will override the behavior of .
+ /// Contains the same parameters in the same order as the method it overrides.
+ ///
+ ///
+ /// This event only supports a single invocation target.
+ ///
+ [PublicAPI]
+ public event Func? GetOcclusionOverride;
+
///
/// Per-tick cache of relevant streams.
///
@@ -341,6 +362,15 @@ public sealed partial class AudioSystem : SharedAudioSystem
private void ProcessStream(EntityUid entity, AudioComponent component, TransformComponent xform, MapCoordinates listener)
{
+ // If content wants to process the stream in their own special way, we simply let them handle that.
+ if (ProcessStreamOverride is not null)
+ {
+ // Assert that we are not processing audio multiple times.
+ DebugTools.Assert(ProcessStreamOverride.HasSingleTarget, $"Event {nameof(ProcessStreamOverride)} has multiple invocation targets. This is not permitted.");
+ ProcessStreamOverride.Invoke(entity, component, xform, listener);
+ return; // Exit and do not perform remaining function behavior
+ }
+
// TODO:
// I Originally tried to be fancier here but it caused audio issues so just trying
// to replicate the old behaviour for now.
@@ -434,6 +464,14 @@ public sealed partial class AudioSystem : SharedAudioSystem
///
public float GetOcclusion(MapCoordinates listener, Vector2 delta, float distance, EntityUid? ignoredEnt = null)
{
+ // If content has defined a custom occlusion method, use that instead.
+ if (GetOcclusionOverride is not null)
+ {
+ // There can only be one occlusion override defined.
+ DebugTools.Assert(GetOcclusionOverride.HasSingleTarget, $"Event {nameof(GetOcclusionOverride)} has multiple invocation targets. This is not permitted.");
+ return GetOcclusionOverride.Invoke(listener, delta, distance, ignoredEnt);
+ }
+
float occlusion = 0;
if (distance > 0.1)