Decompiled source of WorkbenchesPlus v1.0.2

plugins/WorkbenchesPlus/WorkbenchesPlus.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("WorkbenchesPlus")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2+279192a2c58228e53c616183abeb60b04b7ecdc5")]
[assembly: AssemblyProduct("Workbenches+")]
[assembly: AssemblyTitle("WorkbenchesPlus")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.2.0")]
[module: UnverifiableCode]
namespace WorkbenchesPlus;

public class ModConfig
{
	public ConfigEntry<bool> EnableMod { get; }

	public ConfigEntry<bool> EnableCategories { get; }

	public ConfigEntry<bool> EnableCraftableFirst { get; }

	public ConfigEntry<bool> EnableArmorSetGrouping { get; }

	public ConfigEntry<bool> EnableWeaponSetGrouping { get; }

	public ConfigEntry<bool> EnableToolSetGrouping { get; }

	public ConfigEntry<bool> EnableShieldSetGrouping { get; }

	public ConfigEntry<bool> EnableMaterialSectionHeaders { get; }

	public ConfigEntry<string> SortMode { get; }

	public ConfigEntry<string> ArmorPieceOrder { get; }

	public ConfigEntry<string> WeaponPieceOrder { get; }

	public ConfigEntry<string> ToolPieceOrder { get; }

	public ConfigEntry<string> ShieldPieceOrder { get; }

	public ConfigEntry<bool> GroupModdedArmorSets { get; }

	public ConfigEntry<bool> GroupModdedWeaponSets { get; }

	public ConfigEntry<bool> RefreshOnInventoryChange { get; }

	public ConfigEntry<bool> RefreshOnStationChange { get; }

	public ConfigEntry<bool> ShowCraftabilityIndicators { get; }

	public ConfigEntry<bool> EnableCraftMultiplier { get; }

	public ConfigEntry<int> MaxCraftMultiplier { get; }

	public ConfigEntry<bool> EnableDismantle { get; }

	public ConfigEntry<bool> DebugLogging { get; }

	public ModConfig(ConfigFile file)
	{
		EnableMod = file.Bind<bool>("1 - General", "EnableMod", true, "Master switch for Workbenches+.");
		EnableCategories = file.Bind<bool>("1 - General", "EnableCategories", true, "Show category chips above the crafting recipe list.");
		EnableCraftableFirst = file.Bind<bool>("2 - Sorting", "EnableCraftableFirst", true, "When SortMode is CraftableFirst / CategoryThenCraftable, prioritize craftable recipes.");
		EnableArmorSetGrouping = file.Bind<bool>("2 - Sorting", "EnableArmorSetGrouping", true, "Keep armor pieces from the same set next to each other.");
		EnableWeaponSetGrouping = file.Bind<bool>("2 - Sorting", "EnableWeaponSetGrouping", true, "Keep weapons of the same material together (Iron, BlackMetal, Bronze, …).");
		EnableToolSetGrouping = file.Bind<bool>("2 - Sorting", "EnableToolSetGrouping", true, "Keep tools of the same material together (Antler, Bronze, Iron, BlackMetal, …).");
		EnableShieldSetGrouping = file.Bind<bool>("2 - Sorting", "EnableShieldSetGrouping", true, "Keep shields of the same material together (Wood, Bronze, Iron, BlackMetal, …).");
		EnableMaterialSectionHeaders = file.Bind<bool>("2 - Sorting", "EnableMaterialSectionHeaders", true, "Show section labels between material groups (IRON, BRONZE, Arrows — left-aligned).");
		SortMode = file.Bind<string>("2 - Sorting", "SortMode", "CraftableFirst", "CraftableFirst | CategoryThenCraftable | Progression | Alphabetical | Vanilla");
		ArmorPieceOrder = file.Bind<string>("3 - Armor", "ArmorPieceOrder", "Chest,Helmet,Legs,Cape,Other", "Comma-separated piece order within an armor set.");
		GroupModdedArmorSets = file.Bind<bool>("3 - Armor", "GroupModdedArmorSets", true, "Apply set-name heuristics to non-vanilla armor prefabs.");
		WeaponPieceOrder = file.Bind<string>("3b - Weapons", "WeaponPieceOrder", "Arrow,Bolt,Knife,Sword,Mace,Axe,Battleaxe,Spear,Atgeir,Sledge,Bow,Crossbow,Club,Bomb,Pickaxe,Other", "Comma-separated weapon type order within a material group. Arrows first by default.");
		GroupModdedWeaponSets = file.Bind<bool>("3b - Weapons", "GroupModdedWeaponSets", true, "Apply material heuristics to non-vanilla weapon prefabs.");
		ToolPieceOrder = file.Bind<string>("3c - Tools", "ToolPieceOrder", "Hammer,Hoe,Cultivator,Pickaxe,Scythe,Other", "Comma-separated tool type order within a material group.");
		ShieldPieceOrder = file.Bind<string>("3d - Shields", "ShieldPieceOrder", "Shield,Tower,Other", "Comma-separated shield type order within a material group.");
		RefreshOnInventoryChange = file.Bind<bool>("4 - Refresh", "RefreshOnInventoryChange", true, "Re-sort when the local player inventory changes while crafting is open.");
		RefreshOnStationChange = file.Bind<bool>("4 - Refresh", "RefreshOnStationChange", true, "Reset category to All when switching crafting stations.");
		ShowCraftabilityIndicators = file.Bind<bool>("5 - UI", "ShowCraftabilityIndicators", false, "Show a small green checkmark on fully craftable recipe rows.");
		EnableCraftMultiplier = file.Bind<bool>("5 - UI", "EnableCraftMultiplier", true, "Show up/down arrows beside the Craft button for multi-craft amount.");
		MaxCraftMultiplier = file.Bind<int>("5 - UI", "MaxCraftMultiplier", 99, "Upper cap for the craft multiplier (materials still limit how high you can go).");
		EnableDismantle = file.Bind<bool>("5 - UI", "EnableDismantle", true, "Show a Dismantle tab beside Craft/Upgrade to break items back into crafting materials.");
		DebugLogging = file.Bind<bool>("9 - Debug", "DebugLogging", false, "Extra log lines for sorting decisions (set keys, order).");
	}

	public string Mode()
	{
		return (SortMode.Value ?? "CraftableFirst").Trim();
	}

	public bool IsVanillaOrder()
	{
		return Mode().Equals("Vanilla", StringComparison.OrdinalIgnoreCase);
	}

	public bool IsAlphabetical()
	{
		return Mode().Equals("Alphabetical", StringComparison.OrdinalIgnoreCase);
	}

	public bool IsProgression()
	{
		return Mode().Equals("Progression", StringComparison.OrdinalIgnoreCase);
	}

	public bool IsCategoryThenCraftable()
	{
		return Mode().Equals("CategoryThenCraftable", StringComparison.OrdinalIgnoreCase);
	}

	public bool UseCraftBuckets()
	{
		if (!EnableCraftableFirst.Value)
		{
			return false;
		}
		string text = Mode();
		if (!text.Equals("CraftableFirst", StringComparison.OrdinalIgnoreCase) && !text.Equals("CategoryThenCraftable", StringComparison.OrdinalIgnoreCase))
		{
			return string.IsNullOrEmpty(text);
		}
		return true;
	}
}
internal static class ArmorSetDetector
{
	private static readonly string[] StripSuffixes = new string[19]
	{
		"Helmet", "Hood", "Hat", "Circlet", "Crown", "Chest", "Jacket", "Tunic", "Cuirass", "Armor",
		"Legs", "Greaves", "Pants", "Trousers", "Skirt", "Cape", "Cloak", "Mantle", "Shoulder"
	};

	private static readonly string[] KnownSetStems = new string[19]
	{
		"TrollLeather", "Carapace", "Ashlands", "Fenring", "Fenris", "Padded", "Bronze", "Iron", "Wolf", "Root",
		"Mage", "Eitr", "Leather", "Rags", "Rag", "Himmin", "Asksvin", "Embla", "Flametal"
	};

	private static readonly Regex SharedCleanup = new Regex("^\\$?(item[_-]?)?", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Regex SharedPieceTrim = new Regex("[_-]?(helmet|hood|hat|circlet|crown|chest|jacket|tunic|cuirass|legs|greaves|pants|trousers|skirt|cape|cloak|mantle|shoulder|armor)$", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Regex SharedPiecePrefix = new Regex("^(helmet|hood|hat|chest|legs|cape|armor|trousers|tunic)[_-]", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	public static string SetKey(Recipe recipe, bool groupModded)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)recipe == (Object)null)
		{
			return null;
		}
		ItemType itemType = GetItemType(recipe);
		string text = PrefabName(recipe) ?? "";
		string text2 = SharedName(recipe) ?? "";
		string blob = (text + " " + text2).Trim();
		if (!IsArmorPiece(itemType, text, text2))
		{
			return null;
		}
		string text3 = FromPrefab(text);
		if (string.IsNullOrEmpty(text3))
		{
			text3 = FromShared(text2);
		}
		if (string.IsNullOrEmpty(text3))
		{
			text3 = FromKnownStem(blob);
		}
		text3 = Canonicalize(text3);
		if (string.IsNullOrEmpty(text3) || text3.Length < 4)
		{
			return null;
		}
		if (IsSlotType(itemType) || LooksLikeArmorName(blob))
		{
			return text3;
		}
		if (!groupModded && !text3.StartsWith("Armor", StringComparison.OrdinalIgnoreCase) && !text3.StartsWith("Cape", StringComparison.OrdinalIgnoreCase))
		{
			return null;
		}
		return text3;
	}

	private static bool LooksLikeArmorName(string blob)
	{
		if (string.IsNullOrEmpty(blob))
		{
			return false;
		}
		if (blob.IndexOf("armor", StringComparison.OrdinalIgnoreCase) < 0 && blob.IndexOf("leather", StringComparison.OrdinalIgnoreCase) < 0 && blob.IndexOf("troll", StringComparison.OrdinalIgnoreCase) < 0 && blob.IndexOf("bronze", StringComparison.OrdinalIgnoreCase) < 0 && blob.IndexOf("padded", StringComparison.OrdinalIgnoreCase) < 0 && blob.IndexOf("carapace", StringComparison.OrdinalIgnoreCase) < 0 && blob.IndexOf("fenring", StringComparison.OrdinalIgnoreCase) < 0 && blob.IndexOf("fenris", StringComparison.OrdinalIgnoreCase) < 0 && blob.IndexOf("rag", StringComparison.OrdinalIgnoreCase) < 0 && blob.IndexOf("cape", StringComparison.OrdinalIgnoreCase) < 0 && blob.IndexOf("helmet", StringComparison.OrdinalIgnoreCase) < 0 && blob.IndexOf("tunic", StringComparison.OrdinalIgnoreCase) < 0 && blob.IndexOf("trousers", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return blob.IndexOf("greaves", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}

	private static bool IsSlotType(ItemType t)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Invalid comparison between Unknown and I4
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Invalid comparison between Unknown and I4
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Invalid comparison between Unknown and I4
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Invalid comparison between Unknown and I4
		if ((int)t != 6 && (int)t != 7 && (int)t != 11)
		{
			return (int)t == 17;
		}
		return true;
	}

	private static bool IsArmorPiece(ItemType t, string prefab, string shared)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		if (IsSlotType(t))
		{
			return true;
		}
		string text = prefab + " " + shared;
		for (int i = 0; i < StripSuffixes.Length; i++)
		{
			if (text.IndexOf(StripSuffixes[i], StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		if (!prefab.StartsWith("Armor", StringComparison.OrdinalIgnoreCase) && !prefab.StartsWith("Cape", StringComparison.OrdinalIgnoreCase))
		{
			return prefab.StartsWith("Helmet", StringComparison.OrdinalIgnoreCase);
		}
		return true;
	}

	private static string FromPrefab(string prefab)
	{
		if (string.IsNullOrEmpty(prefab))
		{
			return null;
		}
		if (prefab.Equals("HelmetDrake", StringComparison.OrdinalIgnoreCase))
		{
			return "ArmorWolf";
		}
		if (prefab.Equals("HelmetBerserkerUndead", StringComparison.OrdinalIgnoreCase) || prefab.Equals("ArmorBerserkerUndeadChest", StringComparison.OrdinalIgnoreCase) || prefab.Equals("ArmorBerserkerUndeadLegs", StringComparison.OrdinalIgnoreCase))
		{
			return "ArmorVilebone";
		}
		if (prefab.StartsWith("Helmet", StringComparison.OrdinalIgnoreCase) && prefab.Length > 6)
		{
			string text = prefab.Substring(6);
			if (IsStandaloneHelmet(text))
			{
				return "Helmet" + text;
			}
			return "Armor" + text;
		}
		if (prefab.StartsWith("Cape", StringComparison.OrdinalIgnoreCase) && prefab.Length > 4)
		{
			string text2 = prefab.Substring(4);
			if (IsSetCape(text2))
			{
				return "Armor" + text2;
			}
			return "Cape" + text2;
		}
		string text3 = prefab;
		bool flag = false;
		for (int i = 0; i < 2; i++)
		{
			for (int j = 0; j < StripSuffixes.Length; j++)
			{
				string text4 = StripSuffixes[j];
				if (!text4.Equals("Armor", StringComparison.OrdinalIgnoreCase) && text3.EndsWith(text4, StringComparison.OrdinalIgnoreCase) && text3.Length > text4.Length + 3)
				{
					text3 = text3.Substring(0, text3.Length - text4.Length);
					flag = true;
					break;
				}
			}
		}
		if (!flag && !text3.StartsWith("Armor", StringComparison.OrdinalIgnoreCase))
		{
			return null;
		}
		return text3;
	}

	private static string FromShared(string shared)
	{
		if (string.IsNullOrEmpty(shared))
		{
			return null;
		}
		string text = SharedCleanup.Replace(shared, "");
		for (int i = 0; i < 3; i++)
		{
			string input = SharedPieceTrim.Replace(text, "");
			input = SharedPiecePrefix.Replace(input, "");
			input = input.Trim('_', '-', ' ');
			if (input == text)
			{
				break;
			}
			text = input;
		}
		if (text.Length < 3)
		{
			return null;
		}
		return text;
	}

	private static string FromKnownStem(string blob)
	{
		if (string.IsNullOrEmpty(blob))
		{
			return null;
		}
		for (int i = 0; i < KnownSetStems.Length; i++)
		{
			if (blob.IndexOf(KnownSetStems[i], StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return "Armor" + KnownSetStems[i];
			}
		}
		return null;
	}

	private static string Canonicalize(string raw)
	{
		if (string.IsNullOrEmpty(raw))
		{
			return null;
		}
		string text = raw.Replace("-", "_");
		if (text.IndexOf('_') >= 0)
		{
			string[] array = text.Split(new char[1] { '_' });
			StringBuilder stringBuilder = new StringBuilder();
			foreach (string text2 in array)
			{
				if (!string.IsNullOrEmpty(text2))
				{
					stringBuilder.Append(char.ToUpperInvariant(text2[0]));
					if (text2.Length > 1)
					{
						stringBuilder.Append(text2.Substring(1));
					}
				}
			}
			text = stringBuilder.ToString();
		}
		if (text.Length > 0 && char.IsLower(text[0]))
		{
			text = char.ToUpperInvariant(text[0]) + text.Substring(1);
		}
		if (!text.StartsWith("Armor", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("Cape", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("Helmet", StringComparison.OrdinalIgnoreCase))
		{
			text = "Armor" + text;
		}
		if (text.StartsWith("ArmorArmor", StringComparison.OrdinalIgnoreCase))
		{
			text = "Armor" + text.Substring("ArmorArmor".Length);
		}
		if (text.Equals("ArmorRag", StringComparison.OrdinalIgnoreCase))
		{
			return "ArmorRags";
		}
		if (text.Equals("ArmorTroll", StringComparison.OrdinalIgnoreCase))
		{
			return "ArmorTrollLeather";
		}
		if (text.Equals("ArmorFenris", StringComparison.OrdinalIgnoreCase))
		{
			return "ArmorFenring";
		}
		if (text.Equals("ArmorPadd", StringComparison.OrdinalIgnoreCase))
		{
			return "ArmorPadded";
		}
		return text;
	}

	private static bool IsStandaloneHelmet(string rest)
	{
		if (!rest.Equals("Yule", StringComparison.OrdinalIgnoreCase) && !rest.Equals("Dverger", StringComparison.OrdinalIgnoreCase) && !rest.Equals("Fisherman", StringComparison.OrdinalIgnoreCase) && !rest.Equals("Hat", StringComparison.OrdinalIgnoreCase))
		{
			return rest.Equals("PointyHat", StringComparison.OrdinalIgnoreCase);
		}
		return true;
	}

	private static bool IsSetCape(string rest)
	{
		if (!rest.Equals("Wolf", StringComparison.OrdinalIgnoreCase) && !rest.Equals("Troll", StringComparison.OrdinalIgnoreCase) && !rest.Equals("Lox", StringComparison.OrdinalIgnoreCase) && !rest.Equals("Feather", StringComparison.OrdinalIgnoreCase) && !rest.Equals("Asksvin", StringComparison.OrdinalIgnoreCase) && !rest.Equals("Carapace", StringComparison.OrdinalIgnoreCase) && !rest.Equals("Ashlands", StringComparison.OrdinalIgnoreCase))
		{
			return rest.Equals("Fenring", StringComparison.OrdinalIgnoreCase);
		}
		return true;
	}

	public static int PieceOrder(Recipe recipe, string[] orderTokens)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Invalid comparison between Unknown and I4
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Invalid comparison between Unknown and I4
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Invalid comparison between Unknown and I4
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Invalid comparison between Unknown and I4
		ItemType itemType = GetItemType(recipe);
		if ((int)itemType == 7)
		{
			return IndexOf(orderTokens, "Chest");
		}
		if ((int)itemType == 6)
		{
			return IndexOf(orderTokens, "Helmet");
		}
		if ((int)itemType == 11)
		{
			return IndexOf(orderTokens, "Legs");
		}
		if ((int)itemType == 17)
		{
			return IndexOf(orderTokens, "Cape");
		}
		string text = PrefabName(recipe) ?? "";
		for (int i = 0; i < orderTokens.Length; i++)
		{
			string text2 = orderTokens[i];
			if (!string.IsNullOrEmpty(text2) && !text2.Equals("Other", StringComparison.OrdinalIgnoreCase) && text.IndexOf(text2, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return i;
			}
		}
		return orderTokens.Length + 10;
	}

	public static string[] ParseOrder(string csv)
	{
		if (string.IsNullOrEmpty(csv))
		{
			return new string[5] { "Chest", "Helmet", "Legs", "Cape", "Other" };
		}
		string[] array = csv.Split(new char[1] { ',' });
		List<string> list = new List<string>();
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length > 0)
			{
				list.Add(text);
			}
		}
		if (list.Count == 0)
		{
			list.AddRange(new string[5] { "Chest", "Helmet", "Legs", "Cape", "Other" });
		}
		return list.ToArray();
	}

	private static string PrefabName(Recipe recipe)
	{
		if ((Object)(object)recipe?.m_item == (Object)null)
		{
			return null;
		}
		string name = ((Object)recipe.m_item).name;
		if (!string.IsNullOrEmpty(name))
		{
			return name;
		}
		if (!((Object)(object)((Component)recipe.m_item).gameObject != (Object)null))
		{
			return null;
		}
		return ((Object)((Component)recipe.m_item).gameObject).name;
	}

	private static string SharedName(Recipe recipe)
	{
		try
		{
			return recipe?.m_item?.m_itemData?.m_shared?.m_name;
		}
		catch
		{
			return null;
		}
	}

	private static ItemType GetItemType(Recipe recipe)
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (recipe?.m_item?.m_itemData?.m_shared != null)
			{
				return recipe.m_item.m_itemData.m_shared.m_itemType;
			}
		}
		catch
		{
		}
		return (ItemType)0;
	}

	private static int IndexOf(string[] order, string token)
	{
		for (int i = 0; i < order.Length; i++)
		{
			if (order[i].Equals(token, StringComparison.OrdinalIgnoreCase))
			{
				return i;
			}
		}
		return order.Length + 5;
	}
}
internal static class Craftability
{
	public const int BucketFully = 0;

	public const int BucketPartial = 1;

	public const int BucketNone = 2;

	public const int BucketBlocked = 3;

	public static int ScoreBucket(Player player, Recipe recipe)
	{
		if ((Object)(object)player == (Object)null || (Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null)
		{
			return 3;
		}
		try
		{
			if (player.HaveRequirements(recipe, false, 1, 1))
			{
				return 0;
			}
		}
		catch (Exception ex)
		{
			if (Plugin.Settings != null && Plugin.Settings.DebugLogging.Value)
			{
				Plugin.Log.LogWarning((object)("HaveRequirements: " + ex.Message));
			}
		}
		int num = 0;
		int num2 = 0;
		Requirement[] resources = recipe.m_resources;
		if (resources == null || resources.Length == 0)
		{
			return 2;
		}
		Inventory inventory = ((Humanoid)player).GetInventory();
		if (inventory == null)
		{
			return 2;
		}
		foreach (Requirement val in resources)
		{
			if (val == null || (Object)(object)val.m_resItem == (Object)null || val.m_resItem.m_itemData == null)
			{
				continue;
			}
			int amount = val.GetAmount(1);
			if (amount > 0)
			{
				num2++;
				string name = val.m_resItem.m_itemData.m_shared.m_name;
				if (inventory.CountItems(name, -1, true) >= amount)
				{
					num++;
				}
			}
		}
		if (num2 <= 0)
		{
			return 2;
		}
		if (num <= 0)
		{
			return 2;
		}
		if (num >= num2)
		{
			return 3;
		}
		return 1;
	}

	public static int MaterialPercent(Player player, Recipe recipe)
	{
		if ((Object)(object)player == (Object)null || recipe?.m_resources == null)
		{
			return 0;
		}
		Inventory inventory = ((Humanoid)player).GetInventory();
		if (inventory == null)
		{
			return 0;
		}
		int num = 0;
		int num2 = 0;
		Requirement[] resources = recipe.m_resources;
		foreach (Requirement val in resources)
		{
			if (val?.m_resItem?.m_itemData?.m_shared != null)
			{
				int amount = val.GetAmount(1);
				if (amount > 0)
				{
					num += amount;
					string name = val.m_resItem.m_itemData.m_shared.m_name;
					num2 += Math.Min(amount, inventory.CountItems(name, -1, true));
				}
			}
		}
		if (num <= 0)
		{
			return 0;
		}
		return num2 * 100 / num;
	}
}
internal static class DismantleMode
{
	public static readonly List<ItemData> Items = new List<ItemData>();

	private static ItemData _progressItem;

	private static Recipe _progressRecipe;

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

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

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

	private static readonly FieldInfo CraftItemEffectsField = AccessTools.Field(typeof(InventoryGui), "m_craftItemEffects");

	private static readonly FieldInfo CraftItemDoneEffectsField = AccessTools.Field(typeof(InventoryGui), "m_craftItemDoneEffects");

	private static readonly FieldInfo CraftingVibrationField = AccessTools.Field(typeof(InventoryGui), "CraftingVibration");

	public static bool Active { get; private set; }

	public static bool HasProgress => _progressItem != null;

	public static void SetActive(bool on)
	{
		Active = on;
		if (!on)
		{
			Items.Clear();
			CancelProgress(null);
		}
	}

	public static void EnterFromUi(InventoryGui gui)
	{
		if (!((Object)(object)gui == (Object)null))
		{
			SetActive(on: true);
			Button val = AccessToolsExt.TabCraft(gui);
			Button val2 = AccessToolsExt.TabUpgrade(gui);
			if ((Object)(object)val != (Object)null)
			{
				((Selectable)val).interactable = true;
			}
			if ((Object)(object)val2 != (Object)null)
			{
				((Selectable)val2).interactable = true;
			}
			CraftMultiplierBar.ForceHideArrows();
			DismantleTab.RefreshVisuals(gui);
			AccessToolsExt.RebuildCraftingPanel();
			DismantleTab.RefreshVisuals(gui);
		}
	}

	public static void ClearIfNotSuppressed()
	{
		if (Active)
		{
			SetActive(on: false);
			DismantleTab.RefreshVisuals(InventoryGui.instance);
		}
	}

	public static void BuildList(List<Recipe> recipes)
	{
		Items.Clear();
		if (recipes == null)
		{
			return;
		}
		recipes.Clear();
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		Inventory inventory = ((Humanoid)localPlayer).GetInventory();
		if (inventory == null)
		{
			return;
		}
		CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation();
		List<ItemData> allItems = inventory.GetAllItems();
		if (allItems == null || allItems.Count == 0)
		{
			return;
		}
		for (int i = 0; i < allItems.Count; i++)
		{
			ItemData val = allItems[i];
			if (val != null && val.m_shared != null && !val.m_equipped && TryGetDismantleRecipe(localPlayer, currentCraftingStation, val, out var recipe))
			{
				recipes.Add(recipe);
				Items.Add(val);
			}
		}
	}

	public static bool CanDismantle(Player player, CraftingStation station, ItemData item)
	{
		Recipe recipe;
		return TryGetDismantleRecipe(player, station, item, out recipe);
	}

	private static bool TryGetDismantleRecipe(Player player, CraftingStation station, ItemData item, out Recipe recipe)
	{
		recipe = null;
		if ((Object)(object)player == (Object)null || item?.m_shared == null)
		{
			return false;
		}
		if (item.m_equipped)
		{
			return false;
		}
		recipe = FindRecipe(item, station);
		if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null)
		{
			return false;
		}
		if (!StationFilter.BelongsToStation(recipe, station))
		{
			return false;
		}
		int num = Mathf.Max(1, recipe.m_amount);
		if (item.m_stack < num)
		{
			return false;
		}
		return true;
	}

	public static void SortPaired(List<Recipe> recipes)
	{
		if (recipes != null && Items.Count == recipes.Count)
		{
			List<int> list = new List<int>(recipes.Count);
			for (int i = 0; i < recipes.Count; i++)
			{
				list.Add(i);
			}
			list.Sort(delegate(int a, int b)
			{
				string strA = ItemSortName(Items[a]);
				string strB = ItemSortName(Items[b]);
				int num3 = string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase);
				return (num3 != 0) ? num3 : a.CompareTo(b);
			});
			List<Recipe> list2 = new List<Recipe>(recipes.Count);
			List<ItemData> list3 = new List<ItemData>(Items.Count);
			for (int num = 0; num < list.Count; num++)
			{
				int index = list[num];
				list2.Add(recipes[index]);
				list3.Add(Items[index]);
			}
			recipes.Clear();
			Items.Clear();
			for (int num2 = 0; num2 < list2.Count; num2++)
			{
				recipes.Add(list2[num2]);
				Items.Add(list3[num2]);
			}
		}
	}

	private static string ItemSortName(ItemData item)
	{
		try
		{
			if (item?.m_shared != null)
			{
				return item.m_shared.m_name ?? "";
			}
		}
		catch
		{
		}
		return "";
	}

	public static Recipe FindRecipe(ItemData item, CraftingStation station)
	{
		if (item?.m_shared == null || (Object)(object)ObjectDB.instance == (Object)null)
		{
			return null;
		}
		try
		{
			Recipe recipe = ObjectDB.instance.GetRecipe(item);
			if ((Object)(object)recipe != (Object)null && StationFilter.BelongsToStation(recipe, station))
			{
				return recipe;
			}
		}
		catch
		{
		}
		List<Recipe> recipes = ObjectDB.instance.m_recipes;
		if (recipes == null)
		{
			return null;
		}
		string value = ResolvePrefabName(item);
		if (string.IsNullOrEmpty(value))
		{
			return null;
		}
		Recipe val = null;
		for (int i = 0; i < recipes.Count; i++)
		{
			Recipe val2 = recipes[i];
			if ((Object)(object)val2 == (Object)null || (Object)(object)val2.m_item == (Object)null)
			{
				continue;
			}
			try
			{
				if (!val2.m_enabled)
				{
					continue;
				}
			}
			catch
			{
			}
			if (((Object)val2.m_item).name.Equals(value, StringComparison.OrdinalIgnoreCase))
			{
				if (StationFilter.BelongsToStation(val2, station))
				{
					return val2;
				}
				if ((Object)(object)val == (Object)null)
				{
					val = val2;
				}
			}
		}
		if ((Object)(object)val != (Object)null && StationFilter.BelongsToStation(val, station))
		{
			return val;
		}
		return null;
	}

	private static string ResolvePrefabName(ItemData item)
	{
		if (item == null)
		{
			return null;
		}
		if ((Object)(object)item.m_dropPrefab != (Object)null && !string.IsNullOrEmpty(((Object)item.m_dropPrefab).name))
		{
			return StripClone(((Object)item.m_dropPrefab).name);
		}
		if ((Object)(object)ObjectDB.instance == (Object)null || item.m_shared == null)
		{
			return null;
		}
		try
		{
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.m_shared);
			if ((Object)(object)itemPrefab != (Object)null)
			{
				return StripClone(((Object)itemPrefab).name);
			}
		}
		catch
		{
		}
		return null;
	}

	private static string StripClone(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return name;
		}
		if (name.EndsWith("(Clone)", StringComparison.Ordinal))
		{
			return name.Substring(0, name.Length - "(Clone)".Length).Trim();
		}
		return name;
	}

	public static List<KeyValuePair<ItemDrop, int>> BuildRefund(Recipe recipe, int quality)
	{
		List<KeyValuePair<ItemDrop, int>> list = new List<KeyValuePair<ItemDrop, int>>();
		if (recipe?.m_resources == null)
		{
			return list;
		}
		if (quality < 1)
		{
			quality = 1;
		}
		Dictionary<string, ItemDrop> dictionary = new Dictionary<string, ItemDrop>(StringComparer.OrdinalIgnoreCase);
		Dictionary<string, int> dictionary2 = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
		Requirement[] resources = recipe.m_resources;
		foreach (Requirement val in resources)
		{
			if ((Object)(object)val?.m_resItem == (Object)null || IsIdolOrUpgraderResource(val))
			{
				continue;
			}
			int num = CraftMaterialAmount(val, quality);
			if (num > 0)
			{
				ItemDrop resItem = val.m_resItem;
				string name = ((Object)resItem).name;
				if (!string.IsNullOrEmpty(name))
				{
					dictionary[name] = resItem;
					dictionary2.TryGetValue(name, out var value);
					dictionary2[name] = value + num;
				}
			}
		}
		foreach (KeyValuePair<string, int> item in dictionary2)
		{
			if (dictionary.TryGetValue(item.Key, out var value2) && !((Object)(object)value2 == (Object)null) && item.Value > 0)
			{
				list.Add(new KeyValuePair<ItemDrop, int>(value2, item.Value));
			}
		}
		return list;
	}

	private static bool IsIdolOrUpgraderResource(Requirement req)
	{
		if (req == null)
		{
			return true;
		}
		try
		{
			if (req.m_upgraderResource)
			{
				return true;
			}
		}
		catch
		{
		}
		ItemDrop resItem = req.m_resItem;
		if ((Object)(object)resItem == (Object)null)
		{
			return true;
		}
		if ((((Object)resItem).name ?? "").StartsWith("Upgrader", StringComparison.OrdinalIgnoreCase))
		{
			return true;
		}
		try
		{
			if ((resItem.m_itemData?.m_shared?.m_name ?? "").IndexOf("idol", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		catch
		{
		}
		return false;
	}

	private static int CraftMaterialAmount(Requirement req, int quality)
	{
		if (req == null || quality < 1)
		{
			return 0;
		}
		int num = 0;
		for (int i = 1; i <= quality; i++)
		{
			if (i <= 1)
			{
				num += Mathf.Max(0, req.m_amount);
				continue;
			}
			float num2 = ((i >= 4) ? (4f + (float)(i - 4) / 2f) : ((float)(i - 1)));
			num += Mathf.FloorToInt(num2 * (float)req.m_amountPerLevel);
		}
		return num;
	}

	public static void BindSelectedItem(InventoryGui gui, int index)
	{
		if (Active && !((Object)(object)gui == (Object)null) && index >= 0 && index < Items.Count && CraftUpgradeItemField != null)
		{
			CraftUpgradeItemField.SetValue(gui, Items[index]);
		}
	}

	public static ItemData ResolveSelectedItem(InventoryGui gui)
	{
		if ((Object)(object)gui == (Object)null)
		{
			return null;
		}
		int num = -1;
		try
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(InventoryGui), "GetSelectedRecipeIndex", new Type[1] { typeof(bool) }, (Type[])null);
			if (methodInfo != null)
			{
				num = (int)methodInfo.Invoke(gui, new object[1] { false });
			}
		}
		catch
		{
			num = -1;
		}
		if (num >= 0 && num < Items.Count && Items[num] != null)
		{
			return Items[num];
		}
		ItemData result = null;
		if (CraftUpgradeItemField != null)
		{
			object? value = CraftUpgradeItemField.GetValue(gui);
			result = (ItemData)((value is ItemData) ? value : null);
		}
		return result;
	}

	public static bool TryBeginProgress(InventoryGui gui)
	{
		if (!Active || (Object)(object)gui == (Object)null || HasProgress)
		{
			return false;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return false;
		}
		ItemData val = ResolveSelectedItem(gui);
		if (val == null)
		{
			return false;
		}
		val = ResolveLiveInventoryItem(((Humanoid)localPlayer).GetInventory(), val);
		if (val == null)
		{
			return false;
		}
		CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation();
		if (!TryGetDismantleRecipe(localPlayer, currentCraftingStation, val, out var recipe))
		{
			return false;
		}
		List<KeyValuePair<ItemDrop, int>> refund = BuildRefund(recipe, val.m_quality);
		if (!CanFitRefund(((Humanoid)localPlayer).GetInventory(), refund))
		{
			((Character)localPlayer).Message((MessageType)2, "$inventory_full", 0, (Sprite)null, false);
			return true;
		}
		_progressItem = val;
		_progressRecipe = recipe;
		if (CraftRecipeField != null)
		{
			CraftRecipeField.SetValue(gui, recipe);
		}
		if (CraftUpgradeItemField != null)
		{
			CraftUpgradeItemField.SetValue(gui, null);
		}
		if (CraftTimerField != null)
		{
			CraftTimerField.SetValue(gui, 0f);
		}
		TryInstantiateVibration(gui);
		PlayCraftEffects(gui, localPlayer, currentCraftingStation, done: false);
		return true;
	}

	public static void CancelProgress(InventoryGui gui)
	{
		_progressItem = null;
		_progressRecipe = null;
		if (!((Object)(object)gui == (Object)null))
		{
			if (CraftRecipeField != null)
			{
				CraftRecipeField.SetValue(gui, null);
			}
			if (CraftTimerField != null)
			{
				CraftTimerField.SetValue(gui, -1f);
			}
		}
	}

	public static bool TryCompleteProgress(InventoryGui gui)
	{
		if (!Active || _progressItem == null || (Object)(object)_progressRecipe == (Object)null)
		{
			return false;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			CancelProgress(gui);
			return true;
		}
		ItemData progressItem = _progressItem;
		Recipe progressRecipe = _progressRecipe;
		int quality = progressItem?.m_quality ?? 1;
		_progressItem = null;
		_progressRecipe = null;
		if (CraftRecipeField != null)
		{
			CraftRecipeField.SetValue(gui, null);
		}
		if (CraftTimerField != null)
		{
			CraftTimerField.SetValue(gui, -1f);
		}
		CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation();
		Inventory inventory = ((Humanoid)localPlayer).GetInventory();
		if (inventory == null || progressItem == null)
		{
			return true;
		}
		ItemData val = ResolveLiveInventoryItem(inventory, progressItem);
		if (val == null || !CanDismantle(localPlayer, currentCraftingStation, val))
		{
			((Character)localPlayer).Message((MessageType)2, "Dismantle failed", 0, (Sprite)null, false);
			return true;
		}
		int num = Mathf.Max(1, progressRecipe.m_amount);
		if (val.m_stack < num)
		{
			((Character)localPlayer).Message((MessageType)2, "Dismantle failed", 0, (Sprite)null, false);
			return true;
		}
		List<KeyValuePair<ItemDrop, int>> list = BuildRefund(progressRecipe, quality);
		if (!CanFitRefund(inventory, list))
		{
			((Character)localPlayer).Message((MessageType)2, "$inventory_full", 0, (Sprite)null, false);
			return true;
		}
		int stack = val.m_stack;
		bool flag = ((num < val.m_stack) ? inventory.RemoveItem(val, num) : inventory.RemoveItem(val));
		if (!flag)
		{
			flag = TryForceRemove(inventory, progressItem, num);
		}
		if (flag)
		{
			ItemData val2 = ResolveLiveInventoryItem(inventory, progressItem);
			if (val2 != null && val2.m_stack >= stack)
			{
				flag = false;
			}
		}
		if (!flag)
		{
			((Character)localPlayer).Message((MessageType)2, "Dismantle failed", 0, (Sprite)null, false);
			if (Plugin.Log != null)
			{
				Plugin.Log.LogWarning((object)"Dismantle: could not remove selected item from inventory");
			}
			return true;
		}
		for (int i = 0; i < list.Count; i++)
		{
			ItemDrop key = list[i].Key;
			int value = list[i].Value;
			if ((Object)(object)key == (Object)null || value <= 0)
			{
				continue;
			}
			string name = ((Object)key).name;
			if (string.IsNullOrEmpty(name))
			{
				continue;
			}
			int num2 = value;
			while (num2 > 0)
			{
				int num3 = num2;
				try
				{
					int num4 = ((key.m_itemData?.m_shared != null) ? Mathf.Max(1, key.m_itemData.m_shared.m_maxStackSize) : num2);
					num3 = Mathf.Min(num2, num4);
				}
				catch
				{
				}
				if (inventory.AddItem(name, num3, 1, 0, 0L, "", false, false) == null && !inventory.AddItem(((Component)key).gameObject, num3))
				{
					if (Plugin.Log != null)
					{
						Plugin.Log.LogWarning((object)("Dismantle could not add " + name + " x" + num3));
					}
					break;
				}
				num2 -= num3;
			}
		}
		PlayCraftEffects(gui, localPlayer, currentCraftingStation, done: true);
		((Character)localPlayer).Message((MessageType)2, "Dismantled", 0, (Sprite)null, false);
		return true;
	}

	private static ItemData ResolveLiveInventoryItem(Inventory inv, ItemData snapshot)
	{
		if (inv == null || snapshot?.m_shared == null)
		{
			return null;
		}
		if (inv.ContainsItem(snapshot))
		{
			return snapshot;
		}
		try
		{
			ItemData itemAt = inv.GetItemAt(snapshot.m_gridPos.x, snapshot.m_gridPos.y);
			if (itemAt != null && SameDismantleTarget(itemAt, snapshot))
			{
				return itemAt;
			}
		}
		catch
		{
		}
		List<ItemData> allItems = inv.GetAllItems();
		if (allItems == null)
		{
			return null;
		}
		for (int i = 0; i < allItems.Count; i++)
		{
			ItemData val = allItems[i];
			if (SameDismantleTarget(val, snapshot))
			{
				return val;
			}
		}
		return null;
	}

	private static bool SameDismantleTarget(ItemData a, ItemData b)
	{
		if (a?.m_shared == null || b?.m_shared == null)
		{
			return false;
		}
		if (a.m_equipped || b.m_equipped)
		{
			return false;
		}
		if (!string.Equals(a.m_shared.m_name, b.m_shared.m_name, StringComparison.Ordinal))
		{
			return false;
		}
		if (a.m_quality != b.m_quality)
		{
			return false;
		}
		if (a.m_variant != b.m_variant)
		{
			return false;
		}
		return true;
	}

	private static bool TryForceRemove(Inventory inv, ItemData snapshot, int amount)
	{
		if (inv == null || snapshot?.m_shared == null || amount <= 0)
		{
			return false;
		}
		ItemData val = ResolveLiveInventoryItem(inv, snapshot);
		if (val == null)
		{
			return false;
		}
		if (amount >= val.m_stack)
		{
			return inv.RemoveItem(val);
		}
		return inv.RemoveItem(val, amount);
	}

	private static void TryInstantiateVibration(InventoryGui gui)
	{
		if ((Object)(object)gui == (Object)null || CraftingVibrationField == null)
		{
			return;
		}
		try
		{
			object? value = CraftingVibrationField.GetValue(gui);
			GameObject val = (GameObject)((value is GameObject) ? value : null);
			if ((Object)(object)val != (Object)null)
			{
				Object.Instantiate<GameObject>(val);
			}
		}
		catch
		{
		}
	}

	private static void PlayCraftEffects(InventoryGui gui, Player player, CraftingStation station, bool done)
	{
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null)
		{
			return;
		}
		EffectList val = null;
		try
		{
			if ((Object)(object)station != (Object)null)
			{
				val = (done ? station.m_craftItemDoneEffects : station.m_craftItemEffects);
			}
		}
		catch
		{
		}
		if (val == null && (Object)(object)gui != (Object)null)
		{
			FieldInfo fieldInfo = (done ? CraftItemDoneEffectsField : CraftItemEffectsField);
			if (fieldInfo != null)
			{
				object? value = fieldInfo.GetValue(gui);
				val = (EffectList)((value is EffectList) ? value : null);
			}
		}
		if (val == null)
		{
			return;
		}
		try
		{
			val.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
		}
		catch (Exception ex)
		{
			if (Plugin.Log != null)
			{
				Plugin.Log.LogWarning((object)("Dismantle effects: " + ex.Message));
			}
		}
	}

	private static bool CanFitRefund(Inventory inv, List<KeyValuePair<ItemDrop, int>> refund)
	{
		if (inv == null || refund == null || refund.Count == 0)
		{
			return true;
		}
		if (inv.GetEmptySlots() > 0)
		{
			return true;
		}
		for (int i = 0; i < refund.Count; i++)
		{
			ItemDrop key = refund[i].Key;
			if (key?.m_itemData?.m_shared == null)
			{
				return false;
			}
			SharedData shared = key.m_itemData.m_shared;
			if (inv.CountItems(shared.m_name, -1, true) <= 0 && shared.m_maxStackSize <= 1)
			{
				return false;
			}
		}
		return true;
	}
}
internal static class MaterialNameBucket
{
	private static readonly string[] Tokens = new string[52]
	{
		"BlackMetal", "Blackmetal", "FineWood", "Finewood", "TrollLeather", "HardAntler", "Serpentscale", "Serpent", "Flametal", "Carapace",
		"Fenring", "Fenris", "Fenrir", "Vilebone", "VileBone", "Bronze", "Copper", "Silver", "Iron", "Flint",
		"Bone", "Leather", "Padded", "Antler", "Wood", "Root", "Ancient", "Abyssal", "Chitin", "Wolf",
		"Frost", "Crystal", "Obsidian", "Needle", "Eitr", "Mage", "Himmin", "Mist", "Dvergr", "Ashlands",
		"Asksvin", "Embla", "Blood", "Bile", "Ooze", "Fire", "Poison", "Banded", "Rags", "Rag",
		"Berserker", "Lox"
	};

	private static readonly Dictionary<string, string> PrefabOverrides = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
	{
		{ "HelmetDrake", "Wolf" },
		{ "TrinketSilverDamage", "Wolf" },
		{ "Bow", "Bows" },
		{ "BowFineWood", "Bows" },
		{ "BowHuntsman", "Bows" },
		{ "BowDraugrFang", "Bows" },
		{ "Battleaxe", "Iron" },
		{ "MaceNeedle", "Iron" },
		{ "BattleaxeWood", "Wood" },
		{ "Club", "Wood" },
		{ "SledgeStagbreaker", "Wood" },
		{ "AxeStone", "Stone" },
		{ "FistBjornClaw", "Bear" },
		{ "ShieldWood", "Shields" },
		{ "ShieldWoodTower", "Shields" },
		{ "ShieldBoneTower", "Shields" },
		{ "ShieldRoots", "Shields" },
		{ "BattleaxeBlackmetal", "BlackMetal" },
		{ "Battleaxe_Blackmetal", "BlackMetal" },
		{ "TrinketSilverResist", "Crystal" },
		{ "BattleaxeCrystal", "Crystal" },
		{ "Battleaxe_Crystal", "Crystal" },
		{ "HelmetBerserkerUndead", "Vilebone" },
		{ "ArmorBerserkerUndeadChest", "Vilebone" },
		{ "ArmorBerserkerUndeadLegs", "Vilebone" },
		{ "FistBjornUndeadClaw", "Vilebone" },
		{ "FistUnbjornClaw", "Vilebone" },
		{ "HelmetBerserker", "Berserker" },
		{ "ArmorBerserkerChest", "Berserker" },
		{ "ArmorBerserkerLegs", "Berserker" },
		{ "HelmetFenrir", "Fenrir" },
		{ "ArmorFenrirChest", "Fenrir" },
		{ "ArmorFenrirLegs", "Fenrir" },
		{ "HelmetLox", "Lox" },
		{ "ArmorLoxChest", "Lox" },
		{ "ArmorLoxLegs", "Lox" },
		{ "CapeLox", "Lox" },
		{ "SadleLox", "Lox" },
		{ "SaddleLox", "Lox" },
		{ "CapeDeerHide", "Cape" },
		{ "CapeLinen", "Cape" },
		{ "CapeOdin", "Cape" },
		{ "CapeTrollHide", "Cape" },
		{ "CapeWolf", "Cape" },
		{ "HelmetOdin", "Clothes" },
		{ "HelmetCelebration", "Clothes" },
		{ "HelmetFishingHat", "Clothes" },
		{ "HelmetMidsummerCrown", "Clothes" },
		{ "HelmetPointyHat", "Clothes" },
		{ "HelmetStrawhat", "Clothes" },
		{ "BombOoze", "Bomb" },
		{ "BombBile", "Bomb" },
		{ "BombBlob_Poison", "Bomb" },
		{ "BombBlob_PoisonElite", "Bomb" },
		{ "BombBlob_Frost", "Bomb" },
		{ "BombBlob_Lava", "Bomb" },
		{ "BombBlob_Morkhalla", "Bomb" },
		{ "BombBlob_Tar", "Bomb" },
		{ "BombDynamite", "Bomb" },
		{ "BombLava", "Bomb" },
		{ "BombSiege", "Bomb" },
		{ "BombSmoke", "Bomb" },
		{ "FireworksBlue", "Firework" },
		{ "FireworksCyan", "Firework" },
		{ "FireworksGreen", "Firework" },
		{ "FireworksPurple", "Firework" },
		{ "FireworksRed", "Firework" },
		{ "FireworksYellow", "Firework" },
		{ "PotionHealthMinor", "Potion" },
		{ "PotionStaminaMinor", "Potion" },
		{ "Tankard", "Skol" },
		{ "TankardAnniversary", "Skol" },
		{ "TankardOdin", "Skol" },
		{ "Snowball", "Misc" },
		{ "CatapultPayload_Grausten", "Misc" },
		{ "AxeEarly", "Special" },
		{ "Feaster", "Furniture" },
		{ "Adze", "Furniture" },
		{ "Chisel", "Tools" },
		{ "Torch", "Tools" },
		{ "StaffFireball", "Staffs" },
		{ "StaffIceShards", "Staffs" },
		{ "StaffLightning", "Staffs" },
		{ "StaffTroll", "Staffs" },
		{ "StaffSkeleton", "Staffs" },
		{ "StaffClusterbomb", "Staffs" },
		{ "StaffShield", "Staffs" },
		{ "StaffProtect", "Staffs" },
		{ "StaffProtection", "Staffs" },
		{ "StaffGreenRoots", "Staffs" },
		{ "StaffNature", "Staffs" },
		{ "StaffWild", "Staffs" }
	};

	private static readonly Regex SpaceyBlackMetal = new Regex("black\\s*metal", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Regex SpaceyFineWood = new Regex("fine\\s*wood", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Regex SpaceyTrollLeather = new Regex("troll\\s*leather", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Regex SpaceySerpent = new Regex("serpent\\s*scale", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Regex SpaceyVileBone = new Regex("vile\\s*[_-]?\\s*bone", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	public static string Resolve(Recipe recipe)
	{
		if ((Object)(object)recipe == (Object)null)
		{
			return null;
		}
		if (WeaponSetDetector.IsAmmo(recipe))
		{
			return "Arrows";
		}
		string text = StripClone(PrefabName(recipe) ?? "");
		if (!string.IsNullOrEmpty(text) && PrefabOverrides.TryGetValue(text, out var value))
		{
			return value;
		}
		string text2 = FromPrefabFamily(text);
		if (!string.IsNullOrEmpty(text2))
		{
			return text2;
		}
		if (text.StartsWith("Staff", StringComparison.OrdinalIgnoreCase))
		{
			return "Staffs";
		}
		string text3 = SharedName(recipe) ?? "";
		string text4 = TryLocalize(text3);
		string text5 = text + " " + text3 + " " + text4;
		string text6 = TrySpecialBucket(text, text5, text4);
		if (!string.IsNullOrEmpty(text6))
		{
			return text6;
		}
		if (IsVilebone(text5, text4, text))
		{
			return "Vilebone";
		}
		if (SpaceyVileBone.IsMatch(text5))
		{
			return "Vilebone";
		}
		if (SpaceyBlackMetal.IsMatch(text5) || ContainsAny(text5, "BlackMetal", "Blackmetal"))
		{
			return "BlackMetal";
		}
		if (SpaceyFineWood.IsMatch(text5))
		{
			return "FineWood";
		}
		if (SpaceyTrollLeather.IsMatch(text5))
		{
			return "TrollLeather";
		}
		if (SpaceySerpent.IsMatch(text5))
		{
			return "Serpentscale";
		}
		for (int i = 0; i < Tokens.Length; i++)
		{
			string text7 = Tokens[i];
			if (text5.IndexOf(text7, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				if (text7.Equals("Bone", StringComparison.OrdinalIgnoreCase) && SpaceyVileBone.IsMatch(text5))
				{
					return "Vilebone";
				}
				if (text7.Equals("Fire", StringComparison.OrdinalIgnoreCase) && ContainsAny(text5, "Firework"))
				{
					return "Firework";
				}
				return Canonical(text7);
			}
		}
		return null;
	}

	private static string FromPrefabFamily(string prefab)
	{
		if (string.IsNullOrEmpty(prefab))
		{
			return null;
		}
		if (prefab.StartsWith("Shield", StringComparison.OrdinalIgnoreCase))
		{
			return "Shields";
		}
		if (prefab.StartsWith("Bomb", StringComparison.OrdinalIgnoreCase))
		{
			return "Bomb";
		}
		if (prefab.StartsWith("Fireworks", StringComparison.OrdinalIgnoreCase) || prefab.StartsWith("Firework", StringComparison.OrdinalIgnoreCase))
		{
			return "Firework";
		}
		if (prefab.StartsWith("Potion", StringComparison.OrdinalIgnoreCase))
		{
			return "Potion";
		}
		if (prefab.StartsWith("Tankard", StringComparison.OrdinalIgnoreCase))
		{
			return "Skol";
		}
		if (prefab.StartsWith("ArmorDress", StringComparison.OrdinalIgnoreCase) || prefab.StartsWith("ArmorTunic", StringComparison.OrdinalIgnoreCase) || prefab.StartsWith("ArmorHarvester", StringComparison.OrdinalIgnoreCase) || prefab.StartsWith("HelmetHat", StringComparison.OrdinalIgnoreCase))
		{
			return "Clothes";
		}
		if (ContainsAny(prefab, "BerserkerUndead"))
		{
			return "Vilebone";
		}
		if (ContainsAny(prefab, "Berserker") && !ContainsAny(prefab, "Undead"))
		{
			return "Berserker";
		}
		if (ContainsAny(prefab, "Fenrir"))
		{
			return "Fenrir";
		}
		if (ContainsAny(prefab, "Lox") || prefab.Equals("SadleLox", StringComparison.OrdinalIgnoreCase))
		{
			return "Lox";
		}
		return null;
	}

	public static int NestedToolTier(Recipe recipe)
	{
		if ((Object)(object)recipe == (Object)null)
		{
			return 5000;
		}
		string text = StripClone(PrefabName(recipe) ?? "");
		string text2 = SharedName(recipe) ?? "";
		string text3 = text + " " + text2 + " " + TryLocalize(text2);
		if (SpaceyBlackMetal.IsMatch(text3) || ContainsAny(text3, "BlackMetal", "Blackmetal"))
		{
			return Tier("BlackMetal");
		}
		if (SpaceyFineWood.IsMatch(text3))
		{
			return Tier("FineWood");
		}
		for (int i = 0; i < Tokens.Length; i++)
		{
			string text4 = Tokens[i];
			if (text3.IndexOf(text4, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				string text5 = Canonical(text4);
				if (!text5.Equals("Vilebone", StringComparison.OrdinalIgnoreCase))
				{
					return Tier(text5);
				}
			}
		}
		if (ContainsAny(text, "Hammer", "Hoe", "Cultivator", "Torch", "Chisel") && !ContainsAny(text, "Iron", "Bronze", "Black"))
		{
			return Tier("Wood");
		}
		return 5000;
	}

	private static string TrySpecialBucket(string prefab, string blob, string localized)
	{
		string blob2 = localized + " " + blob;
		if (IsVilebone(blob, localized, prefab))
		{
			return "Vilebone";
		}
		if (ContainsAny(blob2, "Wolf Sight", "Wolfsicht", "Wolfssicht"))
		{
			return "Wolf";
		}
		if (ContainsAny(blob2, "Crystal Heart", "Crystal Hearth", "Kristallherz", "Kristallherd"))
		{
			return "Crystal";
		}
		if (ContainsAny(blob2, "Porcupine", "Stachelschwein"))
		{
			return "Iron";
		}
		if (ContainsAny(blob2, "Drake Helmet", "Drachenhelm") || prefab.Equals("HelmetDrake", StringComparison.OrdinalIgnoreCase))
		{
			return "Wolf";
		}
		if (ContainsAny(blob2, "Huntsman", "Jägerbogen", "Jaegerbogen") || ContainsAny(blob2, "Draugr Fang", "Draugrzahn"))
		{
			return "Bows";
		}
		if (ContainsAny(blob2, "Battleaxe", "Streitaxt") && !ContainsAny(prefab, "Crystal", "Wood") && !ContainsAny(prefab, "BlackMetal", "Blackmetal") && !ContainsAny(blob2, "Black Metal", "Blackmetal", "Kristall", "Wood"))
		{
			return "Iron";
		}
		if (ContainsAny(blob2, "Staff of", "Dead raiser", "Dead Raiser", "Dundr", "Trollstav", "Troll staff") || ContainsAny(prefab, "Staff"))
		{
			return "Staffs";
		}
		if (ContainsAny(blob, "Pickaxe", "Cultivator", "Scythe", "ButcherKnife", "Butcher", "Torch", "Chisel") || prefab.Equals("Torch", StringComparison.OrdinalIgnoreCase) || prefab.Equals("Hammer", StringComparison.OrdinalIgnoreCase) || prefab.Equals("Hoe", StringComparison.OrdinalIgnoreCase) || prefab.Equals("Chisel", StringComparison.OrdinalIgnoreCase))
		{
			return "Tools";
		}
		if (ContainsAny(blob, "Huntsman") || ContainsAny(blob, "DraugrFang", "Draugrfang") || (ContainsAny(blob, "Draugr") && ContainsAny(blob, "Fang") && ContainsAny(blob, "Bow")) || prefab.Equals("Bow", StringComparison.OrdinalIgnoreCase) || prefab.Equals("BowFineWood", StringComparison.OrdinalIgnoreCase))
		{
			return "Bows";
		}
		if (ContainsAny(blob, "Drake") && ContainsAny(blob, "Helmet", "Hood", "Hat"))
		{
			return "Wolf";
		}
		if (ContainsAny(blob, "WolfSight", "Wolfsight") || (ContainsAny(blob, "Wolf") && ContainsAny(blob, "Sight")))
		{
			return "Wolf";
		}
		if (ContainsAny(blob, "CrystalHearth", "CrystalHeart", "Crystal_Hearth", "Crystal_Heart") || (ContainsAny(blob, "Crystal") && ContainsAny(blob, "Hearth", "Heart")))
		{
			return "Crystal";
		}
		if (prefab.Equals("Battleaxe", StringComparison.OrdinalIgnoreCase) || prefab.Equals("MaceNeedle", StringComparison.OrdinalIgnoreCase))
		{
			return "Iron";
		}
		if (prefab.Equals("BattleaxeWood", StringComparison.OrdinalIgnoreCase))
		{
			return "Wood";
		}
		if (prefab.Equals("Adze", StringComparison.OrdinalIgnoreCase) || prefab.Equals("Feaster", StringComparison.OrdinalIgnoreCase))
		{
			return "Furniture";
		}
		if (prefab.Equals("Snowball", StringComparison.OrdinalIgnoreCase))
		{
			return "Misc";
		}
		return null;
	}

	private static bool IsVilebone(string blob, string localized, string prefab)
	{
		string text = (prefab ?? "").ToLowerInvariant();
		string text2 = (localized + " " + blob + " " + text).ToLowerInvariant();
		if (text.IndexOf("berserkerundead", StringComparison.Ordinal) >= 0 || text.IndexOf("bjornundead", StringComparison.Ordinal) >= 0 || text.IndexOf("unbjorn", StringComparison.Ordinal) >= 0 || text.Equals("helmetberserkerundead", StringComparison.Ordinal) || text.Equals("armorberserkerundeadchest", StringComparison.Ordinal) || text.Equals("armorberserkerundeadlegs", StringComparison.Ordinal) || text.Equals("fistbjornundeadclaw", StringComparison.Ordinal) || text.Equals("fistunbjornclaw", StringComparison.Ordinal))
		{
			return true;
		}
		if (SpaceyVileBone.IsMatch(text2) || text2.IndexOf("vilebone", StringComparison.Ordinal) >= 0 || text2.IndexOf("vile_bone", StringComparison.Ordinal) >= 0 || text2.IndexOf("vile-bone", StringComparison.Ordinal) >= 0)
		{
			return true;
		}
		if (text2.IndexOf("maulclaws", StringComparison.Ordinal) >= 0 || text2.IndexOf("maul claws", StringComparison.Ordinal) >= 0)
		{
			return true;
		}
		if (text2.IndexOf("vilebone cage", StringComparison.Ordinal) >= 0 || text2.IndexOf("vilebone visage", StringComparison.Ordinal) >= 0 || text2.IndexOf("vilebone drap", StringComparison.Ordinal) >= 0)
		{
			return true;
		}
		return false;
	}

	public static int Tier(string bucket)
	{
		if (string.IsNullOrEmpty(bucket))
		{
			return 10000;
		}
		int num = SectionOf(bucket);
		int num2 = WithinSection(bucket);
		return num * 1000 + num2;
	}

	public static int SectionOf(string bucket)
	{
		if (string.IsNullOrEmpty(bucket))
		{
			return 0;
		}
		if (bucket.Equals("Shields", StringComparison.OrdinalIgnoreCase))
		{
			return 1;
		}
		if (bucket.Equals("Tools", StringComparison.OrdinalIgnoreCase))
		{
			return 2;
		}
		if (IsArmorBucket(bucket))
		{
			return 3;
		}
		if (bucket.Equals("Cape", StringComparison.OrdinalIgnoreCase))
		{
			return 4;
		}
		if (bucket.Equals("Bomb", StringComparison.OrdinalIgnoreCase))
		{
			return 5;
		}
		if (bucket.Equals("Firework", StringComparison.OrdinalIgnoreCase))
		{
			return 6;
		}
		if (bucket.Equals("Skol", StringComparison.OrdinalIgnoreCase))
		{
			return 7;
		}
		if (bucket.Equals("Mist", StringComparison.OrdinalIgnoreCase))
		{
			return 8;
		}
		if (bucket.Equals("Potion", StringComparison.OrdinalIgnoreCase) || bucket.Equals("Furniture", StringComparison.OrdinalIgnoreCase) || bucket.Equals("Misc", StringComparison.OrdinalIgnoreCase))
		{
			return 9;
		}
		return 0;
	}

	private static bool IsArmorBucket(string bucket)
	{
		if (!bucket.Equals("Rags", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Leather", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("TrollLeather", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Root", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Berserker", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Fenrir", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Fenring", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Lox", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Clothes", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Wolf", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Vilebone", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Padded", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Carapace", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Mage", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Eitr", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Ashlands", StringComparison.OrdinalIgnoreCase) && !bucket.Equals("Asksvin", StringComparison.OrdinalIgnoreCase))
		{
			return bucket.Equals("Embla", StringComparison.OrdinalIgnoreCase);
		}
		return true;
	}

	private static int WithinSection(string bucket)
	{
		if (bucket.Equals("Arrows", StringComparison.OrdinalIgnoreCase))
		{
			return 0;
		}
		if (bucket.Equals("Bows", StringComparison.OrdinalIgnoreCase))
		{
			return 1;
		}
		if (bucket.Equals("Stone", StringComparison.OrdinalIgnoreCase))
		{
			return 2;
		}
		if (bucket.Equals("Wood", StringComparison.OrdinalIgnoreCase))
		{
			return 3;
		}
		if (bucket.Equals("Flint", StringComparison.OrdinalIgnoreCase))
		{
			return 4;
		}
		if (bucket.Equals("Chitin", StringComparison.OrdinalIgnoreCase))
		{
			return 5;
		}
		if (bucket.Equals("Special", StringComparison.OrdinalIgnoreCase))
		{
			return 6;
		}
		if (bucket.Equals("Bear", StringComparison.OrdinalIgnoreCase))
		{
			return 7;
		}
		if (bucket.Equals("Staffs", StringComparison.OrdinalIgnoreCase))
		{
			return 80;
		}
		if (bucket.Equals("Shields", StringComparison.OrdinalIgnoreCase))
		{
			return 0;
		}
		if (bucket.Equals("Tools", StringComparison.OrdinalIgnoreCase))
		{
			return 0;
		}
		if (bucket.Equals("Rags", StringComparison.OrdinalIgnoreCase))
		{
			return 0;
		}
		if (bucket.Equals("Leather", StringComparison.OrdinalIgnoreCase))
		{
			return 1;
		}
		if (bucket.Equals("TrollLeather", StringComparison.OrdinalIgnoreCase))
		{
			return 2;
		}
		if (bucket.Equals("Root", StringComparison.OrdinalIgnoreCase))
		{
			return 3;
		}
		if (bucket.Equals("Berserker", StringComparison.OrdinalIgnoreCase))
		{
			return 4;
		}
		if (bucket.Equals("Fenrir", StringComparison.OrdinalIgnoreCase) || bucket.Equals("Fenring", StringComparison.OrdinalIgnoreCase))
		{
			return 5;
		}
		if (bucket.Equals("Lox", StringComparison.OrdinalIgnoreCase))
		{
			return 6;
		}
		if (bucket.Equals("Wolf", StringComparison.OrdinalIgnoreCase))
		{
			return 7;
		}
		if (bucket.Equals("Vilebone", StringComparison.OrdinalIgnoreCase))
		{
			return 8;
		}
		if (bucket.Equals("Clothes", StringComparison.OrdinalIgnoreCase))
		{
			return 9;
		}
		if (bucket.Equals("Cape", StringComparison.OrdinalIgnoreCase))
		{
			return 0;
		}
		if (bucket.Equals("Bomb", StringComparison.OrdinalIgnoreCase))
		{
			return 0;
		}
		if (bucket.Equals("Firework", StringComparison.OrdinalIgnoreCase))
		{
			return 0;
		}
		if (bucket.Equals("Skol", StringComparison.OrdinalIgnoreCase))
		{
			return 0;
		}
		if (bucket.Equals("Mist", StringComparison.OrdinalIgnoreCase))
		{
			return 0;
		}
		if (bucket.Equals("Potion", StringComparison.OrdinalIgnoreCase))
		{
			return 0;
		}
		if (bucket.Equals("Furniture", StringComparison.OrdinalIgnoreCase))
		{
			return 1;
		}
		if (bucket.Equals("Misc", StringComparison.OrdinalIgnoreCase))
		{
			return 2;
		}
		return 100 + MaterialProgression.Tier("Weapon" + bucket);
	}

	public static string Label(string bucket)
	{
		if (string.IsNullOrEmpty(bucket))
		{
			return null;
		}
		if (bucket.Equals("Arrows", StringComparison.OrdinalIgnoreCase) || bucket.Equals("Ammo", StringComparison.OrdinalIgnoreCase))
		{
			return "Arrows";
		}
		if (bucket.Equals("Tools", StringComparison.OrdinalIgnoreCase))
		{
			return "Tools";
		}
		if (bucket.Equals("Bows", StringComparison.OrdinalIgnoreCase))
		{
			return "Bows";
		}
		if (bucket.Equals("Staffs", StringComparison.OrdinalIgnoreCase))
		{
			return "Staffs";
		}
		if (bucket.Equals("Shields", StringComparison.OrdinalIgnoreCase))
		{
			return "Shields";
		}
		if (bucket.Equals("Vilebone", StringComparison.OrdinalIgnoreCase))
		{
			return "Vilebone";
		}
		if (bucket.Equals("Bomb", StringComparison.OrdinalIgnoreCase))
		{
			return "Bomb";
		}
		if (bucket.Equals("Firework", StringComparison.OrdinalIgnoreCase))
		{
			return "Firework";
		}
		if (bucket.Equals("Potion", StringComparison.OrdinalIgnoreCase))
		{
			return "Potion";
		}
		if (bucket.Equals("Skol", StringComparison.OrdinalIgnoreCase))
		{
			return "Skol";
		}
		if (bucket.Equals("Cape", StringComparison.OrdinalIgnoreCase))
		{
			return "Cape";
		}
		if (bucket.Equals("Clothes", StringComparison.OrdinalIgnoreCase))
		{
			return "Clothes";
		}
		if (bucket.Equals("Furniture", StringComparison.OrdinalIgnoreCase))
		{
			return "Furniture";
		}
		if (bucket.Equals("Misc", StringComparison.OrdinalIgnoreCase))
		{
			return "Misc";
		}
		if (bucket.Equals("Special", StringComparison.OrdinalIgnoreCase))
		{
			return "Special";
		}
		if (bucket.Equals("Stone", StringComparison.OrdinalIgnoreCase))
		{
			return "Stone";
		}
		if (bucket.Equals("Bear", StringComparison.OrdinalIgnoreCase))
		{
			return "Bear";
		}
		if (bucket.Equals("Berserker", StringComparison.OrdinalIgnoreCase))
		{
			return "Berserker";
		}
		if (bucket.Equals("Fenrir", StringComparison.OrdinalIgnoreCase))
		{
			return "Fenrir";
		}
		if (bucket.Equals("Lox", StringComparison.OrdinalIgnoreCase))
		{
			return "Lox";
		}
		return ToSentenceCase(SplitCamel(Canonical(bucket)));
	}

	private static string StripClone(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return name;
		}
		if (name.EndsWith("(Clone)", StringComparison.OrdinalIgnoreCase))
		{
			return name.Substring(0, name.Length - "(Clone)".Length).Trim();
		}
		return name;
	}

	private static string TryLocalize(string token)
	{
		if (string.IsNullOrEmpty(token))
		{
			return "";
		}
		try
		{
			Type type = Type.GetType("Localization, assembly_valheim") ?? Type.GetType("Localization");
			if (type == null)
			{
				return token;
			}
			PropertyInfo property = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public);
			object obj = ((property != null) ? property.GetValue(null, null) : null);
			if (obj == null)
			{
				return token;
			}
			MethodInfo method = type.GetMethod("Localize", new Type[1] { typeof(string) });
			if (method == null)
			{
				return token;
			}
			return (method.Invoke(obj, new object[1] { token }) as string) ?? token;
		}
		catch
		{
			return token;
		}
	}

	private static bool ContainsAny(string blob, params string[] needles)
	{
		if (string.IsNullOrEmpty(blob) || needles == null)
		{
			return false;
		}
		for (int i = 0; i < needles.Length; i++)
		{
			if (!string.IsNullOrEmpty(needles[i]) && blob.IndexOf(needles[i], StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}

	private static string ToSentenceCase(string s)
	{
		if (string.IsNullOrEmpty(s))
		{
			return s;
		}
		s = s.Trim().ToLowerInvariant();
		if (s.Length == 0)
		{
			return s;
		}
		return char.ToUpperInvariant(s[0]) + s.Substring(1);
	}

	private static string Canonical(string tok)
	{
		if (tok.Equals("Blackmetal", StringComparison.OrdinalIgnoreCase))
		{
			return "BlackMetal";
		}
		if (tok.Equals("Finewood", StringComparison.OrdinalIgnoreCase))
		{
			return "FineWood";
		}
		if (tok.Equals("Banded", StringComparison.OrdinalIgnoreCase))
		{
			return "Bronze";
		}
		if (tok.Equals("Serpent", StringComparison.OrdinalIgnoreCase))
		{
			return "Serpentscale";
		}
		if (tok.Equals("Rag", StringComparison.OrdinalIgnoreCase))
		{
			return "Rags";
		}
		if (tok.Equals("Fenris", StringComparison.OrdinalIgnoreCase) || tok.Equals("Fenring", StringComparison.OrdinalIgnoreCase))
		{
			return "Fenrir";
		}
		if (tok.Equals("VileBone", StringComparison.OrdinalIgnoreCase) || tok.Equals("Vilebone", StringComparison.OrdinalIgnoreCase))
		{
			return "Vilebone";
		}
		if (tok.Length > 0 && char.IsLower(tok[0]))
		{
			return char.ToUpperInvariant(tok[0]) + tok.Substring(1);
		}
		return tok;
	}

	private static string SplitCamel(string s)
	{
		if (string.IsNullOrEmpty(s))
		{
			return s;
		}
		StringBuilder stringBuilder = new StringBuilder(s.Length + 4);
		for (int i = 0; i < s.Length; i++)
		{
			char c = s[i];
			if (i > 0 && char.IsUpper(c) && char.IsLower(s[i - 1]))
			{
				stringBuilder.Append(' ');
			}
			stringBuilder.Append(c);
		}
		return stringBuilder.ToString();
	}

	private static string PrefabName(Recipe recipe)
	{
		if ((Object)(object)recipe?.m_item == (Object)null)
		{
			return null;
		}
		string name = ((Object)recipe.m_item).name;
		if (!string.IsNullOrEmpty(name))
		{
			return name;
		}
		if (!((Object)(object)((Component)recipe.m_item).gameObject != (Object)null))
		{
			return null;
		}
		return ((Object)((Component)recipe.m_item).gameObject).name;
	}

	private static string SharedName(Recipe recipe)
	{
		try
		{
			return recipe?.m_item?.m_itemData?.m_shared?.m_name;
		}
		catch
		{
			return null;
		}
	}
}
internal static class MaterialProgression
{
	private static readonly string[] TierOrder = new string[64]
	{
		"Wood", "FineWood", "Flint", "Stone", "Bone", "Rags", "Rag", "Leather", "Copper", "TrollLeather",
		"Troll", "Bronze", "Banded", "HardAntler", "Antler", "Iron", "Root", "Ancient", "Abyssal", "Chitin",
		"Serpentscale", "Serpent", "Silver", "Wolf", "Fenrir", "Fenring", "Fenris", "Frost", "Crystal", "Obsidian",
		"Drake", "Lox", "Berserker", "Bear", "BlackMetal", "Padded", "Needle", "Fang", "Porcupine", "Carapace",
		"Eitr", "Mage", "Himmin", "Mist", "Dvergr", "Vilebone", "Flametal", "Ashlands", "Asksvin", "Embla",
		"Blood", "Bile", "Ooze", "Fire", "Poison", "Bomb", "Firework", "Potion", "Skol", "Cape",
		"Clothes", "Furniture", "Misc", "Special"
	};

	private static readonly Dictionary<string, int> IndexByMaterial = BuildIndex();

	public static int Tier(string setKey)
	{
		if (string.IsNullOrEmpty(setKey))
		{
			return 10000;
		}
		string text = MaterialName(setKey);
		if (string.IsNullOrEmpty(text))
		{
			return 9000;
		}
		if (IndexByMaterial.TryGetValue(text, out var value))
		{
			return value;
		}
		for (int i = 0; i < TierOrder.Length; i++)
		{
			if (text.IndexOf(TierOrder[i], StringComparison.OrdinalIgnoreCase) >= 0 || TierOrder[i].IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return i;
			}
		}
		return 8000;
	}

	public static string MaterialName(string setKey)
	{
		return StripPrefix(setKey);
	}

	private static string StripPrefix(string setKey)
	{
		string text = setKey;
		if (text.StartsWith("Weapon", StringComparison.OrdinalIgnoreCase))
		{
			text = text.Substring("Weapon".Length);
		}
		else if (text.StartsWith("Tool", StringComparison.OrdinalIgnoreCase))
		{
			text = text.Substring("Tool".Length);
		}
		else if (text.StartsWith("Shield", StringComparison.OrdinalIgnoreCase))
		{
			text = text.Substring("Shield".Length);
		}
		else if (text.StartsWith("Armor", StringComparison.OrdinalIgnoreCase))
		{
			text = text.Substring("Armor".Length);
		}
		else if (text.StartsWith("Cape", StringComparison.OrdinalIgnoreCase))
		{
			text = text.Substring("Cape".Length);
		}
		else if (text.StartsWith("Helmet", StringComparison.OrdinalIgnoreCase))
		{
			text = text.Substring("Helmet".Length);
		}
		return text;
	}

	private static Dictionary<string, int> BuildIndex()
	{
		Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
		for (int i = 0; i < TierOrder.Length; i++)
		{
			string key = TierOrder[i];
			if (!dictionary.ContainsKey(key))
			{
				dictionary[key] = i;
			}
		}
		dictionary["Blackmetal"] = (dictionary.ContainsKey("BlackMetal") ? dictionary["BlackMetal"] : 0);
		dictionary["Finewood"] = (dictionary.ContainsKey("FineWood") ? dictionary["FineWood"] : 0);
		return dictionary;
	}
}
internal enum CraftCategory
{
	All,
	Weapons,
	Armor,
	Shields,
	Tools,
	Building,
	Furniture,
	Food,
	Potions,
	Materials,
	Misc
}
internal static class RecipeCategories
{
	public static readonly CraftCategory[] Order = new CraftCategory[11]
	{
		CraftCategory.All,
		CraftCategory.Weapons,
		CraftCategory.Armor,
		CraftCategory.Shields,
		CraftCategory.Tools,
		CraftCategory.Building,
		CraftCategory.Furniture,
		CraftCategory.Food,
		CraftCategory.Potions,
		CraftCategory.Materials,
		CraftCategory.Misc
	};

	public static string Label(CraftCategory cat)
	{
		return cat switch
		{
			CraftCategory.All => "All", 
			CraftCategory.Weapons => "Weapons", 
			CraftCategory.Armor => "Armor", 
			CraftCategory.Shields => "Shields", 
			CraftCategory.Tools => "Tools", 
			CraftCategory.Building => "Building", 
			CraftCategory.Furniture => "Furniture", 
			CraftCategory.Food => "Food", 
			CraftCategory.Potions => "Potions", 
			CraftCategory.Materials => "Materials", 
			_ => "Misc", 
		};
	}

	public static string ShortLabel(CraftCategory cat)
	{
		return Label(cat);
	}

	public static CraftCategory Classify(Recipe recipe)
	{
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Invalid comparison between Unknown and I4
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Invalid comparison between Unknown and I4
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Invalid comparison between Unknown and I4
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Invalid comparison between Unknown and I4
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Invalid comparison between Unknown and I4
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Invalid comparison between Unknown and I4
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Invalid comparison between Unknown and I4
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Invalid comparison between Unknown and I4
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Invalid comparison between Unknown and I4
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Invalid comparison between Unknown and I4
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Invalid comparison between Unknown and I4
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Invalid comparison between Unknown and I4
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Invalid comparison between Unknown and I4
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Invalid comparison between Unknown and I4
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Invalid comparison between Unknown and I4
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Invalid comparison between Unknown and I4
		if (recipe?.m_item?.m_itemData?.m_shared == null)
		{
			return CraftCategory.Misc;
		}
		SharedData shared = recipe.m_item.m_itemData.m_shared;
		ItemType itemType = shared.m_itemType;
		string text = ((Object)recipe.m_item).name ?? "";
		string text2 = (((Object)(object)recipe.m_craftingStation != (Object)null) ? ((Object)recipe.m_craftingStation).name : "");
		if ((int)itemType == 5)
		{
			return CraftCategory.Shields;
		}
		if (LooksToolPrefab(text))
		{
			return CraftCategory.Tools;
		}
		if ((int)itemType == 3 || (int)itemType == 14 || (int)itemType == 4 || (int)itemType == 20 || (int)itemType == 15)
		{
			return CraftCategory.Weapons;
		}
		if ((int)itemType == 6 || (int)itemType == 7 || (int)itemType == 11 || (int)itemType == 17 || (int)itemType == 18)
		{
			if (text.IndexOf("Tool", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return CraftCategory.Tools;
			}
			return CraftCategory.Armor;
		}
		if ((int)itemType == 19)
		{
			return CraftCategory.Tools;
		}
		if ((int)itemType == 2)
		{
			if (IsPotionLike(text, shared.m_name))
			{
				return CraftCategory.Potions;
			}
			return CraftCategory.Food;
		}
		if ((int)itemType == 1)
		{
			return CraftCategory.Materials;
		}
		if ((int)itemType == 9 || (int)itemType == 23)
		{
			return CraftCategory.Weapons;
		}
		if (text2.IndexOf("piece_workbench", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("Workbench", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			if (LooksFurniture(text))
			{
				return CraftCategory.Furniture;
			}
			if (LooksBuilding(text))
			{
				return CraftCategory.Building;
			}
		}
		if (LooksFurniture(text))
		{
			return CraftCategory.Furniture;
		}
		if (LooksBuilding(text))
		{
			return CraftCategory.Building;
		}
		return CraftCategory.Misc;
	}

	public static bool Matches(Recipe recipe, CraftCategory filter)
	{
		if (filter == CraftCategory.All)
		{
			return true;
		}
		return Classify(recipe) == filter;
	}

	private static bool LooksToolPrefab(string prefab)
	{
		string text = prefab ?? "";
		if (text.IndexOf("Pickaxe", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Hammer", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Hoe", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Cultivator", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return text.IndexOf("Scythe", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}

	private static bool IsPotionLike(string prefab, string sharedName)
	{
		string text = prefab + " " + sharedName;
		if (text.IndexOf("Mead", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Potion", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Flask", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Brew", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Tonic", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Poison", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return text.IndexOf("Bait", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}

	private static bool LooksFurniture(string prefab)
	{
		string text = prefab ?? "";
		if (text.IndexOf("chair", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("table", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("bed", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("rug", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("banner", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("candle", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("torch", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("sign", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("chest", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("itemstand", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return text.IndexOf("armorstand", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}

	private static bool LooksBuilding(string prefab)
	{
		string text = prefab ?? "";
		if (text.IndexOf("wood_", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("stone_", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("darkwood_", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("piece_", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("wall", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("floor", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("roof", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("beam", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("pole", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("stair", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return text.IndexOf("door", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}
}
internal static class RecipeSort
{
	private struct Entry
	{
		public Recipe Recipe;

		public int OriginalIndex;

		public int AmmoFirst;

		public int CraftBucket;

		public int MaterialPct;

		public int StationLevel;

		public int CategoryOrder;

		public string SetKey;

		public string MaterialBucket;

		public int PieceOrder;

		public string SortName;
	}

	private static readonly FieldInfo AvailableRecipesField = AccessTools.Field(typeof(InventoryGui), "m_availableRecipes");

	private static readonly FieldInfo RecipeListSpaceField = AccessTools.Field(typeof(InventoryGui), "m_recipeListSpace");

	private static PropertyInfo _recipeProp;

	private static PropertyInfo _elementProp;

	private static bool _propsResolved;

	public static void Apply(List<Recipe> recipes)
	{
		if (recipes == null || Plugin.Settings == null || !Plugin.Settings.EnableMod.Value)
		{
			return;
		}
		if (!DismantleMode.Active)
		{
			StationFilter.Apply(recipes);
			if (Plugin.Settings.EnableCategories.Value)
			{
				CategoryBar.SyncAvailable(recipes);
			}
			if (recipes.Count == 0)
			{
				return;
			}
			if (Plugin.Settings.EnableCategories.Value && CategoryBar.Active != CraftCategory.All)
			{
				for (int num = recipes.Count - 1; num >= 0; num--)
				{
					if (!RecipeCategories.Matches(recipes[num], CategoryBar.Active))
					{
						recipes.RemoveAt(num);
					}
				}
			}
		}
		if (recipes.Count != 0)
		{
			if (DismantleMode.Active)
			{
				DismantleMode.SortPaired(recipes);
			}
			else
			{
				SortInPlace(recipes);
			}
		}
	}

	public static void ReorderGui(InventoryGui gui)
	{
		if ((Object)(object)gui == (Object)null || Plugin.Settings == null || !Plugin.Settings.EnableMod.Value)
		{
			MaterialSectionHeaders.Clear();
		}
		else if (Plugin.Settings.IsVanillaOrder())
		{
			MaterialSectionHeaders.Clear();
		}
		else if (DismantleMode.Active)
		{
			MaterialSectionHeaders.Clear();
		}
		else
		{
			if (AvailableRecipesField == null)
			{
				return;
			}
			if (AvailableRecipesField.GetValue(gui) is IList { Count: not 0 } list)
			{
				List<Recipe> list2 = new List<Recipe>(list.Count);
				List<object> list3 = new List<object>(list.Count);
				List<GameObject> list4 = new List<GameObject>(list.Count);
				for (int i = 0; i < list.Count; i++)
				{
					object obj = list[i];
					Recipe item = ReadRecipe(obj);
					list2.Add(item);
					list3.Add(obj);
					list4.Add(ReadElement(obj));
				}
				List<int> list5 = new List<int>(list2.Count);
				for (int j = 0; j < list2.Count; j++)
				{
					list5.Add(j);
				}
				List<Entry> entries = BuildEntries(list2);
				bool groupArmor = Plugin.Settings.EnableArmorSetGrouping.Value || Plugin.Settings.EnableWeaponSetGrouping.Value || Plugin.Settings.EnableToolSetGrouping.Value || Plugin.Settings.EnableShieldSetGrouping.Value;
				bool craftBuckets = Plugin.Settings.UseCraftBuckets();
				bool alphabetical = Plugin.Settings.IsAlphabetical();
				bool progression = Plugin.Settings.IsProgression();
				bool categoryThen = Plugin.Settings.IsCategoryThenCraftable();
				list5.Sort((int ia, int ib) => Compare(entries[ia], entries[ib], groupArmor, craftBuckets, alphabetical, progression, categoryThen));
				float rowSpace = 30f;
				if (RecipeListSpaceField != null && RecipeListSpaceField.GetValue(gui) is float num && num > 0.01f)
				{
					rowSpace = num;
				}
				list.Clear();
				List<Recipe> list6 = new List<Recipe>(list5.Count);
				List<GameObject> list7 = new List<GameObject>(list5.Count);
				Transform val = null;
				for (int num2 = 0; num2 < list5.Count; num2++)
				{
					int index = list5[num2];
					object value = list3[index];
					list.Add(value);
					list6.Add(list2[index]);
					GameObject val2 = list4[index];
					list7.Add(val2);
					if ((Object)(object)val == (Object)null && (Object)(object)val2 != (Object)null)
					{
						val = val2.transform.parent;
					}
				}
				MaterialSectionHeaders.Apply(val, list6, list7, rowSpace);
				if (Plugin.Settings.DebugLogging.Value)
				{
					Plugin.Log.LogInfo((object)("ReorderGui rows=" + list.Count + " space=" + rowSpace));
					int num3 = Math.Min(12, list5.Count);
					for (int num4 = 0; num4 < num3; num4++)
					{
						Entry entry = entries[list5[num4]];
						Plugin.Log.LogInfo((object)("  #" + num4 + " set=" + (entry.SetKey ?? "-") + " bucket=" + (entry.MaterialBucket ?? "-") + " ammo=" + entry.AmmoFirst + " tier=" + MaterialNameBucket.Tier(entry.MaterialBucket) + " bucketCraft=" + entry.CraftBucket + " piece=" + entry.PieceOrder + " name=" + entry.SortName));
					}
				}
			}
			else
			{
				MaterialSectionHeaders.Clear();
			}
		}
	}

	private static void SortInPlace(List<Recipe> recipes)
	{
		if (recipes.Count > 1 && !Plugin.Settings.IsVanillaOrder())
		{
			List<Entry> list = BuildEntries(recipes);
			bool groupArmor = Plugin.Settings.EnableArmorSetGrouping.Value || Plugin.Settings.EnableWeaponSetGrouping.Value || Plugin.Settings.EnableToolSetGrouping.Value || Plugin.Settings.EnableShieldSetGrouping.Value;
			bool craftBuckets = Plugin.Settings.UseCraftBuckets();
			bool alphabetical = Plugin.Settings.IsAlphabetical();
			bool progression = Plugin.Settings.IsProgression();
			bool categoryThen = Plugin.Settings.IsCategoryThenCraftable();
			list.Sort((Entry a, Entry b) => Compare(a, b, groupArmor, craftBuckets, alphabetical, progression, categoryThen));
			recipes.Clear();
			for (int num = 0; num < list.Count; num++)
			{
				recipes.Add(list[num].Recipe);
			}
		}
	}

	private static List<Entry> BuildEntries(List<Recipe> recipes)
	{
		Player localPlayer = Player.m_localPlayer;
		string[] orderTokens = ArmorSetDetector.ParseOrder(Plugin.Settings.ArmorPieceOrder.Value);
		string[] orderTokens2 = WeaponSetDetector.ParseOrder(Plugin.Settings.WeaponPieceOrder.Value);
		string[] orderTokens3 = ToolSetDetector.ParseOrder(Plugin.Settings.ToolPieceOrder.Value);
		string[] orderTokens4 = ShieldSetDetector.ParseOrder(Plugin.Settings.ShieldPieceOrder.Value);
		bool value = Plugin.Settings.EnableArmorSetGrouping.Value;
		bool value2 = Plugin.Settings.EnableWeaponSetGrouping.Value;
		bool value3 = Plugin.Settings.EnableToolSetGrouping.Value;
		bool value4 = Plugin.Settings.EnableShieldSetGrouping.Value;
		bool value5 = Plugin.Settings.GroupModdedArmorSets.Value;
		bool value6 = Plugin.Settings.GroupModdedWeaponSets.Value;
		bool flag = Plugin.Settings.UseCraftBuckets();
		bool flag2 = Plugin.Settings.IsCategoryThenCraftable();
		List<Entry> list = new List<Entry>(recipes.Count);
		for (int i = 0; i < recipes.Count; i++)
		{
			Recipe val = recipes[i];
			string text = null;
			int pieceOrder = 0;
			if (value)
			{
				text = ArmorSetDetector.SetKey(val, value5);
				if (!string.IsNullOrEmpty(text))
				{
					pieceOrder = ArmorSetDetector.PieceOrder(val, orderTokens);
				}
			}
			if (string.IsNullOrEmpty(text) && value2)
			{
				if (WeaponSetDetector.IsAmmo(val))
				{
					text = "WeaponAmmo";
					pieceOrder = WeaponSetDetector.PieceOrder(val, orderTokens2);
				}
				else
				{
					text = WeaponSetDetector.SetKey(val, value6);
					if (!string.IsNullOrEmpty(text))
					{
						pieceOrder = WeaponSetDetector.PieceOrder(val, orderTokens2);
					}
				}
			}
			if (string.IsNullOrEmpty(text) && value3)
			{
				text = ToolSetDetector.SetKey(val, value6);
				if (!string.IsNullOrEmpty(text))
				{
					pieceOrder = ToolSetDetector.PieceOrder(val, orderTokens3);
				}
			}
			if (string.IsNullOrEmpty(text) && value4)
			{
				text = ShieldSetDetector.SetKey(val, value6);
				if (!string.IsNullOrEmpty(text))
				{
					pieceOrder = ShieldSetDetector.PieceOrder(val, orderTokens4);
				}
			}
			list.Add(new Entry
			{
				Recipe = val,
				OriginalIndex = i,
				AmmoFirst = ((!WeaponSetDetector.IsAmmo(val)) ? 1 : 0),
				CraftBucket = (flag ? Craftability.ScoreBucket(localPlayer, val) : 0),
				MaterialPct = (flag ? Craftability.MaterialPercent(localPlayer, val) : 0),
				StationLevel = (((Object)(object)val != (Object)null) ? val.m_minStationLevel : 0),
				CategoryOrder = (int)(flag2 ? RecipeCategories.Classify(val) : CraftCategory.All),
				SetKey = text,
				MaterialBucket = MaterialNameBucket.Resolve(val),
				PieceOrder = pieceOrder,
				SortName = DisplayName(val)
			});
		}
		return list;
	}

	private static int Compare(Entry a, Entry b, bool groupArmor, bool craftBuckets, bool alphabetical, bool progression, bool categoryThen)
	{
		if (categoryThen)
		{
			int num = a.CategoryOrder.CompareTo(b.CategoryOrder);
			if (num != 0)
			{
				return num;
			}
		}
		if (Plugin.Settings != null && (Plugin.Settings.EnableMaterialSectionHeaders.Value || Plugin.Settings.EnableWeaponSetGrouping.Value || Plugin.Settings.EnableArmorSetGrouping.Value || Plugin.Settings.EnableToolSetGrouping.Value || Plugin.Settings.EnableShieldSetGrouping.Value))
		{
			int num2 = CompareNameBuckets(a, b, craftBuckets);
			if (num2 != 0)
			{
				return num2;
			}
		}
		else
		{
			if (craftBuckets)
			{
				int num3 = a.CraftBucket.CompareTo(b.CraftBucket);
				if (num3 != 0)
				{
					return num3;
				}
			}
			if (groupArmor)
			{
				int num4 = a.AmmoFirst.CompareTo(b.AmmoFirst);
				if (num4 != 0)
				{
					return num4;
				}
			}
			bool flag = groupArmor && !string.IsNullOrEmpty(a.SetKey);
			bool flag2 = groupArmor && !string.IsNullOrEmpty(b.SetKey);
			if (flag && flag2)
			{
				int num5 = CompareMaterialThenPiece(a, b);
				if (num5 != 0)
				{
					return num5;
				}
			}
			else if (groupArmor && (flag || flag2))
			{
				if (flag)
				{
					return -1;
				}
				return 1;
			}
		}
		if (craftBuckets)
		{
			int num6 = b.MaterialPct.CompareTo(a.MaterialPct);
			if (num6 != 0)
			{
				return num6;
			}
		}
		if (progression)
		{
			int num7 = a.StationLevel.CompareTo(b.StationLevel);
			if (num7 != 0)
			{
				return num7;
			}
		}
		int num8 = string.Compare(a.SortName, b.SortName, StringComparison.OrdinalIgnoreCase);
		if (num8 != 0)
		{
			return num8;
		}
		return a.OriginalIndex.CompareTo(b.OriginalIndex);
	}

	private static int CompareNameBuckets(Entry a, Entry b, bool craftBuckets)
	{
		if (craftBuckets)
		{
			int num = a.CraftBucket.CompareTo(b.CraftBucket);
			if (num != 0)
			{
				return num;
			}
		}
		bool flag = !string.IsNullOrEmpty(a.MaterialBucket);
		bool flag2 = !string.IsNullOrEmpty(b.MaterialBucket);
		if (flag != flag2)
		{
			if (!flag)
			{
				return 1;
			}
			return -1;
		}
		if (!flag)
		{
			return 0;
		}
		if (!string.Equals(a.MaterialBucket, b.MaterialBucket, StringComparison.OrdinalIgnoreCase))
		{
			int num2 = MaterialNameBucket.Tier(a.MaterialBucket);
			int value = MaterialNameBucket.Tier(b.MaterialBucket);
			int num3 = num2.CompareTo(value);
			if (num3 != 0)
			{
				return num3;
			}
			return string.Compare(a.MaterialBucket, b.MaterialBucket, StringComparison.OrdinalIgnoreCase);
		}
		if (a.MaterialBucket.Equals("Tools", StringComparison.OrdinalIgnoreCase))
		{
			int num4 = MaterialNameBucket.NestedToolTier(a.Recipe);
			int value2 = MaterialNameBucket.NestedToolTier(b.Recipe);
			int num5 = num4.CompareTo(value2);
			if (num5 != 0)
			{
				return num5;
			}
		}
		int num6 = MaterialKind(a.SetKey).CompareTo(MaterialKind(b.SetKey));
		if (num6 != 0)
		{
			return num6;
		}
		int num7 = a.PieceOrder.CompareTo(b.PieceOrder);
		if (num7 != 0)
		{
			return num7;
		}
		return string.Compare(a.SortName, b.SortName, StringComparison.OrdinalIgnoreCase);
	}

	private static int CompareMaterialThenPiece(Entry a, Entry b)
	{
		string text = MaterialProgression.MaterialName(a.SetKey);
		string text2 = MaterialProgression.MaterialName(b.SetKey);
		if (string.Equals(text, text2, StringComparison.OrdinalIgnoreCase))
		{
			int num = MaterialKind(a.SetKey).CompareTo(MaterialKind(b.SetKey));
			if (num != 0)
			{
				return num;
			}
			int num2 = a.PieceOrder.CompareTo(b.PieceOrder);
			if (num2 != 0)
			{
				return num2;
			}
			return string.Compare(a.SortName, b.SortName, StringComparison.OrdinalIgnoreCase);
		}
		int num3 = MaterialProgression.Tier(a.SetKey);
		int value = MaterialProgression.Tier(b.SetKey);
		int num4 = num3.CompareTo(value);
		if (num4 != 0)
		{
			return num4;
		}
		return string.Compare(text, text2, StringComparison.OrdinalIgnoreCase);
	}

	private static int MaterialKind(string setKey)
	{
		if (string.IsNullOrEmpty(setKey))
		{
			return 50;
		}
		if (setKey.Equals("WeaponAmmo", StringComparison.OrdinalIgnoreCase))
		{
			return 0;
		}
		if (setKey.StartsWith("Weapon", StringComparison.OrdinalIgnoreCase))
		{
			return 1;
		}
		if (setKey.StartsWith("Shield", StringComparison.OrdinalIgnoreCase))
		{
			return 2;
		}
		if (setKey.StartsWith("Tool", StringComparison.OrdinalIgnoreCase))
		{
			return 3;
		}
		if (setKey.StartsWith("Armor", StringComparison.OrdinalIgnoreCase) || setKey.StartsWith("Cape", StringComparison.OrdinalIgnoreCase) || setKey.StartsWith("Helmet", StringComparison.OrdinalIgnoreCase))
		{
			return 4;
		}
		return 40;
	}

	private static string DisplayName(Recipe recipe)
	{
		try
		{
			if (recipe?.m_item?.m_itemData?.m_shared != null)
			{
				return recipe.m_item.m_itemData.m_shared.m_name ?? "";
			}
		}
		catch
		{
		}
		if (!((Object)(object)recipe?.m_item != (Object)null))
		{
			return "";
		}
		return ((Object)recipe.m_item).name;
	}

	private static void EnsurePairProps(object sample)
	{
		if (!_propsResolved && sample != null)
		{
			Type type = sample.GetType();
			_recipeProp = AccessTools.Property(type, "Recipe");
			_elementProp = AccessTools.Property(type, "InterfaceElement");
			_propsResolved = true;
		}
	}

	private static Recipe ReadRecipe(object pair)
	{
		if (pair == null)
		{
			return null;
		}
		EnsurePairProps(pair);
		if (_recipeProp != null)
		{
			object? value = _recipeProp.GetValue(pair, null);
			return (Recipe)((value is Recipe) ? value : null);
		}
		FieldInfo fieldInfo = AccessTools.Field(pair.GetType(), "<Recipe>k__BackingField");
		if (!(fieldInfo != null))
		{
			return null;
		}
		object? value2 = fieldInfo.GetValue(pair);
		return (Recipe)((value2 is Recipe) ? value2 : null);
	}

	private static GameObject ReadElement(object pair)
	{
		if (pair == null)
		{
			return null;
		}
		EnsurePairProps(pair);
		object obj = null;
		if (_elementProp != null)
		{
			obj = _elementProp.GetValue(pair, null);
		}
		else
		{
			FieldInfo fieldInfo = AccessTools.Field(pair.GetType(), "<InterfaceElement>k__BackingField");
			if (fieldInfo != null)
			{
				obj = fieldInfo.GetValue(pair);
			}
		}
		GameObject val = (GameObject)((obj is GameObject) ? obj : null);
		if (val != null)
		{
			return val;
		}
		Component val2 = (Component)((obj is Component) ? obj : null);
		if (val2 != null)
		{
			return val2.gameObject;
		}
		return null;
	}
}
internal static class ShieldSetDetector
{
	private static readonly string[] MaterialStems = new string[20]
	{
		"BlackMetal", "Flametal", "Carapace", "Serpentscale", "Serpent", "Crystal", "Bronze", "Copper", "Silver", "Iron",
		"Banded", "Bone", "Chitin", "Abyssal", "FineWood", "Wood", "Ancient", "Ashlands", "Asksvin", "Flametal"
	};

	private static readonly string[] ShieldTypeTokens = new string[2] { "TowerShield", "Shield" };

	private static readonly Regex SharedCleanup = new Regex("^\\$?(item[_-]?)?", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Regex SharedShieldTrim = new Regex("[_-]?(towershield|shield)$", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Regex SharedShieldPrefix = new Regex("^(towershield|shield)[_-]", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	public static string SetKey(Recipe recipe, bool groupModded)
	{
		if ((Object)(object)recipe == (Object)null || !IsShield(recipe))
		{
			return null;
		}
		string text = PrefabName(recipe) ?? "";
		string text2 = SharedName(recipe) ?? "";
		string blob = (text + " " + text2).Trim();
		string text3 = FromPrefab(text);
		if (string.IsNullOrEmpty(text3))
		{
			text3 = FromShared(text2);
		}
		if (string.IsNullOrEmpty(text3))
		{
			text3 = FromKnownStem(blob);
		}
		if (string.IsNullOrEmpty(text3) || text3.Length < 3)
		{
			return null;
		}
		text3 = CanonicalizeMaterial(text3);
		if (string.IsNullOrEmpty(text3))
		{
			return null;
		}
		if (text3.Equals("Banded", StringComparison.OrdinalIgnoreCase))
		{
			text3 = "Bronze";
		}
		if (!groupModded && !IsKnownMaterial(text3) && !LooksVanillaShieldPrefab(text))
		{
			return null;
		}
		return "Shield" + text3;
	}

	public static int PieceOrder(Recipe recipe, string[] orderTokens)
	{
		string obj = PrefabName(recipe) ?? "";
		string text = SharedName(recipe) ?? "";
		string text2 = obj + " " + text;
		for (int i = 0; i < orderTokens.Length; i++)
		{
			string text3 = orderTokens[i];
			if (!string.IsNullOrEmpty(text3) && !text3.Equals("Other", StringComparison.OrdinalIgnoreCase) && text2.IndexOf(text3, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return i;
			}
		}
		if (text2.IndexOf("Tower", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			if (IndexOf(orderTokens, "Tower") >= orderTokens.Length)
			{
				return orderTokens.Length + 5;
			}
			return IndexOf(orderTokens, "Tower");
		}
		return IndexOf(orderTokens, "Shield");
	}

	public static string[] ParseOrder(string csv)
	{
		if (string.IsNullOrEmpty(csv))
		{
			return DefaultOrder();
		}
		string[] array = csv.Split(new char[1] { ',' });
		List<string> list = new List<string>();
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length > 0)
			{
				list.Add(text);
			}
		}
		if (list.Count == 0)
		{
			return DefaultOrder();
		}
		return list.ToArray();
	}

	public static bool IsShield(Recipe recipe)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Invalid comparison between Unknown and I4
		if ((Object)(object)recipe == (Object)null)
		{
			return false;
		}
		if ((int)GetItemType(recipe) == 5)
		{
			return true;
		}
		return (PrefabName(recipe) ?? "").IndexOf("Shield", StringComparison.OrdinalIgnoreCase) >= 0;
	}

	private static string[] DefaultOrder()
	{
		return new string[3] { "Shield", "Tower", "Other" };
	}

	private static string FromPrefab(string prefab)
	{
		if (string.IsNullOrEmpty(prefab))
		{
			return null;
		}
		for (int i = 0; i < ShieldTypeTokens.Length; i++)
		{
			string text = ShieldTypeTokens[i];
			if (prefab.StartsWith(text, StringComparison.OrdinalIgnoreCase) && prefab.Length > text.Length)
			{
				return prefab.Substring(text.Length);
			}
			if (prefab.EndsWith(text, StringComparison.OrdinalIgnoreCase) && prefab.Length > text.Length + 2)
			{
				return prefab.Substring(0, prefab.Length - text.Length);
			}
		}
		return FromKnownStem(prefab);
	}

	private static string FromShared(string shared)
	{
		if (string.IsNullOrEmpty(shared))
		{
			return null;
		}
		string text = SharedCleanup.Replace(shared, "");
		for (int i = 0; i < 3; i++)
		{
			string input = SharedShieldTrim.Replace(text, "");
			input = SharedShieldPrefix.Replace(input, "");
			input = input.Trim('_', '-', ' ');
			if (input == text)
			{
				break;
			}
			text = input;
		}
		if (text.Length < 3)
		{
			return null;
		}
		return text;
	}

	private static string FromKnownStem(string blob)
	{
		if (string.IsNullOrEmpty(blob))
		{
			return null;
		}
		for (int i = 0; i < MaterialStems.Length; i++)
		{
			if (blob.IndexOf(MaterialStems[i], StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return MaterialStems[i];
			}
		}
		return null;
	}

	private static string CanonicalizeMaterial(string raw)
	{
		if (string.IsNullOrEmpty(raw))
		{
			return null;
		}
		string text = raw.Replace("-", "_");
		if (text.IndexOf('_') >= 0)
		{
			string[] array = text.Split(new char[1] { '_' });
			StringBuilder stringBuilder = new StringBuilder();
			foreach (string text2 in array)
			{
				if (!string.IsNullOrEmpty(text2))
				{
					stringBuilder.Append(char.ToUpperInvariant(text2[0]));
					if (text2.Length > 1)
					{
						stringBuilder.Append(text2.Substring(1));
					}
				}
			}
			text = stringBuilder.ToString();
		}
		if (text.Length > 0 && char.IsLower(text[0]))
		{
			text = char.ToUpperInvariant(text[0]) + text.Substring(1);
		}
		if (text.Equals("Blackmetal", StringComparison.OrdinalIgnoreCase))
		{
			return "BlackMetal";
		}
		if (text.Equals("Finewood", StringComparison.OrdinalIgnoreCase))
		{
			return "FineWood";
		}
		if (text.Equals("Serpentscale", StringComparison.OrdinalIgnoreCase) || text.Equals("Serpent", StringComparison.OrdinalIgnoreCase))
		{
			return "Serpentscale";
		}
		return text;
	}

	private static bool IsKnownMaterial(string mat)
	{
		for (int i = 0; i < MaterialStems.Length; i++)
		{
			if (mat.Equals(MaterialStems[i], StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
		}
		return mat.Equals("Bronze", StringComparison.OrdinalIgnoreCase);
	}

	private static bool LooksVanillaShieldPrefab(string prefab)
	{
		if (string.IsNullOrEmpty(prefab))
		{
			return false;
		}
		if (!prefab.StartsWith("Shield", StringComparison.OrdinalIgnoreCase))
		{
			return prefab.StartsWith("TowerShield", StringComparison.OrdinalIgnoreCase);
		}
		return true;
	}

	private static string PrefabName(Recipe recipe)
	{
		if ((Object)(object)recipe?.m_item == (Object)null)
		{
			return null;
		}
		string name = ((Object)recipe.m_item).name;
		if (!string.IsNullOrEmpty(name))
		{
			return name;
		}
		if (!((Object)(object)((Component)recipe.m_item).gameObject != (Object)null))
		{
			return null;
		}
		return ((Object)((Component)recipe.m_item).gameObject).name;
	}

	private static string SharedName(Recipe recipe)
	{
		try
		{
			return recipe?.m_item?.m_itemData?.m_shared?.m_name;
		}
		catch
		{
			return null;
		}
	}

	private static ItemType GetItemType(Recipe recipe)
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (recipe?.m_item?.m_itemData?.m_shared != null)
			{
				return recipe.m_item.m_itemData.m_shared.m_itemType;
			}
		}
		catch
		{
		}
		return (ItemType)0;
	}

	private static int IndexOf(string[] order, string token)
	{
		for (int i = 0; i < order.Length; i++)
		{
			if (order[i].Equals(token, StringComparison.OrdinalIgnoreCase))
			{
				return i;
			}
		}
		return order.Length + 5;
	}
}
internal static class StationFilter
{
	public static void Apply(List<Recipe> recipes)
	{
		if (recipes == null || recipes.Count == 0)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation();
		for (int num = recipes.Count - 1; num >= 0; num--)
		{
			if (!BelongsToStation(recipes[num], currentCraftingStation))
			{
				recipes.RemoveAt(num);
			}
		}
	}

	public static bool BelongsToStation(Recipe recipe, CraftingStation current)
	{
		if ((Object)(object)recipe == (Object)null)
		{
			return false;
		}
		CraftingStation craftingStation = recipe.m_craftingStation;
		if ((Object)(object)current == (Object)null)
		{
			return (Object)(object)craftingStation == (Object)null;
		}
		if ((Object)(object)craftingStation != (Object)null)
		{
			if (string.IsNullOrEmpty(craftingStation.m_name) || string.IsNullOrEmpty(current.m_name))
			{
				return false;
			}
			return craftingStation.m_name == current.m_name;
		}
		return current.m_showBasicRecipies;
	}
}
internal static class ToolSetDetector
{
	private static readonly string[] MaterialStems = new string[21]
	{
		"BlackMetal", "Flametal", "Carapace", "Crystal", "Bronze", "Copper", "Silver", "Iron", "Flint", "Chitin",
		"Bone", "Abyssal", "Needle", "FineWood", "Wood", "HardAntler", "Antler", "Obsidian", "Ancient", "Ashlands",
		"Asksvin"
	};

	private static readonly string[] ToolTypeTokens = new string[5] { "Cultivator", "Pickaxe", "Hammer", "Scythe", "Hoe" };

	private static readonly Regex SharedCleanup = new Regex("^\\$?(item[_-]?)?", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Regex SharedToolTrim = new Regex("[_-]?(cultivator|pickaxe|hammer|scythe|hoe)$", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	private static readonly Regex SharedToolPrefix = new Regex("^(cultivator|pickaxe|hammer|scythe|hoe)[_-]", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	public static string SetKey(Recipe recipe, bool groupModded)
	{
		if ((Object)(object)recipe == (Object)null || !IsTool(recipe))
		{
			return null;
		}
		string text = PrefabName(recipe) ?? "";
		string text2 = SharedName(recipe) ?? "";
		string blob = (text + " " + text2).Trim();
		string text3 = FromPrefab(text);
		if (string.IsNullOrEmpty(text3))
		{
			text3 = FromShared(text2);
		}
		if (string.IsNullOrEmpty(text3))
		{
			text3 = FromKnownStem(blob);
		}
		if (string.IsNullOrEmpty(text3) && IsBareEarlyTool(text))
		{
			text3 = "Wood";
		}
		if (string.IsNullOrEmpty(text3) || text3.Length < 3)
		{
			return null;
		}
		text3 = CanonicalizeMaterial(text3);
		if (string.IsNullOrEmpty(text3))
		{
			return null;
		}
		if (!groupModded && !IsKnownMaterial(text3) && !LooksVanillaToolPrefab(text))
		{
			return null;
		}
		return "Tool" + text3;
	}

	public static int PieceOrder(Recipe recipe, string[] orderTokens)
	{
		string obj = PrefabName(recipe) ?? "";
		string text = SharedName(recipe) ?? "";
		string text2 = obj + " " + text;
		for (int i = 0; i < orderTokens.Length; i++)
		{
			string text3 = orderTokens[i];
			if (!string.IsNullOrEmpty(text3) && !text3.Equals("Other", StringComparison.OrdinalIgnoreCase) && text2.IndexOf(text3, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return i;
			}
		}
		return orderTokens.Length + 10;
	}

	public static string[] ParseOrder(string csv)
	{
		if (string.IsNullOrEmpty(csv))
		{
			return DefaultOrder();
		}
		string[] array = csv.Split(new char[1] { ',' });
		List<string> list = new List<string>();
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length > 0)
			{
				list.Add(text);
			}
		}
		if (list.Count == 0)
		{
			return DefaultOrder();
		}
		return list.ToArray();
	}

	public static bool IsTool(Recipe recipe)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Invalid comparison between Unknown and I4
		if ((Object)(object)recipe == (Object)null)
		{
			return false;
		}
		if (LooksToolPrefab(PrefabName(recipe) ?? ""))
		{
			return true;
		}
		re