Decompiled source of SmarterContainers v1.9.0

SmartContainers.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("SmartContainers")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SmartContainers")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("6b695573-f20c-49d7-99c3-26f3bd0c12c1")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 SmartContainers
{
	internal sealed class ConfigurationManagerAttributes
	{
		public bool? ShowRangeAsPercent;

		public Action<ConfigEntryBase> CustomDrawer;

		public bool? Browsable;

		public string Category;

		public object DefaultValue;

		public bool? HideDefaultButton;

		public bool? HideSettingName;

		public string Description;

		public string DispName;

		public int? Order;

		public bool? ReadOnly;

		public bool? IsAdvanced;

		public Func<object, string> ObjToStr;

		public Func<string, object> StrToObj;
	}
	public static class ContainersTracker
	{
		public static ICollection<Container> containerList = new List<Container>();

		public static bool isRearrangingItem = false;

		public static bool teleportingInProgress = false;

		public static List<Container> GetNearbyContainers(Vector3 center, bool all = false)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			List<Container> list = new List<Container>();
			foreach (Container container in containerList)
			{
				int num = (all ? (Mod.range.Value * 2) : Mod.range.Value);
				if ((Object)(object)container != (Object)null && (Object)(object)((Component)container).transform != (Object)null && container.GetInventory() != null && (Object)(object)((Component)container).GetComponentInParent<Piece>() != (Object)null)
				{
					float num2 = Vector3.Distance(center, ((Component)container).transform.position);
					if ((num <= 0 || num2 < (float)num) && container.CheckAccess(Player.m_localPlayer.GetPlayerID()))
					{
						list.Add(container);
					}
				}
			}
			return list;
		}

		public static bool IsReliableRoutingTarget(Container container)
		{
			return GetRoutingExclusionReason(container) == null;
		}

		internal static bool ShouldTrackRoutingTarget(Container container)
		{
			if (Mod.unsafeTargetEnabled || IsReliableRoutingTarget(container))
			{
				return true;
			}
			LogExcludedTarget(container);
			return false;
		}

		public static void RemoveUnsafeRoutingTargets()
		{
			if (Mod.unsafeTargetEnabled)
			{
				return;
			}
			foreach (Container item in containerList.ToList())
			{
				if ((Object)(object)item != (Object)null && !IsReliableRoutingTarget(item))
				{
					LogExcludedTarget(item);
					containerList.Remove(item);
				}
			}
		}

		private static void LogExcludedTarget(Container container)
		{
			Mod.log.LogWarning((object)("[SmartContainers excluded target] reason=" + GetRoutingExclusionReason(container) + " name=" + ((Object)container).name + " componentTypes=" + DescribeComponentHierarchy(container)));
		}

		private static string GetRoutingExclusionReason(Container container)
		{
			if ((Object)(object)container.m_rootObjectOverride != (Object)null)
			{
				return "uses m_rootObjectOverride";
			}
			if ((Object)(object)((Component)container).GetComponentInParent<Ship>() != (Object)null)
			{
				return "has Ship host";
			}
			if ((Object)(object)container.m_wagon != (Object)null || (Object)(object)((Component)container).GetComponentInParent<Vagon>() != (Object)null)
			{
				return "has Vagon host";
			}
			if ((Object)(object)((Component)container).GetComponentInParent<Incinerator>() != (Object)null)
			{
				return "has Incinerator host";
			}
			if ((Object)(object)((Component)container).GetComponentInParent<TombStone>() != (Object)null)
			{
				return "has TombStone host";
			}
			if ((Object)(object)((Component)container).GetComponentInParent<Corpse>() != (Object)null)
			{
				return "has Corpse host";
			}
			return null;
		}

		private static string DescribeComponentHierarchy(Container container)
		{
			List<string> list = new List<string>();
			Transform val = ((Component)container).transform;
			while ((Object)(object)val != (Object)null)
			{
				string[] value = (from component in ((Component)val).GetComponents<Component>()
					where (Object)(object)component != (Object)null && !(component is Transform)
					select ((object)component).GetType().FullName).Distinct().ToArray();
				list.Add(((Object)val).name + ":[" + string.Join(",", value) + "]");
				val = val.parent;
			}
			return string.Join(" <- ", list);
		}

		public static void Init()
		{
			Container[] array = Object.FindObjectsOfType<Container>();
			Mod.log.LogDebug((object)$"tracking {array.Length} discovered containers.");
			Container[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				TryTrack(array2[i]);
			}
		}

		internal static bool TryTrack(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return false;
			}
			if (((Object)container).name.StartsWith("Treasure"))
			{
				return false;
			}
			if (container.GetInventory() == null)
			{
				return false;
			}
			if (!HasNetworkIdentityForTracking(container))
			{
				return false;
			}
			if (!ShouldTrackRoutingTarget(container))
			{
				return false;
			}
			if (!containerList.Contains(container))
			{
				containerList.Add(container);
			}
			OwnerPut.Register(container);
			return true;
		}

		public static void CleanupContainersList()
		{
			Mod.log.LogDebug((object)$"cleanup containerList. size before: {containerList.Count}");
			foreach (Container item in containerList.ToList())
			{
				if (!((Object)(object)item != (Object)null) || !((Object)(object)((Component)item).transform != (Object)null) || item.GetInventory() == null)
				{
					containerList.Remove(item);
				}
			}
			Mod.log.LogDebug((object)$"cleanup containerList. size after: {containerList.Count}");
		}

		internal static bool HasNetworkIdentityForTracking(Container container)
		{
			if ((Object)(object)container?.m_nview == (Object)null || !container.m_nview.IsValid())
			{
				return false;
			}
			ZDO zDO = container.m_nview.GetZDO();
			if (zDO != null)
			{
				if (zDO.GetLong(StringExtensionMethods.GetStableHashCode("creator"), 0L) == 0L)
				{
					return zDO.GetOwner() != 0;
				}
				return true;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class Container_Awake_Patch
	{
		private static void Postfix(Container __instance)
		{
			if (Mod.modEnabled.Value)
			{
				ContainersTracker.TryTrack(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Container), "OnDestroyed")]
	internal static class Container_OnDestroyed_Patch
	{
		private static void Prefix(Container __instance)
		{
			if (Mod.modEnabled.Value)
			{
				ContainersTracker.containerList.Remove(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "OpenServer")]
	internal static class ZNet_OpenServer_Patch
	{
		private static void Postfix()
		{
			if (Mod.modEnabled.Value)
			{
				ContainersTracker.RemoveUnsafeRoutingTargets();
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "StackAll")]
	internal static class Container_OnStackAll_Patch
	{
		private static void Postfix(Inventory __instance, Inventory fromInventory, bool message)
		{
			if (Mod.unloadAllEnabled.Value && Mod.unloadAllInsteadStackBtn.Value)
			{
				InventoryGui instance = InventoryGui.instance;
				if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.m_playerGrid == (Object)null) && !((Object)(object)instance.m_containerGrid == (Object)null) && !((Character)Player.m_localPlayer).IsTeleporting())
				{
					UnloadItems.UnloadAllItems(((Humanoid)Player.m_localPlayer).GetInventory());
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateTeleport")]
	public static class PlayerUpdateTeleport_Patch_Cleanup_Containers
	{
		public static void Prefix(float dt)
		{
			if (Mod.modEnabled.Value)
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null && localPlayer.m_teleporting && !ContainersTracker.teleportingInProgress)
				{
					OwnerPut.RestoreAllPending();
					OwnershipHandoff.Clear();
					ContainersTracker.teleportingInProgress = true;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateTeleport")]
	public static class PlayerUpdateTeleport_Postfix
	{
		public static void Postfix(float dt)
		{
			if (Mod.modEnabled.Value)
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null && ContainersTracker.teleportingInProgress && !localPlayer.m_teleporting)
				{
					ContainersTracker.CleanupContainersList();
					ContainersTracker.teleportingInProgress = false;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData)
	})]
	public static class Inventory_Patch
	{
		internal enum DepositResult
		{
			Done,
			Pending,
			Failed
		}

		internal static bool LastMissInUse;

		public static bool Prefix(Inventory __instance, Inventory fromInventory, ItemData item)
		{
			if (BagStackBlocked(item))
			{
				return false;
			}
			if (!ShouldHandle(__instance, fromInventory, item))
			{
				return true;
			}
			if (__instance.HaveItem(item.m_shared.m_name, true))
			{
				return true;
			}
			return !TryRouteFromPlayer(fromInventory, __instance, item);
		}

		public static void Postfix(Inventory __instance, Inventory fromInventory, ItemData item)
		{
			if (!BagStackBlocked(item) && ShouldHandle(__instance, fromInventory, item) && fromInventory.ContainsItem(item))
			{
				TryRouteFromPlayer(fromInventory, __instance, item);
			}
		}

		private static bool ShouldHandle(Inventory dest, Inventory from, ItemData item)
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			if (!Mod.modEnabled.Value)
			{
				return false;
			}
			if (dest == null || from == null || item == null)
			{
				return false;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null || ((Character)Player.m_localPlayer).IsTeleporting())
			{
				return false;
			}
			if (((object)dest).Equals((object?)((Humanoid)Player.m_localPlayer).GetInventory()))
			{
				return false;
			}
			if (from != ((Humanoid)Player.m_localPlayer).GetInventory())
			{
				return false;
			}
			if (Mod.onlyStackableItems.Value && item.m_shared.m_maxStackSize == 1)
			{
				return false;
			}
			if (ContainersTracker.isRearrangingItem)
			{
				return false;
			}
			if (Input.GetKey((KeyCode)108))
			{
				Console.instance.Print("SmartContainers tracked item [" + item.m_shared.m_name + "]");
			}
			KeyboardShortcut value = Mod.keyModifier.Value;
			if (!((KeyboardShortcut)(ref value)).IsPressed())
			{
				value = Mod.groupingKeyModifier.Value;
				if (!((KeyboardShortcut)(ref value)).IsPressed() && (!Mod.gamepadKey2.Value.MainPressed() || !ZInput.GetButton(Mod.gamepadKey1.Value)))
				{
					return false;
				}
			}
			if ((Object)(object)InventoryGui.instance == (Object)null || !InventoryGui.instance.IsContainerOpen())
			{
				return false;
			}
			if (InventoryGui.instance.m_currentContainer.GetInventory() != dest)
			{
				return false;
			}
			if (!Mod.unsafeTargetEnabled && !Mod.unsafeSourceEnabled.Value && !ContainersTracker.IsReliableRoutingTarget(InventoryGui.instance.m_currentContainer))
			{
				return false;
			}
			return true;
		}

		private static bool TryRouteFromPlayer(Inventory fromInventory, Inventory dest, ItemData item)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			ContainersTracker.isRearrangingItem = true;
			try
			{
				if (TryHandleDrawers(item))
				{
					fromInventory.RemoveItem(item);
					dest.Changed(false, false);
					fromInventory.Changed(false, false);
					Mod.PlayEffect(Mod.audioFeedbackEnabled.Value, "sfx_lootspawn", ((Character)Player.m_localPlayer).GetCenterPoint());
					return true;
				}
				Container val = FindRouteTarget(item);
				if ((Object)(object)val == (Object)null)
				{
					OwnerPut.DebugCenter("SC route failed: no chest");
					return false;
				}
				if (Mod.concurrencyWorkaround.Value.UsesRpc())
				{
					int num = RouteNotice.Open();
					DepositResult num2 = Deposit(val, item, fromInventory, allowCurrent: false, playAudio: true, num);
					if (num2 == DepositResult.Done)
					{
						RouteNotice.Moved(num);
					}
					RouteNotice.Close(num);
					if (num2 == DepositResult.Failed)
					{
						OwnerPut.DebugCenter("SC route failed: put");
						return false;
					}
					dest.Changed(false, false);
					return true;
				}
				if (!AddItemToContainer(item, val))
				{
					OwnerPut.DebugCenter("SC route failed: add");
					return false;
				}
				fromInventory.RemoveItem(item);
				dest.Changed(false, false);
				fromInventory.Changed(false, false);
				PlayRoutedFeedback(item, val, playAudio: true);
				return true;
			}
			finally
			{
				ContainersTracker.isRearrangingItem = false;
			}
		}

		internal static bool TryHandleDrawers(ItemData item)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			if (!Mod.drawersIntegrationEnabled.Value)
			{
				return false;
			}
			if ((Object)(object)item?.m_dropPrefab == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return false;
			}
			foreach (ItemDrawers_API.Drawer item2 in ItemDrawers_API.AllDrawersInRange(((Component)Player.m_localPlayer).transform.position, Mod.range.Value))
			{
				if (((Object)item.m_dropPrefab).name.Equals(item2.Prefab))
				{
					Console.instance.Print($"{item.m_stack}-of-{Localization.instance.Localize(item.m_shared.m_name)} routed because Drawer has same item stack");
					item2.Add(item.m_stack);
					if (Mod.hudMessageEnabled.Value && (Object)(object)MessageHud.instance != (Object)null)
					{
						MessageHud.instance.QueueUnlockMsg(item.GetIcon(), item.m_shared.m_name, Mod.hudMessageText.Value);
					}
					Mod.PlayEffect(Mod.effectFeedbackEnabled.Value, "vfx_Potion_health_medium", item2.Position);
					return true;
				}
			}
			return false;
		}

		public static Container FindRouteTarget(ItemData item, bool allowCurrent = false, bool forceGrouping = false)
		{
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03eb: Unknown result type (might be due to invalid IL or missing references)
			LastMissInUse = false;
			try
			{
				List<Container> nearbyRoutingCandidates = GetNearbyRoutingCandidates();
				if (nearbyRoutingCandidates == null)
				{
					return null;
				}
				Container openedContainer = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_currentContainer : null);
				foreach (Container item2 in nearbyRoutingCandidates)
				{
					Inventory inventory = item2.GetInventory();
					if (Accept(item2, inventory != null && (allowCurrent || (Object)(object)openedContainer == (Object)null || ((object)openedContainer).GetHashCode() != ((object)item2).GetHashCode()) && inventory.HaveItem(item.m_shared.m_name, true)))
					{
						Console.instance.Print(Localization.instance.Localize(item.m_shared.m_name) + " routed because target container has same item stack");
						return item2;
					}
				}
				KeyboardShortcut value;
				if (Mod.groupingEnabled.Value)
				{
					if (!forceGrouping)
					{
						value = Mod.groupingKeyModifier.Value;
						if (!((KeyboardShortcut)(ref value)).IsPressed() && !ZInput.IsGamepadActive())
						{
							goto IL_03d2;
						}
					}
					List<Tuple<string, Container>> list = new List<Tuple<string, Container>>();
					foreach (Container item3 in nearbyRoutingCandidates)
					{
						string text = ItemGroups.FindMatchingUserGroup(item3, item);
						if (text == null)
						{
							continue;
						}
						if (!CanRouteTo(item3, item, openedContainer, allowCurrent))
						{
							if (InUseWithoutMod(item3))
							{
								LastMissInUse = true;
							}
						}
						else
						{
							list.Add(Tuple.Create<string, Container>(text, item3));
						}
					}
					if (list.Any())
					{
						Tuple<string, Container> tuple = list.OrderByDescending((Tuple<string, Container> t) => t.Item1).First();
						if (list.Count > 1)
						{
							Mod.log.LogWarning((object)("Ambiguous item-groups [" + string.Join(",", list) + "] found for " + ((Object)item.m_dropPrefab).name + " item. Using " + tuple.Item1));
						}
						Console.instance.Print(Localization.instance.Localize(item.m_shared.m_name) + " routed because target container has item from group " + tuple.Item1 + " : [" + string.Join(",", ItemGroups.Groups[tuple.Item1]) + "]");
						return tuple.Item2;
					}
					foreach (Container item4 in nearbyRoutingCandidates)
					{
						if (Accept(item4, ItemGroups.ContainerHasSame_SystemGroupItems(item4, item)))
						{
							return item4;
						}
					}
					foreach (Container item5 in nearbyRoutingCandidates)
					{
						if (Accept(item5, ItemGroups.ContainerHasSame_NamePatternItems(item5, item)))
						{
							return item5;
						}
					}
					if (Mod.itemTypeGroupsEnabled.Value)
					{
						foreach (Container item6 in nearbyRoutingCandidates)
						{
							if (Accept(item6, ItemGroups.ContainerHasSameItemType(item6, item)))
							{
								return item6;
							}
						}
					}
					goto IL_03d2;
				}
				goto IL_0448;
				IL_0448:
				return null;
				IL_03d2:
				if (Mod.fuzzyGroupingEnabled.Value)
				{
					if (!forceGrouping)
					{
						value = Mod.groupingKeyModifier.Value;
						if (!((KeyboardShortcut)(ref value)).IsPressed() && !ZInput.IsGamepadActive())
						{
							goto IL_0448;
						}
					}
					foreach (Container item7 in nearbyRoutingCandidates)
					{
						if (Accept(item7, ItemGroups.ContainerHasSimilarGroupItems(item7, item)))
						{
							return item7;
						}
					}
				}
				goto IL_0448;
				bool Accept(Container candidate, bool matched)
				{
					if (!matched)
					{
						return false;
					}
					if (CanRouteTo(candidate, item, openedContainer, allowCurrent))
					{
						return true;
					}
					if (InUseWithoutMod(candidate))
					{
						LastMissInUse = true;
					}
					return false;
				}
			}
			catch (Exception ex)
			{
				Mod.log.LogError((object)("Failed to rearrange container items: " + ex));
			}
			return null;
		}

		internal static List<Container> GetNearbyRoutingCandidates()
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)InventoryGui.instance == (Object)null)
			{
				return null;
			}
			Container openedContainer = InventoryGui.instance.m_currentContainer;
			if ((Object)(object)openedContainer == (Object)null)
			{
				return null;
			}
			if (Mod.concurrencyWorkaround.Value.BlockIfAnyOtherOpened() && ContainersTracker.GetNearbyContainers(((Component)Player.m_localPlayer).transform.position, all: true).Any((Container c) => ((object)openedContainer).GetHashCode() != ((object)c).GetHashCode() && c.m_nview.GetZDO().GetInt("InUse", 0) == 1))
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, "Cant distribute items: other Player is using containers nearby.", 0, (Sprite)null, false);
				return null;
			}
			return ContainersTracker.GetNearbyContainers(((Component)Player.m_localPlayer).transform.position).Where(delegate(Container c)
			{
				if (((object)openedContainer).GetHashCode() == ((object)c).GetHashCode())
				{
					return true;
				}
				if (Mod.concurrencyWorkaround.Value.Ignore())
				{
					return true;
				}
				ZDO zDO = c.m_nview.GetZDO();
				if (zDO == null)
				{
					return false;
				}
				bool flag = zDO.GetInt("InUse", 0) == 1;
				if (Mod.concurrencyWorkaround.Value.UsesRpc())
				{
					if (zDO.GetOwner() == 0L)
					{
						return false;
					}
					if (flag)
					{
						return OwnerPut.PeerHasFlag(c);
					}
					return true;
				}
				return (Mod.concurrencyWorkaround.Value.SkipOpenedOthers() || Mod.concurrencyWorkaround.Value.BlockIfAnyOtherOpened()) && !flag;
			}).ToList();
		}

		private static bool CanRouteTo(Container target, ItemData item, Container opened, bool allowCurrent)
		{
			if ((Object)(object)target == (Object)null || target.GetInventory() == null)
			{
				return false;
			}
			if (Mod.concurrencyWorkaround.Value.UsesRpc() && !target.IsOwner())
			{
				ZDO val = (((Object)(object)target.m_nview != (Object)null) ? target.m_nview.GetZDO() : null);
				if (val == null || val.GetOwner() == 0L)
				{
					return false;
				}
				if (val.GetInt("InUse", 0) == 1 && !OwnerPut.PeerHasFlag(target))
				{
					return false;
				}
			}
			if (!target.GetInventory().CanAddItem(item, -1))
			{
				return false;
			}
			if (!allowCurrent && (Object)(object)opened != (Object)null && ((object)opened).GetHashCode() == ((object)target).GetHashCode())
			{
				return false;
			}
			return true;
		}

		private static bool InUseWithoutMod(Container target)
		{
			if (!Mod.concurrencyWorkaround.Value.UsesRpc() || (Object)(object)target == (Object)null || target.IsOwner())
			{
				return false;
			}
			ZDO val = (((Object)(object)target.m_nview != (Object)null) ? target.m_nview.GetZDO() : null);
			if (val == null || val.GetOwner() == 0L)
			{
				return false;
			}
			if (val.GetInt("InUse", 0) == 1)
			{
				return !OwnerPut.PeerHasFlag(target);
			}
			return false;
		}

		internal static DepositResult Deposit(Container target, ItemData item, Inventory from, bool allowCurrent, bool playAudio, int batch)
		{
			if (BagStackBlocked(item))
			{
				return DepositResult.Pending;
			}
			if (target.IsOwner())
			{
				if (!AddItemToContainer(item, target, allowCurrent))
				{
					return DepositResult.Failed;
				}
				from.RemoveItem(item);
				PlayRoutedFeedback(item, target, playAudio);
				return DepositResult.Done;
			}
			ZDO val = (((Object)(object)target.m_nview != (Object)null) ? target.m_nview.GetZDO() : null);
			if (val == null || val.GetOwner() == 0L)
			{
				return DepositResult.Failed;
			}
			if (val.GetInt("InUse", 0) == 1)
			{
				if (!OwnerPut.PeerHasFlag(target))
				{
					return DepositResult.Failed;
				}
				if (!OwnerPut.BeginRemotePut(target, item, from))
				{
					return DepositResult.Failed;
				}
				return DepositResult.Pending;
			}
			if (!OwnershipHandoff.Begin(target, item, from, playAudio, batch))
			{
				return DepositResult.Failed;
			}
			return DepositResult.Pending;
		}

		internal static bool BagStackBlocked(ItemData item)
		{
			if (item != null)
			{
				if (!OwnerPut.IsAwaitingLateOk(item))
				{
					return OwnershipHandoff.Holds(item);
				}
				return true;
			}
			return false;
		}

		internal static bool AddItemToContainer(ItemData item, Container targetContainer, bool allowCurrent = false)
		{
			if ((Object)(object)targetContainer == (Object)null || item == null)
			{
				return false;
			}
			if (Mod.concurrencyWorkaround.Value.UsesRpc() && !targetContainer.IsOwner())
			{
				return false;
			}
			Container val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_currentContainer : null);
			if (allowCurrent || (Object)(object)val == (Object)null || ((object)val).GetHashCode() != ((object)targetContainer).GetHashCode())
			{
				bool num = targetContainer.GetInventory().AddItem(item);
				if (num && (!Mod.concurrencyWorkaround.Value.UsesRpc() || !targetContainer.IsOwner()))
				{
					targetContainer.Save();
				}
				return num;
			}
			return false;
		}

		internal static void PlayRoutedFeedback(ItemData item, Container targetContainer, bool playAudio)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if (item != null)
			{
				if (playAudio && (Object)(object)Player.m_localPlayer != (Object)null)
				{
					Mod.PlayEffect(Mod.audioFeedbackEnabled.Value, "sfx_lootspawn", ((Character)Player.m_localPlayer).GetCenterPoint());
				}
				Container val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_currentContainer : null);
				if (Mod.hudMessageEnabled.Value && (Object)(object)MessageHud.instance != (Object)null && ((Object)(object)val == (Object)null || !((object)val).Equals((object?)targetContainer)))
				{
					MessageHud.instance.QueueUnlockMsg(item.GetIcon(), item.m_shared.m_name, Mod.hudMessageText.Value);
				}
				if ((Object)(object)targetContainer != (Object)null)
				{
					Mod.PlayEffect(Mod.effectFeedbackEnabled.Value, "vfx_Potion_health_medium", ((Component)targetContainer).transform.position);
				}
			}
		}
	}
	internal static class RouteNotice
	{
		private sealed class Batch
		{
			public int Moved;

			public int Busy;

			public int Pending;
		}

		private static int next;

		private static readonly Dictionary<int, Batch> OpenBatches = new Dictionary<int, Batch>();

		public static int Open()
		{
			int num = ++next;
			OpenBatches[num] = new Batch();
			return num;
		}

		public static void Moved(int id)
		{
			if (OpenBatches.TryGetValue(id, out var value))
			{
				value.Moved++;
			}
		}

		public static void Busy(int id)
		{
			if (OpenBatches.TryGetValue(id, out var value))
			{
				value.Busy++;
			}
		}

		public static void Pending(int id)
		{
			if (id != 0 && OpenBatches.TryGetValue(id, out var value))
			{
				value.Pending++;
			}
		}

		public static void Settle(int id, bool moved)
		{
			if (OpenBatches.TryGetValue(id, out var value))
			{
				if (value.Pending > 0)
				{
					value.Pending--;
				}
				if (moved)
				{
					value.Moved++;
				}
				else
				{
					value.Busy++;
				}
				Announce(id, value);
			}
		}

		public static void Drop(int id)
		{
			if (OpenBatches.TryGetValue(id, out var value))
			{
				if (value.Pending > 0)
				{
					value.Pending--;
				}
				Announce(id, value);
			}
		}

		public static void Close(int id)
		{
			if (OpenBatches.TryGetValue(id, out var value))
			{
				Announce(id, value);
			}
		}

		public static void Clear()
		{
			OpenBatches.Clear();
		}

		private static void Announce(int id, Batch batch)
		{
			if (batch.Pending <= 0)
			{
				OpenBatches.Remove(id);
				if (batch.Busy != 0 && !((Object)(object)Player.m_localPlayer == (Object)null))
				{
					((Character)Player.m_localPlayer).Message((MessageType)2, (batch.Moved == 0) ? "$msg_inuse" : "Some items were not moved", 0, (Sprite)null, false);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData),
		typeof(int),
		typeof(int),
		typeof(int)
	})]
	internal static class Inventory_MoveItemToSlot_Patch
	{
		private static bool Prefix(ItemData item)
		{
			return !Inventory_Patch.BagStackBlocked(item);
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "DropItem")]
	internal static class InventoryGrid_DropItem_Patch
	{
		private static bool Prefix(ItemData item)
		{
			return !Inventory_Patch.BagStackBlocked(item);
		}
	}
	[HarmonyPatch(typeof(Humanoid), "DropItem", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData),
		typeof(int)
	})]
	internal static class Humanoid_DropItem_Patch
	{
		private static bool Prefix(ItemData item)
		{
			return !Inventory_Patch.BagStackBlocked(item);
		}
	}
	public static class ItemDrawers_API
	{
		public class Drawer
		{
			private readonly ZNetView znv;

			public readonly string Prefab;

			public readonly Vector3 Position;

			public readonly int Amount;

			public Drawer(ZNetView znv)
			{
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Unknown result type (might be due to invalid IL or missing references)
				this.znv = znv;
				Amount = znv.m_zdo.GetInt("Amount", 0);
				Position = znv.m_zdo.GetPosition();
				Prefab = znv.m_zdo.GetString("Prefab", "");
			}

			public void Remove(int amount)
			{
				znv.ClaimOwnership();
				znv.InvokeRPC("ForceRemove", new object[1] { amount });
			}

			public void Withdraw(int amount)
			{
				znv.InvokeRPC("WithdrawItem_Request", new object[1] { amount });
			}

			public void Add(int amount)
			{
				znv.InvokeRPC("AddItem_Request", new object[2] { Prefab, amount });
			}
		}

		private static readonly bool _IsInstalled;

		private static readonly MethodInfo MI_GetAllDrawers;

		public static List<Drawer> AllDrawers
		{
			get
			{
				if (!_IsInstalled)
				{
					return new List<Drawer>();
				}
				return ((List<ZNetView>)MI_GetAllDrawers.Invoke(null, null)).Select((ZNetView znv) => new Drawer(znv)).ToList();
			}
		}

		public static List<Drawer> AllDrawersInRange(Vector3 pos, 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)
			if (!_IsInstalled)
			{
				return new List<Drawer>();
			}
			return (from znv in (List<ZNetView>)MI_GetAllDrawers.Invoke(null, null)
				where Vector3.Distance(((Component)znv).transform.position, pos) <= range
				select new Drawer(znv)).ToList();
		}

		static ItemDrawers_API()
		{
			Type type = Type.GetType("API.ClientSide, kg_ItemDrawers");
			if ((object)type == null)
			{
				_IsInstalled = false;
				return;
			}
			_IsInstalled = true;
			MI_GetAllDrawers = type.GetMethod("AllDrawers", BindingFlags.Static | BindingFlags.Public);
		}
	}
	public static class GuiFactory
	{
		public static void CreateMrgDialog(out GameObject dialogObj, string dlgUid, string label1, string label2, string label3, UnityAction onFirst, UnityAction onSecond, UnityAction onThird)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Expected O, but got Unknown
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Expected O, but got Unknown
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Expected O, but got Unknown
			//IL_02f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			//IL_0307: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			//IL_031d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0327: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_033d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			//IL_0358: Unknown result type (might be due to invalid IL or missing references)
			//IL_035d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0367: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_0389: Unknown result type (might be due to invalid IL or missing references)
			//IL_039e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_040c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0435: Unknown result type (might be due to invalid IL or missing references)
			//IL_0463: Unknown result type (might be due to invalid IL or missing references)
			//IL_0472: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0504: Unknown result type (might be due to invalid IL or missing references)
			//IL_0527: Unknown result type (might be due to invalid IL or missing references)
			//IL_0531: Expected O, but got Unknown
			//IL_0533: Unknown result type (might be due to invalid IL or missing references)
			//IL_0547: Expected O, but got Unknown
			//IL_0576: Unknown result type (might be due to invalid IL or missing references)
			//IL_0585: Unknown result type (might be due to invalid IL or missing references)
			//IL_0590: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_05da: Unknown result type (might be due to invalid IL or missing references)
			Transform val = ((Component)InventoryGui.instance).gameObject.transform.Find("root/SplitDialog");
			Transform val2 = Object.Instantiate<Transform>(val, val.parent);
			((Object)val2).name = dlgUid;
			Transform winWrap = val2.Find("win_bkg");
			((RectTransform)winWrap).SetSizeWithCurrentAnchors((Axis)0, 500f);
			dialogObj = ((Component)val2).gameObject;
			Object.Destroy((Object)(object)((Component)winWrap.Find("Icon_bkg")).gameObject);
			Object.Destroy((Object)(object)((Component)winWrap.Find("Slider")).gameObject);
			Object.Destroy((Object)(object)((Component)winWrap.Find("Text")).gameObject);
			Object.Destroy((Object)(object)((Component)winWrap.Find("amount")).gameObject);
			((Component)winWrap.Find("Button_cancel")).gameObject.AddComponent(typeof(UIGamePad));
			((Component)winWrap.Find("Button_ok")).gameObject.AddComponent(typeof(UIGamePad));
			Transform val3 = winWrap.Find("Button_cancel");
			Transform val4 = winWrap.Find("Button_ok");
			Transform val5 = Object.Instantiate<Transform>(val4, ((Component)winWrap).transform);
			((Object)val5).name = "Button_3";
			((TMP_Text)((Component)val3.Find("Text")).GetComponent<TextMeshProUGUI>()).text = label1;
			((TMP_Text)((Component)val4.Find("Text")).GetComponent<TextMeshProUGUI>()).text = label2;
			((TMP_Text)((Component)val5.Find("Text")).GetComponent<TextMeshProUGUI>()).text = label3;
			RectTransform val6 = (RectTransform)((Component)val3).transform;
			val6.pivot += new Vector2(0.5f, 0.7f);
			RectTransform val7 = (RectTransform)((Component)val4).transform;
			val7.pivot += new Vector2(0.57f, 0.7f);
			RectTransform val8 = (RectTransform)((Component)val5).transform;
			val8.pivot += new Vector2(-0.48f, 0.7f);
			((RectTransform)((Component)val3).transform).SetSizeWithCurrentAnchors((Axis)1, 40f);
			((RectTransform)((Component)val4).transform).SetSizeWithCurrentAnchors((Axis)1, 40f);
			((RectTransform)((Component)val5).transform).SetSizeWithCurrentAnchors((Axis)1, 40f);
			InitJoyKey((Transform)(RectTransform)val3, "JoyTabLeft", "(LB)");
			InitJoyKey((Transform)(RectTransform)val4, "JoyTabRight", "(RB)");
			InitJoyKey((Transform)(RectTransform)val5, "", "");
			Transform obj = Object.Instantiate<Transform>(((Component)InventoryGui.instance).gameObject.transform.Find("root/Texts/Texts_frame/TextArea"), winWrap);
			((Object)obj).name = "TextArea";
			((Component)obj).gameObject.SetActive(true);
			((Graphic)((Component)obj).GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.4f);
			RectTransform val9 = (RectTransform)obj;
			((Transform)val9).position = ((Transform)(RectTransform)((Component)val.Find("win_bkg/bkg")).transform).position;
			((Transform)val9).localScale = ((Transform)(RectTransform)((Component)val.Find("win_bkg/bkg")).transform).localScale;
			val9.sizeDelta = ((RectTransform)((Component)val.Find("win_bkg/bkg")).transform).sizeDelta;
			val9.SetSizeWithCurrentAnchors((Axis)1, 190f);
			val9.SetSizeWithCurrentAnchors((Axis)0, 515f);
			val9.pivot = new Vector2(0.51f, 0.33f);
			((RectTransform)obj.Find("ScrollArea/Content/Name")).SetSizeWithCurrentAnchors((Axis)0, 515f);
			((RectTransform)obj.Find("ScrollArea/Content/Description")).SetSizeWithCurrentAnchors((Axis)0, 515f);
			TextMeshProUGUI component = ((Component)obj.Find("ScrollArea/Content/Name")).GetComponent<TextMeshProUGUI>();
			TextMeshProUGUI component2 = ((Component)obj.Find("ScrollArea/Content/Description")).GetComponent<TextMeshProUGUI>();
			((TMP_Text)component).fontSize = 18f;
			((Graphic)component).color = new Color(0.79f, 0.75f, 0.28f, 1f);
			((TMP_Text)component2).fontSize = 18f;
			((Graphic)component2).color = new Color(0.8f, 0.8f, 0.8f, 1f);
			Transform val10 = CreateButton("Selector", "--select--", winWrap.Find("TextArea"), new Vector2(380f, 24f), new Vector2(-0.23f, 11.4f));
			GameObject val11 = Object.Instantiate<GameObject>(Find("RecipeList"), winWrap);
			((Object)val11).name = "SelectorDlg";
			((RectTransform)val11.transform).pivot = new Vector2(-0.55f, 1.7f);
			Transform val12 = val11.transform.Find("Recipes/ListRoot");
			for (int i = 0; i < val12.childCount; i++)
			{
				Object.Destroy((Object)(object)((Component)val12.GetChild(i)).gameObject);
			}
			((RectTransform)val11.transform).SetSizeWithCurrentAnchors((Axis)1, 150f);
			((UnityEvent)((Component)val10).GetComponent<Button>().onClick).AddListener((UnityAction)delegate
			{
				GameObject gameObject = ((Component)winWrap.Find("SelectorDlg")).gameObject;
				gameObject.SetActive(!gameObject.activeSelf);
			});
			InitJoyKey((Transform)(RectTransform)val10, "", "");
			Transform obj2 = Object.Instantiate<Transform>(((Component)InventoryGui.instance.m_takeAllButton).transform.Find("Text"), winWrap.Find("TextArea"));
			((RectTransform)obj2).pivot = new Vector2(-0.05f, -0.13f);
			((RectTransform)obj2).SetSizeWithCurrentAnchors((Axis)1, 24f);
			((RectTransform)obj2).SetSizeWithCurrentAnchors((Axis)0, 150f);
			((TMP_Text)((Component)obj2).GetComponent<TextMeshProUGUI>()).text = "Target group:";
			((RectTransform)((Component)winWrap).transform).pivot = new Vector2(-0.7f, 1f);
			((UnityEvent)((Component)val3).GetComponent<Button>().onClick).AddListener(onFirst);
			((UnityEvent)((Component)val4).GetComponent<Button>().onClick).AddListener(onSecond);
			((UnityEvent)((Component)val5).GetComponent<Button>().onClick).AddListener(onThird);
		}

		public static Transform AddContainerGuiButton(string btnObjName, string text, Vector2 size, Vector2 pos)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			return CreateButton(btnObjName, text, ((Component)InventoryGui.instance.m_takeAllButton).transform.parent, size, pos);
		}

		public static Transform CreateButton(string btnObjName, string text, Transform parent, Vector2 size, Vector2 pos)
		{
			//IL_0007: 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_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			_ = (RectTransform)parent.Find(btnObjName);
			Transform obj = Object.Instantiate<Transform>(((Component)InventoryGui.instance.m_takeAllButton).transform, parent);
			((Object)obj).name = btnObjName;
			RectTransform val = (RectTransform)((Component)obj).transform;
			val.SetSizeWithCurrentAnchors((Axis)0, size.x);
			val.SetSizeWithCurrentAnchors((Axis)1, size.y);
			RectTransform val2 = (RectTransform)((Component)val).transform.Find("Text");
			val2.SetSizeWithCurrentAnchors((Axis)0, size.x);
			val2.SetSizeWithCurrentAnchors((Axis)1, size.y);
			((TMP_Text)((Component)val2).GetComponent<TextMeshProUGUI>()).text = text;
			val.pivot = pos;
			return (Transform)val;
		}

		public static void InitJoyKey(Transform btnComponent, string joyKeyCode, string tooltip)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			UIGamePad component = ((Component)btnComponent).GetComponent<UIGamePad>();
			Transform val = Object.Instantiate<Transform>(((Component)btnComponent).transform.Find("Text"), ((Component)component).transform);
			((TMP_Text)((Component)val).GetComponent<TextMeshProUGUI>()).text = tooltip;
			((Graphic)((Component)val).GetComponent<TextMeshProUGUI>()).color = new Color(0.8f, 0.8f, 0.8f, 1f);
			Transform transform = ((Component)val).transform;
			transform.position += new Vector3(0f, 13f, 0f);
			if ((Object)(object)component.m_hint != (Object)null)
			{
				Object.Destroy((Object)(object)component.m_hint);
			}
			component.m_hint = ((Component)val).gameObject;
			component.m_zinputKey = joyKeyCode;
			((Behaviour)((Component)val).GetComponent<TextMeshProUGUI>()).enabled = ZInput.IsGamepadActive();
		}

		public static void FillResList(RectTransform parent, ICollection<string> names, ICollection<Object> m_resObjects, Action<string> cb)
		{
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			foreach (Object m_resObject in m_resObjects)
			{
				Object.Destroy(m_resObject);
			}
			m_resObjects.Clear();
			float num = 0f;
			GameObject val = Find("RecipeList/Recipes/RecipeElement");
			foreach (string groupName in names)
			{
				GameObject val2 = Object.Instantiate<GameObject>(val, ((Component)parent).transform);
				Object.Destroy((Object)(object)((Component)val2.transform.Find("icon")).gameObject);
				Object.Destroy((Object)(object)((Component)val2.transform.Find("Durability")).gameObject);
				Object.Destroy((Object)(object)((Component)val2.transform.Find("QualityLevel")).gameObject);
				val2.SetActive(true);
				((UnityEvent)val2.GetComponentInChildren<Button>().onClick).AddListener((UnityAction)delegate
				{
					cb(groupName);
				});
				Transform transform = val2.transform;
				((RectTransform)((transform is RectTransform) ? transform : null)).anchoredPosition = new Vector2(0f, num * -20f);
				((TMP_Text)val2.GetComponentInChildren<TextMeshProUGUI>()).text = groupName;
				m_resObjects.Add((Object)(object)val2);
				num += 1f;
			}
			Rect rect = parent.rect;
			parent.SetSizeWithCurrentAnchors((Axis)1, Mathf.Max(((Rect)(ref rect)).height, num * 20f));
		}

		private static GameObject Find(string name)
		{
			GameObject val = GameObject.Find("_GameMain/LoadingGUI/PixelFix/IngameGui/Inventory_screen/root/Crafting/" + name);
			if ((Object)(object)val == (Object)null)
			{
				Mod.log.LogWarning((object)("Failed to locate '" + name + "' GameObject - fallback to IngameGui(Clone) lookup.."));
				val = ((Component)GameObject.Find("IngameGui").transform.Find("Inventory_screen/root/Crafting/" + name)).gameObject;
			}
			return val;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Show")]
	public static class Gui_Patch
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static Func<Tuple<string, List<string>>, string> <>9__9_3;

			public static UnityAction <>9__9_0;

			public static Func<Tuple<string, List<string>>, string> <>9__9_4;

			public static UnityAction <>9__9_1;

			public static Func<Tuple<string, List<string>>, string> <>9__9_5;

			public static UnityAction <>9__9_2;

			public static Func<Tuple<string, List<string>>, string> <>9__14_0;

			public static Func<Tuple<string, List<string>>, IEnumerable<string>> <>9__14_1;

			public static Func<ItemData, bool> <>9__14_5;

			public static Func<ItemData, string> <>9__14_6;

			public static Func<Tuple<string, List<string>>, string> <>9__14_8;

			public static Func<Tuple<string, List<string>>, string> <>9__14_3;

			internal void <InitButtons>b__9_0()
			{
				if (containersUniqueItems != null && intersectedGroups != null)
				{
					ItemGroupUtils.ExtendGroup(SelectedTargetGroupId(), intersectedGroups.Map((Tuple<string, List<string>> _) => _.Item1).ToList(), containersUniqueItems);
				}
				groupsDialogObj.gameObject.SetActive(false);
			}

			internal string <InitButtons>b__9_3(Tuple<string, List<string>> _)
			{
				return _.Item1;
			}

			internal void <InitButtons>b__9_1()
			{
				if (containersUniqueItems != null)
				{
					ItemGroupUtils.ExtendAndMerge(SelectedTargetGroupId(), intersectedGroups.Map((Tuple<string, List<string>> _) => _.Item1).ToList(), containersUniqueItems);
				}
				groupsDialogObj.gameObject.SetActive(false);
			}

			internal string <InitButtons>b__9_4(Tuple<string, List<string>> _)
			{
				return _.Item1;
			}

			internal void <InitButtons>b__9_2()
			{
				if (containersUniqueItems != null && intersectedGroups != null)
				{
					ItemGroupUtils.MergeAllToSingleGroup(SelectedTargetGroupId(), intersectedGroups.Map((Tuple<string, List<string>> _) => _.Item1).ToList(), containersUniqueItems);
				}
				groupsDialogObj.gameObject.SetActive(false);
			}

			internal string <InitButtons>b__9_5(Tuple<string, List<string>> _)
			{
				return _.Item1;
			}

			internal string <InitMergePromptDlg>b__14_0(Tuple<string, List<string>> _)
			{
				return _.Item1;
			}

			internal IEnumerable<string> <InitMergePromptDlg>b__14_1(Tuple<string, List<string>> _)
			{
				return _.Item2;
			}

			internal bool <InitMergePromptDlg>b__14_5(ItemData i)
			{
				return intersectedGroups[0].Item2.Contains(i.SCName());
			}

			internal string <InitMergePromptDlg>b__14_6(ItemData _)
			{
				return Localization.instance.Localize(_.m_shared.m_name);
			}

			internal string <InitMergePromptDlg>b__14_8(Tuple<string, List<string>> _)
			{
				return _.Item1;
			}

			internal string <InitMergePromptDlg>b__14_3(Tuple<string, List<string>> _)
			{
				return _.Item1;
			}
		}

		public static bool initialized = false;

		private static Transform createGroupButton;

		private static Transform storeAllButton;

		private static TMP_Text routingDisabledLabel;

		public static GameObject groupsDialogObj;

		private static readonly ICollection<Object> groupSelectorItems = new List<Object>();

		private static List<Tuple<string, List<string>>> intersectedGroups;

		private static List<string> containersUniqueItems;

		private static void Postfix(InventoryGui __instance)
		{
			if (Mod.modEnabled.Value && !((Object)(object)__instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null) && __instance.IsContainerOpen())
			{
				if (!initialized)
				{
					InitButtons(__instance);
				}
				UpdateRoutingDisabledLabel(__instance);
			}
		}

		private static void InitButtons(InventoryGui __instance)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			//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_00b6: Expected O, but got Unknown
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			if (!Mod.modEnabled.Value)
			{
				return;
			}
			try
			{
				AddRoutingDisabledLabel(__instance);
				if ((Mod.groupingEnabled.Value || Mod.unloadAllEnabled.Value) && Mod.createGroupBtnEnabled.Value)
				{
					AddCreateGroupBtn(__instance);
					if (Mod.groupingEnabled.Value && Mod.mergePromptEnabled.Value)
					{
						object obj = <>c.<>9__9_0;
						if (obj == null)
						{
							UnityAction val = delegate
							{
								if (containersUniqueItems != null && intersectedGroups != null)
								{
									ItemGroupUtils.ExtendGroup(SelectedTargetGroupId(), intersectedGroups.Map((Tuple<string, List<string>> _) => _.Item1).ToList(), containersUniqueItems);
								}
								groupsDialogObj.gameObject.SetActive(false);
							};
							<>c.<>9__9_0 = val;
							obj = (object)val;
						}
						object obj2 = <>c.<>9__9_1;
						if (obj2 == null)
						{
							UnityAction val2 = delegate
							{
								if (containersUniqueItems != null)
								{
									ItemGroupUtils.ExtendAndMerge(SelectedTargetGroupId(), intersectedGroups.Map((Tuple<string, List<string>> _) => _.Item1).ToList(), containersUniqueItems);
								}
								groupsDialogObj.gameObject.SetActive(false);
							};
							<>c.<>9__9_1 = val2;
							obj2 = (object)val2;
						}
						object obj3 = <>c.<>9__9_2;
						if (obj3 == null)
						{
							UnityAction val3 = delegate
							{
								if (containersUniqueItems != null && intersectedGroups != null)
								{
									ItemGroupUtils.MergeAllToSingleGroup(SelectedTargetGroupId(), intersectedGroups.Map((Tuple<string, List<string>> _) => _.Item1).ToList(), containersUniqueItems);
								}
								groupsDialogObj.gameObject.SetActive(false);
							};
							<>c.<>9__9_2 = val3;
							obj3 = (object)val3;
						}
						GuiFactory.CreateMrgDialog(out groupsDialogObj, "mrgToGroupDialog", "Extend", "Extend&Merge", "Merge All", (UnityAction)obj, (UnityAction)obj2, (UnityAction)obj3);
					}
				}
				if (Mod.unloadAllEnabled.Value && !Mod.unloadAllInsteadStackBtn.Value)
				{
					AddStoreAllBtn(__instance);
				}
				initialized = true;
			}
			catch (Exception ex)
			{
				Mod.log.LogError((object)("Failed to properly patch GUI with additional buttons: " + ex));
				Clean();
			}
			finally
			{
				Mod.log.LogInfo((object)"Initialized UI extensions.");
			}
		}

		private static void AddRoutingDisabledLabel(InventoryGui inventoryGui)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)routingDisabledLabel != (Object)null) && !((Object)(object)inventoryGui.m_containerName == (Object)null))
			{
				routingDisabledLabel = Object.Instantiate<TMP_Text>(inventoryGui.m_containerName, (Transform)(object)inventoryGui.m_container);
				((Object)routingDisabledLabel).name = "smartRoutingDisabledLabel";
				routingDisabledLabel.text = "Smart Routing disabled";
				((Graphic)routingDisabledLabel).color = new Color(1f, 0.55f, 0.2f, 1f);
				routingDisabledLabel.fontSize = Mathf.Max(12f, inventoryGui.m_containerName.fontSize * 0.5f);
				((Graphic)routingDisabledLabel).raycastTarget = false;
				RectTransform val = (RectTransform)routingDisabledLabel.transform;
				val.anchorMin = new Vector2(0f, 0f);
				val.anchorMax = new Vector2(0f, 0f);
				val.pivot = new Vector2(0f, 0f);
				val.anchoredPosition = new Vector2(-16f, -16f);
				((Component)routingDisabledLabel).gameObject.SetActive(false);
			}
		}

		private static void UpdateRoutingDisabledLabel(InventoryGui inventoryGui)
		{
			if (!((Object)(object)routingDisabledLabel == (Object)null))
			{
				Container currentContainer = inventoryGui.m_currentContainer;
				bool flag = (Object)(object)currentContainer != (Object)null && (Mod.unsafeTargetEnabled || Mod.unsafeSourceEnabled.Value || ContainersTracker.IsReliableRoutingTarget(currentContainer));
				((Component)routingDisabledLabel).gameObject.SetActive(!flag && Mod.unsafeSourceLabelDisplayed.Value);
				if ((Object)(object)createGroupButton != (Object)null)
				{
					((Component)createGroupButton).gameObject.SetActive(flag);
				}
				if ((Object)(object)storeAllButton != (Object)null)
				{
					((Component)storeAllButton).gameObject.SetActive(flag);
				}
			}
		}

		public static void HideRoutingDisabledLabel()
		{
			if ((Object)(object)routingDisabledLabel != (Object)null)
			{
				((Component)routingDisabledLabel).gameObject.SetActive(false);
			}
		}

		private static void AddCreateGroupBtn(InventoryGui __instance)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			createGroupButton = GuiFactory.AddContainerGuiButton("createGroupBtn", "☀", new Vector2(45f, 30f), Mod.createGroupBtnPos.Value);
			GuiFactory.InitJoyKey(createGroupButton, "JoyMap", "(Гг)");
			((UnityEvent)((Component)createGroupButton).GetComponent<Button>().onClick).AddListener((UnityAction)delegate
			{
				//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)
				//IL_0089: Unknown result type (might be due to invalid IL or missing references)
				//IL_008e: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)__instance.m_playerGrid == (Object)null) && !((Object)(object)__instance.m_containerGrid == (Object)null) && !((Character)Player.m_localPlayer).IsTeleporting())
				{
					Inventory inventory = __instance.m_containerGrid.GetInventory();
					KeyboardShortcut value;
					if (Mod.unloadAllEnabled.Value)
					{
						value = Mod.unloadAllItemsGroupKeyModifier.Value;
						if (((KeyboardShortcut)(ref value)).IsPressed() || ZInput.GetButton("JoyRTrigger"))
						{
							UnloadItems.AddToUnloadAllItemsFilter(inventory);
							return;
						}
					}
					if (Mod.unloadAllEnabled.Value)
					{
						value = Mod.unloadAllSkipItemsGroupKeyModifier.Value;
						if (((KeyboardShortcut)(ref value)).IsPressed() || ZInput.GetButton("JoyLTrigger"))
						{
							UnloadItems.AddToUnloadAllItemsSkipList(inventory);
							return;
						}
					}
					if (Mod.groupingEnabled.Value)
					{
						containersUniqueItems = ItemGroupUtils.ExtractUniqueItemNames(inventory);
						if (containersUniqueItems.Count >= 2)
						{
							if (Mod.mergePromptEnabled.Value)
							{
								InitMergePromptDlg(inventory);
							}
							else
							{
								ItemGroupUtils.RegisterNewGroup(inventory);
							}
						}
					}
				}
			});
		}

		private static void InitMergePromptDlg(Inventory inventory)
		{
			//IL_0379: Unknown result type (might be due to invalid IL or missing references)
			//IL_0383: Expected O, but got Unknown
			//IL_0397: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ee: Expected O, but got Unknown
			intersectedGroups = ItemGroupUtils.FindIntersections(containersUniqueItems);
			if (!intersectedGroups.Any())
			{
				ItemGroupUtils.RegisterNewGroup(inventory);
				return;
			}
			List<string> second = intersectedGroups.Map((Tuple<string, List<string>> _) => _.Item1).ToList();
			HashSet<string> intersectedItemNames = intersectedGroups.FlatMap((Tuple<string, List<string>> _) => _.Item2).ToHashSet();
			List<string> nonGroupedItems = containersUniqueItems.Filter((string _) => !intersectedItemNames.Contains(_)).ToList();
			TextMeshProUGUI component = ((Component)groupsDialogObj.transform.Find("win_bkg/TextArea/ScrollArea/Content/Name")).GetComponent<TextMeshProUGUI>();
			TextMeshProUGUI component2 = ((Component)groupsDialogObj.transform.Find("win_bkg/TextArea/ScrollArea/Content/Description")).GetComponent<TextMeshProUGUI>();
			if (intersectedGroups.Count == 1)
			{
				if (!nonGroupedItems.Any())
				{
					((Character)Player.m_localPlayer).Message((MessageType)2, "Group " + intersectedGroups[0].Item1 + " already contains these items", 0, (Sprite)null, false);
					return;
				}
				List<string> list = inventory.m_inventory.Filter((ItemData i) => intersectedGroups[0].Item2.Contains(i.SCName())).Map((ItemData _) => Localization.instance.Localize(_.m_shared.m_name)).Distinct()
					.ToList();
				((TMP_Text)component).text = string.Join(",", list.Take(3)) + " " + ((list.Count > 3) ? "and others " : "") + ((list.Count > 1) ? "are already members" : "is already a member") + $" of an existing group of {ItemGroups.Groups[intersectedGroups[0].Item1].Count} items. ";
			}
			else
			{
				((TMP_Text)component).text = $"Items from {intersectedGroups.Count} different groups are already presented in this container.";
			}
			((TMP_Text)component2).text = " Please review info bellow, select target-group & action.\n You can: \n • <color=#FFA13CB7>'Extend'</color>: add previously non-grouped items \n(colored in <color=green>green</color>) to a selected  target-group.\n";
			((TMP_Text)component2).text = ((TMP_Text)component2).text + " • <color=#FFA13CB7>'Extend & merge'</color>: add all items from this container to a selected target-group. All conflicting items (colored in <color=red>red</color>) would be removed from their current groups.\n";
			((TMP_Text)component2).text = ((TMP_Text)component2).text + " • <color=#FFA13CB7>'Merge all'</color>: (Not recommended) move all items (from this container) & all conflicting group items to a target group.All members of all listed bellow groups will be moved to a target group. \n<color=grey>(target-group could be a new or an existing group)</color>\n";
			List<string> list2 = UnloadItems.unloadAllGroupsList.Intersect(second).ToList();
			if (Mod.unloadAllEnabled.Value && list2.Any())
			{
				((TMP_Text)component2).text = ((TMP_Text)component2).text + " \n<color=orange> ! Please note that groups [" + string.Join(",", list2) + "] are used for 'Unloading' filtering. Changing them will affect it's behaviour.</color>\n";
			}
			((TMP_Text)component2).text = ((TMP_Text)component2).text + "\n Items in the container: " + GeneralExtensions.Join<string>(containersUniqueItems.Map((string itemName) => "<color=" + (nonGroupedItems.Contains(itemName) ? "green" : "red") + ">" + itemName + "</color>"), (Func<string, string>)null, ",") + ".\n";
			((TMP_Text)component2).text = ((TMP_Text)component2).text + "\n Detected groups: \n";
			((TMP_Text)component2).text = ((TMP_Text)component2).text + GeneralExtensions.Join<string>(intersectedGroups.Map((Tuple<string, List<string>> _) => _.Item1).Map(delegate(string gKey)
			{
				IEnumerable<string> values = ItemGroups.Groups[gKey].Map((string _) => (!intersectedItemNames.Contains(_)) ? _ : ("<color=red>" + _ + "</color>"));
				return "<color=yellow>" + gKey + "</color>: [" + string.Join(", ", values) + "] \n";
			}), (Func<string, string>)null, "");
			RectTransform selectorBtn = (RectTransform)groupsDialogObj.transform.Find("win_bkg/TextArea/Selector");
			RectTransform val = (RectTransform)groupsDialogObj.transform.Find("win_bkg/SelectorDlg/Recipes/ListRoot");
			List<string> names = intersectedGroups.Map((Tuple<string, List<string>> _) => _.Item1).Prepend("--new Group--").ToList();
			GuiFactory.FillResList(val, names, groupSelectorItems, delegate(string selection)
			{
				((TMP_Text)((Component)selectorBtn).GetComponentInChildren<TextMeshProUGUI>()).text = selection;
				((Component)groupsDialogObj.transform.Find("win_bkg/SelectorDlg")).gameObject.SetActive(false);
			});
			((TMP_Text)((Component)selectorBtn).GetComponentInChildren<TextMeshProUGUI>()).text = ((intersectedGroups.Count == 1) ? intersectedGroups[0].Item1 : "--new Group--");
			((Component)groupsDialogObj.transform.Find("win_bkg/SelectorDlg/RecipeScroll")).GetComponent<Scrollbar>().value = 1f;
			((Component)groupsDialogObj.transform.Find("win_bkg/SelectorDlg")).gameObject.SetActive(false);
			groupsDialogObj.SetActive(true);
		}

		private static string SelectedTargetGroupId()
		{
			return ((TMP_Text)((Component)groupsDialogObj.transform.Find("win_bkg/TextArea/Selector")).GetComponentInChildren<TextMeshProUGUI>()).text;
		}

		private static void AddStoreAllBtn(InventoryGui __instance)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			storeAllButton = GuiFactory.AddContainerGuiButton("storeAllButton", "↓↓↓", new Vector2(45f, 36f), Mod.unloadAllBtnPos.Value);
			GuiFactory.InitJoyKey(storeAllButton, "JoyMenu", "(≡)");
			((UnityEvent)((Component)storeAllButton).GetComponent<Button>().onClick).AddListener((UnityAction)delegate
			{
				if (!((Object)(object)__instance.m_playerGrid == (Object)null) && !((Object)(object)__instance.m_containerGrid == (Object)null) && !((Character)Player.m_localPlayer).IsTeleporting())
				{
					UnloadItems.UnloadAllItems(((Humanoid)Player.m_localPlayer).GetInventory());
				}
			});
		}

		public static void Init()
		{
			if ((Object)(object)InventoryGui.instance != (Object)null)
			{
				InitButtons(InventoryGui.instance);
				if (InventoryGui.instance.IsContainerOpen())
				{
					UpdateRoutingDisabledLabel(InventoryGui.instance);
				}
			}
		}

		public static void Clean()
		{
			if ((Object)(object)createGroupButton != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)createGroupButton).gameObject);
			}
			if ((Object)(object)storeAllButton != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)storeAllButton).gameObject);
			}
			if ((Object)(object)routingDisabledLabel != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)routingDisabledLabel).gameObject);
			}
			if ((Object)(object)groupsDialogObj != (Object)null)
			{
				Object.Destroy((Object)(object)groupsDialogObj);
			}
			initialized = false;
			routingDisabledLabel = null;
			Mod.log.LogInfo((object)"Cleaned up UI extensions.");
		}
	}
	[HarmonyPatch(typeof(Game), "Start")]
	public static class Game_Start_Patch
	{
		private static void Postfix(Game __instance)
		{
			Gui_Patch.Clean();
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Hide")]
	public static class Gui_Hide_Patch
	{
		private static void Postfix(InventoryGui __instance)
		{
			if (Gui_Patch.initialized)
			{
				GameObject groupsDialogObj = Gui_Patch.groupsDialogObj;
				if (groupsDialogObj != null)
				{
					groupsDialogObj.SetActive(false);
				}
				Gui_Patch.HideRoutingDisabledLabel();
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "UpdateGui")]
	internal static class InventoryGrid_UpdateGui_Patch
	{
		private static readonly Color Dim = new Color(0.35f, 0.5f, 0.75f, 1f);

		private static readonly Color Lit = new Color(0.75f, 0.9f, 1f, 1f);

		private static void Postfix(InventoryGrid __instance)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)__instance != (Object)(object)instance.m_playerGrid)
			{
				return;
			}
			Inventory inventory = __instance.m_inventory;
			if (inventory == null)
			{
				return;
			}
			int width = inventory.GetWidth();
			Color color = Color.Lerp(Dim, Lit, 0.5f + 0.5f * Mathf.Sin(Time.time * 6f));
			List<ItemData> inventory2 = inventory.m_inventory;
			for (int i = 0; i < inventory2.Count; i++)
			{
				ItemData val = inventory2[i];
				if (Inventory_Patch.BagStackBlocked(val))
				{
					InventoryElement element = __instance.GetElement(val.m_gridPos.x, val.m_gridPos.y, width);
					if (!((Object)(object)element?.m_icon == (Object)null))
					{
						((Graphic)element.m_icon).color = color;
					}
				}
			}
		}
	}
	public static class ItemGroups
	{
		private static ICollection<string> prefixItemGroups = new HashSet<string>();

		private static ICollection<string> postfixItemGroups = new HashSet<string>();

		public static readonly IDictionary<string, ISet<string>> Groups = new Dictionary<string, ISet<string>>();

		public static void ParseConfig()
		{
			foreach (KeyValuePair<ConfigDefinition, string> item in Mod.ConfigOrphanedEntries().ToList())
			{
				if (item.Key.Section.Equals("ItemGroup"))
				{
					Mod.config.Bind<string>(item.Key.Section, item.Key.Key, item.Value.ToLower().Replace("_", ""), Mod.groupDescr("user-added items-group based on item-names list"));
				}
			}
			ConfigEntry<string> val = default(ConfigEntry<string>);
			if (Mod.config.TryGetEntry<string>("PrefixedItemGroups", "prefixes", ref val) && val.Value.Length > 0)
			{
				prefixItemGroups = ItemGroupUtils.ParseGroupConfigEntry(val);
				Mod.log.LogInfo((object)string.Format("parsed PrefixedItemGroups : [{0}] ({1})", string.Join(",", prefixItemGroups), prefixItemGroups.Count));
			}
			ConfigEntry<string> val2 = default(ConfigEntry<string>);
			if (Mod.config.TryGetEntry<string>("PostfixedItemGroups", "posfixes", ref val2) && val2.Value.Length > 0)
			{
				postfixItemGroups = ItemGroupUtils.ParseGroupConfigEntry(val2);
				Mod.log.LogInfo((object)string.Format("parsed PostfixedItemGroups: [{0}] ({1})", string.Join(",", postfixItemGroups), postfixItemGroups.Count));
			}
			ConfigEntry<string> val3 = default(ConfigEntry<string>);
			foreach (ConfigDefinition item2 in from _ in Mod.config.Keys
				where _.Section.Equals("ItemGroup")
				orderby _.Key
				select _)
			{
				if (Mod.config.TryGetEntry<string>(item2.Section, item2.Key, ref val3) && val3.Value.Length > 0)
				{
					ISet<string> set = ItemGroupUtils.ParseGroupConfigEntry(val3);
					Groups.Add(item2.Key, set);
					Mod.log.LogInfo((object)string.Format("parsed items group '{0}': [{1}] ({2})", item2.Key, string.Join(",", set), set.Count));
				}
			}
		}

		public static string FindMatchingUserGroup(Container container, ItemData item)
		{
			if (item.m_shared.m_name.Length < 7)
			{
				Mod.log.LogWarning((object)("Cant process Unexpected Item " + item.m_shared.m_name));
				return null;
			}
			ICollection<string> collection = FindMatchingGroups(container, item.SCName(), item, Groups.Keys.Except(Mod.systemGroupKeys).ToList());
			if (collection.Count > 1)
			{
				Mod.log.LogWarning((object)("Ambiguous item-groups [" + string.Join(",", collection) + "] found for " + ((Object)item.m_dropPrefab).name + " item in single container."));
			}
			if (!collection.Any())
			{
				return null;
			}
			return collection.Max((string _) => _);
		}

		public static bool ContainerHasSame_SystemGroupItems(Container container, ItemData item)
		{
			if (item.m_shared.m_name.Length < 7)
			{
				Mod.log.LogWarning((object)("Cant process Unexpected Item " + item.m_shared.m_name));
				return false;
			}
			return TestConfiguredGroups(container, item.SCName(), item, Mod.systemGroupKeys);
		}

		public static bool ContainerHasSame_NamePatternItems(Container container, ItemData item)
		{
			if (item.m_shared.m_name.Length < 7)
			{
				Mod.log.LogWarning((object)("Cant process Unexpected Item " + item.m_shared.m_name));
				return false;
			}
			string shortItemName = item.SCName();
			if (!TestItemNamePrefix(container, shortItemName, item))
			{
				return TestItemNamePostfix(container, shortItemName, item);
			}
			return true;
		}

		public static bool ContainerHasSameItemType(Container container, ItemData item)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			if (item.m_shared.m_name.Length < 7)
			{
				return false;
			}
			ItemType itemType = item.m_shared.m_itemType;
			string shortItemName = item.SCName();
			int num;
			if ((!IsTrophie(itemType) || !HaveMatchingItem(container, (ItemData _) => IsTrophie(_.m_shared.m_itemType) && !_.SCName().Equals(shortItemName))) && (!IsTool(itemType) || !HaveMatchingItem(container, (ItemData _) => IsTool(_.m_shared.m_itemType) && !_.SCName().Equals(shortItemName))) && (!IsAmmo(itemType) || !HaveMatchingItem(container, (ItemData _) => IsAmmo(_.m_shared.m_itemType) && !_.SCName().Equals(shortItemName))) && (!IsArmor(itemType) || !HaveMatchingItem(container, (ItemData _) => IsArmor(_.m_shared.m_itemType) && !_.SCName().Equals(shortItemName))))
			{
				if (IsWeapon(itemType))
				{
					num = (HaveMatchingItem(container, (ItemData _) => IsWeapon(_.m_shared.m_itemType) && !_.SCName().Equals(shortItemName)) ? 1 : 0);
					if (num != 0)
					{
						goto IL_00c6;
					}
				}
				else
				{
					num = 0;
				}
				goto IL_00ff;
			}
			num = 1;
			goto IL_00c6;
			IL_00c6:
			Console.instance.Print($"{Localization.instance.Localize(item.m_shared.m_name)} routed because target container has same item type {item.m_shared.m_itemType}");
			goto IL_00ff;
			IL_00ff:
			return (byte)num != 0;
		}

		public static bool ContainerHasSimilarGroupItems(Container container, ItemData item)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			if (item.m_shared.m_name.Length < 7)
			{
				return false;
			}
			string shortItemName = item.m_shared.m_name.SCName();
			ItemType itemType = item.m_shared.m_itemType;
			int num;
			if ((int)itemType != 0)
			{
				num = (HaveMatchingItem(container, (ItemData _) => _.m_shared.m_itemType == itemType && !_.SCName().Equals(shortItemName)) ? 1 : 0);
				if (num != 0)
				{
					Console.instance.Print($"{Localization.instance.Localize(item.m_shared.m_name)} routed because target container has same item type (fuzzy) {item.m_shared.m_itemType}");
				}
			}
			else
			{
				num = 0;
			}
			return (byte)num != 0;
		}

		private static bool TestItemNamePrefix(Container container, string shortItemName, ItemData item)
		{
			foreach (string prefix in prefixItemGroups)
			{
				if (shortItemName.StartsWith(prefix) && HaveMatchingItem(container, (ItemData _) => _.m_shared.m_name.StartsWith("$item_" + prefix) && !_.SCName().Equals(shortItemName)))
				{
					Console.instance.Print(Localization.instance.Localize(item.m_shared.m_name) + " routed because target container has item with Prefix " + prefix);
					return true;
				}
			}
			return false;
		}

		private static bool TestItemNamePostfix(Container container, string shortItemName, ItemData item)
		{
			foreach (string postfix in postfixItemGroups)
			{
				if (shortItemName.EndsWith(postfix) && HaveMatchingItem(container, (ItemData _) => _.m_shared.m_name.EndsWith(postfix) && !_.SCName().Equals(shortItemName)))
				{
					Console.instance.Print(Localization.instance.Localize(item.m_shared.m_name) + " routed because target container has item with Postfix " + postfix);
					return true;
				}
			}
			return false;
		}

		private static bool TestConfiguredGroups(Container container, string shortItemName, ItemData item, ICollection<string> grKeys = null)
		{
			foreach (string item2 in grKeys ?? Groups.Keys)
			{
				if (Groups.ContainsKey(item2))
				{
					ISet<string> set = Groups[item2];
					if (TestGroup(shortItemName, container, set))
					{
						Console.instance.Print(Localization.instance.Localize(item.m_shared.m_name) + " routed because target container has item from group " + item2 + " : [" + string.Join(",", set) + "]");
						return true;
					}
				}
			}
			return false;
		}

		private static ICollection<string> FindMatchingGroups(Container container, string shortItemName, ItemData item, ICollection<string> grKeys = null)
		{
			ICollection<string> obj = grKeys ?? Groups.Keys;
			List<string> list = new List<string>();
			foreach (string item2 in obj)
			{
				if (Groups.ContainsKey(item2))
				{
					ISet<string> set = Groups[item2];
					if (TestGroup(shortItemName, container, set))
					{
						list.Add(item2);
					}
				}
			}
			return list;
		}

		private static bool TestGroup(string shortItemName, Container container, ICollection<string> group)
		{
			if (group.Contains(shortItemName))
			{
				foreach (string iname in group)
				{
					if (!iname.Equals(shortItemName) && HaveMatchingItem(container, (ItemData _) => _.SCName().Equals(iname)))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool HaveMatchingItem(Container container, Func<ItemData, bool> predicate)
		{
			foreach (ItemData item in container.GetInventory().m_inventory)
			{
				if (predicate(item))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsArmor(ItemType _)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Invalid comparison between Unknown and I4
			if ((int)_ != 6 && (int)_ != 7 && (int)_ != 11 && (int)_ != 17)
			{
				return (int)_ == 18;
			}
			return true;
		}

		private static bool IsWeapon(ItemType _)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			if ((int)_ != 3 && (int)_ != 4 && (int)_ != 14)
			{
				return (int)_ == 5;
			}
			return true;
		}

		private static bool IsTrophie(ItemType _)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			return (int)_ == 13;
		}

		private static bool IsTool(ItemType _)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			return (int)_ == 19;
		}

		private static bool IsAmmo(ItemType _)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			return (int)_ == 9;
		}

		public static void Clear()
		{
			prefixItemGroups.Clear();
			postfixItemGroups.Clear();
			Groups.Clear();
		}
	}
	public static class ItemGroupUtils
	{
		private static readonly Predicate<string> IsItemSupported = (string itemName) => itemName.StartsWith("$item_") || itemName.StartsWith("$mod_");

		public static void RegisterNewGroup(Inventory inventory)
		{
			List<string> list = ExtractUniqueItemNames(inventory);
			if (list.Count > 1)
			{
				RegisterNewGroup((ICollection<string>)list);
			}
		}

		public static void RegisterNewGroup(ICollection<string> names)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			if (!names.Any())
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, "No new items to create a group from.", 0, (Sprite)null, false);
				return;
			}
			Mod.ignoreUpdate = true;
			ConfigDefinition val = new ConfigDefinition("ItemGroup", "userGroup" + DateTimeOffset.UtcNow.ToUnixTimeSeconds());
			Mod.config.Bind<string>(val, string.Join(",", names), Mod.groupDescr("user-added items-group based on item-names list"));
			ConfigEntry<string> val2 = default(ConfigEntry<string>);
			if (Mod.config.TryGetEntry<string>(val, ref val2) && val2.Value.Length > 0)
			{
				ISet<string> set = ParseGroupConfigEntry(val2);
				ItemGroups.Groups.Add(val.Key, set);
				ManualLogSource log = Mod.log;
				if (log != null)
				{
					log.LogInfo((object)string.Format("parsed items group '{0}': [{1}] ({2})", val.Key, string.Join(",", set), set.Count));
				}
				NotifyPlayer(set.Count, "Registered new group of {0} items.");
			}
		}

		public static void MergeAllToSingleGroup(string targetGroupId, List<string> groupIds, List<string> newItems)
		{
			HashSet<string> hashSet = groupIds.Filter((string _) => !_.Equals(targetGroupId)).FlatMap((string _) => ItemGroups.Groups[_]).ToList()
				.Concat(newItems)
				.ToHashSet();
			if (!ItemGroups.Groups.ContainsKey(targetGroupId))
			{
				RegisterNewGroup((ICollection<string>)hashSet);
			}
			else
			{
				hashSet = hashSet.Except(ItemGroups.Groups[targetGroupId]).ToHashSet();
				if (!hashSet.Any())
				{
					((Character)Player.m_localPlayer).Message((MessageType)2, "Existing group already contained all items", 0, (Sprite)null, false);
				}
				else
				{
					AddToItemsGroup(targetGroupId, hashSet);
					NotifyPlayer(hashSet.Count, "Added {0} new items to existing group.");
				}
			}
			foreach (string item in groupIds.Filter((string _) => !_.Equals(targetGroupId)))
			{
				Mod.log.LogInfo((object)("flattened group : " + item));
				ClearItemsGroup(item);
			}
		}

		public static void ExtendGroup(string targetGroupId, List<string> groupIds, List<string> newItems)
		{
			List<string> second = groupIds.FlatMap((string _) => ItemGroups.Groups[_]).Intersect(newItems).ToList();
			HashSet<string> hashSet = newItems.Except(second).ToHashSet();
			if (!ItemGroups.Groups.ContainsKey(targetGroupId))
			{
				RegisterNewGroup((ICollection<string>)hashSet);
				return;
			}
			if (!hashSet.Any())
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, "Existing group already contained all items", 0, (Sprite)null, false);
				return;
			}
			AddToItemsGroup(targetGroupId, hashSet);
			NotifyPlayer(hashSet.Count, "Added {0} new items to existing group.");
		}

		public static void ExtendAndMerge(string targetGroupId, List<string> groupIds, List<string> newItems)
		{
			foreach (string item in groupIds.Filter((string _) => !_.Equals(targetGroupId)))
			{
				List<string> items = newItems.Intersect(ItemGroups.Groups[item]).ToList();
				RemoveFromItemsGroup(item, items);
			}
			if (!ItemGroups.Groups.ContainsKey(targetGroupId))
			{
				RegisterNewGroup((ICollection<string>)newItems);
				return;
			}
			HashSet<string> hashSet = newItems.Except(ItemGroups.Groups[targetGroupId]).ToHashSet();
			if (!hashSet.Any())
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, "Existing group already contained all items", 0, (Sprite)null, false);
				return;
			}
			AddToItemsGroup(targetGroupId, hashSet);
			NotifyPlayer(hashSet.Count, "Added {0} new items to existing group.");
		}

		public static ConfigEntry<string> AddToItemListConfig(ICollection<string> names, ISet<string> targetSet, ConfigEntry<string> configEntry, string uiMessage)
		{
			foreach (string name in names)
			{
				targetSet.Add(name);
			}
			Mod.ignoreUpdate = true;
			configEntry.Value = string.Join(",", targetSet);
			NotifyPlayer(names.Count, uiMessage);
			return configEntry;
		}

		private static void NotifyPlayer(int count, string uiMessage)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			((Character)Player.m_localPlayer).Message((MessageType)2, string.Format(uiMessage, count), 0, (Sprite)null, false);
			if (Mod.effectFeedbackEnabled.Value)
			{
				Vector3 position = ((Component)InventoryGui.instance.m_currentContainer).gameObject.transform.position;
				Player.m_localPlayer.m_skillLevelupEffects.Create(position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			}
		}

		public static List<Tuple<string, List<string>>> FindIntersections(List<string> names)
		{
			List<Tuple<string, List<string>>> list = new List<Tuple<string, List<string>>>();
			foreach (string item in ItemGroups.Groups.Keys.ToList())
			{
				List<string> list2 = ItemGroups.Groups[item].Intersect(names).ToList();
				if (list2.Any())
				{
					list.Add(new Tuple<string, List<string>>(item, list2));
				}
			}
			return list.OrderByDescending((Tuple<string, List<string>> tt) => tt.Item2.Count).ToList();
		}

		public static List<string> ExtractUniqueItemNames(Inventory inventory)
		{
			foreach (string item in inventory.m_inventory.Map((ItemData invi) => invi.m_shared.m_name).Filter((string _) => !IsItemSupported(_)).ToList())
			{
				Mod.log.LogWarning((object)("Unsupported item name " + item));
			}
			return inventory.m_inventory.Map((ItemData invi) => invi.m_shared.m_name).Filter((string _) => IsItemSupported(_)).Distinct()
				.Map((string _) => _.SCName())
				.ToList();
		}

		public static ISet<string> ParseGroupConfigEntry(ConfigEntry<string> configEntry)
		{
			return new HashSet<string>(configEntry.Value.Split(new char[1] { ',' }).Map((string _) => _.SCName().Replace(" ", "")));
		}

		private static void AddToItemsGroup(string groupId, ICollection<string> items)
		{
			foreach (string item in items)
			{
				ItemGroups.Groups[groupId].Add(item);
			}
			SaveItemsGroup(groupId, ItemGroups.Groups[groupId]);
		}

		private static void RemoveFromItemsGroup(string groupId, ICollection<string> items)
		{
			foreach (string item in items)
			{
				ItemGroups.Groups[groupId].Remove(item);
			}
			SaveItemsGroup(groupId, ItemGroups.Groups[groupId]);
		}

		private static void ClearItemsGroup(string flattenedGroupId)
		{
			ItemGroups.Groups[flattenedGroupId].Clear();
			SaveItemsGroup(flattenedGroupId, ItemGroups.Groups[flattenedGroupId]);
		}

		private static void SaveItemsGroup(string groupId, ICollection<string> groupItems)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			ConfigEntry<string> val = default(ConfigEntry<string>);
			if (Mod.config.TryGetEntry<string>(new ConfigDefinition("ItemGroup", groupId), ref val))
			{
				Mod.ignoreUpdate = true;
				val.Value = string.Join(",", groupItems);
			}
		}
	}
	[BepInPlugin("flueno.SmartContainers", "Smart Containers Mod", "1.9.0")]
	public class Mod : BaseUnityPlugin
	{
		internal readonly Harmony harmony;

		internal readonly Assembly assembly;

		public static ManualLogSource log;

		public static ConfigFile config;

		public static ConfigEntry<bool> modEnabled;

		public static ConfigEntry<bool> groupingEnabled;

		public static ConfigEntry<bool> unsafeSourceEnabled;

		public static ConfigEntry<bool> unsafeSourceLabelDisplayed;

		public static ConfigEntry<bool> itemTypeGroupsEnabled;

		public static ConfigEntry<bool> fuzzyGroupingEnabled;

		public static ConfigEntry<bool> onlyStackableItems;

		public static ConfigEntry<ConcurrentChestModificationWorkaround> concurrencyWorkaround;

		public static ConfigEntry<bool> hudMessageEnabled;

		public static ConfigEntry<bool> audioFeedbackEnabled;

		public static ConfigEntry<bool> effectFeedbackEnabled;

		public static ConfigEntry<bool> drawersIntegrationEnabled;

		public static ConfigEntry<string> hudMessageText;

		public static ConfigEntry<int> range;

		public static ConfigEntry<KeyboardShortcut> keyModifier;

		public static ConfigEntry<string> gamepadKey1;

		public static ConfigEntry<KeyboardShortcut> gamepadKey2;

		public static ConfigEntry<KeyboardShortcut> groupingKeyModifier;

		public static ConfigEntry<bool> createGroupBtnEnabled;

		public static ConfigEntry<bool> mergePromptEnabled;

		public static ConfigEntry<Vector2> createGroupBtnPos;

		public static ConfigEntry<bool> unloadAllEnabled;

		public static ConfigEntry<bool> unloadAllInsteadStackBtn;

		public static ConfigEntry<Vector2> unloadAllBtnPos;

		public static ConfigEntry<KeyboardShortcut> unloadAllItemsGroupKeyModifier;

		public static ConfigEntry<KeyboardShortcut> unloadAllSkipItemsGroupKeyModifier;

		public static ConfigEntry<bool> unloadForceGrouping;

		public static ConfigEntry<string> unloadAllItemsList;

		public static ConfigEntry<string> unloadAllSkipList;

		public static ConfigEntry<string> unloadAllGroupsList;

		public static ConfigEntry<string> unloadAllPrefixItemGroups;

		public static ConfigEntry<string> unloadAllPostfixItemGroups;

		public static ConfigEntry<bool> unloadAllMaterialsFiltering;

		public static ConfigEntry<bool> unloadAllTrophiesFiltering;

		public static ConfigEntry<bool> unloadAllConsumableFiltering;

		public static bool ignoreUpdate = false;

		public static readonly ICollection<string> systemGroupKeys = new HashSet<string> { "valuables", "ore", "rock", "ingots", "wood", "mushrooms", "berries", "vegetables", "cookedMeat", "food" };

		private const int CurrentModVersion = 19;

		public static bool unsafeTargetEnabled
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.IsSinglePlayer;
				}
				return false;
			}
		}

		public Mod()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			harmony = new Harmony("flueno.SmartContainers");
			assembly = Assembly.GetExecutingAssembly();
			config = ((BaseUnityPlugin)this).Config;
			log = ((BaseUnityPlugin)this).Logger;
		}

		public void Start()
		{
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Expected O, but got Unknown
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_032e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0558: Unknown result type (might be due to invalid IL or missing references)
			//IL_0582: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d0: Unknown result type (might be due to invalid IL or missing references)
			((BaseUnityPlugin)this).Config.Bind<int>("General", "NexusID", 332, "Nexus mod ID");
			modEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "enabled", true, "Enables this mod");
			MigrateConfig(ConfigOrphanedEntries());
			onlyStackableItems = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "onlyStackableItems", true, "Rearranges only stackable items");
			hudMessageEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "hudMessageEnabled", true, "Enables hud message indicating that item was routed & placed to some other chest");
			hudMessageText = ((BaseUnityPlugin)this).Config.Bind<string>("General", "hudMessageText", "Routed to another Chest", "HUD message consists of Icon, item-name plus this text.");
			range = ((BaseUnityPlugin)this).Config.Bind<int>("General", "range", 14, new ConfigDescription("Range within which containers will participate in resources arrangement.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 99), Array.Empty<object>()));
			keyModifier = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "hkeyModifier", new KeyboardShortcut((KeyCode)306, Array.Empty<KeyCode>()), "Change only if you wish to have even longer than ctrl+click combination (holding ctrl is mandatory since it's a 'move-stack' ingame key)");
			gamepadKey1 = ((BaseUnityPlugin)this).Config.Bind<string>("General", "gamepadKey1", "JoyLTrigger", "first part of gamepads transfer-item key-combo");
			gamepadKey2 = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "gamepadKey2", new KeyboardShortcut((KeyCode)332, Array.Empty<KeyCode>()), "second part of gamepads transfer-item key-combo");
			drawersIntegrationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "drawersIntegrationEnabled", false, "Enables ItemDrawers mod integration");
			audioFeedbackEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "audioFeedbackEnabled", true, "Enables playing of sound on successful items transfer");
			effectFeedbackEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "effectFeedbackEnabled", true, "Enables highlighting of end-target chests where items have been transferred.");
			concurrencyWorkaround = ((BaseUnityPlugin)this).Config.Bind<ConcurrentChestModificationWorkaround>("General", "concurrentChestModificationWorkaround", ConcurrentChestModificationWorkaround.RequestOwnership, new ConfigDescription("How to avoid possible item loss when two players modify the same chest.\nNone: no precautions. Can lose items in co-op when two players route items to the same chest at the same time.\nRequestOwnership: Safest. Coordinate updates with other players. Can route to opened chests. Worst case, network lag, dupes items instead of losing them.\nExcludeContainersOpenedByOthers: never route into a chest someone else has open. Safer than None, but rare item loss may still occur.\nBlockIfOtherContainersAreOpened: disable routing while any nearby player has a chest open.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					DispName = "Multiplayer chest safeguards"
				}
			}));
			MigrateConcurrencyDefault();
			unsafeSourceEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "unsafeSourceEnabled", false, "Allow routing items from ships, wagons, obliterator, tombstones, and other unsafe containers. Routing to these containers remains disabled outside local single-player worlds.");
			unsafeSourceLabelDisplayed = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "unsafeSourceLabelDisplayed", true, "Show 'Routing disabled' in the container UI when routing from that container is disabled.");
			groupingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Grouping", "enabled", true, "Enables distributing items to containers with 'same-kinded' items (used if no nearby containers contain 'same item')");
			itemTypeGroupsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Grouping", "itemTypeGroupsEnabled", true, "Enable grouping by item-types (Trophie, Tool, Ammo, Armor, Weapon).");
			fuzzyGroupingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Grouping", "fuzzyGroupingEnabled", false, "Enable grouping by more broad criteria.");
			groupingKeyModifier = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Grouping", "groupingKeyModifier", new KeyboardShortcut((KeyCode)306, Array.Empty<KeyCode>()), " Change to 'LeftControl + LeftShift' if you want to trigger grouping with a separate hotkey (ctrl+shift+click)(holding ctrl is mandatory since it's a 'move-stack' ingame key)");
			createGroupBtnEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Grouping", "createGroupBtnEnabled", true, "Adds to the chest UI button [☼] which creates items-group based on the current items set in the chest.");
			mergePromptEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Grouping", "mergePromptEnabled", true, "In case when createGroupBtn clicked and chest already contains some members of one ore more groups - dialog showed with option to merge items & groups into a single group.");
			createGroupBtnPos = ((BaseUnityPlugin)this).Config.Bind<Vector2>("Grouping", "createGroupBtnPos", new Vector2(1.5f, 10.7f), "Adjusts btn position on inventory ui. →x:1.5..-10 ↓y:0.4..10.7  bottom-left: (1.5f, 10.7f) top-right:(-10f, 0.4f) bottom-right:(-10f, 10.7f)");
			((BaseUnityPlugin)this).Config.Bind<string>("PrefixedItemGroups", "prefixes", "trophy,mead,arrow,armor,cape,helmet,atgeir,bow,battleaxe,knife,mace,shield,sledge,spear,sword,tankard,club,mushroom,arrow", groupDescr("If both item names start with same 'prefix' - they are considered to have same group. I.e. arrowFire & arrowFlint"));
			((BaseUnityPlugin)this).Config.Bind<string>("PostfixedItemGroups", "posfixes", "cone,seeds,pelt,hide,berries", groupDescr("If both item names end with same 'postfix' - they are considered to have same group. I.e. carrotSeeds & turnipSeeds"));
			((BaseUnityPlugin)this).Config.Bind<string>("ItemGroup", "valuables", "ruby,coins,amber,amberpearl", groupDescr());
			((BaseUnityPlugin)this).Config.Bind<string>("ItemGroup", "ore", "copperore,flametalore,ironore,silverore,tinore,ironscrap,blackmetalscrap", groupDescr());
			((BaseUnityPlugin)this).Config.Bind<string>("ItemGroup", "rock", "stone,flint,obsidian", groupDescr());
			((BaseUnityPlugin)this).Config.Bind<string>("ItemGroup", "ingots", "copper,bronze,flametal,iron,silver,tin,blackmetal", groupDescr());
			((BaseUnityPlugin)this).Config.Bind<string>("ItemGroup", "wood", "wood,finewood,corewood,elderbark,roundlog", groupDescr());
			((BaseUnityPlugin)this).Config.Bind<string>("ItemGroup", "mushrooms", "Mushroom,MushroomBlue,MushroomYellow,mushroomcommon", groupDescr());
			((BaseUnityPlugin)this).Config.Bind<string>("ItemGroup", "berries", "Blueberries,raspberries,cloudberries,honey", groupDescr());
			((BaseUnityPlugin)this).Config.Bind<string>("ItemGroup", "vegetables", "Carrot,Turnip", groupDescr());
			((BaseUnityPlugin)this).Config.Bind<string>("ItemGroup", "cookedMeat", "CookedLoxMeat,NeckTailGrilled,MeatCooked,FishCooked,SerpentMeatCooked", groupDescr());
			((BaseUnityPlugin)this).Config.Bind<string>("ItemGroup", "food", "CarrotSoup,Sausages,QueensJam,SerpentStew,TurnipStew,BloodPudding", groupDescr());
			unloadAllEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Unload", "enabled", false, "Enables Unload-all option & UI button [↓↓↓]. Allows to batch-unload all eligible items from players inventory to their corresponding stacks & groups in nearby chests.");
			unloadAllInsteadStackBtn = ((BaseUnityPlugin)this).Config.Bind<bool>("Unload", "nativeButton", false, "Use existing 'place-stacks' UI button for unloading. If disabled - new UI button [↓↓↓] will be created.");
			unloadAllBtnPos = ((BaseUnityPlugin)this).Config.Bind<Vector2>("Unload", "btnPos", new Vector2(1.5f, 8.15f), (ConfigDescription)null);
			unloadAllItemsGroupKeyModifier = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Unload", "addToItemsFilterKeyModifier", new KeyboardShortcut((KeyCode)304, Array.Empty<KeyCode>()), "If pressed - overrides createGroupBtn behaviour by passing item-names to unload 'itemsList' instead of creating new items group");
			unloadForceGrouping = ((BaseUnityPlugin)this).Config.Bind<bool>("Unload", "alwaysGrouping", true, "If enabled - check for groupingKeyModifier pressed is ignored for 'unload' button");
			unloadAllSkipItemsGroupKeyModifier = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Unload", "addToSkipItemsFilterKeyModifier", new KeyboardShortcut((KeyCode)306, Array.Empty<KeyCode>()), "If pressed - overrides createGroupBtn behaviour by passing item-names to unload 'itemsSkipList' instead of creating new items group");
			unloadAllItemsList = ((BaseUnityPlugin)this).Config.Bind<string>("Unload", "itemsList", "", groupDescr("List of item-names allowed to be 'unloaded'"));
			unloadAllSkipList = ((BaseUnityPlugin)this).Config.Bind<string>("Unload", "itemsSkipList", "", groupDescr("List of item-names to exclude from being 'unloaded'"));
			unloadAllGroupsList = ((BaseUnityPlugin)this).Config.Bind<string>("Unload", "groupsList", "valuables,ore,wood,mushrooms", "List of items group-ids from [ItemGroup] config section allowed to be 'unloaded'");
			unloadAllPrefixItemGroups = ((BaseUnityPlugin)this).Config.Bind<string>("Unload", "prefixItemGroups", "trophy", (ConfigDescription)null);
			unloadAllPostfixItemGroups = ((BaseUnityPlugin)this).Config.Bind<string>("Unload", "postfixItemGroups", "seeds", (ConfigDescription)null);
			unloadAllMaterialsFiltering = ((BaseUnityPlugin)this).Config.Bind<bool>("Unload", "materialsFiltering", true, "allow all Mater