Decompiled source of ProjectEcho v1.0.6

plugins/ProjectEcho/ProjectEcho.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using ProjectEcho.Analysis;
using ProjectEcho.Core;
using ProjectEcho.Filters;
using ProjectEcho.Hooks;
using ProjectEcho.Profiles;
using ProjectEcho.Propagation;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ProjectEcho")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+fb9301e81d0f6b5e95330dbec2cbc1d773867bb6")]
[assembly: AssemblyProduct("ProjectEcho")]
[assembly: AssemblyTitle("ProjectEcho")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace ProjectEcho.Propagation
{
	public struct PropagationPath
	{
		public bool IsDirectPath;

		public float Attenuation;

		public float DiffractionAmount;

		public PropagationPortal? PortalUsed;

		public Vector3 VirtualSourcePos;
	}
	public class PropagationEngine : MonoBehaviour
	{
		private readonly List<PropagationPortal> _portals = new List<PropagationPortal>();

		private const float MAX_PORTAL_SEARCH_DIST = 30f;

		private static event Action<PropagationPortal>? OnPortalRegistered;

		private static event Action<PropagationPortal>? OnPortalUnregistered;

		public static void RaisePortalRegistered(PropagationPortal p)
		{
			PropagationEngine.OnPortalRegistered?.Invoke(p);
		}

		public static void RaisePortalUnregistered(PropagationPortal p)
		{
			PropagationEngine.OnPortalUnregistered?.Invoke(p);
		}

		private void Awake()
		{
			OnPortalRegistered += RegisterPortal;
			OnPortalUnregistered += UnregisterPortal;
		}

		private void OnDestroy()
		{
			OnPortalRegistered -= RegisterPortal;
			OnPortalUnregistered -= UnregisterPortal;
		}

		public PropagationPath GetPath(Vector3 sourcePos, Vector3 listenerPos)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			PropagationPath result;
			if (!QualityConfig.EnablePropagation.Value || QualityConfig.Tier.Value == QualityTier.Low)
			{
				result = default(PropagationPath);
				result.IsDirectPath = true;
				result.Attenuation = 1f;
				return result;
			}
			SpatialAudioManager instance = SpatialAudioManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				result = default(PropagationPath);
				result.IsDirectPath = true;
				result.Attenuation = 1f;
				return result;
			}
			if (HasDirectLOS(sourcePos, listenerPos, instance.WallLayer))
			{
				result = default(PropagationPath);
				result.IsDirectPath = true;
				result.Attenuation = 1f;
				return result;
			}
			PropagationPortal propagationPortal = null;
			float num = float.MaxValue;
			for (int num2 = _portals.Count - 1; num2 >= 0; num2--)
			{
				if ((Object)(object)_portals[num2] == (Object)null)
				{
					_portals.RemoveAt(num2);
				}
				else
				{
					PropagationPortal propagationPortal2 = _portals[num2];
					if (propagationPortal2.IsOpen)
					{
						float num3 = Vector3.Distance(sourcePos, propagationPortal2.Center);
						if (!(num3 > 30f))
						{
							float num4 = Vector3.Distance(propagationPortal2.Center, listenerPos);
							float num5 = num3 + num4;
							if (num5 < num && HasDirectLOS(sourcePos, propagationPortal2.Center, instance.WallLayer) && HasDirectLOS(propagationPortal2.Center, listenerPos, instance.WallLayer))
							{
								propagationPortal = propagationPortal2;
								num = num5;
							}
						}
					}
				}
			}
			if ((Object)(object)propagationPortal == (Object)null)
			{
				result = default(PropagationPath);
				result.IsDirectPath = false;
				result.Attenuation = 0.03f;
				result.DiffractionAmount = 0f;
				return result;
			}
			float num6 = Vector3.Distance(sourcePos, listenerPos);
			float num7 = Mathf.Max(num - num6, 0f);
			float num8 = Mathf.Clamp(-6f * num7 / 5f, -30f, 0f);
			float num9 = Mathf.Pow(10f, num8 / 20f) * Mathf.Clamp01(propagationPortal.OpenRatio);
			result = default(PropagationPath);
			result.IsDirectPath = false;
			result.PortalUsed = propagationPortal;
			result.Attenuation = Mathf.Clamp01(num9);
			result.DiffractionAmount = Mathf.Abs(num8) / 30f;
			result.VirtualSourcePos = propagationPortal.Center;
			return result;
		}

		private bool HasDirectLOS(Vector3 a, Vector3 b, LayerMask wall)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = b - a;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude < 0.01f)
			{
				return true;
			}
			return !Physics.Raycast(a, val / magnitude, magnitude - 0.05f, LayerMask.op_Implicit(wall));
		}

		private void RegisterPortal(PropagationPortal p)
		{
			if (!_portals.Contains(p))
			{
				_portals.Add(p);
			}
		}

		private void UnregisterPortal(PropagationPortal p)
		{
			_portals.Remove(p);
		}
	}
	public class PropagationPortal : MonoBehaviour
	{
		[Header("포탈 설정")]
		[Tooltip("문이 열려 있는지 여부")]
		public bool IsOpen = true;

		[Tooltip("열림 비율 0=닫힘 1=완전 열림")]
		[Range(0f, 1f)]
		public float OpenRatio = 1f;

		public Vector3 Center => ((Component)this).transform.position;

		private void OnEnable()
		{
			PropagationEngine.RaisePortalRegistered(this);
		}

		private void OnDisable()
		{
			PropagationEngine.RaisePortalUnregistered(this);
		}

		public void SetOpen(bool open, float ratio = 1f)
		{
			IsOpen = open;
			OpenRatio = (open ? ratio : 0f);
		}
	}
}
namespace ProjectEcho.Profiles
{
	public readonly struct ReverbParams
	{
		public readonly float DryLevel;

		public readonly float Room;

		public readonly float RoomHF;

		public readonly float DecayTime;

		public readonly float DecayHFRatio;

		public readonly float ReflectionsLevel;

		public readonly float ReflectionsDelay;

		public readonly float ReverbLevel;

		public readonly float ReverbDelay;

		public readonly float Diffusion;

		public readonly float Density;

		public readonly float LowPassCutoff;

		public readonly float LowPassResonance;

		public ReverbParams(float dryLevel, float room, float roomHF, float decayTime, float decayHFRatio, float reflLevel, float reflDelay, float reverbLevel, float reverbDelay, float diffusion, float density, float lpfCutoff, float lpfRes)
		{
			DryLevel = dryLevel;
			Room = room;
			RoomHF = roomHF;
			DecayTime = decayTime;
			DecayHFRatio = decayHFRatio;
			ReflectionsLevel = reflLevel;
			ReflectionsDelay = reflDelay;
			ReverbLevel = reverbLevel;
			ReverbDelay = reverbDelay;
			Diffusion = diffusion;
			Density = density;
			LowPassCutoff = lpfCutoff;
			LowPassResonance = lpfRes;
		}

		public void ApplyTo(AudioReverbFilter reverb, AudioLowPassFilter lowPass)
		{
			if ((Object)(object)reverb != (Object)null)
			{
				reverb.reverbPreset = (AudioReverbPreset)27;
				reverb.dryLevel = DryLevel;
				reverb.room = Mathf.Clamp(Room, -10000f, 0f);
				reverb.roomHF = Mathf.Clamp(RoomHF, -10000f, 0f);
				reverb.decayTime = Mathf.Clamp(DecayTime, 0.1f, 20f);
				reverb.decayHFRatio = Mathf.Clamp(DecayHFRatio, 0.1f, 2f);
				reverb.reflectionsLevel = Mathf.Clamp(ReflectionsLevel, -10000f, 1000f);
				reverb.reflectionsDelay = Mathf.Clamp(ReflectionsDelay, 0f, 0.3f);
				reverb.reverbLevel = Mathf.Clamp(ReverbLevel, -10000f, 2000f);
				reverb.reverbDelay = Mathf.Clamp(ReverbDelay, 0f, 0.1f);
				reverb.diffusion = Mathf.Clamp01(Diffusion);
				reverb.density = Mathf.Clamp01(Density);
			}
			if ((Object)(object)lowPass != (Object)null)
			{
				lowPass.cutoffFrequency = Mathf.Clamp(LowPassCutoff, 10f, 22000f);
				lowPass.lowpassResonanceQ = Mathf.Max(LowPassResonance, 0.1f);
			}
		}

		public ReverbParams WithDecayTime(float rt60)
		{
			return new ReverbParams(DryLevel, Room, RoomHF, Mathf.Clamp(rt60, 0.1f, 20f), DecayHFRatio, ReflectionsLevel, ReflectionsDelay, ReverbLevel, ReverbDelay, Diffusion, Density, LowPassCutoff, LowPassResonance);
		}
	}
	public readonly struct OcclusionParams
	{
		public readonly float LowPassCutoff;

		public readonly float LowPassResonance;

		public readonly float VolumeMultiplier;

		public OcclusionParams(float cutoff, float res, float volMult)
		{
			LowPassCutoff = cutoff;
			LowPassResonance = res;
			VolumeMultiplier = volMult;
		}

		public void ApplyTo(AudioLowPassFilter lowPass, AudioSource src)
		{
			if ((Object)(object)lowPass != (Object)null)
			{
				lowPass.cutoffFrequency = Mathf.Clamp(LowPassCutoff, 10f, 22000f);
				lowPass.lowpassResonanceQ = LowPassResonance;
			}
			if ((Object)(object)src != (Object)null)
			{
				src.volume = Mathf.Clamp01(src.volume * VolumeMultiplier);
			}
		}
	}
	public static class AudioParameterPresets
	{
		public static readonly ReverbParams SmallMetalRoom = new ReverbParams(0f, -300f, -100f, 0.45f, 1.8f, -200f, 0.007f, -500f, 0.011f, 0.7f, 0.75f, 18000f, 1f);

		public static readonly ReverbParams NarrowCorridor = new ReverbParams(0f, -200f, -300f, 0.75f, 0.8f, -200f, 0.012f, -400f, 0.022f, 0.55f, 0.85f, 17000f, 1f);

		public static readonly ReverbParams MediumWarehouse = new ReverbParams(0f, -100f, -600f, 1.8f, 0.5f, -400f, 0.02f, -300f, 0.03f, 0.8f, 1f, 16000f, 1f);

		public static readonly ReverbParams LargeCavern = new ReverbParams(0f, 0f, -1500f, 5f, 0.15f, -600f, 0.06f, -200f, 0.09f, 0.4f, 0.6f, 12000f, 1f);

		public static readonly ReverbParams Outdoor = new ReverbParams(0f, -10000f, 0f, 0.1f, 1f, -10000f, 0f, -10000f, 0f, 1f, 1f, 22000f, 1f);

		public static readonly ReverbParams ShipInterior = new ReverbParams(0f, -1200f, -400f, 0.45f, 1.5f, -700f, 0.009f, -1100f, 0.015f, 0.75f, 0.8f, 17500f, 1f);

		public static readonly OcclusionParams FullOcclusion = new OcclusionParams(400f, 0.8f, 0.2f);

		public static readonly OcclusionParams PartialOcclusion = new OcclusionParams(2500f, 1f, 0.55f);

		public static readonly OcclusionParams LightOcclusion = new OcclusionParams(7000f, 1f, 0.8f);

		public static ReverbParams GetPresetByVolume(float volumeM3)
		{
			if (volumeM3 < 30f)
			{
				return SmallMetalRoom;
			}
			if (volumeM3 < 80f)
			{
				return NarrowCorridor;
			}
			if (volumeM3 < 500f)
			{
				return MediumWarehouse;
			}
			if (volumeM3 < 10000f)
			{
				return LargeCavern;
			}
			return Outdoor;
		}
	}
	public enum QualityTier
	{
		Low,
		Medium,
		High
	}
	public static class QualityConfig
	{
		private struct TierSettings
		{
			public int RayCount;

			public float ScanInterval;

			public int MaxActiveFilters;

			public float OcclusionRayDist;

			public bool UseJobSystem;

			public float FrameBudgetMs;
		}

		public static ConfigEntry<QualityTier> Tier = null;

		public static ConfigEntry<bool> EnableHRTF = null;

		public static ConfigEntry<bool> EnablePropagation = null;

		public static ConfigEntry<bool> EnableOcclusion = null;

		public static ConfigEntry<float> MasterVolume = null;

		public static ConfigEntry<float> ReverbIntensity = null;

		public static ConfigEntry<float> OcclusionStrength = null;

		public static ConfigEntry<float> RoomTransitionSpeed = null;

		public static ConfigEntry<bool> DebugMode = null;

		private static readonly TierSettings[] _settings = new TierSettings[3]
		{
			new TierSettings
			{
				RayCount = 6,
				ScanInterval = 1f,
				MaxActiveFilters = 8,
				OcclusionRayDist = 20f,
				UseJobSystem = false,
				FrameBudgetMs = 0.3f
			},
			new TierSettings
			{
				RayCount = 10,
				ScanInterval = 0.5f,
				MaxActiveFilters = 16,
				OcclusionRayDist = 40f,
				UseJobSystem = false,
				FrameBudgetMs = 0.5f
			},
			new TierSettings
			{
				RayCount = 14,
				ScanInterval = 0.25f,
				MaxActiveFilters = 32,
				OcclusionRayDist = 50f,
				UseJobSystem = true,
				FrameBudgetMs = 1f
			}
		};

		public static int RayCount => _settings[(int)Tier.Value].RayCount;

		public static float ScanInterval => _settings[(int)Tier.Value].ScanInterval;

		public static float LerpSpeed => RoomTransitionSpeed.Value;

		public static int MaxActiveFilters => _settings[(int)Tier.Value].MaxActiveFilters;

		public static float OcclusionRayDist => _settings[(int)Tier.Value].OcclusionRayDist;

		public static bool UseJobSystem => _settings[(int)Tier.Value].UseJobSystem;

		public static float FrameBudgetMs => _settings[(int)Tier.Value].FrameBudgetMs;

		public static void Bind(ConfigFile cfg)
		{
			Tier = cfg.Bind<QualityTier>("Quality", "QualityTier", QualityTier.Medium, "Low / Medium / High — Low is recommended for older hardware");
			EnableHRTF = cfg.Bind<bool>("Features", "EnableHRTF", true, "Enable 3D HRTF spatialization (disable for performance)");
			EnablePropagation = cfg.Bind<bool>("Features", "EnablePropagation", true, "Enable door/corridor diffraction");
			EnableOcclusion = cfg.Bind<bool>("Features", "EnableOcclusion", true, "Enable wall occlusion low-pass filter");
			MasterVolume = cfg.Bind<float>("Audio", "MasterVolume", 1f, "Overall volume multiplier (0.0 - 2.0)");
			ReverbIntensity = cfg.Bind<float>("Audio", "ReverbIntensity", 1f, "Reverb strength multiplier. 1.0 = default, 2.0 = stronger, 0.5 = subtler");
			OcclusionStrength = cfg.Bind<float>("Audio", "OcclusionStrength", 1f, "Occlusion LPF strength. 1.0 = default, 0.0 = disabled");
			RoomTransitionSpeed = cfg.Bind<float>("Audio", "RoomTransitionSpeed", 3.5f, "How fast audio blends when moving between rooms. Higher = faster (1.0 - 10.0)");
			DebugMode = cfg.Bind<bool>("Debug", "DebugMode", false, "Enable verbose logging (RT60, floor material) to BepInEx console");
			if (SystemInfo.systemMemorySize < 4096 && Tier.Value == QualityTier.Medium)
			{
				Tier.Value = QualityTier.Low;
				ProjectEchoPlugin.Log.LogWarning((object)"[Project Echo] Low RAM detected — quality tier set to Low automatically");
			}
		}
	}
}
namespace ProjectEcho.Hooks
{
	internal static class SpatialHookHelper
	{
		private static readonly Dictionary<int, float> _cooldown = new Dictionary<int, float>(32);

		private const float COOLDOWN_SEC = 0.08f;

		private const float CLEANUP_INTERVAL = 30f;

		private static float _lastCleanup = 0f;

		private static readonly HashSet<int> EXCLUDED_LAYERS = new HashSet<int> { 5, 6 };

		private static readonly string[] EXCLUDED_KEYWORDS = new string[6] { "bgm", "music", "menu", "ambient2d", "ui_sfx", "stinger" };

		public static void Process(AudioSource src)
		{
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)src == (Object)null || (Object)(object)((Component)src).gameObject == (Object)null)
			{
				return;
			}
			SpatialAudioManager instance = SpatialAudioManager.Instance;
			if ((Object)(object)instance == (Object)null || EXCLUDED_LAYERS.Contains(((Component)src).gameObject.layer))
			{
				return;
			}
			string text = ((Object)((Component)src).gameObject).name.ToLowerInvariant();
			string[] eXCLUDED_KEYWORDS = EXCLUDED_KEYWORDS;
			foreach (string value in eXCLUDED_KEYWORDS)
			{
				if (text.Contains(value))
				{
					return;
				}
			}
			if (src.spatialBlend < 0.01f)
			{
				src.spatialBlend = 1f;
			}
			int instanceID = ((Object)src).GetInstanceID();
			float time = Time.time;
			if (_cooldown.TryGetValue(instanceID, out var value2) && time - value2 < 0.08f)
			{
				return;
			}
			_cooldown[instanceID] = time;
			if (time - _lastCleanup > 30f)
			{
				_lastCleanup = time;
				CleanupCooldown(time);
			}
			FilterPool component = ((Component)instance).GetComponent<FilterPool>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			AudioFilterController orCreate = component.GetOrCreate(((Component)src).gameObject);
			if ((Object)(object)orCreate == (Object)null)
			{
				return;
			}
			RoomProfile currentRoom = instance.CurrentRoom;
			if (!currentRoom.IsValid)
			{
				return;
			}
			SoundCategory soundCategory = AudioSourceClassifier.Classify(src);
			orCreate.SetCategory(soundCategory);
			bool occluded = QualityConfig.EnableOcclusion.Value && OcclusionProcessor.CheckOcclusion(((Component)src).transform.position, instance.PlayerPosition);
			orCreate.ApplyRoomReverb(currentRoom);
			orCreate.ApplyOcclusion(occluded, ((Component)src).transform.position);
			if (soundCategory == SoundCategory.Walkie)
			{
				orCreate.ApplyWalkieBandLimit();
			}
			if (QualityConfig.EnableHRTF.Value)
			{
				orCreate.ApplyHRTF(((Component)src).transform.position);
			}
			if (!QualityConfig.EnablePropagation.Value)
			{
				return;
			}
			PropagationEngine component2 = ((Component)instance).GetComponent<PropagationEngine>();
			if (!((Object)(object)component2 != (Object)null))
			{
				return;
			}
			PropagationPath path = component2.GetPath(((Component)src).transform.position, instance.PlayerPosition);
			if (!path.IsDirectPath && path.Attenuation < 0.99f)
			{
				if (QualityConfig.MasterVolume.Value < 0.99f)
				{
					src.volume = Mathf.Clamp01(src.volume * QualityConfig.MasterVolume.Value);
				}
				if (path.DiffractionAmount > 0.1f)
				{
					orCreate.ApplyOcclusion(occluded: true, ((Component)src).transform.position);
				}
			}
		}

		private static void CleanupCooldown(float now)
		{
			List<int> list = new List<int>(8);
			foreach (KeyValuePair<int, float> item in _cooldown)
			{
				if (now - item.Value > 60f)
				{
					list.Add(item.Key);
				}
			}
			foreach (int item2 in list)
			{
				_cooldown.Remove(item2);
			}
		}

		public static void ClearCache()
		{
			_cooldown.Clear();
			AudioSourceClassifier.ClearCache();
		}
	}
	[HarmonyPatch(typeof(AudioSource), "Play", new Type[] { })]
	internal static class Patch_Play
	{
		[HarmonyPrefix]
		private static void Prefix(AudioSource __instance)
		{
			SpatialHookHelper.Process(__instance);
		}
	}
	[HarmonyPatch(typeof(AudioSource), "Play", new Type[] { typeof(ulong) })]
	internal static class Patch_Play_Delay
	{
		[HarmonyPrefix]
		private static void Prefix(AudioSource __instance)
		{
			SpatialHookHelper.Process(__instance);
		}
	}
	[HarmonyPatch(typeof(AudioSource), "PlayOneShot", new Type[] { typeof(AudioClip) })]
	internal static class Patch_PlayOneShot_Clip
	{
		[HarmonyPrefix]
		private static void Prefix(AudioSource __instance, AudioClip clip)
		{
			if ((Object)(object)clip != (Object)null)
			{
				SpatialHookHelper.Process(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(AudioSource), "PlayOneShot", new Type[]
	{
		typeof(AudioClip),
		typeof(float)
	})]
	internal static class Patch_PlayOneShot_ClipVol
	{
		[HarmonyPrefix]
		private static void Prefix(AudioSource __instance, AudioClip clip)
		{
			if ((Object)(object)clip != (Object)null)
			{
				SpatialHookHelper.Process(__instance);
			}
		}
	}
	public static class EntranceTriggerHook
	{
		[HarmonyPatch]
		internal static class Patch_EntranceTeleport
		{
			[HarmonyTargetMethod]
			private static MethodBase? TargetMethod()
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					Type type = assembly.GetType("EntranceTeleport");
					if (!(type == null))
					{
						MethodInfo method = type.GetMethod("TeleportPlayer", BindingFlags.Instance | BindingFlags.Public);
						if (method != null)
						{
							return method;
						}
					}
				}
				return null;
			}

			[HarmonyPostfix]
			private static void Postfix(object __instance)
			{
				try
				{
					Type type = __instance.GetType();
					FieldInfo fieldInfo = null;
					string[] array = new string[3] { "isEntranceToBuilding", "isEntrance", "entranceToBuilding" };
					string[] array2 = array;
					foreach (string name in array2)
					{
						fieldInfo = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (fieldInfo != null)
						{
							break;
						}
					}
					if (fieldInfo == null)
					{
						ProjectEchoPlugin.Log.LogWarning((object)"[EntranceTriggerHook] Could not find 'isEntranceToBuilding' field. LC may have been updated. Entrance detection disabled.");
					}
					else if ((bool)(fieldInfo.GetValue(__instance) ?? ((object)false)))
					{
						EntranceTriggerHook.OnPlayerEnteredFacility?.Invoke();
					}
					else
					{
						EntranceTriggerHook.OnPlayerExitedFacility?.Invoke();
					}
				}
				catch (Exception ex)
				{
					ProjectEchoPlugin.Log.LogWarning((object)("[EntranceTriggerHook] Exception: " + ex.Message));
				}
			}
		}

		public static event Action? OnPlayerEnteredFacility;

		public static event Action? OnPlayerExitedFacility;
	}
	public static class SceneHook
	{
		public static void Register()
		{
			SceneManager.sceneLoaded += OnSceneLoaded;
			SceneManager.sceneUnloaded += OnSceneUnloaded;
			EntranceTriggerHook.OnPlayerEnteredFacility += OnEntranceTransition;
			EntranceTriggerHook.OnPlayerExitedFacility += OnExitTransition;
		}

		public static void Unregister()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			SceneManager.sceneUnloaded -= OnSceneUnloaded;
			EntranceTriggerHook.OnPlayerEnteredFacility -= OnEntranceTransition;
			EntranceTriggerHook.OnPlayerExitedFacility -= OnExitTransition;
		}

		private static void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			ProjectEchoPlugin.Log.LogInfo((object)("[SceneHook] Scene loaded: " + ((Scene)(ref scene)).name));
			if (!SpatialAudioManager.IsAlive())
			{
				ProjectEchoPlugin.Log.LogWarning((object)"[SceneHook] SpatialAudioManager missing — recreating");
				GameObject val = new GameObject("[ProjectEcho]");
				val.AddComponent<SpatialAudioManager>();
				val.AddComponent<RoomAnalyzer>();
				val.AddComponent<PropagationEngine>();
				val.AddComponent<FilterPool>();
				Object.DontDestroyOnLoad((Object)(object)val);
			}
		}

		private static void OnSceneUnloaded(Scene scene)
		{
			ProjectEchoPlugin.Log.LogInfo((object)("[SceneHook] Scene unloaded: " + ((Scene)(ref scene)).name));
			OcclusionProcessor.ClearCache();
		}

		private static void OnEntranceTransition()
		{
			ProjectEchoPlugin.Log.LogInfo((object)"[SceneHook] Entrance transition — forcing room scan");
			Object.FindObjectOfType<RoomAnalyzer>()?.ForceScan();
		}

		private static void OnExitTransition()
		{
			ProjectEchoPlugin.Log.LogInfo((object)"[SceneHook] Exit transition — forcing outdoor profile");
			SpatialAudioManager.Instance?.ForceOutdoorProfile();
			Object.FindObjectOfType<RoomAnalyzer>()?.ForceScan();
		}
	}
}
namespace ProjectEcho.Filters
{
	[RequireComponent(typeof(AudioSource))]
	public class AudioFilterController : MonoBehaviour
	{
		private const float LPF_MIN = 10f;

