Decompiled source of QuantumLocked v0.1.0

neogalt-br.QuantumLocked.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalCompanyInputUtils.Api;
using LethalNetworkAPI;
using LethalNetworkAPI.Utils;
using Microsoft.CodeAnalysis;
using QuantumLocked.Enemies;
using QuantumLocked.Eyes;
using QuantumLocked.Network;
using QuantumLocked.Overlay;
using Steamworks;
using Steamworks.Data;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.InputSystem;
using UnityEngine.UI;

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

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace QuantumLocked
{
	internal static class DebugLog
	{
		[Conditional("DEBUG")]
		public static void Info(string message)
		{
			QuantumLocked.Logger.LogInfo((object)("[debug] " + message));
		}
	}
	[Serializable]
	internal sealed class GameplaySettings
	{
		public float BlinkLength;

		public float CalmIntervalMin;

		public float CalmIntervalMax;

		public float MaxInsanityIntervalMin;

		public float MaxInsanityIntervalMax;

		public float EyesShutInsanityDrain;

		public float CoilHeadBlinkSpeedMultiplier;

		public bool ReflexBlinksAtTerminal;

		[NonSerialized]
		private BlinkSettings? _blinkSettings;

		public BlinkSettings BlinkSettings
		{
			get
			{
				BlinkSettings blinkSettings = _blinkSettings;
				if (blinkSettings == null)
				{
					BlinkSettings obj = new BlinkSettings
					{
						BlinkLength = BlinkLength,
						CalmIntervalMin = CalmIntervalMin,
						CalmIntervalMax = CalmIntervalMax,
						MaxInsanityIntervalMin = MaxInsanityIntervalMin,
						MaxInsanityIntervalMax = MaxInsanityIntervalMax
					};
					BlinkSettings blinkSettings2 = obj;
					_blinkSettings = obj;
					blinkSettings = blinkSettings2;
				}
				return blinkSettings;
			}
		}
	}
	internal sealed class LocalEyes : MonoBehaviour
	{
		private readonly EyesModel _eyes = new EyesModel(new Random().NextDouble);

		private EyelidOverlay? _overlay;

		private WorldBlur? _worldBlur;

		private void Start()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			RawImage playerScreenTexture = HUDManager.Instance.playerScreenTexture;
			RectTransform canvas = (RectTransform)((Component)((Graphic)playerScreenTexture).canvas.rootCanvas).transform;
			QuickMenuManager val = Object.FindObjectOfType<QuickMenuManager>(true);
			Transform keepVisible = (((Object)(object)val != (Object)null && (Object)(object)val.menuContainer != (Object)null) ? val.menuContainer.transform : null);
			_worldBlur = new WorldBlur(playerScreenTexture);
			_overlay = new EyelidOverlay(canvas, keepVisible);
		}

		private void OnDestroy()
		{
			_worldBlur?.Destroy();
			_overlay?.Destroy();
		}

		private void Update()
		{
			GameNetworkManager instance = GameNetworkManager.Instance;
			StartOfRound instance2 = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || _overlay == null || _worldBlur == null)
			{
				return;
			}
			PlayerControllerB localPlayerController = instance.localPlayerController;
			if ((Object)(object)localPlayerController == (Object)null)
			{
				return;
			}
			if (localPlayerController.isPlayerDead)
			{
				_eyes.Reset();
				BlinkStates.SetLocal(localPlayerController, BlinkKind.None);
				_overlay.SetClosure(0f);
				_worldBlur.Set(0f, 0f, 0f);
				return;
			}
			QuantumLockedConfig configuration = QuantumLocked.Configuration;
			GameplaySettings current = EffectiveSettings.Current;
			bool isPlayerControlled = localPlayerController.isPlayerControlled;
			bool flag = !instance2.inShipPhase && instance2.shipDoorsEnabled && (!instance2.shipIsLeaving || !localPlayerController.isInElevator);
			bool flag2 = !localPlayerController.inTerminalMenu || current.ReflexBlinksAtTerminal;
			_eyes.Update(new EyesInput
			{
				DeltaTime = Time.deltaTime,
				Insanity = localPlayerController.insanityLevel,
				MaxInsanity = localPlayerController.maxInsanityLevel,
				ReflexBlinksAllowed = (isPlayerControlled && flag && flag2),
				Cosmetic = !(isPlayerControlled && flag),
				Settings = current.BlinkSettings,
				FadeProportion = configuration.FadeProportion.Value
			});
			InputAction blinkKey = QuantumLocked.Inputs.BlinkKey;
			if (!localPlayerController.inTerminalMenu && !localPlayerController.isTypingChat && blinkKey.WasPressedThisFrame())
			{
				_eyes.PressKey();
			}
			if (blinkKey.WasReleasedThisFrame())
			{
				_eyes.ReleaseKey();
			}
			BlinkStates.SetLocal(localPlayerController, _eyes.SharedKind);
			_overlay.SetClosure(_eyes.Closure);
			_worldBlur.Set(_eyes.Blur, _eyes.Closure, configuration.BlurStrength.Value);
		}
	}
	[BepInPlugin("neogalt-br.QuantumLocked", "Quantum Locked", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency("LethalNetworkAPI", "3.4.0")]
	public class QuantumLocked : BaseUnityPlugin
	{
		public static QuantumLocked Instance { get; private set; }

		internal static ManualLogSource Logger { get; private set; }

		internal static Harmony? Harmony { get; set; }

		internal static QuantumLockedConfig Configuration { get; private set; }

		internal static QuantumLockedInputs Inputs { get; private set; }

		private void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			Configuration = new QuantumLockedConfig(((BaseUnityPlugin)this).Config);
			Inputs = new QuantumLockedInputs();
			EffectiveSettings.Init(Configuration);
			BlinkStates.Init();
			Patch();
			Logger.LogInfo((object)"neogalt-br.QuantumLocked v0.1.0 has loaded!");
		}

		internal static void Patch()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			if (Harmony == null)
			{
				Harmony = new Harmony("neogalt-br.QuantumLocked");
			}
			Logger.LogDebug((object)"Patching...");
			Harmony.PatchAll();
			Logger.LogDebug((object)"Finished patching!");
		}

		internal static void Unpatch()
		{
			Logger.LogDebug((object)"Unpatching...");
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Logger.LogDebug((object)"Finished unpatching!");
		}
	}
	internal sealed class QuantumLockedConfig
	{
		private const string Gameplay = "Gameplay";

		private const string Visuals = "Visuals";

		private readonly ConfigEntry<float> _blinkLength;

		private readonly ConfigEntry<float> _calmIntervalMin;

		private readonly ConfigEntry<float> _calmIntervalMax;

		private readonly ConfigEntry<float> _maxInsanityIntervalMin;

		private readonly ConfigEntry<float> _maxInsanityIntervalMax;

		private readonly ConfigEntry<float> _eyesShutInsanityDrain;

		private readonly ConfigEntry<float> _coilHeadBlinkSpeedMultiplier;

		private readonly ConfigEntry<bool> _reflexBlinksAtTerminal;

		public GameplaySettings LocalGameplay { get; private set; }

		public ConfigEntry<float> FadeProportion { get; }

		public ConfigEntry<float> BlurStrength { get; }

		public event Action? LocalGameplayChanged;

		public QuantumLockedConfig(ConfigFile file)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Expected O, but got Unknown
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Expected O, but got Unknown
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Expected O, but got Unknown
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Expected O, but got Unknown
			BlinkSettings blinkSettings = new BlinkSettings();
			_blinkLength = file.Bind<float>("Gameplay", "Blink length", blinkSettings.BlinkLength, new ConfigDescription("How long a Reflex Blink or a Manual Blink keeps the eyes closed, in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 5f), Array.Empty<object>()));
			_calmIntervalMin = file.Bind<float>("Gameplay", "Calm Blink Interval min", blinkSettings.CalmIntervalMin, new ConfigDescription("The shortest time between Reflex Blinks at zero Insanity, in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 120f), Array.Empty<object>()));
			_calmIntervalMax = file.Bind<float>("Gameplay", "Calm Blink Interval max", blinkSettings.CalmIntervalMax, new ConfigDescription("The longest time between Reflex Blinks at zero Insanity, in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 120f), Array.Empty<object>()));
			_maxInsanityIntervalMin = file.Bind<float>("Gameplay", "Maximum Insanity Blink Interval min", blinkSettings.MaxInsanityIntervalMin, new ConfigDescription("The shortest time between Reflex Blinks at maximum Insanity, in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 120f), Array.Empty<object>()));
			_maxInsanityIntervalMax = file.Bind<float>("Gameplay", "Maximum Insanity Blink Interval max", blinkSettings.MaxInsanityIntervalMax, new ConfigDescription("The longest time between Reflex Blinks at maximum Insanity, in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 120f), Array.Empty<object>()));
			_eyesShutInsanityDrain = file.Bind<float>("Gameplay", "Eyes Shut Insanity drain", 1f, new ConfigDescription("How much Insanity per second Eyes Shut removes, on top of the game's own change.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			_coilHeadBlinkSpeedMultiplier = file.Bind<float>("Gameplay", "Coil Head Blink speed multiplier", 1f, new ConfigDescription("Scales a Coil Head's speed while it moves only because everyone who could see it is Blinking.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			_reflexBlinksAtTerminal = file.Bind<bool>("Gameplay", "Reflex Blinks at the terminal", true, "Whether Reflex Blinks happen while using the terminal.");
			FadeProportion = file.Bind<float>("Visuals", "Eyelid fade proportion", 0.2f, new ConfigDescription("The share of the Blink length that closing, and separately opening, the eyelids takes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), Array.Empty<object>()));
			BlurStrength = file.Bind<float>("Visuals", "Blink blur strength", 0.7f, new ConfigDescription("How out of focus the world gets while the eyelids close and open, and as the eyes refocus. 0 turns the blur off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			file.SettingChanged += delegate(object _, SettingChangedEventArgs args)
			{
				if (!(args.ChangedSetting.Definition.Section != "Gameplay"))
				{
					LocalGameplay = ReadGameplaySettings();
					this.LocalGameplayChanged?.Invoke();
				}
			};
			LocalGameplay = ReadGameplaySettings();
		}

		private GameplaySettings ReadGameplaySettings()
		{
			return new GameplaySettings
			{
				BlinkLength = _blinkLength.Value,
				CalmIntervalMin = _calmIntervalMin.Value,
				CalmIntervalMax = _calmIntervalMax.Value,
				MaxInsanityIntervalMin = _maxInsanityIntervalMin.Value,
				MaxInsanityIntervalMax = _maxInsanityIntervalMax.Value,
				EyesShutInsanityDrain = _eyesShutInsanityDrain.Value,
				CoilHeadBlinkSpeedMultiplier = _coilHeadBlinkSpeedMultiplier.Value,
				ReflexBlinksAtTerminal = _reflexBlinksAtTerminal.Value
			};
		}
	}
	public class QuantumLockedInputs : LcInputActions
	{
		[InputAction(/*Could not decode attribute arguments.*/)]
		public InputAction BlinkKey { get; set; }
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "neogalt-br.QuantumLocked";

		public const string PLUGIN_NAME = "Quantum Locked";

		public const string PLUGIN_VERSION = "0.1.0";
	}
}
namespace QuantumLocked.Patches
{
	[HarmonyPatch(typeof(GameNetworkManager))]
	public class GameNetworkManagerPatch
	{
		private const char Separator = ',';

		[HarmonyPatch("SteamMatchmaking_OnLobbyCreated")]
		[HarmonyPostfix]
		private static void MarkLobbyAsQuantumLocked(Result result, ref Lobby lobby)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			if ((int)result == 1)
			{
				((Lobby)(ref lobby)).SetData("neogalt-br.QuantumLocked", "0.1.0");
			}
		}

		[HarmonyPatch("LobbyDataIsJoinable")]
		[HarmonyPostfix]
		private static void RefuseLobbiesWithoutQuantumLocked(ref Lobby lobby, ref bool __result)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (__result && string.IsNullOrEmpty(((Lobby)(ref lobby)).GetData("neogalt-br.QuantumLocked")))
			{
				__result = false;
				Object.FindObjectOfType<MenuManager>().SetLoadingScreen(false, (RoomEnter)2, "The server host isn't running the Quantum Locked mod.");
				QuantumLocked.Logger.LogInfo((object)string.Format("Refused lobby {0}: its host isn't running {1}.", ((Lobby)(ref lobby)).Id, "Quantum Locked"));
			}
		}

		[HarmonyPatch("SetConnectionDataBeforeConnecting")]
		[HarmonyPostfix]
		private static void AnnounceQuantumLocked()
		{
			NetworkConfig networkConfig = NetworkManager.Singleton.NetworkConfig;
			string text = Encoding.ASCII.GetString(networkConfig.ConnectionData);
			networkConfig.ConnectionData = Encoding.ASCII.GetBytes(text + ",neogalt-br.QuantumLocked");
		}

		[HarmonyPatch("ConnectionApproval")]
		[HarmonyPostfix]
		private static void RejectClientsWithoutQuantumLocked(ref ConnectionApprovalRequest request, ConnectionApprovalResponse response)
		{
			if (request.ClientNetworkId != NetworkManager.Singleton.LocalClientId && response.Approved)
			{
				string[] source = Encoding.ASCII.GetString(request.Payload).Split(',');
				if (!source.Contains("neogalt-br.QuantumLocked"))
				{
					response.Approved = false;
					response.Reason = "This lobby requires the Quantum Locked mod.";
					QuantumLocked.Logger.LogInfo((object)string.Format("Turned away client {0}: {1} isn't loaded.", request.ClientNetworkId, "Quantum Locked"));
				}
			}
		}
	}
	[HarmonyPatch(typeof(HUDManager))]
	public class HUDManagerPatch
	{
		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		private static void AddLocalEyes(HUDManager __instance)
		{
			((Component)__instance).gameObject.AddComponent<LocalEyes>();
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB))]
	public class PlayerControllerBInsanityPatch
	{
		[HarmonyPatch("SetPlayerSanityLevel")]
		[HarmonyPostfix]
		private static void DrainWhileEyesShut(PlayerControllerB __instance)
		{
			__instance.insanityLevel = InsanityDrain.Apply(BlinkStates.KindOf(__instance), __instance.insanityLevel, EffectiveSettings.Current.EyesShutInsanityDrain, Time.deltaTime);
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB))]
	public class PlayerControllerBPatch
	{
		private const float NoLineOfSightAngle = -361f;

		private static bool _seeingThroughBlinks;

		internal static bool HasLineOfSightThroughBlinks(PlayerControllerB player, Vector3 position, float width)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			_seeingThroughBlinks = true;
			try
			{
				return player.HasLineOfSightToPosition(position, width, 60, -1f, -1);
			}
			finally
			{
				_seeingThroughBlinks = false;
			}
		}

		[HarmonyPatch("HasLineOfSightToPosition")]
		[HarmonyPostfix]
		private static void BlindWhileBlinking(PlayerControllerB __instance, ref bool __result)
		{
			if (__result && !_seeingThroughBlinks && BlinkStates.IsBlinking(__instance))
			{
				__result = false;
			}
		}

		[HarmonyPatch("LineOfSightToPositionAngle")]
		[HarmonyPostfix]
		private static void BlindWhileBlinking(PlayerControllerB __instance, ref float __result)
		{
			if (__result != -361f && BlinkStates.IsBlinking(__instance))
			{
				__result = -361f;
			}
		}
	}
	[HarmonyPatch(typeof(SpringManAI))]
	public class SpringManAIPatch
	{
		private const int ChasingState = 1;

		private const float LowPoint = 0.6f;

		private const float HighPoint = 3f;

		private const float ViewWidth = 68f;

		private const float MinimumEyeDistance = 0.3f;

		private static readonly FieldRef<SpringManAI, float> ChaseSpeed = AccessTools.FieldRefAccess<SpringManAI, float>("currentChaseSpeed");

		private static readonly ConditionalWeakTable<SpringManAI, StrongBox<float>> VanillaChaseSpeeds = new ConditionalWeakTable<SpringManAI, StrongBox<float>>();

		private static readonly List<PlayerSight> Sights = new List<PlayerSight>();

		private const int VanillaPlayerSlots = 4;

		private static bool _gameChecksEverySlot;

		[HarmonyPatch("Update")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> CheckEveryPlayerSlot(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[2]
			{
				new CodeMatch((OpCode?)OpCodes.Ldc_I4_4, (object)null, (string)null),
				new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction instruction) => instruction.opcode == OpCodes.Blt || instruction.opcode == OpCodes.Blt_S), (string)null)
			});
			_gameChecksEverySlot = val.IsValid;
			if (val.IsInvalid)
			{
				QuantumLocked.Logger.LogWarning((object)"Couldn't find the Coil Head's 4-player freeze check, so only the first 4 players can freeze a Coil Head or let it move on their Blinks. A game update may have changed it.");
				return val.InstructionEnumeration();
			}
			return val.SetInstruction(CodeInstruction.Call((Expression<Action>)(() => PlayerSlots()))).InstructionEnumeration();
		}

		private static int PlayerSlots()
		{
			return StartOfRound.Instance.allPlayerScripts.Length;
		}

		private static int SlotsTheGameChecks()
		{
			if (!_gameChecksEverySlot)
			{
				return Mathf.Min(4, PlayerSlots());
			}
			return PlayerSlots();
		}

		[HarmonyPatch("Update")]
		[HarmonyPrefix]
		private static void ScaleChaseSpeedForBlinks(SpringManAI __instance)
		{
			if (!((NetworkBehaviour)__instance).IsOwner || ((EnemyAI)__instance).isEnemyDead || ((EnemyAI)__instance).currentBehaviourStateIndex != 1 || __instance.onCooldownPhase > 0f)
			{
				if (VanillaChaseSpeeds.TryGetValue(__instance, out StrongBox<float> value))
				{
					ChaseSpeed.Invoke(__instance) = value.Value;
				}
				return;
			}
			float value2 = VanillaChaseSpeeds.GetValue(__instance, (SpringManAI ai) => new StrongBox<float>(ChaseSpeed.Invoke(ai))).Value;
			float coilHeadBlinkSpeedMultiplier = EffectiveSettings.Current.CoilHeadBlinkSpeedMultiplier;
			if (coilHeadBlinkSpeedMultiplier == 1f)
			{
				ChaseSpeed.Invoke(__instance) = value2;
				return;
			}
			switch (CoilHeadRule.Decide(SightsOf(__instance)))
			{
			case CoilHeadMovement.BlinkMoving:
				ChaseSpeed.Invoke(__instance) = value2 * coilHeadBlinkSpeedMultiplier;
				break;
			case CoilHeadMovement.VanillaMoving:
				ChaseSpeed.Invoke(__instance) = value2;
				break;
			case CoilHeadMovement.Frozen:
				break;
			}
		}

		private static List<PlayerSight> SightsOf(SpringManAI coilHead)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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)
			//IL_0068: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			Sights.Clear();
			Vector3 position = ((Component)coilHead).transform.position;
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			int num = SlotsTheGameChecks();
			for (int i = 0; i < num; i++)
			{
				PlayerControllerB val = allPlayerScripts[i];
				if (((EnemyAI)coilHead).PlayerIsTargetable(val, false, false, true))
				{
					bool hasLineOfSight = (PlayerControllerBPatch.HasLineOfSightThroughBlinks(val, position + Vector3.up * 0.6f, 68f) || PlayerControllerBPatch.HasLineOfSightThroughBlinks(val, position + Vector3.up * 3f, 68f)) && coilHead.PlayerHasHorizontalLOS(val) && Vector3.Distance(((Component)val.gameplayCamera).transform.position, ((EnemyAI)coilHead).eye.position) > 0.3f;
					Sights.Add(new PlayerSight(hasLineOfSight, BlinkStates.IsBlinking(val)));
				}
			}
			return Sights;
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	public class StartOfRoundPatch
	{
		[HarmonyPatch("OnPlayerDC")]
		[HarmonyPostfix]
		private static void ForgetDisconnectedPlayersBlink(int playerObjectNumber)
		{
			BlinkStates.Clear(playerObjectNumber);
		}
	}
}
namespace QuantumLocked.Overlay
{
	internal sealed class BlurBand : MaskableGraphic
	{
		private const float BandHeight = 0.1f;

		private const int Segments = 32;

		private Texture? _texture;

		private Rect _uvRect = new Rect(0f, 0f, 1f, 1f);

		private float _closure;

		public bool Upper { get; set; }

		public override Texture mainTexture
		{
			get
			{
				if (!((Object)(object)_texture != (Object)null))
				{
					return (Texture)(object)Graphic.s_WhiteTexture;
				}
				return _texture;
			}
		}

		public void Set(Texture texture, Rect uvRect, float closure, float alpha)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_texture != (Object)(object)texture)
			{
				_texture = texture;
				((Graphic)this).SetMaterialDirty();
			}
			_uvRect = uvRect;
			_closure = closure;
			((Graphic)this).color = new Color(1f, 1f, 1f, alpha);
			((Graphic)this).SetVerticesDirty();
		}

		protected override void OnPopulateMesh(VertexHelper vh)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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)
			//IL_005a: 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_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			vh.Clear();
			Rect rect = ((Graphic)this).rectTransform.rect;
			float num = EyelidShape.UpperLidBottom(_closure);
			Color32 vertexColor = Color32.op_Implicit(((Graphic)this).color);
			Color32 vertexColor2 = Color32.op_Implicit(new Color(((Graphic)this).color.r, ((Graphic)this).color.g, ((Graphic)this).color.b, 0f));
			for (int i = 0; i <= 32; i++)
			{
				float u = (float)i / 32f;
				float num2 = num + EyelidShape.EdgeRise(u);
				AddVertex(vh, rect, u, num2 + 0.15f, vertexColor);
				AddVertex(vh, rect, u, num2, vertexColor);
				AddVertex(vh, rect, u, num2 - 0.1f, vertexColor2);
				if (i != 0)
				{
					int num3 = i * 3;
					int num4 = num3 - 3;
					for (int j = 0; j < 2; j++)
					{
						vh.AddTriangle(num4 + j, num3 + j, num3 + j + 1);
						vh.AddTriangle(num4 + j, num3 + j + 1, num4 + j + 1);
					}
				}
			}
		}

		private void AddVertex(VertexHelper vh, Rect rect, float u, float upperY, Color32 vertexColor)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_007f: 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)
			float num = (Upper ? upperY : EyelidShape.MirrorToLowerLid(upperY));
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(((Rect)(ref rect)).xMin + u * ((Rect)(ref rect)).width, ((Rect)(ref rect)).yMin + num * ((Rect)(ref rect)).height);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(((Rect)(ref _uvRect)).xMin + u * ((Rect)(ref _uvRect)).width, ((Rect)(ref _uvRect)).yMin + num * ((Rect)(ref _uvRect)).height);
			vh.AddVert(Vector2.op_Implicit(val), vertexColor, Vector4.op_Implicit(val2));
		}
	}
	internal sealed class BlurCopy
	{
		private readonly int _width;

		private readonly List<RenderTexture> _steps = new List<RenderTexture>();

		private RenderTexture? _smoothed;

		private int _sourceWidth;

		private int _sourceHeight;

		private GraphicsFormat _format;

		public RenderTexture Reduced => _steps[_steps.Count - 1];

		public RenderTexture Smoothed => _smoothed;

		public BlurCopy(int width)
		{
			_width = width;
		}

		public void Make(Texture source)
		{
			EnsureTextures(source);
			Texture val = source;
			foreach (RenderTexture step in _steps)
			{
				Graphics.Blit(val, step);
				val = (Texture)(object)step;
			}
			Graphics.Blit(val, _smoothed);
		}

		public void Release()
		{
			foreach (RenderTexture step in _steps)
			{
				Release(step);
			}
			_steps.Clear();
			Release(_smoothed);
			_smoothed = null;
		}

		private void EnsureTextures(Texture source)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: 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)
			RenderTexture val = (RenderTexture)(object)((source is RenderTexture) ? source : null);
			GraphicsFormat val2 = (GraphicsFormat)((val == null) ? 4 : ((int)val.graphicsFormat));
			if (!((Object)(object)_smoothed != (Object)null) || source.width != _sourceWidth || source.height != _sourceHeight || val2 != _format)
			{
				Release();
				_sourceWidth = source.width;
				_sourceHeight = source.height;
				_format = val2;
				int num = source.width;
				int num2 = source.height;
				while (num / 2 >= _width)
				{
					num /= 2;
					num2 = Mathf.Max(1, num2 / 2);
					_steps.Add(CreateTexture(num, num2, val2));
				}
				int num3 = Mathf.Max(1, Mathf.RoundToInt((float)_width * (float)source.height / (float)source.width));
				if (num != _width || _steps.Count == 0)
				{
					_steps.Add(CreateTexture(_width, num3, val2));
				}
				_smoothed = CreateTexture(_width * 2, num3 * 2, val2);
			}
		}

		private static RenderTexture CreateTexture(int width, int height, GraphicsFormat format)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			RenderTexture val = new RenderTexture(width, height, 0, format)
			{
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1,
				hideFlags = (HideFlags)61
			};
			val.Create();
			return val;
		}

		private static void Release(RenderTexture? texture)
		{
			if (!((Object)(object)texture == (Object)null))
			{
				texture.Release();
				Object.Destroy((Object)(object)texture);
			}
		}
	}
	internal sealed class EyelidOverlay
	{
		private const float BlackFrom = 0.85f;

		private readonly GameObject _root;

		private readonly RectTransform _upperLid;

		private readonly RectTransform _lowerLid;

		private readonly RawImage _vignette;

		private readonly RawImage _black;

		public EyelidOverlay(RectTransform canvas, Transform? keepVisible)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = UiLayers.CreateFullScreenRect("QuantumLockedEyelids", canvas);
			_root = ((Component)val).gameObject;
			if ((Object)(object)keepVisible != (Object)null && keepVisible.IsChildOf((Transform)(object)canvas) && (Object)(object)keepVisible != (Object)(object)canvas)
			{
				Transform val2 = keepVisible;
				while ((Object)(object)val2.parent != (Object)(object)canvas)
				{
					val2 = val2.parent;
				}
				((Transform)val).SetSiblingIndex(val2.GetSiblingIndex());
			}
			_vignette = CreateLayer("Vignette", val, (Texture)(object)EyelidTextures.Vignette);
			_upperLid = ((Graphic)CreateLayer("UpperLid", val, (Texture)(object)EyelidTextures.Lid)).rectTransform;
			RawImage val3 = CreateLayer("LowerLid", val, (Texture)(object)EyelidTextures.Lid);
			val3.uvRect = new Rect(0f, 1f, 1f, -1f);
			_lowerLid = ((Graphic)val3).rectTransform;
			_black = CreateLayer("Black", val, (Texture)(object)Texture2D.whiteTexture);
			SetClosure(0f);
		}

		public void SetClosure(float closure)
		{
			//IL_003a: 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_0086: 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)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			closure = Mathf.Clamp01(closure);
			_root.SetActive(closure > 0f);
			if (!(closure <= 0f))
			{
				((Graphic)_vignette).color = new Color(0f, 0f, 0f, closure);
				((Graphic)_black).color = new Color(0f, 0f, 0f, Mathf.InverseLerp(0.85f, 1f, closure));
				float num = EyelidShape.UpperLidBottom(closure);
				_upperLid.anchorMin = new Vector2(0f, num);
				_upperLid.anchorMax = new Vector2(1f, num + 1f);
				float num2 = EyelidShape.MirrorToLowerLid(num);
				_lowerLid.anchorMin = new Vector2(0f, num2 - 1f);
				_lowerLid.anchorMax = new Vector2(1f, num2);
			}
		}

		public void Destroy()
		{
			Object.Destroy((Object)(object)_root);
		}

		private static RawImage CreateLayer(string name, RectTransform parent, Texture texture)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			RawImage val = UiLayers.CreateGraphic<RawImage>(name, parent);
			((Graphic)val).color = Color.black;
			val.texture = texture;
			return val;
		}
	}
	internal static class EyelidShape
	{
		public const float LidHeight = 1f;

		public const float LidArc = 0.3f;

		public const float LidSoftness = 0.15f;

		private const float LidTravel = 0.95000005f;

		public static float MirrorToLowerLid(float upperY)
		{
			return 1f - upperY;
		}

		public static float UpperLidBottom(float closure)
		{
			return 1f - Mathf.SmoothStep(0f, 1f, closure) * 0.95000005f;
		}

		public static float EdgeRise(float u)
		{
			float num = 2f * u - 1f;
			return 0.3f * (1f - num * num) * 1f;
		}
	}
	internal static class EyelidTextures
	{
		private static Texture2D? _lid;

		private static Texture2D? _vignette;

		public static Texture2D Lid => _lid ?? (_lid = CreateLid(256, 128));

		public static Texture2D Vignette => _vignette ?? (_vignette = CreateVignette(256));

		private static Texture2D CreateLid(int width, int height)
		{
			//IL_006b: 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)
			Color32[] array = (Color32[])(object)new Color32[width * height];
			for (int i = 0; i < height; i++)
			{
				float num = ((float)i + 0.5f) / (float)height;
				for (int j = 0; j < width; j++)
				{
					float u = ((float)j + 0.5f) / (float)width;
					float num2 = EyelidShape.EdgeRise(u) / 1f;
					float num3 = Mathf.SmoothStep(0f, 1f, (num - num2) / 0.15f);
					array[i * width + j] = new Color32((byte)0, (byte)0, (byte)0, (byte)Mathf.RoundToInt(num3 * 255f));
				}
			}
			return CreateTexture(width, height, array);
		}

		private static Texture2D CreateVignette(int width)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			Color32[] array = (Color32[])(object)new Color32[width];
			for (int i = 0; i < width; i++)
			{
				float num = Mathf.Abs(2f * ((float)i + 0.5f) / (float)width - 1f);
				float num2 = Mathf.SmoothStep(0f, 1f, num);
				array[i] = new Color32((byte)0, (byte)0, (byte)0, (byte)Mathf.RoundToInt(num2 * 255f));
			}
			return CreateTexture(width, 1, array);
		}

		private static Texture2D CreateTexture(int width, int height, Color32[] pixels)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			Texture2D val = new Texture2D(width, height, (TextureFormat)4, false)
			{
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1,
				hideFlags = (HideFlags)61
			};
			val.SetPixels32(pixels);
			val.Apply(false, true);
			return val;
		}
	}
	internal static class UiLayers
	{
		public static RectTransform CreateFullScreenRect(string name, RectTransform parent)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) })
			{
				layer = ((Component)parent).gameObject.layer
			};
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent((Transform)(object)parent, false);
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.offsetMin = Vector2.zero;
			val2.offsetMax = Vector2.zero;
			((Transform)val2).SetAsLastSibling();
			return val2;
		}

		public static T CreateGraphic<T>(string name, RectTransform parent) where T : Graphic
		{
			T val = ((Component)CreateFullScreenRect(name, parent)).gameObject.AddComponent<T>();
			((Graphic)val).raycastTarget = false;
			return val;
		}
	}
	internal sealed class WorldBlur
	{
		private const int DefocusWidth = 108;

		private const int BandWidth = 54;

		private const float BandFadeIn = 0.25f;

		private readonly RawImage _gameView;

		private readonly GameObject _root;

		private readonly RawImage _defocus;

		private readonly BlurBand _upperBand;

		private readonly BlurBand _lowerBand;

		private readonly BlurCopy _defocusCopy = new BlurCopy(108);

		private readonly BlurCopy _bandCopy = new BlurCopy(54);

		public WorldBlur(RawImage gameView)
		{
			_gameView = gameView;
			RectTransform val = UiLayers.CreateFullScreenRect("QuantumLockedWorldBlur", ((Graphic)gameView).rectTransform);
			_root = ((Component)val).gameObject;
			_defocus = UiLayers.CreateGraphic<RawImage>("Defocus", val);
			_upperBand = UiLayers.CreateGraphic<BlurBand>("UpperBand", val);
			_upperBand.Upper = true;
			_lowerBand = UiLayers.CreateGraphic<BlurBand>("LowerBand", val);
			_root.SetActive(false);
		}

		public void Set(float blur, float closure, float strength)
		{
			//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_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: 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)
			float num = strength * (1f - (1f - blur) * (1f - blur));
			Texture texture = _gameView.texture;
			bool flag = num > 0f && closure < 1f && (Object)(object)texture != (Object)null;
			_root.SetActive(flag);
			if (flag)
			{
				Capture(texture);
				Rect uvRect = _gameView.uvRect;
				_defocus.texture = (Texture)(object)_defocusCopy.Smoothed;
				_defocus.uvRect = uvRect;
				((Graphic)_defocus).color = new Color(1f, 1f, 1f, num);
				float alpha = num * Mathf.Clamp01(closure / 0.25f);
				_upperBand.Set((Texture)(object)_bandCopy.Smoothed, uvRect, closure, alpha);
				_lowerBand.Set((Texture)(object)_bandCopy.Smoothed, uvRect, closure, alpha);
			}
		}

		public void Destroy()
		{
			Object.Destroy((Object)(object)_root);
			_defocusCopy.Release();
			_bandCopy.Release();
		}

		private void Capture(Texture source)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			FilterMode filterMode = source.filterMode;
			source.filterMode = (FilterMode)1;
			_defocusCopy.Make(source);
			source.filterMode = filterMode;
			_bandCopy.Make((Texture)(object)_defocusCopy.Reduced);
		}
	}
}
namespace QuantumLocked.Network
{
	internal static class BlinkStates
	{
		[Serializable]
		private struct Report
		{
			public int Player;

