Decompiled source of RunicInteraction v1.0.0

RunicInteraction.dll

Decompiled 9 hours 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.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using RunicInteraction.Core;
using RunicInteraction.Integration;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Runic Interaction")]
[assembly: AssemblyDescription("Small, reversible Valheim interaction conveniences with vanilla outcomes.")]
[assembly: AssemblyCompany("Chazman")]
[assembly: AssemblyProduct("Runic Interaction")]
[assembly: AssemblyCopyright("Copyright © 2026 Chazman")]
[assembly: ComVisible(false)]
[assembly: Guid("37b6d7d9-ad61-4e8a-9b3b-4d64586e47d0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: InternalsVisibleTo("RunicInteraction.Tests")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RunicInteraction
{
	internal static class InteractionConfig
	{
		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<bool> HoldToRepeat { get; private set; }

		internal static ConfigEntry<bool> TransferGestures { get; private set; }

		internal static ConfigEntry<bool> DragTransfer { get; private set; }

		internal static ConfigEntry<bool> AutoCloseDoors { get; private set; }

		internal static ConfigEntry<bool> EquipmentRestore { get; private set; }

		internal static ConfigEntry<bool> MenuMemory { get; private set; }

		internal static ConfigEntry<bool> TextEntryPolish { get; private set; }

		internal static ConfigEntry<bool> PickupFilters { get; private set; }

		internal static ConfigEntry<float> DoorDelaySeconds { get; private set; }

		internal static ConfigEntry<float> DoorRecentUseSeconds { get; private set; }

		internal static ConfigEntry<float> DoorObstructionRadius { get; private set; }

		internal static ConfigEntry<int> PortalTextLimit { get; private set; }

		internal static ConfigEntry<int> SignTextLimit { get; private set; }

		internal static ConfigEntry<int> TameTextLimit { get; private set; }

		internal static ConfigEntry<float> TextCommitRange { get; private set; }

		internal static ConfigEntry<string> FilteredPickupItems { get; private set; }

		internal static ConfigEntry<bool> FilterQuestItems { get; private set; }

		internal static ConfigEntry<string> PickupBypassControllerModifier { get; private set; }

		internal static ConfigEntry<bool> VerboseLogging { get; private set; }

		internal static event Action<ConfigDefinition> Changed;

		internal static void Bind(ConfigFile config)
		{
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Expected O, but got Unknown
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Expected O, but got Unknown
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Expected O, but got Unknown
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Expected O, but got Unknown
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Expected O, but got Unknown
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Expected O, but got Unknown
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Expected O, but got Unknown
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Expected O, but got Unknown
			Enabled = Bind(config, "General", "Enabled", value: true, "Master switch. When false every patch yields to vanilla behavior.");
			HoldToRepeat = Bind(config, "Features", "HoldToRepeat", value: true, "Let vanilla fuel/input interactions repeat at Valheim's installed 0.2 second hold cadence.");
			TransferGestures = Bind(config, "Features", "TransferGestures", value: true, "Add Alt-click full-stack transfer while retaining vanilla Ctrl-click and controller transfer.");
			DragTransfer = Bind(config, "Features", "DragTransfer", value: false, "Reserved fail-closed gate. Valheim 0.221.12 has no authority-safe drag-sweep transaction boundary.");
			AutoCloseDoors = Bind(config, "Features", "AutoCloseDoors", value: false, "Session-only delayed close for doors opened by the native local player. Off by default.");
			EquipmentRestore = Bind(config, "Features", "EquipmentRestore", value: true, "Restore the previously selected legal weapon/shield after a temporary Tool is put away.");
			MenuMemory = Bind(config, "Features", "MenuMemory", value: true, "Remember crafting selection and active group per station/mode for the current game session.");
			TextEntryPolish = Bind(config, "Features", "TextEntryPolish", value: true, "Validate portal, sign, and tame text immediately before vanilla commit.");
			PickupFilters = Bind(config, "Features", "PickupFilters", value: true, "Decline configured world drops before ownership or inventory mutation. Alt+Use bypasses the filter.");
			DoorDelaySeconds = Bind(config, "Door Auto-Close", "DelaySeconds", 4f, new ConfigDescription("Delay after the last door use.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>()));
			DoorRecentUseSeconds = Bind(config, "Door Auto-Close", "RecentUseSafetySeconds", 1.5f, new ConfigDescription("Minimum quiet time before closing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>()));
			DoorObstructionRadius = Bind(config, "Door Auto-Close", "ObstructionRadiusMeters", 0.9f, new ConfigDescription("Non-alloc character/movable-body check around the doorway.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.35f, 2f), Array.Empty<object>()));
			PortalTextLimit = Bind(config, "Text Entry", "PortalCharacterLimit", 10, new ConfigDescription("Cannot exceed vanilla's portal limit.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
			SignTextLimit = Bind(config, "Text Entry", "SignCharacterLimit", 50, new ConfigDescription("Upper bound; a sign's smaller prefab limit still wins.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 200), Array.Empty<object>()));
			TameTextLimit = Bind(config, "Text Entry", "TameCharacterLimit", 10, new ConfigDescription("Cannot exceed vanilla's tame-name limit.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
			TextCommitRange = Bind(config, "Text Entry", "CommitRangeMeters", 6f, new ConfigDescription("Maximum portal/sign commit distance; tame naming keeps its vanilla 15 m bound.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 15f), Array.Empty<object>()));
			FilteredPickupItems = Bind(config, "Pickup Filter", "Items", string.Empty, "Comma/semicolon/newline-separated prefab IDs or localized item tokens, maximum 128 exact entries.");
			FilterQuestItems = Bind(config, "Pickup Filter", "AllowQuestItemFiltering", value: false, "Dangerous option. False guarantees quest items bypass pickup filters.");
			PickupBypassControllerModifier = Bind(config, "Controller", "PickupBypassModifierAction", "JoyRStick", new ConfigDescription("Generic Valheim controller action held while JoyUse intentionally bypasses a pickup filter.", (AcceptableValueBase)(object)new AcceptableValueList<string>(InteractionInputBindings.CreateControllerModifierOptions()), Array.Empty<object>()));
			VerboseLogging = Bind(config, "Diagnostics", "VerboseLogging", value: false, "Log bounded feature decisions without logging inventory contents or text-entry content.");
			config.SettingChanged += delegate(object _, SettingChangedEventArgs arguments)
			{
				Action<ConfigDefinition> changed = InteractionConfig.Changed;
				if (changed != null)
				{
					object obj;
					if (arguments == null)
					{
						obj = null;
					}
					else
					{
						ConfigEntryBase changedSetting = arguments.ChangedSetting;
						obj = ((changedSetting != null) ? changedSetting.Definition : null);
					}
					changed((ConfigDefinition)obj);
				}
			};
		}

		private static ConfigEntry<T> Bind<T>(ConfigFile file, string section, string key, T value, string description)
		{
			return file.Bind<T>(section, key, value, description);
		}

		private static ConfigEntry<T> Bind<T>(ConfigFile file, string section, string key, T value, ConfigDescription description)
		{
			return file.Bind<T>(section, key, value, description);
		}
	}
	internal static class Diagnostics
	{
		private static ManualLogSource _log;

		internal static void Initialize(ManualLogSource log)
		{
			_log = log;
		}

		internal static void Info(string message)
		{
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogInfo((object)message);
			}
		}

		internal static void Warn(string message)
		{
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogWarning((object)message);
			}
		}

		internal static void Error(string message)
		{
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogError((object)message);
			}
		}

		internal static void Error(Exception exception, string message)
		{
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogError((object)(message + " " + exception.GetType().Name + ": " + exception.Message));
			}
		}

		internal static void Trace(string message)
		{
			if (InteractionConfig.VerboseLogging != null && InteractionConfig.VerboseLogging.Value)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogDebug((object)message);
				}
			}
		}
	}
	[BepInPlugin("chazman.RunicInteraction", "Runic Interaction", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "chazman.RunicInteraction";

		public const string Name = "Runic Interaction";

		public const string Version = "1.0.0";

		public const string ModuleId = "runic.interaction";

		private readonly List<KeybindingDescriptor> _keybindings = new List<KeybindingDescriptor>();

		private Harmony _harmony;

		private InteractionRuntime _runtime;

		private bool _configurationSubscribed;

		private bool _shuttingDown;

		internal static bool RuntimeReady { get; private set; }

		private void Awake()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			Diagnostics.Initialize(((BaseUnityPlugin)this).Logger);
			InteractionConfig.Bind(((BaseUnityPlugin)this).Config);
			InteractionConfig.Changed += OnConfigurationChanged;
			_configurationSubscribed = true;
			try
			{
				if (!ValheimAccess.Initialize(out var problem))
				{
					throw new MissingMethodException(problem);
				}
				_runtime = new InteractionRuntime();
				_runtime.Initialize();
				_harmony = new Harmony("chazman.RunicInteraction");
				_harmony.PatchAll(typeof(Plugin).Assembly);
				DoorAutoCloseRuntime.Initialize();
				RegisterKeybindings();
				RuntimeReady = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Runic Interaction v1.0.0 ready for Valheim 0.221.12. Hold-repeat uses Valheim's 0.2 s cadence and every repeated item still passes the original station callback.");
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Door auto-close is off by default. Drag-sweep and generic filter memory are explicit disabled gates; ordinary vanilla dragging and menu behavior remain available.");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Runic Interaction startup failed closed; all interactions remain vanilla. " + ex.GetType().Name + ": " + ex.Message));
				ShutdownRuntime();
			}
		}

		private void Update()
		{
			if (!RuntimeReady || _runtime == null)
			{
				return;
			}
			try
			{
				_runtime.Tick();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Runic Interaction runtime faulted and was disabled for this session; vanilla behavior was restored. " + ex.GetType().Name + ": " + ex.Message));
				ShutdownRuntime();
			}
		}

		private void OnConfigurationChanged(ConfigDefinition definition)
		{
			if (!RuntimeReady || _runtime == null)
			{
				return;
			}
			try
			{
				if (definition == (ConfigDefinition)null || (definition.Section == "Controller" && definition.Key == "PickupBypassModifierAction"))
				{
					RefreshKeybindings();
				}
				_runtime.OnConfigurationChanged();
				Diagnostics.Info("Interaction configuration refreshed; no restart is required.");
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Configuration refresh failed; previous runtime state remains active.");
			}
		}

		private void OnDestroy()
		{
			ShutdownRuntime();
			Diagnostics.Initialize(null);
		}

		private void RegisterKeybindings()
		{
			ReplaceKeybindings(InteractionInputBindings.CreateDescriptors(InteractionConfig.PickupBypassControllerModifier.Value));
		}

		private void RefreshKeybindings()
		{
			IReadOnlyList<KeybindingDescriptor> readOnlyList = InteractionInputBindings.CreateDescriptors(InteractionConfig.PickupBypassControllerModifier.Value);
			if (!BindingsMatch(readOnlyList))
			{
				ReplaceKeybindings(readOnlyList);
			}
		}

		private bool BindingsMatch(IReadOnlyList<KeybindingDescriptor> candidates)
		{
			if (candidates == null || candidates.Count != _keybindings.Count)
			{
				return false;
			}
			for (int i = 0; i < candidates.Count; i++)
			{
				KeybindingDescriptor keybindingDescriptor = _keybindings[i];
				KeybindingDescriptor keybindingDescriptor2 = candidates[i];
				if (!string.Equals(keybindingDescriptor.QualifiedId, keybindingDescriptor2.QualifiedId, StringComparison.Ordinal) || !string.Equals(keybindingDescriptor.Context, keybindingDescriptor2.Context, StringComparison.Ordinal) || !keybindingDescriptor.Chord.Equals(keybindingDescriptor2.Chord))
				{
					return false;
				}
			}
			return true;
		}

		private void ReplaceKeybindings(IReadOnlyList<KeybindingDescriptor> replacements)
		{
			if (replacements == null || replacements.Count != 6)
			{
				throw new InvalidOperationException("Interaction keybinding catalog is incomplete.");
			}
			List<KeybindingDescriptor> list = new List<KeybindingDescriptor>(replacements.Count);
			for (int i = 0; i < replacements.Count; i++)
			{
				list.Add(replacements[i] ?? throw new InvalidOperationException("Interaction keybinding catalog contains an empty entry."));
			}
			_keybindings.Clear();
			_keybindings.AddRange(list);
		}

		private void DisposeKeybindings()
		{
			_keybindings.Clear();
		}

		private void ShutdownRuntime()
		{
			if (_shuttingDown)
			{
				return;
			}
			_shuttingDown = true;
			RuntimeReady = false;
			if (_configurationSubscribed)
			{
				InteractionConfig.Changed -= OnConfigurationChanged;
				_configurationSubscribed = false;
			}
			try
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch (Exception ex)
			{
				Diagnostics.Warn("Could not remove every interaction patch: " + ex.Message);
			}
			_harmony = null;
			try
			{
				_runtime?.Shutdown();
			}
			catch (Exception ex2)
			{
				Diagnostics.Warn("Runtime cleanup was incomplete: " + ex2.Message);
			}
			_runtime = null;
			DisposeKeybindings();
			_shuttingDown = false;
		}
	}
}
namespace RunicInteraction.Integration
{
	internal static class DoorAutoCloseRuntime
	{
		internal readonly struct DoorOpenCapture
		{
			internal Door Door { get; }

			internal Player Actor { get; }

			internal ZDOID Id { get; }

			internal uint Revision { get; }

			internal int PrefabHash { get; }

			internal bool Eligible
			{
				get
				{
					//IL_001b: 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)
					if (Object.op_Implicit((Object)(object)Door) && Object.op_Implicit((Object)(object)Actor))
					{
						ZDOID id = Id;
						return !((ZDOID)(ref id)).IsNone();
					}
					return false;
				}
			}

			internal DoorOpenCapture(Door door, Player actor, ZDOID id, uint revision, int prefabHash)
			{
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				Door = door;
				Actor = actor;
				Id = id;
				Revision = revision;
				PrefabHash = prefabHash;
			}
		}

		private sealed class PendingDoorTimer
		{
			internal Door Door;

			internal Player Actor;

			internal ZDOID Id;

			internal int ExpectedPrefabHash;

			internal float DueRealtime;

			internal float ExpiresRealtime;
		}

		private const int MaximumTimers = 64;

		private const int TickBudget = 8;

		private const float MaximumLifetimeSeconds = 70f;

		private static readonly List<PendingDoorTimer> Timers = new List<PendingDoorTimer>(64);

		private static readonly Collider[] Obstructions = (Collider[])(object)new Collider[32];

		private static int _cursor;

		internal static void Initialize()
		{
			Shutdown();
		}

		internal static DoorOpenCapture BeforeInteract(Door door, Humanoid character, bool hold)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if (!(!FeatureOn() || !Object.op_Implicit((Object)(object)door) || hold))
			{
				Player val = (Player)(object)((character is Player) ? character : null);
				if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && ((Character)val).IsOwner() && !door.m_canNotBeClosed && !Object.op_Implicit((Object)(object)door.m_keyItem))
				{
					ZNetView component = ((Component)door).GetComponent<ZNetView>();
					ZDO val2 = ((Object.op_Implicit((Object)(object)component) && component.IsValid()) ? component.GetZDO() : null);
					if (val2 == null || !val2.IsValid() || ((ZDOID)(ref val2.m_uid)).IsNone() || val2.GetInt(ZDOVars.s_state, 0) != 0)
					{
						return default(DoorOpenCapture);
					}
					return new DoorOpenCapture(door, val, val2.m_uid, val2.DataRevision, val2.GetPrefab());
				}
			}
			return default(DoorOpenCapture);
		}

		internal static void AfterInteract(DoorOpenCapture capture, bool vanillaAccepted)
		{
			//IL_0029: 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)
			if (!capture.Eligible || !vanillaAccepted || !FeatureOn())
			{
				return;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			for (int i = 0; i < Timers.Count; i++)
			{
				if (!(Timers[i].Id != capture.Id))
				{
					Timers[i] = NewTimer(capture, realtimeSinceStartup);
					return;
				}
			}
			if (Timers.Count >= 64)
			{
				Diagnostics.Warn("Door auto-close reached its bounded timer capacity; this door remains open.");
			}
			else
			{
				Timers.Add(NewTimer(capture, realtimeSinceStartup));
			}
		}

		internal static void Tick()
		{
			if (!FeatureOn())
			{
				Shutdown();
				return;
			}
			int num = Math.Min(8, Timers.Count);
			for (int i = 0; i < num; i++)
			{
				if (Timers.Count <= 0)
				{
					break;
				}
				if (_cursor >= Timers.Count)
				{
					_cursor = 0;
				}
				int cursor = _cursor;
				if (TickOne(Timers[cursor]))
				{
					Timers.RemoveAt(cursor);
					if (_cursor >= Timers.Count)
					{
						_cursor = 0;
					}
				}
				else
				{
					_cursor++;
				}
			}
		}

		internal static void OnConfigurationChanged()
		{
			if (!FeatureOn())
			{
				Shutdown();
			}
		}

		internal static void ObserveWardTopologyMutation(ZDO zdo)
		{
		}

		internal static void Shutdown()
		{
			Timers.Clear();
			_cursor = 0;
		}

		private static PendingDoorTimer NewTimer(DoorOpenCapture capture, float now)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			return new PendingDoorTimer
			{
				Door = capture.Door,
				Actor = capture.Actor,
				Id = capture.Id,
				ExpectedPrefabHash = capture.PrefabHash,
				DueRealtime = now + EffectiveDelaySeconds(),
				ExpiresRealtime = now + 70f
			};
		}

		private static bool TickOne(PendingDoorTimer timer)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (timer == null || realtimeSinceStartup >= timer.ExpiresRealtime || !Object.op_Implicit((Object)(object)timer.Door) || !Object.op_Implicit((Object)(object)timer.Actor))
			{
				return true;
			}
			ZNetView component = ((Component)timer.Door).GetComponent<ZNetView>();
			ZDO val = ((Object.op_Implicit((Object)(object)component) && component.IsValid()) ? component.GetZDO() : null);
			if (val == null || !val.IsValid() || val.m_uid != timer.Id || val.GetPrefab() != timer.ExpectedPrefabHash || val.GetInt(ZDOVars.s_state, 0) == 0)
			{
				return true;
			}
			if (realtimeSinceStartup < timer.DueRealtime)
			{
				return false;
			}
			if (timer.Door.m_canNotBeClosed || Object.op_Implicit((Object)(object)timer.Door.m_keyItem) || !ValheimAccess.DoorCanInteract(timer.Door))
			{
				return true;
			}
			if (!PrivateArea.CheckAccess(((Component)timer.Door).transform.position, 0f, false, false))
			{
				return true;
			}
			if (IsObstructed(timer.Door))
			{
				timer.DueRealtime = realtimeSinceStartup + 0.5f;
				return false;
			}
			if (!component.IsOwner())
			{
				component.ClaimOwnership();
				timer.DueRealtime = realtimeSinceStartup + 0.25f;
				return false;
			}
			try
			{
				ValheimAccess.CloseDoor(timer.Door, ZNet.GetUID());
				return val.GetInt(ZDOVars.s_state, 0) == 0;
			}
			catch (Exception ex)
			{
				Diagnostics.Warn("Door auto-close stopped: " + ex.GetType().Name + ".");
				return true;
			}
		}

		private static bool IsObstructed(Door door)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			int num = Physics.OverlapSphereNonAlloc(((Component)door).transform.position + Vector3.up, InteractionConfig.DoorObstructionRadius.Value, Obstructions, -1, (QueryTriggerInteraction)1);
			bool result = num >= Obstructions.Length;
			for (int i = 0; i < Math.Min(num, Obstructions.Length); i++)
			{
				Collider val = Obstructions[i];
				Obstructions[i] = null;
				if (Object.op_Implicit((Object)(object)val) && !((Component)val).transform.IsChildOf(((Component)door).transform))
				{
					Rigidbody attachedRigidbody = val.attachedRigidbody;
					if (Object.op_Implicit((Object)(object)((Component)val).GetComponentInParent<Character>()) || (Object.op_Implicit((Object)(object)attachedRigidbody) && !attachedRigidbody.isKinematic))
					{
						return true;
					}
				}
			}
			return result;
		}

		private static bool FeatureOn()
		{
			ConfigEntry<bool> enabled = InteractionConfig.Enabled;
			if (enabled != null && enabled.Value)
			{
				return InteractionConfig.AutoCloseDoors?.Value ?? false;
			}
			return false;
		}

		private static float EffectiveDelaySeconds()
		{
			return Mathf.Max(Mathf.Clamp(InteractionConfig.DoorDelaySeconds.Value, 1f, 60f), Mathf.Clamp(InteractionConfig.DoorRecentUseSeconds.Value, 0.5f, 10f));
		}
	}
	internal static class EquipmentRestoreRuntime
	{
		internal readonly struct EquipCapture
		{
			internal bool Local { get; }

			internal bool Tool { get; }

			internal ItemData Right { get; }

			internal ItemData Left { get; }

			internal EquipCapture(bool local, bool tool, ItemData right, ItemData left)
			{
				Local = local;
				Tool = tool;
				Right = right;
				Left = left;
			}
		}

		private static ItemData _right;

		private static ItemData _left;

		private static ItemData _tool;

		private static int _dueFrame = -1;

		private static ItemData _swimRight;

		private static ItemData _swimLeft;

		private static int _swimDueFrame = -1;

		private static bool _restoring;

		internal static void BeforeHideHands(Humanoid actor, bool onlyRightHand)
		{
			if (!(!FeatureOn() || onlyRightHand) && !_restoring && Object.op_Implicit((Object)(object)actor) && !((Object)(object)actor != (Object)(object)Player.m_localPlayer))
			{
				Player val = (Player)(object)((actor is Player) ? actor : null);
				if (Object.op_Implicit((Object)(object)val) && ((Character)val).IsSwimming() && !((Character)val).IsOnGround())
				{
					_swimRight = (IsCombatHand(actor.RightItem) ? actor.RightItem : null);
					_swimLeft = (IsCombatHand(actor.LeftItem) ? actor.LeftItem : null);
					_swimDueFrame = -1;
				}
			}
		}

		internal static void AfterShowHands(Humanoid actor, bool onlyRightHand)
		{
			if (!(!FeatureOn() || onlyRightHand) && !_restoring && Object.op_Implicit((Object)(object)actor) && !((Object)(object)actor != (Object)(object)Player.m_localPlayer) && (_swimRight != null || _swimLeft != null))
			{
				if (SwimCandidatesRestored(actor))
				{
					ClearSwim();
				}
				else
				{
					_swimDueFrame = Time.frameCount + 1;
				}
			}
		}

		internal static EquipCapture BeforeEquip(Humanoid actor, ItemData item)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Invalid comparison between Unknown and I4
			bool flag = Object.op_Implicit((Object)(object)actor) && (Object)(object)actor == (Object)(object)Player.m_localPlayer;
			bool flag2 = item != null && (int)item.m_shared.m_itemType == 19;
			if (!flag || !flag2 || _restoring)
			{
				return new EquipCapture(flag, flag2, null, null);
			}
			return new EquipCapture(local: true, tool: true, IsCombatHand(actor.RightItem) ? actor.RightItem : null, IsCombatHand(actor.LeftItem) ? actor.LeftItem : null);
		}

		internal static void AfterEquip(Humanoid actor, ItemData item, bool result, EquipCapture capture)
		{
			if (!result || !capture.Local || _restoring || item == null)
			{
				return;
			}
			if (capture.Tool)
			{
				_tool = item;
				_dueFrame = -1;
				if (capture.Right != null || capture.Left != null)
				{
					_right = capture.Right;
					_left = capture.Left;
				}
			}
			else if (IsCombatHand(item))
			{
				Clear();
			}
		}

		internal static void AfterUnequip(Humanoid actor, ItemData item)
		{
			if (!_restoring && Object.op_Implicit((Object)(object)actor) && !((Object)(object)actor != (Object)(object)Player.m_localPlayer) && item != null && item == _tool)
			{
				if (_right == null && _left == null)
				{
					Clear();
				}
				else
				{
					_dueFrame = Time.frameCount + 1;
				}
			}
		}

		internal static void Tick()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!FeatureOn() || !Object.op_Implicit((Object)(object)localPlayer))
			{
				Clear();
				return;
			}
			ProcessSwimRestore(localPlayer);
			if (_tool != null && !((Humanoid)localPlayer).GetInventory().ContainsItem(_tool))
			{
				_tool = null;
				if (_right != null || _left != null)
				{
					_dueFrame = Time.frameCount + 1;
				}
			}
			if (_dueFrame < 0 || Time.frameCount < _dueFrame)
			{
				return;
			}
			bool temporaryToolStillEquipped = _tool != null && ((Humanoid)localPlayer).IsItemEquiped(_tool);
			bool flag = HasUnrelatedCombat((Humanoid)(object)localPlayer, _right, _left);
			if (!EquipmentRestorePolicy.MayRestore(enabled: true, (Object)(object)localPlayer == (Object)(object)Player.m_localPlayer, !((Character)localPlayer).IsDead(), ((Character)localPlayer).IsSwimming() && !((Character)localPlayer).IsOnGround(), ((Character)localPlayer).InAttack(), ((Character)localPlayer).InDodge(), temporaryToolStillEquipped, flag))
			{
				if (flag)
				{
					Clear();
				}
				else
				{
					_dueFrame = Time.frameCount + 1;
				}
				return;
			}
			ItemData val = ((((Humanoid)localPlayer).RightItem == _right || ((Humanoid)localPlayer).LeftItem == _right) ? null : (LegalCandidate(localPlayer, _right) ? _right : null));
			ItemData val2 = ((((Humanoid)localPlayer).RightItem == _left || ((Humanoid)localPlayer).LeftItem == _left) ? null : (LegalCandidate(localPlayer, _left) ? _left : null));
			Clear();
			if (val == null && val2 == null)
			{
				return;
			}
			_restoring = true;
			try
			{
				if (val != null)
				{
					((Humanoid)localPlayer).EquipItem(val, true);
				}
				if (val2 != null)
				{
					((Humanoid)localPlayer).EquipItem(val2, true);
				}
			}
			finally
			{
				_restoring = false;
			}
		}

		internal static void OnConfigurationChanged()
		{
			if (!FeatureOn())
			{
				Clear();
			}
		}

		internal static void Shutdown()
		{
			Clear();
		}

		private static bool LegalCandidate(Player player, ItemData item)
		{
			if (item != null && ((Humanoid)player).GetInventory().ContainsItem(item) && IsCombatHand(item))
			{
				if (item.m_shared.m_useDurability)
				{
					return item.m_durability > 0f;
				}
				return true;
			}
			return false;
		}

		private static void ProcessSwimRestore(Player player)
		{
			if (_swimDueFrame < 0 || Time.frameCount < _swimDueFrame)
			{
				return;
			}
			if (SwimCandidatesRestored((Humanoid)(object)player))
			{
				ClearSwim();
				return;
			}
			bool flag = HasUnrelatedCombat((Humanoid)(object)player, _swimRight, _swimLeft);
			if (!EquipmentRestorePolicy.MayRestore(enabled: true, (Object)(object)player == (Object)(object)Player.m_localPlayer, !((Character)player).IsDead(), ((Character)player).IsSwimming() && !((Character)player).IsOnGround(), ((Character)player).InAttack(), ((Character)player).InDodge(), temporaryToolStillEquipped: false, flag))
			{
				if (flag)
				{
					ClearSwim();
				}
				else
				{
					_swimDueFrame = Time.frameCount + 1;
				}
				return;
			}
			ItemData val = ((((Humanoid)player).RightItem == _swimRight) ? null : (LegalCandidate(player, _swimRight) ? _swimRight : null));
			ItemData val2 = ((((Humanoid)player).LeftItem == _swimLeft) ? null : (LegalCandidate(player, _swimLeft) ? _swimLeft : null));
			ClearSwim();
			if (val == null && val2 == null)
			{
				return;
			}
			_restoring = true;
			try
			{
				if (val != null)
				{
					((Humanoid)player).EquipItem(val, true);
				}
				if (val2 != null)
				{
					((Humanoid)player).EquipItem(val2, true);
				}
			}
			finally
			{
				_restoring = false;
			}
		}

		private static bool SwimCandidatesRestored(Humanoid actor)
		{
			if (_swimRight == null || actor.RightItem == _swimRight)
			{
				if (_swimLeft != null)
				{
					return actor.LeftItem == _swimLeft;
				}
				return true;
			}
			return false;
		}

		private static bool HasUnrelatedCombat(Humanoid actor, ItemData storedRight, ItemData storedLeft)
		{
			if (!IsCombatHand(actor.RightItem) || actor.RightItem == storedRight || actor.RightItem == storedLeft)
			{
				if (IsCombatHand(actor.LeftItem) && actor.LeftItem != storedRight)
				{
					return actor.LeftItem != storedLeft;
				}
				return false;
			}
			return true;
		}

		private static bool IsCombatHand(ItemData item)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_0015: Invalid comparison between Unknown and I4
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Invalid comparison between Unknown and I4
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			if (item == null)
			{
				return false;
			}
			ItemType itemType = item.m_shared.m_itemType;
			if (itemType - 3 <= 2 || (int)itemType == 14 || (int)itemType == 22)
			{
				return true;
			}
			return false;
		}

		private static void Clear()
		{
			_right = null;
			_left = null;
			_tool = null;
			_dueFrame = -1;
			ClearSwim();
		}

		private static void ClearSwim()
		{
			_swimRight = null;
			_swimLeft = null;
			_swimDueFrame = -1;
		}

		private static bool FeatureOn()
		{
			if (InteractionConfig.Enabled.Value)
			{
				return InteractionConfig.EquipmentRestore.Value;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Smelter), "Awake")]
	internal static class SmelterAwakePatch
	{
		private static void Postfix(Smelter __instance)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				HoldRepeatRuntime.Register(__instance.m_addOreSwitch);
				HoldRepeatRuntime.Register(__instance.m_addWoodSwitch);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Smelter input registration failed; its switches remain vanilla.");
			}
		}
	}
	[HarmonyPatch(typeof(CookingStation), "Awake")]
	internal static class CookingStationAwakePatch
	{
		private static void Postfix(CookingStation __instance)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				HoldRepeatRuntime.Register(__instance.m_addFoodSwitch);
				HoldRepeatRuntime.Register(__instance.m_addFuelSwitch);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Cooking input registration failed; its switches remain vanilla.");
			}
		}
	}
	[HarmonyPatch(typeof(ShieldGenerator), "Start")]
	internal static class ShieldGeneratorStartPatch
	{
		private static void Postfix(ShieldGenerator __instance)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				HoldRepeatRuntime.Register(__instance.m_addFuelSwitch);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Shield fuel registration failed; its switch remains vanilla.");
			}
		}
	}
	[HarmonyPatch(typeof(Switch), "Interact", new Type[]
	{
		typeof(Humanoid),
		typeof(bool),
		typeof(bool)
	})]
	internal static class SwitchInteractPatch
	{
		private static void Prefix(Switch __instance, bool __1, out HoldRepeatRuntime.IntervalState __state)
		{
			__state = default(HoldRepeatRuntime.IntervalState);
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				__state = HoldRepeatRuntime.Begin(__instance, __1);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Hold-repeat preparation failed; this input remains vanilla.");
			}
		}

		private static Exception Finalizer(Switch __instance, HoldRepeatRuntime.IntervalState __state, Exception __exception)
		{
			try
			{
				HoldRepeatRuntime.End(__instance, __state);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Hold-repeat interval restoration failed.");
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(CookingStation), "Interact", new Type[]
	{
		typeof(Humanoid),
		typeof(bool),
		typeof(bool)
	})]
	internal static class CookingStationInteractPatch
	{
		private static void Prefix(CookingStation __instance, ref bool __1)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				if (HoldRepeatRuntime.ShouldConvertCookingHold(__instance, __1))
				{
					__1 = false;
				}
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Cooking hold-repeat decision failed; vanilla hold handling won.");
			}
		}
	}
	[HarmonyPatch(typeof(Fermenter), "Interact", new Type[]
	{
		typeof(Humanoid),
		typeof(bool),
		typeof(bool)
	})]
	internal static class FermenterInteractPatch
	{
		[HarmonyPriority(800)]
		[HarmonyBefore(new string[] { "chazman.RunicProduction" })]
		private static void Prefix(Fermenter __instance, ref bool __1)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				if (HoldRepeatRuntime.ShouldConvertFermenterHold(__instance, __1))
				{
					__1 = false;
				}
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Fermenter hold-repeat decision failed; vanilla hold handling won.");
			}
		}
	}
	[HarmonyPatch(typeof(Door), "Interact", new Type[]
	{
		typeof(Humanoid),
		typeof(bool),
		typeof(bool)
	})]
	internal static class DoorInteractAutoClosePatch
	{
		private static void Prefix(Door __instance, Humanoid __0, bool __1, out DoorAutoCloseRuntime.DoorOpenCapture __state)
		{
			__state = default(DoorAutoCloseRuntime.DoorOpenCapture);
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				__state = DoorAutoCloseRuntime.BeforeInteract(__instance, __0, __1);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Door opening capture failed; no auto-close intent will be created.");
			}
		}

		private static void Postfix(bool __result, DoorAutoCloseRuntime.DoorOpenCapture __state)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				DoorAutoCloseRuntime.AfterInteract(__state, __result);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Door auto-close approval failed; the door remains at its vanilla state.");
			}
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "AddToSector", new Type[]
	{
		typeof(ZDO),
		typeof(Vector2i)
	})]
	internal static class InteractionWardZdoAddedPatch
	{
		private static void Postfix(ZDO zdo)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				DoorAutoCloseRuntime.ObserveWardTopologyMutation(zdo);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Door ward index add observation failed closed.");
			}
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "RemoveFromSector", new Type[]
	{
		typeof(ZDO),
		typeof(Vector2i)
	})]
	internal static class InteractionWardZdoRemovedPatch
	{
		private static void Prefix(ZDO zdo)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				DoorAutoCloseRuntime.ObserveWardTopologyMutation(zdo);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Door ward index remove observation failed closed.");
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "OnLeftClick", new Type[] { typeof(UIInputHandler) })]
	internal static class InventoryGridLeftClickPatch
	{
		private static bool Prefix(InventoryGrid __instance, UIInputHandler __0)
		{
			if (!Plugin.RuntimeReady)
			{
				return true;
			}
			try
			{
				return !TransferGestureRuntime.TryHandleAltClick(__instance, __0);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Alt-transfer failed closed; vanilla click handling won.");
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnSelectedItem", new Type[]
	{
		typeof(InventoryGrid),
		typeof(ItemData),
		typeof(Vector2i),
		typeof(Modifier)
	})]
	internal static class InventoryGuiSelectedItemPatch
	{
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "chazman.RunicInventory" })]
		private static bool Prefix(InventoryGrid __0, ItemData __1, Modifier __3)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.RuntimeReady)
			{
				return true;
			}
			try
			{
				return TransferGestureRuntime.GuardVanillaMove(__0, __1, __3);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Transfer validation failed closed; the move was declined.");
				return false;
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "EquipItem", new Type[]
	{
		typeof(ItemData),
		typeof(bool)
	})]
	internal static class HumanoidEquipItemPatch
	{
		[HarmonyAfter(new string[] { "chazman.RunicInventory" })]
		[HarmonyPriority(0)]
		private static void Prefix(Humanoid __instance, ItemData __0, bool __runOriginal, out EquipmentRestoreRuntime.EquipCapture __state)
		{
			__state = default(EquipmentRestoreRuntime.EquipCapture);
			if (!__runOriginal || !Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				__state = EquipmentRestoreRuntime.BeforeEquip(__instance, __0);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Equipment capture failed; vanilla equip handling won.");
			}
		}

		[HarmonyBefore(new string[] { "chazman.RunicInventory" })]
		[HarmonyPriority(800)]
		private static void Postfix(Humanoid __instance, ItemData __0, bool __result, EquipmentRestoreRuntime.EquipCapture __state)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				EquipmentRestoreRuntime.AfterEquip(__instance, __0, __result, __state);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Equipment restore capture failed; no item was forced.");
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "UnequipItem", new Type[]
	{
		typeof(ItemData),
		typeof(bool)
	})]
	internal static class HumanoidUnequipItemPatch
	{
		private static void Postfix(Humanoid __instance, ItemData __0)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				EquipmentRestoreRuntime.AfterUnequip(__instance, __0);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Equipment restore scheduling failed; no item was forced.");
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "HideHandItems", new Type[]
	{
		typeof(bool),
		typeof(bool)
	})]
	internal static class HumanoidHideHandItemsPatch
	{
		private static void Prefix(Humanoid __instance, bool __0)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				EquipmentRestoreRuntime.BeforeHideHands(__instance, __0);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Swim equipment capture failed; vanilla hide handling won.");
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "ShowHandItems", new Type[]
	{
		typeof(bool),
		typeof(bool)
	})]
	internal static class HumanoidShowHandItemsPatch
	{
		private static void Postfix(Humanoid __instance, bool __0)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				EquipmentRestoreRuntime.AfterShowHands(__instance, __0);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Swim equipment fallback was not scheduled; vanilla show handling won.");
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "SetupCrafting")]
	internal static class InventoryGuiSetupCraftingPatch
	{
		private static void Prefix(InventoryGui __instance, out bool __state)
		{
			__state = false;
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				MenuMemoryRuntime.BeginSetup(__instance);
				__state = true;
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Menu context capture failed; setup remains vanilla.");
			}
		}

		private static void Postfix(InventoryGui __instance, bool __state)
		{
			if (!Plugin.RuntimeReady || !__state)
			{
				return;
			}
			try
			{
				MenuMemoryRuntime.CompleteSetup(__instance);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Menu context restore failed; vanilla selection remains active.");
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Hide")]
	internal static class InventoryGuiHidePatch
	{
		private static void Prefix(InventoryGui __instance)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				MenuMemoryRuntime.BeforeHide(__instance);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Menu selection capture failed; inventory close remains vanilla.");
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnTabCraftPressed")]
	internal static class InventoryGuiCraftTabPatch
	{
		private static void Prefix(InventoryGui __instance, out bool __state)
		{
			__state = false;
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				MenuMemoryRuntime.BeforeContextChange(__instance);
				__state = true;
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Craft-tab selection capture failed; tab behavior remains vanilla.");
			}
		}

		private static void Postfix(InventoryGui __instance, bool __state)
		{
			if (!Plugin.RuntimeReady || !__state)
			{
				return;
			}
			try
			{
				MenuMemoryRuntime.AfterContextChange(__instance);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Craft-tab selection restore failed; vanilla selection remains active.");
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnTabUpgradePressed")]
	internal static class InventoryGuiUpgradeTabPatch
	{
		private static void Prefix(InventoryGui __instance, out bool __state)
		{
			__state = false;
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				MenuMemoryRuntime.BeforeContextChange(__instance);
				__state = true;
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Upgrade-tab selection capture failed; tab behavior remains vanilla.");
			}
		}

		private static void Postfix(InventoryGui __instance, bool __state)
		{
			if (!Plugin.RuntimeReady || !__state)
			{
				return;
			}
			try
			{
				MenuMemoryRuntime.AfterContextChange(__instance);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Upgrade-tab selection restore failed; vanilla selection remains active.");
			}
		}
	}
	[HarmonyPatch(typeof(TextInput), "RequestText", new Type[]
	{
		typeof(TextReceiver),
		typeof(string),
		typeof(int)
	})]
	internal static class TextInputRequestPatch
	{
		private static void Prefix(TextReceiver __0, ref int __2)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				TextEntryRuntime.Begin(__0, ref __2);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Text-entry setup failed; vanilla limits remain in force.");
			}
		}
	}
	[HarmonyPatch(typeof(TextInput), "setText", new Type[] { typeof(string) })]
	internal static class TextInputCommitPatch
	{
		private static bool Prefix(TextInput __instance, string __0)
		{
			if (!Plugin.RuntimeReady)
			{
				return true;
			}
			try
			{
				return TextEntryRuntime.ValidateCommit(__instance, __0);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Text-entry validation faulted; commit was declined safely.");
				try
				{
					ValheimAccess.ClearQueuedTextReceiver(__instance);
				}
				catch (Exception exception2)
				{
					Diagnostics.Error(exception2, "Stale text receiver cleanup also failed.");
				}
				return false;
			}
		}
	}
	[HarmonyPatch(typeof(TextInput), "Hide")]
	internal static class TextInputHidePatch
	{
		private static void Postfix(TextInput __instance)
		{
			if (!Plugin.RuntimeReady)
			{
				return;
			}
			try
			{
				TextEntryRuntime.AfterHide(__instance);
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Text cancellation cleanup failed; no commit was synthesized.");
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "Pickup", new Type[]
	{
		typeof(GameObject),
		typeof(bool),
		typeof(bool)
	})]
	internal static class HumanoidPickupPatch
	{
		[HarmonyPriority(0)]
		[HarmonyAfter(new string[] { "chazman.RunicInventory" })]
		private static bool Prefix(Humanoid __instance, GameObject __0, ref bool __result)
		{
			if (!Plugin.RuntimeReady)
			{
				return true;
			}
			try
			{
				if (PickupFilterRuntime.Allow(__instance, __0))
				{
					return true;
				}
				__result = false;
				return false;
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, "Pickup filter faulted; vanilla pickup handling won.");
				return true;
			}
		}
	}
	internal static class HoldRepeatRuntime
	{
		private sealed class Marker
		{
		}

		internal readonly struct IntervalState
		{
			internal bool Changed { get; }

			internal float Previous { get; }

			internal IntervalState(bool changed, float previous)
			{
				Changed = changed;
				Previous = previous;
			}
		}

		private static readonly ConditionalWeakTable<Switch, Marker> Inputs = new ConditionalWeakTable<Switch, Marker>();

		internal static void Register(Switch input)
		{
			if (!Object.op_Implicit((Object)(object)input))
			{
				return;
			}
			try
			{
				Inputs.GetValue(input, (Switch _) => new Marker());
			}
			catch (ArgumentException)
			{
			}
		}

		internal static IntervalState Begin(Switch input, bool hold)
		{
			if (!hold || !FeatureOn() || !Object.op_Implicit((Object)(object)input) || !Inputs.TryGetValue(input, out var _))
			{
				return default(IntervalState);
			}
			float holdRepeatInterval = input.m_holdRepeatInterval;
			input.m_holdRepeatInterval = float.Epsilon;
			return new IntervalState(changed: true, holdRepeatInterval);
		}

		internal static void End(Switch input, IntervalState state)
		{
			if (state.Changed && Object.op_Implicit((Object)(object)input))
			{
				input.m_holdRepeatInterval = state.Previous;
			}
		}

		internal static bool ShouldConvertCookingHold(CookingStation station, bool hold)
		{
			if (hold && FeatureOn() && Object.op_Implicit((Object)(object)station) && !Object.op_Implicit((Object)(object)station.m_addFoodSwitch))
			{
				return !ValheimAccess.CookingHasDoneItem(station);
			}
			return false;
		}

		internal static bool ShouldConvertFermenterHold(Fermenter station, bool hold)
		{
			if (!hold || !FeatureOn() || !Object.op_Implicit((Object)(object)station))
			{
				return false;
			}
			ZNetView component = ((Component)station).GetComponent<ZNetView>();
			ZDO val = (Object.op_Implicit((Object)(object)component) ? component.GetZDO() : null);
			if (val != null)
			{
				return string.IsNullOrEmpty(val.GetString(ZDOVars.s_content, ""));
			}
			return false;
		}

		private static bool FeatureOn()
		{
			if (InteractionConfig.Enabled.Value)
			{
				return InteractionConfig.HoldToRepeat.Value;
			}
			return false;
		}
	}
	internal sealed class InteractionRuntime
	{
		private bool _dragWarning;

		private bool _filterMemoryWarning;

		internal void Initialize()
		{
			PickupFilterRuntime.Refresh();
			OnConfigurationChanged();
		}

		internal void Tick()
		{
			DoorAutoCloseRuntime.Tick();
			EquipmentRestoreRuntime.Tick();
		}

		internal void OnConfigurationChanged()
		{
			PickupFilterRuntime.Refresh();
			DoorAutoCloseRuntime.OnConfigurationChanged();
			EquipmentRestoreRuntime.OnConfigurationChanged();
			MenuMemoryRuntime.OnConfigurationChanged();
			TextEntryRuntime.OnConfigurationChanged();
			TransferGestureRuntime.OnConfigurationChanged();
			if (InteractionConfig.DragTransfer.Value && !_dragWarning)
			{
				_dragWarning = true;
				Diagnostics.Warn("DragTransfer remains disabled: Valheim 0.221.12 exposes no authority-safe drag-sweep transaction boundary. Ordinary vanilla dragging is unchanged.");
			}
			if (InteractionConfig.MenuMemory.Value && !_filterMemoryWarning)
			{
				_filterMemoryWarning = true;
				Diagnostics.Info("Menu memory is active for context/category/recipe selection. Filter memory is disabled because Valheim 0.221.12 exposes no stable filter state.");
			}
			if (!InteractionConfig.DragTransfer.Value)
			{
				_dragWarning = false;
			}
			if (!InteractionConfig.MenuMemory.Value)
			{
				_filterMemoryWarning = false;
			}
		}

		internal void Shutdown()
		{
			DoorAutoCloseRuntime.Shutdown();
			EquipmentRestoreRuntime.Shutdown();
			MenuMemoryRuntime.Shutdown();
			TextEntryRuntime.Shutdown();
			PickupFilterRuntime.Shutdown();
		}
	}
	internal static class MenuMemoryRuntime
	{
		private const int MaximumContexts = 64;

		private static readonly BoundedContextMemory<string, MenuSelection> Memory = new BoundedContextMemory<string, MenuSelection>(64, StringComparer.Ordinal);

		private static string _activeContext;

		private static string _pendingContext;

		private static MenuSelection _pendingSelection;

		private static bool _hasPending;

		internal static void BeginSetup(InventoryGui gui)
		{
			if (FeatureOn())
			{
				Capture(gui, _activeContext);
				_pendingContext = CurrentContext();
				_hasPending = Memory.TryGet(_pendingContext, out _pendingSelection);
			}
		}

		internal static void CompleteSetup(InventoryGui gui)
		{
			if (FeatureOn())
			{
				_activeContext = _pendingContext ?? CurrentContext();
				if (_hasPending)
				{
					Restore(gui, _pendingSelection);
				}
				_pendingContext = null;
				_hasPending = false;
			}
		}

		internal static void BeforeContextChange(InventoryGui gui)
		{
			Capture(gui, _activeContext ?? CurrentContext());
		}

		internal static void AfterContextChange(InventoryGui gui)
		{
			if (FeatureOn())
			{
				string key = (_activeContext = CurrentContext());
				if (Memory.TryGet(key, out var value))
				{
					Restore(gui, value);
				}
			}
		}

		internal static void BeforeHide(InventoryGui gui)
		{
			if (FeatureOn())
			{
				Capture(gui, _activeContext ?? CurrentContext());
			}
		}

		internal static void OnConfigurationChanged()
		{
			if (!FeatureOn())
			{
				Memory.Clear();
				_activeContext = null;
				_pendingContext = null;
				_hasPending = false;
			}
		}

		internal static void Shutdown()
		{
			Memory.Clear();
			_activeContext = null;
			_pendingContext = null;
			_hasPending = false;
		}

		private static void Capture(InventoryGui gui, string context)
		{
			if (!((Object)(object)gui == (Object)null) && !string.IsNullOrEmpty(context))
			{
				object selectedRecipePair = ValheimAccess.GetSelectedRecipePair(gui);
				Recipe pairRecipe = ValheimAccess.GetPairRecipe(selectedRecipePair);
				ItemData pairItem = ValheimAccess.GetPairItem(selectedRecipePair);
				string recipePrefab = (Object.op_Implicit((Object)(object)pairRecipe) ? ((Object)pairRecipe).name : string.Empty);
				Memory.Put(context, new MenuSelection(recipePrefab, pairItem != null, gui.ActiveGroup));
			}
		}

		private static void Restore(InventoryGui gui, MenuSelection selection)
		{
			if ((Object)(object)gui == (Object)null)
			{
				return;
			}
			if (selection.ActiveGroup >= 0)
			{
				ValheimAccess.SetActiveGroup(gui, selection.ActiveGroup, sound: false);
			}
			if (!selection.HasRecipe)
			{
				return;
			}
			IList availableRecipePairs = ValheimAccess.GetAvailableRecipePairs(gui);
			if (availableRecipePairs == null)
			{
				return;
			}
			for (int i = 0; i < availableRecipePairs.Count; i++)
			{
				object? pair = availableRecipePairs[i];
				Recipe pairRecipe = ValheimAccess.GetPairRecipe(pair);
				bool flag = ValheimAccess.GetPairItem(pair) != null;
				if (Object.op_Implicit((Object)(object)pairRecipe) && flag == selection.Upgrade && string.Equals(((Object)pairRecipe).name, selection.RecipePrefab, StringComparison.Ordinal))
				{
					ValheimAccess.SetRecipe(gui, i, center: false);
					break;
				}
			}
		}

		private static string CurrentContext()
		{
			Player localPlayer = Player.m_localPlayer;
			CraftingStation val = (Object.op_Implicit((Object)(object)localPlayer) ? localPlayer.GetCurrentCraftingStation() : null);
			string obj = (Object.op_Implicit((Object)(object)val) ? Utils.GetPrefabName(((Component)val).gameObject) : "handcraft");
			InventoryGui instance = InventoryGui.instance;
			string text = (((Object)(object)instance != (Object)null && instance.InUpradeTab()) ? "upgrade" : "craft");
			return obj + ":" + text;
		}

		private static bool FeatureOn()
		{
			if (InteractionConfig.Enabled.Value)
			{
				return InteractionConfig.MenuMemory.Value;
			}
			return false;
		}
	}
	internal static class PickupFilterRuntime
	{
		private static PickupFilterSet _filters = PickupFilterSet.Parse(string.Empty);

		private static string _controllerModifierAction = "JoyRStick";

		internal static void Refresh()
		{
			_filters = PickupFilterSet.Parse(InteractionConfig.FilteredPickupItems.Value);
			_controllerModifierAction = InteractionInputBindings.ResolveControllerModifier(InteractionConfig.PickupBypassControllerModifier.Value, out var usedDefault);
			if (_filters.Truncated)
			{
				Diagnostics.Warn("Pickup filter input exceeded 128 unique rules; extra rules were ignored.");
			}
			if (usedDefault)
			{
				Diagnostics.Warn("Pickup bypass controller modifier was outside the bounded installed-action set; JoyRStick remains active.");
			}
			Diagnostics.Trace("Pickup filter compiled " + _filters.Count + " exact rules.");
		}

		internal static bool Allow(Humanoid actor, GameObject worldObject)
		{
			if (!FeatureOn() || !Object.op_Implicit((Object)(object)actor) || (Object)(object)actor != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)worldObject))
			{
				return true;
			}
			if (BypassHeld())
			{
				return true;
			}
			ItemDrop component = worldObject.GetComponent<ItemDrop>();
			if (!Object.op_Implicit((Object)(object)component) || component.m_itemData == null)
			{
				return true;
			}
			if (component.m_itemData.m_shared.m_questItem && !InteractionConfig.FilterQuestItems.Value)
			{
				return true;
			}
			string prefabName = (Object.op_Implicit((Object)(object)component.m_itemData.m_dropPrefab) ? ((Object)component.m_itemData.m_dropPrefab).name : ((Object)worldObject).name);
			if (!_filters.Matches(prefabName, component.m_itemData.m_shared.m_name))
			{
				return true;
			}
			return false;
		}

		internal static void Shutdown()
		{
			_filters = PickupFilterSet.Parse(string.Empty);
		}

		private static bool BypassHeld()
		{
			if (!ZInput.GetKey((KeyCode)308, true) && !ZInput.GetKey((KeyCode)307, true))
			{
				return ZInput.GetButton(_controllerModifierAction);
			}
			return true;
		}

		private static bool FeatureOn()
		{
			if (InteractionConfig.Enabled.Value && InteractionConfig.PickupFilters.Value)
			{
				return _filters.Count > 0;
			}
			return false;
		}
	}
	internal static class TextEntryRuntime
	{
		private static TextReceiver _receiver;

		private static TextEntryKind _kind;

		private static int _limit;

		internal static void Begin(TextReceiver receiver, ref int requestedLimit)
		{
			if (FeatureOn())
			{
				_receiver = receiver;
				_kind = Classify(receiver);
				if (_kind == TextEntryKind.Unsupported)
				{
					_limit = 0;
					return;
				}
				int num = ConfiguredLimit(_kind, requestedLimit);
				_limit = Mathf.Max(1, Mathf.Min(requestedLimit, num));
				requestedLimit = _limit;
			}
		}

		internal static bool ValidateCommit(TextInput input, string value)
		{
			//IL_00a6: 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)
			if (!FeatureOn() || _kind == TextEntryKind.Unsupported)
			{
				return true;
			}
			TextReceiver queuedTextReceiver = ValheimAccess.GetQueuedTextReceiver(input);
			bool num = queuedTextReceiver != null && queuedTextReceiver == _receiver;
			TextReceiver receiver = _receiver;
			MonoBehaviour val = (MonoBehaviour)(object)((receiver is MonoBehaviour) ? receiver : null);
			Player localPlayer = Player.m_localPlayer;
			ZNetView val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<ZNetView>() : null);
			bool flag = num && Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val2) && val2.IsValid();
			bool authorized = flag && IsAuthorized(val, _kind);
			float num2 = ((_kind == TextEntryKind.Tame) ? 15f : InteractionConfig.TextCommitRange.Value);
			bool inRange = flag && Object.op_Implicit((Object)(object)localPlayer) && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)val).transform.position) <= num2;
			TextEntryDecision textEntryDecision = TextEntryPolicy.Validate(_kind, value, _limit, flag, authorized, inRange);
			if (textEntryDecision.Allowed)
			{
				return true;
			}
			ValheimAccess.ClearQueuedTextReceiver(input);
			if (Object.op_Implicit((Object)(object)localPlayer))
			{
				if (textEntryDecision.Reason == "permission-changed")
				{
					((Character)localPlayer).Message((MessageType)2, "$piece_noaccess", 0, (Sprite)null);
				}
				else if (textEntryDecision.Reason == "out-of-range")
				{
					((Character)localPlayer).Message((MessageType)2, "$msg_outofrange", 0, (Sprite)null);
				}
				else
				{
					((Character)localPlayer).Message((MessageType)2, "$msg_blocked", 0, (Sprite)null);
				}
			}
			Diagnostics.Trace("Text entry declined safely: " + textEntryDecision.Reason + ".");
			ClearSession();
			return false;
		}

		internal static void AfterHide(TextInput input)
		{
			if ((Object)(object)input != (Object)null)
			{
				ValheimAccess.ClearQueuedTextReceiver(input);
			}
			ClearSession();
		}

		internal static void OnConfigurationChanged()
		{
			if (!FeatureOn())
			{
				ClearSession();
			}
		}

		internal static void Shutdown()
		{
			ClearSession();
		}

		private static bool IsAuthorized(MonoBehaviour receiver, TextEntryKind kind)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (kind == TextEntryKind.Tame)
			{
				Tameable val = (Tameable)(object)((receiver is Tameable) ? receiver : null);
				if (val != null)
				{
					return val.IsTamed();
				}
				return false;
			}
			return PrivateArea.CheckAccess(((Component)receiver).transform.position, 0f, false, false);
		}

		private static TextEntryKind Classify(TextReceiver receiver)
		{
			if (receiver is TeleportWorld)
			{
				return TextEntryKind.Portal;
			}
			if (receiver is Sign)
			{
				return TextEntryKind.Sign;
			}
			if (receiver is Tameable)
			{
				return TextEntryKind.Tame;
			}
			return TextEntryKind.Unsupported;
		}

		private static int ConfiguredLimit(TextEntryKind kind, int fallback)
		{
			return kind switch
			{
				TextEntryKind.Portal => InteractionConfig.PortalTextLimit.Value, 
				TextEntryKind.Sign => InteractionConfig.SignTextLimit.Value, 
				TextEntryKind.Tame => InteractionConfig.TameTextLimit.Value, 
				_ => fallback, 
			};
		}

		private static void ClearSession()
		{
			_receiver = null;
			_kind = TextEntryKind.Unsupported;
			_limit = 0;
		}

		private static bool FeatureOn()
		{
			if (InteractionConfig.Enabled.Value)
			{
				return InteractionConfig.TextEntryPolish.Value;
			}
			return false;
		}
	}
	internal static class TransferGestureRuntime
	{
		private static bool _lockWarning;

		internal static bool TryHandleAltClick(InventoryGrid grid, UIInputHandler handler)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			if (!FeatureOn() || !Object.op_Implicit((Object)(object)grid) || !Object.op_Implicit((Object)(object)handler))
			{
				return false;
			}
			if (ZInput.GetKey((KeyCode)306, true) || ZInput.GetKey((KeyCode)305, true) || ZInput.GetKey((KeyCode)304, true) || ZInput.GetKey((KeyCode)303, true) || (!ZInput.GetKey((KeyCode)308, true) && !ZInput.GetKey((KeyCode)307, true)))
			{
				return false;
			}
			InventoryGui instance = InventoryGui.instance;
			Container currentContainer = ValheimAccess.GetCurrentContainer(instance);
			if (!Object.op_Implicit((Object)(object)currentContainer) || ValheimAccess.HasDrag(instance))
			{
				return false;
			}
			Vector2i buttonPosition = ValheimAccess.GetButtonPosition(grid, ((Component)handler).gameObject);
			Inventory inventory = grid.GetInventory();
			ItemData val = ((inventory != null) ? inventory.GetItemAt(buttonPosition.x, buttonPosition.y) : null);
			if (val == null || grid.m_onSelected == null)
			{
				return false;
			}
			if (!CanMove(currentContainer, val, instance, notify: true))
			{
				return true;
			}
			grid.m_onSelected(grid, val, buttonPosition, (Modifier)2);
			return true;
		}

		internal static bool GuardVanillaMove(InventoryGrid grid, ItemData item, Modifier modifier)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			if ((int)modifier != 2 || !FeatureOn())
			{
				return true;
			}
			InventoryGui instance = InventoryGui.instance;
			Container currentContainer = ValheimAccess.GetCurrentContainer(instance);
			if (!Object.op_Implicit((Object)(object)currentContainer))
			{
				return true;
			}
			return CanMove(currentContainer, item, instance, notify: true);
		}

		internal static void OnConfigurationChanged()
		{
			_lockWarning = false;
		}

		private static bool CanMove(Container container, ItemData item, InventoryGui gui, bool notify)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			long playerId = (Object.op_Implicit((Object)(object)localPlayer) ? localPlayer.GetPlayerID() : 0);
			bool flag = Object.op_Implicit((Object)(object)localPlayer) && ValheimAccess.ContainerAllows(container, playerId);
			bool flag2 = Object.op_Implicit((Object)(object)localPlayer) && PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false);
			bool num = TransferGesturePolicy.MayTransfer(FeatureOn(), Object.op_Implicit((Object)(object)container), Object.op_Implicit((Object)(object)container) && container.IsOwner(), flag, flag2, item != null, item?.m_shared.m_questItem ?? false, ValheimAccess.HasDrag(gui), itemProtectionAllowsTransfer: true);
			TransferItemProtection transferItemProtection = TransferItemProtection.NotApplicable;
			if (num)
			{
				transferItemProtection = ItemProtectionQueryAdapter.Resolve(item);
			}
			if (num && ItemProtectionQueryAdapter.AllowsTransfer(transferItemProtection))
			{
				return true;
			}
			if (transferItemProtection == TransferItemProtection.Unknown && !_lockWarning)
			{
				_lockWarning = true;
				Diagnostics.Warn("A transfer was denied because inventory.item-locks was advertised but its typed protection query was missing, incompatible, indeterminate, or faulted.");
			}
			if (notify && Object.op_Implicit((Object)(object)localPlayer))
			{
				if (!flag2)
				{
					((Character)localPlayer).Message((MessageType)2, "$piece_noaccess", 0, (Sprite)null);
				}
				else if (!flag)
				{
					((Character)localPlayer).Message((MessageType)2, "$msg_cantopen", 0, (Sprite)null);
				}
				else if (!ItemProtectionQueryAdapter.AllowsTransfer(transferItemProtection))
				{
					((Character)localPlayer).Message((MessageType)2, "$msg_blocked", 0, (Sprite)null);
				}
			}
			return false;
		}

		private static bool FeatureOn()
		{
			if (InteractionConfig.Enabled.Value)
			{
				return InteractionConfig.TransferGestures.Value;
			}
			return false;
		}
	}
	internal static class ValheimAccess
	{
		internal delegate void DoorRpcDelegate(Door instance, long sender, bool forward);

		internal delegate bool DoorCanInteractDelegate(Door instance);

		internal delegate bool CookingHaveDoneDelegate(CookingStation instance);

		internal delegate Vector2i GridButtonPositionDelegate(InventoryGrid instance, GameObject button);

		internal delegate bool ContainerCheckAccessDelegate(Container instance, long playerId);

		internal delegate void InventoryGuiSetRecipeDelegate(InventoryGui instance, int index, bool center);

		internal delegate void InventoryGuiSetActiveGroupDelegate(InventoryGui instance, int index, bool sound);

		private const BindingFlags InstanceAll = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static DoorRpcDelegate _doorRpc;

		private static DoorCanInteractDelegate _doorCanInteract;

		private static CookingHaveDoneDelegate _cookingHaveDone;

		private static GridButtonPositionDelegate _gridButtonPosition;

		private static ContainerCheckAccessDelegate _containerCheckAccess;

		private static InventoryGuiSetRecipeDelegate _setRecipe;

		private static InventoryGuiSetActiveGroupDelegate _setActiveGroup;

		private static FieldInfo _currentContainer;

		private static FieldInfo _dragObject;

		private static FieldInfo _queuedTextReceiver;

		private static FieldInfo _selectedRecipe;

		private static FieldInfo _availableRecipes;

		private static PropertyInfo _pairRecipe;

		private static PropertyInfo _pairItem;

		private static bool _ready;

		internal static bool Initialize(out string problem)
		{
			problem = string.Empty;
			try
			{
				string text = (string)Exact(typeof(Player).Assembly.GetType("Version", throwOnError: true), "GetVersionString", BindingFlags.Static | BindingFlags.Public, typeof(bool)).Invoke(null, new object[1] { false });
				if (!string.Equals(text, "0.221.12", StringComparison.Ordinal))
				{
					throw new MissingMethodException("Runic Interaction 1.0.0 is audited for Valheim 0.221.12; installed " + text + ".");
				}
				_doorRpc = DelegateFor<DoorRpcDelegate>(typeof(Door), "RPC_UseDoor", new Type[2]
				{
					typeof(long),
					typeof(bool)
				});
				_doorCanInteract = DelegateFor<DoorCanInteractDelegate>(typeof(Door), "CanInteract", Array.Empty<Type>());
				_cookingHaveDone = DelegateFor<CookingHaveDoneDelegate>(typeof(CookingStation), "HaveDoneItem", Array.Empty<Type>());
				_gridButtonPosition = DelegateFor<GridButtonPositionDelegate>(typeof(InventoryGrid), "GetButtonPos", new Type[1] { typeof(GameObject) });
				_containerCheckAccess = DelegateFor<ContainerCheckAccessDelegate>(typeof(Container), "CheckAccess", new Type[1] { typeof(long) });
				_setRecipe = DelegateFor<InventoryGuiSetRecipeDelegate>(typeof(InventoryGui), "SetRecipe", new Type[2]
				{
					typeof(int),
					typeof(bool)
				});
				_setActiveGroup = DelegateFor<InventoryGuiSetActiveGroupDelegate>(typeof(InventoryGui), "SetActiveGroup", new Type[2]
				{
					typeof(int),
					typeof(bool)
				});
				_currentContainer = ExactField(typeof(InventoryGui), "m_currentContainer");
				_dragObject = ExactField(typeof(InventoryGui), "m_dragGo");
				_queuedTextReceiver = ExactField(typeof(TextInput), "m_queuedSign");
				_selectedRecipe = ExactField(typeof(InventoryGui), "m_selectedRecipe");
				_availableRecipes = ExactField(typeof(InventoryGui), "m_availableRecipes");
				Type? nestedType = typeof(InventoryGui).GetNestedType("RecipeDataPair", BindingFlags.NonPublic);
				if (nestedType == null)
				{
					throw new MissingMemberException("InventoryGui.RecipeDataPair");
				}
				_pairRecipe = ExactProperty(nestedType, "Recipe", typeof(Recipe));
				_pairItem = ExactProperty(nestedType, "ItemData", typeof(ItemData));
				Exact(typeof(Switch), "Interact", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, typeof(Humanoid), typeof(bool), typeof(bool));
				Exact(typeof(Smelter), "Awake", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				Exact(typeof(CookingStation), "Awake", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				Exact(typeof(CookingStation), "Interact", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, typeof(Humanoid), typeof(bool), typeof(bool));
				Exact(typeof(Fermenter), "Interact", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, typeof(Humanoid), typeof(bool), typeof(bool));
				Exact(typeof(ShieldGenerator), "Start", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				Exact(typeof(Door), "Interact", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, typeof(Humanoid), typeof(bool), typeof(bool));
				Exact(typeof(InventoryGrid), "OnLeftClick", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, typeof(UIInputHandler));
				Exact(typeof(InventoryGui), "OnSelectedItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, typeof(InventoryGrid), typeof(ItemData), typeof(Vector2i), typeof(Modifier));
				Exact(typeof(Humanoid), "EquipItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, typeof(ItemData), typeof(bool));
				Exact(typeof(Humanoid), "UnequipItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, typeof(ItemData), typeof(bool));
				Exact(typeof(Humanoid), "HideHandItems", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, typeof(bool), typeof(bool));
				Exact(typeof(Humanoid), "ShowHandItems", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, typeof(bool), typeof(bool));
				Exact(typeof(InventoryGui), "SetupCrafting", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				Exact(typeof(InventoryGui), "Hide", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				Exact(typeof(InventoryGui), "OnTabCraftPressed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				Exact(typeof(InventoryGui), "OnTabUpgradePressed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				Exact(typeof(TextInput), "RequestText", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, typeof(TextReceiver), typeof(string), typeof(int));
				Exact(typeof(TextInput), "setText", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, typeof(string));
				Exact(typeof(TextInput), "Hide", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				Exact(typeof(Humanoid), "Pickup", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, typeof(GameObject), typeof(bool), typeof(bool));
				_ready = true;
				return true;
			}
			catch (Exception ex)
			{
				_ready = false;
				problem = ex.GetType().Name + ": " + ex.Message;
				return false;
			}
		}

		internal static bool CookingHasDoneItem(CookingStation station)
		{
			if (_ready && Object.op_Implicit((Object)(object)station))
			{
				return _cookingHaveDone(station);
			}
			return false;
		}

		internal static bool DoorCanInteract(Door door)
		{
			if (_ready && Object.op_Implicit((Object)(object)door))
			{
				return _doorCanInteract(door);
			}
			return false;
		}

		internal static void CloseDoor(Door door, long sender)
		{
			_doorRpc(door, sender, forward: false);
		}

		internal static Vector2i GetButtonPosition(InventoryGrid grid, GameObject button)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			return _gridButtonPosition(grid, button);
		}

		internal static Container GetCurrentContainer(InventoryGui gui)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			if (!((Object)(object)gui == (Object)null))
			{
				return (Container)_currentContainer.GetValue(gui);
			}
			return null;
		}

		internal static bool HasDrag(InventoryGui gui)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if ((Object)(object)gui != (Object)null)
			{
				return Object.op_Implicit((Object)(GameObject)_dragObject.GetValue(gui));
			}
			return false;
		}

		internal static bool ContainerAllows(Container container, long playerId)
		{
			if (Object.op_Implicit((Object)(object)container))
			{
				return _containerCheckAccess(container, playerId);
			}
			return false;
		}

		internal static TextReceiver GetQueuedTextReceiver(TextInput input)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			if (!((Object)(object)input == (Object)null))
			{
				return (TextReceiver)_queuedTextReceiver.GetValue(input);
			}
			return null;
		}

		internal static void ClearQueuedTextReceiver(TextInput input)
		{
			if ((Object)(object)input != (Object)null)
			{
				_queuedTextReceiver.SetValue(input, null);
			}
		}

		internal static object GetSelectedRecipePair(InventoryGui gui)
		{
			if (!((Object)(object)gui == (Object)null))
			{
				return _selectedRecipe.GetValue(gui);
			}
			return null;
		}

		internal static IList GetAvailableRecipePairs(InventoryGui gui)
		{
			if (!((Object)(object)gui == (Object)null))
			{
				return _availableRecipes.GetValue(gui) as IList;
			}
			return null;
		}

		internal static Recipe GetPairRecipe(object pair)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			if (pair != null)
			{
				return (Recipe)_pairRecipe.GetValue(pair, null);
			}
			return null;
		}

		internal static ItemData GetPairItem(object pair)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			if (pair != null)
			{
				return (ItemData)_pairItem.GetValue(pair, null);
			}
			return null;
		}

		internal static void SetRecipe(InventoryGui gui, int index, bool center)
		{
			_setRecipe(gui, index, center);
		}

		internal static void SetActiveGroup(InventoryGui gui, int group, bool sound)
		{
			_setActiveGroup(gui, group, sound);
		}

		private static T DelegateFor<T>(Type type, string name, params Type[] parameters) where T : Delegate
		{
			return AccessTools.MethodDelegate<T>(Exact(type, name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, parameters), (object)null, true);
		}

		private static MethodInfo Exact(Type type, string name, BindingFlags flags, params Type[] parameters)
		{
			return type.GetMethod(name, flags, null, parameters, null) ?? throw new MissingMethodException(type.FullName, name);
		}

		private static FieldInfo ExactField(Type type, string name)
		{
			return type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingFieldException(type.FullName, name);
		}

		private static PropertyInfo ExactProperty(Type type, string name, Type propertyType)
		{
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property == null || property.PropertyType != propertyType)
			{
				throw new MissingMemberException(type.FullName, name);
			}
			return property;
		}
	}
}
namespace RunicInteraction.Core
{
	internal sealed class BoundedContextMemory<TKey, TValue>
	{
		private readonly struct Entry
		{
			internal TValue Value { get; }

			internal long Sequence { get; }

			internal Entry(TValue value, long sequence)
			{
				Value = value;
				Sequence = sequence;
			}
		}

		private readonly int _capacity;

		private readonly Dictionary<TKey, Entry> _entries;

		private long _sequence;

		internal int Count => _entries.Count;

		internal BoundedContextMemory(int capacity, IEqualityComparer<TKey> comparer = null)
		{
			if (capacity < 1)
			{
				throw new ArgumentOutOfRangeException("capacity");
			}
			_capacity = capacity;
			_entries = new Dictionary<TKey, Entry>(comparer ?? EqualityComparer<TKey>.Default);
		}

		internal void Put(TKey key, TValue value)
		{
			long sequence = NextSequence();
			if (_entries.ContainsKey(key))
			{
				_entries[key] = new Entry(value, sequence);
				return;
			}
			if (_entries.Count >= _capacity)
			{
				EvictOldest();
			}
			_entries.Add(key, new Entry(value, sequence));
		}

		internal bool TryGet(TKey key, out TValue value)
		{
			if (!_entries.TryGetValue(key, out var value2))
			{
				value = default(TValue);
				return false;
			}
			_entries[key] = new Entry(value2.Value, NextSequence());
			value = value2.Value;
			return true;
		}

		internal void Clear()
		{
			_entries.Clear();
		}

		private long NextSequence()
		{
			if (_sequence == long.MaxValue)
			{
				long num = 0L;
				List<TKey> list = new List<TKey>(_entries.Keys);
				list.Sort((TKey left, TKey right) => _entries[left].Sequence.CompareTo(_entries[right].Sequence));
				foreach (TKey item in list)
				{
					Entry entry = _entries[item];
					_entries[item] = new Entry(entry.Value, ++num);
				}
				_sequence = num;
			}
			return ++_sequence;
		}

		private void EvictOldest()
		{
			bool flag = false;
			TKey key = default(TKey);
			long num = 0L;
			foreach (KeyValuePair<TKey, Entry> entry in _entries)
			{
				if (!flag || entry.Value.Sequence < num)
				{
					flag = true;
					key = entry.Key;
					num = entry.Value.Sequence;
				}
			}
			if (flag)
			{
				_entries.Remove(key);
			}
		}
	}
	internal sealed class InteractionInputChord : IEquatable<InteractionInputChord>
	{
		private readonly IReadOnlyList<string> _modifiers;

		internal string DeviceId { get; }

		internal string PrimaryControl { get; }

		internal IReadOnlyList<string> Modifiers => _modifiers;

		internal string CanonicalId { get; }

		internal InteractionInputChord(string deviceId, string primaryControl, params string[] modifiers)
		{
			DeviceId = Require(deviceId, "deviceId");
			PrimaryControl = Require(primaryControl, "primaryControl");
			string[] array = ((modifiers == null) ? Array.Empty<string>() : ((string[])modifiers.Clone()));
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = Require(array[i], "modifiers");
			}
			Array.Sort(array, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
			_modifiers = Array.AsReadOnly(array);
			CanonicalId = Canonical(DeviceId) + ":" + string.Join("+", array).ToLowerInvariant() + ":" + Canonical(PrimaryControl);
		}

		public bool Equals(InteractionInputChord other)
		{
			if (other != null)
			{
				return string.Equals(CanonicalId, other.CanonicalId, StringComparison.Ordinal);
			}
			return false;
		}

		public override bool Equals(object value)
		{
			return Equals(value as InteractionInputChord);
		}

		public override int GetHashCode()
		{
			return StringComparer.Ordinal.GetHashCode(CanonicalId);
		}

		private static string Require(string value, string parameter)
		{
			string text = value?.Trim();
			if (string.IsNullOrEmpty(text) || text.Length > 64)
			{
				throw new ArgumentException("An input identifier between 1 and 64 characters is required.", parameter);
			}
			return text;
		}

		private static string Canonical(string value)
		{
			return value.Replace(" ", string.Empty).Replace("_", string.Empty).Replace("-", string.Empty)
				.ToLowerInvariant();
		}
	}
	internal sealed class KeybindingDescriptor
	{
		internal string BindingId { get; }

		internal string QualifiedId => "runic.interaction/" + BindingId;

		internal string DisplayName { get; }

		internal InteractionInputChord Chord { get; }

		internal string Context { get; }

		internal KeybindingDescriptor(string bindingId, string displayName, InteractionInputChord chord, string context)
		{
			BindingId = bindingId ?? throw new ArgumentNullException("bindingId");
			DisplayName = displayName ?? throw new ArgumentNullException("displayName");
			Chord = chord ?? throw new ArgumentNullException("chord");
			Context = context ?? throw new ArgumentNullException("context");
		}
	}
	internal static class InteractionInputBindings
	{
		internal const string PickupBypassControllerPrimary = "JoyUse";

		internal const string DefaultPickupBypassControllerModifier = "JoyRStick";

		internal const int BindingCount = 6;

		internal const int MaximumControllerModifierOptions = 14;

		internal const string DefaultBindingCatalog = "transfer-left-alt|keyboard|Mouse0|LeftAlt|inventory\ntransfer-right-alt|keyboard|Mouse0|RightAlt|inventory\ntransfer-controller|controller|JoyButtonX|JoyLTrigger|inventory\npickup-bypass-left-alt|keyboard|Use|LeftAlt|interaction\npickup-bypass-right-alt|keyboard|Use|RightAlt|interaction\npickup-bypass-controller|controller|JoyUse|JoyRStick|interaction";

		private static readonly string[] ControllerModifierOptions = new string[14]
		{
			"JoyButtonB", "JoyButtonY", "JoyBack", "JoyStart", "JoyLStick", "JoyRStick", "JoyDPadLeft", "JoyDPadRight", "JoyDPadUp", "JoyDPadDown",
			"JoyLBumper", "JoyRBumper", "JoyLTrigger", "JoyRTrigger"
		};

		internal static string[] CreateControllerModifierOptions()
		{
			return (string[])ControllerModifierOptions.Clone();
		}

		internal static string ResolveControllerModifier(string configured, out bool usedDefault)
		{
			string a = (configured ?? string.Empty).Trim();
			for (int i = 0; i < ControllerModifierOptions.Length; i++)
			{
				string text = ControllerModifierOptions[i];
				if (string.Equals(a, text, StringComparison.OrdinalIgnoreCase))
				{
					usedDefault = false;
					return text;
				}
			}
			usedDefault = true;
			return "JoyRStick";
		}

		internal static IReadOnlyList<KeybindingDescriptor> CreateDescriptors(string controllerModifier)
		{
			bool usedDefault;
			string modifier = ResolveControllerModifier(controllerModifier, out usedDefault);
			return Array.AsReadOnly(new KeybindingDescriptor[6]
			{
				Binding("transfer-left-alt", "Full-stack transfer (left Alt)", "keyboard", "Mouse0", "LeftAlt", "inventory"),
				Binding("transfer-right-alt", "Full-stack transfer (right Alt)", "keyboard", "Mouse0", "RightAlt", "inventory"),
				Binding("transfer-controller", "Full-stack transfer (controller)", "controller", "JoyButtonX", "JoyLTrigger", "inventory"),
				Binding("pickup-bypass-left-alt", "Bypass pickup filter (left Alt)", "keyboard", "Use", "LeftAlt", "interaction"),
				Binding("pickup-bypass-right-alt", "Bypass pickup filter (right Alt)", "keyboard", "Use", "RightAlt", "interaction"),
				Binding("pickup-bypass-controller", "Bypass pickup filter (controller)", "controller", "JoyUse", modifier, "interaction")
			});
		}

		private static KeybindingDescriptor Binding(string id, string display, string device, string primary, string modifier, string context)
		{
			return new KeybindingDescriptor(id, display, new InteractionInputChord(device, primary, modifier), context);
		}
	}
	internal enum DoorCloseDecision
	{
		Remove,
		Wait,
		Close
	}
	internal static class DoorClosePolicy
	{
		internal static DoorCloseDecision Evaluate(bool valid, bool owner, bool open, bool eligible, bool obstructed, float quietSeconds, float requiredQuietSeconds)
		{
			if (!valid || !open || !owner)
			{
				return DoorCloseDecision.Remove;
			}
			if (!eligible || obstructed || quietSeconds < requiredQuietSeconds)
			{
				return DoorCloseDecision.Wait;
			}
			return DoorCloseDecision.Close;
		}
	}
	internal enum TextEntryKind
	{
		Unsupported,
		Portal,
		Sign,
		Tame
	}
	internal readonly struct TextEntryDecision
	{
		internal bool Allowed { get; }

		internal string Reason { get; }

		internal TextEntryDecision(bool allowed, string reason)
		{
			Allowed = allowed;
			Reason = reason ?? string.Empty;
		}
	}
	internal static class TextEntryPolicy
	{
		internal static TextEntryDecision Validate(TextEntryKind kind, string value, int limit, bool receiverValid, bool authorized, bool inRange)
		{
			if (kind == TextEntryKind.Unsupported)
			{
				return new TextEntryDecision(allowed: true, string.Empty);
			}
			if (!receiverValid)
			{
				return new TextEntryDecision(allowed: false, "receiver-invalid");
			}
			if (!authorized)
			{
				return new TextEntryDecision(allowed: false, "permission-changed");
			}
			if (!inRange)
			{
				return new TextEntryDecision(allowed: false, "out-of-range");
			}
			if (value == null)
			{
				return new TextEntryDecision(allowed: false, "null-text");
			}
			if (value.Length > limit)
			{
				return new TextEntryDecision(allowed: false, "too-long");
			}
			for (int i = 0; i < value.Length; i++)
			{
				char c = value[i];
				if (char.IsHighSurrogate(c))
				{
					if (i + 1 >= value.Length || !char.IsLowSurrogate(value[i + 1]))
					{
						return new TextEntryDecision(allowed: false, "invalid-unicode");
					}
					i++;
					continue;
				}
				if (char.IsLowSurrogate(c))
				{
					return new TextEntryDecision(allowed: false, "invalid-unicode");
				}
				if (char.IsControl(c) && (kind != TextEntryKind.Sign || (c != '\n' && c != '\t')))
				{
					return new TextEntryDecision(allowed: false, "control-character");
				}
			}
			return new TextEntryDecision(allowed: true, string.Empty);
		}
	}
	internal sealed class PickupFilterSet
	{
		internal const int MaximumRules = 128;

		private readonly HashSet<string> _rules;

		internal int Count => _rules.Count;

		internal bool Truncated { get; }

		private PickupFilterSet(HashSet<string> rules, bool truncated)
		{
			_rules = rules;
			Truncated = truncated;
		}

		internal bool Matches(string prefabName, string sharedName)
		{
			if (string.IsNullOrEmpty(prefabName) || !_rules.Contains(prefabName))
			{
				if (!string.IsNullOrEmpty(sharedName))
				{
					return _rules.Contains(sharedName);
				}
				return false;
			}
			return true;
		}

		internal static PickupFilterSet Parse(string text)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			bool truncated = false;
			if (!string.IsNullOrEmpty(text))
			{
				string[] array = text.Split(new char[4] { ',', ';', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
				for (int i = 0; i < array.Length; i++)
				{
					string text2 = array[i].Trim();
					if (text2.Length != 0 && text2.Length <= 128)
					{
						if (hashSet.Count >= 128 && !hashSet.Contains(text2))
						{
							truncated = true;
						}
						else
						{
							hashSet.Add(text2);
						}
					}
				}
			}
			return new PickupFilterSet(hashSet, truncated);
		}
	}
	internal readonly struct MenuSelection
	{
		internal string RecipePrefab { get; }

		internal bool Upgrade { get; }

		internal int ActiveGroup { get; }

		internal bool HasRecipe => RecipePrefab.Length != 0;

		internal MenuSelection(string recipePrefab, bool upgrade, int activeGroup)
		{
			RecipePrefab = recipePrefab ?? string.Empty;
			Upgrade = upgrade;
			ActiveGroup = activeGroup;
		}
	}
	internal static class EquipmentRestorePolicy
	{
		internal static bool MayRestore(bool enabled, bool isLocalPlayer, bool alive, bool swimming, bool attacking, bool dodging, bool temporaryToolStillEquipped, bool userSelectedReplacement)
		{
			if (enabled && isLocalPlayer && alive && !swimming && !attacking && !dodging && !temporaryToolStillEquipped)
			{
				return !userSelectedReplacement;
			}
			return false;
		}
	}
	internal static class TransferGesturePolicy
	{
		internal static bool MayTransfer(bool enabled, bool containerOpen, bool containerOwned, bool permission, bool wardAccess, bool hasItem, bool questItem, bool dragActive, bool itemProtectionAllowsTransfer)
		{
			return enabled && containerOpen && containerOwned && permission && wardAccess && hasItem && !questItem && !dragActive && itemProtectionAllowsTransfer;
		}
	}
	internal enum TransferItemProtection
	{
		NotApplicable,
		Unlocked,
		Locked,
		Unknown
	}
	internal static class ItemProtectionQueryAdapter
	{
		private const string PluginGuid = "chazman.RunicInventory";

		private const string ApiTypeName = "RunicInventory.Api.InventoryIntegrationApi";

		private static MethodInfo _method;

		private static object[] _arguments;

		private static bool _resolved;

		internal static TransferItemProtection Resolve(object nativeItem)
		{
			if (!Chainloader.PluginInfos.ContainsKey("chazman.RunicInventory"))
			{
				return TransferItemProtection.NotApplicable;
			}
			if (nativeItem == null || !TryResolveMethod())
			{
				return TransferItemProtection.Unknown;
			}
			try
			{
				_arguments[0] = nativeItem;
				_arguments[1] = 0;
				object obj = _method.Invoke(null, _arguments);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				int num2 = num & (flag ? 1 : 0);
				int num3 = ((_arguments[1] is int num4) ? num4 : 0);
				return (num2 != 0) ? (num3 switch
				{
					2 => TransferItemProtection.Locked, 
					1 => TransferItemProtection.Unlocked, 
					_ => TransferItemProtection.Unknown, 
				}) : TransferItemProtection.NotApplicable;
			}
			catch
			{
				_resolved = false;
				_method = null;
				_arguments = null;
				return TransferItemProtection.Unknown;
			}
			finally
			{
				if (_arguments != null)
				{
					_arguments[0] = null;
					_arguments[1] = 0;
				}
			}
		}

		internal static bool AllowsTransfer(TransferItemProtection protection)
		{
			if (protection != TransferItemProtection.NotApplicable)
			{
				return protection == TransferItemProtection.Unlocked;
			}
			return true;
		}

		private static bool TryResolveMethod()
		{
			if (_resolved)
			{
				return _method != null;
			}
			_resolved = true;
			if (!Chainloader.PluginInfos.TryGetValue("chazman.RunicInventory", out var value))
			{
				return false;
			}
			_method = ((value == null) ? null : ((object)value.Instance)?.GetType().Assembly.GetType("RunicInventory.Api.InventoryIntegrationApi", throwOnError: false))?.GetMethod("TryGetProtection", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
			{
				typeof(object),
				typeof(int).MakeByRefType()
			}, null);
			if (_method == null || _method.ReturnType != typeof(bool))
			{
				return false;
			}
			_arguments = new object[2] { null, 0 };
			return true;
		}
	}
}