Decompiled source of WorkbenchesPlus v1.1.1

plugins/WorkbenchesPlus/WorkbenchesPlus.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
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.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1+dd4b6fabf4f0a7d56ccab09662a74f4c59fd0472")]
[assembly: AssemblyProduct("Workbenches+")]
[assembly: AssemblyTitle("WorkbenchesPlus")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.1.0")]
[module: UnverifiableCode]
namespace WorkbenchesPlus;

internal static class PathOfValheimanCompat
{
	private const string Guid = "treextr.pathofvalheiman";

	private static bool _bound;

	private static bool _loaded;

	private static PropertyInfo _salvaging;

	private static PropertyInfo _gridOn;

	private static PropertyInfo _salvageTab;

	private static MethodInfo _setSalvaging;

	private static MethodInfo _isRuneCarver;

	private static MethodInfo _placeSalvage;

	public const string SalvageTabName = "PT_TabSalvage";

	public static bool Loaded
	{
		get
		{
			Bind();
			return _loaded;
		}
	}

	public static bool Salvaging
	{
		get
		{
			if (Loaded)
			{
				return Read(_salvaging);
			}
			return false;
		}
	}

	public static bool CategoryGridOn
	{
		get
		{
			if (Loaded)
			{
				return Read(_gridOn);
			}
			return false;
		}
	}

	public static bool OwnsList(InventoryGui gui)
	{
		if (!Loaded)
		{
			return false;
		}
		if (Salvaging)
		{
			return true;
		}
		if (CategoryGridOn && (Object)(object)gui != (Object)null && gui.InCraftTab())
		{
			return !DismantleMode.Active;
		}
		return false;
	}

	public static bool SalvageHiddenHere()
	{
		if (Plugin.Settings == null || !Plugin.Settings.HidePathOfValheimanSalvage.Value || !Loaded)
		{
			return false;
		}
		if (!Plugin.Settings.EnableMod.Value || !Plugin.Settings.EnableDismantle.Value || CategoryGridOn)
		{
			return false;
		}
		Player localPlayer = Player.m_localPlayer;
		CraftingStation val = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetCurrentCraftingStation() : null);
		if ((Object)(object)val != (Object)null && !StationFilter.IsUpgrader(val))
		{
			return !IsRuneCarver(val);
		}
		return false;
	}

	public static void PlaceSalvageTab(InventoryGui gui)
	{
		if ((Object)(object)gui == (Object)null || !Loaded || _placeSalvage == null)
		{
			return;
		}
		try
		{
			_placeSalvage.Invoke(null, new object[1] { gui });
		}
		catch
		{
		}
	}

	public static void EnforceSalvageHidden(InventoryGui gui)
	{
		if ((Object)(object)gui == (Object)null || _salvageTab == null || !SalvageHiddenHere())
		{
			return;
		}
		Button val;
		try
		{
			object? value = _salvageTab.GetValue(null, null);
			val = (Button)((value is Button) ? value : null);
		}
		catch
		{
			return;
		}
		if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeSelf)
		{
			return;
		}
		((Component)val).gameObject.SetActive(false);
		if (!Salvaging || !(_setSalvaging != null))
		{
			return;
		}
		try
		{
			_setSalvaging.Invoke(null, new object[1] { false });
			gui.OnTabCraftPressed();
		}
		catch
		{
		}
	}

	private static bool IsRuneCarver(CraftingStation station)
	{
		if (_isRuneCarver == null)
		{
			return false;
		}
		try
		{
			object obj = _isRuneCarver.Invoke(null, new object[1] { station });
			bool flag = default(bool);
			int num;
			if (obj is bool)
			{
				flag = (bool)obj;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}
		catch
		{
			return false;
		}
	}

	private static bool Read(PropertyInfo p)
	{
		if (p == null)
		{
			return false;
		}
		try
		{
			object value = p.GetValue(null, null);
			bool flag = default(bool);
			int num;
			if (value is bool)
			{
				flag = (bool)value;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}
		catch
		{
			return false;
		}
	}

	private static void Bind()
	{
		if (_bound)
		{
			return;
		}
		_bound = true;
		if (Chainloader.PluginInfos.TryGetValue("treextr.pathofvalheiman", out var value) && value != null && !((Object)(object)value.Instance == (Object)null))
		{
			_loaded = true;
			Assembly assembly = ((object)value.Instance).GetType().Assembly;
			_salvaging = assembly.GetType("PassiveTree.CraftSalvage")?.GetProperty("Salvaging", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_gridOn = assembly.GetType("PassiveTree.UI.CraftCategories")?.GetProperty("On", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			Type? type = assembly.GetType("PassiveTree.CraftSalvage");
			_salvageTab = type?.GetProperty("Tab", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_setSalvaging = type?.GetMethod("TestSalvaging", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_placeSalvage = type?.GetMethod("Place", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(InventoryGui) }, null);
			_isRuneCarver = assembly.GetType("PassiveTree.RuneCarverTable")?.GetMethod("Is", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(CraftingStation) }, null);
			if (Plugin.Log != null)
			{
				Plugin.Log.LogInfo((object)("Path of Valheiman found: Workbenches+ steps aside while its Salvage tab or category grid is shown" + ((_salvaging == null || _gridOn == null) ? " (some hooks not found, check after updates)." : ".")));
			}
		}
	}
}
internal static class PathOfValheimanTooltips
{
	private static readonly FieldInfo AvailableRecipesField = AccessTools.Field(typeof(InventoryGui), "m_availableRecipes");

	private static readonly MethodInfo CreateItemTooltip = AccessTools.Method(typeof(InventoryGrid), "CreateItemTooltip", (Type[])null, (Type[])null);

	private static bool _bound;

	private static MethodInfo _markPreview;

	private static bool _failed;

	public static void Apply(InventoryGui gui)
	{
		if (_failed || (Object)(object)gui == (Object)null || !PathOfValheimanCompat.Loaded || PathOfValheimanCompat.OwnsList(gui) || AvailableRecipesField == null || CreateItemTooltip == null || (Object)(object)gui.m_playerGrid == (Object)null)
		{
			return;
		}
		Bind();
		if (!(AvailableRecipesField.GetValue(gui) is IList { Count: not 0 } list))
		{
			return;
		}
		GameObject val = ItemTooltipPrefab(gui);
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		try
		{
			for (int i = 0; i < list.Count; i++)
			{
				object pair = list[i];
				Recipe val2 = Read<Recipe>(pair, "Recipe");
				GameObject val3 = Read<GameObject>(pair, "InterfaceElement");
				if ((Object)(object)val2 == (Object)null || (Object)(object)val2.m_item == (Object)null || (Object)(object)val3 == (Object)null)
				{
					continue;
				}
				ItemData val4 = Read<ItemData>(pair, "ItemData") ?? Preview(val2);
				if (val4 != null)
				{
					UITooltip val5 = val3.GetComponent<UITooltip>();
					if ((Object)(object)val5 == (Object)null)
					{
						val5 = val3.AddComponent<UITooltip>();
					}
					if ((Object)(object)val5.m_tooltipPrefab == (Object)null)
					{
						val5.m_tooltipPrefab = val;
					}
					CreateItemTooltip.Invoke(gui.m_playerGrid, new object[2] { val4, val5 });
				}
			}
		}
		catch (Exception ex)
		{
			_failed = true;
			Plugin.Log.LogWarning((object)("Path of Valheiman tooltips disabled: " + ex.Message));
		}
	}

	private static ItemData Preview(Recipe recipe)
	{
		ItemData val = recipe.m_item.m_itemData.Clone();
		val.m_quality = 1;
		val.m_durability = val.GetMaxDurability();
		val.m_dropPrefab = ((Component)recipe.m_item).gameObject;
		if (_markPreview != null)
		{
			_markPreview.Invoke(null, new object[1] { val });
		}
		return val;
	}

	private static GameObject ItemTooltipPrefab(InventoryGui gui)
	{
		GameObject elementPrefab = gui.m_playerGrid.m_elementPrefab;
		UITooltip val = (((Object)(object)elementPrefab != (Object)null) ? elementPrefab.GetComponentInChildren<UITooltip>(true) : null);
		if (!((Object)(object)val != (Object)null))
		{
			return null;
		}
		return val.m_tooltipPrefab;
	}

	private static T Read<T>(object pair, string property) where T : class
	{
		PropertyInfo propertyInfo = pair?.GetType().GetProperty(property);
		if (!(propertyInfo != null))
		{
			return null;
		}
		return propertyInfo.GetValue(pair, null) as T;
	}

	private static void Bind()
	{
		if (!_bound)
		{
			_bound = true;
			if (Chainloader.PluginInfos.TryGetValue("treextr.pathofvalheiman", out var value) && !((Object)(object)((value != null) ? value.Instance : null) == (Object)null))
			{
				Type type = ((object)value.Instance).GetType().Assembly.GetType("PassiveTree.Implicits");
				_markPreview = ((type != null) ? type.GetMethod("MarkPreview", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : null);
			}
		}
	}
}
internal static class LayoutOverrides
{
	internal sealed class VEntry
	{
		public string Path;

		public float L;

		public float T;

		public float R;

		public float B;

		public bool Sub;
	}

	private const string FileName = "WorkbenchesPlus_layout.json";

	private static readonly Regex Pair = new Regex("\"([A-Za-z0-9_.]+)\"\\s*:\\s*(-?[0-9]+(?:\\.[0-9]+)?)", RegexOptions.Compiled);

	private static readonly Regex VanillaObj = new Regex("\\{[^{}]*\"path\"[^{}]*\\}", RegexOptions.Compiled);

	private static readonly Regex PathPair = new Regex("\"path\"\\s*:\\s*\"([^\"]+)\"", RegexOptions.Compiled);

	public static readonly List<VEntry> Vanilla = new List<VEntry>();

	public static int Version;

	private static readonly Dictionary<string, float> Values = new Dictionary<string, float>();

	private static bool _loaded;

	private static float _nextCheck;

	private static DateTime _fileStamp;

	private static string FilePath => Path.Combine(Paths.ConfigPath, "WorkbenchesPlus_layout.json");

	public static float Get(string key, float fallback)
	{
		EnsureLoaded();
		if (!Values.TryGetValue(key, out var value))
		{
			return fallback;
		}
		return value;
	}

	public static void Nudge(RectTransform rt, string name)
	{
		//IL_000c: 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_0040: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)rt == (Object)null))
		{
			rt.anchoredPosition += new Vector2(Get(name + ".dx", 0f), Get(name + ".dy", 0f));
		}
	}

	public static void Refresh()
	{
		EnsureLoaded();
	}

	private static void EnsureLoaded()
	{
		if (_loaded && Time.unscaledTime < _nextCheck)
		{
			return;
		}
		_nextCheck = Time.unscaledTime + 1f;
		string filePath = FilePath;
		bool flag = File.Exists(filePath);
		DateTime dateTime = (flag ? File.GetLastWriteTimeUtc(filePath) : DateTime.MinValue);
		if (_loaded && dateTime == _fileStamp)
		{
			return;
		}
		_loaded = true;
		_fileStamp = dateTime;
		Values.Clear();
		Vanilla.Clear();
		Version++;
		string text = null;
		try
		{
			if (flag)
			{
				text = File.ReadAllText(filePath);
			}
			else
			{
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("WorkbenchesPlus.layout.json");
				if (stream != null)
				{
					using StreamReader streamReader = new StreamReader(stream);
					text = streamReader.ReadToEnd();
				}
			}
		}
		catch
		{
			text = null;
		}
		if (string.IsNullOrEmpty(text))
		{
			return;
		}
		foreach (Match item in VanillaObj.Matches(text))
		{
			Match match2 = PathPair.Match(item.Value);
			if (!match2.Success)
			{
				continue;
			}
			VEntry vEntry = new VEntry
			{
				Path = match2.Groups[1].Value
			};
			foreach (Match item2 in Pair.Matches(item.Value))
			{
				if (float.TryParse(item2.Groups[2].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					switch (item2.Groups[1].Value)
					{
					case "l":
						vEntry.L = result;
						break;
					case "t":
						vEntry.T = result;
						break;
					case "r":
						vEntry.R = result;
						break;
					case "b":
						vEntry.B = result;
						break;
					case "sub":
						vEntry.Sub = result > 0.5f;
						break;
					}
				}
			}
			Vanilla.Add(vEntry);
		}
		foreach (Match item3 in Pair.Matches(text))
		{
			if (float.TryParse(item3.Groups[2].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
			{
				Values[item3.Groups[1].Value] = result2;
			}
		}
	}
}
internal static class Loc
{
	private static readonly Dictionary<string, string> English = new Dictionary<string, string>
	{
		{ "dismantle", "DISMANTLE" },
		{ "dismantled", "Dismantled" },
		{ "dismantle_failed", "Dismantle failed" },
		{ "build_range", "Build range" },
		{ "default", "default" },
		{ "no_access", "You do not have access here" },
		{ "now", "now" },
		{ "search", "Search..." },
		{ "favorites", "Favorites" },
		{ "biome0", "Meadows" },
		{ "biome1", "Black Forest" },
		{ "biome2", "Swamp" },
		{ "biome3", "Mountain" },
		{ "biome4", "Plains" },
		{ "biome5", "Mistlands" },
		{ "biome6", "Ashlands" },
		{ "biome7", "Deep North" },
		{ "sub_OneHand", "1-Hand" },
		{ "sub_TwoHand", "2-Hand" },
		{ "sub_Knives", "Knives" },
		{ "sub_Swords", "Swords" },
		{ "sub_Clubs", "Clubs" },
		{ "sub_Axes", "Axes" },
		{ "sub_Spears", "Spears" },
		{ "sub_Polearms", "Polearms" },
		{ "sub_Bows", "Bows" },
		{ "sub_Crossbows", "Crossbows" },
		{ "sub_Fists", "Fists" },
		{ "sub_Bombs", "Bombs" }
	};

	private static readonly Dictionary<string, string> German = new Dictionary<string, string>
	{
		{ "dismantle", "ZERLEGEN" },
		{ "dismantled", "Zerlegt" },
		{ "dismantle_failed", "Zerlegen fehlgeschlagen" },
		{ "build_range", "Baureichweite" },
		{ "default", "Standard" },
		{ "no_access", "Hier hast du keinen Zugriff" },
		{ "now", "aktuell" },
		{ "search", "Suchen..." },
		{ "favorites", "Favoriten" },
		{ "biome0", "Wiesen" },
		{ "biome1", "Schwarzwald" },
		{ "biome2", "Sumpf" },
		{ "biome3", "Berge" },
		{ "biome4", "Ebenen" },
		{ "biome5", "Nebelland" },
		{ "biome6", "Aschland" },
		{ "biome7", "Hoher Norden" },
		{ "sub_OneHand", "Einhand" },
		{ "sub_TwoHand", "Zweihand" },
		{ "sub_Knives", "Messer" },
		{ "sub_Swords", "Schwerter" },
		{ "sub_Clubs", "Keulen" },
		{ "sub_Axes", "Äxte" },
		{ "sub_Spears", "Speere" },
		{ "sub_Polearms", "Stangenwaffen" },
		{ "sub_Bows", "Bögen" },
		{ "sub_Crossbows", "Armbrüste" },
		{ "sub_Fists", "Fäuste" },
		{ "sub_Bombs", "Bomben" }
	};

	public static string T(string key)
	{
		Dictionary<string, string> dictionary = English;
		try
		{
			if (Localization.instance != null && Localization.instance.GetSelectedLanguage() == "German")
			{
				dictionary = German;
			}
		}
		catch
		{
		}
		if (dictionary.TryGetValue(key, out var value))
		{
			return value;
		}
		if (!English.TryGetValue(key, out value))
		{
			return key;
		}
		return value;
	}
}
public class ModConfig
{
	public ConfigEntry<bool> EnableMod { get; }

	public ConfigEntry<bool> EnableCategories { get; }

	public ConfigEntry<bool> ShowCraftabilityIndicators { get; }

	public ConfigEntry<bool> EnableCraftMultiplier { get; }

	public ConfigEntry<bool> EnableUpgradeCompare { get; }

	public ConfigEntry<bool> EnableDismantle { get; }

	public ConfigEntry<int> DismantleRefundPercent { get; }

	public ConfigEntry<bool> EnableBuildRangeCycle { get; }

	public ConfigEntry<int> BuildRangeMaxMeters { get; }

	public ConfigEntry<bool> EnableAutoRepair { get; }

	public ConfigEntry<int> AutoRepairBelowPercent { get; }

	public ConfigEntry<bool> HidePathOfValheimanSalvage { get; }

	public ConfigEntry<bool> DebugLogging { get; }

	public ModConfig(ConfigFile file)
	{
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Expected O, but got Unknown
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Expected O, but got Unknown
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Expected O, but got Unknown
		EnableMod = file.Bind<bool>("General", "EnableMod", true, "Master switch for Workbenches+.");
		EnableCategories = file.Bind<bool>("Crafting menu", "EnableCategories", true, "Show category chips under the Repair button.");
		ShowCraftabilityIndicators = file.Bind<bool>("Crafting menu", "ShowCraftabilityIndicators", false, "Show a small green checkmark on fully craftable recipe rows.");
		EnableCraftMultiplier = file.Bind<bool>("Crafting menu", "EnableCraftMultiplier", true, "Show up/down arrows beside the Craft button to craft several at once.");
		EnableUpgradeCompare = file.Bind<bool>("Crafting menu", "EnableUpgradeCompare", true, "Upgrade tab: show the current stat value next to the upgraded one, e.g. Durability: 400 (now: 200).");
		EnableDismantle = file.Bind<bool>("Dismantle", "EnableDismantle", true, "Show a Dismantle tab beside Craft/Upgrade to break items back into crafting materials.");
		DismantleRefundPercent = file.Bind<int>("Dismantle", "DismantleRefundPercent", 100, new ConfigDescription("Percent of the crafting materials returned by Dismantle.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
		EnableBuildRangeCycle = file.Bind<bool>("Build range", "EnableBuildRangeCycle", true, "Look at a station the hammer needs and press Shift+E (AltPlace + Use) to cycle its build range: default, 50, 100, 150, 200 m. Saved on the station. Not available inside someone else's ward.");
		BuildRangeMaxMeters = file.Bind<int>("Build range", "BuildRangeMaxMeters", 200, new ConfigDescription("Highest build range the cycle reaches (steps of 50 m).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(50, 500), Array.Empty<object>()));
		EnableAutoRepair = file.Bind<bool>("Auto repair", "EnableAutoRepair", false, "Repair worn armor, weapons and tools automatically while you stand inside the build range of a matching station (needs roof and fire like normal repair). One item every 1.5 s.");
		AutoRepairBelowPercent = file.Bind<int>("Auto repair", "AutoRepairBelowPercent", 90, new ConfigDescription("Auto repair an item once its durability is at or below this percent.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 99), Array.Empty<object>()));
		HidePathOfValheimanSalvage = file.Bind<bool>("Compatibility", "HidePathOfValheimanSalvage", false, "Path of Valheiman only: hide its Salvage tab at normal stations, so only the Dismantle tab is there. You lose Path of Valheiman's salvage bonuses and the Azurite dust from magic items. The Disenchant tab at the Rune Carver Table always stays.");
		DebugLogging = file.Bind<bool>("Debug", "DebugLogging", false, "Writes a recipe dump (BepInEx/WorkbenchesPlus_recipes.tsv).");
	}
}
internal static class AutoRepair
{
	private static readonly List<CraftingStation> AllStations = ResolveStations();

	private static readonly List<ItemData> Worn = new List<ItemData>();

	private const float ScanSeconds = 5f;

	private const float RepairSeconds = 1.5f;

	private static float _nextTick;

	private static List<CraftingStation> ResolveStations()
	{
		try
		{
			return AccessTools.StaticFieldRefAccess<List<CraftingStation>>(typeof(CraftingStation), "m_allStations");
		}
		catch (Exception)
		{
			if (Plugin.Log != null)
			{
				Plugin.Log.LogWarning((object)"CraftingStation.m_allStations not found - auto repair disabled.");
			}
			return null;
		}
	}

	public static void Tick()
	{
		ModConfig settings = Plugin.Settings;
		if (settings == null || !settings.EnableMod.Value || !settings.EnableAutoRepair.Value || Time.unscaledTime < _nextTick)
		{
			return;
		}
		_nextTick = Time.unscaledTime + 5f;
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || ((Character)localPlayer).InCutscene() || (Object)(object)ObjectDB.instance == (Object)null || AllStations == null || AllStations.Count == 0)
		{
			return;
		}
		Inventory inventory = ((Humanoid)localPlayer).GetInventory();
		if (inventory == null)
		{
			return;
		}
		Worn.Clear();
		inventory.GetWornItems(Worn);
		if (Worn.Count == 0)
		{
			return;
		}
		float num = (float)Mathf.Clamp(settings.AutoRepairBelowPercent.Value, 1, 99) / 100f;
		for (int i = 0; i < Worn.Count; i++)
		{
			ItemData val = Worn[i];
			if (val?.m_shared == null || !val.m_shared.m_canBeReparied)
			{
				continue;
			}
			float maxDurability = val.GetMaxDurability();
			if (!(maxDurability <= 0.01f) && !(val.m_durability / maxDurability > num))
			{
				CraftingStation val2 = FindStation(localPlayer, val);
				if (!((Object)(object)val2 == (Object)null))
				{
					Repair(localPlayer, val2, val, maxDurability);
					_nextTick = Time.unscaledTime + 1.5f;
					break;
				}
			}
		}
	}

	private static CraftingStation FindStation(Player player, ItemData item)
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		Recipe recipe = ObjectDB.instance.GetRecipe(item);
		if ((Object)(object)recipe == (Object)null || ((Object)(object)recipe.m_craftingStation == (Object)null && (Object)(object)recipe.m_repairStation == (Object)null))
		{
			return null;
		}
		Vector3 position = ((Component)player).transform.position;
		for (int i = 0; i < AllStations.Count; i++)
		{
			CraftingStation val = AllStations[i];
			if (!((Object)(object)val == (Object)null) && (((Object)(object)recipe.m_repairStation != (Object)null && recipe.m_repairStation.m_name == val.m_name) || ((Object)(object)recipe.m_craftingStation != (Object)null && recipe.m_craftingStation.m_name == val.m_name) || item.m_worldLevel < Game.m_worldLevel) && Mathf.Min(val.GetLevel(true), 4) >= recipe.m_minStationLevel)
			{
				Vector3 val2 = position;
				val2.y = ((Component)val).transform.position.y;
				if (!(Vector3.Distance(((Component)val).transform.position, val2) >= val.GetStationBuildRange()) && val.CheckUsable(player, false))
				{
					return val;
				}
			}
		}
		return null;
	}

	private static void Repair(Player player, CraftingStation station, ItemData item, float max)
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: 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_004a: Unknown result type (might be due to invalid IL or missing references)
		((Character)player).RaiseSkill((SkillType)107, 1f - item.m_durability / max);
		item.m_durability = max;
		if (station.m_repairItemDoneEffects != null)
		{
			station.m_repairItemDoneEffects.Create(((Component)station).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
		}
		((Character)player).Message((MessageType)1, Localization.instance.Localize("$msg_repaired", new string[1] { item.m_shared.m_name }), 0, (Sprite)null, false);
	}
}
internal static class BuildRangeBonus
{
	private struct CacheEntry
	{
		public int Value;

		public int Frame;
	}

	public const string ZdoKey = "WBP_buildExtra";

	public const int Step = 50;

	public const int MaxOverrideLimit = 500;

	private static readonly FieldInfo BuildRangeField = AccessTools.Field(typeof(CraftingStation), "m_buildRange");

	private static readonly FieldInfo ExtensionTimerField = AccessTools.Field(typeof(CraftingStation), "m_updateExtensionTimer");

	private static readonly FieldInfo AreaCircleField = AccessTools.Field(typeof(CraftingStation), "m_areaMarkerCircle");

	private static HashSet<string> _buildStations;

	private static ZNetScene _buildStationsScene;

	private static readonly Dictionary<CraftingStation, CacheEntry> Cache = new Dictionary<CraftingStation, CacheEntry>();

	public static int MaxOverride
	{
		get
		{
			int num = ((Plugin.Settings != null) ? Plugin.Settings.BuildRangeMaxMeters.Value : 200);
			return Math.Max(50, Math.Min(500, num / 50 * 50));
		}
	}

	public static bool Ready()
	{
		if (Plugin.Settings != null && Plugin.Settings.EnableMod.Value)
		{
			return Plugin.Settings.EnableBuildRangeCycle.Value;
		}
		return false;
	}

	public static bool HasBuildRange(CraftingStation station)
	{
		if ((Object)(object)station != (Object)null && station.m_rangeBuild > 0.01f)
		{
			return IsNeededForBuilding(station);
		}
		return false;
	}

	private static bool IsNeededForBuilding(CraftingStation station)
	{
		ZNetScene instance = ZNetScene.instance;
		if ((Object)(object)instance == (Object)null || string.IsNullOrEmpty(station.m_name))
		{
			return false;
		}
		if (_buildStations == null || (Object)(object)_buildStationsScene != (Object)(object)instance)
		{
			HashSet<string> hashSet = new HashSet<string>();
			foreach (GameObject prefab in instance.m_prefabs)
			{
				if (!((Object)(object)prefab == (Object)null))
				{
					Piece component = prefab.GetComponent<Piece>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)component.m_craftingStation == (Object)null) && !((Object)(object)prefab.GetComponent<StationExtension>() != (Object)null))
					{
						hashSet.Add(component.m_craftingStation.m_name);
					}
				}
			}
			_buildStations = hashSet;
			_buildStationsScene = instance;
			if (Plugin.Settings != null && Plugin.Settings.DebugLogging.Value)
			{
				Plugin.Log.LogInfo((object)("Build range stations: " + string.Join(", ", hashSet)));
			}
		}
		return _buildStations.Contains(station.m_name);
	}

	public static int GetExtra(CraftingStation station)
	{
		if ((Object)(object)station == (Object)null)
		{
			return 0;
		}
		int frameCount = Time.frameCount;
		if (Cache.TryGetValue(station, out var value) && value.Frame == frameCount)
		{
			return value.Value;
		}
		int num = ReadZdo(station);
		if (Cache.Count > 256)
		{
			PruneCache();
		}
		Cache[station] = new CacheEntry
		{
			Value = num,
			Frame = frameCount
		};
		return num;
	}

	private static int ReadZdo(CraftingStation station)
	{
		ZNetView component = ((Component)station).GetComponent<ZNetView>();
		ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null);
		if (val == null)
		{
			return 0;
		}
		return Snap(val.GetInt("WBP_buildExtra", 0));
	}

	private static void PruneCache()
	{
		List<CraftingStation> list = new List<CraftingStation>();
		foreach (CraftingStation key in Cache.Keys)
		{
			if ((Object)(object)key == (Object)null)
			{
				list.Add(key);
			}
		}
		for (int i = 0; i < list.Count; i++)
		{
			Cache.Remove(list[i]);
		}
	}

	public static bool TryCycle(CraftingStation station, Player player)
	{
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		if (!Ready() || !HasBuildRange(station) || (Object)(object)player == (Object)null)
		{
			return false;
		}
		ZNetView component = ((Component)station).GetComponent<ZNetView>();
		if ((Object)(object)component == (Object)null || !component.IsValid() || component.GetZDO() == null)
		{
			return false;
		}
		if (!PrivateArea.CheckAccess(((Component)station).transform.position, 0f, true, false))
		{
			((Character)player).Message((MessageType)2, Loc.T("no_access"), 0, (Sprite)null, false);
			return true;
		}
		int extra = GetExtra(station);
		int num = ((extra < MaxOverride) ? (extra + 50) : 0);
		if (!component.IsOwner())
		{
			component.ClaimOwnership();
		}
		component.GetZDO().Set("WBP_buildExtra", num);
		Cache.Remove(station);
		RefreshVisuals(station);
		float stationBuildRange = station.GetStationBuildRange();
		string text = Loc.T("build_range") + ": ";
		if (num <= 0)
		{
			((Character)player).Message((MessageType)2, text + Loc.T("default") + " (" + FormatMeters(stationBuildRange) + ")", 0, (Sprite)null, false);
		}
		else
		{
			((Character)player).Message((MessageType)2, text + FormatMeters(stationBuildRange), 0, (Sprite)null, false);
		}
		return true;
	}

	public static float AddExtra(CraftingStation station, float vanillaRange)
	{
		if (!Ready() || !HasBuildRange(station))
		{
			return vanillaRange;
		}
		int extra = GetExtra(station);
		if (extra <= 0)
		{
			return vanillaRange;
		}
		return extra;
	}

	public static void ApplyVisuals(CraftingStation station)
	{
		if (!((Object)(object)station == (Object)null) && !(BuildRangeField == null))
		{
			float vanillaRange;
			try
			{
				vanillaRange = (float)BuildRangeField.GetValue(station);
			}
			catch
			{
				return;
			}
			float radius = AddExtra(station, vanillaRange);
			ApplyMarkerAndCollider(station, radius);
		}
	}

	public static void RefreshVisuals(CraftingStation station)
	{
		if ((Object)(object)station == (Object)null)
		{
			return;
		}
		try
		{
			if (ExtensionTimerField != null)
			{
				ExtensionTimerField.SetValue(station, 2f);
			}
		}
		catch
		{
		}
		station.GetStationBuildRange();
	}

	public static void ApplyMarkerAndCollider(CraftingStation station, float radius)
	{
		if ((Object)(object)station == (Object)null)
		{
			return;
		}
		try
		{
			if (AreaCircleField != null)
			{
				object? value = AreaCircleField.GetValue(station);
				CircleProjector val = (CircleProjector)((value is CircleProjector) ? value : null);
				if ((Object)(object)val != (Object)null)
				{
					val.m_radius = radius;
				}
			}
		}
		catch
		{
		}
		Collider effectAreaCollider = station.m_effectAreaCollider;
		SphereCollider val2 = (SphereCollider)(object)((effectAreaCollider is SphereCollider) ? effectAreaCollider : null);
		if (val2 != null)
		{
			val2.radius = radius;
			return;
		}
		CapsuleCollider val3 = (CapsuleCollider)(object)((effectAreaCollider is CapsuleCollider) ? effectAreaCollider : null);
		if (val3 != null)
		{
			val3.radius = radius;
		}
	}

	public static string HoverHint(CraftingStation station)
	{
		if (!Ready() || !HasBuildRange(station))
		{
			return "";
		}
		if ((Object)(object)Player.m_localPlayer == (Object)null || !station.InUseDistance((Humanoid)(object)Player.m_localPlayer))
		{
			return "";
		}
		int extra = GetExtra(station);
		float stationBuildRange = station.GetStationBuildRange();
		string text = ((extra <= 0) ? (Loc.T("default") + " (" + FormatMeters(stationBuildRange) + ")") : FormatMeters(stationBuildRange));
		string text2 = ((Localization.instance != null) ? Localization.instance.Localize("$KEY_AltPlace + $KEY_Use") : "Shift+E");
		return "\n[<color=yellow><b>" + text2 + "</b></color>] " + Loc.T("build_range") + "  " + text;
	}

	private static int Snap(int value)
	{
		if (value < 0)
		{
			return 0;
		}
		if (value > MaxOverride)
		{
			return MaxOverride;
		}
		return value / 50 * 50;
	}

	private static string FormatMeters(float meters)
	{
		return Mathf.RoundToInt(meters) + " m";
	}
}
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 SectionTier(int bucket)
	{
		return (bucket != 0) ? 1 : 0;
	}

	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;
		}
		CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
		foreach (Requirement val in resources)
		{
			if (val == null || (Object)(object)val.m_resItem == (Object)null || val.m_resItem.m_itemData == null || !CountsTowardCraft(currentCraftingStation, val))
			{
				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;
	}

	private static bool CountsTowardCraft(CraftingStation station, Requirement req)
	{
		if (req == null)
		{
			return false;
		}
		if ((Object)(object)station != (Object)null && station.m_upgrader != req.m_upgraderResource)
		{
			return false;
		}
		if ((Object)(object)station == (Object)null && req.m_upgraderResource)
		{
			return false;
		}
		return true;
	}
}
internal static class DismantleMode
{
	public static readonly List<ItemData> Items = new List<ItemData>();

	public static readonly List<Recipe> Recipes = new List<Recipe>();

	public static readonly List<ItemData> RowItems = new List<ItemData>();

	private static int _nextEntry;

	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");

	private static readonly FieldInfo SelectedRecipeField = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe");

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

	public static bool Active { get; private set; }

	public static bool HasProgress => _progressItem != null;

	public static void BeginRows()
	{
		RowItems.Clear();
		_nextEntry = 0;
	}

	public static ItemData ClaimItemForRow(Recipe recipe, ItemData fallback)
	{
		int i;
		for (i = _nextEntry; i < Recipes.Count && (Object)(object)Recipes[i] != (Object)(object)recipe; i++)
		{
		}
		ItemData val = fallback;
		if (i < Recipes.Count && i < Items.Count)
		{
			val = Items[i];
			_nextEntry = i + 1;
		}
		RowItems.Add(val);
		return val;
	}

	private static void SnapshotRecipes(List<Recipe> recipes)
	{
		Recipes.Clear();
		if (recipes != null)
		{
			Recipes.AddRange(recipes);
		}
	}

	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();
		Recipes.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) && !recipe.m_noCraftOnlyUpgrade)
			{
				recipes.Add(recipe);
				Items.Add(val);
			}
		}
		SnapshotRecipes(recipes);
	}

	public static List<KeyValuePair<ItemDrop, int>> RefundFor(Player player, CraftingStation station, ItemData item)
	{
		if (!TryGetDismantleRecipe(player, station, item, out var recipe))
		{
			return new List<KeyValuePair<ItemDrop, int>>();
		}
		return BuildRefund(recipe, item.m_quality);
	}

	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)
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Invalid comparison between Unknown and I4
		recipe = null;
		if ((Object)(object)player == (Object)null || item?.m_shared == null)
		{
			return false;
		}
		if (item.m_equipped)
		{
			return false;
		}
		if ((int)item.m_shared.m_itemType == 2)
		{
			return false;
		}
		recipe = FindRecipe(item, station);
		if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null)
		{
			return false;
		}
		if (recipe.m_requireOnlyOneIngredient)
		{
			return false;
		}
		if (StationFilter.IsUpgrader(station))
		{
			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)
		{
			return;
		}
		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)
		{
			int num3 = SortEngine.Compare(recipes[a], recipes[b]);
			if (num3 != 0)
			{
				return num3;
			}
			num3 = Items[b].m_quality.CompareTo(Items[a].m_quality);
			return (num3 == 0) ? a.CompareTo(b) : num3;
		});
		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]);
		}
		SnapshotRecipes(recipes);
	}

	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 = ScaleRefund(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 int ScaleRefund(int amount)
	{
		ModConfig settings = Plugin.Settings;
		if (settings == null || amount <= 0)
		{
			return amount;
		}
		float num = (float)Mathf.Clamp(settings.DismantleRefundPercent.Value, 0, 100) / 100f;
		if (num <= 0f)
		{
			return 0;
		}
		return Mathf.Max(1, Mathf.FloorToInt((float)amount * num));
	}

	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;
	}

	private static ItemData ItemOfPair(object pair)
	{
		if (pair == null)
		{
			return null;
		}
		PropertyInfo property = pair.GetType().GetProperty("ItemData");
		if (!(property != null))
		{
			return null;
		}
		object? value = property.GetValue(pair, null);
		return (ItemData)((value is ItemData) ? value : null);
	}

	private static ItemData ItemOfRow(InventoryGui gui, int index)
	{
		if ((Object)(object)gui == (Object)null || AvailableRecipesField == null || index < 0)
		{
			return null;
		}
		if (!(AvailableRecipesField.GetValue(gui) is IList list) || index >= list.Count)
		{
			return null;
		}
		return ItemOfPair(list[index]);
	}

	public static void BindSelectedItem(InventoryGui gui, int index)
	{
		if (Active && !((Object)(object)gui == (Object)null))
		{
			ItemData val = ItemOfRow(gui, index);
			if (val != null && CraftUpgradeItemField != null)
			{
				CraftUpgradeItemField.SetValue(gui, val);
			}
		}
	}

	public static ItemData ResolveSelectedItem(InventoryGui gui)
	{
		if ((Object)(object)gui == (Object)null)
		{
			return null;
		}
		if (SelectedRecipeField != null)
		{
			ItemData val = ItemOfPair(SelectedRecipeField.GetValue(gui));
			if (val != null)
			{
				return val;
			}
		}
		if (!(CraftUpgradeItemField != null))
		{
			return null;
		}
		object? value = CraftUpgradeItemField.GetValue(gui);
		return (ItemData)((value is ItemData) ? value : null);
	}

	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, FreedSlots(val, recipe)))
		{
			((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, Loc.T("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, Loc.T("dismantle_failed"), 0, (Sprite)null, false);
			return true;
		}
		List<KeyValuePair<ItemDrop, int>> list = BuildRefund(progressRecipe, quality);
		if (!CanFitRefund(inventory, list, FreedSlots(val, progressRecipe)))
		{
			((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)
		{
			flag = ((num >= stack) ? (!inventory.ContainsItem(val)) : (val.m_stack < stack));
		}
		if (!flag)
		{
			((Character)localPlayer).Message((MessageType)2, Loc.T("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, Loc.T("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, int freedSlots)
	{
		if (inv == null || refund == null || refund.Count == 0)
		{
			return true;
		}
		int num = 0;
		for (int i = 0; i < refund.Count; i++)
		{
			ItemDrop key = refund[i].Key;
			int value = refund[i].Value;
			if (value > 0)
			{
				if (key?.m_itemData?.m_shared == null)
				{
					return false;
				}
				SharedData shared = key.m_itemData.m_shared;
				int num2 = Mathf.Max(1, shared.m_maxStackSize);
				int num3 = value - inv.FindFreeStackSpace(shared.m_name, (float)Game.m_worldLevel);
				if (num3 > 0)
				{
					num += (num3 + num2 - 1) / num2;
				}
			}
		}
		return num <= inv.GetEmptySlots() + freedSlots;
	}

	private static int FreedSlots(ItemData item, Recipe recipe)
	{
		if (item == null || !((Object)(object)recipe != (Object)null) || Mathf.Max(1, recipe.m_amount) < item.m_stack)
		{
			return 0;
		}
		return 1;
	}
}
internal static class FavoritesStore
{
	private static HashSet<string> _set;

	private static string FilePath => Path.Combine(Paths.ConfigPath, "WorkbenchesPlus_favorites.txt");

	public static bool Is(Recipe recipe)
	{
		string text = KeyOf(recipe);
		if (text != null)
		{
			return Set().Contains(text);
		}
		return false;
	}

	public static void Toggle(Recipe recipe)
	{
		string text = KeyOf(recipe);
		if (text != null)
		{
			HashSet<string> hashSet = Set();
			if (!hashSet.Remove(text))
			{
				hashSet.Add(text);
			}
			Save(hashSet);
		}
	}

	private static string KeyOf(Recipe recipe)
	{
		return SortEngine.Info(recipe)?.Facts.Prefab;
	}

	private static HashSet<string> Set()
	{
		if (_set != null)
		{
			return _set;
		}
		_set = new HashSet<string>();
		try
		{
			if (File.Exists(FilePath))
			{
				string[] array = File.ReadAllLines(FilePath);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length > 0)
					{
						_set.Add(text);
					}
				}
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Favorites load: " + ex.Message));
		}
		return _set;
	}

	private static void Save(HashSet<string> set)
	{
		try
		{
			File.WriteAllLines(FilePath, new List<string>(set).ToArray());
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Favorites save: " + ex.Message));
		}
	}
}
internal sealed class FavoriteRowClick : MonoBehaviour, IPointerClickHandler, IEventSystemHandler
{
	private Recipe _recipe;

	public static void Attach(GameObject row, Recipe recipe)
	{
		if (!((Object)(object)row == (Object)null) && !((Object)(object)recipe == (Object)null))
		{
			FavoriteRowClick favoriteRowClick = row.GetComponent<FavoriteRowClick>();
			if ((Object)(object)favoriteRowClick == (Object)null)
			{
				favoriteRowClick = row.AddComponent<FavoriteRowClick>();
			}
			favoriteRowClick._recipe = recipe;
		}
	}

	public void OnPointerClick(PointerEventData eventData)
	{
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Invalid comparison between Unknown and I4
		if (eventData != null && (int)eventData.button == 1 && !((Object)(object)_recipe == (Object)null))
		{
			FavoritesStore.Toggle(_recipe);
			AccessToolsExt.RebuildCraftingPanel();
		}
	}
}
internal static class RecipeDump
{
	private static bool _done;

	public static void Tick()
	{
		if (_done || Plugin.Settings == null || !Plugin.Settings.DebugLogging.Value || (Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_recipes == null || ObjectDB.instance.m_recipes.Count == 0 || (Object)(object)Player.m_localPlayer == (Object)null)
		{
			return;
		}
		_done = true;
		try
		{
			Write();
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("RecipeDump: " + ex.Message));
		}
	}

	private static void Write()
	{
		//IL_023f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0257: Unknown result type (might be due to invalid IL or missing references)
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.AppendLine("station\trepairStation\tminLevel\tprefab\tname\titemType\tskill\tmaxQuality\tamount\tfood\tstamina\teitr\tarmor\tdamage\tblock\tweight\tresources\tcategory\tmaterialBucket\trawFood\trawStamina\trawEitr\tfoodFrom\tammoType");
		foreach (Recipe r in ObjectDB.instance.m_recipes)
		{
			if ((Object)(object)r == (Object)null || (Object)(object)r.m_item == (Object)null || !r.m_enabled)
			{
				continue;
			}
			SharedData val = r.m_item.m_itemData?.m_shared;
			if (val == null)
			{
				continue;
			}
			List<string> list = new List<string>();
			if (r.m_resources != null)
			{
				Requirement[] resources = r.m_resources;
				foreach (Requirement val2 in resources)
				{
					if (!((Object)(object)val2?.m_resItem == (Object)null))
					{
						list.Add(((Object)val2.m_resItem).name + ":" + val2.m_amount + ":" + val2.m_amountPerLevel + (val2.m_upgraderResource ? ":idol" : ""));
					}
				}
			}
			RecipeFacts recipeFacts = FactsReader.Read(r);
			string value = Safe(() => SortEngine.CategoryOf(r).ToString());
			string value2 = Safe(() => GroupOf(r));
			stringBuilder.Append(((Object)(object)r.m_craftingStation != (Object)null) ? r.m_craftingStation.m_name : "(hand)").Append('\t').Append(((Object)(object)r.m_repairStation != (Object)null) ? r.m_repairStation.m_name : "")
				.Append('\t')
				.Append(r.m_minStationLevel)
				.Append('\t')
				.Append(((Object)r.m_item).name)
				.Append('\t')
				.Append(val.m_name)
				.Append('\t')
				.Append(val.m_itemType)
				.Append('\t')
				.Append(val.m_skillType)
				.Append('\t')
				.Append(val.m_maxQuality)
				.Append('\t')
				.Append(r.m_amount)
				.Append('\t')
				.Append(recipeFacts.Food)
				.Append('\t')
				.Append(recipeFacts.Stamina)
				.Append('\t')
				.Append(recipeFacts.Eitr)
				.Append('\t')
				.Append(val.m_armor)
				.Append('\t')
				.Append(((DamageTypes)(ref val.m_damages)).GetTotalDamage())
				.Append('\t')
				.Append(val.m_blockPower)
				.Append('\t')
				.Append(val.m_weight)
				.Append('\t')
				.Append(string.Join(";", list))
				.Append('\t')
				.Append(value)
				.Append('\t')
				.Append(value2)
				.Append('\t')
				.Append(val.m_food)
				.Append('\t')
				.Append(val.m_foodStamina)
				.Append('\t')
				.Append(val.m_foodEitr)
				.Append('\t')
				.Append(recipeFacts.FoodFrom)
				.Append('\t')
				.Append(recipeFacts.AmmoType)
				.AppendLine();
		}
		string text = Path.Combine(Paths.BepInExRootPath, "WorkbenchesPlus_recipes.tsv");
		File.WriteAllText(text, stringBuilder.ToString());
		Plugin.Log.LogInfo((object)("Recipe dump written: " + text));
	}

	private static string GroupOf(Recipe r)
	{
		RecipeInfo recipeInfo = SortEngine.Info(r);
		if (recipeInfo == null)
		{
			return "";
		}
		return recipeInfo.Group;
	}

	private static string Safe(Func<string> f)
	{
		try
		{
			return f() ?? "";
		}
		catch
		{
			return "?";
		}
	}
}
internal static class RecipeSort
{
	private struct Entry
	{
		public Recipe Recipe;

		public RecipeInfo Info;

		public int CraftTier;

		public bool Fav;

		public int OriginalIndex;
	}

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

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

	private static readonly HashSet<string> Warned = new HashSet<string>();

	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;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer != (Object)null && StationFilter.IsUpgrader(localPlayer.GetCurrentCraftingStation()))
		{
			if (CategoryBar.Active != CraftCategory.All)
			{
				CategoryBar.SetActive(CraftCategory.All, rebuild: false);
			}
			return;
		}
		if (!DismantleMode.Active)
		{
			List<Recipe> list = new List<Recipe>(recipes);
			StationFilter.Apply(recipes);
			if (recipes.Count == 0 && list.Count > 0)
			{
				recipes.AddRange(list);
				WarnOnce(((Object)(object)localPlayer != (Object)null) ? localPlayer.GetCurrentCraftingStation() : null);
			}
			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 (SortEngine.CategoryOf(recipes[num]) != CategoryBar.Active)
					{
						recipes.RemoveAt(num);
					}
				}
				if (CategoryBar.HasSubFilter)
				{
					for (int num2 = recipes.Count - 1; num2 >= 0; num2--)
					{
						if (!CategoryBar.PassesSubFilter(SortEngine.Info(recipes[num2])))
						{
							recipes.RemoveAt(num2);
						}
					}
				}
			}
			if (SearchBox.Query.Length > 0)
			{
				for (int num3 = recipes.Count - 1; num3 >= 0; num3--)
				{
					if (!SearchBox.Matches(SortEngine.Info(recipes[num3])))
					{
						recipes.RemoveAt(num3);
					}
				}
			}
		}
		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 (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<GameObject> list3 = new List<GameObject>(list.Count);
				for (int i = 0; i < list.Count; i++)
				{
					list2.Add(ReadRecipe(list[i]));
					list3.Add(ReadElement(list[i]));
				}
				Player localPlayer = Player.m_localPlayer;
				bool flag = (Object)(object)localPlayer != (Object)null && StationFilter.IsUpgrader(localPlayer.GetCurrentCraftingStation());
				List<Entry> list4 = BuildEntries(list2, flag ? list : null);
				list4.Sort(CompareEntries);
				float rowSpace = 30f;
				if (RecipeListSpaceField != null && RecipeListSpaceField.GetValue(gui) is float num && num > 0.01f)
				{
					rowSpace = num;
				}
				List<object> list5 = new List<object>(list.Count);
				for (int j = 0; j < list.Count; j++)
				{
					list5.Add(list[j]);
				}
				list.Clear();
				List<Recipe> list6 = new List<Recipe>(list4.Count);
				List<GameObject> list7 = new List<GameObject>(list4.Count);
				Transform val = null;
				for (int k = 0; k < list4.Count; k++)
				{
					int originalIndex = list4[k].OriginalIndex;
					list.Add(list5[originalIndex]);
					list6.Add(list2[originalIndex]);
					GameObject val2 = list3[originalIndex];
					list7.Add(val2);
					if ((Object)(object)val == (Object)null && (Object)(object)val2 != (Object)null)
					{
						val = val2.transform.parent;
					}
				}
				List<HeaderSlot> list8 = new List<HeaderSlot>(list4.Count);
				for (int l = 0; l < list4.Count; l++)
				{
					list8.Add(SlotFor(list4[l]));
				}
				MaterialSectionHeaders.Apply(val, list8, list7, rowSpace);
				if (!flag)
				{
					for (int m = 0; m < list6.Count; m++)
					{
						FavoriteRowClick.Attach(list7[m], list6[m]);
						AppendFoodValue(list7[m], SortEngine.Info(list6[m]));
					}
				}
			}
			else
			{
				MaterialSectionHeaders.Clear();
			}
		}
	}

	private static void WarnOnce(CraftingStation station)
	{
		string text = (((Object)(object)station != (Object)null) ? (((Object)station).name + "|" + station.m_name) : "none");
		if (Warned.Add(text) && Plugin.Log != null)
		{
			Plugin.Log.LogWarning((object)("Station filter would hide every recipe at '" + text + "' - showing the vanilla list."));
		}
	}

	private static void SortInPlace(List<Recipe> recipes)
	{
		if (recipes.Count > 1)
		{
			List<Entry> list = BuildEntries(recipes, null);
			list.Sort(CompareEntries);
			recipes.Clear();
			for (int i = 0; i < list.Count; i++)
			{
				recipes.Add(list[i].Recipe);
			}
		}
	}

	private static List<Entry> BuildEntries(List<Recipe> recipes, IList upgradePairs)
	{
		Player localPlayer = Player.m_localPlayer;
		List<Entry> list = new List<Entry>(recipes.Count);
		for (int i = 0; i < recipes.Count; i++)
		{
			Recipe recipe = recipes[i];
			list.Add(new Entry
			{
				Recipe = recipe,
				Info = SortEngine.Info(recipe),
				CraftTier = ((upgradePairs != null) ? ((!RowCanCraft(upgradePairs[i])) ? 1 : 0) : Craftability.SectionTier(Craftability.ScoreBucket(localPlayer, recipe))),
				Fav = (upgradePairs == null && !DismantleMode.Active && FavoritesStore.Is(recipe)),
				OriginalIndex = i
			});
		}
		return list;
	}

	private static int CompareEntries(Entry a, Entry b)
	{
		if (a.Fav != b.Fav)
		{
			if (!a.Fav)
			{
				return 1;
			}
			return -1;
		}
		int num = ((a.Info != null && b.Info != null) ? RecipeOrder.Compare(a.Info, a.CraftTier, b.Info, b.CraftTier) : ((a.Info == null) ? ((b.Info != null) ? 1 : 0) : (-1)));
		if (num == 0)
		{
			return a.OriginalIndex.CompareTo(b.OriginalIndex);
		}
		return num;
	}

	private static HeaderSlot SlotFor(Entry e)
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		if (e.Fav)
		{
			return new HeaderSlot
			{
				Key = "FAV",
				Label = Loc.T("favorites"),
				Color = MaterialSectionHeaders.HeaderGold
			};
		}
		if (e.Info == null || string.IsNullOrEmpty(e.Info.Group))
		{
			return default(HeaderSlot);
		}
		return new HeaderSlot
		{
			Key = e.Info.GroupBand + "|" + e.Info.GroupOrder + "|" + e.Info.Group + "|" + e.CraftTier,
			Label = e.Info.Group + GroupColors.BiomeSuffix(e.Info),
			Color = MaterialSectionHeaders.HeaderGold
		};
	}

	private static void AppendFoodValue(GameObject row, RecipeInfo info)
	{
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)row == (Object)null) && info != null && !(info.Value <= 0f) && (info.Category == CraftCategory.Health || info.Category == CraftCategory.Stamina || info.Category == CraftCategory.Eitr || info.Category == CraftCategory.Prep))
		{
			Transform val = row.transform.Find("name");
			TMP_Text val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<TMP_Text>() : null);
			if (!((Object)(object)val2 == (Object)null) && !val2.text.EndsWith("</color>"))
			{
				TMP_Text val3 = val2;
				val3.text = val3.text + "  <color=#" + GroupColors.Hex(GroupColors.ForBar(info.Facts)) + ">" + Mathf.RoundToInt(info.Value) + "</color>";
			}
		}
	}

	private static bool RowCanCraft(object pair)
	{
		if (pair == null)
		{
			return false;
		}
		PropertyInfo propertyInfo = AccessTools.Property(pair.GetType(), "CanCraft");
		bool flag = default(bool);
		int num;
		if (propertyInfo != null)
		{
			object value = propertyInfo.GetValue(pair, null);
			if (value is bool)
			{
				flag = (bool)value;
				num = 1;
			}
			else
			{
				num = 0;
			}
		}
		else
		{
			num = 0;
		}
		return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
	}

	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 enum CraftCategory
{
	All,
	Weapons,
	Armor,
	Shields,
	Tools,
	Ammo,
	Magic,
	Health,
	Stamina,
	Eitr,
	Feasts,
	Prep,
	Fish,
	Bait,
	Potions,
	Cast,
	Materials,
	Trinkets,
	Clothes,
	Misc
}
internal static class CraftCategories
{
	public static readonly CraftCategory[] ChipOrder = new CraftCategory[20]
	{
		CraftCategory.All,
		CraftCategory.Weapons,
		CraftCategory.Armor,
		CraftCategory.Shields,
		CraftCategory.Tools,
		CraftCategory.Ammo,
		CraftCategory.Magic,
		CraftCategory.Health,
		CraftCategory.Stamina,
		CraftCategory.Eitr,
		CraftCategory.Feasts,
		CraftCategory.Prep,
		CraftCategory.Fish,
		CraftCategory.Bait,
		CraftCategory.Potions,
		CraftCategory.Cast,
		CraftCategory.Materials,
		CraftCategory.Trinkets,
		CraftCategory.Clothes,
		CraftCategory.Misc
	};

