Decompiled source of VNEI v0.15.4

VNEI.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Auga;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Jotunn;
using Jotunn.Entities;
using Jotunn.GUI;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using SimpleJson;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;
using VNEI.Logic;
using VNEI.Logic.Compatibility;
using VNEI.Patches;
using VNEI.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyTitle("VNEI")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyProduct("VNEI")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("43367351-2A51-4BC5-9028-1303450B71AB")]
[assembly: AssemblyFileVersion("0.15.4")]
[assembly: AssemblyCompany("")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.15.4.0")]
[module: UnverifiableCode]
namespace VNEI
{
	internal static class Log
	{
		internal static ManualLogSource _logSource;

		internal static void Init(ManualLogSource logSource)
		{
			_logSource = logSource;
		}

		internal static void LogDebug(object data)
		{
			_logSource.LogDebug(data);
		}

		internal static void LogError(object data)
		{
			_logSource.LogError(data);
		}

		internal static void LogFatal(object data)
		{
			_logSource.LogFatal(data);
		}

		internal static void LogInfo(object data)
		{
			_logSource.LogInfo(data);
		}

		internal static void LogMessage(object data)
		{
			_logSource.LogMessage(data);
		}

		internal static void LogWarning(object data)
		{
			_logSource.LogWarning(data);
		}
	}
	[BepInPlugin("com.maxsch.valheim.vnei", "VNEI", "0.15.4")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public enum InputButtonWrapper
		{
			Left = 0,
			Right = 1,
			Middle = 2,
			None = -1
		}

		public const string ModName = "VNEI";

		public const string ModGuid = "com.maxsch.valheim.vnei";

		public const string ModVersion = "0.15.4";

		public static ConfigEntry<bool> useBlacklist;

		public static ConfigEntry<bool> invertScroll;

		public static ConfigEntry<bool> normalizeScroll;

		public static ConfigEntry<bool> attachToCrafting;

		public static ConfigEntry<bool> hideUIAtStartup;

		public static ConfigEntry<int> rowCount;

		public static ConfigEntry<int> columnCount;

		public static ConfigEntry<int> transparency;

		public static ConfigEntry<KeyboardShortcut> openHotkey;

		public static ConfigEntry<KeyboardShortcut> viewRecipeHotkey;

		public static ConfigEntry<InputButtonWrapper> itemCheatHotkey;

		public static ConfigEntry<InputButtonWrapper> removeRecentHotkey;

		public static ConfigEntry<KeyboardShortcut> goForwardHotkey;

		public static ConfigEntry<KeyboardShortcut> goBackHotkey;

		private static ConfigEntry<bool> showOnlyKnown;

		private static ConfigEntry<bool> forceShowOnlyKnown;

		public static ConfigEntry<bool> showRecentItems;

		public static ConfigEntry<string> tabName;

		public static ConfigEntry<bool> showModTooltip;

		public static ConfigEntry<bool> cheating;

		public static ConfigEntry<bool> cheatCreatures;

		public static bool isUiOpen = true;

		public Sprite noIconSprite;

		public Sprite starSprite;

		public Sprite noStarSprite;

		public Sprite inventoryIcon;

		public GameObject vneiUI;

		public GameObject displayItemTemplate;

		public GameObject craftingStationTemplate;

		public Item allStations;

		public Item handStation;

		public Item noStation;

		private static readonly Regex RichTagRegex = new Regex("<[^>]*>");

		private Harmony harmony;

		public static Plugin Instance { get; private set; }

		public static AssetBundle AssetBundle { get; private set; }

		private static HashSet<string> ItemBlacklist { get; set; } = new HashSet<string>();


		public static bool ShowOnlyKnown => showOnlyKnown.Value || forceShowOnlyKnown.Value;

		public static event Action OnOpenHotkey;

		private void Awake()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Expected O, but got Unknown
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Expected O, but got Unknown
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Expected O, but got Unknown
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Expected O, but got Unknown
			//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Expected O, but got Unknown
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			//IL_0322: Expected O, but got Unknown
			//IL_0349: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Expected O, but got Unknown
			//IL_037a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0384: Expected O, but got Unknown
			//IL_03af: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b9: Expected O, but got Unknown
			//IL_03e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ea: Expected O, but got Unknown
			//IL_0411: Unknown result type (might be due to invalid IL or missing references)
			//IL_041b: Expected O, but got Unknown
			//IL_0442: Unknown result type (might be due to invalid IL or missing references)
			//IL_044c: Expected O, but got Unknown
			//IL_0469: Unknown result type (might be due to invalid IL or missing references)
			//IL_0473: Expected O, but got Unknown
			Instance = this;
			Log.Init(((BaseUnityPlugin)this).Logger);
			AcceptableValueRange<int> val = new AcceptableValueRange<int>(1, 25);
			AcceptableValueRange<int> val2 = new AcceptableValueRange<int>(0, 100);
			string text = AssetUtils.LoadTextFromResources("Localization.Config.json", Assembly.GetExecutingAssembly());
			Dictionary<string, string> dictionary = SimpleJson.DeserializeObject<Dictionary<string, string>>(text);
			useBlacklist = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Use Item Blacklist", true, new ConfigDescription(dictionary["UseItemBlacklist"], (AcceptableValueBase)null, Array.Empty<object>()));
			showOnlyKnown = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Show Only Known", false, new ConfigDescription(dictionary["ShowOnlyKnown"], (AcceptableValueBase)null, Array.Empty<object>()));
			forceShowOnlyKnown = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Force Show Only Known", false, new ConfigDescription(dictionary["ForceShowOnlyKnown"], (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			showRecentItems = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Show Recent Items", true, new ConfigDescription(dictionary["ShowRecentItems"], (AcceptableValueBase)null, Array.Empty<object>()));
			invertScroll = ((BaseUnityPlugin)this).Config.Bind<bool>("Hotkeys", "Invert Scroll", false, new ConfigDescription(dictionary["InvertScroll"], (AcceptableValueBase)null, Array.Empty<object>()));
			normalizeScroll = ((BaseUnityPlugin)this).Config.Bind<bool>("Hotkeys", "Normalize Scroll", true, new ConfigDescription(dictionary["NormalizeScroll"], (AcceptableValueBase)null, Array.Empty<object>()));
			openHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "Open UI Hotkey", new KeyboardShortcut((KeyCode)104, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), dictionary["OpenUIHotkey"]);
			viewRecipeHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "View Recipe Hotkey", new KeyboardShortcut((KeyCode)114, Array.Empty<KeyCode>()), dictionary["ViewRecipeHotkey"]);
			itemCheatHotkey = ((BaseUnityPlugin)this).Config.Bind<InputButtonWrapper>("Hotkeys", "Item Cheat Mouse Button", InputButtonWrapper.Right, dictionary["ItemCheatMouseButton"]);
			removeRecentHotkey = ((BaseUnityPlugin)this).Config.Bind<InputButtonWrapper>("Hotkeys", "Remove Recent Item Mouse Button", InputButtonWrapper.Middle, dictionary["RemoveRecentMouseButton"]);
			goForwardHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "Go Forward Hotkey", new KeyboardShortcut((KeyCode)275, Array.Empty<KeyCode>()), dictionary["GoForwardHotkey"]);
			goBackHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "Go Back Hotkey", new KeyboardShortcut((KeyCode)276, Array.Empty<KeyCode>()), dictionary["GoBackHotkey"]);
			columnCount = ((BaseUnityPlugin)this).Config.Bind<int>("UI", "Items Horizontal", 12, new ConfigDescription(dictionary["ItemsHorizontal"], (AcceptableValueBase)(object)val, Array.Empty<object>()));
			rowCount = ((BaseUnityPlugin)this).Config.Bind<int>("UI", "Items Vertical", 6, new ConfigDescription(dictionary["ItemsVertical"], (AcceptableValueBase)(object)val, Array.Empty<object>()));
			attachToCrafting = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "Attach To Crafting", true, new ConfigDescription(dictionary["AttachToCrafting"], (AcceptableValueBase)null, Array.Empty<object>()));
			hideUIAtStartup = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "Hide GUI At Start", false, new ConfigDescription(dictionary["HideGUIAtStart"], (AcceptableValueBase)null, Array.Empty<object>()));
			transparency = ((BaseUnityPlugin)this).Config.Bind<int>("Visual", "Background Transparency", 0, new ConfigDescription(dictionary["Transparency"], (AcceptableValueBase)(object)val2, Array.Empty<object>()));
			tabName = ((BaseUnityPlugin)this).Config.Bind<string>("Visual", "Tab Name", "VNEI", new ConfigDescription(dictionary["TabName"], (AcceptableValueBase)null, Array.Empty<object>()));
			showModTooltip = ((BaseUnityPlugin)this).Config.Bind<bool>("Tooltips", "Show Mod Name In Tooltip", true, new ConfigDescription(dictionary["ShowModTooltip"], (AcceptableValueBase)null, Array.Empty<object>()));
			cheating = ((BaseUnityPlugin)this).Config.Bind<bool>("Cheating", "Enable Cheating", false, new ConfigDescription(dictionary["EnableCheating"], (AcceptableValueBase)null, Array.Empty<object>()));
			cheatCreatures = ((BaseUnityPlugin)this).Config.Bind<bool>("Cheating", "Enable Spawning Creatures", false, new ConfigDescription(dictionary["AllowCreatureSpawn"], (AcceptableValueBase)null, Array.Empty<object>()));
			isUiOpen = !hideUIAtStartup.Value;
			harmony = new Harmony("com.maxsch.valheim.vnei");
			harmony.PatchAll();
			AssetBundle = AssetUtils.LoadAssetBundleFromResources("VNEI_AssetBundle");
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			localization.AddJsonFile("English", AssetUtils.LoadTextFromResources("Localization.English.json"));
			localization.AddJsonFile("German", AssetUtils.LoadTextFromResources("Localization.German.json"));
			LoadBlacklist();
			noIconSprite = AssetBundle.LoadAsset<Sprite>("NoSprite.png");
			starSprite = AssetBundle.LoadAsset<Sprite>("Star.png");
			noStarSprite = AssetBundle.LoadAsset<Sprite>("NoStar.png");
			inventoryIcon = AssetBundle.LoadAsset<Sprite>("Hand.png");
			vneiUI = AssetBundle.LoadAsset<GameObject>("VNEI");
			displayItemTemplate = AssetBundle.LoadAsset<GameObject>("_Template");
			craftingStationTemplate = AssetBundle.LoadAsset<GameObject>("CraftingStationTemplate");
			GUIManager.OnCustomGUIAvailable += delegate
			{
				GetMainUI().CreateBaseUI();
			};
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new SelectUITest.ToggleUIConsoleCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new FileWriterControllerCSV());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new FileWriterControllerYAML());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new FileWriterControllerText());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new IconExport());
			PrefabManager.OnPrefabsRegistered += ApplyMocks;
			ModQuery.Enable();
		}

		private static void LoadBlacklist()
		{
			string text = AssetUtils.LoadTextFromResources("ItemBlacklist.json", Assembly.GetExecutingAssembly());
			ItemBlacklist = SimpleJson.DeserializeObject<List<string>>(text).ToHashSet();
			string path = Path.Combine(Paths.ConfigPath, "com.maxsch.valheim.vnei.blacklist.txt");
			if (!File.Exists(path))
			{
				File.WriteAllText(path, "");
				return;
			}
			string[] array = File.ReadAllLines(path);
			foreach (string text2 in array)
			{
				string text3 = text2.Trim();
				if (!string.IsNullOrEmpty(text3) && !ItemBlacklist.Contains(text3))
				{
					ItemBlacklist.Add(text2);
				}
			}
		}

		private void Start()
		{
			ModCompat.Init(harmony);
			PlanBuild.Init();
		}

		private void Update()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			if (openHotkey.Value.IsKeyDown())
			{
				OpenUI();
			}
			if (!Indexing.HasIndexed() && Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				Indexing.IndexAll();
				Indexing.UpdateKnown();
				showOnlyKnown.SettingChanged += delegate
				{
					Indexing.UpdateKnown();
				};
				forceShowOnlyKnown.SettingChanged += delegate
				{
					Indexing.UpdateKnown();
				};
				KnownRecipesPatches.OnUpdateKnownRecipes += Indexing.UpdateKnown;
			}
			if (!viewRecipeHotkey.Value.IsKeyDown() || !Object.op_Implicit((Object)(object)UITooltip.m_current))
			{
				return;
			}
			string text = UITooltip.m_current.m_topic.Trim();
			string text2 = UITooltip.m_current.m_text.Trim();
			Item item = null;
			if (text.Length > 0)
			{
				item = Indexing.GetItem(text);
			}
			if (item == null && text2.Length > 0)
			{
				item = Indexing.GetItem(text2);
			}
			if (item == null && RichTagRegex.IsMatch(text))
			{
				string name = RichTagRegex.Replace(text, string.Empty).Trim();
				item = Indexing.GetItem(name);
			}
			if (item != null)
			{
				if (attachToCrafting.Value)
				{
					GetMainUI().SetTabActive();
				}
				BaseUI baseUI = GetMainUI().GetBaseUI();
				baseUI.ShowRecipe(item, trackHistory: true);
			}
		}

		public static void OpenUI()
		{
			isUiOpen = !isUiOpen;
			Plugin.OnOpenHotkey?.Invoke();
		}

		public static bool AttachToCrafting()
		{
			return attachToCrafting.Value;
		}

		private void ApplyMocks()
		{
			PrefabExtension.FixReferences(vneiUI, true);
			PrefabExtension.FixReferences(displayItemTemplate, true);
			PrefabExtension.FixReferences(craftingStationTemplate, true);
			PrefabManager.OnPrefabsRegistered -= ApplyMocks;
		}

		public static bool IsItemBlacklisted(Item item)
		{
			return ItemBlacklist.Contains(item.internalName) || ItemBlacklist.Contains(Indexing.CleanupName(item.internalName));
		}

		public static VneiHandler GetMainUI()
		{
			if (IsAugaLoaded())
			{
				return MainVneiHandlerAuga.Instance;
			}
			return MainVneiHandler.Instance;
		}

		public static bool IsAugaLoaded()
		{
			return Chainloader.PluginInfos.ContainsKey("randyknapp.mods.auga");
		}
	}
}
namespace VNEI.Patches
{
	[HarmonyPatch]
	public class InventoryGuiPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(InventoryGui), "Awake")]
		[HarmonyPriority(200)]
		public static void AwakePatch(InventoryGui __instance)
		{
			if (!Plugin.IsAugaLoaded())
			{
				MainVneiHandler.Instance.GetTabButton();
				((MonoBehaviour)__instance).StartCoroutine(UpdateOtherTabs());
			}
			else
			{
				MainVneiHandlerAuga.Instance.CreateBaseUI();
			}
		}

		private static IEnumerator UpdateOtherTabs()
		{
			WaitForSeconds wait = new WaitForSeconds(1f);
			while (true)
			{
				yield return wait;
				MainVneiHandler.Instance.UpdateOtherTabs();
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "UpdateCraftingPanel")]
		[HarmonyAfter(new string[] { "org.bepinex.plugins.jewelcrafting" })]
		[HarmonyPostfix]
		public static void UpdateCraftingPanelPatch()
		{
			if (!Plugin.IsAugaLoaded())
			{
				MainVneiHandler.Instance.UpdateTabPosition();
			}
		}

		[HarmonyAfter(new string[] { "org.bepinex.plugins.jewelcrafting" })]
		[HarmonyPostfix]
		[HarmonyPatch(typeof(InventoryGui), "SetupCrafting")]
		public static void SetupCraftingPatch()
		{
			if (!Plugin.IsAugaLoaded() && MainVneiHandler.Instance.VneiTabActive)
			{
				MainVneiHandler.Instance.SetTabActive();
			}
		}
	}
	public class ModCompat
	{
		private class ValheimRecycleFix
		{
			[UsedImplicitly]
			private static IEnumerable<MethodBase> TargetMethods()
			{
				Type valheimRecycle = Type.GetType("ValheimRecycle.ValheimRecycle, ValheimRecycle, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
				yield return valheimRecycle.GetMethod("GetOrCreateRecycleTab", BindingFlags.Instance | BindingFlags.NonPublic);
			}

			[UsedImplicitly]
			private static void Postfix(GameObject __result)
			{
				__result.transform.SetSiblingIndex(2);
			}
		}

		public static void Init(Harmony harmony)
		{
			if (Chainloader.PluginInfos.ContainsKey("org.lafchi.plugins.valheim_recycle"))
			{
				harmony.PatchAll(typeof(ValheimRecycleFix));
			}
		}
	}
	[HarmonyPatch]
	public class KnownRecipesPatches
	{
		public static event Action OnUpdateKnownRecipes;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "ResetCharacterKnownItems")]
		[HarmonyPatch(typeof(Player), "OnSpawned")]
		[HarmonyPatch(typeof(Player), "UpdateKnownRecipesList")]
		public static void UpdateKnownRecipesListPatch()
		{
			KnownRecipesPatches.OnUpdateKnownRecipes?.Invoke();
		}
	}
	[HarmonyPatch]
	public static class TooltipPatches
	{
		[HarmonyPriority(0)]
		[HarmonyPostfix]
		[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
		{
			typeof(ItemData),
			typeof(int),
			typeof(bool),
			typeof(float),
			typeof(int)
		})]
		[HarmonyAfter(new string[] { "randyknapp.mods.epicloot" })]
		public static void AppendModName(ref string __result, ItemData item)
		{
			if (Plugin.showModTooltip.Value)
			{
				string name = PrefabName(item);
				Item item2 = Indexing.GetItem(name);
				if (item2 != null)
				{
					__result = __result.TrimEnd(Array.Empty<char>()) + item2.GetTooltipModName();
				}
			}
		}

		private static string PrefabName(ItemData item)
		{
			if (item == null)
			{
				return string.Empty;
			}
			if ((Object)(object)item.m_dropPrefab != (Object)null)
			{
				return ((Object)item.m_dropPrefab).name;
			}
			return item.m_shared.m_name;
		}
	}
}
namespace VNEI.Logic
{
	public readonly struct Amount
	{
		public readonly int min;

		public readonly int max;

		public readonly float chance;

		public static Amount Zero { get; } = new Amount(0, 0, 0f);


		public static Amount One { get; } = new Amount(1);


		public Amount(int min, int max, float chance = 1f)
		{
			this.min = min;
			this.max = max;
			this.chance = chance;
		}

		public Amount(int amount, float chance = 1f)
		{
			min = amount;
			max = amount;
			this.chance = chance;
		}

		public static bool IsSameMinMax(Amount a, Amount b)
		{
			return a.min == b.min && a.max == b.max;
		}

		public override string ToString()
		{
			int num = Mathf.RoundToInt(chance * 100f);
			string text = "";
			bool flag = false;
			if (num != 100)
			{
				text += $"{num}% ";
				flag = true;
			}
			if (min == max)
			{
				if (!flag || max != 1)
				{
					text += $"{max}x";
				}
			}
			else
			{
				text += $"{min}-{max}x";
			}
			return text;
		}

		public bool Equals(Amount other)
		{
			int result;
			if (min == other.min && max == other.max)
			{
				float num = chance;
				result = (num.Equals(other.chance) ? 1 : 0);
			}
			else
			{
				result = 0;
			}
			return (byte)result != 0;
		}

		public override bool Equals(object obj)
		{
			return obj is Amount other && Equals(other);
		}

		public override int GetHashCode()
		{
			int num = min;
			num = (num * 397) ^ max;
			int num2 = num * 397;
			float num3 = chance;
			return num2 ^ num3.GetHashCode();
		}
	}
	public static class BepInExExtensions
	{
		public static bool IsKeyDown(this KeyboardShortcut shortcut)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			BaseUI baseUI = Plugin.GetMainUI().GetBaseUI();
			if (Object.op_Implicit((Object)(object)baseUI) && baseUI.BlockInput)
			{
				return false;
			}
			return (int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey) && ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey);
		}
	}
	public class ComponentEvent
	{
		private readonly Dictionary<Component, Action> listener = new Dictionary<Component, Action>();

		public void AddListener(Component component, Action action)
		{
			if (!listener.ContainsKey(component))
			{
				listener.Add(component, action);
			}
		}

		public void RemoveListener(Component component)
		{
			listener.Remove(component);
		}

		public void Invoke()
		{
			bool flag = false;
			foreach (KeyValuePair<Component, Action> item in listener)
			{
				if (!Object.op_Implicit((Object)(object)item.Key))
				{
					flag = true;
				}
				else
				{
					item.Value?.Invoke();
				}
			}
			if (flag)
			{
				RemoveDestroyedKeys();
			}
		}

		private void RemoveDestroyedKeys()
		{
			listener.Keys.Where((Component i) => !Object.op_Implicit((Object)(object)i)).ToList().ForEach(delegate(Component i)
			{
				listener.Remove(i);
			});
		}
	}
	public static class ExportPaths
	{
		private static string GetMainExportFolder()
		{
			return Path.Combine(Paths.BepInExRootPath, "VNEI-Export");
		}

		public static string GetExportLocation(string fileName, params string[] path)
		{
			string text = Path.Combine(GetMainExportFolder(), Path.Combine(path), fileName);
			string directoryName = Path.GetDirectoryName(text);
			if (directoryName == null || string.IsNullOrEmpty(directoryName))
			{
				throw new DirectoryNotFoundException("Invalid directory for " + fileName + ": " + directoryName);
			}
			if (!Directory.Exists(directoryName))
			{
				Directory.CreateDirectory(directoryName);
			}
			return text;
		}
	}
	public static class FileWriter
	{
		private const string FileName = "VNEI.indexed.items";

		private const string MissingType = "missing-type";

		public static void PrintSimpleTextFile(List<Item> items)
		{
			string exportLocation = ExportPaths.GetExportLocation("VNEI.indexed.items.txt");
			Log.LogInfo("Writing indexed items to file " + exportLocation);
			File.WriteAllLines(exportLocation, items.Select((Item item) => item.PrintItem()).ToArray());
		}

		public static void PrintCSVFile(List<Item> items, string separator)
		{
			string exportLocation = ExportPaths.GetExportLocation("VNEI.indexed.items.csv");
			Log.LogInfo("Writing indexed items to file " + exportLocation);
			string text = Item.PrintCSVHeader(separator);
			string[] second = items.Select((Item item) => EncapsulateFields(item.PrintItemCSV(separator), separator)).ToArray();
			File.WriteAllLines(exportLocation, new string[1] { text }.Concat(second).ToArray());
		}

		private static string EncapsulateFields(string csvLine, string separator)
		{
			string[] source = csvLine.Split(new string[1] { separator }, StringSplitOptions.None);
			IEnumerable<string> values = source.Select((string field) => "\"" + field.Replace("\"", "\"\"") + "\"");
			return string.Join(separator, values);
		}

		public static void PrintCLLCItemConfigYaml(List<Item> items, List<string> itemNamesFilterExcluded, List<string> modNamesFilterExcluded, List<string> modNamesFilterInclude)
		{
			string exportLocation = ExportPaths.GetExportLocation("VNEI.indexed.items.yml");
			Log.LogInfo("Writing indexed items to file " + exportLocation);
			Log.LogInfo("Excluding items via match terms '" + string.Join(",", itemNamesFilterExcluded) + "' and mods with names matching '" + string.Join(",", modNamesFilterExcluded) + "'");
			if (modNamesFilterInclude.Count > 0)
			{
				Log.LogInfo("Including items from mods with names matching '" + string.Join(",", modNamesFilterInclude) + "'");
			}
			ISerializer serializer = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build();
			ItemDrop val = default(ItemDrop);
			List<(string, string, string)> source = (from item in FilterInvalidItems(items, itemNamesFilterExcluded, modNamesFilterExcluded, modNamesFilterInclude)
				select (item.prefab.TryGetComponent<ItemDrop>(ref val) ? ((object)(ItemType)(ref val.m_itemData.m_shared.m_itemType)).ToString() : item.itemType.ToString(), item.internalName, item.GetModName())).ToList();
			Dictionary<string, List<string>> groups = (from @group in source.GroupBy<(string, string, string), string>(delegate((string itemType, string internalName, string modName) item)
				{
					string result;
					if (!(item.modName != string.Empty))
					{
						(result, _, _) = item;
					}
					else
					{
						result = item.itemType + "_" + CleanModName(item.modName);
					}
					return result;
				})
				where @group.Key != "missing-type"
				select @group).ToDictionary((IGrouping<string, (string itemType, string internalName, string modName)> group) => group.Key, (IGrouping<string, (string itemType, string internalName, string modName)> group) => group.Select(((string itemType, string internalName, string modName) groupedItem) => groupedItem.internalName).ToList());
			string contents = serializer.Serialize(new AllGroups(groups));
			File.WriteAllText(exportLocation, contents);
		}

		private static List<Item> FilterInvalidItems(List<Item> items, List<string> itemNamesFilterExcluded, List<string> modNamesFilterExcluded, List<string> modNamesFilterInclude)
		{
			return (from item in items
				where (Object)(object)item.prefab != (Object)null
				where itemNamesFilterExcluded.All((string filterExclude) => !item.internalName.Contains(filterExclude))
				select item).FilterExcludeMod(modNamesFilterExcluded).FilterIncludeMod(modNamesFilterInclude).ToList();
		}

		private static IEnumerable<Item> FilterExcludeMod(this IEnumerable<Item> items, List<string> modNamesFilterExcluded)
		{
			return items.Where((Item item) => modNamesFilterExcluded.All((string filterExclude) => !item.GetModName().Contains(filterExclude))).ToList();
		}

		private static IEnumerable<Item> FilterIncludeMod(this IEnumerable<Item> items, List<string> modNamesFilterInclude)
		{
			if (modNamesFilterInclude.Count == 0)
			{
				return items;
			}
			return items.Where((Item item) => modNamesFilterInclude.Any((string filterInclude) => item.GetModName().Contains(filterInclude))).ToList();
		}

		private static string CleanModName(string modNameOriginal)
		{
			return Regex.Replace(modNameOriginal, "[^\\w]+", "_");
		}
	}
	public class AllGroups
	{
		[UsedImplicitly]
		public readonly Dictionary<string, List<string>> Groups;

		public AllGroups(Dictionary<string, List<string>> groups)
		{
			Groups = groups;
		}
	}
	public abstract class FileWriterController : ConsoleCommand
	{
		public override string Help => "Writes all indexed items to a file";

		protected static List<Item> GetItems()
		{
			return (from tuple in Indexing.GetActiveItems()
				select tuple.Value).ToList();
		}
	}
	public class FileWriterControllerCSV : FileWriterController
	{
		public override string Name => "vnei_export_csv";

		public override void Run(string[] args)
		{
			FileWriter.PrintCSVFile(separator: ((args.Length != 0) ? args[0] : string.Empty) switch
			{
				"comma" => ",", 
				"tab" => "\t", 
				"semicolon" => ";", 
				_ => ",", 
			}, items: FileWriterController.GetItems());
		}

		public override List<string> CommandOptionList()
		{
			return new List<string> { "comma", "semicolon", "tab" };
		}
	}
	public class FileWriterControllerYAML : FileWriterController
	{
		public override string Name => "vnei_export_yaml";

		public override void Run(string[] args)
		{
			Log.LogInfo("args '" + string.Join("' '", args) + "'");
			List<string> itemNamesFilterExcluded = ((args.Length != 0) ? args[0].Split(new char[1] { ',' }).ToList() : new List<string>());
			List<string> modNamesFilterExcluded = ((args.Length > 1) ? args[1].Split(new char[1] { ',' }).ToList() : new List<string>());
			List<string> modNamesFilterInclude = ((args.Length > 2) ? args[2].Split(new char[1] { ',' }).ToList() : new List<string>());
			FileWriter.PrintCLLCItemConfigYaml(FileWriterController.GetItems(), itemNamesFilterExcluded, modNamesFilterExcluded, modNamesFilterInclude);
		}
	}
	public class FileWriterControllerText : FileWriterController
	{
		public override string Name => "vnei_export_text";

		public override void Run(string[] args)
		{
			FileWriter.PrintSimpleTextFile(FileWriterController.GetItems());
		}
	}
	public class IconExport : ConsoleCommand
	{
		public override string Name => "vnei_export_icons";

		public override string Help => "Exports icons";

		public override void Run(string[] args)
		{
			List<Item> source = (from tuple in Indexing.GetActiveItems()
				select tuple.Value).ToList();
			Log.LogInfo("Exporting icons into " + ExportPaths.GetExportLocation("", "icons"));
			foreach (IGrouping<string, Item> item in from item in source
				group item by item.GetModName())
			{
				if (args.Length >= 1 && !args[0].Split(new char[1] { ',' }).Contains(item.Key))
				{
					continue;
				}
				Log.LogInfo($"Exporting {item.Count()} icons for {item.Key}");
				foreach (Item item2 in item)
				{
					SaveIcon(item2);
				}
			}
		}

		private static void SaveIcon(Item item)
		{
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Expected O, but got Unknown
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)item.prefab))
			{
				return;
			}
			Sprite icon = item.GetIcon();
			if (!Object.op_Implicit((Object)(object)icon))
			{
				Log.LogWarning("No icon for " + item.internalName);
				return;
			}
			string fileName = Path.GetInvalidFileNameChars().Aggregate(item.internalName + ".png", (string current, char c) => current.Replace(c, '_'));
			string text = Path.GetInvalidPathChars().Aggregate(item.GetModName(), (string current, char c) => current.Replace(c, '_'));
			string exportLocation = ExportPaths.GetExportLocation(fileName, "icons", text);
			int width = ((Texture)icon.texture).width;
			int height = ((Texture)icon.texture).height;
			RenderTexture temporary = RenderTexture.GetTemporary(width, height);
			Graphics.Blit((Texture)(object)icon.texture, temporary);
			RenderTexture active = RenderTexture.active;
			RenderTexture.active = temporary;
			Rect val = icon.rect;
			int num = (int)((Rect)(ref val)).width;
			val = icon.rect;
			int num2 = (int)((Rect)(ref val)).height;
			val = icon.textureRect;
			int num3 = (int)(((Rect)(ref val)).x - icon.textureRectOffset.x);
			float num4 = height;
			val = icon.textureRect;
			int num5 = (int)(num4 - (((Rect)(ref val)).y - icon.textureRectOffset.y + (float)num2));
			Texture2D val2 = new Texture2D(num, num2);
			val2.ReadPixels(new Rect((float)num3, (float)num5, (float)num, (float)num2), 0, 0);
			val2.Apply();
			RenderTexture.active = active;
			RenderTexture.ReleaseTemporary(temporary);
			File.WriteAllBytes(exportLocation, ImageConversion.EncodeToPNG(val2));
		}
	}
	public class FavouritesSave
	{
		public const string favouritesFileName = "com.maxsch.valheim.vnei.favourites.txt";

		public static string GetFavouritesFilePath()
		{
			return Path.Combine(Paths.ConfigPath, "com.maxsch.valheim.vnei.favourites.txt");
		}

		public static void Load()
		{
			string favouritesFilePath = GetFavouritesFilePath();
			if (!File.Exists(favouritesFilePath))
			{
				return;
			}
			using StreamReader streamReader = File.OpenText(favouritesFilePath);
			while (!streamReader.EndOfStream)
			{
				string text = streamReader.ReadLine();
				if (!string.IsNullOrEmpty(text))
				{
					Item item = Indexing.GetItem(text);
					if (item != null)
					{
						item.isFavorite = true;
					}
				}
			}
		}

		public static void Save()
		{
			using StreamWriter streamWriter = File.CreateText(GetFavouritesFilePath());
			foreach (KeyValuePair<string, Item> activeItem in Indexing.GetActiveItems())
			{
				if (activeItem.Value.isFavorite)
				{
					streamWriter.WriteLine(activeItem.Value.internalName);
				}
			}
		}
	}
	public static class Indexing
	{
		private static int currentKnownCount;

		private static bool currentShowOnlyKnown;

		private static Dictionary<string, Item> Items { get; } = new Dictionary<string, Item>();


		private static Dictionary<string, Item> ItemsByPreLocalizedName { get; } = new Dictionary<string, Item>();


		private static Dictionary<string, Item> ItemsByLocalizedName { get; } = new Dictionary<string, Item>();


		public static event Action<GameObject> OnIndexingItems;

		public static event Action<GameObject> OnDisableItems;

		public static event Action AfterDisableItems;

		public static event Action<Recipe> OnIndexingRecipes;

		public static event Action<GameObject> OnIndexingItemRecipes;

		public static event Action IndexFinished;

		public static event Action AfterUpdateKnownItems;

		public static void IndexAll()
		{
			if (HasIndexed())
			{
				return;
			}
			if (!Object.op_Implicit((Object)(object)ZNetScene.instance))
			{
				Log.LogWarning("Cannot index: ZNetScene.instance is null");
				return;
			}
			Log.LogInfo("Index items and recipes");
			ModNames.IndexModNames();
			Dictionary<string, PieceTable> pieceTables = new Dictionary<string, PieceTable>();
			List<GameObject> prefabs = GetPrefabs();
			IndexItems(prefabs, pieceTables);
			DisableItems(prefabs);
			IndexRecipes();
			IndexItemRecipes(prefabs, pieceTables);
			foreach (RecipeInfo recipe in RecipeInfo.Recipes)
			{
				recipe.CalculateIsOnBlacklist();
				recipe.CalculateWidth();
			}
			FavouritesSave.Load();
			Log.LogInfo($"Loaded {GetActiveItems().Count()} items and {RecipeInfo.Recipes.Count} recipes");
			try
			{
				Indexing.IndexFinished?.Invoke();
			}
			catch (Exception data)
			{
				Log.LogError(data);
			}
		}

		private static List<GameObject> GetPrefabs()
		{
			HashSet<GameObject> first = new HashSet<GameObject>(ZNetScene.instance.m_prefabs);
			HashSet<GameObject> second = new HashSet<GameObject>(ZNetScene.instance.m_namedPrefabs.Values);
			List<GameObject> list = first.Union(second).ToList();
			list.RemoveAll((GameObject prefab) => !Object.op_Implicit((Object)(object)prefab));
			return list;
		}

		private static void IndexItems(List<GameObject> prefabs, Dictionary<string, PieceTable> pieceTables)
		{
			AddItem(new Item("vnei_any_item", "$vnei_any_item", string.Empty, null, ItemType.Undefined, null));
			AddItem(new Item("vnei_unknown_item", "$vnei_unknown_item", string.Empty, null, ItemType.Undefined, null));
			AddItem(new Item("vnei_all_stations", "$vnei_all_stations", "", null, ItemType.Undefined, null));
			AddItem(new Item("vnei_hand_station", "$vnei_hand_station", "", Plugin.Instance.inventoryIcon, ItemType.Undefined, null));
			AddItem(new Item("vnei_no_station", "$vnei_no_station", "", null, ItemType.Undefined, null));
			Plugin.Instance.allStations = GetItem("vnei_all_stations");
			Plugin.Instance.handStation = GetItem("vnei_hand_station");
			Plugin.Instance.noStation = GetItem("vnei_no_station");
			DisableItem("vnei_any_item", "is not an item");
			DisableItem("vnei_unknown_item", "is not an item");
			DisableItem("vnei_all_stations", "is not an item");
			DisableItem("vnei_hand_station", "is not an item");
			DisableItem("vnei_no_station", "is not an item");
			HoverText val = default(HoverText);
			CraftingStation val2 = default(CraftingStation);
			ItemDrop val3 = default(ItemDrop);
			foreach (GameObject prefab in prefabs)
			{
				string name = ((Object)prefab).name;
				string text = string.Empty;
				if (prefab.TryGetComponent<HoverText>(ref val))
				{
					text = val.m_text;
				}
				if (string.IsNullOrEmpty(text) && prefab.TryGetComponent<CraftingStation>(ref val2))
				{
					text = val2.m_name;
				}
				if (name.StartsWith("TreasureChest"))
				{
					TryAddItem<Piece>(prefab, (Func<Piece, string>)((Piece i) => i.m_name), text, ItemType.Piece, (Func<Piece, string>)((Piece i) => i.m_description), (Func<Piece, Sprite>)((Piece i) => i.m_icon));
				}
				if (prefab.TryGetComponent<ItemDrop>(ref val3))
				{
					ItemData itemData = val3.m_itemData;
					Sprite icon = null;
					if (Object.op_Implicit((Object)(object)val3) && itemData.m_shared.m_icons.Length != 0)
					{
						icon = itemData.GetIcon();
					}
					if (itemData.m_shared.m_damageModifiers == null)
					{
						itemData.m_shared.m_damageModifiers = new List<DamageModPair>();
					}
					ItemType itemType = ItemTypeHelper.GetItemType(itemData);
					SharedData shared = itemData.m_shared;
					AddItem(new Item(((Object)prefab).name, shared.m_name, shared.m_description, icon, itemType, prefab, shared.m_maxQuality));
					if (Object.op_Implicit((Object)(object)itemData.m_shared.m_buildPieces))
					{
						pieceTables.Add(CleanupName(name), itemData.m_shared.m_buildPieces);
						foreach (GameObject piece in itemData.m_shared.m_buildPieces.m_pieces)
						{
							TryAddItem<Piece>(piece, (Func<Piece, string>)((Piece i) => i.m_name), text, ItemType.Piece, (Func<Piece, string>)((Piece i) => i.m_description), (Func<Piece, Sprite>)((Piece i) => i.m_icon));
						}
					}
				}
				TryAddItem<Character>(prefab, (Func<Character, string>)((Character i) => i.m_name), text, ItemType.Creature, (Func<Character, string>)null, (Func<Character, Sprite>)null);
				TryAddItem<MineRock>(prefab, (Func<MineRock, string>)((MineRock i) => i.m_name), text, ItemType.Undefined, (Func<MineRock, string>)null, (Func<MineRock, Sprite>)null);
				TryAddItem<MineRock5>(prefab, (Func<MineRock5, string>)((MineRock5 i) => i.m_name), text, ItemType.Undefined, (Func<MineRock5, string>)null, (Func<MineRock5, Sprite>)null);
				TryAddItem<DropOnDestroyed>(prefab, (Func<DropOnDestroyed, string>)((DropOnDestroyed i) => string.Empty), text, ItemType.Undefined, (Func<DropOnDestroyed, string>)null, (Func<DropOnDestroyed, Sprite>)null);
				TryAddItem<Pickable>(prefab, (Func<Pickable, string>)((Pickable i) => string.Empty), text, ItemType.Undefined, (Func<Pickable, string>)null, (Func<Pickable, Sprite>)null);
				TryAddItem<SpawnArea>(prefab, (Func<SpawnArea, string>)((SpawnArea i) => string.Empty), text, ItemType.Creature, (Func<SpawnArea, string>)null, (Func<SpawnArea, Sprite>)null);
				TryAddItem<Destructible>(prefab, (Func<Destructible, string>)((Destructible i) => string.Empty), text, ItemType.Undefined, (Func<Destructible, string>)null, (Func<Destructible, Sprite>)null);
				TryAddItem<TreeBase>(prefab, (Func<TreeBase, string>)((TreeBase i) => string.Empty), text, ItemType.Undefined, (Func<TreeBase, string>)null, (Func<TreeBase, Sprite>)null);
				TryAddItem<Trader>(prefab, (Func<Trader, string>)((Trader i) => i.m_name), text, ItemType.Undefined, (Func<Trader, string>)null, (Func<Trader, Sprite>)null);
				try
				{
					Indexing.OnIndexingItems?.Invoke(prefab);
				}
				catch (Exception data)
				{
					Log.LogError(data);
				}
			}
		}

		private static void DisableItems(List<GameObject> prefabs)
		{
			Piece val = default(Piece);
			Humanoid val2 = default(Humanoid);
			foreach (GameObject prefab in prefabs)
			{
				if (prefab.TryGetComponent<Piece>(ref val) && GetItem(((Object)prefab).name) == null)
				{
					Log.LogDebug("not indexed piece " + ((Object)val).name + ": not buildable");
				}
				if (prefab.TryGetComponent<Humanoid>(ref val2))
				{
					DisableArray(prefab, val2.m_defaultItems);
					DisableArray(prefab, val2.m_randomWeapon);
					DisableArray(prefab, val2.m_randomShield);
					DisableArray(prefab, val2.m_randomArmor);
					if (val2.m_randomSets != null)
					{
						ItemSet[] randomSets = val2.m_randomSets;
						for (int i = 0; i < randomSets.Length; i++)
						{
							DisableArray(prefab, randomSets[i]?.m_items);
						}
					}
					DisableEffectList(prefab, ((Character)val2).m_hitEffects);
					DisableEffectList(prefab, ((Character)val2).m_critHitEffects);
					DisableEffectList(prefab, ((Character)val2).m_backstabHitEffects);
					DisableEffectList(prefab, ((Character)val2).m_deathEffects);
					DisableEffectList(prefab, ((Character)val2).m_waterEffects);
					DisableEffectList(prefab, ((Character)val2).m_tarEffects);
					DisableEffectList(prefab, ((Character)val2).m_slideEffects);
					DisableEffectList(prefab, ((Character)val2).m_jumpEffects);
					DisableEffectList(prefab, val2.m_pickupEffects);
					DisableEffectList(prefab, val2.m_dropEffects);
					DisableEffectList(prefab, val2.m_consumeItemEffects);
					DisableEffectList(prefab, val2.m_equipEffects);
					DisableEffectList(prefab, val2.m_perfectBlockEffect);
				}
				try
				{
					Indexing.OnDisableItems?.Invoke(prefab);
				}
				catch (Exception data)
				{
					Log.LogError(data);
				}
			}
			try
			{
				Indexing.AfterDisableItems?.Invoke();
			}
			catch (Exception data2)
			{
				Log.LogError(data2);
			}
		}

		private static void DisableArray(GameObject from, GameObject[] array)
		{
			if (array == null)
			{
				return;
			}
			foreach (GameObject item in array.Where((GameObject i) => Object.op_Implicit((Object)(object)i)))
			{
				ItemDrop component = item.GetComponent<ItemDrop>();
				if (Object.op_Implicit((Object)(object)component))
				{
					ItemData itemData = component.m_itemData;
					if (itemData != null && itemData.m_shared?.m_icons?.Length > 0)
					{
						Log.LogDebug("Not disabling item " + ((Object)item).name + " because it has icons");
						continue;
					}
				}
				DisableItem(((Object)item).name, "is defaultItem from " + ((Object)from).name);
			}
		}

		private static void DisableEffectList(GameObject from, EffectList effectList)
		{
			if (effectList?.m_effectPrefabs == null)
			{
				return;
			}
			foreach (EffectData item in effectList.m_effectPrefabs.Where((EffectData i) => i != null && Object.op_Implicit((Object)(object)i.m_prefab)))
			{
				DisableItem(((Object)item.m_prefab).name, "is defaultItem from " + ((Object)from).name);
			}
		}

		private static void IndexRecipes()
		{
			if (!Object.op_Implicit((Object)(object)ObjectDB.instance))
			{
				Log.LogWarning("Cannot index recipes: ObjectDB.instance is null");
				return;
			}
			foreach (Recipe recipe in ObjectDB.instance.m_recipes)
			{
				if (!recipe.m_enabled)
				{
					Log.LogDebug("skipping " + ((Object)recipe).name + ": not enabled");
					continue;
				}
				if (!Object.op_Implicit((Object)(object)recipe.m_item))
				{
					Log.LogDebug("skipping " + ((Object)recipe).name + ": item is null");
					continue;
				}
				for (int i = 1; i <= recipe.m_item.m_itemData.m_shared.m_maxQuality; i++)
				{
					AddRecipeToItems(new RecipeInfo(recipe, i));
				}
				try
				{
					Indexing.OnIndexingRecipes?.Invoke(recipe);
				}
				catch (Exception data)
				{
					Log.LogError(data);
				}
			}
		}

		private static void IndexItemRecipes(List<GameObject> prefabs, Dictionary<string, PieceTable> pieceTables)
		{
			Incinerator val = default(Incinerator);
			Piece val2 = default(Piece);
			Container val3 = default(Container);
			Destructible val4 = default(Destructible);
			foreach (GameObject prefab in prefabs)
			{
				TryAddRecipeToItemsForEach<Smelter, ItemConversion>(prefab, (Func<Smelter, List<ItemConversion>>)((Smelter i) => i.m_conversion), (Func<Smelter, ItemConversion, RecipeInfo>)((Smelter s, ItemConversion i) => new RecipeInfo(i, s)));
				TryAddRecipeToItemsForEach<Fermenter, ItemConversion>(prefab, (Func<Fermenter, List<ItemConversion>>)((Fermenter i) => i.m_conversion), (Func<Fermenter, ItemConversion, RecipeInfo>)((Fermenter f, ItemConversion i) => new RecipeInfo(i, f)));
				TryAddRecipeToItemsForEach<CookingStation, ItemConversion>(prefab, (Func<CookingStation, List<ItemConversion>>)((CookingStation i) => i.m_conversion), (Func<CookingStation, ItemConversion, RecipeInfo>)((CookingStation c, ItemConversion i) => new RecipeInfo(i, c)));
				if (prefab.TryGetComponent<Incinerator>(ref val))
				{
					AddRecipeToItems(new RecipeInfo(val));
					foreach (IncineratorConversion conversion in val.m_conversions)
					{
						if (conversion.m_requireOnlyOneIngredient)
						{
							foreach (Requirement requirement in conversion.m_requirements)
							{
								AddRecipeToItems(new RecipeInfo(conversion, requirement, val));
							}
						}
						else
						{
							AddRecipeToItems(new RecipeInfo(conversion, val));
						}
					}
				}
				TryAddRecipeToItems<CharacterDrop>(prefab, (Func<CharacterDrop, RecipeInfo>)((CharacterDrop i) => new RecipeInfo(i)), (Func<CharacterDrop, bool>)((CharacterDrop i) => Object.op_Implicit((Object)(object)((Component)i).GetComponent<Character>())));
				TryAddRecipeToItems<Growup>(prefab, (Func<Growup, RecipeInfo>)((Growup i) => new RecipeInfo(i)), (Func<Growup, bool>)null);
				TryAddRecipeToItems<MineRock>(prefab, (Func<MineRock, RecipeInfo>)((MineRock i) => new RecipeInfo(prefab, i.m_dropItems)), (Func<MineRock, bool>)null);
				TryAddRecipeToItems<MineRock5>(prefab, (Func<MineRock5, RecipeInfo>)((MineRock5 i) => new RecipeInfo(prefab, i.m_dropItems)), (Func<MineRock5, bool>)null);
				TryAddRecipeToItems<DropOnDestroyed>(prefab, (Func<DropOnDestroyed, RecipeInfo>)((DropOnDestroyed i) => new RecipeInfo(prefab, i.m_dropWhenDestroyed)), (Func<DropOnDestroyed, bool>)null);
				prefab.TryGetComponent<Piece>(ref val2);
				if (Object.op_Implicit((Object)(object)val2) && prefab.TryGetComponent<Container>(ref val3) && val3.m_defaultItems.m_drops.Count > 0)
				{
					AddRecipeToItems(new RecipeInfo(((Component)val3).gameObject, val3.m_defaultItems));
				}
				TryAddRecipeToItems<Pickable>(prefab, (Func<Pickable, RecipeInfo>)((Pickable i) => new RecipeInfo(prefab, i)), (Func<Pickable, bool>)null);
				TryAddRecipeToItems<SpawnArea>(prefab, (Func<SpawnArea, RecipeInfo>)((SpawnArea i) => new RecipeInfo(i)), (Func<SpawnArea, bool>)null);
				if (prefab.TryGetComponent<Destructible>(ref val4))
				{
					if (Object.op_Implicit((Object)(object)val4.m_spawnWhenDestroyed) && GetItem(((Object)val4.m_spawnWhenDestroyed).name) != null)
					{
						AddRecipeToItems(new RecipeInfo(val4));
					}
					else if (((Behaviour)val4).enabled && !Object.op_Implicit((Object)(object)prefab.GetComponent<DropOnDestroyed>()) && !Object.op_Implicit((Object)(object)prefab.GetComponent<Plant>()))
					{
						DisableItem(((Object)prefab).name, "destructible.m_spawnWhenDestroyed is null or not indexed");
					}
				}
				TryAddRecipeToItems<TreeBase>(prefab, (Func<TreeBase, RecipeInfo>)((TreeBase i) => new RecipeInfo(i)), (Func<TreeBase, bool>)null);
				TryAddRecipeToItemsForEach<Trader, TradeItem>(prefab, (Func<Trader, List<TradeItem>>)((Trader i) => i.m_items), (Func<Trader, TradeItem, RecipeInfo>)((Trader t, TradeItem i) => new RecipeInfo(t, i)));
				try
				{
					Indexing.OnIndexingItemRecipes?.Invoke(prefab);
				}
				catch (Exception data)
				{
					Log.LogError(data);
				}
			}
			Piece val5 = default(Piece);
			foreach (KeyValuePair<string, PieceTable> pieceTable in pieceTables)
			{
				if (!Object.op_Implicit((Object)(object)pieceTable.Value))
				{
					Log.LogDebug("IndexItemRecipes: pieceTable " + pieceTable.Key + " is null");
					continue;
				}
				foreach (GameObject piece in pieceTable.Value.m_pieces)
				{
					if (!Object.op_Implicit((Object)(object)piece))
					{
						Log.LogDebug("IndexItemRecipes: prefab in pieceTables " + pieceTable.Key + " is null");
					}
					else if (piece.TryGetComponent<Piece>(ref val5) && !val5.m_repairPiece)
					{
						AddRecipeToItems(new RecipeInfo(piece, val5, GetItem(pieceTable.Key)));
					}
				}
			}
		}

		private static void TryAddItem<T>(GameObject target, Func<T, string> getName, string fallbackLocalizedName, ItemType itemType, Func<T, string> getDescription = null, Func<T, Sprite> getIcon = null) where T : Component
		{
			T arg = default(T);
			if (!target.TryGetComponent<T>(ref arg))
			{
				return;
			}
			try
			{
				string description = getDescription?.Invoke(arg) ?? string.Empty;
				Sprite icon = getIcon?.Invoke(arg);
				string text = getName(arg);
				if (string.IsNullOrEmpty(text))
				{
					text = fallbackLocalizedName;
				}
				Item item = new Item(((Object)target).name, text, description, icon, itemType, target);
				AddItem(item);
			}
			catch (Exception ex)
			{
				Log.LogError(((Object)target).name + Environment.NewLine + ex?.ToString() + Environment.NewLine);
			}
		}

		private static void TryAddRecipeToItems<T>(GameObject target, Func<T, RecipeInfo> getRecipe, Func<T, bool> check = null) where T : Component
		{
			T arg = default(T);
			if (target.TryGetComponent<T>(ref arg) && (check == null || check(arg)))
			{
				RecipeInfo recipeInfo;
				try
				{
					recipeInfo = getRecipe(arg);
				}
				catch (Exception ex)
				{
					Log.LogError(((Object)target).name + Environment.NewLine + ex?.ToString() + Environment.NewLine);
					return;
				}
				AddRecipeToItems(recipeInfo);
			}
		}

		private static void TryAddRecipeToItemsForEach<T1, T2>(GameObject target, Func<T1, List<T2>> getArray, Func<T1, T2, RecipeInfo> getRecipe) where T1 : Component
		{
			T1 val = default(T1);
			if (!target.TryGetComponent<T1>(ref val))
			{
				return;
			}
			foreach (T2 item in getArray(val))
			{
				try
				{
					AddRecipeToItems(getRecipe(val, item));
				}
				catch (Exception ex)
				{
					Log.LogError(((Object)target).name + Environment.NewLine + ex?.ToString() + Environment.NewLine);
				}
			}
		}

		public static bool HasIndexed()
		{
			return Items.Count > 0;
		}

		public static void DisableItem(string name, string context)
		{
			Item item = GetItem(name);
			if (item != null)
			{
				item.isActive = false;
				Log.LogDebug("disabling " + name + ": " + context);
			}
		}

		public static void AddItem(Item item)
		{
			string key = CleanupName(item.internalName);
			if (!Items.ContainsKey(key))
			{
				Items[key] = item;
			}
			if (!string.IsNullOrEmpty(item.preLocalizeName) && (!ItemsByPreLocalizedName.TryGetValue(item.preLocalizeName, out var value) || IsSaplingItem(value)))
			{
				ItemsByPreLocalizedName[item.preLocalizeName] = item;
			}
			if (!string.IsNullOrEmpty(item.localizedName) && (!ItemsByLocalizedName.TryGetValue(item.localizedName, out var value2) || IsSaplingItem(value2)))
			{
				ItemsByLocalizedName[item.localizedName] = item;
			}
		}

		private static bool IsSaplingItem(Item item)
		{
			return item.internalName.StartsWith("sapling_") || Object.op_Implicit((Object)(object)item.prefab.GetComponent<Plant>());
		}

		public static void ItemObtainedInRecipe(string name, RecipeInfo recipeInfo)
		{
			Item item = GetItem(name);
			if (item != null)
			{
				item.result.Add(recipeInfo);
			}
			else
			{
				Log.LogDebug("cannot add recipe to obtaining, '" + CleanupName(name) + "' is not indexed");
			}
		}

		public static void ItemUsedInRecipe(string name, RecipeInfo recipeInfo)
		{
			Item item = GetItem(name);
			if (item != null)
			{
				item.ingredient.Add(recipeInfo);
			}
			else
			{
				Log.LogDebug("cannot add recipe to using, '" + CleanupName(name) + "' is not indexed");
			}
		}

		public static void AddRecipeToItems(RecipeInfo recipeInfo)
		{
			foreach (Part item in recipeInfo.Ingredients.SelectMany((KeyValuePair<Amount, List<Part>> i) => i.Value))
			{
				ItemUsedInRecipe(item.item.internalName, recipeInfo);
			}
			foreach (Part item2 in recipeInfo.Results.SelectMany((KeyValuePair<Amount, List<Part>> i) => i.Value))
			{
				ItemObtainedInRecipe(item2.item.internalName, recipeInfo);
			}
			foreach (Part station in recipeInfo.Stations)
			{
				ItemUsedInRecipe(station.item.internalName, recipeInfo);
			}
		}

		public static string CleanupName(string name)
		{
			return name.Replace("JVLmock_", "").ToLower();
		}

		[Obsolete]
		public static void SetModOfPrefab(string prefabName, BepInPlugin mod)
		{
			ModNames.SetModOfPrefab(prefabName, mod);
		}

		[Obsolete]
		public static BepInPlugin GetModByPrefabName(string name)
		{
			return ModNames.GetModByPrefabName(name);
		}

		public static IEnumerable<KeyValuePair<string, Item>> GetActiveItems()
		{
			return Items.Where((KeyValuePair<string, Item> i) => i.Value.isActive);
		}

		public static Item GetItem(string name)
		{
			if (Items.TryGetValue(CleanupName(name), out var value))
			{
				return value;
			}
			if (ItemsByPreLocalizedName.TryGetValue(name, out value))
			{
				return value;
			}
			if (ItemsByLocalizedName.TryGetValue(name, out value))
			{
				return value;
			}
			return null;
		}

		public static void UpdateKnown()
		{
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return;
			}
			int num = Player.m_localPlayer.m_knownMaterial.Count + Player.m_localPlayer.m_knownRecipes.Count + Player.m_localPlayer.m_knownStations.Count + Player.m_localPlayer.m_knownTexts.Count + Player.m_localPlayer.m_knownBiome.Count;
			if (currentKnownCount == num && currentShowOnlyKnown == Plugin.ShowOnlyKnown)
			{
				return;
			}
			currentKnownCount = num;
			currentShowOnlyKnown = Plugin.ShowOnlyKnown;
			foreach (Item value in Items.Values)
			{
				value.UpdateSelfKnown();
			}
			foreach (RecipeInfo recipe in RecipeInfo.Recipes)
			{
				recipe.UpdateKnown();
			}
			foreach (Item value2 in Items.Values)
			{
				value2.UpdateKnown();
			}
			Indexing.AfterUpdateKnownItems?.Invoke();
		}
	}
	public class Item
	{
		public readonly string internalName;

		public readonly string preLocalizeName;

		public string localizedName;

		public readonly string description;

		public string localizedDescription;

		public readonly GameObject prefab;

		public readonly bool isOnBlacklist;

		public readonly ItemType itemType;

		public readonly BepInPlugin mod;

		public bool isActive = true;

		public int maxQuality;

		public bool isFavorite;

		private bool isKnown;

		private readonly Dictionary<int, string> tooltipsCache = new Dictionary<int, string>();

		public readonly HashSet<RecipeInfo> result = new HashSet<RecipeInfo>();

		public readonly HashSet<RecipeInfo> ingredient = new HashSet<RecipeInfo>();

		public readonly ComponentEvent onFavoriteChanged = new ComponentEvent();

		public readonly ComponentEvent onKnownChanged = new ComponentEvent();

		private Sprite icon;

		public bool IsKnown => isKnown || !Plugin.ShowOnlyKnown;

		public bool IsSelfKnown { get; private set; }

		public static event Action OnAnyFavoriteChanged;

		public Item(string name, string preLocalizeName, string description, Sprite icon, ItemType itemType, GameObject prefab, int maxQuality = 1)
		{
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Expected O, but got Unknown
			internalName = name;
			this.preLocalizeName = preLocalizeName ?? "";
			this.description = description;
			this.prefab = prefab;
			this.itemType = itemType;
			this.maxQuality = maxQuality;
			isOnBlacklist = Plugin.IsItemBlacklisted(this);
			Plugin.showModTooltip.SettingChanged += ClearTooltipCache;
			Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(UpdateLocalizedName));
			UpdateLocalizedName();
			if (Object.op_Implicit((Object)(object)prefab))
			{
				mod = ModNames.GetModByPrefabName(((Object)prefab).name);
			}
			if (Object.op_Implicit((Object)(object)icon))
			{
				SetIcon(icon);
			}
			else if (Object.op_Implicit((Object)(object)prefab))
			{
				RenderRequest val = new RenderRequest(prefab)
				{
					Rotation = RenderManager.IsometricRotation,
					TargetPlugin = mod,
					UseCache = true
				};
				SetIcon(RenderManager.Instance.Render(val));
			}
		}

		~Item()
		{
			Plugin.showModTooltip.SettingChanged -= ClearTooltipCache;
			Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(UpdateLocalizedName));
		}

		public string GetNameContext()
		{
			return internalName + Environment.NewLine + GetModName();
		}

		public string GetDescription()
		{
			return description;
		}

		public string GetTooltip(int quality)
		{
			if (tooltipsCache.TryGetValue(quality, out var value))
			{
				return value;
			}
			value = GenerateTooltip(quality);
			tooltipsCache.Add(quality, value);
			return value;
		}

		private void UpdateLocalizedName()
		{
			localizedName = Localization.instance.Localize(preLocalizeName);
			localizedDescription = Localization.instance.Localize(description);
		}

		private void ClearTooltipCache(object sender, EventArgs e)
		{
			tooltipsCache.Clear();
		}

		private string GenerateTooltip(int quality)
		{
			ItemDrop val = default(ItemDrop);
			if (Object.op_Implicit((Object)(object)prefab) && prefab.TryGetComponent<ItemDrop>(ref val))
			{
				if (!Object.op_Implicit((Object)(object)val.m_itemData.m_dropPrefab))
				{
					val.m_itemData.m_dropPrefab = prefab;
				}
				try
				{
					return ItemData.GetTooltip(val.m_itemData, quality, true, (float)Game.m_worldLevel, -1);
				}
				catch (Exception arg)
				{
					Log.LogWarning($"Failed to generate tooltip for '{internalName}': {arg}");
					return string.Empty;
				}
			}
			return description.TrimEnd(Array.Empty<char>()) + GetTooltipModName();
		}

		public string GetTooltipModName()
		{
			if (!Plugin.showModTooltip.Value)
			{
				return string.Empty;
			}
			string text = "orange";
			if (Chainloader.PluginInfos.ContainsKey("randyknapp.mods.epicloot"))
			{
				text = "#ADD8E6FF";
			}
			return "\n\n<color=" + text + ">" + GetModName() + "</color>";
		}

		public void SetIcon(Sprite sprite)
		{
			if (!Object.op_Implicit((Object)(object)icon))
			{
				icon = sprite;
			}
		}

		public Sprite GetIcon()
		{
			return Object.op_Implicit((Object)(object)icon) ? icon : Plugin.Instance.noIconSprite;
		}

		public string GetPrimaryName()
		{
			return (localizedName.Length > 0) ? localizedName : internalName;
		}

		public string GetModName()
		{
			return (mod != null) ? mod.Name : "Valheim";
		}

		public string PrintItem()
		{
			if (string.IsNullOrEmpty(internalName))
			{
				return " -- invalid item -- ";
			}
			string text = GetDescription().Replace('\n', ' ');
			string text2 = itemType.ToString();
			string text3 = "Internal Name: " + internalName + ", Localized Name: " + GetPrimaryName() + ", Item Type: " + text2;
			if (!string.IsNullOrEmpty(text))
			{
				text3 = text3 + ", Description: " + text;
			}
			if (!string.IsNullOrEmpty(GetModName()))
			{
				text3 = text3 + ", Source Mod: " + GetModName();
			}
			return text3;
		}

		public string PrintItemCSV(string separator)
		{
			if (string.IsNullOrEmpty(internalName))
			{
				return " -- invalid item -- ";
			}
			string text = GetDescription().Replace('\n', ' ');
			return internalName + separator + GetPrimaryName() + separator + $"{itemType}{separator}" + text + separator + GetModName();
		}

		public static string PrintCSVHeader(string separator)
		{
			return "Internal Name" + separator + "Localized Name" + separator + "Item Type" + separator + "Description" + separator + "Source Mod";
		}

		public void UpdateFavorite(bool favorite)
		{
			isFavorite = favorite;
			onFavoriteChanged.Invoke();
			Item.OnAnyFavoriteChanged?.Invoke();
		}

		public void UpdateSelfKnown()
		{
			isKnown = false;
			if (!Plugin.ShowOnlyKnown)
			{
				IsSelfKnown = true;
			}
			else
			{
				IsSelfKnown = Player.m_localPlayer.m_knownMaterial.Contains(preLocalizeName) || Player.m_localPlayer.m_knownStations.ContainsKey(preLocalizeName);
			}
		}

		public void UpdateKnown()
		{
			if (IsSelfKnown)
			{
				isKnown = true;
				onKnownChanged.Invoke();
			}
			else if (result.Any((RecipeInfo recipe) => recipe.IsSelfKnown) || ingredient.Any((RecipeInfo recipe) => recipe.IsSelfKnown))
			{
				isKnown = true;
				onKnownChanged.Invoke();
			}
			else
			{
				isKnown = false;
				onKnownChanged.Invoke();
			}
		}

		public IEnumerable<Item> GetStations()
		{
			List<Item> list = new List<Item>();
			list.AddRange(ingredient.SelectMany((RecipeInfo r) => r.GetStationItems()));
			list.AddRange(result.SelectMany((RecipeInfo r) => r.GetStationItems()));
			return list.Distinct().ToList();
		}
	}
	public enum ItemType
	{
		Undefined,
		Creature,
		Piece,
		Item,
		Food,
		Armor,
		Weapon
	}
	public class ItemTypeHelper
	{
		public static ItemType GetItemType(ItemData itemData)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected I4, but got Unknown
			ItemType itemType = itemData.m_shared.m_itemType;
			ItemType val = itemType;
			switch (val - 2)
			{
			case 0:
				return ItemType.Food;
			case 4:
			case 5:
			case 9:
			case 10:
			case 15:
				return ItemType.Armor;
			case 1:
			case 2:
			case 3:
			case 12:
			case 13:
			case 20:
				return ItemType.Weapon;
			default:
				return ItemType.Item;
			}
		}
	}
	public class ModNames
	{
		private static readonly Dictionary<string, BepInPlugin> SourceMod = new Dictionary<string, BepInPlugin>();

		public static void IndexModNames()
		{
			foreach (IModPrefab prefab in ModQuery.GetPrefabs())
			{
				SetModOfPrefab(((Object)prefab.Prefab).name, prefab.SourceMod);
			}
		}

		public static void SetModOfPrefab(string prefabName, BepInPlugin mod)
		{
			if (!SourceMod.ContainsKey(prefabName))
			{
				SourceMod[prefabName] = mod;
			}
		}

		public static BepInPlugin GetModByPrefabName(string name)
		{
			if (SourceMod.TryGetValue(name, out var value))
			{
				return value;
			}
			return null;
		}
	}
	public class Part
	{
		public readonly Item item;

		public readonly Amount amount;

		public readonly int quality;

		public Part(Item item, Amount amount, int quality)
		{
			this.item = item;
			this.amount = amount;
			this.quality = quality;
		}

		protected bool Equals(Part other)
		{
			return object.Equals(item, other.item) && amount.Equals(other.amount) && quality == other.quality;
		}

		public override bool Equals(object obj)
		{
			if (obj == null)
			{
				return false;
			}
			if (this == obj)
			{
				return true;
			}
			if (obj.GetType() != GetType())
			{
				return false;
			}
			return Equals((Part)obj);
		}

		public override int GetHashCode()
		{
			int num = ((item != null) ? item.GetHashCode() : 0);
			num = (num * 397) ^ amount.GetHashCode();
			return (num * 397) ^ quality;
		}
	}
	public class RecipeInfo
	{
		public Dictionary<Amount, List<Part>> Ingredients { get; private set; } = new Dictionary<Amount, List<Part>>();


		public Dictionary<Amount, List<Part>> Results { get; private set; } = new Dictionary<Amount, List<Part>>();


		public List<Part> Stations { get; private set; } = new List<Part>();


		public bool IsOnBlacklist { get; private set; }

		public static List<RecipeInfo> Recipes { get; private set; } = new List<RecipeInfo>();


		public bool IsSelfKnown { get; private set; }

		public float Width { get; private set; }

		public IEnumerable<Item> GetStationItems()
		{
			if (Stations.Count >= 1)
			{
				return new Item[1] { Stations[0].item ?? Plugin.Instance.noStation };
			}
			return new Item[1] { Plugin.Instance.noStation };
		}

		public void UpdateKnown()
		{
			IsSelfKnown = CalcSelfKnown();
		}

		private static bool AnySelfKnown(Dictionary<Amount, List<Part>> list)
		{
			return list.Where((KeyValuePair<Amount, List<Part>> pair) => pair.Key.max != 0 && pair.Value != null).Any((KeyValuePair<Amount, List<Part>> pair) => pair.Value.Any((Part part) => part.item.IsSelfKnown));
		}

		private static bool AllSelfKnown(Dictionary<Amount, List<Part>> list)
		{
			return list.Where((KeyValuePair<Amount, List<Part>> pair) => pair.Key.max != 0 && pair.Value != null).All((KeyValuePair<Amount, List<Part>> pair) => pair.Value.All((Part part) => part.item.IsSelfKnown));
		}

		private bool CalcSelfKnown()
		{
			if (!Plugin.ShowOnlyKnown)
			{
				return true;
			}
			if (Stations.Any((Part s) => !s.item.IsSelfKnown))
			{
				return false;
			}
			if (AllSelfKnown(Ingredients))
			{
				return true;
			}
			if (AllSelfKnown(Results))
			{
				return true;
			}
			return false;
		}

		public void AddIngredient(string name, Amount groupAmount, Amount count, int quality)
		{
			Item item = Indexing.GetItem(name);
			if (item != null)
			{
				if (!Ingredients.ContainsKey(groupAmount))
				{
					Ingredients.Add(groupAmount, new List<Part>());
				}
				Ingredients[groupAmount].Add(new Part(item, count, quality));
			}
		}

		public void AddIngredient<T>(T target, Amount groupAmount, Amount count, int quality, string context) where T : Object
		{
			if (Object.op_Implicit((Object)(object)target))
			{
				AddIngredient(((Object)target).name, groupAmount, count, quality);
			}
			else
			{
				Log.LogDebug($"cannot add ingredient to '{context}', item is null (uses amount {count})");
			}
		}

		public void AddResult(string name, Amount groupAmount, Amount count, int quality)
		{
			Item item = Indexing.GetItem(name);
			if (item != null)
			{
				if (!Results.ContainsKey(groupAmount))
				{
					Results.Add(groupAmount, new List<Part>());
				}
				Results[groupAmount].Add(new Part(item, count, quality));
			}
		}

		public void AddResult<T>(T target, Amount groupAmount, Amount count, int quality, string context) where T : Object
		{
			if (Object.op_Implicit((Object)(object)target))
			{
				AddResult(((Object)target).name, groupAmount, count, quality);
			}
			else
			{
				Log.LogDebug($"cannot add result to '{context}', item is null (uses amount {count})");
			}
		}

		public void AddStation(Item item, int level)
		{
			if (item != null)
			{
				Stations.Add(new Part(item, new Amount(1), level));
			}
		}

		public void AddStation(string name, int level)
		{
			AddStation(Indexing.GetItem(name), level);
		}

		public void AddStation<T>(T target, int level) where T : Object
		{
			if ((Object)(object)target != (Object)null)
			{
				AddStation(((Object)target).name, level);
			}
			else
			{
				Log.LogDebug("cannot set station: is null");
			}
		}

		public void CalculateIsOnBlacklist()
		{
			if (Ingredients.SelectMany((KeyValuePair<Amount, List<Part>> i) => i.Value).Any((Part i) => i.item.isOnBlacklist))
			{
				IsOnBlacklist = true;
			}
			else if (Results.SelectMany((KeyValuePair<Amount, List<Part>> i) => i.Value).Any((Part i) => i.item.isOnBlacklist))
			{
				IsOnBlacklist = true;
			}
		}

		public RecipeInfo()
		{
			Recipes.Add(this);
		}

		public RecipeInfo(Recipe recipe, int quality)
			: this()
		{
			if ((Object)(object)recipe.GetRequiredStation(quality) != (Object)null)
			{
				this.AddStation<CraftingStation>(recipe.GetRequiredStation(quality), recipe.GetRequiredStationLevel(quality));
			}
			else
			{
				AddStation(Plugin.Instance.handStation, 1);
			}
			this.AddResult<ItemDrop>(recipe.m_item, Amount.One, new Amount(recipe.m_amount), quality, ((Object)recipe).name);
			if (quality > 1)
			{
				this.AddIngredient<ItemDrop>(recipe.m_item, Amount.One, Amount.One, quality - 1, ((Object)recipe).name);
			}
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				Amount amount = new Amount(val.GetAmount(quality));
				if (!Amount.IsSameMinMax(amount, Amount.Zero))
				{
					this.AddIngredient<ItemDrop>(val.m_resItem, Amount.One, amount, 1, ((Object)recipe).name);
				}
			}
		}

		public RecipeInfo(ItemConversion conversion, Smelter smelter)
			: this()
		{
			this.AddStation<Smelter>(smelter, 1);
			this.AddIngredient<ItemDrop>(conversion.m_from, Amount.One, Amount.One, 1, ((Object)smelter).name);
			if (Object.op_Implicit((Object)(object)smelter.m_fuelItem))
			{
				this.AddIngredient<ItemDrop>(smelter.m_fuelItem, Amount.One, new Amount(smelter.m_fuelPerProduct), 1, ((Object)smelter).name);
			}
			this.AddResult<ItemDrop>(conversion.m_to, Amount.One, Amount.One, 1, ((Object)smelter).name);
		}

		public RecipeInfo(ItemConversion conversion, Fermenter fermenter)
			: this()
		{
			this.AddStation<Fermenter>(fermenter, 1);
			this.AddIngredient<ItemDrop>(conversion.m_from, Amount.One, Amount.One, 1, ((Object)fermenter).name);
			this.AddResult<ItemDrop>(conversion.m_to, Amount.One, new Amount(conversion.m_producedItems), 1, ((Object)fermenter).name);
		}

		public RecipeInfo(ItemConversion conversion, CookingStation cookingStation)
			: this()
		{
			this.AddStation<CookingStation>(cookingStation, 1);
			this.AddIngredient<ItemDrop>(conversion.m_from, Amount.One, Amount.One, 1, ((Object)cookingStation).name);
			this.AddResult<ItemDrop>(conversion.m_to, Amount.One, Amount.One, 1, ((Object)cookingStation).name);
		}

		public RecipeInfo(Incinerator incinerator)
			: this()
		{
			this.AddStation<Incinerator>(incinerator, 1);
			this.AddResult<ItemDrop>(incinerator.m_defaultResult, Amount.One, Amount.One, 1, ((Object)incinerator).name);
			AddIngredient("vnei_any_item", Amount.One, new Amount(incinerator.m_defaultCost), 1);
		}

		public RecipeInfo(IncineratorConversion conversion, Incinerator incinerator)
			: this()
		{
			this.AddStation<Incinerator>(incinerator, 1);
			this.AddResult<ItemDrop>(conversion.m_result, Amount.One, new Amount(conversion.m_resultAmount), 1, ((Object)incinerator).name);
			foreach (Requirement requirement in conversion.m_requirements)
			{
				this.AddIngredient<ItemDrop>(requirement.m_resItem, Amount.One, new Amount(requirement.m_amount), 1, ((Object)incinerator).name);
			}
		}

		public RecipeInfo(IncineratorConversion conversion, Requirement requirement, Incinerator incinerator)
			: this()
		{
			this.AddStation<Incinerator>(incinerator, 1);
			this.AddResult<ItemDrop>(conversion.m_result, Amount.One, new Amount(conversion.m_resultAmount), 1, ((Object)incinerator).name);
			this.AddIngredient<ItemDrop>(requirement.m_resItem, Amount.One, new Amount(requirement.m_amount), 1, ((Object)incinerator).name);
		}

		public RecipeInfo(CharacterDrop characterDrop)
			: this()
		{
			Character component = ((Component)characterDrop).GetComponent<Character>();
			this.AddIngredient<Character>(component, Amount.One, Amount.One, 1, ((Object)component).name);
			foreach (Drop drop in characterDrop.m_drops)
			{
				this.AddResult<GameObject>(count: new Amount(drop.m_amountMin, drop.m_amountMax, drop.m_chance), target: drop.m_prefab, groupAmount: Amount.One, quality: 1, context: ((Object)component).name);
			}
		}

		public RecipeInfo(GameObject prefab, Piece piece, Item crafter)
			: this()
		{
			Stations.Add(new Part(crafter, new Amount(1), 1));
			if (Object.op_Implicit((Object)(object)piece.m_craftingStation))
			{
				this.AddStation<CraftingStation>(piece.m_craftingStation, 1);
			}
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val in resources)
			{
				this.AddIngredient<ItemDrop>(val.m_resItem, Amount.One, new Amount(val.m_amount), 1, ((Object)prefab).name);
			}
			Plant val2 = default(Plant);
			if (((Component)piece).TryGetComponent<Plant>(ref val2))
			{
				GameObject[] grownPrefabs = val2.m_grownPrefabs;
				if (grownPrefabs != null && grownPrefabs.Length >= 1)
				{
					GameObject[] grownPrefabs2 = val2.m_grownPrefabs;
					foreach (GameObject target in grownPrefabs2)
					{
						this.AddResult<GameObject>(target, Amount.One, new Amount(1, 1f / (float)val2.m_grownPrefabs.Length), 1, ((Object)prefab).name);
					}
					Indexing.DisableItem(((Object)prefab).name, "Plant has grownPrefabs");
					return;
				}
			}
			this.AddResult<GameObject>(prefab, Amount.One, Amount.One, 1, ((Object)prefab).name);
		}

		public RecipeInfo(GameObject prefab, Pickable pickable)
			: this()
		{
			this.AddIngredient<GameObject>(prefab, Amount.One, Amount.One, 1, ((Object)prefab).name);
			this.AddResult<GameObject>(pickable.m_itemPrefab, Amount.One, new Amount(pickable.m_amount), 1, ((Object)pickable).name);
			if (pickable.m_extraDrops != null && pickable.m_extraDrops.m_drops.Count > 0)
			{
				AddDropTable(prefab, pickable.m_extraDrops);
			}
			CombineGroupAmounts(Results);
		}

		public RecipeInfo(GameObject from, DropTable dropTable)
			: this()
		{
			this.AddIngredient<GameObject>(from, Amount.One, Amount.One, 1, ((Object)from).name);
			AddDropTable(from, dropTable);
			if (dropTable.m_drops != null && dropTable.m_drops.Count == 0)
			{
				Indexing.DisableItem(((Object)from).name, "No drops in DropTable");
			}
		}

		public RecipeInfo(SpawnArea spawnArea)
			: this()
		{
			this.AddIngredient<SpawnArea>(spawnArea, Amount.One, Amount.One, 1, ((Object)spawnArea).name);
			foreach (SpawnData prefab in spawnArea.m_prefabs)
			{
				this.AddResult<GameObject>(prefab.m_prefab, Amount.One, new Amount(1, 1f / (float)spawnArea.m_prefabs.Count), 1, ((Object)spawnArea).name);
			}
		}

		public RecipeInfo(Destructible spawnArea)
			: this()
		{
			this.AddIngredient<Destructible>(spawnArea, Amount.One, Amount.One, 1, ((Object)spawnArea).name);
			this.AddResult<GameObject>(spawnArea.m_spawnWhenDestroyed, Amount.One, Amount.One, 1, ((Object)spawnArea).name);
		}

		private void AddTreeLog(GameObject logPrefab, int depth, string name, int treeCount)
		{
			TreeLog val = default(TreeLog);
			if (depth > 50)
			{
				Log.LogWarning("TreeLog is recursively " + name);
			}
			else if (Object.op_Implicit((Object)(object)logPrefab) && logPrefab.TryGetComponent<TreeLog>(ref val))
			{
				if (val.m_subLogPoints != null)
				{
					AddTreeLog(val.m_subLogPrefab, depth + 1, name, val.m_subLogPoints.Length);
				}
				AddDropTable(((Component)val).gameObject, val.m_dropWhenDestroyed, treeCount);
				Indexing.DisableItem(((Object)((Component)val).gameObject).name, name);
			}
		}

		public RecipeInfo(TreeBase treeBase)
			: this()
		{
			this.AddIngredient<TreeBase>(treeBase, Amount.One, Amount.One, 1, ((Object)treeBase).name);
			AddTreeLog(treeBase.m_logPrefab, 0, ((Object)treeBase).name, 1);
			AddDropTable(((Component)treeBase).gameObject, treeBase.m_dropWhenDestroyed);
			DropOnDestroyed val = default(DropOnDestroyed);
			if (Object.op_Implicit((Object)(object)treeBase.m_stubPrefab) && treeBase.m_stubPrefab.TryGetComponent<DropOnDestroyed>(ref val))
			{
				AddDropTable(((Component)treeBase).gameObject, val.m_dropWhenDestroyed);
				Indexing.DisableItem(((Object)treeBase.m_stubPrefab).name, ((Object)((Component)treeBase).gameObject).name);
			}
		}

		public RecipeInfo(Trader trader, TradeItem tradeItem)
			: this()
		{
			this.AddStation<Trader>(trader, 1);
			this.AddIngredient<ItemDrop>(StoreGui.instance.m_coinPrefab, Amount.One, new Amount(tradeItem.m_price), 1, ((Object)trader).name);
			this.AddResult<ItemDrop>(tradeItem.m_prefab, Amount.One, new Amount(tradeItem.m_stack), 1, ((Object)trader).name);
		}

		public RecipeInfo(Growup growup)
			: this()
		{
			this.AddIngredient<GameObject>(((Component)growup).gameObject, Amount.One, Amount.One, 1, "growup base ");
			this.AddResult<GameObject>(growup.m_grownPrefab, Amount.One, Amount.One, 1, "growup result");
		}

		private void AddDropTable(GameObject from, DropTable dropTable, int groupMultiplier = 1)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			Amount groupAmount = new Amount(dropTable.m_dropMin * groupMultiplier, dropTable.m_dropMax * groupMultiplier, dropTable.m_dropChance);
			float num = dropTable.m_drops.Sum((DropData i) => i.m_weight);
			foreach (DropData drop in dropTable.m_drops)
			{
				float chance = ((num == 0f) ? 1f : (drop.m_weight / num));
				this.AddResult<GameObject>(drop.m_item, groupAmount, new Amount(drop.m_stackMin, drop.m_stackMax, chance), 1, ((Object)from).name);
			}
		}

		public bool IngredientsAndResultSame()
		{
			return Ingredients.SequenceEqual(Results);
		}

		public void CalculateWidth()
		{
			float num = 0f;
			num += (float)Mathf.Max(1, Stations.Count) * 50f;
			foreach (KeyValuePair<Amount, List<Part>> ingredient in Ingredients)
			{
				if (Ingredients.Count > 1 || ingredient.Key.min != 1 || ingredient.Key.max != 1 || Math.Abs(ingredient.Key.chance - 1f) > 0.01f)
				{
					num += 50f;
				}
				num += (float)ingredient.Value.Count * 50f;
			}
			foreach (KeyValuePair<Amount, List<Part>> result in Results)
			{
				if (Results.Count > 1 || result.Key.min != 1 || result.Key.max != 1 || Math.Abs(result.Key.chance - 1f) > 0.01f)
				{
					num += 50f;
				}
				num += (float)result.Value.Count * 50f;
			}
			Width = num;
		}

		private void CombineGroupAmounts(Dictionary<Amount, List<Part>> groups)
		{
			if (groups.Keys.Count > 1 && !groups.Keys.Any((Amount i) => Math.Abs(i.chance - groups.Keys.First().chance) > 0.001f) && groups.Values.Count > 1 && !groups.Values.Any((List<Part> i) => !i.SequenceEqual(groups.Values.First())))
			{
				Amount key = new Amount(groups.Keys.Sum((Amount i) => i.min), groups.Keys.Sum((Amount i) => i.max), groups.Keys.First().chance);
				List<Part> value = groups.Values.First();
				groups.Clear();
				groups.Add(key, value);
			}
		}

		public bool IsUpgrade(out Item item)
		{
			List<Part> list = (from i in Ingredients.Values.SelectMany((List<Part> i) => i)
				where i.item.maxQuality > 1
				select i).ToList();
			List<Part> list2 = (from i in Results.Values.SelectMany((List<Part> i) => i)
				where i.item.maxQuality > 1
				select i).ToList();
			if (list.Count != 1 || list2.Count != 1)
			{
				item = null;
				return false;
			}
			Part part = list[0];
			Part part2 = list2[0];
			item = part.item;
			return part.item == part2.item && part.quality < part2.quality;
		}
	}
}
namespace VNEI.Logic.Compatibility
{
	public class PlanBuild
	{
		private static List<string> disableToolPieces = new List<string>
		{
			"piece_bpcapture", "piece_bpselectadd", "piece_bpselectremove", "piece_bpselectedit", "piece_bpsnappoint", "piece_bpcenterpoint", "piece_bpterrainmod", "piece_bpdelete", "piece_bpterrain", "piece_bpobjects",
			"piece_bppaint", "piece_plan_delete"
		};

