Decompiled source of NokepCraftTabs v0.2.0

plugins\NokepCraftTabs.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using NokepCommon;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
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: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace NokepCommon
{
	internal static class Icons
	{
		private static readonly Dictionary<string, Sprite> cache = new Dictionary<string, Sprite>();

		public static Sprite Get(string name)
		{
			if (cache.TryGetValue(name, out var value))
			{
				return value;
			}
			Sprite val = Load(name);
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogWarning((object)("Иконка " + name + " не загрузилась"));
			}
			cache[name] = val;
			return val;
		}

		private static Sprite Load(string name)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			string text = executingAssembly.GetName().Name + ".Assets." + name.Replace("/", ".") + ".png";
			using Stream stream = executingAssembly.GetManifestResourceStream(text);
			if (stream == null)
			{
				Logger.LogWarning((object)("Не нашёл ресурс " + text));
				return null;
			}
			byte[] array = new byte[stream.Length];
			stream.Read(array, 0, array.Length);
			Texture2D val = AssetUtils.LoadImage(array);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
		}

		public static void LogResourceNames()
		{
			string[] manifestResourceNames = Assembly.GetExecutingAssembly().GetManifestResourceNames();
			Logger.LogInfo((object)("Встроенные ресурсы: " + string.Join(", ", manifestResourceNames)));
		}
	}
	internal static class Translations
	{
		private static readonly string[] Languages = new string[3] { "English", "Russian", "German" };

		public static void Load()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			string name = executingAssembly.GetName().Name;
			string[] languages = Languages;
			foreach (string text in languages)
			{
				string text2 = name + ".Translations." + text + ".strings.json";
				using Stream stream = executingAssembly.GetManifestResourceStream(text2);
				if (stream == null)
				{
					Logger.LogWarning((object)("Не нашёл перевод " + text2));
					continue;
				}
				using StreamReader streamReader = new StreamReader(stream);
				localization.AddJsonFile(text, streamReader.ReadToEnd());
			}
		}

		public static string Get(string text)
		{
			return Localization.instance.Localize(text);
		}
	}
	internal static class NokepUtils
	{
		public static Dictionary<string, int> ParseRecipe(string text)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			if (string.IsNullOrEmpty(text))
			{
				return dictionary;
			}
			string[] array = text.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Trim().Split(new char[1] { ':' });
				if (array2.Length == 2)
				{
					string text2 = array2[0].Trim();
					if (text2.Length != 0 && int.TryParse(array2[1].Trim(), out var result) && result >= 1)
					{
						dictionary[text2] = result;
					}
				}
			}
			return dictionary;
		}
	}
	internal enum DamageGroup
	{
		Melee,
		Ranged,
		Magic
	}
	internal static class DamageGroups
	{
		private static readonly Dictionary<DamageGroup, HashSet<SkillType>> GroupSkills = new Dictionary<DamageGroup, HashSet<SkillType>>
		{
			[DamageGroup.Melee] = new HashSet<SkillType>
			{
				(SkillType)1,
				(SkillType)2,
				(SkillType)3,
				(SkillType)7,
				(SkillType)4,
				(SkillType)5,
				(SkillType)11
			},
			[DamageGroup.Ranged] = new HashSet<SkillType>
			{
				(SkillType)8,
				(SkillType)14
			},
			[DamageGroup.Magic] = new HashSet<SkillType>
			{
				(SkillType)9,
				(SkillType)10
			}
		};

		private static readonly Dictionary<int, Dictionary<DamageGroup, float>> Bonuses = new Dictionary<int, Dictionary<DamageGroup, float>>();

		public static void Set(StatusEffect effect, DamageGroup group, float percent)
		{
			int key = effect.NameHash();
			if (!Bonuses.TryGetValue(key, out var value))
			{
				value = (Bonuses[key] = new Dictionary<DamageGroup, float>());
			}
			if (percent == 0f)
			{
				value.Remove(group);
			}
			else
			{
				value[group] = percent;
			}
		}

		public static IEnumerable<KeyValuePair<DamageGroup, float>> Get(StatusEffect effect)
		{
			if (Bonuses.TryGetValue(effect.NameHash(), out var value))
			{
				return value;
			}
			return new KeyValuePair<DamageGroup, float>[0];
		}

		public static float Multiplier(StatusEffect effect, SkillType skill)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if (!Bonuses.TryGetValue(effect.NameHash(), out var value))
			{
				return 1f;
			}
			float num = 0f;
			foreach (KeyValuePair<DamageGroup, float> item in value)
			{
				if (GroupSkills[item.Key].Contains(skill))
				{
					num += item.Value;
				}
			}
			return 1f + num / 100f;
		}
	}
	[HarmonyPatch(typeof(SE_Stats), "ModifyAttack")]
	internal static class DamageGroups_ModifyAttack
	{
		private static void Postfix(SE_Stats __instance, SkillType skill, ref HitData hitData)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			float num = DamageGroups.Multiplier((StatusEffect)(object)__instance, skill);
			if (num != 1f)
			{
				((DamageTypes)(ref hitData.m_damage)).Modify(num);
			}
		}
	}
	internal abstract class CraftTabSource
	{
		public abstract int Count { get; }

		public abstract KeyCode NextKey { get; }

		public abstract KeyCode PrevKey { get; }

		public virtual string NextGamepadButton => null;

		public virtual string PrevGamepadButton => null;

		public virtual bool RememberPage => false;

		public abstract string Name(int page);

		public abstract bool IsActive(CraftingStation station);

		public abstract int PageOf(Recipe recipe);

		public virtual Sprite Icon(int page)
		{
			return null;
		}

		public virtual string ButtonText(int page)
		{
			return Name(page);
		}

		public virtual string TooltipText(int page, int count)
		{
			return count.ToString();
		}

		public virtual void OnStationOpened(CraftingStation station)
		{
		}

		public virtual void AfterRecipeList(InventoryGui gui, int page)
		{
		}
	}
	internal static class CraftTabs
	{
		private class PageButton
		{
			public GameObject Go;

			public Button Button;

			public TMP_Text[] Texts;

			public Image Icon;

			public UITooltip Tooltip;
		}

		private const float Gap = 8f;

		private const float Spacing = 2f;

		private const float TextWidth = 56f;

		private const float MinIcon = 20f;

		private const float MaxIcon = 40f;

		private const float EdgeMargin = 6f;

		private static CraftTabSource source;

		private static string buttonPrefix;

		public static int Current;

		private static int[] counts;

		private static int presentCount;

		private static CraftingStation openStation;

		private static string openKey;

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

		private static CraftingStation checkedStation;

		private static bool checkedActive;

		private static InventoryGui gui;

		private static RectTransform row;

		private static PageButton[] buttons;

		private static bool layoutDirty = true;

		public static string CurrentName => source.Name(Current);

		public static void Install(Harmony harmony, CraftTabSource tabSource, string namePrefix)
		{
			source = tabSource;
			buttonPrefix = namePrefix;
			counts = new int[source.Count];
			Patch(harmony, "Awake", null, "AwakePostfix");
			Patch(harmony, "Update", null, "UpdatePostfix");
			Patch(harmony, "UpdateRecipeList", "RecipeListPrefix", "RecipeListPostfix");
			Patch(harmony, "UpdateRecipe", null, "TitlePostfix");
		}

		private static void Patch(Harmony harmony, string method, string prefix = null, string postfix = null)
		{
			//IL_0025: 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)
			harmony.Patch((MethodBase)AccessTools.Method(typeof(InventoryGui), method, (Type[])null, (Type[])null), (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(CraftTabs), prefix, (Type[])null), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(CraftTabs), postfix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static CraftingStation ActiveStation()
		{
			Player localPlayer = Player.m_localPlayer;
			CraftingStation val = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetCurrentCraftingStation() : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			if ((Object)(object)val != (Object)(object)checkedStation)
			{
				checkedStation = val;
				checkedActive = source.IsActive(val);
			}
			if (!checkedActive)
			{
				return null;
			}
			return val;
		}

		public static void ResetCache()
		{
			checkedStation = null;
			openStation = null;
			layoutDirty = true;
		}

		private static bool Present(int page)
		{
			if (page != 0)
			{
				return counts[page] > 0;
			}
			return true;
		}

		private static int Step(int dir)
		{
			int count = source.Count;
			int num = Current;
			for (int i = 0; i < count; i++)
			{
				num = (num + dir + count) % count;
				if (Present(num))
				{
					return num;
				}
			}
			return Current;
		}

		private static void Select(int page)
		{
			Current = page;
			if (openKey != null && source.RememberPage)
			{
				remembered[openKey] = page;
			}
			gui.UpdateCraftingPanel(true);
		}

		private static void RecipeListPrefix(InventoryGui __instance, List<Recipe> recipes)
		{
			if (!__instance.InCraftTab())
			{
				return;
			}
			CraftingStation val = ActiveStation();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if ((Object)(object)val != (Object)(object)openStation)
			{
				openStation = val;
				openKey = Utils.GetPrefabName(((Component)val).gameObject);
				source.OnStationOpened(val);
				Current = ((source.RememberPage && remembered.TryGetValue(openKey, out var value)) ? value : 0);
			}
			int[] array = (int[])counts.Clone();
			Array.Clear(counts, 0, counts.Length);
			foreach (Recipe recipe in recipes)
			{
				if (!((Object)(object)recipe == (Object)null))
				{
					counts[Clamp(source.PageOf(recipe))]++;
					counts[0]++;
				}
			}
			presentCount = 0;
			for (int i = 0; i < counts.Length; i++)
			{
				if (Present(i))
				{
					presentCount++;
				}
				if (array[i] > 0 != counts[i] > 0 || array[i] != counts[i])
				{
					layoutDirty = true;
				}
			}
			if (Current < 0 || Current >= counts.Length || !Present(Current))
			{
				Current = 0;
			}
			if (Current != 0)
			{
				recipes.RemoveAll((Recipe r) => (Object)(object)r == (Object)null || Clamp(source.PageOf(r)) != Current);
			}
		}

		private static void RecipeListPostfix(InventoryGui __instance)
		{
			if (__instance.InCraftTab() && (Object)(object)ActiveStation() != (Object)null)
			{
				source.AfterRecipeList(__instance, Current);
			}
		}

		private static int Clamp(int page)
		{
			if (page >= 1 && page < source.Count)
			{
				return page;
			}
			return source.Count - 1;
		}

		private static void TitlePostfix(InventoryGui __instance)
		{
			if (__instance.InCraftTab() && !((Object)(object)ActiveStation() == (Object)null))
			{
				TMP_Text craftingStationName = __instance.m_craftingStationName;
				craftingStationName.text = craftingStationName.text + " — " + CurrentName;
			}
		}

		private static void UpdatePostfix(InventoryGui __instance)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			bool num = InventoryGui.IsVisible();
			if (!num)
			{
				openStation = null;
			}
			bool num2 = num && __instance.InCraftTab() && (Object)(object)ActiveStation() != (Object)null && presentCount > 1;
			UpdateButtons(num2);
			if (num2 && !__instance.m_splitDialog.IsActive)
			{
				if (ZInput.GetKeyDown(source.NextKey, true) || GamepadDown(source.NextGamepadButton))
				{
					Select(Step(1));
				}
				else if (ZInput.GetKeyDown(source.PrevKey, true) || GamepadDown(source.PrevGamepadButton))
				{
					Select(Step(-1));
				}
			}
		}

		private static bool GamepadDown(string button)
		{
			if (button != null && ZInput.IsGamepadActive())
			{
				return ZInput.GetButtonDown(button);
			}
			return false;
		}

		private static void UpdateButtons(bool active)
		{
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			if (buttons == null)
			{
				return;
			}
			if (!active)
			{
				PageButton[] array = buttons;
				for (int i = 0; i < array.Length; i++)
				{
					Show(array[i].Go, visible: false);
				}
				layoutDirty = true;
				return;
			}
			if (layoutDirty)
			{
				Layout();
			}
			for (int j = 0; j < buttons.Length; j++)
			{
				PageButton pageButton = buttons[j];
				if (pageButton.Go.activeSelf)
				{
					SetLabel(pageButton, j);
					bool flag = j == Current;
					if (((Selectable)pageButton.Button).interactable == flag)
					{
						((Selectable)pageButton.Button).interactable = !flag;
					}
					if ((Object)(object)pageButton.Icon != (Object)null)
					{
						((Graphic)pageButton.Icon).color = (Color)(flag ? Color.white : new Color(1f, 1f, 1f, 0.55f));
					}
				}
			}
		}

		private static void Layout()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			layoutDirty = false;
			RectTransform val = (RectTransform)((Component)gui.m_tabUpgrade).transform;
			SyncRow(val);
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < buttons.Length; i++)
			{
				if (Present(i))
				{
					if (IsSquare(i))
					{
						num++;
					}
					else
					{
						num2++;
					}
				}
			}
			Rect rect = val.rect;
			float num3 = Mathf.Min(((Rect)(ref rect)).height, 40f);
			float num4 = AvailableWidth();
			if (num > 0 && num4 > 0f)
			{
				float num5 = (num4 - (float)num2 * 56f - (float)(num + num2 - 1) * 2f) / (float)num;
				num3 = Mathf.Min(num3, num5);
			}
			num3 = Mathf.Max(num3, 20f);
			float num6 = 8f;
			for (int j = 0; j < buttons.Length; j++)
			{
				PageButton pageButton = buttons[j];
				bool flag = Present(j);
				Show(pageButton.Go, flag);
				if (flag)
				{
					Sprite val2 = source.Icon(j);
					bool flag2 = (Object)(object)val2 != (Object)null;
					if (flag2 && (Object)(object)pageButton.Icon == (Object)null)
					{
						pageButton.Icon = AddIcon(pageButton.Go);
					}
					if ((Object)(object)pageButton.Icon != (Object)null)
					{
						pageButton.Icon.sprite = val2;
						((Component)pageButton.Icon).gameObject.SetActive(flag2);
					}
					SetLabel(pageButton, j);
					if ((Object)(object)pageButton.Tooltip != (Object)null)
					{
						pageButton.Tooltip.m_topic = source.Name(j);
						pageButton.Tooltip.m_text = source.TooltipText(j, counts[j]);
					}
					float num7 = (IsSquare(j) ? num3 : 56f);
					RectTransform val3 = (RectTransform)pageButton.Go.transform;
					val3.anchorMin = new Vector2(1f, 0.5f);
					val3.anchorMax = new Vector2(1f, 0.5f);
					val3.pivot = new Vector2(0f, 0.5f);
					val3.sizeDelta = new Vector2(num7, num3);
					val3.anchoredPosition = new Vector2(num6, 0f);
					num6 += num7 + 2f;
				}
			}
		}

		private static void SetLabel(PageButton b, int page)
		{
			string text = (((Object)(object)b.Icon != (Object)null && ((Component)b.Icon).gameObject.activeSelf) ? "" : source.ButtonText(page));
			TMP_Text[] texts = b.Texts;
			foreach (TMP_Text val in texts)
			{
				if ((Object)(object)val != (Object)null && val.text != text)
				{
					val.text = text;
				}
			}
		}

		private static bool IsSquare(int page)
		{
			if (!((Object)(object)source.Icon(page) != (Object)null))
			{
				return (source.ButtonText(page) ?? "").Length <= 2;
			}
			return true;
		}

		private static void SyncRow(RectTransform tab)
		{
			//IL_0006: 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)
			//IL_0026: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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)
			row.anchorMin = tab.anchorMin;
			row.anchorMax = tab.anchorMax;
			row.pivot = tab.pivot;
			row.sizeDelta = tab.sizeDelta;
			row.anchoredPosition = tab.anchoredPosition;
			((Transform)row).localScale = ((Transform)tab).localScale;
			((Transform)row).localRotation = ((Transform)tab).localRotation;
		}

		private static float AvailableWidth()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)gui.m_crafting == (Object)null)
			{
				return 0f;
			}
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			gui.m_crafting.GetWorldCorners(array);
			float x = ((Transform)row).InverseTransformPoint(array[2]).x;
			Rect rect = row.rect;
			return x - ((Rect)(ref rect)).xMax - 8f - 6f;
		}

		private static void AwakePostfix(InventoryGui __instance)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected O, but got Unknown
			gui = __instance;
			RectTransform val = (RectTransform)((Component)gui.m_tabUpgrade).transform;
			GameObject val2 = FindTooltipPrefab(gui);
			row = (RectTransform)new GameObject(buttonPrefix + "Row", new Type[1] { typeof(RectTransform) }).transform;
			((Transform)row).SetParent(((Transform)val).parent, false);
			((Transform)row).SetSiblingIndex(((Transform)val).GetSiblingIndex() + 1);
			SyncRow(val);
			buttons = new PageButton[source.Count];
			for (int i = 0; i < buttons.Length; i++)
			{
				int page = i;
				GameObject val3 = MakeButton(gui, row, $"{buttonPrefix}Page{i}", (UnityAction)delegate
				{
					Select(page);
				});
				PageButton pageButton = new PageButton
				{
					Go = val3,
					Button = val3.GetComponent<Button>(),
					Texts = val3.GetComponentsInChildren<TMP_Text>(true)
				};
				if ((Object)(object)val2 != (Object)null)
				{
					pageButton.Tooltip = val3.GetComponent<UITooltip>();
					if ((Object)(object)pageButton.Tooltip == (Object)null)
					{
						pageButton.Tooltip = val3.AddComponent<UITooltip>();
					}
					pageButton.Tooltip.m_tooltipPrefab = val2;
				}
				buttons[i] = pageButton;
			}
			layoutDirty = true;
			TMP_Text craftingStationName = gui.m_craftingStationName;
			if ((Object)(object)craftingStationName != (Object)null && !craftingStationName.enableAutoSizing)
			{
				craftingStationName.fontSizeMax = craftingStationName.fontSize;
				craftingStationName.fontSizeMin = Mathf.Min(12f, craftingStationName.fontSize);
				craftingStationName.enableAutoSizing = true;
			}
		}

		private static GameObject FindTooltipPrefab(InventoryGui gui)
		{
			UITooltip[] componentsInChildren = ((Component)gui).GetComponentsInChildren<UITooltip>(true);
			foreach (UITooltip val in componentsInChildren)
			{
				if ((Object)(object)val.m_tooltipPrefab != (Object)null)
				{
					return val.m_tooltipPrefab;
				}
			}
			Logger.LogWarning((object)"Не нашёл шаблон подсказки — у вкладок не будет подсказок");
			return null;
		}

		private static Image AddIcon(GameObject button)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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)
			GameObject val = new GameObject("Icon", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val.transform.SetParent(button.transform, false);
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.offsetMin = new Vector2(4f, 4f);
			val2.offsetMax = new Vector2(-4f, -4f);
			Image component = val.GetComponent<Image>();
			component.preserveAspect = true;
			((Graphic)component).raycastTarget = false;
			return component;
		}

		private static GameObject MakeButton(InventoryGui gui, RectTransform parent, string name, UnityAction onClick)
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			GameObject val = Object.Instantiate<GameObject>(((Component)gui.m_tabCraft).gameObject, (Transform)(object)parent);
			((Object)val).name = name;
			UIGamePad[] componentsInChildren = val.GetComponentsInChildren<UIGamePad>(true);
			foreach (UIGamePad val2 in componentsInChildren)
			{
				if ((Object)(object)val2.m_hint != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val2.m_hint);
				}
				Object.DestroyImmediate((Object)(object)val2);
			}
			MonoBehaviour[] componentsInChildren2 = val.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val3 in componentsInChildren2)
			{
				if (((object)val3).GetType().Name == "Localize")
				{
					Object.DestroyImmediate((Object)(object)val3);
				}
			}
			TMP_Text[] componentsInChildren3 = val.GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text obj in componentsInChildren3)
			{
				obj.fontSizeMax = obj.fontSize;
				obj.fontSizeMin = 10f;
				obj.enableAutoSizing = true;
			}
			val.transform.localScale = Vector3.one;
			Button component = val.GetComponent<Button>();
			component.onClick = new ButtonClickedEvent();
			((Selectable)component).interactable = true;
			((UnityEvent)component.onClick).AddListener(onClick);
			val.SetActive(false);
			return val;
		}

		private static void Show(GameObject go, bool visible)
		{
			if ((Object)(object)go != (Object)null && go.activeSelf != visible)
			{
				go.SetActive(visible);
			}
		}
	}
}
namespace NokepCraftTabs
{
	internal static class BiomeMap
	{
		private static readonly Dictionary<string, Biome> Table = BuildTable();

