Decompiled source of LethalCCTV v1.0.1

LethalCCTV.dll

Decompiled an hour ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using CSync.Extensions;
using CSync.Lib;
using DunGen;
using DunGen.Graph;
using DunGen.Tags;
using GameNetcodeStuff;
using HarmonyLib;
using LethalCCTV.NetcodePatcher;
using LethalCompanyInputUtils.Api;
using LethalLib.Modules;
using LethalNetworkAPI;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using Unity.Netcode.Components;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Networking;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine.Video;
using Y4NGZCompany.Bootstrap;
using Y4NGZCompany.Core;
using Y4NGZCompany.Core.Compat;
using Y4NGZCompany.Facility.Cameras;
using Y4NGZCompany.Facility.Cameras.Placement;
using Y4NGZCompany.Facility.Cameras.Probe;
using Y4NGZCompany.Facility.Interior;
using Y4NGZCompany.Facility.Interior.Placement;
using Y4NGZCompany.Facility.Interior.Placement.Authored;
using Y4NGZCompany.Facility.Mainframe;
using Y4NGZCompany.Facility.Security;
using Y4NGZCompany.Facility.Shared;
using Y4NGZCompany.Facility.Stash;
using Y4NGZCompany.ShipSystems.Rendering;
using Y4NGZCompany.ShipSystems.Surveillance;
using Y4NGZCompany.ShipSystems.Surveillance.OutlineEffect;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("ClientNetworkTransform")]
[assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")]
[assembly: IgnoresAccessChecksTo("DissonanceVoip")]
[assembly: IgnoresAccessChecksTo("DunGen")]
[assembly: IgnoresAccessChecksTo("DunGen.Integration.ASPP")]
[assembly: IgnoresAccessChecksTo("DunGen.Integration.UnityNav")]
[assembly: IgnoresAccessChecksTo("EasyTextEffects")]
[assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")]
[assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")]
[assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")]
[assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")]
[assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")]
[assembly: IgnoresAccessChecksTo("Unity.Burst")]
[assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")]
[assembly: IgnoresAccessChecksTo("Unity.Collections")]
[assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")]
[assembly: IgnoresAccessChecksTo("Unity.Jobs")]
[assembly: IgnoresAccessChecksTo("Unity.Mathematics")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")]
[assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")]
[assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")]
[assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")]
[assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")]
[assembly: IgnoresAccessChecksTo("Unity.Services.QoS")]
[assembly: IgnoresAccessChecksTo("Unity.Services.Relay")]
[assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")]
[assembly: IgnoresAccessChecksTo("Unity.Timeline")]
[assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.XR.CoreUtils")]
[assembly: IgnoresAccessChecksTo("Unity.XR.Management")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.ConformanceAutomation")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MetaQuestSupport")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MockRuntime")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.OculusQuestSupport")]
[assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.RuntimeDebugger")]
[assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.SpatialTracking")]
[assembly: IgnoresAccessChecksTo("UnityEngine.UI")]
[assembly: IgnoresAccessChecksTo("UnityEngine.XR.LegacyInputHelpers")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LethalCCTV")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Y4NGZ surveillance and CCTV systems")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+6ae9e9f17528b47cbc796b51a05c6f7380dda81b")]
[assembly: AssemblyProduct("LethalCCTV")]
[assembly: AssemblyTitle("LethalCCTV")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
[module: NetcodePatchedAssembly]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 LethalCCTV
{
	[BepInPlugin("com.y4ngz.company.lethalcctv", "LethalCCTV", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class LethalCCTVPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.y4ngz.company.lethalcctv";

		public const string PluginName = "LethalCCTV";

		public const string PluginVersion = "1.0.1";

		private SurveillanceBootstrap _host;

		internal const string HostObjectName = "LethalCCTV_Host";

		public static LethalCCTVPlugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_003c: 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_004a: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			DependencyVersionAudit.RunForLethalCCTV(((BaseUnityPlugin)this).Logger);
			NetcodeRuntimeBootstrap.Initialize(((BaseUnityPlugin)this).Logger, typeof(LethalCCTVPlugin).Assembly, true);
			GameObject val = new GameObject("LethalCCTV_Host")
			{
				hideFlags = (HideFlags)61
			};
			Object.DontDestroyOnLoad((Object)(object)val);
			_host = val.AddComponent<SurveillanceBootstrap>();
			_host.Initialize(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger);
			Log.LogInfo((object)"LethalCCTV 1.0.1 loaded.");
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "LethalCCTV";

		public const string PLUGIN_NAME = "LethalCCTV";

		public const string PLUGIN_VERSION = "1.0.1";
	}
}
namespace Y4NGZCompany.ShipSystems.Surveillance
{
	[HarmonyPatch(typeof(HUDManager), "PingScan_performed")]
	internal static class CameraOperatorPingScanPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(ref CallbackContext context)
		{
			if (!MonitorFocus.IsFocused)
			{
				return true;
			}
			if (Y4NGZPlayerAnimationBridge.IsFirstPersonHandEditModeActive)
			{
				return false;
			}
			if (((CallbackContext)(ref context)).performed)
			{
				MonitorFocus.TriggerCameraContextOrScan();
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(HUDManager), "UpdateScanNodes")]
	internal static class CameraOperatorScanNodesPatch
	{
		[HarmonyPrefix]
		private static bool Prefix()
		{
			return !MonitorFocus.IsFocused;
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "ActivateItem_performed")]
	internal static class CameraOperatorActivateItemPatch
	{
		[HarmonyPrefix]
		private static bool Prefix()
		{
			return !MonitorFocus.IsFocused;
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "ActivateItem_canceled")]
	internal static class CameraOperatorActivateItemCancelPatch
	{
		[HarmonyPrefix]
		private static bool Prefix()
		{
			return !MonitorFocus.IsFocused;
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "Jump_performed")]
	internal static class CameraOperatorJumpPerformedPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(PlayerControllerB __instance)
		{
			return !MonitorFocus.ShouldSuppressPlayerInput(__instance);
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "Update")]
	internal static class CameraOperatorPlayerUpdatePatch
	{
		[HarmonyPrefix]
		private static void Prefix(PlayerControllerB __instance)
		{
			if (MonitorFocus.ShouldSuppressPlayerInput(__instance))
			{
				MonitorFocus.ApplyFocusInputLock(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "PlayerLookInput")]
	internal static class CameraOperatorPlayerLookInputPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(PlayerControllerB __instance)
		{
			return !MonitorFocus.ShouldSuppressPlayerInput(__instance);
		}
	}
	internal static class CCTVAccessButton
	{
		private const string RootName = "LethalCCTV_AccessButton";

		private const string TemplateChildName = "ButtonContainer/ButtonAnimContainer";

		private const string RedButtonChildName = "RedButton";

		private const string HoverTip = "View cameras : [E]";

		private const string PressTriggerName = "press";

		private const string GlassOpenBoolName = "GlassOpen";

		private const float StateRefreshIntervalSeconds = 0.2f;

		private const float PressSfxVolume = 0.9f;

		private const string ScanNodeName = "LethalCCTV_AccessButtonScanNode";

		private const string ScanNodeHeader = "CCTV Station";

		private const string ScanNodeSubText = "View cameras";

		private const int ScanNodeMaxRange = 8;

		private const int ScanNodeFallbackLayer = 22;

		private static GameObject _root;

		private static Animator _buttonAnimator;

		private static InteractTrigger _trigger;

		private static AudioSource _audio;

		private static AudioClip _pressSfx;

		private static UnityAction<PlayerControllerB> _listener;

		private static float _nextStateRefreshAt;

		private static bool _templateMissingLogged;

		private static bool _spawnLogged;

		internal static bool IsAvailable
		{
			get
			{
				if ((Object)(object)_root != (Object)null)
				{
					return (Object)(object)_trigger != (Object)null;
				}
				return false;
			}
		}

		internal static Transform Root
		{
			get
			{
				if (!((Object)(object)_root != (Object)null))
				{
					return null;
				}
				return _root.transform;
			}
		}

		internal static Transform PressContactTransform
		{
			get
			{
				if (!((Object)(object)_trigger != (Object)null))
				{
					return null;
				}
				return ((Component)_trigger).transform;
			}
		}

		internal static bool TryGetPressSurface(out Vector3 point, out Vector3 normal)
		{
			//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_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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//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_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: 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_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_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: 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_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			point = Vector3.zero;
			normal = Vector3.up;
			Transform pressContactTransform = PressContactTransform;
			if ((Object)(object)pressContactTransform == (Object)null)
			{
				return false;
			}
			Vector3 bestAxis = pressContactTransform.up;
			float bestScore = Mathf.Abs(Vector3.Dot(bestAxis, Vector3.up));
			ConsiderAxis(pressContactTransform.right);
			ConsiderAxis(pressContactTransform.forward);
			if (Vector3.Dot(bestAxis, Vector3.up) < 0f)
			{
				bestAxis = -bestAxis;
			}
			normal = ((((Vector3)(ref bestAxis)).sqrMagnitude > 0.0001f) ? ((Vector3)(ref bestAxis)).normalized : Vector3.up);
			Renderer val = ((Component)pressContactTransform).GetComponent<Renderer>();
			if ((Object)(object)val == (Object)null)
			{
				val = ((Component)pressContactTransform).GetComponentInChildren<Renderer>(true);
			}
			if ((Object)(object)val == (Object)null)
			{
				point = pressContactTransform.position + normal * 0.015f;
				return true;
			}
			Bounds bounds = val.bounds;
			Vector3 extents = ((Bounds)(ref bounds)).extents;
			float num = Mathf.Abs(normal.x) * extents.x + Mathf.Abs(normal.y) * extents.y + Mathf.Abs(normal.z) * extents.z;
			point = ((Bounds)(ref bounds)).center + normal * num;
			return true;
			void ConsiderAxis(Vector3 candidate)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0001: 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_001d: Unknown result type (might be due to invalid IL or missing references)
				float num2 = Mathf.Abs(Vector3.Dot(candidate, Vector3.up));
				if (!(num2 <= bestScore))
				{
					bestAxis = candidate;
					bestScore = num2;
				}
			}
		}

		internal static void Ensure(Transform stationRoot)
		{
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)stationRoot == (Object)null || (Object)(object)_root != (Object)null)
			{
				return;
			}
			AudioClip pressSfx;
			Transform val = ResolveTemplateButtonContainer(out pressSfx);
			if ((Object)(object)val == (Object)null)
			{
				if (!_templateMissingLogged)
				{
					_templateMissingLogged = true;
					ManualLogSource log = SurveillanceBootstrap.Log;
					if (log != null)
					{
						log.LogWarning((object)"[LethalCCTV] Access button template unavailable (teleporter unlockable prefab or ButtonContainer not found); red bezel button remains the CCTV entry.");
					}
				}
				return;
			}
			GameObject val2;
			try
			{
				val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, stationRoot, false);
			}
			catch (Exception ex)
			{
				if (!_templateMissingLogged)
				{
					_templateMissingLogged = true;
					ManualLogSource log2 = SurveillanceBootstrap.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)("[LethalCCTV] Access button clone failed: " + ex.Message));
					}
				}
				return;
			}
			((Object)val2).name = "LethalCCTV_AccessButton";
			_root = val2;
			_pressSfx = pressSfx;
			StripClonedBehaviours(val2);
			_buttonAnimator = val2.GetComponentInChildren<Animator>(true);
			_audio = val2.AddComponent<AudioSource>();
			_audio.playOnAwake = false;
			_audio.spatialBlend = 1f;
			_audio.minDistance = 0.8f;
			_audio.maxDistance = 12f;
			WireInteractTrigger(val2);
			CCTVOperatorStation.ApplyAccessButtonPlacement(val2.transform);
			BuildScanNode(val2);
			if ((Object)(object)_buttonAnimator != (Object)null)
			{
				_buttonAnimator.SetBool("GlassOpen", true);
			}
			if (_spawnLogged)
			{
				return;
			}
			_spawnLogged = true;
			ManualLogSource log3 = SurveillanceBootstrap.Log;
			if (log3 != null)
			{
				string[] obj = new string[8]
				{
					$"[LethalCCTV] Access button spawned from teleporter template: local={val2.transform.localPosition} ",
					"trigger=",
					((Object)(object)_trigger != (Object)null) ? "wired" : "MISSING",
					" animator=",
					null,
					null,
					null,
					null
				};
				object obj2;
				if (!((Object)(object)_buttonAnimator != (Object)null))
				{
					obj2 = "MISSING";
				}
				else
				{
					RuntimeAnimatorController runtimeAnimatorController = _buttonAnimator.runtimeAnimatorController;
					obj2 = ((runtimeAnimatorController != null) ? ((Object)runtimeAnimatorController).name : null) ?? "<none>";
				}
				obj[4] = (string)obj2;
				obj[5] = " pressSfx=";
				obj[6] = (((Object)(object)_pressSfx != (Object)null) ? ((Object)_pressSfx).name : "<none>");
				obj[7] = ".";
				log3.LogInfo((object)string.Concat(obj));
			}
		}

		internal static void Shutdown()
		{
			if ((Object)(object)_trigger != (Object)null && _listener != null && _trigger.onInteract != null)
			{
				((UnityEvent<PlayerControllerB>)(object)_trigger.onInteract).RemoveListener(_listener);
			}
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
			}
			_root = null;
			_buttonAnimator = null;
			_trigger = null;
			_audio = null;
			_pressSfx = null;
			_listener = null;
			_nextStateRefreshAt = 0f;
			_spawnLogged = false;
		}

		internal static void Tick()
		{
			if ((Object)(object)_trigger == (Object)null || Time.unscaledTime < _nextStateRefreshAt)
			{
				return;
			}
			_nextStateRefreshAt = Time.unscaledTime + 0.2f;
			if ((Object)(object)_buttonAnimator != (Object)null)
			{
				try
				{
					_buttonAnimator.SetBool("GlassOpen", true);
				}
				catch
				{
				}
			}
			if (CCTVMonitorFeedSync.IsEntryAvailable(out var unavailableReason))
			{
				_trigger.interactable = true;
				_trigger.hoverTip = "View cameras : [E]";
			}
			else
			{
				_trigger.interactable = false;
				_trigger.disabledHoverTip = unavailableReason ?? string.Empty;
			}
		}

		internal static void PlayPressAnimation()
		{
			if ((Object)(object)_buttonAnimator != (Object)null)
			{
				try
				{
					_buttonAnimator.SetBool("GlassOpen", true);
					_buttonAnimator.ResetTrigger("press");
					_buttonAnimator.SetTrigger("press");
				}
				catch (Exception ex)
				{
					ManualLogSource log = SurveillanceBootstrap.Log;
					if (log != null)
					{
						log.LogDebug((object)("[LethalCCTV] Access button press animation failed: " + ex.Message));
					}
				}
			}
			if ((Object)(object)_audio != (Object)null && (Object)(object)_pressSfx != (Object)null)
			{
				_audio.PlayOneShot(_pressSfx, 0.9f);
			}
		}

		private static void BuildScanNode(GameObject clone)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			Transform val = (((Object)(object)_trigger != (Object)null) ? ((Component)_trigger).transform : clone.transform);
			GameObject val2 = new GameObject("LethalCCTV_AccessButtonScanNode");
			val2.transform.SetParent(clone.transform, false);
			val2.transform.position = val.position;
			int num = LayerMask.NameToLayer("ScanNode");
			val2.layer = ((num >= 0) ? num : 22);
			try
			{
				val2.tag = "DoNotSet";
			}
			catch (UnityException)
			{
			}
			SphereCollider obj = val2.AddComponent<SphereCollider>();
			((Collider)obj).isTrigger = true;
			obj.radius = 0.3f;
			ScanNodeProperties obj2 = val2.AddComponent<ScanNodeProperties>();
			obj2.headerText = "CCTV Station";
			obj2.subText = "View cameras";
			obj2.minRange = 0;
			obj2.maxRange = 8;
			obj2.requiresLineOfSight = false;
			obj2.nodeType = 0;
			obj2.scrapValue = 0;
			obj2.creatureScanID = -1;
		}

		private static void OnPressed(PlayerControllerB player)
		{
			CCTVVanillaMonitorButtons.RequestEntry(player);
		}

		private static Transform ResolveTemplateButtonContainer(out AudioClip pressSfx)
		{
			pressSfx = null;
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.unlockablesList == (Object)null || instance.unlockablesList.unlockables == null)
			{
				return null;
			}
			for (int i = 0; i < instance.unlockablesList.unlockables.Count; i++)
			{
				GameObject val = instance.unlockablesList.unlockables[i]?.prefabObject;
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				ShipTeleporter component = val.GetComponent<ShipTeleporter>();
				if (!((Object)(object)component == (Object)null) && !component.isInverseTeleporter)
				{
					Transform val2 = val.transform.Find("ButtonContainer/ButtonAnimContainer");
					if (!((Object)(object)val2 == (Object)null))
					{
						pressSfx = component.buttonPressSFX;
						return val2;
					}
				}
			}
			return null;
		}

		private static void StripClonedBehaviours(GameObject clone)
		{
			MonoBehaviour[] componentsInChildren = clone.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && (!(val is InteractTrigger) || !string.Equals(((Object)((Component)val).gameObject).name, "RedButton", StringComparison.Ordinal)))
				{
					Object.Destroy((Object)(object)val);
				}
			}
			Transform val2 = FindChildRecursive(clone.transform, "ButtonGlass");
			if ((Object)(object)val2 != (Object)null)
			{
				Collider[] components = ((Component)val2).GetComponents<Collider>();
				for (int j = 0; j < components.Length; j++)
				{
					Object.Destroy((Object)(object)components[j]);
				}
			}
		}

		private static void WireInteractTrigger(GameObject clone)
		{
			Transform val = FindChildRecursive(clone.transform, "RedButton");
			_trigger = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<InteractTrigger>() : null);
			if ((Object)(object)_trigger == (Object)null)
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogWarning((object)"[LethalCCTV] Access button clone has no RedButton InteractTrigger; entry stays on the bezel button.");
				}
				return;
			}
			if (_trigger.onInteract != null)
			{
				int persistentEventCount = ((UnityEventBase)_trigger.onInteract).GetPersistentEventCount();
				for (int i = 0; i < persistentEventCount; i++)
				{
					((UnityEventBase)_trigger.onInteract).SetPersistentListenerState(i, (UnityEventCallState)0);
				}
			}
			_listener = OnPressed;
			((UnityEvent<PlayerControllerB>)(object)_trigger.onInteract).AddListener(_listener);
			_trigger.hoverTip = "View cameras : [E]";
			_trigger.interactable = true;
		}

		private static Transform FindChildRecursive(Transform root, string childName)
		{
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			if (string.Equals(((Object)root).name, childName, StringComparison.Ordinal))
			{
				return root;
			}
			for (int i = 0; i < root.childCount; i++)
			{
				Transform val = FindChildRecursive(root.GetChild(i), childName);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}
	}
	internal static class CctvCommandTargetBridge
	{
		internal sealed class TargetInfo
		{
			internal Component Component;

			internal string DisplayName;

			internal string CommandHint;

			internal Vector3 Position;

			internal float Radius;
		}

		private const string ApiTypeName = "Y4NGZCompany.Facility.Security.CctvCommandTargetApi, LethalCCTV";

		private static Type _apiType;

		private static bool _resolved;

		private static MethodInfo _describeTargetMethod;

		private static MethodInfo _getActiveTargetsMethod;

		private static MethodInfo _executeCommandMethod;

		internal static bool TryDescribeTarget(Component component, out TargetInfo info)
		{
			info = null;
			Resolve();
			if (_describeTargetMethod == null || (Object)(object)component == (Object)null)
			{
				return false;
			}
			try
			{
				object descriptor = _describeTargetMethod.Invoke(null, new object[1] { component });
				info = ReadDescriptor(descriptor);
				return info != null;
			}
			catch
			{
				return false;
			}
		}

		internal static List<TargetInfo> GetActiveTargets()
		{
			Resolve();
			List<TargetInfo> list = new List<TargetInfo>();
			if (_getActiveTargetsMethod == null)
			{
				return list;
			}
			try
			{
				if (!(_getActiveTargetsMethod.Invoke(null, Array.Empty<object>()) is IEnumerable enumerable))
				{
					return list;
				}
				foreach (object item in enumerable)
				{
					TargetInfo targetInfo = ReadDescriptor(item);
					if (targetInfo != null)
					{
						list.Add(targetInfo);
					}
				}
			}
			catch
			{
			}
			return list;
		}

		internal static bool TryExecuteCommand(Component component, string command, out string statusText)
		{
			statusText = "UNSUPPORTED TARGET";
			Resolve();
			if (_executeCommandMethod == null || (Object)(object)component == (Object)null)
			{
				return false;
			}
			try
			{
				object obj = _executeCommandMethod.Invoke(null, new object[2]
				{
					component,
					command ?? string.Empty
				});
				if (obj == null)
				{
					statusText = "NO RESPONSE";
					return false;
				}
				statusText = ReadString(obj, "StatusText", "COMMAND FAILED");
				return ReadBool(obj, "Success", fallback: false);
			}
			catch
			{
				statusText = "COMMAND FAILED";
				return false;
			}
		}

		internal static bool TryFindMainframeTarget(out Component target, out string displayName, out string commandHint)
		{
			target = null;
			displayName = "MAINFRAME";
			commandHint = "hack";
			Resolve();
			if (_apiType == null)
			{
				return false;
			}
			try
			{
				object obj = null;
				PropertyInfo propertyInfo = _apiType.GetProperty("MainframeSupport", BindingFlags.Static | BindingFlags.Public) ?? _apiType.GetProperty("Mainframe", BindingFlags.Static | BindingFlags.Public);
				if (propertyInfo != null)
				{
					obj = propertyInfo.GetValue(null);
				}
				if (obj == null)
				{
					PropertyInfo propertyInfo2 = Type.GetType("Y4NGZCompany.Facility.Mainframe.MainframeSupport, LethalCCTV", throwOnError: false)?.GetProperty("Active", BindingFlags.Static | BindingFlags.Public);
					if (propertyInfo2 != null)
					{
						obj = propertyInfo2.GetValue(null);
					}
				}
				if (obj == null)
				{
					return false;
				}
				target = (Component)((obj is Component) ? obj : null);
				if ((Object)(object)target == (Object)null)
				{
					return false;
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static void Resolve()
		{
			if (!_resolved || !(_apiType != null))
			{
				_resolved = true;
				_apiType = Type.GetType("Y4NGZCompany.Facility.Security.CctvCommandTargetApi, LethalCCTV", throwOnError: false);
				_describeTargetMethod = _apiType?.GetMethod("DescribeTarget", BindingFlags.Static | BindingFlags.Public);
				_getActiveTargetsMethod = _apiType?.GetMethod("GetActiveTargets", BindingFlags.Static | BindingFlags.Public);
				_executeCommandMethod = _apiType?.GetMethod("ExecuteCommand", BindingFlags.Static | BindingFlags.Public);
			}
		}

		private static TargetInfo ReadDescriptor(object descriptor)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if (descriptor == null)
			{
				return null;
			}
			Component val = ReadComponent(descriptor, "Target");
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			return new TargetInfo
			{
				Component = val,
				DisplayName = ReadString(descriptor, "DisplayName", "SUPPORT NODE"),
				CommandHint = ReadString(descriptor, "CommandHint", string.Empty),
				Position = ReadVector3(descriptor, "Position", val.transform.position),
				Radius = Mathf.Clamp(ReadFloat(descriptor, "Radius", 0.9f), 0.35f, 4f)
			};
		}

		private static Component ReadComponent(object target, string memberName)
		{
			object obj = ReadMember(target, memberName);
			return (Component)((obj is Component) ? obj : null);
		}

		private static string ReadString(object target, string memberName, string fallback)
		{
			return (ReadMember(target, memberName) as string) ?? fallback;
		}

		private static bool ReadBool(object target, string memberName, bool fallback)
		{
			object obj = ReadMember(target, memberName);
			if (obj is bool)
			{
				return (bool)obj;
			}
			return fallback;
		}

		private static float ReadFloat(object target, string memberName, float fallback)
		{
			object obj = ReadMember(target, memberName);
			if (obj is float)
			{
				return (float)obj;
			}
			if (obj is double num)
			{
				return (float)num;
			}
			return fallback;
		}

		private static Vector3 ReadVector3(object target, string memberName, Vector3 fallback)
		{
			//IL_0019: 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_0016: 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)
			object obj = ReadMember(target, memberName);
			if (obj is Vector3)
			{
				return (Vector3)obj;
			}
			return fallback;
		}

		private static object ReadMember(object target, string memberName)
		{
			if (target == null)
			{
				return null;
			}
			Type type = target.GetType();
			PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public);
			if (property != null)
			{
				return property.GetValue(target);
			}
			return type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public)?.GetValue(target);
		}
	}
	internal static class CCTVFocusControlsOverlay
	{
		private static GameObject _root;

		private static Canvas _canvas;

		private static Image _background;

		private static RectTransform _panelRect;

		private static RectTransform _textRect;

		private static TextMeshProUGUI _text;

		private static string _lastText;

		private const float PanelPaddingX = 18f;

		private const float PanelPaddingY = 12f;

		private const float PanelMinWidth = 260f;

		private const float PanelMaxWidth = 620f;

		private const float PanelMinHeight = 56f;

		private const string PlayerControlsText = "CCTV CONTROLS\nMOUSE   AIM CAMERA\nARROWS  CAMERA LIST\nSPACE   RADAR VIEW\nSPC+WHL RADAR ZOOM\nLMB     PING\nRMB     TARGET\nV HOLD  WALKIE\nE/ESC   EXIT";

		private const string DebugControlsText = "\nH       HIDE PANEL\nF1      EDIT MENU\nF2      CAMERA EDIT";

		internal static Transform RootTransform
		{
			get
			{
				if (!((Object)(object)_root != (Object)null))
				{
					return null;
				}
				return _root.transform;
			}
		}

		private static bool ConfiguredOn
		{
			get
			{
				if (SurveillanceBootstrap.Config?.ShowOperatorControlsOverlay != null)
				{
					return SurveillanceBootstrap.Config.ShowOperatorControlsOverlay.Value;
				}
				return false;
			}
		}

		private static bool OverlayEnabled
		{
			get
			{
				if (!ConfiguredOn)
				{
					return CCTVStickyNoteItem.RegistrationFailed;
				}
				return true;
			}
		}

		internal static void Tick()
		{
			if (!OverlayEnabled)
			{
				Shutdown();
				return;
			}
			if (!MonitorFocus.IsFocused)
			{
				SetVisible(visible: false);
				return;
			}
			Ensure();
			SetVisible(visible: true);
			RefreshText();
		}

		internal static void Shutdown()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
			}
			_root = null;
			_canvas = null;
			_background = null;
			_panelRect = null;
			_textRect = null;
			_text = null;
			_lastText = null;
		}

		private static void Ensure()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Expected O, but got Unknown
			//IL_0139: 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_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_root != (Object)null))
			{
				_root = new GameObject("LethalCCTV_FocusControlsOverlay", new Type[4]
				{
					typeof(RectTransform),
					typeof(Canvas),
					typeof(CanvasScaler),
					typeof(GraphicRaycaster)
				});
				Object.DontDestroyOnLoad((Object)(object)_root);
				_canvas = _root.GetComponent<Canvas>();
				_canvas.renderMode = (RenderMode)0;
				_canvas.sortingOrder = 32760;
				CanvasScaler component = _root.GetComponent<CanvasScaler>();
				component.uiScaleMode = (ScaleMode)1;
				component.referenceResolution = new Vector2(1920f, 1080f);
				component.screenMatchMode = (ScreenMatchMode)0;
				component.matchWidthOrHeight = 0.5f;
				((Behaviour)_root.GetComponent<GraphicRaycaster>()).enabled = false;
				GameObject val = new GameObject("ControlsPanel", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(Image)
				});
				val.transform.SetParent(_root.transform, false);
				_panelRect = val.GetComponent<RectTransform>();
				_panelRect.anchorMin = new Vector2(0f, 1f);
				_panelRect.anchorMax = new Vector2(0f, 1f);
				_panelRect.pivot = new Vector2(0f, 1f);
				_panelRect.anchoredPosition = new Vector2(26f, -86f);
				_panelRect.sizeDelta = new Vector2(260f, 56f);
				_background = val.GetComponent<Image>();
				((Graphic)_background).color = new Color(0f, 0f, 0f, 0.56f);
				((Graphic)_background).raycastTarget = false;
				GameObject val2 = new GameObject("ControlsText", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(TextMeshProUGUI)
				});
				val2.transform.SetParent(val.transform, false);
				_textRect = val2.GetComponent<RectTransform>();
				_textRect.anchorMin = Vector2.zero;
				_textRect.anchorMax = Vector2.one;
				_textRect.offsetMin = new Vector2(18f, 12f);
				_textRect.offsetMax = new Vector2(-18f, -12f);
				_text = val2.GetComponent<TextMeshProUGUI>();
				((Graphic)_text).raycastTarget = false;
				((TMP_Text)_text).enableWordWrapping = false;
				((TMP_Text)_text).richText = true;
				((TMP_Text)_text).alignment = (TextAlignmentOptions)257;
				((TMP_Text)_text).fontSize = 24f;
				((TMP_Text)_text).lineSpacing = -7f;
				((Graphic)_text).color = new Color(1f, 0.58f, 0.14f, 0.96f);
				TryAssignHudFont(_text);
			}
		}

		private static void RefreshText()
		{
			if ((Object)(object)_text == (Object)null)
			{
				return;
			}
			if (MonitorFocus.IsOperatorDebugToolsEnabled)
			{
				string cameraPlacementEditOverlayText = MonitorFocus.GetCameraPlacementEditOverlayText();
				if (!string.IsNullOrWhiteSpace(cameraPlacementEditOverlayText))
				{
					SetText(cameraPlacementEditOverlayText);
					return;
				}
				string stationEditMenuOverlayText = MonitorFocus.GetStationEditMenuOverlayText();
				if (!string.IsNullOrWhiteSpace(stationEditMenuOverlayText))
				{
					SetText(stationEditMenuOverlayText);
					return;
				}
				string firstPersonHandEditOverlayText = Y4NGZPlayerAnimationBridge.GetFirstPersonHandEditOverlayText();
				if (!string.IsNullOrWhiteSpace(firstPersonHandEditOverlayText))
				{
					SetText(firstPersonHandEditOverlayText);
				}
				else
				{
					SetText("CCTV CONTROLS\nMOUSE   AIM CAMERA\nARROWS  CAMERA LIST\nSPACE   RADAR VIEW\nSPC+WHL RADAR ZOOM\nLMB     PING\nRMB     TARGET\nV HOLD  WALKIE\nE/ESC   EXIT\nH       HIDE PANEL\nF1      EDIT MENU\nF2      CAMERA EDIT");
				}
			}
			else
			{
				SetText("CCTV CONTROLS\nMOUSE   AIM CAMERA\nARROWS  CAMERA LIST\nSPACE   RADAR VIEW\nSPC+WHL RADAR ZOOM\nLMB     PING\nRMB     TARGET\nV HOLD  WALKIE\nE/ESC   EXIT");
			}
		}

		private static void SetText(string value)
		{
			if (!(value == _lastText))
			{
				_lastText = value;
				((TMP_Text)_text).text = value;
				ResizePanelToText();
			}
		}

		private static void ResizePanelToText()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: 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)
			if (!((Object)(object)_panelRect == (Object)null) && !((Object)(object)_text == (Object)null))
			{
				((TMP_Text)_text).ForceMeshUpdate(true, false);
				Vector2 preferredValues = ((TMP_Text)_text).GetPreferredValues(((TMP_Text)_text).text, 584f, 0f);
				float num = Mathf.Clamp(Mathf.Ceil(preferredValues.x + 36f), 260f, 620f);
				float num2 = Mathf.Max(56f, Mathf.Ceil(preferredValues.y + 24f));
				_panelRect.sizeDelta = new Vector2(num, num2);
				if ((Object)(object)_textRect != (Object)null)
				{
					_textRect.offsetMin = new Vector2(18f, 12f);
					_textRect.offsetMax = new Vector2(-18f, -12f);
				}
			}
		}

		private static void SetVisible(bool visible)
		{
			if ((Object)(object)_root != (Object)null && _root.activeSelf != visible)
			{
				_root.SetActive(visible);
			}
		}

		private static void TryAssignHudFont(TextMeshProUGUI target)
		{
			if ((Object)(object)target == (Object)null)
			{
				return;
			}
			try
			{
				HUDManager instance = HUDManager.Instance;
				TextMeshProUGUI[] array = (((Object)(object)instance != (Object)null) ? instance.controlTipLines : null);
				if (array != null && array.Length != 0 && (Object)(object)array[0] != (Object)null && (Object)(object)((TMP_Text)array[0]).font != (Object)null)
				{
					((TMP_Text)target).font = ((TMP_Text)array[0]).font;
				}
			}
			catch
			{
			}
		}
	}
	[DefaultExecutionOrder(32000)]
	internal sealed class CCTVLocalCameraPositionStabilizer : MonoBehaviour
	{
		private PlayerControllerB _player;

		private Transform _playerRoot;

		private Transform _cameraTransform;

		private Vector3 _playerLocalPosition;

		private int _releaseLateUpdates = -1;

		internal void Initialize(PlayerControllerB player, Transform playerRoot, Transform cameraTransform, Vector3 playerLocalPosition)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			_player = player;
			_playerRoot = playerRoot;
			_cameraTransform = cameraTransform;
			_playerLocalPosition = playerLocalPosition;
			_releaseLateUpdates = -1;
			((Behaviour)this).enabled = true;
		}

		internal void ReleaseAfterLateUpdates(int lateUpdates)
		{
			_releaseLateUpdates = Mathf.Max(1, lateUpdates);
			((Behaviour)this).enabled = true;
		}

		internal void ApplyNow()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_playerRoot == (Object)null) && !((Object)(object)_cameraTransform == (Object)null))
			{
				_cameraTransform.position = _playerRoot.TransformPoint(_playerLocalPosition);
			}
		}

		private void LateUpdate()
		{
			if (_releaseLateUpdates < 0)
			{
				return;
			}
			ApplyNow();
			if (!((Object)(object)_player != (Object)null) || !Y4NGZPlayerAnimationBridge.IsLocalSessionActive(_player))
			{
				_releaseLateUpdates--;
				if (_releaseLateUpdates <= 0)
				{
					((Behaviour)this).enabled = false;
					Object.Destroy((Object)(object)this);
				}
			}
		}
	}
	[Serializable]
	public sealed class CCTVMarkerMessage
	{
		public int Id;

		public int Kind;

		public Vector3 Position;

		public float Radius;

		public int ColorKind;

		public string Label;

		public float Lifetime;

		public int CameraIndex;
	}
	internal static class CCTVMarkerManager
	{
		private sealed class MarkerVisual
		{
			public int Id;

			public int Kind;

			public Vector3 Position;

			public float Radius;

			public float ExpiresAt;

			public Color Color;

			public string Label;

			public RectTransform Root;

			public Image Top;

			public Image Bottom;

			public Image Left;

			public Image Right;

			public Image Dot;

			public TextMeshProUGUI Text;
		}

		private const float DEFAULT_SCAN_LIFETIME = 8f;

		private const float DEFAULT_PING_LIFETIME = 10f;

		private const float MAX_SCREEN_SIZE = 128f;

		private const float MIN_SCREEN_SIZE = 26f;

		private static readonly Color MARKER_GREEN = new Color(0.1f, 1f, 0.35f, 1f);

		private static readonly Color MARKER_RED = new Color(1f, 0.12f, 0.12f, 1f);

		private static GameObject _root;

		private static Canvas _canvas;

		private static LNetworkMessage<CCTVMarkerMessage> _message;

		private static readonly List<MarkerVisual> _markers = new List<MarkerVisual>(32);

		private static readonly HashSet<int> _seenIds = new HashSet<int>();

		private static int _nextMarkerId;

		internal static bool HasActiveMarkers => _markers.Count > 0;

		internal static void Initialize()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_root != (Object)null))
			{
				_root = new GameObject("LethalCCTV_WorldMarkers");
				if ((Object)(object)SurveillanceBootstrap.Instance != (Object)null)
				{
					_root.transform.SetParent(((Component)SurveillanceBootstrap.Instance).transform, false);
				}
				else
				{
					Object.DontDestroyOnLoad((Object)(object)_root);
				}
				_canvas = _root.AddComponent<Canvas>();
				_canvas.renderMode = (RenderMode)0;
				_canvas.sortingOrder = 135;
				CanvasScaler obj = _root.AddComponent<CanvasScaler>();
				obj.uiScaleMode = (ScaleMode)1;
				obj.referenceResolution = new Vector2(1920f, 1080f);
				obj.matchWidthOrHeight = 0.5f;
				_message = LNetworkMessage<CCTVMarkerMessage>.Connect("cctv_marker_v1", (Action<CCTVMarkerMessage, ulong>)OnServerReceived, (Action<CCTVMarkerMessage>)OnClientReceived, (Action<CCTVMarkerMessage, ulong>)null);
			}
		}

		internal static void Shutdown()
		{
			ClearAll();
			_message?.ClearSubscriptions();
			_message = null;
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
				_root = null;
				_canvas = null;
			}
		}

		internal static void ClearAll()
		{
			for (int i = 0; i < _markers.Count; i++)
			{
				if ((Object)(object)_markers[i]?.Root != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)_markers[i].Root).gameObject);
				}
			}
			_markers.Clear();
			_seenIds.Clear();
		}

		internal static void PublishPing(Vector3 position, int cameraIndex)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Publish(new CCTVMarkerMessage
			{
				Id = NextId(),
				Kind = 1,
				Position = position,
				Radius = 0.55f,
				ColorKind = 0,
				Label = "CCTV PING",
				Lifetime = 10f,
				CameraIndex = cameraIndex
			});
		}

		internal static void PublishContextMarker(Vector3 position, float radius, string label, int colorKind, int cameraIndex)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			Publish(new CCTVMarkerMessage
			{
				Id = NextId(),
				Kind = 2,
				Position = position,
				Radius = Mathf.Clamp(radius, 0.35f, 4f),
				ColorKind = colorKind,
				Label = (string.IsNullOrEmpty(label) ? "CCTV TARGET" : label),
				Lifetime = 8f,
				CameraIndex = cameraIndex
			});
		}

		internal static void Tick()
		{
			if ((Object)(object)_root == (Object)null)
			{
				return;
			}
			Camera camera = ResolveGameplayCamera();
			bool isFocused = MonitorFocus.IsFocused;
			for (int num = _markers.Count - 1; num >= 0; num--)
			{
				MarkerVisual markerVisual = _markers[num];
				if (markerVisual == null || Time.unscaledTime >= markerVisual.ExpiresAt)
				{
					if ((Object)(object)markerVisual?.Root != (Object)null)
					{
						Object.Destroy((Object)(object)((Component)markerVisual.Root).gameObject);
					}
					_markers.RemoveAt(num);
				}
				else
				{
					UpdateMarker(markerVisual, camera, isFocused);
				}
			}
		}

		private static int NextId()
		{
			int num = (int)(((Object)(object)GameNetworkManager.Instance?.localPlayerController != (Object)null) ? GameNetworkManager.Instance.localPlayerController.playerClientId : 0);
			_nextMarkerId++;
			return (num << 20) ^ _nextMarkerId ^ Mathf.RoundToInt(Time.realtimeSinceStartup * 1000f);
		}

		private static void Publish(CCTVMarkerMessage data)
		{
			AddLocalMarker(data);
			if (_message == null)
			{
				return;
			}
			try
			{
				_message.SendServer(data);
			}
			catch (Exception ex)
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogDebug((object)("[LethalCCTV] CCTV marker stayed local: " + ex.Message));
				}
			}
		}

		private static void OnServerReceived(CCTVMarkerMessage data, ulong _)
		{
			try
			{
				_message?.SendClients(data);
			}
			catch (Exception ex)
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogDebug((object)("[LethalCCTV] CCTV marker server broadcast failed: " + ex.Message));
				}
			}
		}

		private static void OnClientReceived(CCTVMarkerMessage data)
		{
			AddLocalMarker(data);
		}

		private static void AddLocalMarker(CCTVMarkerMessage data)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if (data == null)
			{
				return;
			}
			Initialize();
			if (!_seenIds.Contains(data.Id))
			{
				_seenIds.Add(data.Id);
				MarkerVisual item = CreateMarkerVisual(data);
				_markers.Add(item);
				if (data.Kind == 0)
				{
					CCTVScanGlowManager.MarkScanTarget(data.Position, data.Radius, data.ColorKind, data.Lifetime);
				}
			}
		}

		private static MarkerVisual CreateMarkerVisual(CCTVMarkerMessage data)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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_006b: 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)
			//IL_009a: 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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: 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_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject($"CCTVMarker_{data.Id}");
			val.transform.SetParent(_root.transform, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.anchorMin = new Vector2(0.5f, 0.5f);
			val2.anchorMax = new Vector2(0.5f, 0.5f);
			val2.pivot = new Vector2(0.5f, 0.5f);
			val2.sizeDelta = new Vector2(64f, 64f);
			Color color = ((data.ColorKind == 1) ? MARKER_RED : MARKER_GREEN);
			MarkerVisual markerVisual = new MarkerVisual
			{
				Id = data.Id,
				Kind = data.Kind,
				Position = data.Position,
				Radius = Mathf.Clamp(data.Radius, 0.25f, 4f),
				ExpiresAt = Time.unscaledTime + Mathf.Clamp(data.Lifetime, 1f, 15f),
				Color = color,
				Label = ((!string.IsNullOrEmpty(data.Label)) ? data.Label : ((data.Kind == 1) ? "PING" : "SCAN")),
				Root = val2
			};
			markerVisual.Top = CreateLine(val2, "Top", color);
			markerVisual.Bottom = CreateLine(val2, "Bottom", color);
			markerVisual.Left = CreateLine(val2, "Left", color);
			markerVisual.Right = CreateLine(val2, "Right", color);
			markerVisual.Dot = CreateLine(val2, "Dot", color);
			markerVisual.Text = CreateText(val2, "Label", markerVisual.Label, color);
			return markerVisual;
		}

		private static Image CreateLine(RectTransform parent, string name, Color color)
		{
			//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_0013: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent((Transform)(object)parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = new Vector2(0.5f, 0.5f);
			obj.anchorMax = new Vector2(0.5f, 0.5f);
			obj.pivot = new Vector2(0.5f, 0.5f);
			Image obj2 = val.AddComponent<Image>();
			((Graphic)obj2).color = color;
			((Graphic)obj2).raycastTarget = false;
			return obj2;
		}

		private static TextMeshProUGUI CreateText(RectTransform parent, string name, string text, Color color)
		{
			//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_0013: 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_0039: 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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent((Transform)(object)parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = new Vector2(0.5f, 0f);
			obj.anchorMax = new Vector2(0.5f, 0f);
			obj.pivot = new Vector2(0.5f, 1f);
			obj.anchoredPosition = new Vector2(0f, -8f);
			obj.sizeDelta = new Vector2(160f, 24f);
			TextMeshProUGUI obj2 = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj2).text = text;
			((TMP_Text)obj2).fontSize = 13f;
			((TMP_Text)obj2).fontStyle = (FontStyles)1;
			((TMP_Text)obj2).alignment = (TextAlignmentOptions)514;
			((Graphic)obj2).color = color;
			((TMP_Text)obj2).enableWordWrapping = false;
			((Graphic)obj2).raycastTarget = false;
			return obj2;
		}

		private static void UpdateMarker(MarkerVisual marker, Camera camera, bool hide)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)marker?.Root == (Object)null)
			{
				return;
			}
			if (hide || (Object)(object)camera == (Object)null)
			{
				((Component)marker.Root).gameObject.SetActive(false);
				return;
			}
			Vector3 val = camera.WorldToViewportPoint(marker.Position);
			if (val.z <= 0.05f || val.x < -0.08f || val.x > 1.08f || val.y < -0.08f || val.y > 1.08f)
			{
				((Component)marker.Root).gameObject.SetActive(false);
				return;
			}
			((Component)marker.Root).gameObject.SetActive(true);
			Transform transform = ((Component)_canvas).transform;
			RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			float num;
			Rect rect;
			if (!((Object)(object)val2 != (Object)null))
			{
				num = Screen.width;
			}
			else
			{
				rect = val2.rect;
				num = ((Rect)(ref rect)).width;
			}
			float num2 = num;
			float num3;
			if (!((Object)(object)val2 != (Object)null))
			{
				num3 = Screen.height;
			}
			else
			{
				rect = val2.rect;
				num3 = ((Rect)(ref rect)).height;
			}
			float num4 = num3;
			marker.Root.anchoredPosition = new Vector2((val.x - 0.5f) * num2, (val.y - 0.5f) * num4);
			float num5 = Mathf.Max(1f, Vector3.Distance(((Component)camera).transform.position, marker.Position));
			float num6 = ((marker.Kind == 1) ? 34f : Mathf.Clamp(marker.Radius / num5 * 1150f, 26f, 128f));
			marker.Root.sizeDelta = new Vector2(num6, num6);
			ApplyMarkerShape(marker, num6);
		}

		private static void ApplyMarkerShape(MarkerVisual marker, float size)
		{
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: 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_01af: 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_005c: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			float num = ((marker.Kind == 1) ? 5f : 4f);
			float num2 = size * 0.5f;
			if (marker.Kind == 1)
			{
				float num3 = Mathf.Clamp(size * 0.32f, 8f, 14f);
				SetLine(marker.Top, new Vector2(0f, num2 - num3 * 0.5f), new Vector2(num, num3));
				SetLine(marker.Bottom, new Vector2(0f, 0f - num2 + num3 * 0.5f), new Vector2(num, num3));
				SetLine(marker.Left, new Vector2(0f - num2 + num3 * 0.5f, 0f), new Vector2(num3, num));
				SetLine(marker.Right, new Vector2(num2 - num3 * 0.5f, 0f), new Vector2(num3, num));
				SetLine(marker.Dot, Vector2.zero, new Vector2(5f, 5f));
			}
			else
			{
				float num4 = Mathf.Clamp(size * 0.34f, 12f, 34f);
				SetLine(marker.Top, new Vector2(0f - num2 + num4 * 0.5f, num2), new Vector2(num4, num));
				SetLine(marker.Left, new Vector2(0f - num2, num2 - num4 * 0.5f), new Vector2(num, num4));
				SetLine(marker.Bottom, new Vector2(num2 - num4 * 0.5f, 0f - num2), new Vector2(num4, num));
				SetLine(marker.Right, new Vector2(num2, 0f - num2 + num4 * 0.5f), new Vector2(num, num4));
				SetLine(marker.Dot, Vector2.zero, new Vector2(4f, 4f));
			}
			if ((Object)(object)marker.Text != (Object)null)
			{
				((TMP_Text)marker.Text).rectTransform.anchoredPosition = new Vector2(0f, 0f - num2 - 10f);
				((Graphic)marker.Text).color = new Color(marker.Color.r, marker.Color.g, marker.Color.b, 0.95f);
			}
		}

		private static void SetLine(Image image, Vector2 position, Vector2 size)
		{
			//IL_0011: 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)
			if (!((Object)(object)image == (Object)null))
			{
				RectTransform rectTransform = ((Graphic)image).rectTransform;
				rectTransform.anchoredPosition = position;
				rectTransform.sizeDelta = size;
			}
		}

		private static Camera ResolveGameplayCamera()
		{
			PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
			if ((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null)
			{
				return val.gameplayCamera;
			}
			return Camera.main;
		}
	}
	[Serializable]
	public sealed class CCTVFeedStateMessage
	{
		public byte Kind;

		public int PlayerId;

		public byte FeedCctv;

		public int OperatorId;

		public int ActiveSlot = -1;
	}
	internal static class CCTVMonitorFeedSync
	{
		private const string CHANNEL = "y4ngz_cctv_feed_v1";

		private const byte KindRequestClaim = 1;

		private const byte KindRequestRelease = 2;

		private const byte KindRequestToggle = 3;

		private const byte KindState = 4;

		private const byte KindRequestActiveSlot = 5;

		private const float ClaimTimeoutSeconds = 1.5f;

		private static LNetworkMessage<CCTVFeedStateMessage> _message;

		private static bool _initialized;

		private static bool _feedCctv;

		private static int _operatorId = -1;

		private static bool _serverFeedCctv;

		private static int _serverOperatorId = -1;

		private static int _serverActiveSlot = -1;

		private static PlayerControllerB _claimPlayer;

		private static Action _claimConfirmed;

		private static Action<string> _claimDenied;

		private static float _claimTimeoutAt;

		private static int _remoteActiveSlot = -1;

		private static int _lastSentActiveSlot = -1;

		internal static bool IsFeedCctv => _feedCctv;

		internal static bool HasOperator => _operatorId >= 0;

		internal static int OperatorId => _operatorId;

		internal static bool IsClaimPending => (Object)(object)_claimPlayer != (Object)null;

		internal static int RemoteActiveSlot => _remoteActiveSlot;

		internal static bool IsLocalOperator
		{
			get
			{
				PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
				if ((Object)(object)val != (Object)null)
				{
					return _operatorId == (int)val.playerClientId;
				}
				return false;
			}
		}

		internal static void Initialize()
		{
			if (_initialized)
			{
				return;
			}
			_initialized = true;
			try
			{
				_message = LNetworkMessage<CCTVFeedStateMessage>.Connect("y4ngz_cctv_feed_v1", (Action<CCTVFeedStateMessage, ulong>)OnServerReceived, (Action<CCTVFeedStateMessage>)OnClientReceived, (Action<CCTVFeedStateMessage, ulong>)null);
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogInfo((object)"[LethalCCTV] Monitor feed sync channel 'y4ngz_cctv_feed_v1' connected.");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = SurveillanceBootstrap.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("[LethalCCTV] Monitor feed sync unavailable (feed state stays local): " + ex.Message));
				}
				_message = null;
			}
			CCTVStationEvents.ChairEntered += OnLocalChairEntered;
			CCTVStationEvents.ChairExited += OnLocalChairExited;
		}

		internal static void Shutdown()
		{
			if (_initialized)
			{
				_initialized = false;
				CCTVStationEvents.ChairEntered -= OnLocalChairEntered;
				CCTVStationEvents.ChairExited -= OnLocalChairExited;
				_message?.ClearSubscriptions();
				_message = null;
				ResetLocal("shutdown");
			}
		}

		internal static void Tick()
		{
			if (!((Object)(object)_claimPlayer == (Object)null) && !(Time.unscaledTime < _claimTimeoutAt))
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogWarning((object)"[LethalCCTV] Feed sync claim timed out; failing open into local entry.");
				}
				PlayerControllerB claimPlayer = _claimPlayer;
				Action claimConfirmed = _claimConfirmed;
				ClearPendingClaim();
				ApplyState(feedCctv: true, GetPlayerId(claimPlayer), "claim-timeout-failopen");
				claimConfirmed?.Invoke();
			}
		}

		internal static bool IsEntryAvailable(out string unavailableReason)
		{
			if (!CCTVTerminalUnlockable.IsPurchased())
			{
				unavailableReason = "Requires CCTV terminal upgrade";
				return false;
			}
			if (!IsShipLandedForCctv())
			{
				unavailableReason = "Comes online once the ship has landed";
				return false;
			}
			if (HasOperator && !IsLocalOperator)
			{
				unavailableReason = "In use by another employee";
				return false;
			}
			if (MonitorFocus.IsFocused || MonitorFocus.IsEntryPoseSettleActive || IsClaimPending)
			{
				unavailableReason = string.Empty;
				return false;
			}
			unavailableReason = null;
			return true;
		}

		internal static bool IsFeedToggleAvailable()
		{
			if (CCTVTerminalUnlockable.IsPurchased() && !HasOperator && !IsClaimPending && !MonitorFocus.IsFocused)
			{
				return IsShipLandedForCctv();
			}
			return false;
		}

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

		internal static void RequestEntry(PlayerControllerB player, Action onConfirmed, Action<string> onDenied)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)_claimPlayer != (Object)null)
			{
				return;
			}
			if (HasOperator && !IsLocalOperator)
			{
				onDenied?.Invoke("Another employee is on the cameras.");
				return;
			}
			int playerId = GetPlayerId(player);
			if (playerId < 0)
			{
				onDenied?.Invoke("Player not ready.");
				return;
			}
			if (_message == null)
			{
				ApplyState(feedCctv: true, playerId, "offline-claim");
				onConfirmed?.Invoke();
				return;
			}
			_claimPlayer = player;
			_claimConfirmed = onConfirmed;
			_claimDenied = onDenied;
			_claimTimeoutAt = Time.unscaledTime + 1.5f;
			SendRequest(new CCTVFeedStateMessage
			{
				Kind = 1,
				PlayerId = playerId
			});
		}

		internal static void NotifyLocalActiveSlotChanged(int slot)
		{
			if (!IsLocalOperator || slot == _lastSentActiveSlot)
			{
				return;
			}
			_lastSentActiveSlot = slot;
			if (_message == null)
			{
				_remoteActiveSlot = slot;
				return;
			}
			try
			{
				_message.SendServer(new CCTVFeedStateMessage
				{
					Kind = 5,
					PlayerId = _operatorId,
					ActiveSlot = slot
				});
			}
			catch (Exception ex)
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogDebug((object)("[LethalCCTV] Active-slot sync stayed local: " + ex.Message));
				}
			}
		}

		internal static void RequestFeedToggle(PlayerControllerB player)
		{
			if (IsFeedToggleAvailable())
			{
				int playerId = GetPlayerId(player);
				if (_message == null)
				{
					ApplyState(!_feedCctv, _operatorId, "offline-toggle");
					return;
				}
				SendRequest(new CCTVFeedStateMessage
				{
					Kind = 3,
					PlayerId = playerId
				});
			}
		}

		internal static void ResetForOrbit(string reason)
		{
			_serverFeedCctv = false;
			_serverOperatorId = -1;
			_serverActiveSlot = -1;
			ClearPendingClaim();
			ApplyState(feedCctv: false, -1, reason, -1);
		}

		private static void OnLocalChairEntered(PlayerControllerB player)
		{
			int playerId = GetPlayerId(player);
			if (playerId >= 0 && _operatorId != playerId)
			{
				if (_message == null)
				{
					ApplyState(feedCctv: true, playerId, "late-claim-offline");
					return;
				}
				SendRequest(new CCTVFeedStateMessage
				{
					Kind = 1,
					PlayerId = playerId
				});
			}
		}

		private static void OnLocalChairExited(PlayerControllerB player)
		{
			ReleaseLocalEntryClaim(player, "chair-exited");
		}

		internal static void ReleaseLocalEntryClaim(PlayerControllerB player, string reason)
		{
			int num = GetPlayerId(player);
			if (num < 0 && IsLocalOperator)
			{
				num = _operatorId;
			}
			ClearPendingClaim();
			if (num < 0)
			{
				return;
			}
			ManualLogSource log = SurveillanceBootstrap.Log;
			if (log != null)
			{
				log.LogDebug((object)string.Format("[LethalCCTV] Releasing local CCTV entry claim ({0}) for player #{1}.", reason ?? "unspecified", num));
			}
			if (_message == null)
			{
				if (_operatorId == num)
				{
					ApplyState(_feedCctv, -1, "offline-release-" + (reason ?? "unspecified"));
				}
			}
			else
			{
				SendRequest(new CCTVFeedStateMessage
				{
					Kind = 2,
					PlayerId = num
				});
			}
		}

		private static void SendRequest(CCTVFeedStateMessage message)
		{
			try
			{
				_message.SendServer(message);
			}
			catch (Exception ex)
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogDebug((object)("[LethalCCTV] Feed sync request stayed local: " + ex.Message));
				}
				switch (message.Kind)
				{
				case 1:
				{
					_ = _claimPlayer;
					Action claimConfirmed = _claimConfirmed;
					ClearPendingClaim();
					ApplyState(feedCctv: true, message.PlayerId, "send-failed-claim");
					claimConfirmed?.Invoke();
					break;
				}
				case 2:
					if (_operatorId == message.PlayerId)
					{
						ApplyState(_feedCctv, -1, "send-failed-release");
					}
					break;
				case 3:
					ApplyState(!_feedCctv, _operatorId, "send-failed-toggle");
					break;
				}
			}
		}

		private static void OnServerReceived(CCTVFeedStateMessage data, ulong _)
		{
			if (data == null)
			{
				return;
			}
			switch (data.Kind)
			{
			default:
				return;
			case 1:
				if (_serverOperatorId < 0 || _serverOperatorId == data.PlayerId)
				{
					_serverOperatorId = data.PlayerId;
					_serverFeedCctv = true;
					_serverActiveSlot = -1;
				}
				break;
			case 2:
				if (_serverOperatorId == data.PlayerId)
				{
					_serverOperatorId = -1;
				}
				break;
			case 3:
				if (_serverOperatorId < 0)
				{
					_serverFeedCctv = !_serverFeedCctv;
				}
				break;
			case 5:
				if (_serverOperatorId == data.PlayerId)
				{
					_serverActiveSlot = data.ActiveSlot;
				}
				break;
			case 4:
				return;
			}
			BroadcastState();
		}

		private static void BroadcastState()
		{
			try
			{
				_message?.SendClients(new CCTVFeedStateMessage
				{
					Kind = 4,
					PlayerId = -1,
					FeedCctv = (_serverFeedCctv ? ((byte)1) : ((byte)0)),
					OperatorId = _serverOperatorId,
					ActiveSlot = _serverActiveSlot
				});
			}
			catch (Exception ex)
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogDebug((object)("[LethalCCTV] Feed sync broadcast failed: " + ex.Message));
				}
			}
		}

		private static void OnClientReceived(CCTVFeedStateMessage data)
		{
			if (data != null && data.Kind == 4)
			{
				ApplyState(data.FeedCctv != 0, data.OperatorId, "network-state", data.ActiveSlot);
			}
		}

		private static void ApplyState(bool feedCctv, int operatorId, string reason, int? activeSlot = null)
		{
			bool num = _feedCctv != feedCctv;
			bool flag = _operatorId != operatorId;
			_feedCctv = feedCctv;
			_operatorId = operatorId;
			if (activeSlot.HasValue)
			{
				_remoteActiveSlot = activeSlot.Value;
			}
			if (operatorId < 0)
			{
				_lastSentActiveSlot = -1;
			}
			if (num || flag)
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogInfo((object)("[LethalCCTV] Monitor feed state applied (" + reason + "): feed=" + (feedCctv ? "cctv" : "vanilla") + " operator=" + ((operatorId < 0) ? "none" : ("#" + operatorId)) + "."));
				}
			}
			ApplyFeedToDisplay();
			ResolvePendingClaim();
		}

		internal static void ApplyFeedToDisplay()
		{
			bool flag = _feedCctv && CCTVTerminalUnlockable.IsPurchased();
			if (!flag || !MonitorFocus.IsStationFeedFlipPending)
			{
				CCTVVanillaMonitorDisplay.SetCctvModeActive(flag, announce: false);
				if (flag)
				{
					CCTVVanillaMonitorDisplay.PrimeMonitorOwnershipNow(renderNow: false);
				}
			}
		}

		private static void ResolvePendingClaim()
		{
			if (!((Object)(object)_claimPlayer == (Object)null))
			{
				int playerId = GetPlayerId(_claimPlayer);
				if (_operatorId == playerId && playerId >= 0)
				{
					Action claimConfirmed = _claimConfirmed;
					ClearPendingClaim();
					claimConfirmed?.Invoke();
				}
				else if (HasOperator)
				{
					Action<string> claimDenied = _claimDenied;
					ClearPendingClaim();
					claimDenied?.Invoke("Another employee is on the cameras.");
				}
			}
		}

		private static void ClearPendingClaim()
		{
			_claimPlayer = null;
			_claimConfirmed = null;
			_claimDenied = null;
			_claimTimeoutAt = 0f;
		}

		private static void ResetLocal(string reason)
		{
			_serverFeedCctv = false;
			_serverOperatorId = -1;
			_serverActiveSlot = -1;
			ClearPendingClaim();
			_feedCctv = false;
			_operatorId = -1;
			_remoteActiveSlot = -1;
			_lastSentActiveSlot = -1;
			ManualLogSource log = SurveillanceBootstrap.Log;
			if (log != null)
			{
				log.LogDebug((object)("[LethalCCTV] Monitor feed state reset locally (" + reason + ")."));
			}
		}

		private static int GetPlayerId(PlayerControllerB player)
		{
			if (!((Object)(object)player != (Object)null))
			{
				return -1;
			}
			return (int)player.playerClientId;
		}
	}
	[HarmonyPatch(typeof(ManualCameraRenderer), "SwitchScreenButton")]
	internal static class CCTVMonitorModeButtonPatch
	{
		[HarmonyPriority(600)]
		private static bool Prefix(ManualCameraRenderer __instance)
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)__instance != (Object)(object)instance.mapScreen)
			{
				return true;
			}
			if (CCTVVanillaMonitorDisplay.IsGeneralImprovementsMonitorWallActive() && !CCTVVanillaMonitorDisplay.OwnsGeneralImprovementsMapScreen())
			{
				return true;
			}
			return false;
		}
	}
	[HarmonyPatch]
	internal static class CCTVMonitorModeShipLeavePatch
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(StartOfRound), "ShipLeave")]
		private static void OnShipLeave()
		{
			ResetForOrbit("ship-leave");
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(StartOfRound), "ShipLeaveAutomatically")]
		private static void OnShipLeaveAutomatically()
		{
			ResetForOrbit("ship-leave-auto");
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(GameNetworkManager), "Disconnect")]
		private static void OnDisconnect()
		{
			ResetForOrbit("disconnect");
		}

		private static void ResetForOrbit(string reason)
		{
			MonitorFocus.ForceExit(reason);
			CCTVMonitorFeedSync.ResetForOrbit(reason);
		}
	}
	[HarmonyPatch(typeof(ManualCameraRenderer), "Update")]
	internal static class CCTVMonitorOwnershipManualCameraRendererUpdatePatch
	{
		private static bool Prefix(ManualCameraRenderer __instance)
		{
			if (!CCTVVanillaMonitorDisplay.ShouldSuppressVanillaMonitorRendererUpdate(__instance))
			{
				return true;
			}
			CCTVVanillaMonitorDisplay.ReassertCctvScreenOwnershipAfterVanilla();
			return false;
		}

		private static void Postfix()
		{
			CCTVVanillaMonitorDisplay.ReassertCctvScreenOwnershipAfterVanilla();
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "LateUpdate")]
	internal static class CCTVMonitorOwnershipStartOfRoundLateUpdatePatch
	{
		private static void Postfix()
		{
			CCTVVanillaMonitorDisplay.ReassertCctvScreenOwnershipAfterVanilla();
		}
	}
	[Serializable]
	public sealed class CCTVOperatorAnimMessage
	{
		public byte Kind;

		public int PlayerId;

		public sbyte JoyX;

		public sbyte JoyY;

		public byte Arg;

		public int Yaw;
	}
	internal static class CCTVOperatorAnimSync
	{
		private const string CHANNEL = "y4ngz_cctv_anim_v1";

		private const float JoystickSendInterval = 1f / 12f;

		private const float JoystickHeartbeatSeconds = 1f;

		private const float JoystickDiagnosticInterval = 1f;

		private const string MainframeDefaultStopTrigger = "SA_stopAnimation";

		private const byte KindEnter = 1;

		private const byte KindExit = 2;

		private const byte KindCameraControl = 3;

		private const byte KindButtonPress = 4;

		private const byte KindSelectCamera = 5;

		private const byte KindRadarLook = 6;

		private const byte KindJoystick = 7;

		private const byte KindMainframeEnter = 8;

		private const byte KindMainframeExit = 9;

		private static LNetworkMessage<CCTVOperatorAnimMessage> _message;

		private static readonly Dictionary<int, OperatorAnimSession> _remoteSessions = new Dictionary<int, OperatorAnimSession>();

		private static readonly HashSet<int> _remoteMainframePlayers = new HashSet<int>();

		private static readonly List<int> _endedSessionIds = new List<int>();

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

		private static readonly HashSet<int> _remoteJoystickDiagnosticLogged = new HashSet<int>();

		private static float _nextJoystickSendAt;

		private static float _nextJoystickHeartbeatAt;

		private static float _nextLocalJoystickDiagnosticAt;

		private static sbyte _lastSentJoyX = sbyte.MaxValue;

		private static sbyte _lastSentJoyY = sbyte.MaxValue;

		private static bool _localJoystickDiagnosticLogged;

		internal static void Initialize()
		{
			if (_message != null)
			{
				return;
			}
			try
			{
				_message = LNetworkMessage<CCTVOperatorAnimMessage>.Connect("y4ngz_cctv_anim_v1", (Action<CCTVOperatorAnimMessage, ulong>)OnServerReceived, (Action<CCTVOperatorAnimMessage>)OnClientReceived, (Action<CCTVOperatorAnimMessage, ulong>)null);
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogInfo((object)"[LethalCCTV] Operator anim sync channel 'y4ngz_cctv_anim_v1' connected.");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = SurveillanceBootstrap.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("[LethalCCTV] Operator anim sync unavailable (animations stay local): " + ex.Message));
				}
				_message = null;
			}
		}

		internal static void Shutdown()
		{
			foreach (KeyValuePair<int, OperatorAnimSession> remoteSession in _remoteSessions)
			{
				remoteSession.Value?.EndImmediate("sync-shutdown");
			}
			_remoteSessions.Clear();
			foreach (int item in new List<int>(_remoteMainframePlayers))
			{
				ApplyRemoteMainframeExit(item, ResolvePlayer(item), "sync-shutdown");
			}
			_remoteMainframePlayers.Clear();
			_nextRemoteJoystickDiagnosticAt.Clear();
			_remoteJoystickDiagnosticLogged.Clear();
			_message?.ClearSubscriptions();
			_message = null;
		}

		internal static void Tick()
		{
			if (_remoteSessions.Count == 0)
			{
				return;
			}
			_endedSessionIds.Clear();
			foreach (KeyValuePair<int, OperatorAnimSession> remoteSession in _remoteSessions)
			{
				OperatorAnimSession value = remoteSession.Value;
				if (value == null)
				{
					_endedSessionIds.Add(remoteSession.Key);
					continue;
				}
				value.Tick();
				if (!value.IsActive && !value.IsWindingDown)
				{
					_endedSessionIds.Add(remoteSession.Key);
				}
			}
			for (int i = 0; i < _endedSessionIds.Count; i++)
			{
				_remoteSessions.Remove(_endedSessionIds[i]);
			}
		}

		internal static void TickLocalJoystickSend(OperatorAnimSession localSession)
		{
			//IL_002a: 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 (_message != null && localSession != null && localSession.IsActive && localSession.CameraControlActive && !(Time.unscaledTime < _nextJoystickSendAt))
			{
				sbyte b = QuantizeAxis(localSession.JoystickCommand.x);
				sbyte b2 = QuantizeAxis(localSession.JoystickCommand.y);
				bool num = b != _lastSentJoyX || b2 != _lastSentJoyY;
				bool flag = Time.unscaledTime >= _nextJoystickHeartbeatAt;
				if (num || flag)
				{
					_nextJoystickSendAt = Time.unscaledTime + 1f / 12f;
					_nextJoystickHeartbeatAt = Time.unscaledTime + 1f;
					_lastSentJoyX = b;
					_lastSentJoyY = b2;
					Send(new CCTVOperatorAnimMessage
					{
						Kind = 7,
						PlayerId = GetPlayerId(localSession.Player),
						JoyX = b,
						JoyY = b2
					});
				}
			}
		}

		internal static void SendEnter(PlayerControllerB player, int activeSlot)
		{
			_lastSentJoyX = sbyte.MaxValue;
			_lastSentJoyY = sbyte.MaxValue;
			_localJoystickDiagnosticLogged = false;
			_nextLocalJoystickDiagnosticAt = 0f;
			Send(new CCTVOperatorAnimMessage
			{
				Kind = 1,
				PlayerId = GetPlayerId(player),
				Arg = (byte)Mathf.Clamp(activeSlot, 0, 255)
			});
			ReportOperatorNoiseToBundy(player);
		}

		internal static void SendExit(PlayerControllerB player)
		{
			Send(new CCTVOperatorAnimMessage
			{
				Kind = 2,
				PlayerId = GetPlayerId(player)
			});
		}

		internal static void SendCameraControl(PlayerControllerB player, bool controlling)
		{
			Send(new CCTVOperatorAnimMessage
			{
				Kind = 3,
				PlayerId = GetPlayerId(player),
				Arg = (controlling ? ((byte)1) : ((byte)0))
			});
		}

		internal static void SendButtonPress(PlayerControllerB player, int buttonId)
		{
			Send(new CCTVOperatorAnimMessage
			{
				Kind = 4,
				PlayerId = GetPlayerId(player),
				Arg = (byte)Mathf.Clamp(buttonId, 0, 255)
			});
		}

		internal static void SendSelectCamera(PlayerControllerB player, int slot)
		{
			Send(new CCTVOperatorAnimMessage
			{
				Kind = 5,
				PlayerId = GetPlayerId(player),
				Arg = (byte)Mathf.Clamp(slot, 0, 255)
			});
		}

		internal static void SendRadarLook(PlayerControllerB player, bool looking)
		{
			Send(new CCTVOperatorAnimMessage
			{
				Kind = 6,
				PlayerId = GetPlayerId(player),
				Arg = (looking ? ((byte)1) : ((byte)0))
			});
		}

		internal static void SendMainframeEnter(PlayerControllerB player, short yaw)
		{
			Send(new CCTVOperatorAnimMessage
			{
				Kind = 8,
				PlayerId = GetPlayerId(player),
				Yaw = yaw
			});
			ReportOperatorNoiseToBundy(player);
		}

		private static void ReportOperatorNoiseToBundy(PlayerControllerB player)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)player == (Object)null))
			{
				BundyWorldNoiseBridge.Report(((Component)player).transform.position, 8, player);
			}
		}

		internal static void SendMainframeExit(PlayerControllerB player)
		{
			Send(new CCTVOperatorAnimMessage
			{
				Kind = 9,
				PlayerId = GetPlayerId(player)
			});
		}

		private static void Send(CCTVOperatorAnimMessage message)
		{
			if (message == null)
			{
				return;
			}
			if (message.PlayerId < 0)
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogWarning((object)("[LethalCCTV] Operator anim sync send skipped: invalid player id for " + KindName(message.Kind) + "."));
				}
				return;
			}
			LogLocalSend(message, _message != null);
			if (_message == null)
			{
				return;
			}
			try
			{
				_message.SendServer(message);
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = SurveillanceBootstrap.Log;
				if (log2 != null)
				{
					log2.LogDebug((object)("[LethalCCTV] Operator anim message stayed local: " + ex.Message));
				}
			}
		}

		private static int GetPlayerId(PlayerControllerB player)
		{
			if (!((Object)(object)player != (Object)null))
			{
				return -1;
			}
			return (int)player.playerClientId;
		}

		private static PlayerControllerB ResolvePlayer(int playerId)
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null)
			{
				return null;
			}
			if (playerId < 0 || playerId >= instance.allPlayerScripts.Length)
			{
				return null;
			}
			return instance.allPlayerScripts[playerId];
		}

		private static sbyte QuantizeAxis(float value)
		{
			return (sbyte)Mathf.RoundToInt(Mathf.Clamp(value, -1f, 1f) * 127f);
		}

		private static void OnServerReceived(CCTVOperatorAnimMessage data, ulong _)
		{
			try
			{
				_message?.SendClients(data);
			}
			catch (Exception ex)
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogDebug((object)("[LethalCCTV] Operator anim server broadcast failed: " + ex.Message));
				}
			}
		}

		private static void OnClientReceived(CCTVOperatorAnimMessage data)
		{
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			if (data == null)
			{
				return;
			}
			PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
			if ((Object)(object)val != (Object)null && data.PlayerId == (int)val.playerClientId)
			{
				return;
			}
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null || data.PlayerId < 0 || data.PlayerId >= instance.allPlayerScripts.Length)
			{
				return;
			}
			PlayerControllerB val2 = instance.allPlayerScripts[data.PlayerId];
			if ((Object)(object)val2 == (Object)null)
			{
				return;
			}
			LogRemoteReceive(data);
			switch (data.Kind)
			{
			case 1:
				ApplyRemoteEnter(data.PlayerId, val2, data.Arg);
				break;
			case 2:
			{
				if (_remoteSessions.TryGetValue(data.PlayerId, out var value))
				{
					value.BeginGracefulEnd("remote-exit");
				}
				_nextRemoteJoystickDiagnosticAt.Remove(data.PlayerId);
				_remoteJoystickDiagnosticLogged.Remove(data.PlayerId);
				break;
			}
			case 3:
				GetRemoteSession(data.PlayerId)?.SetCameraControl(data.Arg != 0);
				break;
			case 4:
				GetRemoteSession(data.PlayerId)?.PressButton(data.Arg, isStandUp: false);
				break;
			case 5:
				GetRemoteSession(data.PlayerId)?.SelectCamera(data.Arg);
				break;
			case 6:
				GetRemoteSession(data.PlayerId)?.SetRadarLook(data.Arg != 0);
				break;
			case 7:
				GetRemoteSession(data.PlayerId)?.SetJoystickTargetAbsolute(new Vector2((float)data.JoyX / 127f, (float)data.JoyY / 127f));
				break;
			case 8:
				ApplyRemoteMainframeEnter(data.PlayerId, val2, data.Yaw);
				break;
			case 9:
				ApplyRemoteMainframeExit(data.PlayerId, val2, "remote-exit");
				break;
			}
		}

		private static void ApplyRemoteEnter(int playerId, PlayerControllerB player, int activeSlot)
		{
			if (_remoteSessions.TryGetValue(playerId, out var value))
			{
				value?.EndImmediate("remote-reenter");
				_remoteSessions.Remove(playerId);
			}
			_nextRemoteJoystickDiagnosticAt.Remove(playerId);
			_remoteJoystickDiagnosticLogged.Remove(playerId);
			OperatorAnimSession operatorAnimSession = new OperatorAnimSession(player, isLocal: false);
			if (operatorAnimSession.Begin(Y4NGZPlayerAnimationBridge.ResolveControllerShared(), enterFlourish: true))
			{
				operatorAnimSession.SetActiveSlotQuiet(activeSlot);
				_remoteSessions[playerId] = operatorAnimSession;
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogInfo((object)$"[LethalCCTV] Remote operator anim session started for player #{playerId}.");
				}
				ReportOperatorNoiseToBundy(player);
			}
		}

		private static void ApplyRemoteMainframeEnter(int playerId, PlayerControllerB player, int yaw)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			ResolveVanillaTerminalAnimation(out var enterTrigger, out var _);
			short num = (short)Mathf.RoundToInt(Mathf.Repeat((float)yaw, 360f));
			try
			{
				player.UpdateSpecialAnimationValue(true, num, 0f, false);
				player.inSpecialInteractAnimation = true;
				player.enteringSpecialAnimation = false;
			}
			catch (Exception ex)
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogWarning((object)$"[LethalCCTV][MainframeInteract] Remote mainframe enter failed for player #{playerId}: {ex.Message}");
				}
			}
			TryTriggerPlayerAnimator(player, enterTrigger, "mainframe-enter");
			_remoteMainframePlayers.Add(playerId);
			ManualLogSource log2 = SurveillanceBootstrap.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)$"[LethalCCTV][MainframeInteract] Remote mainframe TypeOnTerminal stance entered for player #{playerId} yaw={num}.");
			}
			ReportOperatorNoiseToBundy(player);
		}

		private static void ApplyRemoteMainframeExit(int playerId, PlayerControllerB player, string reason)
		{
			if ((Object)(object)player != (Object)null)
			{
				ResolveVanillaTerminalAnimation(out var _, out var stopTrigger);
				TryTriggerPlayerAnimator(player, stopTrigger, "mainframe-exit");
				try
				{
					player.UpdateSpecialAnimationValue(false, (short)0, 0f, false);
					player.inSpecialInteractAnimation = false;
					player.enteringSpecialAnimation = false;
				}
				catch (Exception ex)
				{
					ManualLogSource log = SurveillanceBootstrap.Log;
					if (log != null)
					{
						log.LogWarning((object)$"[LethalCCTV][MainframeInteract] Remote mainframe exit failed for player #{playerId}: {ex.Message}");
					}
				}
			}
			_remoteMainframePlayers.Remove(playerId);
			ManualLogSource log2 = SurveillanceBootstrap.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)string.Format("[LethalCCTV][MainframeInteract] Remote mainframe TypeOnTerminal stance exited for player #{0} reason='{1}'.", playerId, reason ?? "unspecified"));
			}
		}

		private static void ResolveVanillaTerminalAnimation(out string enterTrigger, out string stopTrigger)
		{
			enterTrigger = null;
			stopTrigger = "SA_stopAnimation";
			try
			{
				Terminal val = Object.FindObjectOfType<Terminal>();
				InteractTrigger val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<InteractTrigger>() : null);
				if (!((Object)(object)val2 == (Object)null))
				{
					if (!string.IsNullOrWhiteSpace(val2.animationString))
					{
						enterTrigger = val2.animationString;
					}
					if (!string.IsNullOrWhiteSpace(val2.stopAnimationString))
					{
						stopTrigger = val2.stopAnimationString;
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogWarning((object)("[LethalCCTV][MainframeInteract] Remote vanilla terminal animation probe failed: " + ex.Message));
				}
			}
		}

		private static void TryTriggerPlayerAnimator(PlayerControllerB player, string trigger, string phase)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)player.playerBodyAnimator == (Object)null || string.IsNullOrWhiteSpace(trigger))
			{
				return;
			}
			try
			{
				if (AnimatorHasTrigger(player.playerBodyAnimator, trigger))
				{
					player.playerBodyAnimator.ResetTrigger(trigger);
					player.playerBodyAnimator.SetTrigger(trigger);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogWarning((object)("[LethalCCTV][MainframeInteract] Remote animator " + phase + " trigger '" + trigger + "' failed: " + ex.Message));
				}
			}
		}

		private static bool AnimatorHasTrigger(Animator animator, string trigger)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Invalid comparison between Unknown and I4
			if ((Object)(object)animator == (Object)null || string.IsNullOrWhiteSpace(trigger))
			{
				return false;
			}
			try
			{
				AnimatorControllerParameter[] parameters = animator.parameters;
				for (int i = 0; i < parameters.Length; i++)
				{
					if ((int)parameters[i].type == 9 && string.Equals(parameters[i].name, trigger, StringComparison.Ordinal))
					{
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static OperatorAnimSession GetRemoteSession(int playerId)
		{
			if (!_remoteSessions.TryGetValue(playerId, out var value) || !value.IsActive)
			{
				return null;
			}
			return value;
		}

		private static void LogLocalSend(CCTVOperatorAnimMessage message, bool channelReady)
		{
			if (message.Kind != 7 || ShouldLogLocalJoystick(message))
			{
				ManualLogSource log = SurveillanceBootstrap.Log;
				if (log != null)
				{
					log.LogInfo((object)$"[LethalCCTV] Operator anim sync SEND {FormatMessage(message)} channelReady={channelReady}.");
				}
			}
		}

		private static bool ShouldLogLocalJoystick(CCTVOperatorAnimMessage message)
		{
			if (message.Kind != 7)
			{
				return true;
			}
			if (!_localJoystickDiagnosticLogged || Time.unscaledTime >= _nextLocalJoystickDiagnosticAt)
			{
				_localJoystickDiagnosticLogged = true;
				_nextLocalJoystickDiagnosticAt = Time.unscaledTime + 1f;
				return true;
			}
			return false;
		}

		private static void LogRemoteReceive(CCTVOperatorAnimMessage message)
		{
			if (message.Kind == 7)
			{
				bool num = _remoteJoystickDiagnosticLogged.Contains(message.PlayerId);
				_nextRemoteJoystickDiagnosticAt.TryGetValue(message.PlayerId, out var value);
				if (num && Time.unscaledTime < value)
				{
					return;
				}
				_remoteJoystickDiagnosticLogged.Add(message.PlayerId);
				_nextRemoteJoystickDiagnosticAt[message.PlayerId] = Time.unscaledTime + 1f;
			}
			ManualLogSource log = SurveillanceBootstrap.Log;
			if (log != null)
			{
				log.LogInfo((object)("[LethalCCTV] Operator anim sync RECV " + FormatMessage(message) + "."));
			}
		}

		private static string FormatMessage(CCTVOperatorAnimMessage message)
		{
			if (message == null)
			{
				return "<null>";
			}
			if (message.Kind == 7)
			{
				return $"{KindName(message.Kind)} player=#{message.PlayerId} " + $"joy=({(float)message.JoyX / 127f:F2},{(float)message.JoyY / 127f:F2})";
			}
			if (message.Kind == 8)
			{
				return $"{KindName(message.Kind)} player=#{message.PlayerId} yaw={message.Yaw}";
			}
			return $"{KindName(message.Kind)} player=#{message.PlayerId} arg={message.Arg}";
		}

		private static string KindName(byte kind)
		{
			return kind switch
			{
				1 => "Enter", 
				2 => "Exit", 
				3 => "CameraControl", 
				4 => "ButtonPress", 
				5 => "SelectCamera", 
				6 => "RadarLook", 
				7 => "Joystick", 
				8 => "MainframeEnter", 
				9 => "MainframeExit", 
				_ => "Unknown(" + kind + ")", 
			};
		}
	}
	[HarmonyPatch(typeof(PlayerControllerB), "UpdatePlayerAnimationsToOtherClients")]
	internal static class CCTVOperatorAnimSyncSuppressPatch
	{
		private static bool Prefix(PlayerControllerB __instance)
		{
			return !Y4NGZPlayerAnimationBridge.IsLocalSessionActive(__instance);
		}
	}
	internal static class CCTVOperatorInteractionsBridge
	{
		private sealed class CctvOperatorManifest
		{
			public string interactionId;

			public string bundleInternalName;

			public CctvLocalViewmodelManifest localViewmodel;

			public CctvBodyManifest body;
		}

		private sealed class CctvLocalViewmodelManifest
		{
			public string bundleFileName;

			public string prefabAssetName;

			public string controllerAssetName;
		}

		private sealed class CctvBodyManifest
		{
			public bool enabled;

			public string bundleFileName;

			public string controllerAssetName;
		}

		private static class ManifestJson
		{
			internal static bool TryParseObject(string json, out Dictionary<string, object> value, out string error)
			{
				value = null;
				if (string.IsNullOrWhiteSpace(json))
				{
					error = "detail='empty document'";
					return false;
				}
				try
				{
					int index = 0;
					object obj = ParseValue(json, ref index);
					SkipWhitespace(json, ref index);
					if (index != json.Length)
					{
						throw new FormatException($"trailing content at offset {index}");
					}
					value = obj as Dictionary<string, object>;
					if (value == null)
					{
						error = "detail='document root is not a JSON object'";
						return false;
					}
					error = string.Empty;
					return true;
				}
				catch (Exception ex)
				{
					error = "detail='" + ex.GetType().Name + ": " + ex.Message + "'";
					return false;
				}
			}

			internal static bool TryReadObject(Dictionary<string, object> owner, string key, out Dictionary<string, object> value)
			{
				value = ((owner != null && owner.TryGetValue(key, out var value2)) ? (value2 as Dictionary<string, object>) : null);
				return value != null;
			}

			internal static string ReadString(Dictionary<string, object> owner, string key)
			{
				if (owner == null || !owner.TryGetValue(key, out var value))
				{
					return null;
				}
				return value as string;
			}

			internal static bool ReadBool(Dictionary<string, object> owner, string key)
			{
				bool flag = default(bool);
				int num;
				if (owner != null && owner.TryGetValue(key, out var value))
				{
					if (value is bool)
					{
						flag = (bool)value;
						num = 1;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}

			private static object ParseValue(string json, ref int index)
			{
				SkipWhitespace(json, ref index);
				if (index >= json.Length)
				{
					throw new FormatException("unexpected end of document");
				}
				return json[index] switch
				{
					'{' => ParseObject(json, ref index), 
					'[' => ParseArray(json, ref index), 
					'"' => ParseString(json, ref index), 
					_ => ParseLiteral(json, ref index), 
				};
			}

			private static Dictionary<string, object> ParseObject(string json, ref int index)
			{
				Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
				index++;
				SkipWhitespace(json, ref index);
				if (index < json.Length && json[index] == '}')
				{
					index++;
					return dictionary;
				}
				while (true)