	public static string Label(CraftCategory cat)
	{
		if (cat != CraftCategory.All)
		{
			return cat.ToString();
		}
		return "All";
	}
}
internal static class MaterialCatalog
{
	public const int MaxTier = 7;

	private static readonly Dictionary<string, int> TierByIngredient = BuildTiers();

	private static readonly string[][] Defining = new string[22][]
	{
		new string[2] { "Wood", "Wood" },
		new string[2] { "Stone", "Stone" },
		new string[2] { "Flint", "Flint" },
		new string[2] { "BjornPaw", "Bear" },
		new string[2] { "HardAntler", "Antler" },
		new string[2] { "FineWood", "Fine Wood" },
		new string[2] { "RoundLog", "Fine Wood" },
		new string[2] { "Copper", "Copper" },
		new string[2] { "Tin", "Bronze" },
		new string[2] { "Bronze", "Bronze" },
		new string[2] { "Chitin", "Chitin" },
		new string[2] { "Iron", "Iron" },
		new string[2] { "SerpentScale", "Serpent" },
		new string[2] { "Silver", "Silver" },
		new string[2] { "Crystal", "Crystal" },
		new string[2] { "YggdrasilWood", "Yggdrasil" },
		new string[2] { "BlackMetal", "Black Metal" },
		new string[2] { "Needle", "Needle" },
		new string[2] { "Carapace", "Carapace" },
		new string[2] { "CharredBone", "Charred" },
		new string[2] { "FlametalNew", "Flametal" },
		new string[2] { "Gold", "Bloodgold" }
	};