		private static Dictionary<string, List<Recipe>> recipesByItem;

		private static Dictionary<string, List<string>> madeFrom;

		private static Dictionary<string, Biome> itemCache;

		private static Dictionary<Recipe, Biome> recipeCache;

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

		private static readonly SortedSet<string> unknown = new SortedSet<string>();

		private static ObjectDB builtFor;

		private static int builtRecipes;

		private static int builtOverrides = -1;

		private static bool builtWithScene;

		private static bool reportWritten;

		private static Dictionary<string, Biome> BuildTable()
		{
			Dictionary<string, Biome> table = new Dictionary<string, Biome>();
			Add(Biome.Meadows, "Wood Stone Flint Resin Feathers LeatherScraps DeerHide DeerMeat RawMeat NeckTail\n                Raspberry Honey Mushroom Dandelion HardAntler TrophyDeer TrophyBoar TrophyNeck TrophyEikthyr Fish1");
			Add(Biome.BlackForest, "FineWood RoundLog CopperOre CopperScrap TinOre BronzeScrap Blueberries Carrot Thistle\n                GreydwarfEye AncientSeed TrollHide BoneFragments SurtlingCore TrophyGreydwarf TrophyGreydwarfBrute\n                TrophyGreydwarfShaman TrophySkeleton TrophyFrostTroll TrophyTheElder TrophyBjorn BjornHide BjornMeat BjornPaw\n                MushroomYellow Ruby Amber AmberPearl Fish2");
			Add(Biome.Swamp, "IronScrap IronOre ElderBark Guck Bloodbag Entrails Chain Ooze WitheredBone Root Turnip Ectoplasm\n                TrophyBlob TrophyLeech TrophyDraugr TrophyDraugrElite TrophyAbomination TrophySurtling TrophyWraith\n                TrophyBonemass Fish5");
			Add(Biome.Ocean, "Chitin SerpentScale SerpentMeat TrophySerpent Fish3 Fish6 Fish8 Fish9 SpiceOceans");
			Add(Biome.Mountains, "SilverOre WolfPelt WolfFang WolfClaw WolfHairBundle WolfMeat FreezeGland Crystal Obsidian\n                Onion DragonEgg PowderedDragonEgg DragonTear TrophyHatchling TrophyFenring TrophyCultist TrophySGolem\n                TrophyWolf TrophyDragonQueen Fish4_cave SpiceMountains");
			Add(Biome.Plains, "BlackMetalScrap Flax Barley LoxMeat LoxPelt TrophyLox Needle Cloudberry Tar TrophyGrowth\n                GoblinTotem TrophyGoblin TrophyGoblinBrute TrophyGoblinShaman TrophyDeathsquito TrophyGoblinKing YagluthDrop\n                TrophyBjornUndead UndeadBjornRibcage ChickenEgg ChickenMeat Fish7 SpicePlains");
			Add(Biome.Mistlands, "YggdrasilWood BlackMarble BlackCore Softtissue Carapace Mandible ScaleHide RoyalJelly\n                BugMeat HareMeat Sap Wisp MushroomJotunPuffs MushroomMagecap Bilebag GiantBloodSack TrophySeeker\n                TrophySeekerBrute TrophyGjall TrophyTick TrophyHare TrophyDvergr TrophySeekerQueen QueenDrop\n                DvergrKeyFragment Fish12 SpiceMistlands");
			Add(Biome.Ashlands, "Blackwood CharredBone MoltenCore SulfurStone FlametalOreNew Grausten GemstoneBlue GemstoneGreen\n                GemstoneRed AsksvinMeat AskHide AskBladder VoltureMeat VoltureEgg MorgenHeart MorgenSinew TrophyMorgen\n                TrophyCharredMelee TrophyCharredArcher TrophyCharredMage TrophyAsksvin TrophyVolture TrophyFallenValkyrie\n                CelestialFeather BellFragment FaderEmber TrophyFader ProustitePowder BonemawSerpentTooth BoneMawSerpentMeat\n                TrophyBonemawSerpent Fiddleheadfern Vineberry MushroomSmokePuff DyrnwynBladeFragment DyrnwynHiltFragment\n                DyrnwynTipFragment Fish11 SpiceAshlands");
			Add(Biome.DeepNorth, "GoldOre Frostwood Kale Poteitr OatSeeds Lingonberry MooseHide MooseMeat MooseSinew TrophyMoose\n                SealBlubber SealHide ElakingHairBundle MoleClaws BarkaBranch NornThread TrophyJotunWitch Leatherstraps OozeMork\n                TrophyBlob_Morkhalla CrownJewel WrithanRoots OrbFrostFire OrbThunderBlood Ice Fish10 SpiceDeepNorth\n                MoldArmorGoldChest MoldArmorGoldHelmet MoldArmorGoldLegs MoldArmorMageChest MoldArmorMageHelmet\n                MoldArmorMageLegs MoldArmorMediumHelmet MoldArmorMediumLegs MoldArmormediumChest MoldAtgeir MoldAxe\n                MoldAxe2H MoldBow MoldCrossbow MoldFistweapon MoldKeys MoldKnife MoldMace MoldMace2H MoldShieldBuckler\n                MoldShieldRound MoldShieldTower MoldSpear MoldStaffOrbofAhri MoldStafffrostorbs MoldStaffspiritcaller\n                MoldStaffthunderblood MoldSword MoldSword2H");
			return table;
			void Add(Biome biome, string names)
			{
				string[] array = names.Split(new char[3] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
				foreach (string key in array)
				{
					table[key] = biome;
				}
			}
		}

		public static void EnsureBuilt()
		{
			ObjectDB instance = ObjectDB.instance;
			if (!((Object)(object)instance == (Object)null) && (!((Object)(object)instance == (Object)(object)builtFor) || instance.m_recipes.Count != builtRecipes || Overrides.Version != builtOverrides || (!builtWithScene && !((Object)(object)ZNetScene.instance == (Object)null))))
			{
				Build(instance);
			}
		}

		private static void Build(ObjectDB db)
		{
			builtFor = db;
			builtRecipes = db.m_recipes.Count;
			builtOverrides = Overrides.Version;
			builtWithScene = (Object)(object)ZNetScene.instance != (Object)null;
			recipesByItem = new Dictionary<string, List<Recipe>>();
			madeFrom = new Dictionary<string, List<string>>();
			itemCache = new Dictionary<string, Biome>();
			recipeCache = new Dictionary<Recipe, Biome>();
			unknown.Clear();
			foreach (Recipe recipe in db.m_recipes)
			{
				if (!((Object)(object)recipe == (Object)null) && !((Object)(object)recipe.m_item == (Object)null) && recipe.m_enabled)
				{
					AddTo(recipesByItem, ((Object)((Component)recipe.m_item).gameObject).name, recipe);
				}
			}
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				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)
						{
							AddConversion(item.m_from, 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)
						{
							AddConversion(item2.m_from, 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)
					{
						AddConversion(item3.m_from, item3.m_to);
					}
				}
			}
			int num = 0;
			foreach (Recipe recipe2 in db.m_recipes)
			{
				if ((Object)(object)recipe2 != (Object)null && (Object)(object)recipe2.m_item != (Object)null && Of(recipe2) == Biome.Other)
				{
					num++;
				}
			}
			Logger.LogInfo((object)($"Биомы рецептов посчитаны: рецептов {recipeCache.Count}, в «Другое» {num}, " + string.Format("ингредиентов без биома {0}: {1}", unknown.Count, string.Join(", ", unknown))));
			reportWritten = false;
		}

