Decompiled source of splendiphius v2.2.9

CapsCrew.Splendiphius.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Dissonance;
using Dissonance.Audio.Capture;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using NAudio.Wave;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CapsCrew.Splendiphius")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("CapsCrew.Splendiphius")]
[assembly: AssemblyTitle("CapsCrew.Splendiphius")]
[assembly: AssemblyVersion("1.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 CapsCrew.KylesMom
{
	internal sealed class KyleMomVisual : MonoBehaviour
	{
		private const string PlaneName = "KylesMomPlane";

		private FlowermanAI? _bracken;

		private GameObject? _plane;

		private MeshRenderer? _planeRenderer;

		private bool _scanRenamed;

		internal static void Ensure(FlowermanAI bracken)
		{
			if (!((Object)(object)bracken == (Object)null) && Plugin.EnableSwap.Value && !((Object)(object)Plugin.FaceTexture == (Object)null))
			{
				KyleMomVisual kyleMomVisual = ((Component)bracken).GetComponent<KyleMomVisual>();
				if ((Object)(object)kyleMomVisual == (Object)null)
				{
					kyleMomVisual = ((Component)bracken).gameObject.AddComponent<KyleMomVisual>();
				}
				kyleMomVisual.Bind(bracken);
			}
		}

		private void Bind(FlowermanAI bracken)
		{
			_bracken = bracken;
			if ((Object)(object)_plane == (Object)null)
			{
				CreatePlane(bracken);
			}
			RenameScanOnce(bracken);
			ApplyVisibility();
		}

		private void LateUpdate()
		{
			if (!((Object)(object)_bracken == (Object)null) && !((Object)(object)_plane == (Object)null))
			{
				ApplyVisibility();
				FaceLocalPlayer();
			}
		}

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

		private void CreatePlane(FlowermanAI bracken)
		{
			//IL_00bd: 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_00e5: Unknown result type (might be due to invalid IL or missing references)
			Texture2D faceTexture = Plugin.FaceTexture;
			if (!((Object)(object)faceTexture == (Object)null))
			{
				GameObject val = GameObject.CreatePrimitive((PrimitiveType)5);
				((Object)val).name = "KylesMomPlane";
				val.layer = ((Component)bracken).gameObject.layer;
				Collider component = val.GetComponent<Collider>();
				if ((Object)(object)component != (Object)null)
				{
					Object.Destroy((Object)(object)component);
				}
				float num = Mathf.Max(0.4f, Plugin.PlaneHeight.Value);
				float num2 = ((((Texture)faceTexture).height > 0) ? ((float)((Texture)faceTexture).width / (float)((Texture)faceTexture).height) : 0.63f);
				float num3 = num * num2;
				float num4 = num * 0.5f + Plugin.GroundOffset.Value;
				val.transform.SetParent(((Component)bracken).transform, false);
				val.transform.localPosition = new Vector3(0f, num4, 0f);
				val.transform.localRotation = Quaternion.identity;
				val.transform.localScale = new Vector3(num3, num, 1f);
				MeshRenderer component2 = val.GetComponent<MeshRenderer>();
				Material sharedMaterial = MakeMaterial(faceTexture);
				((Renderer)component2).sharedMaterial = sharedMaterial;
				((Renderer)component2).shadowCastingMode = (ShadowCastingMode)0;
				((Renderer)component2).receiveShadows = false;
				((Renderer)component2).lightProbeUsage = (LightProbeUsage)0;
				((Renderer)component2).reflectionProbeUsage = (ReflectionProbeUsage)0;
				_plane = val;
				_planeRenderer = component2;
				Plugin.Log.LogInfo((object)$"Pinned plane at local y={num4:0.00} size={num3:0.00}x{num:0.00}");
			}
		}

		private static Material MakeMaterial(Texture2D texture)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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_006d: Expected O, but got Unknown
			Material val = new Material(Shader.Find("Sprites/Default") ?? Shader.Find("Unlit/Transparent") ?? Shader.Find("Unlit/Texture") ?? Shader.Find("UI/Default") ?? Shader.Find("Standard"))
			{
				name = "KylesMomSprite",
				mainTexture = (Texture)(object)texture,
				color = Color.white,
				hideFlags = (HideFlags)61
			};
			if (val.HasProperty("_MainTex"))
			{
				val.SetTexture("_MainTex", (Texture)(object)texture);
			}
			if (val.HasProperty("_Cull"))
			{
				val.SetInt("_Cull", 0);
			}
			val.renderQueue = 3000;
			return val;
		}

		private void ApplyVisibility()
		{
			if (!((Object)(object)_bracken == (Object)null) && !((Object)(object)_planeRenderer == (Object)null))
			{
				bool flag = !((EnemyAI)_bracken).isEnemyDead && ((Component)_bracken).gameObject.activeInHierarchy;
				bool saw = false;
				bool anyEnabled = false;
				Renderer[] skinnedMeshRenderers = (Renderer[])(object)((EnemyAI)_bracken).skinnedMeshRenderers;
				NoteEnabled(skinnedMeshRenderers, ref saw, ref anyEnabled);
				skinnedMeshRenderers = (Renderer[])(object)((EnemyAI)_bracken).meshRenderers;
				NoteEnabled(skinnedMeshRenderers, ref saw, ref anyEnabled);
				if (saw)
				{
					flag = flag && anyEnabled;
				}
				if ((Object)(object)_bracken.animationContainer != (Object)null && !((Component)_bracken.animationContainer).gameObject.activeInHierarchy)
				{
					flag = false;
				}
				SuppressAllGameRenderers();
				((Renderer)_planeRenderer).enabled = flag;
			}
		}

		private static void NoteEnabled(Renderer[]? renderers, ref bool saw, ref bool anyEnabled)
		{
			if (renderers == null)
			{
				return;
			}
			foreach (Renderer val in renderers)
			{
				if (!((Object)(object)val == (Object)null))
				{
					saw = true;
					if (val.enabled)
					{
						anyEnabled = true;
					}
				}
			}
		}

		private void SuppressAllGameRenderers()
		{
			if ((Object)(object)_bracken == (Object)null)
			{
				return;
			}
			Renderer[] skinnedMeshRenderers = (Renderer[])(object)((EnemyAI)_bracken).skinnedMeshRenderers;
			HideArray(skinnedMeshRenderers);
			skinnedMeshRenderers = (Renderer[])(object)((EnemyAI)_bracken).meshRenderers;
			HideArray(skinnedMeshRenderers);
			Renderer[] componentsInChildren = ((Component)_bracken).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer renderer in componentsInChildren)
			{
				if (!IsOurRenderer(renderer))
				{
					HideRenderer(renderer);
				}
			}
		}

		private static void HideArray(Renderer[]? renderers)
		{
			if (renderers == null)
			{
				return;
			}
			for (int i = 0; i < renderers.Length; i++)
			{
				if ((Object)(object)renderers[i] != (Object)null)
				{
					HideRenderer(renderers[i]);
				}
			}
		}

		private bool IsOurRenderer(Renderer renderer)
		{
			if ((Object)(object)_plane == (Object)null || (Object)(object)renderer == (Object)null)
			{
				return false;
			}
			if (!((Object)(object)((Component)renderer).gameObject == (Object)(object)_plane))
			{
				return ((Component)renderer).transform.IsChildOf(_plane.transform);
			}
			return true;
		}

		private static void HideRenderer(Renderer renderer)
		{
			renderer.forceRenderingOff = true;
			renderer.shadowCastingMode = (ShadowCastingMode)0;
			renderer.receiveShadows = false;
		}

		private void FaceLocalPlayer()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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)
			if (!Plugin.FacePlayer.Value || (Object)(object)_plane == (Object)null)
			{
				return;
			}
			Camera val = LocalCamera();
			if (!((Object)(object)val == (Object)null))
			{
				Vector3 val2 = ((Component)val).transform.position - _plane.transform.position;
				val2.y = 0f;
				if (!(((Vector3)(ref val2)).sqrMagnitude < 0.0001f))
				{
					_plane.transform.rotation = Quaternion.LookRotation(((Vector3)(ref val2)).normalized, Vector3.up);
				}
			}
		}

		private static Camera? LocalCamera()
		{
			GameNetworkManager instance = GameNetworkManager.Instance;
			PlayerControllerB val = (((Object)(object)instance != (Object)null) ? instance.localPlayerController : null);
			if ((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null)
			{
				return val.gameplayCamera;
			}
			return Camera.main;
		}

		private void RenameScanOnce(FlowermanAI bracken)
		{
			if (_scanRenamed)
			{
				return;
			}
			string value = Plugin.ScanName.Value;
			if (string.IsNullOrWhiteSpace(value))
			{
				_scanRenamed = true;
				return;
			}
			ScanNodeProperties componentInChildren = ((Component)bracken).GetComponentInChildren<ScanNodeProperties>(true);
			if (!((Object)(object)componentInChildren == (Object)null))
			{
				componentInChildren.headerText = value.Trim();
				_scanRenamed = true;
			}
		}
	}
	[BepInPlugin("capscrew.splendiphius", "Splendiphius", "2.2.9")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "capscrew.splendiphius";

		public const string PluginName = "Splendiphius";

		public const string PluginVersion = "2.2.9";

		public const string ImageFileName = "content.png";

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		internal static Texture2D? FaceTexture;

		internal static ConfigEntry<bool> EnableSwap;

		internal static ConfigEntry<float> PlaneHeight;

		internal static ConfigEntry<float> GroundOffset;

		internal static ConfigEntry<bool> FacePlayer;

		internal static ConfigEntry<string> ScanName;

		internal static ConfigEntry<bool> ExtraLayer;

		internal static ConfigEntry<bool> ExtraLayerWeatherGate;

		internal static ConfigEntry<int> ExtraLayerAfterHour;

		internal static ConfigEntry<float> ExtraLayerChanceMin;

		internal static ConfigEntry<float> ExtraLayerChanceMax;

		internal static ConfigEntry<float> ExtraLayerIntervalMin;

		internal static ConfigEntry<float> ExtraLayerIntervalMax;

		internal static ConfigEntry<bool> ExtraLayerTestMode;

		internal static ConfigEntry<float> ExtraLayerDelay;

		internal static ConfigEntry<float> ExtraLayerPitch;

		internal static ConfigEntry<float> ExtraLayerVolume;

		private void Awake()
		{
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			EnableSwap = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableSwap", true, "On or off.");
			PlaneHeight = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PlaneHeight", 2.85f, "Height in meters.");
			GroundOffset = ((BaseUnityPlugin)this).Config.Bind<float>("General", "GroundOffset", 0.02f, "Lift off the root.");
			FacePlayer = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "FacePlayer", true, "Yaw toward the local player.");
			ScanName = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ScanName", "Kyle's Mom", "Scanner label. Empty keeps the original.");
			ExtraLayer = ((BaseUnityPlugin)this).Config.Bind<bool>("Layer", "Enable", true, "On or off.");
			ExtraLayerWeatherGate = ((BaseUnityPlugin)this).Config.Bind<bool>("Layer", "WeatherGate", true, "Only while the current world allows it.");
			ExtraLayerAfterHour = ((BaseUnityPlugin)this).Config.Bind<int>("Layer", "AfterHour", 12, "HUD clock hour (24h). 12 = noon. Ignored in test mode.");
			ExtraLayerChanceMin = ((BaseUnityPlugin)this).Config.Bind<float>("Layer", "ChanceMin", 0.1f, "Low end of the per-roll chance.");
			ExtraLayerChanceMax = ((BaseUnityPlugin)this).Config.Bind<float>("Layer", "ChanceMax", 0.2f, "High end of the per-roll chance.");
			ExtraLayerIntervalMin = ((BaseUnityPlugin)this).Config.Bind<float>("Layer", "IntervalMin", 60f, "Seconds between rolls, low end.");
			ExtraLayerIntervalMax = ((BaseUnityPlugin)this).Config.Bind<float>("Layer", "IntervalMax", 120f, "Seconds between rolls, high end.");
			ExtraLayerTestMode = ((BaseUnityPlugin)this).Config.Bind<bool>("Layer", "TestMode", false, "Skip fog/noon only. Same 60-120s rolls as live. Off in space. Also on if layer.test sits next to the DLL.");
			ExtraLayerDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Layer", "Delay", 0.72f, "Seconds.");
			ExtraLayerPitch = ((BaseUnityPlugin)this).Config.Bind<float>("Layer", "Pitch", 0.91f, "Playback pitch.");
			ExtraLayerVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Layer", "Volume", 0.82f, "Playback volume.");
			FaceTexture = LoadFaceTexture();
			if ((Object)(object)FaceTexture == (Object)null)
			{
				Log.LogError((object)"content.png failed to load.");
			}
			new Harmony("capscrew.splendiphius").PatchAll();
			Log.LogInfo((object)("Splendiphius v2.2.9 loaded from " + ((BaseUnityPlugin)this).Info.Location));
			VoiceBoxLog.Note("LOAD", string.Format("v{0} test={1} interval={2:0}-{3:0}s chance={4:0.00}-{5:0.00}", "2.2.9", IsTestMode(), ExtraLayerIntervalMin.Value, ExtraLayerIntervalMax.Value, ExtraLayerChanceMin.Value, ExtraLayerChanceMax.Value));
		}

		internal static bool IsTestMode()
		{
			if (ExtraLayerTestMode != null && ExtraLayerTestMode.Value)
			{
				return true;
			}
			try
			{
				string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)Instance).Info.Location);
				if (!string.IsNullOrWhiteSpace(directoryName) && File.Exists(Path.Combine(directoryName, "layer.test")))
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		internal static Texture2D? LoadFaceTexture()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)Instance).Info.Location);
			if (string.IsNullOrWhiteSpace(directoryName))
			{
				return null;
			}
			string text = Path.Combine(directoryName, "content.png");
			if (!File.Exists(text))
			{
				Log.LogError((object)("Missing image next to the plugin DLL: " + text));
				return null;
			}
			byte[] array = File.ReadAllBytes(text);
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (!ImageConversion.LoadImage(val, array))
			{
				Log.LogError((object)("Unity could not decode " + text));
				Object.Destroy((Object)(object)val);
				return null;
			}
			((Object)val).name = "content";
			((Texture)val).wrapMode = (TextureWrapMode)1;
			((Texture)val).filterMode = (FilterMode)0;
			((Texture)val).anisoLevel = 0;
			((Object)val).hideFlags = (HideFlags)61;
			Log.LogInfo((object)string.Format("Loaded {0} {1}x{2}", "content.png", ((Texture)val).width, ((Texture)val).height));
			return val;
		}
	}
	internal static class VoiceBox
	{
		private static bool _loggedTickError;

		internal static float ChanceMin => Plugin.ExtraLayerChanceMin.Value;

		internal static float ChanceMax => Plugin.ExtraLayerChanceMax.Value;

		internal static float IntervalMin => Plugin.ExtraLayerIntervalMin.Value;

		internal static float IntervalMax => Plugin.ExtraLayerIntervalMax.Value;

		internal static bool IsTestMode()
		{
			return Plugin.IsTestMode();
		}

		internal static bool IsOnMoon()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance != (Object)null && !instance.inShipPhase && instance.shipHasLanded)
			{
				return !instance.shipIsLeaving;
			}
			return false;
		}

		internal static bool WorldAllowsLayer()
		{
			string text = GateStatus();
			if (text == "test-ok" || text == "live-ok")
			{
				return true;
			}
			return false;
		}

		internal static string GateStatus()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Invalid comparison between Unknown and I4
			if (Plugin.ExtraLayer == null || !Plugin.ExtraLayer.Value)
			{
				return "layer-off";
			}
			if (!IsOnMoon())
			{
				return "not-on-moon";
			}
			if (IsTestMode())
			{
				return "test-ok";
			}
			if (Plugin.ExtraLayerWeatherGate.Value)
			{
				TimeOfDay instance = TimeOfDay.Instance;
				if ((Object)(object)instance == (Object)null || (int)instance.currentLevelWeather != 3)
				{
					return "not-foggy";
				}
			}
			if (!IsAtOrAfterNoon())
			{
				return "before-noon";
			}
			return "live-ok";
		}

		internal static bool IsAtOrAfterNoon()
		{
			TimeOfDay instance = TimeOfDay.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			int clockHour = GetClockHour24(instance);
			int num = Mathf.Clamp(Plugin.ExtraLayerAfterHour.Value, 0, 23);
			if (clockHour == 0)
			{
				return num <= 12;
			}
			return clockHour >= num;
		}

		internal static int GetClockHour24(TimeOfDay time)
		{
			int num = Mathf.Max(1, time.numberOfHours);
			int num2 = ((int)(time.normalizedTimeOfDay * (60f * (float)num)) + 360) / 60;
			if (num2 < 24)
			{
				return num2;
			}
			return 0;
		}

		internal static bool IsLocalPlayer(PlayerControllerB player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return true;
			}
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)instance.localPlayerController == (Object)(object)player)
			{
				return true;
			}
			if (((NetworkBehaviour)player).IsOwner)
			{
				return player.isPlayerControlled;
			}
			return false;
		}

		internal static void Tick()
		{
			try
			{
				VoiceBoxDirector.Tick();
			}
			catch (Exception ex)
			{
				if (!_loggedTickError)
				{
					_loggedTickError = true;
					VoiceBoxLog.Note("SKIP", "tick-error " + ex.Message);
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogError((object)$"VoiceBox tick: {ex}");
					}
				}
			}
		}
	}
	internal sealed class VoiceBoxCache : MonoBehaviour, IMicrophoneSubscriber
	{
		private const int MaxClips = 24;

		private const int NetworkRate = 16000;

		private const float MinSeconds = 1f;

		private const float MaxSeconds = 10f;

		private const float EndSilence = 0.7f;

		private const float SpeakStart = 0.012f;

		private const float SpeakHold = 0.006f;

		private static GameObject? _host;

		private readonly List<float[]> _clips = new List<float[]>();

		private readonly object _gate = new object();

		private readonly List<float> _current = new List<float>();

		private DissonanceComms? _comms;

		private bool _subscribed;

		private volatile bool _pendingReady;

		private float _silence;

		private int _micRate = 16000;

		private int _lastBucket = -1;

		internal static VoiceBoxCache? Instance { get; private set; }

		internal int ClipCount
		{
			get
			{
				lock (_gate)
				{
					return _clips.Count;
				}
			}
		}

		internal bool IsSubscribed => _subscribed;

		internal bool HasSnippets
		{
			get
			{
				lock (_gate)
				{
					return _clips.Count > 0;
				}
			}
		}

		internal static void Ensure()
		{
			if (Object.op_Implicit((Object)(object)Instance))
			{
				return;
			}
			try
			{
				GameObject val = ResolveHost();
				if (!((Object)(object)val == (Object)null))
				{
					Instance = val.GetComponent<VoiceBoxCache>() ?? val.AddComponent<VoiceBoxCache>();
					Instance.ImportMimicFolder();
					VoiceBoxLog.Note("CACHE", "host-ready");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("VoiceBox cache start failed: " + ex.Message));
				}
			}
		}

		private static GameObject? ResolveHost()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			if (Object.op_Implicit((Object)(object)_host))
			{
				return _host;
			}
			Plugin instance = Plugin.Instance;
			if (Object.op_Implicit((Object)(object)instance))
			{
				_host = ((Component)instance).gameObject;
				return _host;
			}
			_host = new GameObject("CapsCrew.SLayer.Cache");
			Object.DontDestroyOnLoad((Object)(object)_host);
			return _host;
		}

		internal bool TryPick(out float[] samples, out int sampleRate)
		{
			sampleRate = 16000;
			lock (_gate)
			{
				if (_clips.Count == 0)
				{
					samples = Array.Empty<float>();
					return false;
				}
				int index = PickVariedIndex();
				samples = _clips[index];
				_lastBucket = Bucket(samples.Length);
				return samples.Length != 0;
			}
		}

		private int PickVariedIndex()
		{
			List<int>[] array = new List<int>[3]
			{
				new List<int>(),
				new List<int>(),
				new List<int>()
			};
			for (int i = 0; i < _clips.Count; i++)
			{
				array[Bucket(_clips[i].Length)].Add(i);
			}
			List<int> list = new List<int>();
			for (int j = 0; j < 3; j++)
			{
				if (j != _lastBucket && array[j].Count > 0)
				{
					list.Add(j);
				}
			}
			for (int k = 0; k < 3; k++)
			{
				if (!list.Contains(k) && array[k].Count > 0)
				{
					list.Add(k);
				}
			}
			if (list.Count == 0)
			{
				return 0;
			}
			int num = list[0];
			if (list.Count > 1 && Random.value < 0.65f)
			{
				num = list[Random.Range(0, list.Count)];
			}
			List<int> list2 = array[num];
			return list2[Random.Range(0, list2.Count)];
		}

		private static int Bucket(int sampleCount)
		{
			float num = (float)sampleCount / 16000f;
			if (num < 3f)
			{
				return 0;
			}
			if (!(num < 6f))
			{
				return 2;
			}
			return 1;
		}

		internal void TrySubscribe()
		{
			if (_subscribed)
			{
				return;
			}
			StartOfRound instance = StartOfRound.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				ref DissonanceComms? comms = ref _comms;
				object? obj = AccessTools.Field(typeof(StartOfRound), "voiceChatModule")?.GetValue(instance);
				comms = (DissonanceComms?)((obj is DissonanceComms) ? obj : null);
				if ((Object)(object)_comms == (Object)null)
				{
					_comms = Object.FindObjectOfType<DissonanceComms>();
				}
				if (!((Object)(object)_comms == (Object)null))
				{
					_comms.SubscribeToRecordedAudio((IMicrophoneSubscriber)(object)this);
					_subscribed = true;
					VoiceBoxLog.Note("MIC", "subscribed");
				}
			}
		}

		internal void ImportMimicFolder()
		{
			try
			{
				string fullPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", "Mirage"));
				if (!Directory.Exists(fullPath))
				{
					VoiceBoxLog.Note("CACHE", "disk-folder-missing");
					return;
				}
				string[] files = Directory.GetFiles(fullPath, "*.wav", SearchOption.AllDirectories);
				string[] files2 = Directory.GetFiles(fullPath, "*.opus", SearchOption.AllDirectories);
				int num = 0;
				string[] array = files;
				for (int i = 0; i < array.Length; i++)
				{
					if (TryReadWavMono(array[i], out float[] samples) && samples.Length > 16000)
					{
						PushClip(samples);
						num++;
					}
				}
				VoiceBoxLog.Note("CACHE", $"disk wav={files.Length} imported={num} opus={files2.Length} (opus-not-read)");
			}
			catch (Exception ex)
			{
				VoiceBoxLog.Note("CACHE", "disk-error " + ex.Message);
			}
		}

		internal void FlushReady()
		{
			if (_pendingReady)
			{
				_pendingReady = false;
				VoiceBoxNetwork.NotifyReady();
			}
		}

		public void ReceiveMicrophoneData(ArraySegment<float> buffer, WaveFormat format)
		{
			try
			{
				ReceiveMicrophoneDataInner(buffer, format);
			}
			catch
			{
			}
		}

		private void ReceiveMicrophoneDataInner(ArraySegment<float> buffer, WaveFormat format)
		{
			if (buffer.Array == null || buffer.Count < 8)
			{
				return;
			}
			_micRate = ((format.SampleRate > 0) ? format.SampleRate : 16000);
			float num = 0f;
			int num2 = buffer.Offset + buffer.Count;
			for (int i = buffer.Offset; i < num2; i++)
			{
				float num3 = buffer.Array[i];
				num += num3 * num3;
			}
			num = Mathf.Sqrt(num / (float)buffer.Count);
			bool flag = _current.Count > 0;
			bool num4 = num > (flag ? 0.006f : 0.012f);
			int num5 = (int)((float)_micRate * 10f);
			if (num4)
			{
				_silence = 0f;
				for (int j = buffer.Offset; j < num2; j++)
				{
					if (_current.Count >= num5)
					{
						break;
					}
					_current.Add(buffer.Array[j]);
				}
				if (_current.Count >= num5)
				{
					FlushCurrent();
				}
			}
			else if (_current.Count > 0)
			{
				_silence += (float)buffer.Count / (float)_micRate;
				if (_silence >= 0.7f)
				{
					FlushCurrent();
				}
			}
		}

		public void Reset()
		{
			_current.Clear();
			_silence = 0f;
		}

		private void FlushCurrent()
		{
			if ((float)_current.Count < (float)_micRate * 1f)
			{
				_current.Clear();
				_silence = 0f;
				return;
			}
			float[] source = _current.ToArray();
			_current.Clear();
			_silence = 0f;
			PushClip(Downsample(source, _micRate));
		}

		private void PushClip(float[] samples)
		{
			if ((float)samples.Length < 16000f)
			{
				return;
			}
			bool flag = false;
			lock (_gate)
			{
				flag = _clips.Count == 0;
				if (_clips.Count >= 24)
				{
					_clips.RemoveAt(0);
				}
				_clips.Add(samples);
			}
			VoiceBoxLog.Note("CLIP", $"keep secs={(float)samples.Length / 16000f:0.00} n={ClipCount}");
			if (flag)
			{
				_pendingReady = true;
			}
		}

		internal static float[] Resample(float[] source, int sourceRate, int destRate)
		{
			if (sourceRate <= 0 || destRate <= 0 || source.Length == 0)
			{
				return Array.Empty<float>();
			}
			if (sourceRate == destRate)
			{
				return source;
			}
			float num = (float)sourceRate / (float)destRate;
			int num2 = Mathf.Max(1, Mathf.FloorToInt((float)source.Length / num));
			float[] array = new float[num2];
			for (int i = 0; i < num2; i++)
			{
				int num3 = Mathf.Clamp(Mathf.FloorToInt((float)i * num), 0, source.Length - 1);
				array[i] = source[num3];
			}
			return array;
		}

		private static float[] Downsample(float[] source, int sourceRate)
		{
			return Resample(source, sourceRate, 16000);
		}

		private static bool TryReadWavMono(string path, out float[] samples)
		{
			samples = Array.Empty<float>();
			byte[] array = File.ReadAllBytes(path);
			if (array.Length < 44)
			{
				return false;
			}
			short num = BitConverter.ToInt16(array, 22);
			int sourceRate = BitConverter.ToInt32(array, 24);
			short num2 = BitConverter.ToInt16(array, 34);
			int i;
			int num3;
			for (i = 12; i + 8 < array.Length && (array[i] != 100 || array[i + 1] != 97); i += 8 + num3)
			{
				num3 = BitConverter.ToInt32(array, i + 4);
			}
			if (i + 8 >= array.Length)
			{
				return false;
			}
			int num4 = BitConverter.ToInt32(array, i + 4);
			int num5 = i + 8;
			if (num2 != 16 || num < 1)
			{
				return false;
			}
			int num6 = num4 / (2 * num);
			float[] array2 = new float[num6];
			for (int j = 0; j < num6; j++)
			{
				int num7 = num5 + j * 2 * num;
				if (num7 + 1 >= array.Length)
				{
					break;
				}
				array2[j] = (float)BitConverter.ToInt16(array, num7) / 32768f;
			}
			samples = Resample(array2, sourceRate, 16000);
			return samples.Length != 0;
		}

		private void OnDestroy()
		{
			if (_subscribed && (Object)(object)_comms != (Object)null)
			{
				try
				{
					_comms.UnsubscribeFromRecordedAudio((IMicrophoneSubscriber)(object)this);
				}
				catch
				{
				}
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}
	}
	internal static class VoiceBoxDirector
	{
		private static readonly HashSet<ulong> ReadyClients = new HashSet<ulong>();

		private static float _nextRollAt;

		private static bool _busy;

		private static bool _onMoon;

		private static float _nextMonitorAt;

		private static int _lastSnippetCount = -1;

		private static bool _loggedClientWait;

		internal static float LastIntervalSeconds { get; private set; }

		internal static int ReadyCount => ReadyClients.Count;

		internal static void MarkReady(ulong clientId)
		{
			if (ReadyClients.Add(clientId))
			{
				VoiceBoxLog.Note("READY", $"client={clientId} ready={ReadyClients.Count}");
			}
		}

		internal static void StopProcess()
		{
			_busy = false;
			_nextRollAt = 0f;
			_onMoon = false;
			VoiceBoxNetwork.ClearSend();
			VoiceBoxSpeaker.Stop();
			VoiceBoxGhost.ReleaseAll();
		}

		internal static void ClearRound()
		{
			ReadyClients.Clear();
			VoiceBoxLog.Note("LEAVE", "round-clear");
			StopProcess();
		}

		internal static void Tick()
		{
			VoiceBoxCache.Ensure();
			VoiceBoxNetwork.TryRegister();
			VoiceBoxCache.Instance?.TrySubscribe();
			VoiceBoxCache.Instance?.FlushReady();
			VoiceBoxNetwork.PumpSend();
			VoiceBoxSpeaker.Tick();
			if (!VoiceBox.IsOnMoon())
			{
				if (_onMoon)
				{
					VoiceBoxLog.Note("LEAVE", "left-moon");
					StopProcess();
				}
				return;
			}
			if (!_onMoon)
			{
				_onMoon = true;
				_nextRollAt = 0f;
				_nextMonitorAt = 0f;
				_lastSnippetCount = -1;
				_loggedClientWait = false;
				ScheduleNext();
				VoiceBoxLog.Note("ENTER", $"test={VoiceBox.IsTestMode()} gate={VoiceBox.GateStatus()} host={IsHost()}");
			}
			LogSnippetDelta();
			MaybeMonitor();
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsServer)
			{
				if (!_loggedClientWait)
				{
					_loggedClientWait = true;
					VoiceBoxLog.Note("MONITOR", $"client-wait host=false gate={VoiceBox.GateStatus()} snippets={SnippetCount()} mic={MicOn()}");
				}
				return;
			}
			if (singleton.IsListening)
			{
				ReadyClients.Add(singleton.LocalClientId);
			}
			if (!VoiceBox.WorldAllowsLayer())
			{
				return;
			}
			if (_nextRollAt <= 0.01f)
			{
				ScheduleNext();
			}
			if (Time.realtimeSinceStartup < _nextRollAt || _busy)
			{
				return;
			}
			if (SnippetCount() == 0)
			{
				LastIntervalSeconds = 0.5f;
				_nextRollAt = Time.realtimeSinceStartup + 0.5f;
				VoiceBoxLog.Note("SKIP", "waiting-snippets");
				return;
			}
			ScheduleNext();
			float num = Random.Range(VoiceBox.ChanceMin, VoiceBox.ChanceMax);
			float value = Random.value;
			if (value > num)
			{
				VoiceBoxLog.Note("ROLL", $"result=MISS roll={value:0.00} chance={num:0.00} snippets={SnippetCount()} ready={ReadyClients.Count} gate={VoiceBox.GateStatus()}");
				return;
			}
			if (!TryPickOnePlayer(out var clientId))
			{
				VoiceBoxLog.Note("SKIP", $"reason=no-ready-player snippets={SnippetCount()} ready={ReadyClients.Count} mic={MicOn()}");
				return;
			}
			VoiceBoxLog.Note("ROLL", $"result=HIT roll={value:0.00} chance={num:0.00} target={clientId} snippets={SnippetCount()} ready={ReadyClients.Count}");
			_busy = true;
			try
			{
				VoiceBoxNetwork.HostFire(clientId);
			}
			catch (Exception ex)
			{
				VoiceBoxLog.Note("SKIP", "fire-error " + ex.Message);
			}
			finally
			{
				_busy = false;
			}
		}

		private static void MaybeMonitor()
		{
			if (!(Time.realtimeSinceStartup < _nextMonitorAt))
			{
				_nextMonitorAt = Time.realtimeSinceStartup + 15f;
				float num = ((_nextRollAt > 0.01f) ? Mathf.Max(0f, _nextRollAt - Time.realtimeSinceStartup) : 0f);
				VoiceBoxLog.Note("MONITOR", $"moon=1 test={VoiceBox.IsTestMode()} host={IsHost()} gate={VoiceBox.GateStatus()} snippets={SnippetCount()} ready={ReadyClients.Count} mic={MicOn()} nextRoll={num:0}s");
			}
		}

		private static void LogSnippetDelta()
		{
			int num = SnippetCount();
			if (num != _lastSnippetCount)
			{
				_lastSnippetCount = num;
				VoiceBoxLog.Note("CLIP", $"snippets={num} mic={MicOn()}");
			}
		}

		private static int SnippetCount()
		{
			if (!((Object)(object)VoiceBoxCache.Instance != (Object)null))
			{
				return 0;
			}
			return VoiceBoxCache.Instance.ClipCount;
		}

		private static bool MicOn()
		{
			if ((Object)(object)VoiceBoxCache.Instance != (Object)null)
			{
				return VoiceBoxCache.Instance.IsSubscribed;
			}
			return false;
		}

		private static bool IsHost()
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton != (Object)null)
			{
				return singleton.IsServer;
			}
			return false;
		}

		private static void ScheduleNext()
		{
			LastIntervalSeconds = Random.Range(VoiceBox.IntervalMin, VoiceBox.IntervalMax);
			_nextRollAt = Time.realtimeSinceStartup + LastIntervalSeconds;
		}

		private static bool TryPickOnePlayer(out ulong clientId)
		{
			clientId = 0uL;
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null)
			{
				return false;
			}
			List<ulong> list = new List<ulong>();
			PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (!((Object)(object)val == (Object)null) && !val.isPlayerDead && val.isPlayerControlled)
				{
					ulong ownerClientId = ((NetworkBehaviour)val).OwnerClientId;
					if (ReadyClients.Contains(ownerClientId) || ((Object)(object)NetworkManager.Singleton != (Object)null && ownerClientId == NetworkManager.Singleton.LocalClientId && (Object)(object)VoiceBoxCache.Instance != (Object)null && VoiceBoxCache.Instance.HasSnippets))
					{
						list.Add(ownerClientId);
					}
				}
			}
			if (list.Count == 0)
			{
				return false;
			}
			clientId = list[Random.Range(0, list.Count)];
			return true;
		}
	}
	internal static class VoiceBoxSpeaker
	{
		private static AudioSource? _source;

		private static AudioClip? _playing;

		private static AudioSource? _duck;

		private static float _duckRestore = 1f;

		private static float _duckUntil;

		internal static void Play(ulong sourceClientId, float[] samples, int sampleRate)
		{
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			if (!VoiceBox.IsOnMoon())
			{
				VoiceBoxLog.Note("SKIP", $"play-not-on-moon from={sourceClientId}");
				return;
			}
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || samples == null || samples.Length < 64)
			{
				VoiceBoxLog.Note("SKIP", $"play-bad-clip from={sourceClientId} samples={((samples != null) ? samples.Length : 0)}");
				return;
			}
			PlayerControllerB val = null;
			PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts;
			foreach (PlayerControllerB val2 in allPlayerScripts)
			{
				if ((Object)(object)val2 != (Object)null && ((NetworkBehaviour)val2).OwnerClientId == sourceClientId)
				{
					val = val2;
					break;
				}
			}
			if ((Object)(object)val == (Object)null || VoiceBox.IsLocalPlayer(val))
			{
				VoiceBoxLog.Note("SKIP", $"play-self-or-missing from={sourceClientId}");
				return;
			}
			AudioSource currentVoiceChatAudioSource = val.currentVoiceChatAudioSource;
			EnsureSource(val, currentVoiceChatAudioSource);
			if ((Object)(object)_source == (Object)null)
			{
				VoiceBoxLog.Note("SKIP", "play-no-source");
				return;
			}
			if ((Object)(object)_playing != (Object)null)
			{
				Object.Destroy((Object)(object)_playing);
				_playing = null;
			}
			AudioClip val3 = AudioClip.Create("slayer", samples.Length, 1, sampleRate, false);
			val3.SetData(samples, 0);
			float num = Mathf.Max(0.01f, Plugin.ExtraLayerPitch.Value);
			float num2 = val3.length / num;
			Duck(currentVoiceChatAudioSource, num2);
			_source.Stop();
			Transform val4 = (((Object)(object)currentVoiceChatAudioSource != (Object)null) ? ((Component)currentVoiceChatAudioSource).transform : ((Component)val).transform);
			((Component)_source).transform.SetParent(val4, false);
			((Component)_source).transform.localPosition = Vector3.zero;
			_source.pitch = num;
			_source.volume = Plugin.ExtraLayerVolume.Value;
			_source.clip = val3;
			_playing = val3;
			_source.Play();
			VoiceBoxLog.Note("PLAY", $"from={sourceClientId} samples={samples.Length} rate={sampleRate} secs={num2:0.00} duck={(Object)(object)currentVoiceChatAudioSource != (Object)null}");
		}

		internal static void Tick()
		{
			if (!((Object)(object)_duck == (Object)null) && !(Time.realtimeSinceStartup < _duckUntil))
			{
				_duck.volume = _duckRestore;
				VoiceBoxLog.Note("DUCK", "restore");
				_duck = null;
				if ((Object)(object)_playing != (Object)null && ((Object)(object)_source == (Object)null || !_source.isPlaying))
				{
					Object.Destroy((Object)(object)_playing);
					_playing = null;
				}
			}
		}

		internal static void Stop()
		{
			if ((Object)(object)_source != (Object)null)
			{
				_source.Stop();
				_source.clip = null;
			}
			if ((Object)(object)_playing != (Object)null)
			{
				Object.Destroy((Object)(object)_playing);
				_playing = null;
			}
			if ((Object)(object)_duck != (Object)null)
			{
				_duck.volume = _duckRestore;
				_duck = null;
			}
		}

		private static void Duck(AudioSource? voice, float seconds)
		{
			if (!((Object)(object)voice == (Object)null))
			{
				if ((Object)(object)_duck != (Object)null && (Object)(object)_duck != (Object)(object)voice)
				{
					_duck.volume = _duckRestore;
					_duck = null;
				}
				if ((Object)(object)_duck != (Object)(object)voice)
				{
					_duck = voice;
					_duckRestore = ((voice.volume > 0.001f) ? voice.volume : 1f);
					voice.volume = 0f;
				}
				_duckUntil = Time.realtimeSinceStartup + Mathf.Max(0.05f, seconds);
				VoiceBoxLog.Note("DUCK", $"secs={seconds:0.00} was={_duckRestore:0.00}");
			}
		}

		private static void EnsureSource(PlayerControllerB player, AudioSource? voice)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_source == (Object)null)
			{
				GameObject val = new GameObject("SLayerOne");
				Object.DontDestroyOnLoad((Object)val);
				_source = val.AddComponent<AudioSource>();
				_source.playOnAwake = false;
				_source.loop = false;
				_source.dopplerLevel = 0f;
				_source.spatialize = false;
			}
			if ((Object)(object)voice != (Object)null)
			{
				_source.outputAudioMixerGroup = voice.outputAudioMixerGroup;
				_source.spatialBlend = voice.spatialBlend;
				_source.rolloffMode = voice.rolloffMode;
				_source.minDistance = voice.minDistance;
				_source.maxDistance = voice.maxDistance;
			}
			else
			{
				_source.spatialBlend = 1f;
				_source.rolloffMode = (AudioRolloffMode)1;
				_source.minDistance = 1.5f;
				_source.maxDistance = 48f;
			}
		}
	}
	internal sealed class VoiceBoxGhost : MonoBehaviour
	{
		private const string ChildName = "SLayer";

		private static readonly Dictionary<int, VoiceBoxGhost> Active = new Dictionary<int, VoiceBoxGhost>();

		private PlayerControllerB? _player;

		private AudioSource? _source;

		private VoiceBoxTap? _tap;

		private float[] _ring = Array.Empty<float>();

		private readonly object _gate = new object();

		private int _write;

		private int _read;

		private bool _draining;

		private float _drainLeft;

		private float _lowpass;

		internal static void Ensure(PlayerControllerB player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)player).GetInstanceID();
			if (Active.TryGetValue(instanceID, out VoiceBoxGhost value) && (Object)(object)value != (Object)null)
			{
				value._draining = false;
				value._drainLeft = 0f;
				value.Bind(player);
				return;
			}
			VoiceBoxGhost voiceBoxGhost = ((Component)player).GetComponent<VoiceBoxGhost>();
			if ((Object)(object)voiceBoxGhost == (Object)null)
			{
				voiceBoxGhost = ((Component)player).gameObject.AddComponent<VoiceBoxGhost>();
			}
			Active[instanceID] = voiceBoxGhost;
			voiceBoxGhost._draining = false;
			voiceBoxGhost.Bind(player);
		}

		internal static void BeginRelease(PlayerControllerB player)
		{
			if (!((Object)(object)player == (Object)null) && Active.TryGetValue(((Object)player).GetInstanceID(), out VoiceBoxGhost value) && !((Object)(object)value == (Object)null))
			{
				value._draining = true;
				value._drainLeft = Mathf.Max(0.12f, Plugin.ExtraLayerDelay.Value);
			}
		}

		internal static void Release(PlayerControllerB player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				int instanceID = ((Object)player).GetInstanceID();
				if (Active.TryGetValue(instanceID, out VoiceBoxGhost value) && (Object)(object)value != (Object)null)
				{
					value.Teardown();
				}
				Active.Remove(instanceID);
			}
		}

		internal static void ReleaseAll()
		{
			List<VoiceBoxGhost> list = new List<VoiceBoxGhost>(Active.Values);
			Active.Clear();
			foreach (VoiceBoxGhost item in list)
			{
				if ((Object)(object)item != (Object)null)
				{
					item.Teardown();
				}
			}
		}

		private void Bind(PlayerControllerB player)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			_player = player;
			AudioSource currentVoiceChatAudioSource = player.currentVoiceChatAudioSource;
			if ((Object)(object)currentVoiceChatAudioSource == (Object)null)
			{
				return;
			}
			_tap = ((Component)currentVoiceChatAudioSource).GetComponent<VoiceBoxTap>();
			if ((Object)(object)_tap == (Object)null)
			{
				_tap = ((Component)currentVoiceChatAudioSource).gameObject.AddComponent<VoiceBoxTap>();
			}
			_tap.Ghost = this;
			Transform val = ((Component)player).transform.Find("SLayer");
			GameObject val2;
			if ((Object)(object)val == (Object)null)
			{
				val2 = new GameObject("SLayer");
				val2.transform.SetParent(((Component)player).transform, false);
				val2.transform.localPosition = new Vector3(0f, 1.6f, 0f);
			}
			else
			{
				val2 = ((Component)val).gameObject;
			}
			_source = val2.GetComponent<AudioSource>();
			if ((Object)(object)_source == (Object)null)
			{
				_source = val2.AddComponent<AudioSource>();
			}
			VoiceBoxPlayback voiceBoxPlayback = val2.GetComponent<VoiceBoxPlayback>();
			if ((Object)(object)voiceBoxPlayback == (Object)null)
			{
				voiceBoxPlayback = val2.AddComponent<VoiceBoxPlayback>();
			}
			voiceBoxPlayback.Ghost = this;
			_source.spatialBlend = 1f;
			_source.rolloffMode = (AudioRolloffMode)1;
			_source.minDistance = 1.5f;
			_source.maxDistance = 42f;
			_source.dopplerLevel = 0f;
			_source.loop = true;
			_source.playOnAwake = false;
			_source.priority = 64;
			_source.pitch = Plugin.ExtraLayerPitch.Value;
			_source.volume = Plugin.ExtraLayerVolume.Value;
			int outputSampleRate = AudioSettings.outputSampleRate;
			float num = Mathf.Clamp(Plugin.ExtraLayerDelay.Value, 0.18f, 2.4f);
			int num2 = Mathf.Max(1024, Mathf.CeilToInt(num * (float)outputSampleRate));
			lock (_gate)
			{
				if (_ring.Length != num2)
				{
					_ring = new float[num2];
					_write = 0;
					_read = ((num2 > 1) ? 1 : 0);
				}
			}
			if ((Object)(object)_source.clip == (Object)null)
			{
				_source.clip = AudioClip.Create("slayer", 1024, 1, outputSampleRate, false);
			}
			if (!_source.isPlaying)
			{
				_source.Play();
			}
		}

		internal void PushSamples(float[] data, int channels)
		{
			if (data == null || data.Length == 0 || _ring.Length == 0)
			{
				return;
			}
			int num = Mathf.Max(1, channels);
			lock (_gate)
			{
				for (int i = 0; i < data.Length; i += num)
				{
					_ring[_write] = data[i];
					_write++;
					if (_write >= _ring.Length)
					{
						_write = 0;
					}
				}
			}
		}

		internal void FillPlayback(float[] data, int channels)
		{
			if (_ring.Length == 0)
			{
				Array.Clear(data, 0, data.Length);
				return;
			}
			float num = 0.18f;
			int num2 = Mathf.Max(1, channels);
			lock (_gate)
			{
				for (int i = 0; i < data.Length; i++)
				{
					if (i % num2 != 0)
					{
						data[i] = 0f;
						continue;
					}
					float num3 = _ring[_read];
					_read++;
					if (_read >= _ring.Length)
					{
						_read = 0;
					}
					_lowpass += num * (num3 - _lowpass);
					data[i] = _lowpass;
				}
			}
		}

		private void LateUpdate()
		{
			if ((Object)(object)_player == (Object)null)
			{
				Teardown();
			}
			else if (_draining)
			{
				_drainLeft -= Time.deltaTime;
				if (_drainLeft <= 0f)
				{
					Release(_player);
				}
			}
		}

		private void Teardown()
		{
			if ((Object)(object)_tap != (Object)null && (Object)(object)_tap.Ghost == (Object)(object)this)
			{
				_tap.Ghost = null;
			}
			if ((Object)(object)_source != (Object)null)
			{
				VoiceBoxPlayback component = ((Component)_source).GetComponent<VoiceBoxPlayback>();
				if ((Object)(object)component != (Object)null)
				{
					component.Ghost = null;
				}
				_source.Stop();
			}
			_draining = false;
			Object.Destroy((Object)(object)this);
		}

		private void OnDestroy()
		{
			if ((Object)(object)_player != (Object)null)
			{
				Active.Remove(((Object)_player).GetInstanceID());
			}
		}
	}
	internal sealed class VoiceBoxTap : MonoBehaviour
	{
		internal VoiceBoxGhost? Ghost;

		private void OnAudioFilterRead(float[] data, int channels)
		{
			Ghost?.PushSamples(data, channels);
		}
	}
	internal sealed class VoiceBoxPlayback : MonoBehaviour
	{
		internal VoiceBoxGhost? Ghost;

		private void OnAudioFilterRead(float[] data, int channels)
		{
			if ((Object)(object)Ghost == (Object)null)
			{
				Array.Clear(data, 0, data.Length);
			}
			else
			{
				Ghost.FillPlayback(data, channels);
			}
		}
	}
	internal static class VoiceBoxLog
	{
		internal static void Note(string action, string detail)
		{
			string text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
			float lastIntervalSeconds = VoiceBoxDirector.LastIntervalSeconds;
			string text2 = ((lastIntervalSeconds > 0.01f) ? lastIntervalSeconds.ToString("0") : VoiceBox.IntervalMin.ToString("0"));
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("[SPLENDIPHIUS] [" + action + "] [" + text + "] " + detail + " [MONITORING INTERVAL IN SECONDS: " + text2 + "]"));
			}
		}
	}
	internal static class VoiceBoxNetwork
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__OnReady;

			public static HandleNamedMessageDelegate <1>__OnFire;

			public static HandleNamedMessageDelegate <2>__OnPlay;
		}

		private const string ReadyName = "CapsCrew_SLayer_Ready";

		private const string FireName = "CapsCrew_SLayer_Fire";

		private const string PlayName = "CapsCrew_SLayer_Play";

		private const int ChunkSamples = 500;

		private const int WriterMax = 1280;

		private const int SendRate = 8000;

		private const int MaxSendSamples = 80000;

		private static bool _registered;

		private static float[]? _assemble;

		private static int _assembleGot;

		private static int _assembleTotal;

		private static int _assembleRate;

		private static ulong _assembleFrom;

		private static float[]? _sendSamples;

		private static int _sendRate;

		private static int _sendOffset;

		private static int _sendTotalChunks;

		private static int _sendSentChunks;

		private static readonly List<ulong> SendPeers = new List<ulong>();

		private static ulong _sendFrom;

		internal static void TryRegister()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			if (_registered)
			{
				return;
			}
			NetworkManager singleton = NetworkManager.Singleton;
			if (!((Object)(object)singleton == (Object)null) && singleton.CustomMessagingManager != null)
			{
				CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager;
				object obj = <>O.<0>__OnReady;
				if (obj == null)
				{
					HandleNamedMessageDelegate val = OnReady;
					<>O.<0>__OnReady = val;
					obj = (object)val;
				}
				customMessagingManager.RegisterNamedMessageHandler("CapsCrew_SLayer_Ready", (HandleNamedMessageDelegate)obj);
				CustomMessagingManager customMessagingManager2 = singleton.CustomMessagingManager;
				object obj2 = <>O.<1>__OnFire;
				if (obj2 == null)
				{
					HandleNamedMessageDelegate val2 = OnFire;
					<>O.<1>__OnFire = val2;
					obj2 = (object)val2;
				}
				customMessagingManager2.RegisterNamedMessageHandler("CapsCrew_SLayer_Fire", (HandleNamedMessageDelegate)obj2);
				CustomMessagingManager customMessagingManager3 = singleton.CustomMessagingManager;
				object obj3 = <>O.<2>__OnPlay;
				if (obj3 == null)
				{
					HandleNamedMessageDelegate val3 = OnPlay;
					<>O.<2>__OnPlay = val3;
					obj3 = (object)val3;
				}
				customMessagingManager3.RegisterNamedMessageHandler("CapsCrew_SLayer_Play", (HandleNamedMessageDelegate)obj3);
				_registered = true;
			}
		}

		internal unsafe static void NotifyReady()
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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)
			try
			{
				TryRegister();
				NetworkManager singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton == (Object)null || !singleton.IsListening || singleton.IsServer)
				{
					if ((Object)(object)singleton != (Object)null && singleton.IsServer)
					{
						VoiceBoxDirector.MarkReady(singleton.LocalClientId);
					}
					return;
				}
				FastBufferWriter val = default(FastBufferWriter);
				((FastBufferWriter)(ref val))..ctor(8, (Allocator)2, 64);
				try
				{
					ulong localClientId = singleton.LocalClientId;
					((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref localClientId, default(ForPrimitives));
					singleton.CustomMessagingManager.SendNamedMessage("CapsCrew_SLayer_Ready", 0uL, val, (NetworkDelivery)2);
					VoiceBoxLog.Note("READY", $"sent-to-host client={singleton.LocalClientId}");
				}
				finally
				{
					((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
				}
			}
			catch (Exception ex)
			{
				VoiceBoxLog.Note("SKIP", "ready-error " + ex.Message);
			}
		}

		internal unsafe static void HostFire(ulong targetClientId)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			TryRegister();
			if (!VoiceBox.IsOnMoon())
			{
				VoiceBoxLog.Note("SKIP", "fire-not-on-moon");
				return;
			}
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsServer)
			{
				VoiceBoxLog.Note("SKIP", "fire-not-host");
				return;
			}
			VoiceBoxLog.Note("FIRE", $"target={targetClientId}");
			if (targetClientId == singleton.LocalClientId)
			{
				SendLocalSnippet();
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(8, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref targetClientId, default(ForPrimitives));
				singleton.CustomMessagingManager.SendNamedMessage("CapsCrew_SLayer_Fire", targetClientId, val, (NetworkDelivery)2);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private static void OnReady(ulong senderClientId, FastBufferReader reader)
		{
			//IL_0006: 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)
			ulong num = default(ulong);
			((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
			VoiceBoxDirector.MarkReady((num == 0L) ? senderClientId : num);
		}

		private static void OnFire(ulong senderClientId, FastBufferReader reader)
		{
			VoiceBoxLog.Note("FIRE", $"recv-host-fire sender={senderClientId}");
			SendLocalSnippet();
		}

		private static void SendLocalSnippet()
		{
			TryRegister();
			VoiceBoxCache instance = VoiceBoxCache.Instance;
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)instance == (Object)null || (Object)(object)singleton == (Object)null || !instance.TryPick(out float[] samples, out int sampleRate))
			{
				VoiceBoxLog.Note("SKIP", "send-no-snippet");
				return;
			}
			samples = VoiceBoxCache.Resample(samples, sampleRate, 8000);
			sampleRate = 8000;
			int num = Mathf.Min(samples.Length, 80000);
			if (num < 8000)
			{
				VoiceBoxLog.Note("SKIP", "send-too-short");
				return;
			}
			SendPeers.Clear();
			foreach (ulong connectedClientsId in singleton.ConnectedClientsIds)
			{
				if (connectedClientsId != singleton.LocalClientId)
				{
					SendPeers.Add(connectedClientsId);
				}
			}
			if (SendPeers.Count == 0)
			{
				VoiceBoxLog.Note("SKIP", "send-no-peers");
				return;
			}
			float[] array = new float[num];
			Array.Copy(samples, array, num);
			_sendSamples = array;
			_sendRate = sampleRate;
			_sendOffset = 0;
			_sendFrom = singleton.LocalClientId;
			_sendTotalChunks = (num + 500 - 1) / 500;
			_sendSentChunks = 0;
			VoiceBoxLog.Note("SEND", $"queue samples={num} rate={sampleRate} secs={(float)num / (float)sampleRate:0.00} peers={SendPeers.Count} chunks={_sendTotalChunks}");
		}

		internal static void ClearSend()
		{
			_sendSamples = null;
			_sendOffset = 0;
			SendPeers.Clear();
		}

		internal unsafe static void PumpSend()
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: 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_00bd: 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_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: 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_011e: 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_0143: Unknown result type (might be due to invalid IL or missing references)
			if (_sendSamples == null || SendPeers.Count == 0)
			{
				return;
			}
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null)
			{
				return;
			}
			int num = _sendSamples.Length;
			FastBufferWriter val = default(FastBufferWriter);
			for (int i = 0; i < 4; i++)
			{
				if (_sendSamples == null)
				{
					break;
				}
				if (_sendOffset >= num)
				{
					break;
				}
				int num2 = Mathf.Min(500, num - _sendOffset);
				if (num2 <= 0)
				{
					break;
				}
				foreach (ulong sendPeer in SendPeers)
				{
					int num3 = 24 + num2 * 2;
					((FastBufferWriter)(ref val))..ctor(num3, (Allocator)2, 1280);
					try
					{
						((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref _sendFrom, default(ForPrimitives));
						((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref _sendRate, default(ForPrimitives));
						((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives));
						((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref _sendOffset, default(ForPrimitives));
						((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num2, default(ForPrimitives));
						for (int j = 0; j < num2; j++)
						{
							short num4 = (short)Mathf.Clamp(_sendSamples[_sendOffset + j] * 32767f, -32768f, 32767f);
							((FastBufferWriter)(ref val)).WriteValueSafe<short>(ref num4, default(ForPrimitives));
						}
						singleton.CustomMessagingManager.SendNamedMessage("CapsCrew_SLayer_Play", sendPeer, val, (NetworkDelivery)2);
					}
					finally
					{
						((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
					}
				}
				_sendOffset += num2;
				_sendSentChunks++;
			}
			if (_sendSamples == null || _sendOffset >= num)
			{
				VoiceBoxLog.Note("SEND", $"done chunks={_sendSentChunks}/{_sendTotalChunks} samples={num}");
				ClearSend();
			}
		}

		private static void OnPlay(ulong senderClientId, FastBufferReader reader)
		{
			//IL_0006: 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_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: 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_003f: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			ulong num = default(ulong);
			((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
			int num2 = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num2, default(ForPrimitives));
			int num3 = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num3, default(ForPrimitives));
			int num4 = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num4, default(ForPrimitives));
			int num5 = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num5, default(ForPrimitives));
			num3 = Mathf.Clamp(num3, 0, 80000);
			num4 = Mathf.Clamp(num4, 0, 80000);
			num5 = Mathf.Clamp(num5, 0, 500);
			if (num3 <= 0 || num5 <= 0 || num4 + num5 > num3)
			{
				VoiceBoxLog.Note("SKIP", $"recv-bad-chunk from={num} off={num4} len={num5} total={num3}");
				return;
			}
			if (_assemble == null || _assembleFrom != num || _assembleTotal != num3)
			{
				_assemble = new float[num3];
				_assembleGot = 0;
				_assembleTotal = num3;
				_assembleRate = ((num2 > 0) ? num2 : 16000);
				_assembleFrom = num;
			}
			short num6 = default(short);
			for (int i = 0; i < num5; i++)
			{
				((FastBufferReader)(ref reader)).ReadValueSafe<short>(ref num6, default(ForPrimitives));
				_assemble[num4 + i] = (float)num6 / 32768f;
			}
			_assembleGot += num5;
			if (_assembleGot < num3)
			{
				return;
			}
			float[] assemble = _assemble;
			ulong assembleFrom = _assembleFrom;
			int assembleRate = _assembleRate;
			_assemble = null;
			_assembleGot = 0;
			if (!VoiceBox.IsOnMoon())
			{
				VoiceBoxLog.Note("SKIP", $"recv-not-on-moon from={assembleFrom}");
				return;
			}
			ulong num7 = (((Object)(object)NetworkManager.Singleton != (Object)null) ? NetworkManager.Singleton.LocalClientId : ulong.MaxValue);
			if (assembleFrom != num7)
			{
				VoiceBoxSpeaker.Play(assembleFrom, assemble, assembleRate);
			}
		}
	}
}
namespace CapsCrew.KylesMom.Patches
{
	[HarmonyPatch(typeof(FlowermanAI), "Start")]
	internal static class FlowermanStartPatch
	{
		[HarmonyPostfix]
		private static void AfterStart(FlowermanAI __instance)
		{
			KyleMomVisual.Ensure(__instance);
		}
	}
	[HarmonyPatch(typeof(FlowermanAI), "Update")]
	internal static class FlowermanUpdatePatch
	{
		[HarmonyPostfix]
		private static void AfterUpdate(FlowermanAI __instance)
		{
			KyleMomVisual.Ensure(__instance);
		}
	}
	[HarmonyPatch(typeof(EnemyAI), "EnableEnemyMesh")]
	internal static class EnableEnemyMeshPatch
	{
		[HarmonyPostfix]
		private static void AfterEnableMesh(EnemyAI __instance)
		{
			FlowermanAI val = (FlowermanAI)(object)((__instance is FlowermanAI) ? __instance : null);
			if (val != null)
			{
				KyleMomVisual.Ensure(val);
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "LateUpdate")]
	internal static class VoiceBoxLateUpdatePatch
	{
		[HarmonyPostfix]
		private static void AfterLateUpdate()
		{
			VoiceBox.Tick();
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "Start")]
	internal static class VoiceBoxStartPatch
	{
		[HarmonyPostfix]
		private static void AfterStart()
		{
			VoiceBoxCache.Ensure();
			VoiceBoxNetwork.TryRegister();
			VoiceBoxCache.Instance?.TrySubscribe();
		}
	}
	[HarmonyPatch(typeof(GameNetworkManager), "Start")]
	internal static class VoiceBoxNetworkStartPatch
	{
		[HarmonyPostfix]
		private static void AfterNetworkStart()
		{
			VoiceBoxNetwork.TryRegister();
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "OnDisable")]
	internal static class VoiceBoxDisablePatch
	{
		[HarmonyPostfix]
		private static void AfterDisable()
		{
			VoiceBoxDirector.ClearRound();
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "ShipLeave")]
	internal static class VoiceBoxShipLeavePatch
	{
		[HarmonyPrefix]
		private static void BeforeShipLeave()
		{
			VoiceBoxDirector.StopProcess();
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "ShipHasLeft")]
	internal static class VoiceBoxShipHasLeftPatch
	{
		[HarmonyPostfix]
		private static void AfterShipHasLeft()
		{
			VoiceBoxDirector.StopProcess();
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "EndOfGame")]
	internal static class VoiceBoxEndOfGamePatch
	{
		[HarmonyPrefix]
		private static void BeforeEndOfGame()
		{
			VoiceBoxDirector.StopProcess();
		}
	}
}