	public static int TierOf(string ingredient)
	{
		if (!TierByIngredient.TryGetValue(ingredient, out var value))
		{
			return -1;
		}
		return value;
	}

	public static int ItemTier(RecipeFacts f)
	{
		int num = -1;
		for (int i = 0; i < f.Ingredients.Count; i++)
		{
			if (!f.Ingredients[i].Idol)
			{
				int num2 = TierOf(f.Ingredients[i].Prefab);
				if (num2 > num)
				{
					num = num2;
				}
			}
		}
		if (num >= 0)
		{
			return num;
		}
		return 0;
	}

	public static bool TryFamily(RecipeFacts f, out string family, out int order, out int tier)
	{
		family = null;
		order = -1;
		tier = 0;
		for (int num = Defining.Length - 1; num >= 0; num--)
		{
			if (f.HasIngredient(Defining[num][0]))
			{
				family = Defining[num][1];
				order = FirstIndexOf(family);
				tier = FamilyTier(family);
				return true;
			}
		}
		return false;
	}

	private static int FirstIndexOf(string family)
	{
		for (int i = 0; i < Defining.Length; i++)
		{
			if (Defining[i][1] == family)
			{
				return i;
			}
		}
		return Defining.Length;
	}

	private static int FamilyTier(string family)
	{
		return TierOf(Defining[FirstIndexOf(family)][0]);
	}