		private const float LPF_MAX = 22000f;

		private const float HPF_MIN = 10f;

		private const float CHANGE_THRESHOLD = 0.5f;

		private const float REVERB_THRESHOLD = 1f;

		private const float DECAY_THRESHOLD = 0.01f;

		private AudioSource? _source;

		private AudioReverbFilter? _reverb;

		private AudioLowPassFilter? _lowPass;

		private AudioHighPassFilter? _highPass;

		private float _targetLPF = 22000f;

		private float _targetHPF = 10f;

		private float _targetDecayTime = 0.1f;

		private float _targetDecayHF = 1f;

		private float _targetRoomGain = -10000f;

		private float _targetRoomHF;

		private float _targetReflLevel = -10000f;

		private float _targetDryLevel;

		private float _targetReverbLvl;

		private bool _initialized;

		private bool _isOccluded;

		private float _occlusionVolMult = 1f;

		private float _originalVolume = 1f;

		private bool _settled = true;

		private SoundCategory _category;

		private AudioSource? _reflectionSource;

		private AudioLowPassFilter? _reflectionLPF;

		private AudioHighPassFilter? _reflectionHPF;

		private GameObject? _reflectionGO;

		private float _reflectionTimer;

		private const float REFLECTION_INTERVAL = 0.3f;

		private void Awake()
		{
			_source = ((Component)this).GetComponent<AudioSource>();
			if (!((Object)(object)_source == (Object)null))
			{
				int layer = ((Component)this).gameObject.layer;
				if (layer == LayerMask.NameToLayer("UI") || layer == 5)
				{
					((Behaviour)this).enabled = false;
					return;
				}
				_reverb = ((Component)this).GetComponent<AudioReverbFilter>() ?? ((Component)this).gameObject.AddComponent<AudioReverbFilter>();
				_lowPass = ((Component)this).GetComponent<AudioLowPassFilter>() ?? ((Component)this).gameObject.AddComponent<AudioLowPassFilter>();
				_highPass = ((Component)this).GetComponent<AudioHighPassFilter>() ?? ((Component)this).gameObject.AddComponent<AudioHighPassFilter>();
				_reverb.reverbPreset = (AudioReverbPreset)27;
				_reverb.decayTime = 0.1f;
				_reverb.decayHFRatio = 1f;
				_reverb.room = -10000f;
				_reverb.roomHF = 0f;
				_reverb.reflectionsLevel = -10000f;
				_reverb.reverbLevel = 0f;
				_reverb.dryLevel = 0f;
				_reverb.diffusion = 1f;
				_reverb.density = 1f;
				_lowPass.cutoffFrequency = 22000f;
				_lowPass.lowpassResonanceQ = 1f;
				_highPass.cutoffFrequency = 10f;
				_highPass.highpassResonanceQ = 1f;
				_originalVolume = _source.volume;
				_initialized = true;
			}
		}

		public void SetCategory(SoundCategory cat)
		{
			_category = cat;
		}

		public void ApplyRoomReverb(RoomProfile room)
		{
			if (!_initialized || (Object)(object)_reverb == (Object)null || !room.IsValid)
			{
				return;
			}
			float num = Mathf.Clamp(QualityConfig.ReverbIntensity.Value, 0f, 3f);
			float categoryReverbMult = GetCategoryReverbMult();
			float categoryDecayMult = GetCategoryDecayMult();
			float num2 = Mathf.Lerp(room.RoomGain, 0f, Mathf.Clamp01(num - 1f));
			float num3 = Mathf.Lerp(-10000f, room.RoomGain, Mathf.Clamp01(num));
			float num4 = ((num >= 1f) ? num2 : num3) * categoryReverbMult;
			_targetDecayTime = Mathf.Clamp(room.RT60 * Mathf.Max(num, 0.1f) * categoryDecayMult, 0.1f, 20f);
			_targetRoomGain = Mathf.Clamp(num4, -10000f, 0f);
			_targetRoomHF = Mathf.Clamp(room.HighFreqDamping, -10000f, 0f);
			_targetDecayHF = Mathf.Clamp(room.DecayHFRatio, 0.1f, 2f);
			_targetReflLevel = ((_category == SoundCategory.Walkie) ? (-10000f) : Mathf.Clamp(room.ReflectionsLevel, -10000f, 1000f));
			float num5 = Mathf.InverseLerp(10f, 2000f, room.Volume);
			_targetDryLevel = ((_category == SoundCategory.Walkie) ? 0f : Mathf.Lerp(0f, -500f, num5 * categoryReverbMult));
			_targetReverbLvl = ((_category == SoundCategory.Walkie) ? (-10000f) : Mathf.Lerp(-3000f, 0f, num5 * categoryReverbMult));
			_reverb.reflectionsDelay = Mathf.Clamp(room.EarlyReflectionDelay, 0f, 0.3f);
			_reverb.reverbDelay = Mathf.Clamp(room.LateReverbDelay, 0f, 0.1f);
			_reverb.diffusion = Mathf.Clamp01(room.Diffusion);
			_reverb.density = Mathf.Clamp01(room.Density);
			if ((Object)(object)_highPass != (Object)null)
			{
				if (_category != SoundCategory.Footstep)
				{
					float materialHardness = room.MaterialHardness;
					_targetHPF = Mathf.Lerp(10f, 400f, materialHardness * materialHardness);
					_highPass.highpassResonanceQ = Mathf.Lerp(1f, 3.5f, materialHardness);
				}
				else
				{
					_targetHPF = 10f;
				}
			}
			_reflectionTimer = 0.3f;
			_settled = false;
		}

		public void ApplyOcclusion(bool occluded, Vector3 sourcePos)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (!_initialized || (Object)(object)_source == (Object)null)
			{
				return;
			}
			_isOccluded = occluded;
			float num = Vector3.Distance(sourcePos, SpatialAudioManager.Instance?.PlayerPosition ?? sourcePos);
			if (!QualityConfig.EnableOcclusion.Value)
			{
				ApplyAirAbsorption(num);
				_occlusionVolMult = 1f;
				_settled = false;
				return;
			}
			float num2 = Mathf.Clamp01(QualityConfig.OcclusionStrength.Value);
			if (occluded)
			{
				float num3 = ((_category == SoundCategory.Monster) ? 1.3f : 1f);
				float num4 = Mathf.Lerp(300f, 700f, Mathf.InverseLerp(30f, 2f, num)) / num3;
				_targetLPF = Mathf.Lerp(22000f, num4, num2);
				_targetHPF = 10f;
				_occlusionVolMult = Mathf.Lerp(1f, 0.2f / num3, num2);
			}
			else
			{
				ApplyAirAbsorption(num);
				_occlusionVolMult = 1f;
			}
			_settled = false;
		}

		private void ApplyAirAbsorption(float dist)
		{
			float num = _category switch
			{
				SoundCategory.Footstep => 1.5f, 
				SoundCategory.Monster => 1.2f, 
				SoundCategory.Machine => 0.6f, 
				SoundCategory.Ambient => 0.4f, 
				SoundCategory.Walkie => 0f, 
				_ => 1f, 
			};
			if (!(num <= 0f))
			{
				float num2 = Mathf.InverseLerp(2f, 35f, dist);
				_targetLPF = Mathf.Lerp(22000f, 3000f, num2 * num2 * num);
			}
		}

