Decompiled source of NearbyStorage v1.0.1

Landoria.NearbyStorage.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Landoria.Shared;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Landoria.NearbyStorage")]
[assembly: AssemblyDescription("Stacks nearby items and crafts from nearby chests.")]
[assembly: AssemblyCompany("Landoria")]
[assembly: AssemblyProduct("Landoria.NearbyStorage")]
[assembly: ComVisible(false)]
[assembly: AssemblyFileVersion("1.0.1")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.1.24460")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Landoria.Shared
{
	internal static class ConfigWatcher
	{
		private const int ReloadDelayMilliseconds = 250;

		private const int MaximumReloadAttempts = 5;

		private static ConfigFile config;

		private static string configPath;

		private static ManualLogSource logger;

		private static FileSystemWatcher watcher;

		private static long lastChangeTicks;

		private static int reloadPending;

		private static int reloadAttempts;

		private static long ignoredWriteTicks;

		private static string displayName;

		private static Action restoreDefaults;

		private static Action applyConfiguration;

		internal static void Initialize(ConfigFile configFile, ManualLogSource logSource, string configurationDisplayName, Action restoreDefaultsAction, Action applyConfigurationAction = null)
		{
			Dispose();
			config = configFile;
			logger = logSource;
			displayName = configurationDisplayName;
			restoreDefaults = restoreDefaultsAction;
			applyConfiguration = applyConfigurationAction;
			configPath = Path.GetFullPath(config.ConfigFilePath);
			watcher = new FileSystemWatcher(Path.GetDirectoryName(configPath), Path.GetFileName(configPath));
			watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime;
			watcher.Changed += OnChanged;
			watcher.Created += OnChanged;
			watcher.Deleted += OnChanged;
			watcher.Renamed += OnRenamed;
			watcher.EnableRaisingEvents = true;
		}

		internal static void IgnoreCurrentFileVersion()
		{
			if (string.IsNullOrEmpty(configPath) || !File.Exists(configPath))
			{
				return;
			}
			try
			{
				Interlocked.Exchange(ref ignoredWriteTicks, File.GetLastWriteTimeUtc(configPath).Ticks);
				Interlocked.Exchange(ref reloadPending, 0);
			}
			catch (IOException)
			{
				ManualLogSource obj = logger;
				if (obj != null)
				{
					obj.LogDebug((object)"Could not mark the internal configuration write.");
				}
			}
		}

		internal static void Update()
		{
			if (Volatile.Read(in reloadPending) == 0 || !ReloadDelayElapsed())
			{
				return;
			}
			Interlocked.Exchange(ref reloadPending, 0);
			try
			{
				bool flag = !File.Exists(configPath);
				if (flag)
				{
					restoreDefaults?.Invoke();
				}
				else
				{
					config.Reload();
				}
				applyConfiguration?.Invoke();
				reloadAttempts = 0;
				logger.LogInfo((object)(flag ? "Recreated the deleted configuration with default values." : "Reloaded configuration after a file change."));
				ShowReloadMessage(flag);
			}
			catch (Exception exception)
			{
				RetryReload(exception);
			}
		}

		internal static void Dispose()
		{
			if (watcher != null)
			{
				watcher.EnableRaisingEvents = false;
				watcher.Dispose();
				watcher = null;
			}
			Interlocked.Exchange(ref reloadPending, 0);
			Interlocked.Exchange(ref ignoredWriteTicks, 0L);
			reloadAttempts = 0;
			displayName = null;
			restoreDefaults = null;
			applyConfiguration = null;
		}

		private static void OnChanged(object sender, FileSystemEventArgs eventArgs)
		{
			QueueReload();
		}

		private static void OnRenamed(object sender, RenamedEventArgs eventArgs)
		{
			QueueReload();
		}

		private static void QueueReload()
		{
			if (!IsIgnoredWrite())
			{
				Interlocked.Exchange(ref lastChangeTicks, DateTime.UtcNow.Ticks);
				Interlocked.Exchange(ref reloadPending, 1);
				reloadAttempts = 0;
			}
		}

		private static bool IsIgnoredWrite()
		{
			try
			{
				long num = (File.Exists(configPath) ? File.GetLastWriteTimeUtc(configPath).Ticks : 0);
				return num != 0L && num == Interlocked.Read(in ignoredWriteTicks);
			}
			catch (IOException)
			{
				return false;
			}
		}

		private static bool ReloadDelayElapsed()
		{
			return DateTime.UtcNow.Ticks - Interlocked.Read(in lastChangeTicks) >= 2500000;
		}

		private static void RetryReload(Exception exception)
		{
			reloadAttempts++;
			logger.LogWarning((object)("Configuration reload failed: " + exception.Message));
			if (reloadAttempts < 5)
			{
				Interlocked.Exchange(ref lastChangeTicks, DateTime.UtcNow.Ticks);
				Interlocked.Exchange(ref reloadPending, 1);
			}
		}

		private static void ShowReloadMessage(bool recreated)
		{
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)1, recreated ? (displayName + " configuration restored to defaults.") : (displayName + " configuration reloaded."), 0, (Sprite)null, false);
			}
		}
	}
}
namespace Landoria.NearbyStorage
{
	[HarmonyPatch(typeof(Player), "ConsumeResources", new Type[]
	{
		typeof(Requirement[]),
		typeof(int),
		typeof(int),
		typeof(int)
	})]
	internal static class BuildingDebitPatch
	{
		private static bool Prefix(Player __instance, Requirement[] requirements, int qualityLevel)
		{
			if (!BuildingState.Matches(__instance, requirements, qualityLevel))
			{
				return true;
			}
			BuildingState.Consume(__instance);
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	internal static class BuildingPlacementPatch
	{
		private static bool Prefix(Player __instance, Piece piece, ref bool __result)
		{
			if (BuildingState.Prepare(__instance, piece))
			{
				return true;
			}
			__result = false;
			return false;
		}

		private static void Postfix(bool __result)
		{
			if (!__result)
			{
				BuildingState.Reset();
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			if (__exception != null)
			{
				BuildingState.Reset();
			}
			return __exception;
		}
	}
	internal static class BuildingPlan
	{
		internal static bool IsBuildPiece(Player player, Piece piece)
		{
			if ((Object)(object)player != (Object)null && ((Character)player).InPlaceMode())
			{
				return (Object)(object)piece != (Object)null;
			}
			return false;
		}

		internal static CraftPlan Build(Player player, Piece piece)
		{
			if (!CanBuild(player, piece))
			{
				return null;
			}
			CraftPlan craftPlan = new CraftPlan();
			StorageSupply storageSupply = new StorageSupply(player);
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val in resources)
			{
				if (!((Object)(object)val.m_resItem == (Object)null) && val.m_amount > 0)
				{
					string name = val.m_resItem.m_itemData.m_shared.m_name;
					int amount = val.GetAmount(0);
					amount = storageSupply.AllocateAnyQuality(craftPlan, name, amount);
					if (amount > 0)
					{
						return null;
					}
				}
			}
			return craftPlan;
		}

		private static bool CanBuild(Player player, Piece piece)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			if (IsBuildPiece(player, piece) && (piece.m_dlc.Length == 0 || DLCMan.instance.IsDLCInstalled(piece.m_dlc)))
			{
				if (!((Object)(object)piece.m_craftingStation == (Object)null) && !Object.op_Implicit((Object)(object)CraftingStation.HaveBuildStationInRange(piece.m_craftingStation.m_name, ((Component)player).transform.position)))
				{
					return ZoneSystem.instance.GetGlobalKey((GlobalKeys)27);
				}
				return true;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
	{
		typeof(Piece),
		typeof(RequirementMode)
	})]
	internal static class BuildingRequirementsPatch
	{
		private static void Postfix(Player __instance, Piece piece, RequirementMode mode, ref bool __result)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			if (!__result && ((int)mode == 0 || (int)mode == 2) && (Object)(object)__instance == (Object)(object)Player.m_localPlayer && NearbyStorageActionInput.IsHeld() && BuildingPlan.IsBuildPiece(__instance, piece))
			{
				__result = BuildingPlan.Build(__instance, piece) != null;
			}
		}
	}
	internal static class BuildingState
	{
		private static CraftPlan _plan;

		private static Player _player;

		private static Piece _piece;

		internal static bool Matches(Player player, Requirement[] requirements, int qualityLevel)
		{
			if (_plan != null && (Object)(object)player == (Object)(object)_player && qualityLevel == 0)
			{
				return requirements == _piece.m_resources;
			}
			return false;
		}

		internal static bool Prepare(Player player, Piece piece)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			Reset();
			if ((Object)(object)player != (Object)(object)Player.m_localPlayer || !NearbyStorageActionInput.IsHeld() || !BuildingPlan.IsBuildPiece(player, piece) || player.NoCostCheat() || ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey()))
			{
				return true;
			}
			CraftPlan craftPlan = BuildingPlan.Build(player, piece);
			if (craftPlan == null || !craftPlan.ClaimSources(player) || !craftPlan.StillAvailable(player))
			{
				return false;
			}
			_player = player;
			_piece = piece;
			_plan = craftPlan;
			return true;
		}

		internal static void Consume(Player player)
		{
			if ((Object)(object)player != (Object)(object)_player || _plan == null)
			{
				return;
			}
			try
			{
				_plan.Consume();
			}
			finally
			{
				Reset();
			}
		}

