Decompiled source of MimicReplay v0.12.6

BepInEx\plugins\MimicReplay\MimicReplay.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Rendering;

[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace MimicReplay;

[BepInPlugin("com.schetnikov.repo.mimicreplay", "MimicReplay", "0.12.6")]
public class Plugin : BaseUnityPlugin
{
	private sealed class RigidbodyState
	{
		internal bool UseGravity;

		internal bool IsKinematic;

		internal bool DetectCollisions;
	}

	private sealed class PendingNetworkSpawn
	{
		internal int ActorNumber;

		internal float Delay;

		internal float Expires;

		internal bool Authoritative;
	}

	private const byte NetworkSpawnEventCode = 197;

	private const string NetworkSpawnSignature = "MimicReplaySpawnV1";

	internal static ManualLogSource Log;

	internal static Plugin Instance;

	internal ConfigEntry<bool> Enabled;

	internal ConfigEntry<KeyboardShortcut> SpawnKey;

	internal ConfigEntry<KeyboardShortcut> RemoveKey;

	internal ConfigEntry<int> RecordingRateHz;

	internal ConfigEntry<int> HistorySeconds;

	internal ConfigEntry<int> ReplayDelayMinSeconds;

	internal ConfigEntry<int> ReplayDelayMaxSeconds;

	internal ConfigEntry<float> PositionInterpolationSpeed;

	internal ConfigEntry<float> RotationInterpolationSpeed;

	internal ConfigEntry<bool> ShowCopiedName;

	internal ConfigEntry<bool> HostOnlySpawn;

	internal ConfigEntry<bool> VerboseLogging;

	internal ConfigEntry<float> VerticalOffset;

	internal ConfigEntry<float> TemporaryVisualDesyncDistance;

	internal ConfigEntry<bool> DebugTrail;

	internal ConfigEntry<KeyboardShortcut> DebugTrailKey;

	internal ConfigEntry<float> PlaybackSpeed;

	internal ConfigEntry<float> ChaseSpeed;

	internal ConfigEntry<float> VisionDistance;

	internal ConfigEntry<float> AttackRange;

	internal ConfigEntry<int> AttackDamage;

	internal ConfigEntry<float> AttackCooldown;

	internal ConfigEntry<float> TurnSpeed;

	internal ConfigEntry<KeyboardShortcut> SpectatorKey;

	internal ConfigEntry<KeyboardShortcut> NoclipKey;

	internal ConfigEntry<float> NoclipSpeed;

	internal ConfigEntry<KeyboardShortcut> AddMoneyKey;

	internal ConfigEntry<int> AddMoneyAmount;

	internal ConfigEntry<float> KnockbackForce;

	private readonly List<PlayerRecorder> _recorders = new List<PlayerRecorder>();

	private readonly List<MimicController> _mimics = new List<MimicController>();

	private float _nextPlayerScan;

	private float _nextSpawnAllowedTime;

	private float _nextDebugToggleAllowedTime;

	private float _nextSpectatorToggleAllowedTime;

	private float _nextNoclipToggleAllowedTime;

	private float _nextMoneyAllowedTime;

	private float _moneyNoticeUntil;

	private string _moneyNotice;

	private bool _previousGodMode;

	private GUIStyle _spectatorStyle;

	private GUIStyle _spectatorShadowStyle;

	private readonly Dictionary<Collider, bool> _noclipColliderStates = new Dictionary<Collider, bool>();

	private readonly Dictionary<Rigidbody, RigidbodyState> _noclipBodyStates = new Dictionary<Rigidbody, RigidbodyState>();

	private bool _networkSubscribed;

	private readonly List<PendingNetworkSpawn> _pendingNetworkSpawns = new List<PendingNetworkSpawn>();

	private static readonly FieldInfo PlayerHealthField = typeof(PlayerAvatar).GetField("playerHealth", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo GodModeField = typeof(PlayerHealth).GetField("godMode", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo AvatarPhotonViewField = typeof(PlayerAvatar).GetField("photonView", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly MethodInfo StatGetRunCurrencyMethod = AccessTools.Method(typeof(SemiFunc), "StatGetRunCurrency", Type.EmptyTypes, (Type[])null);

	private static readonly MethodInfo StatSetRunCurrencyMethod = AccessTools.Method(typeof(SemiFunc), "StatSetRunCurrency", new Type[1] { typeof(int) }, (Type[])null);

	internal bool SpectatorMode { get; private set; }

	internal bool NoclipMode { get; private set; }

	private void Awake()
	{
		//IL_004e: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Expected O, but got Unknown
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Expected O, but got Unknown
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Expected O, but got Unknown
		//IL_015a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0164: Expected O, but got Unknown
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a3: Expected O, but got Unknown
		//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e2: Expected O, but got Unknown
		//IL_029f: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a9: Expected O, but got Unknown
		//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_032f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0339: Expected O, but got Unknown
		//IL_036e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0378: Expected O, but got Unknown
		//IL_03ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b7: Expected O, but got Unknown
		//IL_03ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f6: Expected O, but got Unknown
		//IL_0421: Unknown result type (might be due to invalid IL or missing references)
		//IL_042b: Expected O, but got Unknown
		//IL_0460: Unknown result type (might be due to invalid IL or missing references)
		//IL_046a: Expected O, but got Unknown
		//IL_049f: Unknown result type (might be due to invalid IL or missing references)
		//IL_04a9: Expected O, but got Unknown
		//IL_04ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_04fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_053e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0548: Expected O, but got Unknown
		//IL_0569: Unknown result type (might be due to invalid IL or missing references)
		//IL_05ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_05b7: Expected O, but got Unknown
		//IL_05ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_05f6: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable the mod");
		SpawnKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "SpawnKey", new KeyboardShortcut((KeyCode)288, (KeyCode[])(object)new KeyCode[0]), "Spawn a mimic");
		RemoveKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "RemoveKey", new KeyboardShortcut((KeyCode)289, (KeyCode[])(object)new KeyCode[0]), "Remove all mimics");
		RecordingRateHz = ((BaseUnityPlugin)this).Config.Bind<int>("General", "RecordingRateHz", 10, new ConfigDescription("Record snapshots per second", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 30), new object[0]));
		HistorySeconds = ((BaseUnityPlugin)this).Config.Bind<int>("General", "HistorySeconds", 60, new ConfigDescription("History buffer length in seconds", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 300), new object[0]));
		ReplayDelayMinSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("General", "ReplayDelayMinSeconds", 15, new ConfigDescription("Minimum replay delay", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 120), new object[0]));
		ReplayDelayMaxSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("General", "ReplayDelayMaxSeconds", 30, new ConfigDescription("Maximum replay delay", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 120), new object[0]));
		PositionInterpolationSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PositionInterpolationSpeed", 2f, new ConfigDescription("Position interpolation speed", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f), new object[0]));
		RotationInterpolationSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RotationInterpolationSpeed", 2f, new ConfigDescription("Rotation interpolation speed", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f), new object[0]));
		ShowCopiedName = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowCopiedName", true, "Show the copied player name above mimic");
		HostOnlySpawn = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "HostOnlySpawn", false, "Only host spawns mimics (mimics are currently client-local)");
		VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "VerboseLogging", false, "Log extra details");
		VerticalOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance", "VerticalOffset", 0f, "Manual vertical correction applied after automatic bounds alignment");
		TemporaryVisualDesyncDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance", "TemporaryVisualDesyncDistance", 0f, new ConfigDescription("Temporary horizontal offset between visible mimic and its real collider/hitbox; set 0 to disable", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), new object[0]));
		DebugTrail = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugTrail", false, "Draw the recorded and replayed trajectory");
		DebugTrailKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Debug", "DebugTrailKey", new KeyboardShortcut((KeyCode)287, (KeyCode[])(object)new KeyCode[0]), "Toggle trajectory visualization");
		PlaybackSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "PlaybackSpeed", 0.65f, new ConfigDescription("Replay speed multiplier", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), new object[0]));
		ChaseSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Enemy", "ChaseSpeed", 2.5f, new ConfigDescription("Movement speed while chasing", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 8f), new object[0]));
		VisionDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Enemy", "VisionDistance", 12f, new ConfigDescription("Player detection distance", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), new object[0]));
		AttackRange = ((BaseUnityPlugin)this).Config.Bind<float>("Enemy", "AttackRange", 1.5f, new ConfigDescription("Attack distance", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 5f), new object[0]));
		AttackDamage = ((BaseUnityPlugin)this).Config.Bind<int>("Enemy", "AttackDamage", 10, new ConfigDescription("Damage per attack", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), new object[0]));
		AttackCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Enemy", "AttackCooldown", 2f, new ConfigDescription("Seconds between attacks", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 10f), new object[0]));
		TurnSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "TurnSpeed", 540f, new ConfigDescription("Turning speed in degrees per second", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 1080f), new object[0]));
		SpectatorKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Spectator", "SpectatorKey", new KeyboardShortcut((KeyCode)291, (KeyCode[])(object)new KeyCode[0]), "Toggle spectator mode");
		NoclipKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Noclip", "NoclipKey", new KeyboardShortcut((KeyCode)290, (KeyCode[])(object)new KeyCode[0]), "Toggle flight and wall noclip");
		NoclipSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Noclip", "NoclipSpeed", 8f, new ConfigDescription("Flight speed", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), new object[0]));
		AddMoneyKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Cheats", "AddMoneyKey", new KeyboardShortcut((KeyCode)286, (KeyCode[])(object)new KeyCode[0]), "Add money (host/singleplayer only)");
		AddMoneyAmount = ((BaseUnityPlugin)this).Config.Bind<int>("Cheats", "AddMoneyAmount", 10000, new ConfigDescription("Dollars added per key press", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1000, 1000000), new object[0]));
		KnockbackForce = ((BaseUnityPlugin)this).Config.Bind<float>("Enemy", "KnockbackForce", 6f, new ConfigDescription("Player knockback force", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), new object[0]));
		Log.LogMessage((object)"MimicReplay loaded");
		Log.LogMessage((object)("Game version: " + Application.version));
		Harmony.CreateAndPatchAll(typeof(EnvironmentDirectorDisconnectPatch), "com.schetnikov.repo.mimicreplay.environment");
		Harmony.CreateAndPatchAll(typeof(LocalVoiceShortInputPatch), "com.schetnikov.repo.mimicreplay.voice.short");
		((MonoBehaviour)this).StartCoroutine(PeriodicUpdate());
	}

	private IEnumerator PeriodicUpdate()
	{
		while (true)
		{
			yield return (object)new WaitForSeconds(0.25f);
			if (Enabled.Value)
			{
				DiscoverPlayers();
				ProcessPendingNetworkSpawns();
			}
		}
	}

	private void Update()
	{
		//IL_0026: 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_005f: 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_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: 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_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		if (Enabled.Value)
		{
			MaintainNetworkSubscription();
			UpdateRecorders();
			UpdateMimics();
			KeyboardShortcut value = SpawnKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown() && Time.unscaledTime >= _nextSpawnAllowedTime)
			{
				_nextSpawnAllowedTime = Time.unscaledTime + 0.75f;
				SpawnMimic();
			}
			KeyboardShortcut value2 = RemoveKey.Value;
			if (((KeyboardShortcut)(ref value2)).IsDown())
			{
				RemoveAllMimics();
			}
			KeyboardShortcut value3 = DebugTrailKey.Value;
			if (((KeyboardShortcut)(ref value3)).IsDown() && Time.unscaledTime >= _nextDebugToggleAllowedTime)
			{
				_nextDebugToggleAllowedTime = Time.unscaledTime + 0.75f;
				DebugTrail.Value = !DebugTrail.Value;
				Log.LogMessage((object)("DebugTrail: " + DebugTrail.Value));
			}
			KeyboardShortcut value4 = SpectatorKey.Value;
			if (((KeyboardShortcut)(ref value4)).IsDown() && Time.unscaledTime >= _nextSpectatorToggleAllowedTime)
			{
				_nextSpectatorToggleAllowedTime = Time.unscaledTime + 0.75f;
				SetSpectatorMode(!SpectatorMode);
			}
			KeyboardShortcut value5 = NoclipKey.Value;
			if (((KeyboardShortcut)(ref value5)).IsDown() && Time.unscaledTime >= _nextNoclipToggleAllowedTime)
			{
				_nextNoclipToggleAllowedTime = Time.unscaledTime + 0.5f;
				SetNoclipMode(!NoclipMode);
			}
			KeyboardShortcut value6 = AddMoneyKey.Value;
			if (((KeyboardShortcut)(ref value6)).IsDown() && Time.unscaledTime >= _nextMoneyAllowedTime)
			{
				_nextMoneyAllowedTime = Time.unscaledTime + 0.75f;
				AddMoney();
			}
			MaintainSpectatorMode();
			MaintainNoclipMode();
		}
	}

	private void OnGUI()
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Expected O, but got Unknown
		//IL_008f: 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_00aa: Expected O, but got Unknown
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0191: Unknown result type (might be due to invalid IL or missing references)
		//IL_0196: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f0: 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)
		if (Enabled.Value && (SpectatorMode || NoclipMode || !(Time.unscaledTime >= _moneyNoticeUntil)))
		{
			if (_spectatorStyle == null)
			{
				_spectatorStyle = new GUIStyle(GUI.skin.label);
				_spectatorStyle.alignment = (TextAnchor)4;
				_spectatorStyle.fontSize = 22;
				_spectatorStyle.fontStyle = (FontStyle)1;
				_spectatorStyle.normal.textColor = new Color(0.25f, 1f, 0.9f, 1f);
				_spectatorShadowStyle = new GUIStyle(_spectatorStyle);
				_spectatorShadowStyle.normal.textColor = new Color(0f, 0f, 0f, 0.9f);
			}
			string text = ((SpectatorMode && NoclipMode) ? "SPECTATOR [F10]   |   NOCLIP [F9]" : (SpectatorMode ? "SPECTATOR MODE  [F10]" : "NOCLIP / FLIGHT  [F9]"));
			if (!SpectatorMode && !NoclipMode)
			{
				text = _moneyNotice ?? "MONEY UPDATED";
			}
			float num = Mathf.Min(420f, (float)Screen.width - 20f);
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(((float)Screen.width - num) * 0.5f, 18f, num, 38f);
			Rect val2 = default(Rect);
			((Rect)(ref val2))..ctor(((Rect)(ref val)).x - 8f, ((Rect)(ref val)).y - 3f, ((Rect)(ref val)).width + 16f, ((Rect)(ref val)).height + 6f);
			Color color = GUI.color;
			GUI.color = new Color(0.02f, 0.06f, 0.08f, 0.78f);
			GUI.Box(val2, GUIContent.none);
			GUI.color = color;
			GUI.Label(new Rect(((Rect)(ref val)).x + 2f, ((Rect)(ref val)).y + 2f, ((Rect)(ref val)).width, ((Rect)(ref val)).height), text, _spectatorShadowStyle);
			GUI.Label(val, text, _spectatorStyle);
		}
	}

	private void SetSpectatorMode(bool enabled)
	{
		PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
		PlayerHealth val2 = (PlayerHealth)(((Object)(object)val != (Object)null && PlayerHealthField != null) ? /*isinst with value type is only supported in some contexts*/: null);
		if (enabled && (Object)(object)val2 != (Object)null && GodModeField != null)
		{
			_previousGodMode = (bool)GodModeField.GetValue(val2);
		}
		SpectatorMode = enabled;
		if (!enabled && (Object)(object)val2 != (Object)null && GodModeField != null)
		{
			GodModeField.SetValue(val2, _previousGodMode);
		}
		Log.LogMessage((object)("Spectator mode: " + (enabled ? "ON" : "OFF")));
	}

	private void MaintainSpectatorMode()
	{
		if (!SpectatorMode)
		{
			return;
		}
		PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
		if (!((Object)(object)val == (Object)null))
		{
			val.EnemyVisionFreezeTimerSet(1f);
			val.OverrideDisableEnemyInvestigate(1f);
			PlayerHealth val2 = (PlayerHealth)((PlayerHealthField != null) ? /*isinst with value type is only supported in some contexts*/: null);
			if ((Object)(object)val2 != (Object)null && GodModeField != null)
			{
				GodModeField.SetValue(val2, true);
			}
		}
	}

	private void SetNoclipMode(bool enabled)
	{
		if (NoclipMode != enabled)
		{
			NoclipMode = enabled;
			if (enabled)
			{
				CaptureAndDisableLocalPhysics();
			}
			else
			{
				RestoreLocalPhysics();
			}
			Log.LogMessage((object)("Noclip mode: " + (enabled ? "ON [WASD/Space/Ctrl]" : "OFF")));
		}
	}

	private void CaptureAndDisableLocalPhysics()
	{
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		_noclipColliderStates.Clear();
		_noclipBodyStates.Clear();
		Collider[] componentsInChildren = ((Component)val).GetComponentsInChildren<Collider>(true);
		foreach (Collider val2 in componentsInChildren)
		{
			if (!((Object)(object)val2 == (Object)null))
			{
				_noclipColliderStates[val2] = val2.enabled;
				val2.enabled = false;
			}
		}
		Rigidbody[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<Rigidbody>(true);
		foreach (Rigidbody val3 in componentsInChildren2)
		{
			if (!((Object)(object)val3 == (Object)null))
			{
				_noclipBodyStates[val3] = new RigidbodyState
				{
					UseGravity = val3.useGravity,
					IsKinematic = val3.isKinematic,
					DetectCollisions = val3.detectCollisions
				};
				val3.velocity = Vector3.zero;
				val3.angularVelocity = Vector3.zero;
				val3.useGravity = false;
				val3.detectCollisions = false;
				val3.isKinematic = true;
			}
		}
	}

	private void MaintainNoclipMode()
	{
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: 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_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: 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_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_0093: 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_0096: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: 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_00b9: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: 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_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: 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_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_00eb: 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_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: 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)
		//IL_013a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
		if (!NoclipMode)
		{
			return;
		}
		PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		if (_noclipColliderStates.Count == 0 && _noclipBodyStates.Count == 0)
		{
			CaptureAndDisableLocalPhysics();
		}
		Camera main = Camera.main;
		Vector3 val2 = (((Object)(object)main != (Object)null) ? ((Component)main).transform.forward : ((Component)val).transform.forward);
		Vector3 val3 = (((Object)(object)main != (Object)null) ? ((Component)main).transform.right : ((Component)val).transform.right);
		Vector3 val4 = Vector3.zero;
		if (Input.GetKey((KeyCode)119))
		{
			val4 += val2;
		}
		if (Input.GetKey((KeyCode)115))
		{
			val4 -= val2;
		}
		if (Input.GetKey((KeyCode)100))
		{
			val4 += val3;
		}
		if (Input.GetKey((KeyCode)97))
		{
			val4 -= val3;
		}
		if (Input.GetKey((KeyCode)32))
		{
			val4 += Vector3.up;
		}
		if (Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)99))
		{
			val4 -= Vector3.up;
		}
		Rigidbody[] array = _noclipBodyStates.Keys.ToArray();
		foreach (Rigidbody val5 in array)
		{
			if (!((Object)(object)val5 == (Object)null))
			{
				val5.velocity = Vector3.zero;
				val5.angularVelocity = Vector3.zero;
			}
		}
		if (!(((Vector3)(ref val4)).sqrMagnitude < 0.001f))
		{
			float num = NoclipSpeed.Value * (Input.GetKey((KeyCode)304) ? 2f : 1f);
			Vector3 val6 = ((Vector3)(ref val4)).normalized * num * Time.unscaledDeltaTime;
			Transform val7 = (((Object)(object)val.playerTransform != (Object)null) ? val.playerTransform : ((Component)val).transform);
			val7.position += val6;
		}
	}

	private void RestoreLocalPhysics()
	{
		//IL_00c8: 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)
		foreach (KeyValuePair<Collider, bool> noclipColliderState in _noclipColliderStates)
		{
			if ((Object)(object)noclipColliderState.Key != (Object)null)
			{
				noclipColliderState.Key.enabled = noclipColliderState.Value;
			}
		}
		foreach (KeyValuePair<Rigidbody, RigidbodyState> noclipBodyState in _noclipBodyStates)
		{
			if (!((Object)(object)noclipBodyState.Key == (Object)null))
			{
				noclipBodyState.Key.useGravity = noclipBodyState.Value.UseGravity;
				noclipBodyState.Key.isKinematic = noclipBodyState.Value.IsKinematic;
				noclipBodyState.Key.detectCollisions = noclipBodyState.Value.DetectCollisions;
				noclipBodyState.Key.velocity = Vector3.zero;
				noclipBodyState.Key.angularVelocity = Vector3.zero;
			}
		}
		_noclipColliderStates.Clear();
		_noclipBodyStates.Clear();
	}

	private void AddMoney()
	{
		if (!SemiFunc.IsMasterClientOrSingleplayer())
		{
			ShowMoneyNotice("MONEY: HOST ONLY");
			Log.LogWarning((object)"F5 money ignored: only the host can change run currency");
			return;
		}
		if (StatGetRunCurrencyMethod == null || StatSetRunCurrencyMethod == null)
		{
			ShowMoneyNotice("MONEY API NOT FOUND");
			Log.LogError((object)"Run currency API was not found");
			return;
		}
		try
		{
			int num = Convert.ToInt32(StatGetRunCurrencyMethod.Invoke(null, null));
			int num2 = Mathf.Max(1, Mathf.RoundToInt((float)AddMoneyAmount.Value / 1000f));
			int num3 = ((num > int.MaxValue - num2) ? int.MaxValue : (num + num2));
			StatSetRunCurrencyMethod.Invoke(null, new object[1] { num3 });
			RefreshCurrencyUi();
			int num4 = num2 * 1000;
			ShowMoneyNotice("+$" + num4.ToString("N0") + "  [F5]");
			Log.LogMessage((object)("Money added: $" + num4 + ", balance=$" + (long)num3 * 1000L));
		}
		catch (Exception ex)
		{
			ShowMoneyNotice("MONEY ERROR — CHECK LOG");
			Log.LogError((object)("Failed to add money: " + ex));
		}
	}

	private void ShowMoneyNotice(string text)
	{
		_moneyNotice = text;
		_moneyNoticeUntil = Time.unscaledTime + 2.5f;
	}

	private static void RefreshCurrencyUi()
	{
		Type type = AccessTools.TypeByName("CurrencyUI");
		if (!(type == null))
		{
			FieldInfo fieldInfo = AccessTools.Field(type, "instance");
			object obj = ((fieldInfo != null) ? fieldInfo.GetValue(null) : null);
			MethodInfo methodInfo = AccessTools.Method(type, "FetchCurrency", Type.EmptyTypes, (Type[])null);
			if (obj != null && methodInfo != null)
			{
				methodInfo.Invoke(obj, null);
			}
		}
	}

	internal void RegisterRecorder(PlayerRecorder recorder)
	{
		_recorders.Add(recorder);
	}

	internal PlayerRecorder FindRecorderForPlayer(GameObject playerGo)
	{
		return _recorders.FirstOrDefault((PlayerRecorder r) => (Object)(object)r.TargetPlayer == (Object)(object)playerGo);
	}

	internal void RegisterMimic(MimicController mimic)
	{
		_mimics.Add(mimic);
	}

	internal void RemoveMimic(MimicController mimic)
	{
		_mimics.Remove(mimic);
	}

	internal void RemoveAllMimics()
	{
		MimicController[] array = _mimics.ToArray();
		MimicController[] array2 = array;
		foreach (MimicController mimicController in array2)
		{
			mimicController.Dispose();
		}
		_mimics.Clear();
	}

	private void DiscoverPlayers()
	{
		if (Time.unscaledTime < _nextPlayerScan)
		{
			return;
		}
		_nextPlayerScan = Time.unscaledTime + 1f;
		PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>();
		foreach (PlayerAvatar val in array)
		{
			if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy && FindRecorderForPlayer(((Component)val).gameObject) == null)
			{
				RegisterRecorder(new PlayerRecorder(val, this));
				if (VerboseLogging.Value)
				{
					Log.LogInfo((object)("Recording player: " + ((Object)((Component)val).gameObject).name));
				}
			}
		}
		_recorders.RemoveAll((PlayerRecorder r) => (Object)(object)r.TargetPlayer == (Object)null);
	}

	private void UpdateRecorders()
	{
		foreach (PlayerRecorder item in _recorders.ToList())
		{
			item.Tick();
		}
	}

	private void UpdateMimics()
	{
		foreach (MimicController item in _mimics.ToList())
		{
			item.Tick();
		}
	}

	internal void SpawnMimic()
	{
		//IL_019e: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a5: Expected O, but got Unknown
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
		if (!Enabled.Value)
		{
			return;
		}
		if (HostOnlySpawn.Value && !IsHost())
		{
			Log.LogWarning((object)"F7 ignored: HostOnlySpawn is enabled and this client is not the host");
			return;
		}
		PlayerRecorder playerRecorder = FindRandomMimicSource();
		if (playerRecorder == null)
		{
			Log.LogWarning((object)"No active player has enough recorded history to spawn a mimic yet");
			return;
		}
		_ = playerRecorder.TargetPlayer;
		PlayerSnapshot playerSnapshot = playerRecorder.History[0];
		PlayerSnapshot playerSnapshot2 = playerRecorder.History[playerRecorder.History.Count - 1];
		float num = playerSnapshot2.Timestamp - playerSnapshot.Timestamp;
		if (num < (float)ReplayDelayMinSeconds.Value)
		{
			Log.LogWarning((object)("Not enough recorded history. Available: " + num.ToString("F1") + "s, required: " + ReplayDelayMinSeconds.Value + "s"));
			return;
		}
		float num2 = Mathf.Min((float)ReplayDelayMaxSeconds.Value, num);
		float num3 = Mathf.Min((float)ReplayDelayMinSeconds.Value, num2);
		float num4 = Random.Range(num3, num2);
		PhotonView val = (PhotonView)((AvatarPhotonViewField != null) ? /*isinst with value type is only supported in some contexts*/: null);
		if (PhotonNetwork.InRoom && (Object)(object)val != (Object)null && val.Owner != null)
		{
			object[] array = new object[3] { "MimicReplaySpawnV1", val.OwnerActorNr, num4 };
			RaiseEventOptions val2 = new RaiseEventOptions();
			val2.Receivers = (ReceiverGroup)1;
			val2.CachingOption = (EventCaching)0;
			RaiseEventOptions val3 = val2;
			bool flag = PhotonNetwork.RaiseEvent((byte)197, (object)array, val3, SendOptions.SendReliable);
			Log.LogMessage((object)("Network mimic spawn sent=" + flag + " sourceActor=" + val.OwnerActorNr + " delay=" + num4.ToString("F2")));
		}
		else
		{
			SpawnMimicForRecorder(playerRecorder, num4, "local", authoritative: true);
		}
	}

	private void SpawnMimicForRecorder(PlayerRecorder recorder, float selectedDelay, string origin, bool authoritative)
	{
		if (recorder == null || (Object)(object)recorder.TargetPlayer == (Object)null || recorder.History.Count < 2)
		{
			return;
		}
		GameObject targetPlayer = recorder.TargetPlayer;
		PlayerSnapshot playerSnapshot = recorder.History[0];
		PlayerSnapshot playerSnapshot2 = recorder.History[recorder.History.Count - 1];
		ManualLogSource log = Log;
		object[] array = new object[14]
		{
			"Mimic spawn origin=",
			origin,
			" source=",
			((Object)targetPlayer).name,
			" id=",
			((Object)targetPlayer).GetInstanceID(),
			" snapshots=",
			recorder.History.Count,
			" oldest=",
			null,
			null,
			null,
			null,
			null
		};
		float timestamp = playerSnapshot.Timestamp;
		array[9] = timestamp.ToString("F3");
		array[10] = " newest=";
		float timestamp2 = playerSnapshot2.Timestamp;
		array[11] = timestamp2.ToString("F3");
		array[12] = " delay=";
		array[13] = selectedDelay.ToString("F3");
		log.LogMessage((object)string.Concat(array));
		try
		{
			MimicController item = new MimicController(targetPlayer, recorder, selectedDelay, this, authoritative);
			_mimics.Add(item);
			Log.LogMessage((object)("Spawned mimic for " + ((Object)targetPlayer).name));
		}
		catch (Exception ex)
		{
			Log.LogError((object)("Failed to create mimic: " + ex));
		}
	}

	private void OnNetworkEvent(EventData eventData)
	{
		if (eventData != null && eventData.Code == 197 && eventData.CustomData is object[] array && array.Length >= 3 && array[0] is string && !((string)array[0] != "MimicReplaySpawnV1"))
		{
			int num = Convert.ToInt32(array[1]);
			float num2 = Convert.ToSingle(array[2]);
			bool authoritative = PhotonNetwork.LocalPlayer != null && eventData.Sender == PhotonNetwork.LocalPlayer.ActorNumber;
			PlayerRecorder playerRecorder = FindRecorderForActor(num);
			if (playerRecorder != null && playerRecorder.History.Count >= 2)
			{
				float num3 = playerRecorder.History[playerRecorder.History.Count - 1].Timestamp - playerRecorder.History[0].Timestamp;
				SpawnMimicForRecorder(playerRecorder, Mathf.Min(num2, num3), "network actor=" + num, authoritative);
				return;
			}
			_pendingNetworkSpawns.Add(new PendingNetworkSpawn
			{
				ActorNumber = num,
				Delay = num2,
				Expires = Time.unscaledTime + 15f,
				Authoritative = authoritative
			});
			Log.LogInfo((object)("Network mimic spawn queued sourceActor=" + num));
		}
	}

	private void MaintainNetworkSubscription()
	{
		LoadBalancingClient networkingClient = PhotonNetwork.NetworkingClient;
		bool flag = PhotonNetwork.InRoom && networkingClient != null;
		if (flag && !_networkSubscribed)
		{
			networkingClient.EventReceived += OnNetworkEvent;
			_networkSubscribed = true;
			Log.LogInfo((object)"MimicReplay network listener enabled after room join");
		}
		else if (!flag && _networkSubscribed)
		{
			if (networkingClient != null)
			{
				networkingClient.EventReceived -= OnNetworkEvent;
			}
			_networkSubscribed = false;
			Log.LogInfo((object)"MimicReplay network listener disabled outside room");
		}
	}

	private PlayerRecorder FindRecorderForActor(int actorNumber)
	{
		foreach (PlayerRecorder recorder in _recorders)
		{
			if (recorder != null && !((Object)(object)recorder.Avatar == (Object)null))
			{
				PhotonView val = (PhotonView)((AvatarPhotonViewField != null) ? /*isinst with value type is only supported in some contexts*/: null);
				if ((Object)(object)val != (Object)null && val.OwnerActorNr == actorNumber)
				{
					return recorder;
				}
			}
		}
		return null;
	}

	private void ProcessPendingNetworkSpawns()
	{
		for (int num = _pendingNetworkSpawns.Count - 1; num >= 0; num--)
		{
			PendingNetworkSpawn pendingNetworkSpawn = _pendingNetworkSpawns[num];
			if (Time.unscaledTime >= pendingNetworkSpawn.Expires)
			{
				Log.LogWarning((object)("Network mimic spawn expired sourceActor=" + pendingNetworkSpawn.ActorNumber));
				_pendingNetworkSpawns.RemoveAt(num);
			}
			else
			{
				PlayerRecorder playerRecorder = FindRecorderForActor(pendingNetworkSpawn.ActorNumber);
				if (playerRecorder != null && playerRecorder.History.Count >= 2)
				{
					float num2 = playerRecorder.History[playerRecorder.History.Count - 1].Timestamp - playerRecorder.History[0].Timestamp;
					if (!(num2 < Mathf.Min((float)ReplayDelayMinSeconds.Value, pendingNetworkSpawn.Delay)))
					{
						SpawnMimicForRecorder(playerRecorder, Mathf.Min(pendingNetworkSpawn.Delay, num2), "queued network actor=" + pendingNetworkSpawn.ActorNumber, pendingNetworkSpawn.Authoritative);
						_pendingNetworkSpawns.RemoveAt(num);
					}
				}
			}
		}
	}

	private PlayerRecorder FindRandomMimicSource()
	{
		List<PlayerRecorder> list = new List<PlayerRecorder>();
		foreach (PlayerRecorder recorder in _recorders)
		{
			if (recorder != null && !((Object)(object)recorder.Avatar == (Object)null) && !((Object)(object)recorder.TargetPlayer == (Object)null) && recorder.TargetPlayer.activeInHierarchy && recorder.History.Count >= 2)
			{
				PlayerSnapshot playerSnapshot = recorder.History[0];
				PlayerSnapshot playerSnapshot2 = recorder.History[recorder.History.Count - 1];
				if (playerSnapshot2.Timestamp - playerSnapshot.Timestamp >= (float)ReplayDelayMinSeconds.Value)
				{
					list.Add(recorder);
				}
			}
		}
		if (list.Count == 0)
		{
			return null;
		}
		PlayerRecorder playerRecorder = list[Random.Range(0, list.Count)];
		Log.LogMessage((object)("Random mimic source selected index=" + list.IndexOf(playerRecorder) + "/" + list.Count + " object=" + ((Object)playerRecorder.TargetPlayer).name + " avatarId=" + ((Object)playerRecorder.Avatar).GetInstanceID()));
		return playerRecorder;
	}

	private bool IsHost()
	{
		return SemiFunc.IsMasterClientOrSingleplayer();
	}

	private void OnDestroy()
	{
		if (_networkSubscribed && PhotonNetwork.NetworkingClient != null)
		{
			PhotonNetwork.NetworkingClient.EventReceived -= OnNetworkEvent;
		}
		_networkSubscribed = false;
		if (NoclipMode)
		{
			SetNoclipMode(enabled: false);
		}
		if (SpectatorMode)
		{
			SetSpectatorMode(enabled: false);
		}
		RemoveAllMimics();
	}
}
[HarmonyPatch(typeof(EnvironmentDirector), "AmbientLightLogic")]
internal static class EnvironmentDirectorDisconnectPatch
{
	private static Exception Finalizer(Exception __exception)
	{
		if (__exception is NullReferenceException && !PhotonNetwork.IsConnected)
		{
			return null;
		}
		return __exception;
	}
}
[HarmonyPatch]
internal static class LocalVoiceInputPatch
{
	private static IEnumerable<MethodBase> TargetMethods()
	{
		Type type = AccessTools.TypeByName("Photon.Voice.LocalVoiceAudioFloat");
		if (!(type == null))
		{
			MethodInfo push = AccessTools.Method(type, "PushData", new Type[1] { typeof(float[]) }, (Type[])null);
			MethodInfo pushAsync = AccessTools.Method(type, "PushDataAsync", new Type[1] { typeof(float[]) }, (Type[])null);
			if (push != null)
			{
				yield return push;
			}
			if (pushAsync != null)
			{
				yield return pushAsync;
			}
		}
	}

