Decompiled source of ChestButler v2.1.3

ChestButler.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ChestButler.Core;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using MultiUserChest;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("ChestButler")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("2.1.3.0")]
[assembly: AssemblyInformationalVersion("2.1.3")]
[assembly: AssemblyProduct("ChestButler")]
[assembly: AssemblyTitle("ChestButler")]
[assembly: AssemblyVersion("2.1.3.0")]
namespace ChestButler
{
	[BepInPlugin("eksolutions.chestbutler", "ChestButler", "2.1.3")]
	[BepInProcess("valheim.exe")]
	[BepInProcess("valheim_server.exe")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModGuid = "eksolutions.chestbutler";

		public const string ModName = "ChestButler";

		public const string ModVersion = "2.1.3";

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		internal static ConfigEntry<float> SorterRadius;

		internal static ConfigEntry<float> TransferInterval;

		internal static ConfigEntry<int> StacksPerTick;

		internal static ConfigEntry<bool> ContainsFallback;

		internal static ConfigEntry<bool> FreeChestFallback;

		internal static ConfigEntry<bool> VehiclesAreStorage;

		private Harmony _harmony;

		internal static ConfigEntry<float> StationRange => OrganizeConfig.StationRange;

		private void Awake()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Expected O, but got Unknown
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected O, but got Unknown
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Expected O, but got Unknown
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Expected O, but got Unknown
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Expected O, but got Unknown
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Expected O, but got Unknown
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Expected O, but got Unknown
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			SorterRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Sorting", "Radius", 128f, new ConfigDescription("Radius (m) around a sorter chest in which target chests are searched.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 128f), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			TransferInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Sorting", "TransferInterval", 1f, new ConfigDescription("Seconds between transfer ticks per sorter. Client-side: raise it if the mod costs you frames.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			StacksPerTick = ((BaseUnityPlugin)this).Config.Bind<int>("Sorting", "StacksPerTick", 2, new ConfigDescription("How many item stacks a sorter moves per tick. Client-side.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>()));
			ContainsFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Sorting", "ContainsFallback", true, new ConfigDescription("Route items to chests that already contain them when no explicit filter matches.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			FreeChestFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Sorting", "FreeChestFallback", true, new ConfigDescription("When no chest holds an item yet, put it in the nearest empty chest, or the chest with the most free slots if none is empty. Chests with a pin, a sign label or a station next to them are never used for this, and empty Organize homes are kept for their category.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			VehiclesAreStorage = ((BaseUnityPlugin)this).Config.Bind<bool>("Sorting", "VehiclesAreStorage", false, new ConfigDescription("Treat cart and ship inventories as storage. Off (default): the sorter, Organize, Pull and Gather all ignore vehicles entirely - they are transport, and their own Pin/Pull buttons still work for manual loading.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			Groups.Init(((BaseUnityPlugin)this).Config);
			Stations.Init(((BaseUnityPlugin)this).Config);
			OrganizeConfig.Init(((BaseUnityPlugin)this).Config);
			Gather.Init(((BaseUnityPlugin)this).Config);
			PullerConfig.Init(((BaseUnityPlugin)this).Config);
			SorterChestPiece.Register();
			PullerChestPiece.Register();
			_harmony = new Harmony("eksolutions.chestbutler");
			_harmony.PatchAll();
			Log.LogInfo((object)"ChestButler 2.1.3 loaded");
		}
	}
}
namespace ChestButler.Patches
{
	[HarmonyPatch(typeof(Hud))]
	internal static class BuildGatherPatch
	{
		internal static bool InBuildInfo { get; private set; }

		[HarmonyPrefix]
		[HarmonyPatch("SetupPieceInfo")]
		private static void SetupPieceInfoPrefix()
		{
			InBuildInfo = true;
		}

		[HarmonyPostfix]
		[HarmonyPatch("SetupPieceInfo")]
		private static void SetupPieceInfoPostfix()
		{
			InBuildInfo = false;
		}
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class Container_Awake_Patch
	{
		private static void Postfix(Container __instance)
		{
			ContainerTracker.Register(__instance);
			if ((Object)(object)((Component)__instance).GetComponent<SorterBehaviour>() == (Object)null && (Object)(object)((Component)__instance).GetComponentInParent<Piece>() != (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<SorterBehaviour>();
			}
			if ((Object)(object)((Component)__instance).GetComponent<PullerBehaviour>() == (Object)null && (Object)(object)((Component)__instance).GetComponentInParent<Piece>() != (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<PullerBehaviour>();
			}
		}
	}
	[HarmonyPatch(typeof(Container), "OnDestroyed")]
	internal static class Container_OnDestroyed_Patch
	{
		private static void Prefix(Container __instance)
		{
			ContainerTracker.Unregister(__instance);
		}
	}
	[HarmonyPatch(typeof(InventoryGui))]
	internal static class GatherPatch
	{
		private static Button _gatherBtn;

		private static TMP_Text _gatherLabel;

		private static readonly List<GatherNeed> Needs = new List<GatherNeed>();

		private static readonly HashSet<string> Seen = new HashSet<string>();

		private static bool _onlyOneIngredient;

		private static List<Container> _sources;

		private const int MaxNeeds = 16;

		private static readonly FieldInfo SelectedRecipeField = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe");

		private static readonly MethodInfo RecipeGetter = ((SelectedRecipeField != null) ? AccessTools.PropertyGetter(SelectedRecipeField.FieldType, "Recipe") : null);

		private static bool _reflectionWarned;

		private static Recipe SelectedRecipe(InventoryGui gui)
		{
			if ((Object)(object)gui == (Object)null || SelectedRecipeField == null || RecipeGetter == null)
			{
				if (!_reflectionWarned)
				{
					_reflectionWarned = true;
					Plugin.Log.LogWarning((object)"[gather] could not reach InventoryGui.m_selectedRecipe; require-only-one-ingredient recipes will be treated as normal ones");
				}
				return null;
			}
			try
			{
				object value = SelectedRecipeField.GetValue(gui);
				return (Recipe)((value == null) ? null : /*isinst with value type is only supported in some contexts*/);
			}
			catch (Exception ex)
			{
				if (!_reflectionWarned)
				{
					_reflectionWarned = true;
					Plugin.Log.LogWarning((object)("[gather] reading m_selectedRecipe failed: " + ex.Message));
				}
				return null;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("SetupRequirementList")]
		private static void SetupRequirementListPrefix(InventoryGui __instance)
		{
			Needs.Clear();
			Seen.Clear();
			_sources = null;
			Recipe val = SelectedRecipe(__instance);
			_onlyOneIngredient = (Object)(object)val != (Object)null && val.m_requireOnlyOneIngredient;
		}

		[HarmonyPostfix]
		[HarmonyPatch("SetupRequirement")]
		private static void SetupRequirementPostfix(Transform elementRoot, Requirement req, Player player, bool craft, int quality, int craftMultiplier, bool __result)
		{
			if (!__result || req?.m_resItem?.m_itemData?.m_shared == null || (Object)(object)player == (Object)null)
			{
				return;
			}
			string name = req.m_resItem.m_itemData.m_shared.m_name;
			if (string.IsNullOrEmpty(name))
			{
				return;
			}
			int amount = req.GetAmount(quality);
			if (amount <= 0)
			{
				return;
			}
			int needed = amount * Mathf.Max(1, craftMultiplier);
			Inventory inventory = ((Humanoid)player).GetInventory();
			int inPlayer = ((inventory != null) ? inventory.CountItems(name, -1, true) : 0);
			bool inBuildInfo = BuildGatherPatch.InBuildInfo;
			List<Container> sources;
			if (inBuildInfo)
			{
				sources = BuildGather.SourcesCached();
			}
			else
			{
				if (_sources == null)
				{
					_sources = Gatherer.Sources();
				}
				sources = _sources;
			}
			int num = Gatherer.CountInStorage(sources, name);
			if (!inBuildInfo && Seen.Add(name) && Needs.Count < 16)
			{
				Needs.Add(new GatherNeed
				{
					SharedName = name,
					Display = Names.Normalize(name),
					Needed = needed,
					InPlayer = inPlayer,
					InStorage = num
				});
			}
			if (Gather.CountsShown && num > 0)
			{
				Annotate(elementRoot, num);
			}
		}

		private static void Annotate(Transform elementRoot, int inStorage)
		{
			if ((Object)(object)elementRoot == (Object)null)
			{
				return;
			}
			TMP_Text val = null;
			Transform val2 = elementRoot.Find("res_amount");
			if ((Object)(object)val2 != (Object)null)
			{
				val = ((Component)val2).GetComponent<TMP_Text>();
			}
			if ((Object)(object)val == (Object)null)
			{
				TMP_Text[] componentsInChildren = ((Component)elementRoot).GetComponentsInChildren<TMP_Text>(true);
				if (componentsInChildren != null && componentsInChildren.Length != 0)
				{
					val = componentsInChildren[^1];
				}
			}
			if (!((Object)(object)val == (Object)null) && (val.text == null || !val.text.Contains("  <color=#9BE07A>")))
			{
				val.textWrappingMode = (TextWrappingModes)0;
				val.overflowMode = (TextOverflowModes)0;
				TMP_Text obj = val;
				obj.text = obj.text + "  <color=#9BE07A><size=85%>(" + inStorage + ")</size></color>";
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("Show")]
		private static void ShowPostfix(InventoryGui __instance)
		{
			EnsureButton(__instance);
		}

		[HarmonyPostfix]
		[HarmonyPatch("UpdateRecipe")]
		private static void UpdateRecipePostfix(InventoryGui __instance)
		{
			EnsureButton(__instance);
			RefreshButton();
		}

		[HarmonyPostfix]
		[HarmonyPatch("Hide")]
		private static void HidePostfix()
		{
			if ((Object)(object)_gatherBtn != (Object)null)
			{
				((Component)_gatherBtn).gameObject.SetActive(false);
			}
		}

		private static void EnsureButton(InventoryGui gui)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			//IL_00c6: 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_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_gatherBtn != (Object)null || (Object)(object)gui == (Object)null)
			{
				return;
			}
			Button craftButton = gui.m_craftButton;
			if ((Object)(object)craftButton == (Object)null)
			{
				return;
			}
			RectTransform component = ((Component)craftButton).GetComponent<RectTransform>();
			if (!((Object)(object)component == (Object)null) && !((Object)(object)((Transform)component).parent == (Object)null))
			{
				Button val = Object.Instantiate<Button>(craftButton, ((Transform)component).parent);
				((Object)val).name = "psort_gather";
				Localize componentInChildren = ((Component)val).GetComponentInChildren<Localize>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)componentInChildren);
				}
				UIGamePad[] componentsInChildren = ((Component)val).GetComponentsInChildren<UIGamePad>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
				}
				val.onClick = new ButtonClickedEvent();
				((UnityEvent)val.onClick).AddListener(new UnityAction(OnGatherClick));
				RectTransform component2 = ((Component)val).GetComponent<RectTransform>();
				component2.anchorMin = component.anchorMin;
				component2.anchorMax = component.anchorMax;
				component2.pivot = component.pivot;
				component2.sizeDelta = component.sizeDelta;
				Vector2 anchoredPosition = component.anchoredPosition;
				Rect rect = component.rect;
				component2.anchoredPosition = anchoredPosition - new Vector2(0f, ((Rect)(ref rect)).height + 6f);
				_gatherLabel = ((Component)val).GetComponentInChildren<TMP_Text>();
				if ((Object)(object)_gatherLabel != (Object)null)
				{
					float fontSize = _gatherLabel.fontSize;
					_gatherLabel.enableAutoSizing = true;
					_gatherLabel.fontSizeMax = fontSize;
					_gatherLabel.fontSizeMin = fontSize - 4f;
					_gatherLabel.text = "Gather";
				}
				_gatherBtn = val;
			}
		}

		private static void RefreshButton()
		{
			if ((Object)(object)_gatherBtn == (Object)null)
			{
				return;
			}
			if (!Gather.IsEnabled)
			{
				((Component)_gatherBtn).gameObject.SetActive(false);
				return;
			}
			List<GatherNeed> list = GatherMath.Resolve(Needs, _onlyOneIngredient);
			int num = 0;
			foreach (GatherNeed item in list)
			{
				num += item.Gatherable;
			}
			bool flag = num > 0;
			((Component)_gatherBtn).gameObject.SetActive(Needs.Count > 0);
			((Selectable)_gatherBtn).interactable = flag;
			if ((Object)(object)_gatherLabel != (Object)null)
			{
				_gatherLabel.text = (flag ? ("Gather (" + num + ")") : "Gather");
			}
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)instance.m_craftButton != (Object)null && ((Component)_gatherBtn).gameObject.activeInHierarchy)
			{
				GamepadNav.LinkVertical((Selectable)(object)instance.m_craftButton, (Selectable)(object)_gatherBtn, onlyIfVanillaEmpty: false);
			}
		}

		private static void OnGatherClick()
		{
			if (Gather.IsEnabled && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				List<GatherNeed> list = GatherMath.Resolve(Needs, _onlyOneIngredient);
				if (list.Count == 0)
				{
					Msg((Needs.Count == 0) ? "Select a recipe first" : "Nothing to gather - nearby chests have none of what this needs");
					return;
				}
				Gatherer.Pull(list, out var movedTotal, out var typesMoved);
				Msg((movedTotal > 0) ? ("Gathered " + movedTotal + " item" + ((movedTotal == 1) ? "" : "s") + " (" + typesMoved + " type" + ((typesMoved == 1) ? "" : "s") + ")") : "Nothing could be gathered - your inventory may be full");
			}
		}

		private static void Msg(string text)
		{
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui))]
	internal static class GuiPatch
	{
		private static RectTransform _bar;

		private static Button _sorterBtn;

		private static Button _pinBtn;

		private static Button _clearBtn;

		private static Button _pullBtn;

		private static Button _organizeBtn;

		private static TMP_Text _sorterLabel;

		private static TMP_Text _pinLabel;

		private static TMP_Text _clearLabel;

		private static TMP_Text _pullLabel;

		private static TMP_Text _organizeLabel;

		private static Container _current;

		private static readonly FieldRef<InventoryGui, InventoryGrid> ContainerGridRef = AccessTools.FieldRefAccess<InventoryGui, InventoryGrid>("m_containerGrid");

		private static readonly FieldRef<InventoryGrid, RectTransform> GridRootRef = AccessTools.FieldRefAccess<InventoryGrid, RectTransform>("m_gridRoot");

		private static float _lastBarY = float.NaN;

		private static readonly Vector3[] Corners = (Vector3[])(object)new Vector3[4];

		private const float ConfirmWindow = 5f;

		private const float MinConfirmDelay = 0.3f;

		private static OrganizePlan _pendingPlan;

		private static Container _pendingChest;

		private static float _pendingAt;

		[HarmonyPostfix]
		[HarmonyPatch("Show")]
		private static void ShowPostfix(InventoryGui __instance, Container container)
		{
			if ((Object)(object)container != (Object)(object)_pendingChest)
			{
				ClearPending();
			}
			_current = container;
			_lastBarY = float.NaN;
			EnsureBar(__instance);
			PositionBar(__instance);
			Refresh();
		}

		[HarmonyPostfix]
		[HarmonyPatch("UpdateContainer")]
		private static void UpdateContainerPostfix(InventoryGui __instance)
		{
			if (!((Object)(object)_current == (Object)null) && !((Object)(object)_bar == (Object)null) && ((Component)_bar).gameObject.activeSelf)
			{
				PositionBar(__instance);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("Hide")]
		private static void HidePostfix()
		{
			_current = null;
			ClearPending();
			if ((Object)(object)_bar != (Object)null)
			{
				((Component)_bar).gameObject.SetActive(false);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("Update")]
		private static void UpdatePostfix()
		{
			if (_pendingPlan != null && Time.time - _pendingAt > 5f)
			{
				ClearPending();
			}
		}

		private static void EnsureBar(InventoryGui gui)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Expected O, but got Unknown
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Expected O, but got Unknown
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Expected O, but got Unknown
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Expected O, but got Unknown
			if ((Object)(object)_bar != (Object)null)
			{
				return;
			}
			Button takeAllButton = gui.m_takeAllButton;
			if (!((Object)(object)takeAllButton == (Object)null))
			{
				RectTransform component = ((Component)takeAllButton).GetComponent<RectTransform>();
				Transform parent = ((Transform)component).parent;
				if (!((Object)(object)((parent is RectTransform) ? parent : null) == (Object)null))
				{
					GameObject val = new GameObject("psort_bar", new Type[1] { typeof(RectTransform) });
					_bar = (RectTransform)val.transform;
					((Transform)_bar).SetParent(((Transform)component).parent, false);
					_bar.anchorMin = new Vector2(0f, 0f);
					_bar.anchorMax = new Vector2(0f, 0f);
					_bar.pivot = new Vector2(0f, 1f);
					HorizontalLayoutGroup obj = val.AddComponent<HorizontalLayoutGroup>();
					((HorizontalOrVerticalLayoutGroup)obj).spacing = 8f;
					((LayoutGroup)obj).childAlignment = (TextAnchor)6;
					((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
					((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
					((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
					((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
					ContentSizeFitter obj2 = val.AddComponent<ContentSizeFitter>();
					obj2.horizontalFit = (FitMode)2;
					obj2.verticalFit = (FitMode)2;
					_sorterBtn = MakeButton(takeAllButton, "psort_toggle", new UnityAction(OnSorterClick), out _sorterLabel);
					_pinBtn = MakeButton(takeAllButton, "psort_pin", new UnityAction(OnPinClick), out _pinLabel);
					_clearBtn = MakeButton(takeAllButton, "psort_clear", new UnityAction(OnClearClick), out _clearLabel);
					_pullBtn = MakeButton(takeAllButton, "psort_pull", new UnityAction(OnPullClick), out _pullLabel);
					_organizeBtn = MakeButton(takeAllButton, "psort_organize", new UnityAction(OnOrganizeClick), out _organizeLabel);
				}
			}
		}

		private unsafe static void PositionBar(InventoryGui gui)
		{
			//IL_003d: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: 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_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_bar == (Object)null)
			{
				return;
			}
			Button takeAllButton = gui.m_takeAllButton;
			if ((Object)(object)takeAllButton == (Object)null)
			{
				return;
			}
			RectTransform component = ((Component)takeAllButton).GetComponent<RectTransform>();
			Transform parent = ((Transform)component).parent;
			RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Rect rect = val.rect;
			Rect val2 = component.rect;
			float width = ((Rect)(ref val2)).width;
			val2 = component.rect;
			float height = ((Rect)(ref val2)).height;
			float num = ((Rect)(ref rect)).x + ((Rect)(ref rect)).width * component.anchorMin.x;
			float num2 = ((Rect)(ref rect)).y + ((Rect)(ref rect)).height * component.anchorMin.y;
			float num3 = num + component.anchoredPosition.x + (0.5f - component.pivot.x) * width;
			float num4 = num2 + component.anchoredPosition.y + (0.5f - component.pivot.y) * height;
			float num5 = num3 - width * 0.5f - ((Rect)(ref rect)).xMin;
			float num6 = ((Rect)(ref rect)).yMax - (num4 + height * 0.5f) + height;
			if (TryGetUsedGridBottom(gui, val, out var bottomLocalY) && ((Rect)(ref rect)).yMin + num6 > bottomLocalY - 6f)
			{
				num6 = bottomLocalY - 6f - ((Rect)(ref rect)).yMin;
				float num7 = 0f - height;
				if (num6 < num7)
				{
					num6 = num7;
				}
			}
			if (float.IsNaN(_lastBarY) || Mathf.Abs(num6 - _lastBarY) > 0.5f)
			{
				_bar.anchoredPosition = new Vector2(num5, num6);
				_lastBarY = num6;
				ManualLogSource log = Plugin.Log;
				string[] obj = new string[8] { "[ui] panel=", null, null, null, null, null, null, null };
				val2 = rect;
				obj[1] = ((object)(*(Rect*)(&val2))/*cast due to .constrained prefix*/).ToString();
				obj[2] = " usedGridBottom=";
				obj[3] = bottomLocalY.ToString();
				obj[4] = " barY=";
				obj[5] = num6.ToString();
				obj[6] = " leftMargin=";
				obj[7] = num5.ToString();
				log.LogDebug((object)string.Concat(obj));
			}
		}

		private static bool TryGetUsedGridBottom(InventoryGui gui, RectTransform space, out float bottomLocalY)
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			bottomLocalY = 0f;
			InventoryGrid val = ContainerGridRef.Invoke(gui);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			RectTransform val2 = GridRootRef.Invoke(val);
			if ((Object)(object)val2 == (Object)null || ((Transform)val2).childCount == 0)
			{
				return false;
			}
			float num = float.MaxValue;
			bool flag = false;
			for (int i = 0; i < ((Transform)val2).childCount; i++)
			{
				Transform child = ((Transform)val2).GetChild(i);
				if ((Object)(object)child == (Object)null || !((Component)child).gameObject.activeSelf)
				{
					continue;
				}
				RectTransform val3 = (RectTransform)(object)((child is RectTransform) ? child : null);
				if (!((Object)(object)val3 == (Object)null))
				{
					val3.GetWorldCorners(Corners);
					float y = ((Transform)space).InverseTransformPoint(Corners[0]).y;
					if (y < num)
					{
						num = y;
						flag = true;
					}
				}
			}
			if (!flag)
			{
				return false;
			}
			bottomLocalY = num;
			return true;
		}

		private static Button MakeButton(Button template, string name, UnityAction onClick, out TMP_Text label)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			Button val = Object.Instantiate<Button>(template, (Transform)(object)_bar);
			((Object)val).name = name;
			Localize componentInChildren = ((Component)val).GetComponentInChildren<Localize>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)componentInChildren);
			}
			UIGamePad[] componentsInChildren = ((Component)val).GetComponentsInChildren<UIGamePad>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			val.onClick = new ButtonClickedEvent();
			((UnityEvent)val.onClick).AddListener(onClick);
			RectTransform component = ((Component)template).GetComponent<RectTransform>();
			LayoutElement obj = ((Component)val).gameObject.AddComponent<LayoutElement>();
			Rect rect = component.rect;
			obj.preferredWidth = ((Rect)(ref rect)).width;
			rect = component.rect;
			obj.preferredHeight = ((Rect)(ref rect)).height;
			label = ((Component)val).GetComponentInChildren<TMP_Text>();
			if ((Object)(object)label != (Object)null)
			{
				float fontSize = label.fontSize;
				label.enableAutoSizing = true;
				label.fontSizeMax = fontSize;
				label.fontSizeMin = fontSize - 4f;
			}
			return val;
		}

		private static void OnSorterClick()
		{
			if (!((Object)(object)_current == (Object)null))
			{
				bool flag = !SorterZdo.IsSorter(_current);
				SorterZdo.SetSorter(_current, flag);
				ClearPending();
				Msg(flag ? "Sorter enabled. Contents distribute when the chest is closed" : "Sorter disabled");
				Refresh();
			}
		}

		private static void OnPinClick()
		{
			if ((Object)(object)_current == (Object)null)
			{
				return;
			}
			if (Filters.GetPinned(_current).Count <= 0)
			{
				if (Filters.PinContents(_current) > 0)
				{
					Filters.SetManual(_current, manual: false);
					List<string> pinned = Filters.GetPinned(_current);
					Msg("Pinned: " + string.Join(", ", pinned) + " (auto-fill on)");
					Plugin.Log.LogInfo((object)("[pin] " + string.Join(", ", pinned)));
				}
				else
				{
					bool flag = !Filters.GetManual(_current);
					Filters.SetManual(_current, flag);
					Msg(flag ? "Chest is empty - marked Manual. Organize will not claim or fill it" : "Manual off. Organize may claim this empty chest as a home");
				}
			}
			else
			{
				bool flag2 = !Filters.GetManual(_current);
				Filters.SetManual(_current, flag2);
				Msg(flag2 ? "Auto-fill off. This chest only fills when you click Pull" : "Auto-fill on. The sorter routes matching items here");
			}
			Refresh();
		}

		private static void OnClearClick()
		{
			if (!((Object)(object)_current == (Object)null))
			{
				Filters.ClearPinned(_current);
				Filters.SetManual(_current, manual: false);
				Filters.ClearHome(_current);
				Msg("Filters cleared");
				Refresh();
			}
		}

		private static void OnPullClick()
		{
			if (!((Object)(object)_current == (Object)null))
			{
				Puller.PullInto(_current, out var movedTotal, out var typesMoved);
				Msg((movedTotal > 0) ? ("Pulled " + movedTotal + " item" + ((movedTotal == 1) ? "" : "s") + " (" + typesMoved + " type" + ((typesMoved == 1) ? "" : "s") + ")") : "Nothing to pull from nearby chests");
			}
		}

		private static void OnOrganizeClick()
		{
			if ((Object)(object)_current == (Object)null)
			{
				return;
			}
			if (_pendingPlan != null && (Object)(object)_pendingChest == (Object)(object)_current && Time.time - _pendingAt <= 5f)
			{
				if (!(Time.time - _pendingAt < 0.3f))
				{
					Container current = _current;
					ClearPending();
					OrganizePlan organizePlan = Organizer.BuildPlan(current, Plugin.SorterRadius.Value);
					if (organizePlan.IsEmpty)
					{
						Msg("Nothing left to organize");
					}
					else
					{
						Organizer.Execute(organizePlan);
					}
				}
				return;
			}
			OrganizePlan organizePlan2 = Organizer.BuildPlan(_current, Plugin.SorterRadius.Value);
			if (organizePlan2.IsEmpty)
			{
				ClearPending();
				Msg("Nothing to organize");
				return;
			}
			_pendingPlan = organizePlan2;
			_pendingChest = _current;
			_pendingAt = Time.time;
			OrganizeSummary summary = organizePlan2.Summary;
			if ((Object)(object)_organizeLabel != (Object)null)
			{
				_organizeLabel.text = "Confirm?";
			}
			Plugin.Log.LogInfo((object)("[organize] plan ready: " + summary.TotalItems + " items -> " + summary.TargetChests + " chest(s) from " + summary.SourceChests + " source(s); awaiting confirm"));
			string text = "Organize: move " + summary.TotalItems + " item" + ((summary.TotalItems == 1) ? "" : "s") + " across " + summary.TargetChests + " chest" + ((summary.TargetChests == 1) ? "" : "s");
			if (summary.HomelessItems > 0)
			{
				text = text + " (" + summary.HomelessItems + " won't fit - add more chests)";
			}
			Msg(text + " - press again to confirm");
		}

		private static void ClearPending()
		{
			_pendingPlan = null;
			_pendingChest = null;
			_pendingAt = 0f;
			if ((Object)(object)_organizeLabel != (Object)null)
			{
				_organizeLabel.text = "Organize";
			}
		}

		private static void Refresh()
		{
			if ((Object)(object)_bar == (Object)null)
			{
				return;
			}
			bool flag = (Object)(object)_current != (Object)null && SorterZdo.HasValidNView(_current) && !PullerChestPiece.IsPullerChest(_current);
			((Component)_bar).gameObject.SetActive(flag);
			if (!flag)
			{
				return;
			}
			bool flag2 = SorterZdo.IsSorter(_current);
			_sorterLabel.text = (flag2 ? "Sorter: ON" : "Sorter: OFF");
			bool flag3 = !flag2;
			bool flag4 = false;
			bool flag5 = false;
			if (flag3)
			{
				int count = Filters.GetPinned(_current).Count;
				bool manual = Filters.GetManual(_current);
				FilterSpec spec = Filters.GetSpec(_current);
				_pinLabel.text = ((count != 0) ? (manual ? ("Manual (" + count + ")") : ("Auto (" + count + ")")) : (manual ? "Manual" : "Pin"));
				flag4 = count > 0 || manual || !string.IsNullOrEmpty(spec.Home);
				if (flag4)
				{
					_clearLabel.text = "Clear";
				}
				flag5 = spec.HasExplicit;
				if (flag5)
				{
					_pullLabel.text = "Pull";
				}
			}
			if (flag2)
			{
				_organizeLabel.text = ((_pendingPlan != null && (Object)(object)_pendingChest == (Object)(object)_current) ? "Confirm?" : "Organize");
			}
			((Component)_pinBtn).gameObject.SetActive(flag3);
			((Component)_clearBtn).gameObject.SetActive(flag4);
			((Component)_pullBtn).gameObject.SetActive(flag5);
			((Component)_organizeBtn).gameObject.SetActive(flag2);
			LinkGamepadNav();
		}

		private static void LinkGamepadNav()
		{
			if (!((Object)(object)_bar == (Object)null))
			{
				List<Selectable> row = new List<Selectable>
				{
					(Selectable)(object)_sorterBtn,
					(Selectable)(object)_pinBtn,
					(Selectable)(object)_clearBtn,
					(Selectable)(object)_pullBtn,
					(Selectable)(object)_organizeBtn
				};
				GamepadNav.LinkRow(row);
				InventoryGui instance = InventoryGui.instance;
				if ((Object)(object)instance != (Object)null && (Object)(object)instance.m_takeAllButton != (Object)null)
				{
					GamepadNav.AttachRowToAnchor((Selectable)(object)instance.m_takeAllButton, row);
				}
			}
		}

		private static void Msg(string text)
		{
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryHandler), "RPC_RequestItemRemoveResponse", new Type[]
	{
		typeof(Inventory),
		typeof(RequestChestRemoveResponse)
	})]
	internal static class InventoryHandler_RemoveResponse_Patch
	{
		private static void Postfix(RequestChestRemoveResponse response)
		{
			if (response != null)
			{
				MucResults.RecordRemove(response.SourceID, response.Success, response.Amount);
			}
		}
	}
	[HarmonyPatch]
	internal static class PlaceGatherPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(Player), "UpdatePlacement", new Type[2]
			{
				typeof(bool),
				typeof(float)
			}, (Type[])null);
		}

		[HarmonyPrefix]
		private static void Prefix(Player __instance, bool takeInput)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			if (!Gather.IsEnabled || !Gather.IsBuildEnabled || (Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || !takeInput || !((Character)__instance).InPlaceMode() || Hud.IsPieceSelectionVisible() || __instance.PlacementCostDisabled || (!ZInput.GetButtonDown("Attack") && !ZInput.GetButtonDown("JoyPlace")) || ZInput.GetButton("AltPlace") || ZInput.GetButton("JoyAltKeys"))
			{
				return;
			}
			Piece selectedPiece = __instance.GetSelectedPiece();
			if ((Object)(object)selectedPiece == (Object)null || selectedPiece.m_resources == null || selectedPiece.m_resources.Length == 0 || __instance.HaveRequirements(selectedPiece, (RequirementMode)0) || (int)__instance.GetPlacementStatus() != 0)
			{
				return;
			}
			BuildGather.Invalidate();
			List<GatherNeed> list = GatherMath.Resolve(BuildGather.NeedsFor(selectedPiece), onlyOneIngredient: false);
			if (list.Count != 0)
			{
				Gatherer.Pull(list, out var movedTotal, out var typesMoved);
				BuildGather.Invalidate();
				if (movedTotal > 0)
				{
					Plugin.Log.LogInfo((object)("[gather] place-triggered pull: " + movedTotal + " item(s), " + typesMoved + " type(s) for " + ((Object)selectedPiece).name));
				}
			}
		}
	}
	[HarmonyPatch(typeof(Smelter), "Awake")]
	internal static class Smelter_Awake_Patch
	{
		private static void Postfix(Smelter __instance)
		{
			Stations.RegisterProcessor((Component)(object)__instance, __instance.m_name);
		}
	}
	[HarmonyPatch(typeof(Smelter), "OnDestroyed")]
	internal static class Smelter_OnDestroyed_Patch
	{
		private static void Prefix(Smelter __instance)
		{
			Stations.UnregisterProcessor((Component)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(Fermenter), "Awake")]
	internal static class Fermenter_Awake_Patch
	{
		private static void Postfix(Fermenter __instance)
		{
			Stations.RegisterProcessor((Component)(object)__instance, __instance.m_name);
		}
	}
	[HarmonyPatch(typeof(Fermenter), "OnDestroyed")]
	internal static class Fermenter_OnDestroyed_Patch
	{
		private static void Prefix(Fermenter __instance)
		{
			Stations.UnregisterProcessor((Component)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(CookingStation), "Awake")]
	internal static class CookingStation_Awake_Patch
	{
		private static void Postfix(CookingStation __instance)
		{
			Stations.RegisterProcessor((Component)(object)__instance, __instance.m_name);
		}
	}
	[HarmonyPatch(typeof(CookingStation), "OnDestroyed")]
	internal static class CookingStation_OnDestroyed_Patch
	{
		private static void Prefix(CookingStation __instance)
		{
			Stations.UnregisterProcessor((Component)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(InventoryGui))]
	internal static class PullerPanelPatch
	{
		private const float CensusInterval = 1f;

		private const float Pad = 12f;

		private const float TitleHeight = 32f;

		private const float SearchHeight = 34f;

		private const float StatusHeight = 26f;

		private const float ButtonHeight = 34f;

		private static readonly FieldRef<InventoryGui, Container> CurrentContainerRef = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer");

		private static readonly FieldInfo BuildSearchField = AccessTools.Field(typeof(BuildUi), "m_searchField");

		private static RectTransform _panel;

		private static TMP_Text _title;

		private static TMP_InputField _search;

		private static ScrollRect _scroll;

		private static RectTransform _content;

		private static TMP_Text _status;

		private static Button _sendBack;

		private static float _rowHeight = 30f;

		private static readonly List<GameObject> Rows = new List<GameObject>();

		private static readonly List<string> RowNames = new List<string>();

		private static Container _chest;

		private static bool _hidCrafting;

		private static List<PullerStorage.Entry> _census = new List<PullerStorage.Entry>();

		private static string _censusKey = "";

		private static float _censusAt = float.NegativeInfinity;

		private const int RowsPerNotch = 3;

		internal static bool SearchFocused
		{
			get
			{
				if ((Object)(object)_chest != (Object)null && (Object)(object)_search != (Object)null && _search.isFocused && (Object)(object)_panel != (Object)null)
				{
					return ((Component)_panel).gameObject.activeInHierarchy;
				}
				return false;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("Show")]
		private static void ShowPostfix(InventoryGui __instance, Container container)
		{
			if ((Object)(object)container != (Object)null && PullerChestPiece.IsPullerChest(container))
			{
				Open(__instance, container);
			}
			else
			{
				Close(__instance);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("Hide")]
		private static void HidePostfix(InventoryGui __instance)
		{
			Close(__instance);
		}

		[HarmonyPostfix]
		[HarmonyPatch("UpdateContainer")]
		private static void UpdateContainerPostfix(InventoryGui __instance)
		{
			if ((Object)(object)_chest == (Object)null)
			{
				if (_hidCrafting || ((Object)(object)_panel != (Object)null && ((Component)_panel).gameObject.activeSelf))
				{
					Close(__instance);
				}
				return;
			}
			if ((Object)(object)CurrentContainerRef.Invoke(__instance) != (Object)(object)_chest)
			{
				Close(__instance);
				return;
			}
			if ((Object)(object)__instance.m_crafting != (Object)null && ((Component)__instance.m_crafting).gameObject.activeSelf)
			{
				((Component)__instance.m_crafting).gameObject.SetActive(false);
				_hidCrafting = true;
			}
			ScrollWithWheel();
			if (Time.unscaledTime - _censusAt >= 1f)
			{
				RefreshCensus(force: false);
			}
		}

		private static bool PointerOverList()
		{
			//IL_003c: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_scroll == (Object)null || (Object)(object)_scroll.viewport == (Object)null)
			{
				return false;
			}
			RectTransform viewport = _scroll.viewport;
			Canvas componentInParent = ((Component)viewport).GetComponentInParent<Canvas>();
			Camera val = (((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null);
			return RectTransformUtility.RectangleContainsScreenPoint(viewport, Vector2.op_Implicit(ZInput.pointerPosition), val);
		}

		private static void ScrollWithWheel()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_scroll == (Object)null || (Object)(object)_content == (Object)null || (Object)(object)_scroll.viewport == (Object)null)
			{
				return;
			}
			float mouseScrollWheel = ZInput.GetMouseScrollWheel();
			if (!Mathf.Approximately(mouseScrollWheel, 0f) && PointerOverList())
			{
				RectTransform viewport = _scroll.viewport;
				Rect rect = _content.rect;
				float height = ((Rect)(ref rect)).height;
				rect = viewport.rect;
				float num = height - ((Rect)(ref rect)).height;
				if (!(num <= 0f))
				{
					float num2 = 3f * _rowHeight / num;
					_scroll.verticalNormalizedPosition = Mathf.Clamp01(_scroll.verticalNormalizedPosition + Mathf.Sign(mouseScrollWheel) * num2);
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("Update")]
		private static void UpdatePrefix()
		{
			if (SearchFocused)
			{
				ZInput.ResetButtonStatus("Use");
				ZInput.ResetButtonStatus("Inventory");
			}
		}

		private static void Open(InventoryGui gui, Container chest)
		{
			if (EnsurePanel(gui))
			{
				_chest = chest;
				if ((Object)(object)gui.m_crafting != (Object)null && ((Component)gui.m_crafting).gameObject.activeSelf)
				{
					((Component)gui.m_crafting).gameObject.SetActive(false);
					_hidCrafting = true;
				}
				((Component)_panel).gameObject.SetActive(true);
				((Transform)_panel).SetAsLastSibling();
				if ((Object)(object)_search != (Object)null)
				{
					_search.text = "";
				}
				RefreshCensus(force: true);
				if ((Object)(object)_scroll != (Object)null)
				{
					_scroll.verticalNormalizedPosition = 1f;
				}
			}
		}

		private static void Close(InventoryGui gui)
		{
			_chest = null;
			if ((Object)(object)_search != (Object)null && _search.isFocused)
			{
				_search.DeactivateInputField(false);
			}
			if ((Object)(object)_panel != (Object)null && ((Component)_panel).gameObject.activeSelf)
			{
				((Component)_panel).gameObject.SetActive(false);
			}
			if (_hidCrafting)
			{
				_hidCrafting = false;
				if ((Object)(object)gui != (Object)null && (Object)(object)gui.m_crafting != (Object)null)
				{
					((Component)gui.m_crafting).gameObject.SetActive(true);
				}
			}
		}

		private static void RefreshCensus(bool force)
		{
			_censusAt = Time.unscaledTime;
			if ((Object)(object)_chest == (Object)null || (!force && PointerOverList()))
			{
				return;
			}
			List<PullerStorage.Entry> list = PullerStorage.Census(_chest);
			StringBuilder stringBuilder = new StringBuilder();
			foreach (PullerStorage.Entry item in list)
			{
				stringBuilder.Append(item.SharedName).Append('=').Append(item.Count)
					.Append(';');
			}
			string text = stringBuilder.ToString();
			if (force || !(text == _censusKey))
			{
				_census = list;
				_censusKey = text;
				Rebuild();
			}
		}

		private static void Rebuild()
		{
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_content == (Object)null)
			{
				return;
			}
			string text = (((Object)(object)_search != (Object)null) ? (_search.text ?? "").Trim() : "");
			int num = 0;
			foreach (PullerStorage.Entry censu in _census)
			{
				if (text.Length <= 0 || censu.Display.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0 || censu.SharedName.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					GameObject val = RowAt(num);
					if ((Object)(object)val == (Object)null)
					{
						break;
					}
					FillRow(val, censu);
					RowNames[num] = censu.SharedName;
					num++;
				}
			}
			for (int i = num; i < Rows.Count; i++)
			{
				if ((Object)(object)Rows[i] != (Object)null && Rows[i].activeSelf)
				{
					Rows[i].SetActive(false);
				}
			}
			_content.sizeDelta = new Vector2(_content.sizeDelta.x, (float)num * _rowHeight);
			if ((Object)(object)_status != (Object)null)
			{
				_status.text = ((num > 0) ? "Click pulls a stack, Shift-click pulls all" : ((_census.Count == 0) ? "Nothing in storage nearby" : "No matches"));
			}
		}

		private static void OnRowClick(int index)
		{
			if (!((Object)(object)_chest == (Object)null) && index >= 0 && index < RowNames.Count)
			{
				string text = RowNames[index];
				if (!string.IsNullOrEmpty(text))
				{
					bool all = ZInput.GetKey((KeyCode)304, false) || ZInput.GetKey((KeyCode)303, false);
					int num = PullerStorage.Pull(_chest, text, all);
					string text2 = ((Localization.instance != null) ? Localization.instance.Localize(text) : text);
					Msg((num > 0) ? ("Pulling " + num + " " + text2) : ("Couldn't pull " + text2 + ", the chest may be full"));
					_censusAt = Time.unscaledTime - 1f + 0.3f;
				}
			}
		}

		private static bool EnsurePanel(InventoryGui gui)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: 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_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_panel != (Object)null)
			{
				return true;
			}
			if ((Object)(object)gui == (Object)null || (Object)(object)gui.m_crafting == (Object)null)
			{
				return false;
			}
			Rows.Clear();
			RowNames.Clear();
			_title = null;
			_search = null;
			_scroll = null;
			_content = null;
			_status = null;
			_sendBack = null;
			RectTransform crafting = gui.m_crafting;
			Transform parent = ((Transform)crafting).parent;
			RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			try
			{
				GameObject val2 = new GameObject("psort_puller_panel", new Type[1] { typeof(RectTransform) });
				_panel = (RectTransform)val2.transform;
				((Transform)_panel).SetParent((Transform)(object)val, false);
				_panel.anchorMin = crafting.anchorMin;
				_panel.anchorMax = crafting.anchorMax;
				_panel.pivot = crafting.pivot;
				_panel.anchoredPosition = crafting.anchoredPosition;
				_panel.sizeDelta = crafting.sizeDelta;
				((Transform)_panel).localScale = ((Transform)crafting).localScale;
				Image val3 = val2.AddComponent<Image>();
				Image component = ((Component)crafting).GetComponent<Image>();
				if ((Object)(object)component != (Object)null && (Object)(object)component.sprite != (Object)null)
				{
					val3.sprite = component.sprite;
					val3.type = component.type;
					((Graphic)val3).color = ((Graphic)component).color;
					val3.pixelsPerUnitMultiplier = component.pixelsPerUnitMultiplier;
				}
				else
				{
					((Graphic)val3).color = new Color(0f, 0f, 0f, 0.8f);
				}
				((Graphic)val3).raycastTarget = true;
				BuildTitle(gui);
				BuildSearch();
				BuildStatus(gui);
				BuildSendBack(gui);
				BuildList(gui);
				((Component)_panel).gameObject.SetActive(false);
				ManualLogSource log = Plugin.Log;
				string[] obj = new string[7] { "[puller] panel built over the crafting panel (", null, null, null, null, null, null };
				Rect rect = crafting.rect;
				obj[1] = ((Rect)(ref rect)).width.ToString("0");
				obj[2] = "x";
				rect = crafting.rect;
				obj[3] = ((Rect)(ref rect)).height.ToString("0");
				obj[4] = ", search ";
				obj[5] = (((Object)(object)_search != (Object)null) ? "on" : "unavailable");
				obj[6] = ")";
				log.LogInfo((object)string.Concat(obj));
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[puller] could not build the panel: " + ex));
				if ((Object)(object)_panel != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)_panel).gameObject);
				}
				_panel = null;
				_search = null;
				_scroll = null;
				_content = null;
				Rows.Clear();
				RowNames.Clear();
				return false;
			}
		}

		private static void BuildTitle(InventoryGui gui)
		{
			if (!((Object)(object)gui.m_containerName == (Object)null))
			{
				_title = Object.Instantiate<TMP_Text>(gui.m_containerName, (Transform)(object)_panel, false);
				((Object)_title).name = "psort_puller_title";
				StripLocalize(((Component)_title).gameObject);
				_title.text = "Pull from storage";
				_title.alignment = (TextAlignmentOptions)514;
				Stretch(_title.rectTransform, 12f, -44f, -12f, -12f, top: true);
			}
		}

		private static void BuildSearch()
		{
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Expected O, but got Unknown
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Expected O, but got Unknown
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Expected O, but got Unknown
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Expected O, but got Unknown
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Expected O, but got Unknown
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Expected O, but got Unknown
			Hud instance = Hud.instance;
			TMP_InputField val = (TMP_InputField)(((Object)(object)instance != (Object)null && (Object)(object)instance.m_buildUi != (Object)null && BuildSearchField != null) ? /*isinst with value type is only supported in some contexts*/: null);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"[puller] build menu search field not found; the Puller list will have no search box");
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, (Transform)(object)_panel, false);
			((Object)val2).name = "psort_puller_search";
			val2.SetActive(true);
			MonoBehaviour[] componentsInChildren = val2.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val3 in componentsInChildren)
			{
				string name = ((object)val3).GetType().Name;
				if (name == "UIInputHint" || name == "UIGamePad")
				{
					Object.DestroyImmediate((Object)(object)val3);
				}
			}
			_search = val2.GetComponent<TMP_InputField>();
			if ((Object)(object)_search == (Object)null)
			{
				Object.Destroy((Object)(object)val2);
				return;
			}
			_search.onValueChanged = new OnChangeEvent();
			_search.onEndEdit = new SubmitEvent();
			_search.onSubmit = new SubmitEvent();
			_search.onSelect = new SelectionEvent();
			_search.onDeselect = new SelectionEvent();
			_search.text = "";
			((UnityEvent<string>)(object)_search.onValueChanged).AddListener((UnityAction<string>)delegate
			{
				Rebuild();
			});
			LayoutElement component = val2.GetComponent<LayoutElement>();
			if ((Object)(object)component != (Object)null)
			{
				component.ignoreLayout = true;
			}
			float num = -48f;
			Stretch((RectTransform)val2.transform, 12f, num - 34f, -12f, num, top: true);
		}

		private static void BuildList(InventoryGui gui)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_004f: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: 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_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Expected O, but got Unknown
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			if (gui.m_recipeListSpace > 1f)
			{
				_rowHeight = gui.m_recipeListSpace;
			}
			GameObject val = new GameObject("psort_puller_list", new Type[1] { typeof(RectTransform) });
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent((Transform)(object)_panel, false);
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.pivot = new Vector2(0.5f, 0.5f);
			float num = 48f + (((Object)(object)_search != (Object)null) ? 42f : 0f);
			float num2 = 38f + (((Object)(object)_sendBack != (Object)null) ? 44f : 0f);
			val2.offsetMin = new Vector2(12f, num2);
			val2.offsetMax = new Vector2(-12f, 0f - num);
			((Graphic)val.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.25f);
			val.AddComponent<RectMask2D>();
			_content = (RectTransform)new GameObject("psort_puller_content", new Type[1] { typeof(RectTransform) }).transform;
			((Transform)_content).SetParent((Transform)(object)val2, false);
			_content.anchorMin = new Vector2(0f, 1f);
			_content.anchorMax = new Vector2(1f, 1f);
			_content.pivot = new Vector2(0.5f, 1f);
			_content.anchoredPosition = Vector2.zero;
			_content.sizeDelta = new Vector2(0f, 0f);
			_scroll = val.AddComponent<ScrollRect>();
			_scroll.horizontal = false;
			_scroll.vertical = true;
			_scroll.movementType = (MovementType)2;
			_scroll.scrollSensitivity = 0f;
			_scroll.viewport = val2;
			_scroll.content = _content;
		}

		private static void BuildStatus(InventoryGui gui)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)gui.m_containerName == (Object)null))
			{
				_status = Object.Instantiate<TMP_Text>(gui.m_containerName, (Transform)(object)_panel, false);
				((Object)_status).name = "psort_puller_status";
				StripLocalize(((Component)_status).gameObject);
				_status.alignment = (TextAlignmentOptions)514;
				_status.enableAutoSizing = true;
				_status.fontSizeMax = _status.fontSize * 0.8f;
				_status.fontSizeMin = _status.fontSize * 0.5f;
				_status.text = "";
				RectTransform rectTransform = _status.rectTransform;
				rectTransform.anchorMin = new Vector2(0f, 0f);
				rectTransform.anchorMax = new Vector2(1f, 0f);
				rectTransform.pivot = new Vector2(0.5f, 0f);
				rectTransform.offsetMin = new Vector2(12f, 6f);
				rectTransform.offsetMax = new Vector2(-12f, 32f);
			}
		}

		private static void BuildSendBack(InventoryGui gui)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)gui.m_takeAllButton == (Object)null)
			{
				return;
			}
			Button val = Object.Instantiate<Button>(gui.m_takeAllButton, (Transform)(object)_panel, false);
			((Object)val).name = "psort_puller_sendback";
			StripLocalize(((Component)val).gameObject);
			UIGamePad[] componentsInChildren = ((Component)val).GetComponentsInChildren<UIGamePad>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
			MonoBehaviour[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val2 in componentsInChildren2)
			{
				if (((object)val2).GetType().Name == "UITooltip")
				{
					Object.DestroyImmediate((Object)(object)val2);
				}
			}
			val.onClick = new ButtonClickedEvent();
			((UnityEvent)val.onClick).AddListener(new UnityAction(OnSendBackClick));
			TMP_Text componentInChildren = ((Component)val).GetComponentInChildren<TMP_Text>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				float fontSize = componentInChildren.fontSize;
				componentInChildren.enableAutoSizing = true;
				componentInChildren.fontSizeMax = fontSize;
				componentInChildren.fontSizeMin = fontSize - 4f;
				componentInChildren.text = "Send back";
			}
			RectTransform component = ((Component)val).GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, 0f);
			component.anchorMax = new Vector2(1f, 0f);
			component.pivot = new Vector2(0.5f, 0f);
			component.offsetMin = new Vector2(36f, 38f);
			component.offsetMax = new Vector2(-36f, 72f);
			((Transform)component).localScale = Vector3.one;
			_sendBack = val;
			List<Selectable> list = new List<Selectable>();
			if ((Object)(object)_search != (Object)null)
			{
				list.Add((Selectable)(object)_search);
			}
			list.Add((Selectable)(object)val);
			GamepadNav.LinkRow(list);
			if ((Object)(object)gui.m_takeAllButton != (Object)null)
			{
				GamepadNav.AttachRowToAnchor((Selectable)(object)gui.m_takeAllButton, list);
			}
		}

		private static void OnSendBackClick()
		{
			if (!((Object)(object)_chest == (Object)null))
			{
				int types;
				int num = PullerStorage.ReturnAll(_chest, out types);
				Msg((num > 0) ? ("Sent back " + num + " item" + ((num == 1) ? "" : "s") + " (" + types + " type" + ((types == 1) ? "" : "s") + ")") : "Nothing to send back, or no chest in range has room");
				_censusAt = Time.unscaledTime - 1f + 0.3f;
			}
		}

		private static GameObject RowAt(int index)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: 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)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Expected O, but got Unknown
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Expected O, but got Unknown
			while (Rows.Count <= index)
			{
				InventoryGui instance = InventoryGui.instance;
				if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_recipeElementPrefab == (Object)null || (Object)(object)_content == (Object)null)
				{
					return null;
				}
				GameObject val = Object.Instantiate<GameObject>(instance.m_recipeElementPrefab, (Transform)(object)_content, false);
				((Object)val).name = "psort_puller_row";
				int i = Rows.Count;
				RectTransform val2 = (RectTransform)val.transform;
				val2.anchorMin = new Vector2(0f, 1f);
				val2.anchorMax = new Vector2(1f, 1f);
				val2.pivot = new Vector2(0.5f, 1f);
				val2.sizeDelta = new Vector2(0f, _rowHeight);
				val2.anchoredPosition = new Vector2(0f, (float)(-i) * _rowHeight);
				Transform val3 = val.transform.Find("Durability");
				if ((Object)(object)val3 != (Object)null)
				{
					((Component)val3).gameObject.SetActive(false);
				}
				Transform val4 = val.transform.Find("QualityLevel");
				if ((Object)(object)val4 != (Object)null)
				{
					((Component)val4).gameObject.SetActive(false);
				}
				Transform val5 = val.transform.Find("selected");
				if ((Object)(object)val5 != (Object)null)
				{
					((Component)val5).gameObject.SetActive(false);
				}
				Button component = val.GetComponent<Button>();
				if ((Object)(object)component != (Object)null)
				{
					component.onClick = new ButtonClickedEvent();
					((UnityEvent)component.onClick).AddListener((UnityAction)delegate
					{
						OnRowClick(i);
					});
				}
				Rows.Add(val);
				RowNames.Add(null);
			}
			GameObject val6 = Rows[index];
			if ((Object)(object)val6 != (Object)null && !val6.activeSelf)
			{
				val6.SetActive(true);
			}
			return val6;
		}

		private static void FillRow(GameObject row, PullerStorage.Entry e)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			Transform val = row.transform.Find("icon");
			if ((Object)(object)val != (Object)null)
			{
				Image component = ((Component)val).GetComponent<Image>();
				if ((Object)(object)component != (Object)null)
				{
					component.sprite = e.Icon;
					((Graphic)component).color = Color.white;
				}
			}
			Transform val2 = row.transform.Find("name");
			if ((Object)(object)val2 != (Object)null)
			{
				TMP_Text component2 = ((Component)val2).GetComponent<TMP_Text>();
				if ((Object)(object)component2 != (Object)null)
				{
					component2.text = e.Display + "  <color=#9BE07A>" + e.Count + "</color>";
					((Graphic)component2).color = Color.white;
				}
			}
		}

		private static void Stretch(RectTransform rt, float left, float bottomY, float right, float topY, bool top)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			float num = (top ? 1f : 0f);
			rt.anchorMin = new Vector2(0f, num);
			rt.anchorMax = new Vector2(1f, num);
			rt.pivot = new Vector2(0.5f, num);
			rt.offsetMin = new Vector2(left, bottomY);
			rt.offsetMax = new Vector2(right, topY);
			((Transform)rt).localScale = Vector3.one;
		}

		private static void StripLocalize(GameObject go)
		{
			Localize[] componentsInChildren = go.GetComponentsInChildren<Localize>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
			}
		}

		private static void Msg(string text)
		{
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class PullerSearchFocusPatch
	{
		private static void Postfix(ref bool __result)
		{
			if (!__result && PullerPanelPatch.SearchFocused)
			{
				__result = true;
			}
		}
	}
	[HarmonyPatch(typeof(Sign), "Awake")]
	internal static class Sign_Awake_Patch
	{
		private static void Postfix(Sign __instance)
		{
			Filters.RegisterSign(__instance);
		}
	}
	[HarmonyPatch(typeof(Sign), "SetText")]
	internal static class Sign_SetText_Patch
	{
		private static void Postfix()
		{
			Filters.InvalidateAll();
		}
	}
}
namespace ChestButler.Core
{
	internal static class BucketKeys
	{
		internal const string GearPrefix = "gear:";

