Decompiled source of Extraction1 v0.2.80

plugins/Extraction1/DoorLockpickPlus.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ScrapVisbility")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ScrapVisbility")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("8a6853bd-bdc9-4741-95c7-5aa2c8c6a6f9")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace DoorLockpickPlus;

[BepInPlugin("YourFurnace.DoorLockpickPlus", "DoorLockpickPlus", "1.3.8")]
public class DoorLockpickPlus : BaseUnityPlugin
{
	private const string modGUID = "YourFurnace.DoorLockpickPlus";

	private const string modName = "DoorLockpickPlus";

	private const string modVersion = "1.3.8";

	private readonly Harmony harmony = new Harmony("YourFurnace.DoorLockpickPlus");

	internal static DoorLockpickPlus Instance;

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> EnableNormalDoorLockpicking;

	internal static ConfigEntry<bool> EnableBigDoorHacking;

	internal static ConfigEntry<bool> EnableBigDoorInPersonClose;

	internal static ConfigEntry<bool> DebugRollLogging;

	internal static ConfigEntry<bool> EnableLockpickSounds;

	internal static ConfigEntry<float> NormalDoorPickTime;

	internal static ConfigEntry<float> NormalDoorSuccessChance;

	internal static ConfigEntry<float> BigDoorHackTime;

	internal static ConfigEntry<float> BigDoorSuccessChance;

	internal static ConfigEntry<float> BigDoorCloseTime;

	internal static ConfigEntry<float> RetryCooldown;

	internal static ConfigEntry<float> FailFlashTime;

	internal static ConfigEntry<float> LockpickSoundVolume;

	internal static ConfigEntry<float> NormalDoorTriggerWidth;

	internal static ConfigEntry<float> NormalDoorTriggerHeight;

	internal static ConfigEntry<float> NormalDoorTriggerDepth;

	internal static ConfigEntry<float> DoorTintStrength;

	internal static ConfigEntry<float> DoorOverlayAlpha;

	internal static ConfigEntry<float> DoorEmissionStrength;

	internal static ConfigEntry<float> DoorLightStrength;

	internal static ConfigEntry<float> NormalDoorVisualMultiplier;

	internal static ConfigEntry<float> BigDoorVisualMultiplier;

	private float nextScanTime = 0f;

	private void Awake()
	{
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		EnableNormalDoorLockpicking = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable normal door lockpicking", true, "Adds a normal Lethal Company E hold prompt to locked normal doors.");
		EnableBigDoorHacking = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable big door prying", true, "Adds a normal Lethal Company E hold prompt to closed terminal controlled big doors.");
		EnableBigDoorInPersonClose = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable big door in-person close", true, "Adds a normal Lethal Company E hold prompt to close open terminal controlled big doors.");
		EnableLockpickSounds = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "Enable lockpick sounds", true, "Plays the base game lockpicking sound while holding E on a locked normal door.");
		LockpickSoundVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "Lockpick sound volume", 0.75f, "Volume for lockpick, success, and fail sounds.");
		NormalDoorPickTime = ((BaseUnityPlugin)this).Config.Bind<float>("Normal Doors", "Normal door pick time", 10f, "Seconds required to lockpick a normal locked door.");
		NormalDoorSuccessChance = ((BaseUnityPlugin)this).Config.Bind<float>("Normal Doors", "Normal door success chance", 0.5f, "Chance to unlock a normal locked door. 0.50 means 50%.");
		BigDoorHackTime = ((BaseUnityPlugin)this).Config.Bind<float>("Big Doors", "Big door pry time", 25f, "Seconds required to pry open a terminal controlled big door.");
		BigDoorSuccessChance = ((BaseUnityPlugin)this).Config.Bind<float>("Big Doors", "Big door pry success chance", 0.33f, "Chance to pry open a terminal controlled big door. 0.33 means 33%.");
		BigDoorCloseTime = ((BaseUnityPlugin)this).Config.Bind<float>("Big Doors", "Big door close time", 2f, "Seconds required to close an open terminal controlled big door in person. This is always successful.");
		RetryCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "Retry cooldown", 2f, "Cooldown after a lockpick or pry attempt finishes. Helps prevent retry race conditions.");
		FailFlashTime = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals", "Fail flash time", 1.5f, "How long a failed door flashes red.");
		DebugRollLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Debug roll logging", true, "Logs lockpick and pry rolls to the BepInEx console. Leave this on while playtesting.");
		NormalDoorTriggerWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Trigger Sizes", "Normal door trigger width", 2.25f, "Minimum width of the invisible lockpick interaction trigger on normal doors.");
		NormalDoorTriggerHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Trigger Sizes", "Normal door trigger height", 3.5f, "Minimum height of the invisible lockpick interaction trigger on normal doors.");
		NormalDoorTriggerDepth = ((BaseUnityPlugin)this).Config.Bind<float>("Trigger Sizes", "Normal door trigger depth", 2.25f, "Minimum depth of the invisible lockpick interaction trigger on normal doors.");
		DoorTintStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Door Visuals", "Door tint strength", 0.55f, "How much the real door mesh is tinted while lockpicking or failing. This belongs to DoorLockpickPlus only.");
		DoorOverlayAlpha = ((BaseUnityPlugin)this).Config.Bind<float>("Door Visuals", "Door overlay alpha", 0.18f, "Transparent overlay strength for the door mesh. Lower this if the highlight is too solid.");
		DoorEmissionStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Door Visuals", "Door emission strength", 1.15f, "Emission pushed into the door material while highlighted.");
		DoorLightStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Door Visuals", "Door light strength", 0.55f, "Point light strength while highlighted.");
		NormalDoorVisualMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Door Visuals", "Normal door visual multiplier", 1.35f, "Extra brightness multiplier for normal doors. Raise this if normal doors are too faint.");
		BigDoorVisualMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Door Visuals", "Big door visual multiplier", 0.35f, "Brightness multiplier for big doors. Lower this if big doors are too bright.");
		harmony.PatchAll();
		Log.LogInfo((object)"DoorLockpickPlus 1.3.8 loaded. Scene-safe big door commands and fixed door highlights enabled.");
	}

	private void OnDestroy()
	{
		harmony.UnpatchSelf();
	}

	private void Update()
	{
		if (!(Time.time < nextScanTime))
		{
			nextScanTime = Time.time + 1f;
			AttachToNormalDoors();
			AttachToBigDoors();
		}
	}

	private void AttachToNormalDoors()
	{
		if (EnableNormalDoorLockpicking == null || !EnableNormalDoorLockpicking.Value)
		{
			return;
		}
		DoorLock[] array = Object.FindObjectsOfType<DoorLock>();
		DoorLock[] array2 = array;
		foreach (DoorLock val in array2)
		{
			if (!((Object)(object)val == (Object)null) && (Object)(object)((Component)val).GetComponent<NormalDoorLockpickTarget>() == (Object)null)
			{
				NormalDoorLockpickTarget normalDoorLockpickTarget = ((Component)val).gameObject.AddComponent<NormalDoorLockpickTarget>();
				normalDoorLockpickTarget.Init(val);
			}
		}
	}

	private void AttachToBigDoors()
	{
		if ((EnableBigDoorHacking == null || !EnableBigDoorHacking.Value) && (EnableBigDoorInPersonClose == null || !EnableBigDoorInPersonClose.Value))
		{
			return;
		}
		TerminalAccessibleObject[] array = Object.FindObjectsOfType<TerminalAccessibleObject>();
		TerminalAccessibleObject[] array2 = array;
		foreach (TerminalAccessibleObject val in array2)
		{
			if (!((Object)(object)val == (Object)null) && BigDoorUtil.IsProbablyBigDoor(val) && (Object)(object)((Component)val).GetComponent<BigDoorLockpickTarget>() == (Object)null)
			{
				BigDoorLockpickTarget bigDoorLockpickTarget = ((Component)val).gameObject.AddComponent<BigDoorLockpickTarget>();
				bigDoorLockpickTarget.Init(val);
			}
		}
	}

	internal static void ShowTip(string header, string body, bool warning)
	{
		try
		{
			HUDManager val = Object.FindObjectOfType<HUDManager>();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(HUDManager), "DisplayTip", new Type[5]
			{
				typeof(string),
				typeof(string),
				typeof(bool),
				typeof(bool),
				typeof(string)
			}, (Type[])null);
			if (methodInfo != null)
			{
				methodInfo.Invoke(val, new object[5] { header, body, warning, false, "DoorLockpickPlus" });
				return;
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(HUDManager), "DisplayTip", (Type[])null, (Type[])null);
			if (methodInfo2 == null)
			{
				return;
			}
			ParameterInfo[] parameters = methodInfo2.GetParameters();
			object[] array = new object[parameters.Length];
			int num = 0;
			for (int i = 0; i < parameters.Length; i++)
			{
				Type parameterType = parameters[i].ParameterType;
				if (parameterType == typeof(string))
				{
					switch (num)
					{
					case 0:
						array[i] = header;
						break;
					case 1:
						array[i] = body;
						break;
					default:
						array[i] = "DoorLockpickPlus";
						break;
					}
					num++;
				}
				else if (parameterType == typeof(bool))
				{
					array[i] = warning;
				}
				else
				{
					array[i] = null;
				}
			}
			methodInfo2.Invoke(val, array);
		}
		catch
		{
		}
	}

	internal static Color YellowHighlight()
	{
		//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_001d: Unknown result type (might be due to invalid IL or missing references)
		return new Color(1f, 0.78f, 0.08f, 1f);
	}

	internal static Color RedHighlight()
	{
		//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_001d: Unknown result type (might be due to invalid IL or missing references)
		return new Color(1f, 0.12f, 0.08f, 1f);
	}
}
internal class NormalDoorLockpickTarget : MonoBehaviour
{
	[CompilerGenerated]
	private sealed class <EndOfFrameUnlockGuard>d__21 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public NormalDoorLockpickTarget <>4__this;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <EndOfFrameUnlockGuard>d__21(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				break;
			case 1:
				<>1__state = -1;
				<>4__this.MaintainDoorUnlockedState();
				break;
			}
			if (<>4__this.unlockedByThisMod && (Object)(object)<>4__this.door != (Object)null)
			{
				<>2__current = (object)new WaitForEndOfFrame();
				<>1__state = 1;
				return true;
			}
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	private DoorLock door;

	private DoorVisualHighlighter highlighter;

	private DoorAudioController audioController;

	private readonly List<InteractTrigger> vanillaTriggers = new List<InteractTrigger>();

	private readonly Dictionary<InteractTrigger, TriggerSnapshot> vanillaSnapshots = new Dictionary<InteractTrigger, TriggerSnapshot>();

	private GameObject customTriggerObject;

	private InteractTrigger customTrigger;

	private float cooldownUntil = 0f;

	private float redFlashUntil = 0f;

	private bool resolvingAttempt = false;

	private bool unlockedByThisMod = false;

	private bool unlockGuardStarted = false;

	private bool unlockMethodsCalled = false;

	private bool wasHoldingLockpick = false;

	public void Init(DoorLock targetDoor)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		door = targetDoor;
		Bounds doorFocusBounds = GetDoorFocusBounds();
		highlighter = ((Component)this).gameObject.AddComponent<DoorVisualHighlighter>();
		highlighter.Init(((Component)this).gameObject, doorFocusBounds, bigDoor: false);
		audioController = ((Component)this).gameObject.AddComponent<DoorAudioController>();
		audioController.Init(door);
		CacheVanillaTriggers();
		CreateCustomTrigger();
	}

	private Bounds GetDoorFocusBounds()
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		Bounds bounds = default(Bounds);
		((Bounds)(ref bounds))..ctor(((Component)this).transform.position, Vector3.one * 2f);
		bool hasBounds = false;
		if ((Object)(object)door != (Object)null && (Object)(object)door.doorTrigger != (Object)null)
		{
			BoundsUtil.EncapsulateTransformBounds(((Component)door.doorTrigger).transform, ref bounds, ref hasBounds);
		}
		if ((Object)(object)door != (Object)null && (Object)(object)door.doorTriggerB != (Object)null)
		{
			BoundsUtil.EncapsulateTransformBounds(((Component)door.doorTriggerB).transform, ref bounds, ref hasBounds);
		}
		if (!hasBounds)
		{
			bounds = BoundsUtil.GetBounds(((Component)this).gameObject);
			return bounds;
		}
		return bounds;
	}

	private void CacheVanillaTriggers()
	{
		TryAddVanillaTrigger(door.doorTrigger);
		TryAddVanillaTrigger(door.doorTriggerB);
		InteractTrigger[] componentsInChildren = ((Component)door).GetComponentsInChildren<InteractTrigger>(true);
		InteractTrigger[] array = componentsInChildren;
		foreach (InteractTrigger trigger in array)
		{
			TryAddVanillaTrigger(trigger);
		}
	}

	private void TryAddVanillaTrigger(InteractTrigger trigger)
	{
		if (!((Object)(object)trigger == (Object)null) && !vanillaTriggers.Contains(trigger))
		{
			vanillaTriggers.Add(trigger);
			vanillaSnapshots[trigger] = new TriggerSnapshot(trigger);
		}
	}

	private void CreateCustomTrigger()
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Expected O, but got Unknown
		//IL_0025: 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)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		Bounds doorFocusBounds = GetDoorFocusBounds();
		customTriggerObject = new GameObject("DoorLockpickPlus_NormalDoor_InteractTrigger");
		customTriggerObject.transform.position = ((Bounds)(ref doorFocusBounds)).center;
		customTriggerObject.transform.rotation = Quaternion.identity;
		BoxCollider val = customTriggerObject.AddComponent<BoxCollider>();
		((Collider)val).isTrigger = true;
		val.size = new Vector3(Mathf.Max(DoorLockpickPlus.NormalDoorTriggerWidth.Value, ((Bounds)(ref doorFocusBounds)).size.x + 1f), Mathf.Max(DoorLockpickPlus.NormalDoorTriggerHeight.Value, ((Bounds)(ref doorFocusBounds)).size.y + 0.8f), Mathf.Max(DoorLockpickPlus.NormalDoorTriggerDepth.Value, ((Bounds)(ref doorFocusBounds)).size.z + 1f));
		InteractTriggerUtil.MakeGameObjectARealInteractTrigger(customTriggerObject);
		customTrigger = customTriggerObject.AddComponent<InteractTrigger>();
		InteractTriggerUtil.ConfigureBaseTrigger(customTrigger);
		InteractTriggerUtil.CopyIconFromTriggers(customTrigger, vanillaTriggers);
		InteractTriggerUtil.AddInteractListener(customTrigger, OnDoorInteract);
		customTriggerObject.SetActive(false);
	}

	private void Update()
	{
		//IL_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)door == (Object)null)
		{
			ClearAll();
			return;
		}
		if (unlockedByThisMod)
		{
			StopLockpickLoopIfNeeded();
			MaintainDoorUnlockedState();
			ConfigureUnlockedVanillaTriggers();
			SetCustomTriggerActive(active: false);
			highlighter.Clear();
			return;
		}
		if (!IsLocked())
		{
			StopLockpickLoopIfNeeded();
			ConfigureUnlockedVanillaTriggers();
			SetCustomTriggerActive(active: false);
			highlighter.Clear();
			return;
		}
		DisableVanillaTriggersWhileLocked();
		UpdateCustomTriggerPosition();
		bool flag = Time.time < cooldownUntil;
		bool flag2 = (Object)(object)customTrigger != (Object)null && customTrigger.isBeingHeldByPlayer;
		ConfigureCustomTrigger(flag);
		UpdateLockpickLoop(flag2 && !flag && !resolvingAttempt);
		if (Time.time < redFlashUntil)
		{
			highlighter.Apply(DoorLockpickPlus.RedHighlight(), 1f, enableLight: true, DoorLockpickPlus.NormalDoorVisualMultiplier.Value);
		}
		else if (flag2 || resolvingAttempt)
		{
			highlighter.Apply(DoorLockpickPlus.YellowHighlight(), 1f, enableLight: true, DoorLockpickPlus.NormalDoorVisualMultiplier.Value);
		}
		else
		{
			highlighter.Clear();
		}
	}

	private void LateUpdate()
	{
		if (unlockedByThisMod)
		{
			MaintainDoorUnlockedState();
		}
	}

	[IteratorStateMachine(typeof(<EndOfFrameUnlockGuard>d__21))]
	private IEnumerator EndOfFrameUnlockGuard()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <EndOfFrameUnlockGuard>d__21(0)
		{
			<>4__this = this
		};
	}

	private void UpdateLockpickLoop(bool shouldPlay)
	{
		if (DoorLockpickPlus.EnableLockpickSounds == null || !DoorLockpickPlus.EnableLockpickSounds.Value)
		{
			StopLockpickLoopIfNeeded();
		}
		else if (shouldPlay)
		{
			if (!wasHoldingLockpick)
			{
				wasHoldingLockpick = true;
				if ((Object)(object)audioController != (Object)null)
				{
					audioController.StartLockpickLoop();
				}
			}
		}
		else
		{
			StopLockpickLoopIfNeeded();
		}
	}

	private void StopLockpickLoopIfNeeded()
	{
		if (wasHoldingLockpick)
		{
			wasHoldingLockpick = false;
			if ((Object)(object)audioController != (Object)null)
			{
				audioController.StopLockpickLoop();
			}
		}
	}

	private void UpdateCustomTriggerPosition()
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: 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_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)customTriggerObject == (Object)null))
		{
			Bounds doorFocusBounds = GetDoorFocusBounds();
			customTriggerObject.transform.position = ((Bounds)(ref doorFocusBounds)).center;
			customTriggerObject.transform.rotation = Quaternion.identity;
			BoxCollider component = customTriggerObject.GetComponent<BoxCollider>();
			if ((Object)(object)component != (Object)null)
			{
				component.size = new Vector3(Mathf.Max(DoorLockpickPlus.NormalDoorTriggerWidth.Value, ((Bounds)(ref doorFocusBounds)).size.x + 1f), Mathf.Max(DoorLockpickPlus.NormalDoorTriggerHeight.Value, ((Bounds)(ref doorFocusBounds)).size.y + 0.8f), Mathf.Max(DoorLockpickPlus.NormalDoorTriggerDepth.Value, ((Bounds)(ref doorFocusBounds)).size.z + 1f));
			}
			InteractTriggerUtil.MakeGameObjectARealInteractTrigger(customTriggerObject);
		}
	}

	private void ConfigureCustomTrigger(bool inCooldown)
	{
		if (!((Object)(object)customTrigger == (Object)null))
		{
			InteractTriggerUtil.ConfigureBaseTrigger(customTrigger);
			customTrigger.interactable = !inCooldown && !resolvingAttempt;
			customTrigger.timeToHold = Mathf.Max(0.1f, DoorLockpickPlus.NormalDoorPickTime.Value);
			customTrigger.hoverTip = (inCooldown ? "Lockpick cooling down..." : "Pick lock");
			customTrigger.holdTip = "Lockpicking...";
			SetCustomTriggerActive(active: true);
		}
	}

	private void SetCustomTriggerActive(bool active)
	{
		if ((Object)(object)customTriggerObject != (Object)null && customTriggerObject.activeSelf != active)
		{
			customTriggerObject.SetActive(active);
		}
	}

	private void DisableVanillaTriggersWhileLocked()
	{
		foreach (InteractTrigger vanillaTrigger in vanillaTriggers)
		{
			if (!((Object)(object)vanillaTrigger == (Object)null))
			{
				vanillaTrigger.interactable = false;
				vanillaTrigger.hoverTip = "Locked";
				vanillaTrigger.holdTip = "Locked";
			}
		}
	}

	private void RestoreVanillaTriggers()
	{
		foreach (KeyValuePair<InteractTrigger, TriggerSnapshot> vanillaSnapshot in vanillaSnapshots)
		{
			if ((Object)(object)vanillaSnapshot.Key != (Object)null)
			{
				vanillaSnapshot.Value.Restore(vanillaSnapshot.Key);
			}
		}
	}

	private void OnDoorInteract(PlayerControllerB player)
	{
		if ((Object)(object)door == (Object)null || (Object)(object)player == (Object)null)
		{
			return;
		}
		StopLockpickLoopIfNeeded();
		if (!IsLocked())
		{
			return;
		}
		if (resolvingAttempt || Time.time < cooldownUntil)
		{
			DoorLockpickPlus.ShowTip("Lockpick", "Wait a moment before trying again.", warning: true);
			return;
		}
		resolvingAttempt = true;
		float num = Mathf.Clamp01(DoorLockpickPlus.NormalDoorSuccessChance.Value);
		float num2 = Random.Range(0f, 1f);
		bool flag = num2 <= num;
		if (DoorLockpickPlus.DebugRollLogging != null && DoorLockpickPlus.DebugRollLogging.Value && DoorLockpickPlus.Log != null)
		{
			DoorLockpickPlus.Log.LogInfo((object)("Normal door lockpick roll: " + num2.ToString("0.000") + " / chance " + num.ToString("0.000") + " / success " + flag));
		}
		if (flag)
		{
			unlockedByThisMod = true;
			unlockMethodsCalled = false;
			CallKeyStyleUnlockOnce();
			MaintainDoorUnlockedState();
			ConfigureUnlockedVanillaTriggers();
			SetCustomTriggerActive(active: false);
			if ((Object)(object)audioController != (Object)null)
			{
				audioController.PlayUnlockSoundOnce();
			}
			if (!unlockGuardStarted)
			{
				unlockGuardStarted = true;
				((MonoBehaviour)this).StartCoroutine(EndOfFrameUnlockGuard());
			}
			DoorLockpickPlus.ShowTip("Lockpick Success", "The door unlocked.", warning: false);
			cooldownUntil = Time.time + 0.5f;
		}
		else
		{
			if ((Object)(object)audioController != (Object)null)
			{
				audioController.PlayFailSoundOnce();
			}
			DoorLockpickPlus.ShowTip("Lockpick Failed", "The lock held. Try again.", warning: true);
			redFlashUntil = Time.time + Mathf.Max(0.1f, DoorLockpickPlus.FailFlashTime.Value);
			cooldownUntil = Time.time + Mathf.Max(0.1f, DoorLockpickPlus.RetryCooldown.Value);
		}
		resolvingAttempt = false;
	}

	private bool IsLocked()
	{
		if ((Object)(object)door == (Object)null)
		{
			return false;
		}
		return door.isLocked;
	}

	private void CallKeyStyleUnlockOnce()
	{
		if (!((Object)(object)door == (Object)null) && !unlockMethodsCalled)
		{
			unlockMethodsCalled = true;
			CallUnlockMethodsOnDoor(door);
			if ((Object)(object)door.twinDoor != (Object)null)
			{
				CallUnlockMethodsOnDoor(door.twinDoor);
			}
		}
	}

	private void CallUnlockMethodsOnDoor(DoorLock targetDoor)
	{
		if (!((Object)(object)targetDoor == (Object)null) && !ReflectionUtil.CallNoArgExact(targetDoor, "UnlockDoorSyncWithServer"))
		{
			ReflectionUtil.CallNoArgExact(targetDoor, "UnlockDoor");
			ReflectionUtil.CallNoArgExact(targetDoor, "UnlockDoorServerRpc");
		}
	}

	private void MaintainDoorUnlockedState()
	{
		MaintainDoorUnlockedStateFor(door);
		if ((Object)(object)door != (Object)null && (Object)(object)door.twinDoor != (Object)null)
		{
			MaintainDoorUnlockedStateFor(door.twinDoor);
		}
	}

	private void MaintainDoorUnlockedStateFor(DoorLock targetDoor)
	{
		if (!((Object)(object)targetDoor == (Object)null))
		{
			targetDoor.isLocked = false;
			targetDoor.isPickingLock = false;
			targetDoor.lockPickTimeLeft = 0f;
			targetDoor.canBeLocked = false;
			if ((Object)(object)targetDoor.doorTrigger != (Object)null)
			{
				targetDoor.doorTrigger.interactable = true;
				targetDoor.doorTrigger.timeToHoldSpeedMultiplier = 1f;
				targetDoor.doorTrigger.timeToHold = targetDoor.defaultTimeToHold;
			}
			if ((Object)(object)targetDoor.doorTriggerB != (Object)null)
			{
				targetDoor.doorTriggerB.interactable = true;
				targetDoor.doorTriggerB.timeToHoldSpeedMultiplier = 1f;
				targetDoor.doorTriggerB.timeToHold = targetDoor.defaultTimeToHold;
			}
		}
	}

	private void ConfigureUnlockedVanillaTriggers()
	{
		ConfigureUnlockedVanillaTriggersFor(door);
		if ((Object)(object)door != (Object)null && (Object)(object)door.twinDoor != (Object)null)
		{
			ConfigureUnlockedVanillaTriggersFor(door.twinDoor);
		}
	}

	private void ConfigureUnlockedVanillaTriggersFor(DoorLock targetDoor)
	{
		if (!((Object)(object)targetDoor == (Object)null))
		{
			if ((Object)(object)targetDoor.doorTrigger != (Object)null)
			{
				targetDoor.doorTrigger.interactable = true;
				targetDoor.doorTrigger.oneHandedItemAllowed = true;
				targetDoor.doorTrigger.twoHandedItemAllowed = true;
				targetDoor.doorTrigger.timeToHoldSpeedMultiplier = 1f;
				targetDoor.doorTrigger.timeToHold = targetDoor.defaultTimeToHold;
			}
			if ((Object)(object)targetDoor.doorTriggerB != (Object)null)
			{
				targetDoor.doorTriggerB.interactable = true;
				targetDoor.doorTriggerB.oneHandedItemAllowed = true;
				targetDoor.doorTriggerB.twoHandedItemAllowed = true;
				targetDoor.doorTriggerB.timeToHoldSpeedMultiplier = 1f;
				targetDoor.doorTriggerB.timeToHold = targetDoor.defaultTimeToHold;
			}
		}
	}

	private void ClearAll()
	{
		StopLockpickLoopIfNeeded();
		RestoreVanillaTriggers();
		if ((Object)(object)customTriggerObject != (Object)null)
		{
			Object.Destroy((Object)(object)customTriggerObject);
			customTriggerObject = null;
		}
		if ((Object)(object)highlighter != (Object)null)
		{
			highlighter.Clear();
		}
	}

	private void OnDestroy()
	{
		ClearAll();
	}
}
internal class BigDoorLockpickTarget : MonoBehaviour
{
	[CompilerGenerated]
	private sealed class <ExecuteBigDoorCommand>d__20 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public bool targetOpen;