	public static bool IsBasicFamily(string family)
	{
		switch (family)
		{
		default:
			return family == "Bear";
		case "Wood":
		case "Stone":
		case "Flint":
		case "Antler":
		case "Fine Wood":
			return true;
		}
	}

	private static Dictionary<string, int> BuildTiers()
	{
		Dictionary<string, int> dictionary = new Dictionary<string, int>();
		Add(dictionary, 0, "Wood", "Stone", "Flint", "LeatherScraps", "Leatherstraps", "DeerHide", "BoneFragments", "Resin", "Feathers", "Coal", "HardAntler");
		Add(dictionary, 1, "FineWood", "RoundLog", "Copper", "Tin", "Bronze", "TrollHide", "BjornHide", "BjornPaw", "GreydwarfEye", "AncientSeed");
		Add(dictionary, 2, "Iron", "ElderBark", "Root", "WrithanRoots", "Chitin", "SerpentScale", "Ooze", "Bloodbag", "YmirRemains", "SurtlingCore", "Chain", "Guck", "Entrails", "Wisp");
		Add(dictionary, 3, "Silver", "Crystal", "Obsidian", "WolfPelt", "WolfFang", "WolfClaw", "WolfHairBundle", "FreezeGland", "Ice");
		Add(dictionary, 4, "BlackMetal", "Needle", "LinenThread", "LoxPelt");
		Add(dictionary, 5, "Carapace", "YggdrasilWood", "Eitr", "ScaleHide", "Mandible", "BlackMarble", "BlackCore", "Sap", "MushroomMagecap", "BugMeat", "Fiddleheadfern", "GemstoneRed", "GemstoneGreen", "GemstoneBlue", "DvergrKeyFragment", "BellFragment");
		Add(dictionary, 6, "DyrnwynBladeFragment", "DyrnwynHiltFragment", "DyrnwynTipFragment", "FlametalNew", "CharredBone", "AskHide", "AskBladder", "Blackwood", "MorgenSinew", "MorgenHeart", "ProustitePowder", "SulfurStone", "Grausten", "MoltenCore", "FaderEmber", "BarkaBranch", "ElakingHairBundle");
		Add(dictionary, 7, "Gold", "Frostwood", "MooseHide", "MooseSinew", "SealHide", "NornThread", "SealBlubber", "OrbFrostFire", "OrbThunderBlood", "CrownJewel");
		return dictionary;
	}