			public BlinkKind Kind;
		}

		private static LNetworkMessage<Report> _message = null;

		private static readonly Dictionary<int, BlinkKind> Kinds = new Dictionary<int, BlinkKind>();

		private static BlinkKind _localKind;

		public static void Init()
		{
			_message = LNetworkMessage<Report>.Connect("BlinkState", (Action<Report, ulong>)Relay, (Action<Report>)Store, (Action<Report, ulong>)null);
			LNetworkUtils.OnNetworkStart += delegate
			{
				Kinds.Clear();
				_localKind = BlinkKind.None;
			};
		}

		public static bool IsBlinking(PlayerControllerB player)
		{
			return KindOf(player) != BlinkKind.None;
		}

		public static BlinkKind KindOf(PlayerControllerB player)
		{
			if (player.isPlayerDead || !Kinds.TryGetValue((int)player.playerClientId, out var value))
			{
				return BlinkKind.None;
			}
			return value;
		}

		public static void SetLocal(PlayerControllerB local, BlinkKind kind)
		{
			if (kind == _localKind)
			{
				return;
			}
			_localKind = kind;
			Set((int)local.playerClientId, kind);
			if (LNetworkUtils.IsConnected)
			{
				Report report = new Report
				{
					Kind = kind
				};
				if (LNetworkUtils.IsHostOrServer)
				{
					Relay(report, NetworkManager.Singleton.LocalClientId);
				}
				else
				{
					_message.SendServer(report);
				}
			}
		}

