Decompiled source of GearAndStorage v0.5.17

plugins/GearAndStorage/GearAndStorage.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GaS;
using GearAndStorage.Core;
using HarmonyLib;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("GearAndStorage")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.17.0")]
[assembly: AssemblyInformationalVersion("0.5.17")]
[assembly: AssemblyProduct("GearAndStorage")]
[assembly: AssemblyTitle("GearAndStorage")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.5.17.0")]
[module: UnverifiableCode]
namespace GaS
{
	internal static class L
	{
		internal static Func<string> Language = () => "English";

		internal static readonly Dictionary<string, string[]> Catalog = Load();

		internal static string Code
		{
			get
			{
				switch (Language()?.ToLowerInvariant() ?? "")
				{
				default:
					return "en";
				case "german":
				case "deutsch":
				case "de":
					return "de";
				case "czech":
				case "čeština":
				case "cs":
					return "cs";
				}
			}
		}

		private static Dictionary<string, string[]> Load()
		{
			Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>(StringComparer.Ordinal);
			using StreamReader streamReader = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("GaS.Translations.tsv"));
			string text;
			while ((text = streamReader.ReadLine()) != null)
			{
				if (text.Length != 0 && !text.StartsWith("#"))
				{
					string[] array = text.Split('\t');
					if (array.Length != 4)
					{
						throw new InvalidDataException("Invalid localization row: " + array[0]);
					}
					dictionary.Add(array[0], new string[3]
					{
						array[1],
						array[2],
						array[3]
					});
				}
			}
			return dictionary;
		}

		internal static string T(string source)
		{
			if (source == null)
			{
				return "";
			}
			if (!Catalog.TryGetValue(source, out var value))
			{
				return source;
			}
			return value[(Code == "cs") ? 1 : ((Code == "de") ? 2 : 0)];
		}

		internal static string F(string source, params object[] args)
		{
			return string.Format(CultureInfo.CurrentCulture, T(source), args);
		}

		internal static string Notice(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			foreach (KeyValuePair<string, string[]> item in Catalog)
			{
				string[] value = item.Value;
				foreach (string text2 in value)
				{
					if (text == text2)
					{
						return T(item.Key);
					}
					if (item.Key.EndsWith(": ", StringComparison.Ordinal) && text.StartsWith(text2, StringComparison.Ordinal))
					{
						return T(item.Key) + text.Substring(text2.Length);
					}
				}
			}
			return text;
		}
	}
	internal static class LanguageRuntime
	{
		private sealed class ConfigurationManagerAttributes
		{
			public string DispName;

			public string Category;

			public string Description;

			public Action<ConfigEntryBase> CustomDrawer;

			private bool expanded;

			public ConfigurationManagerAttributes(ConfigEntryBase entry)
			{
				DispName = Label(entry.Definition.Key);
				Category = Label(entry.Definition.Section);
				Description = Describe(entry.Description.Description);
				if (entry.SettingType.IsEnum && entry.Definition.Key == "Difficulty")
				{
					CustomDrawer = DrawDifficulty;
				}
			}

			private void DrawDifficulty(ConfigEntryBase entry)
			{
				GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
				if (GUILayout.Button(L.T(entry.BoxedValue.ToString()) + " ▾", Array.Empty<GUILayoutOption>()))
				{
					expanded = !expanded;
				}
				if (expanded)
				{
					foreach (object value in Enum.GetValues(entry.SettingType))
					{
						if (GUILayout.Button(L.T(value.ToString()), Array.Empty<GUILayoutOption>()))
						{
							entry.BoxedValue = value;
							expanded = false;
						}
					}
				}
				GUILayout.EndVertical();
			}
		}

		private static string language;

		private static readonly List<ConfigFile> files = new List<ConfigFile>();

		internal static void Register(ConfigFile file)
		{
			if (!files.Contains(file))
			{
				files.Add(file);
			}
		}

		internal static void Tick()
		{
			L.Language = () => (Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : "English";
			if (language == L.Code)
			{
				return;
			}
			language = L.Code;
			foreach (ConfigFile file in files)
			{
				foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in file)
				{
					Attach(item.Value);
				}
			}
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				BaseUnityPlugin instance = value.Instance;
				if (!((Object)(object)instance == (Object)null) && !(((object)instance).GetType().FullName != "ConfigurationManager.ConfigurationManager"))
				{
					((object)instance).GetType().GetMethod("BuildSettingList", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(instance, null);
				}
			}
		}

		private static void Attach(ConfigEntryBase entry)
		{
			object[] tags = entry.Description.Tags;
			tags = tags.Where((object t) => !(t is ConfigurationManagerAttributes)).ToArray();
			typeof(ConfigDescription).GetField("<Tags>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(entry.Description, tags.Concat(new object[1]
			{
				new ConfigurationManagerAttributes(entry)
			}).ToArray());
		}

		internal static string Label(string key)
		{
			if (key.StartsWith("Storage."))
			{
				return L.T("Storage") + ": " + L.T(key.Substring(8));
			}
			if (key.StartsWith("Allow") && key.Length > 5)
			{
				return L.T("Allow") + ": " + L.T(key.Substring(5));
			}
			if (key.StartsWith("Hotkey"))
			{
				return L.T("Hotkey") + " " + key.Substring(6);
			}
			return L.T(key);
		}

		internal static string Describe(string source)
		{
			if (source.StartsWith("Additionally allow restricted items of ") && source.EndsWith(" even before the trophy unlock. False does not revoke an altar unlock."))
			{
				return L.F("portal.allow.description", L.T(source.Substring("Additionally allow restricted items of ".Length, source.Length - "Additionally allow restricted items of ".Length - " even before the trophy unlock. False does not revoke an altar unlock.".Length)));
			}
			return L.T(source);
		}

		internal static string Creature(string prefab)
		{
			ZNetScene instance = ZNetScene.instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				GameObject prefab2 = instance.GetPrefab(prefab);
				obj = ((prefab2 == null) ? null : prefab2.GetComponent<Character>()?.m_name);
			}
			if (obj == null)
			{
				obj = prefab;
			}
			string text = (string)obj;
			if (Localization.instance != null)
			{
				return Localization.instance.Localize(text);
			}
			return text;
		}
	}
}
namespace GearAndStorage
{
	internal static class ChestCrafting
	{
		[HarmonyPatch(typeof(Container), "Awake")]
		private static class Register
		{
			private static void Postfix(Container __instance)
			{
				if (__instance.m_inventory != null)
				{
					Containers.Add(__instance);
					nextScan = 0f;
				}
			}
		}

		[HarmonyPatch(typeof(Player), "HaveRequirementItems")]
		private static class Requirements
		{
			private static void Prefix(Player __instance, bool discover, out bool __state)
			{
				__state = !discover && Enter(__instance);
			}

			private static void Finalizer(bool __state)
			{
				Exit(__state);
			}
		}

		[HarmonyPatch(typeof(Player), "GetFirstRequiredItem")]
		private static class OneIngredient
		{
			private static void Prefix(Player __instance, out bool __state)
			{
				__state = Enter(__instance);
			}

			private static void Finalizer(bool __state)
			{
				Exit(__state);
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
		private static class RequirementLabel
		{
			private static void Prefix(Player player, bool craft, out bool __state)
			{
				__state = Enter(player);
			}

			private static void Finalizer(bool __state)
			{
				Exit(__state);
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
		private static class Craft
		{
			private static void Prefix(Player player, out bool __state)
			{
				__state = Enter(player, force: true);
				if (__state)
				{
					craftingDepth++;
				}
			}

			private static void Finalizer(bool __state)
			{
				if (__state)
				{
					craftingDepth--;
				}
				Exit(__state);
			}
		}

		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Piece),
			typeof(RequirementMode)
		})]
		private static class BuildRequirements
		{
			private static void Prefix(Player __instance, RequirementMode mode, out bool __state)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0003: Invalid comparison between Unknown and I4
				__state = (int)mode != 1 && Enter(__instance);
			}

			private static void Finalizer(bool __state)
			{
				Exit(__state);
			}
		}

		[HarmonyPatch(typeof(Player), "UpdatePlacement")]
		private static class Build
		{
			private static void Prefix(Player __instance, bool takeInput, out bool __state)
			{
				__state = takeInput && ((Character)__instance).InPlaceMode() && Enter(__instance);
			}

			private static void Finalizer(bool __state)
			{
				Exit(__state);
			}
		}

		[HarmonyPatch(typeof(Player), "TryPlacePiece")]
		private static class Place
		{
			private static bool Prefix(Player __instance, Piece piece, ref bool __result, out bool __state)
			{
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				__state = Enter(__instance, force: true);
				if (__state && !__instance.m_noPlacementCost && !ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey()) && !__instance.HaveRequirements(piece, (RequirementMode)0))
				{
					__result = false;
					return false;
				}
				return true;
			}

