Decompiled source of SpiritStorage v0.10.3

plugins/SpiritStorage.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
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: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SpiritStorage
{
	[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
	{
		typeof(Recipe),
		typeof(bool),
		typeof(int),
		typeof(int)
	})]
	internal static class CraftHaveRequirementsPatch
	{
		private static void Postfix(Player __instance, Recipe recipe, bool discover, int qualityLevel, int amount, ref bool __result)
		{
			if (!__result && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)recipe == (Object)null) && recipe.m_resources != null && !CraftFromStorageHelper.StorageSuppressed && !discover && !recipe.m_requireOnlyOneIngredient && __instance.RequiredCraftingStation(recipe, qualityLevel, true) && (recipe.m_item.m_itemData.m_shared.m_dlc.Length <= 0 || DLCMan.instance.IsDLCInstalled(recipe.m_item.m_itemData.m_shared.m_dlc)))
			{
				__result = CraftFromStorageHelper.HaveEnoughIncludingStorage(__instance, recipe.m_resources, qualityLevel, Mathf.Max(1, amount), out var _);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
	{
		typeof(Piece),
		typeof(RequirementMode)
	})]
	internal static class BuildHaveRequirementsPatch
	{
		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)
			if (!__result && (int)mode == 0 && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)piece == (Object)null) && piece.m_resources != null && !CraftFromStorageHelper.StorageSuppressed)
			{
				__result = CraftFromStorageHelper.HaveEnoughIncludingStorage(__instance, piece.m_resources, 1, 1, out var _);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
	[HarmonyAfter(new string[] { "Nosferatu.NoVikingLeftBehind", "com.morda.storeandcraft" })]
	internal static class RequirementDisplayPatch
	{
		[HarmonyPriority(0)]
		private static void Postfix(Transform elementRoot, Requirement req, Player player, bool craft, int quality, int craftMultiplier, bool __result)
		{
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			if (!__result || req == null || (Object)(object)req.m_resItem == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			Transform obj = elementRoot.Find("res_amount");
			TMP_Text val = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			int num = CraftFromStorageHelper.CountInNearbyStorage(player, ((Object)((Component)req.m_resItem).gameObject).name);
			if (num <= 0)
			{
				return;
			}
			string text = val.text ?? "";
			int num2 = text.IndexOf('/');
			int result3;
			int num3;
			if (num2 >= 0 && int.TryParse(text.Substring(0, num2).Trim(), out var result) && int.TryParse(text.Substring(num2 + 1).Trim(), out var result2))
			{
				num3 = result;
				result3 = result2;
			}
			else
			{
				num3 = ((Humanoid)player).GetInventory().CountItems(req.m_resItem.m_itemData.m_shared.m_name, -1, true);
				if (!int.TryParse(text.Trim(), out result3))
				{
					result3 = req.GetAmount(quality) * craftMultiplier;
				}
			}
			num3 += num;
			val.text = $"{FormatCount(num3)}/{result3}";
			if (num3 >= result3)
			{
				((Graphic)val).color = Color.white;
			}
		}

		private static string FormatCount(int count)
		{
			if (count < 10000)
			{
				return count.ToString();
			}
			return $"{count / 1000}k";
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class CraftFetchPatch
	{
		private static readonly FieldRef<InventoryGui, Recipe> s_recipe = AccessTools.FieldRefAccess<InventoryGui, Recipe>("m_craftRecipe");

		private static readonly FieldRef<InventoryGui, ItemData> s_upgradeItem = AccessTools.FieldRefAccess<InventoryGui, ItemData>("m_craftUpgradeItem");

		private static readonly FieldRef<InventoryGui, bool> s_multiCrafting = AccessTools.FieldRefAccess<InventoryGui, bool>("m_multiCrafting");

		private static readonly MethodInfo s_doCrafting = AccessTools.Method(typeof(InventoryGui), "DoCrafting", (Type[])null, (Type[])null);

		private static CraftFromStorageHelper.Fetch s_pending;

		private static bool Prefix(InventoryGui __instance, Player player, out bool __state)
		{
			__state = false;
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || CraftFromStorageHelper.StorageSuppressed)
			{
				return true;
			}
			if (s_pending != null && !s_pending.Completed)
			{
				return false;
			}
			Recipe recipe = s_recipe.Invoke(__instance);
			if ((Object)(object)recipe == (Object)null || recipe.m_resources == null || recipe.m_requireOnlyOneIngredient || CraftFromStorageHelper.IsFree(player, craft: true))
			{
				return true;
			}
			ItemData val = s_upgradeItem.Invoke(__instance);
			int qualityLevel = ((val == null) ? 1 : (val.m_quality + 1));
			int multiplier = ((!s_multiCrafting.Invoke(__instance)) ? 1 : __instance.m_multiCraftAmount);
			bool waiting = false;
			CraftFromStorageHelper.Fetch fetch = CraftFromStorageHelper.FetchShortfalls(player, recipe.m_resources, qualityLevel, multiplier, delegate(bool ok)
			{
				if (waiting && !((Object)(object)s_recipe.Invoke(__instance) != (Object)(object)recipe))
				{
					if (!ok)
					{
						using (CraftFromStorageHelper.SuppressStorage())
						{
							s_doCrafting.Invoke(__instance, new object[1] { player });
							return;
						}
					}
					s_doCrafting.Invoke(__instance, new object[1] { player });
				}
			});
			if (!fetch.Completed)
			{
				waiting = true;
				s_pending = fetch;
				return false;
			}
			if (!fetch.Ok)
			{
				CraftFromStorageHelper.BeginSuppress();
				__state = true;
			}
			return true;
		}

		private static void Finalizer(bool __state)
		{
			if (__state)
			{
				CraftFromStorageHelper.EndSuppress();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class BuildPrefetchPatch
	{
		internal static CraftFromStorageHelper.Fetch s_pending;

		private static float s_nextCheck;

		private static void Postfix(Player __instance)
		{
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !((Character)__instance).InPlaceMode() || Time.time < s_nextCheck)
			{
				return;
			}
			s_nextCheck = Time.time + 0.25f;
			if (s_pending == null || s_pending.Completed)
			{
				Piece selectedPiece = __instance.GetSelectedPiece();
				if (!((Object)(object)selectedPiece == (Object)null) && selectedPiece.m_resources != null && !selectedPiece.m_repairPiece && !selectedPiece.m_removePiece && !CraftFromStorageHelper.IsFree(__instance, craft: false, selectedPiece))
				{
					s_pending = CraftFromStorageHelper.FetchShortfalls(__instance, selectedPiece.m_resources, 1, 1, null);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	internal static class BuildWaitPatch
	{
		private static bool Prefix(Player __instance, Piece piece, ref bool __result)
		{
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || (Object)(object)piece == (Object)null || piece.m_resources == null || CraftFromStorageHelper.IsFree(__instance, craft: false, piece))
			{
				return true;
			}
			CraftFromStorageHelper.Fetch fetch = BuildPrefetchPatch.s_pending;
			if (fetch == null || fetch.Completed)
			{
				fetch = (BuildPrefetchPatch.s_pending = CraftFromStorageHelper.FetchShortfalls(__instance, piece.m_resources, 1, 1, null));
			}
			if (fetch.Completed)
			{
				if (fetch.Ok)
				{
					return true;
				}
				bool flag;
				using (CraftFromStorageHelper.SuppressStorage())
				{
					flag = __instance.HaveRequirements(piece, (RequirementMode)0);
				}
				if (flag)
				{
					return true;
				}
				((Character)__instance).Message((MessageType)2, "$msg_missingrequirement", 0, (Sprite)null, false);
				__result = false;
				return false;
			}
			((Character)__instance).Message((MessageType)2, "Summoning materials from Spirit Storage...", 0, (Sprite)null, false);
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "ConsumeResources")]
	internal static class CraftConsumeResourcesPatch
	{
		private static void Prefix(Player __instance, Requirement[] requirements, int qualityLevel, int multiplier)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && requirements != null)
			{
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
				{
					CraftFromStorageHelper.FetchShortfalls(__instance, requirements, Mathf.Max(1, qualityLevel), Mathf.Max(1, multiplier), null);
				}
				else if (CraftFromStorageHelper.Shortfalls(__instance, requirements, Mathf.Max(1, qualityLevel), Mathf.Max(1, multiplier)).Count > 0)
				{
					SpiritStorageContainer.LogVerbose("resources consumed with materials still missing from the inventory (a flow Spirit Storage doesn't prefetch for).");
				}
			}
		}
	}
	internal static class CraftFromStorageHelper
	{
		private sealed class SuppressScope : IDisposable
		{
			private bool m_disposed;

			public void Dispose()
			{
				if (!m_disposed)
				{
					m_disposed = true;
					EndSuppress();
				}
			}
		}

		internal class Fetch
		{
			public bool Completed;

			public bool Ok = true;
		}

		private static int s_suppressDepth;

		private static float Range => SpiritStoragePlugin.ConfigCraftRange.Value;

		public static bool StorageSuppressed => s_suppressDepth > 0;

		public static void BeginSuppress()
		{
			s_suppressDepth++;
		}

		public static void EndSuppress()
		{
			s_suppressDepth = Mathf.Max(0, s_suppressDepth - 1);
		}

		public static IDisposable SuppressStorage()
		{
			BeginSuppress();
			return new SuppressScope();
		}

		public static bool IsFree(Player player, bool craft, Piece piece = null)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (player.NoCostCheat() || (Object)(object)ZoneSystem.instance == (Object)null)
			{
				return true;
			}
			if (craft)
			{
				return ZoneSystem.instance.GetGlobalKey((GlobalKeys)25);
			}
			if ((Object)(object)piece != (Object)null)
			{
				return ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey());
			}
			return false;
		}

		private static IEnumerable<Requirement> RealRequirements(Player player, Requirement[] requirements)
		{
			CraftingStation station = player.GetCurrentCraftingStation();
			return requirements.Where((Requirement r) => r != null && (Object)(object)r.m_resItem != (Object)null && ((!((Object)(object)station != (Object)null)) ? (!r.m_upgraderResource) : (station.m_upgrader == r.m_upgraderResource)));
		}

		private static int InventoryCount(Player player, Requirement req)
		{
			return ((Humanoid)player).GetInventory().CountItems(req.m_resItem.m_itemData.m_shared.m_name, -1, true);
		}

		public static bool HaveEnoughIncludingStorage(Player player, Requirement[] requirements, int qualityLevel, int multiplier, out string debug)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			List<SpiritStorageContainer> list = SpiritStorageContainer.FindNearby(((Component)player).transform.position, Range).ToList();
			debug = $"range={Range} nearbyStorages={list.Count}";
			if (list.Count == 0)
			{
				return false;
			}
			foreach (Requirement item in RealRequirements(player, requirements))
			{
				int num = item.GetAmount(qualityLevel) * multiplier;
				string prefabName = ((Object)((Component)item.m_resItem).gameObject).name;
				if (InventoryCount(player, item) + list.Sum((SpiritStorageContainer s) => s.CountItemsByName(prefabName)) < num)
				{
					return false;
				}
			}
			return true;
		}

		public static int CountInNearbyStorage(Player player, string prefabName)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			return SpiritStorageContainer.FindNearby(((Component)player).transform.position, Range).Sum((SpiritStorageContainer s) => s.CountItemsByName(prefabName));
		}

		public static Dictionary<string, int> Shortfalls(Player player, Requirement[] requirements, int qualityLevel, int multiplier)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (Requirement item in RealRequirements(player, requirements))
			{
				int num = item.GetAmount(qualityLevel) * multiplier - InventoryCount(player, item);
				if (num > 0)
				{
					string name = ((Object)((Component)item.m_resItem).gameObject).name;
					dictionary[name] = (dictionary.TryGetValue(name, out var value) ? value : 0) + num;
				}
			}
			return dictionary;
		}

		public static Fetch FetchShortfalls(Player player, Requirement[] requirements, int qualityLevel, int multiplier, Action<bool> onDone)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			Fetch fetch = new Fetch();
			Dictionary<string, int> dictionary = Shortfalls(player, requirements, qualityLevel, multiplier);
			if (dictionary.Count == 0)
			{
				fetch.Completed = true;
				onDone?.Invoke(obj: true);
				return fetch;
			}
			Dictionary<SpiritStorageContainer, List<KeyValuePair<string, int>>> dictionary2 = new Dictionary<SpiritStorageContainer, List<KeyValuePair<string, int>>>();
			foreach (SpiritStorageContainer item in SpiritStorageContainer.FindNearby(((Component)player).transform.position, Range))
			{
				foreach (string item2 in dictionary.Keys.ToList())
				{
					int num = Mathf.Min(dictionary[item2], item.CountItemsByName(item2));
					if (num > 0)
					{
						if (!dictionary2.TryGetValue(item, out var value))
						{
							value = (dictionary2[item] = new List<KeyValuePair<string, int>>());
						}
						value.Add(new KeyValuePair<string, int>(item2, num));
						dictionary[item2] -= num;
					}
				}
			}
			bool ok = dictionary.Values.All((int v) => v <= 0);
			if (dictionary2.Count == 0)
			{
				fetch.Completed = true;
				fetch.Ok = false;
				onDone?.Invoke(obj: false);
				return fetch;
			}
			fetch.Ok = ok;
			int outstanding = dictionary2.Count;
			foreach (KeyValuePair<SpiritStorageContainer, List<KeyValuePair<string, int>>> item3 in dictionary2)
			{
				SpiritStorageContainer.LogVerbose("fetching " + string.Join(", ", item3.Value.Select((KeyValuePair<string, int> v) => v.Key + " x" + v.Value)) + " from storage for crafting/building.");
				item3.Key.RequestPrefabs(item3.Value, player, delegate(bool flag)
				{
					fetch.Ok &= flag;
					if (--outstanding == 0)
					{
						fetch.Completed = true;
						onDone?.Invoke(fetch.Ok);
					}
				});
			}
			return fetch;
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "DropItem", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData),
		typeof(int),
		typeof(Vector2i)
	})]
	internal static class GridDropRedirectPatch
	{
		private static bool Prefix(ItemData item, int amount, ref bool __result)
		{
			SpiritStorageContainer spiritStorageContainer = SpiritStoragePanel.Instance?.CurrentTarget;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)spiritStorageContainer == (Object)null || (Object)(object)localPlayer == (Object)null)
			{
				if ((Object)(object)SpiritStoragePanel.Instance == (Object)null)
				{
					SpiritStorageContainer.LogVerbose($"grid drop of '{item?.m_shared?.m_name}' x{amount} not redirected - storage panel doesn't exist.");
				}
				return true;
			}
			SpiritStorageContainer.LogVerbose($"grid drop (Ctrl+click/drag out) of '{item?.m_shared?.m_name}' x{amount} -> redirecting to storage.");
			bool flag = spiritStorageContainer.DepositAmount(item, amount, localPlayer);
			if (!flag)
			{
				Logger.LogWarning((object)$"SpiritStorage: grid drop-redirect could not deposit '{item?.m_shared?.m_name}' x{amount} - see the lines above for why.");
			}
			__result = flag;
			SpiritStoragePanel.Instance.RefreshNow();
			return false;
		}
	}
	[HarmonyPatch(typeof(Humanoid), "DropItem", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData),
		typeof(int)
	})]
	internal static class HumanoidDropRedirectPatch
	{
		private static bool Prefix(Humanoid __instance, ItemData item, int amount, ref bool __result)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			SpiritStorageContainer spiritStorageContainer = SpiritStoragePanel.Instance?.CurrentTarget;
			if ((Object)(object)spiritStorageContainer == (Object)null)
			{
				return true;
			}
			SpiritStorageContainer.LogVerbose($"drop (fallback path) of '{item?.m_shared?.m_name}' x{amount} -> redirecting to storage.");
			bool flag = spiritStorageContainer.DepositAmount(item, amount, (Player)__instance);
			if (!flag)
			{
				Logger.LogWarning((object)$"SpiritStorage: drop-redirect could not deposit '{item?.m_shared?.m_name}' x{amount} - see the lines above for why.");
			}
			__result = flag;
			SpiritStoragePanel.Instance.RefreshNow();
			return false;
		}
	}
	internal static class EpicLootCompat
	{
		private const string ProviderId = "SpiritStorage";

		public static void Init(Harmony harmony)
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("EpicLoot.API");
			Type type2 = AccessTools.TypeByName("EpicLoot_UnityLib.InventoryManagement");
			if (type == null || type2 == null)
			{
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "RegisterInventoryProvider", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(type2, "RemoveItem", new Type[2]
			{
				typeof(string),
				typeof(int)
			}, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				Logger.LogWarning((object)"SpiritStorage: found EpicLoot, but not its inventory-provider API - the enchanting table won't see Spirit Storage.");
				return;
			}
			try
			{
				harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(EpicLootCompat), "RemoveItemPrefix", (Type[])null)
				{
					priority = 800
				}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				methodInfo.Invoke(null, new object[5]
				{
					"SpiritStorage",
					new Func<List<ItemData>>(GetItems),
					new Func<string, int>(CountItem),
					new Func<string, int, int>(RemoveItem),
					new Func<ItemData, int, int>(RemoveExactItem)
				});
				Logger.LogInfo((object)"SpiritStorage: EpicLoot's enchanting table can now use materials in nearby Spirit Storage.");
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("SpiritStorage: couldn't hook into EpicLoot's enchanting table: " + ex.Message));
			}
		}

		private static IEnumerable<SpiritStorageContainer> Nearby()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				return SpiritStorageContainer.FindNearby(((Component)localPlayer).transform.position, SpiritStoragePlugin.ConfigCraftRange.Value);
			}
			return Enumerable.Empty<SpiritStorageContainer>();
		}

		private static List<ItemData> GetItems()
		{
			return Nearby().SelectMany((SpiritStorageContainer s) => s.GetStackableDisplayItems()).ToList();
		}

		private static int CountItem(string token)
		{
			return Nearby().Sum((SpiritStorageContainer s) => s.CountItemsByToken(token));
		}

		private static int RemoveItem(string token, int amount)
		{
			return 0;
		}

		private static int RemoveExactItem(ItemData item, int amount)
		{
			int num = 0;
			foreach (SpiritStorageContainer item2 in Nearby())
			{
				num += item2.ConsumeExact(item, amount - num);
				if (num >= amount)
				{
					break;
				}
			}
			return num;
		}

		private static bool RemoveItemPrefix(string item, ref int amount)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || amount <= 0 || string.IsNullOrEmpty(item))
			{
				return true;
			}
			int num = amount - ((Humanoid)localPlayer).GetInventory().CountItems(item, -1, true);
			int num2 = 0;
			foreach (SpiritStorageContainer item2 in Nearby())
			{
				if (num2 >= num)
				{
					break;
				}
				num2 += item2.ConsumeByToken(item, num - num2);
			}
			if (num2 <= 0)
			{
				return true;
			}
			SpiritStorageContainer.LogVerbose($"enchanting table: paid {num2} x '{item}' from Spirit Storage.");
			amount -= num2;
			return amount > 0;
		}
	}
	internal static class ExternalStackSize
	{
		private static bool s_resolved;

		private static object s_multiplierConfig;

		private static object s_enabledConfig;

		private static PropertyInfo s_multiplierValueProp;

		private static PropertyInfo s_enabledValueProp;

		private static void Resolve()
		{
			if (s_resolved)
			{
				return;
			}
			s_resolved = true;
			try
			{
				Type type = AppDomain.CurrentDomain.GetAssemblies().SelectMany(SafeGetTypes).FirstOrDefault((Type t) => t.FullName == "DropMoreLoot.DropMoreLootMain");
				if (!(type == null))
				{
					FieldInfo field = type.GetField("itemStackMultiplier", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					FieldInfo field2 = type.GetField("enableStacking", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					if (field == null || field2 == null)
					{
						Logger.LogWarning((object)"SpiritStorage: found DropMoreLoot but not its expected config fields - stack sizes will use each item's own live max stack instead.");
						return;
					}
					s_multiplierConfig = field.GetValue(null);
					s_enabledConfig = field2.GetValue(null);
					s_multiplierValueProp = s_multiplierConfig?.GetType().GetProperty("Value");
					s_enabledValueProp = s_enabledConfig?.GetType().GetProperty("Value");
					Logger.LogInfo((object)"SpiritStorage: found DropMoreLoot - storage max stack sizes will follow its 'Item Stack Multiplier' setting uniformly.");
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("SpiritStorage: failed to resolve DropMoreLoot's stack multiplier config - falling back to each item's own live max stack. " + ex.Message));
			}
		}

		private static Type[] SafeGetTypes(Assembly a)
		{
			try
			{
				return a.GetTypes();
			}
			catch
			{
				return Array.Empty<Type>();
			}
		}

		public static int GetMultiplier()
		{
			Resolve();
			if (s_multiplierConfig == null || s_enabledConfig == null || s_multiplierValueProp == null || s_enabledValueProp == null)
			{
				return 1;
			}
			try
			{
				if (!(bool)s_enabledValueProp.GetValue(s_enabledConfig))
				{
					return 1;
				}
				return Mathf.Max(1, (int)s_multiplierValueProp.GetValue(s_multiplierConfig));
			}
			catch
			{
				return 1;
			}
		}
	}
	internal static class ExtraSlotsCompat
	{
		private static bool s_initialized;

		private static MethodInfo s_isItemInSlot;

		public static bool IsInExtraSlot(ItemData item)
		{
			EnsureInitialized();
			if (s_isItemInSlot == null || item == null)
			{
				return false;
			}
			try
			{
				object obj = s_isItemInSlot.Invoke(null, new object[1] { item });
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		private static void EnsureInitialized()
		{
			if (s_initialized)
			{
				return;
			}
			s_initialized = true;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				Type type = assemblies[i].GetType("ExtraSlots.API");
				if (!(type == null))
				{
					s_isItemInSlot = type.GetMethod("IsItemInSlot", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(ItemData) }, null);
					if (s_isItemInSlot != null)
					{
						Logger.LogInfo((object)"SpiritStorage: found ExtraSlots - Deposit All will skip items in its extra slots.");
					}
					break;
				}
			}
		}
	}
	internal static class ItemNaming
	{
		private static bool s_initialized;

		private static MethodInfo s_getDisplayName;

		private static MethodInfo s_hasRarity;

		private static MethodInfo s_getRarityColor;

		public static string GetDisplayName(ItemData item)
		{
			EnsureInitialized();
			if (s_getDisplayName != null)
			{
				try
				{
					if (s_getDisplayName.Invoke(null, new object[1] { item }) is string text && !string.IsNullOrEmpty(text))
					{
						return text;
					}
				}
				catch
				{
				}
			}
			return LocalizationManager.Instance.TryTranslate(item.m_shared.m_name);
		}

		public static bool TryGetRarityColor(ItemData item, out Color color)
		{
			//IL_0006: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			EnsureInitialized();
			color = default(Color);
			if (s_hasRarity == null || s_getRarityColor == null)
			{
				return false;
			}
			try
			{
				object obj = s_hasRarity.Invoke(null, new object[1] { item });
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0)
				{
					color = (Color)s_getRarityColor.Invoke(null, new object[1] { item });
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		private static void EnsureInitialized()
		{
			if (s_initialized)
			{
				return;
			}
			s_initialized = true;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				Type type = assemblies[i].GetType("EpicLoot.ItemDataExtensions");
				if (!(type == null))
				{
					s_getDisplayName = type.GetMethod("GetDisplayName", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(ItemData) }, null);
					s_hasRarity = type.GetMethod("HasRarity", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(ItemData) }, null);
					s_getRarityColor = type.GetMethod("GetRarityColor", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(ItemData) }, null);
					break;
				}
			}
		}
	}
	public class StorageGroup
	{
		public string Key;

		public string DisplayName;

		public Sprite Icon;

		public int TotalCount;

		public int Quality;

		public float DurabilityPercent = -1f;

		public readonly List<ItemData> BackingItems = new List<ItemData>();

		public readonly List<long> BackingIds = new List<long>();

		public readonly List<int> BackingStacks = new List<int>();
	}
	public class SpiritStorageContainer : MonoBehaviour, Hoverable, Interactable
	{
		private static readonly List<SpiritStorageContainer> s_active = new List<SpiritStorageContainer>();

		private ZNetView m_nview;

		private StorageLedger m_ledger;

		private uint m_ledgerRevision;

		private bool m_ledgerLoaded;

		private readonly Dictionary<long, ItemData> m_displayItems = new Dictionary<long, ItemData>();

		public GameObject OpenVisual;

		public GameObject ClosedVisual;

		private static readonly ConditionalWeakTable<ItemData, string> s_prefabNameCache = new ConditionalWeakTable<ItemData, string>();

		private static readonly FieldInfo[] s_sharedDataFields = typeof(SharedData).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private bool IsValid
		{
			get
			{
				if ((Object)(object)m_nview != (Object)null)
				{
					return m_nview.IsValid();
				}
				return false;
			}
		}

		public ZDOID StorageId => m_nview.GetZDO().m_uid;

		public uint Revision
		{
			get
			{
				if (!IsValid)
				{
					return 0u;
				}
				return m_nview.GetZDO().DataRevision;
			}
		}

		private static int MaxTypes => SpiritStoragePlugin.ConfigMaxItemTypes.Value;

		public int Capacity => MaxTypes;

		public int CurrentCount => GetLedger()?.TypeKeys().Count ?? 0;

		private void Awake()
		{
			m_nview = ((Component)this).GetComponent<ZNetView>();
		}

		private void OnEnable()
		{
			s_active.Add(this);
		}

		private void OnDisable()
		{
			s_active.Remove(this);
		}

		public static IEnumerable<SpiritStorageContainer> FindNearby(Vector3 position, float range)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			float rangeSq = range * range;
			return s_active.Where(delegate(SpiritStorageContainer s)
			{
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)s != (Object)null && s.IsValid)
				{
					Vector3 val = ((Component)s).transform.position - position;
					return ((Vector3)(ref val)).sqrMagnitude <= rangeSq;
				}
				return false;
			}).OrderBy(delegate(SpiritStorageContainer s)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Component)s).transform.position - position;
				return ((Vector3)(ref val)).sqrMagnitude;
			}).ToList();
		}

		private StorageLedger GetLedger()
		{
			if (!IsValid)
			{
				return null;
			}
			ZDO zDO = m_nview.GetZDO();
			if (!m_ledgerLoaded || zDO.DataRevision != m_ledgerRevision)
			{
				m_ledger = StorageLedger.Read(zDO);
				m_ledgerRevision = zDO.DataRevision;
				m_ledgerLoaded = true;
				m_displayItems.Clear();
			}
			return m_ledger;
		}

		public string GetHoverName()
		{
			return LocalizationManager.Instance.TryTranslate("$piece_spiritstorage");
		}

		public string GetHoverText()
		{
			return LocalizationManager.Instance.TryTranslate("$piece_spiritstorage") + "\n[<color=yellow><b>$KEY_Use</b></color>] $piece_spiritstorage_open";
		}

		public float GetHoverOffset()
		{
			return 0.5f;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}

		public bool Interact(Humanoid character, bool hold, bool alt)
		{
			if (hold || !IsValid)
			{
				return false;
			}
			if (GetLedger() == null)
			{
				((Character)character).Message((MessageType)2, "This Spirit Storage needs a newer version of the mod.", 0, (Sprite)null, false);
				return true;
			}
			SetOpenVisual(open: true);
			InventoryGui.instance.Show((Container)null, 0);
			SpiritStoragePanel.Instance?.Open(this, (Player)(object)((character is Player) ? character : null));
			return true;
		}

		public void OnPanelClosed()
		{
			SetOpenVisual(open: false);
			InventoryGui.instance.Hide();
		}

		internal static void LogVerbose(string message)
		{
			if (SpiritStoragePlugin.ConfigVerboseLogging.Value)
			{
				Logger.LogInfo((object)("SpiritStorage: " + message));
			}
		}

		private static string Describe(ItemData item)
		{
			if (item != null)
			{
				return $"'{item.m_shared?.m_name}' x{item.m_stack}";
			}
			return "<null item>";
		}

		private void SetOpenVisual(bool open)
		{
			if ((Object)(object)OpenVisual != (Object)null)
			{
				OpenVisual.SetActive(open);
			}
			if ((Object)(object)ClosedVisual != (Object)null)
			{
				ClosedVisual.SetActive(!open);
			}
		}

		internal static ItemData ToItemData(StorageRecord record)
		{
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(record.PrefabName);
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			ItemData obj = val.m_itemData.Clone();
			obj.m_dropPrefab = itemPrefab;
			obj.m_stack = record.Stack;
			obj.m_durability = record.Durability;
			obj.m_quality = record.Quality;
			obj.m_variant = record.Variant;
			obj.m_crafterID = record.CrafterID;
			obj.m_crafterName = record.CrafterName;
			obj.m_worldLevel = record.WorldLevel;
			obj.m_pickedUp = record.PickedUp;
			obj.m_customData = new Dictionary<string, string>(record.CustomData);
			obj.m_equipped = false;
			RememberPrefabName(obj, record.PrefabName);
			return obj;
		}

		internal static StorageRecord FromItemData(ItemData item, int stack)
		{
			string prefabName = ResolvePrefabName(item);
			return new StorageRecord
			{
				PrefabName = prefabName,
				Stack = stack,
				Durability = item.m_durability,
				Quality = item.m_quality,
				Variant = item.m_variant,
				CrafterID = item.m_crafterID,
				CrafterName = (item.m_crafterName ?? ""),
				WorldLevel = item.m_worldLevel,
				PickedUp = item.m_pickedUp,
				Stackable = (StorageRecord.IsStackablePrefab(prefabName) ?? (item.m_shared.m_maxStackSize > 1)),
				CustomData = ((item.m_customData != null) ? new Dictionary<string, string>(item.m_customData) : new Dictionary<string, string>())
			};
		}

		private ItemData GetDisplayItem(StorageRecord record)
		{
			if (!m_displayItems.TryGetValue(record.Id, out var value))
			{
				value = ToItemData(record);
				m_displayItems[record.Id] = value;
			}
			return value;
		}

		internal static string ResolvePrefabName(ItemData item)
		{
			return s_prefabNameCache.GetValue(item, ResolvePrefabNameUncached);
		}

		private static void RememberPrefabName(ItemData item, string name)
		{
			s_prefabNameCache.Remove(item);
			s_prefabNameCache.Add(item, name);
		}

		private static string ResolvePrefabNameUncached(ItemData item)
		{
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.m_shared);
			if ((Object)(object)itemPrefab != (Object)null)
			{
				return ((Object)itemPrefab).name;
			}
			if ((Object)(object)item.m_dropPrefab != (Object)null)
			{
				return ((Object)item.m_dropPrefab).name;
			}
			foreach (GameObject item2 in ObjectDB.instance.m_items)
			{
				ItemDrop val = (((Object)(object)item2 != (Object)null) ? item2.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val != (Object)null && val.m_itemData?.m_shared?.m_name == item.m_shared.m_name)
				{
					return ((Object)item2).name;
				}
			}
			Logger.LogWarning((object)("SpiritStorage: could not resolve a prefab name for '" + item.m_shared.m_name + "' - falling back to its display token, which likely won't reload correctly."));
			return item.m_shared.m_name;
		}

		internal static int EffectiveMaxStack(ItemData item)
		{
			int maxStackSize = item.m_shared.m_maxStackSize;
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(ResolvePrefabName(item));
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val != (Object)null && val.m_itemData?.m_shared != null)
			{
				maxStackSize = val.m_itemData.m_shared.m_maxStackSize;
			}
			if (maxStackSize <= 1)
			{
				return 1;
			}
			return Mathf.Max(1, maxStackSize * ExternalStackSize.GetMultiplier());
		}

		private static SharedData CloneShared(SharedData src)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			SharedData val = new SharedData();
			FieldInfo[] array = s_sharedDataFields;
			foreach (FieldInfo fieldInfo in array)
			{
				fieldInfo.SetValue(val, fieldInfo.GetValue(src));
			}
			return val;
		}

		public List<StorageGroup> GetGroupedView()
		{
			List<StorageGroup> list = new List<StorageGroup>();
			StorageLedger ledger = GetLedger();
			if (ledger == null)
			{
				return list;
			}
			Dictionary<string, StorageGroup> dictionary = new Dictionary<string, StorageGroup>();
			foreach (StorageRecord record in ledger.Records)
			{
				ItemData displayItem = GetDisplayItem(record);
				if (displayItem == null)
				{
					continue;
				}
				StorageGroup value;
				if (record.Stackable)
				{
					string key = "type#" + record.PrefabName;
					if (!dictionary.TryGetValue(key, out value))
					{
						value = (dictionary[key] = new StorageGroup
						{
							Key = key,
							DisplayName = ItemNaming.GetDisplayName(displayItem),
							Icon = displayItem.GetIcon()
						});
						list.Add(value);
					}
				}
				else
				{
					value = new StorageGroup
					{
						Key = "item#" + record.Id,
						DisplayName = ItemNaming.GetDisplayName(displayItem),
						Icon = displayItem.GetIcon(),
						Quality = displayItem.m_quality,
						DurabilityPercent = (displayItem.m_shared.m_useDurability ? displayItem.GetDurabilityPercentage() : (-1f))
					};
					list.Add(value);
				}
				value.TotalCount += record.Stack;
				value.BackingItems.Add(displayItem);
				value.BackingIds.Add(record.Id);
				value.BackingStacks.Add(record.Stack);
			}
			return list.OrderBy((StorageGroup g) => g.DisplayName).ToList();
		}

		public int CountItemsByName(string prefabName)
		{
			return GetLedger()?.Records.Where((StorageRecord r) => r.PrefabName == prefabName).Sum((StorageRecord r) => r.Stack) ?? 0;
		}

		public int CountItemsByToken(string token)
		{
			return GetLedger()?.Records.Where((StorageRecord r) => GetDisplayItem(r)?.m_shared.m_name == token).Sum((StorageRecord r) => r.Stack) ?? 0;
		}

		public List<ItemData> GetStackableDisplayItems()
		{
			StorageLedger ledger = GetLedger();
			List<ItemData> list = new List<ItemData>();
			if (ledger == null)
			{
				return list;
			}
			foreach (StorageRecord item in ledger.Records.Where((StorageRecord r) => r.Stackable))
			{
				ItemData displayItem = GetDisplayItem(item);
				if (displayItem != null)
				{
					list.Add(displayItem);
				}
			}
			return list;
		}

		internal int ConsumeByToken(string token, int amount)
		{
			StorageLedger ledger = GetLedger();
			if (ledger == null || amount <= 0)
			{
				return 0;
			}
			List<KeyValuePair<string, int>> plan = new List<KeyValuePair<string, int>>();
			int num = amount;
			foreach (IGrouping<string, StorageRecord> item in from r in ledger.Records
				where GetDisplayItem(r)?.m_shared.m_name == token
				group r by r.PrefabName)
			{
				int num2 = Mathf.Min(num, item.Sum((StorageRecord r) => r.Stack));
				if (num2 > 0)
				{
					plan.Add(new KeyValuePair<string, int>(item.Key, num2));
					num -= num2;
				}
				if (num <= 0)
				{
					break;
				}
			}
			if (plan.Count != 0)
			{
				return SendConsume(StorageNetwork.Op.WithdrawPrefabs, delegate(ZPackage pkg)
				{
					pkg.Write(plan.Count);
					foreach (KeyValuePair<string, int> item2 in plan)
					{
						pkg.Write(item2.Key);
						pkg.Write(item2.Value);
					}
				}, plan.Sum((KeyValuePair<string, int> p) => p.Value), token);
			}
			return 0;
		}

		internal int ConsumeExact(ItemData displayItem, int amount)
		{
			GetLedger();
			long id = m_displayItems.FirstOrDefault((KeyValuePair<long, ItemData> kvp) => kvp.Value == displayItem).Key;
			StorageRecord storageRecord = ((id == 0L) ? null : m_ledger?.FindById(id));
			int take = ((storageRecord != null) ? Mathf.Min(amount, storageRecord.Stack) : 0);
			if (take <= 0)
			{
				return 0;
			}
			return SendConsume(StorageNetwork.Op.WithdrawIds, delegate(ZPackage pkg)
			{
				pkg.Write(1);
				pkg.Write(id);
				pkg.Write(take);
			}, take, displayItem.m_shared.m_name);
		}

		private int SendConsume(StorageNetwork.Op op, Action<ZPackage> body, int planned, string what)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			StorageNetwork.TxHandle txHandle = StorageNetwork.Send(StorageId, op, body, delegate(StorageNetwork.TxResult result)
			{
				if (!result.Ok)
				{
					Logger.LogWarning((object)$"SpiritStorage: server refused consuming {planned} x '{what}' ({result.Message}).");
				}
			});
			if (!txHandle.Completed)
			{
				return planned;
			}
			if (!txHandle.Result.Ok)
			{
				return 0;
			}
			return txHandle.Result.Returned.Sum((StorageRecord r) => r.Stack);
		}

		public void DepositAll(Player player, Action<string> onDone, bool onlyExisting = false)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			List<ItemData> hotbar = inventory.GetHotbar(false);
			List<ItemData> list = new List<ItemData>();
			int num = 0;
			HashSet<string> hashSet = null;
			if (onlyExisting)
			{
				hashSet = new HashSet<string>(GetLedger()?.Records.Select((StorageRecord r) => r.PrefabName) ?? Enumerable.Empty<string>());
			}
			foreach (ItemData item in new List<ItemData>(inventory.GetAllItems()))
			{
				if (item.m_equipped || hotbar.Contains(item) || ExtraSlotsCompat.IsInExtraSlot(item))
				{
					num++;
				}
				else if (hashSet == null || hashSet.Contains(ResolvePrefabName(item)))
				{
					list.Add(item);
				}
			}
			if (list.Count == 0)
			{
				onDone?.Invoke(onlyExisting ? "Nothing matches what's already stored." : "Nothing to store.");
				return;
			}
			List<StorageRecord> records = list.Select((ItemData i) => FromItemData(i, i.m_stack)).ToList();
			foreach (ItemData item2 in list)
			{
				inventory.RemoveItem(item2);
			}
			int units = list.Sum((ItemData i) => i.m_stack);
			LogVerbose(string.Format("{0}: sending {1} stacks ({2} units); skipped {3} hotbar/equipped/extra-slot.", onlyExisting ? "Deposit Fill" : "Deposit All", list.Count, units, num));
			SendDeposit(records, list, player, delegate(int rejected)
			{
				onDone?.Invoke((rejected == 0) ? $"Stored {units} items." : $"Storage full - {rejected} stack(s) kept.");
			});
		}

		public bool DepositAmount(ItemData item, int amount, Player player)
		{
			if (item == null || amount <= 0 || !IsValid)
			{
				LogVerbose($"deposit rejected - {Describe(item)}, amount={amount}.");
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (!inventory.ContainsItem(item))
			{
				LogVerbose("deposit of " + Describe(item) + " failed - item isn't in the player's inventory (already moved?).");
				return false;
			}
			if (item.m_equipped)
			{
				((Humanoid)player).UnequipItem(item, false);
			}
			ItemData val;
			if (amount >= item.m_stack)
			{
				inventory.RemoveItem(item);
				val = item;
			}
			else
			{
				val = item.Clone();
				RememberPrefabName(val, ResolvePrefabName(item));
				val.m_stack = amount;
				item.m_stack -= amount;
			}
			SendDeposit(new List<StorageRecord> { FromItemData(val, val.m_stack) }, new List<ItemData> { val }, player, delegate(int rejected)
			{
				if (rejected > 0)
				{
					((Character)player).Message((MessageType)2, "Spirit Storage is full.", 0, (Sprite)null, false);
				}
			});
			return true;
		}

		private void SendDeposit(List<StorageRecord> records, List<ItemData> items, Player player, Action<int> onDone)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			StorageNetwork.Send(StorageId, StorageNetwork.Op.Deposit, delegate(ZPackage pkg)
			{
				pkg.Write(records.Count);
				foreach (StorageRecord record in records)
				{
					record.Write(pkg);
				}
			}, delegate(StorageNetwork.TxResult result)
			{
				IEnumerable<int> enumerable;
				if (!result.Ok)
				{
					enumerable = Enumerable.Range(0, items.Count);
				}
				else
				{
					IEnumerable<int> rejected = result.Rejected;
					enumerable = rejected;
				}
				int num = 0;
				foreach (int item in enumerable)
				{
					if (item >= 0 && item < items.Count)
					{
						GiveItem(player, items[item]);
						num++;
					}
				}
				if (!result.Ok)
				{
					Logger.LogWarning((object)$"SpiritStorage: deposit failed ({result.Message}) - {num} stack(s) returned to the player.");
				}
				onDone?.Invoke(num);
			});
		}

		public void Withdraw(StorageGroup group, int amount, Player player, Action<string> onDone)
		{
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			if (group == null || group.BackingIds.Count == 0 || amount <= 0 || !IsValid)
			{
				return;
			}
			ItemData sample = group.BackingItems[0];
			int num = RoomFor(((Humanoid)player).GetInventory(), sample);
			int wanted = Mathf.Min(new int[3] { amount, group.TotalCount, num });
			if (wanted <= 0)
			{
				onDone?.Invoke("Your inventory is full.");
				return;
			}
			List<KeyValuePair<long, int>> picks = new List<KeyValuePair<long, int>>();
			int num2 = wanted;
			for (int i = 0; i < group.BackingIds.Count; i++)
			{
				if (num2 <= 0)
				{
					break;
				}
				int num3 = Mathf.Min(num2, group.BackingStacks[i]);
				picks.Add(new KeyValuePair<long, int>(group.BackingIds[i], num3));
				num2 -= num3;
			}
			StorageNetwork.Send(StorageId, StorageNetwork.Op.WithdrawIds, delegate(ZPackage pkg)
			{
				pkg.Write(picks.Count);
				foreach (KeyValuePair<long, int> item in picks)
				{
					pkg.Write(item.Key);
					pkg.Write(item.Value);
				}
			}, delegate(StorageNetwork.TxResult result)
			{
				int num4 = GiveRecords(player, result.Returned);
				LogVerbose($"withdraw '{group.DisplayName}': asked {wanted}, got {num4}.");
				if (!result.Ok)
				{
					onDone?.Invoke("Someone else already took that.");
				}
				else
				{
					onDone?.Invoke((wanted < Mathf.Min(amount, group.TotalCount)) ? "Your inventory is full." : null);
				}
			});
		}

		internal StorageNetwork.TxHandle RequestPrefabs(List<KeyValuePair<string, int>> wanted, Player player, Action<bool> onDone)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			return StorageNetwork.Send(StorageId, StorageNetwork.Op.WithdrawPrefabs, delegate(ZPackage pkg)
			{
				pkg.Write(wanted.Count);
				foreach (KeyValuePair<string, int> item in wanted)
				{
					pkg.Write(item.Key);
					pkg.Write(item.Value);
				}
			}, delegate(StorageNetwork.TxResult result)
			{
				GiveRecords(player, result.Returned);
				if (!result.Ok)
				{
					LogVerbose("material request refused: " + result.Message + ".");
				}
				onDone?.Invoke(result.Ok);
			});
		}

		private static int RoomFor(Inventory inv, ItemData sample)
		{
			int num = EffectiveMaxStack(sample);
			long num2 = (long)inv.GetEmptySlots() * (long)num;
			if (num > 1)
			{
				foreach (ItemData allItem in inv.GetAllItems())
				{
					if (allItem.m_shared.m_name == sample.m_shared.m_name && allItem.m_quality == sample.m_quality)
					{
						num2 += Mathf.Max(0, allItem.m_shared.m_maxStackSize - allItem.m_stack);
					}
				}
			}
			return (int)Math.Min(num2, 2147483647L);
		}

		private static int GiveRecords(Player player, List<StorageRecord> records)
		{
			int num = 0;
			foreach (StorageRecord record in records)
			{
				ItemData val = ToItemData(record);
				if (val == null)
				{
					Logger.LogError((object)$"SpiritStorage: withdrew '{record.PrefabName}' x{record.Stack} but that item no longer exists in the game (removed mod?) - it can't be given back.");
					continue;
				}
				int num2 = EffectiveMaxStack(val);
				int num3 = val.m_stack;
				while (num3 > 0)
				{
					int num4 = Mathf.Min(num3, num2);
					ItemData val2 = ((num3 == val.m_stack && num4 == num3) ? val : val.Clone());
					RememberPrefabName(val2, record.PrefabName);
					val2.m_stack = num4;
					val2.m_shared = CloneShared(val2.m_shared);
					val2.m_shared.m_maxStackSize = num2;
					GiveItem(player, val2);
					num3 -= num4;
					num += num4;
				}
			}
			return num;
		}

		private static void GiveItem(Player player, ItemData item)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			Inventory val = (((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null);
			if (val != null && val.CanAddItem(item, -1))
			{
				val.AddItem(item);
			}
			else if ((Object)(object)player != (Object)null)
			{
				Transform transform = ((Component)player).transform;
				ItemDrop.DropItem(item, item.m_stack, transform.position + transform.forward + Vector3.up, transform.rotation);
				LogVerbose("inventory full - dropped " + Describe(item) + " at the player's feet.");
			}
		}
	}
	public class SpiritStoragePanel : MonoBehaviour
	{
		public static SpiritStoragePanel Instance;

		private const float CellSize = 56f;

		private const float CellSpacing = 6f;

		private const float PanelWidth = 860f;

		private const float PanelHeight = 440f;

		private const float GridSideMargin = 16f;

		private const float ScrollbarWidth = 8f;

		private const float GridPadding = 6f;

		private const int FilterColumns = 2;

		private const float FilterButtonWidth = 104f;

		private const float FilterButtonHeight = 28f;

		private const float FilterButtonSpacing = 4f;

		private const float FilterColumnWidth = 212f;

		private static readonly Color FilterOnTint = new Color(1f, 0.75f, 0.35f, 1f);

		private static readonly Color FilterOffTint = new Color(0.55f, 0.55f, 0.55f, 1f);

		private static readonly string[] SortLabels = new string[5] { "A-Z", "Z-A", "Most", "Least", "Type" };

		private GameObject m_root;

		private RectTransform m_rootRt;

		private Text m_capacityText;

		private Text m_statusText;

		private InputField m_searchField;

		private readonly List<Button> m_filterButtons = new List<Button>();

		private Text m_sortButtonText;

		private RectTransform m_storageContent;

		private GameObject m_tooltipPrefab;

		private GameObject m_tooltipGo;

		private SpiritStorageCell m_hoverCell;

		private float m_hoverTime;

		private const float TooltipDelay = 0.35f;

		private GameObject m_elementPrefab;

		private GameObject m_splitOverlay;

		private Text m_splitTitleText;

		private InputField m_splitInput;

		private StorageGroup m_splitGroup;

		private readonly Dictionary<string, GameObject> m_activeCells = new Dictionary<string, GameObject>();

		private readonly Stack<GameObject> m_freeCells = new Stack<GameObject>();

		private SpiritStorageContainer m_target;

		private Player m_player;

		private string m_searchFilter = "";

		private int m_typeMask;

		private int m_sortMode;

		private bool m_inventoryWasVisible;

		private uint m_shownRevision;

		public SpiritStorageContainer CurrentTarget => m_target;

		public static void Create()
		{
			//IL_0049: 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)
			if ((Object)(object)Instance != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)Instance).gameObject);
				Instance = null;
			}
			if (!((Object)(object)GUIManager.CustomGUIFront == (Object)null))
			{
				GameObject val = GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 860f, 440f, true);
				val.SetActive(false);
				Instance = val.AddComponent<SpiritStoragePanel>();
				Instance.BuildContents(val);
			}
		}

		private void BuildContents(GameObject root)
		{
			//IL_006c: 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_008a: 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_009d: 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_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Expected O, but got Unknown
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_026f: Expected O, but got Unknown
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_0341: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			//IL_0388: Unknown result type (might be due to invalid IL or missing references)
			//IL_0397: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03db: Expected O, but got Unknown
			//IL_0402: Unknown result type (might be due to invalid IL or missing references)
			//IL_0411: Unknown result type (might be due to invalid IL or missing references)
			//IL_041c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0424: Unknown result type (might be due to invalid IL or missing references)
			//IL_042a: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0505: Unknown result type (might be due to invalid IL or missing references)
			//IL_050f: Expected O, but got Unknown
			//IL_0562: Unknown result type (might be due to invalid IL or missing references)
			//IL_056d: Unknown result type (might be due to invalid IL or missing references)
			m_root = root;
			m_rootRt = root.GetComponent<RectTransform>();
			Font averiaSerifBold = GUIManager.Instance.AveriaSerifBold;
			m_typeMask = ItemTypeFilters.ParseMask(SpiritStoragePlugin.ConfigPanelTypeFilters.Value);
			m_sortMode = Mathf.Clamp(SpiritStoragePlugin.ConfigPanelSortMode.Value, 0, SortLabels.Length - 1);
			GUIManager.Instance.CreateText("Spirit Storage", root.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(130f, -26f), averiaSerifBold, 22, GUIManager.Instance.ValheimOrange, true, Color.black, 220f, 32f, false).GetComponent<Text>().alignment = (TextAnchor)3;
			m_statusText = GUIManager.Instance.CreateText("", root.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(350f, -26f), averiaSerifBold, 14, Color.yellow, true, Color.black, 200f, 24f, false).GetComponent<Text>();
			m_statusText.alignment = (TextAnchor)4;
			m_capacityText = GUIManager.Instance.CreateText("0 / 0 types", root.transform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-338f, -26f), averiaSerifBold, 14, Color.white, true, Color.black, 120f, 24f, false).GetComponent<Text>();
			m_capacityText.alignment = (TextAnchor)5;
			((UnityEvent)GUIManager.Instance.CreateButton("Deposit All", root.transform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-80f, -26f), 120f, 28f).GetComponent<Button>().onClick).AddListener(new UnityAction(OnDepositAll));
			((UnityEvent)GUIManager.Instance.CreateButton("Deposit Fill", root.transform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-208f, -26f), 120f, 28f).GetComponent<Button>().onClick).AddListener(new UnityAction(OnDepositFill));
			float num = -238f;
			float num2 = -122f;
			GameObject val = GUIManager.Instance.CreateInputField(root.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(150f, -66f), (ContentType)0, "Search", 16, 260f, 30f);
			m_searchField = val.GetComponent<InputField>();
			((UnityEvent<string>)(object)m_searchField.onValueChanged).AddListener((UnityAction<string>)OnSearchChanged);
			GUIManager.Instance.CreateText("Order", root.transform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(num - 60f - 6f - 22f, -66f), averiaSerifBold, 16, Color.white, true, Color.black, 44f, 30f, false).GetComponent<Text>().alignment = (TextAnchor)5;
			GameObject val2 = GUIManager.Instance.CreateButton(SortLabels[m_sortMode], root.transform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(num - 30f, -66f), 60f, 30f);
			((UnityEvent)val2.GetComponent<Button>().onClick).AddListener(new UnityAction(OnSortClicked));
			m_sortButtonText = val2.GetComponentInChildren<Text>();
			GUIManager.Instance.CreateText("Types Filter", root.transform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(num2, -66f), averiaSerifBold, 16, Color.white, true, Color.black, 212f, 30f, false).GetComponent<Text>().alignment = (TextAnchor)4;
			float num3 = num2 - 106f + 52f;
			Vector2 val3 = default(Vector2);
			for (int i = 0; i < ItemTypeFilters.Labels.Length; i++)
			{
				int bit = 1 << i;
				int num4 = i / 2;
				int num5 = i % 2;
				((Vector2)(ref val3))..ctor(num3 + (float)num5 * 108f, -102f - (float)num4 * 32f);
				GameObject obj = GUIManager.Instance.CreateButton(ItemTypeFilters.Labels[i], root.transform, new Vector2(1f, 1f), new Vector2(1f, 1f), val3, 104f, 28f);
				Button component = obj.GetComponent<Button>();
				((UnityEvent)component.onClick).AddListener((UnityAction)delegate
				{
					OnTypeFilterToggled(bit);
				});
				m_filterButtons.Add(component);
				Text componentInChildren = obj.GetComponentInChildren<Text>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.fontSize = 14;
				}
			}
			UpdateFilterButtons();
			m_storageContent = BuildGridScrollView(root.transform, new Vector2(16f, 16f), new Vector2(num, -88f));
			BuildSplitOverlay(root, averiaSerifBold);
			root.AddComponent<SpiritStoragePanelDragSaver>().Panel = this;
		}

		private RectTransform BuildGridScrollView(Transform parent, Vector2 offsetMin, Vector2 offsetMax)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: 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_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Expected O, but got Unknown
			float num = 860f + offsetMax.x - offsetMin.x;
			float num2 = 440f + offsetMax.y - offsetMin.y;
			GameObject obj = GUIManager.Instance.CreateScrollView(parent, false, true, 8f, 2f, ColorBlock.defaultColorBlock, new Color(0f, 0f, 0f, 0.3f), num, num2);
			RectTransform component = obj.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, 0f);
			component.anchorMax = new Vector2(1f, 1f);
			component.offsetMin = offsetMin;
			component.offsetMax = offsetMax;
			ScrollRect componentInChildren = obj.GetComponentInChildren<ScrollRect>();
			RectTransform content = componentInChildren.content;
			GraphicRaycaster component2 = ((Component)content).GetComponent<GraphicRaycaster>();
			if ((Object)(object)component2 != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component2);
			}
			Canvas component3 = ((Component)content).GetComponent<Canvas>();
			if ((Object)(object)component3 != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component3);
			}
			RectTransform viewport = componentInChildren.viewport;
			if ((Object)(object)viewport != (Object)null)
			{
				viewport.anchorMin = Vector2.zero;
				viewport.anchorMax = Vector2.one;
				viewport.offsetMin = Vector2.zero;
				viewport.offsetMax = Vector2.zero;
				if ((Object)(object)((Component)viewport).GetComponent<RectMask2D>() == (Object)null && (Object)(object)((Component)viewport).GetComponent<Mask>() == (Object)null)
				{
					((Component)viewport).gameObject.AddComponent<RectMask2D>();
				}
			}
			VerticalLayoutGroup component4 = ((Component)content).gameObject.GetComponent<VerticalLayoutGroup>();
			if ((Object)(object)component4 != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component4);
			}
			GridLayoutGroup obj2 = ((Component)content).gameObject.AddComponent<GridLayoutGroup>();
			obj2.cellSize = new Vector2(56f, 56f);
			obj2.spacing = new Vector2(6f, 6f);
			int num3 = Mathf.RoundToInt(6f);
			((LayoutGroup)obj2).padding = new RectOffset(num3, num3, num3, num3);
			obj2.constraint = (Constraint)1;
			float num4 = num - 8f - 12f + 6f;
			obj2.constraintCount = Mathf.Max(1, Mathf.FloorToInt(num4 / 62f));
			((LayoutGroup)obj2).childAlignment = (TextAnchor)0;
			(((Component)content).gameObject.GetComponent<ContentSizeFitter>() ?? ((Component)content).gameObject.AddComponent<ContentSizeFitter>()).verticalFit = (FitMode)2;
			return content;
		}

		private void BuildSplitOverlay(GameObject root, Font font)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Expected O, but got Unknown
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Expected O, but got Unknown
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Expected O, but got Unknown
			m_splitOverlay = new GameObject("SplitOverlay", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			m_splitOverlay.transform.SetParent(root.transform, false);
			RectTransform component = m_splitOverlay.GetComponent<RectTransform>();
			component.anchorMin = Vector2.zero;
			component.anchorMax = Vector2.one;
			component.offsetMin = Vector2.zero;
			component.offsetMax = Vector2.zero;
			((Graphic)m_splitOverlay.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.6f);
			GameObject val = GUIManager.Instance.CreateWoodpanel(m_splitOverlay.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 320f, 220f, false);
			m_splitTitleText = GUIManager.Instance.CreateText("Item", val.transform, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0f, -30f), font, 18, Color.white, true, Color.black, 280f, 30f, false).GetComponent<Text>();
			GameObject val2 = GUIManager.Instance.CreateInputField(val.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -80f), (ContentType)2, "Amount", 16, 140f, 30f);
			m_splitInput = val2.GetComponent<InputField>();
			((UnityEvent)GUIManager.Instance.CreateButton("Max", val.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(90f, -80f), 70f, 30f).GetComponent<Button>().onClick).AddListener(new UnityAction(OnSplitMax));
			((UnityEvent)GUIManager.Instance.CreateButton("Take", val.transform, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(-80f, 30f), 120f, 34f).GetComponent<Button>().onClick).AddListener(new UnityAction(OnSplitConfirm));
			((UnityEvent)GUIManager.Instance.CreateButton("Cancel", val.transform, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(80f, 30f), 120f, 34f).GetComponent<Button>().onClick).AddListener(new UnityAction(CloseSplitPanel));
			m_splitOverlay.SetActive(false);
		}

		private void Update()
		{
			if ((Object)(object)m_target == (Object)null)
			{
				return;
			}
			if (Input.GetKeyDown((KeyCode)27))
			{
				if (m_splitOverlay.activeSelf)
				{
					CloseSplitPanel();
				}
				else
				{
					Close();
				}
				return;
			}
			bool flag = InventoryGui.IsVisible();
			if (m_inventoryWasVisible && !flag)
			{
				Close();
				return;
			}
			m_inventoryWasVisible = flag;
			if (m_target.Revision != m_shownRevision)
			{
				Refresh();
			}
			UpdateTooltip();
		}

		internal void OnCellHoverEnter(SpiritStorageCell cell)
		{
			HideTooltip();
			m_hoverCell = cell;
			m_hoverTime = 0f;
		}

		internal void OnCellHoverExit(SpiritStorageCell cell)
		{
			if ((Object)(object)m_hoverCell == (Object)(object)cell)
			{
				m_hoverCell = null;
				HideTooltip();
			}
		}

		private void UpdateTooltip()
		{
			if ((Object)(object)m_hoverCell == (Object)null || !((Behaviour)m_hoverCell).isActiveAndEnabled || m_splitOverlay.activeSelf)
			{
				HideTooltip();
				return;
			}
			if ((Object)(object)m_tooltipGo == (Object)null)
			{
				m_hoverTime += Time.unscaledDeltaTime;
				if (m_hoverTime < 0.35f)
				{
					return;
				}
				ShowTooltip();
			}
			else
			{
				SetTooltipTexts();
			}
			PositionTooltip();
		}

		private void ShowTooltip()
		{
			Canvas componentInParent = m_root.GetComponentInParent<Canvas>();
			Canvas val = ((componentInParent != null) ? componentInParent.rootCanvas : null);
			if (!((Object)(object)m_tooltipPrefab == (Object)null) && !((Object)(object)val == (Object)null))
			{
				m_tooltipGo = Object.Instantiate<GameObject>(m_tooltipPrefab, ((Component)val).transform, false);
				((Object)m_tooltipGo).name = "SpiritStorageTooltip";
				m_tooltipGo.transform.SetAsLastSibling();
				CanvasGroup obj = m_tooltipGo.GetComponent<CanvasGroup>() ?? m_tooltipGo.AddComponent<CanvasGroup>();
				obj.blocksRaycasts = false;
				obj.interactable = false;
				SetTooltipTexts();
				m_tooltipGo.SetActive(true);
			}
		}

		private void SetTooltipTexts()
		{
			if (!((Object)(object)m_tooltipGo == (Object)null) && !((Object)(object)m_hoverCell == (Object)null))
			{
				Transform val = Utils.FindChild(m_tooltipGo.transform, "Topic", (IterativeSearchType)0);
				if ((Object)(object)val != (Object)null)
				{
					((Component)val).GetComponent<TMP_Text>().text = Localization.instance.Localize(m_hoverCell.TooltipTopic ?? "");
				}
				Transform val2 = Utils.FindChild(m_tooltipGo.transform, "Text", (IterativeSearchType)0);
				if ((Object)(object)val2 != (Object)null)
				{
					((Component)val2).GetComponent<TMP_Text>().text = Localization.instance.Localize(m_hoverCell.TooltipText ?? "");
				}
			}
		}

		private void PositionTooltip()
		{
			//IL_0064: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: 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)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)m_tooltipGo == (Object)null)
			{
				return;
			}
			Canvas componentInParent = m_tooltipGo.GetComponentInParent<Canvas>();
			Canvas val = ((componentInParent != null) ? componentInParent.rootCanvas : null);
			RectTransform val2 = (RectTransform)(((Object)(object)val != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
			Transform transform = m_tooltipGo.transform;
			RectTransform val3 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null)
			{
				return;
			}
			Camera val4 = (((int)val.renderMode == 0) ? null : val.worldCamera);
			Vector2 val5 = default(Vector2);
			if (RectTransformUtility.ScreenPointToLocalPointInRectangle(val2, Vector2.op_Implicit(Input.mousePosition), val4, ref val5))
			{
				((Transform)val3).localPosition = new Vector3(val5.x + 16f, val5.y - 16f, 0f);
				object obj = ((((Transform)val3).childCount > 0) ? ((object)/*isinst with value type is only supported in some contexts*/) : ((object)val3));
				LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)obj);
				Vector3[] array = (Vector3[])(object)new Vector3[4];
				((RectTransform)obj).GetWorldCorners(array);
				Vector2 val6 = Vector2.op_Implicit(((Transform)val2).InverseTransformPoint(array[0]));
				Vector2 val7 = Vector2.op_Implicit(((Transform)val2).InverseTransformPoint(array[2]));
				Rect rect = val2.rect;
				Vector2 zero = Vector2.zero;
				if (val7.x > ((Rect)(ref rect)).xMax)
				{
					zero.x = ((Rect)(ref rect)).xMax - val7.x;
				}
				if (val6.x + zero.x < ((Rect)(ref rect)).xMin)
				{
					zero.x = ((Rect)(ref rect)).xMin - val6.x;
				}
				if (val6.y < ((Rect)(ref rect)).yMin)
				{
					zero.y = ((Rect)(ref rect)).yMin - val6.y;
				}
				if (val7.y + zero.y > ((Rect)(ref rect)).yMax)
				{
					zero.y = ((Rect)(ref rect)).yMax - val7.y;
				}
				((Transform)val3).localPosition = ((Transform)val3).localPosition + Vector2.op_Implicit(zero);
			}
		}

		private void HideTooltip()
		{
			if ((Object)(object)m_tooltipGo != (Object)null)
			{
				Object.Destroy((Object)(object)m_tooltipGo);
				m_tooltipGo = null;
			}
			m_hoverTime = 0f;
		}

		public void Open(SpiritStorageContainer target, Player player)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)target == (Object)null) && !((Object)(object)player == (Object)null))
			{
				m_target = target;
				m_player = player;
				m_inventoryWasVisible = false;
				m_searchFilter = "";
				if ((Object)(object)m_searchField != (Object)null)
				{
					m_searchField.SetTextWithoutNotify(string.Empty);
				}
				SpiritStorageContainer.LogVerbose($"panel opened on storage at {((Component)target).transform.position}.");
				m_root.SetActive(true);
				RestorePosition();
				GUIManager.BlockInput(true);
				SetStatus(null);
				Refresh();
			}
		}

		public void Close()
		{
			m_hoverCell = null;
			HideTooltip();
			SpiritStorageContainer.LogVerbose("panel closed.");
			SavePosition();
			SpiritStorageContainer target = m_target;
			m_target = null;
			m_player = null;
			m_splitOverlay.SetActive(false);
			m_root.SetActive(false);
			GUIManager.BlockInput(false);
			if ((Object)(object)target != (Object)null)
			{
				target.OnPanelClosed();
			}
		}

		private void RestorePosition()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			float value = SpiritStoragePlugin.ConfigPanelScale.Value;
			((Transform)m_rootRt).localScale = new Vector3(value, value, 1f);
			m_rootRt.anchoredPosition = ClampOnScreen(SpiritStoragePlugin.ConfigPanelPosition.Value);
		}

		internal void SavePosition()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)m_rootRt == (Object)null))
			{
				Vector2 val = ClampOnScreen(m_rootRt.anchoredPosition);
				m_rootRt.anchoredPosition = val;
				if (SpiritStoragePlugin.ConfigPanelPosition.Value != val)
				{
					SpiritStoragePlugin.ConfigPanelPosition.Value = val;
				}
			}
		}

		private Vector2 ClampOnScreen(Vector2 pos)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0044: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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_0065: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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_007d: 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_008e: 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_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			Transform parent = ((Transform)m_rootRt).parent;
			RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if ((Object)(object)val == (Object)null)
			{
				return pos;
			}
			Rect rect = m_rootRt.rect;
			Vector2 val2 = Vector2.Scale(((Rect)(ref rect)).size, Vector2.op_Implicit(((Transform)m_rootRt).localScale));
			rect = val.rect;
			Vector2 val3 = (((Rect)(ref rect)).size - val2) * 0.5f;
			val3 = Vector2.Max(val3, Vector2.zero);
			return new Vector2(Mathf.Clamp(pos.x, 0f - val3.x, val3.x), Mathf.Clamp(pos.y, 0f - val3.y, val3.y));
		}

		private void OnSearchChanged(string value)
		{
			m_searchFilter = value ?? "";
			Refresh();
		}

		private void OnTypeFilterToggled(int bit)
		{
			m_typeMask ^= bit;
			SpiritStoragePlugin.ConfigPanelTypeFilters.Value = ItemTypeFilters.FormatMask(m_typeMask);
			UpdateFilterButtons();
			Refresh();
		}

		private void UpdateFilterButtons()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < m_filterButtons.Count; i++)
			{
				bool flag = (m_typeMask & (1 << i)) != 0;
				bool flag2 = m_typeMask == 0 || flag;
				Image component = ((Component)m_filterButtons[i]).GetComponent<Image>();
				if ((Object)(object)component != (Object)null)
				{
					((Graphic)component).color = (flag ? FilterOnTint : (flag2 ? Color.white : FilterOffTint));
				}
				Text componentInChildren = ((Component)m_filterButtons[i]).GetComponentInChildren<Text>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					((Graphic)componentInChildren).color = (Color)(flag ? GUIManager.Instance.ValheimOrange : (flag2 ? Color.white : new Color(0.7f, 0.7f, 0.7f, 1f)));
				}
			}
		}

		private void OnSortClicked()
		{
			m_sortMode = (m_sortMode + 1) % SortLabels.Length;
			SpiritStoragePlugin.ConfigPanelSortMode.Value = m_sortMode;
			if ((Object)(object)m_sortButtonText != (Object)null)
			{
				m_sortButtonText.text = SortLabels[m_sortMode];
			}
			Refresh();
		}

		private void OnDepositAll()
		{
			if (!((Object)(object)m_target == (Object)null) && !((Object)(object)m_player == (Object)null))
			{
				m_target.DepositAll(m_player, SetStatus);
			}
		}

		private void OnDepositFill()
		{
			if (!((Object)(object)m_target == (Object)null) && !((Object)(object)m_player == (Object)null))
			{
				m_target.DepositAll(m_player, SetStatus, onlyExisting: true);
			}
		}

		public void RequestTake(StorageGroup group)
		{
			if (!((Object)(object)m_target == (Object)null) && !((Object)(object)m_player == (Object)null) && group.BackingItems.Count != 0)
			{
				m_target.Withdraw(group, SpiritStorageContainer.EffectiveMaxStack(group.BackingItems[0]), m_player, SetStatus);
			}
		}

		public void RequestTakeAll(StorageGroup group)
		{
			if (!((Object)(object)m_target == (Object)null) && !((Object)(object)m_player == (Object)null))
			{
				m_target.Withdraw(group, group.TotalCount, m_player, SetStatus);
			}
		}

		public void OpenSplitPanel(StorageGroup group)
		{
			if (group != null && group.BackingItems.Count != 0)
			{
				m_splitGroup = group;
				m_splitTitleText.text = group.DisplayName;
				m_splitInput.SetTextWithoutNotify("1");
				m_splitOverlay.SetActive(true);
			}
		}

		private void CloseSplitPanel()
		{
			m_splitOverlay.SetActive(false);
			m_splitGroup = null;
		}

		private void OnSplitMax()
		{
			if (m_splitGroup != null)
			{
				m_splitInput.SetTextWithoutNotify(m_splitGroup.TotalCount.ToString());
			}
		}

		private void OnSplitConfirm()
		{
			if (m_splitGroup == null || (Object)(object)m_target == (Object)null || (Object)(object)m_player == (Object)null)
			{
				CloseSplitPanel();
				return;
			}
			if (!int.TryParse(m_splitInput.text, out var result) || result <= 0)
			{
				SetStatus("Enter a valid amount.");
				return;
			}
			m_target.Withdraw(m_splitGroup, result, m_player, SetStatus);
			CloseSplitPanel();
			Refresh();
		}

		private void SetStatus(string message)
		{
			if ((Object)(object)m_statusText != (Object)null)
			{
				m_statusText.text = message ?? "";
			}
		}

		public void RefreshNow()
		{
			Refresh();
		}

		private void Refresh()
		{
			if ((Object)(object)m_target == (Object)null)
			{
				return;
			}
			IEnumerable<StorageGroup> enumerable = m_target.GetGroupedView();
			if (!string.IsNullOrEmpty(m_searchFilter))
			{
				enumerable = enumerable.Where((StorageGroup g) => g.DisplayName.IndexOf(m_searchFilter, StringComparison.OrdinalIgnoreCase) >= 0);
			}
			if (m_typeMask != 0)
			{
				enumerable = enumerable.Where((StorageGroup g) => g.BackingItems.Count > 0 && ItemTypeFilters.Matches(m_typeMask, g.BackingItems[0]));
			}
			List<StorageGroup> list = Sort(enumerable).ToList();
			m_shownRevision = m_target.Revision;
			HashSet<string> hashSet = new HashSet<string>();
			for (int num = 0; num < list.Count; num++)
			{
				StorageGroup storageGroup = list[num];
				hashSet.Add(storageGroup.Key);
				if (!m_activeCells.TryGetValue(storageGroup.Key, out var value))
				{
					value = ((m_freeCells.Count > 0) ? m_freeCells.Pop() : CreateCell());
					m_activeCells[storageGroup.Key] = value;
					value.SetActive(true);
				}
				value.transform.SetSiblingIndex(num);
				PopulateCell(value, storageGroup);
			}
			List<string> list2 = null;
			foreach (KeyValuePair<string, GameObject> activeCell in m_activeCells)
			{
				if (!hashSet.Contains(activeCell.Key))
				{
					activeCell.Value.SetActive(false);
					m_freeCells.Push(activeCell.Value);
					(list2 ?? (list2 = new List<string>())).Add(activeCell.Key);
				}
			}
			if (list2 != null)
			{
				foreach (string item in list2)
				{
					m_activeCells.Remove(item);
				}
			}
			m_capacityText.text = $"{m_target.CurrentCount} / {m_target.Capacity} types";
		}

		private IEnumerable<StorageGroup> Sort(IEnumerable<StorageGroup> groups)
		{
			return m_sortMode switch
			{
				1 => groups.OrderByDescending<StorageGroup, string>((StorageGroup g) => g.DisplayName, StringComparer.OrdinalIgnoreCase), 
				2 => groups.OrderByDescending((StorageGroup g) => g.TotalCount).ThenBy<StorageGroup, string>((StorageGroup g) => g.DisplayName, StringComparer.OrdinalIgnoreCase), 
				3 => groups.OrderBy((StorageGroup g) => g.TotalCount).ThenBy<StorageGroup, string>((StorageGroup g) => g.DisplayName, StringComparer.OrdinalIgnoreCase), 
				4 => groups.OrderBy((StorageGroup g) => (g.BackingItems.Count > 0) ? ((int)g.BackingItems[0].m_shared.m_itemType) : int.MaxValue).ThenBy<StorageGroup, string>((StorageGroup g) => g.DisplayName, StringComparer.OrdinalIgnoreCase), 
				_ => groups.OrderBy<StorageGroup, string>((StorageGroup g) => g.DisplayName, StringComparer.OrdinalIgnoreCase), 
			};
		}

		private GameObject GetElementPrefab()
		{
			if ((Object)(object)m_elementPrefab == (Object)null && (Object)(object)InventoryGui.instance != (Object)null && (Object)(object)InventoryGui.instance.m_playerGrid != (Object)null)
			{
				m_elementPrefab = InventoryGui.instance.m_playerGrid.m_elementPrefab;
			}
			return m_elementPrefab;
		}

		private GameObject CreateCell()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: 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_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			GameObject elementPrefab = GetElementPrefab();
			GameObject val = (GameObject)(((Object)(object)elementPrefab != (Object)null) ? ((object)Object.Instantiate<GameObject>(elementPrefab, (Transform)(object)m_storageContent, false)) : ((object)new GameObject("SpiritStorageCell", new Type[1] { typeof(RectTransform) })));
			((Object)val).name = "SpiritStorageCell";
			GameObject val2 = new GameObject("RarityTint", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			RectTransform component = val2.GetComponent<RectTransform>();
			val2.transform.SetParent(val.transform, false);
			((Transform)component).SetAsFirstSibling();
			component.anchorMin = Vector2.zero;
			component.anchorMax = Vector2.one;
			component.offsetMin = Vector2.zero;
			component.offsetMax = Vector2.zero;
			Image component2 = val2.GetComponent<Image>();
			((Graphic)component2).color = new Color(0f, 0f, 0f, 0.35f);
			((Graphic)component2).raycastTarget = false;
			SpiritStorageCell spiritStorageCell = val.AddComponent<SpiritStorageCell>();
			spiritStorageCell.Panel = this;
			spiritStorageCell.Element = val.GetComponent<InventoryElement>();
			spiritStorageCell.RarityTint = component2;
			InventoryElement element = spiritStorageCell.Element;
			if ((Object)(object)element != (Object)null)
			{
				if ((Object)(object)element.m_equiped != (Object)null)
				{
					((Behaviour)element.m_equiped).enabled = false;
				}
				if ((Object)(object)element.m_queued != (Object)null)
				{
					((Behaviour)element.m_queued).enabled = false;
				}
				if ((Object)(object)element.m_noteleport != (Object)null)
				{
					((Behaviour)element.m_noteleport).enabled = false;
				}
				if ((Object)(object)element.m_food != (Object)null)
				{
					((Behaviour)element.m_food).enabled = false;
				}
				if ((Object)(object)element.m_dropFocus != (Object)null)
				{
					((Behaviour)element.m_dropFocus).enabled = false;
				}
				if ((Object)(object)element.m_selected != (Object)null)
				{
					element.m_selected.SetActive(false);
				}
				if ((Object)(object)element.m_tooltip != (Object)null)
				{
					if ((Object)(object)m_tooltipPrefab == (Object)null)
					{
						m_tooltipPrefab = element.m_tooltip.m_tooltipPrefab;
					}
					((Behaviour)element.m_tooltip).enabled = false;
				}
			}
			Transform val3 = val.transform.Find("binding");
			if ((Object)(object)val3 != (Object)null)
			{
				((Component)val3).gameObject.SetActive(false);
			}
			return val;
		}

		private void PopulateCell(GameObject cell, StorageGroup group)
		{
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			SpiritStorageCell component = cell.GetComponent<SpiritStorageCell>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			component.Group = group;
			InventoryElement element = component.Element;
			if ((Object)(object)element != (Object)null)
			{
				if ((Object)(object)element.m_icon != (Object)null)
				{
					element.m_icon.sprite = group.Icon;
					((Behaviour)element.m_icon).enabled = (Object)(object)group.Icon != (Object)null;
				}
				if ((Object)(object)element.m_amount != (Object)null)
				{
					element.m_amount.text = ((group.TotalCount > 1) ? group.TotalCount.ToString() : "");
				}
				if ((Object)(object)element.m_quality != (Object)null)
				{
					element.m_quality.text = ((group.Quality > 1) ? group.Quality.ToString() : "");
				}
				if ((Object)(object)element.m_durability != (Object)null)
				{
					if (group.DurabilityPercent >= 0f)
					{
						((Component)element.m_durability).gameObject.SetActive(true);
						element.m_durability.SetMaxValue(1f);
						element.m_durability.SetValue(group.DurabilityPercent);
						element.m_durability.SetColor(Color.Lerp(Color.red, Color.green, Mathf.Clamp01(group.DurabilityPercent)));
					}
					else
					{
						((Component)element.m_durability).gameObject.SetActive(false);
					}
				}
				if (group.BackingItems.Count > 0)
				{
					component.TooltipTopic = group.DisplayName;
					component.TooltipText = group.BackingItems[0].GetTooltip(group.TotalCount) + "\n\n<color=#a0a0a0>Click: take one stack\nCtrl+Click: take all\nShift+Click: choose amount</color>";
				}
			}
			if ((Object)(object)component.RarityTint != (Object)null)
			{
				if (group.BackingItems.Count > 0 && ItemNaming.TryGetRarityColor(group.BackingItems[0], out var color))
				{
					color.a = 0.55f;
					((Graphic)component.RarityTint).color = color;
				}
				else
				{
					((Graphic)component.RarityTint).color = new Color(0f, 0f, 0f, 0.35f);
				}
			}
		}
	}
	internal class SpiritStorageCell : MonoBehaviour, IPointerClickHandler, IEventSystemHandler, IPointerEnterHandler, IPointerExitHandler
	{
		public SpiritStoragePanel Panel;

		public StorageGroup Group;

		public InventoryElement Element;

		public Image RarityTint;

		public string TooltipTopic;

		public string TooltipText;

		public void OnPointerEnter(PointerEventData eventData)
		{
			Panel?.OnCellHoverEnter(this);
		}

		public void OnPointerExit(PointerEventData eventData)
		{
			Panel?.OnCellHoverExit(this);
		}

		private void OnDisable()
		{
			Panel?.OnCellHoverExit(this);
		}

		public void OnPointerClick(PointerEventData eventData)
		{
			if (!((Object)(object)Panel == (Object)null) && Group != null)
			{
				bool num = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
				bool flag = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305);
				if (num)
				{
					Panel.OpenSplitPanel(Group);
				}
				else if (flag)
				{
					Panel.RequestTakeAll(Group);
				}
				else
				{
					Panel.RequestTake(Group);
				}
			}
		}
	}
	internal class SpiritStoragePanelDragSaver : MonoBehaviour, IEndDragHandler, IEventSystemHandler
	{
		public SpiritStoragePanel Panel;

		public void OnEndDrag(PointerEventData eventData)
		{
			Panel?.SavePosition();
		}
	}
	internal static class ItemTypeFilters
	{
		public static readonly string[] Labels = new string[15]
		{
			"Food", "Potions", "Metals", "Woods", "Ingredients", "Valuables", "Weapons", "Ranged", "Ammo", "Armour",
			"Magic", "Tools", "Trophies", "Misc", "Mods"
		};

		private static readonly int Food = Bit("Food");

		private static readonly int Potions = Bit("Potions");

		private static readonly int Metals = Bit("Metals");

		private static readonly int Woods = Bit("Woods");

		private static readonly int Ingredients = Bit("Ingredients");

		private static readonly int Valuables = Bit("Valuables");

		private static readonly int Weapons = Bit("Weapons");

		private static readonly int Ranged = Bit("Ranged");

		private static readonly int Ammo = Bit("Ammo");

		private static readonly int Armour = Bit("Armour");

		private static readonly int Magic = Bit("Magic");

		private static readonly int Tools = Bit("Tools");

		private static readonly int Trophies = Bit("Trophies");

		private static readonly int Misc = Bit("Misc");

		private static readonly int Mods = Bit("Mods");

		private static readonly string[] MetalHints = new string[11]
		{
			"Ore", "Scrap", "Metal", "Copper", "Tin", "Bronze", "Iron", "Silver", "Flametal", "Nails",
			"Chain"
		};

		private static readonly string[] WoodHints = new string[3] { "Wood", "Log", "Bark" };

		private static int Bit(string label)
		{
			return 1 << Array.IndexOf(Labels, label);
		}

		public static int ParseMask(string saved)
		{
			int num = 0;
			string[] array = (saved ?? "").Split(new char[1] { ',' });
			foreach (string text in array)
			{
				int num2 = Array.IndexOf(Labels, text.Trim());
				if (num2 >= 0)
				{
					num |= 1 << num2;
				}
			}
			return num;
		}

		public static string FormatMask(int mask)
		{
			return string.Join(",", Labels.Where((string _, int i) => (mask & (1 << i)) != 0));
		}

		public static bool Matches(int mask, ItemData item)
		{
			if (mask != 0)
			{
				return (mask & Categorize(item)) != 0;
			}
			return true;
		}

		private static int Categorize(ItemData item)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected I4, but got Unknown
			//IL_0116: 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)
			//IL_011d: 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_0142: Expected I4, but got Unknown
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Invalid comparison between Unknown and I4
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Invalid comparison between Unknown and I4
			SharedData shared = item.m_shared;
			string text = SpiritStorageContainer.ResolvePrefabName(item) ?? "";
			int num = 0;
			ItemType itemType = shared.m_itemType;
			switch (itemType - 1)
			{
			case 1:
				num |= ((shared.m_food > 0f || shared.m_foodStamina > 0f || shared.m_foodEitr > 0f) ? Food : Potions);
				break;
			case 0:
				if (ContainsAny(text, MetalHints))
				{
					num |= Metals;
				}
				else if (ContainsAny(text, WoodHints))
				{
					num |= Woods;
				}
				else if (shared.m_value <= 0)
				{
					num |= Ingredients;
				}
				break;
			case 2:
			case 3:
			case 13:
			case 21:
			{
				SkillType skillType = shared.m_skillType;
				switch (skillType - 8)
				{
				default:
					if ((int)skillType == 104)
					{
						goto case 4;
					}
					goto case 3;
				case 0:
				case 6:
					num |= Ranged;
					break;
				case 1:
				case 2:
					num |= Ranged | Magic;
					break;
				case 4:
					num |= Tools;
					break;
				case 3:
				case 5:
					num |= (((int)shared.m_itemType == 4) ? Ranged : Weapons);
					break;
				}
				break;
			}
			case 8:
			case 22:
				num |= Ammo;
				break;
			case 4:
			case 5:
			case 6:
			case 10:
			case 11:
			case 16:
			case 17:
			case 23:
				num |= Armour;
				break;
			case 14:
			case 18:
				num |= Tools;
				break;
			case 12:
				num |= Trophies;
				break;
			}
			if (shared.m_value > 0)
			{
				num |= Valuables;
			}
			if (ItemNaming.TryGetRarityColor(item, out var _))
			{
				num |= Magic;
			}
			if (IsModded(text))
			{
				num |= Mods;
			}
			if ((num & ~Mods) == 0)
			{
				num |= Misc;
			}
			return num;
		}

		private static bool ContainsAny(string text, string[] hints)
		{
			foreach (string value in hints)
			{
				if (text.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsModded(string prefabName)
		{
			try
			{
				return ModQuery.GetPrefab(prefabName) != null;
			}
			catch (Exception)
			{
				return false;
			}
		}
	}
	[BepInPlugin("com.codex.valheim.spiritstorage", "Spirit Storage", "0.10.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class SpiritStoragePlugin : BaseUnityPlugin
	{
		public const string ModGuid = "com.codex.valheim.spiritstorage";

		public const string ModName = "Spirit Storage";

		public const string ModVersion = "0.10.3";

		private const string PrefabName = "SpiritStorage";

		private const string PieceToken = "$piece_spiritstorage";

		private const string PieceDescriptionTok