Decompiled source of Echolocation v1.7.5

Mods/Echolocation.dll

Decompiled 6 hours 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 BoneLib;
using BoneLib.BoneMenu;
using BoneLib.BoneMenu.UI;
using Echolocation;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Bonelab;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.AI;
using Il2CppSLZ.Marrow.Audio;
using Il2CppSLZ.Marrow.Pool;
using Il2CppSLZ.Marrow.PuppetMasta;
using Il2CppSLZ.Rig;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(EcholocationMod), "Echolocation", "1.7.5", "Echolocation", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Echolocation
{
	internal sealed class BlackoutView : IDisposable
	{
		internal const int BlackQueue = 4998;

		internal const int EchoQueue = 4999;

		private readonly Action<string> _warning;

		private Mesh? _mesh;

		private Material? _material;

		private int _attempts;

		private float _nextAttempt;

		private bool _disposed;

		private readonly Camera?[] _desktopCameras = (Camera?[])(object)new Camera[64];

		private readonly bool[] _knownDesktopCameras = new bool[64];

		private readonly Camera?[] _overlayCameras = (Camera?[])(object)new Camera[16];

		private Il2CppReferenceArray<Camera>? _cameraBuffer;

		private int _desktopCount;

		private int _overlayCount;

		private int _lastCameraCount = -1;

		private float _nextCameraRefresh;

		private bool _cameraWarning;

		internal bool Active { get; private set; }

		internal string Status { get; private set; } = "Waiting for player";

		internal BlackoutView(Action<string> warning)
		{
			_warning = warning;
		}

		internal void Tick(float now, bool enabled)
		{
			Active = false;
			if (_disposed)
			{
				return;
			}
			if (!enabled)
			{
				Status = "Normal vision";
				return;
			}
			GUIMenu instance = GUIMenu.Instance;
			UIRig uIRig = Player.UIRig;
			if (((Object)(object)uIRig != (Object)null && (Object)(object)uIRig.popUpMenu != (Object)null && uIRig.popUpMenu.isActive) || ((Object)(object)instance != (Object)null && ((Component)instance).gameObject.activeInHierarchy))
			{
				Status = "Normal vision while a game menu is open";
				return;
			}
			EnsureAssets(now);
			if ((Object)(object)_mesh == (Object)null || (Object)(object)_material == (Object)null)
			{
				return;
			}
			OpenControllerRig controllerRig = Player.ControllerRig;
			Il2CppReferenceArray<Camera> val = (((Object)(object)controllerRig != (Object)null) ? controllerRig.cameras : null);
			Camera main = Camera.main;
			RefreshDesktopCameras(now);
			bool flag = false;
			int num = 0;
			while (val != null && num < ((Il2CppArrayBase<Camera>)(object)val).Length)
			{
				Camera val2 = ((Il2CppArrayBase<Camera>)(object)val)[num];
				if ((Object)(object)val2 == (Object)(object)main)
				{
					flag = true;
				}
				DrawCamera(val2);
				num++;
			}
			if (!flag)
			{
				DrawCamera(main);
			}
			for (int i = 0; i < _desktopCount; i++)
			{
				Camera val3 = _desktopCameras[i];
				if ((Object)(object)val3 == (Object)null || (Object)(object)val3 == (Object)(object)main || (!_knownDesktopCameras[i] && !IsDisplayCamera(val3)))
				{
					continue;
				}
				bool flag2 = false;
				int num2 = 0;
				while (val != null && num2 < ((Il2CppArrayBase<Camera>)(object)val).Length)
				{
					if ((Object)(object)((Il2CppArrayBase<Camera>)(object)val)[num2] == (Object)(object)val3)
					{
						flag2 = true;
						break;
					}
					num2++;
				}
				if (!flag2)
				{
					DrawCamera(val3);
				}
			}
			Status = (Active ? "Blackout active: echolocation only" : "Waiting for player camera");
		}

		private void RefreshDesktopCameras(float now)
		{
			int allCamerasCount = Camera.allCamerasCount;
			if (now < _nextCameraRefresh && allCamerasCount == _lastCameraCount)
			{
				return;
			}
			_nextCameraRefresh = now + 0.5f;
			_lastCameraCount = allCamerasCount;
			Array.Clear(_desktopCameras, 0, _desktopCameras.Length);
			Array.Clear(_overlayCameras, 0, _overlayCameras.Length);
			_desktopCount = (_overlayCount = 0);
			try
			{
				Il2CppArrayBase<RigScreenOptions> val = Object.FindObjectsOfType<RigScreenOptions>();
				for (int i = 0; i < val.Length && i < _overlayCameras.Length; i++)
				{
					RigScreenOptions val2 = val[i];
					if (!((Object)(object)val2 == (Object)null))
					{
						if ((Object)(object)val2.OverlayCam != (Object)null)
						{
							_overlayCameras[_overlayCount++] = val2.OverlayCam;
						}
						AddDesktopCamera(val2.cam, knownView: true);
					}
				}
				RigManager rigManager = Player.RigManager;
				PancakeControllerRig val3 = (((Object)(object)rigManager != (Object)null && (Object)(object)rigManager.controllerRig != (Object)null) ? ((Il2CppObjectBase)rigManager.controllerRig).TryCast<PancakeControllerRig>() : null);
				if ((Object)(object)val3 != (Object)null && (Object)(object)val3.wasdCam != (Object)null)
				{
					AddDesktopCamera(val3.wasdCam.Camera, knownView: true);
				}
				if (allCamerasCount < 1 || allCamerasCount > 1024)
				{
					return;
				}
				if (_cameraBuffer == null || ((Il2CppArrayBase<Camera>)(object)_cameraBuffer).Length < allCamerasCount)
				{
					_cameraBuffer = new Il2CppReferenceArray<Camera>((long)Math.Max(32, allCamerasCount));
				}
				allCamerasCount = Camera.GetAllCameras(_cameraBuffer);
				for (int j = 0; j < allCamerasCount; j++)
				{
					Camera val4 = ((Il2CppArrayBase<Camera>)(object)_cameraBuffer)[j];
					if ((Object)(object)val4 != (Object)null && IsDisplayCamera(val4))
					{
						AddDesktopCamera(val4, knownView: false);
					}
				}
			}
			catch (Exception ex)
			{
				if (!_cameraWarning)
				{
					_cameraWarning = true;
					_warning("Desktop blackout camera discovery will retry: " + ex.Message);
				}
			}
		}

		private static bool IsDisplayCamera(Camera camera)
		{
			if (!camera.stereoEnabled && (Object)(object)camera.targetTexture == (Object)null)
			{
				return camera.targetDisplay >= 0;
			}
			return false;
		}

		private void AddDesktopCamera(Camera? camera, bool knownView)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			if ((Object)(object)camera == (Object)null || !((Behaviour)camera).isActiveAndEnabled || (int)camera.cameraType != 1 || (camera.cullingMask & -33) == 0 || _desktopCount >= _desktopCameras.Length)
			{
				return;
			}
			for (int i = 0; i < _desktopCount; i++)
			{
				if ((Object)(object)_desktopCameras[i] == (Object)(object)camera)
				{
					return;
				}
			}
			_knownDesktopCameras[_desktopCount] = knownView;
			_desktopCameras[_desktopCount++] = camera;
		}

		private void DrawCamera(Camera? camera)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)camera == (Object)null || !((Behaviour)camera).isActiveAndEnabled || (int)camera.cameraType != 1 || (camera.cullingMask & -33) == 0)
			{
				return;
			}
			for (int i = 0; i < _overlayCount; i++)
			{
				if ((Object)(object)_overlayCameras[i] == (Object)(object)camera)
				{
					return;
				}
			}
			float num = Mathf.Max(2f, camera.nearClipPlane * 4f + camera.stereoSeparation);
			num = Mathf.Min(num, camera.farClipPlane * 0.4f);
			Matrix4x4 val = Matrix4x4.TRS(((Component)camera).transform.position, Quaternion.identity, Vector3.one * num);
			int num2 = VisibleLayer(camera.cullingMask);
			if (num2 >= 0)
			{
				Graphics.DrawMesh(_mesh, val, _material, num2, camera, 0, (MaterialPropertyBlock)null, (ShadowCastingMode)0, false, (Transform)null, (LightProbeUsage)0, (LightProbeProxyVolume)null);
				Active = true;
			}
		}

		internal void Reset()
		{
			Active = false;
			_attempts = 0;
			_nextAttempt = 0f;
			_lastCameraCount = -1;
			_nextCameraRefresh = 0f;
			_cameraWarning = false;
			_desktopCount = (_overlayCount = 0);
			Array.Clear(_desktopCameras, 0, _desktopCameras.Length);
			Array.Clear(_overlayCameras, 0, _overlayCameras.Length);
			_cameraBuffer = null;
			Status = "Waiting for player";
		}

		private void EnsureAssets(float now)
		{
			//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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			if (((Object)(object)_mesh != (Object)null && (Object)(object)_material != (Object)null) || _attempts >= 6 || now < _nextAttempt)
			{
				return;
			}
			_attempts++;
			_nextAttempt = now + 2f;
			DestroyAssets();
			string[] array = new string[2] { "Hidden/Internal-Colored", "GUI/Text Shader" };
			foreach (string text in array)
			{
				Shader val = Shader.Find(text);
				if ((Object)(object)val == (Object)null || !val.isSupported)
				{
					continue;
				}
				Material val2 = new Material(val)
				{
					hideFlags = (HideFlags)61,
					renderQueue = 4998
				};
				if (text == "Hidden/Internal-Colored" && (!val2.HasProperty("_ZTest") || !val2.HasProperty("_ZWrite")))
				{
					Object.Destroy((Object)(object)val2);
					continue;
				}
				_material = val2;
				if (val2.HasProperty("_Color"))
				{
					val2.SetColor("_Color", Color.black);
				}
				if (val2.HasProperty("_MainTex"))
				{
					val2.SetTexture("_MainTex", (Texture)(object)Texture2D.whiteTexture);
				}
				SetInt(val2, "_ZTest", 8);
				SetInt(val2, "_ZWrite", 0);
				SetInt(val2, "_Cull", 0);
				SetInt(val2, "_SrcBlend", 1);
				SetInt(val2, "_DstBlend", 0);
				_mesh = MakeCube();
				return;
			}
			Status = "Blackout shader unavailable";
			if (_attempts == 1 || _attempts == 6)
			{
				_warning("Blackout could not find an always-visible shader. F6 restores normal vision; check Echolocation diagnostics.");
			}
		}

		private static Mesh MakeCube()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: 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_00d9: 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_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Expected O, but got Unknown
			Vector3[] array = (Vector3[])(object)new Vector3[8]
			{
				new Vector3(-1f, -1f, -1f),
				new Vector3(1f, -1f, -1f),
				new Vector3(1f, 1f, -1f),
				new Vector3(-1f, 1f, -1f),
				new Vector3(-1f, -1f, 1f),
				new Vector3(1f, -1f, 1f),
				new Vector3(1f, 1f, 1f),
				new Vector3(-1f, 1f, 1f)
			};
			int[] array2 = new int[36]
			{
				0, 2, 1, 0, 3, 2, 4, 5, 6, 4,
				6, 7, 0, 4, 7, 0, 7, 3, 1, 2,
				6, 1, 6, 5, 3, 7, 6, 3, 6, 2,
				0, 1, 5, 0, 5, 4
			};
			Il2CppStructArray<Vector3> val = new Il2CppStructArray<Vector3>((long)array.Length);
			Il2CppStructArray<Color> val2 = new Il2CppStructArray<Color>((long)array.Length);
			Il2CppStructArray<Vector2> val3 = new Il2CppStructArray<Vector2>((long)array.Length);
			Il2CppStructArray<int> val4 = new Il2CppStructArray<int>((long)(array2.Length * 2));
			for (int i = 0; i < array.Length; i++)
			{
				((Il2CppArrayBase<Vector3>)(object)val)[i] = array[i];
				((Il2CppArrayBase<Color>)(object)val2)[i] = Color.black;
				((Il2CppArrayBase<Vector2>)(object)val3)[i] = new Vector2(0.5f, 0.5f);
			}
			for (int j = 0; j < array2.Length; j += 3)
			{
				((Il2CppArrayBase<int>)(object)val4)[j] = array2[j];
				((Il2CppArrayBase<int>)(object)val4)[j + 1] = array2[j + 1];
				((Il2CppArrayBase<int>)(object)val4)[j + 2] = array2[j + 2];
				((Il2CppArrayBase<int>)(object)val4)[array2.Length + j] = array2[j];
				((Il2CppArrayBase<int>)(object)val4)[array2.Length + j + 1] = array2[j + 2];
				((Il2CppArrayBase<int>)(object)val4)[array2.Length + j + 2] = array2[j + 1];
			}
			Mesh val5 = new Mesh
			{
				name = "Echolocation.Blackout",
				hideFlags = (HideFlags)61
			};
			val5.SetVertices(val, 0, array.Length);
			val5.SetColors(val2, 0, array.Length);
			val5.SetUVs(0, val3, 0, array.Length);
			val5.SetTriangles(val4, 0, ((Il2CppArrayBase<int>)(object)val4).Length, 0, true, 0);
			return val5;
		}

		private static int VisibleLayer(int mask)
		{
			for (int i = 0; i < 32; i++)
			{
				if ((mask & (1 << i)) != 0)
				{
					return i;
				}
			}
			return -1;
		}

		private static void SetInt(Material material, string name, int value)
		{
			if (material.HasProperty(name))
			{
				material.SetInt(name, value);
			}
		}

		private void DestroyAssets()
		{
			if ((Object)(object)_mesh != (Object)null)
			{
				Object.Destroy((Object)(object)_mesh);
			}
			if ((Object)(object)_material != (Object)null)
			{
				Object.Destroy((Object)(object)_material);
			}
			_mesh = null;
			_material = null;
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
				Reset();
				DestroyAssets();
			}
		}
	}
	internal sealed class DoubleClapGesture
	{
		private const float MinimumClapInterval = 0.12f;

		private const float MaximumClapInterval = 1.5f;

		private const float PulseCooldown = 0.6f;

		private const float MaximumSampleGap = 0.3f;

		private bool _initialized;

		private bool _armed;

		private bool _hasFirstClap;

		private Vector3 _lastLeft;

		private Vector3 _lastRight;

		private float _lastTime;

		private float _lastDistance;

		private float _firstClapTime;

		private float _nextPulseTime;

		private float _lastContactDistance;

		internal bool ContactDetected { get; private set; }

		public bool Tick(float now, Vector3 left, Vector3 right, bool available, float contactDistance = 0.3f)
		{
			//IL_0013: 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_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: 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)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			ContactDetected = false;
			if (!available || !float.IsFinite(now) || !Finite(left) || !Finite(right))
			{
				Reset();
				return false;
			}
			float num = Vector3.Distance(left, right);
			if (!float.IsFinite(num))
			{
				Reset();
				return false;
			}
			contactDistance = (float.IsFinite(contactDistance) ? Math.Clamp(contactDistance, 0.15f, 0.45f) : 0.3f);
			float num2 = contactDistance + 0.1f;
			float num3 = now - _lastTime;
			float num4 = Math.Max(0.6f, num3 * 14f);
			if (!_initialized || contactDistance != _lastContactDistance || num3 <= 0f || num3 > 0.3f || Vector3.Distance(left, _lastLeft) > num4 || Vector3.Distance(right, _lastRight) > num4)
			{
				BeginSample(now, left, right, num, contactDistance);
				return false;
			}
			bool flag = _lastDistance > contactDistance && num <= contactDistance;
			_lastTime = now;
			_lastLeft = left;
			_lastRight = right;
			_lastDistance = num;
			if (_hasFirstClap && now - _firstClapTime > 1.5f)
			{
				_hasFirstClap = false;
			}
			if (now < _nextPulseTime)
			{
				_armed = false;
				_hasFirstClap = false;
				return false;
			}
			if (num >= num2)
			{
				_armed = true;
			}
			if (!_armed || !flag)
			{
				return false;
			}
			_armed = false;
			if (!_hasFirstClap)
			{
				_hasFirstClap = true;
				_firstClapTime = now;
				ContactDetected = true;
				return false;
			}
			if (now - _firstClapTime < 0.12f)
			{
				return false;
			}
			_hasFirstClap = false;
			_nextPulseTime = now + 0.6f;
			ContactDetected = true;
			return true;
		}

		private void BeginSample(float now, Vector3 left, Vector3 right, float distance, float contactDistance)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if (_initialized && now < _lastTime)
			{
				_nextPulseTime = 0f;
			}
			_initialized = true;
			_lastTime = now;
			_lastLeft = left;
			_lastRight = right;
			_lastDistance = distance;
			_lastContactDistance = contactDistance;
			_armed = distance >= contactDistance + 0.1f && now >= _nextPulseTime;
			_hasFirstClap = false;
		}

		private static bool Finite(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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)
			if (float.IsFinite(value.x) && float.IsFinite(value.y))
			{
				return float.IsFinite(value.z);
			}
			return false;
		}

		public void Reset()
		{
			ContactDetected = false;
			_initialized = false;
			_armed = false;
			_hasFirstClap = false;
			_nextPulseTime = 0f;
		}
	}
	internal enum EchoKind
	{
		Manual,
		Microphone,
		Footstep,
		World,
		Voice
	}
	internal readonly struct EchoEvent
	{
		public Vector3 Origin { get; }

		public float Strength { get; }

		public EchoKind Kind { get; }

		public float Time { get; }

		public int PlayerId { get; }

		public AudioSource? Source { get; }

		public NpcSoundEmission? NpcEmission { get; }

		public SoundEmitterSurface? SurfaceEmission { get; }

		public EchoEvent(Vector3 origin, float strength, EchoKind kind, float time, int playerId = -1, AudioSource? source = null, NpcSoundEmission? npcEmission = null, SoundEmitterSurface? surfaceEmission = null)
		{
			//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)
			Origin = origin;
			Strength = (float.IsFinite(strength) ? Mathf.Clamp01(strength) : 0f);
			Kind = kind;
			Time = time;
			PlayerId = playerId;
			Source = source;
			NpcEmission = npcEmission;
			SurfaceEmission = surfaceEmission ?? npcEmission;
		}

		internal EchoEvent WithTime(float time)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return new EchoEvent(Origin, Strength, Kind, time, PlayerId, Source, NpcEmission, SurfaceEmission);
		}
	}
	public sealed class EcholocationMod : MelonMod
	{
		private Settings _settings;

		private EchoRenderer _renderer;

		private MicrophoneInput _microphone;

		private WorldSoundInput _world;

		private ObjectSoundSources _objectSources;

		private WorldAudioMonitor _worldMonitor;

		private LocomotionInput _locomotion;

		private MenuPage _menu;

		private PulseAudio _audio;

		private Action<EchoEvent> _emit;

		private Action<EchoEvent> _observeSound;

		private Transform? _head;

		private Transform? _rigRoot;

		private bool _active;

		private float _nextWarning;

		private float _nextPulse;

		private readonly int[] _counts = new int[5];

		private readonly EchoEvent[] _pending = new EchoEvent[5];

		private readonly bool[] _hasPending = new bool[5];

		private int _inputCursor = 1;

		private float _gestureStarted = -1f;

		private bool _gestureLatched;

		private readonly DoubleClapGesture _clap = new DoubleClapGesture();

		private int _clapPulseCount;

		public override void OnInitializeMelon()
		{
			_settings = new Settings();
			_renderer = new EchoRenderer(_settings, Warn);
			_microphone = new MicrophoneInput(_settings, Warn);
			_objectSources = new ObjectSoundSources(Warn);
			_world = new WorldSoundInput(_settings, Warn, _objectSources.Capture);
			_locomotion = new LocomotionInput(_settings);
			_menu = new MenuPage(_settings, this, Warn);
			_audio = new PulseAudio(Warn);
			_emit = Emit;
			_observeSound = ObserveSound;
			_worldMonitor = new WorldAudioMonitor(RegisterWorldPlayback, Warn);
			((MelonBase)this).LoggerInstance.Msg("Echolocation 1.7.5 loaded. Closer walking/sound dots with 9cm merging and bounded automatic ray work. Gunshot echoes use the muzzle. No native audio patches. Controls in BoneMenu. F6 toggle; F5 pulse.");
			((MelonBase)this).LoggerInstance.Msg("Visuals are local. Mic loudness is measured in memory only; no audio is saved or sent by this mod.");
			Save();
		}

		public override void OnUpdate()
		{
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			if (_settings == null)
			{
				return;
			}
			_menu.EnsureBuilt();
			ReadVisionGesture();
			if (_settings.DesktopHotkeys)
			{
				if (Input.GetKeyDown((KeyCode)287))
				{
					SetEnabled(!_settings.Enabled);
				}
				if (Input.GetKeyDown((KeyCode)286))
				{
					ManualPulse();
				}
			}
			Transform val = null;
			Transform val2 = null;
			try
			{
				val = Player.Head;
				if ((Object)(object)Player.RigManager != (Object)null)
				{
					val2 = ((Component)Player.RigManager).transform;
				}
			}
			catch (Exception)
			{
			}
			if (!_settings.Enabled || (Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				_clap.Reset();
				if (_active)
				{
					ResetState();
				}
				return;
			}
			if (_active && ((Object)(object)_head != (Object)(object)val || (Object)(object)_rigRoot != (Object)(object)val2))
			{
				ResetState();
			}
			_head = val;
			_rigRoot = val2;
			_active = true;
			float unscaledTime = Time.unscaledTime;
			ReadClapGesture(unscaledTime, val);
			try
			{
				_microphone.Tick(unscaledTime, val.position, _emit, _observeSound);
			}
			catch (Exception ex2)
			{
				Warn("Microphone/voice input: " + ex2.Message);
			}
			try
			{
				_locomotion.Tick(unscaledTime, val, val2, _emit);
			}
			catch (Exception ex3)
			{
				Warn("Footstep input: " + ex3.Message);
			}
			try
			{
				if (_settings.WorldSoundEnabled)
				{
					_worldMonitor.Tick(unscaledTime);
				}
				else
				{
					_worldMonitor.Reset();
				}
			}
			catch (Exception ex4)
			{
				Warn("World playback monitoring: " + ex4.Message);
			}
			try
			{
				_world.Tick(unscaledTime, val.position, _emit, _observeSound);
			}
			catch (Exception ex5)
			{
				Warn("World sound input: " + ex5.Message);
			}
			FlushInput(unscaledTime);
		}

		public override void OnLateUpdate()
		{
			if (!_active || !_settings.Enabled || (Object)(object)_head == (Object)null)
			{
				return;
			}
			try
			{
				_renderer.Tick(Time.unscaledTime, _head, _rigRoot);
			}
			catch (Exception ex)
			{
				Warn("Surface renderer: " + ex.Message);
			}
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			if (_settings != null)
			{
				ResetState();
			}
		}

		public override void OnDeinitializeMelon()
		{
			if (_settings != null)
			{
				ResetState();
				try
				{
					_worldMonitor.Dispose();
				}
				catch (Exception ex)
				{
					Warn(ex.Message);
				}
				try
				{
					_microphone.Dispose();
				}
				catch (Exception ex2)
				{
					Warn(ex2.Message);
				}
				try
				{
					_world.Dispose();
				}
				catch (Exception ex3)
				{
					Warn(ex3.Message);
				}
				try
				{
					_renderer.Dispose();
				}
				catch (Exception ex4)
				{
					Warn(ex4.Message);
				}
				try
				{
					_audio.Dispose();
				}
				catch (Exception ex5)
				{
					Warn(ex5.Message);
				}
				Save();
			}
		}

		internal void SetEnabled(bool enabled)
		{
			_settings.Enabled = enabled;
			if (!enabled)
			{
				ResetState();
			}
			Save();
		}

		internal void SetBlackout(bool enabled)
		{
			_settings.Blackout = enabled;
			Save();
		}

		internal void SetDoubleClap(bool enabled)
		{
			_settings.DoubleClapEnabled = enabled;
			_clap.Reset();
			Save();
		}

		internal void SetClapDistance(float distance)
		{
			_settings.ClapContactDistance = distance;
			_clap.Reset();
			Save();
		}

		internal void SetPulseSound(bool enabled)
		{
			_settings.PulseSoundEnabled = enabled;
			if (!enabled)
			{
				_audio.Stop();
			}
			Save();
		}

		internal void TestPulseSound()
		{
			_audio.PlayPulse(_settings.PulseSoundVolume);
		}

		private void ReadClapGesture(float now, Transform head)
		{
			//IL_010b: 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_01c3: Unknown result type (might be due to invalid IL or missing references)
			if (!_settings.DoubleClapEnabled)
			{
				_clap.Reset();
				return;
			}
			try
			{
				OpenControllerRig controllerRig = Player.ControllerRig;
				BaseController leftController = Player.LeftController;
				BaseController rightController = Player.RightController;
				UIRig uIRig = Player.UIRig;
				GUIMenu instance = GUIMenu.Instance;
				if (((Object)(object)uIRig != (Object)null && (Object)(object)uIRig.popUpMenu != (Object)null && uIRig.popUpMenu.isActive) || ((Object)(object)instance != (Object)null && ((Component)instance).gameObject.activeInHierarchy) || !((Object)(object)controllerRig != (Object)null) || ((ControllerRig)controllerRig).IsPaused || !((Object)(object)leftController != (Object)null) || !((Object)(object)rightController != (Object)null) || !((Behaviour)leftController).isActiveAndEnabled || !((Behaviour)rightController).isActiveAndEnabled || !Player.HandsExist || Player.LeftHand.HasAttachedObject() || Player.RightHand.HasAttachedObject() || (Player.LeftHand._indexButton && Player.RightHand._indexButton))
				{
					_clap.Reset();
					return;
				}
				if (!_clap.Tick(now, ((Component)leftController).transform.position, ((Component)rightController).transform.position, available: true, _settings.ClapContactDistance))
				{
					if (_clap.ContactDetected && _settings.PulseSoundEnabled)
					{
						_audio.PlayClap(_settings.PulseSoundVolume);
					}
					return;
				}
				Transform val = head;
				if (controllerRig.cameras != null)
				{
					foreach (Camera item in (Il2CppArrayBase<Camera>)(object)controllerRig.cameras)
					{
						if ((Object)(object)item != (Object)null && ((Behaviour)item).isActiveAndEnabled)
						{
							val = ((Component)item).transform;
							break;
						}
					}
				}
				if (_renderer.EnqueueClap(val.position, now))
				{
					_clapPulseCount++;
					if (_settings.PulseSoundEnabled)
					{
						_audio.PlayPulse(_settings.PulseSoundVolume);
					}
				}
			}
			catch (Exception ex)
			{
				_clap.Reset();
				Warn("Double-clap input: " + ex.Message);
			}
		}

		private void ReadVisionGesture()
		{
			bool flag = false;
			try
			{
				flag = Player.HandsExist && Player.LeftHand._indexButton && Player.RightHand._indexButton && !Player.LeftHand.HasAttachedObject() && !Player.RightHand.HasAttachedObject();
			}
			catch (Exception)
			{
			}
			if (!flag)
			{
				_gestureStarted = -1f;
				_gestureLatched = false;
			}
			else if (!_gestureLatched)
			{
				float unscaledTime = Time.unscaledTime;
				if (_gestureStarted < 0f)
				{
					_gestureStarted = unscaledTime;
				}
				if (!(unscaledTime - _gestureStarted < 1.5f))
				{
					_gestureLatched = true;
					SetBlackout(!_settings.Blackout);
				}
			}
		}

		internal void ManualPulse()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (!_settings.Enabled)
			{
				return;
			}
			try
			{
				Transform head = Player.Head;
				if ((Object)(object)head != (Object)null)
				{
					Emit(new EchoEvent(head.position, 1f, EchoKind.Manual, Time.unscaledTime));
				}
			}
			catch (Exception ex)
			{
				Warn("Manual pulse needs a loaded player rig: " + ex.Message);
			}
		}

		internal void RestartMicrophone()
		{
			_microphone.Restart();
		}

		internal void Diagnostics()
		{
			((MelonBase)this).LoggerInstance.Msg($"Enabled={_settings.Enabled}; rig={_active}; renderer={_renderer.Ready}; dots={_renderer.DotCount}");
			((MelonBase)this).LoggerInstance.Msg($"Vision: {_renderer.VisionStatus}; blackout setting={_settings.Blackout}");
			((MelonBase)this).LoggerInstance.Msg($"Players: {_renderer.PlayerStatus}; highlights={_settings.PlayerHighlights}");
			((MelonBase)this).LoggerInstance.Msg($"Double clap enabled={_settings.DoubleClapEnabled}; contact distance={_settings.ClapContactDistance:F2}m; 360 pulses={_clapPulseCount}");
			((MelonBase)this).LoggerInstance.Msg($"Pulse sound enabled={_settings.PulseSoundEnabled}; volume={_settings.PulseSoundVolume:F2}; {_audio.Status}");
			((MelonBase)this).LoggerInstance.Msg($"Visual limits: rays/pulse={_settings.RayCount}; rays/frame={_settings.RaysPerFrame}; max dots={_settings.MaxDots}");
			((MelonBase)this).LoggerInstance.Msg($"Optimize walking/sound echoes={_settings.OptimizeAutomaticEchoes}; when enabled: 512 rays/pulse, 256 rays/frame, 0.85s dot lifetime caps");
			((MelonBase)this).LoggerInstance.Msg($"Microphone: {_microphone.Status}; level={_microphone.Level:F4}; threshold={_settings.MicThreshold:F4}");
			((MelonBase)this).LoggerInstance.Msg($"World sources={_world.SourceCount}; threshold={_settings.SoundThreshold:F4}; range={_settings.WorldRange:F0}m");
			((MelonBase)this).LoggerInstance.Msg("Impact audio: " + _worldMonitor.Status);
			((MelonBase)this).LoggerInstance.Msg($"Footsteps: {_locomotion.Status}; world: {_world.Status}; peak={_world.PeakLevel:F4}");
			((MelonBase)this).LoggerInstance.Msg($"Accepted pulses: manual={_counts[0]}, microphone={_counts[1]}, footsteps={_counts[2]}, world={_counts[3]}, voice={_counts[4]}");
		}

		internal void Save()
		{
			try
			{
				_settings.Save();
			}
			catch (Exception ex)
			{
				Warn("Could not save settings: " + ex.Message);
			}
		}

		private void ObserveSound(EchoEvent echo)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (_active && _settings.Enabled && Finite(echo.Origin))
			{
				_renderer.ObserveSound(echo, _rigRoot);
			}
		}

		private void RegisterWorldPlayback(AudioSource source)
		{
			if (_active && _settings.Enabled && _settings.WorldSoundEnabled && !((Object)(object)source == (Object)null) && ((Behaviour)source).isActiveAndEnabled && !source.mute && !(source.volume <= 0f) && !(source.spatialBlend < 0.5f) && (!source.loop || _settings.IncludeLoopingAudio) && !((Object)(object)_head == (Object)null))
			{
				float unscaledTime = Time.unscaledTime;
				_world.RegisterPlayingSource(source, _objectSources.Capture(unscaledTime, source));
			}
		}

		private void Emit(EchoEvent echo)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (!_settings.Enabled || !float.IsFinite(echo.Time) || !Finite(echo.Origin))
			{
				return;
			}
			if (echo.Kind == EchoKind.World && echo.SurfaceEmission != null && echo.SurfaceEmission.Count > 0)
			{
				if (_renderer.Enqueue(echo))
				{
					_counts[(int)echo.Kind]++;
				}
			}
			else if (echo.Kind != EchoKind.Manual)
			{
				int kind = (int)echo.Kind;
				if (!_hasPending[kind] || echo.Time - _pending[kind].Time > 0.3f || echo.Strength > _pending[kind].Strength)
				{
					_pending[kind] = echo;
				}
				_hasPending[kind] = true;
			}
			else if (!(echo.Time < _nextPulse) && _renderer.Enqueue(echo))
			{
				_nextPulse = echo.Time + 0.12f;
				_counts[(int)echo.Kind]++;
			}
		}

		private void FlushInput(float now)
		{
			if (now < _nextPulse)
			{
				return;
			}
			for (int i = 0; i < 4; i++)
			{
				int inputCursor = _inputCursor;
				_inputCursor = _inputCursor % 4 + 1;
				if (_hasPending[inputCursor])
				{
					EchoEvent echoEvent = _pending[inputCursor];
					_hasPending[inputCursor] = false;
					if (!(now - echoEvent.Time > 0.35f) && _renderer.Enqueue(echoEvent.WithTime(now)))
					{
						_nextPulse = now + 0.12f;
						_counts[inputCursor]++;
						break;
					}
				}
			}
		}

		private void ResetState()
		{
			_active = false;
			_head = (_rigRoot = null);
			_nextPulse = 0f;
			_gestureStarted = -1f;
			_gestureLatched = false;
			_clap.Reset();
			Array.Clear(_hasPending, 0, _hasPending.Length);
			try
			{
				_audio.Stop();
			}
			catch (Exception ex)
			{
				Warn(ex.Message);
			}
			try
			{
				_microphone.Restart();
			}
			catch (Exception ex2)
			{
				Warn(ex2.Message);
			}
			try
			{
				_world.Reset();
			}
			catch (Exception ex3)
			{
				Warn(ex3.Message);
			}
			_worldMonitor.Reset();
			_objectSources.Reset();
			_locomotion.Reset();
			try
			{
				_renderer.Reset();
			}
			catch (Exception ex4)
			{
				Warn(ex4.Message);
			}
		}

		private void Warn(string message)
		{
			float unscaledTime = Time.unscaledTime;
			if (!(unscaledTime < _nextWarning))
			{
				_nextWarning = unscaledTime + 5f;
				((MelonBase)this).LoggerInstance.Warning(message);
			}
		}

		private static bool Finite(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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)
			if (float.IsFinite(value.x) && float.IsFinite(value.y))
			{
				return float.IsFinite(value.z);
			}
			return false;
		}
	}
	internal sealed class EchoRenderer : IDisposable
	{
		private struct Pulse
		{
			internal Vector3 Origin;

			internal Quaternion Rotation;

			internal bool Clap;

			internal bool Automatic;

			internal bool Deduplicate;

			internal EchoKind Kind;

			internal SoundEmitterSurface? SurfaceEmission;

			internal Vector3 ListenerOrigin;

			internal int SurfaceRays;

			internal float Strength;

			internal float Time;

			internal float Range;

			internal float Phase;

			internal int Total;

			internal int Next;

			internal int ShuffleBits;

			internal int ShuffleCursor;
		}

		private struct Dot
		{
			internal Vector3 Position;

			internal Color Color;

			internal float Birth;

			internal float FadeInStart;

			internal float Lifetime;

			internal float Strength;

			internal bool Indexed;

			internal bool Clap;

			internal SurfaceCell Cell;
		}

		private struct ColliderFlags
		{
			internal Collider Collider;

			internal bool Local;

			internal bool Player;
		}

		private struct RecentSound
		{
			internal Vector3 Origin;

			internal float Time;

			internal Collider? Owner;

			internal bool Valid;
		}

		private readonly struct SurfaceCell : IEquatable<SurfaceCell>
		{
			private readonly int _collider;

			private readonly int _x;

			private readonly int _y;

			private readonly int _z;

			private readonly int _facing;

			private readonly bool _detail;

			internal SurfaceCell(int collider, Vector3 point, Vector3 normal, float spacing, bool detail)
			{
				//IL_0008: 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_0032: 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_0052: Unknown result type (might be due to invalid IL or missing references)
				//IL_005e: Unknown result type (might be due to invalid IL or missing references)
				//IL_008a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0077: Unknown result type (might be due to invalid IL or missing references)
				//IL_009d: Unknown result type (might be due to invalid IL or missing references)
				_collider = collider;
				_x = (int)MathF.Floor(point.x / spacing);
				_y = (int)MathF.Floor(point.y / spacing);
				_z = (int)MathF.Floor(point.z / spacing);
				float num = MathF.Abs(normal.x);
				float num2 = MathF.Abs(normal.y);
				float num3 = MathF.Abs(normal.z);
				_facing = ((num > num2 && num > num3) ? ((!(normal.x >= 0f)) ? 1 : 0) : ((!(num2 > num3)) ? ((normal.z >= 0f) ? 4 : 5) : ((normal.y >= 0f) ? 2 : 3)));
				_detail = detail;
			}

			public bool Equals(SurfaceCell other)
			{
				if (_collider == other._collider && _x == other._x && _y == other._y && _z == other._z && _facing == other._facing)
				{
					return _detail == other._detail;
				}
				return false;
			}

			public override bool Equals(object? obj)
			{
				if (obj is SurfaceCell other)
				{
					return Equals(other);
				}
				return false;
			}

			public override int GetHashCode()
			{
				return HashCode.Combine(_collider, _x, _y, _z, _facing, _detail);
			}
		}

		private const int QueueLimit = 4;

		private const int HardDotLimit = 65536;

		private const float QueueLifetime = 1.25f;

		private const float WaveSpeed = 45f;

		private const float GoldenAngle = 2.3999631f;

		private const int ShaderAttempts = 6;

		private const int AutomaticRaysPerPulse = 512;

		private const int AutomaticRaysPerFrame = 256;

		private const int EmitterSurfaceRayLimit = 128;

		private const float AutomaticLifetime = 0.85f;

		private const float AutomaticDotSpacing = 0.09f;

		private const int ColliderCacheLimit = 512;

		private static readonly string[] ShaderNames = new string[3] { "Sprites/Default", "Legacy Shaders/Particles/Alpha Blended", "Hidden/Internal-Colored" };

		private readonly Settings _settings;

		private readonly Action<string> _warning;

		private readonly BlackoutView _blackout;

		private readonly PlayerEchoes _players;

		private readonly Pulse[] _pulses = new Pulse[4];

		private readonly Il2CppStructArray<RaycastHit> _hits = new Il2CppStructArray<RaycastHit>(32L);

		private readonly Dictionary<SurfaceCell, int> _surfaceCells = new Dictionary<SurfaceCell, int>();

		private readonly Dictionary<int, ColliderFlags> _colliderFlags = new Dictionary<int, ColliderFlags>(512);

		private readonly RecentSound[] _recentSounds = new RecentSound[8];

		private int _recentSoundCursor;

		private Dot[] _dots = Array.Empty<Dot>();

		private int _dotCount;

		private int _pulseCount;

		private int _pulseCursor;

		private int _overwriteCursor;

		private uint _sequence;

		private Mesh? _mesh;

		private Material? _material;

		private Il2CppStructArray<Vector3>? _vertices;

		private Il2CppStructArray<Color>? _colors;

		private Il2CppStructArray<Vector2>? _uvs;

		private Il2CppStructArray<int>? _indices;

		private int _uploadCapacity;

		private int _shaderAttempts;

		private float _nextShaderAttempt;

		private bool _disposed;

		internal bool Ready
		{
			get
			{
				if (!_disposed && (Object)(object)_mesh != (Object)null)
				{
					return (Object)(object)_material != (Object)null;
				}
				return false;
			}
		}

		internal int DotCount => _dotCount;

		internal string VisionStatus => _blackout.Status;

		internal string PlayerStatus => _players.Status;

		internal EchoRenderer(Settings settings, Action<string> warning)
		{
			_settings = settings;
			_warning = warning;
			_blackout = new BlackoutView(warning);
			_players = new PlayerEchoes(settings, warning);
		}

		internal void ObserveSound(EchoEvent echo, Transform? localRoot)
		{
			if (_disposed)
			{
				return;
			}
			try
			{
				_players.ObserveSound(echo, localRoot);
			}
			catch (Exception ex)
			{
				_warning("Player sound ownership: " + ex.Message);
			}
		}

		internal bool Enqueue(EchoEvent echo)
		{
			return Enqueue(echo, clap: false);
		}

		internal bool EnqueueClap(Vector3 origin, float now)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return Enqueue(new EchoEvent(origin, 1f, EchoKind.Manual, now), clap: true);
		}

		private bool Enqueue(EchoEvent echo, bool clap)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_037b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0380: Unknown result type (might be due to invalid IL or missing references)
			if (_disposed || !Finite(echo.Origin) || !float.IsFinite(echo.Time) || !float.IsFinite(echo.Strength) || echo.Strength <= 0f)
			{
				return false;
			}
			PrunePulses(echo.Time);
			bool flag = _settings.OptimizeAutomaticEchoes && !clap && echo.Kind != EchoKind.Manual;
			SoundEmitterSurface soundEmitterSurface = ((echo.Kind == EchoKind.World) ? echo.SurfaceEmission : null);
			if (soundEmitterSurface != null && soundEmitterSurface.Count == 0)
			{
				soundEmitterSurface = null;
			}
			Collider owner = ((soundEmitterSurface != null && soundEmitterSurface.Count > 0) ? soundEmitterSurface.GetCollider(0) : null);
			if (flag && HasRecentNearbySound(echo.Origin, echo.Time, owner))
			{
				return false;
			}
			if (clap && ClapPulseIndex() >= 0)
			{
				return false;
			}
			if (_pulseCount >= 4)
			{
				bool flag2 = soundEmitterSurface != null && soundEmitterSurface.Count > 0;
				if (!clap && !flag2)
				{
					return false;
				}
				int num = -1;
				for (int i = 0; i < _pulseCount; i++)
				{
					if ((clap || (!_pulses[i].Clap && _pulses[i].Kind != EchoKind.Manual && _pulses[i].SurfaceEmission == null)) && (num < 0 || _pulses[i].Time < _pulses[num].Time))
					{
						num = i;
					}
				}
				if (num < 0)
				{
					return false;
				}
				RemovePulse(num);
			}
			float num2 = Mathf.Clamp01(echo.Strength);
			int num3 = Mathf.Clamp(_settings.RayCount, 32, 16384);
			int num4 = (clap ? Math.Min(Math.Clamp(num3 * 3, 256, 32768), Math.Clamp(_settings.RaysPerFrame, 8, 4096) * 45) : Mathf.Max(16, Mathf.RoundToInt((float)num3 * Mathf.Lerp(0.55f, 1f, num2))));
			if (flag)
			{
				num4 = Math.Min(num4, 512);
			}
			int j = 0;
			if (clap)
			{
				for (; 1 << j < num4; j++)
				{
				}
			}
			_pulses[_pulseCount++] = new Pulse
			{
				Origin = echo.Origin,
				Strength = num2,
				Time = echo.Time,
				Kind = echo.Kind,
				SurfaceEmission = soundEmitterSurface,
				SurfaceRays = ((soundEmitterSurface != null && soundEmitterSurface.Count > 0) ? Math.Min(num4 / 4, 128) : 0),
				Range = Mathf.Clamp(_settings.Range, 2f, 200f) * Mathf.Lerp(0.4f, 1f, num2),
				Total = num4,
				Phase = (float)(++_sequence % 65536) * 2.3999631f,
				Clap = clap,
				Automatic = flag,
				Deduplicate = _settings.OptimizeAutomaticEchoes,
				ShuffleBits = j
			};
			if (flag)
			{
				_recentSounds[_recentSoundCursor++ % _recentSounds.Length] = new RecentSound
				{
					Origin = echo.Origin,
					Time = echo.Time,
					Owner = owner,
					Valid = true
				};
			}
			if (_recentSoundCursor >= _recentSounds.Length)
			{
				_recentSoundCursor = 0;
			}
			return true;
		}

		internal void Tick(float now, Transform head, Transform? rigRoot)
		{
			//IL_0119: 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_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			if (_disposed || (Object)(object)head == (Object)null || !float.IsFinite(now))
			{
				return;
			}
			_players.PrepareTargets(now, rigRoot);
			EnsureAssets(now);
			PrunePulses(now);
			if (!Ready)
			{
				return;
			}
			_blackout.Tick(now, _settings.Blackout);
			_material.renderQueue = (_blackout.Active ? 4999 : 3001);
			EnsureCapacity(Mathf.Clamp(_settings.MaxDots, 64, 65536));
			ExpireDots(now);
			_colliderFlags.Clear();
			int val = Mathf.Clamp(_settings.RaysPerFrame, 8, 4096);
			int num = Math.Min(val, 256);
			while (val-- > 0 && _pulseCount > 0)
			{
				int num2 = NextPulseIndex(num > 0);
				if (num2 < 0)
				{
					break;
				}
				_pulseCursor = num2;
				ref Pulse reference = ref _pulses[_pulseCursor];
				if (reference.Automatic)
				{
					num--;
				}
				if (reference.Next == 0 && !reference.Clap)
				{
					reference.Rotation = head.rotation;
					reference.ListenerOrigin = head.position;
				}
				CastReturn(ref reference, now, rigRoot);
				if (reference.Next >= reference.Total)
				{
					RemovePulse(_pulseCursor);
				}
				else
				{
					_pulseCursor++;
				}
			}
			Draw(now, head);
			try
			{
				_players.Tick(now, head, rigRoot);
			}
			catch (Exception ex)
			{
				_warning("Player visibility: " + ex.Message);
			}
		}

		internal void Reset()
		{
			_blackout.Reset();
			_players.Reset();
			_dotCount = 0;
			_pulseCount = 0;
			_pulseCursor = 0;
			_overwriteCursor = 0;
			_surfaceCells.Clear();
			_colliderFlags.Clear();
			Array.Clear(_recentSounds, 0, _recentSounds.Length);
			_recentSoundCursor = 0;
			_shaderAttempts = 0;
			_nextShaderAttempt = 0f;
			if ((Object)(object)_mesh != (Object)null)
			{
				_mesh.Clear();
			}
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
				_blackout.Dispose();
				_players.Dispose();
				_pulseCount = 0;
				_dotCount = 0;
				_surfaceCells.Clear();
				_colliderFlags.Clear();
				DestroyAssets();
				_dots = Array.Empty<Dot>();
				_vertices = null;
				_colors = null;
				_uvs = null;
				_indices = null;
				_uploadCapacity = 0;
			}
		}

		private void CastReturn(ref Pulse pulse, float now, Transform? rigRoot)
		{
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: 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_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			int num = pulse.Next++;
			if (num % 4 == 0 && num / 4 < pulse.SurfaceRays && TryEmitterRay(pulse, num / 4, rigRoot, out var direction, out var range))
			{
				if (FindReturn(pulse.ListenerOrigin, direction, range, rigRoot, null, out var selected) && pulse.SurfaceEmission.IsOwnCollider(((RaycastHit)(ref selected)).collider))
				{
					AddReturn(ref pulse, selected, ((RaycastHit)(ref selected)).distance, now);
				}
				return;
			}
			float num2 = ((float)num + 0.5f) / (float)pulse.Total;
			float num3 = (float)num * 2.3999631f + pulse.Phase;
			Vector3 direction2 = default(Vector3);
			if (pulse.Clap)
			{
				int num4;
				do
				{
					num4 = ReverseLowBits(pulse.ShuffleCursor++, pulse.ShuffleBits);
				}
				while (num4 >= pulse.Total);
				float num5 = 1f - 2f * (((float)num4 + 0.5f) / (float)pulse.Total);
				float num6 = Mathf.Sqrt(Mathf.Max(0f, 1f - num5 * num5));
				float num7 = (float)num4 * 2.3999631f + pulse.Phase;
				((Vector3)(ref direction2))..ctor(num6 * Mathf.Cos(num7), num5, num6 * Mathf.Sin(num7));
			}
			else if (num % 3 == 0)
			{
				float num8 = Mathf.Lerp(1f, 0.25f, num2);
				float num9 = Mathf.Sqrt(Mathf.Max(0f, 1f - num8 * num8));
				direction2 = pulse.Rotation * new Vector3(num9 * Mathf.Cos(num3), num9 * Mathf.Sin(num3), num8);
			}
			else
			{
				float num10 = 1f - 2f * num2;
				float num11 = Mathf.Sqrt(Mathf.Max(0f, 1f - num10 * num10));
				((Vector3)(ref direction2))..ctor(num11 * Mathf.Cos(num3), num10, num11 * Mathf.Sin(num3));
			}
			if (FindReturn(pulse.Origin, direction2, pulse.Range, rigRoot, pulse.SurfaceEmission, out var selected2))
			{
				AddReturn(ref pulse, selected2, ((RaycastHit)(ref selected2)).distance, now);
			}
		}

		private bool TryEmitterRay(Pulse pulse, int sample, Transform? rigRoot, out Vector3 direction, out float range)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: 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_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: 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_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: 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)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			direction = default(Vector3);
			range = 0f;
			SoundEmitterSurface? surfaceEmission = pulse.SurfaceEmission;
			int count = surfaceEmission.Count;
			int index = ((count > pulse.SurfaceRays) ? (sample * count / pulse.SurfaceRays) : (sample % count));
			Collider collider = surfaceEmission.GetCollider(index);
			if ((Object)(object)collider == (Object)null)
			{
				return false;
			}
			ColliderFlags colliderFlags = GetColliderFlags(collider, rigRoot);
			if (colliderFlags.Local || colliderFlags.Player)
			{
				return false;
			}
			Bounds bounds = collider.bounds;
			Vector3 center = ((Bounds)(ref bounds)).center;
			Vector3 extents = ((Bounds)(ref bounds)).extents;
			if (!Finite(center) || !Finite(extents) || ((Vector3)(ref extents)).sqrMagnitude < 1E-06f || ((Bounds)(ref bounds)).Contains(pulse.ListenerOrigin) || !Finite(pulse.ListenerOrigin) || Vector3.Distance(pulse.ListenerOrigin, pulse.Origin) > _settings.WorldRange)
			{
				return false;
			}
			Vector3 val = pulse.ListenerOrigin - center;
			int num = sample / count;
			float num2 = ((num == 0) ? 0f : (0.85f * Mathf.Sqrt((float)num * 0.618034f % 1f)));
			float num3 = (float)num * 2.3999631f + pulse.Phase;
			float num4 = num2 * Mathf.Cos(num3);
			float num5 = num2 * Mathf.Sin(num3);
			float num6 = Mathf.Abs(val.x);
			float num7 = Mathf.Abs(val.y);
			float num8 = Mathf.Abs(val.z);
			Vector3 val2 = ((num6 > num7 && num6 > num8) ? new Vector3((val.x >= 0f) ? extents.x : (0f - extents.x), num4 * extents.y, num5 * extents.z) : ((num7 > num8) ? new Vector3(num4 * extents.x, (val.y >= 0f) ? extents.y : (0f - extents.y), num5 * extents.z) : new Vector3(num4 * extents.x, num5 * extents.y, (val.z >= 0f) ? extents.z : (0f - extents.z))));
			Vector3 val3 = center + val2 - pulse.ListenerOrigin;
			float magnitude = ((Vector3)(ref val3)).magnitude;
			if (!float.IsFinite(magnitude) || magnitude < 0.001f)
			{
				return false;
			}
			direction = val3 / magnitude;
			range = Math.Min(magnitude + ((Vector3)(ref extents)).magnitude * 2f + 0.02f, _settings.WorldRange + ((Vector3)(ref extents)).magnitude * 2f);
			if (float.IsFinite(range))
			{
				return range > 0f;
			}
			return false;
		}

		private bool FindReturn(Vector3 origin, Vector3 direction, float range, Transform? rigRoot, SoundEmitterSurface? ignoredOwner, out RaycastHit selected)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			selected = default(RaycastHit);
			int num = Physics.RaycastNonAlloc(origin, direction, _hits, range, -5, (QueryTriggerInteraction)1);
			if (num == 0 || num >= ((Il2CppArrayBase<RaycastHit>)(object)_hits).Length)
			{
				return false;
			}
			float num2 = float.PositiveInfinity;
			bool flag = false;
			for (int i = 0; i < num; i++)
			{
				RaycastHit val = ((Il2CppArrayBase<RaycastHit>)(object)_hits)[i];
				Collider collider = ((RaycastHit)(ref val)).collider;
				if (!((Object)(object)collider == (Object)null) && !(((RaycastHit)(ref val)).distance >= num2))
				{
					ColliderFlags colliderFlags = GetColliderFlags(collider, rigRoot);
					if (!colliderFlags.Local && (colliderFlags.Player || ignoredOwner == null || !ignoredOwner.IsOwnCollider(collider)))
					{
						num2 = ((RaycastHit)(ref val)).distance;
						selected = val;
						flag = true;
					}
				}
			}
			if (!flag)
			{
				return false;
			}
			if (GetColliderFlags(((RaycastHit)(ref selected)).collider, rigRoot).Player)
			{
				return false;
			}
			return true;
		}

		private void AddReturn(ref Pulse pulse, RaycastHit selected, float nearest, float now)
		{
			//IL_007b: 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_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			float num = (pulse.Clap ? now : pulse.Time) + nearest / 45f;
			float num2 = Mathf.Clamp(_settings.DotLifetime, 0.15f, 8f);
			if (pulse.Clap)
			{
				num2 = Mathf.Max(2.5f, num2);
			}
			if (pulse.Automatic)
			{
				num2 = Math.Min(num2, 0.85f);
			}
			if (now >= num + num2)
			{
				return;
			}
			SurfaceCell surfaceCell = default(SurfaceCell);
			if (pulse.Deduplicate)
			{
				if (!Finite(((RaycastHit)(ref selected)).point) || !Finite(((RaycastHit)(ref selected)).normal))
				{
					return;
				}
				surfaceCell = new SurfaceCell(((Object)((RaycastHit)(ref selected)).collider).GetInstanceID(), ((RaycastHit)(ref selected)).point, ((RaycastHit)(ref selected)).normal, pulse.Automatic ? 0.09f : 0.04f, !pulse.Automatic);
				if (_surfaceCells.TryGetValue(surfaceCell, out var value))
				{
					ref Dot reference = ref _dots[value];
					if (!reference.Clap || pulse.Clap)
					{
						reference.Birth = ((reference.Birth <= now) ? Math.Min(num, now) : Math.Min(reference.Birth, num));
						reference.Lifetime = num2;
						reference.Strength = Math.Max(reference.Strength, ReturnStrength(pulse, nearest));
						reference.Color = (Color)(pulse.Clap ? new Color(0.65f, 0.98f, 1f, 1f) : ColorFor(pulse.Kind));
						reference.Clap = pulse.Clap;
					}
					return;
				}
			}
			int num3;
			if (_dotCount < _dots.Length)
			{
				num3 = _dotCount++;
			}
			else
			{
				num3 = _overwriteCursor++ % _dots.Length;
				if (_overwriteCursor >= _dots.Length)
				{
					_overwriteCursor = 0;
				}
				RemoveCell(num3);
			}
			_dots[num3] = new Dot
			{
				Position = ((RaycastHit)(ref selected)).point + ((RaycastHit)(ref selected)).normal * 0.007f,
				Birth = num,
				FadeInStart = num,
				Lifetime = num2,
				Strength = ReturnStrength(pulse, nearest),
				Color = (Color)(pulse.Clap ? new Color(0.65f, 0.98f, 1f, 1f) : ColorFor(pulse.Kind)),
				Indexed = pulse.Deduplicate,
				Cell = surfaceCell,
				Clap = pulse.Clap
			};
			if (pulse.Deduplicate)
			{
				_surfaceCells[surfaceCell] = num3;
			}
		}

		private static float ReturnStrength(Pulse pulse, float distance)
		{
			return pulse.Strength * Mathf.Lerp(1f, pulse.Clap ? 0.7f : 0.35f, distance / pulse.Range);
		}

		private ColliderFlags GetColliderFlags(Collider collider, Transform? rigRoot)
		{
			int instanceID = ((Object)collider).GetInstanceID();
			if (_colliderFlags.TryGetValue(instanceID, out var value) && (Object)(object)value.Collider == (Object)(object)collider)
			{
				return value;
			}
			Transform transform = ((Component)collider).transform;
			bool flag = (Object)(object)rigRoot != (Object)null && ((Object)(object)transform == (Object)(object)rigRoot || transform.IsChildOf(rigRoot));
			value = new ColliderFlags
			{
				Collider = collider,
				Local = flag,
				Player = (!flag && _players.IsPlayerTransform(transform))
			};
			if (_colliderFlags.Count < 512)
			{
				_colliderFlags[instanceID] = value;
			}
			return value;
		}

		private bool HasRecentNearbySound(Vector3 origin, float now, Collider? owner)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			RecentSound[] recentSounds = _recentSounds;
			for (int i = 0; i < recentSounds.Length; i++)
			{
				RecentSound recentSound = recentSounds[i];
				if (recentSound.Valid && (Object)(object)recentSound.Owner == (Object)(object)owner && now >= recentSound.Time && now - recentSound.Time < 0.18f)
				{
					Vector3 val = origin - recentSound.Origin;
					if (((Vector3)(ref val)).sqrMagnitude < 0.5625f)
					{
						return true;
					}
				}
			}
			return false;
		}

		private int NextPulseIndex(bool allowAutomatic)
		{
			int num = ClapPulseIndex();
			if (num >= 0)
			{
				return num;
			}
			for (int i = 0; i < _pulseCount; i++)
			{
				int num2 = (_pulseCursor + i) % _pulseCount;
				if (allowAutomatic || !_pulses[num2].Automatic)
				{
					return num2;
				}
			}
			return -1;
		}

		private void Draw(float now, Transform head)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: 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_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: 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)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			float num2 = Mathf.Clamp(_settings.DotSize, 0.002f, 0.2f);
			Vector3 val = head.right * num2;
			Vector3 val2 = head.up * num2;
			for (int i = 0; i < _dotCount; i++)
			{
				ref Dot reference = ref _dots[i];
				float num3 = now - reference.Birth;
				if (num3 < 0f)
				{
					continue;
				}
				float num4 = Mathf.Clamp01(1f - num3 / reference.Lifetime);
				float num5 = reference.Strength * num4 * num4 * Mathf.Clamp01((now - reference.FadeInStart) / 0.025f);
				if (!(num5 < 0.006f))
				{
					int num6 = num++ * 5;
					Color color = reference.Color;
					color.a = num5;
					((Il2CppArrayBase<Vector3>)(object)_vertices)[num6] = reference.Position;
					((Il2CppArrayBase<Color>)(object)_colors)[num6] = color;
					color.a = 0f;
					((Il2CppArrayBase<Vector3>)(object)_vertices)[num6 + 1] = reference.Position + val2;
					((Il2CppArrayBase<Vector3>)(object)_vertices)[num6 + 2] = reference.Position + val;
					((Il2CppArrayBase<Vector3>)(object)_vertices)[num6 + 3] = reference.Position - val2;
					((Il2CppArrayBase<Vector3>)(object)_vertices)[num6 + 4] = reference.Position - val;
					for (int j = 1; j <= 4; j++)
					{
						((Il2CppArrayBase<Color>)(object)_colors)[num6 + j] = color;
					}
				}
			}
			if (num != 0)
			{
				int num7 = num * 5;
				_mesh.Clear();
				_mesh.indexFormat = (IndexFormat)(num7 > 65535);
				_mesh.SetVertices(_vertices, 0, num7);
				_mesh.SetColors(_colors, 0, num7);
				_mesh.SetUVs(0, _uvs, 0, num7);
				_mesh.SetTriangles(_indices, 0, num * 12, 0, true, 0);
				Graphics.DrawMesh(_mesh, Matrix4x4.identity, _material, 0, (Camera)null, 0, (MaterialPropertyBlock)null, (ShadowCastingMode)0, false, (Transform)null, (LightProbeUsage)0, (LightProbeProxyVolume)null);
			}
		}

		private void EnsureCapacity(int capacity)
		{
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			if (_dots.Length != capacity)
			{
				for (int i = capacity; i < _dotCount; i++)
				{
					RemoveCell(i);
				}
				Array.Resize(ref _dots, capacity);
				_dotCount = Math.Min(_dotCount, capacity);
				_overwriteCursor = 0;
			}
			if (_uploadCapacity >= capacity)
			{
				return;
			}
			_uploadCapacity = capacity;
			_vertices = new Il2CppStructArray<Vector3>((long)(capacity * 5));
			_colors = new Il2CppStructArray<Color>((long)(capacity * 5));
			_uvs = new Il2CppStructArray<Vector2>((long)(capacity * 5));
			_indices = new Il2CppStructArray<int>((long)(capacity * 12));
			for (int j = 0; j < capacity; j++)
			{
				int num = j * 5;
				for (int k = 0; k < 5; k++)
				{
					((Il2CppArrayBase<Vector2>)(object)_uvs)[num + k] = new Vector2(0.5f, 0.5f);
				}
				for (int l = 0; l < 4; l++)
				{
					int num2 = j * 12 + l * 3;
					((Il2CppArrayBase<int>)(object)_indices)[num2] = num;
					((Il2CppArrayBase<int>)(object)_indices)[num2 + 1] = num + l + 1;
					((Il2CppArrayBase<int>)(object)_indices)[num2 + 2] = num + (l + 1) % 4 + 1;
				}
			}
		}

		private void ExpireDots(float now)
		{
			int num = 0;
			while (num < _dotCount)
			{
				if (now >= _dots[num].Birth + _dots[num].Lifetime)
				{
					RemoveCell(num);
					_dots[num] = _dots[--_dotCount];
					if (num < _dotCount && _dots[num].Indexed)
					{
						_surfaceCells[_dots[num].Cell] = num;
					}
				}
				else
				{
					num++;
				}
			}
		}

		private void RemoveCell(int slot)
		{
			if (_dots[slot].Indexed)
			{
				_surfaceCells.Remove(_dots[slot].Cell);
			}
		}

		private void PrunePulses(float now)
		{
			for (int num = _pulseCount - 1; num >= 0; num--)
			{
				if (now - _pulses[num].Time > (_pulses[num].Clap ? 3f : 1.25f))
				{
					RemovePulse(num);
				}
			}
		}

		private int ClapPulseIndex()
		{
			for (int i = 0; i < _pulseCount; i++)
			{
				if (_pulses[i].Clap)
				{
					return i;
				}
			}
			return -1;
		}

		private static int ReverseLowBits(int value, int bits)
		{
			int num = 0;
			for (int i = 0; i < bits; i++)
			{
				num = (num << 1) | (value & 1);
				value >>= 1;
			}
			return num;
		}

		private void RemovePulse(int index)
		{
			_pulses[index] = _pulses[--_pulseCount];
			if (_pulseCursor >= _pulseCount)
			{
				_pulseCursor = 0;
			}
		}

		private void EnsureAssets(float now)
		{
			//IL_00e7: 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_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Expected O, but got Unknown
			//IL_0121: 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_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Expected O, but got Unknown
			if (Ready || now < _nextShaderAttempt || _shaderAttempts >= 6)
			{
				return;
			}
			_shaderAttempts++;
			_nextShaderAttempt = now + 3f;
			DestroyAssets();
			try
			{
				Shader val = null;
				string[] shaderNames = ShaderNames;
				for (int i = 0; i < shaderNames.Length; i++)
				{
					Shader val2 = Shader.Find(shaderNames[i]);
					if ((Object)(object)val2 != (Object)null && val2.isSupported)
					{
						val = val2;
						break;
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					if (_shaderAttempts == 1 || _shaderAttempts == 6)
					{
						_warning($"Echolocation: no supported depth-tested vertex-color shader (attempt {_shaderAttempts}/{6}).");
					}
					return;
				}
				_material = new Material(val)
				{
					hideFlags = (HideFlags)61,
					renderQueue = 3001
				};
				if (_material.HasProperty("_Color"))
				{
					_material.SetColor("_Color", Color.white);
				}
				if (_material.HasProperty("_MainTex"))
				{
					_material.SetTexture("_MainTex", (Texture)(object)Texture2D.whiteTexture);
				}
				SetIntIfPresent(_material, "_ZTest", 4);
				SetIntIfPresent(_material, "_ZWrite", 0);
				SetIntIfPresent(_material, "_Cull", 0);
				SetIntIfPresent(_material, "_SrcBlend", 5);
				SetIntIfPresent(_material, "_DstBlend", 10);
				_mesh = new Mesh
				{
					name = "Echolocation.SurfaceReturns",
					hideFlags = (HideFlags)61
				};
				_mesh.MarkDynamic();
			}
			catch (Exception ex)
			{
				DestroyAssets();
				if (_shaderAttempts == 1 || _shaderAttempts == 6)
				{
					_warning($"Echolocation render initialization failed ({_shaderAttempts}/{6}): {ex.Message}");
				}
			}
		}

		private void DestroyAssets()
		{
			if ((Object)(object)_mesh != (Object)null)
			{
				Object.Destroy((Object)(object)_mesh);
			}
			if ((Object)(object)_material != (Object)null)
			{
				Object.Destroy((Object)(object)_material);
			}
			_mesh = null;
			_material = null;
		}

		private static void SetIntIfPresent(Material material, string property, int value)
		{
			if (material.HasProperty(property))
			{
				material.SetInt(property, value);
			}
		}

		private static bool Finite(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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)
			if (float.IsFinite(value.x) && float.IsFinite(value.y))
			{
				return float.IsFinite(value.z);
			}
			return false;
		}

		private static Color ColorFor(EchoKind kind)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_0062: 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_0084: 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)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			return (Color)(kind switch
			{
				EchoKind.Footstep => new Color(0.25f, 1f, 0.42f, 1f), 
				EchoKind.World => new Color(1f, 0.55f, 0.16f, 1f), 
				EchoKind.Voice => new Color(0.78f, 0.42f, 1f, 1f), 
				_ => new Color(0.16f, 0.88f, 1f, 1f), 
			});
		}
	}
	internal sealed class EchoReveal
	{
		private struct Window
		{
			internal float Start;

			internal float Duration;

			internal float Strength;
		}

		private readonly Window[] _windows = new Window[4];

		private int _next;

		internal void Schedule(float arrival, float lifetime, float strength)
		{
			if (float.IsFinite(arrival) && float.IsFinite(lifetime) && !(lifetime <= 0f) && float.IsFinite(strength))
			{
				_windows[_next] = new Window
				{
					Start = arrival,
					Duration = Math.Clamp(lifetime, 0.1f, 5f),
					Strength = Math.Clamp(strength, 0f, 1f)
				};
				_next = (_next + 1) % _windows.Length;
			}
		}

		internal float Opacity(float now)
		{
			if (!float.IsFinite(now))
			{
				return 0f;
			}
			float num = 0f;
			Window[] windows = _windows;
			for (int i = 0; i < windows.Length; i++)
			{
				Window window = windows[i];
				float num2 = now - window.Start;
				if (!(window.Duration <= 0f) && !(num2 < 0f) && !(num2 >= window.Duration))
				{
					float num3 = 1f - num2 / window.Duration;
					num = Math.Max(num, window.Strength * num3 * num3);
				}
			}
			return num;
		}

		internal void Clear()
		{
			Array.Clear(_windows, 0, _windows.Length);
			_next = 0;
		}
	}
	internal sealed class FusionBridge
	{
		private readonly Settings _settings;

		private readonly Action<string> _warning;

		private readonly Dictionary<int, PulseGate> _voiceGates = new Dictionary<int, PulseGate>();

		private readonly HashSet<int> _seenSources = new HashSet<int>();

		private readonly List<int> _expiredSources = new List<int>();

		private readonly Il2CppStructArray<float> _outputSamples = new Il2CppStructArray<float>(128L);

		private Func<float>? _localAmplitude;

		private Func<bool>? _hasServer;

		private Func<bool>? _isMuted;

		private Func<bool>? _isDeafened;

		private Func<bool>? _canTalk;

		private Func<bool>? _hasVoiceActivity;

		private Func<byte>? _localID;

		private PropertyInfo? _manager;

		private PropertyInfo? _voices;

		private PropertyInfo? _sourceAmplitude;

		private PropertyInfo? _sourcePlaying;

		private PropertyInfo? _sourceID;

		private PropertyInfo? _sourceAudio;

		private float _nextDiscovery;

		private bool _failed;

		internal bool Present { get; private set; }

		internal string Status { get; private set; } = "Standalone microphone";

		internal FusionBridge(Settings settings, Action<string> warning)
		{
			_settings = settings;
			_warning = warning;
		}

		internal void Discover(float now)
		{
			if (Present || now < _nextDiscovery)
			{
				return;
			}
			_nextDiscovery = now + 1f;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (string.Equals(assembly.GetName().Name, "LabFusion", StringComparison.Ordinal))
				{
					Present = true;
					try
					{
						Type type = RequiredType(assembly, "LabFusion.Voice.VoiceInfo");
						Type type2 = RequiredType(assembly, "LabFusion.Voice.VoiceSourceManager");
						Type type3 = RequiredType(assembly, "LabFusion.Voice.VoiceSource");
						_localAmplitude = StaticGetter<float>(type, "VoiceAmplitude");
						_isMuted = StaticGetter<bool>(type, "IsMuted");
						_isDeafened = StaticGetter<bool>(type, "IsDeafened");
						_canTalk = StaticGetter<bool>(type, "CanTalk");
						_hasVoiceActivity = StaticGetter<bool>(type, "HasVoiceActivity");
						_hasServer = StaticGetter<bool>(RequiredType(assembly, "LabFusion.Network.NetworkInfo"), "HasServer");
						_localID = StaticGetter<byte>(RequiredType(assembly, "LabFusion.Player.PlayerIDManager"), "LocalSmallID");
						_manager = RequiredProperty(type, "VoiceManager");
						_voices = RequiredProperty(type2, "ActiveVoices");
						_sourceAmplitude = RequiredProperty(type3, "Amplitude");
						_sourcePlaying = RequiredProperty(type3, "Playing");
						_sourceID = RequiredProperty(type3, "ID");
						_sourceAudio = RequiredProperty(type3, "AudioSource");
						Status = "Fusion detected; join a session and enable voice chat for mic echoes";
						break;
					}
					catch (Exception error)
					{
						Fail(error);
						break;
					}
				}
			}
		}

		internal float ReadLocalLevel()
		{
			if (!Present || _failed)
			{
				return 0f;
			}
			try
			{
				Func<bool>? hasServer = _hasServer;
				if (hasServer == null || !hasServer() || _manager?.GetValue(null) == null)
				{
					Status = "Fusion: join a session and enable voice chat for mic echoes";
					return 0f;
				}
				Func<bool>? isMuted = _isMuted;
				if (isMuted == null || isMuted())
				{
					Status = "Fusion: microphone echoes follow voice mute/deafen settings";
					return 0f;
				}
				Status = "Fusion microphone meter (voice chat)";
				Func<bool>? canTalk = _canTalk;
				if (canTalk != null && canTalk())
				{
					Func<bool>? hasVoiceActivity = _hasVoiceActivity;
					if (hasVoiceActivity != null && hasVoiceActivity())
					{
						return Sanitize((_localAmplitude?.Invoke() ?? 0f) * 0.1f);
					}
				}
				return 0f;
			}
			catch (Exception error)
			{
				Fail(error);
				return 0f;
			}
		}

		internal void TickRemote(float now, Vector3 head, Action<EchoEvent> emit, Action<EchoEvent>? observeSound = null)
		{
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
			if (!Present || _failed || !_settings.FusionVoiceEnabled)
			{
				_voiceGates.Clear();
				return;
			}
			try
			{
				Func<bool>? hasServer = _hasServer;
				if (hasServer != null && hasServer())
				{
					Func<bool>? isDeafened = _isDeafened;
					if (isDeafened != null && !isDeafened() && _voices?.GetValue(null) is IList list)
					{
						_seenSources.Clear();
						int num = _localID?.Invoke() ?? 0;
						float num2 = Mathf.Clamp(_settings.WorldRange, 1f, 200f);
						for (int i = 0; i < Math.Min(list.Count, 128); i++)
						{
							object obj = list[i];
							Component val = (Component)((obj is Component) ? obj : null);
							if (val == null || !Object.op_Implicit((Object)(object)val))
							{
								continue;
							}
							int instanceID = ((Object)val).GetInstanceID();
							_seenSources.Add(instanceID);
							int num3 = Convert.ToInt32(_sourceID.GetValue(obj));
							if (num3 < 0 || num3 > 255 || num3 == num)
							{
								continue;
							}
							object value = _sourcePlaying.GetValue(obj);
							if (!(value is bool) || !(bool)value)
							{
								continue;
							}
							object? value2 = _sourceAudio.GetValue(obj);
							AudioSource val2 = (AudioSource)((value2 is AudioSource) ? value2 : null);
							if (val2 == null || !Object.op_Implicit((Object)(object)val2) || val2.mute || val2.volume <= 0f || !val2.isPlaying || val2.isVirtual || (AudioListener.pause && !val2.ignoreListenerPause))
							{
								continue;
							}
							Vector3 position = val.transform.position;
							if (!(Vector3.Distance(head, position) > num2))
							{
								float num4 = Sanitize(Convert.ToSingle(_sourceAmplitude.GetValue(obj)) * 0.1f * Mathf.Clamp01(val2.volume));
								val2.GetOutputData(_outputSamples, 0);
								double num5 = 0.0;
								for (int j = 0; j < ((Il2CppArrayBase<float>)(object)_outputSamples).Length; j++)
								{
									num5 += (double)(((Il2CppArrayBase<float>)(object)_outputSamples)[j] * ((Il2CppArrayBase<float>)(object)_outputSamples)[j]);
								}
								float num6 = (val2.ignoreListenerVolume ? 1f : Mathf.Clamp01(AudioListener.volume));
								float num7 = Sanitize((float)Math.Sqrt(num5 / (double)((Il2CppArrayBase<float>)(object)_outputSamples).Length) * val2.volume * num6);
								num4 = Mathf.Min(num4, num7);
								float strength = Mathf.Clamp01(num4 / Mathf.Max(0.01f, _settings.MicThreshold * 4f));
								EchoEvent obj2 = new EchoEvent(position, strength, EchoKind.Voice, now, num3, val2);
								if (num4 >= Mathf.Clamp(_settings.MicThreshold, 0.0001f, 1f))
								{
									observeSound?.Invoke(obj2);
								}
								if (!_voiceGates.TryGetValue(instanceID, out PulseGate value3))
								{
									value3 = new PulseGate();
									_voiceGates.Add(instanceID, value3);
								}
								if (value3.TryFire(now, num4, _settings.MicThreshold, _settings.MicCooldown))
								{
									emit(obj2);
								}
							}
						}
						_expiredSources.Clear();
						foreach (int key in _voiceGates.Keys)
						{
							if (!_seenSources.Contains(key))
							{
								_expiredSources.Add(key);
							}
						}
						{
							foreach (int expiredSource in _expiredSources)
							{
								_voiceGates.Remove(expiredSource);
							}
							return;
						}
					}
				}
				_voiceGates.Clear();
			}
			catch (Exception error)
			{
				Fail(error);
			}
		}

		internal void Reset()
		{
			_voiceGates.Clear();
			_seenSources.Clear();
			_expiredSources.Clear();
			if (_failed)
			{
				Present = false;
				_failed = false;
				_nextDiscovery = 0f;
			}
		}

		private void Fail(Exception error)
		{
			_failed = true;
			Status = "Fusion voice API unavailable; microphone left to Fusion";
			_warning("Fusion voice integration disabled: " + error.GetBaseException().Message);
		}

		private static Type RequiredType(Assembly assembly, string name)
		{
			return assembly.GetType(name, throwOnError: true);
		}

		private static PropertyInfo RequiredProperty(Type type, string name)
		{
			return type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public) ?? throw new MissingMemberException(type.FullName, name);
		}

		private static Func<T> StaticGetter<T>(Type type, string name)
		{
			return RequiredProperty(type, name).GetMethod.CreateDelegate<Func<T>>();
		}

		private static float Sanitize(float level)
		{
			if (!float.IsFinite(level))
			{
				return 0f;
			}
			return Mathf.Clamp01(level);
		}
	}
	internal sealed class LocomotionInput
	{
		private readonly Settings _settings;

		private readonly Il2CppStructArray<RaycastHit> _groundHits = new Il2CppStructArray<RaycastHit>(16L);

		private bool _initialized;

		private bool _wasGrounded;

		private Vector3 _lastPosition;

		private float _lastTime;

		private float _nextSample;

		private float _distance;

		private float _lastStep = float.NegativeInfinity;

		private float _airborneSince;

		private float _fallSpeed;

		private Transform? _lastSurface;

		private Vector3 _lastLocalPosition;

		private int _lastTracker;

		public string Status { get; private set; } = "Waiting for player";

		public LocomotionInput(Settings settings)
		{
			_settings = settings;
		}

		public void Tick(float now, Transform head, Transform? rigRoot, Action<EchoEvent> emit)
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: 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)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: 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_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0439: Unknown result type (might be due to invalid IL or missing references)
			//IL_043b: Unknown result type (might be due to invalid IL or missing references)
			//IL_044b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03df: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
			if (!_settings.FootstepsEnabled || (Object)(object)head == (Object)null)
			{
				Reset();
				Status = (_settings.FootstepsEnabled ? "Waiting for player" : "Footsteps disabled");
			}
			else
			{
				if (now < _nextSample)
				{
					return;
				}
				_nextSample = now + 0.05f;
				try
				{
					PhysicsRig physicsRig = Player.PhysicsRig;
					Transform val = (Transform)(((Object)(object)rigRoot != (Object)null) ? ((object)rigRoot) : ((object)head));
					float num = 0.2f;
					GameObject val2 = (((Object)(object)physicsRig != (Object)null) ? physicsRig.feet : null);
					bool flag = (Object)(object)val2 != (Object)null;
					if (flag)
					{
						val = val2.transform;
						num = Mathf.Clamp(physicsRig.footballRadius, 0.05f, 1.5f);
					}
					Vector3 position = val.position;
					Vector3 origin = position + Vector3.up * 0.15f;
					float length = num + 0.4f;
					if (!flag && (Object)(object)rigRoot == (Object)null)
					{
						origin = head.position;
						length = 1.9f;
					}
					Vector3 contact;
					Transform surface;
					bool flag2 = TryGround(origin, length, rigRoot, out contact, out surface);
					bool flag3 = ((!flag) ? flag2 : (physicsRig.footSupported > 0.05f && !physicsRig.ungroundedThisFrame && physicsRig.onLadder == 0));
					if (!flag2)
					{
						contact = position - Vector3.up * num;
						surface = null;
					}
					int instanceID = ((Object)val).GetInstanceID();
					float num2 = now - _lastTime;
					Vector3 val3 = position - _lastPosition;
					if (!_initialized || instanceID != _lastTracker || num2 <= 0f || num2 > 0.5f || ((Vector3)(ref val3)).magnitude > Mathf.Max(1.5f, num2 * 14f))
					{
						BeginSample(now, position, instanceID, flag3, surface);
						return;
					}
					Vector3 val4 = val3;
					if ((Object)(object)surface != (Object)null && (Object)(object)_lastSurface != (Object)null && (Object)(object)surface == (Object)(object)_lastSurface)
					{
						val4 = position - surface.TransformPoint(_lastLocalPosition);
					}
					else if (flag)
					{
						val4 -= physicsRig.groundVelocity * num2;
					}
					float num3 = val3.y / num2;
					if (!flag3)
					{
						if (_wasGrounded)
						{
							_airborneSince = now;
							_fallSpeed = 0f;
						}
						_fallSpeed = Mathf.Max(_fallSpeed, 0f - num3);
						_distance = 0f;
						Status = "Airborne or climbing";
					}
					else
					{
						if (!_wasGrounded)
						{
							if (now - _airborneSince > 0.15f && _fallSpeed > 0.8f && now - _lastStep > 0.2f)
							{
								float strength = Mathf.Lerp(0.4f, 1f, Mathf.Clamp01((_fallSpeed - 0.8f) / 6f));
								emit(new EchoEvent(contact + Vector3.up * 0.05f, strength, EchoKind.Footstep, now));
								_lastStep = now;
							}
							_fallSpeed = 0f;
							_distance = 0f;
						}
						val4.y = 0f;
						float magnitude = ((Vector3)(ref val4)).magnitude;
						float num4 = magnitude / num2;
						Status = "Tracking grounded footsteps";
						if (_wasGrounded && num4 >= 0.18f && num4 < 12f)
						{
							_distance += magnitude;
							float num5 = Mathf.Clamp(_settings.StepDistance, 0.25f, 2f);
							if (_distance >= num5 && now - _lastStep >= 0.18f)
							{
								_distance %= num5;
								_lastStep = now;
								float strength2 = Mathf.Lerp(0.3f, 0.85f, Mathf.Clamp01(num4 / 6f));
								emit(new EchoEvent(contact + Vector3.up * 0.05f, strength2, EchoKind.Footstep, now));
							}
						}
						else if (num4 < 0.18f)
						{
							_distance = Mathf.Max(0f, _distance - num2 * 0.6f);
						}
					}
					_lastTime = now;
					_lastPosition = position;
					_wasGrounded = flag3;
					RememberSurface(surface, position);
				}
				catch (Exception ex)
				{
					Reset();
					_nextSample = now + 0.5f;
					Status = "Player physics unavailable: " + ex.GetType().Name;
				}
			}
		}

		private bool TryGround(Vector3 origin, float length, Transform? rigRoot, out Vector3 contact, out Transform? surface)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: 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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			contact = origin;
			surface = null;
			int num = Physics.RaycastNonAlloc(origin, Vector3.down, _groundHits, length, -5, (QueryTriggerInteraction)1);
			float num2 = float.PositiveInfinity;
			for (int i = 0; i < num; i++)
			{
				RaycastHit val = ((Il2CppArrayBase<RaycastHit>)(object)_groundHits)[i];
				if (!((Object)(object)((RaycastHit)(ref val)).collider == (Object)null) && !(((RaycastHit)(ref val)).distance >= num2) && !(((RaycastHit)(ref val)).normal.y < 0.5f))
				{
					Transform transform = ((Component)((RaycastHit)(ref val)).collider).transform;
					if (!((Object)(object)rigRoot != (Object)null) || (!((Object)(object)transform == (Object)(object)rigRoot) && !transform.IsChildOf(rigRoot)))
					{
						contact = ((RaycastHit)(ref val)).point;
						surface = transform;
						num2 = ((RaycastHit)(ref val)).distance;
					}
				}
			}
			return (Object)(object)surface != (Object)null;
		}

		private void BeginSample(float now, Vector3 position, int trackerId, bool grounded, Transform? surface)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			_initialized = true;
			_lastPosition = position;
			_lastTracker = trackerId;
			_lastTime = now;
			_wasGrounded = grounded;
			_distance = 0f;
			_fallSpeed = 0f;
			_airborneSince = now;
			RememberSurface(surface, position);
		}

		private void RememberSurface(Transform? surface, Vector3 position)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			_lastSurface = surface;
			if ((Object)(object)surface != (Object)null)
			{
				_lastLocalPosition = surface.InverseTransformPoint(position);
			}
		}

		public void Reset()
		{
			_initialized = false;
			_wasGrounded = false;
			_lastSurface = null;
			_distance = 0f;
			_fallSpeed = 0f;
			_lastStep = float.NegativeInfinity;
			_nextSample = 0f;
			Status = "Waiting for player";
		}
	}
	internal sealed class MenuPage
	{
		private readonly Settings _settings;

		private readonly EcholocationMod _mod;

		private readonly Action<string> _warning;

		private Page? _page;

		private int _section;

		private float _nextTry;

		private static readonly Color Accent = new Color(0.2f, 0.95f, 1f);

		public MenuPage(Settings settings, EcholocationMod mod, Action<string> warning)
		{
			_settings = settings;
			_mod = mod;
			_warning = warning;
		}

		public void EnsureBuilt()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			if (_section >= 4 || Time.unscaledTime < _nextTry)
			{
				return;
			}
			_nextTry = Time.unscaledTime + 2f;
			try
			{
				if (Page.Root == null)
				{
					return;
				}
				if (_page == null)
				{
					_page = Page.Root.CreatePage("Echolocation", Accent, 0, true);
				}
				while (_section < 4)
				{
					switch (_section++)
					{
					case 0:
						Main(_page);
						break;
					case 1:
						Input(_page);
						break;
					case 2:
						Visuals(_page);
						break;
					case 3:
						Advanced(_page);
						break;
					}
				}
			}
			catch (Exception ex)
			{
				_warning("BoneMenu: " + ex.Message);
			}
		}

		private void Main(Page page)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			page.CreateBool("Enabled", Accent, _settings.Enabled, (Action<bool>)_mod.SetEnabled);
			page.CreateBool("Blackout (Echolocation Only)", Accent, _settings.Blackout, (Action<bool>)_mod.SetBlackout);
			page.CreateBool("Double Clap: 360 Pulse", Accent, _settings.DoubleClapEnabled, (Action<bool>)_mod.SetDoubleClap);
			page.CreateBool("Clap and Pulse Sounds", Accent, _settings.PulseSoundEnabled, (Action<bool>)_mod.SetPulseSound);
			page.CreateFloat("Pulse Sound Volume", Accent, _settings.PulseSoundVolume, 0.05f, 0f, 1f, (Action<float>)delegate(float value)
			{
				_settings.PulseSoundVolume = value;
				_mod.Save();
			});
			page.CreateFloat("Clap Distance (Larger = Easier)", Accent, _settings.ClapContactDistance, 0.025f, 0.15f, 0.45f, (Action<float>)_mod.SetClapDistance);
			page.CreateFunction("Test Pulse Sound", Accent, (Action)_mod.TestPulseSound);
			page.CreateFunction("Send Pulse", Color.green, (Action)_mod.ManualPulse);
			page.CreateFunction("Print Input Levels to Console", Accent, (Action)_mod.Diagnostics);
		}

		private void Input(Page root)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown r