Decompiled source of InnangardBetrValheimDrawerBridge v1.0.5

plugins/InnangardBetrValheimDrawerBridge.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using API;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using InnangardBetrValheimDrawerBridge;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;
using kg_ItemDrawers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("kg_ItemDrawers")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("kg_ItemDrawers")]
[assembly: AssemblyTitle("kg_ItemDrawers")]
[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 API
{
	public static class ClientSideV2
	{
		public static List<ZNetView> AllDrawers()
		{
			return Bridge.AllViews();
		}
	}
	public static class ClientSide
	{
		public static List<ZNetView> AllDrawers()
		{
			return Bridge.AllViews();
		}
	}
	public static class ItemDrawers_API
	{
		public sealed class Drawer
		{
			private readonly ZNetView _znv;

			public readonly string Prefab;

			public readonly Vector3 Position;

			public readonly int Amount;

			public readonly int Quality;

			public Drawer(ZNetView znv)
			{
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				//IL_006f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0074: Unknown result type (might be due to invalid IL or missing references)
				_znv = znv;
				if ((Object)(object)znv == (Object)null || !znv.IsValid() || znv.GetZDO() == null)
				{
					Prefab = string.Empty;
					Position = Vector3.zero;
					Amount = 0;
					Quality = 1;
				}
				else
				{
					ZDO zDO = znv.GetZDO();
					Prefab = zDO.GetString("Prefab", string.Empty);
					Position = ((Component)znv).transform.position;
					Amount = zDO.GetInt("Amount", 0);
					Quality = Math.Max(1, zDO.GetInt("Quality", 1));
				}
			}

			public void Add(int amount, int quality)
			{
				Bridge.ApiAdd(_znv, Prefab, amount, quality);
			}

			public void Remove(int amount)
			{
				Bridge.ApiRemove(_znv, amount);
			}
		}

		public static List<Drawer> AllDrawers => (from v in Bridge.AllViews()
			select new Drawer(v)).ToList();

		public static List<Drawer> AllDrawersInRange(Vector3 position, float range)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			Bridge.RememberRange(position, range);
			float num = range * range;
			List<ZNetView> list = Bridge.AllViews();
			List<Drawer> list2 = new List<Drawer>(list.Count);
			foreach (ZNetView item in list)
			{
				if (!((Object)(object)item == (Object)null) && item.IsValid())
				{
					Vector3 val = ((Component)item).transform.position - position;
					if (!(((Vector3)(ref val)).sqrMagnitude > num))
					{
						list2.Add(new Drawer(item));
					}
				}
			}
			if (Plugin.DetailedLogging.Value)
			{
				Plugin.Log.LogInfo((object)$"[RANGE] pos={position} range={range:0.0} returned={list2.Count}/{list.Count}");
			}
			return list2;
		}
	}
}
namespace kg_ItemDrawers
{
	public sealed class DrawerComponent : MonoBehaviour
	{
		public static readonly List<DrawerComponent> AllDrawers = new List<DrawerComponent>();

		internal object RealDrawer;

		internal ZNetView View;

		public string CurrentPrefab => Bridge.ResolvePrefabName(Bridge.GetAssigned(RealDrawer));

		public int CurrentAmount => Bridge.GetCount(RealDrawer);

		internal void Bind(object real, ZNetView view)
		{
			RealDrawer = real;
			View = view;
		}

		internal void Sync()
		{
			if (RealDrawer == null || (Object)(object)View == (Object)null || !View.IsValid())
			{
				return;
			}
			ZDO zDO = View.GetZDO();
			if (zDO != null && View.IsOwner())
			{
				string text = CurrentPrefab ?? string.Empty;
				int num = ((!string.IsNullOrEmpty(text)) ? Math.Max(0, CurrentAmount) : 0);
				if (zDO.GetString("Prefab", string.Empty) != text)
				{
					zDO.Set("Prefab", text);
				}
				if (zDO.GetInt("Amount", 0) != num)
				{
					zDO.Set("Amount", num);
				}
				if (zDO.GetInt("Quality", 1) != 1)
				{
					zDO.Set("Quality", 1);
				}
			}
		}

