Decompiled source of CommandQueueAutoPickup v1.5.0

BepInEx/plugins/CommandQueue.AutoPickup.dll

Decompiled 14 hours 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.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CommandQueue;
using On.RoR2;
using RoR2;
using RoR2.Artifacts;
using RoR2.UI;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CommandQueue.AutoPickup")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("CommandQueue.AutoPickup")]
[assembly: AssemblyTitle("CommandQueue.AutoPickup")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace CommandQueueAutoPickup;

[BepInPlugin("com.ror2modding.commandqueue-autopickup", "CommandQueue.AutoPickup", "1.5.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class AutoPickupPlugin : BaseUnityPlugin
{
	private struct PendingGrab
	{
		public PickupIndex index;

		public float createdRealtime;
	}

	private struct LiveTierInfo
	{
		public ItemTier tier;

		public int count;

		public PickupIndex head;
	}

	public const string VERSION = "1.5.0";

	private const string BUTTON_MARKER = "CQAutoPickupToggle";

	private const float TRIGGER_COOLDOWN_SECONDS = 1f;

	private const float TRIGGER_RETRY_BACKOFF_SECONDS = 3f;

	private const float PENDING_GRAB_TIMEOUT_SECONDS = 10f;

	private static ManualLogSource s_logger;

	private static ConfigEntry<bool> s_enabled;

	private static ConfigEntry<float> s_rangeBonus;

	private static GameObject s_toggleButton;

	private static readonly Dictionary<GameObject, float> s_triggerCooldown = new Dictionary<GameObject, float>();

	private static string s_lastStatus;

	private static bool s_scannerAnnounced;

	private const float IDLE_SCAN_INTERVAL = 0.05f;

	private const float ACTIVE_WINDOW_SECONDS = 2.5f;

	private static float s_activityUntil = 0f;

	private static readonly List<PendingGrab> s_pendingGrabs = new List<PendingGrab>();

	private static readonly HashSet<GameObject> s_claimedPickups = new HashSet<GameObject>();

	private static IDictionary s_liveMainQueues;

	private static Type s_liveQueueManagerType;

	private static string s_bridgeDescription = "not resolved";

	private static readonly ItemTier[] ALL_TIERS = (ItemTier[])(object)new ItemTier[3]
	{
		default(ItemTier),
		(ItemTier)1,
		(ItemTier)2
	};

	private static bool s_gateRegistered;

	private unsafe static void TryGrabPending(Interactor interactor)
	{
		//IL_025a: Unknown result type (might be due to invalid IL or missing references)
		//IL_025c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: 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_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_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: Invalid comparison between Unknown and I4
		if ((Object)(object)interactor == (Object)null || (Object)(object)((Component)interactor).gameObject == (Object)null)
		{
			return;
		}
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		for (int num = s_pendingGrabs.Count - 1; num >= 0; num--)
		{
			if (realtimeSinceStartup - s_pendingGrabs[num].createdRealtime > 10f)
			{
				s_pendingGrabs.RemoveAt(num);
			}
		}
		foreach (GameObject item in new List<GameObject>(s_claimedPickups))
		{
			if ((Object)(object)item == (Object)null)
			{
				s_claimedPickups.Remove(item);
			}
		}
		if (s_pendingGrabs.Count == 0)
		{
			return;
		}
		GenericPickupController[] array;
		try
		{
			array = Object.FindObjectsOfType<GenericPickupController>();
		}
		catch
		{
			return;
		}
		if (array.Length == 0)
		{
			return;
		}
		for (int num2 = s_pendingGrabs.Count - 1; num2 >= 0; num2--)
		{
			PickupIndex index = s_pendingGrabs[num2].index;
			GenericPickupController val = null;
			float num3 = float.MaxValue;
			GenericPickupController[] array2 = array;
			foreach (GenericPickupController val2 in array2)
			{
				if ((Object)(object)val2 == (Object)null || !((Component)val2).gameObject.activeInHierarchy || s_claimedPickups.Contains(((Component)val2).gameObject) || val2.pickupIndex != index)
				{
					continue;
				}
				bool flag = false;
				try
				{
					flag = (int)val2.GetInteractability(interactor) == 2;
				}
				catch
				{
				}
				if (flag)
				{
					float num4 = Vector3.Distance(((Component)interactor).transform.position, ((Component)val2).transform.position);
					if (num4 < num3)
					{
						num3 = num4;
						val = val2;
					}
				}
			}
			if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null))
			{
				s_claimedPickups.Add(((Component)val).gameObject);
				s_pendingGrabs.RemoveAt(num2);
				try
				{
					interactor.AttemptInteraction(((Component)val).gameObject);
					LogInfo("force-picked up chosen item " + ((object)(*(PickupIndex*)(&index))/*cast due to .constrained prefix*/).ToString() + " (pending left: " + s_pendingGrabs.Count + ")");
				}
				catch (Exception ex)
				{
					s_claimedPickups.Remove(((Component)val).gameObject);
					s_pendingGrabs.Insert(num2, new PendingGrab
					{
						index = index,
						createdRealtime = realtimeSinceStartup
					});
					LogWarn("force-pickup of " + ((object)(*(PickupIndex*)(&index))/*cast due to .constrained prefix*/).ToString() + " failed: " + ex.Message + "\n" + (ex.StackTrace ?? "(no stack trace)"));
				}
			}
		}
	}

	private static void ClearPendingGrabs()
	{
		s_pendingGrabs.Clear();
		s_claimedPickups.Clear();
	}

	private static string PendingSummary()
	{
		//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)
		if (s_pendingGrabs.Count == 0)
		{
			return null;
		}
		List<string> list = new List<string>();
		foreach (IGrouping<PickupIndex, PendingGrab> item in from p in s_pendingGrabs
			group p by p.index)
		{
			list.Add(((object)item.Key/*cast due to .constrained prefix*/).ToString() + ((item.Count() > 1) ? (" x" + item.Count()) : ""));
		}
		return string.Join(", ", list);
	}

	public void Awake()
	{
		s_logger = ((BaseUnityPlugin)this).Logger;
		if (((BaseUnityPlugin)this).Config != null)
		{
			s_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("AutoPickup", "Enabled", false, "While enabled: command essences («эссенция управления») you stand next to are picked up automatically when the queue for their tier has items");
			s_rangeBonus = ((BaseUnityPlugin)this).Config.Bind<float>("AutoPickup", "RangeBonus", 0f, "Extra range added on top of the normal interact distance, in meters (0 = exactly like pressing E)");
			LogInfo("Loaded v1.5.0");
		}
	}

	public void OnEnable()
	{
		if (!IsCommandQueueLoaded())
		{
			LogWarn("CommandQueue is not installed — the extension has nothing to extend and stays idle.");
		}
		((MonoBehaviour)this).StartCoroutine(WatchForPanel());
		((MonoBehaviour)this).StartCoroutine(AutoPickupLoop());
		((MonoBehaviour)this).StartCoroutine(RegisterDisplayGate());
	}

	private static void LogInfo(string msg)
	{
		ManualLogSource obj = s_logger;
		if (obj != null)
		{
			obj.LogInfo((object)("[CommandQueue.AutoPickup] " + msg));
		}
	}

	private static void LogWarn(string msg)
	{
		ManualLogSource obj = s_logger;
		if (obj != null)
		{
			obj.LogWarning((object)("[CommandQueue.AutoPickup] " + msg));
		}
	}

	private static void LogStatus(string status)
	{
		if (status != s_lastStatus)
		{
			s_lastStatus = status;
			LogInfo(status);
		}
	}

	private static bool IsCommandQueueLoaded()
	{
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		for (int i = 0; i < assemblies.Length; i++)
		{
			if (string.Equals(assemblies[i].GetName().Name, "CommandQueue", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
		}
		return false;
	}

	private static IDictionary GetLiveMainQueues()
	{
		if (s_liveMainQueues != null)
		{
			return s_liveMainQueues;
		}
		List<Assembly> list = new List<Assembly>();
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			try
			{
				if (string.Equals(assembly.GetName().Name, "CommandQueue", StringComparison.OrdinalIgnoreCase))
				{
					list.Add(assembly);
				}
			}
			catch
			{
			}
		}
		Assembly assembly2 = null;
		if (list.Count == 1)
		{
			assembly2 = list[0];
		}
		else if (list.Count > 1)
		{
			LogWarn(list.Count + " CommandQueue assemblies loaded — detecting the live one");
			foreach (Assembly item in list)
			{
				Type type = null;
				try
				{
					Type[] source;
					try
					{
						source = item.GetTypes();
					}
					catch (ReflectionTypeLoadException ex)
					{
						source = ex.Types.Where((Type t) => t != null).ToArray();
					}
					type = source.FirstOrDefault((Type t) => t.IsSubclassOf(typeof(BaseUnityPlugin)));
				}
				catch
				{
					continue;
				}
				if (!(type == null))
				{
					int num = 0;
					try
					{
						num = Resources.FindObjectsOfTypeAll(type).Length;
					}
					catch
					{
					}
					LogInfo("  candidate v" + item.GetName().Version?.ToString() + ": plugin " + type.FullName + ", instantiated objects: " + num);
					if (num > 0 && assembly2 == null)
					{
						assembly2 = item;
					}
				}
			}
		}
		if (assembly2 != null)
		{
			try
			{
				Type type2 = assembly2.GetType("CommandQueue.QueueManager");
				IDictionary result = (s_liveMainQueues = ((type2?.GetField("mainQueues", BindingFlags.Static | BindingFlags.Public))?.GetValue(null) as IDictionary) ?? throw new MissingMemberException("mainQueues not found in " + assembly2.FullName));
				s_liveQueueManagerType = type2;
				s_bridgeDescription = assembly2.GetName().Name + " v" + assembly2.GetName().Version;
				LogInfo("queue bridge attached to the RUNNING mod: " + s_bridgeDescription);
				return result;
			}
			catch (Exception ex2)
			{
				LogWarn("bridge failed (" + ex2.Message + ") — falling back to direct reference");
			}
		}
		try
		{
			s_liveMainQueues = QueueManager.mainQueues;
			s_liveQueueManagerType = typeof(QueueManager);
			s_bridgeDescription = "direct reference";
			if (list.Count > 1)
			{
				LogWarn("could not determine the live CommandQueue — remove duplicate CommandQueue plugin folders if queues look empty!");
			}
			else
			{
				LogInfo("queue bridge attached (single instance): " + ((list.Count == 1) ? list[0].GetName().Version.ToString() : "none loaded yet"));
			}
			return s_liveMainQueues;
		}
		catch
		{
			s_bridgeDescription = "unresolved";
			return null;
		}
	}

	private static List<LiveTierInfo> SnapshotQueues(IDictionary queues)
	{
		//IL_00a2: 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_00b3: 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_001b: 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_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		List<LiveTierInfo> list = new List<LiveTierInfo>();
		if (queues == null)
		{
			return list;
		}
		ItemTier[] aLL_TIERS = ALL_TIERS;
		foreach (ItemTier val in aLL_TIERS)
		{
			object obj = null;
			try
			{
				obj = queues[val];
			}
			catch
			{
			}
			IList list2 = obj as IList;
			int num = list2?.Count ?? 0;
			PickupIndex head = PickupIndex.none;
			if (num > 0)
			{
				try
				{
					object obj3 = list2[0];
					if (obj3 != null)
					{
						FieldInfo field = obj3.GetType().GetField("pickupIndex");
						if (field != null)
						{
							head = (PickupIndex)field.GetValue(obj3);
						}
					}
				}
				catch
				{
				}
			}
			list.Add(new LiveTierInfo
			{
				tier = val,
				count = num,
				head = head
			});
		}
		return list;
	}

	private static string QueueSummary(List<LiveTierInfo> snapshot)
	{
		return "White=" + snapshot.FirstOrDefault((LiveTierInfo t) => (int)t.tier == 0).count + ", Green=" + snapshot.FirstOrDefault((LiveTierInfo t) => (int)t.tier == 1).count + ", Red=" + snapshot.FirstOrDefault((LiveTierInfo t) => (int)t.tier == 2).count;
	}

	private static ItemTier? DefTier(PickupIndex idx)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			return PickupCatalog.GetPickupDef(idx)?.itemTier;
		}
		catch
		{
			return null;
		}
	}

	private static List<ItemTier> GetCubeTiers(PickupPickerController picker)
	{
		//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_0022: 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_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: 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)
		List<ItemTier> list = new List<ItemTier>();
		try
		{
			Option[] array = picker.options ?? Array.Empty<Option>();
			foreach (Option val in array)
			{
				if (val.available)
				{
					ItemTier? val2 = DefTier(val.pickup.pickupIndex);
					if (val2.HasValue && !list.Contains(val2.Value))
					{
						list.Add(val2.Value);
					}
				}
			}
		}
		catch
		{
		}
		return list;
	}

	private static string DescribeCube(PickupPickerController picker, int maxSamples = 5)
	{
		//IL_0036: 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_003d: 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_006a: 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_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Option[] array = picker.options ?? Array.Empty<Option>();
			if (array.Length == 0)
			{
				return "options not populated yet";
			}
			int num = 0;
			StringBuilder stringBuilder = new StringBuilder();
			Option[] array2 = array;
			foreach (Option val in array2)
			{
				if (val.available)
				{
					num++;
					if (stringBuilder.Length >= maxSamples)
					{
						break;
					}
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append(", ");
					}
					PickupIndex pickupIndex = val.pickup.pickupIndex;
					string value = "?";
					ItemTier? val2 = DefTier(pickupIndex);
					if (val2.HasValue)
					{
						value = TierName(val2.Value);
					}
					stringBuilder.Append(pickupIndex).Append('(').Append(value)
						.Append(')');
				}
			}
			return num + " of " + array.Length + " option(s) available" + ((stringBuilder.Length > 0) ? ("; first: " + stringBuilder) : "");
		}
		catch
		{
			return "options unreadable";
		}
	}

	private static GameObject GetCommandPanelPrefab()
	{
		try
		{
			GameObject commandCubePrefab = CommandArtifactManager.commandCubePrefab;
			if ((Object)(object)commandCubePrefab == (Object)null)
			{
				return null;
			}
			return commandCubePrefab.GetComponent<PickupPickerController>()?.panelPrefab;
		}
		catch
		{
			return null;
		}
	}

	private static IEnumerator RegisterDisplayGate()
	{
		yield return (object)new WaitForSeconds(2f);
		if (s_gateRegistered)
		{
			yield break;
		}
		try
		{
			PickupPickerController.OnDisplayBegin += new hook_OnDisplayBegin(HandleDisplayBeginGate);
			s_gateRegistered = true;
			LogInfo("display-pipeline gate is active — auto-submissions are now protected from crashes in other mods' OnDisplayBegin hooks (e.g. LookingGlass)");
		}
		catch (Exception ex)
		{
			LogWarn("could not register the display-pipeline gate: " + ex.Message);
		}
	}

	private static void HandleDisplayBeginGate(orig_OnDisplayBegin orig, PickupPickerController self, NetworkUIPromptController nupc, LocalUser localUser, CameraRigController cam)
	{
		string text = QueueSignature();
		Exception ex = null;
		try
		{
			orig.Invoke(self, nupc, localUser, cam);
		}
		catch (Exception ex2)
		{
			ex = ex2;
			LogWarn("OnDisplayBegin chain threw in a downstream hook - suppressed so the pipeline can continue. Fault: " + ex2.GetType().Name + ": " + ex2.Message + " | first frame: " + FirstFrame(ex2.StackTrace));
		}
		if (ex == null)
		{
			return;
		}
		bool flag = false;
		try
		{
			flag = QueueSignature() != text;
		}
		catch
		{
		}
		if (flag)
		{
			LogInfo("gate: an auto-submission is in progress despite the downstream crash - nothing else to do");
			return;
		}
		bool flag2 = false;
		try
		{
			FieldInfo field = typeof(PickupPickerController).GetField("panelInstance", BindingFlags.Instance | BindingFlags.NonPublic);
			flag2 = field != null && (bool)field.GetValue(self);
		}
		catch
		{
		}
		if (!flag2 && TryOpenPickerPanel(self, cam))
		{
			LogInfo("gate: a downstream hook crashed before the picker could open - recreated the panel manually, manual selection restored");
		}
	}

	private static string FirstFrame(string stack)
	{
		if (string.IsNullOrEmpty(stack))
		{
			return "(no stack trace)";
		}
		int num = stack.IndexOf('\n');
		string text = ((num > 0) ? stack.Substring(0, num) : stack).Trim();
		if (text.Length <= 220)
		{
			return text;
		}
		return text.Substring(0, 220) + "…";
	}

	private unsafe static string QueueSignature()
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		List<LiveTierInfo> list = SnapshotQueues(GetLiveMainQueues());
		if (list.Count == 0)
		{
			return "(empty)";
		}
		List<string> list2 = new List<string>(list.Count);
		foreach (LiveTierInfo item in list)
		{
			string text = TierName(item.tier);
			string text2 = item.count.ToString();
			object obj;
			if (item.count <= 0)
			{
				obj = "";
			}
			else
			{
				PickupIndex head = item.head;
				obj = "@" + ((object)(*(PickupIndex*)(&head))/*cast due to .constrained prefix*/).ToString();
			}
			list2.Add(text + "=" + text2 + (string?)obj);
		}
		return string.Join("|", list2);
	}

	private static bool TryOpenPickerPanel(PickupPickerController self, CameraRigController cam)
	{
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			GameObject panelPrefab = self.panelPrefab;
			if ((Object)(object)panelPrefab == (Object)null)
			{
				LogWarn("gate: cannot open the picker - panelPrefab is null");
				return false;
			}
			Transform val = null;
			try
			{
				val = (((Object)(object)cam != (Object)null) ? cam.hud.mainContainer.transform : null);
			}
			catch
			{
			}
			GameObject val2 = Object.Instantiate<GameObject>(panelPrefab, val);
			try
			{
				if (((Object)val2).name.ToLower().Contains("rebirthpickerpanel"))
				{
					Scene activeScene = SceneManager.GetActiveScene();
					if (((Scene)(ref activeScene)).name == "meridian")
					{
						SettingsPanelController componentInChildren = val2.GetComponentInChildren<SettingsPanelController>();
						if ((Object)(object)componentInChildren != (Object)null)
						{
							componentInChildren.ToggleDialogBoxCreation(false);
						}
					}
				}
			}
			catch
			{
			}
			typeof(PickupPickerController).GetField("panelInstance", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(self, val2);
			PickupPickerPanel component = val2.GetComponent<PickupPickerPanel>();
			if ((Object)(object)component != (Object)null)
			{
				typeof(PickupPickerController).GetField("panelInstanceController", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(self, component);
				component.pickerController = self;
				FieldInfo field = typeof(PickupPickerController).GetField("excludedItemsDropTable", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field != null && field.GetValue(self) == null)
				{
					field.SetValue(self, component.excludedItemsDropTable);
				}
				component.SetPickupOptions(self.options);
			}
			MethodInfo method = typeof(PickupPickerController).GetMethod("OnPanelDestroyed", BindingFlags.Instance | BindingFlags.NonPublic);
			Action<OnDestroyCallback> action = null;
			if (method != null)
			{
				action = (Action<OnDestroyCallback>)method.CreateDelegate(typeof(Action<OnDestroyCallback>), self);
			}
			OnDestroyCallback.AddCallback(val2, action);
			return true;
		}
		catch (Exception ex)
		{
			LogWarn("gate: panel re-creation failed (" + ex.GetType().Name + ": " + ex.Message + ")");
			return false;
		}
	}

	private IEnumerator WatchForPanel()
	{
		while (true)
		{
			if (IsCommandQueueLoaded())
			{
				try
				{
					EnsureToggleButton();
				}
				catch (Exception ex)
				{
					LogWarn("Button injection failed: " + ex.Message);
				}
			}
			yield return (object)new WaitForSeconds(2f);
		}
	}

	private static void EnsureToggleButton()
	{
		//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cf: Expected O, but got Unknown
		//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02da: Expected O, but got Unknown
		//IL_02db: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e5: Expected O, but got Unknown
		//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fb: Expected O, but got Unknown
		//IL_0286: Unknown result type (might be due to invalid IL or missing references)
		//IL_029c: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)s_toggleButton != (Object)null)
		{
			UpdateButtonVisual(s_toggleButton);
			return;
		}
		try
		{
			GameObject val = null;
			HGButton[] array = Object.FindObjectsOfType<HGButton>();
			foreach (HGButton val2 in array)
			{
				if (!((Object)(object)val2 == (Object)null) && !(((Object)((Component)val2).gameObject).name != "CQAutoPickupToggle"))
				{
					if ((Object)(object)val == (Object)null)
					{
						val = ((Component)val2).gameObject;
					}
					else
					{
						Object.Destroy((Object)(object)((Component)val2).gameObject);
					}
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				s_toggleButton = val;
				UpdateButtonVisual(val);
				LogInfo("Adopted existing AutoPickup button");
				return;
			}
		}
		catch
		{
		}
		GameObject val3 = null;
		try
		{
			val3 = GameObject.Find("CommandQueueContainer");
		}
		catch
		{
			val3 = null;
		}
		if ((Object)(object)val3 == (Object)null)
		{
			HUD[] array2 = Object.FindObjectsOfType<HUD>();
			for (int i = 0; i < array2.Length; i++)
			{
				GameObject val4 = array2[i]?.scoreboardPanel;
				if ((Object)(object)val4 != (Object)null)
				{
					Transform val5 = FindDescendantByName(val4.transform, "CommandQueueContainer");
					if ((Object)(object)val5 != (Object)null)
					{
						val3 = ((Component)val5).gameObject;
						break;
					}
				}
			}
		}
		if ((Object)(object)val3 == (Object)null || (Object)(object)val3.transform.parent == (Object)null)
		{
			return;
		}
		Transform parent = val3.transform.parent;
		Transform[] componentsInChildren = ((Component)parent).GetComponentsInChildren<Transform>(true);
		foreach (Transform val6 in componentsInChildren)
		{
			if ((Object)(object)val6 != (Object)null && ((Object)val6).name == "CQAutoPickupToggle")
			{
				s_toggleButton = ((Component)val6).gameObject;
				UpdateButtonVisual(s_toggleButton);
				return;
			}
		}
		GameObject commandPanelPrefab = GetCommandPanelPrefab();
		if ((Object)(object)commandPanelPrefab == (Object)null)
		{
			return;
		}
		try
		{
			HGButton[] componentsInChildren2 = commandPanelPrefab.GetComponentsInChildren<HGButton>(true);
			HGButton val7 = null;
			HGButton[] array = componentsInChildren2;
			foreach (HGButton val8 in array)
			{
				if ((Object)(object)val8 != (Object)null && ((Object)val8).name == "CancelButton")
				{
					val7 = val8;
					break;
				}
			}
			if ((Object)(object)val7 == (Object)null)
			{
				array = componentsInChildren2;
				foreach (HGButton val9 in array)
				{
					if ((Object)(object)val9 != (Object)null)
					{
						val7 = val9;
						break;
					}
				}
			}
			if ((Object)(object)val7 == (Object)null)
			{
				LogWarn("Could not find a prototype button in the command picker panel");
				return;
			}
			GameObject obj3 = Object.Instantiate<GameObject>(((Component)val7).gameObject, parent);
			((Object)obj3).name = "CQAutoPickupToggle";
			RectTransform component = obj3.GetComponent<RectTransform>();
			if ((Object)(object)component != (Object)null)
			{
				component.anchorMin = new Vector2(0.667f, 0.93f);
				component.anchorMax = new Vector2(1f, 1f);
				component.anchoredPosition = Vector2.zero;
				component.sizeDelta = Vector2.zero;
			}
			HGButton component2 = obj3.GetComponent<HGButton>();
			((MPButton)component2).onSelect = new UnityEvent();
			((MPButton)component2).onDeselect = new UnityEvent();
			((Button)component2).onClick = new ButtonClickedEvent();
			((UnityEvent)((Button)component2).onClick).AddListener(new UnityAction(ToggleAutoPickup));
			UpdateButtonVisual(obj3);
			s_toggleButton = obj3;
			LogInfo("AutoPickup toggle button added to the CommandQueue panel (game button style)");
		}
		catch (Exception ex)
		{
			LogWarn("Failed to create the toggle button: " + ex.Message);
		}
	}

	private static void SetButtonText(GameObject buttonGo, string text)
	{
		try
		{
			LanguageTextMeshController componentInChildren = buttonGo.GetComponentInChildren<LanguageTextMeshController>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.token = text;
				return;
			}
			TextMeshProUGUI componentInChildren2 = buttonGo.GetComponentInChildren<TextMeshProUGUI>();
			if ((Object)(object)componentInChildren2 != (Object)null)
			{
				((TMP_Text)componentInChildren2).text = text;
			}
		}
		catch
		{
		}
	}

	private static void ToggleAutoPickup()
	{
		if (s_enabled == null)
		{
			return;
		}
		s_enabled.Value = !s_enabled.Value;
		UpdateButtonVisual(s_toggleButton);
		try
		{
			EventSystem current = EventSystem.current;
			if ((Object)(object)current != (Object)null)
			{
				current.SetSelectedGameObject((GameObject)null);
			}
		}
		catch
		{
		}
		LogInfo("AutoPickup " + (s_enabled.Value ? "ENABLED" : "disabled") + " by button");
	}

	private static void UpdateButtonVisual(GameObject buttonGo)
	{
		if (!((Object)(object)buttonGo == (Object)null))
		{
			SetButtonText(buttonGo, (s_enabled != null && s_enabled.Value) ? "AutoPickup ON" : "AutoPickup OFF");
		}
	}

	private static Transform FindDescendantByName(Transform root, string name)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Expected O, but got Unknown
		foreach (Transform item in root)
		{
			Transform val = item;
			if (((Object)val).name == name)
			{
				return val;
			}
			Transform val2 = FindDescendantByName(val, name);
			if ((Object)(object)val2 != (Object)null)
			{
				return val2;
			}
		}
		return null;
	}

	private static bool PopLiveQueue(ItemTier tier)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			MethodInfo methodInfo = s_liveQueueManagerType?.GetMethod("Pop", BindingFlags.Static | BindingFlags.Public);
			if (methodInfo == null)
			{
				return false;
			}
			methodInfo.Invoke(null, new object[1] { tier });
			return true;
		}
		catch (Exception ex)
		{
			LogWarn("live queue Pop failed: " + ex.Message);
			return false;
		}
	}

	private static void EnqueueLive(PickupIndex idx)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			(s_liveQueueManagerType?.GetMethod("Enqueue", BindingFlags.Static | BindingFlags.Public))?.Invoke(null, new object[1] { idx });
		}
		catch (Exception ex)
		{
			LogWarn("live queue Enqueue failed: " + ex.Message);
		}
	}

	private unsafe static bool TryDirectGrant(GameObject cubeGO, CharacterBody body, ItemTier tier, PickupIndex head)
	{
		//IL_0107: 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_0049: 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_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: 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_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: 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_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: 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_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)cubeGO == (Object)null || (Object)(object)body == (Object)null)
		{
			return false;
		}
		PickupDef pickupDef = PickupCatalog.GetPickupDef(head);
		if (pickupDef == null || pickupDef.attemptGrant == null)
		{
			LogWarn("direct grant: no valid PickupDef/attemptGrant for " + ((object)(*(PickupIndex*)(&head))/*cast due to .constrained prefix*/).ToString() + " — falling back to the classic path");
			return false;
		}
		if (!PopLiveQueue(tier))
		{
			LogWarn("direct grant: could not pop the live queue (" + TierName(tier) + ") — falling back to the classic path");
			return false;
		}
		UniquePickup val = new UniquePickup
		{
			pickupIndex = head
		};
		GrantContext val2 = new GrantContext
		{
			body = body,
			position = cubeGO.transform.position,
			rotation = Quaternion.identity,
			uniquePickup = val
		};
		try
		{
			pickupDef.attemptGrant.Invoke(ref val2);
			if ((Object)(object)body.inventory != (Object)null)
			{
				body.OnPickup((PickupClass)3);
			}
			if (val2.shouldNotify && (Object)(object)body.master != (Object)null)
			{
				GenericPickupController.SendPickupMessage(body.master, val, 1);
			}
			Object.Destroy((Object)(object)cubeGO);
		}
		catch (Exception ex)
		{
			EnqueueLive(head);
			LogWarn("direct grant of " + ((object)(*(PickupIndex*)(&head))/*cast due to .constrained prefix*/).ToString() + " failed: " + ex.Message + " — rolled back to the queue, using the classic path");
			return false;
		}
		LogInfo("direct-granted " + ((object)(*(PickupIndex*)(&head))/*cast due to .constrained prefix*/).ToString() + " (" + TierName(tier) + ") straight into the inventory; essence consumed, no ground item");
		return true;
	}

	private IEnumerator AutoPickupLoop()
	{
		while (true)
		{
			yield return null;
			if (s_enabled == null || !s_enabled.Value)
			{
				s_scannerAnnounced = false;
				if (s_pendingGrabs.Count > 0)
				{
					ClearPendingGrabs();
				}
				continue;
			}
			if (!IsCommandQueueLoaded())
			{
				if (!s_scannerAnnounced)
				{
					s_scannerAnnounced = true;
					LogInfo("waiting — CommandQueue assembly not found yet");
				}
				continue;
			}
			if (!s_scannerAnnounced)
			{
				s_scannerAnnounced = true;
				LogStatus("scanner active — looking for essences in range");
				try
				{
					LogInfo("queue bridge → " + s_bridgeDescription + " | queues now: " + QueueSummary(SnapshotQueues(GetLiveMainQueues())));
				}
				catch (Exception ex)
				{
					LogWarn("queue bridge announcement failed: " + ex.Message);
				}
			}
			try
			{
				TryAutoPickup();
			}
			catch (Exception ex2)
			{
				LogWarn("AutoPickup tick failed: " + ex2.Message + "\n" + (ex2.StackTrace ?? "(no stack trace)"));
			}
		}
	}

	private unsafe static void TryAutoPickup()
	{
		//IL_062d: Unknown result type (might be due to invalid IL or missing references)
		//IL_063e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0643: Unknown result type (might be due to invalid IL or missing references)
		//IL_021a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0228: Unknown result type (might be due to invalid IL or missing references)
		//IL_022d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0203: Unknown result type (might be due to invalid IL or missing references)
		//IL_0600: Unknown result type (might be due to invalid IL or missing references)
		//IL_0605: Unknown result type (might be due to invalid IL or missing references)
		//IL_0618: Unknown result type (might be due to invalid IL or missing references)
		//IL_0301: Unknown result type (might be due to invalid IL or missing references)
		//IL_0306: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_06e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0423: Unknown result type (might be due to invalid IL or missing references)
		//IL_0425: Unknown result type (might be due to invalid IL or missing references)
		//IL_0467: Unknown result type (might be due to invalid IL or missing references)
		//IL_04fe: 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_01ca: Unknown result type (might be due to invalid IL or missing references)
		if (!GetLocalInteractor(out var interactor, out var failReason))
		{
			LogStatus("waiting — " + failReason);
			return;
		}
		if ((Object)(object)interactor == (Object)null || (Object)(object)((Component)interactor).gameObject == (Object)null)
		{
			LogStatus("waiting — Interactor reference is stale (respawn / scene change?)");
			return;
		}
		CharacterBody val = ResolveLocalBody();
		TryGrabPending(interactor);
		float num = Mathf.Max(interactor.maxInteractionDistance, 1f) + (s_rangeBonus?.Value ?? 0f) + 0.1f;
		Vector3 position = ((Component)interactor).transform.position;
		foreach (GameObject item in new List<GameObject>(s_triggerCooldown.Keys))
		{
			if ((Object)(object)item == (Object)null)
			{
				s_triggerCooldown.Remove(item);
			}
		}
		GameObject commandPanelPrefab = GetCommandPanelPrefab();
		if ((Object)(object)commandPanelPrefab == (Object)null)
		{
			LogStatus("enabled, but the command cube prefab is not loaded yet — nothing to do");
			return;
		}
		PickupPickerController[] array;
		try
		{
			array = Object.FindObjectsOfType<PickupPickerController>();
		}
		catch (Exception ex)
		{
			LogStatus("error while scanning for essences: " + ex.Message);
			return;
		}
		int num2 = 0;
		int num3 = 0;
		bool flag = false;
		PickupPickerController val2 = null;
		List<LiveTierInfo> list = SnapshotQueues(GetLiveMainQueues());
		PickupPickerController[] array2 = array;
		foreach (PickupPickerController val3 in array2)
		{
			if ((Object)(object)val3 == (Object)null || !((Component)val3).gameObject.activeInHierarchy || val3.panelPrefab != commandPanelPrefab || Vector3.Distance(position, ((Component)val3).transform.position) > num)
			{
				continue;
			}
			num2++;
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = val3;
			}
			if (s_triggerCooldown.TryGetValue(((Component)val3).gameObject, out var value) && Time.time < value)
			{
				continue;
			}
			ItemTier? val4 = null;
			PickupIndex val5 = PickupIndex.none;
			foreach (LiveTierInfo item2 in list)
			{
				if (item2.count != 0 && !(item2.head == PickupIndex.none))
				{
					bool flag2 = false;
					try
					{
						flag2 = val3.IsChoiceAvailable(item2.head);
					}
					catch
					{
					}
					if (flag2)
					{
						val4 = item2.tier;
						val5 = item2.head;
						break;
					}
				}
			}
			if (!val4.HasValue)
			{
				num3++;
				continue;
			}
			if ((Object)(object)val3 == (Object)null || (Object)(object)((Component)val3).gameObject == (Object)null)
			{
				num3++;
				continue;
			}
			s_triggerCooldown[((Component)val3).gameObject] = Time.time + 1f;
			s_activityUntil = Time.time + 2.5f;
			bool flag3 = false;
			if (NetworkServer.active)
			{
				CharacterBody val6 = val ?? (((Object)(object)interactor != (Object)null) ? ((Component)interactor).GetComponent<CharacterBody>() : null);
				if ((Object)(object)val6 == (Object)null || (Object)(object)val6.inventory == (Object)null)
				{
					LogWarn("direct grant skipped (local CharacterBody not resolved) — falling back to the classic path");
				}
				else
				{
					flag3 = TryDirectGrant(((Component)val3).gameObject, val6, val4.Value, val5);
				}
			}
			if (!flag3)
			{
				LogInfo("using classic path (reason: " + (NetworkServer.active ? "direct grant returned false" : "not hosting / client") + ")");
				try
				{
					interactor.AttemptInteraction(((Component)val3).gameObject);
				}
				catch (Exception ex2)
				{
					s_triggerCooldown[((Component)val3).gameObject] = Time.time + 3f;
					string text = "";
					string text2 = ex2.StackTrace ?? "";
					int num4 = text2.IndexOf('\n');
					if (num4 > 0)
					{
						text = text2.Substring(0, num4).Trim();
					}
					LogWarn("trigger attempt on the essence threw (" + ex2.GetType().Name + ": " + ex2.Message + ") — a hook from another mod is likely crashing the display pipeline; retrying this cube in a few seconds." + ((text != "") ? (" Failing frame: " + text) : ""));
					num3++;
					continue;
				}
				s_pendingGrabs.Add(new PendingGrab
				{
					index = val5,
					createdRealtime = Time.realtimeSinceStartup
				});
				LogInfo("Auto-picked up a command essence (classic path) — submitting queued item " + ((object)(*(PickupIndex*)(&val5))/*cast due to .constrained prefix*/).ToString() + " (" + TierName(val4.Value) + "); will force-grab it off the ground");
			}
			flag = true;
			break;
		}
		if (flag)
		{
			return;
		}
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.Append("waiting — ").Append(num2).Append(" essence(s) in interact range");
		int num5 = 0;
		List<string> list2 = new List<string>();
		foreach (LiveTierInfo item3 in list)
		{
			if (item3.count > 0)
			{
				num5 += item3.count;
				list2.Add(TierName(item3.tier) + "=" + item3.count);
			}
		}
		if (list2.Count == 0)
		{
			stringBuilder.Append("; QUEUES ARE EMPTY (bridge: ").Append(s_bridgeDescription).Append(" — add items first: Tab → Command Queue → click item buttons)");
		}
		else
		{
			stringBuilder.Append(" [queues: ").Append(string.Join(", ", list2)).Append("] (bridge: ")
				.Append(s_bridgeDescription)
				.Append(')');
		}
		if ((Object)(object)val2 != (Object)null && num5 > 0)
		{
			stringBuilder.Append(". cube: ").Append(DescribeCube(val2));
			List<string> list3 = new List<string>();
			foreach (LiveTierInfo item4 in list)
			{
				if (item4.count != 0 && !(item4.head == PickupIndex.none))
				{
					bool flag4 = false;
					try
					{
						flag4 = val2.IsChoiceAvailable(item4.head);
					}
					catch
					{
					}
					string text3 = TierName(item4.tier);
					PickupIndex head = item4.head;
					list3.Add(text3 + " head=" + ((object)(*(PickupIndex*)(&head))/*cast due to .constrained prefix*/).ToString() + (flag4 ? " MATCH" : " not offered"));
				}
			}
			stringBuilder.Append("; your heads: ").Append(string.Join("; ", list3));
			List<ItemTier> cubeTiers = GetCubeTiers(val2);
			HashSet<ItemTier> queuedTiers = new HashSet<ItemTier>();
			foreach (LiveTierInfo item5 in list)
			{
				if (item5.count > 0)
				{
					queuedTiers.Add(item5.tier);
				}
			}
			if (!cubeTiers.Any((ItemTier t) => queuedTiers.Contains(t)) && cubeTiers.Count > 0)
			{
				stringBuilder.Append(". NOTE: this essence only offers ").Append(string.Join("/", cubeTiers.Select(TierName))).Append(" item(s), but you queued: ")
					.Append(string.Join("/", queuedTiers.Select(TierName)))
					.Append(" — queue an item of the essence's color (or wait for a matching essence)");
			}
		}
		string text4 = PendingSummary();
		if (text4 != null)
		{
			stringBuilder.Append("; awaiting ground item(s): ").Append(text4);
		}
		LogStatus(stringBuilder.ToString());
	}

	private static string TierName(ItemTier tier)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Expected I4, but got Unknown
		return (int)tier switch
		{
			0 => "White", 
			1 => "Green", 
			2 => "Red", 
			_ => "?", 
		};
	}

	private static CharacterBody ResolveLocalBody()
	{
		try
		{
			LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser();
			if (firstLocalUser != null)
			{
				if ((Object)(object)firstLocalUser.cachedBodyObject != (Object)null)
				{
					CharacterBody component = firstLocalUser.cachedBodyObject.GetComponent<CharacterBody>();
					if ((Object)(object)component != (Object)null)
					{
						return component;
					}
				}
				if ((Object)(object)firstLocalUser.cachedMasterObject != (Object)null)
				{
					CharacterMaster component2 = firstLocalUser.cachedMasterObject.GetComponent<CharacterMaster>();
					CharacterBody val = (((Object)(object)component2 != (Object)null) ? component2.GetBody() : null);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
			}
		}
		catch
		{
		}
		try
		{
			foreach (PlayerController localPlayer in ClientScene.localPlayers)
			{
				if (localPlayer != null && !((Object)(object)localPlayer.gameObject == (Object)null))
				{
					CharacterBody component3 = localPlayer.gameObject.GetComponent<CharacterBody>();
					if ((Object)(object)component3 != (Object)null)
					{
						return component3;
					}
					CharacterMaster component4 = localPlayer.gameObject.GetComponent<CharacterMaster>();
					CharacterBody val2 = (((Object)(object)component4 != (Object)null) ? component4.GetBody() : null);
					if ((Object)(object)val2 != (Object)null)
					{
						return val2;
					}
				}
			}
		}
		catch
		{
		}
		return null;
	}

	private static bool GetLocalInteractor(out Interactor interactor, out string failReason)
	{
		interactor = null;
		failReason = "unknown";
		try
		{
			LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser();
			if (firstLocalUser != null)
			{
				GameObject[] array = (GameObject[])(object)new GameObject[2] { firstLocalUser.cachedBodyObject, firstLocalUser.cachedMasterObject };
				foreach (GameObject val in array)
				{
					if (!((Object)(object)val == (Object)null))
					{
						interactor = FindInteractor(val);
						if ((Object)(object)interactor != (Object)null)
						{
							return true;
						}
					}
				}
			}
		}
		catch
		{
		}
		try
		{
			List<PlayerController> localPlayers = ClientScene.localPlayers;
			if (localPlayers != null)
			{
				for (int j = 0; j < localPlayers.Count; j++)
				{
					PlayerController val2 = localPlayers[j];
					if (val2 != null && !((Object)(object)val2.gameObject == (Object)null))
					{
						interactor = FindInteractor(val2.gameObject);
						if ((Object)(object)interactor != (Object)null)
						{
							return true;
						}
					}
				}
			}
		}
		catch
		{
		}
		try
		{
			NetworkIdentity[] array2 = Object.FindObjectsOfType<NetworkIdentity>();
			foreach (NetworkIdentity val3 in array2)
			{
				if (!((Object)(object)val3 == (Object)null) && val3.isLocalPlayer && !((Object)(object)((Component)val3).gameObject == (Object)null))
				{
					interactor = FindInteractor(((Component)val3).gameObject);
					if ((Object)(object)interactor != (Object)null)
					{
						return true;
					}
				}
			}
		}
		catch
		{
		}
		failReason = "no local player found (tried LocalUserManager, ClientScene.localPlayers, NetworkIdentity scan) — not in a run yet?";
		return false;
	}

	private static Interactor FindInteractor(GameObject masterGo)
	{
		try
		{
			Interactor component = masterGo.GetComponent<Interactor>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			CharacterMaster component2 = masterGo.GetComponent<CharacterMaster>();
			CharacterBody val = (((Object)(object)component2 != (Object)null) ? component2.GetBody() : null);
			if ((Object)(object)val != (Object)null)
			{
				component = ((Component)val).GetComponent<Interactor>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
				component = ((Component)val).GetComponentInChildren<Interactor>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
			}
			return masterGo.GetComponentInChildren<Interactor>();
		}
		catch
		{
			return null;
		}
	}
}