		public void ApplyHRTF(Vector3 srcPos)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if (!_initialized || (Object)(object)_source == (Object)null || !QualityConfig.EnableHRTF.Value || _category == SoundCategory.Walkie || _category == SoundCategory.Ambient)
			{
				return;
			}
			_source.spatialBlend = 1f;
			_source.spatialize = true;
			_source.spatializePostEffects = false;
			SpatialAudioManager instance = SpatialAudioManager.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				Vector3 val = srcPos - instance.PlayerPosition;
				float num = Mathf.Atan2(val.y, ((Vector3)(ref val)).magnitude) * 57.29578f;
				float num2 = Mathf.Lerp(-400f, 400f, Mathf.InverseLerp(-45f, 45f, num));
				if ((Object)(object)_reverb != (Object)null)
				{
					_reverb.roomHF = Mathf.Clamp(_targetRoomHF + num2, -10000f, 0f);
				}
				_settled = false;
			}
		}

		public void ApplyWalkieBandLimit()
		{
			if (_category == SoundCategory.Walkie)
			{
				_targetLPF = 3400f;
				_targetHPF = 300f;
				if ((Object)(object)_highPass != (Object)null)
				{
					_highPass.highpassResonanceQ = 1.5f;
				}
				_settled = false;
			}
		}

		private void UpdateReflectionSource(RoomProfile room)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Expected O, but got Unknown
			if ((Object)(object)_source == (Object)null || (Object)(object)_source.clip == (Object)null || !_source.isPlaying || QualityConfig.Tier.Value == QualityTier.Low || _category == SoundCategory.Walkie || _category == SoundCategory.Footstep)
			{
				return;
			}
			SpatialAudioManager instance = SpatialAudioManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			Vector3 position = ((Component)this).transform.position;
			Vector3 position2 = position;
			float num = float.MaxValue;
			string tag = "Default";
			Vector3[] array = (Vector3[])(object)new Vector3[4]
			{
				Vector3.right,
				Vector3.left,
				Vector3.forward,
				Vector3.back
			};
			Vector3[] array2 = array;
			RaycastHit val2 = default(RaycastHit);
			foreach (Vector3 val in array2)
			{
				if (Physics.Raycast(position, val, ref val2, 20f, LayerMask.op_Implicit(instance.WallLayer)) && ((RaycastHit)(ref val2)).distance < num)
				{
					num = ((RaycastHit)(ref val2)).distance;
					position2 = ((RaycastHit)(ref val2)).point;
					Collider collider = ((RaycastHit)(ref val2)).collider;
					object obj;
					if (collider == null)
					{
						obj = null;
					}
					else
					{
						PhysicMaterial sharedMaterial = collider.sharedMaterial;
						obj = ((sharedMaterial != null) ? ((Object)sharedMaterial).name : null);
					}
					if (obj == null)
					{
						Collider collider2 = ((RaycastHit)(ref val2)).collider;
						obj = ((collider2 != null) ? ((Component)collider2).gameObject.tag : null) ?? "Default";
					}
					tag = (string)obj;
				}
			}
			if (!(num > 19f) && !(num < 0.5f))
			{
				if ((Object)(object)_reflectionGO == (Object)null)
				{
					_reflectionGO = new GameObject($"[Echo_Refl_{((Object)this).GetInstanceID()}]");
					_reflectionSource = _reflectionGO.AddComponent<AudioSource>();
					_reflectionLPF = _reflectionGO.AddComponent<AudioLowPassFilter>();
					_reflectionHPF = _reflectionGO.AddComponent<AudioHighPassFilter>();
					_reflectionSource.spatialBlend = 1f;
					_reflectionSource.rolloffMode = (AudioRolloffMode)1;
					_reflectionSource.minDistance = 0.5f;
					_reflectionSource.maxDistance = 30f;
					_reflectionSource.loop = false;
					_reflectionSource.playOnAwake = false;
				}
				_reflectionGO.transform.position = position2;
				float hardnessFromTag = GetHardnessFromTag(tag);
				if ((Object)(object)_reflectionLPF != (Object)null)
				{
					_reflectionLPF.cutoffFrequency = Mathf.Lerp(3000f, 22000f, hardnessFromTag);
					_reflectionLPF.lowpassResonanceQ = Mathf.Lerp(1f, 2.5f, hardnessFromTag);
				}
				if ((Object)(object)_reflectionHPF != (Object)null)
				{
					_reflectionHPF.cutoffFrequency = Mathf.Lerp(10f, 200f, hardnessFromTag * 0.5f);
					_reflectionHPF.highpassResonanceQ = 1f;
				}
				if ((Object)(object)_reflectionSource != (Object)null && !_reflectionSource.isPlaying)
				{
					float volume = Mathf.Lerp(0.08f, 0.28f, hardnessFromTag * Mathf.InverseLerp(5f, 500f, room.Volume));
					_reflectionSource.clip = _source.clip;
					_reflectionSource.volume = volume;
					_reflectionSource.PlayDelayed(num / 343f);
				}
			}
		}

		private static float GetHardnessFromTag(string tag)
		{
			if (string.IsNullOrEmpty(tag))
			{
				return 0.5f;
			}
			string text = tag.ToLowerInvariant();
			if (text.Contains("metal") || text.Contains("steel"))
			{
				return 1f;
			}
			if (text.Contains("glass"))
			{
				return 0.95f;
			}
			if (text.Contains("concrete") || text.Contains("brick"))
			{
				return 0.8f;
			}
			if (text.Contains("wood"))
			{
				return 0.5f;
			}
			if (text.Contains("carpet"))
			{
				return 0.05f;
			}
			if (text.Contains("dirt") || text.Contains("grass"))
			{
				return 0.15f;
			}
			return 0.5f;
		}

		public void ResetToDefault()
		{
			if (!_initialized)
			{
				return;
			}
			_targetLPF = 22000f;
			_targetHPF = 10f;
			_targetDecayTime = 0.1f;
			_targetDecayHF = 1f;
			_targetRoomGain = -10000f;
			_targetRoomHF = 0f;
			_targetReflLevel = -10000f;
			_targetDryLevel = 0f;
			_targetReverbLvl = 0f;
			_occlusionVolMult = 1f;
			_settled = false;
			if ((Object)(object)_reverb != (Object)null)
			{
				_reverb.decayTime = 0.1f;
				_reverb.room = -10000f;
			}
			if ((Object)(object)_lowPass != (Object)null)
			{
				_lowPass.cutoffFrequency = 22000f;
			}
			if ((Object)(object)_highPass != (Object)null)
			{
				_highPass.cutoffFrequency = 10f;
			}
			if ((Object)(object)_source != (Object)null)
			{
				_source.volume = _originalVolume;
			}
			if ((Object)(object)_reflectionGO != (Object)null)
			{
				AudioSource? reflectionSource = _reflectionSource;
				if (reflectionSource != null)
				{
					reflectionSource.Stop();
				}
				Object.Destroy((Object)(object)_reflectionGO);
				_reflectionGO = null;
				_reflectionSource = null;
				_reflectionLPF = null;
				_reflectionHPF = null;
			}
		}

		private void Update()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Invalid comparison between Unknown and I4
			if (!_initialized)
			{
				return;
			}
			if ((Object)(object)_source != (Object)null && !_source.isPlaying)
			{
				_reflectionTimer = 0f;
			}
			else
			{
				if (_settled)
				{
					return;
				}
				float num = Time.deltaTime * QualityConfig.LerpSpeed;
				bool flag = false;
				if ((Object)(object)_reverb != (Object)null && (int)_reverb.reverbPreset != 27)
				{
					_reverb.reverbPreset = (AudioReverbPreset)27;
				}
				if ((Object)(object)_lowPass != (Object)null)
				{
					float num2 = Mathf.Lerp(_lowPass.cutoffFrequency, _targetLPF, num);
					if (Mathf.Abs(num2 - _lowPass.cutoffFrequency) > 0.5f)
					{
						_lowPass.cutoffFrequency = Mathf.Clamp(num2, 10f, 22000f);
						flag = true;
					}
				}
				if ((Object)(object)_highPass != (Object)null)
				{
					float num3 = Mathf.Lerp(_highPass.cutoffFrequency, _targetHPF, num);
					if (Mathf.Abs(num3 - _highPass.cutoffFrequency) > 0.5f)
					{
						_highPass.cutoffFrequency = Mathf.Clamp(num3, 10f, 22000f);
						flag = true;
					}
				}
				if ((Object)(object)_reverb != (Object)null)
				{
					float num4 = Mathf.Lerp(_reverb.decayTime, _targetDecayTime, num);
					if (Mathf.Abs(num4 - _reverb.decayTime) > 0.01f)
					{
						_reverb.decayTime = Mathf.Clamp(num4, 0.1f, 20f);
						flag = true;
					}
					float num5 = Mathf.Lerp(_reverb.decayHFRatio, _targetDecayHF, num);
					if (Mathf.Abs(num5 - _reverb.decayHFRatio) > 0.01f)
					{
						_reverb.decayHFRatio = Mathf.Clamp(num5, 0.1f, 2f);
						flag = true;
					}
					float num6 = Mathf.Lerp(_reverb.room, _targetRoomGain, num);
					if (Mathf.Abs(num6 - _reverb.room) > 1f)
					{
						_reverb.room = Mathf.Clamp(num6, -10000f, 0f);
						flag = true;
					}
					float num7 = Mathf.Lerp(_reverb.reflectionsLevel, _targetReflLevel, num);
					if (Mathf.Abs(num7 - _reverb.reflectionsLevel) > 1f)
					{
						_reverb.reflectionsLevel = Mathf.Clamp(num7, -10000f, 1000f);
						flag = true;
					}
					float num8 = Mathf.Lerp(_reverb.dryLevel, _targetDryLevel, num);
					if (Mathf.Abs(num8 - _reverb.dryLevel) > 1f)
					{
						_reverb.dryLevel = Mathf.Clamp(num8, -10000f, 0f);
						flag = true;
					}
					float num9 = Mathf.Lerp(_reverb.reverbLevel, _targetReverbLvl, num);
					if (Mathf.Abs(num9 - _reverb.reverbLevel) > 1f)
					{
						_reverb.reverbLevel = Mathf.Clamp(num9, -10000f, 2000f);
						flag = true;
					}
				}
				if ((Object)(object)_source != (Object)null)
				{
					float num10 = (_isOccluded ? (_originalVolume * _occlusionVolMult) : _originalVolume);
					float num11 = Mathf.Lerp(_source.volume, num10, num);
					if (Mathf.Abs(num11 - _source.volume) > 0.001f)
					{
						_source.volume = Mathf.Clamp01(num11);
						flag = true;
					}
				}
				if (!flag)
				{
					_settled = true;
				}
				if (!(_reflectionTimer > 0f))
				{
					return;
				}
				_reflectionTimer -= Time.deltaTime;
				if (_reflectionTimer <= 0f)
				{
					SpatialAudioManager instance = SpatialAudioManager.Instance;
					if ((Object)(object)instance != (Object)null)
					{
						UpdateReflectionSource(instance.CurrentRoom);
					}
				}
			}
		}

		private void OnDestroy()
		{
			_initialized = false;
			if ((Object)(object)_reflectionGO != (Object)null)
			{
				Object.Destroy((Object)(object)_reflectionGO);
			}
		}

		private float GetCategoryReverbMult()
		{
			return _category switch
			{
				SoundCategory.Machine => 1.4f, 
				SoundCategory.Ambient => 1.6f, 
				SoundCategory.Footstep => 0.8f, 
				SoundCategory.Walkie => 0f, 
				SoundCategory.Monster => 1.1f, 
				_ => 1f, 
			};
		}

		private float GetCategoryDecayMult()
		{
			return _category switch
			{
				SoundCategory.Machine => 1.3f, 
				SoundCategory.Ambient => 1.5f, 
				SoundCategory.Footstep => 0.7f, 
				SoundCategory.Walkie => 0f, 
				_ => 1f, 
			};
		}
	}
	public enum SoundCategory
	{
		General,
		Footstep,
		Monster,
		Machine,
		Walkie,
		Player,
		Ambient
	}
	public static class AudioSourceClassifier
	{
		private static readonly Dictionary<int, SoundCategory> _cache = new Dictionary<int, SoundCategory>(128);

		private static readonly (string[] keys, SoundCategory cat)[] NAME_RULES = new(string[], SoundCategory)[6]
		{
			(new string[6] { "footstep", "step", "walk", "run", "land", "feet" }, SoundCategory.Footstep),
			(new string[17]
			{
				"enemy", "monster", "creature", "beast", "spider", "crawler", "bracken", "thumper", "hoarding", "baboon",
				"eyeless", "ghost", "nutcracker", "coilhead", "snare", "masked", "jester"
			}, SoundCategory.Monster),
			(new string[12]
			{
				"machine", "engine", "fan", "vent", "pipe", "steam", "factory", "generator", "electricity", "buzz",
				"hum", "drone"
			}, SoundCategory.Machine),
			(new string[5] { "walkie", "radio", "transmit", "static", "comms" }, SoundCategory.Walkie),
			(new string[10] { "player", "breathe", "breath", "heartbeat", "hurt", "damage", "grab", "drop", "interact", "helmet" }, SoundCategory.Player),
			(new string[6] { "ambient", "wind", "rain", "atmo", "atmosphere", "background" }, SoundCategory.Ambient)
		};

		private static readonly (string typeName, SoundCategory cat)[] TYPE_RULES = new(string, SoundCategory)[5]
		{
			("EnemyAI", SoundCategory.Monster),
			("WalkieTalkie", SoundCategory.Walkie),
			("PlayerControllerB", SoundCategory.Player),
			("AnimatedObjectTrigger", SoundCategory.Machine),
			("SoundManager", SoundCategory.Ambient)
		};

		public static SoundCategory Classify(AudioSource src)
		{
			if ((Object)(object)src == (Object)null)
			{
				return SoundCategory.General;
			}
			int instanceID = ((Object)src).GetInstanceID();
			if (_cache.TryGetValue(instanceID, out var value))
			{
				return value;
			}
			SoundCategory soundCategory = ClassifyInternal(src);
			_cache[instanceID] = soundCategory;
			return soundCategory;
		}

		private static SoundCategory ClassifyInternal(AudioSource src)
		{
			string text = ((Object)((Component)src).gameObject).name.ToLowerInvariant();
			string text2 = (((Object)(object)src.clip != (Object)null) ? ((Object)src.clip).name.ToLowerInvariant() : "");
			(string[], SoundCategory)[] nAME_RULES = NAME_RULES;
			for (int i = 0; i < nAME_RULES.Length; i++)
			{
				(string[], SoundCategory) tuple = nAME_RULES[i];
				string[] item = tuple.Item1;
				SoundCategory item2 = tuple.Item2;
				string[] array = item;
				foreach (string value in array)
				{
					if (text.Contains(value) || text2.Contains(value))
					{
						return item2;
					}
				}
			}
			Transform parent = ((Component)src).transform.parent;
			for (int k = 0; k < 2; k++)
			{
				if (!((Object)(object)parent != (Object)null))
				{
					break;
				}
				string text3 = ((Object)parent).name.ToLowerInvariant();
				(string[], SoundCategory)[] nAME_RULES2 = NAME_RULES;
				for (int l = 0; l < nAME_RULES2.Length; l++)
				{
					(string[], SoundCategory) tuple2 = nAME_RULES2[l];
					string[] item3 = tuple2.Item1;
					SoundCategory item4 = tuple2.Item2;
					string[] array2 = item3;
					foreach (string value2 in array2)
					{
						if (text3.Contains(value2))
						{
							return item4;
						}
					}
				}
				parent = parent.parent;
			}
			GameObject gameObject = ((Component)((Component)src).transform.root).gameObject;
			MonoBehaviour[] componentsInChildren = gameObject.GetComponentsInChildren<MonoBehaviour>(true);
			MonoBehaviour[] array3 = componentsInChildren;
			foreach (MonoBehaviour val in array3)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				string name = ((object)val).GetType().Name;
				(string, SoundCategory)[] tYPE_RULES = TYPE_RULES;
				for (int num = 0; num < tYPE_RULES.Length; num++)
				{
					var (text4, result) = tYPE_RULES[num];
					if (name == text4)
					{
						return result;
					}
				}
			}
			return SoundCategory.General;
		}

		public static void ClearCache()
		{
			_cache.Clear();
		}
	}
	public class FilterPool : MonoBehaviour
	{
		private struct PoolEntry
		{
			public AudioFilterController? Ctrl;

			public float LastAccess;
		}

		[CompilerGenerated]
		private sealed class <PeriodicCleanup>d__8 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public FilterPool <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <PeriodicCleanup>d__8(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Expected O, but got Unknown
				int num = <>1__state;
				FilterPool filterPool = <>4__this;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
					filterPool._toRemove.Clear();
					foreach (KeyValuePair<int, PoolEntry> item in filterPool._pool)
					{
						if ((Object)(object)item.Value.Ctrl == (Object)null)
						{
							filterPool._toRemove.Add(item.Key);
						}
					}
					foreach (int item2 in filterPool._toRemove)
					{
						filterPool._pool.Remove(item2);
					}
					if (filterPool._toRemove.Count > 0)
					{
						ProjectEchoPlugin.Log.LogDebug((object)$"[FilterPool] {filterPool._toRemove.Count} stale entries removed");
					}
				}
				else
				{
					<>1__state = -1;
				}
				<>2__current = (object)new WaitForSeconds(30f);
				<>1__state = 1;
				return true;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private readonly Dictionary<int, PoolEntry> _pool = new Dictionary<int, PoolEntry>(32);

		private readonly List<int> _toRemove = new List<int>(8);

		private Coroutine? _cleanupCoroutine;

		private void Start()
		{
			_cleanupCoroutine = ((MonoBehaviour)this).StartCoroutine(PeriodicCleanup());
		}

		private void OnDestroy()
		{
			if (_cleanupCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_cleanupCoroutine);
			}
		}

		public AudioFilterController? GetOrCreate(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return null;
			}
			int instanceID = ((Object)go).GetInstanceID();
			float time = Time.time;
			if (_pool.TryGetValue(instanceID, out var value))
			{
				if ((Object)(object)value.Ctrl != (Object)null)
				{
					if (time - value.LastAccess > 0.5f)
					{
						_pool[instanceID] = new PoolEntry
						{
							Ctrl = value.Ctrl,
							LastAccess = time
						};
					}
					return value.Ctrl;
				}
				_pool.Remove(instanceID);
			}
			if (_pool.Count >= QualityConfig.MaxActiveFilters)
			{
				EvictLRU();
			}
			AudioFilterController audioFilterController = go.GetComponent<AudioFilterController>() ?? go.AddComponent<AudioFilterController>();
			_pool[instanceID] = new PoolEntry
			{
				Ctrl = audioFilterController,
				LastAccess = time
			};
			return audioFilterController;
		}

		public void ClearAll()
		{
			foreach (KeyValuePair<int, PoolEntry> item in _pool)
			{
				item.Value.Ctrl?.ResetToDefault();
			}
			_pool.Clear();
		}

		[IteratorStateMachine(typeof(<PeriodicCleanup>d__8))]
		private IEnumerator PeriodicCleanup()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <PeriodicCleanup>d__8(0)
			{
				<>4__this = this
			};
		}

		private void EvictLRU()
		{
			int num = -1;
			float num2 = float.MaxValue;
			foreach (KeyValuePair<int, PoolEntry> item in _pool)
			{
				if (item.Value.LastAccess < num2)
				{
					num2 = item.Value.LastAccess;
					num = item.Key;
				}
			}
			if (num != -1)
			{
				_pool[num].Ctrl?.ResetToDefault();
				_pool.Remove(num);
			}
		}
	}
	[RequireComponent(typeof(AudioSource))]
	public class HRTFController : MonoBehaviour
	{
		private const float AIR_ABSORPTION_COEFF = 0.0002f;

		private const float PROXIMITY_DIST = 1.5f;

		private const float PROXIMITY_DB_BOOST = 6f;

		private const float REAR_HF_ROLLOFF = -800f;

		private AudioSource? _source;

		private AudioReverbFilter? _reverb;

		private AudioLowPassFilter? _lowPass;

		private float _cachedElevation;

		private float _cachedAzimuth;

		private float _cachedDistance = 5f;

		private float _smoothedHFAdj;

		private float _smoothedLFAdj;

		private float _nextUpdateTime;

		private const float UPDATE_INTERVAL = 0.05f;

		private static Transform? _cachedListenerTransform;

		private void Awake()
		{
			_source = ((Component)this).GetComponent<AudioSource>();
			_reverb = ((Component)this).GetComponent<AudioReverbFilter>();
			_lowPass = ((Component)this).GetComponent<AudioLowPassFilter>();
		}

		private void OnEnable()
		{
			if (QualityConfig.Tier.Value == QualityTier.Low || !QualityConfig.EnableHRTF.Value)
			{
				((Behaviour)this).enabled = false;
			}
			else if ((Object)(object)_source != (Object)null)
			{
				_source.spatialBlend = 1f;
				_source.spatialize = true;
				_source.spatializePostEffects = false;
				_source.dopplerLevel = 0.3f;
				_source.rolloffMode = (AudioRolloffMode)2;
				_source.minDistance = 1f;
				_source.maxDistance = 50f;
			}
		}

		private void Update()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			if (!(Time.time < _nextUpdateTime))
			{
				_nextUpdateTime = Time.time + 0.05f;
				SpatialAudioManager instance = SpatialAudioManager.Instance;
				if (!((Object)(object)instance == (Object)null) && !((Object)(object)_source == (Object)null))
				{
					UpdateSpatialParams(instance.PlayerPosition);
				}
			}
		}

		private void UpdateSpatialParams(Vector3 listenerPos)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((Component)this).transform.position - listenerPos;
			_cachedDistance = ((Vector3)(ref val)).magnitude;
			if (!(_cachedDistance < 0.01f))
			{
				Vector3 val2 = val / _cachedDistance;
				Vector2 val3 = new Vector2(val2.x, val2.z);
				float magnitude = ((Vector2)(ref val3)).magnitude;
				_cachedElevation = Mathf.Atan2(val2.y, magnitude) * 57.29578f;
				float num = Mathf.Lerp(-600f, 600f, Mathf.InverseLerp(-45f, 45f, _cachedElevation));
				Vector3 listenerForward = GetListenerForward();
				float num2 = Mathf.InverseLerp(90f, 180f, Mathf.Abs(_cachedAzimuth = Vector3.SignedAngle(listenerForward, val2, Vector3.up)));
				float num3 = -800f * num2;
				float num4 = (0f - _cachedDistance) * 0.0002f * 10000f;
				num4 = Mathf.Clamp(num4, -2000f, 0f);
				float num5 = 0f;
				if (_cachedDistance < 1.5f)
				{
					float num6 = 1f - _cachedDistance / 1.5f;
					num5 = 6f * num6 * 100f;
				}
				float num7 = Mathf.Clamp(num + num3 + num4, -5000f, 600f);
				float num8 = num5;
				_smoothedHFAdj = Mathf.Lerp(_smoothedHFAdj, num7, Time.deltaTime * QualityConfig.LerpSpeed);
				_smoothedLFAdj = Mathf.Lerp(_smoothedLFAdj, num8, Time.deltaTime * QualityConfig.LerpSpeed);
				ApplyToFilters();
			}
		}

		private void ApplyToFilters()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			if ((Object)(object)_reverb != (Object)null && (int)_reverb.reverbPreset == 27)
			{
				float num = _reverb.roomHF - _smoothedHFAdj;
				_reverb.roomHF = Mathf.Clamp(num + _smoothedHFAdj, -10000f, 0f);
			}
			if ((Object)(object)_lowPass != (Object)null)
			{
				float num2 = Mathf.Lerp(22000f, 8000f, Mathf.InverseLerp(1f, 50f, _cachedDistance));
				float cutoffFrequency = _lowPass.cutoffFrequency;
				float num3 = Mathf.Min(cutoffFrequency, num2);
				_lowPass.cutoffFrequency = Mathf.Clamp(num3, 10f, 22000f);
			}
		}

		private static Vector3 GetListenerForward()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_cachedListenerTransform == (Object)null)
			{
				AudioListener val = Object.FindObjectOfType<AudioListener>();
				_cachedListenerTransform = (((Object)(object)val != (Object)null) ? ((Component)val).transform : null);
			}
			if (!((Object)(object)_cachedListenerTransform != (Object)null))
			{
				return Vector3.forward;
			}
			return _cachedListenerTransform.forward;
		}

		public HRTFDebugInfo GetDebugInfo()
		{
			HRTFDebugInfo result = default(HRTFDebugInfo);
			result.Elevation = _cachedElevation;
			result.Azimuth = _cachedAzimuth;
			result.Distance = _cachedDistance;
			result.HFAdjust = _smoothedHFAdj;
			result.LFAdjust = _smoothedLFAdj;
			return result;
		}
	}
	public struct HRTFDebugInfo
	{
		public float Elevation;

		public float Azimuth;

		public float Distance;

		public float HFAdjust;

		public float LFAdjust;
	}
	public static class OcclusionProcessor
	{
		private struct CacheEntry
		{
			public bool Result;

			public float Time;

			public int Frame;
		}

		private static readonly Dictionary<int, CacheEntry> _cache = new Dictionary<int, CacheEntry>(64);

		private static readonly Dictionary<int, bool> _frameCache = new Dictionary<int, bool>(32);

		private static int _lastFrameCacheFrame = -1;

		private const float CACHE_TTL = 0.15f;

		private const float SKIP_DIST_SQR = 4f;

		private static int _frameSkip = 0;

		public static bool CheckOcclusion(Vector3 sourcePos, Vector3 listenerPos)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			SpatialAudioManager instance = SpatialAudioManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			if (!QualityConfig.EnableOcclusion.Value)
			{
				return false;
			}
			Vector3 val = sourcePos - listenerPos;
			float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
			if (sqrMagnitude < 4f)
			{
				return false;
			}
			int hashCode = ((object)(Vector3)(ref sourcePos)).GetHashCode();
			int frameCount = Time.frameCount;
			if (_lastFrameCacheFrame != frameCount)
			{
				_frameCache.Clear();
				_lastFrameCacheFrame = frameCount;
			}
			if (_frameCache.TryGetValue(hashCode, out var value))
			{
				return value;
			}
			if (QualityConfig.Tier.Value == QualityTier.Low)
			{
				_frameSkip++;
				if (_frameSkip % 2 != 0)
				{
					return GetCached(sourcePos, listenerPos);
				}
			}
			int key = ((object)(Vector3)(ref sourcePos)).GetHashCode() ^ (((object)(Vector3)(ref listenerPos)).GetHashCode() << 2);
			float time = Time.time;
			if (_cache.TryGetValue(key, out var value2) && time - value2.Time < 0.15f)
			{
				_frameCache[hashCode] = value2.Result;
				return value2.Result;
			}
			float num = Mathf.Sqrt(sqrMagnitude);
			if (num < 0.01f)
			{
				return false;
			}
			Vector3 val2 = (listenerPos - sourcePos) / num;
			bool flag = Physics.Raycast(sourcePos, val2, num - 0.1f, LayerMask.op_Implicit(instance.WallLayer));
			_cache[key] = new CacheEntry
			{
				Result = flag,
				Time = time,
				Frame = frameCount
			};
			_frameCache[hashCode] = flag;
			return flag;
		}

		private static bool GetCached(Vector3 sourcePos, Vector3 listenerPos)
		{
			int key = ((object)(Vector3)(ref sourcePos)).GetHashCode() ^ (((object)(Vector3)(ref listenerPos)).GetHashCode() << 2);
			if (_cache.TryGetValue(key, out var value))
			{
				return value.Result;
			}
			return false;
		}

		public static void ClearCache()
		{
			_cache.Clear();
			_frameCache.Clear();
		}
	}
}
namespace ProjectEcho.Core
{
	public class PlayerTracker : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <FindPlayerLoop>d__18 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public PlayerTracker <>4__this;