	private static void Add(Dictionary<string, int> m, int tier, params string[] names)
	{
		for (int i = 0; i < names.Length; i++)
		{
			m[names[i]] = tier;
		}
	}
}
internal static class RecipeClassifier
{
	private const int KindAmmo = 0;

	private const int KindWeapon = 1;

	private const int KindShield = 2;

	private const int KindTool = 3;

	private const int KindArmor = 4;

	private const int KindTrinket = 5;

	private const int KindOther = 6;

	private static readonly HashSet<string> ClothesPrefabs = new HashSet<string> { "HelmetCelebration", "HelmetFishingHat", "HelmetOdin", "CapeOdin" };

	private static readonly Dictionary<string, string> SetAlias = new Dictionary<string, string>
	{
		{ "DeerHide", "Leather" },
		{ "TrollHide", "TrollLeather" },
		{ "Drake", "Wolf" },
		{ "DNHeavy", "DeepNorthHeavy" },
		{ "DNMedium", "DeepNorthMedium" },
		{ "DNMage", "DeepNorthMage" },
		{ "DeepNorth", "DeepNorthMedium" },
		{ "Ash", "AshlandsMedium" },
		{ "Asksvin", "Mage_Ashlands" },
		{ "Feather", "Mage" }
	};

	private static readonly Dictionary<string, string> SetLabel = new Dictionary<string, string>
	{
		{ "Fenring", "Fenrir" },
		{ "BerserkerUndead", "Vilebone" },
		{ "AshlandsMedium", "Ashlands" },
		{ "Mage_Ashlands", "Mage (Ashlands)" }
	};