	private static void Prefix(float[] __0)
	{
		VoicePhraseCapture.AcceptLocalFrame(__0);
	}
}
[HarmonyPatch]
internal static class LocalVoiceShortInputPatch
{
	private static MethodBase TargetMethod()
	{
		Type type = AccessTools.TypeByName("Photon.Voice.LocalVoiceFramed`1");
		if (type == null)
		{
			return null;
		}
		Type type2 = type.MakeGenericType(typeof(short));
		return AccessTools.Method(type2, "PushDataAsync", new Type[1] { typeof(short[]) }, (Type[])null);
	}

	private static void Prefix(short[] __0)
	{
		VoicePhraseCapture.AcceptLocalShortFrame(__0);
	}
}
[HarmonyPatch]
internal static class WindowsMicrophoneInputPatch
{
	private static MethodBase TargetMethod()
	{
		Type type = AccessTools.TypeByName("Photon.Voice.Windows.WindowsAudioInPusher");
		if (!(type != null))
		{
			return null;
		}
		return AccessTools.Method(type, "push", (Type[])null, (Type[])null);
	}

	private static void Prefix(object __instance, IntPtr __0, int __1)
	{
		Type type = __instance.GetType();
		MethodInfo methodInfo = AccessTools.PropertyGetter(type, "Channels");
		MethodInfo methodInfo2 = AccessTools.PropertyGetter(type, "SamplingRate");
		int channels = ((!(methodInfo != null)) ? 1 : ((int)methodInfo.Invoke(__instance, null)));
		int sampleRate = ((methodInfo2 != null) ? ((int)methodInfo2.Invoke(__instance, null)) : 48000);
		VoicePhraseCapture.AcceptLocalPcm16(__0, __1, channels, sampleRate);
	}
}
[HarmonyPatch]
internal static class UnityMicrophoneReaderPatch
{
	private static MethodBase TargetMethod()
	{
		Type type = AccessTools.TypeByName("Photon.Voice.Unity.MicWrapper");
		if (!(type != null))
		{
			return null;
		}
		return AccessTools.Method(type, "Read", (Type[])null, (Type[])null);
	}

