Decompiled source of InnangardBetrValheimDrawerBridge v1.0.1

plugins/InnangardBetrValheimDrawerBridge.dll

Decompiled 5 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			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", "1.0.1")]
	[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 = "1.0.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> DetailedLogging;

		internal static ConfigEntry<bool> DeliverBeforeCollectingMore;

		internal static ConfigEntry<bool> SortAfterStall;

		internal static ConfigEntry<bool> ReduceClaimRenewals;

		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 void Awake()
		{
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: 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.");
			try
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Innangard BetrValheim Drawer Bridge 1.0.1 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);
					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 1.0.1 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 (_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 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) ? 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 Bridge
	{
		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 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 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)));
			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)));
			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_02ca;
				}
			}
			else
			{
				num = 0;
			}
			Plugin.Log.LogError((object)"[Bind] One or more required BetrValheim members are missing.");
			goto IL_02ca;
			IL_02ca:
			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] No Innangard methods are patched by this build.");
			}
			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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			if (DrawerType == null)
			{
				return;
			}
			Object[] array;
			try
			{
				array = Resources.FindObjectsOfTypeAll(DrawerType);
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[Scan] FindObjectsOfTypeAll failed: " + Root(e)));
				return;
			}
			HashSet<object> hashSet = new HashSet<object>(ReferenceComparer.Instance);
			HashSet<int> hashSet2 = new HashSet<int>();
			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() && val2.gameObject.activeInHierarchy)
					{
						object obj = val;
						ZNetView nview = GetNview(obj);
						if ((Object)(object)nview == (Object)null || !nview.IsValid() || nview.GetZDO() == null)
						{
							num3++;
							continue;
						}
						GameObject val3 = PhysicalRoot(nview);
						if ((Object)(object)val3 == (Object)null || !val3.activeInHierarchy)
						{
							num2++;
							continue;
						}
						hashSet.Add(obj);
						int instanceID = ((Object)nview).GetInstanceID();
						hashSet2.Add(instanceID);
						ByViewId[instanceID] = obj;
						if (!ByReal.TryGetValue(obj, out var value) || (Object)(object)value == (Object)null || (Object)(object)((Component)value).gameObject != (Object)(object)val3)
						{
							if ((Object)(object)value != (Object)null)
							{
								DrawerComponent.AllDrawers.Remove(value);
							}
							value = val3.GetComponent<DrawerComponent>();
							if ((Object)(object)value == (Object)null)
							{
								value = val3.AddComponent<DrawerComponent>();
							}
							ByReal[obj] = value;
						}
						value.Bind(obj, nview);
						value.Sync();
						if (!DrawerComponent.AllDrawers.Contains(value))
						{
							DrawerComponent.AllDrawers.Add(value);
						}
						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='{value.CurrentPrefab}' " + $"count={GetCount(obj)} free={GetFreeSpace(obj)} zdo={nview.GetZDO()?.m_uid}"));
						}
						continue;
					}
				}
				num2++;
			}
			KeyValuePair<object, DrawerComponent>[] array3 = ByReal.ToArray();
			for (int i = 0; i < array3.Length; i++)
			{
				KeyValuePair<object, DrawerComponent> keyValuePair = array3[i];
				if (!hashSet.Contains(keyValuePair.Key) || !((Object)(object)keyValuePair.Value != (Object)null))
				{
					if ((Object)(object)keyValuePair.Value != (Object)null)
					{
						DrawerComponent.AllDrawers.Remove(keyValuePair.Value);
					}
					ByReal.Remove(keyValuePair.Key);
				}
			}
			int[] array4 = ByViewId.Keys.ToArray();
			foreach (int num6 in array4)
			{
				if (!hashSet2.Contains(num6))
				{
					ByViewId.Remove(num6);
				}
			}
			DrawerComponent.AllDrawers.RemoveAll((DrawerComponent d) => (Object)(object)d == (Object)null || (Object)(object)d.View == (Object)null || !d.View.IsValid() || (Object)(object)((Component)d).gameObject == (Object)null || !((Component)d).gameObject.activeInHierarchy);
			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} active={count} 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();
			_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;
		}

		internal static bool TryGetReal(ZNetView view, out object real)
		{
			real = null;
			if ((Object)(object)view == (Object)null)
			{
				return false;
			}
			if (!ByViewId.TryGetValue(((Object)view).GetInstanceID(), out real) || real == null)
			{
				return false;
			}
			ZNetView nview = GetNview(real);
			if ((Object)(object)nview == (Object)null || (Object)(object)nview != (Object)(object)view || !nview.IsValid())
			{
				real = null;
				return false;
			}
			return true;
		}

		internal static List<ZNetView> AllViews()
		{
			List<ZNetView> list = new List<ZNetView>();
			DrawerComponent[] array = DrawerComponent.AllDrawers.ToArray();
			foreach (DrawerComponent drawerComponent in array)
			{
				if (!((Object)(object)drawerComponent == (Object)null) && !((Object)(object)drawerComponent.View == (Object)null) && drawerComponent.View.IsValid() && !((Object)(object)((Component)drawerComponent).gameObject == (Object)null) && ((Component)drawerComponent).gameObject.activeInHierarchy)
				{
					drawerComponent.Sync();
					list.Add(drawerComponent.View);
				}
			}
			return list;
		}

		internal static void SyncOne(object real)
		{
			if (real == null)
			{
				return;
			}
			if (ByReal.TryGetValue(real, out var value) && (Object)(object)value != (Object)null)
			{
				value.Sync();
				return;
			}
			ZNetView nview = GetNview(real);
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return;
			}
			GameObject val = PhysicalRoot(nview);
			if (!((Object)(object)val == (Object)null))
			{
				value = val.GetComponent<DrawerComponent>();
				if ((Object)(object)value == (Object)null)
				{
					value = val.AddComponent<DrawerComponent>();
				}
				value.Bind(real, nview);
				value.Sync();
				ByReal[real] = value;
				ByViewId[((Object)nview).GetInstanceID()] = real;
				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;
		}

		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 = DepositMethod.Invoke(real, new object[2] { text, amount });
			int num2 = ((obj != null) ? Convert.ToInt32(obj) : 0);
			if (num2 != amount)
			{
				if (num2 > 0)
				{
					try
					{
						WithdrawMethod.Invoke(real, new object[1] { 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 = WithdrawMethod.Invoke(real, new object[1] { amount });
			int num2 = ((obj != null) ? Convert.ToInt32(obj) : 0);
			if (num2 != amount)
			{
				if (num2 > 0 && !string.IsNullOrEmpty(text))
				{
					try
					{
						DepositMethod.Invoke(real, new object[2] { 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;
			}
			__result = ((string.IsNullOrEmpty(__1) || !accepted.Contains(__1)) ? int.MaxValue : 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.Static | BindingFlags.Public | BindingFlags.NonPublic);
				_includeItems = _configType.GetProperty("IncludeItems", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				_excludeItems = _configType.GetProperty("ExcludeItems", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				_acceptsEverything = _configType.GetProperty("AcceptsEverything", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				_acceptsStash = _configType.GetProperty("AcceptsStash", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				_serializeConfig = _configType.GetMethod("Serialize", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (_categoryType != null)
				{
					_isLinkedCategory = _configType.GetMethod("IsLinked", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { _categoryType }, null);
				}
				if (_groupType != null)
				{
					_isLinkedGroup = _configType.GetMethod("IsLinked", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { _groupType }, null);
				}
			}
			_categoryGroup = _categoryType?.GetProperty("Group", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_categoryId = _categoryType?.GetProperty("Id", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_groupId = _groupType?.GetProperty("Id", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_bound = _getConfig != null && _getCategory != null && _configType != null && _includeItems != null && _excludeItems != null;
			if (!_bound)
			{
				Plugin.Log.LogWarning((object)"[BO] Better Organisation 1.0.0 was found but its chest-rule API could not be bound; Innangard chest rules are untouched.");
				return;
			}
			Plugin.Log.LogInfo((object)"[BO] Bound Better Organisation ChestConfigStore.Get(Container) and CategoryService.GetCategory(ItemData).");
			Plugin.Log.LogInfo((object)"[BO] BO categories and per-item include/exclude rules override Innangard chest destinations whenever that BO chest has rules.");
		}

		internal static void Refresh(bool forceSummary)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			if (!_installed || !_bound || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			Container[] array;
			try
			{
				array = Resources.FindObjectsOfTypeAll<Container>();
			}
			catch (Exception e)
			{
				if (!_reportedUnavailable)
				{
					_reportedUnavailable = true;
					Plugin.Log.LogWarning((object)("[BO] container scan failed: " + Bridge.Root(e)));
				}
				return;
			}
			int num = 0;
			int num2 = 0;
			Container[] array2 = array;
			foreach (Container val in array2)
			{
				if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null)
				{
					continue;
				}
				Scene scene = ((Component)val).gameObject.scene;
				if (!((Scene)(ref scene)).IsValid() || !((Component)val).gameObject.activeInHierarchy)
				{
					continue;
				}
				ZNetView containerView = GetContainerView(val);
				if (!((Object)(object)containerView == (Object)null) && containerView.IsValid() && containerView.GetZDO() != null)
				{
					RuleCache ruleCache = RefreshCache(val, containerView);
					if (ruleCache != null && ruleCache.HasAnyRule)
					{
						num2++;
					}
					if (containerView.IsOwner() && Restore(containerView.GetZDO()))
					{
						num++;
					}
				}
			}
			HashSet<int> liveIds = new HashSet<int>(from view in array.Select(GetContainerView)
				where (Object)(object)view != (Object)null
				select ((Object)view).GetInstanceID());
			int[] array3 = RuleCaches.Keys.Where((int id) => !liveIds.Contains(id)).ToArray();
			foreach (int key in array3)
			{
				RuleCaches.Remove(key);
			}
			if (forceSummary || num2 != _lastActive || num > 0)
			{
				_lastActive = num2;
				Plugin.Log.LogInfo((object)$"[BO] localRuleChests={num2} oldMirrorsRestored={num}");
			}
		}

		private static RuleCache RefreshCache(Container container, ZNetView view)
		{
			object obj;
			try
			{
				obj = _getConfig.Invoke(null, new object[1] { container });
			}
			catch
			{
				return null;
			}
			if (obj == null)
			{
				return null;
			}
			RuleSnapshot ruleSnapshot = ReadRules(obj);
			string text = ConfigSignature(obj, ruleSnapshot);
			int instanceID = ((Object)view).GetInstanceID();
			if (RuleCaches.TryGetValue(instanceID, out var value) && value.Signature == text && value.HasAnyRule == ruleSnapshot.HasAnyRule)
			{
				return value;
			}
			value = new RuleCache
			{
				Signature = text,
				HasAnyRule = ruleSnapshot.HasAnyRule,
				Accepted = (ruleSnapshot.HasAnyRule ? BuildAcceptedSharedNames(obj, ruleSnapshot) : new HashSet<string>(StringComparer.Ordinal))
			};
			RuleCaches[instanceID] = value;
			return value;
		}

		internal static void RestoreAll()
		{
			if (!_bound)
			{
				return;
			}
			RuleCaches.Clear();
			try
			{
				Container[] array = Resources.FindObjectsOfTypeAll<Container>();
				for (int i = 0; i < array.Length; i++)
				{
					ZNetView containerView = GetContainerView(array[i]);
					if (!((Object)(object)containerView == (Object)null) && containerView.IsValid())
					{
						ZDO zDO = containerView.GetZDO();
						if (zDO != null && containerView.IsOwner())
						{
							Restore(zDO);
						}
					}
				}
			}
			catch
			{
			}
		}

		private static ZNetView GetContainerView(Container container)
		{
			if (!((Object)(object)container != (Object)null))
			{
				return null;
			}
			return ((Component)container).GetComponent<ZNetView>();
		}

		private static RuleSnapshot ReadRules(object config)
		{
			RuleSnapshot ruleSnapshot = new RuleSnapshot();
			try
			{
				ruleSnapshot.HasLinks = ReadBool(_hasLinks, config, fallback: false) || CollectionHasAny(_links?.GetValue(config, null));
				ruleSnapshot.AcceptsEverything = ReadBool(_acceptsEverything, config, fallback: false);
				ruleSnapshot.AcceptsStash = ReadBool(_acceptsStash, config, fallback: true);
				CopyStrings(_includeItems?.GetValue(config, null), ruleSnapshot.Include);
				CopyStrings(_excludeItems?.GetValue(config, null), ruleSnapshot.Exclude);
			}
			catch (Exception e)
			{
				Plugin.Log.LogWarning((object)("[BO] rule read failed: " + Bridge.Root(e)));
			}
			return ruleSnapshot;
		}

		private static string ConfigSignature(object config, RuleSnapshot rules)
		{
			try
			{
				if (_serializeConfig != null)
				{
					object obj = _serializeConfig.Invoke(config, null);
					if (obj != null)
					{
						return obj.ToString() + "#odb=" + (ObjectDB.instance?.m_items?.Count).GetValueOrDefault();
					}
				}
			}
			catch
			{
			}
			return string.Join(";", rules.HasLinks ? "L1" : "L0", rules.AcceptsEverything ? "A1" : "A0", rules.AcceptsStash ? "S1" : "S0", "I=" + string.Join(",", rules.Include.OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase)), "E=" + string.Join(",", rules.Exclude.OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase)), "ODB=" + (ObjectDB.instance?.m_items?.Count).GetValueOrDefault());
		}

		private static HashSet<string> BuildAcceptedSharedNames(object config, RuleSnapshot rules)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			if (!rules.AcceptsStash)
			{
				return hashSet;
			}
			foreach (GameObject item in ObjectDB.instance.m_items)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				ItemData val = item.GetComponent<ItemDrop>()?.m_itemData;
				if (val?.m_shared == null)
				{
					continue;
				}
				string name = ((Object)item).name;
				string name2 = val.m_shared.m_name;
				if (!string.IsNullOrEmpty(name2) && !rules.Exclude.Contains(name))
				{
					if (rules.Include.Contains(name))
					{
						hashSet.Add(name2);
					}
					else if (rules.AcceptsEverything || MatchesLinkedCategory(config, val))
					{
						hashSet.Add(name2);
					}
				}
			}
			return hashSet;
		}

		private static bool MatchesLinkedCategory(object config, ItemData item)
		{
			if (config == null || item == null || _getCategory == null)
			{
				return false;
			}
			object obj;
			try
			{
				obj = _getCategory.Invoke(null, new object[1] { item });
			}
			catch
			{
				return false;
			}
			if (obj == null)
			{
				return false;
			}
			try
			{
				if (_isLinkedCategory != null && Convert.ToBoolean(_isLinkedCategory.Invoke(config, new object[1] { obj })))
				{
					return true;
				}
				object obj3 = _categoryGroup?.GetValue(obj, null);
				if (obj3 != null && _isLinkedGroup != null && Convert.ToBoolean(_isLinkedGroup.Invoke(config, new object[1] { obj3 })))
				{
					return true;
				}
				object obj4 = _links?.GetValue(config, null);
				if (obj4 != null)
				{
					string wanted = _categoryId?.GetValue(obj, null) as string;
					string wanted2 = ((obj3 != null) ? (_groupId?.GetValue(obj3, null) as string) : null);
					if (ContainsString(obj4, "*") || ContainsString(obj4, wanted) || ContainsString(obj4, wanted2))
					{
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static bool Restore(ZDO zdo)
		{
			if (zdo == null || !zdo.GetBool("pho_bo_inn_mirror", false))
			{
				return false;
			}
			SetBoolIfDifferent(zdo, "inn_sort_configured", zdo.GetBool("pho_bo_inn_b_cfg", false));
			SetIntIfDifferent(zdo, "inn_sort_kinds", zdo.GetInt("pho_bo_inn_b_kinds", 0));
			SetStringIfDifferent(zdo, "inn_sort_items", zdo.GetString("pho_bo_inn_b_items", string.Empty));
			SetIntIfDifferent(zdo, "inn_chest_sort_kind", zdo.GetInt("pho_bo_inn_b_legacy", 0));
			SetBoolIfDifferent(zdo, "pho_bo_inn_mirror", value: false);
			return true;
		}

		private static void SetBoolIfDifferent(ZDO zdo, string key, bool value)
		{
			if (zdo.GetBool(key, !value) != value)
			{
				zdo.Set(key, value);
			}
		}

		private static void SetIntIfDifferent(ZDO zdo, string key, int value)
		{
			if (zdo.GetInt(key, int.MinValue) != value)
			{
				zdo.Set(key, value);
			}
		}

		private static void SetStringIfDifferent(ZDO zdo, string key, string value)
		{
			if (zdo.GetString(key, (string)null) != value)
			{
				zdo.Set(key, value);
			}
		}

		private static bool ReadBool(PropertyInfo p, object instance, bool fallback)
		{
			try
			{
				object obj = p?.GetValue(instance, null);
				return (obj == null) ? fallback : Convert.ToBoolean(obj);
			}
			catch
			{
				return fallback;
			}
		}

		private static void CopyStrings(object collection, HashSet<string> into)
		{
			if (collection == null || !(collection is IEnumerable enumerable))
			{
				return;
			}
			foreach (object item in enumerable)
			{
				string text = item as string;
				if (!string.IsNullOrEmpty(text))
				{
					into.Add(text);
				}
			}
		}

		private static bool CollectionHasAny(object collection)
		{
			if (collection == null)
			{
				return false;
			}
			if (!(collection is IEnumerable enumerable))
			{
				return false;
			}
			IEnumerator enumerator = enumerable.GetEnumerator();
			try
			{
				return enumerator.MoveNext();
			}
			finally
			{
				IDisposable disposable = enumerator as IDisposable;
				if (disposable != null)
				{
					disposable.Dispose();
				}
			}
		}

		private static bool ContainsString(object collection, string wanted)
		{
			if (collection == null || string.IsNullOrEmpty(wanted))
			{
				return false;
			}
			if (!(collection is IEnumerable enumerable))
			{
				return false;
			}
			foreach (object item in enumerable)
			{
				if (string.Equals(item as string, wanted, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return false;
		}

		private static Type[] SafeTypes(Assembly a)
		{
			try
			{
				return a.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				return ex.Types.Where((Type t) => t != null).ToArray();
			}
			catch
			{
				return Array.Empty<Type>();
			}
		}
	}
}