	private static readonly Dictionary<string, int> SetTierOverride = new Dictionary<string, int>
	{
		{ "AshlandsMedium", 6 },
		{ "Mage_Ashlands", 6 }
	};

	public static List<RecipeInfo> Build(IList<RecipeFacts> all)
	{
		HashSet<string> hashSet = new HashSet<string>();
		for (int i = 0; i < all.Count; i++)
		{
			RecipeFacts recipeFacts = all[i];
			if ((recipeFacts.ItemType == "Chest" || recipeFacts.ItemType == "Legs" || recipeFacts.ItemType == "Helmet") && !IsClothes(recipeFacts))
			{
				hashSet.Add(Alias(SetStem(recipeFacts.Prefab)));
			}
		}
		List<RecipeInfo> list = new List<RecipeInfo>(all.Count);
		for (int j = 0; j < all.Count; j++)
		{
			list.Add(Classify(all[j], hashSet));
		}
		Dictionary<string, int> dictionary = new Dictionary<string, int>();
		Dictionary<string, int> dictionary2 = new Dictionary<string, int>();
		for (int k = 0; k < list.Count; k++)
		{
			RecipeInfo recipeInfo = list[k];
			if (recipeInfo.Category == CraftCategory.Armor && recipeInfo.SetKey.Length != 0)
			{
				if (!dictionary.TryGetValue(recipeInfo.SetKey, out var value) || recipeInfo.ItemTier > value)
				{
					dictionary[recipeInfo.SetKey] = recipeInfo.ItemTier;
				}
				if (!dictionary2.TryGetValue(recipeInfo.SetKey, out var value2) || recipeInfo.Facts.MinLevel < value2)
				{
					dictionary2[recipeInfo.SetKey] = recipeInfo.Facts.MinLevel;
				}
			}
		}
		for (int l = 0; l < list.Count; l++)
		{
			RecipeInfo recipeInfo2 = list[l];
			if (recipeInfo2.Category == CraftCategory.Armor && recipeInfo2.SetKey.Length != 0)
			{
				recipeInfo2.GroupBand = (SetTierOverride.TryGetValue(recipeInfo2.SetKey, out var value3) ? value3 : dictionary[recipeInfo2.SetKey]);
				recipeInfo2.GroupOrder = 500 + dictionary2[recipeInfo2.SetKey];
			}
		}
		ResolveAnchors(list);
		return list;
	}

	private static RecipeInfo Classify(RecipeFacts f, HashSet<string> sets)
	{
		RecipeInfo recipeInfo = new RecipeInfo
		{
			Facts = f,
			SortName = f.Prefab,
			ItemTier = MaterialCatalog.ItemTier(f)
		};
		string prefab = f.Prefab;
		string itemType = f.ItemType;
		string skill = f.Skill;
		bool twoHanded = itemType == "TwoHandedWeapon" || itemType == "TwoHandedWeaponLeft";
		if (prefab.StartsWith("Tankard", StringComparison.Ordinal))
		{
			return Fixed(recipeInfo, CraftCategory.Misc, "Tankards", 90, 6);
		}
		if (prefab.StartsWith("FireworksRocket", StringComparison.Ordinal))
		{
			return Fixed(recipeInfo, CraftCategory.Misc, "Fireworks", 91, 6);
		}
		if (prefab == "Snowball")
		{
			return Fixed(recipeInfo, CraftCategory.Misc, "Misc", 92, 6);
		}
		if (prefab == "BombSiege" || prefab.StartsWith("Catapult_", StringComparison.OrdinalIgnoreCase))
		{
			return Fixed(recipeInfo, CraftCategory.Ammo, "Siege", -1, 0);
		}
		if (itemType == "Material" && prefab.EndsWith("Uncooked", StringComparison.Ordinal) && f.FoodTotal <= 0f)
		{
			return Fixed(recipeInfo, CraftCategory.Cast, "Cast", 85, 6);
		}
		if (itemType == "Material" && prefab.StartsWith("Feast", StringComparison.Ordinal) && prefab.EndsWith("_Material", StringComparison.Ordinal))
		{
			recipeInfo.ItemTier = Biome(prefab);
			return Fixed(recipeInfo, CraftCategory.Feasts, "Feasts", 100, 6);
		}
		if (itemType == "Material" && f.FoodTotal > 0f)
		{
			if (prefab == "FishRaw")
			{
				return Fixed(recipeInfo, CraftCategory.Fish, "Fish", 102, 6);
			}
			recipeInfo.Value = Dominant(f);
			return Fixed(recipeInfo, CraftCategory.Prep, "Prep", 101, 6);
		}
		if (itemType == "Ammo" && f.HasIngredient("FishingBait"))
		{
			recipeInfo.ItemTier = Biome(prefab);
			return Fixed(recipeInfo, CraftCategory.Bait, "Bait", 103, 6);
		}
		if (prefab.StartsWith("MeadBase", StringComparison.Ordinal) || prefab == "BarleyWineBase")
		{
			recipeInfo.ItemTier = MeadStrength(prefab);
			CraftCategory craftCategory = (prefab.Contains("Health") ? CraftCategory.Health : (prefab.Contains("Stamina") ? CraftCategory.Stamina : (prefab.Contains("Eitr") ? CraftCategory.Eitr : CraftCategory.Potions)));
			return Fixed(recipeInfo, craftCategory, craftCategory.ToString(), FoodBand(craftCategory), 6);
		}
		switch (itemType)
		{
		case "Consumable":
		{
			CraftCategory craftCategory2 = DominantBar(f);
			recipeInfo.Value = Dominant(f);
			return Fixed(recipeInfo, craftCategory2, craftCategory2.ToString(), FoodBand(craftCategory2), 6);
		}
		case "Ammo":
		case "AmmoNonEquipable":
			if (itemType == "AmmoNonEquipable")
			{
				return Fixed(recipeInfo, CraftCategory.Ammo, "Turret bolts", -2, 0);
			}
			if (!(skill == "Crossbows") && f.AmmoType.IndexOf("bolt", StringComparison.OrdinalIgnoreCase) < 0 && !prefab.StartsWith("Bolt", StringComparison.OrdinalIgnoreCase))
			{
				return Fixed(recipeInfo, CraftCategory.Ammo, "Arrows", -4, 0);
			}
			return Fixed(recipeInfo, CraftCategory.Ammo, "Bolts", -3, 0);
		case "Shield":
			recipeInfo.ClassRank = (prefab.Contains("Tower") ? 1 : 0);
			return Family(recipeInfo, CraftCategory.Shields, 2);
		case "Helmet":
		case "Chest":
		case "Legs":
		case "Shoulder":
		{
			if (IsClothes(f))
			{
				return Fixed(recipeInfo, CraftCategory.Clothes, "Clothes", 81, 4);
			}
			if (prefab == "HelmetCrownofValheim")
			{
				return Fixed(recipeInfo, CraftCategory.Armor, "Special", 7, 4);
			}
			recipeInfo.KindRank = 4;
			recipeInfo.Category = CraftCategory.Armor;
			recipeInfo.PieceRank = itemType switch
			{
				"Legs" => 2, 
				"Helmet" => 1, 
				"Chest" => 0, 
				_ => 3, 
			};
			string text = Alias(SetStem(prefab));
			if (itemType == "Shoulder" && !sets.Contains(text))
			{
				recipeInfo.Group = "Capes";
				recipeInfo.GroupBand = recipeInfo.ItemTier;
				return recipeInfo;
			}
			recipeInfo.SetKey = text;
			recipeInfo.Group = Label(text);
			recipeInfo.GroupBand = recipeInfo.ItemTier;
			return recipeInfo;
		}
		case "Trinket":
		case "Utility":
			return Fixed(recipeInfo, CraftCategory.Trinkets, "Trinkets", 80, 5);
		default:
			if (skill == "ElementalMagic" || skill == "BloodMagic")
			{
				recipeInfo.ClassRank = ((!(skill == "ElementalMagic")) ? 1 : 0);
				return Fixed(recipeInfo, CraftCategory.Magic, "Staffs", -1, 1);
			}
			if (itemType == "Tool" || itemType == "Torch" || skill == "Pickaxes" || skill == "Farming" || (itemType == "Bow" && skill == "None") || prefab == "KnifeButcher" || (itemType == "Misc" && prefab.StartsWith("Saddle", StringComparison.Ordinal)))
			{
				recipeInfo.ClassRank = ToolRank(prefab);
				return Family(recipeInfo, CraftCategory.Tools, 3);
			}
			switch (itemType)
			{
			case "OneHandedWeapon":
			case "TwoHandedWeapon":
			case "TwoHandedWeaponLeft":
			case "Bow":
				if (skill == "None" && prefab.StartsWith("Bomb", StringComparison.Ordinal))
				{
					recipeInfo.Subs = SubCategories.ForWeapon(itemType, skill, prefab);
					recipeInfo.ClassRank = 22;
					return Fixed(recipeInfo, CraftCategory.Weapons, "Bombs", 88, 1);
				}
				recipeInfo.ClassRank = WeaponRank(skill, twoHanded);
				recipeInfo.Subs = SubCategories.ForWeapon(itemType, skill, prefab);
				return Family(recipeInfo, CraftCategory.Weapons, 1);
			case "Material":
				return Fixed(recipeInfo, CraftCategory.Materials, "Materials", 86, 6);
			default:
				return Fixed(recipeInfo, CraftCategory.Misc, "Misc", 93, 6);
			}
		}
	}