		public BigDoorLockpickTarget <>4__this;

		private bool <beforeOpen>5__1;

		private bool <afterTerminal>5__2;

		private bool <actualOpen>5__3;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <ExecuteBigDoorCommand>d__20(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Expected O, but got Unknown
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Expected O, but got Unknown
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				if ((Object)(object)<>4__this.bigDoor == (Object)null)
				{
					<>4__this.resolvingAttempt = false;
					return false;
				}
				<beforeOpen>5__1 = BigDoorUtil.IsOpen(<>4__this.bigDoor);
				if (<beforeOpen>5__1 == targetOpen)
				{
					if (targetOpen)
					{
						DoorLockpickPlus.ShowTip("Big Door", "The big door is already open.", warning: false);
					}
					else
					{
						DoorLockpickPlus.ShowTip("Big Door", "The big door is already closed.", warning: false);
					}
					<>4__this.cooldownUntil = Time.time + 0.5f;
					<>4__this.resolvingAttempt = false;
					return false;
				}
				if (DoorLockpickPlus.DebugRollLogging != null && DoorLockpickPlus.DebugRollLogging.Value && DoorLockpickPlus.Log != null)
				{
					DoorLockpickPlus.Log.LogInfo((object)("Big door command start. targetOpen=" + targetOpen + " beforeOpen=" + <beforeOpen>5__1 + " code=" + BigDoorUtil.GetObjectCode(<>4__this.bigDoor)));
				}
				BigDoorUtil.ClearCooldown(<>4__this.bigDoor);
				BigDoorUtil.SendTerminalCommand(<>4__this.bigDoor);
				<>2__current = (object)new WaitForSeconds(0.35f);
				<>1__state = 1;
				return true;
			case 1:
				<>1__state = -1;
				if ((Object)(object)<>4__this.bigDoor == (Object)null)
				{
					<>4__this.resolvingAttempt = false;
					return false;
				}
				<afterTerminal>5__2 = BigDoorUtil.IsOpen(<>4__this.bigDoor);
				if (<afterTerminal>5__2 != targetOpen)
				{
					BigDoorUtil.ClearCooldown(<>4__this.bigDoor);
					BigDoorUtil.ForceSetOpen(<>4__this.bigDoor, targetOpen);
					<>2__current = (object)new WaitForSeconds(0.2f);
					<>1__state = 2;
					return true;
				}
				goto IL_023f;
			case 2:
				<>1__state = -1;
				goto IL_023f;
			case 3:
				{
					<>1__state = -1;
					break;
				}
				IL_023f:
				if ((Object)(object)<>4__this.bigDoor != (Object)null && BigDoorUtil.IsOpen(<>4__this.bigDoor) != targetOpen)
				{
					BigDoorUtil.ForceSetOpen(<>4__this.bigDoor, targetOpen);
					<>2__current = (object)new WaitForSeconds(0.1f);
					<>1__state = 3;
					return true;
				}
				break;
			}
			<actualOpen>5__3 = (Object)(object)<>4__this.bigDoor != (Object)null && BigDoorUtil.IsOpen(<>4__this.bigDoor);
			if (DoorLockpickPlus.DebugRollLogging != null && DoorLockpickPlus.DebugRollLogging.Value && DoorLockpickPlus.Log != null)
			{
				DoorLockpickPlus.Log.LogInfo((object)("Big door command end. targetOpen=" + targetOpen + " actualOpen=" + <actualOpen>5__3));
			}
			if (<actualOpen>5__3 == targetOpen)
			{
				if (targetOpen)
				{
					DoorLockpickPlus.ShowTip("Pry Success", "The big door opened.", warning: false);
				}
				else
				{
					DoorLockpickPlus.ShowTip("Big Door Closed", "The big door closed.", warning: false);
				}
			}
			else
			{
				DoorLockpickPlus.ShowTip("Door Command Failed", "The command ran but the door did not reach the expected state.", warning: true);
			}
			<>4__this.cooldownUntil = Time.time + 0.5f;
			<>4__this.resolvingAttempt = false;
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	private TerminalAccessibleObject bigDoor;

	private DoorVisualHighlighter highlighter;

	private readonly List<InteractTrigger> triggers = new List<InteractTrigger>();

	private readonly List<GameObject> triggerObjects = new List<GameObject>();

	private float cooldownUntil = 0f;

	private float redFlashUntil = 0f;

	private bool resolvingAttempt = false;

	private float nextTriggerRefreshTime = 0f;

	private int lastBoundsSignature = 0;

	public void Init(TerminalAccessibleObject targetDoor)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		bigDoor = targetDoor;
		Bounds bounds = BoundsUtil.GetBounds(((Component)this).gameObject);
		highlighter = ((Component)this).gameObject.AddComponent<DoorVisualHighlighter>();
		highlighter.Init(((Component)this).gameObject, bounds, bigDoor: true);
		RebuildSurfaceTriggers();
	}

	private void Update()
	{
		//IL_00d5: 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)
		if ((Object)(object)bigDoor == (Object)null || (Object)(object)((Component)bigDoor).gameObject == (Object)null)
		{
			Clear();
			return;
		}
		if (!((Component)bigDoor).gameObject.activeInHierarchy)
		{
			Clear();
			return;
		}
		if (Time.time >= nextTriggerRefreshTime)
		{
			nextTriggerRefreshTime = Time.time + 0.75f;
			RebuildSurfaceTriggersIfNeeded();
		}
		bool isOpen = BigDoorUtil.IsOpen(bigDoor);
		bool inCooldown = Time.time < cooldownUntil;
		bool flag = AnyTriggerHeld();
		UpdateTriggerTransforms();
		ConfigureTriggers(isOpen, inCooldown);
		if (Time.time < redFlashUntil)
		{
			highlighter.Apply(DoorLockpickPlus.RedHighlight(), 1f, enableLight: true, DoorLockpickPlus.BigDoorVisualMultiplier.Value);
		}
		else if (flag || resolvingAttempt)
		{
			highlighter.Apply(DoorLockpickPlus.YellowHighlight(), 1f, enableLight: true, DoorLockpickPlus.BigDoorVisualMultiplier.Value);
		}
		else
		{
			highlighter.Clear();
		}
	}

	private bool AnyTriggerHeld()
	{
		for (int i = 0; i < triggers.Count; i++)
		{
			if ((Object)(object)triggers[i] != (Object)null && triggers[i].isBeingHeldByPlayer)
			{
				return true;
			}
		}
		return false;
	}

	private void RebuildSurfaceTriggersIfNeeded()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		List<Bounds> individualVisualBounds = BoundsUtil.GetIndividualVisualBounds(((Component)this).gameObject, bigDoor: true);
		if (individualVisualBounds.Count == 0)
		{
			individualVisualBounds.Add(BoundsUtil.GetBounds(((Component)this).gameObject));
		}
		int num = Mathf.Clamp(individualVisualBounds.Count, 1, 8);
		int boundsSignature = GetBoundsSignature(individualVisualBounds, num);
		if (num != triggerObjects.Count || boundsSignature != lastBoundsSignature)
		{
			RebuildSurfaceTriggers();
		}
	}

	private int GetBoundsSignature(List<Bounds> boundsList, int count)
	{
		//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)
		//IL_0022: 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_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		int num = 17;
		num = num * 31 + count;
		for (int i = 0; i < count && i < boundsList.Count; i++)
		{
			Bounds val = boundsList[i];
			num = num * 31 + Mathf.RoundToInt(((Bounds)(ref val)).center.x * 10f);
			num = num * 31 + Mathf.RoundToInt(((Bounds)(ref val)).center.y * 10f);
			num = num * 31 + Mathf.RoundToInt(((Bounds)(ref val)).center.z * 10f);
			num = num * 31 + Mathf.RoundToInt(((Bounds)(ref val)).size.x * 10f);
			num = num * 31 + Mathf.RoundToInt(((Bounds)(ref val)).size.y * 10f);
			num = num * 31 + Mathf.RoundToInt(((Bounds)(ref val)).size.z * 10f);
		}
		return num;
	}

	private void RebuildSurfaceTriggers()
	{
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		DestroyTriggers();
		if (!((Object)(object)bigDoor == (Object)null) && !((Object)(object)((Component)bigDoor).gameObject == (Object)null))
		{
			List<Bounds> individualVisualBounds = BoundsUtil.GetIndividualVisualBounds(((Component)this).gameObject, bigDoor: true);
			if (individualVisualBounds.Count == 0)
			{
				individualVisualBounds.Add(BoundsUtil.GetBounds(((Component)this).gameObject));
			}
			int num = Mathf.Clamp(individualVisualBounds.Count, 1, 8);
			lastBoundsSignature = GetBoundsSignature(individualVisualBounds, num);
			for (int i = 0; i < num; i++)
			{
				CreateSurfaceTrigger(i, individualVisualBounds[i]);
			}
			UpdateTriggerTransforms();
		}
	}

	private void CreateSurfaceTrigger(int index, Bounds bounds)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Expected O, but got Unknown
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("DoorLockpickPlus_BigDoor_SurfaceTrigger_" + index);
		if ((Object)(object)bigDoor != (Object)null)
		{
			val.transform.SetParent(((Component)bigDoor).transform, true);
		}
		BoxCollider val2 = val.AddComponent<BoxCollider>();
		((Collider)val2).isTrigger = true;
		ConfigureSurfaceBox(val2, bounds);
		InteractTriggerUtil.MakeGameObjectARealInteractTrigger(val);
		InteractTrigger val3 = val.AddComponent<InteractTrigger>();
		InteractTriggerUtil.ConfigureBaseTrigger(val3);
		InteractTriggerUtil.CopyIconFromScene(val3);
		InteractTriggerUtil.AddInteractListener(val3, OnBigDoorInteract);
		triggerObjects.Add(val);
		triggers.Add(val3);
	}

	private void UpdateTriggerTransforms()
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)bigDoor == (Object)null || (Object)(object)((Component)bigDoor).gameObject == (Object)null)
		{
			return;
		}
		List<Bounds> individualVisualBounds = BoundsUtil.GetIndividualVisualBounds(((Component)this).gameObject, bigDoor: true);
		if (individualVisualBounds.Count == 0)
		{
			individualVisualBounds.Add(BoundsUtil.GetBounds(((Component)this).gameObject));
		}
		int num = Mathf.Min(triggerObjects.Count, individualVisualBounds.Count);
		for (int i = 0; i < num; i++)
		{
			GameObject val = triggerObjects[i];
			if (!((Object)(object)val == (Object)null))
			{
				if ((Object)(object)val.transform.parent != (Object)(object)((Component)bigDoor).transform)
				{
					val.transform.SetParent(((Component)bigDoor).transform, true);
				}
				Transform transform = val.transform;
				Bounds val2 = individualVisualBounds[i];
				transform.position = ((Bounds)(ref val2)).center;
				val.transform.rotation = Quaternion.identity;
				BoxCollider component = val.GetComponent<BoxCollider>();
				if ((Object)(object)component != (Object)null)
				{
					ConfigureSurfaceBox(component, individualVisualBounds[i]);
				}
				InteractTriggerUtil.MakeGameObjectARealInteractTrigger(val);
			}
		}
	}

	private void ConfigureSurfaceBox(BoxCollider box, Bounds bounds)
	{
		//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_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: 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)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)box == (Object)null))
		{
			Vector3 size = ((Bounds)(ref bounds)).size;
			size.x = Mathf.Max(0.6f, size.x);
			size.y = Mathf.Max(1.2f, size.y);
			size.z = Mathf.Max(0.45f, size.z);
			box.center = Vector3.zero;
			box.size = size;
		}
	}

	private void ConfigureTriggers(bool isOpen, bool inCooldown)
	{
		bool flag = false;
		float timeToHold = 1f;
		string hoverTip = "";
		string holdTip = "";
		if (isOpen && DoorLockpickPlus.EnableBigDoorInPersonClose.Value)
		{
			flag = true;
			timeToHold = Mathf.Max(0.1f, DoorLockpickPlus.BigDoorCloseTime.Value);
			hoverTip = (inCooldown ? "Door controls cooling down..." : "Close big door");
			holdTip = "Closing door...";
		}
		else if (!isOpen && DoorLockpickPlus.EnableBigDoorHacking.Value)
		{
			flag = true;
			timeToHold = Mathf.Max(0.1f, DoorLockpickPlus.BigDoorHackTime.Value);
			hoverTip = (inCooldown ? "Pry attempt cooling down..." : "Pry open big door");
			holdTip = "Attempting to pry open...";
		}
		for (int i = 0; i < triggers.Count; i++)
		{
			InteractTrigger val = triggers[i];
			if (!((Object)(object)val == (Object)null))
			{
				InteractTriggerUtil.ConfigureBaseTrigger(val);
				val.interactable = flag && !inCooldown && !resolvingAttempt;
				val.timeToHold = timeToHold;
				val.hoverTip = hoverTip;
				val.holdTip = holdTip;
			}
		}
	}

	private void OnBigDoorInteract(PlayerControllerB player)
	{
		if ((Object)(object)bigDoor == (Object)null || (Object)(object)player == (Object)null)
		{
			resolvingAttempt = false;
			return;
		}
		if (resolvingAttempt || Time.time < cooldownUntil)
		{
			DoorLockpickPlus.ShowTip("Door Controls", "Wait a moment before trying again.", warning: true);
			return;
		}
		resolvingAttempt = true;
		bool flag = BigDoorUtil.IsOpen(bigDoor);
		if (DoorLockpickPlus.DebugRollLogging != null && DoorLockpickPlus.DebugRollLogging.Value && DoorLockpickPlus.Log != null)
		{
			DoorLockpickPlus.Log.LogInfo((object)("Big door interact on " + ((Object)bigDoor).name + " / current open " + flag));
		}
		if (flag)
		{
			((MonoBehaviour)this).StartCoroutine(ExecuteBigDoorCommand(targetOpen: false));
			return;
		}
		float num = Mathf.Clamp01(DoorLockpickPlus.BigDoorSuccessChance.Value);
		float num2 = Random.Range(0f, 1f);
		bool flag2 = num2 <= num;
		if (DoorLockpickPlus.DebugRollLogging != null && DoorLockpickPlus.DebugRollLogging.Value && DoorLockpickPlus.Log != null)
		{
			DoorLockpickPlus.Log.LogInfo((object)("Big door pry roll: " + num2.ToString("0.000") + " / chance " + num.ToString("0.000") + " / success " + flag2));
		}
		if (flag2)
		{
			((MonoBehaviour)this).StartCoroutine(ExecuteBigDoorCommand(targetOpen: true));
			return;
		}
		DoorLockpickPlus.ShowTip("Pry Failed", "You failed to pry it open. Try again.", warning: true);
		redFlashUntil = Time.time + Mathf.Max(0.1f, DoorLockpickPlus.FailFlashTime.Value);
		cooldownUntil = Time.time + Mathf.Max(0.1f, DoorLockpickPlus.RetryCooldown.Value);
		resolvingAttempt = false;
	}

	[IteratorStateMachine(typeof(<ExecuteBigDoorCommand>d__20))]
	private IEnumerator ExecuteBigDoorCommand(bool targetOpen)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <ExecuteBigDoorCommand>d__20(0)
		{
			<>4__this = this,
			targetOpen = targetOpen
		};
	}

	private void DestroyTriggers()
	{
		for (int i = 0; i < triggerObjects.Count; i++)
		{
			if ((Object)(object)triggerObjects[i] != (Object)null)
			{
				Object.Destroy((Object)(object)triggerObjects[i]);
			}
		}
		triggerObjects.Clear();
		triggers.Clear();
	}

	private void Clear()
	{
		resolvingAttempt = false;
		if ((Object)(object)highlighter != (Object)null)
		{
			highlighter.Clear();
		}
		DestroyTriggers();
	}

	private void OnDisable()
	{
		resolvingAttempt = false;
		DestroyTriggers();
	}

	private void OnDestroy()
	{
		Clear();
	}
}
internal class DoorAudioController : MonoBehaviour
{
	private DoorLock door;

	private AudioSource source;

	private AudioClip pickingClip;

	private AudioClip unlockClip;

	private AudioClip failClip;

	public void Init(DoorLock targetDoor)
	{
		door = targetDoor;
		source = ((Component)this).gameObject.GetComponent<AudioSource>();
		if ((Object)(object)source == (Object)null)
		{
			source = ((Component)this).gameObject.AddComponent<AudioSource>();
		}
		source.playOnAwake = false;
		source.loop = false;
		source.spatialBlend = 1f;
		source.rolloffMode = (AudioRolloffMode)1;
		source.minDistance = 1f;
		source.maxDistance = 18f;
		source.dopplerLevel = 0f;
		source.spatialize = false;
		pickingClip = AudioUtil.GetAudioClip(door, new string[3] { "pickingLockSFX", "pickLockSFX", "lockpickSFX" });
		unlockClip = AudioUtil.GetAudioClip(door, new string[3] { "unlockSFX", "unlockDoorSFX", "doorUnlockSFX" });
		failClip = AudioUtil.GetAudioClip(door, new string[3] { "doorLockSFX", "lockedDoorSFX", "lockSFX" });
	}

	public void StartLockpickLoop()
	{
		if (!((Object)(object)source == (Object)null) && !((Object)(object)pickingClip == (Object)null))
		{
			float volume = ((DoorLockpickPlus.LockpickSoundVolume != null) ? Mathf.Clamp01(DoorLockpickPlus.LockpickSoundVolume.Value) : 0.75f);
			if (source.isPlaying && (Object)(object)source.clip == (Object)(object)pickingClip && source.loop)
			{
				source.volume = volume;
				return;
			}
			source.Stop();
			source.clip = pickingClip;
			source.loop = true;
			source.volume = volume;
			source.Play();
		}
	}

	public void StopLockpickLoop()
	{
		if (!((Object)(object)source == (Object)null) && (Object)(object)source.clip == (Object)(object)pickingClip && source.loop)
		{
			source.Stop();
			source.loop = false;
			source.clip = null;
		}
	}

	public void PlayUnlockSoundOnce()
	{
		StopLockpickLoop();
		PlayOneShotClean(unlockClip);
	}

	public void PlayFailSoundOnce()
	{
		StopLockpickLoop();
		PlayOneShotClean(failClip);
	}

	private void PlayOneShotClean(AudioClip clip)
	{
		if (!((Object)(object)source == (Object)null) && !((Object)(object)clip == (Object)null))
		{
			float num = ((DoorLockpickPlus.LockpickSoundVolume != null) ? Mathf.Clamp01(DoorLockpickPlus.LockpickSoundVolume.Value) : 0.75f);
			source.loop = false;
			source.clip = null;
			source.Stop();
			source.PlayOneShot(clip, num);
		}
	}

	private void OnDestroy()
	{
		if ((Object)(object)source != (Object)null)
		{
			source.Stop();
		}
	}
}
internal class DoorVisualHighlighter : MonoBehaviour
{
	private readonly List<Renderer> renderers = new List<Renderer>();

	private readonly Dictionary<Renderer, MaterialPropertyBlock> blocks = new Dictionary<Renderer, MaterialPropertyBlock>();

	private readonly List<GameObject> overlayObjects = new List<GameObject>();

	private readonly List<Material> overlayMaterials = new List<Material>();

	private GameObject lightObject;

	private Light lightSource;

	private bool isBigDoor;