		private void OnDestroy()
		{
			AllDrawers.Remove(this);
			RealDrawer = null;
			View = null;
		}
	}
}
namespace InnangardBetrValheimDrawerBridge
{
	[BepInPlugin("com.phoenixf.innangard.betrvalheimdrawerbridge", "Innangard BetrValheim Drawer Bridge", "0.5.18-betr458-loading-config-ui-typefix")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string ModGuid = "com.phoenixf.innangard.betrvalheimdrawerbridge";

		public const string ModName = "Innangard BetrValheim Drawer Bridge";

		public const string ModVersion = "0.5.18-betr458-loading-config-ui-typefix";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> DetailedLogging;

		internal static ConfigEntry<bool> DeliverBeforeCollectingMore;

		internal static ConfigEntry<bool> SortAfterStall;

		internal static ConfigEntry<bool> ReduceClaimRenewals;

		internal static ConfigEntry<bool> DrawerUseChestApproach;

		private float _nextRefresh;

		private Harmony _harmony;

		private bool _caretakerPatchInstalled;

		private bool _sortRangePatchInstalled;

		private bool _boRulesPatchInstalled;

		private bool _claimRenewalPatchInstalled;

		private bool _boUiPatchInstalled;

		private bool _betterOrganisationInstalled;

		private bool _drawerMovementPatchInstalled;

		private bool _loadingScreenConfigPatchInstalled;

		private void Awake()
		{
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			DetailedLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Detailed logging", false, "Log individual bridged drawers and API range queries. Add/remove failures are always logged.");
			DeliverBeforeCollectingMore = ((BaseUnityPlugin)this).Config.Bind<bool>("Caretaker", "Deliver before collecting more", true, "When a caretaker has already collected sorting cargo, deliver it physically before visiting another chest. Prevents CollectMore from trapping an undelivered load.");
			SortAfterStall = ((BaseUnityPlugin)this).Config.Bind<bool>("Caretaker", "Deposit when stuck near storage", true, "After a caretaker gets stuck near its sorting destination, allow a deposit within Innangard's usual interaction distance.");
			ReduceClaimRenewals = ((BaseUnityPlugin)this).Config.Bind<bool>("Networking", "Reduce station claim renewals", true, "Renew Innangard work claims only after one third of their lease has elapsed, reducing redundant ZDO updates.");
			DrawerUseChestApproach = ((BaseUnityPlugin)this).Config.Bind<bool>("Drawer Movement", "Use chest reach and standing positions", true, "Make Innangard drawers use the same StorageReach and chest-style standing-position search as normal storage instead of the hardcoded drawer reach/direct approach.");
			try
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Innangard BetrValheim Drawer Bridge 0.5.18-betr458-loading-config-ui-typefix starting.");
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Assembly identity is kg_ItemDrawers. Drawer storage uses the ItemDrawers API; caretaker sorting delivers first and deposits nearby only after getting stuck.");
				if (!Bridge.BindBetrValheim())
				{
					((BaseUnityPlugin)this).Logger.LogError((object)"BetrValheim drawer binding failed; bridge is inactive.");
					return;
				}
				Bridge.LogApiSurface();
				_betterOrganisationInstalled = AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => string.Equals(a.GetName().Name, "ChestCategories", StringComparison.OrdinalIgnoreCase));
				if (_betterOrganisationInstalled)
				{
					BetterOrganisationRules.Bind();
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"[BO] Better Organisation not installed; all BO-specific integration is disabled. Drawer/caretaker/network patches remain active.");
				}
				try
				{
					_harmony = new Harmony("com.phoenixf.innangard.betrvalheimdrawerbridge");
					_caretakerPatchInstalled = CaretakerDeliverFirst.Install(_harmony);
					_sortRangePatchInstalled = CaretakerSortRange.Install(_harmony);
					if (_betterOrganisationInstalled)
					{
						_boRulesPatchInstalled = BetterOrganisationRules.Install(_harmony);
					}
					_claimRenewalPatchInstalled = StationClaimRenewal.Install(_harmony);
					_drawerMovementPatchInstalled = DrawerMovementCompat.Install(_harmony);
					_loadingScreenConfigPatchInstalled = BetrValheimLoadingScreenConfigRedirect.Install(_harmony);
					if (_betterOrganisationInstalled)
					{
						_boUiPatchInstalled = BetterOrganisationChestPanelCompat.Install(_harmony);
					}
				}
				catch (Exception e)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("[Patch] Innangard patch failed: " + Bridge.Root(e)));
				}
				Bridge.Refresh(forceSummary: true);
				if (_betterOrganisationInstalled)
				{
					BetterOrganisationRules.Refresh(forceSummary: true);
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Innangard BetrValheim Drawer Bridge 0.5.18-betr458-loading-config-ui-typefix loaded.");
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Expected Innangard log: compatible ItemDrawers API detected; sorting and supply access enabled.");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Bridge initialisation failed: " + ex));
			}
		}

		private void Update()
		{
			if (Time.unscaledTime < _nextRefresh)
			{
				return;
			}
			_nextRefresh = Time.unscaledTime + 0.5f;
			try
			{
				if (!_caretakerPatchInstalled && _harmony != null)
				{
					_caretakerPatchInstalled = CaretakerDeliverFirst.Install(_harmony);
				}
				if (!_sortRangePatchInstalled && _harmony != null)
				{
					_sortRangePatchInstalled = CaretakerSortRange.Install(_harmony);
				}
				if (_betterOrganisationInstalled && !_boRulesPatchInstalled && _harmony != null)
				{
					_boRulesPatchInstalled = BetterOrganisationRules.Install(_harmony);
				}
				if (!_claimRenewalPatchInstalled && _harmony != null)
				{
					_claimRenewalPatchInstalled = StationClaimRenewal.Install(_harmony);
				}
				if (!_drawerMovementPatchInstalled && _harmony != null)
				{
					_drawerMovementPatchInstalled = DrawerMovementCompat.Install(_harmony);
				}
				if (!_loadingScreenConfigPatchInstalled && _harmony != null)
				{
					_loadingScreenConfigPatchInstalled = BetrValheimLoadingScreenConfigRedirect.Install(_harmony);
				}
				if (_betterOrganisationInstalled && !_boUiPatchInstalled && _harmony != null)
				{
					_boUiPatchInstalled = BetterOrganisationChestPanelCompat.Install(_harmony);
				}
				Bridge.Refresh(forceSummary: false);
				if (_betterOrganisationInstalled)
				{
					BetterOrganisationRules.Refresh(forceSummary: false);
				}
			}
			catch (Exception e)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("[Refresh] " + Bridge.Root(e)));
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if (_betterOrganisationInstalled)
			{
				BetterOrganisationRules.RestoreAll();
			}
			Bridge.Clear();
		}
	}
	internal static class BetrValheimLoadingScreenConfigRedirect
	{
		private static bool _installed;

		private static MethodInfo _load;

		private static FieldInfo _backgroundSprite;

		private static FieldInfo _logoSprite;

		private static FieldInfo _missing;

		private static string _originalFolder;

		private static bool _customBackgroundApplied;

		private static bool _customLogoApplied;

		private static bool _failureLogged;

		internal static bool Install(Harmony harmony)
		{
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Expected O, but got Unknown
			if (_installed)
			{
				return true;
			}
			if (harmony == null)
			{
				return false;
			}
			try
			{
				Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "BetrValheim", StringComparison.OrdinalIgnoreCase));
				Type type = assembly?.GetType("BetrValheim.UI.LoadingScreen", throwOnError: false);
				if (type == null && assembly != null)
				{
					try
					{
						type = assembly.GetTypes().FirstOrDefault((Type t) => string.Equals(t.Name, "LoadingScreen", StringComparison.Ordinal) && AccessTools.Method(t, "Build", new Type[1] { typeof(Hud) }, (Type[])null) != null && AccessTools.Method(t, "Load", new Type[2]
						{
							typeof(string),
							typeof(string)
						}, (Type[])null) != null && AccessTools.Field(t, "_logoSprite") != null && AccessTools.Field(t, "_backgroundSprite") != null);
					}
					catch
					{
						type = null;
					}
				}
				if (assembly == null || type == null)
				{
					LogFailureOnce("BetrValheim 4.58 LoadingScreen type was not found.");
					return false;
				}
				MethodInfo methodInfo = AccessTools.Method(type, "Build", new Type[1] { typeof(Hud) }, (Type[])null);
				_load = AccessTools.Method(type, "Load", new Type[2]
				{
					typeof(string),
					typeof(string)
				}, (Type[])null);
				_backgroundSprite = AccessTools.Field(type, "_backgroundSprite");
				_logoSprite = AccessTools.Field(type, "_logoSprite");
				_missing = AccessTools.Field(type, "_missing");
				_originalFolder = Path.GetDirectoryName(assembly.Location);
				if (methodInfo == null || _load == null || _backgroundSprite == null || _logoSprite == null)
				{
					LogFailureOnce("BetrValheim 4.58 loading-screen members were not found.");
					return false;
				}
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(BetrValheimLoadingScreenConfigRedirect), "BuildPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_installed = true;
				return true;
			}
			catch (Exception e)
			{
				LogFailureOnce("BetrValheim 4.58 loading-image patch failed: " + Bridge.Root(e));
				return false;
			}
		}

		private static void BuildPrefix()
		{
			try
			{
				PrepareSprite("loading_background.jpg", _backgroundSprite, ref _customBackgroundApplied);
				PrepareSprite("loading_logo.png", _logoSprite, ref _customLogoApplied);
				if (_backgroundSprite.GetValue(null) is Sprite && _logoSprite.GetValue(null) is Sprite)
				{
					_missing?.SetValue(null, false);
				}
			}
			catch
			{
			}
		}

		private static void PrepareSprite(string name, FieldInfo field, ref bool customApplied)
		{
			if (field == null || _load == null)
			{
				return;
			}
			string text = Path.Combine(Paths.ConfigPath, "BetrValheim");
			if (File.Exists(Path.Combine(text, name)))
			{
				if (!customApplied)
				{
					object? obj = _load.Invoke(null, new object[2] { text, name });
					Sprite val = (Sprite)((obj is Sprite) ? obj : null);
					if ((Object)(object)val != (Object)null)
					{
						field.SetValue(null, val);
						customApplied = true;
					}
				}
			}
			else if (field.GetValue(null) == null && !string.IsNullOrEmpty(_originalFolder))
			{
				object? obj2 = _load.Invoke(null, new object[2] { _originalFolder, name });
				Sprite val2 = (Sprite)((obj2 is Sprite) ? obj2 : null);
				if ((Object)(object)val2 != (Object)null)
				{
					field.SetValue(null, val2);
				}
			}
		}

		private static void LogFailureOnce(string message)
		{
			if (!_failureLogged)
			{
				_failureLogged = true;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[Loading Screen] " + message));
				}
			}
		}
	}
	internal static class StationClaimRenewal
	{
		private static bool _installed;

		private static bool _reportedMissing;

		internal static bool Install(Harmony harmony)
		{
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			if (_installed)
			{
				return true;
			}
			Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase))?.GetType("Innangard.StationClaim", throwOnError: false);
			if (type == null)
			{
				return false;
			}
			MethodInfo method = type.GetMethod("Renew", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3]
			{
				typeof(ZNetView),
				typeof(ZDOID),
				typeof(float)
			}, null);
			if (method == null || method.ReturnType != typeof(void))
			{
				if (!_reportedMissing)
				{
					_reportedMissing = true;
					Plugin.Log.LogWarning((object)"[Network] StationClaim.Renew signature changed; claim throttling unavailable.");
				}
				return false;
			}
			harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(StationClaimRenewal), "RenewPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_installed = true;
			Plugin.Log.LogInfo((object)"[Network] Innangard station claim renewals reduced while the lease is healthy.");
			return true;
		}

		private static bool RenewPrefix(ZNetView __0, ZDOID __1, float __2)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.ReduceClaimRenewals == null || !Plugin.ReduceClaimRenewals.Value || (Object)(object)__0 == (Object)null || !__0.IsValid() || !__0.IsOwner() || __1 == ZDOID.None || __2 <= 0f)
			{
				return true;
			}
			ZDO zDO = __0.GetZDO();
			if (zDO == null || zDO.GetZDOID("inn_station_claim") != __1)
			{
				return true;
			}
			long num = zDO.GetLong("inn_station_claim_until", 0L);
			long ticks = DateTime.UtcNow.Ticks;
			long num2 = (long)((double)(__2 * 10000000f) * (2.0 / 3.0));
			if (num > ticks)
			{
				return num - ticks <= num2;
			}
			return true;
		}
	}
	internal static class CaretakerDeliverFirst
	{
		private static FieldInfo _load;

		private static FieldInfo _remaining;

		private static bool _reportedDeferred;

		private static bool _reportedMissingMember;

		internal static bool Install(Harmony harmony)
		{
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Expected O, but got Unknown
			Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase))?.GetType("Innangard.Jobs.CaretakerJob", throwOnError: false);
			if (type == null)
			{
				if (!_reportedDeferred)
				{
					_reportedDeferred = true;
					Plugin.Log.LogInfo((object)"[Caretaker] Innangard is not loaded yet; will bind the deliver-first patch when it loads.");
				}
				return false;
			}
			MethodInfo method = type.GetMethod("TryCollectFromAnother", BindingFlags.Instance | BindingFlags.NonPublic);
			_load = type.GetField("_load", BindingFlags.Instance | BindingFlags.NonPublic);
			_remaining = type.GetField("_remaining", BindingFlags.Instance | BindingFlags.NonPublic);
			if (method == null || method.ReturnType != typeof(void) || _load == null || _remaining == null || _remaining.FieldType != typeof(int))
			{
				if (!_reportedMissingMember)
				{
					_reportedMissingMember = true;
					Plugin.Log.LogError((object)"[Caretaker] Innangard caretaker method or queued-delivery field is missing; deliver-first patch could not bind.");
				}
				return false;
			}
			harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(CaretakerDeliverFirst), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Plugin.Log.LogInfo((object)"[Caretaker] Deliver-first patch active: carry one chest's sorting cargo to its destination before collecting from another chest.");
			return true;
		}

		private static bool Prefix(object __instance)
		{
			if (!Plugin.DeliverBeforeCollectingMore.Value || __instance == null)
			{
				return true;
			}
			try
			{
				ICollection collection = _load.GetValue(__instance) as ICollection;
				int num = (int)_remaining.GetValue(__instance);
				if (num <= 0 && (collection == null || collection.Count == 0))
				{
					return true;
				}
				if (Plugin.DetailedLogging.Value)
				{
					Plugin.Log.LogInfo((object)$"[Caretaker] Skipped CollectMore with remaining={num}, queued={collection?.Count ?? 0}; proceeding to physical delivery.");
				}
				return false;
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[Caretaker] Could not read pending deliveries: " + Bridge.Root(e)));
				return true;
			}
		}
	}
	internal static class CaretakerSortRange
	{
		private sealed class StallState
		{
			internal GameObject Goal;

			internal Vector3 Position;

			internal float Since;
		}

		private sealed class Context
		{
			internal Component Settler;
		}

		private static FieldInfo _chore;

		private static FieldInfo _remaining;

		private static FieldInfo _target;

		private static MethodInfo _putAway;

		private static PropertyInfo _ai;

		private static PropertyInfo _pathBlocked;

		private static readonly ConditionalWeakTable<object, StallState> Stalls = new ConditionalWeakTable<object, StallState>();

		[ThreadStatic]
		private static Context _active;

		internal static bool Install(Harmony harmony)
		{
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Expected O, but got Unknown
			//IL_01b0: Expected O, but got Unknown
			//IL_01b0: Expected O, but got Unknown
			Assembly? assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase));
			Type type = assembly?.GetType("Innangard.Jobs.CaretakerJob", throwOnError: false);
			Type type2 = assembly?.GetType("Innangard.SettlerController", throwOnError: false);
			Type type3 = assembly?.GetType("Innangard.SettlerAI", throwOnError: false);
			if (type == null || type2 == null || type3 == null)
			{
				return false;
			}
			_chore = type.GetField("_chore", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_remaining = type.GetField("_remaining", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_target = type.GetField("_target", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_putAway = type.GetMethod("PutAway", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_ai = type2.GetProperty("Ai", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_pathBlocked = type3.GetProperty("PathBlocked", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			MethodInfo method = type.GetMethod("Pursue", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (_chore == null || _remaining == null || _target == null || _putAway == null || _ai == null || _pathBlocked == null || method == null)
			{
				Plugin.Log.LogWarning((object)"[Caretaker] Stalled-deposit patch could not bind to this Innangard version.");
				return false;
			}
			harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(CaretakerSortRange), "Begin", (Type[])null), new HarmonyMethod(typeof(CaretakerSortRange), "End", (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(CaretakerSortRange), "Finish", (Type[])null), (HarmonyMethod)null);
			Plugin.Log.LogInfo((object)"[Caretaker] Sorting deposits after a blocked approach or 3 seconds stalled nearby.");
			return true;
		}

		private static void Begin(object __instance, object __0, out Context __state)
		{
			__state = _active;
			_active = null;
			if (!Plugin.SortAfterStall.Value || __instance == null)
			{
				return;
			}
			try
			{
				int num = Convert.ToInt32(_chore.GetValue(__instance));
				if ((num == 1 || num == 2) && (int)_remaining.GetValue(__instance) > 0)
				{
					_active = new Context
					{
						Settler = (Component)((__0 is Component) ? __0 : null)
					};
				}
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[Caretaker] Stalled-deposit context failed: " + Bridge.Root(e)));
			}
		}

		private static void End(object __instance, ref bool __result, Context __state)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)_active?.Settler == (Object)null || (int)_remaining.GetValue(__instance) <= 0)
				{
					Stalls.Remove(__instance);
					return;
				}
				object? value = _target.GetValue(__instance);
				GameObject val = (GameObject)((value is GameObject) ? value : null);
				if (!((Object)(object)val == (Object)null) && val.activeInHierarchy)
				{
					Vector3 position = _active.Settler.transform.position;
					StallState value2 = Stalls.GetValue(__instance, (object _) => new StallState());
					if ((Object)(object)value2.Goal != (Object)(object)val || Vector3.Distance(value2.Position, position) > 0.4f)
					{
						value2.Goal = val;
						value2.Position = position;
						value2.Since = Time.time;
					}
					float range = ((Convert.ToInt32(_chore.GetValue(__instance)) == 2) ? DrawerMovementCompat.EffectiveDrawerReach(3.55f) : 2.8f);
					bool flag = Time.time - value2.Since >= 3f;
					bool flag2 = Blocked();
					if ((__result || flag2) && (flag || flag2) && InRange(_active.Settler, val, range))
					{
						_putAway.Invoke(__instance, new object[1] { _active.Settler });
						__result = true;
						Stalls.Remove(__instance);
						Plugin.Log.LogInfo((object)("[Caretaker] Deposited sorting cargo after " + (flag ? "standing still" : "blocked route") + " near " + ((Object)val).name));
					}
				}
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[Caretaker] Stalled deposit failed: " + Bridge.Root(e)));
			}
			finally
			{
				_active = __state;
			}
		}

		private static Exception Finish(Exception __exception, Context __state)
		{
			_active = __state;
			return __exception;
		}

		private static bool Blocked()
		{
			object value = _ai.GetValue(_active.Settler, null);
			if (value != null)
			{
				return (bool)_pathBlocked.GetValue(value, null);
			}
			return false;
		}

		private static bool InRange(Component settler, GameObject target, float range)
		{
			//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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = settler.transform.position + Vector3.up * 1.05f;
			float num = float.MaxValue;
			Collider[] componentsInChildren = target.GetComponentsInChildren<Collider>(true);
			foreach (Collider val2 in componentsInChildren)
			{
				if (!((Object)(object)val2 == (Object)null) && val2.enabled && !val2.isTrigger)
				{
					num = Mathf.Min(num, Vector3.Distance(val, val2.ClosestPoint(val)));
				}
			}
			if (num == float.MaxValue)
			{
				num = Vector3.Distance(val, target.transform.position);
			}
			return num <= range;
		}
	}
	internal static class DrawerMovementCompat
	{
		private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static bool _installed;

		private static bool _reportedMissing;

		private static MethodInfo _approachStorage;

		private static PropertyInfo _storageReach;

		[ThreadStatic]
		private static int _insideApproachStorage;

		internal static bool Install(Harmony harmony)
		{
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Expected O, but got Unknown
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Expected O, but got Unknown
			if (_installed)
			{
				return true;
			}
			Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase));
			Type controller = assembly?.GetType("Innangard.SettlerController", throwOnError: false);
			Type type = assembly?.GetType("Innangard.ItemDrawerBridge", throwOnError: false);
			if (controller == null || type == null)
			{
				return false;
			}
			MethodInfo method = controller.GetMethod("Approach", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3]
			{
				typeof(GameObject),
				typeof(float),
				typeof(bool)
			}, null);
			_approachStorage = controller.GetMethod("ApproachStorage", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
			{
				typeof(GameObject),
				typeof(bool)
			}, null);
			_storageReach = controller.GetProperty("StorageReach", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo m)
			{
				if (m.Name != "CanReach" || m.ReturnType != typeof(bool))
				{
					return false;
				}
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length == 3 && parameters[0].ParameterType == controller && parameters[2].ParameterType == typeof(float);
			});
			if (method == null || _approachStorage == null || _storageReach == null || methodInfo == null)
			{
				if (!_reportedMissing)
				{
					_reportedMissing = true;
					Plugin.Log.LogWarning((object)("[Drawer Movement] Innangard storage movement binding incomplete; " + $"Approach={method != null} ApproachStorage={_approachStorage != null} " + $"StorageReach={_storageReach != null} CanReach={methodInfo != null}."));
				}
				return false;
			}
			harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(DrawerMovementCompat), "ApproachPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(DrawerMovementCompat), "CanReachPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_installed = true;
			Plugin.Log.LogInfo((object)"[Drawer Movement] Drawers now use Innangard StorageReach and the same standing-position search as chests.");
			return true;
		}

		internal static float EffectiveDrawerReach(float originalReach)
		{
			if (Plugin.DrawerUseChestApproach == null || !Plugin.DrawerUseChestApproach.Value)
			{
				return originalReach;
			}
			float num = ReadStorageReach();
			if (num <= 0f)
			{
				return originalReach;
			}
			return Mathf.Min(originalReach, num);
		}

		private static float ReadStorageReach()
		{
			try
			{
				if (_storageReach == null)
				{
					return -1f;
				}
				object value = _storageReach.GetValue(null, null);
				return (value == null) ? (-1f) : Convert.ToSingle(value);
			}
			catch
			{
				return -1f;
			}
		}

		private static void CanReachPrefix(ref float __2)
		{
			__2 = EffectiveDrawerReach(__2);
		}

		private static bool ApproachPrefix(object __instance, GameObject __0, bool __2)
		{
			if (Plugin.DrawerUseChestApproach == null || !Plugin.DrawerUseChestApproach.Value || __instance == null || (Object)(object)__0 == (Object)null || _approachStorage == null || _insideApproachStorage > 0 || !IsBridgedDrawer(__0))
			{
				return true;
			}
			try
			{
				_insideApproachStorage++;
				_approachStorage.Invoke(__instance, new object[2] { __0, __2 });
				return false;
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[Drawer Movement] Chest-style drawer approach failed; using Innangard's direct approach: " + Bridge.Root(e)));
				return true;
			}
			finally
			{
				_insideApproachStorage--;
			}
		}

		private static bool IsBridgedDrawer(GameObject target)
		{
			if ((Object)(object)target == (Object)null)
			{
				return false;
			}
			if ((Object)(object)target.GetComponent<DrawerComponent>() != (Object)null)
			{
				return true;
			}
			if ((Object)(object)target.GetComponentInParent<DrawerComponent>() != (Object)null)
			{
				return true;
			}
			return (Object)(object)target.GetComponentInChildren<DrawerComponent>(true) != (Object)null;
		}
	}
	internal static class Bridge
	{
		private sealed class RecentRange
		{
			internal Vector3 Centre;

			internal float Range;

			internal float Seen;
		}

		private sealed class ReferenceComparer : IEqualityComparer<object>
		{
			internal static readonly ReferenceComparer Instance = new ReferenceComparer();

			public new bool Equals(object x, object y)
			{
				return x == y;
			}

			public int GetHashCode(object obj)
			{
				return RuntimeHelpers.GetHashCode(obj);
			}
		}

		private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		internal static Type DrawerType;

		internal static PropertyInfo ItemNameProp;

		internal static PropertyInfo CountProp;

		internal static PropertyInfo FreeSpaceProp;

		internal static MethodInfo DepositMethod;

		internal static MethodInfo WithdrawMethod;

		internal static MemberInfo NviewMember;

		private const float MissingDrawerGraceSeconds = 2.5f;

		private const float RecoveryRefreshInterval = 0.25f;

		private static readonly Dictionary<object, DrawerComponent> ByReal = new Dictionary<object, DrawerComponent>(ReferenceComparer.Instance);

		private static readonly Dictionary<int, object> ByViewId = new Dictionary<int, object>();

		private static readonly Dictionary<ZDOID, object> ByZdoId = new Dictionary<ZDOID, object>();

		private static readonly Dictionary<ZDOID, DrawerComponent> ByZdoIdMarker = new Dictionary<ZDOID, DrawerComponent>();

		private static readonly Dictionary<ZDOID, float> LastActiveSeen = new Dictionary<ZDOID, float>();

		private static readonly List<RecentRange> RecentRanges = new List<RecentRange>();

		private static float _nextRecoveryRefresh;

		private static int _lastFound = -1;

		private static int _lastActive = -1;

		internal static bool BindBetrValheim()
		{
			Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "BetrValheim", StringComparison.OrdinalIgnoreCase));
			if (assembly == null)
			{
				Plugin.Log.LogError((object)"[Bind] BetrValheim assembly not found.");
				return false;
			}
			DrawerType = assembly.GetType("BetrValheim.Drawers.Drawer", throwOnError: false);
			if (DrawerType == null)
			{
				Plugin.Log.LogError((object)"[Bind] BetrValheim.Drawers.Drawer not found.");
				return false;
			}
			ItemNameProp = DrawerType.GetProperty("ItemName", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			CountProp = DrawerType.GetProperty("Count", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			FreeSpaceProp = DrawerType.GetProperty("FreeSpace", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			DepositMethod = DrawerType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Deposit" && m.ReturnType == typeof(int) && ParametersAre(m, typeof(string), typeof(int), typeof(bool))) ?? DrawerType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Deposit" && m.ReturnType == typeof(int) && ParametersAre(m, typeof(string), typeof(int)));
			WithdrawMethod = DrawerType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Withdraw" && m.ReturnType == typeof(int) && ParametersAre(m, typeof(int), typeof(bool))) ?? DrawerType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Withdraw" && m.ReturnType == typeof(int) && ParametersAre(m, typeof(int)));
			NviewMember = (MemberInfo)(((object)DrawerType.GetField("_nview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) ?? ((object)DrawerType.GetProperty("_nview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)));
			if (NviewMember == null)
			{
				NviewMember = (MemberInfo)(((object)DrawerType.GetField("m_nview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) ?? ((object)DrawerType.GetProperty("m_nview", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)));
			}
			Plugin.Log.LogInfo((object)("[Bind] BetrValheim drawer type: " + DrawerType.FullName));
			Plugin.Log.LogInfo((object)("[Bind] ItemName: " + Describe(ItemNameProp)));
			Plugin.Log.LogInfo((object)("[Bind] Count: " + Describe(CountProp)));
			Plugin.Log.LogInfo((object)("[Bind] FreeSpace: " + Describe(FreeSpaceProp)));
			Plugin.Log.LogInfo((object)("[Bind] Deposit: " + Describe(DepositMethod)));
			Plugin.Log.LogInfo((object)("[Bind] Withdraw: " + Describe(WithdrawMethod)));
			Plugin.Log.LogInfo((object)("[Bind] ZNetView member: " + (NviewMember?.Name ?? "<missing>")));
			int num;
			if (ItemNameProp != null && CountProp != null && FreeSpaceProp != null && DepositMethod != null && WithdrawMethod != null)
			{
				num = ((NviewMember != null) ? 1 : 0);
				if (num != 0)
				{
					goto IL_0332;
				}
			}
			else
			{
				num = 0;
			}
			Plugin.Log.LogError((object)"[Bind] One or more required BetrValheim members are missing.");
			goto IL_0332;
			IL_0332:
			return (byte)num != 0;
		}

		internal static void LogApiSurface()
		{
			try
			{
				Type? typeFromHandle = typeof(ItemDrawers_API);
				Type typeFromHandle2 = typeof(ItemDrawers_API.Drawer);
				MethodInfo method = typeFromHandle.GetMethod("AllDrawersInRange", BindingFlags.Static | BindingFlags.Public);
				MethodInfo method2 = typeFromHandle2.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public);
				MethodInfo method3 = typeFromHandle2.GetMethod("Remove", BindingFlags.Instance | BindingFlags.Public);
				FieldInfo field = typeFromHandle2.GetField("Prefab", BindingFlags.Instance | BindingFlags.Public);
				FieldInfo field2 = typeFromHandle2.GetField("Amount", BindingFlags.Instance | BindingFlags.Public);
				FieldInfo field3 = typeFromHandle2.GetField("Quality", BindingFlags.Instance | BindingFlags.Public);
				FieldInfo fieldInfo = typeFromHandle2.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo f) => typeof(ZNetView).IsAssignableFrom(f.FieldType));
				Plugin.Log.LogInfo((object)"[API] API.ItemDrawers_API, kg_ItemDrawers");
				Plugin.Log.LogInfo((object)("[API] AllDrawersInRange(Vector3, Single): " + ((method != null && ParametersAre(method, typeof(Vector3), typeof(float))) ? "OK" : "MISSING/WRONG")));
				Plugin.Log.LogInfo((object)("[API] Drawer.Add(Int32, Int32): " + ((method2 != null && ParametersAre(method2, typeof(int), typeof(int))) ? "OK" : "MISSING/WRONG")));
				Plugin.Log.LogInfo((object)("[API] Drawer.Remove(Int32): " + ((method3 != null && ParametersAre(method3, typeof(int))) ? "OK" : "MISSING/WRONG")));
				Plugin.Log.LogInfo((object)("[API] Drawer fields: Prefab=" + (field != null) + " Amount=" + (field2 != null) + " Quality=" + (field3 != null) + " ZNetView=" + (fieldInfo?.Name ?? "<missing>")));
				Plugin.Log.LogInfo((object)"[API] ItemDrawers compatibility surface ready.");
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[API] self-check failed: " + Root(e)));
			}
		}

		private static bool ParametersAre(MethodInfo method, params Type[] types)
		{
			if (method == null)
			{
				return false;
			}
			ParameterInfo[] parameters = method.GetParameters();
			if (parameters.Length != types.Length)
			{
				return false;
			}
			for (int i = 0; i < parameters.Length; i++)
			{
				if (parameters[i].ParameterType != types[i])
				{
					return false;
				}
			}
			return true;
		}

		internal static void Refresh(bool forceSummary)
		{
			//IL_040c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0419: Unknown result type (might be due to invalid IL or missing references)
			//IL_0426: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			//IL_039f: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0343: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			if (DrawerType == null)
			{
				return;
			}
			Object[] array;
			try
			{
				array = Resources.FindObjectsOfTypeAll(DrawerType);
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[Scan] FindObjectsOfTypeAll failed: " + Root(e)));
				return;
			}
			float unscaledTime = Time.unscaledTime;
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			Object[] array2 = array;
			foreach (Object val in array2)
			{
				if (val == (Object)null)
				{
					num++;
					continue;
				}
				Component val2 = (Component)(object)((val is Component) ? val : null);
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.gameObject == (Object)null))
				{
					Scene scene = val2.gameObject.scene;
					if (((Scene)(ref scene)).IsValid())
					{
						object obj = val;
						ZNetView nview = GetNview(obj);
						if ((Object)(object)nview == (Object)null || !nview.IsValid())
						{
							num3++;
							continue;
						}
						ZDO zDO = nview.GetZDO();
						if (zDO == null || zDO.m_uid == ZDOID.None)
						{
							num3++;
							continue;
						}
						ZDOID uid = zDO.m_uid;
						GameObject val3 = PhysicalRoot(nview);
						if (!((Object)(object)val3 == (Object)null))
						{
							scene = val3.scene;
							if (((Scene)(ref scene)).IsValid())
							{
								bool flag = val2.gameObject.activeInHierarchy && val3.activeInHierarchy;
								bool flag2 = LastActiveSeen.ContainsKey(uid);
								if (!flag)
								{
									num2++;
									if (!flag2)
									{
										continue;
									}
								}
								else
								{
									LastActiveSeen[uid] = unscaledTime;
								}
								if (ByZdoId.TryGetValue(uid, out var value) && value != null && value != obj)
								{
									ByReal.Remove(value);
								}
								ByZdoId[uid] = obj;
								ByViewId[((Object)nview).GetInstanceID()] = obj;
								if (!ByZdoIdMarker.TryGetValue(uid, out var value2) || (Object)(object)value2 == (Object)null || (Object)(object)((Component)value2).gameObject != (Object)(object)val3)
								{
									if ((Object)(object)value2 != (Object)null)
									{
										DrawerComponent.AllDrawers.Remove(value2);
										try
										{
											Object.Destroy((Object)(object)value2);
										}
										catch
										{
										}
									}
									value2 = val3.GetComponent<DrawerComponent>();
									if ((Object)(object)value2 == (Object)null)
									{
										value2 = val3.AddComponent<DrawerComponent>();
									}
									ByZdoIdMarker[uid] = value2;
								}
								ByReal[obj] = value2;
								value2.Bind(obj, nview);
								value2.Sync();
								if (!DrawerComponent.AllDrawers.Contains(value2))
								{
									DrawerComponent.AllDrawers.Add(value2);
								}
								string assigned = GetAssigned(obj);
								if (string.IsNullOrEmpty(assigned))
								{
									num5++;
								}
								else
								{
									num4++;
								}
								if (Plugin.DetailedLogging.Value)
								{
									Plugin.Log.LogInfo((object)("[Drawer] root='" + ((Object)val3).name + "' real='" + ((Object)val2.gameObject).name + "' " + $"pos={val3.transform.position} assigned='{assigned}' prefab='{value2.CurrentPrefab}' " + $"count={GetCount(obj)} free={GetFreeSpace(obj)} zdo={uid} active={flag}"));
								}
								continue;
							}
						}
						num2++;
						continue;
					}
				}
				num2++;
			}
			ZDOID[] array3 = LastActiveSeen.Keys.ToArray();
			foreach (ZDOID key in array3)
			{
				float num6 = LastActiveSeen[key];
				if (unscaledTime - num6 <= 2.5f)
				{
					continue;
				}
				if (ByZdoId.TryGetValue(key, out var value3) && value3 != null)
				{
					ByReal.Remove(value3);
				}
				if (ByZdoIdMarker.TryGetValue(key, out var value4) && (Object)(object)value4 != (Object)null)
				{
					DrawerComponent.AllDrawers.Remove(value4);
					try
					{
						Object.Destroy((Object)(object)value4);
					}
					catch
					{
					}
				}
				ByZdoId.Remove(key);
				ByZdoIdMarker.Remove(key);
				LastActiveSeen.Remove(key);
			}
			KeyValuePair<int, object>[] array4 = ByViewId.ToArray();
			for (int i = 0; i < array4.Length; i++)
			{
				KeyValuePair<int, object> keyValuePair = array4[i];
				ZNetView nview2 = GetNview(keyValuePair.Value);
				if ((Object)(object)nview2 == (Object)null || !nview2.IsValid() || ((Object)nview2).GetInstanceID() != keyValuePair.Key)
				{
					ByViewId.Remove(keyValuePair.Key);
				}
			}
			DrawerComponent.AllDrawers.RemoveAll(delegate(DrawerComponent d)
			{
				//IL_0045: Unknown result type (might be due to invalid IL or missing references)
				//IL_004a: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)d == (Object)null) && !((Object)(object)d.View == (Object)null) && d.View.IsValid() && d.View.GetZDO() != null && !((Object)(object)((Component)d).gameObject == (Object)null))
				{
					Scene scene2 = ((Component)d).gameObject.scene;
					return !((Scene)(ref scene2)).IsValid();
				}
				return true;
			});
			int num7 = 0;
			int num8 = 0;
			foreach (DrawerComponent allDrawer in DrawerComponent.AllDrawers)
			{
				if (!((Object)(object)allDrawer == (Object)null) && !((Object)(object)((Component)allDrawer).gameObject == (Object)null))
				{
					if (((Component)allDrawer).gameObject.activeInHierarchy)
					{
						num7++;
					}
					else
					{
						num8++;
					}
				}
			}
			int count = DrawerComponent.AllDrawers.Count;
			if (forceSummary || array.Length != _lastFound || count != _lastActive)
			{
				_lastFound = array.Length;
				_lastActive = count;
				Plugin.Log.LogInfo((object)($"[Scan] found={array.Length} cached={count} active={num7} grace={num8} " + $"assigned={num4} unassigned={num5} invalidNview={num3} inactive={num2} null={num}"));
			}
		}

		internal static void Clear()
		{
			DrawerComponent[] array = DrawerComponent.AllDrawers.ToArray();
			foreach (DrawerComponent drawerComponent in array)
			{
				if ((Object)(object)drawerComponent != (Object)null)
				{
					try
					{
						Object.Destroy((Object)(object)drawerComponent);
					}
					catch
					{
					}
				}
			}
			DrawerComponent.AllDrawers.Clear();
			ByReal.Clear();
			ByViewId.Clear();
			ByZdoId.Clear();
			ByZdoIdMarker.Clear();
			LastActiveSeen.Clear();
			RecentRanges.Clear();
			_nextRecoveryRefresh = 0f;
			_lastFound = -1;
			_lastActive = -1;
		}

		internal static GameObject PhysicalRoot(ZNetView view)
		{
			if ((Object)(object)view == (Object)null)
			{
				return null;
			}
			Piece val = ((Component)view).GetComponent<Piece>() ?? ((Component)view).GetComponentInParent<Piece>();
			if (!((Object)(object)val != (Object)null))
			{
				return ((Component)view).gameObject;
			}
			return ((Component)val).gameObject;
		}

		internal static ZNetView GetNview(object real)
		{
			try
			{
				if (real == null || NviewMember == null)
				{
					return null;
				}
				if (NviewMember is FieldInfo fieldInfo)
				{
					object? value = fieldInfo.GetValue(real);
					return (ZNetView)((value is ZNetView) ? value : null);
				}
				if (NviewMember is PropertyInfo propertyInfo)
				{
					object? value2 = propertyInfo.GetValue(real, null);
					return (ZNetView)((value2 is ZNetView) ? value2 : null);
				}
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[ZNetView] " + Root(e)));
			}
			return null;
		}

		private static bool TryGetZdoId(ZNetView view, out ZDOID zdoId)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			zdoId = ZDOID.None;
			if ((Object)(object)view == (Object)null || !view.IsValid())
			{
				return false;
			}
			ZDO zDO = view.GetZDO();
			if (zDO == null || zDO.m_uid == ZDOID.None)
			{
				return false;
			}
			zdoId = zDO.m_uid;
			return true;
		}

		internal static bool TryGetReal(ZNetView view, out object real)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			real = null;
			if ((Object)(object)view == (Object)null)
			{
				return false;
			}
			if (TryGetZdoId(view, out var zdoId) && ByZdoId.TryGetValue(zdoId, out real) && real != null && TryGetZdoId(GetNview(real), out var zdoId2) && zdoId2 == zdoId)
			{
				return true;
			}
			if (ByViewId.TryGetValue(((Object)view).GetInstanceID(), out real) && real != null)
			{
				ZNetView nview = GetNview(real);
				if ((Object)(object)nview != (Object)null && (Object)(object)nview == (Object)(object)view && nview.IsValid())
				{
					return true;
				}
			}
			real = null;
			return false;
		}

		private static bool MarkerUsable(DrawerComponent marker, float now)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)marker == (Object)null) && !((Object)(object)marker.View == (Object)null) && marker.View.IsValid() && marker.View.GetZDO() != null && !((Object)(object)((Component)marker).gameObject == (Object)null))
			{
				Scene scene = ((Component)marker).gameObject.scene;
				if (((Scene)(ref scene)).IsValid())
				{
					if (((Component)marker).gameObject.activeInHierarchy)
					{
						return true;
					}
					if (TryGetZdoId(marker.View, out var zdoId) && LastActiveSeen.TryGetValue(zdoId, out var value))
					{
						return now - value <= 2.5f;
					}
					return false;
				}
			}
			return false;
		}

		internal static List<ZNetView> AllViews()
		{
			float unscaledTime = Time.unscaledTime;
			List<ZNetView> list = new List<ZNetView>();
			DrawerComponent[] array = DrawerComponent.AllDrawers.ToArray();
			foreach (DrawerComponent drawerComponent in array)
			{
				if (MarkerUsable(drawerComponent, unscaledTime))
				{
					drawerComponent.Sync();
					list.Add(drawerComponent.View);
				}
			}
			return list;
		}

		internal static void RememberRange(Vector3 centre, float range)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (range <= 0f)
			{
				return;
			}
			float now = Time.unscaledTime;
			RecentRanges.RemoveAll((RecentRange q) => q == null || now - q.Seen > 3f);
			RecentRange recentRange = RecentRanges.FirstOrDefault((RecentRange q) => Vector3.Distance(q.Centre, centre) < 1f && Mathf.Abs(q.Range - range) < 1f);
			if (recentRange != null)
			{
				recentRange.Centre = centre;
				recentRange.Range = range;
				recentRange.Seen = now;
				return;
			}
			RecentRanges.Add(new RecentRange
			{
				Centre = centre,
				Range = range,
				Seen = now
			});
			while (RecentRanges.Count > 8)
			{
				RecentRange recentRange2 = RecentRanges.OrderBy((RecentRange q) => q.Seen).FirstOrDefault();
				if (recentRange2 != null)
				{
					RecentRanges.Remove(recentRange2);
					continue;
				}
				break;
			}
		}

		private static bool TryRelevantRange(Vector3 chestPosition, out Vector3 centre, out float range)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			centre = Vector3.zero;
			range = 0f;
			float unscaledTime = Time.unscaledTime;
			RecentRange recentRange = null;
			float num = float.MaxValue;
			foreach (RecentRange recentRange2 in RecentRanges)
			{
				if (recentRange2 != null && !(unscaledTime - recentRange2.Seen > 2f))
				{
					float num2 = Vector3.Distance(chestPosition, recentRange2.Centre);
					if (!(num2 > recentRange2.Range + 10f) && !(num2 >= num))
					{
						recentRange = recentRange2;
						num = num2;
					}
				}
			}
			if (recentRange == null)
			{
				return false;
			}
			centre = recentRange.Centre;
			range = recentRange.Range;
			return true;
		}

		private static bool MatchingDrawerInRange(string sharedName, Vector3 centre, float range)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			string text = ResolvePrefabName(sharedName);
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			float unscaledTime = Time.unscaledTime;
			float num = range * range;
			DrawerComponent[] array = DrawerComponent.AllDrawers.ToArray();
			foreach (DrawerComponent drawerComponent in array)
			{
				if (MarkerUsable(drawerComponent, unscaledTime) && drawerComponent.RealDrawer != null)
				{
					Vector3 val = ((Component)drawerComponent.View).transform.position - centre;
					if (!(((Vector3)(ref val)).sqrMagnitude > num) && GetFreeSpace(drawerComponent.RealDrawer) > 0 && (Matches(drawerComponent.CurrentPrefab, text) || Matches(GetAssigned(drawerComponent.RealDrawer), sharedName)))
					{
						return true;
					}
				}
			}
			return false;
		}

		internal static bool MatchingDrawerBeforeBo(Container chest, string sharedName)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)chest == (Object)null || string.IsNullOrEmpty(sharedName))
			{
				return false;
			}
			if (!TryRelevantRange(((Component)chest).transform.position, out var centre, out var range))
			{
				return false;
			}
			if (MatchingDrawerInRange(sharedName, centre, range))
			{
				return true;
			}
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime < _nextRecoveryRefresh)
			{
				return false;
			}
			_nextRecoveryRefresh = unscaledTime + 0.25f;
			Refresh(forceSummary: false);
			bool num = MatchingDrawerInRange(sharedName, centre, range);
			if (num)
			{
				Plugin.Log.LogInfo((object)("[Drawer] Recovered matching drawer for '" + sharedName + "' before Better Organisation fallback."));
			}
			return num;
		}

		internal static void SyncOne(object real)
		{
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if (real == null)
			{
				return;
			}
			if (ByReal.TryGetValue(real, out var value) && (Object)(object)value != (Object)null)
			{
				if (TryGetZdoId(value.View, out var zdoId))
				{
					LastActiveSeen[zdoId] = Time.unscaledTime;
				}
				value.Sync();
				return;
			}
			ZNetView nview = GetNview(real);
			if (!TryGetZdoId(nview, out var zdoId2))
			{
				return;
			}
			GameObject val = PhysicalRoot(nview);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Scene scene = val.scene;
			if (!((Scene)(ref scene)).IsValid())
			{
				return;
			}
			if (ByZdoIdMarker.TryGetValue(zdoId2, out var value2) && (Object)(object)value2 != (Object)null && (Object)(object)((Component)value2).gameObject != (Object)(object)val)
			{
				DrawerComponent.AllDrawers.Remove(value2);
				try
				{
					Object.Destroy((Object)(object)value2);
				}
				catch
				{
				}
			}
			value = val.GetComponent<DrawerComponent>();
			if ((Object)(object)value == (Object)null)
			{
				value = val.AddComponent<DrawerComponent>();
			}
			value.Bind(real, nview);
			value.Sync();
			if (ByZdoId.TryGetValue(zdoId2, out var value3) && value3 != null && value3 != real)
			{
				ByReal.Remove(value3);
			}
			ByReal[real] = value;
			ByZdoId[zdoId2] = real;
			ByZdoIdMarker[zdoId2] = value;
			ByViewId[((Object)nview).GetInstanceID()] = real;
			LastActiveSeen[zdoId2] = Time.unscaledTime;
			if (!DrawerComponent.AllDrawers.Contains(value))
			{
				DrawerComponent.AllDrawers.Add(value);
			}
		}

		internal static string GetAssigned(object real)
		{
			try
			{
				return (ItemNameProp?.GetValue(real, null) as string) ?? string.Empty;
			}
			catch
			{
				return string.Empty;
			}
		}

		internal static int GetCount(object real)
		{
			try
			{
				object obj = CountProp?.GetValue(real, null);
				return (obj != null) ? Convert.ToInt32(obj) : 0;
			}
			catch
			{
				return 0;
			}
		}

		internal static int GetFreeSpace(object real)
		{
			try
			{
				object obj = FreeSpaceProp?.GetValue(real, null);
				return (obj != null) ? Convert.ToInt32(obj) : 0;
			}
			catch
			{
				return 0;
			}
		}

		internal static string ResolvePrefabName(string assigned)
		{
			if (string.IsNullOrEmpty(assigned))
			{
				return string.Empty;
			}
			try
			{
				if ((Object)(object)ObjectDB.instance != (Object)null)
				{
					GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(assigned);
					if ((Object)(object)itemPrefab != (Object)null)
					{
						return ((Object)itemPrefab).name;
					}
					foreach (GameObject item in ObjectDB.instance.m_items)
					{
						if ((Object)(object)item == (Object)null)
						{
							continue;
						}
						ItemDrop component = item.GetComponent<ItemDrop>();
						if (!((Object)(object)component == (Object)null) && component.m_itemData?.m_shared != null)
						{
							string name = component.m_itemData.m_shared.m_name;
							if (string.Equals(name, assigned, StringComparison.Ordinal) || string.Equals(name?.TrimStart('$'), assigned?.TrimStart('$'), StringComparison.OrdinalIgnoreCase))
							{
								return ((Object)item).name;
							}
						}
					}
				}
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[Prefab] could not resolve '" + assigned + "': " + Root(e)));
			}
			return assigned;
		}

		private static object InvokeDeposit(object real, string prefab, int amount)
		{
			if (DepositMethod.GetParameters().Length != 3)
			{
				return DepositMethod.Invoke(real, new object[2] { prefab, amount });
			}
			return DepositMethod.Invoke(real, new object[3] { prefab, amount, true });
		}

		private static object InvokeWithdraw(object real, int amount)
		{
			if (WithdrawMethod.GetParameters().Length != 2)
			{
				return WithdrawMethod.Invoke(real, new object[1] { amount });
			}
			return WithdrawMethod.Invoke(real, new object[2] { amount, true });
		}

		internal static void ApiAdd(ZNetView view, string snapshotPrefab, int amount, int quality)
		{
			if ((Object)(object)view == (Object)null || !view.IsValid())
			{
				throw new InvalidOperationException("ItemDrawers API Add: drawer ZNetView is invalid.");
			}
			if (amount <= 0)
			{
				throw new ArgumentOutOfRangeException("amount");
			}
			if (quality <= 0)
			{
				quality = 1;
			}
			if (!TryGetReal(view, out var real))
			{
				throw new InvalidOperationException("ItemDrawers API Add: backing BetrValheim drawer was not found.");
			}
			string text = ResolvePrefabName(GetAssigned(real));
			if (string.IsNullOrEmpty(text))
			{
				throw new InvalidOperationException("ItemDrawers API Add: backing drawer is unassigned.");
			}
			if (!string.IsNullOrEmpty(snapshotPrefab) && !Matches(text, snapshotPrefab))
			{
				throw new InvalidOperationException("ItemDrawers API Add: drawer changed from '" + snapshotPrefab + "' to '" + text + "'.");
			}
			if (quality != 1)
			{
				throw new InvalidOperationException($"ItemDrawers API Add: BetrValheim drawer cannot represent quality {quality}.");
			}
			int num = Math.Max(0, GetFreeSpace(real));
			if (num < amount)
			{
				throw new InvalidOperationException($"ItemDrawers API Add: requested {amount}, free space is {num}.");
			}
			if (Plugin.DetailedLogging.Value)
			{
				Plugin.Log.LogInfo((object)$"[ADD->] prefab='{text}' amount={amount} quality={quality} count={GetCount(real)} free={num}");
			}
			object obj = InvokeDeposit(real, text, amount);
			int num2 = ((obj != null) ? Convert.ToInt32(obj) : 0);
			if (num2 != amount)
			{
				if (num2 > 0)
				{
					try
					{
						InvokeWithdraw(real, num2);
					}
					catch (Exception e)
					{
						Plugin.Log.LogError((object)("[ADD] partial-deposit rollback failed: " + Root(e)));
					}
				}
				SyncOne(real);
				throw new InvalidOperationException($"ItemDrawers API Add: BetrValheim moved {num2}, expected {amount}.");
			}
			SyncOne(real);
			if (Plugin.DetailedLogging.Value)
			{
				Plugin.Log.LogInfo((object)$"[ADD<-] prefab='{text}' moved={num2} count={GetCount(real)} free={GetFreeSpace(real)}");
			}
		}

		internal static void ApiRemove(ZNetView view, int amount)
		{
			if ((Object)(object)view == (Object)null || !view.IsValid())
			{
				throw new InvalidOperationException("ItemDrawers API Remove: drawer ZNetView is invalid.");
			}
			if (amount <= 0)
			{
				throw new ArgumentOutOfRangeException("amount");
			}
			if (!TryGetReal(view, out var real))
			{
				throw new InvalidOperationException("ItemDrawers API Remove: backing BetrValheim drawer was not found.");
			}
			int num = Math.Max(0, GetCount(real));
			if (num < amount)
			{
				throw new InvalidOperationException($"ItemDrawers API Remove: requested {amount}, only {num} available.");
			}
			string text = ResolvePrefabName(GetAssigned(real));
			if (Plugin.DetailedLogging.Value)
			{
				Plugin.Log.LogInfo((object)$"[REMOVE->] prefab='{text}' amount={amount} count={num}");
			}
			object obj = InvokeWithdraw(real, amount);
			int num2 = ((obj != null) ? Convert.ToInt32(obj) : 0);
			if (num2 != amount)
			{
				if (num2 > 0 && !string.IsNullOrEmpty(text))
				{
					try
					{
						InvokeDeposit(real, text, num2);
					}
					catch (Exception e)
					{
						Plugin.Log.LogError((object)("[REMOVE] partial-withdraw rollback failed: " + Root(e)));
					}
				}
				SyncOne(real);
				throw new InvalidOperationException($"ItemDrawers API Remove: BetrValheim moved {num2}, expected {amount}.");
			}
			SyncOne(real);
			if (Plugin.DetailedLogging.Value)
			{
				Plugin.Log.LogInfo((object)$"[REMOVE<-] prefab='{text}' moved={num2} count={GetCount(real)}");
			}
		}

		internal static bool Matches(string left, string right)
		{
			if (string.IsNullOrEmpty(left) || string.IsNullOrEmpty(right))
			{
				return false;
			}
			if (string.Equals(left, right, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			return string.Equals(left.TrimStart('$').Replace("_", string.Empty), right.TrimStart('$').Replace("_", string.Empty), StringComparison.OrdinalIgnoreCase);
		}

		internal static string Describe(PropertyInfo p)
		{
			if (!(p == null))
			{
				return p.DeclaringType?.FullName + "." + p.Name + " : " + p.PropertyType.FullName;
			}
			return "<missing>";
		}

		internal static string Describe(MethodInfo m)
		{
			if (m == null)
			{
				return "<missing>";
			}
			return m.DeclaringType?.FullName + "." + m.Name + "(" + string.Join(", ", from p in m.GetParameters()
				select p.ParameterType.Name) + ") -> " + m.ReturnType.Name;
		}

		internal static string Root(Exception e)
		{
			while (e is TargetInvocationException { InnerException: not null } ex)
			{
				e = ex.InnerException;
			}
			return e?.Message ?? "<unknown>";
		}
	}
	internal static class BetterOrganisationChestPanelCompat
	{
		private const BindingFlags InstanceAny = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private const BindingFlags StaticAny = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private const string CompatObjectName = "pho_bo_compat_note";

		private const string CompatButtonObjectName = "pho_bo_compat_button";

		private const string CompatText = "Override: Storage Rules managed by Better Organisation";

		private const string CompatButtonText = "Open Better Organisation";

		[ThreadStatic]
		private static bool _inPanelRead;

		private static bool _installed;

		private static bool _reportedMissing;

		private static bool _reportedSuppression;

		private static FieldInfo _container;

		private static FieldInfo _picking;

		private static FieldInfo _column;

		private static FieldInfo _grid;

		private static FieldInfo _choices;

		private static FieldInfo _mineHelp;

		private static FieldInfo _origin;

		private static FieldInfo _byHand;

		private static FieldInfo _slack;

		private static FieldInfo _remeasureFlag;

		private static MethodInfo _remeasure;

		private static MethodInfo _resize;

		private static PropertyInfo _boUiInstance;

		private static PropertyInfo _boGui;

		private static MethodInfo _boToggleEditor;

		internal static bool InPanelRead => _inPanelRead;

		internal static bool Install(Harmony harmony)
		{
			//IL_03cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d2: Expected O, but got Unknown
			//IL_03e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e9: Expected O, but got Unknown
			//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0400: Expected O, but got Unknown
			//IL_0433: Unknown result type (might be due to invalid IL or missing references)
			//IL_0441: Expected O, but got Unknown
			//IL_0455: Unknown result type (might be due to invalid IL or missing references)
			//IL_0463: Expected O, but got Unknown
			if (_installed)
			{
				return true;
			}
			if (!BetterOrganisationRules.IsAvailable)
			{
				return false;
			}
			Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase));
			Type type = assembly?.GetType("Innangard.ChestSortPanel", throwOnError: false);
			if (type == null)
			{
				if (!_reportedMissing && assembly != null)
				{
					_reportedMissing = true;
					Plugin.Log.LogWarning((object)"[BO UI] Innangard.ChestSortPanel was not found; Shift+E picker suppression is unavailable.");
				}
				return false;
			}
			MethodInfo method = type.GetMethod("Open", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Container) }, null);
			MethodInfo method2 = type.GetMethod("Refresh", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo m)
			{
				if (m.Name != "BuildChoices")
				{
					return false;
				}
				ParameterInfo[] parameters = m.GetParameters();
				return parameters.Length == 2 && typeof(Container).IsAssignableFrom(parameters[0].ParameterType);
			});
			MethodInfo methodInfo2 = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Populate" && m.GetParameters().Length == 1 && m.ReturnType == typeof(void));
			_container = type.GetField("_container", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			_picking = type.GetField("_picking", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			_column = type.GetField("_column", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			_grid = type.GetField("_grid", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			_choices = type.GetField("_choices", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			_mineHelp = type.GetField("_mineHelp", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			_origin = type.GetField("_origin", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			_byHand = type.GetField("_byHand", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			_slack = type.GetField("_slack", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			_remeasureFlag = type.GetField("_remeasure", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			_remeasure = type.GetMethod("Remeasure", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			_resize = type.GetMethod("Resize", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			Type? obj = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "ChestCategories", StringComparison.OrdinalIgnoreCase))?.GetType("ChestCategories.UI.ModUi", throwOnError: false);
			_boUiInstance = obj?.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_boGui = obj?.GetProperty("Gui", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			_boToggleEditor = obj?.GetMethod("ToggleEditor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Container) }, null);
			if (method == null || method2 == null || methodInfo == null || methodInfo2 == null || _container == null || _picking == null || _column == null || _mineHelp == null)
			{
				if (!_reportedMissing)
				{
					_reportedMissing = true;
					Plugin.Log.LogWarning((object)("[BO UI] Innangard 0.1.9 Shift+E panel binding incomplete: " + $"Open={method != null} Refresh={method2 != null} BuildChoices={methodInfo != null} " + $"Populate={methodInfo2 != null} container={_container != null} picking={_picking != null} column={_column != null} mineHelp={_mineHelp != null}."));
				}
				return false;
			}
			HarmonyMethod val = new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "PanelScopePrefix", (Type[])null);
			HarmonyMethod val2 = new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "PanelScopePostfix", (Type[])null);
			HarmonyMethod val3 = new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "PanelScopeFinalizer", (Type[])null);
			harmony.Patch((MethodBase)method, val, val2, (HarmonyMethod)null, val3, (HarmonyMethod)null);
			harmony.Patch((MethodBase)method2, val, val2, (HarmonyMethod)null, val3, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "BuildChoicesPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(BetterOrganisationChestPanelCompat), "PopulatePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_installed = true;
			Plugin.Log.LogInfo((object)"[BO UI] Bound Innangard.ChestSortPanel Open/Refresh/BuildChoices/Populate.");
			Plugin.Log.LogInfo((object)"[BO UI] BO-managed Shift+E panels keep intake/private controls and replace only the material picker with a Better Organisation storage-rules shortcut.");
			if (_boUiInstance != null && _boToggleEditor != null)
			{
				Plugin.Log.LogInfo((object)"[BO UI] Better Organisation category editor shortcut bound.");
			}
			else
			{
				Plugin.Log.LogWarning((object)"[BO UI] Better Organisation category editor shortcut was not found; the compatibility heading will still be shown.");
			}
			return true;
		}

		private static void PanelScopePrefix()
		{
			_inPanelRead = true;
		}

		private static void PanelScopePostfix(object __instance)
		{
			_inPanelRead = false;
			Apply(__instance);
		}

		private static Exception PanelScopeFinalizer(Exception __exception)
		{
			_inPanelRead = false;
			return __exception;
		}

		private static bool BuildChoicesPrefix(Container __0)
		{
			return !BetterOrganisationRules.IsManaged(__0);
		}

		private static bool PopulatePrefix(object __instance)
		{
			if (!BetterOrganisationRules.IsManaged(GetContainer(__instance)))
			{
				return true;
			}
			ClearPickerRows(__instance);
			return false;
		}

		private static Container GetContainer(object panel)
		{
			try
			{
				return (Container)((panel != null) ? /*isinst with value type is only supported in some contexts*/: null);
			}
			catch
			{
				return null;
			}
		}

		private static void ClearPickerRows(object panel)
		{
			try
			{
				object? obj = _grid?.GetValue(panel);
				RectTransform val = (RectTransform)((obj is RectTransform) ? obj : null);
				if ((Object)(object)val != (Object)null)
				{
					for (int num = ((Transform)val).childCount - 1; num >= 0; num--)
					{
						Object.Destroy((Object)(object)((Component)((Transform)val).GetChild(num)).gameObject);
					}
				}
				(_choices?.GetValue(panel) as IList)?.Clear();
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[BO UI] Could not clear Innangard's old material rows: " + Bridge.Root(e)));
			}
		}

		private static void Apply(object panel)
		{
			if (panel == null)
			{
				return;
			}
			try
			{
				Container container = GetContainer(panel);
				bool flag = BetterOrganisationRules.IsManaged(container);
				object? value = _picking.GetValue(panel);
				RectTransform val = (RectTransform)((value is RectTransform) ? value : null);
				object? value2 = _column.GetValue(panel);
				RectTransform val2 = (RectTransform)((value2 is RectTransform) ? value2 : null);
				if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
				{
					return;
				}
				GameObject val3 = FindCompatObject(val2, "pho_bo_compat_note");
				GameObject val4 = FindCompatObject(val2, "pho_bo_compat_button");
				bool flag2 = false;
				if (flag)
				{
					if (((Component)val).gameObject.activeSelf)
					{
						((Component)val).gameObject.SetActive(false);
						flag2 = true;
					}
					if ((Object)(object)val3 == (Object)null)
					{
						val3 = CreateCompatNote(panel, val2, val);
						flag2 = (Object)(object)val3 != (Object)null;
					}
					if ((Object)(object)val4 == (Object)null && _boUiInstance != null && _boToggleEditor != null)
					{
						val4 = CreateCompatButton(panel, val2, val, container);
						flag2 = (Object)(object)val4 != (Object)null;
					}
					if ((Object)(object)val3 != (Object)null)
					{
						if (!val3.activeSelf)
						{
							val3.SetActive(true);
							flag2 = true;
						}
						int siblingIndex = ((Transform)val).GetSiblingIndex();
						if (val3.transform.GetSiblingIndex() != siblingIndex)
						{
							val3.transform.SetSiblingIndex(siblingIndex);
							flag2 = true;
						}
					}
					if ((Object)(object)val4 != (Object)null)
					{
						BetterOrganisationEditorButtonAction component = val4.GetComponent<BetterOrganisationEditorButtonAction>();
						if ((Object)(object)component != (Object)null)
						{
							component.Target = container;
							component.InnangardPanel = panel;
						}
						if (!val4.activeSelf)
						{
							val4.SetActive(true);
							flag2 = true;
						}
						int num = (((Object)(object)val3 != (Object)null) ? Math.Min(val3.transform.GetSiblingIndex() + 1, Math.Max(0, ((Transform)val2).childCount - 1)) : ((Transform)val).GetSiblingIndex());
						if (val4.transform.GetSiblingIndex() != num)
						{
							val4.transform.SetSiblingIndex(num);
							flag2 = true;
						}
					}
					ClearPickerRows(panel);
					SetSlackFlexible(panel, 1f);
					if (!_reportedSuppression)
					{
						_reportedSuppression = true;
						Plugin.Log.LogInfo((object)"[BO UI] Suppressed the Shift+E Search/material list for a Better Organisation-managed chest.");
					}
				}
				else
				{
					if ((Object)(object)val3 != (Object)null && val3.activeSelf)
					{
						val3.SetActive(false);
						flag2 = true;
					}
					if ((Object)(object)val4 != (Object)null && val4.activeSelf)
					{
						val4.SetActive(false);
						flag2 = true;
					}
				}
				if (flag2)
				{
					if (_remeasureFlag != null)
					{
						_remeasureFlag.SetValue(panel, true);
					}
					_remeasure?.Invoke(panel, null);
					_resize?.Invoke(panel, null);
				}
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[BO UI] Could not apply Shift+E material-picker compatibility layout: " + Bridge.Root(e)));
			}
		}

		private static GameObject FindCompatObject(RectTransform column, string name)
		{
			for (int i = 0; i < ((Transform)column).childCount; i++)
			{
				Transform child = ((Transform)column).GetChild(i);
				if ((Object)(object)child != (Object)null && (Object)(object)((Component)child).gameObject != (Object)null && ((Object)((Component)child).gameObject).name == name)
				{
					return ((Component)child).gameObject;
				}
			}
			return null;
		}

		private static GameObject CreateCompatNote(object panel, RectTransform column, RectTransform picking)
		{
			object? obj = _origin?.GetValue(panel);
			Component val = (Component)((obj is Component) ? obj : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null)
			{
				object? obj2 = _mineHelp?.GetValue(panel);
				val = (Component)((obj2 is Component) ? obj2 : null);
			}
			if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null)
			{
				return null;
			}
			GameObject obj3 = Object.Instantiate<GameObject>(val.gameObject);
			((Object)obj3).name = "pho_bo_compat_note";
			obj3.transform.SetParent((Transform)(object)column, false);
			obj3.transform.SetSiblingIndex(((Transform)picking).GetSiblingIndex());
			Component component = obj3.GetComponent(((object)val).GetType());
			SetText(component, "Override: Storage Rules managed by Better Organisation");
			SetNumericProperty(component, "fontSize", 20);
			obj3.SetActive(true);
			return obj3;
		}

		private static GameObject CreateCompatButton(object panel, RectTransform column, RectTransform picking, Container container)
		{
			object? obj = _byHand?.GetValue(panel);
			Component val = (Component)((obj is Component) ? obj : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null)
			{
				return null;
			}
			GameObject val2 = Object.Instantiate<GameObject>(val.gameObject);
			((Object)val2).name = "pho_bo_compat_button";
			val2.transform.SetParent((Transform)(object)column, false);
			val2.transform.SetSiblingIndex(((Transform)picking).GetSiblingIndex());
			SetFirstChildText(val2, "Open Better Organisation");
			Component component = val2.GetComponent(((object)val).GetType());
			object obj2 = ((object)component)?.GetType().GetProperty("onClick", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(component, null);
			if (obj2 == null)
			{
				Object.Destroy((Object)(object)val2);
				return null;
			}
			obj2.GetType().GetMethod("RemoveAllListeners", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null)?.Invoke(obj2, null);
			BetterOrganisationEditorButtonAction betterOrganisationEditorButtonAction = val2.AddComponent<BetterOrganisationEditorButtonAction>();
			betterOrganisationEditorButtonAction.Target = container;
			betterOrganisationEditorButtonAction.InnangardPanel = panel;
			MethodInfo method = typeof(BetterOrganisationEditorButtonAction).GetMethod("Click", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			MethodInfo methodInfo = obj2.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "AddListener" && m.GetParameters().Length == 1);
			if (method == null || methodInfo == null)
			{
				Object.Destroy((Object)(object)val2);
				return null;
			}
			Delegate obj3 = Delegate.CreateDelegate(methodInfo.GetParameters()[0].ParameterType, betterOrganisationEditorButtonAction, method);
			methodInfo.Invoke(obj2, new object[1] { obj3 });
			val2.SetActive(true);
			return val2;
		}

		private static void SetFirstChildText(GameObject root, string value)
		{
			if (!((Object)(object)root == (Object)null))
			{
				Component[] componentsInChildren = root.GetComponentsInChildren<Component>(true);
				for (int i = 0; i < componentsInChildren.Length && !SetText(componentsInChildren[i], value); i++)
				{
				}
			}
		}

		private static bool SetText(Component component, string value)
		{
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			try
			{
				PropertyInfo property = ((object)component).GetType().GetProperty("text", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property == null || !property.CanWrite || property.PropertyType != typeof(string))
				{
					return false;
				}
				property.SetValue(component, value, null);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static void SetNumericProperty(Component component, string name, int value)
		{
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			try
			{
				PropertyInfo property = ((object)component).GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (!(property == null) && property.CanWrite)
				{
					if (property.PropertyType == typeof(int))
					{
						property.SetValue(component, value, null);
					}
					else if (property.PropertyType == typeof(float))
					{
						property.SetValue(component, (float)value, null);
					}
				}
			}
			catch
			{
			}
		}

		internal static void CloseInnangardPanel(object panel)
		{
			if (panel == null)
			{
				return;
			}
			try
			{
				Type type = panel.GetType();
				MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => (m.Name == "Close" || m.Name == "Hide" || m.Name == "Dismiss") && m.GetParameters().Length == 0 && m.ReturnType == typeof(void));
				if (methodInfo != null)
				{
					methodInfo.Invoke(panel, null);
					Plugin.Log.LogInfo((object)"[BO UI] Closed Innangard Shift+E panel before opening Better Organisation.");
					return;
				}
				string[] array = new string[4] { "m_panel", "_panel", "_root", "m_root" };
				foreach (string name in array)
				{
					object obj = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(panel);
					GameObject val = (GameObject)((obj is GameObject) ? obj : null);
					if (val != null)
					{
						val.SetActive(false);
						Plugin.Log.LogInfo((object)"[BO UI] Closed Innangard Shift+E panel before opening Better Organisation.");
						return;
					}
					Component val2 = (Component)((obj is Component) ? obj : null);
					if (val2 != null && (Object)(object)val2.gameObject != (Object)null)
					{
						val2.gameObject.SetActive(false);
						Plugin.Log.LogInfo((object)"[BO UI] Closed Innangard Shift+E panel before opening Better Organisation.");
						return;
					}
				}
				Component val3 = (Component)((panel is Component) ? panel : null);
				if (val3 != null && (Object)(object)val3.gameObject != (Object)null)
				{
					val3.gameObject.SetActive(false);
					Plugin.Log.LogInfo((object)"[BO UI] Closed Innangard Shift+E panel before opening Better Organisation.");
				}
				else
				{
					Plugin.Log.LogWarning((object)"[BO UI] Could not find Innangard's panel root to close it.");
				}
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[BO UI] Could not close Innangard Shift+E panel: " + Bridge.Root(e)));
			}
		}

		internal static void OpenBetterOrganisationEditor(Container container)
		{
			if ((Object)(object)container == (Object)null || _boUiInstance == null || _boToggleEditor == null)
			{
				return;
			}
			try
			{
				object value = _boUiInstance.GetValue(null, null);
				if (value == null)
				{
					Plugin.Log.LogWarning((object)"[BO UI] Better Organisation UI is not ready yet.");
					return;
				}
				bool flag = false;
				MethodInfo method = typeof(InventoryGui).GetMethod("IsVisible", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (method != null)
				{
					flag = (bool)method.Invoke(null, null);
				}
				if (!flag)
				{
					object obj = _boGui?.GetValue(value, null);
					(obj?.GetType().GetMethod("Show", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
					{
						typeof(Container),
						typeof(int)
					}, null))?.Invoke(obj, new object[2] { null, 1 });
				}
				_boToggleEditor.Invoke(value, new object[1] { container });
				Plugin.Log.LogInfo((object)"[BO UI] Opened Better Organisation storage rules for the current chest.");
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[BO UI] Could not open Better Organisation storage rules: " + Bridge.Root(e)));
			}
		}

		private static void SetSlackFlexible(object panel, float value)
		{
			try
			{
				object obj = _slack?.GetValue(panel);
				(obj?.GetType().GetProperty("flexibleHeight", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.SetValue(obj, value, null);
			}
			catch
			{
			}
		}
	}
	internal sealed class BetterOrganisationEditorButtonAction : MonoBehaviour
	{
		internal Container Target;

		internal object InnangardPanel;

		public void Click()
		{
			BetterOrganisationChestPanelCompat.CloseInnangardPanel(InnangardPanel);
			BetterOrganisationChestPanelCompat.OpenBetterOrganisationEditor(Target);
		}
	}
	internal static class BetterOrganisationRules
	{
		private sealed class RuleCache
		{
			internal string Signature;

			internal bool HasAnyRule;

			internal HashSet<string> Accepted;
		}

		private sealed class RuleSnapshot
		{
			internal bool HasLinks;

			internal bool AcceptsEverything;

			internal bool AcceptsStash = true;

			internal readonly HashSet<string> Include = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

			internal readonly HashSet<string> Exclude = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

			internal bool HasAnyRule
			{
				get
				{
					if (!HasLinks && !AcceptsEverything && AcceptsStash && Include.Count <= 0)
					{
						return Exclude.Count > 0;
					}
					return true;
				}
			}
		}

		private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private const string MirrorKey = "pho_bo_inn_mirror";

		private const string BackupConfiguredKey = "pho_bo_inn_b_cfg";

		private const string BackupKindsKey = "pho_bo_inn_b_kinds";

		private const string BackupItemsKey = "pho_bo_inn_b_items";

		private const string BackupLegacyKindKey = "pho_bo_inn_b_legacy";

		private static Assembly _assembly;

		private static Type _storeType;

		private static Type _configType;

		private static Type _categoryServiceType;

		private static Type _categoryType;

		private static Type _groupType;

		private static MethodInfo _getConfig;

		private static MethodInfo _getCategory;

		private static PropertyInfo _hasLinks;

		private static PropertyInfo _links;

		private static PropertyInfo _includeItems;

		private static PropertyInfo _excludeItems;

		private static PropertyInfo _acceptsEverything;

		private static PropertyInfo _acceptsStash;

		private static MethodInfo _isLinkedCategory;

		private static MethodInfo _isLinkedGroup;

		private static PropertyInfo _categoryGroup;

		private static PropertyInfo _categoryId;

		private static PropertyInfo _groupId;

		private static MethodInfo _serializeConfig;

		private static readonly Dictionary<int, RuleCache> RuleCaches = new Dictionary<int, RuleCache>();

		private static bool _bound;

		private static bool _reportedUnavailable;

		private static bool _installed;

		private static bool _reportedMissing;

		private static int _lastActive = -1;

		internal static bool IsAvailable => _bound;

		internal static bool Install(Harmony harmony)
		{
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Expected O, but got Unknown
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Expected O, but got Unknown
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Expected O, but got Unknown
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Expected O, but got Unknown
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Expected O, but got Unknown
			if (!_bound)
			{
				return false;
			}
			if (_installed)
			{
				return true;
			}
			Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Innangard", StringComparison.OrdinalIgnoreCase))?.GetType("Innangard.ChestSortRules", throwOnError: false);
			if (type == null)
			{
				return false;
			}
			BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
			Type[] types = new Type[1] { typeof(Container) };
			MethodInfo method = type.GetMethod("IsConfigured", bindingAttr, null, types, null);
			MethodInfo method2 = type.GetMethod("KindsOf", bindingAttr, null, types, null);
			MethodInfo method3 = type.GetMethod("ReadItems", bindingAttr, null, types, null);
			Type[] types2 = new Type[2]
			{
				typeof(Container),
				typeof(string)
			};
			MethodInfo method4 = type.GetMethod("Accepts", bindingAttr, null, types2, null);
			MethodInfo method5 = type.GetMethod("Precision", bindingAttr, null, types2, null);
			if (method == null || method.ReturnType != typeof(bool) || method2 == null || method2.ReturnType != typeof(int) || method3 == null || method3.ReturnType != typeof(HashSet<string>) || method4 == null || method4.ReturnType != typeof(bool) || method5 == null || method5.ReturnType != typeof(int))
			{
				if (!_reportedMissing)
				{
					_reportedMissing = true;
					Plugin.Log.LogError((object)"[BO] Innangard chest rule methods changed; local BO matching is unavailable.");
				}
				return false;
			}
			harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(BetterOrganisationRules), "ConfiguredPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)method2, new HarmonyMethod(typeof(BetterOrganisationRules), "KindsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)method3, new HarmonyMethod(typeof(BetterOrganisationRules), "ItemsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)method4, new HarmonyMethod(typeof(BetterOrganisationRules), "AcceptsPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)method5, new HarmonyMethod(typeof(BetterOrganisationRules), "PrecisionPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			_installed = true;
			Plugin.Log.LogInfo((object)"[BO] Innangard now reads BO category/item rules locally; no extra rule list is written to chests.");
			return true;
		}

		private static bool ConfiguredPrefix(Container __0, ref bool __result)
		{
			if (!TryGetAccepted(__0, out var _))
			{
				return true;
			}
			__result = true;
			return false;
		}

		private static bool KindsPrefix(Container __0, ref int __result)
		{
			if (!TryGetAccepted(__0, out var _))
			{
				return true;
			}
			__result = 0;
			return false;
		}

		private static bool ItemsPrefix(Container __0, ref HashSet<string> __result)
		{
			if (!TryGetAccepted(__0, out var accepted))
			{
				return true;
			}
			if (BetterOrganisationChestPanelCompat.InPanelRead)
			{
				__result = new HashSet<string>(StringComparer.Ordinal) { "__pho_bo_managed__" };
				return false;
			}
			__result = new HashSet<string>(accepted, StringComparer.Ordinal);
			return false;
		}

		private static bool AcceptsPrefix(Container __0, string __1, ref bool __result)
		{
			if (!TryGetAccepted(__0, out var accepted))
			{
				return true;
			}
			__result = !string.IsNullOrEmpty(__1) && accepted.Contains(__1);
			return false;
		}

		private static bool PrecisionPrefix(Container __0, string __1, ref int __result)
		{
			if (!TryGetAccepted(__0, out var accepted))
			{
				return true;
			}
			if (string.IsNullOrEmpty(__1) || !accepted.Contains(__1))
			{
				__result = int.MaxValue;
				return false;
			}
			if (Bridge.MatchingDrawerBeforeBo(__0, __1))
			{
				__result = int.MaxValue;
				return false;
			}
			__result = 0;
			return false;
		}

		internal static bool IsManaged(Container container)
		{
			HashSet<string> accepted;
			return TryGetAccepted(container, out accepted);
		}

		private static bool TryGetAccepted(Container container, out HashSet<string> accepted)
		{
			accepted = null;
			if (!_installed || !_bound || (Object)(object)ObjectDB.instance == (Object)null || (Object)(object)container == (Object)null)
			{
				return false;
			}
			ZNetView containerView = GetContainerView(container);
			if ((Object)(object)containerView == (Object)null || !containerView.IsValid() || containerView.GetZDO() == null)
			{
				return false;
			}
			int instanceID = ((Object)containerView).GetInstanceID();
			if (!RuleCaches.TryGetValue(instanceID, out var value))
			{
				value = RefreshCache(container, containerView);
			}
			if (value == null || !value.HasAnyRule)
			{
				return false;
			}
			accepted = value.Accepted;
			return true;
		}

		internal static void Bind()
		{
			_assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "ChestCategories", StringComparison.OrdinalIgnoreCase));
			if (_assembly == null)
			{
				Plugin.Log.LogInfo((object)"[BO] Better Organisation not loaded; Innangard chest rules are untouched.");
				return;
			}
			_storeType = _assembly.GetType("ChestCategories.Chests.ChestConfigStore", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "ChestConfigStore");
			_configType = _assembly.GetType("ChestCategories.Chests.ChestConfig", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "ChestConfig");
			_categoryServiceType = _assembly.GetType("ChestCategories.Categories.CategoryService", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "CategoryService");
			_categoryType = _assembly.GetType("ChestCategories.Categories.Category", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "Category");
			_groupType = _assembly.GetType("ChestCategories.Categories.CategoryGroup", throwOnError: false) ?? SafeTypes(_assembly).FirstOrDefault((Type t) => t.Name == "CategoryGroup");
			_getConfig = _storeType?.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Get" && m.IsStatic && m.GetParameters().Length == 1 && typeof(Container).IsAssignableFrom(m.GetParameters()[0].ParameterType));
			_getCategory = _categoryServiceType?.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "GetCategory" && m.IsStatic && m.GetParameters().Length == 1 && typeof(ItemData).IsAssignableFrom(m.GetParameters()[0].ParameterType));
			if (_configType != null)
			{
				_hasLinks = _configType.GetProperty("HasLinks", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				_links = _configType.GetProperty("Links", BindingFlags.Instance | BindingFlags.S