	private static RecipeInfo Fixed(RecipeInfo n, CraftCategory cat, string group, int band, int kind)
	{
		n.Category = cat;
		n.Group = group;
		n.GroupBand = band;
		n.KindRank = kind;
		return n;
	}

	private static RecipeInfo Family(RecipeInfo n, CraftCategory cat, int kind)
	{
		n.Category = cat;
		n.KindRank = kind;
		if (MaterialCatalog.TryFamily(n.Facts, out var family, out var order, out var tier))
		{
			n.Group = family;
			n.GroupOrder = order;
			n.GroupBand = tier;
			if (MaterialCatalog.IsBasicFamily(family) && n.ItemTier > tier)
			{
				n.Group = "Special";
				n.GroupOrder = 500;
				n.GroupBand = n.ItemTier;
			}
		}
		else
		{
			n.Group = "Special";
			n.GroupBand = n.ItemTier;
		}
		return n;
	}

	private static int WeaponRank(string skill, bool twoHanded)
	{
		int num = (twoHanded ? 1 : 0);
		switch (skill)
		{
		case "Knives":
			return 0;
		case "Swords":
			return 2 + num;
		case "Clubs":
			if (!twoHanded)
			{
				return 4;
			}
			return 14;
		case "Axes":
			return 6 + num;
		case "Spears":
			return 8;
		case "Polearms":
			return 10;
		case "Bows":
			return 16;
		case "Crossbows":
			return 18;
		case "Unarmed":
			return 20;
		default:
			return 24;
		}
	}

	private static int ToolRank(string prefab)
	{
		if (prefab.StartsWith("Hammer", StringComparison.Ordinal))
		{
			return 0;
		}
		if (prefab.StartsWith("Hoe", StringComparison.Ordinal))
		{
			return 1;
		}
		if (prefab.StartsWith("Cultivator", StringComparison.Ordinal))
		{
			return 2;
		}
		if (prefab.StartsWith("Pickaxe", StringComparison.Ordinal))
		{
			return 3;
		}
		if (prefab.StartsWith("Scythe", StringComparison.Ordinal))
		{
			return 4;
		}
		if (prefab.StartsWith("Shovel", StringComparison.Ordinal))
		{
			return 5;
		}
		if (prefab.StartsWith("Torch", StringComparison.Ordinal) || prefab == "Lantern")
		{
			return 6;
		}
		if (prefab.StartsWith("Saddle", StringComparison.Ordinal))
		{
			return 8;
		}
		return 7;
	}

	private static int FoodBand(CraftCategory c)
	{
		return c switch
		{
			CraftCategory.Health => 0, 
			CraftCategory.Stamina => 1, 
			CraftCategory.Eitr => 2, 
			_ => 3, 
		};
	}

	private static CraftCategory DominantBar(RecipeFacts f)
	{
		if (f.Food >= f.Stamina && f.Food >= f.Eitr)
		{
			return CraftCategory.Health;
		}
		if (!(f.Stamina >= f.Eitr))
		{
			return CraftCategory.Eitr;
		}
		return CraftCategory.Stamina;
	}

	private static float Dominant(RecipeFacts f)
	{
		return Math.Max(f.Food, Math.Max(f.Stamina, f.Eitr));
	}

	private static int MeadStrength(string p)
	{
		if (p.EndsWith("Minor", StringComparison.Ordinal))
		{
			return 0;
		}
		if (p.EndsWith("Medium", StringComparison.Ordinal))
		{
			return 1;
		}
		if (p.EndsWith("Major", StringComparison.Ordinal))
		{
			return 2;
		}
		if (p.EndsWith("Lingering", StringComparison.Ordinal))
		{
			return 3;
		}
		return 0;
	}

	private static int Biome(string p)
	{
		string text = p.ToLowerInvariant();
		if (text.Contains("deepnorth"))
		{
			return 8;
		}
		if (text.Contains("ashlands"))
		{
			return 7;
		}
		if (text.Contains("mistlands"))
		{
			return 6;
		}
		if (text.Contains("ocean"))
		{
			return 5;
		}
		if (text.Contains("plains"))
		{
			return 4;
		}
		if (text.Contains("mountain") || text.Contains("cave"))
		{
			return 3;
		}
		if (text.Contains("swamp"))
		{
			return 2;
		}
		if (text.Contains("forest"))
		{
			return 1;
		}
		return 0;
	}

	private static bool IsClothes(RecipeFacts f)
	{
		return ClothesPrefabs.Contains(f.Prefab);
	}

	private static string SetStem(string prefab)
	{
		string text = prefab;
		string[] array = new string[3] { "Armor", "Helmet", "Cape" };
		foreach (string text2 in array)
		{
			if (text.StartsWith(text2, StringComparison.Ordinal) && text.Length > text2.Length)
			{
				text = text.Substring(text2.Length);
				break;
			}
		}
		bool flag = text.EndsWith("_Ashlands", StringComparison.Ordinal);
		if (flag)
		{
			text = text.Substring(0, text.Length - "_Ashlands".Length);
		}
		array = new string[6] { "Chest", "Legs", "legs", "Hood", "Cuirass", "Greaves" };
		foreach (string text3 in array)
		{
			if (text.EndsWith(text3, StringComparison.Ordinal) && text.Length > text3.Length)
			{
				text = text.Substring(0, text.Length - text3.Length);
				break;
			}
		}
		if (!flag)
		{
			return text;
		}
		return text + "_Ashlands";
	}

	private static string Alias(string stem)
	{
		if (!SetAlias.TryGetValue(stem, out var value))
		{
			return stem;
		}
		return value;
	}

	private static string Label(string stem)
	{
		if (SetLabel.TryGetValue(stem, out var value))
		{
			return value;
		}
		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 0; i < stem.Length; i++)
		{
			char c = stem[i];
			if (i > 0 && char.IsUpper(c) && !char.IsUpper(stem[i - 1]))
			{
				stringBuilder.Append(' ');
			}
			stringBuilder.Append((c == '_') ? ' ' : c);
		}
		return stringBuilder.ToString();
	}

	private static void ResolveAnchors(List<RecipeInfo> infos)
	{
		Dictionary<string, RecipeInfo> dictionary = new Dictionary<string, RecipeInfo>();
		Dictionary<string, RecipeInfo> dictionary2 = new Dictionary<string, RecipeInfo>();
		for (int i = 0; i < infos.Count; i++)
		{
			RecipeInfo recipeInfo = infos[i];
			string key = recipeInfo.Facts.Station + "|" + recipeInfo.Facts.Prefab;
			if (!dictionary.ContainsKey(key))
			{
				dictionary[key] = recipeInfo;
			}
			if (recipeInfo.Category == CraftCategory.Cast)
			{
				continue;
			}
			for (int j = 0; j < recipeInfo.Facts.Ingredients.Count; j++)
			{
				string prefab = recipeInfo.Facts.Ingredients[j].Prefab;
				if (prefab.StartsWith("Mold", StringComparison.Ordinal) && !dictionary2.ContainsKey(prefab))
				{
					dictionary2[prefab] = recipeInfo;
				}
			}
		}
		for (int k = 0; k < infos.Count; k++)
		{
			RecipeInfo recipeInfo2 = infos[k];
			if (recipeInfo2.Category == CraftCategory.Cast)
			{
				recipeInfo2.Root = FindProduct(recipeInfo2, dictionary, dictionary2);
			}
			else if (IsEquipment(recipeInfo2))
			{
				recipeInfo2.Root = FindBase(recipeInfo2, dictionary);
			}
		}
		for (int l = 0; l < infos.Count; l++)
		{
			RecipeInfo recipeInfo3 = infos[l];
			int num = 0;
			RecipeInfo root = recipeInfo3.Root;
			while (root != null && root.Root != null && num < 8)
			{
				root = root.Root;
				num++;
			}
			if (recipeInfo3.Root != null)
			{
				recipeInfo3.Root = root;
				recipeInfo3.Depth = num + 1;
			}
		}
	}

	private static bool IsEquipment(RecipeInfo n)
	{
		if (n.Category != CraftCategory.Weapons && n.Category != CraftCategory.Shields && n.Category != CraftCategory.Tools && n.Category != CraftCategory.Armor)
		{
			return n.Category == CraftCategory.Magic;
		}
		return true;
	}

	private static RecipeInfo FindBase(RecipeInfo n, Dictionary<string, RecipeInfo> byKey)
	{
		for (int i = 0; i < n.Facts.Ingredients.Count; i++)
		{
			Ingredient ingredient = n.Facts.Ingredients[i];
			if (!ingredient.Idol && !(ingredient.Prefab == n.Facts.Prefab) && byKey.TryGetValue(n.Facts.Station + "|" + ingredient.Prefab, out var value) && IsEquipment(value) && value != n)
			{
				return value;
			}
		}
		return null;
	}

	private static RecipeInfo FindProduct(RecipeInfo cast, Dictionary<string, RecipeInfo> byKey, Dictionary<string, RecipeInfo> byMold)
	{
		string prefab = cast.Facts.Prefab;
		RecipeInfo value;
		if (prefab.EndsWith("Uncooked", StringComparison.Ordinal))
		{
			string text = prefab.Substring(0, prefab.Length - "Uncooked".Length);
			if (byKey.TryGetValue(cast.Facts.Station + "|" + text, out value) && value != cast)
			{
				return value;
			}
		}
		for (int i = 0; i < cast.Facts.Ingredients.Count; i++)
		{
			string prefab2 = cast.Facts.Ingredients[i].Prefab;
			if (prefab2.StartsWith("Mold", StringComparison.Ordinal) && byMold.TryGetValue(prefab2, out value) && value != cast)
			{
				return value;
			}
		}
		return null;
	}
}
internal sealed class Ingredient
{
	public string Prefab;