	public void Init(GameObject root, Bounds focusBounds, bool bigDoor)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		isBigDoor = bigDoor;
		CacheRenderers(root, focusBounds, bigDoor);
		CreateOverlayMeshes();
		CreateLight();
		if (DoorLockpickPlus.Log != null)
		{
			string text = (((Object)(object)root != (Object)null) ? ((Object)root).name : "null");
			DoorLockpickPlus.Log.LogInfo((object)("DoorLockpickPlus visual renderers for " + text + ": " + renderers.Count));
		}
	}

	private void CacheRenderers(GameObject root, Bounds focusBounds, bool bigDoor)
	{
		//IL_0037: 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_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Expected O, but got Unknown
		renderers.Clear();
		blocks.Clear();
		if ((Object)(object)root == (Object)null)
		{
			return;
		}
		List<Renderer> list = new List<Renderer>();
		CollectRendererCandidates(root.transform, focusBounds, bigDoor, list);
		if (!bigDoor && list.Count == 0 && (Object)(object)root.transform.parent != (Object)null)
		{
			CollectRendererCandidates(root.transform.parent, focusBounds, bigDoor, list);
		}
		if (!bigDoor && list.Count == 0 && (Object)(object)root.transform.parent != (Object)null && (Object)(object)root.transform.parent.parent != (Object)null)
		{
			CollectRendererCandidates(root.transform.parent.parent, focusBounds, bigDoor, list);
		}
		for (int i = 0; i < list.Count; i++)
		{
			Renderer val = list[i];
			if (!((Object)(object)val == (Object)null) && !renderers.Contains(val))
			{
				renderers.Add(val);
				blocks[val] = new MaterialPropertyBlock();
			}
		}
	}

	private void CollectRendererCandidates(Transform root, Bounds focusBounds, bool bigDoor, List<Renderer> output)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)root == (Object)null || output == null)
		{
			return;
		}
		Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(true);
		Bounds val = focusBounds;
		((Bounds)(ref val)).Expand(bigDoor ? 4f : 5f);
		Renderer[] array = componentsInChildren;
		foreach (Renderer val2 in array)
		{
			if (!RendererUtil.IsVisibleDoorRenderer(val2, bigDoor))
			{
				continue;
			}
			if (!bigDoor && !((Bounds)(ref val)).Intersects(val2.bounds))
			{
				Bounds bounds = val2.bounds;
				float num = Vector3.Distance(((Bounds)(ref bounds)).center, ((Bounds)(ref focusBounds)).center);
				if (num > 5.5f)
				{
					continue;
				}
			}
			if (!output.Contains(val2))
			{
				output.Add(val2);
			}
		}
	}

	private void CreateOverlayMeshes()
	{
		foreach (Renderer renderer in renderers)
		{
			if ((Object)(object)renderer == (Object)null)
			{
				continue;
			}
			MeshFilter component = ((Component)renderer).GetComponent<MeshFilter>();
			if ((Object)(object)component != (Object)null && (Object)(object)component.sharedMesh != (Object)null)
			{
				CreateMeshOverlay(renderer, component.sharedMesh);
				continue;
			}
			SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null);
			if ((Object)(object)val != (Object)null && (Object)(object)val.sharedMesh != (Object)null)
			{
				CreateSkinnedMeshOverlay(val);
			}
		}
	}

	private void CreateMeshOverlay(Renderer originalRenderer, Mesh mesh)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: 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_0085: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("DoorLockpickPlus_OverlayMesh");
		val.transform.SetParent(((Component)originalRenderer).transform, false);
		val.transform.localPosition = Vector3.zero;
		val.transform.localRotation = Quaternion.identity;
		val.transform.localScale = Vector3.one * 1.006f;
		val.layer = ((Component)originalRenderer).gameObject.layer;
		MeshFilter val2 = val.AddComponent<MeshFilter>();
		val2.sharedMesh = mesh;
		MeshRenderer val3 = val.AddComponent<MeshRenderer>();
		Material item = (((Renderer)val3).sharedMaterial = CreateOverlayMaterial(Color.clear));
		((Renderer)val3).shadowCastingMode = (ShadowCastingMode)0;
		((Renderer)val3).receiveShadows = false;
		((Renderer)val3).enabled = false;
		overlayObjects.Add(val);
		overlayMaterials.Add(item);
	}

	private void CreateSkinnedMeshOverlay(SkinnedMeshRenderer originalRenderer)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: 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_009d: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("DoorLockpickPlus_OverlaySkinnedMesh");
		val.transform.SetParent(((Component)originalRenderer).transform, false);
		val.transform.localPosition = Vector3.zero;
		val.transform.localRotation = Quaternion.identity;
		val.transform.localScale = Vector3.one * 1.006f;
		val.layer = ((Component)originalRenderer).gameObject.layer;
		SkinnedMeshRenderer val2 = val.AddComponent<SkinnedMeshRenderer>();
		val2.sharedMesh = originalRenderer.sharedMesh;
		val2.bones = originalRenderer.bones;
		val2.rootBone = originalRenderer.rootBone;
		Material item = (((Renderer)val2).sharedMaterial = CreateOverlayMaterial(Color.clear));
		((Renderer)val2).shadowCastingMode = (ShadowCastingMode)0;
		((Renderer)val2).receiveShadows = false;
		((Renderer)val2).enabled = false;
		overlayObjects.Add(val);
		overlayMaterials.Add(item);
	}

	private Material CreateOverlayMaterial(Color color)
	{
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Expected O, but got Unknown
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		Shader val = Shader.Find("Sprites/Default");
		if ((Object)(object)val == (Object)null)
		{
			val = Shader.Find("Particles/Standard Unlit");
		}
		if ((Object)(object)val == (Object)null)
		{
			val = Shader.Find("Unlit/Color");
		}
		if ((Object)(object)val == (Object)null)
		{
			val = Shader.Find("Standard");
		}
		Material val2 = new Material(val);
		((Object)val2).name = "DoorLockpickPlus_OverlayMaterial";
		val2.renderQueue = 3100;
		if (val2.HasProperty("_SrcBlend"))
		{
			val2.SetInt("_SrcBlend", 5);
		}
		if (val2.HasProperty("_DstBlend"))
		{
			val2.SetInt("_DstBlend", 10);
		}
		if (val2.HasProperty("_ZWrite"))
		{
			val2.SetInt("_ZWrite", 0);
		}
		if (val2.HasProperty("_Cull"))
		{
			val2.SetInt("_Cull", 0);
		}
		SetMaterialColor(val2, color, 1f);
		return val2;
	}

	private void CreateLight()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		lightObject = new GameObject("DoorLockpickPlus_HighlightLight");
		lightObject.transform.SetParent(((Component)this).transform, false);
		lightObject.transform.localPosition = Vector3.zero;
		lightSource = lightObject.AddComponent<Light>();
		lightSource.type = (LightType)2;
		lightSource.range = 4f;
		lightSource.intensity = 0f;
		lightSource.shadows = (LightShadows)0;
		lightSource.renderMode = (LightRenderMode)1;
		((Behaviour)lightSource).enabled = false;
	}

	public void Apply(Color color, float strength, bool enableLight, float multiplier)
	{
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: 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_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: 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_0161: Unknown result type (might be due to invalid IL or missing references)
		//IL_0234: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0259: Unknown result type (might be due to invalid IL or missing references)
		//IL_0268: Unknown result type (might be due to invalid IL or missing references)
		//IL_026d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
		float num = ((DoorLockpickPlus.DoorTintStrength != null) ? Mathf.Clamp01(DoorLockpickPlus.DoorTintStrength.Value) : 0.55f);
		float num2 = ((DoorLockpickPlus.DoorOverlayAlpha != null) ? Mathf.Clamp01(DoorLockpickPlus.DoorOverlayAlpha.Value) : 0.18f);
		float num3 = ((DoorLockpickPlus.DoorEmissionStrength != null) ? Mathf.Max(0f, DoorLockpickPlus.DoorEmissionStrength.Value) : 1.15f);
		float num4 = ((DoorLockpickPlus.DoorLightStrength != null) ? Mathf.Max(0f, DoorLockpickPlus.DoorLightStrength.Value) : 0.55f);
		float num5 = Mathf.Max(0f, multiplier);
		Color val = Color.Lerp(Color.white, color, num * num5);
		Color val2 = color * num3 * strength * num5;
		foreach (Renderer renderer in renderers)
		{
			if (!((Object)(object)renderer == (Object)null))
			{
				MaterialPropertyBlock val3 = blocks[renderer];
				renderer.GetPropertyBlock(val3);
				val3.SetColor("_Color", val);
				val3.SetColor("_BaseColor", val);
				val3.SetColor("_EmissionColor", val2);
				val3.SetColor("_EmissiveColor", val2);
				renderer.SetPropertyBlock(val3);
			}
		}
		Color color2 = color;
		color2.a = num2 * num5;
		for (int i = 0; i < overlayObjects.Count; i++)
		{
			GameObject val4 = overlayObjects[i];
			if (!((Object)(object)val4 == (Object)null))
			{
				Renderer component = val4.GetComponent<Renderer>();
				if ((Object)(object)component != (Object)null)
				{
					component.enabled = true;
				}
				if (i < overlayMaterials.Count && (Object)(object)overlayMaterials[i] != (Object)null)
				{
					SetMaterialColor(overlayMaterials[i], color2, num5);
				}
			}
		}
		if ((Object)(object)lightSource != (Object)null)
		{
			Bounds acceptedRendererBounds = GetAcceptedRendererBounds();
			((Component)lightSource).transform.position = ((Bounds)(ref acceptedRendererBounds)).center;
			lightSource.color = color;
			Light obj = lightSource;
			Vector3 extents = ((Bounds)(ref acceptedRendererBounds)).extents;
			obj.range = Mathf.Clamp(((Vector3)(ref extents)).magnitude * (isBigDoor ? 0.75f : 1.05f), 1.5f, isBigDoor ? 5.5f : 7f);
			lightSource.intensity = (enableLight ? Mathf.Clamp(num4 * strength * num5, 0f, isBigDoor ? 1.2f : 2.5f) : 0f);
			((Behaviour)lightSource).enabled = enableLight && renderers.Count > 0;
		}
	}

	public void Clear()
	{
		foreach (Renderer renderer in renderers)
		{
			if (!((Object)(object)renderer == (Object)null))
			{
				renderer.SetPropertyBlock((MaterialPropertyBlock)null);
			}
		}
		for (int i = 0; i < overlayObjects.Count; i++)
		{
			GameObject val = overlayObjects[i];
			if (!((Object)(object)val == (Object)null))
			{
				Renderer component = val.GetComponent<Renderer>();
				if ((Object)(object)component != (Object)null)
				{
					component.enabled = false;
				}
			}
		}
		if ((Object)(object)lightSource != (Object)null)
		{
			((Behaviour)lightSource).enabled = false;
			lightSource.intensity = 0f;
		}
	}

	private Bounds GetAcceptedRendererBounds()
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: 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)
		Bounds bounds = default(Bounds);
		((Bounds)(ref bounds))..ctor(((Component)this).transform.position, Vector3.one);
		bool flag = false;
		for (int i = 0; i < renderers.Count; i++)
		{
			Renderer val = renderers[i];
			if (!((Object)(object)val == (Object)null))
			{
				if (!flag)
				{
					bounds = val.bounds;
					flag = true;
				}
				else
				{
					((Bounds)(ref bounds)).Encapsulate(val.bounds);
				}
			}
		}
		return bounds;
	}

	private void SetMaterialColor(Material mat, Color color, float multiplier)
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)mat == (Object)null))
		{
			if (mat.HasProperty("_Color"))
			{
				mat.SetColor("_Color", color);
			}
			if (mat.HasProperty("_BaseColor"))
			{
				mat.SetColor("_BaseColor", color);
			}
			if (mat.HasProperty("_EmissionColor"))
			{
				mat.SetColor("_EmissionColor", color * (1f + multiplier));
			}
		}
	}

	private void OnDestroy()
	{
		Clear();
		for (int i = 0; i < overlayObjects.Count; i++)
		{
			if ((Object)(object)overlayObjects[i] != (Object)null)
			{
				Object.Destroy((Object)(object)overlayObjects[i]);
			}
		}
		overlayObjects.Clear();
		for (int j = 0; j < overlayMaterials.Count; j++)
		{
			if ((Object)(object)overlayMaterials[j] != (Object)null)
			{
				Object.Destroy((Object)(object)overlayMaterials[j]);
			}
		}
		overlayMaterials.Clear();
		if ((Object)(object)lightObject != (Object)null)
		{
			Object.Destroy((Object)(object)lightObject);
			lightObject = null;
		}
	}
}
internal class TriggerSnapshot
{
	private readonly string hoverTip;

	private readonly string holdTip;

	private readonly bool interactable;

	private readonly bool holdInteraction;

	private readonly float timeToHold;

	private readonly float timeToHoldSpeedMultiplier;

	private readonly bool oneHandedItemAllowed;

	private readonly bool twoHandedItemAllowed;

	private readonly bool interactCooldown;

	private readonly float cooldownTime;

	private readonly bool triggerOnce;

	public TriggerSnapshot(InteractTrigger trigger)
	{
		hoverTip = trigger.hoverTip;
		holdTip = trigger.holdTip;
		interactable = trigger.interactable;
		holdInteraction = trigger.holdInteraction;
		timeToHold = trigger.timeToHold;
		timeToHoldSpeedMultiplier = trigger.timeToHoldSpeedMultiplier;
		oneHandedItemAllowed = trigger.oneHandedItemAllowed;
		twoHandedItemAllowed = trigger.twoHandedItemAllowed;
		interactCooldown = trigger.interactCooldown;
		cooldownTime = trigger.cooldownTime;
		triggerOnce = trigger.triggerOnce;
	}

	public void Restore(InteractTrigger trigger)
	{
		trigger.hoverTip = hoverTip;
		trigger.holdTip = holdTip;
		trigger.interactable = interactable;
		trigger.holdInteraction = holdInteraction;
		trigger.timeToHold = timeToHold;
		trigger.timeToHoldSpeedMultiplier = timeToHoldSpeedMultiplier;
		trigger.oneHandedItemAllowed = oneHandedItemAllowed;
		trigger.twoHandedItemAllowed = twoHandedItemAllowed;
		trigger.interactCooldown = interactCooldown;
		trigger.cooldownTime = cooldownTime;
		trigger.triggerOnce = triggerOnce;
	}
}
internal static class InteractTriggerUtil
{
	private static readonly FieldInfo onInteractField = AccessTools.Field(typeof(InteractTrigger), "onInteract");

	private static readonly FieldInfo onInteractEarlyField = AccessTools.Field(typeof(InteractTrigger), "onInteractEarly");

	private static readonly FieldInfo onStopInteractField = AccessTools.Field(typeof(InteractTrigger), "onStopInteract");

	private static readonly FieldInfo holdingInteractEventField = AccessTools.Field(typeof(InteractTrigger), "holdingInteractEvent");

	private static readonly FieldInfo onCancelAnimationField = AccessTools.Field(typeof(InteractTrigger), "onCancelAnimation");

	public static void MakeGameObjectARealInteractTrigger(GameObject obj)
	{
		if (!((Object)(object)obj == (Object)null))
		{
			TrySetTag(obj, "InteractTrigger");
			obj.layer = LayerUtil.InteractableLayer();
			Collider component = obj.GetComponent<Collider>();
			if ((Object)(object)component != (Object)null)
			{
				component.isTrigger = true;
			}
			Renderer component2 = obj.GetComponent<Renderer>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.enabled = false;
			}
		}
	}

	private static void TrySetTag(GameObject obj, string tag)
	{
		try
		{
			obj.tag = tag;
		}
		catch
		{
		}
	}

	public static void ConfigureBaseTrigger(InteractTrigger trigger)
	{
		if (!((Object)(object)trigger == (Object)null))
		{
			MakeGameObjectARealInteractTrigger(((Component)trigger).gameObject);
			trigger.interactable = true;
			trigger.oneHandedItemAllowed = true;
			trigger.twoHandedItemAllowed = true;
			trigger.holdInteraction = true;
			trigger.timeToHoldSpeedMultiplier = 1f;
			trigger.interactCooldown = true;
			trigger.cooldownTime = 0.5f;
			trigger.triggerOnce = false;
			trigger.stopAnimationString = "SA_stopAnimation";
			trigger.animationWaitTime = 2f;
			trigger.lockPlayerPosition = true;
			trigger.disableTriggerMesh = true;
			EnsureEventObject(trigger, onInteractField);
			EnsureEventObject(trigger, onInteractEarlyField);
			EnsureEventObject(trigger, onStopInteractField);
			EnsureEventObject(trigger, holdingInteractEventField);
			EnsureEventObject(trigger, onCancelAnimationField);
			CopyIconFromScene(trigger);
		}
	}

	public static void CopyIconFromTriggers(InteractTrigger target, List<InteractTrigger> templates)
	{
		if ((Object)(object)target == (Object)null)
		{
			return;
		}
		if (templates != null)
		{
			for (int i = 0; i < templates.Count; i++)
			{
				InteractTrigger val = templates[i];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)target))
				{
					if ((Object)(object)val.hoverIcon != (Object)null)
					{
						target.hoverIcon = val.hoverIcon;
					}
					if ((Object)(object)val.disabledHoverIcon != (Object)null)
					{
						target.disabledHoverIcon = val.disabledHoverIcon;
					}
					if ((Object)(object)target.hoverIcon != (Object)null)
					{
						return;
					}
				}
			}
		}
		CopyIconFromScene(target);
	}

	public static void CopyIconFromScene(InteractTrigger target)
	{
		if ((Object)(object)target == (Object)null || (Object)(object)target.hoverIcon != (Object)null)
		{
			return;
		}
		InteractTrigger[] array = Object.FindObjectsOfType<InteractTrigger>();
		foreach (InteractTrigger val in array)
		{
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)target) && (Object)(object)val.hoverIcon != (Object)null)
			{
				target.hoverIcon = val.hoverIcon;
				target.disabledHoverIcon = (((Object)(object)val.disabledHoverIcon != (Object)null) ? val.disabledHoverIcon : val.hoverIcon);
				break;
			}
		}
	}

	private static object EnsureEventObject(InteractTrigger trigger, FieldInfo field)
	{
		try
		{
			if ((Object)(object)trigger == (Object)null || field == null)
			{
				return null;
			}
			object obj = field.GetValue(trigger);
			if (obj == null)
			{
				obj = Activator.CreateInstance(field.FieldType);
				field.SetValue(trigger, obj);
			}
			return obj;
		}
		catch
		{
			return null;
		}
	}

	public static void AddInteractListener(InteractTrigger trigger, Action<PlayerControllerB> callback)
	{
		try
		{
			if ((Object)(object)trigger == (Object)null || callback == null || onInteractField == null)
			{
				return;
			}
			object obj = EnsureEventObject(trigger, onInteractField);
			if (obj == null)
			{
				return;
			}
			UnityAction<PlayerControllerB> val = callback.Invoke;
			MethodInfo method = obj.GetType().GetMethod("AddListener", new Type[1] { typeof(UnityAction<PlayerControllerB>) });
			if (method != null)
			{
				method.Invoke(obj, new object[1] { val });
				return;
			}
			MethodInfo[] methods = obj.GetType().GetMethods();
			foreach (MethodInfo methodInfo in methods)
			{
				if (!(methodInfo.Name != "AddListener"))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == 1)
					{
						methodInfo.Invoke(obj, new object[1] { val });
						break;
					}
				}
			}
		}
		catch (Exception ex)
		{
			if (DoorLockpickPlus.Log != null)
			{
				DoorLockpickPlus.Log.LogWarning((object)("Failed to add InteractTrigger listener: " + ex.Message));
			}
		}
	}
}
internal static class BigDoorUtil
{
	public static bool IsProbablyBigDoor(TerminalAccessibleObject obj)
	{
		if ((Object)(object)obj == (Object)null)
		{
			return false;
		}
		if (obj.isBigDoor)
		{
			return true;
		}
		string text = ((Object)((Component)obj).gameObject).name.ToLowerInvariant();
		if (text.Contains("bigdoor") || text.Contains("big door"))
		{
			return true;
		}
		Transform parent = ((Component)obj).transform.parent;
		while ((Object)(object)parent != (Object)null)
		{
			string text2 = ((Object)parent).name.ToLowerInvariant();
			if (text2.Contains("bigdoor") || text2.Contains("big door"))
			{
				return true;
			}
			parent = parent.parent;
		}
		return false;
	}

	public static bool IsOpen(TerminalAccessibleObject obj)
	{
		if ((Object)(object)obj == (Object)null)
		{
			return false;
		}
		return ReflectionUtil.GetBoolFieldExact(obj, "isDoorOpen");
	}

	public static string GetObjectCode(TerminalAccessibleObject obj)
	{
		if ((Object)(object)obj == (Object)null)
		{
			return "";
		}
		try
		{
			if (!string.IsNullOrEmpty(obj.objectCode))
			{
				return obj.objectCode;
			}
		}
		catch
		{
		}
		return "";
	}

	public static void ClearCooldown(TerminalAccessibleObject obj)
	{
		if (!((Object)(object)obj == (Object)null))
		{
			ReflectionUtil.SetBoolFieldExact(obj, "inCooldown", value: false);
			ReflectionUtil.SetFloatFieldExact(obj, "currentCooldownTimer", 0f);
		}
	}

	public static void SendTerminalCommand(TerminalAccessibleObject obj)
	{
		if (!((Object)(object)obj == (Object)null))
		{
			ClearCooldown(obj);
			ReflectionUtil.CallNoArgExact(obj, "CallFunctionFromTerminal");
		}
	}

	public static void ForceSetOpen(TerminalAccessibleObject obj, bool open)
	{
		if (!((Object)(object)obj == (Object)null))
		{
			ClearCooldown(obj);
			ReflectionUtil.SetBoolFieldExact(obj, "isDoorOpen", open);
			ReflectionUtil.CallBoolArgExact(obj, "SetDoorOpen", open);
			ReflectionUtil.CallBoolArgExact(obj, "SetDoorLocalClient", open);
			ReflectionUtil.CallBoolArgExact(obj, "SetDoorOpenClientRpc", open);
			ReflectionUtil.CallBoolArgExact(obj, "SetDoorOpenServerRpc", open);
			ReflectionUtil.SetBoolFieldExact(obj, "isDoorOpen", open);
		}
	}
}
internal static class AudioUtil
{
	public static AudioClip GetAudioClip(object instance, string[] fieldNames)
	{
		if (instance == null || fieldNames == null)
		{
			return null;
		}
		for (int i = 0; i < fieldNames.Length; i++)
		{
			AudioClip fieldValueExact = ReflectionUtil.GetFieldValueExact<AudioClip>(instance, fieldNames[i]);
			if ((Object)(object)fieldValueExact != (Object)null)
			{
				return fieldValueExact;
			}
		}
		return null;
	}
}
internal static class RendererUtil
{
	public static bool IsVisibleDoorRenderer(Renderer renderer, bool bigDoor)
	{
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)renderer == (Object)null)
		{
			return false;
		}
		if (!renderer.enabled || !((Component)renderer).gameObject.activeInHierarchy)
		{
			return false;
		}
		string lowerName = ((Object)((Component)renderer).gameObject).name.ToLowerInvariant();
		string text = GetPath(((Component)renderer).transform).ToLowerInvariant();
		string text2 = ((object)renderer).GetType().Name.ToLowerInvariant();
		if (text2.Contains("particle") || text2.Contains("line") || text2.Contains("trail"))
		{
			return false;
		}
		if (ShouldSkipVisualName(lowerName) || ShouldSkipVisualName(text))
		{
			return false;
		}
		Bounds bounds = renderer.bounds;
		Vector3 size = ((Bounds)(ref bounds)).size;
		if (((Vector3)(ref size)).sqrMagnitude <= 0.0001f)
		{
			return false;
		}
		MeshFilter component = ((Component)renderer).GetComponent<MeshFilter>();
		SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null);
		if (((Object)(object)component == (Object)null || (Object)(object)component.sharedMesh == (Object)null) && ((Object)(object)val == (Object)null || (Object)(object)val.sharedMesh == (Object)null))
		{
			return false;
		}
		if (bigDoor && (text.Contains("doorcode") || text.Contains("map") || text.Contains("screen") || text.Contains("ui")))
		{
			return false;
		}
		return true;
	}

	private static bool ShouldSkipVisualName(string lowerName)
	{
		if (string.IsNullOrEmpty(lowerName))
		{
			return false;
		}
		return lowerName.Contains("trigger") || lowerName.Contains("collider") || lowerName.Contains("collision") || lowerName.Contains("hitbox") || lowerName.Contains("lockpickplus") || lowerName.Contains("interact") || lowerName.Contains("scan") || lowerName.Contains("node") || lowerName.Contains("radar") || lowerName.Contains("code") || lowerName.Contains("ui") || lowerName.Contains("audio") || lowerName.Contains("light") || lowerName.Contains("particle") || lowerName.Contains("spark") || lowerName.Contains("nav") || lowerName.Contains("bounds");
	}

	private static string GetPath(Transform transform)
	{
		if ((Object)(object)transform == (Object)null)
		{
			return "";
		}
		string text = ((Object)transform).name;
		Transform parent = transform.parent;
		int num = 0;
		while ((Object)(object)parent != (Object)null && num < 12)
		{
			text = ((Object)parent).name + "/" + text;
			parent = parent.parent;
			num++;
		}
		return text;
	}
}
internal static class LayerUtil
{
	public static int InteractableLayer()
	{
		int num = LayerMask.NameToLayer("InteractableObject");
		if (num >= 0)
		{
			return num;
		}
		num = LayerMask.NameToLayer("Interactable");
		if (num >= 0)
		{
			return num;
		}
		return 0;
	}
}
internal static class BoundsUtil
{
	public static Bounds GetBounds(GameObject root)
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: 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_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0171: Unknown result type (might be due to invalid IL or missing references)
		//IL_014d: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: 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_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)root == (Object)null)
		{
			return new Bounds(Vector3.zero, Vector3.one);
		}
		Bounds bounds = default(Bounds);
		((Bounds)(ref bounds))..ctor(root.transform.position, Vector3.one);
		bool flag = false;
		Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true);
		Renderer[] array = componentsInChildren;
		foreach (Renderer val in array)
		{
			if (RendererUtil.IsVisibleDoorRenderer(val, bigDoor: true) || RendererUtil.IsVisibleDoorRenderer(val, bigDoor: false))
			{
				if (!flag)
				{
					bounds = val.bounds;
					flag = true;
				}
				else
				{
					((Bounds)(ref bounds)).Encapsulate(val.bounds);
				}
			}
		}
		if (flag)
		{
			return bounds;
		}
		Collider[] componentsInChildren2 = root.GetComponentsInChildren<Collider>(true);
		Collider[] array2 = componentsInChildren2;
		foreach (Collider val2 in array2)
		{
			if ((Object)(object)val2 == (Object)null)
			{
				continue;
			}
			string lowerName = ((Object)((Component)val2).gameObject).name.ToLowerInvariant();
			if (!ShouldSkipBoundsName(lowerName))
			{
				if (!flag)
				{
					bounds = val2.bounds;
					flag = true;
				}
				else
				{
					((Bounds)(ref bounds)).Encapsulate(val2.bounds);
				}
			}
		}
		if (flag)
		{
			return bounds;
		}
		return new Bounds(root.transform.position, Vector3.one * 3f);
	}

	public static List<Bounds> GetIndividualVisualBounds(GameObject root, bool bigDoor)
	{
		//IL_0049: 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_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: 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_00c2: Unknown result type (might be due to invalid IL or missing references)
		List<Bounds> list = new List<Bounds>();
		if ((Object)(object)root == (Object)null)
		{
			return list;
		}
		Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true);
		Renderer[] array = componentsInChildren;
		Vector3 size;
		foreach (Renderer val in array)
		{
			if (RendererUtil.IsVisibleDoorRenderer(val, bigDoor))
			{
				Bounds bounds = val.bounds;
				size = ((Bounds)(ref bounds)).size;
				if (!(((Vector3)(ref size)).sqrMagnitude <= 0.0001f))
				{
					list.Add(bounds);
				}
			}
		}
		if (list.Count == 0)
		{
			Bounds bounds2 = GetBounds(root);
			size = ((Bounds)(ref bounds2)).size;
			if (((Vector3)(ref size)).sqrMagnitude > 0.0001f)
			{
				list.Add(bounds2);
			}
		}
		return list;
	}

	public static void EncapsulateTransformBounds(Transform transform, ref Bounds bounds, ref bool hasBounds)
	{
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: 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_007c: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)transform == (Object)null)
		{
			return;
		}
		Collider component = ((Component)transform).GetComponent<Collider>();
		if ((Object)(object)component != (Object)null)
		{
			if (!hasBounds)
			{
				bounds = component.bounds;
				hasBounds = true;
			}
			else
			{
				((Bounds)(ref bounds)).Encapsulate(component.bounds);
			}
			return;
		}
		Renderer component2 = ((Component)transform).GetComponent<Renderer>();
		if ((Object)(object)component2 != (Object)null)
		{
			if (!hasBounds)
			{
				bounds = component2.bounds;
				hasBounds = true;
			}
			else
			{
				((Bounds)(ref bounds)).Encapsulate(component2.bounds);
			}
		}
	}

	private static bool ShouldSkipBoundsName(string lowerName)
	{
		if (string.IsNullOrEmpty(lowerName))
		{
			return false;
		}
		return lowerName.Contains("trigger") || lowerName.Contains("lockpickplus") || lowerName.Contains("interact") || lowerName.Contains("scan") || lowerName.Contains("node") || lowerName.Contains("ui") || lowerName.Contains("audio") || lowerName.Contains("light");
	}
}
internal static class ReflectionUtil
{
	public static T GetFieldValueExact<T>(object instance, string fieldName) where T : class
	{
		try
		{
			if (instance == null)
			{
				return null;
			}
			FieldInfo field = instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null)
			{
				return null;
			}
			object value = field.GetValue(instance);
			return value as T;
		}
		catch
		{
			return null;
		}
	}

	public static bool GetBoolFieldExact(object instance, string fieldName)
	{
		try
		{
			if (instance == null)
			{
				return false;
			}
			FieldInfo field = instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null || field.FieldType != typeof(bool))
			{
				return false;
			}
			object value = field.GetValue(instance);
			if (value is bool)
			{
				return (bool)value;
			}
		}
		catch
		{
		}
		return false;
	}

	public static void SetBoolFieldExact(object instance, string fieldName, bool value)
	{
		try
		{
			if (instance != null)
			{
				FieldInfo field = instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (!(field == null) && !(field.FieldType != typeof(bool)))
				{
					field.SetValue(instance, value);
				}
			}
		}
		catch
		{
		}
	}

	public static void SetFloatFieldExact(object instance, string fieldName, float value)
	{
		try
		{
			if (instance != null)
			{
				FieldInfo field = instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (!(field == null) && !(field.FieldType != typeof(float)))
				{
					field.SetValue(instance, value);
				}
			}
		}
		catch
		{
		}
	}

	public static bool CallNoArgExact(object instance, string methodName)
	{
		try
		{
			if (instance == null)
			{
				return false;
			}
			MethodInfo method = instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (method == null || method.GetParameters().Length != 0)
			{
				return false;
			}
			method.Invoke(instance, null);
			return true;
		}
		catch
		{
			return false;
		}
	}

	public static bool CallBoolArgExact(object instance, string methodName, bool value)
	{
		try
		{
			if (instance == null)
			{
				return false;
			}
			MethodInfo method = instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (method == null)
			{
				return false;
			}
			ParameterInfo[] parameters = method.GetParameters();
			if (parameters.Length != 1 || parameters[0].ParameterType != typeof(bool))
			{
				return false;
			}
			method.Invoke(instance, new object[1] { value });
			return true;
		}
		catch
		{
			return false;
		}
	}
}