			private static void Finalizer(bool __state)
			{
				Exit(__state);
			}
		}

		[HarmonyPatch(typeof(Player), "ConsumeResources")]
		private static class Consume
		{
			private static void Prefix(Player __instance, out bool __state)
			{
				__state = !__instance.m_noPlacementCost && Enter(__instance, queryDepth == 0);
				if (__state)
				{
					craftingDepth++;
				}
			}

			private static void Finalizer(bool __state)
			{
				if (__state)
				{
					craftingDepth--;
				}
				Exit(__state);
			}
		}

		[HarmonyPatch(typeof(Inventory), "HaveItem", new Type[]
		{
			typeof(string),
			typeof(bool)
		})]
		private static class HaveMaterial
		{
			private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result)
			{
				if (__result || !Query(__instance))
				{
					return;
				}
				foreach (Container source in sources)
				{
					if (Accessible(source, Player.m_localPlayer) && source.m_inventory.HaveItem(name, matchWorldLevel))
					{
						__result = true;
						break;
					}
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "CountItems")]
		private static class Count
		{
			private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result)
			{
				if (!Query(__instance))
				{
					return;
				}
				foreach (Container source in sources)
				{
					if (Accessible(source, Player.m_localPlayer))
					{
						__result += source.m_inventory.CountItems(name, quality, matchWorldLevel);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "GetItem", new Type[]
		{
			typeof(string),
			typeof(int),
			typeof(bool)
		})]
		private static class Lookup
		{
			private static void Postfix(Inventory __instance, string name, int quality, bool isPrefabName, ref ItemData __result)
			{
				if (__result != null || !Query(__instance))
				{
					return;
				}
				foreach (Container source in sources)
				{
					if (Accessible(source, Player.m_localPlayer))
					{
						__result = source.m_inventory.GetItem(name, quality, isPrefabName);
						if (__result != null)
						{
							break;
						}
					}
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "ItemCheated", new Type[]
		{
			typeof(string),
			typeof(int),
			typeof(bool)
		})]
		private static class CheatedIngredients
		{
			private static void Postfix(Inventory __instance, string itemName, int quality, bool matchWorldLevel, ref bool __result)
			{
				if (__result || !Query(__instance))
				{
					return;
				}
				foreach (Container source in sources)
				{
					if (Accessible(source, Player.m_localPlayer) && source.m_inventory.ItemCheated(itemName, quality, matchWorldLevel))
					{
						__result = true;
						break;
					}
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "ItemCheated", new Type[]
		{
			typeof(Requirement[]),
			typeof(int),
			typeof(bool)
		})]
		private static class CheatedRecipe
		{
			private static void Postfix(Inventory __instance, Requirement[] resources, int quality, bool matchWorldLevel, ref bool __result)
			{
				if (__result || !Query(__instance))
				{
					return;
				}
				foreach (Container source in sources)
				{
					if (Accessible(source, Player.m_localPlayer) && source.m_inventory.ItemCheated(resources, quality, matchWorldLevel))
					{
						__result = true;
						break;
					}
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
		{
			typeof(string),
			typeof(int),
			typeof(int),
			typeof(bool)
		})]
		private static class Payment
		{
			private static bool Prefix(Inventory __instance, string name, int amount, int itemQuality, bool worldLevelBased)
			{
				if (craftingDepth == 0 || !Query(__instance))
				{
					return true;
				}
				List<Inventory> list = new List<Inventory>();
				list.Add(__instance);
				list.AddRange(from c in sources
					where Accessible(c, Player.m_localPlayer)
					select c.m_inventory);
				List<Tuple<Inventory, ItemData>> list2 = new List<Tuple<Inventory, ItemData>>();
				foreach (Inventory item2 in list)
				{
					foreach (ItemData allItem in item2.GetAllItems())
					{
						if (!(allItem.m_shared.m_name != name) && (itemQuality < 0 || allItem.m_quality == itemQuality) && (!worldLevelBased || allItem.m_worldLevel >= Game.m_worldLevel))
						{
							list2.Add(Tuple.Create<Inventory, ItemData>(item2, allItem));
						}
					}
				}
				int[] array = PaymentPlanner.Allocate(list2.Select((Tuple<Inventory, ItemData> entry) => entry.Item2.m_stack).ToArray(), amount);
				if (array == null)
				{
					throw new InvalidOperationException("GearAndStorage: crafting materials changed during native crafting; payment aborted.");
				}
				HashSet<Inventory> changed = new HashSet<Inventory>();
				for (int num = 0; num < array.Length; num++)
				{
					if (array[num] != 0)
					{
						ItemData item = list2[num].Item2;
						item.m_stack -= array[num];
						changed.Add(list2[num].Item1);
					}
				}
				foreach (Inventory item3 in changed)
				{
					item3.m_inventory.RemoveAll((ItemData val) => val.m_stack <= 0);
				}
				foreach (Container item4 in sources.Where((Container c) => changed.Contains(c.m_inventory)))
				{
					SaveChest(item4);
				}
				if (changed.Contains(__instance))
				{
					__instance.Changed(false, false);
				}
				return false;
			}
		}

		private static ConfigEntry<bool> Enabled;

		private static ConfigEntry<float> Radius;

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

		private static List<Container> nearby = new List<Container>();

		private static float nextScan;

		private static Vector3 scanPosition;

		private static Player scanningPlayer;

		private static int queryDepth;

		private static int craftingDepth;

		private static List<Container> sources;

		internal static IEnumerable<Container> LoadedContainers => Containers.Where((Container c) => (Object)(object)c != (Object)null);

		internal static bool IsEnabled => Enabled.Value;

		internal static float ChestRadius => Radius.Value;

		internal static void Initialize(ConfigFile config)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			Enabled = config.Bind<bool>("Crafting", "CraftFromChests", false, "Use materials from nearby accessible closed chests for crafting, upgrades and building. One toggle and range for all three. Furnace feeding is separate.");
			Radius = config.Bind<float>("Crafting", "ChestRange", 20f, new ConfigDescription("Maximum distance from the player in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
		}

		internal static bool Eligible(Container c, Player player)
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)c != (Object)null && c.m_inventory != null && (Object)(object)((Component)c).GetComponentInParent<TombStone>() == (Object)null && (Object)(object)player != (Object)null && (Object)(object)c.m_nview != (Object)null && c.m_nview.IsValid() && !c.IsInUse() && c.m_nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 0 && ((Object)(object)c.m_wagon == (Object)null || !c.m_wagon.InUse()) && Vector3.Distance(((Component)player).transform.position, ((Component)c).transform.position) <= Radius.Value && c.CheckAccess(player.GetPlayerID()))
			{
				if (c.m_checkGuardStone)
				{
					return PrivateArea.CheckAccess(((Component)c).transform.position, 0f, false, false);
				}
				return true;
			}
			return false;
		}

		private static bool Accessible(Container c, Player player)
		{
			return Eligible(c, player);
		}

		internal static void SaveChest(Container chest)
		{
			chest.Save();
			chest.m_inventory.Changed(false, false);
		}

		internal static List<Container> ExternalSources()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!Enabled.Value || (Object)(object)localPlayer == (Object)null)
			{
				return new List<Container>();
			}
			List<Container> list = Scan(localPlayer, force: false);
			foreach (Container item in list)
			{
				item.Load();
			}
			return list;
		}

		internal static bool MachineAccess(Vector3 position, long creator)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			foreach (PrivateArea allArea in PrivateArea.m_allAreas)
			{
				if (!((Object)(object)allArea == (Object)null) && allArea.IsEnabled() && allArea.IsInside(position, 0f))
				{
					if (allArea.m_piece.GetCreator() == creator || allArea.IsPermitted(creator))
					{
						return true;
					}
					flag = true;
				}
			}
			return !flag;
		}

		internal static List<Container> ForMachine(Vector3 center, long creator, float range = 10f)
		{
			//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)
			Containers.RemoveWhere((Container c) => (Object)(object)c == (Object)null);
			return (from c in Containers
				where MachineChestAccess.Allowed(c, center, creator, range)
				orderby Vector3.SqrMagnitude(((Component)c).transform.position - center)
				select c).ToList();
		}

		private static List<Container> Scan(Player player, bool force)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			if (force || (Object)(object)player != (Object)(object)scanningPlayer || Time.time >= nextScan || Vector3.Distance(scanPosition, ((Component)player).transform.position) > 1f)
			{
				Containers.RemoveWhere((Container c) => (Object)(object)c == (Object)null);
				nearby = (from c in Containers
					where Accessible(c, player)
					orderby Vector3.SqrMagnitude(((Component)c).transform.position - ((Component)player).transform.position)
					select c).ToList();
				foreach (Container item in nearby)
				{
					item.Load();
				}
				scanningPlayer = player;
				scanPosition = ((Component)player).transform.position;
				nextScan = Time.time + 3f;
			}
			return nearby.Where((Container c) => Accessible(c, player)).ToList();
		}

		private static bool Enter(Player player, bool force = false)
		{
			if (!Enabled.Value || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			if (queryDepth == 0 || force)
			{
				sources = Scan(player, force);
				if (force)
				{
					foreach (Container source in sources)
					{
						source.Load();
					}
				}
			}
			queryDepth++;
			return true;
		}

		private static void Exit(bool entered)
		{
			if (entered)
			{
				queryDepth--;
				if (queryDepth == 0)
				{
					sources = null;
				}
			}
		}

		private static bool Query(Inventory inventory)
		{
			if (queryDepth > 0 && sources != null && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				return inventory == ((Humanoid)Player.m_localPlayer).GetInventory();
			}
			return false;
		}
	}
	internal static class CoinPouchEpicLoot
	{
		[HarmonyPatch]
		private static class Passive
		{
			private static bool Prepare()
			{
				return Available;
			}

			private static MethodBase TargetMethod()
			{
				return Aggregate;
			}

			private static void Postfix(Player player, string effectType, object __result)
			{
				int num = CoinPouches.Tier(CoinPouches.Equipped(player));
				if (num >= 0 && (effectType == null || !(effectType != Effects[num])) && __result is IList list)
				{
					list.Add(CreateEffect.Invoke(new object[2]
					{
						Effects[num],
						Values[num]
					}));
				}
			}
		}

		internal static readonly string[] Effects = new string[8] { "ModifyStaminaRegen", "ModifyMovementSpeed", "AddPoisonResistancePercentage", "AddFrostResistancePercentage", "ModifyStaminaRegen", "ModifyEitrRegen", "AddFireResistancePercentage", "ModifyHealthRegen" };

		internal static readonly float[] Values = new float[8] { 3f, 2f, 5f, 5f, 6f, 6f, 8f, 8f };

		private static readonly Type Extensions = AccessTools.TypeByName("EpicLoot.PlayerExtensions");

		private static readonly MethodInfo Aggregate = ((Extensions == null) ? null : AccessTools.Method(Extensions, "GetAllActiveMagicEffects", (Type[])null, (Type[])null));

		private static readonly Type Cache = ((Extensions == null) ? null : AccessTools.TypeByName("EpicLoot.EquipmentEffectCache"));

		private static readonly MethodInfo Reset = ((Cache == null) ? null : AccessTools.Method(Cache, "Reset", new Type[1] { typeof(Player) }, (Type[])null));

		private static readonly Type Effect = ((Extensions == null) ? null : AccessTools.TypeByName("EpicLoot.MagicItemEffect"));

		private static readonly ConstructorInfo CreateEffect = Effect?.GetConstructor(new Type[2]
		{
			typeof(string),
			typeof(float)
		});

		internal static bool Available
		{
			get
			{
				if (Aggregate != null)
				{
					return CreateEffect != null;
				}
				return false;
			}
		}

		internal static void Invalidate(Player player)
		{
			if ((Object)(object)player != (Object)null)
			{
				Reset?.Invoke(null, new object[1] { player });
			}
		}
	}
	internal static class CoinPouches
	{
		[HarmonyPatch(typeof(Inventory), "CanAddItem", new Type[]
		{
			typeof(ItemData),
			typeof(int)
		})]
		private static class CapacityCheck
		{
			private static void Postfix(Inventory __instance, ItemData item, int stack, ref bool __result)
			{
				if (!__result && OrdinaryCoin(item) && PlayerSlots.TryState(__instance, out var state) && !state.Loading)
				{
					int num = ((stack <= 0) ? item.m_stack : stack);
					__result = RewardRoom(__instance) + __instance.FindFreeStackSpace(item.m_shared.m_name, (float)item.m_worldLevel) + __instance.GetEmptySlots() * 10000 >= num;
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })]
		private static class Pickup
		{
			private static bool Prefix(Inventory __instance, ItemData item, ref bool __result)
			{
				if (Busy || !OrdinaryCoin(item) || __instance.ContainsItem(item) || RewardRoom(__instance) == 0 || !PlayerSlots.TryState(__instance, out var state) || state.Loading)
				{
					return true;
				}
				__result = Grant(__instance, state.NormalRows, (IEnumerable<ItemData>)(object)new ItemData[1] { item });
				if (__result)
				{
					__instance.Changed(false, false);
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(Inventory), "CountItems")]
		private static class Count
		{
			private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result)
			{
				if (!Busy)
				{
					__result += Available(__instance, name, quality, matchWorldLevel);
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "HaveItem", new Type[]
		{
			typeof(string),
			typeof(bool)
		})]
		private static class Have
		{
			private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result)
			{
				if (!__result && !Busy)
				{
					__result = Available(__instance, name, -1, matchWorldLevel) > 0;
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
		{
			typeof(string),
			typeof(int),
			typeof(int),
			typeof(bool)
		})]
		private static class Spend
		{
			[HarmonyPriority(800)]
			private static void Prefix(Inventory __instance, string name, ref int amount, int itemQuality, bool worldLevelBased)
			{
				if (Busy || amount <= 0)
				{
					return;
				}
				int num = Available(__instance, name, itemQuality, worldLevelBased);
				if (num != 0)
				{
					int num2 = __instance.m_inventory.Where((ItemData i) => i.m_shared.m_name == name && (itemQuality < 0 || i.m_quality == itemQuality) && (!worldLevelBased || i.m_worldLevel >= Game.m_worldLevel)).Sum((ItemData i) => i.m_stack);
					int num3 = Math.Min(num, Math.Max(0, amount - num2));
					if (num3 > 0)
					{
						ItemData item = Wallet(__instance);
						Set(item, Balance(item) - num3);
						amount -= num3;
					}
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "EquipItem")]
		private static class Equip
		{
			[HarmonyPriority(800)]
			private static bool Prefix(Humanoid __instance, ItemData item, ref bool __result)
			{
				if (IsPouch(item))
				{
					Player val = (Player)(object)((__instance is Player) ? __instance : null);
					if (val != null)
					{
						__result = false;
						Inventory inventory = ((Humanoid)val).GetInventory();
						if (!inventory.ContainsItem(item) || ((Character)val).IsDead() || inventory.GetWidth() < 8)
						{
							return false;
						}
						ItemData val2 = Equipped(val);
						if (val2 != null && val2 != item)
						{
							if (!Read(val2, out var balance) || !Read(item, out var balance2) || (balance > 0 && val2.m_worldLevel != item.m_worldLevel))
							{
								return false;
							}
							int num = balance + balance2;
							int num2 = Math.Min(num, Capacity(item));
							int num3 = num - num2;
							if (num3 > 0)
							{
								GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins");
								ItemData val3 = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone();
								val3.m_dropPrefab = itemPrefab;
								val3.m_worldLevel = item.m_worldLevel;
								val3.m_stack = num3;
								if (!QuestRewardInventory.TryGrant(inventory, PlayerSlots.Track(val).NormalRows, (IEnumerable<ItemData>)(object)new ItemData[1] { val3 }))
								{
									return false;
								}
							}
							Set(val2, 0);
							Set(item, num2);
							val2.m_equipped = false;
							PlayerSlots.SetTag(val2, -1);
						}
						item.m_equipped = true;
						PlayerSlots.SetTag(item, 13);
						PlayerSlots.Normalize(val);
						CoinPouchEpicLoot.Invalidate(val);
						inventory.Changed(false, false);
						__result = true;
						return false;
					}
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Humanoid), "UnequipItem")]
		private static class Unequip
		{
			private static bool Prefix(Humanoid __instance, ItemData item)
			{
				if (IsPouch(item))
				{
					Player val = (Player)(object)((__instance is Player) ? __instance : null);
					if (val != null)
					{
						item.m_equipped = false;
						PlayerSlots.SetTag(item, -1);
						CoinPouchEpicLoot.Invalidate(val);
						return false;
					}
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Humanoid), "UnequipAllItems")]
		private static class UnequipAll
		{
			private static void Prefix(Humanoid __instance)
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val != null)
				{
					ItemData val2 = Equipped(val);
					if (val2 != null)
					{
						val2.m_equipped = false;
						CoinPouchEpicLoot.Invalidate(val);
					}
				}
			}
		}

		[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
		{
			typeof(ItemData),
			typeof(int),
			typeof(bool),
			typeof(float),
			typeof(int),
			typeof(bool)
		})]
		private static class Tooltip
		{
			private static void Postfix(ItemData item, ref string __result)
			{
				int num = Tier(item);
				if (num >= 0)
				{
					__result = __result + "\n\n" + L.T("Click: withdraw coins. Shift: split stack. Right-click: unequip. Ctrl + right-click: upgrade at workbench.");
					__result = __result + "\n\n" + (Read(item, out var balance) ? (balance.ToString("N0") + " / " + Capacity(item).ToString("N0") + " " + L.T("coins")) : L.T("Pouch data cannot be read. Contents preserved."));
					__result = __result + "\n" + (CoinPouchEpicLoot.Available ? (L.T("Passive (Epic Loot)") + ": " + L.T(CoinPouchEpicLoot.Effects[num]) + " +" + CoinPouchEpicLoot.Values[num] + "%") : L.T("Install Epic Loot to enable the passive."));
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "IsItemEquiped")]
		private static class IsEquipped
		{
			private static void Postfix(Humanoid __instance, ItemData item, ref bool __result)
			{
				if (IsPouch(item))
				{
					Player val = (Player)(object)((__instance is Player) ? __instance : null);
					if (val != null)
					{
						__result = item.m_equipped && ((Humanoid)val).GetInventory().ContainsItem(item);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "UnequipDeathDropItems")]
		private static class Death
		{
			private static void Prefix(Player __instance)
			{
				ItemData val = Equipped(__instance);
				if (val != null)
				{
					val.m_equipped = false;
					CoinPouchEpicLoot.Invalidate(__instance);
				}
			}
		}

		internal static ConfigEntry<bool> AutoDeposit;

		internal static bool Busy;

		private static float nextTick;

		internal static void Initialize(ConfigFile config)
		{
			AutoDeposit = config.Bind<bool>("CoinPouch", "AutoDeposit", true, "Automatically store ordinary picked-up coins and quest coin rewards in the equipped pouch. Overflow stays in normal inventory.");
		}

		internal static int Tier(ItemData item)
		{
			object obj;
			if (item == null)
			{
				obj = null;
			}
			else
			{
				GameObject dropPrefab = item.m_dropPrefab;
				obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
			}
			string text = (string)obj;
			if (text == null || !text.StartsWith("GaS_CoinPouch_", StringComparison.Ordinal))
			{
				return -1;
			}
			if (!int.TryParse(text.Substring(14), out var result) || result < 1 || result > 8)
			{
				return -1;
			}
			return result - 1;
		}

		internal static bool IsPouch(ItemData item)
		{
			return Tier(item) >= 0;
		}

		internal static int Capacity(ItemData item)
		{
			if (Tier(item) < 0)
			{
				return 0;
			}
			return CoinPouchState.Capacities[Tier(item)];
		}

		internal static bool Read(ItemData item, out int balance)
		{
			balance = 0;
			if (!IsPouch(item))
			{
				return false;
			}
			item.m_customData.TryGetValue("GearAndStorage.pouch.coins.v1", out var value);
			return CoinPouchState.TryRead(value, out balance);
		}

		internal static int Balance(ItemData item)
		{
			if (!Read(item, out var balance))
			{
				return 0;
			}
			return balance;
		}

		internal static void Set(ItemData item, int balance)
		{
			item.m_customData["GearAndStorage.pouch.coins.v1"] = balance.ToString(CultureInfo.InvariantCulture);
		}

		internal static ItemData Equipped(Player p)
		{
			if (p == null)
			{
				return null;
			}
			return ((IEnumerable<ItemData>)((Humanoid)p).GetInventory().m_inventory).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => i.m_equipped && PlayerSlots.Tag(i) == 13 && IsPouch(i)));
		}

		internal static ItemData Wallet(Inventory inv)
		{
			if (!PlayerSlots.TryState(inv, out var state) || state.Loading || state.Player.m_isLoading || ((Character)state.Player).IsDead())
			{
				return null;
			}
			return Equipped(state.Player);
		}

		internal static bool OrdinaryCoin(ItemData item)
		{
			object obj;
			if (item == null)
			{
				obj = null;
			}
			else
			{
				GameObject dropPrefab = item.m_dropPrefab;
				obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
			}
			if ((string?)obj == "Coins" && item.m_quality == 1 && item.m_worldLevel == Game.m_worldLevel && item.m_customData.Count == 0)
			{
				return !item.m_equipped;
			}
			return false;
		}

		internal static int Available(Inventory inv, string name, int quality, bool matchWorld)
		{
			if (name != "$item_coins" || (quality != -1 && quality != 1))
			{
				return 0;
			}
			ItemData val = Wallet(inv);
			if (val == null || (matchWorld && val.m_worldLevel < Game.m_worldLevel))
			{
				return 0;
			}
			return Balance(val);
		}

		internal static int RewardRoom(Inventory inv)
		{
			ItemData val = Wallet(inv);
			if (!AutoDeposit.Value || val == null || val.m_worldLevel != Game.m_worldLevel || !Read(val, out var balance))
			{
				return 0;
			}
			return Math.Max(0, Capacity(val) - balance);
		}

		internal static bool Grant(Inventory inv, int normalRows, IEnumerable<ItemData> rewards)
		{
			List<ItemData> list = new List<ItemData>();
			int num = RewardRoom(inv);
			int num2 = 0;
			foreach (ItemData reward in rewards)
			{
				int num3 = (OrdinaryCoin(reward) ? Math.Min(num, reward.m_stack) : 0);
				num -= num3;
				num2 += num3;
				if (reward.m_stack > num3)
				{
					ItemData val = reward.Clone();
					val.m_stack -= num3;
					list.Add(val);
				}
			}
			if (!QuestRewardInventory.TryGrant(inv, normalRows, list))
			{
				return false;
			}
			if (num2 > 0)
			{
				ItemData item = Wallet(inv);
				Set(item, Balance(item) + num2);
			}
			return true;
		}

		internal static void Tick(Player player)
		{
			if (Busy || Time.realtimeSinceStartup < nextTick)
			{
				return;
			}
			nextTick = Time.realtimeSinceStartup + 0.25f;
			CoinPouchPrefabs.RefreshNames();
			foreach (ItemData item in ((Humanoid)player).GetInventory().m_inventory.Where(IsPouch))
			{
				if (((Object)(object)InventoryGui.instance == (Object)null || InventoryGui.instance.m_dragItem == null) && PlayerSlots.Tag(item) == 13 && !item.m_equipped && Equipped(player) == null && item.m_gridPos.x == 7 && item.m_gridPos.y == PlayerSlots.Track(player).NormalRows)
				{
					((Humanoid)player).EquipItem(item, false);
				}
				if (PlayerSlots.Tag(item) != 13 && item.m_equipped)
				{
					((Humanoid)player).UnequipItem(item, false);
				}
			}
		}

		internal static bool Withdraw(Player player, ItemData pouch, int amount, Vector2i? position = null)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: 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)
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (amount <= 0 || amount > 10000 || Equipped(player) != pouch || !Read(pouch, out var balance) || amount > balance)
			{
				return false;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins");
			ItemData val = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone();
			val.m_dropPrefab = itemPrefab;
			val.m_worldLevel = pouch.m_worldLevel;
			val.m_stack = amount;
			if (position.HasValue)
			{
				Vector2i value = position.Value;
				if (value.x < 0 || value.x >= inventory.GetWidth() || value.y < 0 || value.y >= PlayerSlots.Track(player).NormalRows)
				{
					return false;
				}
				ItemData itemAt = inventory.GetItemAt(value.x, value.y);
				if (itemAt != null)
				{
					if (!OrdinaryCoin(itemAt) || itemAt.m_worldLevel != val.m_worldLevel || itemAt.m_stack + amount > 10000)
					{
						return false;
					}
					itemAt.m_stack += amount;
				}
				else
				{
					val.m_gridPos = value;
					inventory.m_inventory.Add(val);
				}
			}
			else if (!QuestRewardInventory.TryGrant(inventory, PlayerSlots.Track(player).NormalRows, (IEnumerable<ItemData>)(object)new ItemData[1] { val }))
			{
				return false;
			}
			Set(pouch, balance - amount);
			CoinPouchUi.Withdrawn = true;
			inventory.Changed(false, false);
			return true;
		}
	}
	internal static class CoinPouchPrefabs
	{
		[HarmonyPatch]
		private static class Database
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return new string[2] { "Awake", "CopyOtherDB" }.Select((string n) => AccessTools.Method(typeof(ObjectDB), n, (Type[])null, (Type[])null));
			}

			[HarmonyPriority(0)]
			private static void Postfix(ObjectDB __instance)
			{
				Register(__instance);
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class Network
		{
			private static void Postfix(ZNetScene __instance)
			{
				if ((Object)(object)ObjectDB.instance != (Object)null)
				{
					Register(ObjectDB.instance);
				}
				RegisterNetwork(__instance);
			}
		}

		internal static readonly string[] Names = new string[8] { "Meadows pouch", "Black Forest pouch", "Swamp pouch", "Mountain pouch", "Plains pouch", "Mistlands pouch", "Ashlands pouch", "Deep North pouch" };

		internal static readonly string[][] Materials = new string[8][]
		{
			new string[2] { "LeatherScraps", "Wood" },
			new string[2] { "DeerHide", "Bronze" },
			new string[2] { "Guck", "Iron" },
			new string[2] { "WolfPelt", "Silver" },
			new string[3] { "LoxPelt", "Flax", "BlackMetal" },
			new string[3] { "ScaleHide", "Carapace", "Softtissue" },
			new string[2] { "AskHide", "FlametalNew" },
			new string[2] { "SealHide", "ElakingHairBundle" }
		};

		internal static readonly int[][] Costs = new int[8][]
		{
			new int[2] { 10, 5 },
			new int[2] { 10, 4 },
			new int[2] { 10, 4 },
			new int[2] { 10, 4 },
			new int[3] { 6, 20, 6 },
			new int[3] { 10, 10, 10 },
			new int[2] { 10, 6 },
			new int[2] { 10, 10 }
		};

		internal static readonly GameObject[] Prefabs = (GameObject[])(object)new GameObject[8];

		private static GameObject root;

		private static string language;

		private static Sprite[] icons;

		private static void Icons()
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_009c: 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)
			if (icons != null)
			{
				return;
			}
			icons = (Sprite[])(object)new Sprite[8];
			using Stream stream = typeof(Plugin).Assembly.GetManifestResourceStream("GaS.Pouches.png");
			if (stream == null)
			{
				return;
			}
			byte[] array = new byte[stream.Length];
			stream.Read(array, 0, array.Length);
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (ImageConversion.LoadImage(val, array))
			{
				for (int i = 0; i < 8; i++)
				{
					icons[i] = Sprite.Create(val, new Rect((float)(i % 4 * ((Texture)val).width / 4), (float)((1 - i / 4) * ((Texture)val).height / 2), (float)(((Texture)val).width / 4), (float)(((Texture)val).height / 2)), new Vector2(0.5f, 0.5f));
				}
			}
		}

		internal static void Register(ObjectDB db)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Expected O, but got Unknown
			GameObject val = ((IEnumerable<GameObject>)db.m_items).FirstOrDefault((Func<GameObject, bool>)((GameObject p) => (Object)(object)p != (Object)null && ((Object)p).name == "BeltStrength"));
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Icons();
			if ((Object)(object)root == (Object)null)
			{
				root = new GameObject("GaS.CoinPouchPrefabs");
				root.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)root);
			}
			int i;
			for (i = 0; i < 8; i++)
			{
				if ((Object)(object)Prefabs[i] == (Object)null)
				{
					GameObject val2 = Object.Instantiate<GameObject>(val, root.transform);
					((Object)val2).name = "GaS_CoinPouch_" + (i + 1);
					ItemData itemData = val2.GetComponent<ItemDrop>().m_itemData;
					itemData.m_shared = (SharedData)AccessTools.Method(typeof(object), "MemberwiseClone", (Type[])null, (Type[])null).Invoke(itemData.m_shared, null);
					itemData.m_dropPrefab = val2;
					itemData.m_customData = new Dictionary<string, string>();
					itemData.m_stack = 1;
					itemData.m_shared.m_name = L.T(Names[i]);
					itemData.m_shared.m_description = L.T("Coin storage. Equip in the pouch slot. Coins inside add no weight.");
					itemData.m_shared.m_weight = 1f + (float)i * 0.25f;
					itemData.m_shared.m_maxStackSize = 1;
					itemData.m_shared.m_maxQuality = 1;
					itemData.m_shared.m_equipStatusEffect = null;
					itemData.m_shared.m_setStatusEffect = null;
					itemData.m_shared.m_setName = "";
					itemData.m_shared.m_useDurability = false;
					itemData.m_shared.m_value = 0;
					itemData.m_shared.m_questItem = false;
					if ((Object)(object)icons[i] != (Object)null)
					{
						itemData.m_shared.m_icons = (Sprite[])(object)new Sprite[1] { icons[i] };
					}
					CoinPouches.Set(itemData, 0);
					Prefabs[i] = val2;
				}
				GameObject prefab = Prefabs[i];
				if (!db.m_items.Contains(prefab))
				{
					db.m_items.Add(prefab);
				}
				db.m_itemByHash[StringExtensionMethods.GetStableHashCode(((Object)prefab).name)] = prefab;
				db.m_itemByData[prefab.GetComponent<ItemDrop>().m_itemData.m_shared] = prefab;
				if (db.m_recipes.Any((Recipe r) => (Object)(object)r != (Object)null && ((Object)r).name == "Recipe_" + ((Object)prefab).name))
				{
					continue;
				}
				ItemDrop[] source = Materials[i].Select(delegate(string n)
				{
					GameObject itemPrefab = db.GetItemPrefab(n);
					return (itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>();
				}).ToArray();
				if (source.Any((ItemDrop m) => (Object)(object)m == (Object)null))
				{
					Plugin.Log.LogWarning((object)("Pouch recipe unavailable: missing materials for " + Names[i]));
					continue;
				}
				Recipe val3 = ScriptableObject.CreateInstance<Recipe>();
				((Object)val3).name = "Recipe_" + ((Object)prefab).name;
				val3.m_item = prefab.GetComponent<ItemDrop>();
				val3.m_amount = 1;
				val3.m_enabled = true;
				val3.m_minStationLevel = 1;
				val3.m_craftingStation = ((IEnumerable<Recipe>)db.m_recipes).FirstOrDefault((Func<Recipe, bool>)delegate(Recipe r)
				{
					object obj;
					if (r == null)
					{
						obj = null;
					}
					else
					{
						CraftingStation craftingStation = r.m_craftingStation;
						obj = ((craftingStation != null) ? ((Object)craftingStation).name : null);
					}
					return (string?)obj == "piece_workbench";
				})?.m_craftingStation;
				if ((Object)(object)val3.m_craftingStation == (Object)null)
				{
					Object.Destroy((Object)(object)val3);
					continue;
				}
				val3.m_resources = ((IEnumerable<ItemDrop>)source).Select((Func<ItemDrop, int, Requirement>)((ItemDrop m, int j) => new Requirement
				{
					m_resItem = m,
					m_amount = Costs[i][j],
					m_amountPerLevel = 0,
					m_recover = true
				})).ToArray();
				db.m_recipes.Add(val3);
			}
			RegisterNetwork(ZNetScene.instance);
		}

		private static void RegisterNetwork(ZNetScene scene)
		{
			if ((Object)(object)scene == (Object)null)
			{
				return;
			}
			GameObject[] prefabs = Prefabs;
			foreach (GameObject val in prefabs)
			{
				if ((Object)(object)val != (Object)null)
				{
					if (!scene.m_prefabs.Contains(val))
					{
						scene.m_prefabs.Add(val);
					}
					scene.m_namedPrefabs[StringExtensionMethods.GetStableHashCode(((Object)val).name)] = val;
				}
			}
		}

		internal static void RefreshNames()
		{
			if (language == L.Code)
			{
				return;
			}
			language = L.Code;
			for (int i = 0; i < 8; i++)
			{
				if ((Object)(object)Prefabs[i] != (Object)null)
				{
					SharedData shared = Prefabs[i].GetComponent<ItemDrop>().m_itemData.m_shared;
					shared.m_name = L.T(Names[i]);
					shared.m_description = L.T("Coin storage. Equip in the pouch slot. Coins inside add no weight.");
				}
			}
		}

		internal static bool Upgrade(Player player, ItemData item)
		{
			int tier = CoinPouches.Tier(item) + 1;
			if (tier < 1 || tier >= 8 || CoinPouches.Equipped(player) != item || !CoinPouches.Read(item, out var _))
			{
				return false;
			}
			Recipe val = ((IEnumerable<Recipe>)ObjectDB.instance.m_recipes).FirstOrDefault((Func<Recipe, bool>)((Recipe r) => (Object)(object)r != (Object)null && ((Object)r).name == "Recipe_GaS_CoinPouch_" + (tier + 1)));
			if ((Object)(object)val == (Object)null || !player.HaveRequirements(val, false, 1, 1))
			{
				return false;
			}
			player.ConsumeResources(val.m_resources, 1, -1, 1);
			item.m_dropPrefab = Prefabs[tier];
			item.m_shared = val.m_item.m_itemData.m_shared;
			CoinPouchEpicLoot.Invalidate(player);
			((Humanoid)player).GetInventory().Changed(false, false);
			return true;
		}
	}
	internal static class CoinPouchUi
	{
		[HarmonyPatch(typeof(InventoryGui), "UpdateContainer")]
		private static class KeepLocalCurrencyCursor
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				List<CodeInstruction> list = instructions.ToList();
				MethodInfo methodInfo = AccessTools.Method(typeof(InventoryGui), "SetupDragItem", (Type[])null, (Type[])null);
				MethodInfo operand = AccessTools.Method(typeof(CoinPouchUi), "ContainerCleanup", (Type[])null, (Type[])null);
				int num = 0;
				foreach (CodeInstruction item in list)
				{
					if (CodeInstructionExtensions.Calls(item, methodInfo))
					{
						item.opcode = OpCodes.Call;
						item.operand = operand;
						num++;
					}
				}
				if (num != 1)
				{
					throw new InvalidOperationException("Unexpected InventoryGui.UpdateContainer cursor cleanup; expected one SetupDragItem call, found " + num);
				}
				return list;
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "OnDropOutside")]
		private static class Outside
		{
			private static bool Prefix(InventoryGui __instance)
			{
				if (!IsPreview(__instance.m_dragInventory))
				{
					return true;
				}
				DropOutside(__instance);
				return false;
			}
		}

		[HarmonyPatch(typeof(InventoryGrid), "DropItem")]
		private static class GuardVirtualTransfer
		{
			private static bool Prefix(Inventory fromInventory, ref bool __result)
			{
				if (!IsPreview(fromInventory))
				{
					return true;
				}
				__result = false;
				return false;
			}
		}

		[HarmonyPatch(typeof(Humanoid), "DropItem")]
		private static class GuardVirtualDrop
		{
			private static bool Prefix(Inventory inventory, ref bool __result)
			{
				if (!IsPreview(inventory) || dropping)
				{
					return true;
				}
				__result = false;
				return false;
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "OnRightClickItem")]
		private static class RightClick
		{
			private static bool Prefix(InventoryGui __instance, InventoryGrid grid, ItemData item)
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || __instance.m_dragItem != null || item == null || CoinPouches.Equipped(localPlayer) != item || grid.GetInventory() != ((Humanoid)localPlayer).GetInventory())
				{
					return true;
				}
				if (ZInput.GetKey((KeyCode)306, true) || ZInput.GetKey((KeyCode)305, true))
				{
					((Character)localPlayer).Message((MessageType)2, L.T(CoinPouchPrefabs.Upgrade(localPlayer, item) ? "Pouch upgraded. Coins preserved." : "Missing materials or workbench requirements."), 0, (Sprite)null, false);
				}
				else
				{
					((Humanoid)localPlayer).RemoveEquipAction(item);
					((Humanoid)localPlayer).UnequipItem(item, false);
					PlayerSlots.Normalize(localPlayer);
					((Humanoid)localPlayer).GetInventory().Changed(false, false);
				}
				return false;
			}
		}

		internal static bool Withdrawn;

		private static ItemData sourcePouch;

		private static Inventory preview;

		private static bool dropping;

		private static bool Ready(Player player, int count, out int balance)
		{
			balance = 0;
			if ((Object)(object)player != (Object)null && !((Character)player).IsDead() && !player.m_isLoading && !((Character)player).IsTeleporting() && sourcePouch != null && CoinPouches.Equipped(player) == sourcePouch && CoinPouches.Read(sourcePouch, out balance) && count > 0 && count <= balance)
			{
				return count <= 10000;
			}
			return false;
		}

		private static bool IsPreview(Inventory inv)
		{
			if (preview != null)
			{
				return inv == preview;
			}
			return false;
		}

		private static void Clear(InventoryGui gui)
		{
			gui.SetupDragItem((ItemData)null, (Inventory)null, 1);
		}

		internal static void Tick()
		{
			InventoryGui instance = InventoryGui.instance;
			if (!InventoryGui.IsVisible() || !((Object)(object)Player.m_localPlayer != (Object)null) || ((Character)Player.m_localPlayer).IsDead())
			{
				Withdrawn = false;
				if ((Object)(object)instance != (Object)null && IsPreview(instance.m_dragInventory))
				{
					Clear(instance);
				}
				sourcePouch = null;
				preview = null;
			}
		}

		internal static bool Select(InventoryGui gui, InventoryGrid grid, ItemData item, Vector2i pos, Modifier mod)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || localPlayer.m_isLoading || ((Character)localPlayer).IsTeleporting())
			{
				return false;
			}
			if (IsPreview(gui.m_dragInventory))
			{
				if (grid.GetInventory() == ((Humanoid)localPlayer).GetInventory() && PlayerSlots.SlotAt(PlayerSlots.Track(localPlayer), pos.x, pos.y) == 13)
				{
					Clear(gui);
					return true;
				}
				if (!Ready(localPlayer, gui.m_dragAmount, out var _))
				{
					Clear(gui);
					return true;
				}
				Place(gui, localPlayer, grid.GetInventory(), pos);
				return true;
			}
			if (grid.GetInventory() != ((Humanoid)localPlayer).GetInventory() || PlayerSlots.SlotAt(PlayerSlots.Track(localPlayer), pos.x, pos.y) != 13)
			{
				return false;
			}
			if (gui.m_dragItem != null)
			{
				if (CoinPouches.IsPouch(gui.m_dragItem) && gui.m_dragInventory == ((Humanoid)localPlayer).GetInventory())
				{
					if (((Humanoid)localPlayer).EquipItem(gui.m_dragItem, false))
					{
						Clear(gui);
					}
				}
				else if (CoinPouches.OrdinaryCoin(gui.m_dragItem))
				{
					Inventory dragInventory = gui.m_dragInventory;
					ItemData val = CoinPouches.Equipped(localPlayer);
					if ((dragInventory != ((Humanoid)localPlayer).GetInventory() && (!((Object)(object)gui.m_currentContainer != (Object)null) || dragInventory != gui.m_currentContainer.GetInventory())) || val == null || !dragInventory.ContainsItem(gui.m_dragItem) || val.m_worldLevel != gui.m_dragItem.m_worldLevel || !CoinPouches.Read(val, out var balance2))
					{
						return true;
					}
					int num = CoinPouchState.Deposit(balance2, CoinPouches.Capacity(val), Math.Max(0, Math.Min(gui.m_dragAmount, gui.m_dragItem.m_stack)));
					if (num > 0)
					{
						ItemData dragItem = gui.m_dragItem;
						dragItem.m_stack -= num;
						if (dragItem.m_stack == 0)
						{
							dragInventory.m_inventory.Remove(dragItem);
						}
						CoinPouches.Set(val, balance2 + num);
						Clear(gui);
						dragInventory.Changed(false, false);
						if (dragInventory != ((Humanoid)localPlayer).GetInventory())
						{
							((Humanoid)localPlayer).GetInventory().Changed(false, false);
						}
					}
				}
				return true;
			}
			if (!CoinPouches.IsPouch(item) || CoinPouches.Equipped(localPlayer) != item)
			{
				return true;
			}
			StartDrag(gui, localPlayer, item, mod);
			return true;
		}

		private static void StartDrag(InventoryGui gui, Player player, ItemData pouch, Modifier mod)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Invalid comparison between Unknown and I4
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Invalid comparison between Unknown and I4
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Invalid comparison between Unknown and I4
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Invalid comparison between Unknown and I4
			//IL_0102: 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)
			if (!CoinPouches.Read(pouch, out var balance) || balance <= 0)
			{
				return;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins");
			ItemData val = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone();
			val.m_dropPrefab = itemPrefab;
			val.m_stack = Math.Min(balance, 10000);
			val.m_worldLevel = pouch.m_worldLevel;
			sourcePouch = pouch;
			preview = new Inventory("GaS.CoinPouchCursor", (Sprite)null, 1, 1);
			preview.m_inventory.Add(val);
			if ((int)mod == 1 && val.m_stack > 1)
			{
				gui.ShowSplitDialog(val, preview);
				return;
			}
			gui.SetupDragItem(val, preview, val.m_stack);
			if ((int)mod == 3 || ((int)mod == 2 && (Object)(object)gui.m_currentContainer == (Object)null))
			{
				DropOutside(gui);
			}
			else
			{
				if ((int)mod != 2)
				{
					return;
				}
				Inventory inventory = gui.m_currentContainer.GetInventory();
				for (int i = 0; i < inventory.GetHeight(); i++)
				{
					if (gui.m_dragItem == null)
					{
						break;
					}
					for (int j = 0; j < inventory.GetWidth(); j++)
					{
						if (gui.m_dragItem == null)
						{
							break;
						}
						ItemData itemAt = inventory.GetItemAt(j, i);
						if (itemAt != null && CoinPouches.OrdinaryCoin(itemAt))
						{
							Place(gui, player, inventory, new Vector2i(j, i));
						}
					}
				}
				for (int k = 0; k < inventory.GetHeight(); k++)
				{
					if (gui.m_dragItem == null)
					{
						break;
					}
					for (int l = 0; l < inventory.GetWidth(); l++)
					{
						if (gui.m_dragItem == null)
						{
							break;
						}
						if (inventory.GetItemAt(l, k) == null)
						{
							Place(gui, player, inventory, new Vector2i(l, k));
						}
					}
				}
			}
		}

		private static void Place(InventoryGui gui, Player player, Inventory target, Vector2i pos)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			ItemData itemAt = target.GetItemAt(pos.x, pos.y);
			int num = gui.m_dragAmount;
			if (itemAt != null)
			{
				num = Math.Min(num, Math.Max(0, 10000 - itemAt.m_stack));
			}
			if (Transfer(player, target, pos, num))
			{
				int num2 = gui.m_dragAmount - num;
				if (num2 == 0)
				{
					Clear(gui);
					return;
				}
				gui.m_dragItem.m_stack = num2;
				gui.SetupDragItem(gui.m_dragItem, preview, num2);
			}
		}

		private static bool Transfer(Player player, Inventory target, Vector2i pos, int count)
		{
			//IL_001d: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			if (!Ready(player, count, out var balance))
			{
				return false;
			}
			if (target == ((Humanoid)player).GetInventory())
			{
				return CoinPouches.Withdraw(player, sourcePouch, count, pos);
			}
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance.m_currentContainer == (Object)null || target != instance.m_currentContainer.GetInventory() || pos.x < 0 || pos.x >= target.GetWidth() || pos.y < 0 || pos.y >= target.GetHeight())
			{
				return false;
			}
			ItemData itemAt = target.GetItemAt(pos.x, pos.y);
			if (itemAt != null && (!CoinPouches.OrdinaryCoin(itemAt) || itemAt.m_worldLevel != sourcePouch.m_worldLevel || itemAt.m_stack + count > 10000))
			{
				return false;
			}
			ItemData val = preview.m_inventory[0].Clone();
			val.m_stack = count;
			val.m_gridPos = pos;
			if (itemAt != null)
			{
				itemAt.m_stack += count;
			}
			else
			{
				target.m_inventory.Add(val);
			}
			CoinPouches.Set(sourcePouch, balance - count);
			Withdrawn = true;
			target.Changed(false, false);
			((Humanoid)player).GetInventory().Changed(false, false);
			return true;
		}

		private static void DropOutside(InventoryGui gui)
		{
			Player localPlayer = Player.m_localPlayer;
			int dragAmount = gui.m_dragAmount;
			if (!Ready(localPlayer, dragAmount, out var balance))
			{
				Clear(gui);
				return;
			}
			CoinPouches.Set(sourcePouch, balance - dragAmount);
			dropping = true;
			try
			{
				if (!((Humanoid)localPlayer).DropItem(preview, gui.m_dragItem, dragAmount))
				{
					CoinPouches.Set(sourcePouch, balance);
					return;
				}
				Withdrawn = true;
				((Humanoid)localPlayer).GetInventory().Changed(false, false);
				Clear(gui);
			}
			finally
			{
				dropping = false;
			}
		}

		private static void ContainerCleanup(InventoryGui gui, ItemData item, Inventory inventory, int amount)
		{
			if (item != null || !IsPreview(gui.m_dragInventory) || gui.m_dragItem == null || !preview.ContainsItem(gui.m_dragItem) || !InventoryGui.IsVisible() || !Ready(Player.m_localPlayer, gui.m_dragAmount, out var _))
			{
				gui.SetupDragItem(item, inventory, amount);
			}
		}
	}
	internal static class DualUtility
	{
		private sealed class State
		{
			internal ItemData Extra;

			internal bool Swapping;
		}

		private sealed class Swap
		{
			internal Player Player;

			internal State State;

			internal ItemData Primary;
		}

		[HarmonyPatch(typeof(Humanoid), "EquipItem")]
		private static class Equip
		{
			private static bool Prefix(Humanoid __instance, ItemData item, ref bool __result, out Swap __state)
			{
				__state = null;
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val == null || item == null || PlayerSlots.EquipmentIndex(item) != 4 || Get(val).Swapping)
				{
					return true;
				}
				State state = Get(val);
				if (((Humanoid)val).IsItemEquiped(item))
				{
					return true;
				}
				int num = PlayerSlots.Tag(item);
				bool flag = num == 11 || (num != 4 && ((Humanoid)val).m_utilityItem != null && state.Extra == null);
				ItemData val2 = (flag ? ((Humanoid)val).m_utilityItem : state.Extra);
				if (val2 != null && val2.m_shared.m_name == item.m_shared.m_name)
				{
					__result = false;
					return false;
				}
				if (flag)
				{
					__state = Begin(val);
				}
				return true;
			}

			private static void Postfix(Humanoid __instance, ItemData item, bool __result, Swap __state)
			{
				if (__result && __instance is Player && PlayerSlots.EquipmentIndex(item) == 4)
				{
					PlayerSlots.SetTag(item, (__state == null) ? 4 : 11);
				}
			}

			private static void Finalizer(Swap __state)
			{
				End(__state);
			}
		}

		[HarmonyPatch(typeof(Humanoid), "UnequipItem")]
		private static class Unequip
		{
			private static void Prefix(Humanoid __instance, ItemData item, out Swap __state)
			{
				__state = null;
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val != null && !Get(val).Swapping && item != null && Extra((Humanoid)(object)val) == item)
				{
					__state = Begin(val);
				}
			}

			private static void Finalizer(Swap __state)
			{
				End(__state);
			}
		}

		[HarmonyPatch(typeof(Humanoid), "IsItemEquiped")]
		private static class IsEquipped
		{
			private static void Postfix(Humanoid __instance, ItemData item, ref bool __result)
			{
				if (item != null && Extra(__instance) == item)
				{
					__result = true;
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "UnequipAllItems")]
		private static class UnequipAll
		{
			private static void Prefix(Humanoid __instance)
			{
				ItemData val = Extra(__instance);
				if (val != null)
				{
					__instance.UnequipItem(val, false);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "UnequipDeathDropItems")]
		private static class Death
		{
			private static void Prefix(Player __instance)
			{
				ItemData val = Extra((Humanoid)(object)__instance);
				if (val != null)
				{
					((Humanoid)__instance).UnequipItem(val, false);
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "UpdateEquipmentStatusEffects")]
		private static class Effects
		{
			private static bool Prefix(Humanoid __instance)
			{
				if (!(__instance is Player))
				{
					return true;
				}
				ItemData[] obj = new ItemData[9]
				{
					__instance.m_leftItem,
					__instance.m_rightItem,
					__instance.m_chestItem,
					__instance.m_legItem,
					__instance.m_helmetItem,
					__instance.m_shoulderItem,
					__instance.m_utilityItem,
					__instance.m_trinketItem,
					Extra(__instance)
				};
				HashSet<StatusEffect> hashSet = new HashSet<StatusEffect>();
				ItemData[] array = (ItemData[])(object)obj;
				foreach (ItemData val in array)
				{
					if (val != null)
					{
						if ((Object)(object)val.m_shared.m_equipStatusEffect != (Object)null)
						{
							hashSet.Add(val.m_shared.m_equipStatusEffect);
						}
						if (__instance.HaveSetEffect(val))
						{
							hashSet.Add(val.m_shared.m_setStatusEffect);
						}
					}
				}
				foreach (StatusEffect equipmentStatusEffect in __instance.m_equipmentStatusEffects)
				{
					if (!hashSet.Contains(equipmentStatusEffect))
					{
						((Character)__instance).m_seman.RemoveStatusEffect(equipmentStatusEffect.NameHash(), false);
					}
				}
				foreach (StatusEffect item in hashSet)
				{
					if (!__instance.m_equipmentStatusEffects.Contains(item))
					{
						((Character)__instance).m_seman.AddStatusEffect(item, false, 0, 0f, (short)(-1));
					}
				}
				__instance.m_equipmentStatusEffects.Clear();
				__instance.m_equipmentStatusEffects.UnionWith(hashSet);
				return false;
			}
		}

		[HarmonyPatch(typeof(Humanoid), "GetSetCount")]
		private static class SetCount
		{
			private static void Postfix(Humanoid __instance, string setName, ref int __result)
			{
				if (Extra(__instance)?.m_shared.m_setName == setName)
				{
					__result++;
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "UpdateEquipment")]
		private static class Durability
		{
			private static void Postfix(Humanoid __instance, float dt)
			{
				ItemData val = Extra(__instance);
				if (val != null && val.m_shared.m_useDurability)
				{
					__instance.DrainEquipedItemDurability(val, dt);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "UpdateModifiers")]
		private static class Modifiers
		{
			private static void Postfix(Player __instance)
			{
				ItemData val = Extra((Humanoid)(object)__instance);
				if (val != null && Player.s_equipmentModifierSourceFields != null)
				{
					for (int i = 0; i < __instance.m_equipmentModifierValues.Length; i++)
					{
						__instance.m_equipmentModifierValues[i] += (float)Player.s_equipmentModifierSourceFields[i].GetValue(val.m_shared);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "GetEquipmentWeight")]
		private static class EquipmentWeight
		{
			private static void Postfix(Humanoid __instance, ref float __result)
			{
				__result += Extra(__instance)?.m_shared.m_weight ?? 0f;
			}
		}

		[HarmonyPatch(typeof(Player), "GetEquipmentEitrRegenModifier")]
		private static class Eitr
		{
			private static void Postfix(Player __instance, ref float __result)
			{
				__result += Extra((Humanoid)(object)__instance)?.m_shared.m_eitrRegenModifier ?? 0f;
			}
		}

		private static readonly ConditionalWeakTable<Player, State> States = new ConditionalWeakTable<Player, State>();

		private static State Get(Player player)
		{
			return States.GetValue(player, (Player _) => new State());
		}

		internal static ItemData Extra(Humanoid player)
		{
			Player val = (Player)(object)((player is Player) ? player : null);
			if (val == null)
			{
				return null;
			}
			return Get(val).Extra;
		}

		internal static void Validate(Player player)
		{
			ItemData val = Extra((Humanoid)(object)player);
			if (val != null && !((Humanoid)player).GetInventory().ContainsItem(val))
			{
				((Humanoid)player).UnequipItem(val, false);
			}
		}

		private static Swap Begin(Player player)
		{
			State state = Get(player);
			Swap swap = new Swap
			{
				Player = player,
				State = state,
				Primary = ((Humanoid)player).m_utilityItem
			};
			((Humanoid)player).m_utilityItem = state.Extra;
			state.Extra = swap.Primary;
			state.Swapping = true;
			return swap;
		}

		private static void End(Swap swap)
		{
			if (swap != null)
			{
				swap.State.Extra = ((Humanoid)swap.Player).m_utilityItem;
				((Humanoid)swap.Player).m_utilityItem = swap.Primary;
				swap.State.Swapping = false;
				((Humanoid)swap.Player).SetupEquipment();
			}
		}
	}
	internal static class EpicLootCrafting
	{
		private static MethodInfo unregister;

		internal static void Initialize()
		{
			try
			{
				Type type = (from a in AppDomain.CurrentDomain.GetAssemblies()
					select a.GetType("EpicLoot.API", throwOnError: false)).FirstOrDefault((Type t) => t != null);
				if (!(type == null))
				{
					Type[] types = new Type[5]
					{
						typeof(string),
						typeof(Func<List<ItemData>>),
						typeof(Func<string, int>),
						typeof(Func<string, int, int>),
						typeof(Func<ItemData, int, int>)
					};
					MethodInfo method = type.GetMethod("RegisterInventoryProvider", types);
					if (method == null || !method.IsStatic || method.ReturnType != typeof(bool))
					{
						Plugin.Log.LogWarning((object)"Epic Loot inventory API unavailable; chest integration skipped.");
					}
					else if ((bool)method.Invoke(null, new object[5]
					{
						"mkova.GearAndStorage",
						new Func<List<ItemData>>(Items),
						new Func<string, int>(Count),
						new Func<string, int, int>(Remove),
						new Func<ItemData, int, int>(RemoveExact)
					}))
					{
						unregister = type.GetMethod("UnregisterInventoryProvider", new Type[1] { typeof(string) });
						Plugin.Log.LogInfo((object)"Epic Loot optional crafting-material provider registered.");
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Epic Loot integration skipped: " + ex.GetBaseException().Message));
			}
		}

		internal static void Shutdown()
		{
			try
			{
				unregister?.Invoke(null, new object[1] { "mkova.GearAndStorage" });
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)ex.GetBaseException().Message);
			}
			unregister = null;
		}

		private static bool Material(ItemData item)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Invalid comparison between Unknown and I4
			if (item?.m_shared != null)
			{
				return (int)item.m_shared.m_itemType == 1;
			}
			return false;
		}

		internal static List<ItemData> Items()
		{
			return ChestCrafting.ExternalSources().SelectMany((Container c) => c.m_inventory.GetAllItems()).Where(Material)
				.ToList();
		}

		internal static int Count(string name)
		{
			return (from i in Items()
				where i.m_shared.m_name == name
				select i).Sum((ItemData i) => i.m_stack);
		}

		internal static int Remove(string name, int amount)
		{
			return Spend(name, null, amount);
		}

		internal static int RemoveExact(ItemData item, int amount)
		{
			if (!Material(item))
			{
				return 0;
			}
			return Spend(null, item, amount);
		}

		private static int Spend(string name, ItemData exact, int amount)
		{
			if (amount <= 0)
			{
				return 0;
			}
			int num = amount;
			foreach (Container item in ChestCrafting.ExternalSources())
			{
				bool flag = false;
				ItemData[] array = (from i in item.m_inventory.GetAllItems()
					where Material(i) && ((exact == null) ? (i.m_shared.m_name == name) : (i == exact))
					select i).ToArray();
				foreach (ItemData val in array)
				{
					int num3 = Math.Min(num, val.m_stack);
					val.m_stack -= num3;
					num -= num3;
					flag = flag || num3 > 0;
					if (val.m_stack == 0)
					{
						item.m_inventory.m_inventory.Remove(val);
					}
					if (num == 0)
					{
						break;
					}
				}
				if (flag)
				{
					ChestCrafting.SaveChest(item);
				}
				if (num == 0)
				{
					break;
				}
			}
			return amount - num;
		}
	}
	internal static class EquipmentRecovery
	{
		private static readonly ConditionalWeakTable<Player, HashSet<ItemData>> Pending = new ConditionalWeakTable<Player, HashSet<ItemData>>();

		internal static void Clear(Player player)
		{
			Pending.Remove(player);
		}

		internal static void Queue(Inventory inventory, ItemData item)
		{
			if (item == null || !PlayerSlots.TryState(inventory, out var state) || state.Loading || state.Player.m_isLoading || ((Character)state.Player).IsDead())
			{
				return;
			}
			int slot = PlayerSlots.Tag(item);
			if (LayoutPlanner.IsEquipment(slot) && PlayerSlots.ValidSlot(item, slot) && inventory.ContainsItem(item) && !((Humanoid)state.Player).IsItemEquiped(item))
			{
				Pending.GetValue(state.Player, (Player _) => new HashSet<ItemData>()).Add(item);
			}
		}

		internal static void Tick(Player player)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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)
			if (!Pending.TryGetValue(player, out var value))
			{
				return;
			}
			if (((Character)player).IsDead() || player.m_isLoading || PlayerSlots.Track(player).Loading)
			{
				Clear(player);
			}
			else
			{
				if (((Character)player).InAttack() || ((Character)player).InDodge() || (((Character)player).IsSwimming() && !((Character)player).IsOnGround()))
				{
					return;
				}
				Inventory inventory = ((Humanoid)player).GetInventory();
				foreach (ItemData item in new List<ItemData>(value))
				{
					value.Remove(item);
					int slot = PlayerSlots.Tag(item);
					if (inventory.ContainsItem(item) && LayoutPlanner.IsEquipment(slot) && PlayerSlots.ValidSlot(item, slot) && !((Humanoid)player).IsItemEquiped(item))
					{
						Vector2i val = PlayerSlots.Position(PlayerSlots.Track(player), slot);
						if (item.m_gridPos.x == val.x && item.m_gridPos.y == val.y && Worn(player, slot) == null)
						{
							((Humanoid)player).EquipItem(item, false);
						}
					}
				}
				if (value.Count == 0)
				{
					Clear(player);
				}
			}
		}

		private static ItemData Worn(Player player, int slot)
		{
			return (ItemData)(slot switch
			{
				0 => ((Humanoid)player).m_helmetItem, 
				1 => ((Humanoid)player).m_chestItem, 
				2 => ((Humanoid)player).m_legItem, 
				3 => ((Humanoid)player).m_shoulderItem, 
				4 => ((Humanoid)player).m_utilityItem, 
				11 => DualUtility.Extra((Humanoid)(object)player), 
				13 => CoinPouches.Equipped(player), 
				12 => ((Humanoid)player).m_trinketItem, 
				_ => null, 
			});
		}
	}
	internal static class FurnaceFeeding
	{
		[HarmonyPatch(typeof(Smelter), "UpdateSmelter")]
		private static class Feed
		{
			private static void Prefix(Smelter __instance)
			{
				//IL_008d: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
				if (!Enabled.Value || (Object)(object)__instance.m_nview == (Object)null || !__instance.m_nview.IsValid() || !__instance.m_nview.IsOwner() || (Object)(object)__instance.m_fuelItem == (Object)null || ((Object)((Component)__instance.m_fuelItem).gameObject).name != "Coal")
				{
					return;
				}
				Piece val = ((Component)__instance).GetComponent<Piece>() ?? ((Component)__instance).GetComponentInParent<Piece>();
				long num = (((Object)(object)val != (Object)null) ? val.GetCreator() : 0);
				if (num == 0L || !ChestCrafting.MachineAccess(((Component)__instance).transform.position, num))
				{
					return;
				}
				int num2 = FurnacePolicy.FuelSpace(__instance.GetFuel(), __instance.m_maxFuel);
				int num3 = Math.Max(0, __instance.m_maxOre - __instance.GetQueueSize());
				if (num2 == 0 && num3 == 0)
				{
					return;
				}
				foreach (Container item in ChestCrafting.ForMachine(((Component)__instance).transform.position, num))
				{
					Inventory inventory = item.GetInventory();
					bool flag = false;
					ItemData[] array = inventory.GetAllItems().ToArray();
					foreach (ItemData val2 in array)
					{
						string text = (((Object)(object)val2.m_dropPrefab != (Object)null) ? ((Object)val2.m_dropPrefab).name : "");
						int num4 = 0;
						if (text == "Coal" && num2 > 0)
						{
							num4 = Math.Min(val2.m_stack, num2);
							__instance.SetFuel(__instance.GetFuel() + (float)num4);
							num2 -= num4;
						}
						else if (num3 > 0 && FurnacePolicy.IsRawOre(text) && __instance.IsItemAllowed(text))
						{
							num4 = Math.Min(val2.m_stack, num3);
							for (int j = 0; j < num4; j++)
							{
								__instance.QueueOre(text, val2.m_cheated);
							}
							num3 -= num4;
						}
						if (num4 != 0)
						{
							val2.m_stack -= num4;
							flag = true;
						}
					}
					if (flag)
					{
						inventory.m_inventory.RemoveAll((ItemData item) => item.m_stack <= 0);
						inventory.Changed(false, false);
					}
					if (num2 == 0 && num3 == 0)
					{
						break;
					}
				}
			}
		}

		private static ConfigEntry<bool> Enabled;

		internal static void Initialize(ConfigFile config)
		{
			Enabled = config.Bind<bool>("Furnaces", "AutoFeedCoalAndOre", false, "Automatically supply coal and raw ore from accessible closed chests within a fixed 10 metres of the furnace. Only coal-fuelled furnaces and accepted raw ores; no wood, food or refined bars. Output collection has its own Production setting.");
		}
	}
	internal static class MachineChestAccess
	{
		internal static bool Allowed(Container chest, Vector3 center, long creator, float range)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)chest != (Object)null && chest.m_inventory != null && (Object)(object)((Component)chest).GetComponentInParent<TombStone>() == (Object)null && (Object)(object)chest.m_nview != (Object)null && chest.m_nview.IsValid() && chest.m_nview.IsOwner() && !chest.IsInUse() && chest.m_nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 0 && ((Object)(object)chest.m_wagon == (Object)null || !chest.m_wagon.InUse()) && Vector3.Distance(center, ((Component)chest).transform.position) <= range && chest.CheckAccess(creator))
			{
				if (chest.m_checkGuardStone)
				{
					return ChestCrafting.MachineAccess(((Component)chest).transform.position, creator);
				}
				return true;
			}
			return false;
		}
	}
	internal static class PlayerSlots
	{
		internal sealed class State
		{
			internal Player Player;

			internal int NormalRows = 6;

			internal bool Loading;
		}

		[HarmonyPatch(typeof(Player), "Awake")]
		private static class AwakePatch
		{
			private static void Postfix(Player __instance)
			{
				Track(__instance);
			}
		}

		[HarmonyPatch(typeof(Player), "Load")]
		private static class LoadPatch
		{
			private static void Prefix(Player __instance)
			{
				State state = Track(__instance);
				state.Loading = true;
				EquipmentRecovery.Clear(__instance);
				((Humanoid)__instance).GetInventory().m_height = Math.Max(((Humanoid)__instance).GetInventory().GetHeight(), state.NormalRows + 2);
			}

			private static void Postfix(Player __instance)
			{
				Track(__instance).Loading = false;
				Normalize(__instance);
			}

			private static void Finalizer(Player __instance)
			{
				Track(__instance).Loading = false;
			}
		}

		[HarmonyPatch(typeof(Player), "Save")]
		private static class SavePatch
		{
			private static void Prefix(Player __instance)
			{
				Normalize(__instance);
			}
		}

		[HarmonyPatch(typeof(Humanoid), "DropInvalidItems")]
		private static class InvalidItemsPatch
		{
			private static bool Prefix(Humanoid __instance)
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val == null)
				{
					return true;
				}
				if (Track(val).Loading || val.m_isLoading)
				{
					return false;
				}
				Normalize(val);
				return true;
			}
		}

		[HarmonyPatch(typeof(Player), "SetInventorySize")]
		private static class ResizePatch
		{
			private static bool Prefix(Player __instance, int rows)
			{
				__instance.AddUniqueKeyValue("invrows", Math.Max(0, Math.Min(9, rows)).ToString());
				Normalize(__instance);
				if ((Object)(object)InventoryGui.instance != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					InventoryGui.instance.SetInventorySize(Track(__instance).NormalRows);
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(Inventory), "FindEmptySlot")]
		private static class EmptyCellPatch
		{
			private static bool Prefix(Inventory __instance, bool topFirst, ref Vector2i __result)
			{
				//IL_0063: Unknown result type (might be due to invalid IL or missing references)
				//IL_0068: Unknown result type (might be due to invalid IL or missing references)
				//IL_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)
				if (!TryState(__instance, out var state) || state.Loading)
				{
					return true;
				}
				for (int i = 0; i < state.NormalRows; i++)
				{
					int num = (topFirst ? i : (state.NormalRows - i - 1));
					for (int j = 0; j < __instance.GetWidth(); j++)
					{
						if (__instance.GetItemAt(j, num) == null)
						{
							__result = new Vector2i(j, num);
							return false;
						}
					}
				}
				__result = new Vector2i(-1, -1);
				return false;
			}
		}

		[HarmonyPatch(typeof(Inventory), "GetEmptySlots")]
		private static class EmptyCountPatch
		{
			private static bool Prefix(Inventory __instance, ref int __result)
			{
				if (!TryState(__instance, out var state) || state.Loading)
				{
					return true;
				}
				__result = 0;
				for (int i = 0; i < state.NormalRows; i++)
				{
					for (int j = 0; j < __instance.GetWidth(); j++)
					{
						if (__instance.GetItemAt(j, i) == null)
						{
							__result++;
						}
					}
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(Inventory), "HaveEmptySlot")]
		private static class HasSpacePatch
		{
			private static bool Prefix(Inventory __instance, ref bool __result)
			{
				if (!TryState(__instance, out var state) || state.Loading)
				{
					return true;
				}
				__result = __instance.GetEmptySlots() > 0;
				return false;
			}
		}

		[HarmonyPatch(typeof(Inventory), "CanAddItem", new Type[]
		{
			typeof(ItemData),
			typeof(int)
		})]
		private static class CanAddPatch
		{
			private static bool Prefix(Inventory __instance, ItemData item, int stack, ref bool __result)
			{
				//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_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				if (!TryState(__instance, out var state) || state.Loading)
				{
					return true;
				}
				int slot = Tag(item);
				Vector2i val = Position(state, slot);
				__result = (ValidSlot(item, slot) && __instance.GetItemAt(val.x, val.y) == null) || __instance.FindFreeStackSpace(item.m_shared.m_name, (float)item.m_worldLevel) + __instance.GetEmptySlots() * item.m_shared.m_maxStackSize >= ((stack <= 0) ? item.m_stack : stack);
				return false;
			}
		}

		[HarmonyPatch(typeof(Inventory), "StackAll")]
		private static class StackPatch
		{
			private static void Prefix(Inventory fromInventory, out bool __state)
			{
				__state = Plugin.ProtectSlots.Value && TryState(fromInventory, out var _);
				if (__state)
				{
					stackDepth++;
				}
			}

			private static void Finalizer(bool __state)
			{
				if (__state)
				{
					stackDepth--;
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })]
		private static class AddPatch
		{
			private static bool Prefix(Inventory __instance, ItemData item, ref bool __result, out bool __state)
			{
				//IL_0067: 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_006e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0074: Unknown result type (might be due to invalid IL or missing references)
				//IL_0084: 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)
				__state = !__instance.ContainsItem(item);
				if (stackDepth > 0 && Tag(item) >= 0)
				{
					__result = false;
					return false;
				}
				if (QuickStack.Transferring && QuickStack.Protected(item))
				{
					__result = false;
					return false;
				}
				if (!TryState(__instance, out var state) || state.Loading || __instance.ContainsItem(item))
				{
					return true;
				}
				int slot = Tag(item);
				if (!ValidSlot(item, slot))
				{
					return true;
				}
				Vector2i val = Position(state, slot);
				if (__instance.GetItemAt(val.x, val.y) != null)
				{
					return true;
				}
				item.m_gridPos = val;
				__instance.m_inventory.Add(item);
				__instance.Changed(true, item.m_cheated && !Achievements.IsCheatedAtAll());
				__result = true;
				return false;
			}

			private static void Postfix(Inventory __instance, ItemData item, bool __result, bool __state)
			{
				if (__result && __state)
				{
					EquipmentRecovery.Queue(__instance, item);
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
		{
			typeof(ItemData),
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(bool)
		})]
		private static class CoordinatePatch
		{
			private static bool Prefix(Inventory __instance, ItemData item, int x, int y, ref bool __result)
			{
				if (!TryState(__instance, out var state) || state.Loading || y < state.NormalRows)
				{
					return true;
				}
				int slot = SlotAt(state, x, y);
				if (ValidSlot(item, slot))
				{
					return true;
				}
				__result = false;
				return false;
			}

			private static void Postfix(Inventory __instance, int x, int y, bool __result)
			{
				if (__result)
				{
					EquipmentRecovery.Queue(__instance, __instance.GetItemAt(x, y));
				}
			}
		}

		internal const string SlotKey = "GearAndStorage.slot";

		private static readonly ConditionalWeakTable<Inventory, State> States = new ConditionalWeakTable<Inventory, State>();

		private static int stackDepth;

		internal static bool TryState(Inventory inventory, out State state)
		{
			return States.TryGetValue(inventory, out state);
		}

		internal static State Track(Player player)
		{
			return States.GetValue(((Humanoid)player).GetInventory(), (Inventory _) => new State
			{
				Player = player,
				NormalRows = Plugin.Rows.Value
			});
		}

		internal static int EquipmentIndex(ItemData item)
		{
			//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)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Invalid comparison between Unknown and I4
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Invalid comparison between Unknown and I4
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Invalid comparison between Unknown and I4
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Invalid comparison between Unknown and I4
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Invalid comparison between Unknown and I4
			if (CoinPouches.IsPouch(item))
			{
				return 13;
			}
			ItemType itemType = item.m_shared.m_itemType;
			if ((int)itemType <= 11)
			{
				if ((int)itemType == 6)
				{
					return 0;
				}
				if ((int)itemType == 7)
				{
					return 1;
				}
				if ((int)itemType == 11)
				{
					return 2;
				}
			}
			else
			{
				if ((int)itemType == 17)
				{
					return 3;
				}
				if ((int)itemType == 18)
				{
					return 4;
				}
				if ((int)itemType == 24)
				{
					return 12;
				}
			}
			return -1;
		}

		internal static int Tag(ItemData item)
		{
			if (item != null && item.m_customData.TryGetValue("GearAndStorage.slot", out var value) && int.TryParse(value, out var result))
			{
				return result;
			}
			return -1;
		}

		internal static void SetTag(ItemData item, int slot)
		{
			if (slot < 0)
			{
				item.m_customData.Remove("GearAndStorage.slot");
			}
			else
			{
				item.m_customData["GearAndStorage.slot"] = slot.ToString();
			}
		}

		internal static bool ValidSlot(ItemData item, int slot)
		{
			if (slot != 13)
			{
				if (slot != 12)
				{
					if (slot != 11)
					{
						if (slot >= 0 && slot < 5 + Plugin.QuickCount.Value)
						{
							if (slot < 5)
							{
								return EquipmentIndex(item) == slot;
							}
							return true;
						}
						return false;
					}
					return EquipmentIndex(item) == 4;
				}
				return EquipmentIndex(item) == 12;
			}
			return CoinPouches.IsPouch(item);
		}

		internal static Vector2i Position(State state, int slot)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			return new Vector2i((slot == 13) ? 7 : ((slot == 12) ? 6 : ((slot == 11) ? 5 : ((slot < 5) ? slot : (slot - 5)))), state.NormalRows + ((!LayoutPlanner.IsEquipment(slot)) ? 1 : 0));
		}

		internal static int SlotAt(State state, int x, int y)
		{
			if (y == state.NormalRows && x >= 0 && x < 5)
			{
				return x;
			}
			if (y == state.NormalRows && x == 5)
			{
				return 11;
			}
			if (y == state.NormalRows && x == 7)
			{
				return 13;
			}
			if (y == state.NormalRows && x == 6)
			{
				return 12;
			}
			if (y == state.NormalRows + 1 && x >= 0 && x < Plugin.QuickCount.Value)
			{
				return 5 + x;
			}
			return -1;
		}

		internal static ItemData GetSlot(Player player, int slot)
		{
			return ((IEnumerable<ItemData>)((Humanoid)player).GetInventory().GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData x) => Tag(x) == slot));
		}

		internal static void Normalize(Player player)
		{
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			State state = Track(player);
			if (state.Loading || player.m_isLoading)
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			int num = Plugin.Rows.Value;
			string s = default(string);
			if (player.TryGetUniqueKeyValue("invrows", ref s) && int.TryParse(s, out var result))
			{
				num = Math.Max(num, Math.Min(9, result));
			}
			List<ItemData> allItems = inventory.GetAllItems();
			List<LayoutItem> list = new List<LayoutItem>(allItems.Count);
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val = allItems[i];
				int slot = Tag(val);
				if (val.m_equipped && EquipmentIndex(val) >= 0)
				{
					slot = ((val == DualUtility.Extra((Humanoid)(object)player)) ? 11 : EquipmentIndex(val));
				}
				if (!ValidSlot(val, slot))
				{
					slot = -1;
				}
				list.Add(new LayoutItem
				{
					Id = i,
					X = val.m_gridPos.x,
					Y = val.m_gridPos.y,
					Slot = slot,
					Equipped = val.m_equipped
				});
			}
			LayoutResult layoutResult = LayoutPlanner.Arrange(list, inventory.GetWidth(), num, Plugin.QuickCount.Value);
			if (layoutResult.NormalRows > num && layoutResult.NormalRows != state.NormalRows)
			{
				Plugin.Log.LogWarning((object)("Inventory recovery: added visible space to preserve all items (" + layoutResult.NormalRows + " normal rows)."));
			}
			state.NormalRows = layoutResult.NormalRows;
			foreach (LayoutItem item in layoutResult.Items)
			{
				ItemData obj = allItems[item.Id];
				obj.m_gridPos = new Vector2i(item.X, item.Y);
				SetTag(obj, item.Slot);
			}
			inventory.m_height = state.NormalRows + 2;
		}
	}
	[BepInPlugin("mkova.GearAndStorage", "GearAndStorage", "0.5.17")]
	[BepInIncompatibility("randyknapp.mods.equipmentandquickslots")]
	[BepInIncompatibility("shudnal.ExtraSlots")]
	[BepInIncompatibility("Azumatt.AzuExtendedPlayerInventory")]
	[BepInIncompatibility("aedenthorn.ExtendedPlayerInventory")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Id = "mkova.GearAndStorage";

		public const string Version = "0.5.17";

		internal static ModDataLayout Data;

		internal static ConfigEntry<int> Rows;

		internal static ConfigEntry<int> QuickCount;

		internal static ConfigEntry<bool> ProtectSlots;

		internal static ConfigEntry<bool> AnchorAboveGuardianPower;

		internal static ConfigEntry<Vector2> HudPosition;

		internal static ConfigEntry<Vector2> PanelPosition;

		internal static ConfigEntry<Vector2> GuardianPowerOffset;

		internal static readonly ConfigEntry<KeyboardShortcut>[] Keys = new ConfigEntry<KeyboardShortcut>[6];

		internal static ManualLogSource Log;

		private Harmony harmony;

		private bool active;

		internal static bool CanUseQuickSlots
		{
			get
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && !localPlayer.m_isLoading && !QuestUi.Open)
				{
					return CanAct(localPlayer);
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Expected O, but got Unknown
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0370: Unknown result type (might be due to invalid IL or missing references)
			//IL_037a: Expected O, but got Unknown
			//IL_0342: Unknown result type (might be due to invalid IL or missing references)
			//IL_034e: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Data = new ModDataLayout(Paths.ConfigPath);
			Data.Ensure();
			if (!File.Exists(Data.SettingsPath) && File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath))
			{
				File.Copy(((BaseUnityPlugin)this).Config.ConfigFilePath, Data.SettingsPath);
			}
			ConfigFile value = new ConfigFile(Data.SettingsPath, false, ((BaseUnityPlugin)this).Info.Metadata);
			(AccessTools.Field(typeof(BaseUnityPlugin), "<Config>k__BackingField") ?? throw new MissingFieldException("BepInEx BaseUnityPlugin.Config backing field")).SetValue(this, value);
			Rows = ((BaseUnityPlugin)this).Config.Bind<int>("Player", "InventoryRows", 6, new ConfigDescription("Minimum normal inventory rows; purchased rows and recovery space are preserved.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(4, 9), Array.Empty<object>()));
			QuickCount = ((BaseUnityPlugin)this).Config.Bind<int>("QuickSlots", "Count", 3, new ConfigDescription("Quick slots. Removed slots return items to normal inventory.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 6), Array.Empty<object>()));
			HudPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("QuickSlots", "HudPosition", new Vector2(20f, 160f), "HUD position in pixels from the lower left corner.");
			AnchorAboveGuardianPower = ((BaseUnityPlugin)this).Config.Bind<bool>("QuickSlots", "AnchorAboveGuardianPower", true, "Center the quick bar above the guardian power (Eikthyr) icon. Disable to use HudPosition.");
			GuardianPowerOffset = ((BaseUnityPlugin)this).Config.Bind<Vector2>("QuickSlots", "GuardianPowerOffset", new Vector2(0f, 12f), "Offset in screen pixels from the top of the guardian power display: X horizontal, Y gap above it.");
			PanelPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("Equipment", "PanelPosition", new Vector2(12f, -20f), "Slot panel offset from the upper right corner of the player inventory.");
			ProtectSlots = ((BaseUnityPlugin)this).Config.Bind<bool>("Protection", "ProtectFromStackAll", true, "Keep equipment and quick slot items out of bulk container stacking.");
			KeyCode[] array = new KeyCode[6];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			KeyCode[] array2 = (KeyCode[])(object)array;
			for (int i = 0; i < Keys.Length; i++)
			{
				Keys[i] = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("QuickSlots", "Hotkey" + (i + 1), (i < 3) ? new KeyboardShortcut(array2[i], (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }) : new KeyboardShortcut(array2[i], Array.Empty<KeyCode>()), "Use this quick slot once per key press, also while moving or sprinting. Letters follow the current keyboard layout; hold the specified modifiers (LeftAlt is the left Alt).");
			}
			Storage.Initialize(((BaseUnityPlugin)this).Config);
			WeightSettings.Initialize(((BaseUnityPlugin)this).Config);
			StackSettings.Initialize(((BaseUnityPlugin)this).Config);
			PortalRules.Initialize(((BaseUnityPlugin)this).Config);
			ChestCrafting.Initialize(((BaseUnityPlugin)this).Config);
			FurnaceFeeding.Initialize(((BaseUnityPlugin)this).Config);
			ProductionStorage.Initialize(((BaseUnityPlugin)this).Config);
			WorkbenchNetwork.Initialize(((BaseUnityPlugin)this).Config);
			QuickStack.Initialize(((BaseUnityPlugin)this).Config);
			QuestSystem.Initialize(((BaseUnityPlugin)this).Config);
			QuestUi.Initialize(((BaseUnityPlugin)this).Config);
			TrashUi.Initialize(((BaseUnityPlugin)this).Config);
			CoinPouches.Initialize(((BaseUnityPlugin)this).Config);
			ServerSettings.Initialize(((BaseUnityPlugin)this).Config);
			LanguageRuntime.Register(((BaseUnityPlugin)this).Config);
			LanguageRuntime.Register(((ConfigEntryBase)QuestPreferences.AutoAcceptNext).ConfigFile);
			LanguageRuntime.Tick();
			if (Chainloader.PluginInfos.TryGetValue("org.bepinex.plugins.valheim_plus", out var value2))
			{
				BaseUnityPlugin instance = value2.Instance;
				ConfigEntry<bool> val = default(ConfigEntry<bool>);
				if ((Object)(object)instance == (Object)null || !instance.Config.TryGetEntry<bool>(new ConfigDefinition("Inventory", "enabled"), ref val) || val.Value)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)"GearAndStorage not started: disable Valheim Plus [Inventory] first. Other V+ features may remain enabled.");
					return;
				}
			}
			harmony = new Harmony("mkova.GearAndStorage");
			harmony.PatchAll(typeof(Plugin).Assembly);
			EpicLootCrafting.Initialize();
			active = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"GearAndStorage 0.5.17 loaded: independent storage, equipment and quick slots.");
		}

		private void LateUpdate()
		{
			//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_00f0: Unknown result type (might be due to invalid IL or missing references)
			if (!active)
			{
				return;
			}
			LanguageRuntime.Tick();
			TrashUi.Tick();
			ServerSettings.Tick();
			StackSettings.Tick();
			WorkbenchNetwork.Tick();
			QuestNetwork.Tick();
			Player localPlayer = Player.m_localPlayer;
			CoinPouchUi.Tick();
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || localPlayer.m_isLoading)
			{
				if ((Object)(object)localPlayer != (Object)null)
				{
					EquipmentRecovery.Clear(localPlayer);
				}
				QuickStack.Tick(localPlayer, canAct: false);
				QuestUi.Close();
				return;
			}
			CoinPouches.Tick(localPlayer);
			QuestSystem.Tick(localPlayer);
			DualUtility.Validate(localPlayer);
			if ((Object)(object)InventoryGui.instance == (Object)null || InventoryGui.instance.m_dragItem == null)
			{
				PlayerSlots.Normalize(localPlayer);
				EquipmentRecovery.Tick(localPlayer);
			}
			bool flag = CanAct(localPlayer);
			QuestUi.Tick(flag);
			QuickStack.Tick(localPlayer, flag && !QuestUi.Open);
			if (!flag || QuestUi.Open)
			{
				return;
			}
			KeyboardShortcut value = QuickStack.Key.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				QuickStack.Start(localPlayer);
				return;
			}
			for (int i = 0; i < QuickCount.Value; i++)
			{
				if (QuickSlotInput.IsDown(Keys[i].Value))
				{
					ItemData slot = PlayerSlots.GetSlot(localPlayer, 5 + i);
					if (slot != null)
					{
						((Humanoid)localPlayer).UseItem(((Humanoid)localPlayer).GetInventory(), slot, false);
					}
				}
			}
		}

		private static bool CanAct(Player player)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Invalid comparison between Unknown and I4
			if (Application.isFocused)
			{
				if (!InventoryGui.IsVisible() && !Menu.IsVisible() && !Console.IsVisible() && !TextInput.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !((Character)player).IsTeleporting() && !((Character)player).InCutscene() && !Hud.InRadial())
				{
					if ((Object)(object)Minimap.instance != (Object)null)
					{
						return (int)Minimap.instance.m_mode != 2;
					}
					return true;
				}
				return false;
			}
			return false;
		}

		private void OnGUI()
		{
			if (active)
			{
				if (!QuestUi.Open)
				{
					SlotUi.DrawHud();
				}
				QuestUi.Draw();
			}
		}

		private void OnDestroy()
		{
			TrashUi.Cancel();
			EpicLootCrafting.Shutdown();
			ServerSettings.Restore();
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.Unpat