		internal static void Reset()
		{
			_plan = null;
			_player = null;
			_piece = null;
		}
	}
	internal static class BuildingUiRefresh
	{
		private static readonly MethodInfo RefreshHudIcons = AccessTools.Method(typeof(Hud), "UpdatePieceBuildStatusAll", (Type[])null, (Type[])null);

		private static bool _lastHeld;

		internal static void Update()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !((Character)localPlayer).InPlaceMode())
			{
				_lastHeld = false;
				return;
			}
			bool flag = NearbyStorageActionInput.IsHeld();
			if (flag == _lastHeld)
			{
				return;
			}
			_lastHeld = flag;
			try
			{
				if ((Object)(object)Hud.instance != (Object)null && RefreshHudIcons != null)
				{
					RefreshHudIcons.Invoke(Hud.instance, new object[2]
					{
						localPlayer.GetBuildPieces(),
						localPlayer
					});
				}
				BuildUiPieceButton[] array = Object.FindObjectsByType<BuildUiPieceButton>((FindObjectsSortMode)0);
				foreach (BuildUiPieceButton val in array)
				{
					if (((Behaviour)val).isActiveAndEnabled && (Object)(object)val.Piece != (Object)null)
					{
						val.UpdateRequirements();
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("Nearby Storage build icon refresh failed: " + ex));
			}
		}

		internal static void Reset()
		{
			_lastHeld = false;
		}
	}
	internal sealed class ModConfigFile
	{
		private const float DefaultRadius = 60f;

		internal ConfigEntry<float> Radius { get; }

		internal ConfigEntry<bool> KeepOneIngredientPerChest { get; }

		internal ConfigEntry<KeyboardShortcut> NearbyStorageShortcut { get; }

		internal ModConfigFile(ConfigFile config)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			Radius = config.Bind<float>("General", "SearchRadius", 60f, new ConfigDescription("Radius around the player for nearby storage and ground items, in meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 100f), Array.Empty<object>()));
			KeepOneIngredientPerChest = config.Bind<bool>("General", "KeepOneIngredientPerChest", true, "Keep at least one of each item type in every storage container when using the nearby storage shortcut.");
			NearbyStorageShortcut = config.Bind<KeyboardShortcut>("Controls", "NearbyStorageShortcut", new KeyboardShortcut((KeyCode)308, Array.Empty<KeyCode>()), "Hold this shortcut to use nearby storage while crafting, building, refueling, or feeding.");
		}

		internal void RestoreDefaults(ConfigFile config)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			bool saveOnConfigSet = config.SaveOnConfigSet;
			config.SaveOnConfigSet = false;
			try
			{
				Radius.Value = 60f;
				KeepOneIngredientPerChest.Value = true;
				NearbyStorageShortcut.Value = new KeyboardShortcut((KeyCode)308, Array.Empty<KeyCode>());
			}
			finally
			{
				config.SaveOnConfigSet = saveOnConfigSet;
			}
			config.Save();
			ConfigWatcher.IgnoreCurrentFileVersion();
		}
	}
	internal static class NearbyStorageActionInput
	{
		internal static bool IsHeld()
		{
			//IL_0024: 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_002c: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.Instance?.Settings == null)
			{
				return false;
			}
			KeyboardShortcut value = Plugin.Instance.Settings.NearbyStorageShortcut.Value;
			if ((int)((KeyboardShortcut)(ref value)).MainKey == 0 || !ZInput.GetKey(((KeyboardShortcut)(ref value)).MainKey, true))
			{
				return false;
			}
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers)
			{
				if (!ZInput.GetKey(modifier, true))
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class ButtonHalo
	{
		private static Image _craft;

		private static Sprite _glowSprite;

		internal static void Update()
		{
			bool flag = InventoryGui.IsVisible() && (Object)(object)Player.m_localPlayer != (Object)null && NearbyStorageActionInput.IsHeld();
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null)
			{
				Clear();
			}
			else
			{
				Set(ref _craft, instance.m_craftButton, flag && ((Selectable)instance.m_craftButton).interactable);
			}
		}

		internal static void Clear()
		{
			if ((Object)(object)_craft != (Object)null)
			{
				((Behaviour)_craft).enabled = false;
			}
		}

		internal static void Dispose()
		{
			if ((Object)(object)_craft != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_craft).gameObject);
			}
			_craft = null;
			_glowSprite = null;
		}

		private static void Set(ref Image glow, Button button, bool active)
		{
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)button == (Object)null || (Object)(object)((Selectable)button).targetGraphic == (Object)null)
			{
				if ((Object)(object)glow != (Object)null)
				{
					((Behaviour)glow).enabled = false;
				}
				return;
			}
			if (!active || !((Component)button).gameObject.activeInHierarchy)
			{
				if ((Object)(object)glow != (Object)null)
				{
					((Behaviour)glow).enabled = false;
				}
				return;
			}
			if ((Object)(object)_glowSprite == (Object)null)
			{
				_glowSprite = FindGlowSprite();
				if ((Object)(object)_glowSprite == (Object)null)
				{
					return;
				}
			}
			if ((Object)(object)glow == (Object)null || (Object)(object)((Component)glow).transform.parent != (Object)(object)((Component)((Selectable)button).targetGraphic).transform)
			{
				glow = CreateGlow(glow, button);
			}
			((Behaviour)glow).enabled = true;
			float num = 0.5f + 0.15f * Mathf.Sin(Time.unscaledTime * 3f);
			((Graphic)glow).color = new Color(1f, 61f / 85f, 0.35686275f, num);
		}

		private static Sprite FindGlowSprite()
		{
			Sprite[] array = Resources.FindObjectsOfTypeAll<Sprite>();
			foreach (Sprite val in array)
			{
				if (((Object)val).name == "button_glow")
				{
					return val;
				}
			}
			return null;
		}

		private static Image CreateGlow(Image existingGlow, Button button)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: 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)
			if ((Object)(object)existingGlow != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)existingGlow).gameObject);
			}
			GameObject val = new GameObject("NearbyStorageGlow", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val.transform.SetParent(((Component)((Selectable)button).targetGraphic).transform, false);
			val.transform.SetAsFirstSibling();
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.offsetMin = Vector2.zero;
			val2.offsetMax = Vector2.zero;
			Image component = val.GetComponent<Image>();
			component.sprite = _glowSprite;
			component.type = (Type)1;
			((Graphic)component).raycastTarget = false;
			return component;
		}
	}
	[HarmonyPatch(typeof(Inventory), "ItemCheated", new Type[]
	{
		typeof(Requirement[]),
		typeof(int),
		typeof(bool)
	})]
	internal static class CheatedIngredientPatch
	{
		private static void Postfix(Inventory __instance, ref bool __result)
		{
			if (!__result && CraftState.Armed && CraftState.Plan != null && (Object)(object)Player.m_localPlayer != (Object)null && __instance == ((Humanoid)Player.m_localPlayer).GetInventory())
			{
				__result = CraftState.Plan.HasCheatedIngredient();
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnCraftCancelPressed")]
	internal static class CraftCancelPatch
	{
		private static void Postfix()
		{
			CraftState.Reset();
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnCraftPressed")]
	internal static class CraftClickPatch
	{
		private static bool Prefix(InventoryGui __instance)
		{
			return CraftState.Arm(__instance);
		}

		private static void Postfix(InventoryGui __instance)
		{
			CraftState.ResetIfNoTimer(__instance);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class CraftCompletionPatch
	{
		private static bool Prefix(InventoryGui __instance, Player player)
		{
			return CraftState.Prepare(__instance, player);
		}

		private static void Postfix()
		{
			CraftState.Reset();
		}
	}
	internal sealed class CraftPlan
	{
		internal readonly List<Withdrawal> Withdrawals = new List<Withdrawal>();

		internal ItemData SingleItem;

		internal int SingleNeed;

		internal int SingleExtra;

		internal static CraftPlan Build(Player player, Recipe recipe, int quality, int multiplier)
		{
			if (!CanPlan(player, recipe, multiplier))
			{
				return null;
			}
			CraftPlan craftPlan = new CraftPlan();
			StorageSupply supply = new StorageSupply(player);
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			bool flag = false;
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (!Relevant(val, currentCraftingStation))
				{
					continue;
				}
				int num = val.GetAmount(quality) * multiplier;
				if (num <= 0)
				{
					continue;
				}
				if (TryRequirement(craftPlan, supply, val, num, recipe.m_requireOnlyOneIngredient))
				{
					flag = true;
					if (recipe.m_requireOnlyOneIngredient)
					{
						craftPlan.SingleNeed = num;
						craftPlan.SingleExtra = val.m_extraAmountOnlyOneIngredient;
						break;
					}
				}
				else if (!recipe.m_requireOnlyOneIngredient)
				{
					return null;
				}
			}
			if (!(!recipe.m_requireOnlyOneIngredient || flag))
			{
				return null;
			}
			return craftPlan;
		}

		private static bool CanPlan(Player player, Recipe recipe, int multiplier)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)recipe != (Object)null)
			{
				return multiplier >= 1;
			}
			return false;
		}

		private static bool TryRequirement(CraftPlan plan, StorageSupply supply, Requirement requirement, int need, bool single)
		{
			string name = requirement.m_resItem.m_itemData.m_shared.m_name;
			int maxQuality = requirement.m_resItem.m_itemData.m_shared.m_maxQuality;
			for (int i = 1; i <= maxQuality; i++)
			{
				if (supply.Available(plan, name, i) >= need)
				{
					int num = supply.Allocate(plan, name, i, need);
					if (single)
					{
						plan.SingleItem = FindItem(plan, name, i);
					}
					return num == 0;
				}
			}
			return false;
		}

		private static ItemData FindItem(CraftPlan plan, string name, int quality)
		{
			foreach (Withdrawal withdrawal in plan.Withdrawals)
			{
				if (withdrawal.Name != name || withdrawal.Quality != quality)
				{
					continue;
				}
				if ((Object)(object)withdrawal.Drop != (Object)null)
				{
					return withdrawal.GroundItem;
				}
				foreach (ItemData allItem in withdrawal.Inventory.GetAllItems())
				{
					if (allItem.m_shared.m_name == name && allItem.m_quality == quality)
					{
						return allItem;
					}
				}
			}
			return null;
		}

		private static bool Relevant(Requirement requirement, CraftingStation station)
		{
			if ((Object)(object)requirement.m_resItem != (Object)null)
			{
				if (!((Object)(object)station == (Object)null) || requirement.m_upgraderResource)
				{
					if ((Object)(object)station != (Object)null)
					{
						return station.m_upgrader == requirement.m_upgraderResource;
					}
					return false;
				}
				return true;
			}
			return false;
		}

		internal bool ClaimSources(Player player)
		{
			foreach (Withdrawal withdrawal in Withdrawals)
			{
				if ((Object)(object)withdrawal.Chest != (Object)null && !StorageLocator.TryClaimAndLoad(withdrawal.Chest, player))
				{
					return false;
				}
				if ((Object)(object)withdrawal.Drop != (Object)null && !GroundSource.TryClaim(withdrawal.Drop, player, withdrawal.GroundItem, PlannedForDrop(withdrawal)))
				{
					return false;
				}
			}
			return true;
		}

		internal bool StillAvailable(Player player)
		{
			foreach (Withdrawal withdrawal in Withdrawals)
			{
				if ((Object)(object)withdrawal.Drop != (Object)null)
				{
					if (!GroundSource.TryClaim(withdrawal.Drop, player, withdrawal.GroundItem, PlannedForDrop(withdrawal)))
					{
						return false;
					}
					continue;
				}
				if ((Object)(object)withdrawal.Chest != (Object)null && !StorageLocator.Eligible(withdrawal.Chest, player, StorageLocator.CurrentContainer(), Plugin.Instance.Settings.Radius.Value))
				{
					return false;
				}
				int num = 0;
				foreach (Withdrawal withdrawal2 in Withdrawals)
				{
					if (withdrawal2.Inventory == withdrawal.Inventory && withdrawal2.Name == withdrawal.Name && withdrawal2.Quality == withdrawal.Quality)
					{
						num += withdrawal2.Amount;
					}
				}
				if (withdrawal.Inventory.CountItems(withdrawal.Name, withdrawal.Quality, true) < num)
				{
					return false;
				}
				if ((Object)(object)withdrawal.Chest != (Object)null && Plugin.Instance.Settings.KeepOneIngredientPerChest.Value && withdrawal.Inventory.CountItems(withdrawal.Name, -1, false) - PlannedForName(withdrawal) < 1)
				{
					return false;
				}
			}
			return true;
		}

		private int PlannedForName(Withdrawal step)
		{
			int num = 0;
			foreach (Withdrawal withdrawal in Withdrawals)
			{
				if (withdrawal.Inventory == step.Inventory && withdrawal.Name == step.Name)
				{
					num += withdrawal.Amount;
				}
			}
			return num;
		}

		private int PlannedForDrop(Withdrawal step)
		{
			int num = 0;
			foreach (Withdrawal withdrawal in Withdrawals)
			{
				if ((Object)(object)withdrawal.Drop == (Object)(object)step.Drop)
				{
					num += withdrawal.Amount;
				}
			}
			return num;
		}

		internal void Consume()
		{
			foreach (Withdrawal withdrawal in Withdrawals)
			{
				if ((Object)(object)withdrawal.Drop == (Object)null)
				{
					withdrawal.Inventory.RemoveItem(withdrawal.Name, withdrawal.Amount, withdrawal.Quality, true);
					continue;
				}
				for (int i = 0; i < withdrawal.Amount; i++)
				{
					if (!GroundSource.RemoveOne(withdrawal.Drop, Player.m_localPlayer, withdrawal.GroundItem))
					{
						break;
					}
				}
			}
		}

		internal bool HasCheatedIngredient()
		{
			foreach (Withdrawal withdrawal in Withdrawals)
			{
				if (((Object)(object)withdrawal.Drop != (Object)null) ? withdrawal.GroundItem.m_cheated : withdrawal.Inventory.ItemCheated(withdrawal.Name, withdrawal.Quality, true))
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class CraftState
	{
		private static readonly FieldInfo SelectedField = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe");

		private static readonly FieldInfo RecipeField = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe");

		private static readonly FieldInfo UpgradeField = AccessTools.Field(typeof(InventoryGui), "m_craftUpgradeItem");

		private static readonly FieldInfo MultiField = AccessTools.Field(typeof(InventoryGui), "m_multiCrafting");

		private static readonly FieldInfo TouchField = AccessTools.Field(typeof(InventoryGui), "m_touchMultiCrafting");

		private static readonly FieldInfo TimerField = AccessTools.Field(typeof(InventoryGui), "m_craftTimer");

		private static readonly PropertyInfo SelectedRecipe = AccessTools.Property(SelectedField.FieldType, "Recipe");

		private static readonly PropertyInfo SelectedItem = AccessTools.Property(SelectedField.FieldType, "ItemData");

		internal static bool Armed { get; private set; }

		internal static bool Consuming { get; private set; }

		internal static CraftPlan Plan { get; private set; }

		internal static bool Checking
		{
			get
			{
				if ((Object)(object)Plugin.Instance != (Object)null && InventoryGui.IsVisible())
				{
					if (!NearbyStorageActionInput.IsHeld())
					{
						return Armed;
					}
					return true;
				}
				return false;
			}
		}

		internal static bool Arm(InventoryGui gui)
		{
			Reset();
			if (!NearbyStorageActionInput.IsHeld())
			{
				return true;
			}
			object value = SelectedField.GetValue(gui);
			object? value2 = SelectedRecipe.GetValue(value);
			Recipe val = (Recipe)((value2 is Recipe) ? value2 : null);
			object? value3 = SelectedItem.GetValue(value);
			ItemData val2 = (ItemData)((value3 is ItemData) ? value3 : null);
			if ((Object)(object)val == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return false;
			}
			int multiplier = ((val2 != null || (!ZInput.GetButton("AltPlace") && !ZInput.GetButton("JoyLStick") && !(bool)TouchField.GetValue(gui))) ? 1 : gui.m_multiCraftAmount);
			int quality = ((val2 == null) ? 1 : (val2.m_quality + 1));
			if ((val.m_requireOnlyOneIngredient || !FreeCraft(Player.m_localPlayer)) && CraftPlan.Build(Player.m_localPlayer, val, quality, multiplier) == null)
			{
				return false;
			}
			Armed = true;
			return true;
		}

		internal static bool Prepare(InventoryGui gui, Player player)
		{
			if (!Armed)
			{
				return true;
			}
			object? value = RecipeField.GetValue(gui);
			Recipe val = (Recipe)((value is Recipe) ? value : null);
			object? value2 = UpgradeField.GetValue(gui);
			ItemData val2 = (ItemData)((value2 is ItemData) ? value2 : null);
			int quality = ((val2 == null) ? 1 : (val2.m_quality + 1));
			int multiplier = ((!(bool)MultiField.GetValue(gui)) ? 1 : gui.m_multiCraftAmount);
			if (FreeCraft(player))
			{
				if ((Object)(object)val != (Object)null)
				{
					if (val.m_requireOnlyOneIngredient)
					{
						return CraftPlan.Build(player, val, quality, multiplier) != null;
					}
					return true;
				}
				return false;
			}
			Plan = CraftPlan.Build(player, val, quality, multiplier);
			if (Plan == null || !Plan.ClaimSources(player))
			{
				return false;
			}
			Plan = CraftPlan.Build(player, val, quality, multiplier);
			if (Plan != null)
			{
				return Plan.StillAvailable(player);
			}
			return false;
		}

		internal static void Consume()
		{
			if (Plan == null || Consuming)
			{
				return;
			}
			Consuming = true;
			try
			{
				Plan.Consume();
			}
			finally
			{
				Consuming = false;
				Plan = null;
			}
		}

		internal static void ResetIfNoTimer(InventoryGui gui)
		{
			if ((float)TimerField.GetValue(gui) < 0f)
			{
				Reset();
			}
		}

		internal static void Reset()
		{
			Armed = false;
			Consuming = false;
			Plan = null;
		}

		internal static bool FreeCraft(Player player)
		{
			if (!player.NoCostCheat())
			{
				return ZoneSystem.instance.GetGlobalKey((GlobalKeys)25);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateRecipe")]
	internal static class CraftTooltipPatch
	{
		private static void Prefix(InventoryGui __instance, out string __state)
		{
			UITooltip component = ((Component)__instance.m_craftButton).GetComponent<UITooltip>();
			__state = (((Object)(object)component == (Object)null) ? null : component.m_text);
		}

		private static void Postfix(InventoryGui __instance, string __state)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			UITooltip component = ((Component)__instance.m_craftButton).GetComponent<UITooltip>();
			if (!((Object)(object)component == (Object)null) && !(component.m_text == __state))
			{
				string text = component.m_text;
				component.m_text = __state;
				component.Set(component.m_topic, text, (RectTransform)null, default(Vector2));
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Hide")]
	internal static class InventoryClosePatch
	{
		private static void Postfix()
		{
			CraftState.Reset();
			ButtonHalo.Clear();
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Update")]
	internal static class RecipeListRefreshPatch
	{
		private static readonly MethodInfo UpdatePanel = AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", (Type[])null, (Type[])null);

		private static InventoryGui _lastGui;

		private static bool _lastChecking;

		private static void Postfix(InventoryGui __instance)
		{
			if ((Object)(object)_lastGui != (Object)(object)__instance)
			{
				_lastGui = __instance;
				_lastChecking = false;
			}
			bool checking = CraftState.Checking;
			if (checking == _lastChecking)
			{
				return;
			}
			_lastChecking = checking;
			if (!InventoryGui.IsVisible() || UpdatePanel == null)
			{
				return;
			}
			try
			{
				UpdatePanel.Invoke(__instance, new object[1] { false });
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("Nearby Storage recipe refresh failed: " + ex));
			}
		}

		internal static void Reset()
		{
			_lastGui = null;
			_lastChecking = false;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
	internal static class RequirementDisplayPatch
	{
		private static void Postfix(Transform elementRoot, Requirement req, Player player, bool craft, int quality, int craftMultiplier, bool __result)
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			if (!__result || (Object)(object)player != (Object)(object)Player.m_localPlayer || (Object)(object)req?.m_resItem == (Object)null)
			{
				return;
			}
			bool num = craft && CraftState.Checking;
			bool flag = ((Character)player).InPlaceMode() && NearbyStorageActionInput.IsHeld();
			if (!num && !flag)
			{
				return;
			}
			string name = req.m_resItem.m_itemData.m_shared.m_name;
			if (new StorageSupply(player).Available(null, name, -1) >= req.GetAmount(quality) * craftMultiplier)
			{
				Transform val = elementRoot.Find("res_amount");
				if ((Object)(object)val != (Object)null)
				{
					((Graphic)((Component)val).GetComponent<TMP_Text>()).color = Color.white;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
	{
		typeof(Recipe),
		typeof(bool),
		typeof(int),
		typeof(int)
	})]
	internal static class RequirementPatch
	{
		private static void Postfix(Player __instance, Recipe recipe, bool discover, int qualityLevel, int amount, ref bool __result)
		{
			if (!(__result || discover) && CraftState.Checking && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && __instance.RequiredCraftingStation(recipe, qualityLevel, true))
			{
				string dlc = recipe.m_item.m_itemData.m_shared.m_dlc;
				if (dlc.Length <= 0 || DLCMan.instance.IsDLCInstalled(dlc))
				{
					__result = CraftPlan.Build(__instance, recipe, qualityLevel, amount) != null;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "ConsumeResources", new Type[]
	{
		typeof(Requirement[]),
		typeof(int),
		typeof(int),
		typeof(int)
	})]
	internal static class ResourceDebitPatch
	{
		private static bool Prefix(Player __instance)
		{
			if (!CraftState.Armed || CraftState.Plan == null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			CraftState.Consume();
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
	{
		typeof(string),
		typeof(int),
		typeof(int),
		typeof(bool)
	})]
	internal static class SingleDebitPatch
	{
		private static bool Prefix(Inventory __instance)
		{
			if (!CraftState.Armed || CraftState.Plan == null || CraftState.Consuming || (Object)(object)Player.m_localPlayer == (Object)null || __instance != ((Humanoid)Player.m_localPlayer).GetInventory())
			{
				return true;
			}
			CraftState.Consume();
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "GetFirstRequiredItem")]
	internal static class SingleIngredientPatch
	{
		private static void Postfix(Player __instance, Recipe recipe, int qualityLevel, int craftMultiplier, ref ItemData __result, ref int amount, ref int extraAmount)
		{
			if (__result == null && CraftState.Checking && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				CraftPlan craftPlan = CraftPlan.Build(__instance, recipe, qualityLevel, craftMultiplier);
				if (craftPlan != null)
				{
					__result = craftPlan.SingleItem;
					amount = craftPlan.SingleNeed;
					extraAmount = craftPlan.SingleExtra;
				}
			}
		}
	}
	internal sealed class Withdrawal
	{
		internal Inventory Inventory;

		internal Container Chest;

		internal ItemDrop Drop;

		internal ItemData GroundItem;

		internal ItemData SelectedItem;

		internal string Name;

		internal int Quality;

		internal int Amount;
	}
	[HarmonyPatch(typeof(CookingStation), "OnInteract")]
	internal static class CookingFeedPatch
	{
		private static void Prefix(Humanoid user)
		{
			FeedContext.Begin(user);
		}

		private static void Finalizer()
		{
			FeedContext.Reset();
		}
	}
	[HarmonyPatch(typeof(CookingStation), "FindCookableItem")]
	internal static class CookingInputPatch
	{
		private static void Postfix(CookingStation __instance, Inventory inventory, ref ItemData __result)
		{
			if (__result == null)
			{
				__result = FeedContext.FindConversion(inventory, __instance.m_conversion, (ItemConversion conversion) => conversion.m_from);
			}
		}
	}
	internal static class FeedContext
	{
		private static Player _player;

		private static Container _chest;

		private static ItemDrop _drop;

		private static ItemData _item;

		internal static void Begin(Humanoid user)
		{
			Reset();
			if ((Object)(object)user == (Object)(object)Player.m_localPlayer && NearbyStorageActionInput.IsHeld())
			{
				_player = Player.m_localPlayer;
			}
		}

		internal static ItemData Find(Inventory inventory, IEnumerable<string> names)
		{
			if (names == null)
			{
				return null;
			}
			List<string> candidates = new List<string>(names);
			return FindStored(inventory, delegate(Inventory source)
			{
				foreach (string item2 in candidates)
				{
					if (!string.IsNullOrEmpty(item2))
					{
						ItemData item = source.GetItem(item2, -1, false);
						if (StorageSupply.CanWithdrawOne(source, item))
						{
							return item;
						}
					}
				}
				return (ItemData)null;
			}, (ItemData candidate) => candidates.Contains(candidate.m_shared.m_name));
		}

		internal static ItemData FindConversion<T>(Inventory inventory, IEnumerable<T> conversions, Func<T, ItemDrop> sourceItem)
		{
			List<string> list = new List<string>();
			foreach (T conversion in conversions)
			{
				ItemDrop val = sourceItem(conversion);
				if ((Object)(object)val != (Object)null)
				{
					list.Add(val.m_itemData.m_shared.m_name);
				}
			}
			return Find(inventory, list);
		}

		internal static ItemData FindAmmo(Inventory inventory, string ammoType, string prefab)
		{
			Func<ItemData, bool> matches = (ItemData candidate) => ((int)candidate.m_shared.m_itemType == 9 || (int)candidate.m_shared.m_itemType == 23 || (int)candidate.m_shared.m_itemType == 2) && candidate.m_shared.m_ammoType == ammoType && (prefab == null || ((Object)candidate.m_dropPrefab).name == prefab);
			return FindStored(inventory, delegate(Inventory source)
			{
				ItemData ammoItem = source.GetAmmoItem(ammoType, prefab);
				if (StorageSupply.CanWithdrawOne(source, ammoItem))
				{
					return ammoItem;
				}
				foreach (ItemData allItem in source.GetAllItems())
				{
					if (matches(allItem) && StorageSupply.CanWithdrawOne(source, allItem))
					{
						return allItem;
					}
				}
				return (ItemData)null;
			}, matches);
		}

		internal static ItemData FindByPriority(Inventory inventory, IEnumerable<string> names)
		{
			if ((Object)(object)_player == (Object)null || inventory != ((Humanoid)_player).GetInventory() || names == null)
			{
				return null;
			}
			foreach (string name in names)
			{
				if (!string.IsNullOrEmpty(name))
				{
					ItemData item = inventory.GetItem(name, -1, false);
					if (item != null)
					{
						return item;
					}
					ItemData val = FindStored(inventory, delegate(Inventory source)
					{
						ItemData item2 = source.GetItem(name, -1, false);
						return (!StorageSupply.CanWithdrawOne(source, item2)) ? null : item2;
					}, (ItemData candidate) => candidate.m_shared.m_name == name);
					if (val != null)
					{
						return val;
					}
				}
			}
			return null;
		}

		internal static bool IsBorrowed(Inventory inventory, ItemData item)
		{
			if ((Object)(object)_player != (Object)null && inventory == ((Humanoid)_player).GetInventory())
			{
				return item == _item;
			}
			return false;
		}

		internal static bool RemoveBorrowed(Inventory inventory, ItemData item, int amount, ref bool result)
		{
			if (!IsBorrowed(inventory, item))
			{
				return true;
			}
			result = Withdraw(amount);
			return false;
		}

		internal static bool Withdraw(int amount)
		{
			if ((Object)(object)_drop != (Object)null)
			{
				if (amount == 1)
				{
					return GroundSource.RemoveOne(_drop, _player, _item);
				}
				return false;
			}
			if ((Object)(object)_chest == (Object)null || _item == null || amount != 1 || !StorageLocator.Eligible(_chest, _player, StorageLocator.CurrentContainer(), Plugin.Instance.Settings.Radius.Value))
			{
				return false;
			}
			ZNetView val = StorageLocator.View(_chest);
			if ((Object)(object)val == (Object)null || !val.IsOwner() || !_chest.GetInventory().ContainsItem(_item) || !StorageSupply.CanWithdrawOne(_chest.GetInventory(), _item))
			{
				return false;
			}
			return _chest.GetInventory().RemoveOneItem(_item);
		}

		internal static void Reset()
		{
			_player = null;
			_chest = null;
			_drop = null;
			_item = null;
		}

		private static ItemData FindStored(Inventory inventory, Func<Inventory, ItemData> selectChest, Func<ItemData, bool> matchesGround)
		{
			if ((Object)(object)_player == (Object)null || inventory != ((Humanoid)_player).GetInventory())
			{
				return null;
			}
			Withdrawal withdrawal = new StorageSupply(_player).FindStored(selectChest, matchesGround);
			if (withdrawal == null)
			{
				return null;
			}
			_chest = withdrawal.Chest;
			_drop = withdrawal.Drop;
			_item = withdrawal.SelectedItem;
			return _item;
		}
	}
	[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
	{
		typeof(ItemData),
		typeof(int)
	})]
	internal static class FeedRemoveAmountPatch
	{
		private static bool Prefix(Inventory __instance, ItemData item, int amount, ref bool __result)
		{
			return FeedContext.RemoveBorrowed(__instance, item, amount, ref __result);
		}
	}
	[HarmonyPatch(typeof(Inventory), "RemoveOneItem")]
	internal static class FeedRemoveOnePatch
	{
		private static bool Prefix(Inventory __instance, ItemData item, ref bool __result)
		{
			return FeedContext.RemoveBorrowed(__instance, item, 1, ref __result);
		}
	}
	[HarmonyPatch(typeof(Fermenter), "Interact")]
	internal static class FermenterFeedPatch
	{
		private static void Prefix(Humanoid user)
		{
			FeedContext.Begin(user);
		}

		private static void Finalizer()
		{
			FeedContext.Reset();
		}
	}
	[HarmonyPatch(typeof(Fermenter), "FindCookableItem")]
	internal static class FermenterInputPatch
	{
		private static void Postfix(Fermenter __instance, Inventory inventory, ref ItemData __result)
		{
			if (__result == null)
			{
				__result = FeedContext.FindConversion(inventory, __instance.m_conversion, (ItemConversion conversion) => conversion.m_from);
			}
		}
	}
	[HarmonyPatch(typeof(Smelter), "OnAddOre")]
	internal static class SmelterFeedPatch
	{
		private static void Prefix(Humanoid user, ItemData item)
		{
			if (item == null)
			{
				FeedContext.Begin(user);
			}
		}

		private static void Finalizer()
		{
			FeedContext.Reset();
		}
	}
	[HarmonyPatch(typeof(Smelter), "FindCookableItem")]
	internal static class SmelterInputPatch
	{
		private static readonly string[] KilnWoodPriority = new string[3] { "$item_wood", "$item_roundlog", "$item_finewood" };

		private static void Postfix(Smelter __instance, Inventory inventory, ref ItemData __result)
		{
			List<string> list = new List<string>();
			foreach (ItemConversion item2 in __instance.m_conversion)
			{
				if ((Object)(object)item2.m_from != (Object)null)
				{
					list.Add(item2.m_from.m_itemData.m_shared.m_name);
				}
			}
			if (((Object)((Component)__instance).gameObject).name.StartsWith("charcoal_kiln", StringComparison.OrdinalIgnoreCase))
			{
				List<string> list2 = new List<string>();
				string[] kilnWoodPriority = KilnWoodPriority;
				foreach (string item in kilnWoodPriority)
				{
					if (list.Contains(item))
					{
						list2.Add(item);
					}
				}
				ItemData val = FeedContext.FindByPriority(inventory, list2);
				if (val != null)
				{
					__result = val;
				}
			}
			else if (__result == null)
			{
				__result = FeedContext.Find(inventory, list);
			}
		}
	}
	[HarmonyPatch(typeof(Turret), "UseItem")]
	internal static class TurretFeedPatch
	{
		private static void Prefix(Humanoid user, ItemData item)
		{
			if (item == null)
			{
				FeedContext.Begin(user);
			}
		}

		private static void Finalizer()
		{
			FeedContext.Reset();
		}
	}
	[HarmonyPatch(typeof(Turret), "FindAmmoItem")]
	internal static class TurretInputPatch
	{
		private static void Postfix(Turret __instance, Inventory inventory, bool onlyCurrentlyLoadableType, ref ItemData __result)
		{
			if (__result == null)
			{
				string prefab = ((onlyCurrentlyLoadableType && __instance.HasAmmo()) ? __instance.GetAmmoType() : null);
				__result = FeedContext.FindAmmo(inventory, __instance.m_ammoType, prefab);
			}
		}
	}
	internal static class NearbyStorageCatalog
	{
		internal static List<NearbyStorageItem> Read(Player player)
		{
			Dictionary<string, NearbyStorageItem> dictionary = new Dictionary<string, NearbyStorageItem>();
			foreach (Container item in StorageLocator.Nearby(player))
			{
				foreach (ItemData allItem in item.GetInventory().GetAllItems())
				{
					if (allItem != null && !((Object)(object)allItem.m_dropPrefab == (Object)null) && allItem.m_stack > 0)
					{
						string key = NearbyStorageItemKey.For(allItem, item);
						if (!dictionary.TryGetValue(key, out var value))
						{
							value = new NearbyStorageItem
							{
								Key = key,
								Sample = allItem,
								Name = Localization.instance.Localize(allItem.m_shared.m_name)
							};
							dictionary.Add(key, value);
						}
						else if (value.Sample.m_stack < value.Sample.m_shared.m_maxStackSize && allItem.m_stack >= allItem.m_shared.m_maxStackSize)
						{
							value.Sample = allItem;
						}
						value.Count += allItem.m_stack;
						AddSource(value, item, allItem);
					}
				}
			}
			return new List<NearbyStorageItem>(dictionary.Values);
		}

		private static void AddSource(NearbyStorageItem entry, Container chest, ItemData item)
		{
			foreach (NearbyStorageSource source in entry.Sources)
			{
				if (!((Object)(object)source.Chest != (Object)(object)chest))
				{
					source.Count += item.m_stack;
					return;
				}
			}
			entry.Sources.Add(new NearbyStorageSource
			{
				Chest = chest,
				Item = item,
				Count = item.m_stack
			});
		}
	}
	internal static class NearbyStorageCategory
	{
		private static readonly HashSet<string> Valuables = new HashSet<string>(StringComparer.Ordinal)
		{
			"Amber", "AmberPearl", "AncientCoin", "AncientGemstoneBlack", "AncientGemstoneGreen", "AncientGemstoneOrange", "AncientGemstonePurple", "Coins", "Ruby", "SilverNecklace",
			"GemstoneBlue", "GemstoneGreen", "GemstoneRed"
		};

		internal static readonly string[] French = new string[13]
		{
			"Matériaux", "Précieux", "Nourriture", "Boissons", "Armes", "Boucliers", "Armures", "Accessoires", "Outils", "Munitions",
			"Poissons", "Trophées", "Divers"
		};

		internal static readonly string[] English = new string[13]
		{
			"Materials", "Valuables", "Food", "Drinks", "Weapons", "Shields", "Armor", "Accessories", "Tools", "Ammunition",
			"Fish", "Trophies", "Miscellaneous"
		};

		internal static string Label(int index)
		{
			if (Localization.instance == null || !(Localization.instance.GetSelectedLanguage() == "French"))
			{
				return English[index];
			}
			return French[index];
		}

		internal static int For(ItemData item)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected I4, but got Unknown
			if ((Object)(object)item?.m_dropPrefab != (Object)null && Valuables.Contains(((Object)item.m_dropPrefab).name))
			{
				return 1;
			}
			ItemType itemType = item.m_shared.m_itemType;
			switch (itemType - 1)
			{
			case 0:
				return 0;
			case 1:
				if (!item.m_shared.m_isDrink)
				{
					return 2;
				}
				return 3;
			case 2:
			case 3:
			case 13:
			case 21:
				return 4;
			case 4:
				return 5;
			case 5:
			case 6:
			case 10:
			case 11:
			case 16:
				return 6;
			case 17:
			case 23:
				return 7;
			case 14:
			case 18:
				return 8;
			case 8:
			case 22:
				return 9;
			case 20:
				return 10;
			case 12:
				return 11;
			default:
				return 12;
			}
		}
	}
	internal static class NearbyStorageDeposit
	{
		private static readonly HashSet<Container> Expired = new HashSet<Container>();

		private static Container _pending;

		private static ItemData _item;

		private static Player _player;

		private static int _remaining;

		private static int _moved;

		internal static bool Running { get; private set; }

		internal static bool TryStart(ItemData item, int amount, bool clearDrag)
		{
			Player localPlayer = Player.m_localPlayer;
			if (Running || (Object)(object)localPlayer == (Object)null || (Object)(object)item?.m_dropPrefab == (Object)null || amount <= 0 || !((Humanoid)localPlayer).GetInventory().ContainsItem(item))
			{
				return false;
			}
			NearbyStorageDialog.FocusInventoryItem(item);
			List<Container> list = Candidates(item, localPlayer);
			if (list.Count == 0)
			{
				return false;
			}
			if (clearDrag)
			{
				NearbyStorageTransfer.ClearPlayerDrag();
			}
			((MonoBehaviour)Plugin.Instance).StartCoroutine(Run(item, localPlayer, amount, list));
			return true;
		}

		private static List<Container> Candidates(ItemData item, Player player)
		{
			List<Container> list = new List<Container>();
			foreach (Container item2 in StorageLocator.Nearby(player))
			{
				if (Capacity(item2.GetInventory(), item) > 0)
				{
					list.Add(item2);
				}
			}
			NearbyStorageDestination.Sort(list, item, player);
			return list;
		}

		private static IEnumerator Run(ItemData item, Player player, int amount, List<Container> chests)
		{
			Running = true;
			_item = item;
			_player = player;
			_remaining = Mathf.Min(amount, item.m_stack);
			_moved = 0;
			try
			{
				foreach (Container chest in chests)
				{
					if (_remaining <= 0 || !((Humanoid)player).GetInventory().ContainsItem(item))
					{
						break;
					}
					if (StorageLocator.Eligible(chest, player, StorageLocator.CurrentContainer(), Plugin.Instance.Settings.Radius.Value))
					{
						yield return RequestChest(chest);
					}
				}
			}
			finally
			{
				if (_moved > 0)
				{
					NearbyStorageDialog.FocusInventoryItem(item);
				}
				_pending = null;
				_item = null;
				_player = null;
				_remaining = 0;
				Running = false;
			}
		}

		private static IEnumerator RequestChest(Container chest)
		{
			_pending = chest;
			chest.StackAll();
			float deadline = Time.realtimeSinceStartup + 10f;
			while ((Object)(object)_pending == (Object)(object)chest && Time.realtimeSinceStartup < deadline)
			{
				yield return (object)new WaitForSecondsRealtime(0.1f);
			}
			if ((Object)(object)_pending == (Object)(object)chest)
			{
				Expired.Add(chest);
				_pending = null;
			}
		}

		internal static bool AllowResponse(Container chest, bool granted)
		{
			if (Expired.Remove(chest))
			{
				return false;
			}
			if (!Running || (Object)(object)chest != (Object)(object)_pending)
			{
				return true;
			}
			_pending = null;
			if (granted)
			{
				try
				{
					Store(chest);
				}
				catch (Exception ex)
				{
					Debug.LogError((object)("Nearby Storage deposit failed: " + ex));
				}
			}
			return false;
		}

		private static void Store(Container chest)
		{
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = ((Humanoid)_player).GetInventory();
			if (!inventory.ContainsItem(_item) || _remaining <= 0)
			{
				return;
			}
			Inventory inventory2 = chest.GetInventory();
			int num = Mathf.Min(new int[3]
			{
				_remaining,
				_item.m_stack,
				Capacity(inventory2, _item)
			});
			if (num <= 0)
			{
				return;
			}
			ItemData val = _item.Clone();
			val.m_stack = num;
			val.m_equipped = false;
			int num2 = inventory2.NrOfItemsIncludingStacks();
			inventory2.AddItem(val);
			int num3 = inventory2.NrOfItemsIncludingStacks() - num2;
			if (num3 > 0)
			{
				if (num3 == _item.m_stack && ((Humanoid)_player).IsItemEquiped(_item))
				{
					((Humanoid)_player).RemoveEquipAction(_item);
					((Humanoid)_player).UnequipItem(_item, true);
				}
				inventory.RemoveItem(_item, num3);
				_remaining -= num3;
				_moved += num3;
				InventoryGui instance = InventoryGui.instance;
				if ((Object)(object)instance != (Object)null)
				{
					instance.m_moveItemEffects.Create(((Component)instance).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
				}
				NearbyStorageDialog.RefreshSoon();
			}
		}

		internal static int Capacity(Inventory inventory, ItemData item)
		{
			int num = inventory.GetEmptySlots() * item.m_shared.m_maxStackSize;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_shared.m_name == item.m_shared.m_name && allItem.m_quality == item.m_quality && allItem.m_worldLevel == item.m_worldLevel)
				{
					num += Mathf.Max(0, allItem.m_shared.m_maxStackSize - allItem.m_stack);
				}
			}
			return num;
		}

		internal static void Reset()
		{
			_pending = null;
			_item = null;
			_player = null;
			_remaining = 0;
			_moved = 0;
			Expired.Clear();
			Running = false;
		}
	}
	internal static class NearbyStorageDestination
	{
		internal static void Sort(List<Container> chests, ItemData item, Player player)
		{
			Dictionary<Container, (int level, int count)> scores = new Dictionary<Container, (int, int)>();
			foreach (Container chest in chests)
			{
				Score(chest, item, out var level, out var count);
				scores.Add(chest, (level, count));
			}
			chests.Sort((Container a, Container b) => Compare(a, b, player, scores));
		}

		private static int Compare(Container a, Container b, Player player, Dictionary<Container, (int level, int count)> scores)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			int num = scores[b].level.CompareTo(scores[a].level);
			if (num != 0)
			{
				return num;
			}
			int num2 = scores[b].count.CompareTo(scores[a].count);
			if (num2 != 0)
			{
				return num2;
			}
			Vector3 val = ((Component)a).transform.position - ((Component)player).transform.position;
			float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
			val = ((Component)b).transform.position - ((Component)player).transform.position;
			float sqrMagnitude2 = ((Vector3)(ref val)).sqrMagnitude;
			return sqrMagnitude.CompareTo(sqrMagnitude2);
		}

		private static void Score(Container chest, ItemData selected, out int level, out int count)
		{
			string name = Localization.instance.Localize(selected.m_shared.m_name);
			int selectedCategory = NearbyStorageCategory.For(selected);
			CountContents(chest, selected, name, selectedCategory, out var identical, out var family, out var similar, out var category);
			level = ((identical > 0) ? 4 : ((family > 0) ? 3 : ((similar > 0) ? 2 : ((category > 0) ? 1 : 0))));
			count = ((level == 4) ? identical : ((level == 3) ? family : ((level == 2) ? similar : category)));
		}

		private static void CountContents(Container chest, ItemData selected, string name, int selectedCategory, out int identical, out int family, out int similar, out int category)
		{
			identical = 0;
			family = 0;
			similar = 0;
			category = 0;
			foreach (ItemData allItem in chest.GetInventory().GetAllItems())
			{
				if (allItem?.m_shared == null || allItem.m_stack <= 0)
				{
					continue;
				}
				int num = NearbyStorageCategory.For(allItem);
				if (NearbyStorageItemKey.For(allItem) == NearbyStorageItemKey.For(selected))
				{
					identical += allItem.m_stack;
				}
				else
				{
					if (num != selectedCategory)
					{
						goto IL_00ff;
					}
					GameObject dropPrefab = selected.m_dropPrefab;
					string left = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
					GameObject dropPrefab2 = allItem.m_dropPrefab;
					if (!NearbyStorageSort.SharesIdentifierWord(left, (dropPrefab2 != null) ? ((Object)dropPrefab2).name : null))
					{
						StatusEffect consumeStatusEffect = selected.m_shared.m_consumeStatusEffect;
						string left2 = ((consumeStatusEffect != null) ? ((Object)consumeStatusEffect).name : null);
						StatusEffect consumeStatusEffect2 = allItem.m_shared.m_consumeStatusEffect;
						if (!NearbyStorageSort.SharesIdentifierWord(left2, (consumeStatusEffect2 != null) ? ((Object)consumeStatusEffect2).name : null) && !NearbyStorageRecipeFamily.SameStationFamily(selected, allItem) && !NearbyStorageProductionFamily.SameMachine(selected, allItem))
						{
							goto IL_00ff;
						}
					}
					family += allItem.m_stack;
				}
				goto IL_012d;
				IL_012d:
				if (num == selectedCategory)
				{
					category += allItem.m_stack;
				}
				continue;
				IL_00ff:
				if (num == selectedCategory && NearbyStorageSort.Similar(name, Localization.instance.Localize(allItem.m_shared.m_name)))
				{
					similar += allItem.m_stack;
				}
				goto IL_012d;
			}
		}
	}
	internal sealed class NearbyStorageItem
	{
		internal string Key;

		internal string Name;

		internal ItemData Sample;

		internal int Count;

		internal readonly List<NearbyStorageSource> Sources = new List<NearbyStorageSource>();
	}
	internal static class NearbyStorageItemKey
	{
		internal static string For(ItemData item)
		{
			if (!((Object)(object)item?.m_dropPrefab == (Object)null))
			{
				return ((Object)item.m_dropPrefab).name + "|" + item.m_quality;
			}
			return null;
		}

		internal static string For(ItemData item, Container chest)
		{
			string text = For(item);
			if (text == null || item.m_shared.m_maxStackSize > 1)
			{
				return text;
			}
			return text + "|" + ((Object)chest).GetInstanceID() + "|" + item.m_gridPos.x + "," + item.m_gridPos.y;
		}
	}
	internal static class NearbyStorageProductionFamily
	{
		private static readonly Dictionary<string, HashSet<string>> Machines = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal);

		private static ZNetScene _scene;

		private static int _prefabCount = -1;

		internal static bool SameMachine(ItemData left, ItemData right)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (left?.m_shared == null || right?.m_shared == null || left.m_shared.m_itemType != right.m_shared.m_itemType)
			{
				return false;
			}
			HashSet<string> hashSet = MachinesFor(left);
			HashSet<string> hashSet2 = MachinesFor(right);
			if (hashSet == null || hashSet2 == null)
			{
				return false;
			}
			foreach (string item in hashSet)
			{
				if (hashSet2.Contains(item))
				{
					return true;
				}
			}
			return false;
		}

		private static HashSet<string> MachinesFor(ItemData item)
		{
			if (item?.m_shared == null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return null;
			}
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)_scene != (Object)(object)instance || _prefabCount != instance.m_prefabs.Count)
			{
				Refresh(instance);
			}
			if (!Machines.TryGetValue(item.m_shared.m_name, out var value))
			{
				return null;
			}
			return value;
		}

		private static void Refresh(ZNetScene scene)
		{
			Machines.Clear();
			foreach (GameObject prefab in scene.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				Smelter component = prefab.GetComponent<Smelter>();
				if ((Object)(object)component != (Object)null)
				{
					foreach (ItemConversion item in component.m_conversion)
					{
						Add(((Object)prefab).name, item?.m_to);
					}
				}
				CookingStation component2 = prefab.GetComponent<CookingStation>();
				if ((Object)(object)component2 != (Object)null)
				{
					foreach (ItemConversion item2 in component2.m_conversion)
					{
						Add(((Object)prefab).name, item2?.m_to);
					}
				}
				Fermenter component3 = prefab.GetComponent<Fermenter>();
				if (!((Object)(object)component3 != (Object)null))
				{
					continue;
				}
				foreach (ItemConversion item3 in component3.m_conversion)
				{
					Add(((Object)prefab).name, item3?.m_to);
				}
			}
			_scene = scene;
			_prefabCount = scene.m_prefabs.Count;
		}

		private static void Add(string machine, ItemDrop output)
		{
			string text = output?.m_itemData?.m_shared?.m_name;
			if (!string.IsNullOrEmpty(text))
			{
				if (!Machines.TryGetValue(text, out var value))
				{
					value = new HashSet<string>(StringComparer.Ordinal);
					Machines.Add(text, value);
				}
				value.Add(machine);
			}
		}
	}
	internal static class NearbyStorageRecipeFamily
	{
		private static readonly Dictionary<string, HashSet<string>> Stations = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal);

		private static ObjectDB _database;

		private static int _recipeCount = -1;

		internal static bool SameStationFamily(ItemData left, ItemData right)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (left?.m_shared == null || right?.m_shared == null || left.m_shared.m_itemType != right.m_shared.m_itemType)
			{
				return false;
			}
			HashSet<string> hashSet = StationsFor(left);
			HashSet<string> hashSet2 = StationsFor(right);
			if (hashSet == null || hashSet2 == null)
			{
				return false;
			}
			foreach (string item in hashSet)
			{
				if (hashSet2.Contains(item))
				{
					return true;
				}
			}
			return false;
		}

		private static HashSet<string> StationsFor(ItemData item)
		{
			if (item?.m_shared == null || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)_database != (Object)(object)instance || _recipeCount != instance.m_recipes.Count)
			{
				Refresh(instance);
			}
			if (!Stations.TryGetValue(item.m_shared.m_name, out var value))
			{
				return null;
			}
			return value;
		}

		private static void Refresh(ObjectDB database)
		{
			Stations.Clear();
			foreach (Recipe recipe in database.m_recipes)
			{
				string text = recipe?.m_item?.m_itemData?.m_shared?.m_name;
				string text2 = recipe?.m_craftingStation?.m_name;
				if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(text2))
				{
					if (!Stations.TryGetValue(text, out var value))
					{
						value = new HashSet<string>(StringComparer.Ordinal);
						Stations.Add(text, value);
					}
					value.Add(text2);
				}
			}
			_database = database;
			_recipeCount = database.m_recipes.Count;
		}
	}
	internal static class NearbyStorageSort
	{
		private static readonly Regex IdentifierWordPattern = new Regex("[A-Z][a-z0-9]*|[a-z0-9]+", RegexOptions.Compiled);

		internal static bool Similar(string left, string right)
		{
			HashSet<string> hashSet = new HashSet<string>(Words(left));
			foreach (string item in Words(right))
			{
				if (hashSet.Contains(item))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool SharesIdentifierWord(string left, string right)
		{
			if (string.IsNullOrEmpty(left) || string.IsNullOrEmpty(right))
			{
				return false;
			}
			HashSet<string> hashSet = new HashSet<string>(IdentifierWords(left));
			foreach (string item in IdentifierWords(right))
			{
				if (hashSet.Contains(item))
				{
					return true;
				}
			}
			return false;
		}

		private static IEnumerable<string> IdentifierWords(string identifier)
		{
			foreach (Match item in IdentifierWordPattern.Matches(identifier ?? ""))
			{
				string text = Normalize(item.Value.ToLowerInvariant());
				if (text.Length >= 4)
				{
					yield return text;
				}
			}
		}

		internal static int ClosestIndex(List<NearbyStorageItem> items, string name)
		{
			if (items.Count == 0)
			{
				return -1;
			}
			HashSet<string> hashSet = new HashSet<string>(Words(name));
			int result = 0;
			int num = -1;
			for (int i = 0; i < items.Count; i++)
			{
				int num2 = 0;
				foreach (string item in Words(items[i].Name))
				{
					if (hashSet.Contains(item))
					{
						num2 += 100;
					}
				}
				int j;
				for (j = 0; j < name.Length && j < items[i].Name.Length && char.ToLowerInvariant(name[j]) == char.ToLowerInvariant(items[i].Name[j]); j++)
				{
				}
				num2 += j;
				if (num2 > num)
				{
					num = num2;
					result = i;
				}
			}
			if (num <= 0)
			{
				return -1;
			}
			return result;
		}

		internal static void Sort(List<NearbyStorageItem> items)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (NearbyStorageItem item in items)
			{
				foreach (string item2 in Words(item.Name))
				{
					dictionary[item2] = ((!dictionary.TryGetValue(item2, out var value)) ? 1 : (value + 1));
				}
			}
			Dictionary<string, string> groups = new Dictionary<string, string>();
			foreach (NearbyStorageItem item3 in items)
			{
				groups[item3.Key] = GroupKey(item3.Name, dictionary);
			}
			items.Sort((NearbyStorageItem a, NearbyStorageItem b) => Compare(a, b, groups));
		}

		private static int Compare(NearbyStorageItem a, NearbyStorageItem b, Dictionary<string, string> groups)
		{
			if (a.Key == b.Key)
			{
				return 0;
			}
			int num = string.Compare(groups[a.Key], groups[b.Key], StringComparison.CurrentCultureIgnoreCase);
			if (num != 0)
			{
				return num;
			}
			int num2 = string.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase);
			if (num2 == 0)
			{
				return string.CompareOrdinal(a.Key, b.Key);
			}
			return num2;
		}

		private static string GroupKey(string name, Dictionary<string, int> frequency)
		{
			string text = null;
			int num = 1;
			foreach (string item in Words(name))
			{
				int num2 = frequency[item];
				if (num2 > num)
				{
					text = item;
					num = num2;
				}
			}
			return text ?? name.ToLowerInvariant();
		}

		private static IEnumerable<string> Words(string name)
		{
			StringBuilder word = new StringBuilder();
			HashSet<string> seen = new HashSet<string>();
			foreach (char c in name)
			{
				if (char.IsLetterOrDigit(c))
				{
					word.Append(char.ToLowerInvariant(c));
					continue;
				}
				string text = Normalize(word.ToString());
				if (text.Length >= 4 && seen.Add(text))
				{
					yield return text;
				}
				word.Clear();
			}
			string text2 = Normalize(word.ToString());
			if (text2.Length >= 4 && seen.Add(text2))
			{
				yield return text2;
			}
		}

		private static string Normalize(string word)
		{
			if (word.Length <= 4 || !word.EndsWith("s", StringComparison.Ordinal))
			{
				return word;
			}
			return word.Substring(0, word.Length - 1);
		}
	}
	internal sealed class NearbyStorageSource
	{
		internal Container Chest;

		internal ItemData Item;

		internal int Count;
	}
	internal static class NearbyStorageTransfer
	{
		private static readonly MethodInfo SetupDrag = AccessTools.Method(typeof(InventoryGui), "SetupDragItem", new Type[3]
		{
			typeof(ItemData),
			typeof(Inventory),
			typeof(int)
		}, (Type[])null);

		private static readonly MethodInfo ShowSplit = AccessTools.Method(typeof(InventoryGui), "ShowSplitDialog", new Type[2]
		{
			typeof(ItemData),
			typeof(Inventory)
		}, (Type[])null);

		private static readonly FieldInfo DragItem = AccessTools.Field(typeof(InventoryGui), "m_dragItem");

		private static readonly FieldInfo DragAmount = AccessTools.Field(typeof(InventoryGui), "m_dragAmount");

		private static readonly FieldInfo DragInventory = AccessTools.Field(typeof(InventoryGui), "m_dragInventory");

		private static Inventory _trackedInventory;

		private static ItemData _trackedItem;

		private static Container _trackedChest;

		internal static string DraggedPlayerItemKey()
		{
			return NearbyStorageItemKey.For(DraggedPlayerItem());
		}

		internal static ItemData DraggedPlayerItem()
		{
			InventoryGui instance = InventoryGui.instance;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null || (object)/*isinst with value type is only supported in some contexts*/ != ((Humanoid)localPlayer).GetInventory())
			{
				return null;
			}
			object? obj = DragItem?.GetValue(instance);
			return (ItemData)((obj is ItemData) ? obj : null);
		}

		internal static string DraggedNearbyItemKey()
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null || _trackedItem == null || (Object)(object)_trackedChest == (Object)null || DragItem?.GetValue(instance) != _trackedItem || DragInventory?.GetValue(instance) != _trackedInventory)
			{
				return null;
			}
			return NearbyStorageItemKey.For(_trackedItem, _trackedChest);
		}

		internal static void CancelNearbyDrag()
		{
			if (DraggedNearbyItemKey() != null && !(SetupDrag == null))
			{
				SetupDrag.Invoke(InventoryGui.instance, new object[3] { null, null, 1 });
				_trackedInventory = null;
				_trackedItem = null;
				_trackedChest = null;
				NearbyStorageDialog.RefreshSoon();
			}
		}

		internal static bool TryDropInPanel()
		{
			Player localPlayer = Player.m_localPlayer;
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)instance == (Object)null || ((Character)localPlayer).IsTeleporting() || NearbyStorageDeposit.Running || (Object)(object)Plugin.Instance == (Object)null || SetupDrag == null)
			{
				return false;
			}
			object? obj = DragItem?.GetValue(instance);
			ItemData val = (ItemData)((obj is ItemData) ? obj : null);
			object? obj2 = DragInventory?.GetValue(instance);
			Inventory val2 = (Inventory)((obj2 is Inventory) ? obj2 : null);
			if (val2 != ((Humanoid)localPlayer).GetInventory() || (Object)(object)val?.m_dropPrefab == (Object)null || !val2.ContainsItem(val))
			{
				return false;
			}
			int amount = (int)(DragAmount?.GetValue(instance) ?? ((object)val.m_stack));
			return NearbyStorageDeposit.TryStart(val, amount, clearDrag: true);
		}

		internal static void ClearPlayerDrag()
		{
			SetupDrag?.Invoke(InventoryGui.instance, new object[3] { null, null, 1 });
		}

		internal static bool IsTracked(Inventory inventory, ItemData item)
		{
			if (inventory != null && inventory == _trackedInventory && item != null && item == _trackedItem && (Object)(object)_trackedChest != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)Plugin.Instance != (Object)null)
			{
				return StorageLocator.Eligible(_trackedChest, Player.m_localPlayer, StorageLocator.CurrentContainer(), Plugin.Instance.Settings.Radius.Value);
			}
			return false;
		}

		internal static void Select(NearbyStorageItem entry)
		{
			Player localPlayer = Player.m_localPlayer;
			InventoryGui instance = InventoryGui.instance;
			if (entry != null && !((Object)(object)localPlayer == (Object)null) && !((Object)(object)instance == (Object)null) && !((Character)localPlayer).IsTeleporting() && DragItem?.GetValue(instance) == null && !SelectStack(entry, localPlayer, instance, fullOnly: true))
			{
				SelectStack(entry, localPlayer, instance, fullOnly: false);
			}
		}

		private static bool SelectStack(NearbyStorageItem entry, Player player, InventoryGui gui, bool fullOnly)
		{
			foreach (NearbyStorageSource source in entry.Sources)
			{
				if (!((Object)(object)source.Chest == (Object)null) && FindStack(source.Chest.GetInventory(), source.Chest, entry.Key, fullOnly) != null && StorageLocator.TryClaimAndLoad(source.Chest, player))
				{
					Inventory inventory = source.Chest.GetInventory();
					ItemData val = FindStack(inventory, source.Chest, entry.Key, fullOnly);
					if (val != null)
					{
						Act(gui, player, source.Chest, inventory, val);
						NearbyStorageDialog.RefreshSoon();
						return true;
					}
				}
			}
			return false;
		}

		private static ItemData FindStack(Inventory inventory, Container chest, string key, bool fullOnly)
		{
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (NearbyStorageItemKey.For(allItem, chest) == key && allItem.m_stack > 0 && (!fullOnly || allItem.m_stack >= allItem.m_shared.m_maxStackSize))
				{
					return allItem;
				}
			}
			return null;
		}

		private static void Act(InventoryGui gui, Player player, Container chest, Inventory inventory, ItemData item)
		{
			bool num = ZInput.GetKey((KeyCode)304, true) || ZInput.GetKey((KeyCode)303, true);
			bool flag = ZInput.GetKey((KeyCode)306, true) || ZInput.GetKey((KeyCode)305, true);
			if (num)
			{
				if (item.m_stack > 1)
				{
					_trackedInventory = inventory;
					_trackedItem = item;
					_trackedChest = chest;
					ShowSplit?.Invoke(gui, new object[2] { item, inventory });
				}
			}
			else if (flag)
			{
				if (!item.m_shared.m_questItem)
				{
					((Humanoid)player).GetInventory().MoveItemToThis(inventory, item);
				}
			}
			else
			{
				_trackedInventory = inventory;
				_trackedItem = item;
				_trackedChest = chest;
				SetupDrag?.Invoke(gui, new object[3] { item, inventory, item.m_stack });
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")]
	internal static class StackItemPatch
	{
		private static readonly FieldInfo DragField = AccessTools.Field(typeof(InventoryGui), "m_dragGo");

		private static bool Prefix(InventoryGui __instance, InventoryGrid grid, ItemData item, Modifier mod)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			Player localPlayer = Player.m_localPlayer;
			bool flag = NearbyStorageDialog.IsOpen && (int)mod == 2 && (ZInput.GetKey((KeyCode)306, true) || ZInput.GetKey((KeyCode)305, true));
			if (item?.m_shared == null || (Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsTeleporting() || !InventoryGui.IsVisible() || !flag || grid.GetInventory() != ((Humanoid)localPlayer).GetInventory() || DragField.GetValue(__instance) != null)
			{
				return true;
			}
			NearbyStorageDeposit.TryStart(item, item.m_stack, clearDrag: false);
			return false;
		}
	}
	[HarmonyPatch(typeof(Container), "RPC_StackResponse")]
	internal static class StackResponsePatch
	{
		private static bool Prefix(Container __instance, bool granted)
		{
			return NearbyStorageDeposit.AllowResponse(__instance, granted);
		}
	}
	internal static class StorageLabel
	{
		private const float SignRangeSquared = 16f;

		internal static string ShortLabel(Container chest)
		{
			return SignLabel(chest) ?? Localization.instance.Localize(chest.GetHoverName());
		}

		internal static string SignLabel(Container chest)
		{
			return ClosestSign(chest);
		}

		private static string ClosestSign(Container chest)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			Sign val = null;
			float num = 16f;
			Container[] containers = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
			Sign[] array = Object.FindObjectsByType<Sign>((FindObjectsSortMode)0);
			foreach (Sign val2 in array)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					Vector3 val3 = ((Component)val2).transform.position - ((Component)chest).transform.position;
					float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
					if (!(sqrMagnitude > num) && !string.IsNullOrWhiteSpace(SignText(val2)) && IsClosestContainer(val2, chest, containers, sqrMagnitude))
					{
						val = val2;
						num = sqrMagnitude;
					}
				}
			}
			if (!((Object)(object)val == (Object)null))
			{
				return SignText(val);
			}
			return null;
		}

		private static bool IsClosestContainer(Sign sign, Container chest, Container[] containers, float chestDistance)
		{
			//IL_002a: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			foreach (Container val in containers)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)chest) && !StorageLocator.IsExcluded(val))
				{
					Vector3 val2 = ((Component)sign).transform.position - ((Component)val).transform.position;
					float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
					if (sqrMagnitude < chestDistance || (sqrMagnitude == chestDistance && ((Object)val).GetInstanceID() < ((Object)chest).GetInstanceID()))
					{
						return false;
					}
				}
			}
			return true;
		}

		private static string SignText(Sign sign)
		{
			string text = sign.GetText();
			if (text == null)
			{
				return null;
			}
			return StringExtensionMethods.RemoveRichTextTags(text).Replace('\r', ' ').Replace('\n', ' ')
				.Trim();
		}
	}
	internal sealed class NearbyStorageCard : MonoBehaviour, IPointerDownHandler, IEventSystemHandler, IPointerEnterHandler, IPointerExitHandler, IDropHandler
	{
		internal Color IdleColor;

		private static readonly Color HoverColor = new Color(0.42f, 0.36f, 0.32f, 0.96f);

		internal NearbyStorageItem Item;

		internal Image Background;

		public void OnPointerDown(PointerEventData eventData)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			if ((int)eventData.button == 0 && !NearbyStorageTransfer.TryDropInPanel())
			{
				NearbyStorageTransfer.Select(Item);
			}
		}

		public void OnDrop(PointerEventData eventData)
		{
			if (NearbyStorageTransfer.TryDropInPanel())
			{
				((AbstractEventData)eventData).Use();
			}
		}

		public void OnPointerEnter(PointerEventData eventData)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Background != (Object)null)
			{
				((Graphic)Background).color = HoverColor;
			}
			NearbyStorageDialog.ScheduleTooltip(Item);
		}

		public void OnPointerExit(PointerEventData eventData)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Background != (Object)null)
			{
				((Graphic)Background).color = IdleColor;
			}
			NearbyStorageDialog.HideTooltip();
		}
	}
	internal sealed class NearbyStorageCategoryHover : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler
	{
		internal int Index;

		public void OnPointerEnter(PointerEventData eventData)
		{
			NearbyStorageDialog.SetCategoryHover(Index, hovered: true);
		}

		public void OnPointerExit(PointerEventData eventData)
		{
			NearbyStorageDialog.SetCategoryHover(Index, hovered: false);
		}
	}
	internal static class NearbyStorageDialog
	{
		private static RectTransform _root;

		private static RectTransform _cards;

		private static RectTransform _categoryContent;

		private static RectTransform _tooltip;

		private static RectTransform _dragCancelOverlay;

		private static TMP_InputField _filter;

		private static InventoryGui _gui;

		private static float _nextRefresh;

		private static int _category;

		private static string _fingerprint;

		private static bool _dirtyView = true;

		private static bool _hasNearbyItems;

		private static string _dragKey;

		private static string _scrollToDragKey;

		private static string _scrollToSimilarName;

		private static bool _scrollCategoryToSelection;

		private static bool _selectFirstCategory = true;

		private static string _nearbyDragKey;

		private const float GridTopPadding = 6f;

		private const float GridLeftPadding = 6f;

		private const float HeaderHeight = 106f;

		private const float CategoryRowHeight = 23f;

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

		private static readonly FieldInfo TagScroll = AccessTools.Field(typeof(BuildUi), "m_tagListScrollRect");

		private const float TooltipWidth = 340f;

		private const float SourceRowHeight = 36f;

		private const float TooltipDelay = 1f;

		private static TextMeshProUGUI _tooltipTitle;

		private static TextMeshProUGUI _tooltipDescription;

		private static RectTransform _tooltipSources;

		private static NearbyStorageItem _hoverItem;

		private static float _hoverReadyAt;

		internal static bool FilterFocused
		{
			get
			{
				if ((Object)(object)_root != (Object)null && ((Component)_root).gameObject.activeInHierarchy && (Object)(object)_filter != (Object)null && ((Behaviour)_filter).isActiveAndEnabled)
				{
					return _filter.isFocused;
				}
				return false;
			}
		}

		internal static bool IsOpen
		{
			get
			{
				if ((Object)(object)_root != (Object)null && ((Component)_root).gameObject.activeInHierarchy)
				{
					return _hasNearbyItems;
				}
				return false;
			}
		}

		private static void DrawCards(List<NearbyStorageItem> items)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			foreach (Transform item in (Transform)_cards)
			{
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			Rect rect = ((Component)((Transform)_cards).parent).GetComponent<RectTransform>().rect;
			float num = Mathf.Max(260f, ((Rect)(ref rect)).width - 6f);
			float num2 = SlotStep();
			int num3 = Mathf.Max(5, Mathf.FloorToInt(num / num2));
			for (int i = 0; i < items.Count; i++)
			{
				DrawCard(items[i], i % num3, i / num3, num2, items[i].Key == _nearbyDragKey);
			}
			int num4 = Mathf.Max(4, (items.Count + num3 - 1) / num3);
			for (int j = items.Count; j < num4 * num3; j++)
			{
				CreateCardBackground("Empty slot", j % num3, j / num3, num2);
			}
			_cards.sizeDelta = new Vector2(0f, 6f + (float)num4 * num2);
			ScrollToDraggedItem(items, num3, num2);
		}

		private static void ScrollToDraggedItem(List<NearbyStorageItem> items, int columns, float step)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			if (_scrollToDragKey == null)
			{
				return;
			}
			int num = items.FindIndex((NearbyStorageItem item) => item.Key == _scrollToDragKey);
			if (num < 0)
			{
				string prefix = _scrollToDragKey + "|";
				num = items.FindIndex((NearbyStorageItem item) => item.Key.StartsWith(prefix, StringComparison.Ordinal));
			}
			if (num < 0 && _scrollToSimilarName != null)
			{
				num = NearbyStorageSort.ClosestIndex(items, _scrollToSimilarName);
			}
			_scrollToDragKey = null;
			_scrollToSimilarName = null;
			if (num >= 0)
			{
				Transform parent = ((Transform)_cards).parent;
				Rect rect = ((RectTransform)((parent is RectTransform) ? parent : null)).rect;
				float height = ((Rect)(ref rect)).height;
				float num2 = 6f + (float)(num / columns) * step;
				float num3 = num2 + SlotSize();
				float num4 = _cards.anchoredPosition.y;
				if (num2 < num4)
				{
					num4 = num2;
				}
				if (num3 > num4 + height)
				{
					num4 = num3 - height;
				}
				rect = _cards.rect;
				float num5 = Mathf.Max(0f, ((Rect)(ref rect)).height - height);
				ScrollRect component = ((Component)_root).GetComponent<ScrollRect>();
				if (component != null)
				{
					component.StopMovement();
				}
				_cards.anchoredPosition = new Vector2(_cards.anchoredPosition.x, Mathf.Clamp(num4, 0f, num5));
			}
		}

		private static RectTransform CreateCardBackground(string name, int column, int row, float step)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			float num = SlotSize();
			RectTransform obj = Rect(name, (Transform)(object)_cards, new Vector2(num, num));
			obj.anchoredPosition = new Vector2(6f + (float)column * step, -6f - (float)row * step);
			StyleSlot(((Component)obj).gameObject.AddComponent<Image>());
			return obj;
		}

		private static void DrawCard(NearbyStorageItem item, int column, int row, float step, bool held)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: 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)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: 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)
			RectTransform val = CreateCardBackground(item.Key, column, row, step);
			float num = SlotSize();
			NearbyStorageCard nearbyStorageCard = ((Component)val).gameObject.AddComponent<NearbyStorageCard>();
			nearbyStorageCard.Item = item;
			nearbyStorageCard.Background = ((Component)val).GetComponent<Image>();
			nearbyStorageCard.IdleColor = ((Graphic)nearbyStorageCard.Background).color;
			RectTransform obj = Rect("Icon", (Transform)(object)val, Vector2.zero);
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.offsetMin = Vector2.zero;
			obj.offsetMax = Vector2.zero;
			Image val2 = ((Component)obj).gameObject.AddComponent<Image>();
			StyleIcon(val2);
			val2.sprite = item.Sample.GetIcon();
			if (held)
			{
				((Graphic)val2).color = new Color(0.65f, 0.65f, 0.65f, ((Graphic)val2).color.a);
			}
			((Graphic)val2).raycastTarget = false;
			DrawItemMarkers(val, item.Sample);
			if (item.Sample.m_shared.m_maxStackSize > 1)
			{
				TextMeshProUGUI obj2 = Label("Quantity", (Transform)(object)val, item.Count.ToString("N0"), 15f, new Vector2(2f, 0f - (num - 19f)), new Vector2(num - 4f, 18f));
				StyleCount(obj2);
				((TMP_Text)obj2).alignment = (TextAlignmentOptions)1028;
			}
		}

		private static void DrawItemMarkers(RectTransform card, ItemData item)
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Invalid comparison between Unknown and I4
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			InventoryElement val = SlotElement();
			Transform obj = ((val != null) ? ((Component)val).transform : null);
			RectTransform val2 = (RectTransform)(object)((obj is RectTransform) ? obj : null);
			if (!((Object)(object)val2 == (Object)null))
			{
				TMP_Text quality = val.m_quality;
				if (item.m_shared.m_maxQuality > 1 && (Object)(object)quality != (Object)null)
				{
					TextMeshProUGUI val3 = Label("Quality", (Transform)(object)card, item.m_quality.ToString(), quality.fontSize, Vector2.zero, Vector2.zero);
					((TMP_Text)val3).font = quality.font;
					((TMP_Text)val3).fontSharedMaterial = quality.fontSharedMaterial;
					((TMP_Text)val3).fontStyle = quality.fontStyle;
					((Graphic)val3).color = ((Graphic)quality).color;
					((TMP_Text)val3).alignment = quality.alignment;
					MatchIconRect(quality.rectTransform, ((TMP_Text)val3).rectTransform, val2);
				}
				if ((int)item.m_shared.m_itemType == 2 && (!(item.m_shared.m_food <= 0f) || !(item.m_shared.m_foodStamina <= 0f) || !(item.m_shared.m_foodEitr <= 0f)) && !((Object)(object)val.m_food == (Object)null))
				{
					RectTransform val4 = Rect("Food", (Transform)(object)card, Vector2.zero);
					Image val5 = ((Component)val4).gameObject.AddComponent<Image>();
					CopyImage(val.m_food, val5);
					MatchIconRect(((Graphic)val.m_food).rectTransform, val4, val2);
					((Graphic)val5).color = FoodColor(item);
					((Graphic)val5).raycastTarget = false;
				}
			}
		}

		private static Color FoodColor(ItemData item)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			float food = item.m_shared.m_food;
			float foodStamina = item.m_shared.m_foodStamina;
			float foodEitr = item.m_shared.m_foodEitr;
			if (food < foodEitr / 2f && foodStamina < foodEitr / 2f)
			{
				return new Color(0.6f, 0.6f, 1f, 1f);
			}
			if (foodStamina < food / 2f)
			{
				return new Color(1f, 0.5f, 0.5f, 1f);
			}
			if (food < foodStamina / 2f)
			{
				return new Color(1f, 1f, 0.5f, 1f);
			}
			return Color.white;
		}

		private static void CreateCategories()
		{
			//IL_000a: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: 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_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Expected O, but got Unknown
			RectTransform val = Rect("Categories", (Transform)(object)_root, Vector2.zero);
			val.anchorMin = new Vector2(0f, 0f);
			val.anchorMax = new Vector2(0f, 1f);
			val.offsetMin = new Vector2(16f, 10f);
			val.offsetMax = new Vector2(196f, -106f);
			Image obj = ((Component)val).gameObject.AddComponent<Image>();
			((Graphic)obj).color = new Color(0f, 0f, 0f, 0.72f);
			((Graphic)obj).raycastTarget = false;
			((Component)val).gameObject.AddComponent<RectMask2D>();
			_categoryContent = Rect("Category content", (Transform)(object)val, new Vector2(164f, (float)NearbyStorageCategory.French.Length * 23f));
			ScrollRect val2 = ((Component)val).gameObject.AddComponent<ScrollRect>();
			val2.viewport = val;
			val2.content = _categoryContent;
			val2.horizontal = false;
			val2.scrollSensitivity = 100f;
			val2.movementType = (MovementType)2;
			for (int i = 0; i < NearbyStorageCategory.French.Length; i++)
			{
				int index = i;
				RectTransform val3 = Rect("Category " + i, (Transform)(object)_categoryContent, new Vector2(164f, 23f));
				val3.anchoredPosition = new Vector2(0f, (float)(-i) * 23f);
				((Graphic)((Component)val3).gameObject.AddComponent<Image>()).color = Color.clear;
				RectTransform obj2 = Rect("Selected", (Transform)(object)val3, Vector2.zero);
				obj2.anchorMin = Vector2.zero;
				obj2.anchorMax = Vector2.one;
				Vector2 offsetMin = (obj2.offsetMax = Vector2.zero);
				obj2.offsetMin = offsetMin;
				StyleCategorySelection(((Component)obj2).gameObject.AddComponent<Image>());
				StyleCategoryText(Label("Text", (Transform)(object)val3, NearbyStorageCategory.Label(i), 14f, new Vector2(5f, -3f), new Vector2(158f, 22f)));
				Button obj3 = ((Component)val3).gameObject.AddComponent<Button>();
				((Selectable)obj3).transition = (Transition)0;
				((Component)val3).gameObject.AddComponent<NearbyStorageCategoryHover>().Index = index;
				((UnityEvent)obj3.onClick).AddListener((UnityAction)delegate
				{
					SelectCategory((_category == index) ? (-1) : index);
				});
			}
			CreateCategoryScrollbar(val2, val);
			SelectCategory(_category);
		}

		private static void CreateCategoryScrollbar(ScrollRect scroll, RectTransform list)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Rect("Categories scrollbar", (Transform)(object)list, Vector2.zero);
			val.anchorMin = new Vector2(1f, 0f);
			val.anchorMax = new Vector2(1f, 1f);
			val.offsetMin = new Vector2(-16f, 0f);
			val.offsetMax = Vector2.zero;
			Image obj = ((Component)val).gameObject.AddComponent<Image>();
			((Graphic)obj).color = new Color(0f, 0f, 0f, 0.4f);
			RectTransform val2 = Rect("Handle", (Transform)(object)val, Vector2.zero);
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			Image val3 = ((Component)val2).gameObject.AddComponent<Image>();
			((Graphic)val3).color = new Color(1f, 0.65f, 0.2f, 0.9f);
			StyleScrollbar(obj, val3);
			Scrollbar val4 = ((Component)val).gameObject.AddComponent<Scrollbar>();
			val4.handleRect = val2;
			((Selectable)val4).targetGraphic = (Graphic)(object)val3;
			val4.direction = (Direction)2;
			scroll.verticalScrollbar = val4;
			scroll.verticalScrollbarVisibility = (ScrollbarVisibility)1;
		}

		private static void SelectCategory(int index, bool clearFilter = true)
		{
			if (clearFilter && index >= 0 && (Object)(object)_filter != (Object)null && !string.IsNullOrEmpty(_filter.text))
			{
				_filter.SetTextWithoutNotify("");
			}
			_category = index;
			_scrollCategoryToSelection = index >= 0;
			for (int i = 0; i < NearbyStorageCategory.French.Length; i++)
			{
				SetCategoryHover(i, hovered: false);
			}
			RefreshSoon();
		}

		internal static void SetCategoryHover(int index, bool hovered)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			RectTransform categoryContent = _categoryContent;
			Transform val = ((categoryContent != null) ? ((Transform)categoryContent).Find("Category " + index) : null);
			if (!((Object)(object)val == (Object)null))
			{
				Transform obj = val.Find("Selected");
				if (obj != null)
				{
					((Component)obj).gameObject.SetActive(index == _category);
				}
				((Graphic)((Component)val).GetComponent<Image>()).color = (Color)((hovered && index != _category) ? new Color(0.45f, 0.44f, 0.44f, 0.9f) : Color.clear);
			}
		}

		private static void ScrollToSelectedCategory()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: 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_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			if (!_scrollCategoryToSelection || _category < 0 || (Object)(object)_categoryContent == (Object)null)
			{
				return;
			}
			_scrollCategoryToSelection = false;
			Transform obj = ((Transform)_categoryContent).Find("Category " + _category);
			RectTransform val = (RectTransform)(object)((obj is RectTransform) ? obj : null);
			Transform parent = ((Transform)_categoryContent).parent;
			RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null))
			{
				float num = 0f - val.anchoredPosition.y;
				float num2 = _categoryContent.anchoredPosition.y;
				if (num < num2)
				{
					num2 = num;
				}
				float num3 = num + 23f;
				float num4 = num2;
				Rect rect = val2.rect;
				if (num3 > num4 + ((Rect)(ref rect)).height)
				{
					float num5 = num + 23f;
					rect = val2.rect;
					num2 = num5 - ((Rect)(ref rect)).height;
				}
				rect = _categoryContent.rect;
				float height = ((Rect)(ref rect)).height;
				rect = val2.rect;
				float num6 = Mathf.Max(0f, height - ((Rect)(ref rect)).height);
				ScrollRect component = ((Component)val2).GetComponent<ScrollRect>();
				if (component != null)
				{
					component.StopMovement();
				}
				_categoryContent.anchoredPosition = new Vector2(0f, Mathf.Clamp(num2, 0f, num6));
			}
		}

		internal static void FocusInventoryItem(ItemData item)
		{
			if (item?.m_shared != null && !((Object)(object)_root == (Object)null))
			{
				SelectCategory(NearbyStorageCategory.For(item));
				_scrollToDragKey = NearbyStorageItemKey.For(item);
				_scrollToSimilarName = Localization.instance.Localize(item.m_shared.m_name);
				RefreshSoon();
			}
		}

		internal static void Update()
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || !InventoryGui.IsVisible() || (Object)(object)StorageLocator.CurrentContainer() != (Object)null)
			{
				if ((Object)(object)_root != (Object)null)
				{
					((Component)_root).gameObject.SetActive(false);
				}
				HideTooltip();
				_nextRefresh = 0f;
				return;
			}
			if ((Object)(object)_root == (Object)null || (Object)(object)_gui != (Object)(object)instance)
			{
				Dispose();
				Create(instance);
			}
			if (!((Component)_root).gameObject.activeSelf && (Object)(object)_filter != (Object)null && !string.IsNullOrEmpty(_filter.text))
			{
				_filter.SetTextWithoutNotify("");
				RefreshSoon();
			}
			Position();
			UpdateDragFocus();
			if (Time.unscaledTime >= _nextRefresh)
			{
				Refresh();
			}
			((Component)_root).gameObject.SetActive(_hasNearbyItems);
			if (_hasNearbyItems)
			{
				UpdateVisibleDialog(instanc