plugins/Extraction1/Extraction.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ScrapVisbility")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ScrapVisbility")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("8a6853bd-bdc9-4741-95c7-5aa2c8c6a6f9")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Extraction;

[BepInPlugin("robert.lethalcompany.extraction", "Extraction", "0.2.49")]
public sealed class Plugin : BaseUnityPlugin
{
	private struct CubeShape
	{
		public Vector3 Center;

		public Vector3 Size;

		public CubeShape(Vector3 center, Vector3 size)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			Center = center;
			Size = size;
		}
	}

	private struct DynamicBoundarySegment
	{
		public int Axis;

		public int Sign;

		public float FixedCoord;

		public float FromCoord;

		public float ToCoord;

		public DynamicBoundarySegment(int axis, int sign, float fixedCoord, float fromCoord, float toCoord)
		{
			Axis = axis;
			Sign = sign;
			FixedCoord = fixedCoord;
			FromCoord = fromCoord;
			ToCoord = toCoord;
		}
	}

	private struct ShipDoorAudioState
	{
		internal readonly bool Mute;

		internal readonly float Volume;

		internal ShipDoorAudioState(bool mute, float volume)
		{
			Mute = mute;
			Volume = volume;
		}
	}

	private sealed class ScrapGlowState
	{
		private readonly GrabbableObject _item;

		private readonly Dictionary<Renderer, MaterialPropertyBlock> _originalBlocks = new Dictionary<Renderer, MaterialPropertyBlock>();

		private GameObject _lightObject;

		internal ScrapGlowState(GrabbableObject item)
		{
			_item = item;
		}

		internal void Apply()
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: 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_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Expected O, but got Unknown
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_item == (Object)null)
			{
				return;
			}
			Renderer[] componentsInChildren = ((Component)_item).GetComponentsInChildren<Renderer>(true);
			Color val = default(Color);
			((Color)(ref val))..ctor(1f, 0.03f, 0.02f, 1f);
			float num = Mathf.Max(0f, (_cubeScrapGlowEmissionStrength != null) ? _cubeScrapGlowEmissionStrength.Value : 4f);
			Color val2 = val * Mathf.Max(1f, num);
			Renderer[] array = componentsInChildren;
			foreach (Renderer val3 in array)
			{
				if (!((Object)(object)val3 == (Object)null) && val3.enabled && !(val3 is ParticleSystemRenderer))
				{
					MaterialPropertyBlock val4 = new MaterialPropertyBlock();
					val3.GetPropertyBlock(val4);
					_originalBlocks[val3] = val4;
					MaterialPropertyBlock val5 = new MaterialPropertyBlock();
					val3.GetPropertyBlock(val5);
					val5.SetColor("_Color", val);
					val5.SetColor("_BaseColor", val);
					val5.SetColor("_TintColor", val);
					val5.SetColor("_MainColor", val);
					val5.SetColor("_EmissionColor", val2);
					val5.SetColor("_EmissiveColor", val2);
					val5.SetFloat("_UseEmissiveIntensity", 1f);
					val5.SetFloat("_EmissiveIntensity", num);
					val3.SetPropertyBlock(val5);
				}
			}
			float num2 = Mathf.Max(0f, (_cubeScrapGlowLightIntensity != null) ? _cubeScrapGlowLightIntensity.Value : 1.25f);
			if (num2 > 0f)
			{
				_lightObject = new GameObject("Extraction_CubeScrap_RedGlowLight");
				_lightObject.transform.SetParent(((Component)_item).transform, false);
				_lightObject.transform.localPosition = Vector3.up * 0.15f;
				Light val6 = _lightObject.AddComponent<Light>();
				val6.type = (LightType)2;
				val6.color = val;
				val6.intensity = num2;
				val6.range = Mathf.Max(0.25f, (_cubeScrapGlowLightRange != null) ? _cubeScrapGlowLightRange.Value : 2.25f);
				val6.shadows = (LightShadows)0;
				val6.renderMode = (LightRenderMode)1;
			}
		}

		internal void Restore()
		{
			foreach (KeyValuePair<Renderer, MaterialPropertyBlock> originalBlock in _originalBlocks)
			{
				if ((Object)(object)originalBlock.Key != (Object)null)
				{
					originalBlock.Key.SetPropertyBlock(originalBlock.Value);
				}
			}
			_originalBlocks.Clear();
			if ((Object)(object)_lightObject != (Object)null)
			{
				Object.Destroy((Object)(object)_lightObject);
				_lightObject = null;
			}
		}
	}

	internal sealed class ScrapValueHighlighter : MonoBehaviour
	{
		private GrabbableObject item;

		private GameObject textObject;

		private TextMesh textMesh;

		private MeshRenderer textRenderer;

		private Material textMaterial;

		private GameObject lightObject;

		private Light glowLight;

		private readonly List<Renderer> targetRenderers = new List<Renderer>();

		private readonly Dictionary<Renderer, MaterialPropertyBlock> blocks = new Dictionary<Renderer, MaterialPropertyBlock>();

		private int lastValue = -999999;

		private string lastName = string.Empty;

		private Color lastColor = Color.clear;

		private bool textVisible;

		private float lastGoodTextVisibleTime;

		private static readonly FieldInfo fieldIsHeld = typeof(GrabbableObject).GetField("isHeld", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo fieldIsPocketed = typeof(GrabbableObject).GetField("isPocketed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo fieldIsHeldByEnemy = typeof(GrabbableObject).GetField("isHeldByEnemy", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo fieldPlayerHeldBy = typeof(GrabbableObject).GetField("playerHeldBy", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo fieldCurrentlyHeldObjectServer = typeof(PlayerControllerB).GetField("currentlyHeldObjectServer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly FieldInfo fieldCurrentlyHeldObject = typeof(PlayerControllerB).GetField("currentlyHeldObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal void Init(GrabbableObject grabbable)
		{
			item = grabbable;
			CacheTargetRenderers();
			CreateText();
			CreateApparatusLight();
			SetVisualsActive(active: false);
		}

		private void CacheTargetRenderers()
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			targetRenderers.Clear();
			blocks.Clear();
			if ((Object)(object)item == (Object)null)
			{
				return;
			}
			Renderer[] componentsInChildren = ((Component)item).GetComponentsInChildren<Renderer>(true);
			Renderer[] array = componentsInChildren;
			foreach (Renderer val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					string lowerName = ((Object)((Component)val).gameObject).name.ToLowerInvariant();
					if (!ShouldSkipRenderer(val, lowerName))
					{
						targetRenderers.Add(val);
						blocks[val] = new MaterialPropertyBlock();
					}
				}
			}
		}

		private bool ShouldSkipRenderer(Renderer renderer, string lowerName)
		{
			if ((Object)(object)renderer == (Object)null)
			{
				return true;
			}
			if (!renderer.enabled)
			{
				return true;
			}
			if (string.IsNullOrEmpty(lowerName))
			{
				lowerName = ((Object)((Component)renderer).gameObject).name.ToLowerInvariant();
			}
			string text = ((object)renderer).GetType().Name.ToLowerInvariant();
			if (text.Contains("particle") || text.Contains("trail") || text.Contains("line"))
			{
				return true;
			}
			if (lowerName.Contains("scan") || lowerName.Contains("scannode") || lowerName.Contains("scan node") || lowerName.Contains("trigger") || lowerName.Contains("collider") || lowerName.Contains("collision") || lowerName.Contains("hitbox") || lowerName.Contains("interact") || lowerName.Contains("scrapvisibility") || lowerName.Contains("mapdot") || lowerName.Contains("radar") || lowerName.Contains("audio") || lowerName.Contains("sound") || lowerName.Contains("light") || lowerName.Contains("vfx") || lowerName.Contains("sfx"))
			{
				return true;
			}
			return false;
		}

		private void CreateText()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Expected O, but got Unknown
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Expected O, but got Unknown
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)textObject != (Object)null)
			{
				return;
			}
			textObject = new GameObject("ScrapVisibility_3DText");
			textObject.transform.SetParent(GetScrapTextRoot(), false);
			textObject.transform.position = ((Component)this).transform.position + Vector3.up * 0.5f;
			textObject.transform.rotation = Quaternion.identity;
			textObject.transform.localScale = Vector3.one;
			textMesh = textObject.AddComponent<TextMesh>();
			textMesh.text = string.Empty;
			textMesh.alignment = (TextAlignment)1;
			textMesh.anchor = (TextAnchor)4;
			textMesh.fontSize = 90;
			textMesh.characterSize = 0.022f;
			textMesh.color = Color.white;
			Font builtinResource = Resources.GetBuiltinResource<Font>("Arial.ttf");
			if ((Object)(object)builtinResource != (Object)null)
			{
				textMesh.font = builtinResource;
			}
			textRenderer = textObject.GetComponent<MeshRenderer>();
			if ((Object)(object)textRenderer == (Object)null)
			{
				textRenderer = textObject.AddComponent<MeshRenderer>();
			}
			if ((Object)(object)textMesh.font != (Object)null && (Object)(object)textMesh.font.material != (Object)null)
			{
				textMaterial = new Material(textMesh.font.material);
			}
			else
			{
				Shader val = Shader.Find("GUI/Text Shader");
				if ((Object)(object)val == (Object)null)
				{
					val = Shader.Find("Unlit/Transparent");
				}
				if ((Object)(object)val == (Object)null)
				{
					val = Shader.Find("Sprites/Default");
				}
				textMaterial = new Material(val);
			}
			((Object)textMaterial).name = "ScrapVisibility_ReadableTextMaterial";
			textMaterial.renderQueue = 3000;
			if (textMaterial.HasProperty("_Cull"))
			{
				textMaterial.SetInt("_Cull", 0);
			}
			if (textMaterial.HasProperty("_ZWrite"))
			{
				textMaterial.SetInt("_ZWrite", 0);
			}
			if (textMaterial.HasProperty("_Color"))
			{
				textMaterial.SetColor("_Color", Color.white);
			}
			if (textMaterial.HasProperty("_FaceColor"))
			{
				textMaterial.SetColor("_FaceColor", Color.white);
			}
			((Renderer)textRenderer).sharedMaterial = textMaterial;
			((Renderer)textRenderer).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)textRenderer).receiveShadows = false;
		}

		private void CreateApparatusLight()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)lightObject != (Object)null))
			{
				lightObject = new GameObject("ScrapVisibility_ApparatusStyleLight");
				lightObject.transform.SetParent(((Component)this).transform, false);
				lightObject.transform.localPosition = Vector3.zero;
				glowLight = lightObject.AddComponent<Light>();
				glowLight.type = (LightType)2;
				glowLight.color = Color.white;
				glowLight.intensity = 6f;
				glowLight.range = 6f;
				((Behaviour)glowLight).enabled = false;
				glowLight.shadows = (LightShadows)2;
				glowLight.renderMode = (LightRenderMode)1;
				glowLight.cullingMask = -1;
				glowLight.bounceIntensity = 1.2f;
			}
		}

		private void LateUpdate()
		{
			//IL_00fc: 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_013e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)item == (Object)null)
			{
				DestroySelf();
				return;
			}
			if ((Object)(object)item.itemProperties == (Object)null)
			{
				ClearGlow();
				SetVisualsActive(active: false);
				return;
			}
			PlayerControllerB localPlayer = GetLocalPlayer();
			if ((Object)(object)localPlayer == (Object)null || !localPlayer.isPlayerControlled || localPlayer.isPlayerDead)
			{
				ClearGlow();
				SetVisualsActive(active: false);
				return;
			}
			if (IsHeldByAnyone(localPlayer))
			{
				ClearGlow();
				SetVisualsActive(active: false);
				return;
			}
			if (!((Component)item).gameObject.activeInHierarchy)
			{
				ClearGlow();
				SetVisualsActive(active: false);
				return;
			}
			if (!TryGetBounds(out var bounds))
			{
				ClearGlow();
				SetVisualsActive(active: false);
				return;
			}
			float num = Vector3.Distance(((Component)localPlayer).transform.position, ((Bounds)(ref bounds)).center);
			if (_scrapVisibilityMaxDistance != null && num > _scrapVisibilityMaxDistance.Value)
			{
				ClearGlow();
				SetVisualsActive(active: false);
			}
			else
			{
				UpdateVisuals(bounds, num);
				SetVisualsActive(active: true);
			}
		}

		private PlayerControllerB GetLocalPlayer()
		{
			if ((Object)(object)GameNetworkManager.Instance == (Object)null)
			{
				return null;
			}
			return GameNetworkManager.Instance.localPlayerController;
		}

		private bool IsHeldByAnyone(PlayerControllerB localPlayer)
		{
			if (GetBoolField(fieldIsHeld))
			{
				return true;
			}
			if (GetBoolField(fieldIsPocketed))
			{
				return true;
			}
			if (GetBoolField(fieldIsHeldByEnemy))
			{
				return true;
			}
			if (GetObjectField(fieldPlayerHeldBy) != null)
			{
				return true;
			}
			if ((Object)(object)localPlayer != (Object)null)
			{
				object playerObjectField = GetPlayerObjectField(localPlayer, fieldCurrentlyHeldObjectServer);
				object playerObjectField2 = GetPlayerObjectField(localPlayer, fieldCurrentlyHeldObject);
				if (playerObjectField == item || playerObjectField2 == item)
				{
					return true;
				}
			}
			Transform parent = ((Component)item).transform.parent;
			if ((Object)(object)parent != (Object)null)
			{
				string text = ((Object)parent).name.ToLowerInvariant();
				if (text.Contains("player") || text.Contains("hand") || text.Contains("hold") || text.Contains("itemholder") || text.Contains("pocket"))
				{
					return true;
				}
			}
			return false;
		}

		private bool GetBoolField(FieldInfo field)
		{
			if (field == null || (Object)(object)item == (Object)null)
			{
				return false;
			}
			object value = field.GetValue(item);
			bool flag = default(bool);
			int num;
			if (value is bool)
			{
				flag = (bool)value;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private object GetObjectField(FieldInfo field)
		{
			if (field == null || (Object)(object)item == (Object)null)
			{
				return null;
			}
			return field.GetValue(item);
		}

		private object GetPlayerObjectField(PlayerControllerB player, FieldInfo field)
		{
			if (field == null || (Object)(object)player == (Object)null)
			{
				return null;
			}
			return field.GetValue(player);
		}

		private bool TryGetBounds(out Bounds bounds)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			bounds = new Bounds(((Component)this).transform.position, Vector3.one * 0.25f);
			bool flag = false;
			foreach (Renderer targetRenderer in targetRenderers)
			{
				if ((Object)(object)targetRenderer == (Object)null)
				{
					continue;
				}
				string lowerName = ((Object)((Component)targetRenderer).gameObject).name.ToLowerInvariant();
				if (!ShouldSkipRenderer(targetRenderer, lowerName) && ((Component)targetRenderer).gameObject.activeInHierarchy)
				{
					if (!flag)
					{
						bounds = targetRenderer.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref bounds)).Encapsulate(targetRenderer.bounds);
					}
				}
			}
			return flag;
		}

		private void UpdateVisuals(Bounds bounds, float distance)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: 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_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			bool flag = (Object)(object)item.itemProperties != (Object)null && item.itemProperties.isScrap;
			int num = Mathf.Max(0, item.scrapValue);
			string text = "Item";
			if ((Object)(object)item.itemProperties != (Object)null && !string.IsNullOrEmpty(item.itemProperties.itemName))
			{
				text = item.itemProperties.itemName;
			}
			Color scrapDisplayColor = GetScrapDisplayColor(flag, num);
			if (ScrapIsRegisteredInsideCubeForVisuals(item))
			{
				((Color)(ref scrapDisplayColor))..ctor(1f, 0.03f, 0.02f, 1f);
			}
			float num2 = 0.78f + Mathf.Sin(Time.time * GetPulseSpeed()) * 0.22f;
			Color tintColor = Color.Lerp(Color.white, scrapDisplayColor, GetTintStrength());
			tintColor.a = 1f;
			Color emissionColor = scrapDisplayColor * GetEmissionStrength() * num2;
			emissionColor.a = 1f;
			ApplyGlow(tintColor, emissionColor);
			if (num != lastValue || text != lastName || scrapDisplayColor != lastColor)
			{
				lastValue = num;
				lastName = text;
				lastColor = scrapDisplayColor;
				if ((Object)(object)textMesh != (Object)null)
				{
					if (flag)
					{
						textMesh.text = text + "\n$" + num;
					}
					else
					{
						textMesh.text = text;
					}
				}
			}
			UpdateApparatusLight(bounds, flag, num, scrapDisplayColor, num2);
			UpdateText(bounds, distance, scrapDisplayColor);
		}

		private void UpdateApparatusLight(Bounds bounds, bool isScrap, int value, Color color, float pulse)
		{
			//IL_0029: 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)
			if (!((Object)(object)glowLight == (Object)null))
			{
				float lightMultiplierForItem = GetLightMultiplierForItem(isScrap, value);
				((Component)glowLight).transform.position = ((Bounds)(ref bounds)).center;
				glowLight.color = color;
				glowLight.intensity = GetLightIntensity() * lightMultiplierForItem * pulse;
				glowLight.range = GetLightRange() * lightMultiplierForItem;
				glowLight.bounceIntensity = 1.5f + lightMultiplierForItem * 0.35f;
			}
		}

		private float GetLightMultiplierForItem(bool isScrap, int value)
		{
			if (!isScrap)
			{
				return 1.25f;
			}
			if (value >= 300)
			{
				return 2.25f;
			}
			if (value >= 150)
			{
				return 1.8f;
			}
			if (value >= 100)
			{
				return 1.45f;
			}
			if (value >= 50)
			{
				return 1.15f;
			}
			return 0.9f;
		}

		private void ApplyGlow(Color tintColor, Color emissionColor)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			if (_showScrapModelGlow == null || !_showScrapModelGlow.Value)
			{
				ClearGlow();
				return;
			}
			foreach (Renderer targetRenderer in targetRenderers)
			{
				if (!((Object)(object)targetRenderer == (Object)null))
				{
					if (!blocks.TryGetValue(targetRenderer, out var value))
					{
						value = new MaterialPropertyBlock();
						blocks[targetRenderer] = value;
					}
					targetRenderer.GetPropertyBlock(value);
					value.SetColor("_Color", tintColor);
					value.SetColor("_BaseColor", tintColor);
					value.SetColor("_EmissionColor", emissionColor);
					value.SetColor("_EmissiveColor", emissionColor);
					targetRenderer.SetPropertyBlock(value);
				}
			}
		}

		private void ClearGlow()
		{
			foreach (Renderer targetRenderer in targetRenderers)
			{
				if (!((Object)(object)targetRenderer == (Object)null))
				{
					targetRenderer.SetPropertyBlock((MaterialPropertyBlock)null);
				}
			}
		}

		private void UpdateText(Bounds bounds, float distance, Color color)
		{
			//IL_0066: 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_00a3: 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_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)textObject == (Object)null || (Object)(object)textMesh == (Object)null)
			{
				return;
			}
			Transform scrapTextRoot = GetScrapTextRoot();
			if ((Object)(object)textObject.transform.parent != (Object)(object)scrapTextRoot)
			{
				textObject.transform.SetParent(scrapTextRoot, true);
			}
			textObject.transform.position = GetStableLabelPosition(bounds);
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null)
			{
				textObject.transform.rotation = ((Component)main).transform.rotation;
			}
			textVisible = IsObjectVisibleForText(bounds);
			textMesh.characterSize = GetTextSize();
			textObject.transform.localScale = Vector3.one;
			textMesh.color = color;
			if ((Object)(object)textMaterial != (Object)null)
			{
				if (textMaterial.HasProperty("_Color"))
				{
					textMaterial.SetColor("_Color", color);
				}
				if (textMaterial.HasProperty("_FaceColor"))
				{
					textMaterial.SetColor("_FaceColor", color);
				}
			}
		}

		private Vector3 GetStableLabelPosition(Bounds bounds)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_002d: Unknown result type (might be due to invalid IL or missing references)
			return ((Bounds)(ref bounds)).center + Vector3.up * (((Bounds)(ref bounds)).extents.y + GetTextHeightOffset());
		}

		private Vector3 GetParentScaleCompensatedLocalScale(float desiredWorldScale)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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_0075: Unknown result type (might be due to invalid IL or missing references)
			Transform val = (((Object)(object)textObject != (Object)null) ? textObject.transform.parent : null);
			if ((Object)(object)val == (Object)null)
			{
				return Vector3.one * desiredWorldScale;
			}
			Vector3 lossyScale = val.lossyScale;
			return new Vector3(desiredWorldScale / SafeSignedScale(lossyScale.x), desiredWorldScale / SafeSignedScale(lossyScale.y), desiredWorldScale / SafeSignedScale(lossyScale.z));
		}

		private float SafeSignedScale(float value)
		{
			if (Mathf.Abs(value) < 0.001f)
			{
				return (value < 0f) ? (-0.001f) : 0.001f;
			}
			return value;
		}

		private bool IsObjectVisibleForText(Bounds bounds)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: 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_00a6: 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_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: 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_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: 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_0166: 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_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: 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_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: 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_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			if (_hideScrapTextBehindWalls == null || !_hideScrapTextBehindWalls.Value)
			{
				return true;
			}
			Camera main = Camera.main;
			if ((Object)(object)main == (Object)null)
			{
				return true;
			}
			Vector3 center = ((Bounds)(ref bounds)).center;
			Vector3 extents = ((Bounds)(ref bounds)).extents;
			Vector3[] array = (Vector3[])(object)new Vector3[15]
			{
				center,
				center + new Vector3(0f, extents.y, 0f),
				center - new Vector3(0f, extents.y, 0f),
				center + new Vector3(extents.x, 0f, 0f),
				center - new Vector3(extents.x, 0f, 0f),
				center + new Vector3(0f, 0f, extents.z),
				center - new Vector3(0f, 0f, extents.z),
				center + new Vector3(extents.x, extents.y, extents.z),
				center + new Vector3(0f - extents.x, extents.y, extents.z),
				center + new Vector3(extents.x, extents.y, 0f - extents.z),
				center + new Vector3(0f - extents.x, extents.y, 0f - extents.z),
				center + new Vector3(extents.x, 0f - extents.y, extents.z),
				center + new Vector3(0f - extents.x, 0f - extents.y, extents.z),
				center + new Vector3(extents.x, 0f - extents.y, 0f - extents.z),
				center + new Vector3(0f - extents.x, 0f - extents.y, 0f - extents.z)
			};
			Vector3[] array2 = array;
			foreach (Vector3 point in array2)
			{
				if (PointIsOnScreenAndNotBlocked(main, point))
				{
					lastGoodTextVisibleTime = Time.time;
					return true;
				}
			}
			float num = ((_scrapTextVisibilityGraceTime != null) ? Mathf.Max(0f, _scrapTextVisibilityGraceTime.Value) : 0.2f);
			return Time.time - lastGoodTextVisibleTime <= num;
		}

		private bool PointIsOnScreenAndNotBlocked(Camera cam, Vector3 point)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0076: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: 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_00ed: 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_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = cam.WorldToViewportPoint(point);
			if (val.z < 0f)
			{
				return false;
			}
			float num = ((_scrapScreenEdgeMargin != null) ? Mathf.Clamp(_scrapScreenEdgeMargin.Value, 0f, 0.35f) : 0.12f);
			if (val.x < 0f - num || val.x > 1f + num)
			{
				return false;
			}
			if (val.y < 0f - num || val.y > 1f + num)
			{
				return false;
			}
			Vector3 position = ((Component)cam).transform.position;
			Vector3 val2 = point - position;
			float magnitude = ((Vector3)(ref val2)).magnitude;
			if (magnitude <= 0.05f)
			{
				return true;
			}
			Vector3 normalized = ((Vector3)(ref val2)).normalized;
			position += normalized * 0.25f;
			float num2 = ((_scrapTextOcclusionPadding != null) ? Mathf.Clamp(_scrapTextOcclusionPadding.Value, 0.01f, 1f) : 0.12f);
			float num3 = Mathf.Max(0.05f, magnitude - num2);
			RaycastHit[] array = Physics.RaycastAll(position, normalized, num3, -1, (QueryTriggerInteraction)1);
			if (array == null || array.Length == 0)
			{
				return true;
			}
			Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
			PlayerControllerB localPlayer = GetLocalPlayer();
			RaycastHit[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				RaycastHit val3 = array2[i];
				if ((Object)(object)((RaycastHit)(ref val3)).collider == (Object)null)
				{
					continue;
				}
				Transform transform = ((Component)((RaycastHit)(ref val3)).collider).transform;
				if ((Object)(object)textObject != (Object)null && transform.IsChildOf(textObject.transform))
				{
					continue;
				}
				if (transform.IsChildOf(((Component)item).transform))
				{
					return true;
				}
				GrabbableObject componentInParent = ((Component)((RaycastHit)(ref val3)).collider).GetComponentInParent<GrabbableObject>();
				if ((Object)(object)componentInParent == (Object)(object)item)
				{
					return true;
				}
				if ((!((Object)(object)localPlayer != (Object)null) || !transform.IsChildOf(((Component)localPlayer).transform)) && !((Object)(object)componentInParent != (Object)null))
				{
					string text = ((Object)transform).name.ToLowerInvariant();
					if (!text.Contains("player") && !text.Contains("camera") && !text.Contains("hand") && !text.Contains("itemholder") && !text.Contains("held") && !text.Contains("helmet") && !text.Contains("scan") && !text.Contains("trigger"))
					{
						return false;
					}
				}
			}
			return true;
		}

		private void SetVisualsActive(bool active)
		{
			bool flag = active && textVisible && _showScrapValueText != null && _showScrapValueText.Value;
			bool enabled = active && _showScrapLight != null && _showScrapLight.Value;
			if ((Object)(object)textObject != (Object)null && textObject.activeSelf != flag)
			{
				textObject.SetActive(flag);
			}
			if ((Object)(object)glowLight != (Object)null)
			{
				((Behaviour)glowLight).enabled = enabled;
			}
		}

		private float GetTintStrength()
		{
			if (_scrapTintStrength == null)
			{
				return 1f;
			}
			return Mathf.Clamp01(_scrapTintStrength.Value);
		}

		private float GetEmissionStrength()
		{
			if (_scrapEmissionStrength == null)
			{
				return 5.5f;
			}
			return Mathf.Clamp(_scrapEmissionStrength.Value, 0f, 12f);
		}

		private float GetPulseSpeed()
		{
			if (_scrapPulseSpeed == null)
			{
				return 2.8f;
			}
			return Mathf.Max(0f, _scrapPulseSpeed.Value);
		}

		private float GetLightIntensity()
		{
			if (_scrapLightIntensity == null)
			{
				return 6f;
			}
			return Mathf.Clamp(_scrapLightIntensity.Value, 0f, 30f);
		}

		private float GetLightRange()
		{
			if (_scrapLightRange == null)
			{
				return 6f;
			}
			return Mathf.Clamp(_scrapLightRange.Value, 0f, 20f);
		}

		private float GetTextSize()
		{
			if (_scrapTextSize == null)
			{
				return 0.022f;
			}
			return Mathf.Clamp(_scrapTextSize.Value, 0.005f, 0.14f);
		}

		private float GetTextHeightOffset()
		{
			if (_scrapTextHeightOffset == null)
			{
				return 0.22f;
			}
			return Mathf.Clamp(_scrapTextHeightOffset.Value, 0f, 2f);
		}

		internal void DestroySelf()
		{
			ClearGlow();
			if ((Object)(object)textObject != (Object)null)
			{
				Object.Destroy((Object)(object)textObject);
				textObject = null;
			}
			if ((Object)(object)lightObject != (Object)null)
			{
				Object.Destroy((Object)(object)lightObject);
				lightObject = null;
			}
			if ((Object)(object)textMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)textMaterial);
				textMaterial = null;
			}
			Object.Destroy((Object)(object)this);
		}

		private void OnDestroy()
		{
			ClearGlow();
			if ((Object)(object)textObject != (Object)null)
			{
				Object.Destroy((Object)(object)textObject);
			}
			if ((Object)(object)lightObject != (Object)null)
			{
				Object.Destroy((Object)(object)lightObject);
			}
			if ((Object)(object)textMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)textMaterial);
			}
		}
	}

	[HarmonyPatch(typeof(EntranceTeleport), "StartOpeningEntrance")]
	private static class EntranceTeleport_StartOpeningEntrance_Patch
	{
		private static bool Prefix(EntranceTeleport __instance)
		{
			if (_disableFacilityExitInteractions == null || !_disableFacilityExitInteractions.Value)
			{
				return true;
			}
			ForceFacilityExitInteractionsDisabled();
			D("Blocked facility entrance/fire-exit interaction: " + GetEntranceDebugName(__instance));
			return false;
		}
	}

	[HarmonyPatch(typeof(EntranceTeleport), "TeleportPlayer")]
	private static class EntranceTeleport_TeleportPlayer_Patch
	{
		private static bool Prefix(EntranceTeleport __instance)
		{
			if (_disableFacilityExitInteractions == null || !_disableFacilityExitInteractions.Value)
			{
				return true;
			}
			ForceFacilityExitInteractionsDisabled();
			D("Blocked facility entrance/fire-exit teleport: " + GetEntranceDebugName(__instance));
			return false;
		}
	}

	[HarmonyPatch(typeof(StartOfRound), "OpenShipDoors")]
	private static class StartOfRound_OpenShipDoors_Patch
	{
		private static void Postfix()
		{
			if (_keepShipDoorsClosed != null && _keepShipDoorsClosed.Value && !IsAtTheCompany())
			{
				ForceShipDoorsClosedNow();
				D("OpenShipDoors ran normally. Extraction hides the moving vanilla ship door and keeps the closed-door overlay.");
			}
		}
	}

	[HarmonyPatch(typeof(StartOfRound), "openingDoorsSequence")]
	private static class StartOfRound_OpeningDoorsSequence_Patch
	{
		private static void Postfix()
		{
			if (_keepShipDoorsClosed != null && _keepShipDoorsClosed.Value && !IsAtTheCompany())
			{
				ForceShipDoorsClosedNow();
				D("openingDoorsSequence ran normally. Extraction hides the moving vanilla ship door and keeps the closed-door overlay.");
			}
		}
	}

	[HarmonyPatch(typeof(HangarShipDoor), "SetDoorOpen")]
	private static class HangarShipDoor_SetDoorOpen_Patch
	{
		private static void Postfix(HangarShipDoor __instance)
		{
			if (_keepShipDoorsClosed != null && _keepShipDoorsClosed.Value && !IsAtTheCompany())
			{
				ForceShipDoorsClosedNow();
			}
		}
	}

	[HarmonyPatch(typeof(HangarShipDoor), "PlayDoorAnimation")]
	private static class HangarShipDoor_PlayDoorAnimation_Patch
	{
		private static void Postfix(HangarShipDoor __instance)
		{
			if (_keepShipDoorsClosed != null && _keepShipDoorsClosed.Value && !IsAtTheCompany())
			{
				ForceShipDoorsClosedNow();
			}
		}
	}

	[HarmonyPatch(typeof(StartOfRound), "ShipLeave")]
	private static class StartOfRound_ShipLeave_Patch
	{
		private static void Prefix()
		{
			D("[" + NetRoleDebugName() + "] StartOfRound.ShipLeave prefix fired.");
			DebugLogStateSnapshot("ShipLeave prefix before LocalShipLeavePrefix", force: true);
			LocalShipLeavePrefix();
		}
	}

	[HarmonyPatch(typeof(StartOfRound), "ShipHasLeft")]
	private static class StartOfRound_ShipHasLeft_Patch
	{
		private static void Postfix()
		{
			_debugShipHasLeftCount++;
			D($"[{NetRoleDebugName()}] StartOfRound.ShipHasLeft postfix fired. count={_debugShipHasLeftCount}");
			DebugLogStateSnapshot("ShipHasLeft before quota/reset", force: true);
			ApplyPendingQuotaValueOnHost("StartOfRound.ShipHasLeft postfix");
			ResetRoundState();
		}
	}

	[HarmonyPatch(typeof(HUDManager), "FillEndGameStats")]
	private static class HUDManager_FillEndGameStats_Patch
	{
		private static void Prefix()
		{
			D("HUDManager.FillEndGameStats prefix fired.");
			ApplyPendingQuotaValueOnHost("HUDManager.FillEndGameStats prefix");
		}
	}

	[HarmonyPatch(typeof(RoundManager), "FinishGeneratingNewLevelClientRpc")]
	private static class RoundManager_FinishGeneratingNewLevelClientRpc_Patch
	{
		private static void Postfix()
		{
			NotifyPlayersFinishedGeneratingNewLevel();
		}
	}

	[HarmonyPatch(typeof(TimeOfDay), "VoteShipToLeaveEarly")]
	private static class TimeOfDay_VoteShipToLeaveEarly_Patch
	{
		private static bool Prefix()
		{
			if (!BaseGameSpectatorVoteToLeaveDisabled())
			{
				return true;
			}
			D("Blocked vanilla dead-spectator vote to leave early because DisableBaseGameSpectatorVoteToLeave is true.");
			if ((Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.DisplayTip("Vote to leave disabled", "The vanilla spectator vote to leave early is disabled by this mod.", false, false, "LC_Tip1");
			}
			return false;
		}

		private static void Postfix()
		{
			if (!BaseGameSpectatorVoteToLeaveDisabled())
			{
				ObserveBaseGameVoteToLeave("local vanilla VoteShipToLeaveEarly called");
			}
		}
	}

	[HarmonyPatch(typeof(TimeOfDay), "SetShipLeaveEarlyServerRpc")]
	private static class TimeOfDay_SetShipLeaveEarlyServerRpc_Patch
	{
		private static bool Prefix()
		{
			if (!BaseGameSpectatorVoteToLeaveDisabled())
			{
				return true;
			}
			D("Blocked vanilla SetShipLeaveEarlyServerRpc because DisableBaseGameSpectatorVoteToLeave is true.");
			return false;
		}
	}

	[HarmonyPatch(typeof(TimeOfDay), "AddVoteForShipToLeaveEarlyClientRpc")]
	private static class TimeOfDay_AddVoteForShipToLeaveEarlyClientRpc_Patch
	{
		private static void Postfix()
		{
			if (!BaseGameSpectatorVoteToLeaveDisabled())
			{
				ObserveBaseGameVoteToLeave("vanilla AddVoteForShipToLeaveEarlyClientRpc fired");
			}
		}
	}

	[HarmonyPatch(typeof(TimeOfDay), "SetShipLeaveEarlyClientRpc")]
	private static class TimeOfDay_SetShipLeaveEarlyClientRpc_Patch
	{
		private static void Postfix()
		{
			if (!BaseGameSpectatorVoteToLeaveDisabled())
			{
				ObserveBaseGameVoteToLeave("vanilla SetShipLeaveEarlyClientRpc fired");
			}
		}
	}

	[HarmonyPatch(typeof(HUDManager), "DisplaySpectatorVoteTip")]
	private static class HUDManager_DisplaySpectatorVoteTip_Patch
	{
		private static bool Prefix()
		{
			return !BaseGameSpectatorVoteToLeaveDisabled();
		}
	}

	[HarmonyPatch(typeof(HUDManager), "AddPlayerChatMessageServerRpc")]
	private static class HUDManager_AddPlayerChatMessageServerRpc_Patch
	{
		private static bool Prefix(string chatMessage, int playerId)
		{
			try
			{
				if (!IsServer())
				{
					return true;
				}
				return !HandleChatCommand(chatMessage, playerId);
			}
			catch (Exception arg)
			{
				Log.LogWarning((object)$"[Extraction] Chat command handler failed: {arg}");
				return true;
			}
		}
	}

	[HarmonyPatch(typeof(HUDManager), "PingScan_performed")]
	private static class HUDManager_PingScan_performed_Patch
	{
		private static bool Prefix()
		{
			bool flag = ShouldBlockManualScanner();
			if (flag)
			{
				D("Blocked manual right click scan because integrated scrap visibility is enabled.");
			}
			return !flag;
		}
	}

	[CompilerGenerated]
	private sealed class <ApplyRoundStartCubeTeleportLocalCoroutine>d__512 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public int token;

		public Vector3 position;

		public string source;

		private int <attempt>5__1;

		private PlayerControllerB <player>5__2;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <ApplyRoundStartCubeTeleportLocalCoroutine>d__512(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<player>5__2 = null;
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<attempt>5__1 = 0;
				goto IL_0200;
			case 1:
				<>1__state = -1;
				goto IL_0087;
			case 2:
				<>1__state = -1;
				goto IL_0087;
			case 3:
				{
					<>1__state = -1;
					if (RoundStartTeleportSucceededLocally(position))
					{
						_localCompletedRoundStartCubeTeleportToken = token;
						D($"Applied and completed round-start cube teleport locally. token={token}, source={source}, attempt={<attempt>5__1 + 1}, pos={position}");
					}
					else
					{
						D($"Applied round-start cube teleport locally but landing was not confirmed yet. Same-token retries remain allowed. token={token}, source={source}, attempt={<attempt>5__1 + 1}, pos={position}");
					}
					if (_localPendingRoundStartCubeTeleportToken == token)
					{
						_localPendingRoundStartCubeTeleportToken = -1;
					}
					return false;
				}
				IL_0087:
				if (_cubeExtractionPassed || ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.shipIsLeaving))
				{
					break;
				}
				<player>5__2 = StartOfRound.Instance?.localPlayerController;
				if (IsLivingControlledConnectedPlayer(<player>5__2))
				{
					TeleportLocalPlayer(position, toShip: false);
					_lastLocalRoundStartCubeTeleportApplyTime = Time.realtimeSinceStartup;
					_localAppliedRoundStartCubeTeleportToken = token;
					<>2__current = null;
					<>1__state = 3;
					return true;
				}
				<attempt>5__1++;
				goto IL_0200;
				IL_0200:
				if (<attempt>5__1 < 28)
				{
					if (<attempt>5__1 > 0)
					{
						<>2__current = (object)new WaitForSeconds(0.25f);
						<>1__state = 1;
						return true;
					}
					<>2__current = null;
					<>1__state = 2;
					return true;
				}
				break;
			}
			if (_localPendingRoundStartCubeTeleportToken == token)
			{
				_localPendingRoundStartCubeTeleportToken = -1;
			}
			D($"Failed to apply round-start cube teleport locally before retry attempts ended. token={token}, source={source}");
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <ClientCubeVisualRefreshRoutine>d__500 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public Vector3 center;

		public int acceptedSyncSerial;

		public int cubeRoundId;

		private int <i>5__1;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <ClientCubeVisualRefreshRoutine>d__500(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			int num = <>1__state;
			if (num == -3 || num == 1)
			{
				try
				{
				}
				finally
				{
					<>m__Finally1();
				}
			}
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			bool result;
			try
			{
				switch (<>1__state)
				{
				default:
					result = false;
					goto end_IL_0000;
				case 0:
					<>1__state = -1;
					_clientCubeVisualRefreshRunning = true;
					<>1__state = -3;
					<i>5__1 = 0;
					goto IL_02c5;
				case 1:
					{
						<>1__state = -3;
						if (acceptedSyncSerial != _clientAcceptedCubeSyncSerial)
						{
							D($"Stopped stale client visual refresh. capturedSerial={acceptedSyncSerial}, currentSerial={_clientAcceptedCubeSyncSerial}, capturedRoundId={cubeRoundId}, currentRoundId={_currentCubeNetworkRoundId}");
							result = false;
							break;
						}
						if (cubeRoundId > 0 && cubeRoundId != _currentCubeNetworkRoundId)
						{
							D($"Stopped old-round client visual refresh. capturedRoundId={cubeRoundId}, currentRoundId={_currentCubeNetworkRoundId}");
							result = false;
							break;
						}
						if (!_cubeNetworkRoundActive || !_hasCube)
						{
							D($"Stopped client visual refresh because cube/round inactive. active={_cubeNetworkRoundActive}, hasCube={_hasCube}, capturedSerial={acceptedSyncSerial}, currentSerial={_clientAcceptedCubeSyncSerial}, capturedRound={cubeRoundId}, currentRound={_currentCubeNetworkRoundId}");
							result = false;
							break;
						}
						if (Vector3.Distance(center, _cubeCenter) > 0.15f)
						{
							D($"Stopped client visual refresh because center changed. capturedCenter={center}, currentCenter={_cubeCenter}");
							result = false;
							break;
						}
						if ((Object)(object)_cubeVisual == (Object)null)
						{
							CreateOrMoveCubeVisual(_cubeCenter);
						}
						else
						{
							if (!_cubeVisual.activeSelf)
							{
								_cubeVisual.SetActive(true);
							}
							ForceCubeVisualVisible();
							UpdateCubePulseVisual(force: true);
						}
						D($"Client forced Extraction Area visual visibility nudge {<i>5__1 + 1}/6. visualExists={(Object)(object)_cubeVisual != (Object)null}, active={(Object)(object)_cubeVisual != (Object)null && _cubeVisual.activeSelf}, dynamicReady={_cubeDynamicFootprintReady}, cells={_cubeDynamicFootprintCells.Count}");
						<i>5__1++;
						goto IL_02c5;
					}
					IL_02c5:
					if (<i>5__1 < 6)
					{
						<>2__current = (object)new WaitForSeconds(0.75f);
						<>1__state = 1;
						result = true;
					}
					else
					{
						<>m__Finally1();
						result = false;
					}
					goto end_IL_0000;
				}
				<>m__Finally1();
				end_IL_0000:;
			}
			catch
			{
				//try-fault
				((IDisposable)this).Dispose();
				throw;
			}
			return result;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		private void <>m__Finally1()
		{
			<>1__state = -1;
			_clientCubeVisualRefreshRunning = false;
			D($"[{NetRoleDebugName()}] ClientCubeVisualRefreshRoutine finished. capturedSerial={acceptedSyncSerial}, capturedRound={cubeRoundId}, currentSerial={_clientAcceptedCubeSyncSerial}, currentRound={_currentCubeNetworkRoundId}, {CubeStateDebugLine()}");
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <DelayedFullExtractionStateSyncToClient>d__254 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public ulong clientId;

		public string reason;

		private float[] <delays>5__1;

		private int <i>5__2;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <DelayedFullExtractionStateSyncToClient>d__254(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<delays>5__1 = null;
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<delays>5__1 = new float[3] { 0.35f, 1.25f, 3f };
				<i>5__2 = 0;
				break;
			case 1:
				<>1__state = -1;
				if (!IsServer() || (Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.ConnectedClientsIds.Contains(clientId))
				{
					return false;
				}
				SendFullExtractionStateToClient(clientId, $"{reason} attempt {<i>5__2 + 1}", allowLateJoinTeleport: false);
				<i>5__2++;
				break;
			}
			if (<i>5__2 < <delays>5__1.Length)
			{
				<>2__current = (object)new WaitForSeconds(<delays>5__1[<i>5__2]);
				<>1__state = 1;
				return true;
			}
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <EmptyCoroutine>d__478 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <EmptyCoroutine>d__478(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			if (<>1__state != 0)
			{
				return false;
			}
			<>1__state = -1;
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <KeepShipDoorsClosedWhileRunning>d__479 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public IEnumerator original;

		public string source;

		private bool <moveNext>5__1;

		private object <current>5__2;

		private Exception <ex>5__3;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <KeepShipDoorsClosedWhileRunning>d__479(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<current>5__2 = null;
			<ex>5__3 = null;
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				if (original == null)
				{
					return false;
				}
				break;
			case 1:
				<>1__state = -1;
				ForceShipDoorsClosedNow();
				<current>5__2 = null;
				break;
			}
			try
			{
				<moveNext>5__1 = original.MoveNext();
				<current>5__2 = (<moveNext>5__1 ? original.Current : null);
			}
			catch (Exception ex)
			{
				<ex>5__3 = ex;
				D("Ship door coroutine wrapper from " + source + " failed: " + <ex>5__3.Message);
				throw;
			}
			ForceShipDoorsClosedNow();
			if (<moveNext>5__1)
			{
				<>2__current = <current>5__2;
				<>1__state = 1;
				return true;
			}
			ForceShipDoorsClosedNow();
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <MaintainLocalInsideFactoryStateCoroutine>d__536 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public float seconds;

		private float <end>5__1;

		private PlayerControllerB <player>5__2;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <MaintainLocalInsideFactoryStateCoroutine>d__536(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<player>5__2 = null;
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<end>5__1 = Time.realtimeSinceStartup + Mathf.Max(0.25f, seconds);
				break;
			case 1:
				<>1__state = -1;
				<player>5__2 = null;
				break;
			}
			if (Time.realtimeSinceStartup < <end>5__1)
			{
				<player>5__2 = StartOfRound.Instance?.localPlayerController;
				if (!IsLivingControlledConnectedPlayer(<player>5__2))
				{
					return false;
				}
				if ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.shipIsLeaving)
				{
					return false;
				}
				ApplyPlayerInsideFactoryState(<player>5__2);
				<>2__current = (object)new WaitForSeconds(0.2f);
				<>1__state = 1;
				return true;
			}
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <RoundStartFacilityTeleportRoutine>d__434 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public string reason;

		private float <delay>5__1;

		private float <waitForLandingMax>5__2;

		private float <landingWaitStart>5__3;

		private int <token>5__4;

		private float <retrySeconds>5__5;

		private float <retryInterval>5__6;

		private float <endTime>5__7;

		private string <destination>5__8;

		private int <pass>5__9;

		private int <sent>5__10;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <RoundStartFacilityTeleportRoutine>d__434(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<destination>5__8 = null;
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_039e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a8: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				if (!IsServer() || !_hasCube || (Object)(object)StartOfRound.Instance == (Object)null)
				{
					return false;
				}
				_roundStartTeleportRoutineRunning = true;
				<delay>5__1 = Mathf.Max(0f, (_roundStartTeleportDelay != null) ? _roundStartTeleportDelay.Value : 1.25f);
				if (<delay>5__1 > 0f)
				{
					<>2__current = (object)new WaitForSeconds(<delay>5__1);
					<>1__state = 1;
					return true;
				}
				<>2__current = null;
				<>1__state = 2;
				return true;
			case 1:
				<>1__state = -1;
				goto IL_00de;
			case 2:
				<>1__state = -1;
				goto IL_00de;
			case 3:
				<>1__state = -1;
				goto IL_017e;
			case 4:
				{
					<>1__state = -1;
					break;
				}
				IL_00de:
				<waitForLandingMax>5__2 = Mathf.Max(0f, (_roundStartTeleportWaitForLandingSeconds != null) ? _roundStartTeleportWaitForLandingSeconds.Value : 12f);
				<landingWaitStart>5__3 = Time.realtimeSinceStartup;
				goto IL_017e;
				IL_017e:
				if (!IsRoundStartFacilityTeleportMomentSafe())
				{
					if (!(<waitForLandingMax>5__2 <= 0f) && !(Time.realtimeSinceStartup - <landingWaitStart>5__3 >= <waitForLandingMax>5__2))
					{
						<>2__current = (object)new WaitForSeconds(0.25f);
						<>1__state = 3;
						return true;
					}
					D($"Round-start teleport landing wait expired after {<waitForLandingMax>5__2:0.0}s. Applying anyway.");
				}
				if (!IsServer() || !_hasCube || (Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.shipIsLeaving || _cubeExtractionPassed)
				{
					_roundStartTeleportRoutineRunning = false;
					return false;
				}
				_roundStartCubeTeleportToken++;
				<token>5__4 = _roundStartCubeTeleportToken;
				_roundStartTeleportTargetsByClient.Clear();
				<retrySeconds>5__5 = Mathf.Max(2f, (_roundStartTeleportRetrySeconds != null) ? _roundStartTeleportRetrySeconds.Value : 12f);
				<retryInterval>5__6 = Mathf.Clamp((_roundStartTeleportRetryInterval != null) ? _roundStartTeleportRetryInterval.Value : 0.75f, 0.15f, 3f);
				<endTime>5__7 = Time.realtimeSinceStartup + <retrySeconds>5__5;
				_roundStartCubeTeleportWindowOpen = true;
				_roundStartCubeTeleportWindowEndTime = <endTime>5__7;
				<destination>5__8 = (RoundStartTeleportUsesCube() ? "the Extraction Area" : "random facility spot");
				D($"Started token-safe round-start facility teleport token={<token>5__4}, destination={<destination>5__8}, reason={reason}, retrySeconds={<retrySeconds>5__5:0.0}, interval={<retryInterval>5__6:0.00}");
				<pass>5__9 = 0;
				break;
			}
			if (Time.realtimeSinceStartup <= <endTime>5__7)
			{
				<pass>5__9++;
				if (IsServer() && _hasCube && !((Object)(object)StartOfRound.Instance == (Object)null) && !StartOfRound.Instance.shipIsLeaving && !_cubeExtractionPassed)
				{
					<sent>5__10 = TeleportAllControlledPlayersIntoFacilityOnHost(<token>5__4, reason, <pass>5__9);
					if (<pass>5__9 == 1 || <sent>5__10 > 0)
					{
						D($"Round-start teleport pass {<pass>5__9} sent/applied token={<token>5__4} to {<sent>5__10} living controlled player(s).");
					}
					BroadcastCubePosition();
					<>2__current = (object)new WaitForSeconds(<retryInterval>5__6);
					<>1__state = 4;
					return true;
				}
			}
			_roundStartCubeTeleportWindowOpen = false;
			_roundStartCubeTeleportWindowEndTime = 0f;
			_roundStartTeleportRoutineRunning = false;
			BroadcastCubePosition();
			D($"Round-start facility teleport retry window closed. token={<token>5__4}");
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <SpawnCubeAfterFrame>d__257 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <SpawnCubeAfterFrame>d__257(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0234: 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_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				D("[" + NetRoleDebugName() + "] SpawnCubeAfterFrame entered. Pre-reset: " + RoundStateDebugLine() + " | " + CubeStateDebugLine());
				<>2__current = null;
				<>1__state = 1;
				return true;
			case 1:
				<>1__state = -1;
				D("[" + NetRoleDebugName() + "] SpawnCubeAfterFrame after one frame. Beginning new cube state setup.");
				_processedThisTakeoff = false;
				_cubeEffectiveHeightOverride = -1f;
				_cubeExtractionPassed = false;
				_earlyExtractionCountdownActive = false;
				_earlyExtractionCountdownEndTime = 0f;
				_earlyExtractionCountdownSecondsRemaining = 0;
				_earlyExtractionTriggered = false;
				ClearBaseGameSpectatorVoteToLeaveState("round cube spawn start", resetVanillaTimeOfDayFields: true);
				CaptureNormalShipLeaveAutomaticallyTime("round cube spawn start");
				_earlyVoteEligible = false;
				_earlyVoteCount = 0;
				_earlyVoteRequired = 0;
				_localEarlyVoteSubmitted = false;
				_earlyVoteRosterKey = string.Empty;
				_earlyExtractionVotes.Clear();
				_clientTrustedEarlyVotePlayers.Clear();
				_lastEarlyVoteSyncSignature = string.Empty;
				_currentCubeScrapRawValue = 0;
				_currentCubeScrapCount = 0;
				_roundStartCubeTeleportWindowOpen = false;
				_roundStartCubeTeleportWindowEndTime = 0f;
				_roundStartTeleportRequested = false;
				_roundStartTeleportFloorReady = false;
				_roundStartTeleportStarted = false;
				_roundStartTeleportRoutineRunning = false;
				_cubePostReadyPlacementFinalized = false;
				_authoritativeCubeFootprintReadyForClients = false;
				_cubeDynamicFootprintAuthoritativeFromHost = false;
				_cubeDynamicFootprintWaitingForAuthoritativeHostSync = false;
				_cubeNetworkRoundActive = IsServer();
				_currentCubeNetworkRoundId = (IsServer() ? _currentCubeNetworkRoundId : (-1));
				_clientAcceptedCubeSyncSerial++;
				_trackedPressedVoteKeys.Clear();
				_roundStartTeleportRequestTime = 0f;
				_roundStartTeleportRequestReason = string.Empty;
				_roundStartTeleportTargetsByClient.Clear();
				_lastInsideFactoryBestEffortTimeByClient.Clear();
				_lastInsideAudioLightingBestEffortTimeByClient.Clear();
				_cachedInteriorEntranceReachabilityAnchors.Clear();
				_cachedInteriorEntranceReachabilityAnchorsFrame = -1;
				_localPendingRoundStartCubeTeleportToken = -1;
				_localAppliedRoundStartCubeTeleportToken = -1;
				_localCompletedRoundStartCubeTeleportToken = -1;
				if (IsServer())
				{
					_hostCubeNetworkRoundId++;
					if (_hostCubeNetworkRoundId <= 0)
					{
						_hostCubeNetworkRoundId = 1;
					}
					_currentCubeNetworkRoundId = _hostCubeNetworkRoundId;
					_cubeNetworkRoundActive = true;
					_cubeCenter = PickCubePosition();
					_hasCube = true;
					D($"Host picked Extraction Area center: {_cubeCenter} for cubeRoundId={_currentCubeNetworkRoundId}");
					DebugLogStateSnapshot("host picked cube before visual/broadcast", force: true);
					CreateOrMoveCubeVisual(_cubeCenter);
					DebugLogStateSnapshot("host after local visual create before first cube sync", force: true);
					BroadcastCubePosition();
					if (_teleportPlayersToCubeOnRoundStart.Value)
					{
						RequestRoundStartCubeTeleport("round start cube placed");
					}
				}
				else
				{
					D("Client finished dungeon generation. Waiting for host cube sync.");
					DebugLogStateSnapshot("client waiting for cube sync after dungeon generation", force: true);
				}
				return false;
			}
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	public const string ModGuid = "robert.lethalcompany.extraction";

	public const string ModName = "Extraction";

	public const string ModVersion = "0.2.49";

	private const string MsgCubeSync = "Extraction_CubeSync_v1";

	private const string MsgTeleport = "Extraction_Teleport_v1";

	private const string MsgQuotaSync = "Extraction_QuotaSync_v1";

	private const string MsgCubeClosed = "Extraction_CubeClosed_v1";

	private const string MsgEarlyVoteRequest = "Extraction_EarlyVoteRequest_v1";

	private const string MsgEarlyVoteSync = "Extraction_EarlyVoteSync_v1";

	private const string MsgRoundStartCubeTeleport = "Extraction_RoundStartCubeTeleport_v1";

	internal static Plugin Instance;

	internal static ManualLogSource Log;

	private static Harmony _harmony;

	private static ConfigEntry<bool> _debugLogging;

	private static ConfigEntry<bool> _enableDebugCommands;

	private static ConfigEntry<bool> _hostOnlyDebugCommands;

	private static ConfigEntry<bool> _debugStateSnapshots;

	private static ConfigEntry<float> _debugStateSnapshotInterval;

	private static ConfigEntry<bool> _debugVoteInput;

	private static ConfigEntry<bool> _debugInsideCubeChecks;

	private static ConfigEntry<bool> _debugCubeSyncPayloads;

	private static ConfigEntry<float> _cubeSize;

	private static ConfigEntry<float> _cubeHeight;

	private static ConfigEntry<float> _cubeVisualAlpha;

	private static ConfigEntry<float> _groundSnapRayStartHeight;

	private static ConfigEntry<float> _groundSnapRayDistance;

	private static ConfigEntry<bool> _applyCompanyBuyingRate;

	private static ConfigEntry<bool> _requireAllControlledPlayersAlive;

	private static ConfigEntry<int> _minDistanceFromShip;

	private static ConfigEntry<float> _cubePlacementWallClearance;

	private static ConfigEntry<float> _cubePlacementFloorInset;

	private static ConfigEntry<float> _cubePlacementMaxFloorDrop;

	private static ConfigEntry<float> _cubePlacementSearchRadius;

	private static ConfigEntry<float> _cubePlacementSearchStep;

	private static ConfigEntry<int> _cubePlacementMaxCandidates;

	private static ConfigEntry<bool> _cubePlacementRejectDangerTriggers;

	private static ConfigEntry<bool> _cubePlacementRequireNavMesh;

	private static ConfigEntry<bool> _cubePlacementRequireReachablePathFromEntrance;

	private static ConfigEntry<bool> _cubePlacementRejectBackfaceGeometry;

	private static ConfigEntry<bool> _cubePlacementPreferScrapSpawnAnchors;

	private static ConfigEntry<bool> _cubePlacementUseEntrancesAndFireExitsOnly;

	private static ConfigEntry<bool> _adaptiveForcefieldBounds;

	private static ConfigEntry<bool> _adaptiveForcefieldDynamicFootprint;

	private static ConfigEntry<bool> _forceFullSizeFootprint;

	private static ConfigEntry<bool> _adaptiveForcefieldFreezeShapeAfterBuild;

	private static ConfigEntry<bool> _adaptiveForcefieldSimplifyMesh;

	private static ConfigEntry<float> _adaptiveForcefieldClearance;

	private static ConfigEntry<float> _adaptiveForcefieldMinimumSide;

	private static ConfigEntry<float> _adaptiveForcefieldFloorProbeStep;

	private static ConfigEntry<float> _adaptiveForcefieldBoundaryInset;

	private static ConfigEntry<bool> _showInsideCubeHud;

	private static ConfigEntry<bool> _showInsideCubeTips;

	private static ConfigEntry<bool> _teleportPlayersToCubeOnRoundStart;

	private static ConfigEntry<bool> _roundStartTeleportToCube;

	private static ConfigEntry<float> _roundStartTeleportDelay;

	private static ConfigEntry<float> _roundStartTeleportRetrySeconds;

	private static ConfigEntry<float> _roundStartTeleportRetryInterval;

	private static ConfigEntry<float> _roundStartTeleportMaxWaitForFloorReady;

	private static ConfigEntry<float> _roundStartTeleportMaintainInsideSeconds;

	private static ConfigEntry<float> _roundStartTeleportWaitForLandingSeconds;

	private static ConfigEntry<bool> _enableCubeLight;

	private static ConfigEntry<float> _cubeLightIntensity;

	private static ConfigEntry<float> _cubeLightRange;

	private static ConfigEntry<bool> _enableCubeScrapGlow;

	private static ConfigEntry<float> _cubeScrapGlowRefreshInterval;

	private static ConfigEntry<float> _cubeScrapGlowEmissionStrength;

	private static ConfigEntry<float> _cubeScrapGlowLightIntensity;

	private static ConfigEntry<float> _cubeScrapGlowLightRange;

	private static ConfigEntry<bool> _enableEarlyExtractionVote;

	private static ConfigEntry<KeyCode> _earlyExtractionVoteKey;

	private static ConfigEntry<float> _earlyExtractionVoteSyncInterval;

	private static ConfigEntry<float> _earlyExtractionCountdownSeconds;

	private static ConfigEntry<float> _earlyExtractionVoteInsideToleranceMeters;

	private static ConfigEntry<float> _earlyExtractionVoteLeaveGraceSeconds;

	private static ConfigEntry<float> _roundStartTeleportReapplyIntervalSeconds;

	private static ConfigEntry<bool> _disableBaseGameSpectatorVoteToLeave;

	private static ConfigEntry<bool> _keepShipDoorsClosed;

	private static ConfigEntry<bool> _disableFacilityExitInteractions;

	private static ConfigEntry<bool> _enableScrapVisibility;

	private static ConfigEntry<bool> _disableManualRightClickScan;

	private static ConfigEntry<bool> _showScrapValueText;

	private static ConfigEntry<bool> _showScrapModelGlow;

	private static ConfigEntry<bool> _showScrapLight;

	private static ConfigEntry<bool> _hideScrapTextBehindWalls;

	private static ConfigEntry<float> _scrapVisibilityMaxDistance;

	private static ConfigEntry<float> _scrapVisibilityRefreshInterval;

	private static ConfigEntry<float> _scrapTextSize;

	private static ConfigEntry<float> _scrapTextHeightOffset;

	private static ConfigEntry<float> _scrapTintStrength;

	private static ConfigEntry<float> _scrapEmissionStrength;

	private static ConfigEntry<float> _scrapLightIntensity;

	private static ConfigEntry<float> _scrapLightRange;

	private static ConfigEntry<float> _scrapPulseSpeed;

	private static ConfigEntry<float> _scrapTextOcclusionPadding;

	private static ConfigEntry<float> _scrapScreenEdgeMargin;

	private static ConfigEntry<float> _scrapTextVisibilityGraceTime;

	private static GameObject _cubeVisual;

	private static string _cubeVisualMeshSignature = string.Empty;

	private static bool _clientCubeVisualRefreshRunning;

	private static GameObject _cubeLightObject;

	private static GameObject _shipDoorClosedOverlay;

	private static GameObject _shipDoorFallbackPanel;

	private static Material _shipDoorFallbackMaterial;

	private static bool _shipDoorOverlaySourceMissingLogged;

	private static readonly Dictionary<Renderer, bool> _shipDoorOriginalRendererStates = new Dictionary<Renderer, bool>();

	private static readonly Dictionary<Component, ShipDoorAudioState> _shipDoorOriginalAudioStates = new Dictionary<Component, ShipDoorAudioState>();

	private static readonly Dictionary<GrabbableObject, ScrapGlowState> _glowingCubeScrap = new Dictionary<GrabbableObject, ScrapGlowState>();

	private static bool _localPlayerInsideCube;

	private static float _cubeScrapGlowTimer;

	private GUIStyle _insideCubeHudStyle;

	private static Vector3 _cubeCenter;

	private static Vector3 _cubeDimensions = new Vector3(6f, 6f, 6f);

	private static float _cubeEffectiveHeightOverride = -1f;

	private static bool _cubeDynamicFootprintReady;

	private static float _cubeDynamicFootprintCellSize = 0.75f;

	private static float _cubeDynamicFootprintBottomY;

	private static float _cubeDynamicFootprintHorizontalSize = 6f;

	private static float _cubeDynamicFootprintHeight = 6f;

	private static Bounds _cubeDynamicFootprintBounds;

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

	private static bool _cubeDynamicFootprintLocked;

	private static Vector3 _cubeDynamicFootprintLockedCenter;

	private static float _cubeDynamicFootprintLockedSize;

	private static float _cubeDynamicFootprintLockedHeight;

	private static float _cubeDynamicFootprintLockedCellSize;

	private static bool _cubeDynamicFootprintAuthoritativeFromHost;

	private static bool _cubeDynamicFootprintWaitingForAuthoritativeHostSync;

	private static bool _authoritativeCubeFootprintReadyForClients;

	private static int _hostCubeNetworkRoundId;

	private static int _currentCubeNetworkRoundId = -1;

	private static int _lastAcceptedCubeNetworkRoundId = -1;

	private static bool _cubeNetworkRoundActive;

	private static int _clientAcceptedCubeSyncSerial;

	private static Mesh _cubeDynamicFootprintSurfaceMesh;

	private static Mesh _cubeDynamicFootprintFrameMesh;

	private static Mesh _cubeDynamicFootprintBandMesh;

	private static bool _hasCube;

	private static bool _processedThisTakeoff;

	private static bool _messagesRegistered;

	private static bool _clientConnectCallbackRegistered;

	private static NetworkManager _registeredNetworkManager;

	private static NetworkManager _lastObservedNetworkManager;

	private static bool _networkSessionStateInitialized;

	private static bool _lastObservedIsListening;

	private static bool _lastObservedIsClient;

	private static bool _lastObservedIsServer;

	private static ulong _lastObservedLocalClientId = ulong.MaxValue;

	private static int _lastObservedThisClientPlayerId = -999;

	private static RoundManager _eventSubscribedRoundManager;

	private static bool _cubeExtractionPassed;

	private static float _cubeScrapValueTimer;

	private static int _currentCubeScrapRawValue;

	private static int _currentCubeScrapCount;

	private static int _pendingQuotaValue;

	private static int _pendingRawValue;

	private static int _pendingItemCount;

	private static bool _pendingQuotaApplied;

	private static Transform _scrapTextRoot;

	private static float _scrapVisibilityAttachTimer;

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

	private static bool _earlyVoteEligible;

	private static int _earlyVoteCount;

	private static int _earlyVoteRequired;

	private static bool _localEarlyVoteSubmitted;

	private static bool _earlyExtractionCountdownActive;

	private static float _earlyExtractionCountdownEndTime;

	private static int _earlyExtractionCountdownSecondsRemaining;

	private static bool _earlyExtractionTriggered;

	private static bool _baseGameVoteToLeaveUrgencyActive;

	private static bool _baseGameVoteToLeaveObserved;

	private static bool _normalShipLeaveAutomaticallyTimeCaptured;

	private static float _normalShipLeaveAutomaticallyTime = -1f;

	private static string _earlyVoteRosterKey = string.Empty;

	private static string _lastEarlyVoteSyncSignature = string.Empty;

	private static float _earlyVoteSyncTimer;

	private static bool _earlyVoteRequestPendingAck;

	private static int _earlyVoteRequestPendingPlayerId = -1;

	private static int _earlyVoteRequestSequence;

	private static float _earlyVoteRequestNextSendTime;

	private static float _earlyVoteRequestStopTime;

	private static bool _earlyVoteRequestTimeoutLogged;

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

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

	private static bool _roundStartCubeTeleportWindowOpen;

	private static int _roundStartCubeTeleportToken;

	private static int _localAppliedRoundStartCubeTeleportToken = -1;

	private static int _localCompletedRoundStartCubeTeleportToken = -1;

	private static int _localPendingRoundStartCubeTeleportToken = -1;

	private static float _lastLocalRoundStartCubeTeleportApplyTime;

	private static float _roundStartCubeTeleportWindowEndTime;

	private static bool _roundStartTeleportRequested;

	private static bool _roundStartTeleportFloorReady;

	private static bool _roundStartTeleportStarted;

	private static float _roundStartTeleportRequestTime;

	private static string _roundStartTeleportRequestReason = string.Empty;

	private static bool _roundStartTeleportRoutineRunning;

	private static bool _cubePostReadyPlacementFinalized;

	private static readonly Dictionary<ulong, Vector3> _roundStartTeleportTargetsByClient = new Dictionary<ulong, Vector3>();

	private static readonly Dictionary<ulong, float> _lastInsideFactoryBestEffortTimeByClient = new Dictionary<ulong, float>();

	private static readonly Dictionary<ulong, float> _lastInsideAudioLightingBestEffortTimeByClient = new Dictionary<ulong, float>();

	private static Type _audioReverbTriggerType;

	private static bool _audioReverbTriggerTypeSearched;

	private static readonly List<Vector3> _cachedInteriorEntranceReachabilityAnchors = new List<Vector3>();

	private static int _cachedInteriorEntranceReachabilityAnchorsFrame = -1;

	private static bool _legacyInputFailureLogged;

	private static bool _inputSystemFailureLogged;

	private static readonly HashSet<KeyCode> _trackedPressedVoteKeys = new HashSet<KeyCode>();

	private static float _debugStateSnapshotTimer;

	private static float _debugVoteBlockLogTimer;

	private static string _lastDebugVoteBlockReason = string.Empty;

	private static float _debugHostVoteGateLogTimer;

	private static string _lastDebugHostVoteGateReason = string.Empty;

	private static float _debugInsideCubeLogTimer;

	private static bool _lastDebugInsideCubeState;

	private static float _debugCubeSyncLogTimer;

	private static int _debugCubeSyncSendCount;

	private static int _debugCubeSyncReceiveCount;

	private static int _debugCubeSyncRejectCount;

	private static int _debugEarlyVoteRequestSendCount;

	private static int _debugEarlyVoteRequestReceiveCount;

	private static int _debugEarlyVoteSyncReceiveCount;

	private static int _debugRoundResetCount;

	private static int _debugShipHasLeftCount;

	private static bool _navMeshReflectionInitialized;

	private static bool _navMeshReflectionAvailable;

	private static bool _navMeshUnavailableLogged;

	private static Type _navMeshType;

	private static Type _navMeshHitType;

	private static MethodInfo _navMeshSamplePositionMethod;

	private static Type _navMeshPathType;

	private static MethodInfo _navMeshCalculatePathMethod;

	private static PropertyInfo _navMeshPathStatusProperty;

	private static bool _navMeshPathUnavailableLogged;

	private static bool _entrancePathUnavailableLogged;

	private static PropertyInfo _navMeshHitPositionProperty;

	private static FieldInfo _navMeshHitPositionField;

	private float _lateSyncTimer;

	private float _exitInteractionClampTimer;

	private void Awake()
	{
		//IL_0d47: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d51: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		_debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLogging", true, "Write detailed Extraction logs to the BepInEx console/log file.");
		_enableDebugCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableChatDebugCommands", true, "Enables chat commands: tpcube, endday, cubepos, countcube, extractionhelp, extractionstate, extractionplayers, extractionvotes.");
		_hostOnlyDebugCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "HostOnlyDebugCommands", true, "Only the host can use debug commands.");
		_debugStateSnapshots = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugStateSnapshots", true, "Writes periodic host/client diagnostic snapshots while a round is active. Use this when tracking desync after death/respawn.");
		_debugStateSnapshotInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Debug", "DebugStateSnapshotIntervalSeconds", 5f, "Seconds between periodic Extraction diagnostic snapshots while DebugStateSnapshots is true.");
		_debugVoteInput = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugVoteInput", true, "Logs every early extraction vote input gate and vote network request. Useful when the R prompt is visible but voting fails.");
		_debugInsideCubeChecks = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugInsideCubeChecks", true, "Logs local player inside Extraction Area checks when the result changes or periodically while active.");
		_debugCubeSyncPayloads = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugCubeSyncPayloads", true, "Logs detailed cube sync payload data including round id, cell counts, hashes, shape source, and stale sync rejection.");
		_cubeSize = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "CubeSizeMeters", 6f, "Horizontal size of the Extraction Area in meters. This controls width and depth only.");
		_cubeHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "CubeHeightMeters", 6f, "Requested roof height of the Extraction Area in meters. The mod will try to place the area where this full height fits. If it cannot, it retries at 80% height steps.");
		_cubeVisualAlpha = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "CubeVisualAlpha", 0.1f, "Transparency of the Extraction Area visual. 0 is invisible. 1 is solid. Default is 0.1.");
		_groundSnapRayStartHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "GroundSnapRayStartHeight", 6f, "How far above the picked node to start the downward floor snap ray.");
		_groundSnapRayDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "GroundSnapRayDistance", 20f, "How far below the picked node to search for floor when placing the Extraction Area.");
		_minDistanceFromShip = ((BaseUnityPlugin)this).Config.Bind<int>("Cube", "MinimumDistanceFromShip", 25, "Try to place the Extraction Area at least this many meters from the ship/elevator transform.");
		_cubePlacementWallClearance = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "CubePlacementWallClearanceMeters", 2.5f, "Extra empty space required past the Extraction Area footprint when choosing a spawn position. Higher values keep the placement farther from walls and map edges.");
		_cubePlacementFloorInset = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "CubePlacementFloorSampleInsetMeters", 0.2f, "How far inside the Extraction Area edge the floor safety samples are checked. Smaller values are stricter near voids and ledges.");
		_cubePlacementMaxFloorDrop = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "CubePlacementMaxFloorDropMeters", 1.25f, "Maximum allowed drop from an inside AI node to the snapped floor. Prevents snapping the Extraction Area down into pits, lower catwalk kill boxes, or the void.");
		_cubePlacementSearchRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "CubePlacementSearchRadiusMeters", 12f, "How far around each inside AI node to search for a centered safe cube position.");
		_cubePlacementSearchStep = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "CubePlacementSearchStepMeters", 1.5f, "Grid step used while searching around an inside AI node for a safe cube position.");
		_cubePlacementMaxCandidates = ((BaseUnityPlugin)this).Config.Bind<int>("Cube", "CubePlacementMaxCandidates", 8000, "Maximum number of cube placement candidates to test before using the safest emergency fallback.");
		_cubePlacementRejectDangerTriggers = ((BaseUnityPlugin)this).Config.Bind<bool>("Cube", "CubePlacementRejectDangerTriggers", true, "Reject cube positions overlapping kill boxes, death triggers, out-of-bounds triggers, pits, or other dangerous trigger volumes.");
		_cubePlacementRequireNavMesh = ((BaseUnityPlugin)this).Config.Bind<bool>("Cube", "CubePlacementRequireNavMesh", true, "Require the Extraction Area teleport spot to be close to the facility navmesh when Unity exposes it. This helps prevent out-of-map or unreachable placements.");
		_cubePlacementRequireReachablePathFromEntrance = ((BaseUnityPlugin)this).Config.Bind<bool>("Cube", "CubePlacementRequireReachablePathFromEntrance", true, "Require the Extraction Area center/player spawn spot to have a complete NavMesh path from an interior entrance. This is the main guard against inaccessible prefab corners, voids, and out-of-map rooms.");
		_cubePlacementRejectBackfaceGeometry = ((BaseUnityPlugin)this).Config.Bind<bool>("Cube", "CubePlacementRejectBackfaceGeometry", true, "Reject spots where physics rays detect the player is on the back side of one-sided level geometry. This catches many out-of-bounds/backside-wall cases.");
		_cubePlacementPreferScrapSpawnAnchors = ((BaseUnityPlugin)this).Config.Bind<bool>("Cube", "CubePlacementPreferScrapSpawnAnchors", true, "Legacy fallback only. Ignored when CubePlacementUseEntrancesAndFireExitsOnly is true.");
		_cubePlacementUseEntrancesAndFireExitsOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Cube", "CubePlacementUseEntrancesAndFireExitsOnly", true, "If true, the Extraction Area only spawns at the real underground/interior side of main entrances and fire exits. This avoids slow wide-area searches, surface door spawns, and unreachable prefab corners.");
		_adaptiveForcefieldBounds = ((BaseUnityPlugin)this).Config.Bind<bool>("Cube", "AdaptiveForcefieldBounds", true, "If true, the Extraction Area forcefield uses adaptive environment shaping instead of clipping through walls, ledges, pits, and non-navmesh space. Props are ignored.");
		_adaptiveForcefieldDynamicFootprint = ((BaseUnityPlugin)this).Config.Bind<bool>("Cube", "AdaptiveForcefieldDynamicFootprint", true, "If true, the Extraction Area uses a connected navmesh footprint that can form L shapes and wrap around invalid walkable space instead of only resizing as a box.");
		_forceFullSizeFootprint = ((BaseUnityPlugin)this).Config.Bind<bool>("Cube", "ForceFullSizeFootprint", false, "If true, the Extraction Area always uses the full configured box size for its visual and detection area. It will not shrink or carve around walls, ceilings, pits, or navmesh cells. This can clip through the level by design.");
		_adaptiveForcefieldFreezeShapeAfterBuild = ((BaseUnityPlugin)this).Config.Bind<bool>("Cube", "AdaptiveForcefieldFreezeShapeAfterBuild", true, "If true, the Extraction Area calculates its dynamic footprint once when spawned, then never recalculates it until the Extraction Area is moved for a new round.");
		_adaptiveForcefieldSimplifyMesh = ((BaseUnityPlugin)this).Config.Bind<bool>("Cube", "AdaptiveForcefieldSimplifyMesh", true, "If true, merges dynamic forcefield cell slices into long faces and main corners after the footprint is finalized.");
		_adaptiveForcefieldClearance = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "AdaptiveForcefieldClearanceMeters", 0.18f, "Gap kept between the forcefield wall and nearby level geometry or ceiling when adaptive bounds are enabled.");
		_adaptiveForcefieldMinimumSide = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "AdaptiveForcefieldMinimumSideMeters", 1.25f, "Smallest horizontal side the Extraction Area forcefield may shrink to when adaptive bounds are enabled.");
		_adaptiveForcefieldFloorProbeStep = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "AdaptiveForcefieldFloorProbeStepMeters", 0.45f, "Step size used to stop the adaptive Extraction Area bounds before non-navmesh cells, floor gaps, pits, or unsupported catwalk edges.");
		_adaptiveForcefieldBoundaryInset = ((BaseUnityPlugin)this).Config.Bind<float>("Cube", "AdaptiveForcefieldBoundaryInsetMeters", 0f, "Deprecated. Boundary inset is ignored and kept at 0 so the forcefield footprint is not artificially shrunken.");
		if (_adaptiveForcefieldBoundaryInset != null && _adaptiveForcefieldBoundaryInset.Value > 0f)
		{
			_adaptiveForcefieldBoundaryInset.Value = 0f;
		}
		_showInsideCubeHud = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowInsideCubeHud", true, "Show an on-screen message while your local player is inside the Extraction Area.");
		_showInsideCubeTips = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "ShowInsideCubeEnterExitTips", true, "Legacy config. No longer used because cube detection now only updates the HUD overlay.");
		_teleportPlayersToCubeOnRoundStart = ((BaseUnityPlugin)this).Config.Bind<bool>("Rules", "TeleportPlayersToCubeOnRoundStart", true, "Master toggle for round-start facility teleport. If false, players stay wherever vanilla puts them.");
		_roundStartTeleportToCube = ((BaseUnityPlugin)this).Config.Bind<bool>("Rules", "RoundStartTeleportToCube", true, "If true, round-start teleport sends players to the Extraction Area. If false, players are sent to a random safe facility position and must find the Extraction Area.");
		_roundStartTeleportDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Rules", "RoundStartCubeTeleportDelaySeconds", 1.25f, "Delay after the game reports players finished generating the floor before the teleport routine begins.");
		_roundStartTeleportRetrySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Rules", "RoundStartCubeTeleportRetrySeconds", 12f, "How long the host keeps resending the same round-start teleport token. Clients only apply that token once.");
		_roundStartTeleportRetryInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Rules", "RoundStartCubeTeleportRetryIntervalSeconds", 0.75f, "How often the host resends the same round-start teleport token during the ret