		private static void AddConversion(ItemDrop from, ItemDrop to)
		{
			if ((Object)(object)from != (Object)null && (Object)(object)to != (Object)null)
			{
				AddTo(madeFrom, ((Object)((Component)to).gameObject).name, ((Object)((Component)from).gameObject).name);
			}
		}

		private static void AddTo<T>(Dictionary<string, List<T>> dict, string key, T value)
		{
			if (!dict.TryGetValue(key, out var value2))
			{
				value2 = (dict[key] = new List<T>());
			}
			value2.Add(value);
		}

		public static Biome Of(Recipe recipe)
		{
			if (recipeCache == null)
			{
				EnsureBuilt();
			}
			if (recipeCache == null || (Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null)
			{
				return Biome.Other;
			}
			if (recipeCache.TryGetValue(recipe, out var value))
			{
				return value;
			}
			if (!Overrides.TryGet(((Object)((Component)recipe.m_item).gameObject).name, out value))
			{
				value = FromIngredients(recipe);
				if (value == (Biome)(-1))
				{
					value = Biome.Other;
				}
			}
			recipeCache[recipe] = value;
			return value;
		}

		private static Biome FromIngredients(Recipe recipe)
		{
			Biome biome = (Biome)(-1);
			if (recipe.m_resources == null)
			{
				return biome;
			}
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (val != null && !((Object)(object)val.m_resItem == (Object)null) && !val.m_upgraderResource && val.m_amount > 0)
				{
					Biome biome2 = OfItem(((Object)((Component)val.m_resItem).gameObject).name);
					if (biome2 != (Biome)(-1) && (biome == (Biome)(-1) || (recipe.m_requireOnlyOneIngredient ? (biome2 < biome) : (biome2 > biome))))
					{
						biome = biome2;
					}
				}
			}
			return biome;
		}