		public static void Init()
		{
			if (!Chainloader.PluginInfos.ContainsKey("marcopogo.PlanBuild"))
			{
				return;
			}
			Indexing.OnDisableItems += delegate(GameObject prefab)
			{
				if (Object.op_Implicit((Object)(object)prefab.GetComponent("PlanBuild.Plans.PlanPiece")))
				{
					Indexing.DisableItem(((Object)prefab).name, "is PlanBuild PlanPiece");
				}
			};
			Indexing.AfterDisableItems += delegate
			{
				foreach (string disableToolPiece in disableToolPieces)
				{
					Indexing.DisableItem(disableToolPiece, "is PlanBuild tool piece");
				}
			};
		}
	}
}
namespace VNEI.UI
{
	public class SelectUITest : MonoBehaviour
	{
		public class ToggleUIConsoleCommand : ConsoleCommand
		{
			public override string Name => "vnei_toggle_select_test";

			public override string Help => "Shows the selection test";

			public override void Run(string[] args)
			{
				if (!Object.op_Implicit((Object)(object)instance))
				{
					Create();
					return;
				}
				bool flag = ((UIBehaviour)instance.background).IsActive();
				((Component)instance.background).gameObject.SetActive(!flag);
				((Component)instance.root).gameObject.SetActive(!flag);
			}
		}