			object? IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object? IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <FindPlayerLoop>d__18(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d0: Expected O, but got Unknown
				//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ab: Expected O, but got Unknown
				int num = <>1__state;
				PlayerTracker playerTracker = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					break;
				case 1:
					<>1__state = -1;
					break;
				case 2:
					<>1__state = -1;
					break;
				}
				if ((Object)(object)playerTracker._playerTransform == (Object)null)
				{
					if (playerTracker._findAttempts >= 20)
					{
						ProjectEchoPlugin.Log.LogWarning((object)"[PlayerTracker] Could not find player after max attempts. Search stopped.");
						return false;
					}
					playerTracker._findAttempts++;
					playerTracker._playerTransform = FindLocalPlayer();
					if ((Object)(object)playerTracker._playerTransform != (Object)null)
					{
						ProjectEchoPlugin.Log.LogInfo((object)("[PlayerTracker] Player found: " + ((Object)playerTracker._playerTransform).name));
					}
					<>2__current = (object)new WaitForSeconds(0.5f);
					<>1__state = 1;
					return true;
				}
				<>2__current = (object)new WaitForSeconds(2f);
				<>1__state = 2;
				return true;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <FindShipLoop>d__20 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public PlayerTracker <>4__this;

			object? IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object? IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <FindShipLoop>d__20(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_009c: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a6: Expected O, but got Unknown
				//IL_007a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0084: Expected O, but got Unknown
				int num = <>1__state;
				PlayerTracker playerTracker = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					break;
				case 1:
					<>1__state = -1;
					break;
				case 2:
					<>1__state = -1;
					break;
				}
				if ((Object)(object)playerTracker._shipCollider == (Object)null)
				{
					GameObject val = GameObject.Find("HangarShip");
					if ((Object)(object)val != (Object)null)
					{
						playerTracker._shipCollider = val.GetComponentInChildren<Collider>();
						if ((Object)(object)playerTracker._shipCollider != (Object)null)
						{
							ProjectEchoPlugin.Log.LogInfo((object)"[PlayerTracker] Ship collider found");
						}
					}
					<>2__current = (object)new WaitForSeconds(1f);
					<>1__state = 1;
					return true;
				}
				<>2__current = (object)new WaitForSeconds(5f);
				<>1__state = 2;
				return true;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private const string LC_PLAYER_TYPE = "GameNetcodeStuff.PlayerControllerB";