		private static Biome OfItem(string name)
		{
			if (Overrides.TryGet(name, out var biome))
			{
				return biome;
			}
			if (Table.TryGetValue(name, out biome))
			{
				return biome;
			}
			if (itemCache.TryGetValue(name, out biome))
			{
				return biome;
			}
			if (!visiting.Add(name))
			{
				return (Biome)(-1);
			}
			biome = (Biome)(-1);
			if (recipesByItem.TryGetValue(name, out var value))
			{
				foreach (Recipe item in value)
				{
					biome = Biomes.Earliest(biome, FromIngredients(item));
				}
			}
			if (madeFrom.TryGetValue(name, out var value2))
			{
				foreach (string item2 in value2)
				{
					biome = Biomes.Earliest(biome, OfItem(item2));
				}
			}
			visiting.Remove(name);
			if (biome == (Biome)(-1) && value == null && value2 == null)
			{
				unknown.Add(name);
			}
			itemCache[name] = biome;
			return biome;
		}

		public static void WriteReportIfAsked()
		{
			if (ModConfig.WriteReport.Value && !reportWritten && !((Object)(object)builtFor == (Object)null))
			{
				reportWritten = true;
				WriteReport(builtFor);
			}
		}

		private static void WriteReport(ObjectDB db)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# NokepCraftTabs: tabs of recipes and biomes of ingredients (? = unknown, not counted)");
			stringBuilder.AppendLine("# Unknown ingredients: " + string.Join(", ", unknown));
			foreach (IGrouping<string, Recipe> item in from r in db.m_recipes
				where (Object)(object)r != (Object)null && (Object)(object)r.m_item != (Object)null && r.m_enabled
				group r by (!((Object)(object)r.m_craftingStation != (Object)null)) ? "(no station)" : ((Object)r.m_craftingStation).name into g
				orderby g.Key
				select g)
			{
				stringBuilder.AppendLine();
				stringBuilder.AppendLine("[" + item.Key + "]");
				foreach (Recipe item2 in from r in item
					orderby Of(r), r.m_minStationLevel, ((Object)r.m_item).name
					select r)
				{
					IEnumerable<string> values = from q in item2.m_resources
						where q != null && (Object)(object)q.m_resItem != (Object)null && !q.m_upgraderResource && q.m_amount > 0
						select ((Object)q.m_resItem).name + "=" + Short(OfItem(((Object)q.m_resItem).name));
					Biome biome;
					string text = (Overrides.TryGet(((Object)item2.m_item).name, out biome) ? " (override)" : "");
					string text2 = (item2.m_requireOnlyOneIngredient ? " (any one)" : "");
					stringBuilder.AppendLine(string.Format("  {0,-11} lvl{1} {2}{3}{4}: {5}", Of(item2), item2.m_minStationLevel, ((Object)item2.m_item).name, text, text2, string.Join(", ", values)));
				}
			}
			string text3 = Path.Combine(Paths.ConfigPath, "NokepCraftTabs_report.txt");
			try
			{
				File.WriteAllText(text3, stringBuilder.ToString());
				Logger.LogInfo((object)("Отчёт по биомам: " + text3));
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Не смог записать отчёт " + text3 + ": " + ex.Message));
			}
		}

