Decompiled source of MimicReplay v0.12.29

MimicReplay.dll

Decompiled 2 months 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: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace MimicReplay;

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

		internal bool IsKinematic;

		internal bool DetectCollisions;
	}

	private sealed class PendingNetworkSpawn
	{
		internal int MimicId;

		internal int ActorNumber;

		internal float Delay;

		internal float Expires;

		internal bool Authoritative;
	}

	private const byte NetworkSpawnEventCode = 197;

	private const byte NetworkStateEventCode = 198;

	private const string NetworkSpawnSignature = "MimicReplaySpawnV1";

	private const string NetworkStateSignature = "MimicReplayStateV1";

	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<KeyboardShortcut> SpawnMostValuableKey;

	internal ConfigEntry<float> KnockbackForce;

	internal ConfigEntry<int> MimicHealth;

	internal ConfigEntry<bool> AutoSpawnEnabled;

	internal ConfigEntry<KeyboardShortcut> AutoSpawnToggleKey;

	internal ConfigEntry<int> AutoSpawnMaxMimics;

	internal ConfigEntry<float> AutoSpawnIntervalMin;

	internal ConfigEntry<float> AutoSpawnIntervalMax;

	internal ConfigEntry<float> AutoSpawnGrowthSeconds;

	internal ConfigEntry<float> AutoSpawnMinDistance;

	internal ConfigEntry<float> AutoSpawnMaxDistance;

	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 _nextValuableSpawnAllowedTime;

	private float _moneyNoticeUntil;

	private string _moneyNotice;

	private bool _autoSpawnWasInLevel;

	private float _autoSpawnLevelStarted;

	private float _nextAutoSpawnTime;

	private float _nextAutoSpawnToggleAllowedTime;

	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 Transform _noclipMovementRoot;

	private Vector3 _noclipTargetPosition;

	private int _nextNetworkMimicId;

	private GameObject _grabProxyPrefab;

	private object _grabProxyPrefabRef;

	private readonly Dictionary<int, MimicGrabProxy> _grabProxies = new Dictionary<int, MimicGrabProxy>();

	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_03cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d9: Expected O, but got Unknown
		//IL_040e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0418: Expected O, but got Unknown
		//IL_0443: Unknown result type (might be due to invalid IL or missing references)
		//IL_044d: Expected O, but got Unknown
		//IL_0482: Unknown result type (might be due to invalid IL or missing references)
		//IL_048c: Expected O, but got Unknown
		//IL_04c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_04cb: Expected O, but got Unknown
		//IL_04ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_051c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0560: Unknown result type (might be due to invalid IL or missing references)
		//IL_056a: Expected O, but got Unknown
		//IL_05ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_05f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_05fb: Expected O, but got Unknown
		//IL_061c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0660: Unknown result type (might be due to invalid IL or missing references)
		//IL_066a: Expected O, but got Unknown
		//IL_0698: Unknown result type (might be due to invalid IL or missing references)
		//IL_06a2: Expected O, but got Unknown
		//IL_06e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_071d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0727: Expected O, but got Unknown
		//IL_075c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0766: Expected O, but got Unknown
		//IL_079b: Unknown result type (might be due to invalid IL or missing references)
		//IL_07a5: Expected O, but got Unknown
		//IL_07da: Unknown result type (might be due to invalid IL or missing references)
		//IL_07e4: Expected O, but got Unknown
		//IL_0819: Unknown result type (might be due to invalid IL or missing references)
		//IL_0823: Expected O, but got Unknown
		//IL_0858: Unknown result type (might be due to invalid IL or missing references)
		//IL_0862: 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", 4.2f, new ConfigDescription("Movement speed while chasing", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 8f), new object[0]));
		if (ChaseSpeed.Value <= 2.51f)
		{
			ChaseSpeed.Value = 4.2f;
		}
		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", 18f, new ConfigDescription("Flight speed", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 40f), new object[0]));
		if (NoclipSpeed.Value <= 8.01f)
		{
			NoclipSpeed.Value = 18f;
		}
		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]));
		SpawnMostValuableKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Cheats", "SpawnMostValuableKey", new KeyboardShortcut((KeyCode)282, (KeyCode[])(object)new KeyCode[0]), "Spawn the most valuable object available in the current level (host/singleplayer only)");
		KnockbackForce = ((BaseUnityPlugin)this).Config.Bind<float>("Enemy", "KnockbackForce", 6f, new ConfigDescription("Player knockback force", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), new object[0]));
		MimicHealth = ((BaseUnityPlugin)this).Config.Bind<int>("Enemy", "MimicHealth", 100, new ConfigDescription("Damage required to kill a mimic", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000), new object[0]));
		AutoSpawnEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("AutoSpawn", "Enabled", true, "Periodically spawn mimics during levels (host only)");
		AutoSpawnToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("AutoSpawn", "ToggleKey", new KeyboardShortcut((KeyCode)284, (KeyCode[])(object)new KeyCode[0]), "Toggle automatic mimic spawning");
		AutoSpawnMaxMimics = ((BaseUnityPlugin)this).Config.Bind<int>("AutoSpawn", "MaximumMimics", 7, new ConfigDescription("Maximum automatic mimic population", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), new object[0]));
		AutoSpawnIntervalMin = ((BaseUnityPlugin)this).Config.Bind<float>("AutoSpawn", "IntervalMinSeconds", 35f, new ConfigDescription("Minimum time between automatic spawn attempts", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 300f), new object[0]));
		AutoSpawnIntervalMax = ((BaseUnityPlugin)this).Config.Bind<float>("AutoSpawn", "IntervalMaxSeconds", 70f, new ConfigDescription("Maximum time between automatic spawn attempts", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 600f), new object[0]));
		AutoSpawnGrowthSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("AutoSpawn", "PopulationGrowthSeconds", 120f, new ConfigDescription("Seconds before the allowed population grows by one", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 600f), new object[0]));
		AutoSpawnMinDistance = ((BaseUnityPlugin)this).Config.Bind<float>("AutoSpawn", "MinimumPlayerDistance", 4f, new ConfigDescription("Minimum spawn distance from a player", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 30f), new object[0]));
		AutoSpawnMaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("AutoSpawn", "MaximumPlayerDistance", 16f, new ConfigDescription("Maximum spawn distance from a player", (AcceptableValueBase)(object)new AcceptableValueRange<float>(4f, 50f), 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");
		Harmony.CreateAndPatchAll(typeof(MimicHurtColliderPatch), "com.schetnikov.repo.mimicreplay.damage");
		RegisterGrabProxyPrefab();
		((MonoBehaviour)this).StartCoroutine(PeriodicUpdate());
	}

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

	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_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_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_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0214: Unknown result type (might be due to invalid IL or missing references)
		//IL_0219: Unknown result type (might be due to invalid IL or missing references)
		//IL_024e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0253: Unknown result type (might be due to invalid IL or missing references)
		if (!Enabled.Value)
		{
			return;
		}
		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 = AutoSpawnToggleKey.Value;
		if (((KeyboardShortcut)(ref value3)).IsDown() && Time.unscaledTime >= _nextAutoSpawnToggleAllowedTime)
		{
			_nextAutoSpawnToggleAllowedTime = Time.unscaledTime + 0.5f;
			AutoSpawnEnabled.Value = !AutoSpawnEnabled.Value;
			((BaseUnityPlugin)this).Config.Save();
			if (AutoSpawnEnabled.Value)
			{
				ScheduleNextAutoSpawn(5f, 10f);
			}
			Log.LogMessage((object)("Automatic mimic spawning: " + (AutoSpawnEnabled.Value ? "ON" : "OFF")));
		}
		KeyboardShortcut value4 = DebugTrailKey.Value;
		if (((KeyboardShortcut)(ref value4)).IsDown() && Time.unscaledTime >= _nextDebugToggleAllowedTime)
		{
			_nextDebugToggleAllowedTime = Time.unscaledTime + 0.75f;
			DebugTrail.Value = !DebugTrail.Value;
			Log.LogMessage((object)("DebugTrail: " + DebugTrail.Value));
		}
		KeyboardShortcut value5 = SpectatorKey.Value;
		if (((KeyboardShortcut)(ref value5)).IsDown() && Time.unscaledTime >= _nextSpectatorToggleAllowedTime)
		{
			_nextSpectatorToggleAllowedTime = Time.unscaledTime + 0.75f;
			SetSpectatorMode(!SpectatorMode);
		}
		KeyboardShortcut value6 = NoclipKey.Value;
		if (((KeyboardShortcut)(ref value6)).IsDown() && Time.unscaledTime >= _nextNoclipToggleAllowedTime)
		{
			_nextNoclipToggleAllowedTime = Time.unscaledTime + 0.2f;
			SetNoclipMode(!NoclipMode);
		}
		KeyboardShortcut value7 = AddMoneyKey.Value;
		if (((KeyboardShortcut)(ref value7)).IsDown() && Time.unscaledTime >= _nextMoneyAllowedTime)
		{
			_nextMoneyAllowedTime = Time.unscaledTime + 0.75f;
			AddMoney();
		}
		KeyboardShortcut value8 = SpawnMostValuableKey.Value;
		if (((KeyboardShortcut)(ref value8)).IsDown() && Time.unscaledTime >= _nextValuableSpawnAllowedTime)
		{
			_nextValuableSpawnAllowedTime = Time.unscaledTime + 1f;
			SpawnMostValuable();
		}
		MaintainSpectatorMode();
	}

	private void LateUpdate()
	{
		if (Enabled.Value && NoclipMode)
		{
			MaintainNoclipMode();
		}
		if (!Enabled.Value)
		{
			return;
		}
		MimicController[] array = _mimics.ToArray();
		foreach (MimicController mimicController in array)
		{
			if (mimicController != null && mimicController.IsAlive)
			{
				mimicController.LateTick();
			}
		}
	}

	private void OnGUI()
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Expected O, but got Unknown
		//IL_0072: 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_008d: Expected O, but got Unknown
		//IL_00ac: 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_02b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_029c: 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_0313: Unknown result type (might be due to invalid IL or missing references)
		//IL_032d: Unknown result type (might be due to invalid IL or missing references)
		//IL_019b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: 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_01c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_020c: Unknown result type (might be due to invalid IL or missing references)
		if (!Enabled.Value)
		{
			return;
		}
		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);
		}
		if (SpectatorMode || NoclipMode || Time.unscaledTime < _moneyNoticeUntil)
		{
			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);
		}
		string text2 = "AUTO MIMICS: " + (AutoSpawnEnabled.Value ? "ON" : "OFF") + "  [F3]";
		Rect val3 = default(Rect);
		((Rect)(ref val3))..ctor(18f, 18f, 300f, 32f);
		Color textColor = _spectatorStyle.normal.textColor;
		_spectatorStyle.normal.textColor = (AutoSpawnEnabled.Value ? new Color(0.3f, 1f, 0.4f, 1f) : new Color(1f, 0.3f, 0.3f, 1f));
		_spectatorStyle.alignment = (TextAnchor)3;
		_spectatorShadowStyle.alignment = (TextAnchor)3;
		GUI.Label(new Rect(((Rect)(ref val3)).x + 2f, ((Rect)(ref val3)).y + 2f, ((Rect)(ref val3)).width, ((Rect)(ref val3)).height), text2, _spectatorShadowStyle);
		GUI.Label(val3, text2, _spectatorStyle);
		_spectatorStyle.normal.textColor = textColor;
		_spectatorStyle.alignment = (TextAnchor)4;
		_spectatorShadowStyle.alignment = (TextAnchor)4;
	}

	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_00a0: 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_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: 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();
		_noclipMovementRoot = (((Object)(object)val.playerTransform != (Object)null) ? val.playerTransform : ((Component)val).transform);
		List<Component> list = new List<Component>();
		list.Add((Component)(object)val);
		List<Component> list2 = list;
		Type type = AccessTools.TypeByName("PlayerController");
		Component val2 = (Component)((type != null) ? /*isinst with value type is only supported in some contexts*/: null);
		if ((Object)(object)val2 != (Object)null)
		{
			list2.Add(val2);
			_noclipMovementRoot = val2.transform;
		}
		_noclipTargetPosition = _noclipMovementRoot.position;
		foreach (Component item in list2)
		{
			Collider[] componentsInChildren = item.GetComponentsInChildren<Collider>(true);
			foreach (Collider val3 in componentsInChildren)
			{
				if (!((Object)(object)val3 == (Object)null) && !_noclipColliderStates.ContainsKey(val3))
				{
					_noclipColliderStates[val3] = val3.enabled;
					val3.enabled = false;
				}
			}
			Rigidbody[] componentsInChildren2 = item.GetComponentsInChildren<Rigidbody>(true);
			foreach (Rigidbody val4 in componentsInChildren2)
			{
				if (!((Object)(object)val4 == (Object)null) && !_noclipBodyStates.ContainsKey(val4))
				{
					_noclipBodyStates[val4] = new RigidbodyState
					{
						UseGravity = val4.useGravity,
						IsKinematic = val4.isKinematic,
						DetectCollisions = val4.detectCollisions
					};
					val4.velocity = Vector3.zero;
					val4.angularVelocity = Vector3.zero;
					val4.useGravity = false;
					val4.detectCollisions = false;
					val4.isKinematic = true;
				}
			}
		}
		Log.LogInfo((object)("Noclip captured controller=" + (((Object)(object)val2 != (Object)null) ? ((Object)val2.gameObject).name : "not found") + " colliders=" + _noclipColliderStates.Count + " rigidbodies=" + _noclipBodyStates.Count));
	}

	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_019b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a2: 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_0192: 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_01e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ee: 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_01f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fd: 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;
			}
		}
		float num = NoclipSpeed.Value * (Input.GetKey((KeyCode)304) ? 2f : 1f);
		Vector3 val6 = ((((Vector3)(ref val4)).sqrMagnitude >= 0.001f) ? (((Vector3)(ref val4)).normalized * num * Time.unscaledDeltaTime) : Vector3.zero);
		Transform val7 = (((Object)(object)_noclipMovementRoot != (Object)null) ? _noclipMovementRoot : (((Object)(object)val.playerTransform != (Object)null) ? val.playerTransform : ((Component)val).transform));
		_noclipTargetPosition += val6;
		val7.position = _noclipTargetPosition;
	}

	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();
		_noclipMovementRoot = null;
	}

	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 SpawnMostValuable()
	{
		//IL_0228: Unknown result type (might be due to invalid IL or missing references)
		//IL_022d: Unknown result type (might be due to invalid IL or missing references)
		//IL_025f: 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_026b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0270: Unknown result type (might be due to invalid IL or missing references)
		//IL_0275: Unknown result type (might be due to invalid IL or missing references)
		//IL_0277: Unknown result type (might be due to invalid IL or missing references)
		//IL_0279: 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_0288: Unknown result type (might be due to invalid IL or missing references)
		//IL_028d: 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_0254: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02be: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e5: 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_02cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
		if (!SemiFunc.IsMasterClientOrSingleplayer())
		{
			ShowMoneyNotice("VALUABLE: HOST ONLY");
			return;
		}
		ValuableDirector instance = ValuableDirector.instance;
		PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
		if ((Object)(object)instance == (Object)null || (Object)(object)val == (Object)null)
		{
			ShowMoneyNotice("VALUABLES NOT READY");
			return;
		}
		try
		{
			PrefabRef val2 = null;
			float num = float.MinValue;
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			string[] array = new string[7] { "tinyValuables", "smallValuables", "mediumValuables", "bigValuables", "wideValuables", "tallValuables", "veryTallValuables" };
			foreach (string name in array)
			{
				FieldInfo field = typeof(ValuableDirector).GetField(name, bindingAttr);
				IEnumerable enumerable = ((field != null) ? (field.GetValue(instance) as IEnumerable) : null);
				if (enumerable == null)
				{
					continue;
				}
				foreach (object item in enumerable)
				{
					PrefabRef val3 = (PrefabRef)((item is PrefabRef) ? item : null);
					GameObject val4 = ((PrefabRef<GameObject>)(object)val3)?.Prefab;
					ValuableObject val5 = (((Object)(object)val4 != (Object)null) ? val4.GetComponentInChildren<ValuableObject>(true) : null);
					if (!((Object)(object)val5 == (Object)null))
					{
						FieldInfo field2 = typeof(ValuableObject).GetField("dollarValueOverride", bindingAttr);
						int num2 = ((field2 != null) ? Convert.ToInt32(field2.GetValue(val5)) : 0);
						float num3 = ((num2 > 0) ? ((float)num2) : (((Object)(object)val5.valuePreset != (Object)null) ? val5.valuePreset.valueMax : 0f));
						if (!(num3 <= num))
						{
							num = num3;
							val2 = val3;
						}
					}
				}
			}
			if (val2 == null || (Object)(object)((PrefabRef<GameObject>)(object)val2).Prefab == (Object)null)
			{
				ShowMoneyNotice("NO VALUABLE PREFAB");
				return;
			}
			Transform val6 = (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform : (((Object)(object)val.playerTransform != (Object)null) ? val.playerTransform : ((Component)val).transform));
			Vector3 forward = val6.forward;
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.01f)
			{
				forward = ((Component)val).transform.forward;
			}
			((Vector3)(ref forward)).Normalize();
			Vector3 val7 = val6.position + forward * 2.2f;
			RaycastHit val8 = default(RaycastHit);
			if (Physics.Raycast(val7 + Vector3.up * 1.5f, Vector3.down, ref val8, 4f, -5, (QueryTriggerInteraction)1))
			{
				val7 = ((RaycastHit)(ref val8)).point + Vector3.up * 0.35f;
			}
			GameObject val9;
			if (PhotonNetwork.InRoom)
			{
				val9 = PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)val2).ResourcePath, val7, Quaternion.identity, (byte)0, (object[])null);
			}
			else
			{
				val9 = Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)val2).Prefab, val7, Quaternion.identity);
				val9.SetActive(true);
			}
			if ((Object)(object)val9 == (Object)null)
			{
				throw new InvalidOperationException("valuable spawn returned null");
			}
			ShowMoneyNotice("SPAWNED: " + ((PrefabRef<GameObject>)(object)val2).PrefabName + "  [F1]");
			Log.LogMessage((object)("Spawned most valuable prefab=" + ((PrefabRef<GameObject>)(object)val2).PrefabName + " maxValue=" + num.ToString("F0") + " path=" + ((PrefabRef<GameObject>)(object)val2).ResourcePath));
		}
		catch (Exception ex)
		{
			ShowMoneyNotice("VALUABLE SPAWN ERROR");
			Log.LogError((object)("Failed to spawn most valuable: " + 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())
		{
			if (item != null && item.IsAlive)
			{
				item.Tick();
			}
		}
		_mimics.RemoveAll((MimicController mimic) => mimic == null || !mimic.IsAlive);
	}

	internal void SpawnMimic()
	{
		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)
		{
			SendNetworkSpawn(val.OwnerActorNr, num4, "manual");
		}
		else
		{
			SpawnMimicForRecorder(playerRecorder, num4, "local", authoritative: true);
		}
	}

	private void SendNetworkSpawn(int actorNumber, float delay, string reason)
	{
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Expected O, but got Unknown
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		int num = ((PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : 0);
		int num2 = num * 100000 + ++_nextNetworkMimicId % 100000;
		object[] array = new object[4] { "MimicReplaySpawnV1", actorNumber, delay, num2 };
		RaiseEventOptions val = new RaiseEventOptions();
		val.Receivers = (ReceiverGroup)1;
		val.CachingOption = (EventCaching)0;
		RaiseEventOptions val2 = val;
		bool flag = PhotonNetwork.RaiseEvent((byte)197, (object)array, val2, SendOptions.SendReliable);
		Log.LogMessage((object)("Network mimic spawn sent=" + flag + " sourceActor=" + actorNumber + " delay=" + delay.ToString("F2") + " reason=" + reason));
	}

	private void TickAutoSpawn()
	{
		if (!SemiFunc.RunIsLevel())
		{
			if (_autoSpawnWasInLevel)
			{
				RemoveAllMimics();
			}
			_autoSpawnWasInLevel = false;
		}
		else if (!_autoSpawnWasInLevel)
		{
			_autoSpawnWasInLevel = true;
			_autoSpawnLevelStarted = Time.time;
			ScheduleNextAutoSpawn(25f, 40f);
			Log.LogInfo((object)"Mimic auto-spawn armed for this level");
		}
		else
		{
			if (!AutoSpawnEnabled.Value || !SemiFunc.IsMasterClientOrSingleplayer() || Time.time < _nextAutoSpawnTime)
			{
				return;
			}
			float num = Mathf.Max(0f, Time.time - _autoSpawnLevelStarted);
			int num2 = Mathf.Clamp(1 + Mathf.FloorToInt(num / Mathf.Max(30f, AutoSpawnGrowthSeconds.Value)), 1, AutoSpawnMaxMimics.Value);
			if (_mimics.Count >= num2)
			{
				ScheduleNextAutoSpawn(AutoSpawnIntervalMin.Value, AutoSpawnIntervalMax.Value);
				return;
			}
			if (!TryFindAutoSpawnRoute(out var selectedRecorder, out var selectedDelay, out var selectedPoint))
			{
				_nextAutoSpawnTime = Time.time + 10f;
				if (VerboseLogging.Value)
				{
					Log.LogInfo((object)"Auto-spawn postponed: no recent visible free route point");
				}
				return;
			}
			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)
			{
				SendNetworkSpawn(val.OwnerActorNr, selectedDelay, "automatic cap=" + num2);
			}
			else
			{
				SpawnMimicForRecorder(selectedRecorder, selectedDelay, "automatic singleplayer", authoritative: true, ++_nextNetworkMimicId);
			}
			Log.LogMessage((object)("Automatic mimic selected route point=" + ((Vector3)(ref selectedPoint)).ToString("F2") + " population=" + (_mimics.Count + 1) + "/" + num2));
			ScheduleNextAutoSpawn(AutoSpawnIntervalMin.Value, AutoSpawnIntervalMax.Value);
		}
	}

	private void ScheduleNextAutoSpawn(float minimum, float maximum)
	{
		minimum = Mathf.Max(1f, minimum);
		maximum = Mathf.Max(minimum, maximum);
		_nextAutoSpawnTime = Time.time + Random.Range(minimum, maximum);
	}

	private bool TryFindAutoSpawnRoute(out PlayerRecorder selectedRecorder, out float selectedDelay, out Vector3 selectedPoint)
	{
		//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_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_00ba: 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)
		selectedRecorder = null;
		selectedDelay = 0f;
		selectedPoint = Vector3.zero;
		List<Tuple<PlayerRecorder, float, Vector3>> list = new List<Tuple<PlayerRecorder, float, Vector3>>();
		foreach (PlayerRecorder recorder in _recorders)
		{
			if (recorder == null || (Object)(object)recorder.Avatar == (Object)null || recorder.History.Count < 2)
			{
				continue;
			}
			IList<PlayerSnapshot> items = recorder.History.Items;
			PlayerSnapshot playerSnapshot = items[items.Count - 1];
			for (int i = 0; i < items.Count; i += 3)
			{
				PlayerSnapshot playerSnapshot2 = items[i];
				float num = playerSnapshot.Timestamp - playerSnapshot2.Timestamp;
				if (!(num < (float)ReplayDelayMinSeconds.Value) && !(num > (float)ReplayDelayMaxSeconds.Value) && IsValidAutoSpawnPoint(playerSnapshot2.Position, out var _))
				{
					list.Add(Tuple.Create<PlayerRecorder, float, Vector3>(recorder, num, playerSnapshot2.Position));
				}
			}
		}
		if (list.Count == 0)
		{
			return false;
		}
		Tuple<PlayerRecorder, float, Vector3> tuple = list[Random.Range(0, list.Count)];
		selectedRecorder = tuple.Item1;
		selectedDelay = tuple.Item2;
		selectedPoint = tuple.Item3;
		return true;
	}

	private bool IsValidAutoSpawnPoint(Vector3 point, out PlayerAvatar observer)
	{
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: 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_00d3: 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_00da: 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_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_013a: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0143: 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_0152: 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_015c: 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_0160: Unknown result type (might be due to invalid IL or missing references)
		observer = null;
		float num = Mathf.Min(AutoSpawnMinDistance.Value, AutoSpawnMaxDistance.Value);
		float num2 = Mathf.Max(AutoSpawnMinDistance.Value, AutoSpawnMaxDistance.Value);
		PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>();
		RaycastHit val5 = default(RaycastHit);
		NavMeshHit val7 = default(NavMeshHit);
		foreach (PlayerAvatar val in array)
		{
			if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy)
			{
				continue;
			}
			Transform val2 = (((Object)(object)val.playerTransform != (Object)null) ? val.playerTransform : ((Component)val).transform);
			float num3 = Vector3.Distance(val2.position, point);
			if (num3 < num || num3 > num2)
			{
				continue;
			}
			Vector3 val3 = val2.position + Vector3.up * 0.9f;
			Vector3 val4 = point + Vector3.up * 0.9f;
			if (Physics.Linecast(val3, val4, ref val5, -5, (QueryTriggerInteraction)1))
			{
				PlayerAvatar val6 = (((Object)(object)((RaycastHit)(ref val5)).collider != (Object)null) ? ((Component)((RaycastHit)(ref val5)).collider).GetComponentInParent<PlayerAvatar>() : null);
				if ((Object)(object)val6 != (Object)(object)val)
				{
					continue;
				}
			}
			if (NavMesh.SamplePosition(point, ref val7, 1f, -1))
			{
				Vector3 val8 = ((NavMeshHit)(ref val7)).position + Vector3.up * 0.38f;
				Vector3 val9 = ((NavMeshHit)(ref val7)).position + Vector3.up * 1.45f;
				if (!Physics.CheckCapsule(val8, val9, 0.32f, -5, (QueryTriggerInteraction)1))
				{
					observer = val;
					return true;
				}
			}
		}
		return false;
	}

	private void SpawnMimicForRecorder(PlayerRecorder recorder, float selectedDelay, string origin, bool authoritative, int mimicId = 0)
	{
		//IL_0158: 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)
		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
		{
			if (mimicId == 0)
			{
				mimicId = ++_nextNetworkMimicId;
			}
			MimicController mimicController = new MimicController(targetPlayer, recorder, selectedDelay, this, authoritative, mimicId);
			_mimics.Add(mimicController);
			if (authoritative)
			{
				SpawnGrabProxy(mimicId, mimicController.WorldPosition, mimicController.WorldRotation);
			}
			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)
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		if (eventData == null)
		{
			return;
		}
		object[] array = eventData.CustomData as object[];
		if (eventData.Code == 198)
		{
			if (array != null && array.Length >= 4 && array[0] is string && !((string)array[0] != "MimicReplayStateV1"))
			{
				int stateMimicId = Convert.ToInt32(array[1]);
				_mimics.FirstOrDefault((MimicController item) => item != null && item.NetworkId == stateMimicId)?.ApplyNetworkState((Vector3)array[2], (Quaternion)array[3]);
			}
		}
		else if (eventData.Code == 197 && array != null && array.Length >= 3 && array[0] is string && !((string)array[0] != "MimicReplaySpawnV1"))
		{
			int num = Convert.ToInt32(array[1]);
			float num2 = Convert.ToSingle(array[2]);
			int mimicId = ((array.Length >= 4) ? Convert.ToInt32(array[3]) : (++_nextNetworkMimicId));
			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, mimicId);
				return;
			}
			_pendingNetworkSpawns.Add(new PendingNetworkSpawn
			{
				MimicId = mimicId,
				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, pendingNetworkSpawn.MimicId);
						_pendingNetworkSpawns.RemoveAt(num);
					}
				}
			}
		}
	}

	internal void SendMimicState(int mimicId, Vector3 position, Quaternion rotation)
	{
		//IL_0022: 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_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Expected O, but got Unknown
		//IL_0042: 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_004f: Unknown result type (might be due to invalid IL or missing references)
		if (PhotonNetwork.InRoom)
		{
			object[] array = new object[4] { "MimicReplayStateV1", mimicId, position, rotation };
			RaiseEventOptions val = new RaiseEventOptions();
			val.Receivers = (ReceiverGroup)0;
			val.CachingOption = (EventCaching)0;
			PhotonNetwork.RaiseEvent((byte)198, (object)array, val, SendOptions.SendUnreliable);
		}
	}

	internal MimicController FindMimicByNetworkId(int mimicId)
	{
		return _mimics.FirstOrDefault((MimicController mimic) => mimic != null && mimic.NetworkId == mimicId);
	}

	internal void RegisterGrabProxy(int mimicId, MimicGrabProxy proxy)
	{
		if (mimicId != 0 && (Object)(object)proxy != (Object)null)
		{
			_grabProxies[mimicId] = proxy;
		}
	}

	internal void KnockDownGrabProxy(int mimicId, Vector3 force)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		if (_grabProxies.TryGetValue(mimicId, out var value) && (Object)(object)value != (Object)null)
		{
			value.KnockDown(force);
		}
	}

	private void RegisterGrabProxyPrefab()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Expected O, but got Unknown
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: 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)
		try
		{
			_grabProxyPrefab = new GameObject("MimicReplay_GrabProxy");
			_grabProxyPrefab.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)_grabProxyPrefab);
			CapsuleCollider val = _grabProxyPrefab.AddComponent<CapsuleCollider>();
			val.radius = 0.42f;
			val.height = 1.8f;
			val.center = Vector3.up * 0.9f;
			Rigidbody val2 = _grabProxyPrefab.AddComponent<Rigidbody>();
			val2.mass = 18f;
			val2.drag = 0.25f;
			val2.angularDrag = 1.5f;
			val2.collisionDetectionMode = (CollisionDetectionMode)2;
			PhotonView val3 = _grabProxyPrefab.AddComponent<PhotonView>();
			PhysGrabObject val4 = _grabProxyPrefab.AddComponent<PhysGrabObject>();
			val4.rb = val2;
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			FieldInfo field = typeof(PhysGrabObject).GetField("isEnemy", bindingAttr);
			FieldInfo field2 = typeof(PhysGrabObject).GetField("isNonValuable", bindingAttr);
			if (field != null)
			{
				field.SetValue(val4, true);
			}
			if (field2 != null)
			{
				field2.SetValue(val4, true);
			}
			_grabProxyPrefab.AddComponent<MimicGrabProxy>();
			val3.ObservedComponents = new List<Component> { (Component)(object)val4 };
			val3.Synchronization = (ViewSynchronization)3;
			Type type = ResolveREPOLibNetworkPrefabsType();
			MethodInfo methodInfo = ((type != null) ? type.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo method) => method.Name == "RegisterNetworkPrefab" && method.GetParameters().Length == 2) : null);
			if (methodInfo == null)
			{
				throw new MissingMethodException("REPOLib NetworkPrefabs.RegisterNetworkPrefab was not found");
			}
			_grabProxyPrefabRef = methodInfo.Invoke(null, new object[2] { "MimicReplay/GrabProxy", _grabProxyPrefab });
			if (_grabProxyPrefabRef == null)
			{
				throw new InvalidOperationException("REPOLib rejected MimicReplay/GrabProxy");
			}
			Log.LogInfo((object)"Registered network PhysGrabObject proxy for mimics");
		}
		catch (Exception ex)
		{
			Log.LogError((object)("Mimic grab proxy registration failed: " + ex.GetBaseException().Message));
			_grabProxyPrefabRef = null;
		}
	}

	private void SpawnGrabProxy(int mimicId, Vector3 position, Quaternion rotation)
	{
		//IL_000a: 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_0082: 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)
		if (_grabProxyPrefabRef == null)
		{
			SpawnLocalGrabProxy(mimicId, position, rotation);
			return;
		}
		try
		{
			Type type = ResolveREPOLibNetworkPrefabsType();
			if (type == null)
			{
				throw new TypeLoadException("REPOLib.Modules.NetworkPrefabs was not found in loaded assemblies");
			}
			MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo method) => method.Name == "SpawnNetworkPrefab" && method.GetParameters().Length == 5);
			if (methodInfo == null)
			{
				throw new MissingMethodException("REPOLib NetworkPrefabs.SpawnNetworkPrefab was not found");
			}
			object? obj = methodInfo.Invoke(null, new object[5]
			{
				_grabProxyPrefabRef,
				position,
				rotation,
				(byte)0,
				new object[1] { mimicId }
			});
			GameObject val = (GameObject)((obj is GameObject) ? obj : null);
			if ((Object)(object)val == (Object)null)
			{
				throw new InvalidOperationException("REPOLib returned no grab proxy instance");
			}
			val.SetActive(true);
			MimicGrabProxy component = val.GetComponent<MimicGrabProxy>();
			if ((Object)(object)component != (Object)null)
			{
				component.Bind(mimicId);
			}
		}
		catch (Exception ex)
		{
			Log.LogError((object)("Mimic grab proxy spawn failed: " + ex.GetBaseException().Message));
		}
	}

	private static Type ResolveREPOLibNetworkPrefabsType()
	{
		Type type = Type.GetType("REPOLib.Modules.NetworkPrefabs, REPOLib", throwOnError: false) ?? AccessTools.TypeByName("REPOLib.Modules.NetworkPrefabs");
		if (type != null)
		{
			return type;
		}
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			if (string.Equals(assembly.GetName().Name, "REPOLib", StringComparison.OrdinalIgnoreCase))
			{
				type = assembly.GetType("REPOLib.Modules.NetworkPrefabs", throwOnError: false);
				if (type != null)
				{
					return type;
				}
			}
		}
		return null;
	}

	private void SpawnLocalGrabProxy(int mimicId, Vector3 position, Quaternion rotation)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_grabProxyPrefab == (Object)null)
		{
			return;
		}
		try
		{
			GameObject val = Object.Instantiate<GameObject>(_grabProxyPrefab, position, rotation);
			((Object)val).name = "MimicReplay_GrabProxy_Local_" + mimicId;
			val.SetActive(true);
			MimicGrabProxy component = val.GetComponent<MimicGrabProxy>();
			if ((Object)(object)component != (Object)null)
			{
				component.Bind(mimicId);
				RegisterGrabProxy(mimicId, component);
				Log.LogWarning((object)"Using local mimic physics proxy because REPOLib network registration is unavailable");
			}
		}
		catch (Exception ex)
		{
			Log.LogError((object)("Mimic local grab proxy spawn failed: " + ex.GetBaseException().Message));
		}
	}

	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(HurtCollider), "CanHit")]