plugins/Extraction1/RandomSuit.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ScrapVisbility")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ScrapVisbility")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("8a6853bd-bdc9-4741-95c7-5aa2c8c6a6f9")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace YourFurnace.RandomStartSuit;

[BepInPlugin("YourFurnace.RandomStartSuit", "RandomStartSuit", "1.6.0")]
public class Plugin : BaseUnityPlugin
{
	private sealed class SuitAssignment
	{
		public ulong ClientId;

		public int PlayerIndex;

		public int SuitId;

		public float ReapplyUntil;
	}

	[HarmonyPatch(typeof(UnlockableSuit), "SwitchSuitServerRpc")]
	private static class SwitchSuitServerRpcPatch
	{
		private static void Postfix(UnlockableSuit __instance, int playerID)
		{
			try
			{
				if (!((Object)(object)Instance == (Object)null) && !((Object)(object)__instance == (Object)null) && suppressSuitCapture <= 0)
				{
					Instance.HostObservedSuitChangeByPlayerIndex(playerID, __instance.suitID, "SwitchSuitServerRpc");
				}
			}
			catch (Exception ex)
			{
				if (Log != null)
				{
					Log.LogWarning((object)("SwitchSuitServerRpc patch failed: " + ShortException(ex)));
				}
			}
		}
	}

