Decompiled source of SortedCrafting v1.0.0

SortedCrafting.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("SortedCrafting")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SortedCrafting")]
[assembly: AssemblyTitle("SortedCrafting")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 SortedCrafting
{
	[BepInPlugin("com.dreich.sortedcrafting", "Sorted Crafting", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class SortedCraftingPlugin : BaseUnityPlugin
	{
		public const string Guid = "com.dreich.sortedcrafting";

		public const string Name = "Sorted Crafting";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> SortEnabled;

		internal static ConfigEntry<bool> UnknownLast;

		internal static ConfigEntry<bool> ShowHeaders;

		internal static ConfigEntry<string> BiomeOverrides;

		internal static ConfigEntry<bool> LogBiomes;

		private void Awake()
		{
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			SortEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Sort the recipes at every crafting station by the biome they come from, Meadows first and the Deep North last.");
			UnknownLast = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UnknownLast", true, "Recipes whose biome can't be worked out go at the end of the list. Off puts them at the start.");
			ShowHeaders = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowHeaders", true, "Show a title with the biome's name above each biome's recipes.");
			BiomeOverrides = ((BaseUnityPlugin)this).Config.Bind<string>("General", "BiomeOverrides", "IronScrap=Swamp,SurtlingCore=BlackForest,AncientSeed=BlackForest,WitheredBone=Swamp,Crystal=Mountain,DragonEgg=Mountain,BlackMetalScrap=Plains,BugMeat=Mistlands,Carapace=Mistlands,BlackCore=Mistlands,Softtissue=Mistlands,Sap=Mistlands,RoyalJelly=Mistlands,DvergrNeedle=Mistlands,FlametalOreNew=AshLands", "Comma separated ItemPrefab=Biome pairs for items whose biome can't be worked out, mostly loot from dungeons. Anything crafted from them follows. Biomes: Meadows, BlackForest, Swamp, Ocean, Mountain, Plains, Mistlands, AshLands, DeepNorth.");
			LogBiomes = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogBiomes", false, "Write every item's biome to the log the first time a crafting station is opened.");
			BiomeOverrides.SettingChanged += delegate
			{
				BiomeTable.Clear();
			};
			Harmony val = new Harmony("com.dreich.sortedcrafting");
			val.PatchAll();
			JatifyHeaders.Patch(val);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")]
	internal static class UpdateRecipeListPatch
	{
		[HarmonyPriority(0)]
		private static void Postfix(InventoryGui __instance)
		{
			try
			{
				RecipeLayout.Apply(__instance);
			}
			catch (Exception ex)
			{
				SortedCraftingPlugin.Log.LogWarning((object)("Sorting recipes failed: " + ex));
			}
		}
	}
	internal static class RecipeLayout
	{
		private struct Row
		{
			internal object Pair;

			internal int Rank;

			internal int Position;
		}

		private const string NameChild = "name";

		private const string HeaderObjectName = "SortedCraftingHeader";

		private const string HeaderFormat = "— {0} —";

		private const string UnknownTitle = "Other";

		private const int UnknownFirst = int.MinValue;

		private const int UnknownLast = int.MaxValue;

		private static readonly Color HeaderColor = new Color(1f, 0.8f, 0.4f, 1f);

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

		private static readonly FieldInfo BaseSizeField = AccessTools.Field(typeof(InventoryGui), "m_recipeListBaseSize");

		private static PropertyInfo recipeProperty;

		private static PropertyInfo elementProperty;

		private static readonly List<Row> rows = new List<Row>();

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

		internal static void Apply(InventoryGui gui)
		{
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			if (!(AvailableField?.GetValue(gui) is IList list))
			{
				return;
			}
			if (!SortedCraftingPlugin.SortEnabled.Value || list.Count == 0)
			{
				HideHeadersFrom(0);
				return;
			}
			Dictionary<string, int> dictionary = BiomeTable.Tiers();
			if (dictionary == null || !LookUpProperties(list[0]))
			{
				return;
			}
			rows.Clear();
			for (int i = 0; i < list.Count; i++)
			{
				object? value = recipeProperty.GetValue(list[i], null);
				Recipe recipe = (Recipe)((value is Recipe) ? value : null);
				rows.Add(new Row
				{
					Pair = list[i],
					Rank = RankOf(recipe, dictionary),
					Position = i
				});
			}
			rows.Sort((Row a, Row b) => (a.Rank == b.Rank) ? a.Position.CompareTo(b.Position) : a.Rank.CompareTo(b.Rank));
			float recipeListSpace = gui.m_recipeListSpace;
			bool value2 = SortedCraftingPlugin.ShowHeaders.Value;
			int num = 0;
			int first = 0;
			bool flag = true;
			int num2 = 0;
			for (int num3 = 0; num3 < rows.Count; num3++)
			{
				Row row = rows[num3];
				list[num3] = row.Pair;
				object? value3 = elementProperty.GetValue(row.Pair, null);
				GameObject val = (GameObject)((value3 is GameObject) ? value3 : null);
				if (!((Object)(object)val == (Object)null))
				{
					if (value2 && (flag || row.Rank != num2))
					{
						PlaceHeader(gui, first++, val, Title(row.Rank), (float)num * recipeListSpace);
						num++;
					}
					flag = false;
					num2 = row.Rank;
					Transform transform = val.transform;
					RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
					if (val2 != null)
					{
						val2.anchoredPosition = new Vector2(0f, (float)(-num) * recipeListSpace);
					}
					num++;
				}
			}
			rows.Clear();
			HideHeadersFrom(first);
			float num4 = ((BaseSizeField?.GetValue(gui) is float num5) ? num5 : 0f);
			gui.m_recipeListRoot.SetSizeWithCurrentAnchors((Axis)1, Mathf.Max(num4, (float)num * recipeListSpace));
		}

		internal static int RankOf(Recipe recipe, Dictionary<string, int> tiers)
		{
			string text = (((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null) ? ((Object)recipe.m_item).name : null);
			if (text != null && tiers.TryGetValue(text, out var value))
			{
				return value;
			}
			if (!SortedCraftingPlugin.UnknownLast.Value)
			{
				return int.MinValue;
			}
			return int.MaxValue;
		}

		internal static string BiomeName(int rank)
		{
			if (rank != int.MinValue && rank != int.MaxValue)
			{
				return BiomeTable.DisplayName(rank);
			}
			return "Other";
		}

		private static bool LookUpProperties(object pair)
		{
			if (recipeProperty != null && elementProperty != null)
			{
				return true;
			}
			Type type = pair.GetType();
			recipeProperty = AccessTools.Property(type, "Recipe");
			elementProperty = AccessTools.Property(type, "InterfaceElement");
			if (recipeProperty != null)
			{
				return elementProperty != null;
			}
			return false;
		}

		private static string Title(int rank)
		{
			return $"— {BiomeName(rank)} —";
		}

		private static void PlaceHeader(InventoryGui gui, int index, GameObject row, string title, float depth)
		{
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			while (headers.Count <= index)
			{
				headers.Add(null);
			}
			GameObject val = headers[index];
			if ((Object)(object)val == (Object)null)
			{
				Transform val2 = row.transform.Find("name");
				if ((Object)(object)val2 == (Object)null)
				{
					return;
				}
				val = Object.Instantiate<GameObject>(((Component)val2).gameObject, (Transform)(object)gui.m_recipeListRoot);
				((Object)val).name = "SortedCraftingHeader";
				headers[index] = val;
			}
			Transform transform = val.transform;
			RectTransform val3 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			if (val3 != null)
			{
				Transform transform2 = row.transform;
				RectTransform val4 = (RectTransform)(object)((transform2 is RectTransform) ? transform2 : null);
				if (val4 != null)
				{
					val3.anchorMin = val4.anchorMin;
					val3.anchorMax = val4.anchorMax;
					val3.pivot = val4.pivot;
					val3.sizeDelta = val4.sizeDelta;
					((Transform)val3).localScale = Vector3.one;
					val3.anchoredPosition = new Vector2(0f, 0f - depth);
				}
			}
			TMP_Text component = val.GetComponent<TMP_Text>();
			if ((Object)(object)component != (Object)null)
			{
				component.text = title;
				((Graphic)component).color = HeaderColor;
				component.alignment = (TextAlignmentOptions)514;
				((Graphic)component).raycastTarget = false;
			}
			val.SetActive(true);
		}

		private static void HideHeadersFrom(int first)
		{
			for (int i = first; i < headers.Count; i++)
			{
				if ((Object)(object)headers[i] != (Object)null && headers[i].activeSelf)
				{
					headers[i].SetActive(false);
				}
			}
		}
	}
	internal static class JatifyHeaders
	{
		internal const string Guid = "jatify.ui";

		private const string TitleFormat = "{0} · {1}";

		private const char WordBreak = ' ';

		private static MethodInfo section;

		private static FieldInfo recipeField;

		private static bool drawingGroup;

		internal static void Patch(Harmony harmony)
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			if (Chainloader.PluginInfos.ContainsKey("jatify.ui"))
			{
				Type type = AccessTools.TypeByName("Jatify.UI.RecipeView");
				section = ((type != null) ? AccessTools.Method(type, "Section", (Type[])null, (Type[])null) : null);
				if (section == null || section.ReturnType != typeof(float))
				{
					SortedCraftingPlugin.Log.LogWarning((object)"Jatify UI's crafting panel has changed; its recipes won't get biome headers.");
				}
				else
				{
					harmony.Patch((MethodBase)section, new HarmonyMethod(typeof(JatifyHeaders), "SectionPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
		}

		private static bool SectionPrefix(object __instance, string title, Color c, object list, int sel, object g, float y, ref float __result)
		{
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			if (drawingGroup || !SortedCraftingPlugin.SortEnabled.Value || !SortedCraftingPlugin.ShowHeaders.Value)
			{
				return true;
			}
			if (!(list is IList { Count: not 0 } list2))
			{
				return true;
			}
			Dictionary<string, int> dictionary = BiomeTable.Tiers();
			if (dictionary == null)
			{
				return true;
			}
			if (recipeField == null)
			{
				recipeField = AccessTools.Field(list2[0].GetType(), "Recipe");
			}
			if (recipeField == null)
			{
				return true;
			}
			string sectionName = SectionName(title);
			float num = y;
			IList list3 = null;
			int num2 = 0;
			drawingGroup = true;
			try
			{
				foreach (object item in list2)
				{
					object? value = recipeField.GetValue(item);
					int num3 = RecipeLayout.RankOf((Recipe)((value is Recipe) ? value : null), dictionary);
					if (list3 != null && num3 != num2)
					{
						num = Draw(__instance, sectionName, num2, c, list3, sel, g, num);
						list3 = null;
					}
					if (list3 == null)
					{
						list3 = (IList)Activator.CreateInstance(list.GetType());
						num2 = num3;
					}
					list3.Add(item);
				}
				if (list3 != null)
				{
					num = Draw(__instance, sectionName, num2, c, list3, sel, g, num);
				}
			}
			finally
			{
				drawingGroup = false;
			}
			__result = num;
			return false;
		}

		private static float Draw(object view, string sectionName, int rank, Color colour, IList group, int sel, object theme, float y)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			string text = $"{sectionName} · {RecipeLayout.BiomeName(rank).ToUpperInvariant()}";
			return (float)section.Invoke(view, new object[6] { text, colour, group, sel, theme, y });
		}

		private static string SectionName(string title)
		{
			int num = title.IndexOf(' ');
			if (num <= 0)
			{
				return title;
			}
			return title.Substring(0, num);
		}
	}
	internal static class BiomeTable
	{
		private sealed class TierScan
		{
			internal readonly Dictionary<string, int> Narrow = new Dictionary<string, int>(StringComparer.Ordinal);

			internal readonly Dictionary<string, int> Broad = new Dictionary<string, int>(StringComparer.Ordinal);

			internal readonly Dictionary<string, string> NarrowFrom = new Dictionary<string, string>(StringComparer.Ordinal);

			internal readonly Dictionary<string, string> BroadFrom = new Dictionary<string, string>(StringComparer.Ordinal);

			internal readonly HashSet<GameObject> Visited = new HashSet<GameObject>();

			internal int Rank;

			internal bool IsBroad;

			internal string Origin;
		}

		private struct Conversion
		{
			internal ItemDrop From;

			internal ItemDrop To;
		}

		private const int NoRank = -1;

		private const int BaseQuality = 1;

		private const int MostBiomesForNarrowSource = 2;

		private const int DeepestDropChain = 4;

		private const int MostTierPasses = 20;

		private const string BossPrefabs = "Meadows=Eikthyr,BlackForest=gd_king,Swamp=Bonemass,Mountain=Dragon,Plains=GoblinKing,Mistlands=SeekerQueen,AshLands=Fader,DeepNorth=FrozenKing";

		private static readonly Biome[] BiomeOrder;

		private static readonly Dictionary<Biome, string> DisplayNames;

		private static ObjectDB builtFor;

		private static Dictionary<string, int> tiers;

		private static Dictionary<string, string> origins;

		internal static void Clear()
		{
			builtFor = null;
			tiers = null;
		}

		internal static Dictionary<string, int> Tiers()
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null)
			{
				return null;
			}
			if ((Object)(object)builtFor == (Object)(object)instance && tiers != null)
			{
				return tiers;
			}
			builtFor = instance;
			tiers = FindTiers(instance);
			if (SortedCraftingPlugin.LogBiomes.Value)
			{
				LogTable();
			}
			return tiers;
		}

		internal unsafe static string DisplayName(int rank)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			Biome key = BiomeOrder[Mathf.Clamp(rank, 0, BiomeOrder.Length - 1)];
			if (!DisplayNames.TryGetValue(key, out var value))
			{
				return ((object)(*(Biome*)(&key))/*cast due to .constrained prefix*/).ToString();
			}
			return value;
		}

		private static int Rank(Biome biome)
		{
			//IL_0004: 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)
			for (int i = 0; i < BiomeOrder.Length; i++)
			{
				if ((biome & BiomeOrder[i]) != 0)
				{
					return i;
				}
			}
			return -1;
		}

		private static int HighestRank(Biome biome)
		{
			//IL_000c: 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)
			for (int num = BiomeOrder.Length - 1; num >= 0; num--)
			{
				if ((biome & BiomeOrder[num]) != 0)
				{
					return num;
				}
			}
			return -1;
		}

		private static int BiomeCount(Biome biome)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			Biome[] biomeOrder = BiomeOrder;
			foreach (Biome val in biomeOrder)
			{
				if ((biome & val) != 0)
				{
					num++;
				}
			}
			return num;
		}

		private static bool TryParseBiome(string text, out Biome biome)
		{
			if (Enum.TryParse<Biome>(text.Trim(), ignoreCase: true, out biome))
			{
				return (int)biome != 0;
			}
			return false;
		}

		private static Dictionary<string, string> ParsePairs(string raw)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			string[] array = (raw ?? "").Split(new char[1] { ',' });
			foreach (string text in array)
			{
				int num = text.IndexOf('=');
				if (num > 0)
				{
					string text2 = text.Substring(0, num).Trim();
					string text3 = text.Substring(num + 1).Trim();
					if (text2.Length > 0 && text3.Length > 0)
					{
						dictionary[text2] = text3;
					}
				}
			}
			return dictionary;
		}

		private static void Begin(TierScan scan, Biome biome, string origin)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			scan.IsBroad = BiomeCount(biome) > 2;
			scan.Rank = (scan.IsBroad ? HighestRank(biome) : Rank(biome));
			scan.Origin = origin;
			scan.Visited.Clear();
		}

		private static void AddTier(TierScan scan, GameObject prefab)
		{
			if (!((Object)(object)prefab == (Object)null) && scan.Rank >= 0 && !((Object)(object)prefab.GetComponent<ItemDrop>() == (Object)null))
			{
				string name = ((Object)prefab).name;
				Dictionary<string, int> dictionary = (scan.IsBroad ? scan.Broad : scan.Narrow);
				Dictionary<string, string> dictionary2 = (scan.IsBroad ? scan.BroadFrom : scan.NarrowFrom);
				if (!dictionary.TryGetValue(name, out var value) || scan.Rank < value)
				{
					dictionary[name] = scan.Rank;
					dictionary2[name] = scan.Origin;
				}
			}
		}

		private static void AddDropTable(TierScan scan, DropTable table, int depth)
		{
			//IL_001c: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			if (table == null || table.m_drops == null)
			{
				return;
			}
			foreach (DropData drop in table.m_drops)
			{
				AddSource(scan, drop.m_item, depth + 1);
			}
		}

		private static void AddSource(TierScan scan, GameObject prefab, int depth)
		{
			if ((Object)(object)prefab == (Object)null || scan.Rank < 0 || depth > 4 || !scan.Visited.Add(prefab))
			{
				return;
			}
			AddTier(scan, prefab);
			Pickable component = prefab.GetComponent<Pickable>();
			if ((Object)(object)component != (Object)null)
			{
				AddTier(scan, component.m_itemPrefab);
				AddDropTable(scan, component.m_extraDrops, depth);
			}
			DropOnDestroyed component2 = prefab.GetComponent<DropOnDestroyed>();
			if ((Object)(object)component2 != (Object)null)
			{
				AddDropTable(scan, component2.m_dropWhenDestroyed, depth);
			}
			MineRock component3 = prefab.GetComponent<MineRock>();
			if ((Object)(object)component3 != (Object)null)
			{
				AddDropTable(scan, component3.m_dropItems, depth);
			}
			MineRock5 component4 = prefab.GetComponent<MineRock5>();
			if ((Object)(object)component4 != (Object)null)
			{
				AddDropTable(scan, component4.m_dropItems, depth);
			}
			TreeBase component5 = prefab.GetComponent<TreeBase>();
			if ((Object)(object)component5 != (Object)null)
			{
				AddDropTable(scan, component5.m_dropWhenDestroyed, depth);
				AddSource(scan, component5.m_logPrefab, depth + 1);
			}
			TreeLog component6 = prefab.GetComponent<TreeLog>();
			if ((Object)(object)component6 != (Object)null)
			{
				AddDropTable(scan, component6.m_dropWhenDestroyed, depth);
				AddSource(scan, component6.m_subLogPrefab, depth + 1);
			}
			Destructible component7 = prefab.GetComponent<Destructible>();
			if ((Object)(object)component7 != (Object)null)
			{
				AddSource(scan, component7.m_spawnWhenDestroyed, depth + 1);
			}
			CharacterDrop component8 = prefab.GetComponent<CharacterDrop>();
			if (!((Object)(object)component8 != (Object)null) || component8.m_drops == null)
			{
				return;
			}
			foreach (Drop drop in component8.m_drops)
			{
				AddTier(scan, drop.m_prefab);
			}
		}

		private static Dictionary<string, int> FindTiers(ObjectDB db)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			TierScan tierScan = new TierScan();
			foreach (ZoneVegetation item in ZoneSystem.instance.m_vegetation)
			{
				if (item != null && item.m_enable && !((Object)(object)item.m_prefab == (Object)null))
				{
					Begin(tierScan, item.m_biome, "vegetation " + ((Object)item.m_prefab).name);
					AddSource(tierScan, item.m_prefab, 0);
				}
			}
			foreach (SpawnSystem item2 in SpawnSystems())
			{
				foreach (SpawnSystemList spawnList in item2.m_spawnLists)
				{
					if ((Object)(object)spawnList == (Object)null)
					{
						continue;
					}
					foreach (SpawnData spawner in spawnList.m_spawners)
					{
						if (spawner != null && spawner.m_enabled && !((Object)(object)spawner.m_prefab == (Object)null))
						{
							Begin(tierScan, spawner.m_biome, "creature " + ((Object)spawner.m_prefab).name);
							AddSource(tierScan, spawner.m_prefab, 0);
						}
					}
				}
			}
			foreach (KeyValuePair<string, string> item3 in ParsePairs("Meadows=Eikthyr,BlackForest=gd_king,Swamp=Bonemass,Mountain=Dragon,Plains=GoblinKing,Mistlands=SeekerQueen,AshLands=Fader,DeepNorth=FrozenKing"))
			{
				if (TryParseBiome(item3.Key, out var biome))
				{
					Begin(tierScan, biome, "boss " + item3.Value);
					AddSource(tierScan, ZNetScene.instance.GetPrefab(item3.Value), 0);
				}
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>(tierScan.Narrow, StringComparer.Ordinal);
			Dictionary<string, string> dictionary2 = new Dictionary<string, string>(tierScan.NarrowFrom, StringComparer.Ordinal);
			foreach (KeyValuePair<string, int> item4 in tierScan.Broad)
			{
				if (!dictionary.ContainsKey(item4.Key))
				{
					dictionary[item4.Key] = item4.Value;
					dictionary2[item4.Key] = tierScan.BroadFrom[item4.Key] + " (many biomes)";
				}
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (KeyValuePair<string, string> item5 in ParsePairs(SortedCraftingPlugin.BiomeOverrides.Value))
			{
				if (TryParseBiome(item5.Value, out var biome2))
				{
					dictionary[item5.Key] = Rank(biome2);
					dictionary2[item5.Key] = "BiomeOverrides";
					hashSet.Add(item5.Key);
				}
			}
			List<Conversion> list = Conversions();
			HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal);
			foreach (Conversion item6 in list)
			{
				if ((Object)(object)item6.To != (Object)null && !hashSet.Contains(((Object)item6.To).name))
				{
					hashSet2.Add(((Object)item6.To).name);
				}
			}
			foreach (Recipe recipe in db.m_recipes)
			{
				if (Usable(recipe) && !hashSet.Contains(((Object)recipe.m_item).name))
				{
					hashSet2.Add(((Object)recipe.m_item).name);
				}
			}
			Dictionary<string, int> dictionary3 = new Dictionary<string, int>(dictionary, StringComparer.Ordinal);
			bool flag = true;
			for (int i = 0; i < 20 && flag; i++)
			{
				flag = false;
				Dictionary<string, int> dictionary4 = new Dictionary<string, int>(StringComparer.Ordinal);
				Dictionary<string, string> dictionary5 = new Dictionary<string, string>(StringComparer.Ordinal);
				foreach (Conversion item7 in list)
				{
					if (!((Object)(object)item7.From == (Object)null) && !((Object)(object)item7.To == (Object)null) && hashSet2.Contains(((Object)item7.To).name) && dictionary.TryGetValue(((Object)item7.From).name, out var value) && (!dictionary4.TryGetValue(((Object)item7.To).name, out var value2) || value < value2))
					{
						dictionary4[((Object)item7.To).name] = value;
						dictionary5[((Object)item7.To).name] = "made from " + ((Object)item7.From).name;
					}
				}
				foreach (Recipe recipe2 in db.m_recipes)
				{
					if (!Usable(recipe2) || !hashSet2.Contains(((Object)recipe2.m_item).name))
					{
						continue;
					}
					int num = -1;
					string text = null;
					Requirement[] resources = recipe2.m_resources;
					foreach (Requirement val in resources)
					{
						if (val != null && !((Object)(object)val.m_resItem == (Object)null) && val.GetAmount(1) > 0 && dictionary.TryGetValue(((Object)val.m_resItem).name, out var value3) && value3 > num)
						{
							num = value3;
							text = ((Object)val.m_resItem).name;
						}
					}
					if (num >= 0 && (!dictionary4.TryGetValue(((Object)recipe2.m_item).name, out var value4) || num < value4))
					{
						dictionary4[((Object)recipe2.m_item).name] = num;
						dictionary5[((Object)recipe2.m_item).name] = "crafted from " + text;
					}
				}
				foreach (KeyValuePair<string, int> item8 in dictionary4)
				{
					int num2 = item8.Value;
					int value5;
					bool flag2 = dictionary3.TryGetValue(item8.Key, out value5) && value5 <= num2;
					if (flag2)
					{
						num2 = value5;
					}
					if (!dictionary.TryGetValue(item8.Key, out var value6) || value6 != num2)
					{
						dictionary[item8.Key] = num2;
						if (!flag2)
						{
							dictionary2[item8.Key] = dictionary5[item8.Key];
						}
						flag = true;
					}
				}
			}
			origins = dictionary2;
			return dictionary;
		}

		private static bool Usable(Recipe recipe)
		{
			if ((Object)(object)recipe != (Object)null && recipe.m_enabled && (Object)(object)recipe.m_item != (Object)null && recipe.m_resources != null)
			{
				return recipe.m_resources.Length != 0;
			}
			return false;
		}

		private static IEnumerable<SpawnSystem> SpawnSystems()
		{
			HashSet<SpawnSystem> seen = new HashSet<SpawnSystem>();
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				SpawnSystem val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<SpawnSystem>() : null);
				if ((Object)(object)val != (Object)null && seen.Add(val))
				{
					yield return val;
				}
			}
			if (!(AccessTools.Field(typeof(SpawnSystem), "m_instances")?.GetValue(null) is List<SpawnSystem> list))
			{
				yield break;
			}
			foreach (SpawnSystem item in list)
			{
				if ((Object)(object)item != (Object)null && seen.Add(item))
				{
					yield return item;
				}
			}
		}

		private static List<Conversion> Conversions()
		{
			List<Conversion> list = new List<Conversion>();
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				Smelter component = prefab.GetComponent<Smelter>();
				if ((Object)(object)component != (Object)null && component.m_conversion != null)
				{
					foreach (ItemConversion item in component.m_conversion)
					{
						list.Add(new Conversion
						{
							From = item.m_from,
							To = item.m_to
						});
					}
				}
				CookingStation component2 = prefab.GetComponent<CookingStation>();
				if ((Object)(object)component2 != (Object)null && component2.m_conversion != null)
				{
					foreach (ItemConversion item2 in component2.m_conversion)
					{
						list.Add(new Conversion
						{
							From = item2.m_from,
							To = item2.m_to
						});
					}
				}
				Fermenter component3 = prefab.GetComponent<Fermenter>();
				if (!((Object)(object)component3 != (Object)null) || component3.m_conversion == null)
				{
					continue;
				}
				foreach (ItemConversion item3 in component3.m_conversion)
				{
					list.Add(new Conversion
					{
						From = item3.m_from,
						To = item3.m_to
					});
				}
			}
			return list;
		}

		private static void LogTable()
		{
			List<string> list = new List<string>();
			foreach (Recipe recipe in builtFor.m_recipes)
			{
				if (Usable(recipe))
				{
					string name = ((Object)recipe.m_item).name;
					int value;
					string text = (tiers.TryGetValue(name, out value) ? ((object)Unsafe.As<Biome, Biome>(ref BiomeOrder[value])/*cast due to .constrained prefix*/).ToString() : "unknown");
					string value2;
					string text2 = (origins.TryGetValue(name, out value2) ? (" (" + value2 + ")") : "");
					list.Add(name + ": " + text + text2);
				}
			}
			list.Sort(StringComparer.Ordinal);
			SortedCraftingPlugin.Log.LogInfo((object)("Recipe biomes:\n" + string.Join("\n", list.ToArray())));
		}

		static BiomeTable()
		{
			Biome[] array = new Biome[9];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			BiomeOrder = (Biome[])(object)array;
			DisplayNames = new Dictionary<Biome, string>
			{
				{
					(Biome)8,
					"Black Forest"
				},
				{
					(Biome)32,
					"Ashlands"
				},
				{
					(Biome)64,
					"Deep North"
				}
			};
			origins = new Dictionary<string, string>(StringComparer.Ordinal);
		}
	}
}