		public static void Clear(int player)
		{
			Kinds.Remove(player);
		}

		public static string DescribeBlinking()
		{
			if (Kinds.Count != 0)
			{
				return string.Join(", ", Kinds.Select((KeyValuePair<int, BlinkKind> entry) => $"player {entry.Key}: {entry.Value}"));
			}
			return "nobody";
		}

		private static void Relay(Report report, ulong sender)
		{
			StartOfRound instance = StartOfRound.Instance;
			if (!((Object)(object)instance == (Object)null) && instance.ClientPlayerList.TryGetValue(sender, out var value))
			{
				report.Player = value;
				_message.SendClients(report);
			}
		}

		private static void Store(Report report)
		{
			PlayerControllerB localPlayerController = GameNetworkManager.Instance.localPlayerController;
			if (!((Object)(object)localPlayerController != (Object)null) || report.Player != (int)localPlayerController.playerClientId)
			{
				Set(report.Player, report.Kind);
			}
		}

		private static void Set(int player, BlinkKind kind)
		{
			if (kind == BlinkKind.None)
			{
				Kinds.Remove(player);
			}
			else
			{
				Kinds[player] = kind;
			}
		}
	}
	internal static class EffectiveSettings
	{
		private static QuantumLockedConfig _config;

		private static LNetworkVariable<GameplaySettings?> _synced;

