Decompiled source of BuildMenu v0.3.13

plugins/BuildMenu.dll

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

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.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 BuildMenu
{
	internal sealed class Sub
	{
		public string Key;

		public string Ru;

		public string En;

		public Group Group;

		public string Name(bool russian)
		{
			if (!russian)
			{
				return En;
			}
			return Ru;
		}
	}
	internal sealed class Group
	{
		public string Key;

		public string Ru;

		public string En;

		public readonly List<Sub> Subs = new List<Sub>();

		public string Name(bool russian)
		{
			if (!russian)
			{
				return En;
			}
			return Ru;
		}
	}
	internal static class Catalog
	{
		public static readonly List<Group> Groups;

		private static readonly Dictionary<string, Sub> byKey;

		static Catalog()
		{
			Groups = new List<Group>();
			byKey = new Dictionary<string, Sub>(StringComparer.OrdinalIgnoreCase);
			Add("crafting", "Ремесло", "Crafting", ("stations", "Верстаки и станции", "Workbenches and stations"), ("upgrades", "Улучшения станций", "Station upgrades"), ("smelting", "Плавильни и производство", "Smelters and production"), ("kitchen", "Кухня", "Kitchen"));
			Add("building", "Строительство", "Building", ("floor", "Полы", "Floors"), ("wall", "Стены", "Walls"), ("roof", "Крыши", "Roofs"), ("stairs", "Лестницы и пандусы", "Stairs and ramps"), ("beam", "Балки и столбы", "Beams and poles"), ("door", "Двери и ворота", "Doors and gates"), ("window", "Окна", "Windows"), ("arch", "Арки", "Arches"), ("fence", "Заборы", "Fences"), ("defense", "Оборона", "Defense"), ("other", "Прочее", "Other"));
			Add("interior", "Интерьер", "Interior", ("furniture", "Мебель", "Furniture"), ("light", "Освещение", "Lighting"), ("decor", "Декор", "Decoration"));
			Add("travel", "Перемещение", "Travel", ("transport", "Транспорт", "Transport"), ("portal", "Порталы", "Portals"));
			Add("storage", "Хранилище", "Storage", ("storage", "Хранилище", "Storage"));
			Add("misc", "Разное", "Miscellaneous", ("misc", "Разное", "Miscellaneous"));
		}

		private static void Add(string key, string ru, string en, params (string key, string ru, string en)[] subs)
		{
			Group obj = new Group
			{
				Key = key,
				Ru = ru,
				En = en
			};
			for (int i = 0; i < subs.Length; i++)
			{
				(string, string, string) tuple = subs[i];
				AddSub(obj, tuple.Item1, tuple.Item2, tuple.Item3);
			}
			Groups.Add(obj);
		}

		private static Sub AddSub(Group g, string key, string ru, string en)
		{
			Sub sub = new Sub
			{
				Key = key,
				Ru = ru,
				En = en,
				Group = g
			};
			g.Subs.Add(sub);
			byKey[key] = sub;
			return sub;
		}

		public static Sub ToolSub(string key, string name)
		{
			Sub sub = Find(key);
			if (sub != null)
			{
				return sub;
			}
			Group obj = Groups.FirstOrDefault((Group x) => x.Key == "tools");
			if (obj == null)
			{
				obj = new Group
				{
					Key = "tools",
					Ru = "Другие инструменты",
					En = "Other tools"
				};
				Groups.Add(obj);
			}
			return AddSub(obj, key, name, name);
		}

		public static Sub Find(string key)
		{
			if (key == null || !byKey.TryGetValue(key, out var value))
			{
				return null;
			}
			return value;
		}

		public static Sub FindOrCreate(string text)
		{
			text = text.Trim();
			Sub sub = Find(text) ?? Groups.SelectMany((Group g) => g.Subs).FirstOrDefault((Sub s) => string.Equals(s.Ru, text, StringComparison.OrdinalIgnoreCase) || string.Equals(s.En, text, StringComparison.OrdinalIgnoreCase));
			if (sub != null || text.Length == 0)
			{
				return sub;
			}
			string groupName = "";
			string text2 = text;
			int num = text.IndexOf('/');
			if (num > 0)
			{
				groupName = text.Substring(0, num).Trim();
				text2 = text.Substring(num + 1).Trim();
			}
			Group obj = ((groupName.Length == 0) ? Groups.First((Group g) => g.Key == "misc") : Groups.FirstOrDefault((Group g) => string.Equals(g.Key, groupName, StringComparison.OrdinalIgnoreCase) || string.Equals(g.Ru, groupName, StringComparison.OrdinalIgnoreCase) || string.Equals(g.En, groupName, StringComparison.OrdinalIgnoreCase)));
			if (obj == null)
			{
				obj = new Group
				{
					Key = groupName,
					Ru = groupName,
					En = groupName
				};
				Groups.Add(obj);
			}
			return Find(text2) ?? AddSub(obj, text2, text2, text2);
		}

		private static bool Any(string words, params string[] parts)
		{
			return parts.Any(words.Contains);
		}

		public static List<string> Classify(PieceFacts f)
		{
			return new List<string> { One(f) };
		}

		private static string One(PieceFacts f)
		{
			string words = f.Words;
			if (f.Has("TeleportWorld"))
			{
				return "portal";
			}
			if ((!f.Has("Vagon") || !f.Tagged("Defense")) && (f.Has("Ship") || f.Has("Vagon") || f.Tagged("Transport")))
			{
				return "transport";
			}
			if (f.Has("Container") && !f.Has("Incinerator"))
			{
				return "storage";
			}
			if (Any(words, "forge", "foundry") && f.Has("CookingStation"))
			{
				return "smelting";
			}
			if (f.Has("CookingStation") || f.Has("Fermenter") || f.Tagged("Food") || f.Tagged("Meads") || f.Tagged("Feasts") || Any(words, "cauldron", "oven"))
			{
				return "kitchen";
			}
			if (f.Has("StationExtension"))
			{
				return "upgrades";
			}
			if (f.Has("CraftingStation"))
			{
				return "stations";
			}
			if (!f.Tagged("Furniture") && (f.Has("Smelter") || f.Has("Beehive") || f.Has("SapCollector") || f.Has("Windmill") || f.Has("Incinerator") || f.Has("WispSpawner")))
			{
				return "smelting";
			}
			if (f.Tagged("Lighting") || f.Has("Fireplace") || f.Has("Demister"))
			{
				return "light";
			}
			if (f.Tagged("Furniture") || f.Has("Bed") || f.Has("Chair") || f.Comfort.StartsWith("Bed") || f.Comfort.StartsWith("Chair") || f.Comfort.StartsWith("Table"))
			{
				return "furniture";
			}
			bool flag = f.Tagged("Floor") || f.Tagged("Wall") || f.Tagged("Roof") || f.Tagged("Stairs") || f.Tagged("Doors") || f.Tagged("Architecture");
			bool flag2 = f.Usage.Split('|').Any((string t) => t.Length > 0 && t != "Misc");
			bool flag3 = flag || f.Category == "BuildingWorkbench" || f.Category == "BuildingStonecutter" || (!flag2 && Any(words, "floor", "wall", "roof", "beam", "pole", "stair", "door", "gate", "window", "fence"));
			if (!flag3 && (f.Tagged("Defense") || f.Has("Trap") || f.Has("Turret") || f.Has("ShieldGenerator") || f.Has("PrivateArea") || Any(words, "stake", "spike")))
			{
				return "defense";
			}
			bool flag4 = f.Tagged("Decor") || Starts(Names(f), "adornment", "ornament", "decoration", "украшен", "орнамент");
			if ((flag3 && (!flag4 || flag)) || (f.Tagged("Building") && !flag4))
			{
				return Kind(f, flag);
			}
			if (f.Tagged("Decor") || f.Tagged("Stacks") || f.Has("ItemStand") || f.Has("ArmorStand") || f.Has("Sign") || Any(f.Comfort, "Banner", "Carpet", "Display", "Decor", "Garland") || flag4)
			{
				return "decor";
			}
			if (f.Tagged("Defense"))
			{
				return "defense";
			}
			return "misc";
		}

		private static string[] Names(PieceFacts f)
		{
			string text = f.NameEn + " " + f.NameRu;
			string text2 = ((f.NameEn.Length > 0) ? f.NameEn : f.NameRu);
			if (text2.Length == 0 || text2.StartsWith("["))
			{
				text = text + " " + f.Prefab.Replace('_', ' ');
			}
			return text.ToLowerInvariant().Split(new char[14]
			{
				' ', '-', '(', ')', '[', ']', '«', '»', '"', '\'',
				'°', ',', '.', '/'
			}, StringSplitOptions.RemoveEmptyEntries);
		}

		private static bool Starts(string[] words, params string[] stems)
		{
			return words.Any((string x) => stems.Any((string s) => x.StartsWith(s, StringComparison.Ordinal)));
		}

		private static bool Is(string[] words, params string[] whole)
		{
			return words.Any((string x) => whole.Contains(x));
		}

		private static string Kind(PieceFacts f, bool shaped)
		{
			string[] words = Names(f);
			if (f.Tagged("Defense") || Starts(words, "stake", "spike", "drawbridge", "palisade", "частокол", "шип", "подъёмн", "подъемн"))
			{
				return "defense";
			}
			if (Starts(words, "window", "shutter", "glass", "crystal", "окн", "окош", "ставн", "створк", "стекл", "хрустал", "кристал"))
			{
				return "window";
			}
			if (f.Tagged("Doors") || f.Has("Door") || Starts(words, "door", "gate", "hatch", "trapdoor", "дверь", "двери", "дверн", "дверц", "дверей", "ворот", "люк", "калитк"))
			{
				return "door";
			}
			if (f.Tagged("Stairs") || Starts(words, "stair", "staircase", "ladder", "ramp", "steps", "лестн", "ступен", "пандус", "трап", "стремянк"))
			{
				return "stairs";
			}
			bool flag = Starts(words, "cross", "wall", "крест", "стен", "полустен");
			if (f.Tagged("Roof") || (!shaped && !flag && Starts(words, "roof", "ridge", "shingle", "thatch", "крыш", "кровл", "конёк", "конек", "черепиц", "солом")))
			{
				return "roof";
			}
			if (Starts(words, "arch", "арк", "арочн"))
			{
				return "arch";
			}
			if (Starts(words, "fence", "railing", "rail", "забор", "огражд", "ограда", "перил", "изгород"))
			{
				return "fence";
			}
			if ((f.Tagged("Floor") && !f.Tagged("Wall")) || (!shaped && (Is(words, "пол", "пола", "полы") || Starts(words, "floor", "настил", "помост", "platform", "платформ"))))
			{
				return "floor";
			}
			if (f.Tagged("Wall") || Starts(words, "wall", "divider", "panel", "block", "cube", "стен", "полустен", "перегород", "панел", "блок", "куб"))
			{
				return "wall";
			}
			if (f.Tagged("Architecture") || Starts(words, "beam", "pole", "pillar", "column", "post", "cross", "spire", "log", "балк", "столб", "колонн", "брус", "опор", "крест", "стойк", "шпил", "бревн"))
			{
				return "beam";
			}
			float num = Math.Max(f.SizeX, f.SizeZ);
			float num2 = Math.Min(f.SizeX, f.SizeZ);
			if (num > 0f && f.SizeY > 1.5f * num)
			{
				return "beam";
			}
			if (f.SizeY > 0f && f.SizeY < 0.5f && num2 >= 0.9f)
			{
				return "floor";
			}
			if (num2 > 0f && num2 < 0.6f && num >= 0.9f && f.SizeY >= 0.9f)
			{
				return "wall";
			}
			return "other";
		}
	}
	internal static class Cells
	{
		private sealed class Look
		{
			public Piece Piece;

			public TMP_Text Caption;

			public Transform Badge;

			public Image BadgeImage;

			public TMP_Text Letter;

			public RawImage Still;

			public bool Done;

			public int Frame;
		}

		private const float Pad = 4f;

		private const float Badge = 20f;

		private static readonly Dictionary<BuildUiPieceButton, Look> looks = new Dictionary<BuildUiPieceButton, Look>();

		private static readonly List<BuildUiPieceButton> pending = new List<BuildUiPieceButton>();

		internal static readonly Stopwatch SetupClock = new Stopwatch();

		internal static readonly Stopwatch FillClock = new Stopwatch();

		internal static int SetUp;

		internal static int Filled;

		private static readonly Color Selected = new Color(1f, 0.62f, 0.22f);

		private const double BudgetMs = 2.0;

		private const double FillBudgetMs = 1.5;

		private static readonly Stopwatch clock = new Stopwatch();

		private static float nextScan;

		private static readonly Vector3[] corners = (Vector3[])(object)new Vector3[4];

		internal static int Pending => pending.Count;

		internal static string Name(Piece piece)
		{
			Localization instance = Localization.instance;
			string name = piece.m_name;
			string text = instance.Localize(name);
			if (!text.StartsWith("["))
			{
				return text;
			}
			int num = name.Length;
			while (num > 0 && char.IsDigit(name[num - 1]))
			{
				num--;
			}
			if (num < name.Length)
			{
				string text2 = name.Substring(0, num).TrimEnd('_');
				string text3 = name.Substring(num);
				string[] array = new string[3] { "26", "45", "67" };
				foreach (string text4 in array)
				{
					if (!(text4 != text3))
					{
						continue;
					}
					string[] array2 = new string[2]
					{
						text2 + text4,
						text2 + "_" + text4
					};
					foreach (string text5 in array2)
					{
						string text6 = instance.Localize(text5);
						if (!text6.StartsWith("[") && text6.Contains(text4))
						{
							return text6.Replace(text4, text3);
						}
					}
				}
			}
			string text7 = Utils.GetPrefabName(((Component)piece).gameObject).Replace('_', ' ').Trim();
			if (text7.Length <= 0)
			{
				return text;
			}
			return char.ToUpperInvariant(text7[0]) + text7.Substring(1);
		}

		internal static void Decorate(BuildUiPieceButton button)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_0062: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: 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_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: 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_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: 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)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			Piece piece = button.Piece;
			if ((Object)(object)piece == (Object)null)
			{
				return;
			}
			SetupClock.Start();
			try
			{
				float num = Plugin.CellSizes[Plugin.CellStep.Value];
				RectTransform val = (RectTransform)((Component)button.m_icon).transform;
				if ((Object)(object)((Component)button).transform.parent == (Object)(object)Window.Ui?.m_specialButtonsContainer)
				{
					val.anchorMin = Vector2.zero;
					val.anchorMax = Vector2.one;
					val.pivot = new Vector2(0.5f, 0.5f);
					val.offsetMin = new Vector2(6f, 6f);
					val.offsetMax = new Vector2(-6f, -6f);
					return;
				}
				if ((Object)(object)((Transform)val).parent == (Object)(object)((Component)button).transform)
				{
					Vector2 anchorMin = (val.anchorMax = new Vector2(0.5f, 1f));
					val.anchorMin = anchorMin;
					val.pivot = new Vector2(0.5f, 1f);
					val.sizeDelta = new Vector2(num - 8f, num - 8f);
					val.anchoredPosition = new Vector2(0f, -4f);
				}
				Button component = ((Component)button).GetComponent<Button>();
				if ((Object)(object)component != (Object)null)
				{
					ColorBlock colors = ((Selectable)component).colors;
					if (((ColorBlock)(ref colors)).selectedColor != Selected)
					{
						ColorBlock colors2 = ((Selectable)component).colors;
						((ColorBlock)(ref colors2)).highlightedColor = new Color(1f, 0.78f, 0.5f);
						((ColorBlock)(ref colors2)).selectedColor = Selected;
						((ColorBlock)(ref colors2)).pressedColor = new Color(1f, 0.55f, 0.15f);
						((Selectable)component).colors = colors2;
					}
				}
				if (!looks.TryGetValue(button, out var value))
				{
					value = (looks[button] = new Look());
				}
				if ((Object)(object)value.Piece != (Object)(object)piece)
				{
					if ((Object)(object)value.Caption != (Object)null)
					{
						((Component)value.Caption).gameObject.SetActive(false);
					}
					if ((Object)(object)value.Badge != (Object)null)
					{
						((Component)value.Badge).gameObject.SetActive(false);
					}
					if ((Object)(object)value.Still != (Object)null)
					{
						value.Still.texture = null;
						Show(button, value.Still);
					}
				}
				value.Piece = piece;
				value.Done = false;
				value.Frame = Time.frameCount;
				if (!pending.Contains(button))
				{
					pending.Add(button);
				}
				SetUp++;
			}
			finally
			{
				SetupClock.Stop();
			}
		}

		private static void Fill(BuildUiPieceButton button, Look look)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: 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_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Expected O, but got Unknown
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0301: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Unknown result type (might be due to invalid IL or missing references)
			//IL_037b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0374: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04df: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0501: Unknown result type (might be due to invalid IL or missing references)
			//IL_0503: Unknown result type (might be due to invalid IL or missing references)
			//IL_050e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0483: Unknown result type (might be due to invalid IL or missing references)
			//IL_0488: Unknown result type (might be due to invalid IL or missing references)
			//IL_049a: Unknown result type (might be due to invalid IL or missing references)
			Piece piece = look.Piece;
			float num = Plugin.CellSizes[Plugin.CellStep.Value];
			bool value = Plugin.Captions.Value;
			RectTransform val = (RectTransform)((Component)button.m_icon).transform;
			bool flag = piece.m_repairPiece || piece.m_removePiece;
			bool flag2 = value && !flag;
			TMP_Text val2 = look.Caption;
			if (flag2 && (Object)(object)val2 == (Object)null)
			{
				val2 = Window.Text(((Component)button).transform, "BuildMenu caption", 13f, (TextAlignmentOptions)258, new Color(0.93f, 0.9f, 0.84f));
				val2.textWrappingMode = (TextWrappingModes)1;
				val2.overflowMode = (TextOverflowModes)1;
				val2.lineSpacing = -18f;
				RectTransform val3 = (RectTransform)val2.transform;
				val3.anchorMin = new Vector2(0f, 0f);
				val3.anchorMax = new Vector2(1f, 0f);
				val3.pivot = new Vector2(0.5f, 0f);
				val3.sizeDelta = new Vector2(-4f, 32f);
				val3.anchoredPosition = new Vector2(0f, 2f);
				look.Caption = val2;
			}
			if ((Object)(object)val2 != (Object)null)
			{
				if (flag2)
				{
					float num2 = ((num >= 84f) ? 14f : 12.5f);
					if (val2.fontSize != num2)
					{
						val2.fontSize = num2;
					}
					string text = Name(piece);
					if (val2.text != text)
					{
						val2.text = text;
					}
				}
				if (((Component)val2).gameObject.activeSelf != flag2)
				{
					((Component)val2).gameObject.SetActive(flag2);
				}
			}
			Sub sub = (flag ? null : Sorting.Main(piece));
			bool flag3 = Plugin.Badges.Value && sub != null;
			if (flag3 && (Object)(object)look.Badge == (Object)null)
			{
				GameObject val4 = new GameObject("BuildMenu badge", new Type[1] { typeof(RectTransform) });
				val4.transform.SetParent(((Component)button).transform, false);
				RectTransform val5 = (RectTransform)val4.transform;
				Vector2 val6 = default(Vector2);
				((Vector2)(ref val6))..ctor(0f, 1f);
				val5.pivot = val6;
				Vector2 anchorMin = (val5.anchorMax = val6);
				val5.anchorMin = anchorMin;
				val5.anchoredPosition = new Vector2(3f, -3f);
				val5.sizeDelta = new Vector2(20f, 20f);
				look.BadgeImage = val4.AddComponent<Image>();
				((Graphic)look.BadgeImage).raycastTarget = false;
				look.Letter = Window.Text(val4.transform, "letter", 13f, (TextAlignmentOptions)514, new Color(1f, 0.85f, 0.55f));
				look.Letter.fontStyle = (FontStyles)1;
				RectTransform val8 = (RectTransform)look.Letter.transform;
				val8.anchorMin = Vector2.zero;
				val8.anchorMax = Vector2.one;
				anchorMin = (val8.offsetMax = Vector2.zero);
				val8.offsetMin = anchorMin;
				look.Badge = val4.transform;
			}
			if ((Object)(object)look.Badge != (Object)null)
			{
				if (flag3)
				{
					Sprite val9 = Icons.For(sub.Key);
					look.BadgeImage.sprite = val9;
					((Graphic)look.BadgeImage).color = (Color)(((Object)(object)val9 != (Object)null) ? Color.white : new Color(0f, 0f, 0f, 0.55f));
					bool flag4 = (Object)(object)val9 == (Object)null;
					if (((Component)look.Letter).gameObject.activeSelf != flag4)
					{
						((Component)look.Letter).gameObject.SetActive(flag4);
					}
					if (flag4)
					{
						string text2 = sub.Name(Plugin.Russian).Substring(0, 1).ToUpperInvariant();
						if (look.Letter.text != text2)
						{
							look.Letter.text = text2;
						}
					}
					if (look.Badge.GetSiblingIndex() != ((Component)button).transform.childCount - 1)
					{
						look.Badge.SetAsLastSibling();
					}
				}
				if (((Component)look.Badge).gameObject.activeSelf != flag3)
				{
					((Component)look.Badge).gameObject.SetActive(flag3);
				}
			}
			RawImage val10 = look.Still;
			bool flag5 = Plugin.GridModels.Value && sub != null;
			if (flag5 && (Object)(object)val10 == (Object)null)
			{
				GameObject val11 = new GameObject("BuildMenu still", new Type[1] { typeof(RectTransform) });
				val11.transform.SetParent(((Component)button).transform, false);
				val10 = val11.AddComponent<RawImage>();
				((Graphic)val10).raycastTarget = false;
				val11.transform.SetSiblingIndex(((Transform)val).GetSiblingIndex() + 1);
				look.Still = val10;
			}
			if ((Object)(object)val10 != (Object)null)
			{
				RectTransform val12 = (RectTransform)((Component)val10).transform;
				val12.anchorMin = val.anchorMin;
				val12.anchorMax = val.anchorMax;
				val12.pivot = val.pivot;
				val12.sizeDelta = val.sizeDelta;
				val12.anchoredPosition = val.anchoredPosition;
				if (((Component)val10).gameObject.activeSelf != flag5)
				{
					((Component)val10).gameObject.SetActive(flag5);
				}
				val10.texture = (flag5 ? Preview.Cached(piece) : null);
				nextScan = 0f;
			}
			Show(button, val10);
			look.Done = true;
			Filled++;
		}

		private static void Show(BuildUiPieceButton button, RawImage still)
		{
			bool flag = (Object)(object)still != (Object)null && ((Component)still).gameObject.activeSelf && (Object)(object)still.texture != (Object)null;
			if ((Object)(object)still != (Object)null)
			{
				((Behaviour)still).enabled = flag;
			}
			((Behaviour)button.m_icon).enabled = !flag;
		}

		internal static void Update(List<BuildUiPieceButton> buttons, RectTransform viewport)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Expected O, but got Unknown
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			if (pending.Count > 0)
			{
				FillPending(viewport);
			}
			if (!Plugin.GridModels.Value || Time.unscaledTime < nextScan)
			{
				return;
			}
			Rect val = WorldRect(viewport);
			bool flag = false;
			int num = 0;
			clock.Restart();
			foreach (BuildUiPieceButton button in buttons)
			{
				if ((Object)(object)button == (Object)null || (Object)(object)button.Piece == (Object)null || !((Component)button).gameObject.activeInHierarchy || !looks.TryGetValue(button, out var value) || !value.Done)
				{
					continue;
				}
				RawImage still = value.Still;
				if ((Object)(object)still == (Object)null || !((Component)still).gameObject.activeSelf)
				{
					continue;
				}
				if ((Object)(object)still.texture == (Object)null && ((Rect)(ref val)).Overlaps(WorldRect((RectTransform)((Component)button).transform)))
				{
					if (num == 0 || clock.Elapsed.TotalMilliseconds < 2.0)
					{
						still.texture = Preview.Still(button.Piece, 128);
						num++;
					}
					else
					{
						flag = true;
					}
				}
				Show(button, still);
				((Graphic)still).color = (Color)((((Graphic)button.m_icon).color == Color.white) ? Color.white : new Color(0.7f, 0.7f, 0.7f, 0.8f));
			}
			nextScan = Time.unscaledTime + (flag ? 0f : 0.15f);
		}

		private static void FillPending(RectTransform viewport)
		{
			//IL_0011: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			FillClock.Start();
			try
			{
				int frameCount = Time.frameCount;
				Rect val = WorldRect(viewport);
				for (int num = pending.Count - 1; num >= 0; num--)
				{
					BuildUiPieceButton val2 = pending[num];
					if ((Object)(object)val2 == (Object)null || !looks.TryGetValue(val2, out var value) || value.Done || (Object)(object)val2.Piece == (Object)null)
					{
						pending.RemoveAt(num);
					}
					else if (value.Frame < frameCount && ((Component)val2).gameObject.activeInHierarchy && ((Rect)(ref val)).Overlaps(WorldRect((RectTransform)((Component)val2).transform)))
					{
						Fill(val2, value);
						pending.RemoveAt(num);
					}
				}
				clock.Restart();
				int num2 = pending.Count - 1;
				while (num2 >= 0 && clock.Elapsed.TotalMilliseconds < 1.5)
				{
					BuildUiPieceButton val3 = pending[num2];
					if ((Object)(object)val3 == (Object)null || !looks.TryGetValue(val3, out var value2) || value2.Done || (Object)(object)val3.Piece == (Object)null)
					{
						pending.RemoveAt(num2);
					}
					else if (value2.Frame < frameCount)
					{
						Fill(val3, value2);
						pending.RemoveAt(num2);
					}
					num2--;
				}
			}
			finally
			{
				FillClock.Stop();
			}
		}

		private static Rect WorldRect(RectTransform rt)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			rt.GetWorldCorners(corners);
			return Rect.MinMaxRect(corners[0].x, corners[0].y, corners[2].x, corners[2].y);
		}
	}
	[HarmonyPatch(typeof(BuildUiPieceButton), "Setup")]
	internal static class CellHook
	{
		private static void Postfix(BuildUiPieceButton __instance)
		{
			if (!Plugin.Enabled.Value)
			{
				return;
			}
			try
			{
				Cells.Decorate(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Warn("cell: " + ex.Message);
			}
		}
	}
	internal sealed class PieceFacts
	{
		public string Tool = "";

		public string Prefab = "";

		public string Token = "";

		public string NameRu = "";

		public string NameEn = "";

		public string Category = "";

		public string Usage = "";

		public string Station = "";

		public string Material = "";

		public string Comfort = "";

		public string Components = "";

		public string Resources = "";

		public int CategoryId;

		public float SizeX;

		public float SizeY;

		public float SizeZ;

		public float SnapX;

		public float SnapY;

		public float SnapZ;

		public int Snaps;

		public bool Ground;

		public bool Water;

		public bool Upgrade;

		public bool Enabled = true;

		private HashSet<string> components;

		private HashSet<string> tags;

		private string words;

		private static readonly string[] Columns = new string[20]
		{
			"tool", "prefab", "token", "name_ru", "name_en", "category", "category_id", "usage", "station", "material",
			"comfort", "components", "resources", "size", "snaps", "snap_spread", "ground", "water", "upgrade", "enabled"
		};

		public string Words => words ?? (words = (Prefab + " " + NameEn + " " + NameRu).ToLowerInvariant());

		public static string Header => string.Join("\t", Columns);

		public bool Has(string component)
		{
			return (components ?? (components = Split(Components))).Contains(component);
		}

		public bool Tagged(string tag)
		{
			return (tags ?? (tags = Split(Usage))).Contains(tag);
		}

		private static HashSet<string> Split(string s)
		{
			return new HashSet<string>(s.Split(new char[2] { '|', ' ' }, StringSplitOptions.RemoveEmptyEntries));
		}

		private static string F(float v)
		{
			return v.ToString("0.##", CultureInfo.InvariantCulture);
		}

		private static string V(float x, float y, float z)
		{
			return F(x) + " " + F(y) + " " + F(z);
		}

		private static string Clean(string s)
		{
			return (s ?? "").Replace('\t', ' ').Replace('\n', ' ').Replace('\r', ' ');
		}

		public string ToTsv()
		{
			return string.Join("\t", new string[20]
			{
				Tool,
				Prefab,
				Token,
				NameRu,
				NameEn,
				Category,
				CategoryId.ToString(),
				Usage,
				Station,
				Material,
				Comfort,
				Components,
				Resources,
				V(SizeX, SizeY, SizeZ),
				Snaps.ToString(),
				V(SnapX, SnapY, SnapZ),
				Ground ? "1" : "0",
				Water ? "1" : "0",
				Upgrade ? "1" : "0",
				Enabled ? "1" : "0"
			}.Select(Clean));
		}

		public static PieceFacts FromTsv(string line, string header)
		{
			string[] names = header.Split('\t');
			string[] cells = line.Split('\t');
			float[] array = Vec("size");
			float[] array2 = Vec("snap_spread");
			int.TryParse(Cell("category_id"), out var result);
			int.TryParse(Cell("snaps"), out var result2);
			return new PieceFacts
			{
				Tool = Cell("tool"),
				Prefab = Cell("prefab"),
				Token = Cell("token"),
				NameRu = Cell("name_ru"),
				NameEn = Cell("name_en"),
				Category = Cell("category"),
				CategoryId = result,
				Usage = Cell("usage"),
				Station = Cell("station"),
				Material = Cell("material"),
				Comfort = Cell("comfort"),
				Components = Cell("components"),
				Resources = Cell("resources"),
				SizeX = array[0],
				SizeY = array[1],
				SizeZ = array[2],
				Snaps = result2,
				SnapX = array2[0],
				SnapY = array2[1],
				SnapZ = array2[2],
				Ground = (Cell("ground") == "1"),
				Water = (Cell("water") == "1"),
				Upgrade = (Cell("upgrade") == "1"),
				Enabled = (Cell("enabled") != "0")
			};
			string Cell(string name)
			{
				int num = Array.IndexOf(names, name);
				if (num < 0 || num >= cells.Length)
				{
					return "";
				}
				return cells[num];
			}
			float[] Vec(string name)
			{
				string[] array3 = Cell(name).Split(' ');
				float[] array4 = new float[3];
				for (int i = 0; i < 3 && i < array3.Length; i++)
				{
					float.TryParse(array3[i], NumberStyles.Float, CultureInfo.InvariantCulture, out array4[i]);
				}
				return array4;
			}
		}
	}
	internal static class FactsReader
	{
		private static readonly HashSet<string> Noise = new HashSet<string>
		{
			"Piece", "ZNetView", "WearNTear", "ZSyncTransform", "ZSyncAnimation", "LightFlicker", "LightLod", "StaticTarget", "TerrainModifier", "EffectFade",
			"SnapToGround", "LodFadeInOut", "TimedDestruction", "InstanceRenderer", "Odin", "PieceBuildable"
		};

		public static PieceFacts Read(Piece piece, string tool, Func<string, string> localize)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected I4, but got Unknown
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: 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)
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_040d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0412: Unknown result type (might be due to invalid IL or missing references)
			//IL_0416: Unknown result type (might be due to invalid IL or missing references)
			//IL_0428: Unknown result type (might be due to invalid IL or missing references)
			//IL_042d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0431: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Unknown result type (might be due to invalid IL or missing references)
			//IL_032a: Unknown result type (might be due to invalid IL or missing references)
			//IL_032f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_033d: Unknown result type (might be due to invalid IL or missing references)
			//IL_034b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0352: Unknown result type (might be due to invalid IL or missing references)
			//IL_047f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0484: Unknown result type (might be due to invalid IL or missing references)
			//IL_0378: Unknown result type (might be due to invalid IL or missing references)
			//IL_037d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0382: Unknown result type (might be due to invalid IL or missing references)
			//IL_0387: Unknown result type (might be due to invalid IL or missing references)
			//IL_038c: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0399: Unknown result type (might be due to invalid IL or missing references)
			//IL_039b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a0: Unknown result type (might be due to invalid IL or missing references)
			GameObject gameObject = ((Component)piece).gameObject;
			PieceFacts pieceFacts = new PieceFacts
			{
				Tool = tool,
				Prefab = ((Object)gameObject).name,
				Token = piece.m_name,
				NameRu = localize(piece.m_name),
				CategoryId = (int)piece.m_category,
				Category = ((object)Unsafe.As<PieceCategory, PieceCategory>(ref piece.m_category)/*cast due to .constrained prefix*/).ToString(),
				Usage = string.Join("|", from UsageTagFlags t in Enum.GetValues(typeof(UsageTagFlags))
					where (piece.m_usage & t) > 0
					select t),
				Station = (((Object)(object)piece.m_craftingStation != (Object)null) ? localize(piece.m_craftingStation.m_name) : ""),
				Comfort = (((int)piece.m_comfortGroup != 0 || piece.m_comfort > 0) ? $"{piece.m_comfortGroup} {piece.m_comfort}" : ""),
				Resources = string.Join(", ", from r in piece.m_resources
					where (Object)(object)r.m_resItem != (Object)null
					select $"{localize(r.m_resItem.m_itemData.m_shared.m_name)}×{r.m_amount}"),
				Ground = (piece.m_groundPiece || piece.m_groundOnly),
				Water = piece.m_waterPiece,
				Upgrade = piece.m_isUpgrade,
				Enabled = piece.m_enabled
			};
			WearNTear component = gameObject.GetComponent<WearNTear>();
			if ((Object)(object)component != (Object)null)
			{
				pieceFacts.Material = ((object)Unsafe.As<MaterialType, MaterialType>(ref component.m_materialType)/*cast due to .constrained prefix*/).ToString();
			}
			pieceFacts.Components = string.Join("|", from n in (from c in gameObject.GetComponentsInChildren<MonoBehaviour>(true)
					where (Object)(object)c != (Object)null
					select ((object)c).GetType().Name into n
					where !Noise.Contains(n)
					select n).Distinct()
				orderby n
				select n);
			Matrix4x4 worldToLocalMatrix = gameObject.transform.worldToLocalMatrix;
			Bounds? val = null;
			MeshFilter[] componentsInChildren = gameObject.GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val2 in componentsInChildren)
			{
				Mesh sharedMesh = val2.sharedMesh;
				if ((Object)(object)sharedMesh == (Object)null || (Object)(object)((Component)val2).GetComponent<MeshRenderer>() == (Object)null)
				{
					continue;
				}
				Matrix4x4 val3 = worldToLocalMatrix * ((Component)val2).transform.localToWorldMatrix;
				Bounds bounds = sharedMesh.bounds;
				for (int num2 = 0; num2 < 8; num2++)
				{
					Vector3 val4 = ((Matrix4x4)(ref val3)).MultiplyPoint3x4(((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3((float)(((num2 & 1) != 0) ? 1 : (-1)), (float)(((num2 & 2) != 0) ? 1 : (-1)), (float)(((num2 & 4) != 0) ? 1 : (-1)))));
					if (!val.HasValue)
					{
						val = new Bounds(val4, Vector3.zero);
						continue;
					}
					Bounds value = val.Value;
					((Bounds)(ref value)).Encapsulate(val4);
					val = value;
				}
			}
			if (val.HasValue)
			{
				Bounds value2 = val.Value;
				pieceFacts.SizeX = ((Bounds)(ref value2)).size.x;
				value2 = val.Value;
				pieceFacts.SizeY = ((Bounds)(ref value2)).size.y;
				value2 = val.Value;
				pieceFacts.SizeZ = ((Bounds)(ref value2)).size.z;
			}
			List<Vector3> list = new List<Vector3>();
			Transform[] componentsInChildren2 = gameObject.GetComponentsInChildren<Transform>(true);
			foreach (Transform val5 in componentsInChildren2)
			{
				if ((Object)(object)val5 != (Object)(object)gameObject.transform && ((Component)val5).CompareTag("snappoint"))
				{
					list.Add(((Matrix4x4)(ref worldToLocalMatrix)).MultiplyPoint3x4(val5.position));
				}
			}
			pieceFacts.Snaps = list.Count;
			if (list.Count > 0)
			{
				pieceFacts.SnapX = list.Max((Vector3 p) => p.x) - list.Min((Vector3 p) => p.x);
				pieceFacts.SnapY = list.Max((Vector3 p) => p.y) - list.Min((Vector3 p) => p.y);
				pieceFacts.SnapZ = list.Max((Vector3 p) => p.z) - list.Min((Vector3 p) => p.z);
			}
			return pieceFacts;
		}
	}
	internal static class Glyphs
	{
		private static Sprite magnifier;

		private static Sprite clock;

		public static Sprite Magnifier()
		{
			return magnifier ?? (magnifier = Draw("magnifier", delegate(int x, int y)
			{
				float num = Dist(x, y, 13f, 19f);
				bool num2 = num >= 7.5f && num <= 11f;
				float num3 = Mathf.Clamp01(((float)(x - 20) * 1f + (float)(12 - y) * 1f) / 2f / 8f);
				float cx = 20f + num3 * 8f;
				float cy = 12f - num3 * 8f;
				bool flag = Dist(x, y, cx, cy) <= 2.6f && x >= 19;
				return num2 || flag;
			}));
		}

		public static Sprite Clock()
		{
			return clock ?? (clock = Draw("clock", delegate(int x, int y)
			{
				float num = Dist(x, y, 16f, 16f);
				bool num2 = num >= 11f && num <= 14f;
				bool flag = (float)Math.Abs(x - 16) <= 1.4f && y >= 16 && y <= 24;
				bool flag2 = (float)Math.Abs(y - 16) <= 1.4f && x >= 16 && x <= 23;
				return num2 || flag || flag2;
			}));
		}

		private static float Dist(float x, float y, float cx, float cy)
		{
			return Mathf.Sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy));
		}

		private static Sprite Draw(string name, Func<int, int, bool> on)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false)
			{
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1,
				name = "BuildMenu " + name
			};
			Color32[] array = (Color32[])(object)new Color32[1024];
			for (int i = 0; i < 32; i++)
			{
				for (int j = 0; j < 32; j++)
				{
					array[i * 32 + j] = (on(j, i) ? new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue) : new Color32((byte)0, (byte)0, (byte)0, (byte)0));
				}
			}
			val.SetPixels32(array);
			val.Apply();
			Sprite obj = Sprite.Create(val, new Rect(0f, 0f, 32f, 32f), new Vector2(0.5f, 0.5f), 100f);
			((Object)obj).name = name;
			return obj;
		}
	}
	internal static class Ground
	{
		private static PieceTable table;

		private static bool ground;

		internal static bool InHand()
		{
			Player localPlayer = Player.m_localPlayer;
			PieceTable val = (((Object)(object)localPlayer != (Object)null) ? localPlayer.m_buildPieces : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if ((Object)(object)val == (Object)(object)table)
			{
				return ground;
			}
			table = val;
			int num = 0;
			int num2 = 0;
			foreach (GameObject piece in val.m_pieces)
			{
				if (!((Object)(object)piece == (Object)null))
				{
					num++;
					if ((Object)(object)piece.GetComponent<TerrainOp>() != (Object)null || (Object)(object)piece.GetComponent<TerrainModifier>() != (Object)null || (Object)(object)piece.GetComponent<Plant>() != (Object)null)
					{
						num2++;
					}
				}
			}
			ground = num > 0 && num2 * 2 >= num;
			return ground;
		}
	}
	[HarmonyPatch(typeof(BuildUi), "Awake")]
	internal static class TreeTab
	{
		private static void Postfix(BuildUi __instance)
		{
			try
			{
				if (Plugin.Enabled.Value && __instance.m_pieceLists.Count != 0)
				{
					__instance.m_pieceLists[0] = (IPieceList)(object)new TreeList(__instance.m_pieceLists[0].DisplayName);
					Window.Attach(__instance);
				}
			}
			catch (Exception ex)
			{
				Plugin.Warn("build window: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(BuildUi), "OpenBuildMenu")]
	internal static class OpenTiming
	{
		private static readonly Stopwatch clock = new Stopwatch();

		internal static int Buttons;

		private static void Prefix()
		{
			if (Plugin.Enabled.Value)
			{
				Cells.SetupClock.Reset();
				Cells.FillClock.Reset();
				Cells.SetUp = 0;
				Cells.Filled = 0;
				Tags.Clock.Reset();
				TreeList.Clock.Reset();
				Sorting.Read = 0;
				clock.Restart();
			}
		}

		private static void Postfix(BuildUi __instance)
		{
			if (Plugin.Enabled.Value)
			{
				clock.Stop();
				Buttons = __instance.m_pieceButtons.Count;
				Window.Opened(clock.Elapsed.TotalMilliseconds, $"{Buttons} cells ({Cells.SetUp} set up, {Cells.SetupClock.Elapsed.TotalMilliseconds:0} ms), tags {Tags.Clock.Elapsed.TotalMilliseconds:0} ms, " + $"tree {TreeList.Clock.Elapsed.TotalMilliseconds:0} ms ({Sorting.Read} pieces read)");
			}
		}
	}
	[HarmonyPatch(typeof(BuildUi), "UpdateTagButtons")]
	internal static class ShowAllCount
	{
		private static void Postfix(BuildUi __instance)
		{
			if (!Plugin.Enabled.Value)
			{
				return;
			}
			try
			{
				Tags.Decorate(__instance.m_showAllTagsButton);
			}
			catch (Exception ex)
			{
				Plugin.Warn("show all: " + ex.Message);
			}
		}
	}
	[HarmonyPatch(typeof(BuildUi), "UpdatePieceButtons")]
	internal static class GridWidth
	{
		private static void Postfix()
		{
			if (Plugin.Enabled.Value)
			{
				Window.ApplyGrid();
			}
		}
	}
	[HarmonyPatch(typeof(BuildUi), "UpdateSearch")]
	internal static class MaterialFilter
	{
		private static void Postfix(BuildUi __instance)
		{
			if (!Plugin.Enabled.Value || !Materials.Active)
			{
				return;
			}
			bool flag = false;
			foreach (BuildUiPieceButton pieceButton in __instance.m_pieceButtons)
			{
				if ((Object)(object)pieceButton != (Object)null && ((Component)pieceButton).gameObject.activeSelf && !Materials.Pass(pieceButton.Piece))
				{
					((Component)pieceButton).gameObject.SetActive(false);
					flag = true;
				}
			}
			if (flag)
			{
				__instance.ConfigureButtonNavigation();
			}
		}
	}
	[HarmonyPatch(typeof(BuildUi), "SelectPieceList")]
	internal static class TabTip
	{
		private static void Postfix()
		{
			Window.HideTip();
		}
	}
	[HarmonyPatch(typeof(BuildUi), "OnSelectTag")]
	internal static class GroupToggle
	{
		private static bool rebuilding;

		private static void Prefix(BuildUi __instance, int tagId)
		{
			if (rebuilding || !(__instance.m_pieceLists[__instance.m_currentPieceList] is TreeList) || !TreeList.IsGroup(tagId))
			{
				return;
			}
			Group obj = TreeList.GroupOf(tagId);
			if (obj != null)
			{
				if (__instance.m_currentTagId == tagId && TreeList.Open.Contains(obj.Key))
				{
					TreeList.Open.Remove(obj.Key);
					return;
				}
				TreeList.Open.Clear();
				TreeList.Open.Add(obj.Key);
			}
		}

		private static void Postfix(BuildUi __instance, int tagId)
		{
			if (!rebuilding)
			{
				Window.HideTip();
			}
			if (rebuilding || !(__instance.m_pieceLists[__instance.m_currentPieceList] is TreeList) || !TreeList.IsGroup(tagId))
			{
				return;
			}
			rebuilding = true;
			try
			{
				__instance.UpdateTagButtons(true);
			}
			finally
			{
				rebuilding = false;
			}
		}
	}
	internal static class Icons
	{
		private static readonly Dictionary<string, Sprite> cache = new Dictionary<string, Sprite>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, string> Samples = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "crafting", "piece_workbench" },
			{ "stations", "piece_workbench" },
			{ "upgrades", "forge_ext1" },
			{ "smelting", "smelter" },
			{ "kitchen", "piece_cauldron" },
			{ "building", "woodwall" },
			{ "floor", "wood_floor" },
			{ "wall", "woodwall" },
			{ "roof", "wood_roof" },
			{ "stairs", "wood_stair" },
			{ "beam", "wood_beam" },
			{ "door", "wood_door" },
			{ "window", "wood_window" },
			{ "arch", "stone_arch" },
			{ "fence", "wood_fence" },
			{ "defense", "piece_sharpstakes" },
			{ "other", "blackmarble_tip" },
			{ "interior", "piece_chair02" },
			{ "furniture", "piece_chair02" },
			{ "light", "piece_groundtorch_wood" },
			{ "decor", "piece_banner01" },
			{ "travel", "Karve" },
			{ "transport", "Cart" },
			{ "portal", "portal_wood" },
			{ "storage", "piece_chest_wood" },
			{ "misc", "piece_ArcheryTarget" }
		};

		private static IEnumerable<string> Folders()
		{
			yield return Path.Combine(Plugin.Folder, "icons");
			yield return Path.Combine(Path.Combine(Plugin.Folder, "BuildMenu"), "icons");
		}

		public static Sprite Get(string key)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(key))
			{
				return null;
			}
			if (cache.TryGetValue(key, out var value))
			{
				return value;
			}
			value = null;
			foreach (string item in Folders())
			{
				string text = Path.Combine(item, key + ".png");
				if (!File.Exists(text))
				{
					continue;
				}
				try
				{
					Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true)
					{
						name = "BuildMenu icon " + key
					};
					if (!ImageConversion.LoadImage(val, File.ReadAllBytes(text)))
					{
						continue;
					}
					((Texture)val).wrapMode = (TextureWrapMode)1;
					((Texture)val).filterMode = (FilterMode)2;
					value = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
					break;
				}
				catch (Exception ex)
				{
					Plugin.Warn("icon " + text + ": " + ex.Message);
				}
			}
			cache[key] = value;
			return value;
		}

		public static void Reload()
		{
			cache.Clear();
		}

		public static Sprite For(string key)
		{
			Sprite val = Get(key);
			if ((Object)(object)val == (Object)null && key != null && key.StartsWith("tool_"))
			{
				return Merge.ToolIcon(key);
			}
			if ((Object)(object)val == (Object)null && key == "tools")
			{
				return Merge.ToolIcon("tool_Hammer");
			}
			if ((Object)(object)val != (Object)null || key == null || !Samples.TryGetValue(key, out var value) || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return val;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(value);
			Piece val2 = ((prefab != null) ? prefab.GetComponent<Piece>() : null);
			if (!((Object)(object)val2 != (Object)null))
			{
				return null;
			}
			return val2.m_icon;
		}
	}
	internal static class Materials
	{
		public enum Family
		{
			None,
			Wood,
			Stone,
			Metal
		}

		public static Family ChosenFamily;

		public static string ChosenItem;

		private static readonly Dictionary<string, (string item, Family family)> cache = new Dictionary<string, (string, Family)>();

		public static bool Active
		{
			get
			{
				if (ChosenFamily == Family.None)
				{
					return ChosenItem != null;
				}
				return true;
			}
		}

		public static event Action Changed;

		public static string MainItem(Piece piece)
		{
			return Info(piece).item;
		}

		public static Family FamilyOf(Piece piece)
		{
			return Info(piece).family;
		}

		private static (string item, Family family) Info(Piece piece)
		{
			//IL_00a0: 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)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected I4, but got Unknown
			string name = ((Object)((Component)piece).gameObject).name;
			if (cache.TryGetValue(name, out (string, Family) value))
			{
				return value;
			}
			Requirement val = null;
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val2 in resources)
			{
				if (val2 != null && (Object)(object)val2.m_resItem != (Object)null && (val == null || val2.m_amount > val.m_amount))
				{
					val = val2;
				}
			}
			string text = ((val != null) ? ((Object)((Component)val.m_resItem).gameObject).name : null);
			Family family = ByName(text);
			if (family == Family.None)
			{
				WearNTear component = ((Component)piece).GetComponent<WearNTear>();
				if ((Object)(object)component != (Object)null)
				{
					MaterialType materialType = component.m_materialType;
					switch ((int)materialType)
					{
					case 0:
					case 3:
					case 8:
						family = Family.Wood;
						break;
					case 1:
					case 4:
					case 5:
						family = Family.Stone;
						break;
					case 2:
						family = Family.Metal;
						break;
					}
				}
			}
			return cache[name] = (text, family);
		}

		private static Family ByName(string item)
		{
			if (item == null)
			{
				return Family.None;
			}
			string w = item.ToLowerInvariant();
			if (Any(new string[5] { "wood", "log", "bark", "timber", "plank" }))
			{
				return Family.Wood;
			}
			if (Any(new string[6] { "stone", "marble", "grausten", "flint", "obsidian", "crystal" }))
			{
				return Family.Stone;
			}
			if (Any(new string[9] { "copper", "bronze", "iron", "silver", "metal", "tin", "flametal", "nail", "chain" }))
			{
				return Family.Metal;
			}
			return Family.None;
			bool Any(params string[] parts)
			{
				return parts.Any(w.Contains);
			}
		}

		public static bool Pass(Piece piece)
		{
			if ((Object)(object)piece == (Object)null || piece.m_repairPiece || piece.m_removePiece || !Active)
			{
				return true;
			}
			if (ChosenItem != null)
			{
				return MainItem(piece) == ChosenItem;
			}
			return FamilyOf(piece) == ChosenFamily;
		}

		public static void Choose(Family family)
		{
			ChosenFamily = ((ChosenFamily != family || ChosenItem != null) ? family : Family.None);
			ChosenItem = null;
			Materials.Changed?.Invoke();
		}

		public static void Choose(string item)
		{
			ChosenItem = ((ChosenItem == item) ? null : item);
			ChosenFamily = Family.None;
			Materials.Changed?.Invoke();
		}

		public static List<(string item, Sprite icon, string name, int count)> InUse(PieceTable table)
		{
			List<(string, Sprite, string, int)> list = new List<(string, Sprite, string, int)>();
			if ((Object)(object)table == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return list;
			}
			foreach (IGrouping<string, Piece> item in from g in table.m_availablePieces.Where((Piece p) => (Object)(object)p != (Object)null && !p.m_repairPiece && !p.m_removePiece).GroupBy(MainItem)
				where g.Key != null
				orderby g.Count() descending
				select g)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.Key);
				ItemDrop val = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if (!((Object)(object)val == (Object)null))
				{
					list.Add((item.Key, val.m_itemData.GetIcon(), Localization.instance.Localize(val.m_itemData.m_shared.m_name), item.Count()));
				}
			}
			return list;
		}

		public static Sprite Icon(Family family)
		{
			string text = family switch
			{
				Family.Stone => "Stone", 
				Family.Wood => "Wood", 
				_ => "Iron", 
			};
			object obj;
			if (!((Object)(object)ObjectDB.instance != (Object)null))
			{
				obj = null;
			}
			else
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text);
				obj = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			}
			ItemDrop val = (ItemDrop)obj;
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return val.m_itemData.GetIcon();
		}

		public static string Name(Family family)
		{
			return family switch
			{
				Family.Wood => Plugin.T("Дерево: всё из дерева", "Wood: everything of wood"), 
				Family.Stone => Plugin.T("Камень: всё из камня", "Stone: everything of stone"), 
				Family.Metal => Plugin.T("Металл: всё из металлов", "Metal: everything of metal"), 
				_ => "", 
			};
		}
	}
	internal static class Merge
	{
		private sealed class Tool
		{
			public string Prefab;

			public string Name;

			public string Mod;

			public PieceTable Table;

			public ConfigEntry<bool> On;

			public Sub Sub;

			public readonly List<GameObject> Added = new List<GameObject>();
		}

		private static readonly HashSet<string> Vanilla = new HashSet<string> { "Hammer", "Hoe", "Cultivator", "Feaster" };

		private static readonly Dictionary<string, Tool> tools = new Dictionary<string, Tool>();

		private static readonly Dictionary<string, Tool> sourceOf = new Dictionary<string, Tool>();

		private static PieceTable hammer;

		private static bool dirty = true;

		public static void Apply()
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			GameObject itemPrefab = instance.GetItemPrefab("Hammer");
			PieceTable val = ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_buildPieces);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if ((Object)(object)val != (Object)(object)hammer)
			{
				hammer = val;
				dirty = true;
				foreach (Tool value in tools.Values)
				{
					value.Added.Clear();
				}
				sourceOf.Clear();
			}
			foreach (GameObject item in instance.m_items)
			{
				if ((Object)(object)item == (Object)null || Vanilla.Contains(((Object)item).name) || tools.ContainsKey(((Object)item).name))
				{
					continue;
				}
				ItemDrop component = item.GetComponent<ItemDrop>();
				PieceTable val2 = (((Object)(object)component != (Object)null) ? component.m_itemData.m_shared.m_buildPieces : null);
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)(object)hammer))
				{
					try
					{
						Discover(((Object)item).name, component, val2);
					}
					catch (Exception ex)
					{
						Plugin.Warn("merge " + ((Object)item).name + ": " + ex.Message);
					}
				}
			}
			if (!dirty)
			{
				return;
			}
			dirty = false;
			foreach (Tool value2 in tools.Values)
			{
				if (value2.On.Value)
				{
					foreach (GameObject piece in value2.Table.m_pieces)
					{
						Piece val3 = (((Object)(object)piece != (Object)null) ? piece.GetComponent<Piece>() : null);
						if (!((Object)(object)val3 == (Object)null) && !val3.m_repairPiece && !val3.m_removePiece && !hammer.m_pieces.Contains(piece))
						{
							hammer.m_pieces.Add(piece);
							value2.Added.Add(piece);
							sourceOf[((Object)piece).name] = value2;
						}
					}
				}
				else
				{
					if (value2.Added.Count <= 0)
					{
						continue;
					}
					foreach (GameObject item2 in value2.Added)
					{
						hammer.m_pieces.Remove(item2);
						if ((Object)(object)item2 != (Object)null)
						{
							sourceOf.Remove(((Object)item2).name);
						}
					}
					value2.Added.Clear();
				}
			}
			Sorting.Forget();
			Plugin.Log("merged tools: " + ((tools.Count == 0) ? "none found" : string.Join(", ", tools.Values.Select((Tool t) => string.Format("{0} {1} ({2} in the hammer)", t.Prefab, t.On.Value ? "on" : "off", t.Added.Count)))));
		}

		private static void Discover(string prefab, ItemDrop drop, PieceTable table)
		{
			int num = table.m_pieces.Count(delegate(GameObject go)
			{
				if ((Object)(object)go != (Object)null)
				{
					Piece component = go.GetComponent<Piece>();
					if (component != null && !component.m_repairPiece)
					{
						return !component.m_removePiece;
					}
				}
				return false;
			});
			Tool tool = new Tool
			{
				Prefab = prefab,
				Table = table,
				Name = Localization.instance.Localize(drop.m_itemData.m_shared.m_name),
				Mod = ModOf(prefab)
			};
			tool.On = Plugin.Cfg.Bind<bool>("Merge tools", prefab, false, $"Merge into our hammer: \"{tool.Name}\" from {tool.Mod}, {num} pieces. On - these pieces are built from the BuildMenu hammer " + "(the mod's very pieces: its station, resources and effects), in the tree also under Other tools. The tool itself keeps working." + ((prefab.IndexOf("hoe", StringComparison.OrdinalIgnoreCase) >= 0) ? " A terrain tool: its mod may expect it in hand, merging is not advised." : "") + "\n" + $"Объединить с нашим молотком: «{tool.Name}» из мода {tool.Mod}, построек: {num}. Включено — эти постройки доступны из молотка BuildMenu " + "(те же постройки мода: станция, ресурсы и эффекты как у него), в дереве — «Другие инструменты». Сам инструмент работает как прежде." + ((prefab.IndexOf("hoe", StringComparison.OrdinalIgnoreCase) >= 0) ? " Это инструмент рельефа: мод может ждать его в руке, объединять не советуем." : ""));
			tool.On.SettingChanged += delegate
			{
				dirty = true;
				try
				{
					if ((Object)(object)Player.m_localPlayer != (Object)null)
					{
						Player.m_localPlayer.UpdateAvailablePiecesList();
					}
				}
				catch (Exception ex)
				{
					Plugin.Warn("merge refresh: " + ex.Message);
				}
			};
			tool.Sub = Catalog.ToolSub("tool_" + prefab, tool.Name);
			tools[prefab] = tool;
			dirty = true;
		}

		private static string ModOf(string prefab)
		{
			try
			{
				Type type = AccessTools.TypeByName("Jotunn.Managers.ItemManager");
				object obj = type?.GetProperty("Instance")?.GetValue(null);
				object obj2 = ((obj == null) ? null : AccessTools.Method(type, "GetItem", new Type[1] { typeof(string) }, (Type[])null)?.Invoke(obj, new object[1] { prefab }));
				object obj3 = obj2?.GetType().GetProperty("SourceMod")?.GetValue(obj2);
				string text = obj3?.GetType().GetProperty("Name")?.GetValue(obj3) as string;
				if (!string.IsNullOrEmpty(text))
				{
					return text;
				}
			}
			catch
			{
			}
			return Plugin.T("(мод не определён)", "(unknown mod)");
		}

		public static Sub SubOf(Piece piece)
		{
			if (!((Object)(object)piece != (Object)null) || !sourceOf.TryGetValue(((Object)((Component)piece).gameObject).name, out var value))
			{
				return null;
			}
			return value.Sub;
		}

		public static string Source(Piece piece)
		{
			if (!((Object)(object)piece != (Object)null) || !sourceOf.TryGetValue(((Object)((Component)piece).gameObject).name, out var value))
			{
				return null;
			}
			return value.Mod + " (" + value.Name + ")";
		}

		public static Sprite ToolIcon(string subKey)
		{
			if (subKey == null || !subKey.StartsWith("tool_"))
			{
				return null;
			}
			object obj;
			if (!((Object)(object)ObjectDB.instance != (Object)null))
			{
				obj = null;
			}
			else
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(subKey.Substring(5));
				obj = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			}
			ItemDrop val = (ItemDrop)obj;
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return val.m_itemData.GetIcon();
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateAvailablePiecesList")]
	internal static class MergeHook
	{
		private static void Prefix()
		{
			if (!Plugin.Enabled.Value)
			{
				return;
			}
			try
			{
				Merge.Apply();
			}
			catch (Exception ex)
			{
				Plugin.Warn("merge: " + ex.Message);
			}
		}
	}
	[BepInPlugin("chistix.buildmenu", "BuildMenu", "0.3.13")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "chistix.buildmenu";

		public const string Version = "0.3.13";

		private static ManualLogSource log;

		internal static ConfigFile Cfg;

		internal static string Folder;

		internal static readonly int[] CellSizes = new int[7] { 56, 64, 72, 84, 96, 112, 128 };

		internal static readonly int[] Presets = new int[4] { 0, 2, 4, 6 };

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> Captions;

		internal static ConfigEntry<bool> Badges;

		internal static ConfigEntry<bool> Grid3D;

		internal static ConfigEntry<bool> Preview3D;

		internal static ConfigEntry<bool> GroundGrid3D;

		internal static ConfigEntry<bool> GroundPreview3D;

		internal static ConfigEntry<bool> ShowPrefab;

		internal static ConfigEntry<bool> DebugLog;

		internal static ConfigEntry<int> CellStep;

		internal static ConfigEntry<int> WindowSize;

		internal static readonly Vector2Int[] WindowSizes = (Vector2Int[])(object)new Vector2Int[3]
		{
			new Vector2Int(1500, 800),
			new Vector2Int(1760, 960),
			new Vector2Int(1960, 1080)
		};

		internal const float TagListWidth = 290f;

		internal static ConfigEntry<bool> GridModels
		{
			get
			{
				if (!Ground.InHand())
				{
					return Grid3D;
				}
				return GroundGrid3D;
			}
		}

		internal static ConfigEntry<bool> TooltipModels
		{
			get
			{
				if (!Ground.InHand())
				{
					return Preview3D;
				}
				return GroundPreview3D;
			}
		}

		internal static bool Russian
		{
			get
			{
				if (Localization.instance != null)
				{
					return Localization.instance.GetSelectedLanguage() == "Russian";
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Expected O, but got Unknown
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			log = ((BaseUnityPlugin)this).Logger;
			Cfg = ((BaseUnityPlugin)this).Config;
			Folder = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "The new build menu (category tree, grid with captions, tooltip with 3D). Off - the game's own menu (after a restart).\nНовое меню стройки (дерево категорий, сетка с подписями, подсказка с 3D). Выключено — родное меню игры (после перезапуска).");
			CellStep = ((BaseUnityPlugin)this).Config.Bind<int>("Grid", "CellSize", 2, new ConfigDescription("Grid cell size, 0-6 (the menu's four Grid size buttons: 0, 2, 4, 6).\nРазмер ячеек сетки, шаг 0–6 (в меню — четыре кнопки «Размер сетки»: 0, 2, 4, 6).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, CellSizes.Length - 1), Array.Empty<object>()));
			WindowSize = ((BaseUnityPlugin)this).Config.Bind<int>("General", "WindowSize", 1, new ConfigDescription("Window size: 0 smaller, 1 medium, 2 large (after a restart).\nРазмер окна: 0 — поменьше, 1 — средний, 2 — крупный (после перезапуска).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, WindowSizes.Length - 1), Array.Empty<object>()));
			Captions = ((BaseUnityPlugin)this).Config.Bind<bool>("Grid", "Captions", true, "Names under the pieces' icons.\nПодписи под значками деталей.");
			Badges = ((BaseUnityPlugin)this).Config.Bind<bool>("Grid", "CategoryIcon", true, "The subcategory's badge in the cell's corner.\nЗначок подкатегории в углу ячейки.");
			Grid3D = ((BaseUnityPlugin)this).Config.Bind<bool>("Grid", "Models3D", false, "3D snapshots of the pieces in the grid instead of the game's icons (made a little each frame for cells in view, then kept in memory).\n3D-снимки деталей в сетке вместо значков игры (снимаются понемногу за кадр для видимых ячеек, потом из памяти).");
			Preview3D = ((BaseUnityPlugin)this).Config.Bind<bool>("Tooltip", "Preview3D", true, "A live turning model of the piece in the tooltip (else the game's icon).\nЖивая поворотная модель детали в подсказке (иначе — значок игры).");
			GroundGrid3D = ((BaseUnityPlugin)this).Config.Bind<bool>("Grid", "GroundModels3D", false, "The same for the ground tools (hoe, cultivator and the like): 3D snapshots in the grid instead of the game's icons.\nТо же для инструментов земли (мотыга, культиватор и подобные): 3D-снимки в сетке вместо значков игры.");
			GroundPreview3D = ((BaseUnityPlugin)this).Config.Bind<bool>("Tooltip", "GroundPreview3D", false, "The same for the ground tools (hoe, cultivator and the like): a turning model in the tooltip instead of the game's icon.\nТо же для инструментов земли (мотыга, культиватор и подобные): поворотная модель в подсказке вместо значка игры.");
			ShowPrefab = ((BaseUnityPlugin)this).Config.Bind<bool>("Tooltip", "ShowPrefab", false, "Show the piece's prefab name in the tooltip (for the categories file).\nПоказывать в подсказке имя prefab детали (для файла подкатегорий).");
			DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced", "DebugLog", false, "More details in the log.\nПодробности в журнал.");
			((BaseUnityPlugin)this).Config.SettingChanged += delegate(object _, SettingChangedEventArgs args)
			{
				if (args.ChangedSetting.Definition.Section != "Advanced" && (object)args.ChangedSetting != Enabled)
				{
					Window.Refresh();
				}
			};
			new Harmony("chistix.buildmenu").PatchAll(typeof(Plugin).Assembly);
			Log("BuildMenu 0.3.13");
		}

		private void OnDestroy()
		{
			Preview.Clear();
		}

		internal static string T(string ru, string en)
		{
			if (!Russian)
			{
				return en;
			}
			return ru;
		}

		internal static void Log(string text)
		{
			ManualLogSource obj = log;
			if (obj != null)
			{
				obj.LogInfo((object)text);
			}
		}

		internal static void Warn(string text)
		{
			ManualLogSource obj = log;
			if (obj != null)
			{
				obj.LogWarning((object)text);
			}
		}

		internal static void Debug(string text)
		{
			if (DebugLog != null && DebugLog.Value)
			{
				ManualLogSource obj = log;
				if (obj != null)
				{
					obj.LogInfo((object)text);
				}
			}
		}
	}
	internal static class Preview
	{
		private static GameObject rig;

		private static GameObject model;

		private static Camera cam;

		private static Light light;

		private static Light fill;

		private static RenderTexture live;

		private static string shown;

		private static float yaw;

		private static readonly Vector3 Place = new Vector3(0f, -20000f, 0f);

		private static int layer = -1;

		private static readonly Dictionary<int, (Vector3 centre, float radius)> spheres = new Dictionary<int, (Vector3, float)>();

		private static float sinceDrawn;

		private static readonly int sunDir = Shader.PropertyToID("_SunDir");

		private static readonly int sunColor = Shader.PropertyToID("_SunColor");

		private static readonly int ambientColor = Shader.PropertyToID("_AmbientColor");

		private static readonly int wet = Shader.PropertyToID("_Wet");

		private static readonly Color Key = new Color(0.9f, 0.87f, 0.82f);

		private static readonly Color Ambient = new Color(0.46f, 0.46f, 0.48f);

		private const int MaxStills = 512;

		private static readonly Dictionary<string, RenderTexture> stills = new Dictionary<string, RenderTexture>();

		private static readonly LinkedList<string> used = new LinkedList<string>();

		public static RenderTexture Texture => live;

		public static int StillCount => stills.Count;

		private static int Layer()
		{
			if (layer >= 0)
			{
				return layer;
			}
			for (int num = 31; num >= 8; num--)
			{
				if (string.IsNullOrEmpty(LayerMask.LayerToName(num)))
				{
					return layer = num;
				}
			}
			return layer = 31;
		}

		private static void Setup()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: 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)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: 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_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)rig != (Object)null))
			{
				rig = new GameObject("BuildMenu preview");
				Object.DontDestroyOnLoad((Object)(object)rig);
				rig.transform.position = Place;
				GameObject val = new GameObject("camera");
				val.transform.SetParent(rig.transform, false);
				cam = val.AddComponent<Camera>();
				((Behaviour)cam).enabled = false;
				cam.cullingMask = 1 << Layer();
				cam.clearFlags = (CameraClearFlags)2;
				cam.backgroundColor = new Color(0f, 0f, 0f, 0f);
				cam.fieldOfView = 28f;
				cam.nearClipPlane = 0.05f;
				cam.farClipPlane = 200f;
				cam.allowHDR = false;
				cam.allowMSAA = true;
				cam.useOcclusionCulling = false;
				GameObject val2 = new GameObject("light");
				val2.transform.SetParent(rig.transform, false);
				val2.transform.rotation = Quaternion.Euler(40f, -35f, 0f);
				light = val2.AddComponent<Light>();
				light.type = (LightType)1;
				light.intensity = 1.1f;
				light.color = new Color(1f, 0.95f, 0.88f);
				light.cullingMask = 1 << Layer();
				light.shadows = (LightShadows)0;
				((Behaviour)light).enabled = false;
				GameObject val3 = new GameObject("fill");
				val3.transform.SetParent(rig.transform, false);
				val3.transform.rotation = Quaternion.Euler(20f, 150f, 0f);
				fill = val3.AddComponent<Light>();
				fill.type = (LightType)1;
				fill.intensity = 0.45f;
				fill.color = new Color(0.75f, 0.82f, 1f);
				fill.cullingMask = 1 << Layer();
				fill.shadows = (LightShadows)0;
				((Behaviour)fill).enabled = false;
			}
		}

		private static GameObject Copy(GameObject prefab)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_014d: 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_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("model " + ((Object)prefab).name);
			val.layer = Layer();
			val.transform.SetParent(rig.transform, false);
			HashSet<Transform> hashSet = new HashSet<Transform>();
			WearNTear component = prefab.GetComponent<WearNTear>();
			if ((Object)(object)component != (Object)null)
			{
				GameObject[] array = (GameObject[])(object)new GameObject[3] { component.m_worn, component.m_broken, component.m_wet };
				foreach (GameObject val2 in array)
				{
					if ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)component.m_new)
					{
						hashSet.Add(val2.transform);
					}
				}
			}
			LODGroup[] componentsInChildren = prefab.GetComponentsInChildren<LODGroup>(true);
			Renderer[] renderers;
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				LOD[] lODs = componentsInChildren[i].GetLODs();
				for (int j = 1; j < lODs.Length; j++)
				{
					renderers = lODs[j].renderers;
					foreach (Renderer val3 in renderers)
					{
						if ((Object)(object)val3 != (Object)null && !lODs[0].renderers.Contains(val3))
						{
							hashSet.Add(((Component)val3).transform);
						}
					}
				}
			}
			Matrix4x4 worldToLocalMatrix = prefab.transform.worldToLocalMatrix;
			renderers = prefab.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val4 in renderers)
			{
				if ((val4 is MeshRenderer || val4 is SkinnedMeshRenderer) && val4.enabled && On(((Component)val4).transform, prefab.transform) && !Skipped(((Component)val4).transform, hashSet))
				{
					SkinnedMeshRenderer val5 = (SkinnedMeshRenderer)(object)((val4 is SkinnedMeshRenderer) ? val4 : null);
					object obj;
					if (val5 == null)
					{
						MeshFilter component2 = ((Component)val4).GetComponent<MeshFilter>();
						obj = ((component2 != null) ? component2.sharedMesh : null);
					}
					else
					{
						obj = val5.sharedMesh;
					}
					Mesh val6 = (Mesh)obj;
					if (!((Object)(object)val6 == (Object)null))
					{
						GameObject val7 = new GameObject(((Object)val4).name)
						{
							layer = Layer()
						};
						val7.transform.SetParent(val.transform, false);
						Matrix4x4 val8 = worldToLocalMatrix * ((Component)val4).transform.localToWorldMatrix;
						val7.transform.localPosition = Vector4.op_Implicit(((Matrix4x4)(ref val8)).GetColumn(3));
						val7.transform.localRotation = ((Matrix4x4)(ref val8)).rotation;
						val7.transform.localScale = ((Matrix4x4)(ref val8)).lossyScale;
						val7.AddComponent<MeshFilter>().sharedMesh = val6;
						MeshRenderer obj2 = val7.AddComponent<MeshRenderer>();
						((Renderer)obj2).sharedMaterials = val4.sharedMaterials;
						((Renderer)obj2).shadowCastingMode = (ShadowCastingMode)0;
						((Renderer)obj2).receiveShadows = false;
					}
				}
			}
			return val;
		}

		private static bool On(Transform t, Transform root)
		{
			while ((Object)(object)t != (Object)null)
			{
				if (!((Component)t).gameObject.activeSelf)
				{
					return false;
				}
				if ((Object)(object)t == (Object)(object)root)
				{
					break;
				}
				t = t.parent;
			}
			return true;
		}

		private static bool Skipped(Transform t, HashSet<Transform> skip)
		{
			while ((Object)(object)t != (Object)null)
			{
				if (skip.Contains(t))
				{
					return true;
				}
				t = t.parent;
			}
			return false;
		}

		private static (Vector3 centre, float radius) Sphere(GameObject root)
		{
			//IL_001e: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			int instanceID = ((Object)root).GetInstanceID();
			if (spheres.TryGetValue(instanceID, out (Vector3, float) value))
			{
				return value;
			}
			Quaternion localRotation = root.transform.localRotation;
			root.transform.localRotation = Quaternion.identity;
			Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>();
			Bounds val = (Bounds)((componentsInChildren.Length != 0) ? componentsInChildren[0].bounds : new Bounds(root.transform.position, Vector3.one * 0.2f));
			Renderer[] array = componentsInChildren;
			foreach (Renderer val2 in array)
			{
				((Bounds)(ref val)).Encapsulate(val2.bounds);
			}
			root.transform.localRotation = localRotation;
			Dictionary<int, (Vector3 centre, float radius)> dictionary = spheres;
			Vector3 item = root.transform.InverseTransformPoint(((Bounds)(ref val)).center);
			Vector3 extents = ((Bounds)(ref val)).extents;
			return dictionary[instanceID] = (item, Mathf.Max(((Vector3)(ref extents)).magnitude, 0.1f));
		}

		private static void Frame(GameObject root, float turn)
		{
			//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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: 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)
			var (val, num) = Sphere(root);
			root.transform.localRotation = Quaternion.Euler(0f, turn, 0f);
			Vector3 val2 = root.transform.TransformPoint(val);
			float num2 = num / Mathf.Sin(cam.fieldOfView * 0.5f * ((float)Math.PI / 180f)) * 0.85f;
			Vector3 val3 = Quaternion.Euler(18f, 180f, 0f) * Vector3.back;
			((Component)cam).transform.position = val2 + val3 * num2;
			((Component)cam).transform.LookAt(val2);
			cam.nearClipPlane = Mathf.Max(0.02f, num2 - num * 1.5f);
			cam.farClipPlane = num2 + num * 1.5f;
		}

		public static void Show(Piece piece)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			if ((Object)(object)piece == (Object)null)
			{
				Hide();
				return;
			}
			Setup();
			if ((Object)(object)live == (Object)null)
			{
				live = new RenderTexture(320, 320, 24, (RenderTextureFormat)0)
				{
					antiAliasing = 2,
					name = "BuildMenu preview"
				};
				live.Create();
			}
			if (shown != ((Object)((Component)piece).gameObject).name || (Object)(object)model == (Object)null)
			{
				if ((Object)(object)model != (Object)null)
				{
					spheres.Remove(((Object)model).GetInstanceID());
					Object.Destroy((Object)(object)model);
				}
				model = Copy(((Component)piece).gameObject);
				shown = ((Object)((Component)piece).gameObject).name;
				yaw = -30f;
			}
			model.SetActive(true);
			sinceDrawn = 1f;
		}

		public static void Hide()
		{
			if ((Object)(object)model != (Object)null)
			{
				model.SetActive(false);
			}
		}

		public static void Update(float dt)
		{
			if (!((Object)(object)model == (Object)null) && model.activeSelf && !((Object)(object)cam == (Object)null))
			{
				yaw += dt * 24f;
				if (!((sinceDrawn += dt) < 1f / 24f))
				{
					sinceDrawn = 0f;
					Frame(model, yaw);
					Render(live);
				}
			}
		}

		private static void Render(RenderTexture target)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: 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_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: 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)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: 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)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: 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_0115: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			Quaternion val = ((Component)cam).transform.rotation * Quaternion.Euler(30f, -35f, 0f);
			if ((Object)(object)light != (Object)null)
			{
				((Component)light).transform.rotation = val;
			}
			if ((Object)(object)fill != (Object)null)
			{
				((Component)fill).transform.rotation = ((Component)cam).transform.rotation * Quaternion.Euler(10f, 145f, 0f);
			}
			bool fog = RenderSettings.fog;
			AmbientMode ambientMode = RenderSettings.ambientMode;
			Color ambientLight = RenderSettings.ambientLight;
			Vector4 globalVector = Shader.GetGlobalVector(sunDir);
			Color globalColor = Shader.GetGlobalColor(sunColor);
			Color globalColor2 = Shader.GetGlobalColor(ambientColor);
			float globalFloat = Shader.GetGlobalFloat(wet);
			RenderSettings.fog = false;
			RenderSettings.ambientMode = (AmbientMode)3;
			RenderSettings.ambientLight = Ambient;
			Shader.SetGlobalVector(sunDir, Vector4.op_Implicit(-(val * Vector3.forward)));
			Shader.SetGlobalColor(sunColor, Key);
			Shader.SetGlobalColor(ambientColor, Ambient);
			Shader.SetGlobalFloat(wet, 0f);
			try
			{
				if ((Object)(object)light != (Object)null)
				{
					((Behaviour)light).enabled = true;
				}
				if ((Object)(object)fill != (Object)null)
				{
					((Behaviour)fill).enabled = true;
				}
				cam.targetTexture = target;
				cam.Render();
			}
			finally
			{
				if ((Object)(object)light != (Object)null)
				{
					((Behaviour)light).enabled = false;
				}
				if ((Object)(object)fill != (Object)null)
				{
					((Behaviour)fill).enabled = false;
				}
				RenderSettings.fog = fog;
				RenderSettings.ambientMode = ambientMode;
				RenderSettings.ambientLight = ambientLight;
				Shader.SetGlobalVector(sunDir, globalVector);
				Shader.SetGlobalColor(sunColor, globalColor);
				Shader.SetGlobalColor(ambientColor, globalColor2);
				Shader.SetGlobalFloat(wet, globalFloat);
				cam.targetTexture = null;
			}
		}

		public static Texture Cached(Piece piece)
		{
			string name = ((Object)((Component)piece).gameObject).name;
			if (!stills.TryGetValue(name, out var value) || (Object)(object)value == (Object)null || !value.IsCreated())
			{
				return null;
			}
			used.Remove(name);
			used.AddFirst(name);
			return (Texture)(object)value;
		}

		public static Texture Still(Piece piece, int size)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			Texture val = Cached(piece);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			Setup();
			string name = ((Object)((Component)piece).gameObject).name;
			RenderTexture val2 = new RenderTexture(size, size, 0, (RenderTextureFormat)0)
			{
				name = "BuildMenu still " + name
			};
			val2.Create();
			RenderTexture temporary = RenderTexture.GetTemporary(size, size, 24, (RenderTextureFormat)0, (RenderTextureReadWrite)0, 4);
			bool active = (Object)(object)model != (Object)null && model.activeSelf;
			if ((Object)(object)model != (Object)null)
			{
				model.SetActive(false);
			}
			GameObject val3 = Copy(((Component)piece).gameObject);
			try
			{
				Frame(val3, -30f);
				Render(temporary);
				Graphics.Blit((Texture)(object)temporary, val2);
			}
			finally
			{
				spheres.Remove(((Object)val3).GetInstanceID());
				Object.DestroyImmediate((Object)(object)val3);
				RenderTexture.ReleaseTemporary(temporary);
				if ((Object)(object)model != (Object)null)
				{
					model.SetActive(active);
				}
			}
			stills[name] = val2;
			used.AddFirst(name);
			while (stills.Count > 512)
			{
				Drop(used.Last.Value);
			}
			return (Texture)(object)val2;
		}

		private static void Drop(string name)
		{
			used.Remove(name);
			if (stills.TryGetValue(name, out var value) && (Object)(object)value != (Object)null)
			{
				value.Release();
				Object.Destroy((Object)(object)value);
			}
			stills.Remove(name);
		}

		public static void ClearStills()
		{
			foreach (string item in stills.Keys.ToList())
			{
				Drop(item);
			}
		}

		public static void Clear()
		{
			ClearStills();
			if ((Object)(object)rig != (Object)null)
			{
				Object.Destroy((Object)(object)rig);
			}
			if ((Object)(object)live != (Object)null)
			{
				live.Release();
				Object.Destroy((Object)(object)live);
			}
			rig = null;
			model = null;
			cam = null;
			live = null;
			shown = null;
		}
	}
	internal static class Sorting
	{
		private static readonly Dictionary<string, List<Sub>> cache = new Dictionary<string, List<Sub>>();

		private static readonly List<(Regex prefab, List<string> subs)> overrides = new List<(Regex, List<string>)>();

		private static DateTime overridesTime;

		internal static int Read;

		private const string Template = "# BuildMenu: подкатегории деталей вручную (для деталей других модов и для несогласных с автоматикой).\n# Строка: prefab = подкатегория[, подкатегория…]. Первая — своя (её значок в углу ячейки).\n# Prefab можно с *: piece_banner* = decor. Prefab детали — в подсказке меню при включённой настройке ShowPrefab.\n# Подкатегории (ключ или название):\n#   Ремесло:       stations (Верстаки и станции), upgrades (Улучшения станций), smelting (Плавильни и производство), kitchen (Кухня)\n#   Строительство: floor (Полы), wall (Стены), roof (Крыши), stairs (Лестницы и пандусы), beam (Балки и столбы),\n#                  door (Двери и ворота), window (Окна), arch (Арки), fence (Заборы), defense (Оборона), other (Прочее)\n#   Интерьер:      furniture (Мебель), light (Освещение), decor (Декор)\n#   Перемещение:   transport (Транспорт), portal (Порталы)\n#   Хранилище:     storage        Разное: misc\n# Новая подкатегория: «Группа/Название» (группа тоже может быть новой), например: piece_mymod_* = Разное/Мой мод\n# Значок подкатегории: icons\\<ключ>.png рядом с BuildMenu.dll (у новой ключ — её название).\n#\n# Building pieces by hand: prefab = subcategory[, subcategory…]; keys as above (or names); \"Group/Name\" makes a new one.\n#\n# wood_wall_log = wall, beam\n";

		public static string OverridesPath => Path.Combine(Paths.ConfigPath, "chistix.buildmenu.categories.txt");

		public static List<Sub> Of(Piece piece)
		{
			string name = ((Object)((Component)piece).gameObject).name;
			if (cache.TryGetValue(name, out var value))
			{
				return value;
			}
			List<string> list = null;
			foreach (var @override in overrides)
			{
				if (@override.prefab.IsMatch(name))
				{
					list = @override.subs;
					break;
				}
			}
			if (list == null)
			{
				try
				{
					list = Catalog.Classify(FactsReader.Read(piece, "", (Func<string, string>)Localization.instance.Localize));
					Read++;
				}
				catch (Exception ex)
				{
					Plugin.Warn(name + ": " + ex.Message);
					list = new List<string> { "misc" };
				}
			}
			value = (from s in list.Select(Catalog.FindOrCreate)
				where s != null
				select s).Distinct().ToList();
			if (value.Count == 0)
			{
				value.Add(Catalog.Find("misc"));
			}
			Sub sub = Merge.SubOf(piece);
			if (sub != null && !value.Contains(sub))
			{
				value.Add(sub);
			}
			cache[name] = value;
			return value;
		}

		public static Sub Main(Piece piece)
		{
			return Of(piece)[0];
		}

		public static void Forget()
		{
			cache.Clear();
		}

		public static void Refresh()
		{
			try
			{
				string overridesPath = OverridesPath;
				if (File.Exists(overridesPath) && File.GetLastWriteTimeUtc(overridesPath) == overridesTime)
				{
					return;
				}
				if (!File.Exists(overridesPath) || (File.ReadAllLines(overridesPath).All((string l) => l.Trim().Length == 0 || l.TrimStart().StartsWith("#")) && File.ReadAllText(overridesPath) != "# BuildMenu: подкатегории деталей вручную (для деталей других модов и для несогласных с автоматикой).\n# Строка: prefab = подкатегория[, подкатегория…]. Первая — своя (её значок в углу ячейки).\n# Prefab можно с *: piece_banner* = decor. Prefab детали — в подсказке меню при включённой настройке ShowPrefab.\n# Подкатегории (ключ или название):\n#   Ремесло:       stations (Верстаки и станции), upgrades (Улучшения станций), smelting (Плавильни и производство), kitchen (Кухня)\n#   Строительство: floor (Полы), wall (Стены), roof (Крыши), stairs (Лестницы и пандусы), beam (Балки и столбы),\n#                  door (Двери и ворота), window (Окна), arch (Арки), fence (Заборы), defense (Оборона), other (Прочее)\n#   Интерьер:      furniture (Мебель), light (Освещение), decor (Декор)\n#   Перемещение:   transport (Транспорт), portal (Порталы)\n#   Хранилище:     storage        Разное: misc\n# Новая подкатегория: «Группа/Название» (группа тоже может быть новой), например: piece_mymod_* = Разное/Мой мод\n# Значок подкатегории: icons\\<ключ>.png рядом с BuildMenu.dll (у новой ключ — её название).\n#\n# Building pieces by hand: prefab = subcategory[, subcategory…]; keys as above (or names); \"Group/Name\" makes a new one.\n#\n# wood_wall_log = wall, beam\n"))
				{
					File.WriteAllText(overridesPath, "# BuildMenu: подкатегории деталей вручную (для деталей других модов и для несогласных с автоматикой).\n# Строка: prefab = подкатегория[, подкатегория…]. Первая — своя (её значок в углу ячейки).\n# Prefab можно с *: piece_banner* = decor. Prefab детали — в подсказке меню при включённой настройке ShowPrefab.\n# Подкатегории (ключ или название):\n#   Ремесло:       stations (Верстаки и станции