		private static string Short(Biome biome)
		{
			if (biome != (Biome)(-1))
			{
				return biome.ToString();
			}
			return "?";
		}
	}
	internal enum Biome
	{
		All,
		Meadows,
		BlackForest,
		Swamp,
		Ocean,
		Mountains,
		Plains,
		Mistlands,
		Ashlands,
		DeepNorth,
		Other
	}
	internal static class Biomes
	{
		public const Biome Unknown = (Biome)(-1);

		public static readonly int Count = Enum.GetValues(typeof(Biome)).Length;

		private static readonly string[] IconItems = new string[11]
		{
			null, "TrophyEikthyr", "TrophyTheElder", "TrophyBonemass", "TrophySerpent", "TrophyDragonQueen", "TrophyGoblinKing", "TrophySeekerQueen", "TrophyFader", "TrophyMoose",
			null
		};

		private static readonly Sprite[] icons = (Sprite[])(object)new Sprite[IconItems.Length];

		private static readonly Color[] Colors = (Color[])(object)new Color[11]
		{
			Color.clear,
			new Color(0.55f, 0.8f, 0.4f),
			new Color(0.2f, 0.5f, 0.25f),
			new Color(0.5f, 0.38f, 0.22f),
			new Color(0.25f, 0.5f, 0.85f),
			new Color(0.85f, 0.88f, 0.92f),
			new Color(0.9f, 0.8f, 0.3f),
			new Color(0.6f, 0.4f, 0.8f),
			new Color(0.85f, 0.3f, 0.2f),
			new Color(0.5f, 0.85f, 0.95f),
			new Color(0.5f, 0.5f, 0.5f)
		};

		public static string Name(Biome biome)
		{
			return Translations.Get("$nokep_tabs_" + biome.ToString().ToLowerInvariant());
		}

		public static Sprite Icon(Biome biome)
		{
			if (biome < Biome.All || (int)biome >= IconItems.Length || IconItems[(int)biome] == null)
			{
				return null;
			}
			if ((Object)(object)icons[(int)biome] == (Object)null && (Object)(object)ObjectDB.instance != (Object)null)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(IconItems[(int)biome]);
				ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val != (Object)null)
				{
					icons[(int)biome] = val.m_itemData.GetIcon();
				}
				else
				{
					Logger.LogWarning((object)$"Не нашёл значок {IconItems[(int)biome]} для вкладки {biome}");
				}
			}
			return icons[(int)biome];
		}

		public static Color StripeColor(Biome biome)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (biome < Biome.All || (int)biome >= Colors.Length)
			{
				return Colors[Colors.Length - 1];
			}
			return Colors[(int)biome];
		}

		public static Biome Earliest(Biome a, Biome b)
		{
			if (a == (Biome)(-1))
			{
				return b;
			}
			if (b == (Biome)(-1))
			{
				return a;
			}
			if (a >= b)
			{
				return b;
			}
			return a;
		}
	}
	internal class BiomeTabs : CraftTabSource
	{
		private static bool? disabled;

		private static HashSet<string> excluded;

		public override int Count => Biomes.Count;

		public override KeyCode NextKey => ModConfig.NextKey.Value;

		public override KeyCode PrevKey => ModConfig.PrevKey.Value;

		public override string NextGamepadButton
		{
			get
			{
				if (!ModConfig.Gamepad.Value)
				{
					return null;
				}
				return "JoyRStickRight";
			}
		}

		public override string PrevGamepadButton
		{
			get
			{
				if (!ModConfig.Gamepad.Value)
				{
					return null;
				}
				return "JoyRStickLeft";
			}
		}

		public override bool RememberPage => ModConfig.RememberTab.Value;

		public override string Name(int page)
		{
			return Biomes.Name((Biome)page);
		}

		public override int PageOf(Recipe recipe)
		{
			return (int)BiomeMap.Of(recipe);
		}

		public override Sprite Icon(int page)
		{
			return Biomes.Icon((Biome)page);
		}

		public override string ButtonText(int page)
		{
			if (page != 10)
			{
				return Name(page);
			}
			return "?";
		}

		public override string TooltipText(int page, int count)
		{
			return string.Format("{0}: {1}", Translations.Get("$nokep_tabs_recipes"), count);
		}

		public override void AfterRecipeList(InventoryGui gui, int page)
		{
			RecipeLook.Apply(gui, (Biome)page);
		}

		public override bool IsActive(CraftingStation station)
		{
			if (!Disabled())
			{
				return !IsExcluded(station);
			}
			return false;
		}

		public override void OnStationOpened(CraftingStation station)
		{
			Overrides.ReloadIfChanged();
			BiomeMap.EnsureBuilt();
			BiomeMap.WriteReportIfAsked();
		}

		public static void ResetSettings()
		{
			disabled = null;
			excluded = null;
		}

		private static IEnumerable<string> SplitList(string value)
		{
			return from s in (value ?? "").Split(new char[1] { ',' })
				select s.Trim() into s
				where s.Length > 0
				select s;
		}

		private static string Normalize(string s)
		{
			return new string((s ?? "").Where(char.IsLetterOrDigit).ToArray()).ToLowerInvariant();
		}

		private static bool Disabled()
		{
			if (disabled.HasValue)
			{
				return disabled.Value;
			}
			disabled = false;
			string[] array = (from k in SplitList(ModConfig.DisableWithMods.Value).Select(Normalize)
				where k.Length > 0
				select k).ToArray();
			foreach (PluginInfo value2 in Chainloader.PluginInfos.Values)
			{
				if (((value2 != null) ? value2.Metadata : null) == null || value2.Metadata.GUID == "nokep.valheim.crafttabs")
				{
					continue;
				}
				string text = Normalize(value2.Metadata.GUID);
				string text2 = Normalize(value2.Metadata.Name);
				string[] array2 = array;
				foreach (string value in array2)
				{
					if (text.Contains(value) || text2.Contains(value))
					{
						Logger.LogWarning((object)("Вкладки выключены: стоит мод " + value2.Metadata.Name + " (" + value2.Metadata.GUID + "), он меняет окно крафта. Настройка «Disable with mods»."));
						disabled = true;
						return true;
					}
				}
			}
			return false;
		}

		private static bool IsExcluded(CraftingStation station)
		{
			if (excluded == null)
			{
				excluded = new HashSet<string>(from s in SplitList(ModConfig.ExcludedStations.Value)
					select s.TrimStart(new char[1] { '$' }), StringComparer.OrdinalIgnoreCase);
			}
			string prefabName = Utils.GetPrefabName(((Component)station).gameObject);
			string item = (station.m_name ?? "").TrimStart(new char[1] { '$' });
			if (!excluded.Contains(prefabName))
			{
				return excluded.Contains(item);
			}
			return true;
		}
	}
	internal static class ModConfig
	{
		public static ConfigEntry<KeyCode> NextKey;

		public static ConfigEntry<KeyCode> PrevKey;

		public static ConfigEntry<bool> Gamepad;

		public static ConfigEntry<bool> RememberTab;

		public static ConfigEntry<bool> SortByBiome;

		public static ConfigEntry<bool> BiomeStripe;

		public static ConfigEntry<string> ExcludedStations;

		public static ConfigEntry<string> DisableWithMods;

		public static ConfigEntry<bool> WriteReport;

		public static void Bind(ConfigFile config)
		{
			NextKey = config.Bind<KeyCode>("General", "Next tab key", (KeyCode)275, "Key for the next biome tab in the crafting window.");
			PrevKey = config.Bind<KeyCode>("General", "Previous tab key", (KeyCode)276, "Key for the previous biome tab in the crafting window.");
			Gamepad = config.Bind<bool>("General", "Gamepad", true, "Switch biome tabs with the right stick left/right.");
			RememberTab = config.Bind<bool>("General", "Remember tab", true, "Each kind of station opens on the tab you used there last time (until you quit the game). Off = always All.");
			SortByBiome = config.Bind<bool>("General", "Sort All by biome", true, "In the All tab: craftable recipes first, then by biome (Meadows → Deep North).");
			BiomeStripe = config.Bind<bool>("General", "Biome stripe", true, "In the All tab: a thin stripe of the biome color at the left edge of each recipe.");
			ExcludedStations = config.Bind<string>("General", "Excluded stations", "piece_nokep_leatherbench, piece_nokep_magictable, piece_artisanstation", "Crafting stations without biome tabs. Prefab name (piece_workbench) or station name ($piece_workbench), comma separated.");
			DisableWithMods = config.Bind<string>("General", "Disable with mods", "Auga, WorkbenchesPlus", "The mod turns itself off if any of these mods is installed (they replace or rebuild the crafting window). Part of the mod name or GUID, comma separated.");
			WriteReport = config.Bind<bool>("General", "Write biome report", false, "Write BepInEx/config/NokepCraftTabs_report.txt when you open a crafting station: every recipe with its tab and the biome of each ingredient. Use it to find items for the overrides file.");
		}
	}
	internal static class Overrides
	{
		private static readonly Dictionary<string, Biome> map = new Dictionary<string, Biome>(StringComparer.OrdinalIgnoreCase);

		private static DateTime lastWrite;

		private const string Template = "# NokepCraftTabs: manual biome tabs.\n# One line per item: PrefabName = Biome\n# Biomes: Meadows, BlackForest, Swamp, Ocean, Mountains, Plains, Mistlands, Ashlands, DeepNorth, Other\n#\n# Works both ways:\n#  - for a crafted item: its recipe goes to this tab;\n#  - for an ingredient: everything made from it is counted with this biome.\n# Changes apply the next time you open a crafting station.\n#\n# Examples (remove # to use):\n# CookedEgg = Meadows\n# YmirRemains = Mountains\n";

		public static string FilePath { get; private set; }

		public static int Version { get; private set; }

		public static void Init()
		{
			FilePath = Path.Combine(Paths.ConfigPath, "nokep.valheim.crafttabs.overrides.txt");
			try
			{
				if (!File.Exists(FilePath))
				{
					File.WriteAllText(FilePath, "# NokepCraftTabs: manual biome tabs.\n# One line per item: PrefabName = Biome\n# Biomes: Meadows, BlackForest, Swamp, Ocean, Mountains, Plains, Mistlands, Ashlands, DeepNorth, Other\n#\n# Works both ways:\n#  - for a crafted item: its recipe goes to this tab;\n#  - for an ingredient: everything made from it is counted with this biome.\n# Changes apply the next time you open a crafting station.\n#\n# Examples (remove # to use):\n# CookedEgg = Meadows\n# YmirRemains = Mountains\n");
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Не смог создать файл исправлений " + FilePath + ": " + ex.Message));
			}
			ReloadIfChanged();
		}

		public static bool TryGet(string prefab, out Biome biome)
		{
			return map.TryGetValue(prefab, out biome);
		}

		public static void ReloadIfChanged()
		{
			if (!File.Exists(FilePath))
			{
				if (map.Count > 0)
				{
					map.Clear();
					Version++;
				}
				return;
			}
			DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(FilePath);
			if (lastWriteTimeUtc == lastWrite)
			{
				return;
			}
			lastWrite = lastWriteTimeUtc;
			map.Clear();
			Version++;
			string[] array;
			try
			{
				array = File.ReadAllLines(FilePath);
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("Не смог прочитать " + FilePath + ": " + ex.Message));
				return;
			}
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0 && !text.StartsWith("#"))
				{
					int num = text.IndexOf('=');
					string text2 = ((num > 0) ? text.Substring(0, num).Trim() : "");
					string value = ((num > 0) ? text.Substring(num + 1).Trim().Replace(" ", "") : "");
					if (text2.Length == 0 || !Enum.TryParse<Biome>(value, ignoreCase: true, out var result) || result == Biome.All || !Enum.IsDefined(typeof(Biome), result))
					{
						Logger.LogWarning((object)$"Файл исправлений, строка {i + 1}: не понял «{array[i]}»");
					}
					else
					{
						map[text2] = result;
					}
				}
			}
			Logger.LogInfo((object)$"Исправлений биомов из файла: {map.Count}");
		}
	}
	[BepInPlugin("nokep.valheim.crafttabs", "NokepCraftTabs", "0.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	internal class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "nokep.valheim.crafttabs";

		public const string PluginName = "NokepCraftTabs";

		public const string PluginVersion = "0.2.0";

		private Harmony harmony;

		private void Awake()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			Translations.Load();
			Overrides.Init();
			harmony = new Harmony("nokep.valheim.crafttabs");
			CraftTabs.Install(harmony, new BiomeTabs(), "NokepCraftTabs");
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				BiomeTabs.ResetSettings();
				CraftTabs.ResetCache();
			};
			Logger.LogInfo((object)"NokepCraftTabs 0.2.0 загружен");
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
	internal static class RecipeLook
	{
		private const string StripeName = "NokepBiomeStripe";

		private const float StripeWidth = 3f;

		private const float StripeInset = 4f;

		public static void Apply(InventoryGui gui, Biome page)
		{
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			if (page != Biome.All)
			{
				return;
			}
			List<RecipeDataPair> availableRecipes = gui.m_availableRecipes;
			if (ModConfig.SortByBiome.Value && availableRecipes.Count > 1)
			{
				List<RecipeDataPair> collection = (from x in availableRecipes.Select((RecipeDataPair pair, int index) => (pair: pair, index: index))
					orderby (!((RecipeDataPair)(ref x.pair)).CanCraft) ? 1 : 0, (int)BiomeMap.Of(((RecipeDataPair)(ref x.pair)).Recipe), x.index
					select x.pair).ToList();
				availableRecipes.Clear();
				availableRecipes.AddRange(collection);
				for (int num = 0; num < availableRecipes.Count; num++)
				{
					RecipeDataPair val = availableRecipes[num];
					RectTransform val2 = (RectTransform)((RecipeDataPair)(ref val)).InterfaceElement.transform;
					val2.anchoredPosition = new Vector2(val2.anchoredPosition.x, (float)num * (0f - gui.m_recipeListSpace));
				}
			}
			if (!ModConfig.BiomeStripe.Value)
			{
				return;
			}
			foreach (RecipeDataPair item in availableRecipes)
			{
				RecipeDataPair current = item;
				AddStripe(((RecipeDataPair)(ref current)).InterfaceElement, Biomes.StripeColor(BiomeMap.Of(((RecipeDataPair)(ref current)).Recipe)));
			}
		}

		private static void AddStripe(GameObject element, Color color)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)element == (Object)null) && !((Object)(object)element.transform.Find("NokepBiomeStripe") != (Object)null))
			{
				GameObject val = new GameObject("NokepBiomeStripe", new Type[2]
				{
					typeof(RectTransform),
					typeof(Image)
				});
				val.transform.SetParent(element.transform, false);
				RectTransform val2 = (RectTransform)val.transform;
				val2.anchorMin = new Vector2(0f, 0f);
				val2.anchorMax = new Vector2(0f, 1f);
				val2.pivot = new Vector2(0f, 0.5f);
				val2.sizeDelta = new Vector2(3f, -8f);
				val2.anchoredPosition = new Vector2(1f, 0f);
				Image component = val.GetComponent<Image>();
				((Graphic)component).color = new Color(color.r, color.g, color.b, 0.85f);
				((Graphic)component).raycastTarget = false;
			}
		}
	}
}