	[HarmonyPatch(typeof(UnlockableSuit), "SwitchSuitToThis")]
	private static class SwitchSuitToThisPatch
	{
		private static void Postfix(UnlockableSuit __instance, PlayerControllerB playerWhoTriggered)
		{
			try
			{
				if (!((Object)(object)Instance == (Object)null) && !((Object)(object)__instance == (Object)null) && suppressSuitCapture <= 0)
				{
					Instance.HostObservedSuitChangeForPlayer(playerWhoTriggered, __instance.suitID, "SwitchSuitToThis");
				}
			}
			catch (Exception ex)
			{
				if (Log != null)
				{
					Log.LogWarning((object)("SwitchSuitToThis patch failed: " + ShortException(ex)));
				}
			}
		}
	}

	[HarmonyPatch(typeof(UnlockableSuit), "SwitchSuitForPlayer")]
	private static class SwitchSuitForPlayerPatch
	{
		private static void Postfix(PlayerControllerB player, int suitID)
		{
			try
			{
				if (!((Object)(object)Instance == (Object)null) && suppressSuitCapture <= 0)
				{
					Instance.HostObservedSuitChangeForPlayer(player, suitID, "SwitchSuitForPlayer");
				}
			}
			catch (Exception ex)
			{
				if (Log != null)
				{
					Log.LogWarning((object)("SwitchSuitForPlayer patch failed: " + ShortException(ex)));
				}
			}
		}
	}