	private static void Postfix(float[] __0, bool __result)
	{
		if (__result)
		{
			VoicePhraseCapture.AcceptLocalFrame(__0);
		}
	}
}
[HarmonyPatch]
internal static class UnityMicrophonePusherPatch
{
	private static MethodBase TargetMethod()
	{
		Type type = AccessTools.TypeByName("Photon.Voice.Unity.MicWrapperPusherOnAudioFilterRead");
		if (!(type != null))
		{
			return null;
		}
		return AccessTools.Method(type, "OnAudioFilterRead", (Type[])null, (Type[])null);
	}

	private static void Prefix(float[] __0)
	{
		VoicePhraseCapture.AcceptLocalFrame(__0);
	}
}
public static class MeshFactory
{
	public static Mesh CreateCubeMesh()
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Expected O, but got Unknown
		//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_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//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_0084: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		Mesh val = new Mesh();
		val.vertices = (Vector3[])(object)new Vector3[8]
		{
			new Vector3(-0.5f, -0.5f, -0.5f),
			new Vector3(0.5f, -0.5f, -0.5f),
			new Vector3(0.5f, 0.5f, -0.5f),
			new Vector3(-0.5f, 0.5f, -0.5f),
			new Vector3(-0.5f, 0.5f, 0.5f),
			new Vector3(0.5f, 0.5f, 0.5f),
			new Vector3(0.5f, -0.5f, 0.5f),
			new Vector3(-0.5f, -0.5f, 0.5f)
		};
		val.triangles = new int[36]
		{
			0, 2, 1, 0, 3, 2, 2, 3, 4, 2,
			4, 5, 1, 2, 5, 1, 5, 6, 0, 7,
			4, 0, 4, 3, 5, 4, 7, 5, 7, 6,
			0, 1, 6, 0, 6, 7
		};
		val.RecalculateNormals();
		return val;
	}
}
internal sealed class MimicAppearanceResult
{
	internal bool Success;