		internal const string Weapons = "gear:weapons";

		internal const string Armor = "gear:armor";

		internal const string Tools = "gear:tools";

		internal const string GearMisc = "gear:misc";

		internal const string TypePrefix = "item:";

		internal const string Misc = "misc";

		internal static string ForType(string norm)
		{
			return "item:" + norm;
		}

		internal static bool IsGear(string key)
		{
			return key?.StartsWith("gear:", StringComparison.Ordinal) ?? false;
		}

		internal static bool IsPerType(string key)
		{
			return key?.StartsWith("item:", StringComparison.Ordinal) ?? false;
		}

		internal static string TypeOf(string key)
		{
			if (!IsPerType(key))
			{
				return null;
			}
			return key.Substring("item:".Length);
		}

		internal static string Label(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return "?";
			}
			if (IsGear(key))
			{
				return key.Substring("gear:".Length);
			}
			if (IsPerType(key))
			{
				return key.Substring("item:".Length);
			}
			return key;
		}
	}
	internal static class BuildGather
	{
		private const float SourceMaxAge = 1f;

		private static List<Container> _sources;

		private static float _sourcesAt = float.NegativeInfinity;

		internal static List<Container> SourcesCached()
		{
			float unscaledTime = Time.unscaledTime;
			if (_sources == null || unscaledTime - _sourcesAt > 1f)
			{
				_sources = Gatherer.Sources();
				_sourcesAt = unscaledTime;
			}
			return _sources;
		}

		internal static void Invalidate()
		{
			_sources = null;
		}

		internal static List<GatherNeed> NeedsFor(Piece piece)
		{
			List<GatherNeed> list = new List<GatherNeed>();
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)piece == (Object)null || (Object)(object)localPlayer == (Object)null || piece.m_resources == null)
			{
				return list;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			List<Container> sources = SourcesCached();
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val in resources)
			{
				SharedData val2 = val?.m_resItem?.m_itemData?.m_shared;
				if (val2 != null && !string.IsNullOrEmpty(val2.m_name) && val.m_amount > 0)
				{
					list.Add(new GatherNeed
					{
						SharedName = val2.m_name,
						Display = Names.Normalize(val2.m_name),
						Needed = val.m_amount,
						InPlayer = ((inventory != null) ? inventory.CountItems(val2.m_name, -1, true) : 0),
						InStorage = Gatherer.CountInStorage(sources, val2.m_name)
					});
				}
			}
			return list;
		}
	}
	internal static class ContainerTracker
	{
		internal struct Candidate
		{
			internal Container Chest;

			internal float Distance;
		}

		private static readonly HashSet<Container> All = new HashSet<Container>();

		private static readonly HashSet<Container> Vehicles = new HashSet<Container>();

		internal static void Register(Container c)
		{
			if (!((Object)(object)c == (Object)null) && !((Object)(object)((Component)c).GetComponentInParent<Incinerator>() != (Object)null))
			{
				All.Add(c);
				if ((Object)(object)((Component)c).GetComponentInParent<Vagon>() != (Object)null || (Object)(object)((Component)c).GetComponentInParent<Ship>() != (Object)null)
				{
					Vehicles.Add(c);
				}
			}
		}

		internal static void Unregister(Container c)
		{
			if (!((Object)(object)c == (Object)null))
			{
				All.Remove(c);
				Vehicles.Remove(c);
				Filters.Invalidate(c);
			}
		}

		internal static List<Container> Candidates(Container sorter, float radius, bool excludeSorters = true)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return Accessible(((Component)sorter).transform.position, radius, sorter, excludeSorters);
		}

		internal static List<Container> AccessibleNear(Vector3 point, float radius, bool excludeSorters = false)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return Accessible(point, radius, null, excludeSorters);
		}

		private static List<Container> Accessible(Vector3 pos, float radius, Container exclude, bool excludeSorters)
		{
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			All.RemoveWhere((Container c) => (Object)(object)c == (Object)null);
			List<float> dists = new List<float>();
			List<Container> found = new List<Container>();
			bool flag = Plugin.VehiclesAreStorage != null && Plugin.VehiclesAreStorage.Value;
			foreach (Container item in All)
			{
				if (!((Object)(object)item == (Object)(object)exclude) && (flag || !Vehicles.Contains(item)) && SorterZdo.HasValidNView(item) && item.GetInventory() != null && !((Object)(object)((Component)item).GetComponentInParent<Piece>() == (Object)null))
				{
					float num = Vector3.Distance(pos, ((Component)item).transform.position);
					if (!(num > radius) && (!excludeSorters || !SorterZdo.IsSorter(item)) && SorterZdo.PlayerCanAccess(item) && PrivateArea.CheckAccess(((Component)item).transform.position, 0f, false, true))
					{
						dists.Add(num);
						found.Add(item);
					}
				}
			}
			List<int> list = new List<int>(found.Count);
			for (int num2 = 0; num2 < found.Count; num2++)
			{
				list.Add(num2);
			}
			list.Sort(delegate(int a, int b)
			{
				int num3 = dists[a].CompareTo(dists[b]);
				return (num3 != 0) ? num3 : CompareUid(found[a], found[b]);
			});
			List<Container> list2 = new List<Container>(list.Count);
			foreach (int item2 in list)
			{
				list2.Add(found[item2]);
			}
			return list2;
		}

		internal static List<Candidate> CandidatesWithDistance(Container sorter, float radius, bool excludeSorters = true)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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)
			List<Container> list = Candidates(sorter, radius, excludeSorters);
			List<Candidate> list2 = new List<Candidate>(list.Count);
			Vector3 position = ((Component)sorter).transform.position;
			foreach (Container item in list)
			{
				list2.Add(new Candidate
				{
					Chest = item,
					Distance = Vector3.Distance(position, ((Component)item).transform.position)
				});
			}
			return list2;
		}

		internal static int CompareUid(Container a, Container b)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			ZNetView val = SorterZdo.NView(a);
			ZNetView val2 = SorterZdo.NView(b);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				if (!((Object)(object)val2 == (Object)null) && val2.IsValid())
				{
					return 1;
				}
				return 0;
			}
			if ((Object)(object)val2 == (Object)null || !val2.IsValid())
			{
				return -1;
			}
			return ((ZDOID)(ref val.GetZDO().m_uid)).CompareTo(val2.GetZDO().m_uid);
		}

		internal static Container NearestTo(Vector3 point, float maxRange)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Container result = null;
			float num = maxRange;
			foreach (Container item in All)
			{
				if (!((Object)(object)item == (Object)null))
				{
					float num2 = Vector3.Distance(point, ((Component)item).transform.position);
					if (num2 < num)
					{
						num = num2;
						result = item;
					}
				}
			}
			return result;
		}
	}
	internal sealed class FilterSpec
	{
		internal readonly HashSet<string> Items = new HashSet<string>();

		internal readonly HashSet<string> GroupNames = new HashSet<string>();

		internal int Priority;

		internal bool Ignore;

		internal bool ManualOnly;

		internal string Home;

		internal bool HasExplicit
		{
			get
			{
				if (Items.Count <= 0)
				{
					return GroupNames.Count > 0;
				}
				return true;
			}
		}

		internal bool MatchesItem(string normName)
		{
			foreach (string item in Items)
			{
				if (Names.Matches(item, normName))
				{
					return true;
				}
			}
			return false;
		}

		internal bool MatchesGroup(string normName)
		{
			foreach (string groupName in GroupNames)
			{
				if (Groups.GroupContains(groupName, normName))
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class Filters
	{
		private const float SignRange = 2.5f;

		private const float CacheTtl = 30f;

		private static readonly int ItemsHash = StringExtensionMethods.GetStableHashCode("psort_items");

		private static readonly int ManualHash = StringExtensionMethods.GetStableHashCode("psort_manual");

		private static readonly int HomeHash = StringExtensionMethods.GetStableHashCode("psort_home");

		private static readonly HashSet<Sign> Signs = new HashSet<Sign>();

		private static readonly Dictionary<Container, KeyValuePair<float, FilterSpec>> Cache = new Dictionary<Container, KeyValuePair<float, FilterSpec>>();

		internal static void RegisterSign(Sign s)
		{
			if ((Object)(object)s != (Object)null)
			{
				Signs.Add(s);
				InvalidateAll();
			}
		}

		internal static void Invalidate(Container c)
		{
			if ((Object)(object)c != (Object)null)
			{
				Cache.Remove(c);
			}
		}

		internal static void InvalidateAll()
		{
			Cache.Clear();
		}

		internal static List<string> GetPinned(Container c)
		{
			List<string> list = new List<string>();
			ZNetView val = SorterZdo.NView(c);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return list;
			}
			string[] array = val.GetZDO().GetString(ItemsHash, "").Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					list.Add(text);
				}
			}
			return list;
		}

		internal static void SetPinned(Container c, IEnumerable<string> tokens)
		{
			ZNetView val = SorterZdo.NView(c);
			if (!((Object)(object)val == (Object)null) && val.IsValid())
			{
				if (!val.IsOwner())
				{
					val.ClaimOwnership();
				}
				val.GetZDO().Set(ItemsHash, string.Join(",", tokens));
				Cache.Remove(c);
			}
		}

		internal static int PinContents(Container c)
		{
			Inventory inventory = c.GetInventory();
			if (inventory == null)
			{
				return 0;
			}
			HashSet<string> hashSet = new HashSet<string>();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem?.m_shared != null)
				{
					string text = Names.Normalize(allItem.m_shared.m_name);
					if (text.Length > 0)
					{
						hashSet.Add(text);
					}
				}
			}
			if (hashSet.Count > 0)
			{
				SetPinned(c, hashSet);
			}
			return hashSet.Count;
		}

		internal static void ClearPinned(Container c)
		{
			SetPinned(c, new string[0]);
		}

		internal static bool GetManual(Container c)
		{
			ZNetView val = SorterZdo.NView(c);
			if ((Object)(object)val != (Object)null && val.IsValid())
			{
				return val.GetZDO().GetBool(ManualHash, false);
			}
			return false;
		}

		internal static void SetManual(Container c, bool manual)
		{
			ZNetView val = SorterZdo.NView(c);
			if (!((Object)(object)val == (Object)null) && val.IsValid())
			{
				if (!val.IsOwner())
				{
					val.ClaimOwnership();
				}
				val.GetZDO().Set(ManualHash, manual);
				Cache.Remove(c);
			}
		}

		internal static string GetHome(Container c)
		{
			ZNetView val = SorterZdo.NView(c);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return null;
			}
			string text = val.GetZDO().GetString(HomeHash, "");
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return null;
		}

		internal static void SetHome(Container c, string bucketKey)
		{
			ZNetView val = SorterZdo.NView(c);
			if (!((Object)(object)val == (Object)null) && val.IsValid())
			{
				if (!val.IsOwner())
				{
					val.ClaimOwnership();
				}
				val.GetZDO().Set(HomeHash, bucketKey ?? "");
				Cache.Remove(c);
			}
		}

		internal static void ClearHome(Container c)
		{
			SetHome(c, null);
		}

		internal static FilterSpec GetSpec(Container c)
		{
			if (Cache.TryGetValue(c, out var value) && Time.time - value.Key < 30f)
			{
				return value.Value;
			}
			FilterSpec filterSpec = new FilterSpec();
			foreach (string item in GetPinned(c))
			{
				filterSpec.Items.Add(item);
			}
			filterSpec.ManualOnly = GetManual(c);
			filterSpec.Home = GetHome(c);
			ParseNearestSign(c, filterSpec);
			if (PullerChestPiece.IsPullerChest(c))
			{
				filterSpec.Ignore = true;
			}
			Cache[c] = new KeyValuePair<float, FilterSpec>(Time.time, filterSpec);
			return filterSpec;
		}

		private static void ParseNearestSign(Container c, FilterSpec spec)
		{
			//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_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)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			Signs.RemoveWhere((Sign s) => (Object)(object)s == (Object)null);
			Vector3 position = ((Component)c).transform.position;
			foreach (Sign sign in Signs)
			{
				string text = sign.GetText();
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				SignSpec signSpec = SignGrammar.Parse(text);
				float num = Vector3.Distance(((Component)sign).transform.position, position);
				if (signSpec.HasOff && signSpec.AreaOffRadius > 0f && num <= signSpec.AreaOffRadius)
				{
					spec.Ignore = true;
				}
				if (num > 2.5f || !IsNearestContainerTo(((Component)sign).transform.position, c))
				{
					continue;
				}
				if (signSpec.HasOff)
				{
					spec.Ignore = true;
				}
				foreach (string token in signSpec.Tokens)
				{
					if (token.Length >= 2 && token[0] == 'p' && int.TryParse(token.Substring(1), out var result))
					{
						spec.Priority = result;
					}
					else if (Groups.IsGroup(token))
					{
						spec.GroupNames.Add(token);
					}
					else
					{
						spec.Items.Add(token);
					}
				}
			}
		}

		private static bool IsNearestContainerTo(Vector3 signPos, Container candidate)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			Container val = ContainerTracker.NearestTo(signPos, 2.5f);
			if (!((Object)(object)val == (Object)null))
			{
				return (Object)(object)val == (Object)(object)candidate;
			}
			return true;
		}
	}
	internal static class GamepadNav
	{
		private static ConfigEntry<bool> _enabled;

		private static bool _bound;

		internal static bool Enabled
		{
			get
			{
				//IL_003b: 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_0048: Expected O, but got Unknown
				//IL_0048: Unknown result type (might be due to invalid IL or missing references)
				//IL_0052: Expected O, but got Unknown
				if (!_bound)
				{
					_bound = true;
					Plugin instance = Plugin.Instance;
					if ((Object)(object)instance != (Object)null)
					{
						_enabled = ((BaseUnityPlugin)instance).Config.Bind<bool>("Gamepad", "Enabled", true, new ConfigDescription("Let a controller reach ChestButler's buttons by linking them into the panel's D-pad navigation. Client-side.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
						{
							IsAdminOnly = false
						} }));
					}
				}
				if (_enabled != null)
				{
					return _enabled.Value;
				}
				return true;
			}
		}

		internal static void LinkRow(IList<Selectable> row)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled || row == null)
			{
				return;
			}
			List<Selectable> list = new List<Selectable>();
			for (int i = 0; i < row.Count; i++)
			{
				Selectable val = row[i];
				if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeInHierarchy)
				{
					list.Add(val);
				}
			}
			if (list.Count != 0)
			{
				for (int j = 0; j < list.Count; j++)
				{
					Navigation navigation = list[j].navigation;
					((Navigation)(ref navigation)).mode = (Mode)4;
					((Navigation)(ref navigation)).selectOnLeft = ((j > 0) ? list[j - 1] : null);
					((Navigation)(ref navigation)).selectOnRight = ((j < list.Count - 1) ? list[j + 1] : null);
					list[j].navigation = navigation;
				}
			}
		}

		internal static void LinkVertical(Selectable above, Selectable below, bool onlyIfVanillaEmpty)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if (Enabled && !((Object)(object)above == (Object)null) && !((Object)(object)below == (Object)null))
			{
				Navigation navigation = above.navigation;
				Navigation navigation2 = below.navigation;
				bool flag = false;
				if (!onlyIfVanillaEmpty || (Object)(object)((Navigation)(ref navigation)).selectOnDown == (Object)null)
				{
					((Navigation)(ref navigation)).mode = (Mode)4;
					((Navigation)(ref navigation)).selectOnDown = below;
					above.navigation = navigation;
					flag = true;
				}
				((Navigation)(ref navigation2)).mode = (Mode)4;
				((Navigation)(ref navigation2)).selectOnUp = above;
				below.navigation = navigation2;
				if (!flag)
				{
					Plugin.Log.LogDebug((object)("[gamepad] '" + ((Object)above).name + "' already navigates down to '" + (((Object)(object)((Navigation)(ref navigation)).selectOnDown != (Object)null) ? ((Object)((Navigation)(ref navigation)).selectOnDown).name : "?") + "'; left it alone. Our button is reachable upward from '" + ((Object)below).name + "' only."));
				}
			}
		}

		internal static void AttachRowToAnchor(Selectable anchor, IList<Selectable> row)
		{
			if (!Enabled || (Object)(object)anchor == (Object)null || row == null)
			{
				return;
			}
			for (int i = 0; i < row.Count; i++)
			{
				Selectable val = row[i];
				if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy)
				{
					LinkVertical(anchor, val, onlyIfVanillaEmpty: true);
					break;
				}
			}
		}
	}
	internal static class Gather
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> BuildEnabled;

		internal static ConfigEntry<bool> ShowStorageCounts;

		internal static bool IsEnabled
		{
			get
			{
				if (Enabled != null)
				{
					return Enabled.Value;
				}
				return true;
			}
		}

		internal static bool IsBuildEnabled
		{
			get
			{
				if (BuildEnabled != null)
				{
					return BuildEnabled.Value;
				}
				return true;
			}
		}

		internal static bool CountsShown
		{
			get
			{
				if (ShowStorageCounts != null)
				{
					return ShowStorageCounts.Value;
				}
				return true;
			}
		}

		internal static void Init(ConfigFile config)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			Enabled = config.Bind<bool>("Gather", "Enabled", true, new ConfigDescription("Show the Gather button in the crafting panel. Client-side.", (AcceptableValueBase)null, Array.Empty<object>()));
			BuildEnabled = config.Bind<bool>("Gather", "BuildEnabled", true, new ConfigDescription("When you place a piece you can't afford, fetch the missing materials from nearby chests. Client-side.", (AcceptableValueBase)null, Array.Empty<object>()));
			ShowStorageCounts = config.Bind<bool>("Gather", "ShowStorageCounts", true, new ConfigDescription("Show \"(N in storage)\" beside each ingredient in the crafting panel. Client-side.", (AcceptableValueBase)null, Array.Empty<object>()));
		}
	}
	internal static class Gatherer
	{
		internal static List<Container> Sources()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			List<Container> list = new List<Container>();
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return list;
			}
			foreach (Container item in ContainerTracker.AccessibleNear(((Component)localPlayer).transform.position, Plugin.SorterRadius.Value))
			{
				if (!Filters.GetSpec(item).Ignore)
				{
					list.Add(item);
				}
			}
			return list;
		}

		internal static int CountInStorage(List<Container> sources, string sharedName)
		{
			if (sources == null || string.IsNullOrEmpty(sharedName))
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < sources.Count; i++)
			{
				Inventory inventory = sources[i].GetInventory();
				if (inventory != null)
				{
					num += inventory.CountItems(sharedName, -1, true);
				}
			}
			return num;
		}

		internal static void Pull(List<GatherNeed> needs, out int movedTotal, out int typesMoved)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			movedTotal = 0;
			typesMoved = 0;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || needs == null || needs.Count == 0)
			{
				return;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			if (inventory == null)
			{
				return;
			}
			ZDOID zDOID = ((Character)localPlayer).GetZDOID();
			List<Container> list = Sources();
			int num = 0;
			foreach (GatherNeed need in needs)
			{
				int num2 = need.Gatherable;
				if (num2 <= 0)
				{
					continue;
				}
				bool flag = false;
				List<KeyValuePair<int, Container>> list2 = new List<KeyValuePair<int, Container>>();
				for (int i = 0; i < list.Count; i++)
				{
					Inventory inventory2 = list[i].GetInventory();
					if (inventory2 != null)
					{
						int num3 = inventory2.CountItems(need.SharedName, -1, true);
						if (num3 > 0)
						{
							list2.Add(new KeyValuePair<int, Container>(num3, list[i]));
						}
					}
				}
				list2.Sort(delegate(KeyValuePair<int, Container> a, KeyValuePair<int, Container> b)
				{
					int num6 = b.Key.CompareTo(a.Key);
					return (num6 == 0) ? ContainerTracker.CompareUid(a.Value, b.Value) : num6;
				});
				foreach (KeyValuePair<int, Container> item in list2)
				{
					if (num2 <= 0)
					{
						break;
					}
					Container value = item.Value;
					Inventory inventory3 = value.GetInventory();
					if (inventory3 == null)
					{
						continue;
					}
					ZNetView val = SorterZdo.NView(value);
					if ((Object)(object)val == (Object)null || !val.IsValid() || !SorterZdo.PlayerCanAccess(value) || !PrivateArea.CheckAccess(((Component)value).transform.position, 0f, false, true))
					{
						continue;
					}
					InventoryBlock val2 = InventoryBlock.Get(inventory3);
					foreach (ItemData item2 in new List<ItemData>(inventory3.GetAllItems()))
					{
						if (num2 <= 0)
						{
							break;
						}
						if (item2?.m_shared != null && !(item2.m_shared.m_name != need.SharedName) && (val2 == null || !val2.IsSlotBlocked(item2.m_gridPos)))
						{
							int num4 = Router.Room(inventory, item2) - num;
							if (num4 <= 0)
							{
								num2 = 0;
								break;
							}
							int num5 = Math.Min(Math.Min(num2, item2.m_stack), num4);
							if (num5 > 0)
							{
								ContainerHandler.RemoveItemFromChest(value, item2, inventory, new Vector2i(-1, -1), zDOID, num5, (ItemData)null);
								num += num5;
								movedTotal += num5;
								num2 -= num5;
								flag = true;
							}
						}
					}
				}
				if (flag)
				{
					typesMoved++;
				}
			}
			if (movedTotal > 0)
			{
				Plugin.Log.LogInfo((object)("[gather] pulled " + movedTotal + " item(s) across " + typesMoved + " type(s)"));
			}
		}
	}
	internal struct GatherNeed
	{
		public string SharedName;

		public string Display;

		public int Needed;

		public int InPlayer;

		public int InStorage;

		public int Shortfall => Math.Max(0, Needed - InPlayer);

		public int Gatherable => Math.Min(Shortfall, InStorage);
	}
	internal static class GatherMath
	{
		internal static List<GatherNeed> Resolve(IList<GatherNeed> raw, bool onlyOneIngredient)
		{
			List<GatherNeed> list = new List<GatherNeed>();
			if (raw == null || raw.Count == 0)
			{
				return list;
			}
			if (!onlyOneIngredient)
			{
				for (int i = 0; i < raw.Count; i++)
				{
					if (raw[i].Gatherable > 0)
					{
						list.Add(raw[i]);
					}
				}
				return list;
			}
			int num = PickSingleIngredient(raw);
			if (num >= 0 && raw[num].Gatherable > 0)
			{
				list.Add(raw[num]);
			}
			return list;
		}

		internal static int PickSingleIngredient(IList<GatherNeed> raw)
		{
			for (int i = 0; i < raw.Count; i++)
			{
				if (raw[i].Shortfall == 0)
				{
					return -1;
				}
			}
			int num = -1;
			for (int j = 0; j < raw.Count; j++)
			{
				if (raw[j].Gatherable <= 0)
				{
					continue;
				}
				if (num < 0)
				{
					num = j;
					continue;
				}
				int num2 = raw[j].Shortfall - raw[j].Gatherable;
				int num3 = raw[num].Shortfall - raw[num].Gatherable;
				if (num2 < num3)
				{
					num = j;
				}
				else if (num2 <= num3)
				{
					if (raw[j].Shortfall < raw[num].Shortfall)
					{
						num = j;
					}
					else if (raw[j].Shortfall <= raw[num].Shortfall && string.CompareOrdinal(raw[j].SharedName ?? "", raw[num].SharedName ?? "") < 0)
					{
						num = j;
					}
				}
			}
			return num;
		}
	}
	internal static class Gear
	{
		private static readonly Dictionary<ItemType, string> ByType = new Dictionary<ItemType, string>
		{
			{
				(ItemType)3,
				"gear:weapons"
			},
			{
				(ItemType)14,
				"gear:weapons"
			},
			{
				(ItemType)22,
				"gear:weapons"
			},
			{
				(ItemType)4,
				"gear:weapons"
			},
			{
				(ItemType)5,
				"gear:weapons"
			},
			{
				(ItemType)15,
				"gear:weapons"
			},
			{
				(ItemType)20,
				"gear:weapons"
			},
			{
				(ItemType)6,
				"gear:armor"
			},
			{
				(ItemType)7,
				"gear:armor"
			},
			{
				(ItemType)11,
				"gear:armor"
			},
			{
				(ItemType)17,
				"gear:armor"
			},
			{
				(ItemType)12,
				"gear:armor"
			},
			{
				(ItemType)18,
				"gear:armor"
			},
			{
				(ItemType)24,
				"gear:armor"
			},
			{
				(ItemType)19,
				"gear:tools"
			}
		};

		private static ConfigEntry<string> _weaponTokens;

		private static ConfigEntry<string> _armorTokens;

		private static ConfigEntry<string> _toolTokens;

		private static readonly List<string> Weapon = new List<string>();

		private static readonly List<string> Armour = new List<string>();

		private static readonly List<string> Tool = new List<string>();

		private static readonly HashSet<string> Reported = new HashSet<string>();

		internal static void Init(ConfigFile config)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_0058: 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_0065: Expected O, but got Unknown
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			_toolTokens = config.Bind<string>("Organize", "ToolTokens", "pickaxe*", new ConfigDescription("Item name tokens ('*' wildcards) forced into the TOOLS gear bucket, overriding the item's own type. Default: pickaxes, which the game classifies as weapons. Comma-separated.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			_weaponTokens = config.Bind<string>("Organize", "WeaponTokens", "", new ConfigDescription("Item name tokens forced into the WEAPONS gear bucket. Comma-separated.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			_armorTokens = config.Bind<string>("Organize", "ArmorTokens", "", new ConfigDescription("Item name tokens forced into the ARMOR gear bucket. Comma-separated.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			config.SettingChanged += delegate
			{
				Rebuild();
			};
			Rebuild();
		}

		private static void Rebuild()
		{
			Fill(Weapon, _weaponTokens);
			Fill(Armour, _armorTokens);
			Fill(Tool, _toolTokens);
			Reported.Clear();
		}

		private static void Fill(List<string> into, ConfigEntry<string> entry)
		{
			into.Clear();
			if (entry == null || string.IsNullOrEmpty(entry.Value))
			{
				return;
			}
			string[] array = entry.Value.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim().ToLowerInvariant();
				if (text.Length > 0)
				{
					into.Add(text);
				}
			}
		}

		internal static string BucketFor(ItemData item, string norm)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (item?.m_shared == null)
			{
				return "gear:misc";
			}
			if (MatchesAny(Tool, norm))
			{
				return "gear:tools";
			}
			if (MatchesAny(Weapon, norm))
			{
				return "gear:weapons";
			}
			if (MatchesAny(Armour, norm))
			{
				return "gear:armor";
			}
			if (ByType.TryGetValue(item.m_shared.m_itemType, out var value))
			{
				return value;
			}
			if (Reported.Add(norm))
			{
				Plugin.Log.LogInfo((object)("[organize] '" + norm + "' (item type " + ((object)Unsafe.As<ItemType, ItemType>(ref item.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString() + ") has no gear bucket - filed under " + BucketKeys.Label("gear:misc") + ". Add it to [Organize] WeaponTokens/ArmorTokens/ToolTokens to place it."));
			}
			return "gear:misc";
		}

		private static bool MatchesAny(List<string> tokens, string norm)
		{
			for (int i = 0; i < tokens.Count; i++)
			{
				if (Names.Matches(tokens[i], norm))
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class Groups
	{
		private static readonly Dictionary<string, ConfigEntry<string>> Entries = new Dictionary<string, ConfigEntry<string>>();

		private static readonly Dictionary<string, List<string>> Parsed = new Dictionary<string, List<string>>();

		private static readonly List<string> Ordered = new List<string>();

		private static Dictionary<string, string> Defaults => GroupTables.Defaults;

		private static string[] GroupOrder => GroupTables.GroupOrder;

		internal static void Init(ConfigFile config)
		{
			//IL_00e6: 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)
			//IL_00f3: Expected O, but got Unknown
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Expected O, but got Unknown
			string[] groupOrder = GroupOrder;
			foreach (string text in groupOrder)
			{
				if (!Defaults.ContainsKey(text))
				{
					Plugin.Log.LogError((object)("[groups] GroupOrder lists '" + text + "', which is not a defined group"));
				}
			}
			foreach (KeyValuePair<string, string> @default in Defaults)
			{
				if (Array.IndexOf(GroupOrder, @default.Key) < 0)
				{
					Plugin.Log.LogError((object)("[groups] group '" + @default.Key + "' is missing from GroupOrder; overlap resolution would be non-deterministic for its items"));
				}
			}
			foreach (KeyValuePair<string, string> default2 in Defaults)
			{
				Entries[default2.Key] = config.Bind<string>("ItemGroups", default2.Key, default2.Value, new ConfigDescription("Comma-separated item name tokens ('*' wildcards allowed).", (AcceptableValueBase)null, new object[1] { (object)new Configuration