	public const string PluginGuid = "YourFurnace.RandomStartSuit";

	public const string PluginName = "RandomStartSuit";

	public const string PluginVersion = "1.6.0";

	private const string AssignmentMessage = "YourFurnace.RandomStartSuit.Assignments.v6";

	private const string RequestMessage = "YourFurnace.RandomStartSuit.RequestAssignments.v6";

	private const string AllowedFirstRandomMessage = "YourFurnace.RandomStartSuit.AllowedFirstRandom.v6";

	private const string FirstRandomPickMessage = "YourFurnace.RandomStartSuit.FirstRandomPick.v6";

	internal static Plugin Instance;

	private static ManualLogSource Log;

	private static readonly Random Rng = new Random();

	private static int suppressSuitCapture;

	private ConfigEntry<string> allowedSuitsCsv;

	private ConfigEntry<float> firstAssignmentDelay;

	private ConfigEntry<float> hostScanInterval;

	private ConfigEntry<float> hostFallbackFirstRandomSeconds;

	private ConfigEntry<float> clientRequestRetrySeconds;

	private ConfigEntry<int> clientRequestMaxAttempts;

	private ConfigEntry<float> eventReapplySeconds;

	private ConfigEntry<float> eventReapplyInterval;

	private ConfigEntry<bool> onlyAssignInShipLobby;

	private ConfigEntry<bool> logDebug;

	private readonly Dictionary<ulong, SuitAssignment> hostOfficialByClientId = new Dictionary<ulong, SuitAssignment>();

	private readonly Dictionary<ulong, SuitAssignment> clientOfficialByClientId = new Dictionary<ulong, SuitAssignment>();

	private readonly Dictionary<ulong, float> firstSeenAtByClientId = new Dictionary<ulong, float>();

	private readonly Dictionary<ulong, float> allowedOfferSentAtByClientId = new Dictionary<ulong, float>();

	private readonly Dictionary<ulong, int> pendingFirstPickByClientId = new Dictionary<ulong, int>();

	private NetworkManager registeredNetworkManager;

	private Harmony harmony;

	private int currentStartOfRoundId = -1;

	private float nextHostScanAt;

	private float nextLocalApplyAt;

	private float nextClientRequestAt;

	private int clientRequestAttempts;

	private bool clientFirstPickSent;

	private bool clientFirstPickAccepted;

	private void Awake()
	{
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0189: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		allowedSuitsCsv = ((BaseUnityPlugin)this).Config.Bind<string>("Suit Selection", "Allowed suit names", "FemaleBlue,FemaleClothed,FemaleLime,FemaleOrange,FemalePink,FemaleStar,RealMan", "Comma separated exact unlockable names used only for the first random suit. Manual suit changes may use any valid suit.");
		firstAssignmentDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Assignment", "First assignment delay seconds", 3f, "How long the host waits after a player appears before choosing that player's first random suit.");
		hostScanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Assignment", "Host scan interval seconds", 0.5f, "How often the host checks for newly joined players that still need their first random suit.");
		hostFallbackFirstRandomSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Assignment", "Host fallback first random seconds", 8f, "If the client never answers the allowed-list handshake, the host picks from the allowed list after this many seconds. This prevents a player from being stuck in the vanilla orange suit.");
		clientRequestRetrySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Networking", "Client request retry seconds", 2f, "How often a joining client asks the host for the official suit table / allowed random list until the host gives that client an official suit.");
		clientRequestMaxAttempts = ((BaseUnityPlugin)this).Config.Bind<int>("Networking", "Client request max attempts", 8, "Maximum handshake request attempts a client sends after joining. This is not a forever loop.");
		onlyAssignInShipLobby = ((BaseUnityPlugin)this).Config.Bind<bool>("Assignment", "Only assign while in ship lobby", false, "If true, the host stops giving first random suits once the ship leaves the lobby phase.");
		eventReapplySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Networking", "Event local apply seconds", 2.5f, "After the host sends a suit update, each machine reapplies that official update briefly to beat vanilla spawn/default-suit timing. This is not a forever loop.");
		eventReapplyInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Networking", "Event local apply interval seconds", 0.25f, "How often a machine reapplies the most recent official update during the short event window.");
		logDebug = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Log debug info", true, "Logs first random assignments, manual suit changes captured by the host, and official table sends.");
		try
		{
			harmony = new Harmony("YourFurnace.RandomStartSuit");
			harmony.PatchAll();
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not apply Harmony patches: " + ShortException(ex)));
		}
		SceneManager.sceneLoaded += OnSceneLoaded;
		((BaseUnityPlugin)this).Logger.LogInfo((object)"RandomStartSuit 1.6.0 loaded. Host authoritative. First join ignores vanilla default suit and uses random handshake/fallback. Event sync only.");
	}

	private void OnDestroy()
	{
		SceneManager.sceneLoaded -= OnSceneLoaded;
		UnregisterMessages();
		try
		{
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
		catch
		{
		}
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		if (IsMenuScene(((Scene)(ref scene)).name))
		{
			ClearAllState();
			DebugLog("Menu scene loaded. Assignment state cleared.");
		}
	}

	private void Update()
	{
		EnsureMessageHandlers();
		StartOfRound startOfRound = GetStartOfRound();
		if ((Object)(object)startOfRound == (Object)null)
		{
			return;
		}
		int instanceID = ((Object)startOfRound).GetInstanceID();
		if (instanceID != currentStartOfRoundId)
		{
			currentStartOfRoundId = instanceID;
			hostOfficialByClientId.Clear();
			clientOfficialByClientId.Clear();
			firstSeenAtByClientId.Clear();
			allowedOfferSentAtByClientId.Clear();
			pendingFirstPickByClientId.Clear();
			nextHostScanAt = Time.realtimeSinceStartup + 0.25f;
			nextLocalApplyAt = 0f;
			nextClientRequestAt = Time.realtimeSinceStartup + 1f;
			clientRequestAttempts = 0;
			clientFirstPickSent = false;
			clientFirstPickAccepted = false;
			DebugLog("New StartOfRound detected. Suit state reset for this round instance.");
		}
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || !singleton.IsListening)
		{
			return;
		}
		if (singleton.IsServer || singleton.IsHost)
		{
			if (Time.realtimeSinceStartup >= nextHostScanAt)
			{
				nextHostScanAt = Time.realtimeSinceStartup + Mathf.Max(0.15f, hostScanInterval.Value);
				HostScanForFirstRandomSuits(startOfRound);
				RemoveStaleHostAssignments(startOfRound);
			}
		}
		else if (singleton.IsClient && !clientFirstPickAccepted && Time.realtimeSinceStartup >= nextClientRequestAt && clientRequestAttempts < Mathf.Max(1, clientRequestMaxAttempts.Value))
		{
			clientRequestAttempts++;
			nextClientRequestAt = Time.realtimeSinceStartup + Mathf.Max(0.5f, clientRequestRetrySeconds.Value);
			SendAssignmentRequestToHost();
		}
		if (Time.realtimeSinceStartup >= nextLocalApplyAt)
		{
			nextLocalApplyAt = Time.realtimeSinceStartup + Mathf.Max(0.05f, eventReapplyInterval.Value);
			if (singleton.IsServer || singleton.IsHost)
			{
				ApplyAssignmentsLocally(startOfRound, hostOfficialByClientId.Values, "host-event-reapply", logResult: false);
			}
			else
			{
				ApplyAssignmentsLocally(startOfRound, clientOfficialByClientId.Values, "client-event-reapply", logResult: false);
			}
		}
	}

	private void ClearAllState()
	{
		hostOfficialByClientId.Clear();
		clientOfficialByClientId.Clear();
		firstSeenAtByClientId.Clear();
		allowedOfferSentAtByClientId.Clear();
		pendingFirstPickByClientId.Clear();
		currentStartOfRoundId = -1;
		clientRequestAttempts = 0;
		clientFirstPickSent = false;
		clientFirstPickAccepted = false;
		nextClientRequestAt = 0f;
		nextHostScanAt = 0f;
		nextLocalApplyAt = 0f;
	}

	private void HostScanForFirstRandomSuits(StartOfRound start)
	{
		if (onlyAssignInShipLobby.Value && !start.inShipPhase)
		{
			return;
		}
		List<int> allowedSuitIds = GetAllowedSuitIds(start);
		if (allowedSuitIds.Count == 0)
		{
			DebugLog("No allowed first-random suits found from config: " + allowedSuitsCsv.Value);
			return;
		}
		PlayerControllerB[] allPlayerScripts = start.allPlayerScripts;
		if (allPlayerScripts == null)
		{
			return;
		}
		NetworkManager singleton = NetworkManager.Singleton;
		ulong num = (((Object)(object)singleton != (Object)null) ? singleton.LocalClientId : 0);
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		bool flag = false;
		for (int i = 0; i < allPlayerScripts.Length; i++)
		{
			PlayerControllerB player = allPlayerScripts[i];
			if (!IsRealPlayer(player))
			{
				continue;
			}
			ulong playerClientId = GetPlayerClientId(player, i);
			if (hostOfficialByClientId.ContainsKey(playerClientId))
			{
				continue;
			}
			if (!firstSeenAtByClientId.ContainsKey(playerClientId))
			{
				firstSeenAtByClientId[playerClientId] = realtimeSinceStartup;
				DebugLog("Host saw new player for first random timer. playerIndex=" + i + " clientId=" + playerClientId);
			}
			else
			{
				if (realtimeSinceStartup - firstSeenAtByClientId[playerClientId] < Mathf.Max(0f, firstAssignmentDelay.Value))
				{
					continue;
				}
				int value;
				if (playerClientId == num)
				{
					value = allowedSuitIds[Rng.Next(allowedSuitIds.Count)];
					flag |= HostSetOfficialFirstRandom(start, player, i, playerClientId, value, "host-local-random");
					continue;
				}
				if (pendingFirstPickByClientId.TryGetValue(playerClientId, out value))
				{
					if (!allowedSuitIds.Contains(value))
					{
						DebugLog("Host rejected invalid pending first pick from clientId=" + playerClientId + " suitID=" + value + ". Picking fallback from host list.");
						value = allowedSuitIds[Rng.Next(allowedSuitIds.Count)];
					}
					pendingFirstPickByClientId.Remove(playerClientId);
					flag |= HostSetOfficialFirstRandom(start, player, i, playerClientId, value, "client-picked-from-host-list");
					continue;
				}
				float value2;
				bool flag2 = allowedOfferSentAtByClientId.TryGetValue(playerClientId, out value2);
				if (!flag2 || realtimeSinceStartup - value2 >= 2f)
				{
					SendAllowedFirstRandomListToClient(playerClientId, allowedSuitIds);
					allowedOfferSentAtByClientId[playerClientId] = realtimeSinceStartup;
					if (!flag2)
					{
						value2 = realtimeSinceStartup;
					}
				}
				if (realtimeSinceStartup - firstSeenAtByClientId[playerClientId] >= Mathf.Max(firstAssignmentDelay.Value + 1f, hostFallbackFirstRandomSeconds.Value))
				{
					value = allowedSuitIds[Rng.Next(allowedSuitIds.Count)];
					DebugLog("Host fallback picked first random suit for clientId=" + playerClientId + " because no handshake pick arrived.");
					flag |= HostSetOfficialFirstRandom(start, player, i, playerClientId, value, "host-fallback-random");
				}
			}
		}
		if (flag)
		{
			BroadcastAssignmentsToClients();
		}
	}

	private bool HostSetOfficialFirstRandom(StartOfRound start, PlayerControllerB player, int playerIndex, ulong clientId, int suitId, string reason)
	{
		if (hostOfficialByClientId.ContainsKey(clientId))
		{
			return false;
		}
		if (!IsValidSuitId(start, suitId))
		{
			return false;
		}
		SuitAssignment suitAssignment = new SuitAssignment
		{
			ClientId = clientId,
			PlayerIndex = playerIndex,
			SuitId = suitId,
			ReapplyUntil = Time.realtimeSinceStartup + Mathf.Max(0.5f, eventReapplySeconds.Value)
		};
		hostOfficialByClientId[clientId] = suitAssignment;
		firstSeenAtByClientId[clientId] = Time.realtimeSinceStartup;
		DebugLog("Host accepted first random suit from " + reason + ": Player #" + playerIndex + " clientId=" + clientId + " suit=" + GetUnlockableName(start, suitId) + " id=" + suitId);
		ApplyAssignmentLocally(start, suitAssignment, "host-first-random", logResult: true);
		return true;
	}

	private void RemoveStaleHostAssignments(StartOfRound start)
	{
		PlayerControllerB[] allPlayerScripts = start.allPlayerScripts;
		if (allPlayerScripts == null)
		{
			return;
		}
		HashSet<ulong> liveClientIds = new HashSet<ulong>();
		for (int i = 0; i < allPlayerScripts.Length; i++)
		{
			if (IsRealPlayer(allPlayerScripts[i]))
			{
				liveClientIds.Add(GetPlayerClientId(allPlayerScripts[i], i));
			}
		}
		List<ulong> list = hostOfficialByClientId.Keys.Where((ulong clientId) => !liveClientIds.Contains(clientId)).ToList();
		if (list.Count == 0)
		{
			return;
		}
		foreach (ulong item in list)
		{
			hostOfficialByClientId.Remove(item);
			firstSeenAtByClientId.Remove(item);
			allowedOfferSentAtByClientId.Remove(item);
			pendingFirstPickByClientId.Remove(item);
			DebugLog("Host removed stale suit assignment for disconnected clientId=" + item);
		}
		BroadcastAssignmentsToClients();
	}

	internal void HostObservedSuitChangeByPlayerIndex(int playerIndex, int suitId, string reason)
	{
		if (suppressSuitCapture > 0)
		{
			return;
		}
		NetworkManager singleton = NetworkManager.Singleton;
		if (!((Object)(object)singleton == (Object)null) && (singleton.IsServer || singleton.IsHost))
		{
			StartOfRound startOfRound = GetStartOfRound();
			if (!((Object)(object)startOfRound == (Object)null) && startOfRound.allPlayerScripts != null && playerIndex >= 0 && playerIndex < startOfRound.allPlayerScripts.Length)
			{
				PlayerControllerB player = startOfRound.allPlayerScripts[playerIndex];
				HostObservedSuitChangeForPlayer(player, suitId, reason);
			}
		}
	}