		private static SelectUITest instance;

		public RectTransform root;

		public Image background;

		public Text text;

		public Button select;

		private SelectUI selection;

		public static void Create()
		{
			GameObject val = Plugin.AssetBundle.LoadAsset<GameObject>("SelectUITest");
			GameObject val2 = Object.Instantiate<GameObject>(val, GUIManager.CustomGUIFront.transform);
			instance = val2.GetComponent<SelectUITest>();
			((Component)instance.background).gameObject.AddComponent<DragWindowCntrl>();
			GUIManager.Instance.ApplyWoodpanelStyle(((Component)instance.background).transform);
			GUIManager.Instance.ApplyTextStyle(instance.text, 16);
			GUIManager.Instance.ApplyButtonStyle(instance.select, 16);
		}

		private void Start()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			((UnityEvent)select.onClick).AddListener((UnityAction)delegate
			{
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				if (!Object.op_Implicit((Object)(object)selection))
				{
					Action<string> onSelect = delegate(string prefabName)
					{
						text.text = prefabName;
					};
					selection = SelectUI.CreateSelection(GUIManager.CustomGUIFront.transform, onSelect, Vector2.zero, new ItemType[2]
					{
						ItemType.Piece,
						ItemType.Item
					});
				}
			});
		}

		private void Update()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			((Transform)root).position = ((Component)background).transform.position;
		}
	}
	[DefaultExecutionOrder(5)]
	public class BaseUI : MonoBehaviour
	{
		[Header("Local References")]
		public RectTransform root;

		public RectTransform dragHandler;

		public RectTransform lastViewItemsParent;

		public SearchUI searchUi;

		public RecipeUI recipeUi;

		[Header("Prefabs")]
		public GameObject itemPrefab;

		public GameObject rowPrefab;

		public GameObject recipeDroppedTextPrefab;

		public GameObject arrowPrefab;

		private List<DisplayItem> lastViewedDisplayItems = new List<DisplayItem>();

		private List<Item> lastViewedItems = new List<Item>();

		public const string HistoryQueueKey = "VNEI_History";

		private bool sizeDirty;

		[HideInInspector]
		public List<ItemTypeToggle> typeToggles = new List<ItemTypeToggle>();

		[HideInInspector]
		public FavoriteTypeToggle favoriteToggle;

		public Action typeToggleChange;

		private bool canBeHidden;

		private bool usePluginSize = true;

		public bool BlockInput { get; private set; }

		public int ItemSizeX { get; private set; }

		public int ItemSizeY { get; private set; }

		public event Action RebuildedSize;

		public static BaseUI CreateBaseUI(bool canBeHidden = false, bool scaleWithPluginSetting = false, bool draggable = true)
		{
			GameObject val = Object.Instantiate<GameObject>(Plugin.Instance.vneiUI, Object.op_Implicit((Object)(object)GUIManager.CustomGUIFront) ? GUIManager.CustomGUIFront.transform : null);
			BaseUI component = val.GetComponent<BaseUI>();
			component.canBeHidden = canBeHidden;
			component.usePluginSize = scaleWithPluginSetting;
			if (component.usePluginSize)
			{
				Plugin.columnCount.SettingChanged += component.RebuildSizeEvent;
				Plugin.rowCount.SettingChanged += component.RebuildSizeEvent;
			}
			if (draggable)
			{
				((Component)component.dragHandler).gameObject.AddComponent<DragWindowCntrl>();
			}
			return component;
		}

		private void Awake()
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			BlockInput = false;
			ShowSearch(trackHistory: false);
			ShowSearch(trackHistory: true);
			Styling.ApplyAllComponents((Transform)(object)root);
			Styling.ApplyAllComponents(Plugin.Instance.craftingStationTemplate.transform);
			GUIManager.Instance.ApplyWoodpanelStyle((Transform)(object)dragHandler);
			recipeUi.OnSetItem += AddItemToLastViewedQueue;
			Plugin.OnOpenHotkey += UpdateVisibility;
			Plugin.transparency.SettingChanged += UpdateTransparency;
			Plugin.showRecentItems.SettingChanged += UpdateLastViewItemsParent;
			if (Object.op_Implicit((Object)(object)InventoryGui.instance))
			{
				((Component)this).transform.SetParent((Transform)(object)InventoryGui.instance.m_player);
				((RectTransform)((Component)this).transform).anchoredPosition = new Vector2(665f, -45f);
				UpdateVisibility();
			}
			else
			{
				SetVisibility(visible: false);
			}
			UpdateTransparency(null, EventArgs.Empty);
			UpdateLastViewItemsParent(null, EventArgs.Empty);
			RebuildSize();
			RebuildDisplayItemRows();
		}

		private void RebuildSizeEvent(object sender, EventArgs e)
		{
			sizeDirty = true;
		}

		private void RebuildSize()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: 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)
			if (usePluginSize)
			{
				ItemSizeX = Plugin.columnCount.Value;
				ItemSizeY = Plugin.rowCount.Value;
			}
			root.sizeDelta = new Vector2((float)ItemSizeX * 50f + 10f, (float)ItemSizeY * 50f + 100f);
			dragHandler.sizeDelta = root.sizeDelta + new Vector2(10f, 10f);
			this.RebuildedSize?.Invoke();
		}

		private void Update()
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			if (searchUi.searchField.isFocused && !BlockInput)
			{
				GUIManager.BlockInput(true);
				BlockInput = true;
			}
			else if (!searchUi.searchField.isFocused && BlockInput)
			{
				GUIManager.BlockInput(false);
				BlockInput = false;
			}
			if (sizeDirty)
			{
				sizeDirty = false;
				RebuildSize();
				RebuildDisplayItemRows();
			}
			if (Plugin.goForwardHotkey.Value.IsKeyDown())
			{
				NextView();
			}
			else if (Plugin.goBackHotkey.Value.IsKeyDown())
			{
				PreviousView();
			}
		}

		public void PreviousView()
		{
			UndoManager.Instance.Undo("VNEI_History");
		}

		public void NextView()
		{
			if (UndoManager.Instance.GetQueue("VNEI_History").GetIndex() == -1)
			{
				UndoManager.Instance.Redo("VNEI_History");
			}
			UndoManager.Instance.Redo("VNEI_History");
		}

		private void RebuildDisplayItemRows()
		{
			RebuildDisplayItemRow(lastViewedDisplayItems, lastViewItemsParent);
			UpdateDisplayItemRow(lastViewedDisplayItems, lastViewedItems);
		}

		private void RebuildDisplayItemRow(List<DisplayItem> displayItems, RectTransform parent)
		{
			//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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			foreach (DisplayItem displayItem in displayItems)
			{
				Object.Destroy((Object)(object)((Component)displayItem).gameObject);
			}
			displayItems.Clear();
			if (!Object.op_Implicit((Object)(object)((Component)this).GetComponent<SelectUI>()))
			{
				int num = Mathf.Max(ItemSizeX - 5, 0);
				parent.anchoredPosition = new Vector2((float)num * 50f / 2f + 5f, parent.anchoredPosition.y);
				parent.sizeDelta = new Vector2((float)num * 50f, 50f);
				for (int i = 0; i < num; i++)
				{
					DisplayItem item = SpawnDisplayItem(new Vector2(25f + (float)(i * 50), -25f), (Transform)(object)parent);
					displayItems.Add(item);
				}
			}
		}

		private DisplayItem SpawnDisplayItem(Vector2 anchoredPosition, Transform parent)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(itemPrefab, parent);
			((RectTransform)val.transform).anchoredPosition = anchoredPosition;
			DisplayItem component = val.GetComponent<DisplayItem>();
			component.Init(this);
			return component;
		}

		private void LateUpdate()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			root.anchoredPosition = dragHandler.anchoredPosition;
		}

		public void ShowSearch(bool trackHistory)
		{
			SwitchView(delegate
			{
				((Component)recipeUi).gameObject.SetActive(false);
				((Component)searchUi).gameObject.SetActive(true);
			}, trackHistory);
		}

		public void ShowRecipe(Item item, bool trackHistory)
		{
			SwitchView(delegate
			{
				recipeUi.SetItem(item);
				((Component)recipeUi).gameObject.SetActive(true);
				((Component)searchUi).gameObject.SetActive(false);
			}, trackHistory && item != recipeUi.GetItem());
		}

		private void SwitchView(Action changeView, bool trackHistory)
		{
			if (trackHistory)
			{
				HistorySnapshot currentView = GetCurrentView();
				changeView?.Invoke();
				HistorySnapshot currentView2 = GetCurrentView();
				UndoManager.Instance.Add("VNEI_History", (IUndoAction)(object)new HistoryElement(currentView, currentView2));
			}
			else
			{
				changeView?.Invoke();
			}
		}

		private HistorySnapshot GetCurrentView()
		{
			if (((Component)recipeUi).gameObject.activeSelf)
			{
				return new HistorySnapshotRecipe(this, recipeUi.GetItem());
			}
			return new HistorySnapshotSearch(this);
		}

		private void AddItemToLastViewedQueue(Item item)
		{
			lastViewedItems.Insert(0, item);
			for (int i = 1; i < lastViewedItems.Count; i++)
			{
				if (lastViewedItems[i] == item)
				{
					lastViewedItems.RemoveAt(i);
				}
			}
			if (lastViewedItems.Count > 50)
			{
				lastViewedItems.RemoveAt(lastViewedItems.Count - 1);
			}
			UpdateDisplayItemRow(lastViewedDisplayItems, lastViewedItems);
		}

		public void RemoveItemFromLastViewedQueue(Item item)
		{
			lastViewedItems.Remove(item);
			UpdateDisplayItemRow(lastViewedDisplayItems, lastViewedItems);
		}

		private static void UpdateDisplayItemRow(List<DisplayItem> displayItems, List<Item> items)
		{
			for (int i = 0; i < displayItems.Count; i++)
			{
				if (i >= items.Count)
				{
					displayItems[i].SetItem(null, 1);
				}
				else
				{
					displayItems[i].SetItem(items[i], 1);
				}
			}
		}

		private void OnDestroy()
		{
			recipeUi.OnSetItem -= AddItemToLastViewedQueue;
			Plugin.OnOpenHotkey -= UpdateVisibility;
			Plugin.transparency.SettingChanged -= UpdateTransparency;
			Plugin.showRecentItems.SettingChanged -= UpdateLastViewItemsParent;
		}

		public void SetSize(bool usePluginSize, int itemsX, int itemsY)
		{
			this.usePluginSize = usePluginSize;
			ItemSizeX = itemsX;
			ItemSizeY = itemsY;
			RebuildSize();
			RebuildDisplayItemRows();
		}

		public void SetVisibility(bool visible)
		{
			((Component)root).gameObject.SetActive(visible);
			((Component)dragHandler).gameObject.SetActive(visible);
		}

		private void UpdateVisibility()
		{
			SetVisibility(!canBeHidden || Plugin.isUiOpen);
		}

		private void UpdateTransparency(object sender, EventArgs args)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			((Graphic)((Component)dragHandler).GetComponent<Image>()).color = new Color(1f, 1f, 1f, 1f - (float)Plugin.transparency.Value / 100f);
		}

		private void UpdateLastViewItemsParent(object sender, EventArgs args)
		{
			((Component)lastViewItemsParent).gameObject.SetActive(Plugin.showRecentItems.Value);
		}
	}
	public class CraftingStationElement : MonoBehaviour
	{
		public Image icon;

		public Button button;

		public UITooltip tooltip;

		[SerializeField]
		private Item station;

		public Item Station
		{
			get
			{
				return station;
			}
			set
			{
				station?.onKnownChanged.RemoveListener((Component)(object)this);
				station = value;
				station?.onKnownChanged.AddListener((Component)(object)this, UpdateIconAndTooltip);
			}
		}

		public void UpdateIconAndTooltip()
		{
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (Station != null)
			{
				if (Station.IsKnown)
				{
					((Graphic)icon).color = Color.white;
					icon.sprite = Station.GetIcon();
					tooltip.Set(Station.preLocalizeName, "", (RectTransform)null, default(Vector2));
				}
				else
				{
					((Graphic)icon).color = DisplayItem.unknownColor;
					icon.sprite = Plugin.Instance.noIconSprite;
					tooltip.Set("$vnei_unknown_item", "", (RectTransform)null, default(Vector2));
				}
			}
			else
			{
				icon.sprite = null;
				tooltip.Set("", "", (RectTransform)null, default(Vector2));
			}
		}

		private void OnDestroy()
		{
			Station?.onKnownChanged.RemoveListener((Component)(object)this);
		}
	}
	public class CraftingStationList : MonoBehaviour
	{
		public List<CraftingStationElement> stationElements = new List<CraftingStationElement>();

		private int currentPage;

		private float elementXSpace = 5f;

		[SerializeField]
		private Item activeStation;

		public Item ActiveStation
		{
			get
			{
				return activeStation;
			}
			private set
			{
				activeStation = value;
				UpdateButtons();
				this.OnStationChange?.Invoke();
			}
		}

		public event Action OnStationChange;

		public void SetStations(List<Item> stations)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Expected O, but got Unknown
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Expected O, but got Unknown
			ActiveStation = (stations.Contains(ActiveStation) ? ActiveStation : stations.FirstOrDefault());
			ClearStations();
			float num = ((RectTransform)Plugin.Instance.craftingStationTemplate.transform).sizeDelta.x + elementXSpace;
			Rect rect = ((RectTransform)((Component)this).transform).rect;
			float width = ((Rect)(ref rect)).width;
			int stationsPerPage = GetStationsPerPage();
			float num2 = (width - num * (float)stationsPerPage + num) / 2f;
			for (int i = 0; i < stations.Count; i++)
			{
				Item station = stations[i];
				CraftingStationElement component = Object.Instantiate<GameObject>(Plugin.Instance.craftingStationTemplate, ((Component)this).transform).GetComponent<CraftingStationElement>();
				stationElements.Add(component);
				((Object)component).name = station.internalName;
				component.Station = station;
				component.UpdateIconAndTooltip();
				((Component)component).gameObject.SetActive(i >= currentPage * stationsPerPage && i < (currentPage + 1) * stationsPerPage);
				RectTransform val = (RectTransform)((Component)component).transform;
				float num3 = num2 + num * (float)(i % stationsPerPage);
				val.anchoredPosition = new Vector2(num3, val.anchoredPosition.y);
				((UnityEvent)component.button.onClick).AddListener((UnityAction)delegate
				{
					ActiveStation = station;
				});
			}
			UpdateButtons();
		}

		private void UpdateButtons()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			int stationsPerPage = GetStationsPerPage();
			for (int i = 0; i < stationElements.Count; i++)
			{
				CraftingStationElement craftingStationElement = stationElements[i];
				ColorBlock colors = ((Selectable)craftingStationElement.button).colors;
				((ColorBlock)(ref colors)).normalColor = ((ActiveStation == craftingStationElement.Station) ? Color.white : Color.gray);
				((ColorBlock)(ref colors)).highlightedColor = Color.white;
				((ColorBlock)(ref colors)).pressedColor = Color.white;
				((ColorBlock)(ref colors)).selectedColor = Color.white;
				((Selectable)craftingStationElement.button).colors = colors;
				((Graphic)craftingStationElement.icon).color = ((ActiveStation == craftingStationElement.Station) ? Color.white : Color.gray);
				((Component)craftingStationElement).gameObject.SetActive(i >= currentPage * stationsPerPage && i < (currentPage + 1) * stationsPerPage);
			}
		}

		private void ClearStations()
		{
			foreach (CraftingStationElement stationElement in stationElements)
			{
				Object.Destroy((Object)(object)((Component)stationElement).gameObject);
			}
			stationElements.Clear();
			currentPage = 0;
		}

		public IEnumerable<RecipeInfo> FilterRecipes(IEnumerable<RecipeInfo> recipes)
		{
			if