		private static GameplaySettings? _lastCurrent;

		public static GameplaySettings Current => (LNetworkUtils.IsConnected && !LNetworkUtils.IsHostOrServer && _synced.Value != null) ? _synced.Value : _config.LocalGameplay;

		public static void Init(QuantumLockedConfig config)
		{
			_config = config;
			_synced = LNetworkVariable<GameplaySettings>.Connect("HostSettings", (GameplaySettings)null, (LNetworkVariableWritePerms)0, (Action<GameplaySettings, GameplaySettings>)delegate
			{
			});
			LNetworkUtils.OnNetworkStart += delegate
			{
				PublishIfHost();
			};
			config.LocalGameplayChanged += PublishIfHost;
		}

		private static void PublishIfHost()
		{
			if (LNetworkUtils.IsConnected && LNetworkUtils.IsHostOrServer)
			{
				_synced.Value = _config.LocalGameplay;
			}
		}

		[Conditional("DEBUG")]
		private static void LogIfChanged(GameplaySettings current)
		{
			if (current != _lastCurrent)
			{
				_lastCurrent = current;
				string text = ((current == _config.LocalGameplay) ? "this machine's own" : "the host's");
			}
		}

		private static string Describe(GameplaySettings? settings)
		{
			if (settings != null)
			{
				return $"Blink length {settings.BlinkLength}, calm interval {settings.CalmIntervalMin}–{settings.CalmIntervalMax}, " + $"max-Insanity interval {settings.MaxInsanityIntervalMin}–{settings.MaxInsanityIntervalMax}, " + $"Eyes Shut drain {settings.EyesShutInsanityDrain}, Coil Head multiplier {settings.CoilHeadBlinkSpeedMultiplier}, " + $"Reflex Blinks at terminal {settings.ReflexBlinksAtTerminal}";
			}
			return "none";
		}
	}
}
namespace QuantumLocked.Eyes
{
	public enum BlinkKind
	{
		None,
		Reflex,
		Manual,
		EyesShut
	}
	public sealed class BlinkSettings
	{
		public float BlinkLength { get; set; } = 0.3f;