		private const string LC_SHIP_NAME = "HangarShip";

		private const int MAX_FIND_ATTEMPTS = 20;

		private Transform? _playerTransform;

		private Coroutine? _searchCoroutine;

		private Coroutine? _shipCoroutine;

		private int _findAttempts;

		private Collider? _shipCollider;

		public Vector3 Position { get; private set; }

		public bool IsInsideShip { get; private set; }

		private void Start()
		{
			_searchCoroutine = ((MonoBehaviour)this).StartCoroutine(FindPlayerLoop());
			_shipCoroutine = ((MonoBehaviour)this).StartCoroutine(FindShipLoop());
		}

		private void Update()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_playerTransform == (Object)null)
			{
				Position = Vector3.zero;
				return;
			}
			Position = _playerTransform.position;
			if ((Object)(object)_shipCollider != (Object)null)
			{
				Bounds bounds = _shipCollider.bounds;
				IsInsideShip = ((Bounds)(ref bounds)).Contains(Position);
			}
		}

		[IteratorStateMachine(typeof(<FindPlayerLoop>d__18))]
		private IEnumerator FindPlayerLoop()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <FindPlayerLoop>d__18(0)
			{
				<>4__this = this
			};
		}

		private static Transform? FindLocalPlayer()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			Assembly[] array = assemblies;
			foreach (Assembly assembly in array)
			{
				Type type = assembly.GetType("GameNetcodeStuff.PlayerControllerB");
				if (type == null)
				{
					continue;
				}
				Object[] array2 = Object.FindObjectsOfType(type);
				Object[] array3 = array2;
				foreach (Object val in array3)
				{
					MonoBehaviour val2 = (MonoBehaviour)(object)((val is MonoBehaviour) ? val : null);
					if (!((Object)(object)val2 == (Object)null))
					{
						PropertyInfo propertyInfo = type.GetProperty("IsOwner") ?? type.GetProperty("isPlayerControlled");
						if (!(propertyInfo != null))
						{
							return ((Component)val2).transform;
						}
						if ((bool)(propertyInfo.GetValue(val) ?? ((object)false)))
						{
							return ((Component)val2).transform;
						}
					}
				}
			}
			GameObject val3 = GameObject.FindWithTag("Player");
			if ((Object)(object)val3 != (Object)null)
			{
				return val3.transform;
			}
			if ((Object)(object)Camera.main != (Object)null)
			{
				return ((Component)Camera.main).transform;
			}
			return null;
		}

		[IteratorStateMachine(typeof(<FindShipLoop>d__20))]
		private IEnumerator FindShipLoop()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <FindShipLoop>d__20(0)
			{
				<>4__this = this
			};
		}

		private void OnDestroy()
		{
			if (_searchCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_searchCoroutine);
			}
			if (_shipCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_shipCoroutine);
			}
		}
	}
	[BepInPlugin("com.yourname.projectecho", "Project Echo", "1.0.6")]
	[BepInProcess("Lethal Company.exe")]
	public class ProjectEchoPlugin : BaseUnityPlugin
	{
		public const string GUID = "com.yourname.projectecho";

		public const string NAME = "Project Echo";

		public const string VERSION = "1.0.6";

		private Harmony? _harmony;

		private GameObject? _systemRoot;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"[Project Echo] v1.0.6 initializing...");
			QualityConfig.Bind(((BaseUnityPlugin)this).Config);
			Log.LogInfo((object)string.Format("[{0}] Quality tier: {1}", "Project Echo", QualityConfig.Tier.Value));
			if (!SpatialAudioManager.IsAlive())
			{
				_systemRoot = new GameObject("[ProjectEcho_Root]");
				_systemRoot.AddComponent<SpatialAudioManager>();
				_systemRoot.AddComponent<RoomAnalyzer>();
				_systemRoot.AddComponent<PropagationEngine>();
				_systemRoot.AddComponent<FilterPool>();
				_systemRoot.AddComponent<HRTFController>();
				Object.DontDestroyOnLoad((Object)(object)_systemRoot);
				Log.LogInfo((object)"[Project Echo] System root created");
			}
			_harmony = new Harmony("com.yourname.projectecho");
			_harmony.PatchAll(typeof(ProjectEchoPlugin).Assembly);
			SceneHook.Register();
			Log.LogInfo((object)"[Project Echo] Initialization complete");
		}

		private void OnDestroy()
		{
			SceneHook.Unregister();
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)_systemRoot != (Object)null)
			{
				Object.Destroy((Object)(object)_systemRoot);
			}
			Log.LogInfo((object)"[Project Echo] Patches unloaded");
		}
	}
	public class SpatialAudioManager : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <FastTransitionCoroutine>d__33 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public SpatialAudioManager <>4__this;

			private float <elapsed>5__2;

			object? IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object? IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <FastTransitionCoroutine>d__33(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				SpatialAudioManager spatialAudioManager = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<elapsed>5__2 = 0f;
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				if (<elapsed>5__2 < 0.5f)
				{
					spatialAudioManager._roomTransitionT = Mathf.MoveTowards(spatialAudioManager._roomTransitionT, 1f, Time.deltaTime * QualityConfig.LerpSpeed * 3f);
					<elapsed>5__2 += Time.deltaTime;
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static SpatialAudioManager? _instance;

		private Vector3 _lastScanPosition;

		private const float SCAN_MOVE_THRESHOLD = 0.5f;

		private RoomProfile _currentRoom = RoomProfile.Default;

		private RoomProfile _previousRoom = RoomProfile.Default;

		private float _roomTransitionT = 1f;

		private PlayerTracker? _playerTracker;

		private FilterPool? _filterPool;

		public static SpatialAudioManager? Instance
		{
			get
			{
				if ((Object)(object)_instance == (Object)null)
				{
					ManualLogSource log = ProjectEchoPlugin.Log;
					if (log != null)
					{
						log.LogWarning((object)"[SAM] Instance not yet initialized");
					}
				}
				return _instance;
			}
		}

		public Vector3 PlayerPosition { get; private set; }

		public bool IsPlayerInShip { get; private set; }

		public LayerMask WallLayer { get; private set; }

		public RoomProfile CurrentRoom
		{
			get
			{
				if (!(_roomTransitionT >= 1f))
				{
					return RoomProfile.Lerp(_previousRoom, _currentRoom, _roomTransitionT);
				}
				return _currentRoom;
			}
		}

		public event Action<RoomProfile>? OnRoomChanged;

		public static bool IsAlive()
		{
			return (Object)(object)_instance != (Object)null;
		}

		private void Awake()
		{
			if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			_instance = this;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			InitWallLayer();
			_playerTracker = ((Component)this).gameObject.AddComponent<PlayerTracker>();
			_filterPool = ((Component)this).gameObject.AddComponent<FilterPool>();
			SceneManager.sceneUnloaded += OnSceneUnloaded;
		}

		private void InitWallLayer()
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			string[] array = new string[6] { "Default", "Room", "Colliders", "NavigationSurface", "Terrain", "InteractableObject" };
			int num = 0;
			List<string> list = new List<string>();
			string[] array2 = array;
			foreach (string text in array2)
			{
				int num2 = LayerMask.NameToLayer(text);
				if (num2 != -1)
				{
					num |= 1 << num2;
					list.Add(text);
				}
			}
			if (num == 0)
			{
				WallLayer = LayerMask.op_Implicit(-1);
				ProjectEchoPlugin.Log.LogWarning((object)"[SAM] No matching layers found — using all layers as fallback");
			}
			else
			{
				WallLayer = LayerMask.op_Implicit(num);
				ProjectEchoPlugin.Log.LogInfo((object)("[SAM] Wall layers: [" + string.Join(", ", list) + "]"));
			}
		}

		private void Update()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_playerTracker != (Object)null)
			{
				PlayerPosition = _playerTracker.Position;
				IsPlayerInShip = _playerTracker.IsInsideShip;
			}
			if (_roomTransitionT < 1f)
			{
				_roomTransitionT = Mathf.MoveTowards(_roomTransitionT, 1f, Time.deltaTime * QualityConfig.LerpSpeed);
			}
		}

		public bool HasPlayerMovedSignificantly()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			bool flag = Vector3.SqrMagnitude(PlayerPosition - _lastScanPosition) > 0.25f;
			if (flag)
			{
				_lastScanPosition = PlayerPosition;
			}
			return flag;
		}

		public void ForceOutdoorProfile()
		{
			_previousRoom = CurrentRoom;
			_currentRoom = RoomProfile.Default;
			_roomTransitionT = 0f;
			((MonoBehaviour)this).StartCoroutine(FastTransitionCoroutine());
			this.OnRoomChanged?.Invoke(_currentRoom);
			ProjectEchoPlugin.Log.LogInfo((object)"[SAM] Forced outdoor profile");
		}

		[IteratorStateMachine(typeof(<FastTransitionCoroutine>d__33))]
		private IEnumerator FastTransitionCoroutine()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <FastTransitionCoroutine>d__33(0)
			{
				<>4__this = this
			};
		}

		public void UpdateRoom(RoomProfile newProfile)
		{
			if (newProfile.IsValid)
			{
				if (IsPlayerInShip)
				{
					ReverbParams shipInterior = AudioParameterPresets.ShipInterior;
					newProfile.RT60 = shipInterior.DecayTime;
					newProfile.RoomGain = shipInterior.Room;
					newProfile.HighFreqDamping = shipInterior.RoomHF;
					newProfile.Diffusion = shipInterior.Diffusion;
					newProfile.Density = shipInterior.Density;
				}
				_previousRoom = CurrentRoom;
				_currentRoom = newProfile;
				_roomTransitionT = 0f;
				this.OnRoomChanged?.Invoke(newProfile);
				if (QualityConfig.DebugMode.Value)
				{
					ProjectEchoPlugin.Log.LogInfo((object)($"[SAM] Room updated | V={newProfile.Volume:F0}m³ " + $"RT60={newProfile.RT60:F2}s " + $"α={newProfile.AverageSurfaceAlpha:F2} " + $"Ship={IsPlayerInShip}"));
				}
			}
		}

		private void OnSceneUnloaded(Scene scene)
		{
			_currentRoom = RoomProfile.Default;
			_previousRoom = RoomProfile.Default;
			_roomTransitionT = 1f;
			_filterPool?.ClearAll();
		}

		private void OnDestroy()
		{
			SceneManager.sceneUnloaded -= OnSceneUnloaded;
			((MonoBehaviour)this).StopAllCoroutines();
			if ((Object)(object)_instance == (Object)(object)this)
			{
				_instance = null;
			}
		}
	}
}
namespace ProjectEcho.Analysis
{
	public class RoomAnalyzer : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <>c__DisplayClass23_0
		{
			public RoomProfile primary;