	internal string Error;

	internal Animator Animator;

	internal Transform FacingTransform;
}
internal static class MimicAppearance
{
	internal static MimicAppearanceResult Create(PlayerAvatar avatar, Transform destination)
	{
		//IL_006f: 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_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_023f: Unknown result type (might be due to invalid IL or missing references)
		MimicAppearanceResult mimicAppearanceResult = new MimicAppearanceResult();
		if ((Object)(object)avatar == (Object)null)
		{
			mimicAppearanceResult.Error = "PlayerAvatar is null";
			return mimicAppearanceResult;
		}
		GameObject val = FindVisualRoot(avatar);
		if ((Object)(object)val == (Object)null)
		{
			mimicAppearanceResult.Error = "playerAvatarVisuals was not found";
			return mimicAppearanceResult;
		}
		GameObject val2 = Object.Instantiate<GameObject>(val);
		((Object)val2).name = ((Object)val).name + "_MimicClone";
		val2.transform.SetParent(destination, false);
		val2.transform.localPosition = val.transform.localPosition;
		val2.transform.localRotation = val.transform.localRotation;
		val2.transform.localScale = val.transform.localScale;
		SetLayerRecursively(val2, 0);
		Transform val3 = null;
		Transform[] componentsInChildren = val2.GetComponentsInChildren<Transform>(true);
		foreach (Transform val4 in componentsInChildren)
		{
			if (((Object)val4).name == "ANIM BOT")
			{
				val3 = val4;
				break;
			}
		}
		List<string> list = new List<string>();
		Component[] componentsInChildren2 = val2.GetComponentsInChildren<Component>(true);
		foreach (Component val5 in componentsInChildren2)
		{
			if (!((Object)(object)val5 == (Object)null) && !(val5 is Transform) && !(val5 is Renderer) && !(val5 is Animator) && !(val5 is MeshFilter) && (val5 is Camera || val5 is AudioListener || val5 is Collider || val5 is Rigidbody || val5 is MonoBehaviour))
			{
				list.Add(((object)val5).GetType().FullName);
				Behaviour val6 = (Behaviour)(object)((val5 is Behaviour) ? val5 : null);
				if ((Object)(object)val6 != (Object)null)
				{
					val6.enabled = false;
				}
				Object.Destroy((Object)(object)val5);
			}
		}
		Renderer[] componentsInChildren3 = val2.GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val7 in componentsInChildren3)
		{
			if ((Object)(object)val3 != (Object)null && (Object)(object)((Component)val7).transform != (Object)(object)val3 && !((Component)val7).transform.IsChildOf(val3))
			{
				val7.enabled = false;
				continue;
			}
			val7.enabled = true;
			val7.shadowCastingMode = (ShadowCastingMode)1;
			val7.receiveShadows = true;
			Material[] sharedMaterials = val7.sharedMaterials;
			Material[] array = (Material[])(object)new Material[sharedMaterials.Length];
			for (int l = 0; l < sharedMaterials.Length; l++)
			{
				array[l] = (((Object)(object)sharedMaterials[l] != (Object)null) ? new Material(sharedMaterials[l]) : ((Material)null));
			}
			val7.materials = array;
		}
		Renderer[] componentsInChildren4 = val2.GetComponentsInChildren<Renderer>(true);
		SkinnedMeshRenderer[] componentsInChildren5 = val2.GetComponentsInChildren<SkinnedMeshRenderer>(true);
		Animator componentInChildren = val2.GetComponentInChildren<Animator>(true);
		if ((Object)(object)componentInChildren != (Object)null)
		{
			componentInChildren.applyRootMotion = false;
			componentInChildren.cullingMode = (AnimatorCullingMode)0;
		}
		mimicAppearanceResult.Animator = componentInChildren;
		mimicAppearanceResult.FacingTransform = (((Object)(object)val3 != (Object)null) ? val3 : (((Object)(object)componentInChildren != (Object)null) ? ((Component)componentInChildren).transform : val2.transform));
		Plugin.Log.LogMessage((object)("Mimic appearance source=" + ((Object)((Component)avatar).gameObject).name + " visualPath=" + GetPath(val.transform) + " renderers=" + componentsInChildren4.Length + " skinnedRenderers=" + componentsInChildren5.Length + " animator=" + ((Object)(object)componentInChildren != (Object)null) + " disabled=[" + string.Join(",", list.ToArray()) + "]"));
		if (componentsInChildren4.Length == 0)
		{
			mimicAppearanceResult.Error = "visual root contains no Renderer";
			return mimicAppearanceResult;
		}
		mimicAppearanceResult.Success = true;
		return mimicAppearanceResult;
	}

	private static void SetLayerRecursively(GameObject root, int layer)
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Expected O, but got Unknown
		root.layer = layer;
		foreach (Transform item in root.transform)
		{
			Transform val = item;
			SetLayerRecursively(((Component)val).gameObject, layer);
		}
	}

	private static GameObject FindVisualRoot(PlayerAvatar avatar)
	{
		FieldInfo field = typeof(PlayerAvatar).GetField("playerAvatarVisuals", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field != null)
		{
			object? value = field.GetValue(avatar);
			Component val = (Component)((value is Component) ? value : null);
			if ((Object)(object)val != (Object)null)
			{
				return val.gameObject;
			}
		}
		Renderer[] componentsInChildren = ((Component)avatar).GetComponentsInChildren<Renderer>(true);
		if (componentsInChildren.Length == 0)
		{
			return null;
		}
		Transform val2 = ((Component)componentsInChildren[0]).transform;
		while ((Object)(object)val2.parent != (Object)null && (Object)(object)val2.parent != (Object)(object)((Component)avatar).transform)
		{
			val2 = val2.parent;
		}
		return ((Component)val2).gameObject;
	}

	internal static Bounds GetCombinedBounds(GameObject root)
	{
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true);
		List<Renderer> list = new List<Renderer>();
		Renderer[] array = componentsInChildren;
		foreach (Renderer val in array)
		{
			if (val.enabled && ((Component)val).gameObject.activeInHierarchy)
			{
				list.Add(val);
			}
		}
		if (list.Count == 0)
		{
			list.AddRange(componentsInChildren);
		}
		Bounds bounds = list[0].bounds;
		for (int j = 1; j < list.Count; j++)
		{
			((Bounds)(ref bounds)).Encapsulate(list[j].bounds);
		}
		return bounds;
	}

	private static string GetPath(Transform transform)
	{
		string text = ((Object)transform).name;
		while ((Object)(object)transform.parent != (Object)null)
		{
			transform = transform.parent;
			text = ((Object)transform).name + "/" + text;
		}
		return text;
	}
}
public class MimicController
{
	private enum MimicMode
	{
		Replay,
		Chase,
		Attack,
		StuckRecovery
	}