	internal void HostObservedSuitChangeForPlayer(PlayerControllerB player, int suitId, string reason)
	{
		if (suppressSuitCapture > 0)
		{
			return;
		}
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || (!singleton.IsServer && !singleton.IsHost))
		{
			return;
		}
		StartOfRound startOfRound = GetStartOfRound();
		if ((Object)(object)startOfRound == (Object)null || !IsRealPlayer(player) || !IsValidSuitId(startOfRound, suitId))
		{
			return;
		}
		int playerIndex = GetPlayerIndex(startOfRound, player);
		if (playerIndex >= 0)
		{
			ulong playerClientId = GetPlayerClientId(player, playerIndex);
			SuitAssignment value;
			if (!hostOfficialByClientId.ContainsKey(playerClientId))
			{
				DebugLog("Host ignored pre-random suit change from " + reason + " for unassigned playerIndex=" + playerIndex + " clientId=" + playerClientId + " suit=" + GetUnlockableName(startOfRound, suitId) + " id=" + suitId);
			}
			else if (!hostOfficialByClientId.TryGetValue(playerClientId, out value) || value.SuitId != suitId || value.PlayerIndex != playerIndex)
			{
				SuitAssignment suitAssignment = new SuitAssignment
				{
					ClientId = playerClientId,
					PlayerIndex = playerIndex,
					SuitId = suitId,
					ReapplyUntil = Time.realtimeSinceStartup + Mathf.Max(0.5f, eventReapplySeconds.Value)
				};
				hostOfficialByClientId[playerClientId] = suitAssignment;
				firstSeenAtByClientId[playerClientId] = Time.realtimeSinceStartup;
				DebugLog("Host accepted suit change from " + reason + ": Player #" + playerIndex + " clientId=" + playerClientId + " suit=" + GetUnlockableName(startOfRound, suitId) + " id=" + suitId);
				ApplyAssignmentLocally(startOfRound, suitAssignment, "host-accepted-change", logResult: false);
				BroadcastAssignmentsToClients();
			}
		}
	}

	private void ApplyAssignmentsLocally(StartOfRound start, IEnumerable<SuitAssignment> assignments, string reason, bool logResult)
	{
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		foreach (SuitAssignment item in assignments.ToList())
		{
			if (!(item.ReapplyUntil <= 0f) && !(realtimeSinceStartup > item.ReapplyUntil))
			{
				ApplyAssignmentLocally(start, item, reason, logResult);
			}
		}
	}

	private bool ApplyAssignmentLocally(StartOfRound start, SuitAssignment assignment, string reason, bool logResult)
	{
		PlayerControllerB val = FindPlayer(start, assignment);
		if ((Object)(object)val == (Object)null)
		{
			if (logResult)
			{
				DebugLog("Apply " + reason + " failed. Player not found for playerIndex=" + assignment.PlayerIndex + " clientId=" + assignment.ClientId);
			}
			return false;
		}
		bool result = ApplySuitToPlayer(val, assignment.SuitId);
		if (logResult || (logDebug.Value && reason == "client-received"))
		{
			DebugLog("Apply " + reason + " playerIndex=" + assignment.PlayerIndex + " clientId=" + assignment.ClientId + " suit=" + GetUnlockableName(start, assignment.SuitId) + " id=" + assignment.SuitId + " applied=" + result);
		}
		return result;
	}

	private bool ApplySuitToPlayer(PlayerControllerB player, int suitId)
	{
		if ((Object)(object)player == (Object)null || suitId < 0)
		{
			return false;
		}
		suppressSuitCapture++;
		try
		{
			player.currentSuitID = suitId;
			UnlockableSuit.SwitchSuitForPlayer(player, suitId, false);
			player.currentSuitID = suitId;
			return true;
		}
		catch (Exception ex)
		{
			DebugLog("SwitchSuitForPlayer failed for suitID=" + suitId + ": " + ShortException(ex));
		}
		finally
		{
			suppressSuitCapture = Math.Max(0, suppressSuitCapture - 1);
		}
		try
		{
			player.currentSuitID = suitId;
			return true;
		}
		catch
		{
			return false;
		}
	}

	private void EnsureMessageHandlers()
	{
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Expected O, but got Unknown
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Expected O, but got Unknown
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Expected O, but got Unknown
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Expected O, but got Unknown
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || !singleton.IsListening)
		{
			if ((Object)(object)registeredNetworkManager != (Object)null)
			{
				UnregisterMessages();
			}
		}
		else if (!((Object)(object)registeredNetworkManager == (Object)(object)singleton))
		{
			UnregisterMessages();
			try
			{
				singleton.CustomMessagingManager.RegisterNamedMessageHandler("YourFurnace.RandomStartSuit.Assignments.v6", new HandleNamedMessageDelegate(OnAssignmentsMessageReceived));
				singleton.CustomMessagingManager.RegisterNamedMessageHandler("YourFurnace.RandomStartSuit.RequestAssignments.v6", new HandleNamedMessageDelegate(OnRequestMessageReceived));
				singleton.CustomMessagingManager.RegisterNamedMessageHandler("YourFurnace.RandomStartSuit.AllowedFirstRandom.v6", new HandleNamedMessageDelegate(OnAllowedFirstRandomListMessageReceived));
				singleton.CustomMessagingManager.RegisterNamedMessageHandler("YourFurnace.RandomStartSuit.FirstRandomPick.v6", new HandleNamedMessageDelegate(OnFirstRandomPickMessageReceived));
				registeredNetworkManager = singleton;
				DebugLog("Registered RandomStartSuit named messages.");
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not register named messages: " + ShortException(ex)));
			}
		}
	}

	private void UnregisterMessages()
	{
		if ((Object)(object)registeredNetworkManager == (Object)null || registeredNetworkManager.CustomMessagingManager == null)
		{
			registeredNetworkManager = null;
			return;
		}
		try
		{
			registeredNetworkManager.CustomMessagingManager.UnregisterNamedMessageHandler("YourFurnace.RandomStartSuit.Assignments.v6");
			registeredNetworkManager.CustomMessagingManager.UnregisterNamedMessageHandler("YourFurnace.RandomStartSuit.RequestAssignments.v6");
			registeredNetworkManager.CustomMessagingManager.UnregisterNamedMessageHandler("YourFurnace.RandomStartSuit.AllowedFirstRandom.v6");
			registeredNetworkManager.CustomMessagingManager.UnregisterNamedMessageHandler("YourFurnace.RandomStartSuit.FirstRandomPick.v6");
		}
		catch
		{
		}
		registeredNetworkManager = null;
	}

	private void BroadcastAssignmentsToClients()
	{
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || !singleton.IsListening || singleton.CustomMessagingManager == null || (!singleton.IsServer && !singleton.IsHost))
		{
			return;
		}
		try
		{
			foreach (ulong connectedClientsId in singleton.ConnectedClientsIds)
			{
				if (connectedClientsId != singleton.LocalClientId)
				{
					FastBufferWriter val = BuildAssignmentsWriter();
					try
					{
						singleton.CustomMessagingManager.SendNamedMessage("YourFurnace.RandomStartSuit.Assignments.v6", connectedClientsId, val, (NetworkDelivery)3);
					}
					finally
					{
						((IDisposable)(FastBufferWriter)(ref val)).Dispose();
					}
				}
			}
			DebugLog("Host broadcast official suit table with " + hostOfficialByClientId.Count + " assignment(s).");
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Could not broadcast assignments: " + ShortException(ex)));
		}
	}

	private void SendAssignmentsToClient(ulong targetClientId)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || !singleton.IsListening || singleton.CustomMessagingManager == null || (!singleton.IsServer && !singleton.IsHost))
		{
			return;
		}
		try
		{
			FastBufferWriter val = BuildAssignmentsWriter();
			try
			{
				singleton.CustomMessagingManager.SendNamedMessage("YourFurnace.RandomStartSuit.Assignments.v6", targetClientId, val, (NetworkDelivery)3);
			}
			finally
			{
				((IDisposable)(FastBufferWriter)(ref val)).Dispose();
			}
			DebugLog("Host answered table request from clientId=" + targetClientId + " with " + hostOfficialByClientId.Count + " assignment(s).");
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Could not send assignments to clientId=" + targetClientId + ": " + ShortException(ex)));
		}
	}

	private FastBufferWriter BuildAssignmentsWriter()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: 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_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		int count = hostOfficialByClientId.Count;
		FastBufferWriter result = default(FastBufferWriter);
		((FastBufferWriter)(ref result))..ctor(16 + count * 24, (Allocator)2, -1);
		((FastBufferWriter)(ref result)).WriteValueSafe<int>(ref count, default(ForPrimitives));
		foreach (SuitAssignment value in hostOfficialByClientId.Values)
		{
			((FastBufferWriter)(ref result)).WriteValueSafe<ulong>(ref value.ClientId, default(ForPrimitives));
			((FastBufferWriter)(ref result)).WriteValueSafe<int>(ref value.PlayerIndex, default(ForPrimitives));
			((FastBufferWriter)(ref result)).WriteValueSafe<int>(ref value.SuitId, default(ForPrimitives));
		}
		return result;
	}

	private void SendAssignmentRequestToHost()
	{
		//IL_0040: 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_005b: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || !singleton.IsListening || singleton.CustomMessagingManager == null)
		{
			return;
		}
		try
		{
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(4, (Allocator)2, -1);
			try
			{
				int num = 1;
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives));
				singleton.CustomMessagingManager.SendNamedMessage("YourFurnace.RandomStartSuit.RequestAssignments.v6", 0uL, val, (NetworkDelivery)3);
			}
			finally
			{
				((IDisposable)(FastBufferWriter)(ref val)).Dispose();
			}
			DebugLog("Client requested official suit table from host.");
		}
		catch (Exception ex)
		{
			DebugLog("Could not request assignments from host: " + ShortException(ex));
		}
	}

	private void OnRequestMessageReceived(ulong senderClientId, FastBufferReader reader)
	{
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || (!singleton.IsServer && !singleton.IsHost))
		{
			return;
		}
		DebugLog("Host received official table request from clientId=" + senderClientId);
		SendAssignmentsToClient(senderClientId);
		StartOfRound startOfRound = GetStartOfRound();
		if (!((Object)(object)startOfRound == (Object)null) && (!onlyAssignInShipLobby.Value || startOfRound.inShipPhase) && !hostOfficialByClientId.ContainsKey(senderClientId))
		{
			List<int> allowedSuitIds = GetAllowedSuitIds(startOfRound);
			if (allowedSuitIds.Count > 0)
			{
				SendAllowedFirstRandomListToClient(senderClientId, allowedSuitIds);
				allowedOfferSentAtByClientId[senderClientId] = Time.realtimeSinceStartup;
			}
		}
	}

	private void SendAllowedFirstRandomListToClient(ulong targetClientId, List<int> allowedSuitIds)
	{
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || !singleton.IsListening || singleton.CustomMessagingManager == null || (!singleton.IsServer && !singleton.IsHost) || allowedSuitIds == null || allowedSuitIds.Count == 0)
		{
			return;
		}
		try
		{
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(8 + allowedSuitIds.Count * 4, (Allocator)2, -1);
			try
			{
				int count = allowedSuitIds.Count;
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref count, default(ForPrimitives));
				for (int i = 0; i < allowedSuitIds.Count; i++)
				{
					count = allowedSuitIds[i];
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref count, default(ForPrimitives));
				}
				singleton.CustomMessagingManager.SendNamedMessage("YourFurnace.RandomStartSuit.AllowedFirstRandom.v6", targetClientId, val, (NetworkDelivery)3);
			}
			finally
			{
				((IDisposable)(FastBufferWriter)(ref val)).Dispose();
			}
			DebugLog("Host sent allowed first-random suit list to clientId=" + targetClientId + " count=" + allowedSuitIds.Count);
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Could not send allowed first-random list to clientId=" + targetClientId + ": " + ShortException(ex)));
		}
	}

	private void OnAllowedFirstRandomListMessageReceived(ulong senderClientId, FastBufferReader reader)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || singleton.IsServer)
		{
			return;
		}
		try
		{
			int num = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
			if (num > 0)
			{
				List<int> list = new List<int>(num);
				int item = default(int);
				for (int i = 0; i < num; i++)
				{
					((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref item, default(ForPrimitives));
					list.Add(item);
				}
				if (clientFirstPickAccepted || clientOfficialByClientId.ContainsKey(singleton.LocalClientId))
				{
					DebugLog("Client ignored allowed first-random list because host already sent an official suit.");
					return;
				}
				if (clientFirstPickSent && clientRequestAttempts < 2)
				{
					DebugLog("Client ignored duplicate allowed first-random list because a first pick was already sent.");
					return;
				}
				int suitId = list[Rng.Next(list.Count)];
				clientFirstPickSent = true;
				SendFirstRandomPickToHost(suitId);
			}
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Could not read allowed first-random list: " + ShortException(ex)));
		}
	}

	private void SendFirstRandomPickToHost(int suitId)
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || !singleton.IsListening || singleton.CustomMessagingManager == null)
		{
			return;
		}
		try
		{
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(4, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref suitId, default(ForPrimitives));
				singleton.CustomMessagingManager.SendNamedMessage("YourFurnace.RandomStartSuit.FirstRandomPick.v6", 0uL, val, (NetworkDelivery)3);
			}
			finally
			{
				((IDisposable)(FastBufferWriter)(ref val)).Dispose();
			}
			DebugLog("Client picked first random suitID=" + suitId + " from host allowed list and sent it for host approval.");
		}
		catch (Exception ex)
		{
			DebugLog("Could not send first-random pick to host: " + ShortException(ex));
		}
	}

	private void OnFirstRandomPickMessageReceived(ulong senderClientId, FastBufferReader reader)
	{
		//IL_0036: 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)
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || (!singleton.IsServer && !singleton.IsHost))
		{
			return;
		}
		try
		{
			int requestedSuitId = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref requestedSuitId, default(ForPrimitives));
			HostAcceptFirstRandomChoice(senderClientId, requestedSuitId);
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Could not read first-random pick from clientId=" + senderClientId + ": " + ShortException(ex)));
		}
	}

	private void HostAcceptFirstRandomChoice(ulong senderClientId, int requestedSuitId)
	{
		StartOfRound startOfRound = GetStartOfRound();
		if ((Object)(object)startOfRound == (Object)null || startOfRound.allPlayerScripts == null)
		{
			pendingFirstPickByClientId[senderClientId] = requestedSuitId;
			DebugLog("Host queued first-random pick from clientId=" + senderClientId + " because StartOfRound/player list is not ready yet.");
			return;
		}
		if (hostOfficialByClientId.ContainsKey(senderClientId))
		{
			DebugLog("Host ignored first-random pick from clientId=" + senderClientId + " because that client already has an official suit.");
			SendAssignmentsToClient(senderClientId);
			return;
		}
		if (onlyAssignInShipLobby.Value && !startOfRound.inShipPhase)
		{
			DebugLog("Host ignored first-random pick from clientId=" + senderClientId + " because ship lobby assignment is disabled now.");
			return;
		}
		List<int> allowedSuitIds = GetAllowedSuitIds(startOfRound);
		int num = requestedSuitId;
		if (!allowedSuitIds.Contains(num))
		{
			if (allowedSuitIds.Count == 0)
			{
				DebugLog("Host rejected first-random pick from clientId=" + senderClientId + " because no allowed suits exist.");
				return;
			}
			DebugLog("Host rejected first-random pick from clientId=" + senderClientId + " suitID=" + num + ". Picking fallback from host list.");
			num = allowedSuitIds[Rng.Next(allowedSuitIds.Count)];
		}
		int playerIndex;
		PlayerControllerB val = FindPlayerByClientId(startOfRound, senderClientId, out playerIndex);
		if ((Object)(object)val == (Object)null)
		{
			pendingFirstPickByClientId[senderClientId] = num;
			DebugLog("Host queued first-random pick from clientId=" + senderClientId + " because player object was not ready yet.");
		}
		else if (HostSetOfficialFirstRandom(startOfRound, val, playerIndex, senderClientId, num, "client-picked-from-host-list"))
		{
			BroadcastAssignmentsToClients();
		}
	}

	private void OnAssignmentsMessageReceived(ulong senderClientId, FastBufferReader reader)
	{
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || singleton.IsServer)
		{
			return;
		}
		StartOfRound startOfRound = GetStartOfRound();
		if ((Object)(object)startOfRound == (Object)null)
		{
			return;
		}
		try
		{
			int num = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
			clientOfficialByClientId.Clear();
			float reapplyUntil = Time.realtimeSinceStartup + Mathf.Max(0.5f, eventReapplySeconds.Value);
			bool flag = false;
			ulong num2 = default(ulong);
			int playerIndex = default(int);
			int suitId = default(int);
			for (int i = 0; i < num; i++)
			{
				((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num2, default(ForPrimitives));
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref playerIndex, default(ForPrimitives));
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref suitId, default(ForPrimitives));
				SuitAssignment suitAssignment = new SuitAssignment
				{
					ClientId = num2,
					PlayerIndex = playerIndex,
					SuitId = suitId,
					ReapplyUntil = reapplyUntil
				};
				clientOfficialByClientId[num2] = suitAssignment;
				if ((Object)(object)singleton != (Object)null && num2 == singleton.LocalClientId)
				{
					flag = true;
					clientFirstPickAccepted = true;
				}
				ApplyAssignmentLocally(startOfRound, suitAssignment, "client-received", logResult: true);
			}
			if (!flag && (Object)(object)singleton != (Object)null && singleton.IsClient && !singleton.IsServer)
			{
				nextClientRequestAt = Time.realtimeSinceStartup + Mathf.Max(0.5f, clientRequestRetrySeconds.Value);
			}
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Could not read official suit table message: " + ShortException(ex)));
		}
	}

	private List<int> GetAllowedSuitIds(StartOfRound start)
	{
		HashSet<string> hashSet = ParseWhitelist(allowedSuitsCsv.Value);
		List<int> list = new List<int>();
		if ((Object)(object)start == (Object)null || (Object)(object)start.unlockablesList == (Object)null || start.unlockablesList.unlockables == null)
		{
			return list;
		}
		List<UnlockableItem> unlockables = start.unlockablesList.unlockables;
		for (int i = 0; i < unlockables.Count; i++)
		{
			UnlockableItem val = unlockables[i];
			if (val != null && !string.IsNullOrWhiteSpace(val.unlockableName))
			{
				string item = NormalizeName(val.unlockableName);
				if (hashSet.Contains(item))
				{
					list.Add(i);
				}
			}
		}
		if (logDebug.Value)
		{
			string text = string.Join(" ", list.Select((int id) => "[" + GetUnlockableName(start, id) + " id=" + id + "]").ToArray());
			DebugLog("Allowed first-random suit matches: " + text);
		}
		return list;
	}

	private static HashSet<string> ParseWhitelist(string csv)
	{
		HashSet<string> hashSet = new HashSet<string>();
		if (string.IsNullOrWhiteSpace(csv))
		{
			return hashSet;
		}
		string[] array = csv.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
		for (int i = 0; i < array.Length; i++)
		{
			string text = NormalizeName(array[i]);
			if (!string.IsNullOrEmpty(text))
			{
				hashSet.Add(text);
			}
		}
		return hashSet;
	}

	private static string NormalizeName(string text)
	{
		if (string.IsNullOrWhiteSpace(text))
		{
			return string.Empty;
		}
		char[] value = text.Trim().ToLowerInvariant().Where(char.IsLetterOrDigit)
			.ToArray();
		return new string(value);
	}

	private static StartOfRound GetStartOfRound()
	{
		try
		{
			if ((Object)(object)StartOfRound.Instance != (Object)null)
			{
				return StartOfRound.Instance;
			}
		}
		catch
		{
		}
		return Object.FindObjectOfType<StartOfRound>();
	}

	private static bool IsRealPlayer(PlayerControllerB player)
	{
		if ((Object)(object)player == (Object)null)
		{
			return false;
		}
		return player.isPlayerControlled;
	}

	private static ulong GetPlayerClientId(PlayerControllerB player, int playerIndex)
	{
		if ((Object)(object)player == (Object)null)
		{
			return (ulong)Mathf.Max(0, playerIndex);
		}
		try
		{
			if (player.actualClientId != 0L || playerIndex == 0)
			{
				return player.actualClientId;
			}
		}
		catch
		{
		}
		try
		{
			return player.playerClientId;
		}
		catch
		{
			return (ulong)Mathf.Max(0, playerIndex);
		}
	}

	private static int GetPlayerIndex(StartOfRound start, PlayerControllerB player)
	{
		if ((Object)(object)start == (Object)null || start.allPlayerScripts == null || (Object)(object)player == (Object)null)
		{
			return -1;
		}
		for (int i = 0; i < start.allPlayerScripts.Length; i++)
		{
			if ((Object)(object)start.allPlayerScripts[i] == (Object)(object)player)
			{
				return i;
			}
		}
		try
		{
			for (int j = 0; j < start.allPlayerScripts.Length; j++)
			{
				PlayerControllerB val = start.allPlayerScripts[j];
				if ((Object)(object)val != (Object)null && GetPlayerClientId(val, j) == GetPlayerClientId(player, j))
				{
					return j;
				}
			}
		}
		catch
		{
		}
		return -1;
	}

	private static PlayerControllerB FindPlayerByClientId(StartOfRound start, ulong clientIdToFind, out int playerIndex)
	{
		playerIndex = -1;
		if ((Object)(object)start == (Object)null || start.allPlayerScripts == null)
		{
			return null;
		}
		PlayerControllerB[] allPlayerScripts = start.allPlayerScripts;
		for (int i = 0; i < allPlayerScripts.Length; i++)
		{
			PlayerControllerB val = allPlayerScripts[i];
			if (IsRealPlayer(val))
			{
				ulong playerClientId = GetPlayerClientId(val, i);
				if (playerClientId == clientIdToFind)
				{
					playerIndex = i;
					return val;
				}
			}
		}
		return null;
	}

	private static PlayerControllerB FindPlayer(StartOfRound start, SuitAssignment assignment)
	{
		if ((Object)(object)start == (Object)null || start.allPlayerScripts == null || assignment == null)
		{
			return null;
		}
		PlayerControllerB[] allPlayerScripts = start.allPlayerScripts;
		if (assignment.PlayerIndex >= 0 && assignment.PlayerIndex < allPlayerScripts.Length && IsRealPlayer(allPlayerScripts[assignment.PlayerIndex]))
		{
			ulong playerClientId = GetPlayerClientId(allPlayerScripts[assignment.PlayerIndex], assignment.PlayerIndex);
			if (playerClientId == assignment.ClientId)
			{
				return allPlayerScripts[assignment.PlayerIndex];
			}
		}
		for (int i = 0; i < allPlayerScripts.Length; i++)
		{
			PlayerControllerB val = allPlayerScripts[i];
			if (IsRealPlayer(val))
			{
				ulong playerClientId2 = GetPlayerClientId(val, i);
				if (playerClientId2 == assignment.ClientId)
				{
					return val;
				}
			}
		}
		return null;
	}

	private static bool IsValidSuitId(StartOfRound start, int suitId)
	{
		try
		{
			return (Object)(object)start != (Object)null && (Object)(object)start.unlockablesList != (Object)null && start.unlockablesList.unlockables != null && suitId >= 0 && suitId < start.unlockablesList.unlockables.Count;
		}
		catch
		{
			return false;
		}
	}

	private static string GetUnlockableName(StartOfRound start, int suitId)
	{
		try
		{
			if (IsValidSuitId(start, suitId))
			{
				UnlockableItem val = start.unlockablesList.unlockables[suitId];
				if (val != null && !string.IsNullOrWhiteSpace(val.unlockableName))
				{
					return val.unlockableName;
				}
			}
		}
		catch
		{
		}
		return "SuitID " + suitId;
	}

	private static bool IsMenuScene(string sceneName)
	{
		if (string.IsNullOrWhiteSpace(sceneName))
		{
			return false;
		}
		return sceneName.IndexOf("menu", StringComparison.OrdinalIgnoreCase) >= 0;
	}

	private static string ShortException(Exception ex)
	{
		if (ex == null)
		{
			return "unknown error";
		}
		if (ex.InnerException != null)
		{
			return ex.GetType().Name + ": " + ex.Message + " -> " + ex.InnerException.GetType().Name + ": " + ex.InnerException.Message;
		}
		return ex.GetType().Name + ": " + ex.Message;
	}

	private void DebugLog(string message)
	{
		if (logDebug != null && logDebug.Value)
		{
			Log.LogInfo((object)message);
		}
	}
}