Decompiled source of CraftingMenuSorter v1.0.0

CraftingMenuSorter.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("CraftingMenuSorter")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+a59541c05bc4a4c108b884cdfa3e1722c866c52c")]
[assembly: AssemblyProduct("CraftingMenuSorter")]
[assembly: AssemblyTitle("CraftingMenuSorter")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CraftingMenuSorter
{
	internal enum Biome
	{
		Unknown,
		Meadows,
		BlackForest,
		Swamp,
		Mountains,
		Plains,
		Mistlands,
		Ashlands,
		DeepNorth
	}
	internal static class Biomes
	{
		private static readonly Dictionary<string, Biome> table;

		private static readonly Dictionary<string, Biome> overrides;

		private static string overridesFrom;

		private static readonly HashSet<string> reportedUnknown;

		static Biomes()
		{
			table = new Dictionary<string, Biome>(StringComparer.OrdinalIgnoreCase);
			overrides = new Dictionary<string, Biome>(StringComparer.OrdinalIgnoreCase);
			reportedUnknown = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			Add(Biome.Meadows, "Wood", "Stone", "Flint", "LeatherScraps", "DeerHide", "Resin", "Feathers", "HardAntler", "BoneFragments", "Honey", "Raspberry", "Mushroom", "Dandelion", "QueenBee", "Thistle", "Beech1", "BeechSeeds", "Carrot", "CarrotSeeds");
			Add(Biome.BlackForest, "Copper", "CopperOre", "CopperScrap", "Tin", "TinOre", "Bronze", "BronzeNails", "BronzeScrap", "FineWood", "RoundLog", "TrollHide", "GreydwarfEye", "SurtlingCore", "Coal", "AncientSeed", "Chitin", "Amber", "AmberPearl", "Ruby", "Coins", "SerpentScale", "SharpeningStone", "ThunderStone");
			Add(Biome.Swamp, "Iron", "IronScrap", "IronOre", "IronNails", "Guck", "ElderBark", "Entrails", "Bloodbag", "BloodBag", "Ooze", "Chain", "WitheredBone", "Root", "Turnip", "TurnipSeeds");
			Add(Biome.Mountains, "Silver", "SilverOre", "Obsidian", "Crystal", "WolfPelt", "WolfFang", "WolfClaw", "WolfHairBundle", "FreezeGland", "DragonTear", "FenrisHair", "FenrisClaw", "Onion", "OnionSeeds");
			Add(Biome.Plains, "BlackMetal", "BlackMetalScrap", "LoxPelt", "LinenThread", "Flax", "Needle", "Tar", "Barley", "GoblinTotem", "JuteRed", "Cloudberry");
			Add(Biome.Mistlands, "BlackMarble", "BlackCore", "Carapace", "Eitr", "Mandible", "Sap", "YggdrasilSap", "YggdrasilWood", "Softtissue", "SoftTissue", "Wisp", "ScaleHide", "DvergrNeedle", "RoyalJelly", "BileBag", "JuteBlue", "MushroomJotunPuffs", "MushroomMagecap", "DvergrKey", "ChickenEgg", "YggdrasilRoot");
			Add(Biome.Ashlands, "Flametal", "FlametalOre", "FlametalNew", "AskHide", "AskBladder", "Blackwood", "BlackWood", "Grausten", "Sulfur", "SulfurStone", "ProustitePowder", "CharredBone", "CharredBones", "CharredCogwheel", "MoltenCore", "MorgenHeart", "CelestialFeather", "BonemawSerpentScale", "BoneMawSerpentScale", "BonemawScale", "Vineberry", "Fiddleheadfern", "VoltureEgg", "CharredSpawnerFragments");
			Add(Biome.DeepNorth, "BlackIce", "FrostCore", "FrostWisp", "HoarFrost", "BearHide", "BjornHide", "MooseHide", "SealHide", "NornThread", "AncientGemstoneBlack", "BloodGoldOre", "BloodGold", "GoldOre", "ElakingHair", "FirConeFrost", "FlowStone", "PowderedDragonEgg", "DarkWood");
		}

		private static void Add(Biome biome, params string[] prefabs)
		{
			foreach (string key in prefabs)
			{
				table[key] = biome;
			}
		}

		public static Biome Of(Recipe recipe)
		{
			Biome biome = Biome.Unknown;
			if ((Object)(object)recipe == (Object)null || recipe.m_resources == null)
			{
				return biome;
			}
			Requirement[] resources = recipe.m_resources;
			for (int i = 0; i < resources.Length; i++)
			{
				Biome biome2 = OfMaterial(resources[i]);
				if (biome2 > biome)
				{
					biome = biome2;
				}
			}
			return biome;
		}

		public static string Describe(Recipe recipe)
		{
			if ((Object)(object)recipe == (Object)null || recipe.m_resources == null)
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder();
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement requirement in resources)
			{
				if (stringBuilder.Length > 0)
				{
					stringBuilder.Append(", ");
				}
				stringBuilder.Append(PrefabName(requirement)).Append('=').Append(OfMaterial(requirement));
			}
			return stringBuilder.ToString();
		}

		public static void Invalidate()
		{
			overridesFrom = null;
		}

		private static Biome OfMaterial(Requirement requirement)
		{
			string text = PrefabName(requirement);
			if (text.Length == 0)
			{
				return Biome.Unknown;
			}
			ReadOverrides();
			if (overrides.TryGetValue(text, out var value) || table.TryGetValue(text, out value))
			{
				return value;
			}
			if (Plugin.LogRows.Value && !text.StartsWith("Trophy", StringComparison.OrdinalIgnoreCase) && !text.EndsWith("Trophy", StringComparison.OrdinalIgnoreCase) && reportedUnknown.Add(text))
			{
				Plugin.Log.LogInfo((object)("Material " + text + " is in no biome table; add it to Biomes.Overrides as " + text + "=<Biome>"));
			}
			return Biome.Unknown;
		}

		private static string PrefabName(Requirement requirement)
		{
			if (requirement == null || (Object)(object)requirement.m_resItem == (Object)null)
			{
				return string.Empty;
			}
			return ((Object)((Component)requirement.m_resItem).gameObject).name;
		}

		private static void ReadOverrides()
		{
			string text = Plugin.BiomeOverrides.Value ?? string.Empty;
			if (text == overridesFrom)
			{
				return;
			}
			overridesFrom = text;
			overrides.Clear();
			string[] array = text.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { '=' });
				if (array2.Length == 2)
				{
					string text2 = array2[0].Trim();
					if (text2.Length > 0 && Enum.TryParse<Biome>(array2[1].Trim(), ignoreCase: true, out var result))
					{
						overrides[text2] = result;
					}
				}
			}
		}
	}
	internal static class CookedResults
	{
		private const int MaxSteps = 4;

		private static readonly Dictionary<string, SharedData> results = new Dictionary<string, SharedData>();

		private static ZNetScene builtFor;

		public static SharedData Resolve(SharedData shared)
		{
			if (shared == null)
			{
				return null;
			}
			Build();
			SharedData val = shared;
			for (int i = 0; i < 4; i++)
			{
				if (string.IsNullOrEmpty(val.m_name))
				{
					break;
				}
				if (!results.TryGetValue(val.m_name, out var value))
				{
					break;
				}
				if (value == val)
				{
					break;
				}
				val = value;
			}
			return val;
		}

		private static void Build()
		{
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)builtFor == (Object)(object)instance)
			{
				return;
			}
			builtFor = instance;
			results.Clear();
			int num = 0;
			foreach (GameObject prefab in instance.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				CookingStation component = prefab.GetComponent<CookingStation>();
				if ((Object)(object)component == (Object)null || component.m_conversion == null)
				{
					continue;
				}
				num++;
				foreach (ItemConversion item in component.m_conversion)
				{
					if (item != null && !((Object)(object)item.m_from == (Object)null) && !((Object)(object)item.m_to == (Object)null))
					{
						SharedData val = ((item.m_from.m_itemData != null) ? item.m_from.m_itemData.m_shared : null);
						SharedData val2 = ((item.m_to.m_itemData != null) ? item.m_to.m_itemData.m_shared : null);
						if (val != null && val2 != null && !string.IsNullOrEmpty(val.m_name) && !results.ContainsKey(val.m_name))
						{
							results[val.m_name] = val2;
						}
					}
				}
			}
			Plugin.Log.LogDebug((object)$"{results.Count} cooking conversions read from {num} cooking station prefabs");
		}
	}
	internal enum FoodKind
	{
		Health,
		Stamina,
		Magic,
		Balanced,
		Other
	}
	internal static class FoodSections
	{
		private static readonly FoodKind[] DefaultOrder = new FoodKind[5]
		{
			FoodKind.Health,
			FoodKind.Stamina,
			FoodKind.Magic,
			FoodKind.Balanced,
			FoodKind.Other
		};

		public static bool TryPlace(List<RecipeDataPair> rows, out List<SectionDef> sections, out Placement[] placements)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			sections = null;
			placements = null;
			FoodKind[] array = new FoodKind[rows.Count];
			SharedData[] array2 = (SharedData[])(object)new SharedData[rows.Count];
			bool flag = false;
			for (int i = 0; i < rows.Count; i++)
			{
				SharedData val = SectionLayout.SharedOf(rows[i]);
				if (IsIngredient(val))
				{
					array2[i] = val;
					array[i] = FoodKind.Other;
				}
				else
				{
					array2[i] = CookedResults.Resolve(val);
					array[i] = Classify(array2[i]);
					flag |= array[i] != FoodKind.Other;
				}
			}
			if (!flag)
			{
				return false;
			}
			if (Plugin.LogRows.Value)
			{
				LogRows(rows, array2, array);
			}
			List<FoodKind> list = SectionLayout.ParseOrder(Plugin.FoodOrder.Value, DefaultOrder, Alias);
			sections = new List<SectionDef>(list.Count);
			foreach (FoodKind item in list)
			{
				FoodKind k = item;
				sections.Add(new SectionDef("food:" + k, () => Label(k), KindColor(k), k == FoodKind.Other, k != FoodKind.Other));
			}
			placements = new Placement[rows.Count];
			for (int num = 0; num < rows.Count; num++)
			{
				SharedData val2 = SectionLayout.SharedOf(rows[num]);
				placements[num] = new Placement
				{
					Section = list.IndexOf(array[num]),
					Value = ((array2[num] != null) ? SortValue(array2[num], array[num]) : 0f),
					Name = ((val2 != null) ? Localization.instance.Localize(val2.m_name) : string.Empty)
				};
			}
			return true;
		}

		public static FoodKind Classify(SharedData shared)
		{
			if (shared == null)
			{
				return FoodKind.Other;
			}
			float food = shared.m_food;
			float foodStamina = shared.m_foodStamina;
			float foodEitr = shared.m_foodEitr;
			if (food <= 0f && foodStamina <= 0f && foodEitr <= 0f)
			{
				return FoodKind.Other;
			}
			if (food < foodEitr / 2f && foodStamina < foodEitr / 2f)
			{
				return FoodKind.Magic;
			}
			if (foodStamina < food / 2f)
			{
				return FoodKind.Health;
			}
			if (food < foodStamina / 2f)
			{
				return FoodKind.Stamina;
			}
			if (Plugin.BalancedSection.Value)
			{
				return FoodKind.Balanced;
			}
			if (!(food >= foodStamina))
			{
				return FoodKind.Stamina;
			}
			return FoodKind.Health;
		}

		public static float SortValue(SharedData shared, FoodKind kind)
		{
			return kind switch
			{
				FoodKind.Health => shared.m_food, 
				FoodKind.Stamina => shared.m_foodStamina, 
				FoodKind.Magic => shared.m_foodEitr, 
				FoodKind.Balanced => Mathf.Max(new float[3] { shared.m_food, shared.m_foodStamina, shared.m_foodEitr }), 
				_ => 0f, 
			};
		}

		private static bool IsIngredient(SharedData own)
		{
			if (own == null)
			{
				return true;
			}
			if (own.m_food <= 0f && own.m_foodStamina <= 0f && own.m_foodEitr <= 0f)
			{
				return true;
			}
			string value = Plugin.Ingredients.Value;
			if (string.IsNullOrEmpty(value))
			{
				return false;
			}
			string value2 = Localization.instance.Localize(own.m_name);
			string[] array = value.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0 && (text.Equals(own.m_name, StringComparison.OrdinalIgnoreCase) || text.Equals(value2, StringComparison.OrdinalIgnoreCase)))
				{
					return true;
				}
			}
			return false;
		}

		private static void LogRows(List<RecipeDataPair> rows, SharedData[] foods, FoodKind[] kinds)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			CraftingStation val = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetCurrentCraftingStation() : null);
			Plugin.Log.LogInfo((object)string.Format("Food list at {0}: {1} rows", ((Object)(object)val != (Object)null) ? val.m_name : "no station", rows.Count));
			for (int i = 0; i < rows.Count; i++)
			{
				RecipeDataPair val2 = rows[i];
				Recipe recipe = ((RecipeDataPair)(ref val2)).Recipe;
				ItemDrop val3 = (((Object)(object)recipe != (Object)null) ? recipe.m_item : null);
				SharedData val4 = SectionLayout.SharedOf(rows[i]);
				string text = (((Object)(object)val3 != (Object)null) ? ((Object)((Component)val3).gameObject).name : "null");
				string text2 = SectionLayout.RowText(rows[i]);
				if (val4 == null)
				{
					Plugin.Log.LogInfo((object)string.Format("  {0}: recipe {1} prefab {2} has no item data; row text '{3}'", i, ((Object)(object)recipe != (Object)null) ? ((Object)recipe).name : "null", text, text2));
					continue;
				}
				SharedData val5 = foods[i];
				string text3 = ((val5 != null && val5 != val4) ? $" cooks into {Localization.instance.Localize(val5.m_name)} ({val5.m_name}) type {val5.m_itemType} health {val5.m_food} stamina {val5.m_foodStamina} eitr {val5.m_foodEitr}" : string.Empty);
				ManualLogSource log = Plugin.Log;
				string text4 = $"  {i}: {Localization.instance.Localize(val4.m_name)} ({val4.m_name}) prefab {text} recipe {((Object)recipe).name} type {val4.m_itemType} ";
				object[] obj = new object[7] { val4.m_food, val4.m_foodStamina, val4.m_foodEitr, null, null, null, null };
				val2 = rows[i];
				obj[3] = ((RecipeDataPair)(ref val2)).CanCraft;
				obj[4] = text3;
				obj[5] = kinds[i];
				obj[6] = text2;
				log.LogInfo((object)(text4 + string.Format("health {0} stamina {1} eitr {2} craftable {3}{4} -> {5}; row text '{6}'", obj)));
			}
		}

		private static string Alias(string name)
		{
			if (!name.Equals("Eitr", StringComparison.OrdinalIgnoreCase))
			{
				return name;
			}
			return FoodKind.Magic.ToString();
		}

		private static string Label(FoodKind kind)
		{
			return kind switch
			{
				FoodKind.Health => Plugin.HealthLabel.Value, 
				FoodKind.Stamina => Plugin.StaminaLabel.Value, 
				FoodKind.Magic => Plugin.MagicFoodLabel.Value, 
				FoodKind.Balanced => Plugin.BalancedLabel.Value, 
				_ => Plugin.OtherLabel.Value, 
			};
		}

		private static Color KindColor(FoodKind kind)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			return (Color)(kind switch
			{
				FoodKind.Health => new Color(1f, 0.5f, 0.5f), 
				FoodKind.Stamina => new Color(1f, 1f, 0.5f), 
				FoodKind.Magic => new Color(0.565f, 0.565f, 1f), 
				FoodKind.Balanced => new Color(1f, 0.8f, 0.5f), 
				_ => new Color(0.8f, 0.8f, 0.8f), 
			});
		}
	}
	internal enum GearKind
	{
		OneHanded,
		TwoHanded,
		DualWielding,
		Bows,
		Magic,
		Shields,
		Armor,
		Capes,
		Trinkets,
		Tools,
		Ammunition,
		Bombs,
		Misc
	}
	internal static class GearSections
	{
		private static readonly GearKind[] DefaultOrder = new GearKind[13]
		{
			GearKind.OneHanded,
			GearKind.TwoHanded,
			GearKind.DualWielding,
			GearKind.Bows,
			GearKind.Magic,
			GearKind.Shields,
			GearKind.Armor,
			GearKind.Capes,
			GearKind.Trinkets,
			GearKind.Tools,
			GearKind.Ammunition,
			GearKind.Bombs,
			GearKind.Misc
		};

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

		public static bool TryPlace(List<RecipeDataPair> rows, out List<SectionDef> sections, out Placement[] placements)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			sections = null;
			placements = null;
			GearKind[] array = new GearKind[rows.Count];
			bool flag = false;
			for (int i = 0; i < rows.Count; i++)
			{
				array[i] = Classify(SectionLayout.SharedOf(rows[i]));
				flag |= array[i] != GearKind.Misc;
			}
			if (!flag)
			{
				return false;
			}
			List<GearKind> list = SectionLayout.ParseOrder(Plugin.GearOrder.Value, DefaultOrder, null);
			sections = new List<SectionDef>(list.Count);
			foreach (GearKind item in list)
			{
				GearKind k = item;
				sections.Add(new SectionDef("gear:" + k, () => Label(k), HeadingColor, k == GearKind.Misc, showValue: false));
			}
			placements = new Placement[rows.Count];
			for (int num = 0; num < rows.Count; num++)
			{
				SharedData val = SectionLayout.SharedOf(rows[num]);
				RecipeDataPair val2 = rows[num];
				float num2 = (float)Biomes.Of(((RecipeDataPair)(ref val2)).Recipe);
				if (array[num] == GearKind.Ammunition)
				{
					float num3 = num2;
					val2 = rows[num];
					num2 = num3 + AmmoGroup(val, ((RecipeDataPair)(ref val2)).Recipe);
				}
				string text = ((val != null) ? Localization.instance.Localize(val.m_name) : string.Empty);
				placements[num] = new Placement
				{
					Section = list.IndexOf(array[num]),
					Value = num2,
					Name = ((array[num] == GearKind.Armor) ? ArmorSortKey(val, text) : text)
				};
			}
			if (Plugin.LogRows.Value)
			{
				LogRows(rows, array, placements);
			}
			return true;
		}

		public static GearKind Classify(SharedData shared)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Invalid comparison between Unknown and I4
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Invalid comparison between Unknown and I4
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Invalid comparison between Unknown and I4
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected I4, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Invalid comparison between Unknown and I4
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Invalid comparison between Unknown and I4
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Invalid comparison between Unknown and I4
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Invalid comparison between Unknown and I4
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Invalid comparison between Unknown and I4
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Invalid comparison between Unknown and I4
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Invalid comparison between Unknown and I4
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Invalid comparison between Unknown and I4
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Invalid comparison between Unknown and I4
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Invalid comparison between Unknown and I4
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Invalid comparison between Unknown and I4
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Invalid comparison between Unknown and I4
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Invalid comparison between Unknown and I4
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Invalid comparison between Unknown and I4
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Invalid comparison between Unknown and I4
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Invalid comparison between Unknown and I4
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Invalid comparison between Unknown and I4
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Invalid comparison between Unknown and I4
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Invalid comparison between Unknown and I4
			if (shared == null)
			{
				return GearKind.Misc;
			}
			ItemType itemType = shared.m_itemType;
			SkillType skillType = shared.m_skillType;
			if ((int)skillType == 9 || (int)skillType == 10)
			{
				return GearKind.Magic;
			}
			if (shared.m_attack != null && shared.m_attack.m_consumeItem)
			{
				if (!((DamageTypes)(ref shared.m_damages)).HaveDamage())
				{
					return GearKind.Misc;
				}
				return GearKind.Bombs;
			}
			if (shared.m_attackStatusEffect is SE_Harpooned)
			{
				return GearKind.Tools;
			}
			if (shared.m_tamedOnly || (int)shared.m_animationState == 17)
			{
				return GearKind.Tools;
			}
			if ((int)itemType <= 17)
			{
				switch (itemType - 4)
				{
				default:
					if ((int)itemType != 17)
					{
						goto IL_00d0;
					}
					return GearKind.Capes;
				case 2:
				case 3:
				case 7:
					return GearKind.Armor;
				case 1:
					return GearKind.Shields;
				case 5:
					break;
				case 0:
					goto IL_00bb;
				case 4:
				case 6:
					goto IL_00d0;
				}
			}
			else
			{
				if ((int)itemType == 19)
				{
					return GearKind.Tools;
				}
				if ((int)itemType != 23)
				{
					if ((int)itemType == 24)
					{
						return GearKind.Trinkets;
					}
					goto IL_00d0;
				}
			}
			return GearKind.Ammunition;
			IL_00bb:
			if (!((DamageTypes)(ref shared.m_damages)).HaveDamage())
			{
				return GearKind.Misc;
			}
			return GearKind.Bows;
			IL_00d0:
			if (((int)itemType == 3 || (int)itemType == 14 || (int)itemType == 22 || (int)itemType == 15) && !((DamageTypes)(ref shared.m_damages)).HaveDamage())
			{
				if ((int)itemType == 15 || shared.m_attack == null || !((Object)(object)shared.m_attack.m_attackProjectile != (Object)null))
				{
					return GearKind.Misc;
				}
				return GearKind.Tools;
			}
			if ((int)skillType == 8 || (int)skillType == 14)
			{
				if (!((DamageTypes)(ref shared.m_damages)).HaveDamage())
				{
					return GearKind.Misc;
				}
				return GearKind.Bows;
			}
			if ((int)skillType == 12)
			{
				return GearKind.Tools;
			}
			if ((int)itemType == 3 && (int)skillType == 7 && !HasElementalDamage(shared))
			{
				return GearKind.Tools;
			}
			if ((int)itemType == 22 || (int)shared.m_animationState == 15 || (int)skillType == 11)
			{
				return GearKind.DualWielding;
			}
			if ((int)itemType == 3 || (int)itemType == 15)
			{
				return GearKind.OneHanded;
			}
			if ((int)itemType == 14)
			{
				return GearKind.TwoHanded;
			}
			return GearKind.Misc;
		}

		private static string ArmorSortKey(SharedData shared, string name)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Invalid comparison between Unknown and I4
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			if (shared == null)
			{
				return name;
			}
			string text = (string.IsNullOrEmpty(shared.m_setName) ? name : shared.m_setName);
			ItemType itemType = shared.m_itemType;
			int num = (((int)itemType != 6) ? (((int)itemType == 7) ? 1 : (((int)itemType != 11) ? 3 : 2)) : 0);
			return text + "|" + num + "|" + name;
		}

		private static float AmmoGroup(SharedData shared, Recipe recipe)
		{
			string text = shared?.m_ammoType;
			string text2 = ((!string.IsNullOrEmpty(text)) ? text : (((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null) ? ((Object)((Component)recipe.m_item).gameObject).name : string.Empty));
			if (text2.IndexOf("arrow", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return 200f;
			}
			if (text2.IndexOf("bolt", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return 100f;
			}
			return 0f;
		}

		private static bool HasElementalDamage(SharedData shared)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			DamageTypes damages = shared.m_damages;
			if (!(damages.m_fire > 0f) && !(damages.m_frost > 0f) && !(damages.m_lightning > 0f) && !(damages.m_poison > 0f))
			{
				return damages.m_spirit > 0f;
			}
			return true;
		}

		private static void LogRows(List<RecipeDataPair> rows, GearKind[] kinds, Placement[] placements)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			CraftingStation val = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetCurrentCraftingStation() : null);
			Plugin.Log.LogInfo((object)string.Format("Gear list at {0}: {1} rows", ((Object)(object)val != (Object)null) ? val.m_name : "no station", rows.Count));
			for (int i = 0; i < rows.Count; i++)
			{
				RecipeDataPair val2 = rows[i];
				Recipe recipe = ((RecipeDataPair)(ref val2)).Recipe;
				ItemDrop val3 = (((Object)(object)recipe != (Object)null) ? recipe.m_item : null);
				SharedData val4 = SectionLayout.SharedOf(rows[i]);
				string text = (((Object)(object)val3 != (Object)null) ? ((Object)((Component)val3).gameObject).name : "null");
				string text2 = SectionLayout.RowText(rows[i]);
				if (val4 == null)
				{
					Plugin.Log.LogInfo((object)string.Format("  {0}: recipe {1} prefab {2} has no item data; row text '{3}'", i, ((Object)(object)recipe != (Object)null) ? ((Object)recipe).name : "null", text, text2));
					continue;
				}
				StringBuilder stringBuilder = new StringBuilder();
				object[] obj = new object[11]
				{
					i,
					Localization.instance.Localize(val4.m_name),
					val4.m_name,
					text,
					((Object)recipe).name,
					val4.m_itemType,
					val4.m_skillType,
					val4.m_animationState,
					val4.m_movementModifier,
					val4.m_setName,
					null
				};
				val2 = rows[i];
				obj[10] = ((RecipeDataPair)(ref val2)).CanCraft;
				stringBuilder.Append(string.Format("  {0}: {1} ({2}) prefab {3} recipe {4} type {5} skill {6} anim {7} movement {8} set '{9}' craftable {10}", obj));
				stringBuilder.Append($" ingredients [{Biomes.Describe(recipe)}] -> {(Biome)(placements[i].Value % 100f)} {kinds[i]}; row text '{text2}'");
				Plugin.Log.LogInfo((object)stringBuilder.ToString());
			}
		}

		private static string Label(GearKind kind)
		{
			return kind switch
			{
				GearKind.OneHanded => Plugin.OneHandedLabel.Value, 
				GearKind.TwoHanded => Plugin.TwoHandedLabel.Value, 
				GearKind.DualWielding => Plugin.DualWieldingLabel.Value, 
				GearKind.Bows => Plugin.BowsLabel.Value, 
				GearKind.Magic => Plugin.MagicGearLabel.Value, 
				GearKind.Shields => Plugin.ShieldsLabel.Value, 
				GearKind.Armor => Plugin.ArmorLabel.Value, 
				GearKind.Capes => Plugin.CapesLabel.Value, 
				GearKind.Trinkets => Plugin.TrinketsLabel.Value, 
				GearKind.Tools => Plugin.ToolsLabel.Value, 
				GearKind.Ammunition => Plugin.AmmunitionLabel.Value, 
				GearKind.Bombs => Plugin.BombsLabel.Value, 
				_ => Plugin.MiscLabel.Value, 
			};
		}
	}
	[BepInPlugin("rhenn.craftingmenusorter", "CraftingMenuSorter", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "rhenn.craftingmenusorter";

		public const string PluginName = "CraftingMenuSorter";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> GreyedOutLast;

		internal static ConfigEntry<bool> ShowValues;

		internal static ConfigEntry<bool> RememberCollapsed;

		internal static ConfigEntry<string> FoodOrder;

		internal static ConfigEntry<bool> BalancedSection;

		internal static ConfigEntry<string> Ingredients;

		internal static ConfigEntry<string> HealthLabel;

		internal static ConfigEntry<string> StaminaLabel;

		internal static ConfigEntry<string> MagicFoodLabel;

		internal static ConfigEntry<string> BalancedLabel;

		internal static ConfigEntry<string> OtherLabel;

		internal static ConfigEntry<string> GearOrder;

		internal static ConfigEntry<string> ArmorLabel;

		internal static ConfigEntry<string> OneHandedLabel;

		internal static ConfigEntry<string> TwoHandedLabel;

		internal static ConfigEntry<string> DualWieldingLabel;

		internal static ConfigEntry<string> BowsLabel;

		internal static ConfigEntry<string> MagicGearLabel;

		internal static ConfigEntry<string> ShieldsLabel;

		internal static ConfigEntry<string> CapesLabel;

		internal static ConfigEntry<string> TrinketsLabel;

		internal static ConfigEntry<string> ToolsLabel;

		internal static ConfigEntry<string> AmmunitionLabel;

		internal static ConfigEntry<string> BombsLabel;

		internal static ConfigEntry<string> MiscLabel;

		internal static ConfigEntry<string> BiomeOverrides;

		internal static ConfigEntry<bool> LogRows;

		private readonly Harmony harmony = new Harmony("rhenn.craftingmenusorter");

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch, read when the game starts. false: the mod loads nothing and every crafting list stays as the game has it.");
			GreyedOutLast = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "GreyedOutLast", true, "true: inside each section the recipes you can craft right now come first and the greyed-out ones you lack ingredients for go to the bottom of the section, each part in the section's own order. false: one list per section in the section's order, whether you have the ingredients or not.");
			ShowValues = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowValues", true, "Food lists only: shows the number a section is sorted by at the right end of each of its rows (health, stamina, eitr). Gear rows never show a number.");
			RememberCollapsed = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RememberCollapsed", true, "Saves which sections are collapsed with the character, so they stay collapsed after a restart. false: every section is open when you log in.");
			FoodOrder = ((BaseUnityPlugin)this).Config.Bind<string>("Food", "Order", "Health,Stamina,Magic,Balanced,Other", "The food sections from top to bottom, comma separated (Eitr is accepted for Magic). A section left out goes to the end. A section with no recipes at the current station is not shown.");
			BalancedSection = ((BaseUnityPlugin)this).Config.Bind<bool>("Food", "Balanced", true, "true: food with no clear main value (a white food icon in the inventory: neither health nor stamina is less than half the other) has its own Balanced section. false: such food is listed under whichever of its values is bigger.");
			Ingredients = ((BaseUnityPlugin)this).Config.Bind<string>("Food", "Ingredients", "$item_fishraw", "Items that are only ingredients for other recipes and belong under Other with no number, even though they can be cooked into food: comma separated, by the game's $ name or the name shown in the list (raw fish is $item_fishraw). An item with no health, stamina or eitr of its own is treated as an ingredient without being listed here.");
			HealthLabel = ((BaseUnityPlugin)this).Config.Bind<string>("FoodLabels", "Health", "Health", "Heading of the section for the red-icon foods. A $ token of the game's own, such as $item_food_health, is translated.");
			StaminaLabel = ((BaseUnityPlugin)this).Config.Bind<string>("FoodLabels", "Stamina", "Stamina", "Heading of the section for the yellow-icon foods. $item_food_stamina gives the game's word.");
			MagicFoodLabel = ((BaseUnityPlugin)this).Config.Bind<string>("FoodLabels", "Magic", "Magic", "Heading of the section for the blue-icon eitr foods. $item_food_eitr gives the game's word (Eitr).");
			BalancedLabel = ((BaseUnityPlugin)this).Config.Bind<string>("FoodLabels", "Balanced", "Balanced", "Heading of the section for the white-icon foods, when Food.Balanced is on.");
			OtherLabel = ((BaseUnityPlugin)this).Config.Bind<string>("FoodLabels", "Other", "Other", "Heading of the section for everything at a food station that is not food, such as the mead bases at the cauldron and the raw fish at the preparation table.");
			GearOrder = ((BaseUnityPlugin)this).Config.Bind<string>("Gear", "Order", "OneHanded,TwoHanded,DualWielding,Bows,Magic,Shields,Armor,Capes,Trinkets,Tools,Ammunition,Bombs,Misc", "The gear sections from top to bottom at the workbench, forge, black forge, galdr table and any other station without food, comma separated. A section left out goes to the end. A section with no recipes at the current station is not shown.");
			OneHandedLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "OneHanded", "One Handed", "Heading for one-handed weapons: swords, maces, knives, spears, torches. One-handed axes are under Tools.");
			TwoHandedLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "TwoHanded", "Two Handed", "Heading for two-handed weapons: battleaxes, sledges, atgeirs, greatswords.");
			DualWieldingLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "DualWielding", "Dual Wielding", "Heading for paired weapons and fist weapons: dual axes, dual knives, claws.");
			BowsLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Bows", "Bows", "Heading for bows and crossbows.");
			MagicGearLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Magic", "Magic", "Heading for staffs and wands: anything that uses the elemental or blood magic skill.");
			ShieldsLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Shields", "Shields", "Heading for shields.");
			ArmorLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Armor", "Armor", "Heading for helmets, chest and leg pieces. The pieces of one set stand together, helmet, chest, legs.");
			CapesLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Capes", "Capes", "Heading for capes.");
			TrinketsLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Trinkets", "Trinkets", "Heading for trinkets.");
			ToolsLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Tools", "Tools", "Heading for the hammer, hoe, cultivator, fishing rod and other tools, pickaxes, and one-handed axes.");
			AmmunitionLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Ammunition", "Ammunition", "Heading for arrows, bolts and any other ammunition.");
			BombsLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Bombs", "Bombs", "Heading for thrown items that are used up on the throw: ooze, bile, tar and frost bombs.");
			MiscLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Misc", "Misc", "Heading for everything else a gear station makes, in the game's order.");
			BiomeOverrides = ((BaseUnityPlugin)this).Config.Bind<string>("Biomes", "Overrides", "", "Gear is ordered by the biome of its ingredients, newest biome first, from a built-in table of the game's materials (by prefab name). Add or change entries here, comma separated, as Prefab=Biome, where Biome is one of Meadows, BlackForest, Swamp, Mountains, Plains, Mistlands, Ashlands, DeepNorth. Example: MyModOre=Swamp. With Debug.LogRecipeRows on, the log names every material the table does not know.");
			LogRows = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogRecipeRows", false, "Writes one line per recipe row to the BepInEx log each time a list is built: item, prefab, type, skill, food values or ingredients with their biomes, and the section it went to. For bug reports; off unless you are looking at a wrong row.");
			((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged;
			if (!Enabled.Value)
			{
				Log.LogInfo((object)"CraftingMenuSorter 1.0.0 is disabled in the config; nothing is patched.");
				return;
			}
			harmony.PatchAll(Assembly.GetExecutingAssembly());
			Log.LogInfo((object)"CraftingMenuSorter 1.0.0 loaded.");
		}

		private static void OnSettingChanged(object sender, SettingChangedEventArgs e)
		{
			if ((object)e.ChangedSetting != Enabled && (object)e.ChangedSetting != LogRows)
			{
				Biomes.Invalidate();
				SectionLayout.Refresh();
			}
		}

		private void OnDestroy()
		{
			harmony.UnpatchSelf();
		}
	}
	internal sealed class SectionDef
	{
		public readonly string Key;

		public readonly Func<string> Label;

		public readonly Color Color;

		public readonly bool KeepGameOrder;

		public readonly bool ShowValue;

		public SectionDef(string key, Func<string> label, Color color, bool keepGameOrder, bool showValue)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			Key = key;
			Label = label;
			Color = color;
			KeepGameOrder = keepGameOrder;
			ShowValue = showValue;
		}
	}
	internal struct Placement
	{
		public int Section;

		public float Value;

		public string Name;
	}
	internal static class SectionLayout
	{
		private struct Entry
		{
			public RecipeDataPair Pair;

			public int GameIndex;

			public float Value;

			public string Name;
		}

		private const string CustomDataKey = "rhenn.craftingmenusorter.collapsed";

		private const string HeadingName = "CraftingMenuSorter_Heading";

		private const string NameObject = "name";

		private const string IconObject = "icon";

		private const string ValueObject = "QualityLevel";

		private const string OpenMark = "[-] ";

		private const string CollapsedMark = "[+] ";

		private static readonly List<GameObject> headings = new List<GameObject>();

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

		private static Player collapsedOwner;

		private static bool warnedAboutPrefab;

		public static void Arrange(InventoryGui gui)
		{
			ClearHeadings();
			if (!((Object)(object)gui == (Object)null) && gui.InCraftTab())
			{
				List<RecipeDataPair> availableRecipes = gui.m_availableRecipes;
				if (availableRecipes != null && availableRecipes.Count != 0 && (FoodSections.TryPlace(availableRecipes, out var sections, out var placements) || GearSections.TryPlace(availableRecipes, out sections, out placements)))
				{
					Layout(gui, availableRecipes, sections, placements);
				}
			}
		}

		private static void Layout(InventoryGui gui, List<RecipeDataPair> rows, List<SectionDef> sections, Placement[] placements)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			LoadCollapsed();
			float recipeListSpace = gui.m_recipeListSpace;
			float num = 0f;
			List<RecipeDataPair> list = new List<RecipeDataPair>(rows.Count);
			for (int i = 0; i < sections.Count; i++)
			{
				SectionDef sectionDef = sections[i];
				List<Entry> list2 = new List<Entry>();
				for (int j = 0; j < rows.Count; j++)
				{
					if (placements[j].Section == i)
					{
						list2.Add(new Entry
						{
							Pair = rows[j],
							GameIndex = j,
							Value = placements[j].Value,
							Name = (placements[j].Name ?? string.Empty)
						});
					}
				}
				if (list2.Count == 0)
				{
					continue;
				}
				Sort(list2, sectionDef.KeepGameOrder);
				bool flag = !collapsed.Contains(sectionDef.Key);
				AddHeading(gui, sectionDef, list2.Count, flag, num);
				num -= recipeListSpace;
				foreach (Entry item in list2)
				{
					Entry current = item;
					list.Add(current.Pair);
					GameObject interfaceElement = ((RecipeDataPair)(ref current.Pair)).InterfaceElement;
					if (!((Object)(object)interfaceElement == (Object)null))
					{
						Transform transform = interfaceElement.transform;
						((RectTransform)((transform is RectTransform) ? transform : null)).anchoredPosition = new Vector2(0f, num);
						interfaceElement.SetActive(flag);
						if (flag)
						{
							num -= recipeListSpace;
						}
						if (sectionDef.ShowValue && Plugin.ShowValues.Value)
						{
							ShowValue(interfaceElement, sectionDef.Color, current.Value);
						}
					}
				}
			}
			if (list.Count == rows.Count)
			{
				rows.Clear();
				rows.AddRange(list);
			}
			else
			{
				Plugin.Log.LogWarning((object)$"{list.Count} of {rows.Count} recipe rows were placed in sections; the game's order is kept.");
			}
			float num2 = Mathf.Max(gui.m_recipeListBaseSize, 0f - num);
			gui.m_recipeListRoot.SetSizeWithCurrentAnchors((Axis)1, num2);
		}

		public static void Refresh()
		{
			InventoryGui instance = InventoryGui.instance;
			if (!((Object)(object)instance == (Object)null) && InventoryGui.IsVisible())
			{
				instance.UpdateCraftingPanel(false);
			}
		}

		public static SharedData SharedOf(RecipeDataPair pair)
		{
			if ((Object)(object)((RecipeDataPair)(ref pair)).Recipe == (Object)null || (Object)(object)((RecipeDataPair)(ref pair)).Recipe.m_item == (Object)null || ((RecipeDataPair)(ref pair)).Recipe.m_item.m_itemData == null)
			{
				return null;
			}
			return ((RecipeDataPair)(ref pair)).Recipe.m_item.m_itemData.m_shared;
		}

		public static string RowText(RecipeDataPair pair)
		{
			GameObject interfaceElement = ((RecipeDataPair)(ref pair)).InterfaceElement;
			if ((Object)(object)interfaceElement == (Object)null)
			{
				return "no row";
			}
			Transform val = interfaceElement.transform.Find("name");
			TMP_Text val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<TMP_Text>() : null);
			if (!((Object)(object)val2 != (Object)null))
			{
				return "no name text";
			}
			return val2.text;
		}

		public static List<T> ParseOrder<T>(string setting, T[] defaults, Func<string, string> alias) where T : struct
		{
			List<T> list = new List<T>(defaults.Length);
			string[] array = (setting ?? string.Empty).Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (alias != null)
				{
					text = alias(text);
				}
				if (Enum.TryParse<T>(text, ignoreCase: true, out var result) && !list.Contains(result))
				{
					list.Add(result);
				}
			}
			foreach (T item in defaults)
			{
				if (!list.Contains(item))
				{
					list.Add(item);
				}
			}
			return list;
		}

		private static void Sort(List<Entry> entries, bool keepGameOrder)
		{
			IOrderedEnumerable<Entry> source = (Plugin.GreyedOutLast.Value ? entries.OrderByDescending((Entry e) => ((RecipeDataPair)(ref e.Pair)).CanCraft) : entries.OrderBy((Entry e) => 0));
			if (!keepGameOrder)
			{
				source = source.ThenByDescending((Entry e) => e.Value).ThenBy<Entry, string>((Entry e) => e.Name, StringComparer.CurrentCultureIgnoreCase);
			}
			List<Entry> collection = source.ThenBy((Entry e) => e.GameIndex).ToList();
			entries.Clear();
			entries.AddRange(collection);
		}

		private static void ShowValue(GameObject element, Color color, float value)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			Transform val = element.transform.Find("QualityLevel");
			if (!((Object)(object)val == (Object)null))
			{
				TMP_Text component = ((Component)val).GetComponent<TMP_Text>();
				if (!((Object)(object)component == (Object)null))
				{
					((Component)val).gameObject.SetActive(true);
					component.text = value.ToString("0.#", CultureInfo.InvariantCulture);
					((Graphic)component).color = color;
				}
			}
		}

		private static void AddHeading(InventoryGui gui, SectionDef section, int count, bool open, float y)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Expected O, but got Unknown
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(gui.m_recipeElementPrefab, (Transform)(object)gui.m_recipeListRoot);
			((Object)val).name = "CraftingMenuSorter_Heading";
			val.SetActive(true);
			Transform transform = val.transform;
			((RectTransform)((transform is RectTransform) ? transform : null)).anchoredPosition = new Vector2(0f, y);
			foreach (Transform item in val.transform)
			{
				Transform val2 = item;
				((Component)val2).gameObject.SetActive(((Object)val2).name == "name");
			}
			Transform val3 = val.transform.Find("name");
			TMP_Text val4 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).GetComponent<TMP_Text>() : null);
			if ((Object)(object)val4 != (Object)null)
			{
				string text = Localization.instance.Localize(section.Label());
				val4.text = (open ? "[-] " : "[+] ") + text + " (" + count + ")";
				((Graphic)val4).color = section.Color;
				val4.fontStyle = (FontStyles)(val4.fontStyle | 1);
				val4.horizontalAlignment = (HorizontalAlignmentOptions)1;
				Transform transform2 = val.transform;
				AlignWithIcon((RectTransform)(object)((transform2 is RectTransform) ? transform2 : null), (RectTransform)(object)((val3 is RectTransform) ? val3 : null));
			}
			else if (!warnedAboutPrefab)
			{
				warnedAboutPrefab = true;
				Plugin.Log.LogWarning((object)"The recipe row prefab has no 'name' text; the section headings are blank.");
			}
			Button component = val.GetComponent<Button>();
			if ((Object)(object)component != (Object)null)
			{
				((UnityEventBase)component.onClick).RemoveAllListeners();
				string key = section.Key;
				((UnityEvent)component.onClick).AddListener((UnityAction)delegate
				{
					Toggle(gui, key);
				});
			}
			headings.Add(val);
		}

		private static void AlignWithIcon(RectTransform row, RectTransform name)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)row == (Object)null || (Object)(object)name == (Object)null)
			{
				return;
			}
			Transform val = ((Transform)row).Find("icon");
			RectTransform val2 = (RectTransform)(object)(((Object)(object)val != (Object)null) ? ((val is RectTransform) ? val : null) : null);
			if (!((Object)(object)val2 == (Object)null))
			{
				Rect rect = row.rect;
				float width = ((Rect)(ref rect)).width;
				float num = val2.anchorMin.x * width + val2.offsetMin.x;
				float num2 = name.anchorMin.x * width + name.offsetMin.x - num;
				if (num2 > 0f)
				{
					name.offsetMin = new Vector2(name.offsetMin.x - num2, name.offsetMin.y);
				}
			}
		}

		private static void Toggle(InventoryGui gui, string key)
		{
			if (!collapsed.Remove(key))
			{
				collapsed.Add(key);
			}
			SaveCollapsed();
			if ((Object)(object)gui != (Object)null)
			{
				Arrange(gui);
			}
		}

		private static void ClearHeadings()
		{
			foreach (GameObject heading in headings)
			{
				if ((Object)(object)heading != (Object)null)
				{
					Object.Destroy((Object)(object)heading);
				}
			}
			headings.Clear();
		}

		private static void LoadCollapsed()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)collapsedOwner == (Object)(object)localPlayer)
			{
				return;
			}
			collapsedOwner = localPlayer;
			collapsed.Clear();
			if (!Plugin.RememberCollapsed.Value || localPlayer.m_customData == null || !localPlayer.m_customData.TryGetValue("rhenn.craftingmenusorter.collapsed", out var value) || string.IsNullOrEmpty(value))
			{
				return;
			}
			string[] array = value.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					collapsed.Add(text);
				}
			}
		}

		private static void SaveCollapsed()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && Plugin.RememberCollapsed.Value && localPlayer.m_customData != null)
			{
				if (collapsed.Count == 0)
				{
					localPlayer.m_customData.Remove("rhenn.craftingmenusorter.collapsed");
				}
				else
				{
					localPlayer.m_customData["rhenn.craftingmenusorter.collapsed"] = string.Join(",", collapsed.ToArray());
				}
			}
		}
	}
}
namespace CraftingMenuSorter.Patches
{
	[HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")]
	internal static class InventoryGui_UpdateRecipeList_Patch
	{
		[HarmonyPriority(200)]
		private static void Postfix(InventoryGui __instance)
		{
			try
			{
				SectionLayout.Arrange(__instance);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Arranging the crafting sections failed: {arg}");
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}