internal static class MimicHurtColliderPatch
{
	private static readonly FieldInfo EnemyDamageField = AccessTools.Field(typeof(HurtCollider), "enemyDamage");

	private static readonly FieldInfo EnemyKillField = AccessTools.Field(typeof(HurtCollider), "enemyKill");

	private static readonly FieldInfo EnemyHitForceField = AccessTools.Field(typeof(HurtCollider), "enemyHitForce");

	private static bool Prefix(HurtCollider __instance, GameObject hitObject, Vector3 hitPosition, ref bool __result)
	{
		//IL_0084: 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)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)hitObject == (Object)null)
		{
			return true;
		}
		MimicDamageReceiver componentInParent = hitObject.GetComponentInParent<MimicDamageReceiver>();
		if ((Object)(object)componentInParent == (Object)null)
		{
			return true;
		}
		int num = ((EnemyDamageField != null) ? Convert.ToInt32(EnemyDamageField.GetValue(__instance)) : 0);
		bool flag = EnemyKillField != null && Convert.ToBoolean(EnemyKillField.GetValue(__instance));
		float force = ((EnemyHitForceField != null) ? Convert.ToSingle(EnemyHitForceField.GetValue(__instance)) : 0f);
		Vector3 val = hitPosition - ((Component)__instance).transform.position;
		if (((Vector3)(ref val)).sqrMagnitude < 0.001f)
		{
			val = ((Component)__instance).transform.forward;
		}
		componentInParent.ReceiveWeaponHit(flag ? int.MaxValue : Mathf.Max(1, num), ((Vector3)(ref val)).normalized, force);
		__result = false;
		return false;
	}
}
[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_0076: 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_00a2: 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.SetActive(true);
		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;
			}
		}
		Transform val5 = FindClonedMeshRoot(avatar, val.transform, val2.transform);
		if ((Object)(object)val5 != (Object)null)
		{
			ActivateHierarchy(val5, val2.transform);
		}
		else if ((Object)(object)val3 != (Object)null)
		{
			ActivateHierarchy(val3, val2.transform);
		}
		List<string> list = new List<string>();
		HashSet<Renderer> hashSet = new HashSet<Renderer>();
		MonoBehaviour[] componentsInChildren2 = val2.GetComponentsInChildren<MonoBehaviour>(true);
		foreach (MonoBehaviour val6 in componentsInChildren2)
		{
			if (!((Object)(object)val6 == (Object)null) && ((object)val6).GetType().Name.IndexOf("Cosmetic", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				Renderer[] componentsInChildren3 = ((Component)val6).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer item in componentsInChildren3)
				{
					hashSet.Add(item);
				}
			}
		}
		Component[] componentsInChildren4 = val2.GetComponentsInChildren<Component>(true);
		foreach (Component val7 in componentsInChildren4)
		{
			if (!((Object)(object)val7 == (Object)null) && !(val7 is Transform) && !(val7 is Renderer) && !(val7 is Animator) && !(val7 is MeshFilter) && (val7 is Camera || val7 is AudioListener || val7 is Collider || val7 is Rigidbody || val7 is MonoBehaviour))
			{
				list.Add(((object)val7).GetType().FullName);
				Behaviour val8 = (Behaviour)(object)((val7 is Behaviour) ? val7 : null);
				if ((Object)(object)val8 != (Object)null)
				{
					val8.enabled = false;
				}
				Object.Destroy((Object)(object)val7);
			}
		}
		Renderer[] componentsInChildren5 = val2.GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val9 in componentsInChildren5)
		{
			Transform val10 = (((Object)(object)val5 != (Object)null) ? val5 : val3);
			if (!((Object)(object)val10 == (Object)null) && !((Object)(object)((Component)val9).transform == (Object)(object)val10) && !((Component)val9).transform.IsChildOf(val10) && !hashSet.Contains(val9))
			{
				val9.enabled = false;
				continue;
			}
			val9.shadowCastingMode = (ShadowCastingMode)1;
			val9.receiveShadows = true;
		}
		Renderer[] componentsInChildren6 = val2.GetComponentsInChildren<Renderer>(true);
		SkinnedMeshRenderer[] componentsInChildren7 = 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));
		if (Plugin.Instance.VerboseLogging.Value)
		{
			Plugin.Log.LogInfo((object)("Mimic appearance source=" + ((Object)((Component)avatar).gameObject).name + " visualPath=" + GetPath(val.transform) + " renderers=" + componentsInChildren6.Length + " skinnedRenderers=" + componentsInChildren7.Length + " animator=" + ((Object)(object)componentInChildren != (Object)null) + " disabled=[" + string.Join(",", list.ToArray()) + "]"));
		}
		int num = 0;
		Renderer[] array = componentsInChildren6;
		foreach (Renderer val11 in array)
		{
			if ((Object)(object)val11 != (Object)null && val11.enabled && ((Component)val11).gameObject.activeInHierarchy)
			{
				num++;
			}
		}
		if (num == 0)
		{
			Object.Destroy((Object)(object)val2);
			mimicAppearanceResult.Error = "visual root has no active Renderer (player appearance is not ready)";
			return mimicAppearanceResult;
		}
		mimicAppearanceResult.Success = true;
		return mimicAppearanceResult;
	}

	private static void ActivateHierarchy(Transform child, Transform root)
	{
		Transform val = child;
		while ((Object)(object)val != (Object)null)
		{
			((Component)val).gameObject.SetActive(true);
			if ((Object)(object)val == (Object)(object)root)
			{
				break;
			}
			val = val.parent;
		}
	}

	private static Transform FindClonedMeshRoot(PlayerAvatar avatar, Transform visualRoot, Transform clonedRoot)
	{
		FieldInfo field = typeof(PlayerAvatar).GetField("playerAvatarVisuals", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		Component val = (Component)((field != null) ? /*isinst with value type is only supported in some contexts*/: null);
		if ((Object)(object)val == (Object)null)
		{
			return null;
		}
		FieldInfo field2 = ((object)val).GetType().GetField("meshParent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		GameObject val2 = (GameObject)((field2 != null) ? /*isinst with value type is only supported in some contexts*/: null);
		if ((Object)(object)val2 == (Object)null)
		{
			return null;
		}
		string relativePath = GetRelativePath(visualRoot, val2.transform);
		if (!string.IsNullOrEmpty(relativePath))
		{
			return clonedRoot.Find(relativePath);
		}
		return clonedRoot;
	}

	private static string GetRelativePath(Transform root, Transform child)
	{
		List<string> list = new List<string>();
		Transform val = child;
		while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root)
		{
			list.Add(((Object)val).name);
			val = val.parent;
		}
		if ((Object)(object)val != (Object)(object)root)
		{
			return null;
		}
		list.Reverse();
		return string.Join("/", list.ToArray());
	}

	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 NavMeshAgent _agent;

	private 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 LineRenderer[] _navigationRays;

	private float _nextObstacleRepath;

	private bool _wasJumping;

	private bool _wasSprinting;

	private bool _chaseCrouching;

	private bool _chaseCrawling;

	private GameObject _fakeHeldObject;

	private int _fakeHeldSourceId;

	private int _health;

	private Vector3 _externalOffset;

	private bool _disposed;

	private float _nextNetworkStateTime;

	private Vector3 _networkPosition;

	private Quaternion _networkRotation;

	private Vector3 _networkVelocity;

	private Vector3 _networkSmoothVelocity;

	private float _lastNetworkStateTime;

	private bool _hasNetworkState;

	private bool _grabPhysicsActive;

	private Transform _mouthTransform;

	private Quaternion _mouthRestRotation;

	private readonly float[] _mouthSamples = new float[64];

	private float _tumbledUntil;

	private float _replayIdleSince;

	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);

	private static readonly FieldInfo DoorClosedField = typeof(PhysGrabHinge).GetField("closed", BindingFlags.Instance | BindingFlags.NonPublic);

	private readonly Dictionary<int, float> _doorOpenCooldowns = new Dictionary<int, float>();

	internal int NetworkId { get; private set; }

	internal Vector3 WorldPosition
	{
		get
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_mimicObject != (Object)null))
			{
				return Vector3.zero;
			}
			return _mimicObject.transform.position;
		}
	}

	internal Quaternion WorldRotation
	{
		get
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_mimicObject != (Object)null))
			{
				return Quaternion.identity;
			}
			return _mimicObject.transform.rotation;
		}
	}

	internal bool CanBeGrabbed
	{
		get
		{
			if (!(Time.time < _tumbledUntil))
			{
				return _grabPhysicsActive;
			}
			return true;
		}
	}

	internal bool IsAlive
	{
		get
		{
			if (!_disposed)
			{
				return (Object)(object)_mimicObject != (Object)null;
			}
			return false;
		}
	}

	public MimicController(GameObject source, PlayerRecorder recorder, float selectedDelay, Plugin plugin, bool authoritative, int networkId)
	{
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Expected O, but got Unknown
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Expected O, but got Unknown
		//IL_00ec: 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_0284: Unknown result type (might be due to invalid IL or missing references)
		//IL_0289: Unknown result type (might be due to invalid IL or missing references)
		//IL_028f: Unknown result type (might be due to invalid IL or missing references)
		//IL_029b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02db: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f7: 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_01cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01db: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_03eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_04b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_04bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_04c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_04c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_033f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0354: Unknown result type (might be due to invalid IL or missing references)
		//IL_0359: Unknown result type (might be due to invalid IL or missing references)
		//IL_0367: Unknown result type (might be due to invalid IL or missing references)
		//IL_036c: Unknown result type (might be due to invalid IL or missing references)
		//IL_036e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0216: Unknown result type (might be due to invalid IL or missing references)
		//IL_021d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0222: Unknown result type (might be due to invalid IL or missing references)
		//IL_0227: Unknown result type (might be due to invalid IL or missing references)
		//IL_0234: Unknown result type (might be due to invalid IL or missing references)
		//IL_0241: Unknown result type (might be due to invalid IL or missing references)
		//IL_0246: Unknown result type (might be due to invalid IL or missing references)
		//IL_025c: Unknown result type (might be due to invalid IL or missing references)
		_plugin = plugin;
		_authoritative = authoritative;
		NetworkId = networkId;
		_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;
		_health = _plugin.MimicHealth.Value;
		MimicDamageReceiver mimicDamageReceiver = _mimicObject.AddComponent<MimicDamageReceiver>();
		mimicDamageReceiver.Initialize(this);
		int num = ResolveEnemyHitLayer();
		if (num >= 0)
		{
			_mimicObject.layer = num;
		}
		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;
		ResolveMouthTransform();
		_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 num2 = Random.Range(0f, (float)Math.PI * 2f);
			Vector3 val5 = new Vector3(Mathf.Cos(num2), 0f, Mathf.Sin(num2)) * _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;
		_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;
		CreateNavigationRays();
		_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()
	{
		if ((Object)(object)_mimicObject == (Object)null || _segment.Count < 2)
		{
			return;
		}
		if (_grabPhysicsActive)
		{
			TickVoiceReplay();
			SendNetworkStateIfNeeded();
			return;
		}
		if (!_authoritative && PhotonNetwork.InRoom)
		{
			TickNetworkReplica();
			TickVoiceReplay();
			return;
		}
		UpdateVision();
		if (_mode == MimicMode.Replay)
		{
			TickReplay();
		}
		else if (_mode == MimicMode.Chase)
		{
			TickChase();
		}
		else if (_mode == MimicMode.Attack)
		{
			TickAttack();
		}
		else
		{
			TickStuckRecovery();
		}
		TickVoiceReplay();
		SendNetworkStateIfNeeded();
		if (_plugin.VerboseLogging.Value && Time.unscaledTime >= _nextVerboseLogTime)
		{
			_nextVerboseLogTime = Time.unscaledTime + 1f;
			LogPath("periodic");
		}
	}

	private void SendNetworkStateIfNeeded()
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		if (_authoritative && PhotonNetwork.InRoom && !(Time.unscaledTime < _nextNetworkStateTime))
		{
			_nextNetworkStateTime = Time.unscaledTime + 0.1f;
			_plugin.SendMimicState(NetworkId, _mimicObject.transform.position, _mimicObject.transform.rotation);
		}
	}

	internal void ApplyGrabPose(Vector3 position, Quaternion rotation, bool active)
	{
		//IL_0099: Unknown result type (might