		public float CalmIntervalMin { get; set; } = 8f;

		public float CalmIntervalMax { get; set; } = 14f;

		public float MaxInsanityIntervalMin { get; set; } = 2f;

		public float MaxInsanityIntervalMax { get; set; } = 4f;
	}
	public struct EyesInput
	{
		public float DeltaTime;

		public float Insanity;

		public float MaxInsanity;

		public bool ReflexBlinksAllowed;

		public bool Cosmetic;

		public BlinkSettings Settings;

		public float FadeProportion;
	}
	public sealed class EyesModel
	{
		private readonly Func<double> _random;

		private EyesInput _input = new EyesInput
		{
			Settings = new BlinkSettings()
		};

		private double _now;

		private double _closedUntil;

		private double _openedAt;

		private bool _keyHeld;

		private bool _wasEligible;

		private bool _pendingPress;

		private double _pendingPressAt;

		private const float RefocusTime = 0.15f;

		private bool _wasOpeningOrOpen;

		private float _refocusFrom;

		private double _refocusStart;

		private double _refocusEnd;

		public bool IsClosed => Kind != BlinkKind.None;

		public BlinkKind Kind { get; private set; }

		public bool IsCosmetic
		{
			get
			{
				if (IsClosed)
				{
					return _input.Cosmetic;
				}
				return false;
			}
		}