	public int Amount;

	public int PerLevel;

	public bool Idol;
}
internal sealed class RecipeFacts
{
	public string Station = "";

	public int MinLevel;

	public string Prefab = "";

	public string Name = "";

	public string ItemType = "";

	public string Skill = "";

	public int MaxQuality;

	public int Amount = 1;

	public float Food;

	public float Stamina;

	public float Eitr;

	public string FoodFrom = "";

	public string AmmoType = "";

	public readonly List<Ingredient> Ingredients = new List<Ingredient>();

	public float FoodTotal => Food + Stamina + Eitr;

	public bool HasIngredient(string prefab)
	{
		for (int i = 0; i < Ingredients.Count; i++)
		{
			if (Ingredients[i].Prefab == prefab)
			{
				return true;
			}
		}
		return false;
	}

	public static RecipeFacts FromTsv(string line)
	{
		string[] array = line.Split(new char[1] { '\t' });
		if (array.Length < 17)
		{
			return null;
		}
		RecipeFacts recipeFacts = new RecipeFacts
		{
			Station = array[0],
			MinLevel = ToInt(array[2]),
			Prefab = array[3],
			Name = array[4],
			ItemType = array[5],
			Skill = array[6],
			MaxQuality = ToInt(array[7]),
			Amount = ToInt(array[8]),
			Food = ToFloat(array[9]),
			Stamina = ToFloat(array[10]),
			Eitr = ToFloat(array[11]),
			AmmoType = ((array.Length > 23) ? array[23] : "")
		};
		string[] array2 = array[16].Split(new char[1] { ';' });
		foreach (string text in array2)
		{
			if (text.Length != 0)
			{
				string[] array3 = text.Split(new char[1] { ':' });
				recipeFacts.Ingredients.Add(new Ingredient
				{
					Prefab = array3[0],
					Amount = ((array3.Length > 1) ? ToInt(array3[1]) : 0),
					PerLevel = ((array3.Length > 2) ? ToInt(array3[2]) : 0),
					Idol = (array3.Length > 3 && array3[3] == "idol")
				});
			}
		}
		return recipeFacts;
	}

	private static int ToInt(string s)
	{
		if (!int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
		{
			return 0;
		}
		return result;
	}

	private static float ToFloat(string s)
	{
		if (!float.TryParse(s.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
		{
			return 0f;
		}
		return result;
	}
}
internal sealed class RecipeInfo
{
	public RecipeFacts Facts;

	public CraftCategory Category = CraftCategory.Misc;

	public string Group = "";

	public int GroupBand;

	public int GroupOrder = 500;

	public int KindRank;

	public int ClassRank;

	public int PieceRank;

	public int ItemTier;

	public float Value;

	public string SetKey = "";

	public string[] Subs = new string[0];

	public string SortName = "";

	public RecipeInfo Root;

	public int Depth;

	public RecipeInfo Anchor => Root ?? this;

	public bool HasSub(string key)
	{
		for (int i = 0; i < Subs.Length; i++)
		{
			if (Subs[i] == key)
			{
				return true;
			}
		}
		return false;
	}
}
internal static class RecipeOrder
{
	public static int Compare(RecipeInfo a, int craftTierA, RecipeInfo b, int craftTierB)
	{
		int num = craftTierA.CompareTo(craftTierB);
		if (num != 0)
		{
			return num;
		}
		num = a.GroupBand.CompareTo(b.GroupBand);
		if (num != 0)
		{
			return num;
		}
		num = a.GroupOrder.CompareTo(b.GroupOrder);
		if (num != 0)
		{
			return num;
		}
		num = string.Compare(a.Group, b.Group, StringComparison.OrdinalIgnoreCase);
		if (num != 0)
		{
			return num;
		}
		return CompareInGroup(a, b);
	}

	public static int CompareInGroup(RecipeInfo a, RecipeInfo b)
	{
		RecipeInfo anchor = a.Anchor;
		RecipeInfo anchor2 = b.Anchor;
		int num = anchor.KindRank.CompareTo(anchor2.KindRank);
		if (num != 0)
		{
			return num;
		}
		num = anchor.ClassRank.CompareTo(anchor2.ClassRank);
		if (num != 0)
		{
			return num;
		}
		num = anchor.PieceRank.CompareTo(anchor2.PieceRank);
		if (num != 0)
		{
			return num;
		}
		num = anchor2.Value.CompareTo(anchor.Value);
		if (num != 0)
		{
			return num;
		}
		num = anchor.ItemTier.CompareTo(anchor2.ItemTier);
		if (num != 0)
		{
			return num;
		}
		num = anchor.Facts.MinLevel.CompareTo(anchor2.Facts.MinLevel);
		if (num != 0)
		{
			return num;
		}
		num = string.Compare(anchor.SortName, anchor2.SortName, StringComparison.OrdinalIgnoreCase);
		if (num != 0)
		{
			return num;
		}
		num = a.Depth.CompareTo(b.Depth);
		if (num != 0)
		{
			return num;
		}
		return string.Compare(a.SortName, b.SortName, StringComparison.OrdinalIgnoreCase);
	}
}
internal static class SortEngine
{
	private static Dictionary<Recipe, RecipeInfo> _map;

	private static ObjectDB _db;

	private static string _language;

	private static readonly HashSet<Recipe> Unknown = new HashSet<Recipe>();

	public static RecipeInfo Info(Recipe recipe)
	{
		if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null)
		{
			return null;
		}
		EnsureBuilt();
		if (_map != null && _map.TryGetValue(recipe, out var value))
		{
			return value;
		}
		if (_map != null && !Unknown.Contains(recipe))
		{
			Rebuild();
			if (_map.TryGetValue(recipe, out value))
			{
				return value;
			}
			Unknown.Add(recipe);
		}
		return null;
	}

	public static CraftCategory CategoryOf(Recipe recipe)
	{
		return Info(recipe)?.Category ?? CraftCategory.Misc;
	}

	public static int Compare(Recipe a, Recipe b)
	{
		RecipeInfo recipeInfo = Info(a);
		RecipeInfo recipeInfo2 = Info(b);
		if (recipeInfo == null || recipeInfo2 == null)
		{
			if (recipeInfo != null)
			{
				return -1;
			}
			return (recipeInfo2 != null) ? 1 : 0;
		}
		return RecipeOrder.Compare(recipeInfo, 0, recipeInfo2, 0);
	}

	private static void EnsureBuilt()
	{
		ObjectDB instance = ObjectDB.instance;
		string text = CurrentLanguage();
		if (_map == null || !((Object)(object)instance == (Object)(object)_db) || !(text == _language))
		{
			Rebuild();
		}
	}

	private static void Rebuild()
	{
		_map = new Dictionary<Recipe, RecipeInfo>();
		_db = ObjectDB.instance;
		_language = CurrentLanguage();
		Unknown.Clear();
		if ((Object)(object)_db == (Object)null || _db.m_recipes == null)
		{
			return;
		}
		List<Recipe> list = new List<Recipe>(_db.m_recipes.Count);
		List<RecipeFacts> list2 = new List<RecipeFacts>(_db.m_recipes.Count);
		foreach (Recipe recipe in _db.m_recipes)
		{
			RecipeFacts recipeFacts = FactsReader.Read(recipe);
			if (recipeFacts != null)
			{
				list.Add(recipe);
				list2.Add(recipeFacts);
			}
		}
		List<RecipeInfo> list3 = RecipeClassifier.Build(list2);
		for (int i = 0; i < list3.Count; i++)
		{
			list3[i].SortName = LocalizedName(list[i], list2[i]);
			_map[list[i]] = list3[i];
		}
	}

	private static string LocalizedName(Recipe r, RecipeFacts f)
	{
		try
		{
			if (Localization.instance != null && !string.IsNullOrEmpty(f.Name))
			{
				return Localization.instance.Localize(f.Name);
			}
		}
		catch
		{
		}
		return f.Prefab;
	}

	private static string CurrentLanguage()
	{
		try
		{
			return (Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : "";
		}
		catch
		{
			return "";
		}
	}
}
internal static class FactsReader
{
	public static SharedData FoodSource(SharedData s)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Invalid comparison between Unknown and I4
		if ((int)s.m_itemType == 2 || (Object)(object)s.m_appendToolTip == (Object)null || s.m_appendToolTip.m_itemData == null)
		{
			return s;
		}
		SharedData shared = s.m_appendToolTip.m_itemData.m_shared;
		if (shared == null || (!(shared.m_food > 0f) && !(shared.m_foodStamina > 0f) && !(shared.m_foodEitr > 0f)))
		{
			return s;
		}
		return shared;
	}

	public static RecipeFacts Read(Recipe r)
	{
		if ((Object)(object)r == (Object)null || (Object)(object)r.m_item == (Object)null)
		{
			return null;
		}
		SharedData val = ((r.m_item.m_itemData != null) ? r.m_item.m_itemData.m_shared : null);
		if (val == null)
		{
			return null;
		}
		SharedData val2 = FoodSource(val);
		RecipeFacts recipeFacts = new RecipeFacts
		{
			Station = (((Object)(object)r.m_craftingStation != (Object)null) ? r.m_craftingStation.m_name : "(hand)"),
			MinLevel = r.m_minStationLevel,
			Prefab = ((Object)r.m_item).name,
			Name = val.m_name,
			ItemType = ((object)Unsafe.As<ItemType, ItemType>(ref val.m_itemType)/*cast due to .constrained prefix*/).ToString(),
			Skill = ((object)Unsafe.As<SkillType, SkillType>(ref val.m_skillType)/*cast due to .constrained prefix*/).ToString(),
			MaxQuality = val.m_maxQuality,
			Amount = r.m_amount,
			Food = val2.m_food,
			Stamina = val2.m_foodStamina,
			Eitr = val2.m_foodEitr,
			FoodFrom = ((val2 == val) ? "" : (((Object)val.m_appendToolTip).name ?? "")),
			AmmoType = (val.m_ammoType ?? "")
		};
		if (r.m_resources != null)
		{
			Requirement[] resources = r.m_resources;
			foreach (Requirement val3 in resources)
			{
				if (val3 != null && !((Object)(object)val3.m_resItem == (Object)null))
				{
					recipeFacts.Ingredients.Add(new Ingredient
					{
						Prefab = ((Object)val3.m_resItem).name,
						Amount = val3.m_amount,
						PerLevel = val3.m_amountPerLevel,
						Idol = val3.m_upgraderResource
					});
				}
			}
		}
		return recipeFacts;
	}
}
internal static class SubCategories
{
	private static readonly string[] None = new string[0];

	private static readonly string[] Weapons = new string[12]
	{
		"OneHand", "TwoHand", "Knives", "Swords", "Clubs", "Axes", "Spears", "Po