	private readonly GameObject _mimicObject;

	private readonly GameObject _visualContainer;

	private readonly Plugin _plugin;

	private readonly bool _authoritative;

	private readonly PlayerRecorder _recorder;

	private readonly Animator _animator;

	private readonly Transform _visualFacing;

	private readonly AudioSource _voiceSource;

	private readonly CapsuleCollider _capsule;

	private readonly NavMeshAgent _agent;

	private readonly EnemyNavMeshAgent _enemyNavigation;

	private List<PlayerSnapshot> _segment;

	private float _sourceStartTime;

	private float _sourceEndTime;

	private bool _reversePlayback;

	private float _localStartTime;

	private float _nextVerboseLogTime;

	private float _nextVisionCheck;

	private float _lastSeenTime;

	private float _nextAttackTime;

	private float _attackStateUntil;

	private float _nextRepathTime;

	private float _nextDoorCheck;

	private float _nextStuckCheck;

	private float _stuckSince;

	private Vector3 _recoveryStartPosition;

	private int _recoveryAttempts;

	private float _lastInteractionTimestamp = -1f;

	private Vector3 _lastProgressPosition;

	private Vector3 _retreatDirection;

	private Vector3 _retreatDestination;

	private Vector3 _lastMovementPosition;

	private Vector3 _lastReplayPosition;

	private float _nextVoiceTime;

	private Vector3 _lastPathDestination;

	private bool _hasPathDestination;

	private PlayerAvatar _chaseTarget;

	private MimicMode _mode;

	private LineRenderer _recordedTrail;

	private LineRenderer _replayTrail;

	private bool _wasJumping;

	private bool _wasSprinting;

	private bool _chaseCrouching;

	private bool _chaseCrawling;

	private GameObject _fakeHeldObject;

	private int _fakeHeldSourceId;

	private static readonly FieldInfo PlayerHealthField = typeof(PlayerAvatar).GetField("playerHealth", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerCrouchingField = typeof(PlayerAvatar).GetField("isCrouching", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerCrawlingField = typeof(PlayerAvatar).GetField("isCrawling", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly MethodInfo DoorOpenMethod = typeof(PhysGrabHinge).GetMethod("OpenImpulse", BindingFlags.Instance | BindingFlags.NonPublic);

	public MimicController(GameObject source, PlayerRecorder recorder, float selectedDelay, Plugin plugin, bool authoritative)
	{
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Expected O, but got Unknown
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Expected O, but got Unknown
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: 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_021c: 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_022e: Unknown result type (might be due to invalid IL or missing references)
		//IL_023a: Unknown result type (might be due to invalid IL or missing references)
		//IL_024a: Unknown result type (might be due to invalid IL or missing references)
		//IL_024f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0264: Unknown result type (might be due to invalid IL or missing references)
		//IL_0269: Unknown result type (might be due to invalid IL or missing references)
		//IL_026e: Unknown result type (might be due to invalid IL or missing references)
		//IL_027c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0281: Unknown result type (might be due to invalid IL or missing references)
		//IL_0283: Unknown result type (might be due to invalid IL or missing references)
		//IL_0285: Unknown result type (might be due to invalid IL or missing references)
		//IL_028a: 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_016e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0368: Unknown result type (might be due to invalid IL or missing references)
		//IL_037e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0559: Unknown result type (might be due to invalid IL or missing references)
		//IL_055e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0565: Unknown result type (might be due to invalid IL or missing references)
		//IL_056a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0571: Unknown result type (might be due to invalid IL or missing references)
		//IL_0576: 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_02e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0301: 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_01b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b5: 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_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d9: 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)
		_plugin = plugin;
		_authoritative = authoritative;
		_recorder = recorder;
		_segment = new List<PlayerSnapshot>(recorder.History.Items);
		PlayerSnapshot playerSnapshot = _segment[_segment.Count - 1];
		_sourceStartTime = playerSnapshot.Timestamp - selectedDelay;
		_sourceEndTime = playerSnapshot.Timestamp;
		TrimToReplayWindow();
		_mimicObject = new GameObject("MimicReplay_Mimic");
		_visualContainer = new GameObject("Visual");
		_visualContainer.transform.SetParent(_mimicObject.transform, false);
		PlayerSnapshot playerSnapshot2 = _segment[0];
		_mimicObject.transform.position = playerSnapshot2.Position;
		_mimicObject.transform.rotation = playerSnapshot2.Rotation;
		MimicAppearanceResult mimicAppearanceResult = MimicAppearance.Create(source.GetComponent<PlayerAvatar>(), _visualContainer.transform);
		if (!mimicAppearanceResult.Success)
		{
			Object.Destroy((Object)(object)_mimicObject);
			throw new InvalidOperationException("Mimic visual clone failed: " + mimicAppearanceResult.Error);
		}
		_animator = mimicAppearanceResult.Animator;
		_visualFacing = mimicAppearanceResult.FacingTransform;
		if ((Object)(object)mimicAppearanceResult.FacingTransform != (Object)null)
		{
			Vector3 forward = mimicAppearanceResult.FacingTransform.forward;
			forward.y = 0f;
			Vector3 val = playerSnapshot2.Rotation * Vector3.forward;
			val.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude > 0.001f && ((Vector3)(ref val)).sqrMagnitude > 0.001f)
			{
				Quaternion val2 = Quaternion.FromToRotation(((Vector3)(ref forward)).normalized, ((Vector3)(ref val)).normalized);
				_visualContainer.transform.rotation = val2 * _visualContainer.transform.rotation;
				ManualLogSource log = Plugin.Log;
				float y = ((Quaternion)(ref val2)).eulerAngles.y;
				log.LogInfo((object)("Mimic visual facing calibrated yaw=" + y.ToString("F1")));
			}
		}
		Bounds combinedBounds = MimicAppearance.GetCombinedBounds(_visualContainer);
		Vector3 val3 = default(Vector3);
		((Vector3)(ref val3))..ctor(((Bounds)(ref combinedBounds)).center.x, ((Bounds)(ref combinedBounds)).min.y, ((Bounds)(ref combinedBounds)).center.z);
		Vector3 val4 = playerSnapshot2.Position + Vector3.up * _plugin.VerticalOffset.Value;
		Transform transform = _visualContainer.transform;
		transform.position += val4 - val3;
		if (_plugin.TemporaryVisualDesyncDistance.Value > 0.01f)
		{
			float num = Random.Range(0f, (float)Math.PI * 2f);
			Vector3 val5 = new Vector3(Mathf.Cos(num), 0f, Mathf.Sin(num)) * _plugin.TemporaryVisualDesyncDistance.Value;
			Transform transform2 = _visualContainer.transform;
			transform2.position += val5;
			Plugin.Log.LogWarning((object)("Temporary visual desync enabled: visible body offset=" + ((Vector3)(ref val5)).ToString("F2") + " while collider/hitbox remains on mimic root"));
		}
		_capsule = _mimicObject.AddComponent<CapsuleCollider>();
		_capsule.radius = 0.36f;
		_capsule.height = playerSnapshot2.CapsuleHeight;
		_capsule.center = Vector3.up * (_capsule.height * 0.5f);
		((Collider)_capsule).isTrigger = false;
		Rigidbody val6 = _mimicObject.AddComponent<Rigidbody>();
		val6.isKinematic = true;
		val6.useGravity = false;
		_agent = _mimicObject.AddComponent<NavMeshAgent>();
		((Behaviour)_agent).enabled = false;
		_agent.radius = 0.36f;
		_agent.height = 1.8f;
		_agent.speed = _plugin.ChaseSpeed.Value;
		_agent.acceleration = 12f;
		_agent.angularSpeed = _plugin.TurnSpeed.Value;
		_agent.stoppingDistance = Mathf.Max(0.2f, _plugin.AttackRange.Value * 0.75f);
		_agent.autoRepath = true;
		_agent.autoTraverseOffMeshLink = true;
		_agent.updateRotation = false;
		_agent.autoBraking = true;
		_agent.obstacleAvoidanceType = (ObstacleAvoidanceType)4;
		_agent.avoidancePriority = Random.Range(25, 60);
		_enemyNavigation = _mimicObject.AddComponent<EnemyNavMeshAgent>();
		((Behaviour)_enemyNavigation).enabled = false;
		_voiceSource = _mimicObject.AddComponent<AudioSource>();
		_voiceSource.playOnAwake = false;
		_voiceSource.loop = false;
		_voiceSource.spatialBlend = 1f;
		_voiceSource.minDistance = 2f;
		_voiceSource.maxDistance = 18f;
		_voiceSource.rolloffMode = (AudioRolloffMode)1;
		_nextVoiceTime = Time.time + Random.Range(6f, 14f);
		_localStartTime = Time.time;
		_lastProgressPosition = playerSnapshot2.Position;
		_lastMovementPosition = playerSnapshot2.Position;
		_lastReplayPosition = playerSnapshot2.Position;
		BuildTrails();
		LogMode("created", null);
	}

	private void TrimToReplayWindow()
	{
		while (_segment.Count > 2 && _segment[1].Timestamp < _sourceStartTime)
		{
			_segment.RemoveAt(0);
		}
	}

	public void Tick()
	{
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_mimicObject == (Object)null) && _segment.Count >= 2)
		{
			UpdateVision();
			if (_mode == MimicMode.Replay)
			{
				TickReplay();
			}
			else if (_mode == MimicMode.Chase)
			{
				TickChase();
			}
			else if (_mode == MimicMode.Attack)
			{
				TickAttack();
			}
			else
			{
				TickStuckRecovery();
			}
			TickVoiceReplay();
			UpdateReplayTrail(_mimicObject.transform.position);
			if (_plugin.VerboseLogging.Value && Time.unscaledTime >= _nextVerboseLogTime)
			{
				_nextVerboseLogTime = Time.unscaledTime + 1f;
				LogPath("periodic");
			}
		}
	}