		public BlinkKind SharedKind
		{
			get
			{
				if (!IsCosmetic)
				{
					return Kind;
				}
				return BlinkKind.None;
			}
		}

		public float BlinkTimer { get; private set; }

		public float Closure { get; private set; }

		public float Blur { get; private set; }

		private float FadeTime => Math.Min(0.5f, Math.Max(0f, _input.FadeProportion)) * _input.Settings.BlinkLength;

		public EyesModel(Func<double> random)
		{
			_random = random;
		}

		public void PressKey()
		{
			if (IsClosed)
			{
				_pendingPress = true;
				_pendingPressAt = _now;
			}
			else
			{
				StartManualBlink();
			}
		}

		public void ReleaseKey()
		{
			if (_pendingPress)
			{
				_pendingPress = false;
			}
			else
			{
				if (!_keyHeld)
				{
					return;
				}
				_keyHeld = false;
				if (IsClosed)
				{
					_closedUntil = Math.Max(_closedUntil, _now + (double)(Closure * FadeTime));
					if (_now >= _closedUntil)
					{
						Open(_now);
					}
				}
			}
		}

		public void Reset()
		{
			_keyHeld = false;
			_pendingPress = false;
			Kind = BlinkKind.None;
			Closure = 0f;
			Blur = 0f;
			_refocusFrom = 0f;
			_openedAt = _now;
		}