			public RoomProfile secondary;

			public bool hasSecondary;

			internal void <ScanCoroutine>b__0(RoomProfile p)
			{
				primary = p;
			}

			internal void <ScanCoroutine>b__1(RoomProfile p)
			{
				secondary = p;
				hasSecondary = true;
			}
		}

		[CompilerGenerated]
		private sealed class <DoScanInto>d__24 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public int rayCount;

			public RoomAnalyzer <>4__this;

			public Vector3 origin;

			public LayerMask wallLayer;

			public Action<RoomProfile> onComplete;

			private float <budget>5__2;

			private float <totalAbsorp>5__3;

			private float <totalArea>5__4;

			private float <totalHFRatio>5__5;

			private float <totalHardness>5__6;

			private float <sumX>5__7;

			private float <sumY>5__8;

			private float <sumZ>5__9;

			private float <ceilHeight>5__10;

			private int <cntX>5__11;

			private int <cntY>5__12;

			private int <cntZ>5__13;

			private int <matSamples>5__14;

			private int <count>5__15;

			private int <i>5__16;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <DoScanInto>d__24(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
				//IL_0101: Unknown result type (might be due to invalid IL or missing references)
				//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
				//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
				//IL_02de: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
				//IL_011e: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
				//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
				//IL_0202: Unknown result type (might be due to invalid IL or missing references)
				//IL_0236: Unknown result type (might be due to invalid IL or missing references)
				int num = <>1__state;
				RoomAnalyzer roomAnalyzer = <>4__this;
				float realtimeSinceStartup;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
					realtimeSinceStartup = Time.realtimeSinceStartup;
					goto IL_0296;
				}
				<>1__state = -1;
				<budget>5__2 = QualityConfig.FrameBudgetMs * 0.001f;
				realtimeSinceStartup = Time.realtimeSinceStartup;
				<totalAbsorp>5__3 = 0f;
				<totalArea>5__4 = 0f;
				<totalHFRatio>5__5 = 0f;
				<totalHardness>5__6 = 0f;
				<sumX>5__7 = 0f;
				<sumY>5__8 = 0f;
				<sumZ>5__9 = 0f;
				<ceilHeight>5__10 = 3f;
				<cntX>5__11 = 0;
				<cntY>5__12 = 0;
				<cntZ>5__13 = 0;
				<matSamples>5__14 = 0;
				<count>5__15 = Mathf.Clamp(rayCount, 6, ALL_DIRECTIONS.Length);
				<i>5__16 = 0;
				goto IL_02a8;
				IL_0296:
				<i>5__16++;
				goto IL_02a8;
				IL_02a8:
				if (<i>5__16 < <count>5__15)
				{
					if (roomAnalyzer._isDestroyed)
					{
						return false;
					}
					Vector3 val = ALL_DIRECTIONS[<i>5__16];
					float num2 = QualityConfig.OcclusionRayDist;
					RaycastHit hit = default(RaycastHit);
					if (Physics.Raycast(origin, val, ref hit, num2, LayerMask.op_Implicit(wallLayer)))
					{
						num2 = ((RaycastHit)(ref hit)).distance;
						string materialTag = GetMaterialTag(hit);
						float num3 = num2 * num2 * 0.3f;
						<totalAbsorp>5__3 += num3 * GetAbsorption(materialTag);
						<totalArea>5__4 += num3;
						float num4 = 1f / Mathf.Max(num2, 0.5f);
						<totalHFRatio>5__5 += GetValue(DECAY_HF_RATIO, materialTag) * num4;
						<totalHardness>5__6 += GetValue(HARDNESS, materialTag) * num4;
						<matSamples>5__14++;
						if (val == Vector3.up)
						{
							<ceilHeight>5__10 = num2;
						}
					}
					if (Mathf.Abs(val.x) > 0.6f)
					{
						<sumX>5__7 += num2;
						<cntX>5__11++;
					}
					if (Mathf.Abs(val.y) > 0.6f)
					{
						<sumY>5__8 += num2;
						<cntY>5__12++;
					}
					if (Mathf.Abs(val.z) > 0.6f)
					{
						<sumZ>5__9 += num2;
						<cntZ>5__13++;
					}
					if (Time.realtimeSinceStartup - realtimeSinceStartup > <budget>5__2)
					{
						<>2__current = null;
						<>1__state = 1;
						return true;
					}
					goto IL_0296;
				}
				SpatialAudioManager instance = SpatialAudioManager.Instance;
				RaycastHit hit2 = default(RaycastHit);
				if ((Object)(object)instance != (Object)null && Physics.Raycast(origin, Vector3.down, ref hit2, 5f, LayerMask.op_Implicit(instance.WallLayer)))
				{
					string materialTag2 = GetMaterialTag(hit2);
					<totalAbsorp>5__3 += 40f * GetAbsorption(materialTag2);
					<totalArea>5__4 += 40f;
					float num5 = 2f / Mathf.Max(((RaycastHit)(ref hit2)).distance, 0.1f);
					<totalHFRatio>5__5 += GetValue(DECAY_HF_RATIO, materialTag2) * num5;
					<totalHardness>5__6 += GetValue(HARDNESS, materialTag2) * num5;
					<matSamples>5__14++;
				}
				float decayHFRatio = ((<matSamples>5__14 > 0) ? (<totalHFRatio>5__5 / (float)<matSamples>5__14) : 0.5f);
				float matHardness = ((<matSamples>5__14 > 0) ? (<totalHardness>5__6 / (float)<matSamples>5__14) : 0.5f);
				onComplete(BuildProfile(<sumX>5__7, <sumY>5__8, <sumZ>5__9, <cntX>5__11, <cntY>5__12, <cntZ>5__13, <totalAbsorp>5__3, <totalArea>5__4, <ceilHeight>5__10, decayHFRatio, matHardness, QualityConfig.OcclusionRayDist));
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <FirstSpawnScan>d__20 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public RoomAnalyzer <>4__this;