	private void TickReplay()
	{
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: 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_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: 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_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: 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_0154: 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_0186: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_chaseTarget != (Object)null)
		{
			EnterChase("target detected");
			return;
		}
		float num = _sourceEndTime - _sourceStartTime;
		if (num <= 0f)
		{
			return;
		}
		float num2 = (Time.time - _localStartTime) * _plugin.PlaybackSpeed.Value;
		if (num2 >= num)
		{
			ChooseNextRoute();
			num = _sourceEndTime - _sourceStartTime;
			num2 = 0f;
		}
		float num3 = (_reversePlayback ? (_sourceEndTime - num2) : (_sourceStartTime + num2));
		FindNeighbours(num3, out var previous, out var next);
		float num4 = next.Timestamp - previous.Timestamp;
		float num5 = ((num4 > 0.0001f) ? Mathf.Clamp01((num3 - previous.Timestamp) / num4) : 0f);
		_mimicObject.transform.position = Vector3.Lerp(previous.Position, next.Position, num5);
		Vector3 direction = _mimicObject.transform.position - _lastReplayPosition;
		direction.y = 0f;
		_lastReplayPosition = _mimicObject.transform.position;
		if (((Vector3)(ref direction)).sqrMagnitude > 4E-06f)
		{
			UpdateFacing(direction);
		}
		else
		{
			Quaternion val = Quaternion.Slerp(previous.Rotation, next.Rotation, num5);
			if (_reversePlayback)
			{
				val *= Quaternion.Euler(0f, 180f, 0f);
			}
			_mimicObject.transform.rotation = val;
		}
		ApplyCapsule(previous.CapsuleHeight);
		UpdateFakeHeldObject(previous, next, num5);
		if (next.InteractStarted && next.Timestamp != _lastInteractionTimestamp)
		{
			_lastInteractionTimestamp = next.Timestamp;
			TryOpenNearbyDoor("recorded interaction");
		}
		UpdateAnimation(previous, next, num4);
	}

	private void EnterChase(string reason)
	{
		//IL_001a: 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_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: 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)
		if (!((Object)(object)_chaseTarget == (Object)null))
		{
			NavMeshHit val = default(NavMeshHit);
			if (!NavMesh.SamplePosition(_mimicObject.transform.position, ref val, 0.75f, -1))
			{
				EnterStuckRecovery(reason + "; replay endpoint is not on NavMesh");
				return;
			}
			((Behaviour)_agent).enabled = true;
			((Behaviour)_enemyNavigation).enabled = true;
			_agent.Warp(((NavMeshHit)(ref val)).position);
			ApplyCapsule(1.8f);
			_lastProgressPosition = _mimicObject.transform.position;
			_lastMovementPosition = _mimicObject.transform.position;
			_nextStuckCheck = Time.time + 0.75f;
			Transition(MimicMode.Chase, reason);
			Repath("enter chase");
		}
	}

	private void TickChase()
	{
		//IL_003d: 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_006f: 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)
		if ((Object)(object)_chaseTarget == (Object)null)
		{
			EnterReplay("target lost");
			return;
		}
		Transform targetTransform = GetTargetTransform(_chaseTarget);
		UpdateChasePosture(_chaseTarget);
		float num = Vector3.Distance(_mimicObject.transform.position, targetTransform.position);
		if (num <= _plugin.AttackRange.Value)
		{
			Transition(MimicMode.Attack, "target entered attack range");
			BeginAttack(targetTransform.position);
			return;
		}
		if (Time.time >= _nextRepathTime)
		{
			Repath("interval");
		}
		if (Time.time >= _nextDoorCheck)
		{
			TryOpenNearbyDoor("chase proximity");
		}
		UpdateFacing(GetNavigationFacingDirection());
		CheckStuck(num);
		UpdateChaseAnimation();
	}

	private void TickAttack()
	{
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_chaseTarget != (Object)null)
		{
			UpdateChasePosture(_chaseTarget);
		}
		if (Time.time < _attackStateUntil)
		{
			if (((Behaviour)_agent).enabled && _agent.isOnNavMesh)
			{
				_agent.speed = _plugin.ChaseSpeed.Value;
				UpdateFacing(GetNavigationFacingDirection());
			}
		}
		else if ((Object)(object)_chaseTarget == (Object)null)
		{
			EnterReplay("attack retreat complete; target lost");
		}
		else
		{
			Transition(MimicMode.Chase, "attack retreat complete");
			Repath("after attack");
		}
	}

	private void BeginAttack(Vector3 targetPosition)
	{
		//IL_00b0: 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_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: 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)
		//IL_012f: 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_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: 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)
		//IL_0177: 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_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_018e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: 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)
		if ((Object)(object)_chaseTarget == (Object)null)
		{
			return;
		}
		PlayerAvatar chaseTarget = _chaseTarget;
		if (_authoritative && (!_plugin.SpectatorMode || !((Object)(object)chaseTarget == (Object)(object)SemiFunc.PlayerAvatarLocal())) && Time.time >= _nextAttackTime)
		{
			PlayerHealth val = (PlayerHealth)((PlayerHealthField != null) ? /*isinst with value type is only supported in some contexts*/: null);
			if ((Object)(object)val != (Object)null)
			{
				_nextAttackTime = Time.time + _plugin.AttackCooldown.Value;
				val.Hurt(_plugin.AttackDamage.Value, false, -1, false);
				Vector3 val2 = targetPosition - _mimicObject.transform.position;
				val2.y = 0f;
				if (((Vector3)(ref val2)).sqrMagnitude < 0.01f)
				{
					val2 = _mimicObject.transform.forward;
				}
				chaseTarget.ForceImpulse(((Vector3)(ref val2)).normalized * _plugin.KnockbackForce.Value + Vector3.up * (_plugin.KnockbackForce.Value * 0.3f));
				_retreatDirection = -((Vector3)(ref val2)).normalized;
				_retreatDestination = _mimicObject.transform.position + _retreatDirection * 4f;
				NavMeshHit val3 = default(NavMeshHit);
				if (NavMesh.SamplePosition(_retreatDestination, ref val3, 2f, -1))
				{
					_retreatDestination = ((NavMeshHit)(ref val3)).position;
				}
				if (((Behaviour)_agent).enabled && _agent.isOnNavMesh)
				{
					_agent.SetDestination(_retreatDestination);
				}
				_attackStateUntil = Time.time + Random.Range(3f, 5f);
				Plugin.Log.LogMessage((object)("Mimic attack target=" + ((Object)((Component)chaseTarget).gameObject).name + " retreatUntil=" + _attackStateUntil.ToString("F2")));
				return;
			}
		}
		_attackStateUntil = Time.time + 0.25f;
	}

	private void TickStuckRecovery()
	{
		//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_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: 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_00f4: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_chaseTarget == (Object)null)
		{
			EnterReplay("stuck recovery has no target");
			return;
		}
		float num = Vector3.Distance(_recoveryStartPosition, _mimicObject.transform.position);
		if (num >= 0.2f)
		{
			_lastProgressPosition = _mimicObject.transform.position;
			_nextStuckCheck = Time.time + 0.75f;
			Transition(MimicMode.Chase, "stock navigation restored progress after attempts=" + _recoveryAttempts);
			return;
		}
		if (Time.time >= _nextDoorCheck)
		{
			TryOpenNearbyDoor("stuck recovery");
		}
		if (!(Time.time < _nextRepathTime))
		{
			NavMeshHit val = default(NavMeshHit);
			if (!((Behaviour)_agent).enabled && NavMesh.SamplePosition(_mimicObject.transform.position, ref val, 0.75f, -1))
			{
				((Behaviour)_agent).enabled = true;
				((Behaviour)_enemyNavigation).enabled = true;
				_agent.Warp(((NavMeshHit)(ref val)).position);
			}
			if (((Behaviour)_agent).enabled && _agent.isOnNavMesh)
			{
				_enemyNavigation.ResetPath();
				Repath("stuck recovery");
				_recoveryAttempts++;
			}
			_nextRepathTime = Time.time + 0.5f;
		}
	}

	private void Repath(string reason)
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Expected O, but got Unknown
		//IL_00ca: 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_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Invalid comparison between Unknown and I4
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Invalid comparison between Unknown and I4
		//IL_018f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0208: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
		_nextRepathTime = Time.time + 0.3f;
		if ((Object)(object)_chaseTarget == (Object)null || !((Behaviour)_agent).enabled || !_agent.isOnNavMesh)
		{
			return;
		}
		Vector3 position = GetTargetTransform(_chaseTarget).position;
		NavMeshHit val = default(NavMeshHit);
		if (!NavMesh.SamplePosition(position, ref val, 1.5f, -1))
		{
			Plugin.Log.LogWarning((object)("Mimic path target is outside NavMesh: " + ((Vector3)(ref position)).ToString("F2")));
		}
		else
		{
			if (_hasPathDestination && Vector3.Distance(_lastPathDestination, ((NavMeshHit)(ref val)).position) < 0.2f && _agent.hasPath && (int)_agent.pathStatus != 2)
			{
				return;
			}
			NavMeshPath val2 = new NavMeshPath();
			if (!NavMesh.CalculatePath(_mimicObject.transform.position, ((NavMeshHit)(ref val)).position, -1, val2) || (int)val2.status == 2 || val2.corners.Length < 2)
			{
				Plugin.Log.LogWarning((object)string.Concat("Mimic path rejected reason=", reason, " status=", val2.status, " corners=", val2.corners.Length, " target=", ((Object)((Component)_chaseTarget).gameObject).name));
				return;
			}
			_agent.speed = _plugin.ChaseSpeed.Value;
			_enemyNavigation.SetDestination(((NavMeshHit)(ref val)).position);
			bool flag = _agent.hasPath || _agent.pathPending;
			if (flag)
			{
				_lastPathDestination = ((NavMeshHit)(ref val)).position;
				_hasPathDestination = true;
			}
			Plugin.Log.LogInfo((object)string.Concat("Mimic repath reason=", reason, " accepted=", flag, " calculatedStatus=", val2.status, " calculatedCorners=", val2.corners.Length, " target=", ((Object)((Component)_chaseTarget).gameObject).name));
			LogPath(reason);
		}
	}

	private void CheckStuck(float targetDistance)
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//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_00dc: 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_00fc: Unknown result type (might be due to invalid IL or missing references)
		if (Time.time < _nextStuckCheck || targetDistance < _plugin.AttackRange.Value * 1.2f)
		{
			return;
		}
		_nextStuckCheck = Time.time + 0.75f;
		float num = Vector3.Distance(_lastProgressPosition, _mimicObject.transform.position);
		if (num < 0.12f)
		{
			if (_stuckSince <= 0f)
			{
				_stuckSince = Time.time;
			}
			if (Time.time - _stuckSince >= 2.25f)
			{
				string text = FindBlockingObjectName();
				object[] array = new object[8]
				{
					"no progress for ",
					(Time.time - _stuckSince).ToString("F1"),
					"s; obstacle=",
					text,
					"; path=",
					_agent.pathStatus,
					"; desiredSpeed=",
					null
				};
				Vector3 desiredVelocity = _agent.desiredVelocity;
				array[7] = ((Vector3)(ref desiredVelocity)).magnitude.ToString("F2");
				EnterStuckRecovery(string.Concat(array));
			}
		}
		else
		{
			_stuckSince = 0f;
			_lastProgressPosition = _mimicObject.transform.position;
		}
	}

	private void EnterStuckRecovery(string reason)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		_recoveryStartPosition = _mimicObject.transform.position;
		_recoveryAttempts = 0;
		_stuckSince = 0f;
		if (((Behaviour)_agent).enabled && _agent.isOnNavMesh)
		{
			_enemyNavigation.ResetPath();
		}
		_nextRepathTime = Time.time + 0.1f;
		Transition(MimicMode.StuckRecovery, reason);
	}

	private string FindBlockingObjectName()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: 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_0046: Unknown result type (might be due to invalid IL or missing references)
		Vector3 desiredVelocity = _agent.desiredVelocity;
		if (((Vector3)(ref desiredVelocity)).sqrMagnitude < 0.01f)
		{
			return "path/no desired velocity";
		}
		RaycastHit val = default(RaycastHit);
		if (Physics.Raycast(_mimicObject.transform.position + Vector3.up * 0.8f, ((Vector3)(ref desiredVelocity)).normalized, ref val, 0.8f, -5, (QueryTriggerInteraction)1))
		{
			return ((Object)((Component)((RaycastHit)(ref val)).collider).gameObject).name;
		}
		return "unknown";
	}

	private void TryOpenNearbyDoor(string reason)
	{
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		_nextDoorCheck = Time.time + 0.5f;
		if (!_authoritative || DoorOpenMethod == null)
		{
			return;
		}
		PhysGrabHinge val = null;
		float num = 1.8f;
		PhysGrabHinge[] array = Object.FindObjectsOfType<PhysGrabHinge>();
		foreach (PhysGrabHinge val2 in array)
		{
			if (!((Object)(object)val2 == (Object)null))
			{
				float num2 = Vector3.Distance(_mimicObject.transform.position, ((Component)val2).transform.position);
				if (num2 < num)
				{
					num = num2;
					val = val2;
				}
			}
		}
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		try
		{
			PhysGrabObject componentInParent = ((Component)val).GetComponentInParent<PhysGrabObject>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				componentInParent.EnemyInteractTimeSet();
			}
			DoorOpenMethod.Invoke(val, null);
			Plugin.Log.LogInfo((object)("Mimic door detected=" + ((Object)((Component)val).gameObject).name + " distance=" + num.ToString("F2") + " action=PhysGrabHinge.OpenImpulse reason=" + reason));
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Mimic door open failed object=" + ((Object)((Component)val).gameObject).name + " error=" + ex.GetBaseException().Message));
		}
	}

	private void EnterReplay(string reason)
	{
		//IL_005b: 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)
		if (((Behaviour)_agent).enabled)
		{
			if (_agent.isOnNavMesh)
			{
				_agent.ResetPath();
			}
			((Behaviour)_agent).enabled = false;
		}
		((Behaviour)_enemyNavigation).enabled = false;
		_hasPathDestination = false;
		_localStartTime = Time.time;
		_lastReplayPosition = _mimicObject.transform.position;
		Transition(MimicMode.Replay, reason);
	}

	private void Transition(MimicMode next, string reason)
	{
		if (_mode != next || !(reason != "created"))
		{
			MimicMode mode = _mode;
			_mode = next;
			Plugin.Log.LogMessage((object)string.Concat("Mimic mode ", mode, " -> ", next, " reason=", reason, " target=", TargetName()));
		}
	}

	private void LogMode(string reason, PlayerAvatar target)
	{
		Plugin.Log.LogMessage((object)string.Concat("Mimic mode=", _mode, " reason=", reason, " target=", ((Object)(object)target != (Object)null) ? ((Object)((Component)target).gameObject).name : "none"));
	}

	private void LogPath(string reason)
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		string text = "agent-disabled";
		int num = 0;
		if ((Object)(object)_agent != (Object)null && ((Behaviour)_agent).enabled && _agent.isOnNavMesh)
		{
			text = (_agent.pathPending ? "Pending" : ((object)_agent.pathStatus).ToString());
			if (!_agent.pathPending && _agent.hasPath)
			{
				num = _agent.path.corners.Length;
			}
		}
		Plugin.Log.LogInfo((object)string.Concat("Mimic debug mode=", _mode, " target=", TargetName(), " pathStatus=", text, " corners=", num, " reason=", reason));
	}

	private string TargetName()
	{
		if (!((Object)(object)_chaseTarget != (Object)null))
		{
			return "none";
		}
		return ((Object)((Component)_chaseTarget).gameObject).name;
	}

	private static Transform GetTargetTransform(PlayerAvatar avatar)
	{
		if (!((Object)(object)avatar.playerTransform != (Object)null))
		{
			return ((Component)avatar).transform;
		}
		return avatar.playerTransform;
	}

	private void ApplyCapsule(float height)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		height = Mathf.Clamp(height, 0.65f, 1.8f);
		_capsule.height = height;
		_capsule.center = Vector3.up * (height * 0.5f);
		_agent.height = height;
	}

	private void UpdateChasePosture(PlayerAvatar target)
	{
		bool flag = ReadAvatarBool(PlayerCrawlingField, target);
		bool flag2 = flag || ReadAvatarBool(PlayerCrouchingField, target);
		float num = (flag ? 0.65f : (flag2 ? 1.05f : 1.8f));
		if (_chaseCrouching != flag2 || _chaseCrawling != flag || !(Mathf.Abs(_capsule.height - num) < 0.01f))
		{
			_chaseCrouching = flag2;
			_chaseCrawling = flag;
			ApplyCapsule(num);
			if (_plugin.VerboseLogging.Value)
			{
				Plugin.Log.LogInfo((object)("Mimic chase posture=" + (flag ? "crawl" : (flag2 ? "crouch" : "stand")) + " target=" + (((Object)(object)target != (Object)null) ? ((Object)((Component)target).gameObject).name : "none") + " capsule=" + num.ToString("F2")));
			}
		}
	}

	private static bool ReadAvatarBool(FieldInfo field, PlayerAvatar avatar)
	{
		if (field == null || (Object)(object)avatar == (Object)null)
		{
			return false;
		}
		try
		{
			return Convert.ToBoolean(field.GetValue(avatar));
		}
		catch
		{
			return false;
		}
	}

	private void UpdateVision()
	{
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		if (Time.time < _nextVisionCheck)
		{
			return;
		}
		_nextVisionCheck = Time.time + 0.2f;
		PlayerAvatar chaseTarget = _chaseTarget;
		PlayerAvatar val = null;
		float num = _plugin.VisionDistance.Value;
		PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>();
		foreach (PlayerAvatar val2 in array)
		{
			if (!((Object)(object)val2 == (Object)null) && ((Component)val2).gameObject.activeInHierarchy && (!_plugin.SpectatorMode || !((Object)(object)val2 == (Object)(object)SemiFunc.PlayerAvatarLocal())))
			{
				Transform targetTransform = GetTargetTransform(val2);
				Vector3 val3 = _mimicObject.transform.position + Vector3.up;
				float num2 = Vector3.Distance(val3, targetTransform.position + Vector3.up);
				if (!(num2 >= num) && HasLineOfSight(val3, targetTransform, ((Component)val2).transform))
				{
					num = num2;
					val = val2;
				}
			}
		}
		if ((Object)(object)val != (Object)null)
		{
			_chaseTarget = val;
			_lastSeenTime = Time.time;
		}
		else if ((Object)(object)chaseTarget != (Object)null && Time.time - _lastSeenTime <= 5f)
		{
			_chaseTarget = chaseTarget;
		}
		else
		{
			_chaseTarget = null;
		}
	}

	private static bool HasLineOfSight(Vector3 origin, Transform target, Transform root)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_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)
		//IL_0019: 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)
		Vector3 val = target.position + Vector3.up;
		Vector3 val2 = val - origin;
		RaycastHit val3 = default(RaycastHit);
		if (!Physics.Raycast(origin, ((Vector3)(ref val2)).normalized, ref val3, ((Vector3)(ref val2)).magnitude, -5, (QueryTriggerInteraction)1))
		{
			return true;
		}
		if (!((Object)(object)((RaycastHit)(ref val3)).transform == (Object)(object)root) && !((RaycastHit)(ref val3)).transform.IsChildOf(root) && !((Object)(object)((RaycastHit)(ref val3)).transform == (Object)(object)target))
		{
			return ((RaycastHit)(ref val3)).transform.IsChildOf(target);
		}
		return true;
	}

	private void UpdateFacing(Vector3 direction)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: 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_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: 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_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: 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_00d2: 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)
		direction.y = 0f;
		if (!(((Vector3)(ref direction)).sqrMagnitude < 0.0025f))
		{
			Quaternion val = Quaternion.LookRotation(((Vector3)(ref direction)).normalized, Vector3.up);
			Vector3 forward = _mimicObject.transform.forward;
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.001f || Vector3.Dot(((Vector3)(ref forward)).normalized, ((Vector3)(ref direction)).normalized) < 0.2f)
			{
				_mimicObject.transform.rotation = val;
				return;
			}
			float num = 1f - Mathf.Exp((0f - Mathf.Max(2f, _plugin.TurnSpeed.Value / 90f)) * Time.deltaTime);
			_mimicObject.transform.rotation = Quaternion.Slerp(_mimicObject.transform.rotation, val, num);
		}
	}

	private Vector3 GetNavigationFacingDirection()
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: 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)
		//IL_0049: 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_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: 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_0138: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_015c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: 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)
		if ((Object)(object)_agent == (Object)null || !((Behaviour)_agent).enabled || !_agent.isOnNavMesh)
		{
			return Vector3.zero;
		}
		Vector3 result = _mimicObject.transform.position - _lastMovementPosition;
		result.y = 0f;
		_lastMovementPosition = _mimicObject.transform.position;
		if (((Vector3)(ref result)).sqrMagnitude > 4E-06f)
		{
			return result;
		}
		Vector3 result2 = _agent.steeringTarget - _mimicObject.transform.position;
		result2.y = 0f;
		if (((Vector3)(ref result2)).sqrMagnitude > 0.01f)
		{
			return result2;
		}
		if (_agent.hasPath && !_agent.pathPending)
		{
			Vector3[] corners = _agent.path.corners;
			for (int i = 0; i < corners.Length; i++)
			{
				Vector3 result3 = corners[i] - _mimicObject.transform.position;
				result3.y = 0f;
				if (((Vector3)(ref result3)).sqrMagnitude > 0.04f)
				{
					return result3;
				}
			}
		}
		Vector3 velocity = _agent.velocity;
		velocity.y = 0f;
		if (((Vector3)(ref velocity)).sqrMagnitude > 0.01f)
		{
			return velocity;
		}
		return Vector3.zero;
	}

	private void TickVoiceReplay()
	{
		if ((Object)(object)_voiceSource == (Object)null || _voiceSource.isPlaying || Time.time < _nextVoiceTime)
		{
			return;
		}
		_nextVoiceTime = Time.time + Random.Range(8f, 20f);
		VoicePhraseCapture voiceCapture = _recorder.VoiceCapture;
		if ((Object)(object)voiceCapture == (Object)null)
		{
			return;
		}
		if (!voiceCapture.TryGetRandomPhrase(out var samples, out var channels, out var sampleRate))
		{
			_nextVoiceTime = Time.time + 1f;
			return;
		}
		if ((Object)(object)_voiceSource.clip != (Object)null)
		{
			Object.Destroy((Object)(object)_voiceSource.clip);
		}
		int num = samples.Length / Math.Max(1, channels);
		float num2 = 0f;
		for (int i = 0; i < samples.Length; i++)
		{
			num2 = Mathf.Max(num2, Mathf.Abs(samples[i]));
		}
		if (num2 > 0.0001f)
		{
			float num3 = Mathf.Min(5f, 0.88f / num2);
			for (int j = 0; j < samples.Length; j++)
			{
				samples[j] = Mathf.Clamp(samples[j] * num3, -1f, 1f);
			}
		}
		AudioClip val = AudioClip.Create("MimicPhrase_" + voiceCapture.OwnerName, num, channels, sampleRate, false);
		val.SetData(samples, 0);
		_voiceSource.clip = val;
		_voiceSource.pitch = Random.Range(0.96f, 1.04f);
		_voiceSource.volume = 1f;
		_voiceSource.Play();
		Plugin.Log.LogInfo((object)("Mimic voice phrase player=" + voiceCapture.OwnerName + " duration=" + val.length.ToString("F2") + "s"));
	}

	private void UpdateChaseAnimation()
	{
		//IL_0015: 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 (!((Object)(object)_animator == (Object)null))
		{
			Vector3 velocity = _agent.velocity;
			float magnitude = ((Vector3)(ref velocity)).magnitude;
			_animator.SetBool("Moving", magnitude > 0.12f);
			_animator.SetBool("Sprinting", magnitude > 3.2f);
			_animator.SetBool("Crouching", _chaseCrouching);
			_animator.SetBool("Crawling", _chaseCrawling);
			_animator.speed = ((magnitude > 0.12f) ? Mathf.Clamp(magnitude / 1.8f, 0.65f, 1.8f) : 1f);
		}
	}

	private void UpdateAnimation(PlayerSnapshot previous, PlayerSnapshot next, float span)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: 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_0050: 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_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_animator == (Object)null) && !(span <= 0.0001f))
		{
			Vector3 val = Vector3.Lerp(previous.Velocity, next.Velocity, 0.5f);
			if (_reversePlayback)
			{
				val = -val;
			}
			Vector2 val2 = new Vector2(val.x, val.z);
			float magnitude = ((Vector2)(ref val2)).magnitude;
			bool flag = next.JumpStarted || (!next.IsGrounded && val.y > 0.25f);
			bool flag2 = !next.IsGrounded && val.y < -0.25f;
			bool flag3 = magnitude > 3.2f;
			_animator.SetBool("Moving", magnitude > 0.12f);
			_animator.SetBool("Sprinting", flag3);
			_animator.SetBool("Jumping", flag);
			_animator.SetBool("Falling", flag2);
			_animator.SetBool("Crouching", previous.IsCrouching);
			_animator.SetBool("Crawling", previous.IsCrawling);
			_animator.speed = ((magnitude > 0.12f) ? Mathf.Clamp(magnitude / 1.8f, 0.65f, 1.8f) : 1f);
			if (flag && !_wasJumping)
			{
				_animator.SetTrigger("JumpingImpulse");
			}
			if (flag3 && !_wasSprinting)
			{
				_animator.SetTrigger("SprintingImpulse");
			}
			_wasJumping = flag;
			_wasSprinting = flag3;
		}
	}

	private void UpdateFakeHeldObject(PlayerSnapshot previous, PlayerSnapshot next, float t)
	{
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: 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)
		//IL_015f: 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_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		PlayerSnapshot playerSnapshot = ((t < 0.5f) ? previous : next);
		GameObject heldObjectSource = playerSnapshot.HeldObjectSource;
		if ((Object)(object)heldObjectSource == (Object)null)
		{
			ClearFakeHeldObject();
			return;
		}
		int instanceID = ((Object)heldObjectSource).GetInstanceID();
		if ((Object)(object)_fakeHeldObject == (Object)null || instanceID != _fakeHeldSourceId)
		{
			ClearFakeHeldObject();
			_fakeHeldObject = CreateVisualOnlyHeldObject(heldObjectSource);
			_fakeHeldSourceId = instanceID;
			if ((Object)(object)_fakeHeldObject != (Object)null)
			{
				_fakeHeldObject.transform.SetParent(_mimicObject.transform, false);
				Plugin.Log.LogInfo((object)("Mimic fake held visual created source=" + ((Object)heldObjectSource).name + " renderers=" + _fakeHeldObject.GetComponentsInChildren<Renderer>(true).Length));
			}
		}
		if (!((Object)(object)_fakeHeldObject == (Object)null))
		{
			bool flag = (Object)(object)previous.HeldObjectSource != (Object)null && (Object)(object)next.HeldObjectSource != (Object)null && ((Object)previous.HeldObjectSource).GetInstanceID() == ((Object)next.HeldObjectSource).GetInstanceID();
			_fakeHeldObject.transform.localPosition = (flag ? Vector3.Lerp(previous.HeldLocalPosition, next.HeldLocalPosition, t) : playerSnapshot.HeldLocalPosition);
			_fakeHeldObject.transform.localRotation = (flag ? Quaternion.Slerp(previous.HeldLocalRotation, next.HeldLocalRotation, t) : playerSnapshot.HeldLocalRotation);
		}
	}

	private static GameObject CreateVisualOnlyHeldObject(GameObject source)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Expected O, but got Unknown
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Expected O, but got Unknown
		//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_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: 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)
		GameObject val = new GameObject("FakeHeld_" + ((Object)source).name);
		Renderer[] componentsInChildren = source.GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val2 in componentsInChildren)
		{
			if (!val2.ena