		public void Update(EyesInput input)
		{
			_input = input;
			float num = Math.Max(0f, input.DeltaTime);
			_now += num;
			if (_pendingPress && IsClosed && !_keyHeld)
			{
				double num2 = _closedUntil - (double)FadeTime;
				if (_pendingPressAt <= num2 && _now >= num2)
				{
					_pendingPress = false;
					_keyHeld = true;
				}
			}
			if (IsClosed && !_keyHeld && _now >= _closedUntil)
			{
				Open(_closedUntil);
			}
			if (!IsClosed && _pendingPress)
			{
				_pendingPress = false;
				StartManualBlink();
			}
			if (!IsClosed && input.ReflexBlinksAllowed && _wasEligible)
			{
				BlinkTimer -= (float)Math.Min(num, _now - _openedAt);
				if (BlinkTimer <= 0f)
				{
					_closedUntil = _now + (double)BlinkTimer + (double)input.Settings.BlinkLength;
					Kind = BlinkKind.Reflex;
				}
			}
			if (input.ReflexBlinksAllowed && !_wasEligible)
			{
				RestartBlinkTimer();
			}
			_wasEligible = input.ReflexBlinksAllowed;
			if (_keyHeld && IsClosed && _now >= _closedUntil)
			{
				Kind = BlinkKind.EyesShut;
			}
			float closure = Closure;
			UpdateClosure(num);
			UpdateBlur(closure);
		}