			private float <w>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <FirstSpawnScan>d__20(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_009c: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a6: Expected O, but got Unknown
				//IL_005d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0067: Expected O, but got Unknown
				//IL_0046: Unknown result type (might be due to invalid IL or missing references)
				//IL_004b: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
				int num = <>1__state;
				RoomAnalyzer roomAnalyzer = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<w>5__2 = 0f;
					goto IL_0089;
				case 1:
					<>1__state = -1;
					<w>5__2 += 0.2f;
					goto IL_0089;
				case 2:
					{
						<>1__state = -1;
						if (roomAnalyzer._isDestroyed)
						{
							return false;
						}
						SpatialAudioManager instance = SpatialAudioManager.Instance;
						if ((Object)(object)instance == (Object)null)
						{
							return false;
						}
						ProjectEchoPlugin.Log.LogInfo((object)"[RoomAnalyzer] First spawn scan");
						if (roomAnalyzer._activeCoroutine != null)
						{
							((MonoBehaviour)roomAnalyzer).StopCoroutine(roomAnalyzer._activeCoroutine);
						}
						roomAnalyzer._activeCoroutine = ((MonoBehaviour)roomAnalyzer).StartCoroutine(roomAnalyzer.ScanCoroutine(instance.PlayerPosition, instance.WallLayer, QualityConfig.RayCount, forced: true));
						return false;
					}
					IL_0089:
					if (<w>5__2 < 10f)
					{
						SpatialAudioManager instance2 = SpatialAudioManager.Instance;
						if (!((Object)(object)instance2 != (Object)null) || !(instance2.PlayerPosition != Vector3.zero))
						{
							<>2__current = (object)new WaitForSeconds(0.2f);
							<>1__state = 1;
							return true;
						}
					}
					<>2__current = (object)new WaitForSeconds(0.3f);
					<>1__state = 2;
					return true;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <ScanCoroutine>d__23 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			private <>c__DisplayClass23_0 <>8__1;

			public RoomAnalyzer <>4__this;

			public Vector3 origin;

			public LayerMask wallLayer;

			public int rayCount;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ScanCoroutine>d__23(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>8__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_0043: Expected O, but got Unknown
				//IL_0083: Unknown result type (might be due to invalid IL or missing references)
				//IL_0089: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
				//IL_0113: Unknown result type (might be due to invalid IL or missing references)
				//IL_0118: Unknown result type (might be due to invalid IL or missing references)
				//IL_0122: Unknown result type (might be due to invalid IL or missing references)
				//IL_0127: Unknown result type (might be due to invalid IL or missing references)
				//IL_012e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0152: Unknown result type (might be due to invalid IL or missing references)
				//IL_0154: Unknown result type (might be due to invalid IL or missing references)
				//IL_0189: Unknown result type (might be due to invalid IL or missing references)
				//IL_018b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0198: Unknown result type (might be due to invalid IL or missing references)
				//IL_019d: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
				int num = <>1__state;
				RoomAnalyzer roomAnalyzer = <>4__this;
				SpatialAudioManager instance;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>8__1 = new <>c__DisplayClass23_0();
					<>2__current = (object)new WaitForEndOfFrame();
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					<>8__1.primary = RoomProfile.Default;
					<>8__1.secondary = RoomProfile.Default;
					<>8__1.hasSecondary = false;
					<>2__current = ((MonoBehaviour)roomAnalyzer).StartCoroutine(roomAnalyzer.DoScanInto(origin, wallLayer, rayCount, delegate(RoomProfile p)
					{
						<>8__1.primary = p;
					}));
					<>1__state = 2;
					return true;
				case 2:
				{
					<>1__state = -1;
					if (roomAnalyzer._isDestroyed)
					{
						return false;
					}
					instance = SpatialAudioManager.Instance;
					if ((Object)(object)instance == (Object)null)
					{
						return false;
					}
					if (QualityConfig.Tier.Value == QualityTier.Low)
					{
						break;
					}
					Vector3 val = Vector3.zero;
					float num2 = 0f;
					float num3 = float.MaxValue;
					Vector3[] hORIZ_DIRS = HORIZ_DIRS;
					RaycastHit val3 = default(RaycastHit);
					foreach (Vector3 val2 in hORIZ_DIRS)
					{
						float num4 = QualityConfig.OcclusionRayDist;
						if (Physics.Raycast(origin, val2, ref val3, num4, LayerMask.op_Implicit(wallLayer)))
						{
							num4 = ((RaycastHit)(ref val3)).distance;
						}
						if (num4 > num2)
						{
							num2 = num4;
							val = val2;
						}
						if (num4 < num3)
						{
							num3 = num4;
						}
					}
					if (num3 < 5f && num2 > 8f && val != Vector3.zero)
					{
						Vector3 val4 = origin + val * 1.5f;
						<>2__current = ((MonoBehaviour)roomAnalyzer).StartCoroutine(roomAnalyzer.DoScanInto(val4, wallLayer, 6, delegate(RoomProfile p)
						{
							<>8__1.secondary = p;
							<>8__1.hasSecondary = true;
						}));
						<>1__state = 3;
						return true;
					}
					break;
				}
				case 3:
					<>1__state = -1;
					break;
				}
				if (roomAnalyzer._isDestroyed)
				{
					return false;
				}
				instance = SpatialAudioManager.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return false;
				}
				RoomProfile newProfile = <>8__1.primary;
				if (<>8__1.hasSecondary)
				{
					newProfile = RoomProfile.Lerp(<>8__1.primary, <>8__1.secondary, 0.35f);
					if (QualityConfig.DebugMode.Value)
					{
						ProjectEchoPlugin.Log.LogInfo((object)($"[RoomAnalyzer] Multizone blend: {0.35f:F2} " + $"(primary RT60={<>8__1.primary.RT60:F2}s, " + $"secondary RT60={<>8__1.secondary.RT60:F2}s)"));
					}
				}
				instance.UpdateRoom(newProfile);
				roomAnalyzer._firstScanDone = true;
				roomAnalyzer._activeCoroutine = null;
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private const float SABINE_K = 0.161f;

		private const float MIN_VOLUME = 1f;

		private const float MIN_SABIN = 0.001f;

		private const float ZONE_NEAR_THRESHOLD = 5f;

		private const float ZONE_FAR_THRESHOLD = 8f;

		private const float ZONE_OFFSET = 1.5f;

		private static readonly Vector3[] ALL_DIRECTIONS;

		private static readonly Vector3[] HORIZ_DIRS;

		private static readonly Dictionary<string, float> ABSORPTION;

		private static readonly Dictionary<string, float> DECAY_HF_RATIO;

		private static readonly Dictionary<string, float> HARDNESS;

		private ScanScheduler? _scheduler;

		private Coroutine? _activeCoroutine;

		private bool _isDestroyed;

		private bool _firstScanDone;

		private void Awake()
		{
			_scheduler = ((Component)this).GetComponent<ScanScheduler>();
			if ((Object)(object)_scheduler == (Object)null)
			{
				_scheduler = ((Component)this).gameObject.AddComponent<ScanScheduler>();
			}
		}

		private void Start()
		{
			((MonoBehaviour)this).StartCoroutine(FirstSpawnScan());
		}

		private void OnEnable()
		{
			if ((Object)(object)_scheduler != (Object)null)
			{
				_scheduler.OnScanRequested += OnScanRequested;
			}
		}

		private void OnDisable()
		{
			if ((Object)(object)_scheduler != (Object)null)
			{
				_scheduler.OnScanRequested -= OnScanRequested;
			}
		}

		private void OnDestroy()
		{
			_isDestroyed = true;
			if (_activeCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_activeCoroutine);
			}
		}

		[IteratorStateMachine(typeof(<FirstSpawnScan>d__20))]
		private IEnumerator FirstSpawnScan()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <FirstSpawnScan>d__20(0)
			{
				<>4__this = this
			};
		}

		private void OnScanRequested(int rayCount)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			SpatialAudioManager instance = SpatialAudioManager.Instance;
			if (!((Object)(object)instance == (Object)null) && (!_firstScanDone || instance.HasPlayerMovedSignificantly()))
			{
				if (_activeCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_activeCoroutine);
				}
				_activeCoroutine = ((MonoBehaviour)this).StartCoroutine(ScanCoroutine(instance.PlayerPosition, instance.WallLayer, rayCount));
			}
		}

		public void ForceScan()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			SpatialAudioManager instance = SpatialAudioManager.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				if (_activeCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_activeCoroutine);
				}
				_activeCoroutine = ((MonoBehaviour)this).StartCoroutine(ScanCoroutine(instance.PlayerPosition, instance.WallLayer, QualityConfig.RayCount, forced: true));
			}
		}

		[IteratorStateMachine(typeof(<ScanCoroutine>d__23))]
		private IEnumerator ScanCoroutine(Vector3 origin, LayerMask wallLayer, int rayCount, bool forced = false)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ScanCoroutine>d__23(0)
			{
				<>4__this = this,
				origin = origin,
				wallLayer = wallLayer,
				rayCount = rayCount
			};
		}

		[IteratorStateMachine(typeof(<DoScanInto>d__24))]
		private IEnumerator DoScanInto(Vector3 origin, LayerMask wallLayer, int rayCount, Action<RoomProfile> onComplete)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DoScanInto>d__24(0)
			{
				<>4__this = this,
				origin = origin,
				wallLayer = wallLayer,
				rayCount = rayCount,
				onComplete = onComplete
			};
		}

		private static RoomProfile BuildProfile(float sumX, float sumY, float sumZ, int cntX, int cntY, int cntZ, float totalAbsorp, float totalArea, float ceilHeight, float decayHFRatio, float matHardness, float maxDist)
		{
			float num = ((cntX > 0) ? (sumX / (float)cntX * 2f) : maxDist);
			float num2 = ((cntY > 0) ? (sumY / (float)cntY * 2f) : 3f);
			float num3 = ((cntZ > 0) ? (sumZ / (float)cntZ * 2f) : maxDist);
			float num4 = Mathf.Max(num * num2 * num3, 1f);
			float num5 = Mathf.Max(totalAbsorp, 0.001f);
			float num6 = Mathf.Clamp(0.161f * num4 / num5, 0.1f, 8f);
			float num7 = ((totalArea > 0f) ? (totalAbsorp / totalArea) : 0.06f);
			float num8 = Mathf.InverseLerp(2f, 10f, ceilHeight);
			float num9 = num6 * Mathf.Lerp(0.75f, 1.25f, num8);
			float num10 = Mathf.Clamp01(Mathf.Lerp(0.3f, 0.9f, num8) * (1f - num4 / 8000f));
			float num11 = Mathf.Max(num, num3);
			float num12 = Mathf.Min(num, num3);
			bool flag = num12 > 0f && num11 / num12 > 3f;
			if (flag)
			{
				num9 *= 0.8f;
				num10 *= 0.6f;
			}
			float num13 = Mathf.Lerp(-2000f, -100f, matHardness * Mathf.InverseLerp(500f, 5f, num4));
			RoomProfile result = default(RoomProfile);
			result.Volume = num4;
			result.RT60 = Mathf.Clamp(num9, 0.1f, 8f);
			result.AverageSurfaceAlpha = num7;
			result.RoomGain = Mathf.Lerp(-3000f, -100f, Mathf.InverseLerp(10f, 2000f, num4));
			result.HighFreqDamping = Mathf.Lerp(-200f, -3000f, Mathf.InverseLerp(0.02f, 0.5f, num7));
			result.EarlyReflectionDelay = Mathf.Clamp(flag ? (num12 / 686f) : (num / 686f), 0.005f, 0.3f);
			result.LateReverbDelay = Mathf.Clamp(num9 * 0.1f, 0f, 0.1f);
			result.Diffusion = Mathf.Clamp01(num10);
			result.Density = (flag ? 0.7f : 1f);
			result.DecayHFRatio = Mathf.Clamp(decayHFRatio, 0.1f, 2f);
			result.ReflectionsLevel = Mathf.Clamp(num13, -10000f, 1000f);
			result.MaterialHardness = Mathf.Clamp01(matHardness);
			result.IsValid = true;
			return result;
		}

		private static string GetMaterialTag(RaycastHit hit)
		{
			if ((Object)(object)((RaycastHit)(ref hit)).collider == (Object)null)
			{
				return "Default";
			}
			if ((Object)(object)((RaycastHit)(ref hit)).collider.sharedM