		private void StartManualBlink()
		{
			_keyHeld = true;
			_closedUntil = _now + (double)_input.Settings.BlinkLength;
			Kind = BlinkKind.Manual;
		}

		private void UpdateBlur(float previousClosure)
		{
			bool flag = Closure < previousClosure || (!IsClosed && Closure <= 0f);
			if (flag && !_wasOpeningOrOpen)
			{
				_refocusFrom = Blur;
				_refocusStart = _now;
				_refocusEnd = _now + (double)(Closure * FadeTime) + 0.15000000596046448;
			}
			_wasOpeningOrOpen = flag;
			float val = 0f;
			if (_refocusEnd > _refocusStart)
			{
				float num = Clamp01((float)((_refocusEnd - _now) / (_refocusEnd - _refocusStart)));
				val = _refocusFrom * num * num;
			}
			Blur = Math.Max(val, Closure);
		}

		private void UpdateClosure(float deltaTime)
		{
			float fadeTime = FadeTime;
			if (fadeTime <= 0f)
			{
				Closure = (IsClosed ? 1f : 0f);
				return;
			}
			float num = deltaTime / fadeTime;
			float val = 0f;
			if (IsClosed)
			{
				val = Closure + num;
				if (!_keyHeld)
				{
					val = Math.Min(val, (float)((_closedUntil - _now) / (double)fadeTime));
				}
			}
			Closure = Clamp01(Math.Max(val, Closure - num));
		}

		private void Open(double at)
		{
			Kind = BlinkKind.None;
			_openedAt = at;
			RestartBlinkTimer();
		}

		private void RestartBlinkTimer()
		{
			BlinkSettings settings = _input.Settings;
			float t = ((_input.MaxInsanity > 0f) ? Clamp01(_input.Insanity / _input.MaxInsanity) : 0f);
			float num = Lerp(settings.CalmIntervalMin, settings.MaxInsanityIntervalMin, t);
			float to = Lerp(settings.CalmIntervalMax, settings.MaxInsanityIntervalMax, t);
			BlinkTimer = Lerp(num, to, (float)_random());
		}

		private static float Lerp(float from, float to, float t)
		{
			return from + (to - from) * t;
		}

		private static float Clamp01(float value)
		{
			return Math.Min(1f, Math.Max(0f, value));
		}
	}
	public static class InsanityDrain
	{
		public static float Apply(BlinkKind kind, float insanity, float drainPerSecond, float deltaTime)
		{
			if (kind != BlinkKind.EyesShut)
			{
				return insanity;
			}
			return Math.Max(0f, insanity - drainPerSecond * deltaTime);
		}
	}
}
namespace QuantumLocked.Enemies
{
	public enum CoilHeadMovement
	{
		Frozen,
		BlinkMoving,
		VanillaMoving
	}
	public readonly struct PlayerSight
	{
		public bool HasLineOfSight { get; }

		public bool IsBlinking { get; }

		public PlayerSight(bool hasLineOfSight, bool isBlinking)
		{
			HasLineOfSight = hasLineOfSight;
			IsBlinking = isBlinking;
		}
	}
	public static class CoilHeadRule
	{
		public static CoilHeadMovement Decide(IReadOnlyList<PlayerSight> players)
		{
			bool flag = false;
			for (int i = 0; i < players.Count; i++)
			{
				PlayerSight playerSight = players[i];
				if (playerSight.HasLineOfSight)
				{
					if (!playerSight.IsBlinking)
					{
						return CoilHeadMovement.Frozen;
					}
					flag = true;
				}
			}
			if (!flag)
			{
				return CoilHeadMovement.VanillaMoving;
			}
			return CoilHeadMovement.BlinkMoving;
		}
	}
}