Decompiled source of PinThoseRecipes v1.0.0

plugins/PinThoseRecipes.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using PinThoseRecipes.Patches;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("PinThoseRecipes")]
[assembly: AssemblyDescription("Middle-click recipes in a crafting station to pin them to a Subnautica-style panel under the minimap.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PinThoseRecipes")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("7b2c4e1a-9d3f-4b6e-8a21-1c5d9f0e7a34")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PinThoseRecipes
{
	[BepInPlugin("shadymods.pinthoserecipes", "Pin Those Recipes", "1.0.0")]
	public sealed class PinThoseRecipesPlugin : BaseUnityPlugin
	{
		internal const string Guid = "shadymods.pinthoserecipes";

		internal const string Name = "Pin Those Recipes";

		internal const string Version = "1.0.0";

		private static GameObject _controllerGo;

		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			PinConfig.Init(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("shadymods.pinthoserecipes");
			try
			{
				_harmony.PatchAll(typeof(PinThoseRecipesPlugin).Assembly);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Harmony patching failed (some features may be disabled): " + ex));
			}
			_controllerGo = new GameObject("PinThoseRecipes_Controller");
			Object.DontDestroyOnLoad((Object)(object)_controllerGo);
			((Object)_controllerGo).hideFlags = (HideFlags)61;
			_controllerGo.AddComponent<PinnedPanelController>();
			Log.LogInfo((object)"Pin Those Recipes 1.0.0 loaded.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)_controllerGo != (Object)null)
			{
				Object.Destroy((Object)(object)_controllerGo);
				_controllerGo = null;
			}
		}
	}
	internal static class PinConfig
	{
		private static bool _resettingClearToggle;

		private static Color _satisfied = new Color(0.3f, 0.88f, 0.3f, 1f);

		private static Color _missing = new Color(1f, 0.42f, 0.42f, 1f);

		private static Color _name = Color.white;

		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<bool> PlayPinSound { get; private set; }

		internal static ConfigEntry<int> MaxPinnedRecipes { get; private set; }

		internal static ConfigEntry<float> PanelScale { get; private set; }

		internal static ConfigEntry<float> OffsetX { get; private set; }

		internal static ConfigEntry<float> ExtraOffsetBelowMinimap { get; private set; }

		internal static ConfigEntry<float> InventoryOpenShiftLeft { get; private set; }

		internal static ConfigEntry<float> InventoryOpenShiftDown { get; private set; }

		internal static ConfigEntry<string> SatisfiedColorHex { get; private set; }

		internal static ConfigEntry<string> MissingColorHex { get; private set; }

		internal static ConfigEntry<string> NameColorHex { get; private set; }

		internal static ConfigEntry<bool> ClearActiveCharacterPins { get; private set; }

		internal static Color SatisfiedColor => _satisfied;

		internal static Color MissingColor => _missing;

		internal static Color NameColor => _name;

		internal static event Action Changed;

		internal static void Init(ConfigFile config)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			Enabled = config.Bind<bool>("General", "Enabled", true, "Master toggle for Pin Those Recipes. When false, no pinning UI is shown or created.");
			PlayPinSound = config.Bind<bool>("General", "PlayPinSound", true, "Play a soft UI sound when you pin or unpin a recipe.");
			MaxPinnedRecipes = config.Bind<int>("Panel", "MaxPinnedRecipes", 8, new ConfigDescription("How many pinned recipes are shown in the side panel at once. Extra pins are kept and shown as a '+X more' row.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 30), Array.Empty<object>()));
			PanelScale = config.Bind<float>("Panel", "PanelScale", 1f, new ConfigDescription("Overall scale of the pinned-recipes panel content.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			OffsetX = config.Bind<float>("Panel", "OffsetX", -14f, "Horizontal offset of the panel from the top-right corner (negative moves left, in UI units).");
			ExtraOffsetBelowMinimap = config.Bind<float>("Panel", "ExtraOffsetBelowMinimap", -14f, "Additional vertical offset applied below the minimap (negative moves further down, in UI units).");
			InventoryOpenShiftLeft = config.Bind<float>("Panel", "InventoryOpenShiftLeft", 640f, "How far LEFT the panel moves while the inventory/crafting screen is open, so it clears the crafting panel on the right (otherwise the pins are hidden behind it). Higher number = further left. Set to 0 to keep it on the right. (Negative moves right.)");
			InventoryOpenShiftDown = config.Bind<float>("Panel", "InventoryOpenShiftDown", 170f, "How far DOWN the panel moves while the inventory/crafting screen is open. Higher number = further down. Default drops it below the repair button; increase it to clear other mods' inventory UI (e.g. Extra Slots by shudnal). (Negative moves up.)");
			SatisfiedColorHex = config.Bind<string>("Colors", "SatisfiedColor", "#4CE04C", "Color of a material count when you have enough.");
			MissingColorHex = config.Bind<string>("Colors", "MissingColor", "#FF6B6B", "Color of a material count when you are missing some.");
			NameColorHex = config.Bind<string>("Colors", "NameColor", "#FFFFFF", "Color of the pinned item name text.");
			ClearActiveCharacterPins = config.Bind<bool>("Maintenance", "ClearActiveCharacterPins", false, "Set to true to clear ALL pins for the currently loaded character. Resets itself to false afterwards. Useful for removing stale/unresolved pins.");
			ClearActiveCharacterPins.SettingChanged += OnClearPinsToggled;
			ReparseColors();
			config.SettingChanged += OnAnySettingChanged;
		}

		private static void OnAnySettingChanged(object sender, SettingChangedEventArgs e)
		{
			ReparseColors();
			PinConfig.Changed?.Invoke();
		}

		private static void OnClearPinsToggled(object sender, EventArgs e)
		{
			if (_resettingClearToggle || ClearActiveCharacterPins == null || !ClearActiveCharacterPins.Value)
			{
				return;
			}
			PinStore.ClearActivePins();
			_resettingClearToggle = true;
			try
			{
				ClearActiveCharacterPins.Value = false;
			}
			finally
			{
				_resettingClearToggle = false;
			}
		}

		private static void ReparseColors()
		{
			//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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			_satisfied = ParseOr(SatisfiedColorHex?.Value, _satisfied);
			_missing = ParseOr(MissingColorHex?.Value, _missing);
			_name = ParseOr(NameColorHex?.Value, _name);
		}

		private static Color ParseOr(string hex, Color fallback)
		{
			//IL_0014: 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)
			Color result = default(Color);
			if (!string.IsNullOrEmpty(hex) && ColorUtility.TryParseHtmlString(hex, ref result))
			{
				return result;
			}
			return fallback;
		}
	}
	internal sealed class PinStoreFile
	{
		public int Version = 1;

		public List<CharacterPinData> Characters = new List<CharacterPinData>();
	}
	internal sealed class CharacterPinData
	{
		public string Profile;

		public List<string> Pins = new List<string>();

		public List<string> BuildPins = new List<string>();

		public List<string> Collapsed = new List<string>();
	}
	internal static class PinStore
	{
		private static readonly List<string> _activePins = new List<string>();

		private static readonly List<string> _activeBuildPins = new List<string>();

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

		private static string _activeProfileKey;

		private static bool _loggedMalformed;

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

		internal static bool HasActiveProfile => !string.IsNullOrEmpty(_activeProfileKey);

		internal static int TotalPinCount => _activePins.Count + _activeBuildPins.Count;

		internal static event Action PinsChanged;

		internal static IReadOnlyList<string> GetPins()
		{
			return _activePins;
		}

		internal static IReadOnlyList<string> GetBuildPins()
		{
			return _activeBuildPins;
		}

		internal static string CraftCollapseKey(string pinKey)
		{
			return pinKey;
		}

		internal static string BuildCollapseKey(string prefabName)
		{
			return "b:" + prefabName;
		}

		internal static string CraftPinKey(string prefabName)
		{
			return prefabName;
		}

		internal static string UpgradePinKey(string prefabName, int targetQuality)
		{
			return "u:" + prefabName + ":" + targetQuality;
		}

		internal static bool TryParseCraftPinKey(string pinKey, out string prefabName, out int upgradeQuality)
		{
			prefabName = null;
			upgradeQuality = 0;
			if (string.IsNullOrEmpty(pinKey))
			{
				return false;
			}
			if (pinKey.Length > 2 && pinKey[0] == 'u' && pinKey[1] == ':')
			{
				int num = pinKey.LastIndexOf(':');
				if (num <= 2 || num >= pinKey.Length - 1)
				{
					return false;
				}
				if (!int.TryParse(pinKey.Substring(num + 1), out var result) || result < 2)
				{
					return false;
				}
				prefabName = pinKey.Substring(2, num - 2);
				if (string.IsNullOrEmpty(prefabName))
				{
					return false;
				}
				upgradeQuality = result;
				return true;
			}
			prefabName = pinKey;
			upgradeQuality = 0;
			return true;
		}

		internal static bool IsPinned(string pinKey)
		{
			if (!string.IsNullOrEmpty(pinKey))
			{
				return _activePins.Contains(pinKey);
			}
			return false;
		}

		internal static bool IsBuildPinned(string prefabName)
		{
			if (!string.IsNullOrEmpty(prefabName))
			{
				return _activeBuildPins.Contains(prefabName);
			}
			return false;
		}

		internal static bool IsCollapsed(string collapseKey)
		{
			if (!string.IsNullOrEmpty(collapseKey))
			{
				return _collapsed.Contains(collapseKey);
			}
			return false;
		}

		internal static bool ToggleCollapsed(string collapseKey, bool isBuild, string prefabName)
		{
			if (string.IsNullOrEmpty(collapseKey) || !HasActiveProfile)
			{
				return true;
			}
			if (isBuild ? (!IsBuildPinned(prefabName)) : (!IsPinned(prefabName)))
			{
				return true;
			}
			bool result;
			if (_collapsed.Contains(collapseKey))
			{
				_collapsed.Remove(collapseKey);
				result = true;
			}
			else
			{
				_collapsed.Add(collapseKey);
				result = false;
			}
			Save();
			return result;
		}

		internal static int GetPinIndex(string prefabName)
		{
			if (string.IsNullOrEmpty(prefabName))
			{
				return int.MaxValue;
			}
			int num = _activePins.IndexOf(prefabName);
			if (num >= 0)
			{
				return num;
			}
			return int.MaxValue;
		}

		internal static bool Toggle(string pinKey)
		{
			if (string.IsNullOrEmpty(pinKey) || !HasActiveProfile)
			{
				return false;
			}
			bool result;
			if (_activePins.Contains(pinKey))
			{
				_activePins.Remove(pinKey);
				_collapsed.Remove(CraftCollapseKey(pinKey));
				result = false;
			}
			else
			{
				_activePins.Add(pinKey);
				result = true;
			}
			Save();
			PinStore.PinsChanged?.Invoke();
			return result;
		}

		internal static bool ToggleBuild(string prefabName)
		{
			if (string.IsNullOrEmpty(prefabName) || !HasActiveProfile)
			{
				return false;
			}
			bool result;
			if (_activeBuildPins.Contains(prefabName))
			{
				_activeBuildPins.Remove(prefabName);
				_collapsed.Remove(BuildCollapseKey(prefabName));
				result = false;
			}
			else
			{
				_activeBuildPins.Add(prefabName);
				result = true;
			}
			Save();
			PinStore.PinsChanged?.Invoke();
			return result;
		}

		internal static void ClearActive()
		{
			bool num = _activePins.Count > 0 || _activeBuildPins.Count > 0;
			_activePins.Clear();
			_activeBuildPins.Clear();
			_collapsed.Clear();
			_activeProfileKey = null;
			if (num)
			{
				PinStore.PinsChanged?.Invoke();
			}
		}

		internal static void ClearActivePins()
		{
			if (HasActiveProfile && (_activePins.Count != 0 || _activeBuildPins.Count != 0))
			{
				_activePins.Clear();
				_activeBuildPins.Clear();
				_collapsed.Clear();
				Save();
				PinStore.PinsChanged?.Invoke();
			}
		}

		internal static void LoadForProfile(string profileKey)
		{
			profileKey = Normalize(profileKey);
			if (string.IsNullOrEmpty(profileKey))
			{
				ClearActive();
			}
			else
			{
				if (profileKey == _activeProfileKey)
				{
					return;
				}
				_activeProfileKey = profileKey;
				_activePins.Clear();
				_activeBuildPins.Clear();
				_collapsed.Clear();
				CharacterPinData characterPinData = ReadFile().Characters.Find((CharacterPinData c) => c != null && Normalize(c.Profile) == profileKey);
				if (characterPinData?.Pins != null)
				{
					foreach (string pin in characterPinData.Pins)
					{
						if (!string.IsNullOrEmpty(pin) && !_activePins.Contains(pin))
						{
							_activePins.Add(pin);
						}
					}
				}
				if (characterPinData?.BuildPins != null)
				{
					foreach (string buildPin in characterPinData.BuildPins)
					{
						if (!string.IsNullOrEmpty(buildPin) && !_activeBuildPins.Contains(buildPin))
						{
							_activeBuildPins.Add(buildPin);
						}
					}
				}
				if (characterPinData?.Collapsed != null)
				{
					foreach (string item2 in characterPinData.Collapsed)
					{
						if (string.IsNullOrEmpty(item2))
						{
							continue;
						}
						if (item2.StartsWith("b:", StringComparison.Ordinal))
						{
							string item = item2.Substring(2);
							if (_activeBuildPins.Contains(item))
							{
								_collapsed.Add(item2);
							}
						}
						else if (_activePins.Contains(item2))
						{
							_collapsed.Add(item2);
						}
					}
				}
				ManualLogSource log = PinThoseRecipesPlugin.Log;
				if (log != null)
				{
					log.LogInfo((object)$"Loaded {_activePins.Count} craft pin(s) and {_activeBuildPins.Count} build pin(s) for profile '{profileKey}'.");
				}
				PinStore.PinsChanged?.Invoke();
			}
		}

		private static void Save()
		{
			if (!HasActiveProfile)
			{
				return;
			}
			try
			{
				PinStoreFile pinStoreFile = ReadFile();
				CharacterPinData characterPinData = pinStoreFile.Characters.Find((CharacterPinData c) => c != null && Normalize(c.Profile) == _activeProfileKey);
				if (characterPinData == null)
				{
					characterPinData = new CharacterPinData
					{
						Profile = _activeProfileKey
					};
					pinStoreFile.Characters.Add(characterPinData);
				}
				characterPinData.Pins = new List<string>(_activePins);
				characterPinData.BuildPins = new List<string>(_activeBuildPins);
				characterPinData.Collapsed = new List<string>(_collapsed);
				string contents = ToJson(pinStoreFile);
				string directoryName = Path.GetDirectoryName(FilePath);
				if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				string text = FilePath + ".tmp";
				File.WriteAllText(text, contents);
				if (File.Exists(FilePath))
				{
					File.Delete(FilePath);
				}
				File.Move(text, FilePath);
			}
			catch (Exception ex)
			{
				ManualLogSource log = PinThoseRecipesPlugin.Log;
				if (log != null)
				{
					log.LogError((object)("Failed to save pins: " + ex));
				}
			}
		}

		private static PinStoreFile ReadFile()
		{
			try
			{
				if (!File.Exists(FilePath))
				{
					return new PinStoreFile();
				}
				string text = File.ReadAllText(FilePath);
				if (string.IsNullOrWhiteSpace(text))
				{
					return new PinStoreFile();
				}
				PinStoreFile pinStoreFile = FromJson(text);
				if (pinStoreFile == null)
				{
					throw new InvalidDataException("Deserialized to null.");
				}
				if (pinStoreFile.Characters == null)
				{
					pinStoreFile.Characters = new List<CharacterPinData>();
				}
				return pinStoreFile;
			}
			catch (Exception ex)
			{
				if (!_loggedMalformed)
				{
					_loggedMalformed = true;
					ManualLogSource log = PinThoseRecipesPlugin.Log;
					if (log != null)
					{
						log.LogWarning((object)("Pin data unreadable, starting fresh (a .bak backup was made): " + ex.Message));
					}
				}
				TryBackupCorrupt();
				return new PinStoreFile();
			}
		}

		private static string ToJson(PinStoreFile file)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("{");
			stringBuilder.Append("  \"Version\": ").Append(file.Version).AppendLine(",");
			stringBuilder.AppendLine("  \"Characters\": [");
			for (int i = 0; i < file.Characters.Count; i++)
			{
				CharacterPinData characterPinData = file.Characters[i];
				if (characterPinData == null)
				{
					continue;
				}
				stringBuilder.AppendLine("    {");
				stringBuilder.Append("      \"Profile\": ").Append(Quote(characterPinData.Profile)).AppendLine(",");
				stringBuilder.Append("      \"Pins\": [");
				List<string> list = characterPinData.Pins ?? new List<string>();
				for (int j = 0; j < list.Count; j++)
				{
					if (j > 0)
					{
						stringBuilder.Append(", ");
					}
					stringBuilder.Append(Quote(list[j]));
				}
				stringBuilder.AppendLine("],");
				stringBuilder.Append("      \"BuildPins\": [");
				List<string> list2 = characterPinData.BuildPins ?? new List<string>();
				for (int k = 0; k < list2.Count; k++)
				{
					if (k > 0)
					{
						stringBuilder.Append(", ");
					}
					stringBuilder.Append(Quote(list2[k]));
				}
				stringBuilder.AppendLine("],");
				stringBuilder.Append("      \"Collapsed\": [");
				List<string> list3 = characterPinData.Collapsed ?? new List<string>();
				for (int l = 0; l < list3.Count; l++)
				{
					if (l > 0)
					{
						stringBuilder.Append(", ");
					}
					stringBuilder.Append(Quote(list3[l]));
				}
				stringBuilder.AppendLine("]");
				stringBuilder.Append("    }");
				if (i < file.Characters.Count - 1)
				{
					stringBuilder.Append(",");
				}
				stringBuilder.AppendLine();
			}
			stringBuilder.AppendLine("  ]");
			stringBuilder.Append("}");
			return stringBuilder.ToString();
		}

		private static PinStoreFile FromJson(string json)
		{
			PinStoreFile pinStoreFile = new PinStoreFile();
			Match match = Regex.Match(json, "\"Version\"\\s*:\\s*(\\d+)");
			if (match.Success)
			{
				int.TryParse(match.Groups[1].Value, out pinStoreFile.Version);
			}
			Match match2 = Regex.Match(json, "\"Characters\"\\s*:\\s*\\[(.*)\\]", RegexOptions.Singleline);
			if (!match2.Success)
			{
				return pinStoreFile;
			}
			foreach (Match item in Regex.Matches(match2.Groups[1].Value, "\\{[^{}]*\\}"))
			{
				string value = item.Value;
				Match match3 = Regex.Match(value, "\"Profile\"\\s*:\\s*\"((?:\\\\.|[^\"\\\\])*)\"");
				if (!match3.Success)
				{
					continue;
				}
				CharacterPinData characterPinData = new CharacterPinData
				{
					Profile = Unescape(match3.Groups[1].Value),
					Pins = new List<string>(),
					BuildPins = new List<string>(),
					Collapsed = new List<string>()
				};
				Match match4 = Regex.Match(value, "\"Pins\"\\s*:\\s*\\[(.*?)\\]", RegexOptions.Singleline);
				if (match4.Success)
				{
					foreach (Match item2 in Regex.Matches(match4.Groups[1].Value, "\"((?:\\\\.|[^\"\\\\])*)\""))
					{
						string text = Unescape(item2.Groups[1].Value);
						if (!string.IsNullOrEmpty(text) && !characterPinData.Pins.Contains(text))
						{
							characterPinData.Pins.Add(text);
						}
					}
				}
				Match match5 = Regex.Match(value, "\"BuildPins\"\\s*:\\s*\\[(.*?)\\]", RegexOptions.Singleline);
				if (match5.Success)
				{
					foreach (Match item3 in Regex.Matches(match5.Groups[1].Value, "\"((?:\\\\.|[^\"\\\\])*)\""))
					{
						string text2 = Unescape(item3.Groups[1].Value);
						if (!string.IsNullOrEmpty(text2) && !characterPinData.BuildPins.Contains(text2))
						{
							characterPinData.BuildPins.Add(text2);
						}
					}
				}
				Match match6 = Regex.Match(value, "\"Collapsed\"\\s*:\\s*\\[(.*?)\\]", RegexOptions.Singleline);
				if (match6.Success)
				{
					foreach (Match item4 in Regex.Matches(match6.Groups[1].Value, "\"((?:\\\\.|[^\"\\\\])*)\""))
					{
						string text3 = Unescape(item4.Groups[1].Value);
						if (!string.IsNullOrEmpty(text3) && !characterPinData.Collapsed.Contains(text3))
						{
							characterPinData.Collapsed.Add(text3);
						}
					}
				}
				pinStoreFile.Characters.Add(characterPinData);
			}
			return pinStoreFile;
		}

		private static string Quote(string s)
		{
			if (s == null)
			{
				return "\"\"";
			}
			return "\"" + s.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n")
				.Replace("\r", "\\r") + "\"";
		}

		private static string Unescape(string s)
		{
			return s.Replace("\\\"", "\"").Replace("\\\\", "\\").Replace("\\n", "\n")
				.Replace("\\r", "\r");
		}

		private static void TryBackupCorrupt()
		{
			try
			{
				if (File.Exists(FilePath))
				{
					string text = FilePath + ".bak";
					if (File.Exists(text))
					{
						File.Delete(text);
					}
					File.Copy(FilePath, text);
				}
			}
			catch
			{
			}
		}

		private static string Normalize(string key)
		{
			if (!string.IsNullOrWhiteSpace(key))
			{
				return key.Trim();
			}
			return null;
		}
	}
	internal sealed class ResolvedRequirement
	{
		public Sprite Icon;

		public string SharedName;

		public string DisplayName;

		public int Need;
	}
	internal sealed class ResolvedRecipe
	{
		public string PrefabName;

		public string ItemPrefabName;

		public Sprite Icon;

		public string DisplayName;

		public int OutputAmount;

		public bool IsBuild;

		public bool IsUpgrade;

		public int UpgradeQuality;

		public readonly List<ResolvedRequirement> Requirements = new List<ResolvedRequirement>();
	}
	internal static class RecipeResolver
	{
		private static readonly Dictionary<string, List<Recipe>> _byPrefab = new Dictionary<string, List<Recipe>>();

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

		private static ObjectDB _cachedInstance;

		internal static void InvalidateCache()
		{
			_byPrefab.Clear();
			_loggedUnresolved.Clear();
			_cachedInstance = null;
		}

		private static bool EnsureCache()
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			if (instance != _cachedInstance || _byPrefab.Count == 0)
			{
				Rebuild(instance);
			}
			return true;
		}

		private static void Rebuild(ObjectDB db)
		{
			_byPrefab.Clear();
			_loggedUnresolved.Clear();
			_cachedInstance = db;
			if (db.m_recipes == null)
			{
				return;
			}
			foreach (Recipe recipe in db.m_recipes)
			{
				if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null)
				{
					continue;
				}
				string name = ((Object)((Component)recipe.m_item).gameObject).name;
				if (!string.IsNullOrEmpty(name))
				{
					if (!_byPrefab.TryGetValue(name, out var value))
					{
						value = new List<Recipe>();
						_byPrefab[name] = value;
					}
					value.Add(recipe);
				}
			}
		}

		internal static List<Recipe> GetRecipes(string prefabName)
		{
			if (string.IsNullOrEmpty(prefabName) || !EnsureCache())
			{
				return null;
			}
			if (!_byPrefab.TryGetValue(prefabName, out var value))
			{
				return null;
			}
			return value;
		}

		internal static Recipe GetPreferredRecipe(string prefabName)
		{
			List<Recipe> recipes = GetRecipes(prefabName);
			if (recipes == null || recipes.Count == 0)
			{
				return null;
			}
			if (recipes.Count == 1)
			{
				return recipes[0];
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				List<Recipe> list = new List<Recipe>();
				localPlayer.GetAvailableRecipes(ref list);
				foreach (Recipe item in recipes)
				{
					if (list.Contains(item))
					{
						return item;
					}
				}
				foreach (Recipe item2 in recipes)
				{
					if ((Object)(object)item2.m_item != (Object)null && localPlayer.IsRecipeKnown(item2.m_item.m_itemData.m_shared.m_name))
					{
						return item2;
					}
				}
			}
			List<Recipe> list2 = new List<Recipe>(recipes);
			list2.Sort(delegate(Recipe a, Recipe b)
			{
				int num = string.CompareOrdinal(((Object)a).name, ((Object)b).name);
				if (num != 0)
				{
					return num;
				}
				string strA = (((Object)(object)a.m_craftingStation != (Object)null) ? ((Object)a.m_craftingStation).name : "");
				string strB = (((Object)(object)b.m_craftingStation != (Object)null) ? ((Object)b.m_craftingStation).name : "");
				return string.CompareOrdinal(strA, strB);
			});
			return list2[0];
		}

		internal static ResolvedRecipe Resolve(string pinKey)
		{
			if (!PinStore.TryParseCraftPinKey(pinKey, out var prefabName, out var upgradeQuality))
			{
				LogUnresolvedOnce(pinKey);
				return null;
			}
			Recipe preferredRecipe = GetPreferredRecipe(prefabName);
			if ((Object)(object)preferredRecipe == (Object)null || (Object)(object)preferredRecipe.m_item == (Object)null)
			{
				LogUnresolvedOnce(pinKey);
				return null;
			}
			bool flag = upgradeQuality >= 2;
			int num = ((!flag) ? 1 : upgradeQuality);
			string text = Localize(preferredRecipe.m_item.m_itemData.m_shared.m_name);
			if (flag)
			{
				text = text + " ★" + upgradeQuality;
			}
			ResolvedRecipe resolvedRecipe = new ResolvedRecipe
			{
				PrefabName = pinKey,
				ItemPrefabName = prefabName,
				Icon = preferredRecipe.m_item.m_itemData.GetIcon(),
				DisplayName = text,
				OutputAmount = (flag ? 1 : Mathf.Max(1, preferredRecipe.m_amount)),
				IsUpgrade = flag,
				UpgradeQuality = (flag ? upgradeQuality : 0)
			};
			if (preferredRecipe.m_resources != null)
			{
				Requirement[] resources = preferredRecipe.m_resources;
				foreach (Requirement val in resources)
				{
					if (val != null && !((Object)(object)val.m_resItem == (Object)null))
					{
						int amount = val.GetAmount(num);
						if (amount > 0)
						{
							string name = val.m_resItem.m_itemData.m_shared.m_name;
							resolvedRecipe.Requirements.Add(new ResolvedRequirement
							{
								Icon = val.m_resItem.m_itemData.GetIcon(),
								SharedName = name,
								DisplayName = Localize(name),
								Need = amount
							});
						}
					}
				}
			}
			return resolvedRecipe;
		}

		private static void LogUnresolvedOnce(string prefabName)
		{
			if (!string.IsNullOrEmpty(prefabName) && !_loggedUnresolved.Contains(prefabName))
			{
				_loggedUnresolved.Add(prefabName);
				ManualLogSource log = PinThoseRecipesPlugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("Pinned recipe '" + prefabName + "' is not resolvable yet (missing prefab/recipe or ObjectDB not ready); kept and hidden."));
				}
			}
		}

		private static string Localize(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return "";
			}
			if (Localization.instance == null)
			{
				return token;
			}
			return Localization.instance.Localize(token);
		}
	}
	internal static class PieceResolver
	{
		private static readonly Dictionary<string, Piece> _byPrefab = new Dictionary<string, Piece>();

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

		private static ZNetScene _cachedScene;

		internal static void InvalidateCache()
		{
			_byPrefab.Clear();
			_loggedUnresolved.Clear();
			_cachedScene = null;
		}

		private static void EnsureCache()
		{
			ZNetScene instance = ZNetScene.instance;
			if (!((Object)(object)instance == (Object)null) && (instance != _cachedScene || _byPrefab.Count == 0))
			{
				Rebuild(instance);
			}
		}

		private static void Rebuild(ZNetScene scene)
		{
			_byPrefab.Clear();
			_loggedUnresolved.Clear();
			_cachedScene = scene;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				PieceTable val = TraverseBuildPieces(localPlayer);
				if ((Object)(object)val != (Object)null && val.m_pieces != null)
				{
					foreach (GameObject piece in val.m_pieces)
					{
						IndexPiece(piece);
					}
				}
			}
			foreach (string buildPin in PinStore.GetBuildPins())
			{
				if (!_byPrefab.ContainsKey(buildPin))
				{
					IndexPiece(scene.GetPrefab(buildPin));
				}
			}
		}

		private static PieceTable TraverseBuildPieces(Player player)
		{
			try
			{
				object? obj = AccessTools.Field(typeof(Player), "m_buildPieces")?.GetValue(player);
				return (PieceTable)((obj is PieceTable) ? obj : null);
			}
			catch
			{
				return null;
			}
		}

		private static void IndexPiece(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return;
			}
			Piece component = go.GetComponent<Piece>();
			if (!((Object)(object)component == (Object)null))
			{
				string text = Utils.GetPrefabName(go);
				if (string.IsNullOrEmpty(text))
				{
					text = ((Object)go).name;
				}
				if (!_byPrefab.ContainsKey(text))
				{
					_byPrefab[text] = component;
				}
			}
		}

		internal static Piece GetPiece(string prefabName)
		{
			if (string.IsNullOrEmpty(prefabName))
			{
				return null;
			}
			EnsureCache();
			if (_byPrefab.TryGetValue(prefabName, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance != (Object)null)
			{
				GameObject prefab = instance.GetPrefab(prefabName);
				value = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Piece>() : null);
				if ((Object)(object)value != (Object)null)
				{
					_byPrefab[prefabName] = value;
					return value;
				}
			}
			return null;
		}

		internal static ResolvedRecipe Resolve(string prefabName)
		{
			Piece piece = GetPiece(prefabName);
			if ((Object)(object)piece == (Object)null)
			{
				if (!string.IsNullOrEmpty(prefabName) && !_loggedUnresolved.Contains(prefabName))
				{
					_loggedUnresolved.Add(prefabName);
					ManualLogSource log = PinThoseRecipesPlugin.Log;
					if (log != null)
					{
						log.LogInfo((object)("Pinned build piece '" + prefabName + "' is not resolvable yet; kept and hidden."));
					}
				}
				return null;
			}
			ResolvedRecipe resolvedRecipe = new ResolvedRecipe
			{
				PrefabName = prefabName,
				Icon = piece.m_icon,
				DisplayName = Localize(piece.m_name),
				OutputAmount = 1,
				IsBuild = true
			};
			if (piece.m_resources != null)
			{
				Requirement[] resources = piece.m_resources;
				foreach (Requirement val in resources)
				{
					if (val != null && !((Object)(object)val.m_resItem == (Object)null))
					{
						int amount = val.GetAmount(1);
						if (amount > 0)
						{
							string name = val.m_resItem.m_itemData.m_shared.m_name;
							resolvedRecipe.Requirements.Add(new ResolvedRequirement
							{
								Icon = val.m_resItem.m_itemData.GetIcon(),
								SharedName = name,
								DisplayName = Localize(name),
								Need = amount
							});
						}
					}
				}
			}
			return resolvedRecipe;
		}

		private static string Localize(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return "";
			}
			if (Localization.instance == null)
			{
				return token;
			}
			return Localization.instance.Localize(token);
		}
	}
	internal static class PinIcon
	{
		private const string EmbeddedName = "PinThoseRecipes.pin.png";

		private static Sprite _sprite;

		private static Sprite _buildSprite;

		private static bool _resolved;

		internal static Sprite Sprite
		{
			get
			{
				Ensure();
				return _sprite;
			}
		}

		internal static Sprite BuildSprite
		{
			get
			{
				Ensure();
				return _buildSprite;
			}
		}

		internal static void Ensure()
		{
			if (_resolved)
			{
				return;
			}
			_resolved = true;
			try
			{
				_sprite = LoadEmbedded() ?? BuildStarFallback();
			}
			catch (Exception ex)
			{
				ManualLogSource log = PinThoseRecipesPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("PinIcon load failed, using fallback: " + ex.Message));
				}
				try
				{
					_sprite = BuildStarFallback();
				}
				catch
				{
					_sprite = null;
				}
			}
			try
			{
				_buildSprite = BuildDiamondFallback();
			}
			catch
			{
				_buildSprite = _sprite;
			}
		}

		private static Sprite LoadEmbedded()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			using Stream stream = typeof(PinIcon).Assembly.GetManifestResourceStream("PinThoseRecipes.pin.png");
			if (stream == null)
			{
				return null;
			}
			byte[] array = new byte[stream.Length];
			int num;
			for (int i = 0; i < array.Length; i += num)
			{
				num = stream.Read(array, i, array.Length - i);
				if (num <= 0)
				{
					break;
				}
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (!LoadImageInto(val, array))
			{
				Object.Destroy((Object)(object)val);
				return null;
			}
			((Object)val).name = "PinThoseRecipes_PinEmbedded";
			((Texture)val).filterMode = (FilterMode)1;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			return MakeSprite(val, "PinThoseRecipes_PinEmbedded");
		}

		private static Sprite BuildStarFallback()
		{
			//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_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: 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_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: 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)
			Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false)
			{
				name = "PinThoseRecipes_PinFallback",
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1
			};
			Color32 val2 = default(Color32);
			((Color32)(ref val2))..ctor((byte)0, (byte)0, (byte)0, (byte)0);
			Color32[] array = (Color32[])(object)new Color32[4096];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = val2;
			}
			Vector2 center = new Vector2(32f, 32f);
			float num = 29.44f;
			float num2 = num * 0.42f;
			Vector2[] poly = BuildStar(center, num, num2);
			Vector2[] poly2 = BuildStar(center, num * 0.8f, num2 * 0.8f);
			Color32 val3 = default(Color32);
			((Color32)(ref val3))..ctor(byte.MaxValue, (byte)205, (byte)60, byte.MaxValue);
			Color32 val4 = default(Color32);
			((Color32)(ref val4))..ctor((byte)60, (byte)40, (byte)5, byte.MaxValue);
			Vector2 p = default(Vector2);
			for (int j = 0; j < 64; j++)
			{
				for (int k = 0; k < 64; k++)
				{
					((Vector2)(ref p))..ctor((float)k + 0.5f, (float)j + 0.5f);
					if (PolygonContains(poly, p))
					{
						array[j * 64 + k] = (PolygonContains(poly2, p) ? val3 : val4);
					}
				}
			}
			val.SetPixels32(array);
			val.Apply(false, false);
			return MakeSprite(val, "PinThoseRecipes_Pin");
		}

		private static Sprite BuildDiamondFallback()
		{
			//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_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: 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_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: 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_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false)
			{
				name = "PinThoseRecipes_BuildPinFallback",
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1
			};
			Color32 val2 = default(Color32);
			((Color32)(ref val2))..ctor((byte)0, (byte)0, (byte)0, (byte)0);
			Color32[] array = (Color32[])(object)new Color32[4096];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = val2;
			}
			float num = 32f;
			float num2 = 32f;
			float num3 = 26.88f;
			float num4 = num3 * 0.72f;
			Vector2[] poly = (Vector2[])(object)new Vector2[4]
			{
				new Vector2(num, num2 + num3),
				new Vector2(num + num3, num2),
				new Vector2(num, num2 - num3),
				new Vector2(num - num3, num2)
			};
			Vector2[] poly2 = (Vector2[])(object)new Vector2[4]
			{
				new Vector2(num, num2 + num4),
				new Vector2(num + num4, num2),
				new Vector2(num, num2 - num4),
				new Vector2(num - num4, num2)
			};
			Color32 val3 = default(Color32);
			((Color32)(ref val3))..ctor((byte)70, (byte)210, (byte)200, byte.MaxValue);
			Color32 val4 = default(Color32);
			((Color32)(ref val4))..ctor((byte)10, (byte)50, (byte)55, byte.MaxValue);
			Vector2 p = default(Vector2);
			for (int j = 0; j < 64; j++)
			{
				for (int k = 0; k < 64; k++)
				{
					((Vector2)(ref p))..ctor((float)k + 0.5f, (float)j + 0.5f);
					if (PolygonContains(poly, p))
					{
						array[j * 64 + k] = (PolygonContains(poly2, p) ? val3 : val4);
					}
				}
			}
			val.SetPixels32(array);
			val.Apply(false, false);
			return MakeSprite(val, "PinThoseRecipes_BuildPin");
		}

		private static Vector2[] BuildStar(Vector2 center, float outer, float inner)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			Vector2[] array = (Vector2[])(object)new Vector2[10];
			for (int i = 0; i < 10; i++)
			{
				float num = ((i % 2 == 0) ? outer : inner);
				float num2 = (float)Math.PI / 2f + (float)i * (float)Math.PI / 5f;
				array[i] = new Vector2(center.x + Mathf.Cos(num2) * num, center.y + Mathf.Sin(num2) * num);
			}
			return array;
		}

		private static bool PolygonContains(Vector2[] poly, Vector2 p)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			int num = poly.Length - 1;
			int num2 = 0;
			while (num2 < poly.Length)
			{
				if (poly[num2].y > p.y != poly[num].y > p.y && p.x < (poly[num].x - poly[num2].x) * (p.y - poly[num2].y) / (poly[num].y - poly[num2].y) + poly[num2].x)
				{
					flag = !flag;
				}
				num = num2++;
			}
			return flag;
		}

		private static bool LoadImageInto(Texture2D tex, byte[] data)
		{
			MethodInfo methodInfo = typeof(ImageConversion).GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
			{
				typeof(Texture2D),
				typeof(byte[]),
				typeof(bool)
			}, null) ?? typeof(ImageConversion).GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
			{
				typeof(Texture2D),
				typeof(byte[])
			}, null);
			if (methodInfo == null)
			{
				return false;
			}
			object[] parameters = ((methodInfo.GetParameters().Length != 3) ? new object[2] { tex, data } : new object[3] { tex, data, false });
			return (bool)methodInfo.Invoke(null, parameters);
		}

		private static Sprite MakeSprite(Texture2D tex, string name)
		{
			//IL_0019: 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)
			Sprite obj = Sprite.Create(tex, new Rect(0f, 0f, (float)((Texture)tex).width, (float)((Texture)tex).height), new Vector2(0.5f, 0.5f), 100f);
			((Object)obj).name = name;
			return obj;
		}
	}
	internal static class UiUtils
	{
		private static TMP_FontAsset _cachedFont;

		internal static TMP_FontAsset ResolveFont()
		{
			if ((Object)(object)_cachedFont != (Object)null)
			{
				return _cachedFont;
			}
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)instance.m_recipeName != (Object)null && (Object)(object)instance.m_recipeName.font != (Object)null)
			{
				_cachedFont = instance.m_recipeName.font;
			}
			return _cachedFont;
		}

		internal static RectTransform NewRect(Transform parent, string name)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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)
			RectTransform component = new GameObject(name, new Type[1] { typeof(RectTransform) }).GetComponent<RectTransform>();
			((Transform)component).SetParent(parent, false);
			((Transform)component).localScale = Vector3.one;
			component.anchoredPosition = Vector2.zero;
			return component;
		}

		internal static Image NewImage(Transform parent, string name, Sprite sprite = null, bool raycast = false)
		{
			Image obj = ((Component)NewRect(parent, name)).gameObject.AddComponent<Image>();
			obj.sprite = sprite;
			((Graphic)obj).raycastTarget = raycast;
			obj.preserveAspect = true;
			return obj;
		}

		internal static TextMeshProUGUI NewText(Transform parent, string name, float fontSize, TextAlignmentOptions align)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			TextMeshProUGUI val = ((Component)NewRect(parent, name)).gameObject.AddComponent<TextMeshProUGUI>();
			TMP_FontAsset val2 = ResolveFont();
			if ((Object)(object)val2 != (Object)null)
			{
				((TMP_Text)val).font = val2;
			}
			((TMP_Text)val).fontSize = fontSize;
			((TMP_Text)val).alignment = align;
			((TMP_Text)val).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)val).overflowMode = (TextOverflowModes)0;
			((Graphic)val).raycastTarget = false;
			((Graphic)val).color = Color.white;
			((TMP_Text)val).fontStyle = (FontStyles)0;
			((TMP_Text)val).outlineWidth = 0.15f;
			((TMP_Text)val).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)200);
			return val;
		}

		internal static void SetAnchors(RectTransform rt, Vector2 min, Vector2 max, Vector2 pivot)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			rt.anchorMin = min;
			rt.anchorMax = max;
			rt.pivot = pivot;
		}
	}
	internal sealed class RecipePinInteraction : MonoBehaviour, IPointerClickHandler, IEventSystemHandler
	{
		internal string PinKey;

		internal Recipe Recipe;

		internal Image PinImage;

		internal InventoryGui Owner;

		public void OnPointerClick(PointerEventData eventData)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Invalid comparison between Unknown and I4
			if (eventData != null && (int)eventData.button == 2 && ((Behaviour)this).isActiveAndEnabled && !string.IsNullOrEmpty(PinKey) && PinStore.HasActiveProfile)
			{
				PinStore.Toggle(PinKey);
				RefreshPinVisual();
				PinSound.Play();
			}
		}

		internal void RefreshPinVisual()
		{
			if (!((Object)(object)PinImage == (Object)null))
			{
				bool flag = PinStore.IsPinned(PinKey);
				if (((Component)PinImage).gameObject.activeSelf != flag)
				{
					((Component)PinImage).gameObject.SetActive(flag);
				}
			}
		}

		internal static void RefreshAllPinVisuals()
		{
			RecipePinInteraction[] array = Object.FindObjectsByType<RecipePinInteraction>((FindObjectsSortMode)0);
			for (int i = 0; i < array.Length; i++)
			{
				if ((Object)(object)array[i] != (Object)null)
				{
					array[i].RefreshPinVisual();
				}
			}
		}
	}
	internal static class PinSound
	{
		internal static void Play()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			if (PinConfig.PlayPinSound == null || !PinConfig.PlayPinSound.Value)
			{
				return;
			}
			try
			{
				InventoryGui instance = InventoryGui.instance;
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)instance != (Object)null && (Object)(object)localPlayer != (Object)null && instance.m_moveItemEffects != null)
				{
					instance.m_moveItemEffects.Create(((Component)localPlayer).transform.position, Quaternion.identity, (Transform)null, 1f, -1);
				}
			}
			catch
			{
			}
		}
	}
	internal sealed class PinnedPanelController : MonoBehaviour
	{
		internal sealed class EntryView
		{
			public RectTransform Root;

			public RectTransform ReqsRoot;

			public TextMeshProUGUI ArrowLabel;

			public string PrefabName;

			public string CollapseKey;

			public bool IsBuild;

			public bool Expanded = true;

			public readonly List<ReqView> Reqs = new List<ReqView>();

			public void ApplyExpanded(bool expanded)
			{
				//IL_0068: Unknown result type (might be due to invalid IL or missing references)
				Expanded = expanded;
				if ((Object)(object)ReqsRoot != (Object)null)
				{
					((Component)ReqsRoot).gameObject.SetActive(expanded);
				}
				if ((Object)(object)ArrowLabel != (Object)null)
				{
					((TMP_Text)ArrowLabel).text = (expanded ? "▼" : "▶");
					((Graphic)ArrowLabel).color = new Color(0.85f, 0.85f, 0.85f, 1f);
				}
			}
		}

		internal sealed class ReqView
		{
			public string SharedName;

			public int Need;

			public TextMeshProUGUI Label;
		}

		private const float CountRefreshInterval = 0.25f;

		private const float ProfileSyncInterval = 0.5f;

		private const float UnresolvedRetryInterval = 1f;

		private const float ItemIconSize = 40f;

		private const float MatIconSize = 26f;

		private const float ArrowSize = 22f;

		private RectTransform _root;

		private CanvasGroup _canvasGroup;

		private RectTransform _content;

		private Transform _boundParent;

		private RectTransform _contextMenu;

		private string _contextMenuPrefab;

		private bool _contextMenuIsBuild;

		private TextMeshProUGUI _contextMenuLabel;

		private static string _hoverTipText;

		private static bool _hoverTipVisible;

		private readonly List<EntryView> _entries = new List<EntryView>();

		private bool _dirty = true;

		private float _nextCountRefresh;

		private float _nextProfileSync;

		private float _nextUnresolvedRetry;

		private bool _hasUnresolved;

		private bool _wasInventoryOpen;

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

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

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

		private void Awake()
		{
			PinIcon.Ensure();
			PinStore.PinsChanged += OnPinsChanged;
			PinConfig.Changed += OnConfigChanged;
		}

		private void OnDestroy()
		{
			PinStore.PinsChanged -= OnPinsChanged;
			PinConfig.Changed -= OnConfigChanged;
			DestroyRoot();
			HideMaterialTip();
		}

		private void OnPinsChanged()
		{
			HideContextMenu();
			_dirty = true;
			RecipePinInteraction.RefreshAllPinVisuals();
		}

		private void OnConfigChanged()
		{
			_dirty = true;
			ApplyScale();
		}

		private void Update()
		{
			if (Time.unscaledTime >= _nextProfileSync)
			{
				_nextProfileSync = Time.unscaledTime + 0.5f;
				ProfileLifecyclePatches.SyncProfile();
			}
			EnsureHudBinding();
			if ((Object)(object)_root == (Object)null)
			{
				return;
			}
			bool flag = ShouldShowPanel();
			_canvasGroup.alpha = (flag ? 1f : 0f);
			_canvasGroup.blocksRaycasts = flag;
			_canvasGroup.interactable = flag;
			if (!flag)
			{
				HideContextMenu();
				HideMaterialTip();
				return;
			}
			if (_dirty || (_hasUnresolved && Time.unscaledTime >= _nextUnresolvedRetry))
			{
				HideContextMenu();
				HideMaterialTip();
				RebuildPanel();
			}
			if (Time.unscaledTime >= _nextCountRefresh)
			{
				_nextCountRefresh = Time.unscaledTime + 0.25f;
				RefreshCounts();
			}
			UpdatePosition();
			bool flag2 = IsInventoryOpen();
			if (_wasInventoryOpen && !flag2)
			{
				HideContextMenu();
			}
			_wasInventoryOpen = flag2;
			if ((Object)(object)_contextMenu != (Object)null && ((Component)_contextMenu).gameObject.activeSelf && (Input.GetKeyDown((KeyCode)27) || Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(2)) && !IsPointerOverContextMenu())
			{
				HideContextMenu();
			}
		}

		private bool IsPointerOverContextMenu()
		{
			//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_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_003d: Expected O, but got Unknown
			//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)
			if ((Object)(object)_contextMenu == (Object)null || !Object.op_Implicit((Object)(object)EventSystem.current))
			{
				return false;
			}
			List<RaycastResult> list = new List<RaycastResult>();
			PointerEventData val = new PointerEventData(EventSystem.current)
			{
				position = Vector2.op_Implicit(Input.mousePosition)
			};
			EventSystem.current.RaycastAll(val, list);
			foreach (RaycastResult item in list)
			{
				RaycastResult current = item;
				if ((Object)(object)((RaycastResult)(ref current)).gameObject != (Object)null && ((RaycastResult)(ref current)).gameObject.transform.IsChildOf((Transform)(object)_contextMenu))
				{
					return true;
				}
			}
			return false;
		}

		private Transform ResolveParent()
		{
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)instance.m_smallRoot != (Object)null)
			{
				return instance.m_smallRoot.transform.parent;
			}
			return null;
		}

		private void EnsureHudBinding()
		{
			if (PinConfig.Enabled != null && !PinConfig.Enabled.Value)
			{
				DestroyRoot();
				return;
			}
			Transform val = ResolveParent();
			if ((!((Object)(object)val == (Object)null) || (!((Object)(object)_root == (Object)null) && !((Object)(object)_boundParent == (Object)null))) && (!((Object)(object)_root != (Object)null) || !((Object)(object)_boundParent == (Object)(object)val)))
			{
				DestroyRoot();
				if (!((Object)(object)val == (Object)null))
				{
					BuildRoot(val);
				}
			}
		}

		private void BuildRoot(Transform parent)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Expected O, but got Unknown
			_boundParent = parent;
			GameObject val = new GameObject("PinThoseRecipes_PanelRoot", new Type[2]
			{
				typeof(RectTransform),
				typeof(CanvasGroup)
			});
			_root = val.GetComponent<RectTransform>();
			((Transform)_root).SetParent(parent, false);
			UiUtils.SetAnchors(_root, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(1f, 1f));
			_root.sizeDelta = new Vector2(0f, 0f);
			((Transform)_root).localScale = Vector3.one;
			Canvas obj = val.AddComponent<Canvas>();
			obj.overrideSorting = true;
			obj.sortingOrder = 1200;
			val.AddComponent<GraphicRaycaster>();
			_canvasGroup = val.GetComponent<CanvasGroup>();
			_canvasGroup.alpha = 0f;
			_canvasGroup.blocksRaycasts = false;
			_canvasGroup.interactable = false;
			_content = UiUtils.NewRect((Transform)(object)_root, "Content");
			UiUtils.SetAnchors(_content, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(1f, 1f));
			_content.anchoredPosition = Vector2.zero;
			Image obj2 = ((Component)_content).gameObject.AddComponent<Image>();
			((Graphic)obj2).color = new Color(0f, 0f, 0f, 0.55f);
			((Graphic)obj2).raycastTarget = false;
			VerticalLayoutGroup obj3 = ((Component)_content).gameObject.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj3).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj3).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj3).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj3).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj3).spacing = 8f;
			((LayoutGroup)obj3).padding = new RectOffset(10, 10, 8, 8);
			((LayoutGroup)obj3).childAlignment = (TextAnchor)2;
			ContentSizeFitter obj4 = ((Component)_content).gameObject.AddComponent<ContentSizeFitter>();
			obj4.horizontalFit = (FitMode)2;
			obj4.verticalFit = (FitMode)2;
			BuildContextMenu();
			ApplyScale();
			_dirty = true;
		}

		private void BuildContextMenu()
		{
			//IL_0026: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: 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_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Expected O, but got Unknown
			_contextMenu = UiUtils.NewRect((Transform)(object)_root, "ContextMenu");
			UiUtils.SetAnchors(_contextMenu, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f));
			_contextMenu.sizeDelta = new Vector2(150f, 42f);
			((Component)_contextMenu).gameObject.SetActive(false);
			Image obj = ((Component)_contextMenu).gameObject.AddComponent<Image>();
			((Graphic)obj).color = new Color(0.1f, 0.09f, 0.07f, 0.96f);
			((Graphic)obj).raycastTarget = true;
			Outline obj2 = ((Component)_contextMenu).gameObject.AddComponent<Outline>();
			((Shadow)obj2).effectColor = new Color(0.85f, 0.72f, 0.35f, 1f);
			((Shadow)obj2).effectDistance = new Vector2(1.5f, -1.5f);
			RectTransform val = UiUtils.NewRect((Transform)(object)_contextMenu, "UnpinButton");
			UiUtils.SetAnchors(val, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f));
			val.offsetMin = new Vector2(3f, 3f);
			val.offsetMax = new Vector2(-3f, -3f);
			Image val2 = ((Component)val).gameObject.AddComponent<Image>();
			((Graphic)val2).color = new Color(0.18f, 0.16f, 0.12f, 1f);
			((Graphic)val2).raycastTarget = true;
			Button obj3 = ((Component)val).gameObject.AddComponent<Button>();
			((Selectable)obj3).targetGraphic = (Graphic)(object)val2;
			ColorBlock colors = ((Selectable)obj3).colors;
			((ColorBlock)(ref colors)).normalColor = new Color(0.18f, 0.16f, 0.12f, 1f);
			((ColorBlock)(ref colors)).highlightedColor = new Color(0.32f, 0.28f, 0.18f, 1f);
			((ColorBlock)(ref colors)).pressedColor = new Color(0.45f, 0.3f, 0.12f, 1f);
			((ColorBlock)(ref colors)).selectedColor = ((ColorBlock)(ref colors)).highlightedColor;
			((Selectable)obj3).colors = colors;
			UnpinMenuItem unpinMenuItem = ((Component)val).gameObject.AddComponent<UnpinMenuItem>();
			unpinMenuItem.Owner = this;
			((UnityEvent)obj3.onClick).AddListener(new UnityAction(unpinMenuItem.OnClick));
			_contextMenuLabel = null;
		}

		private void EnsureContextMenuLabel()
		{
			//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_008c: 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_00b6: 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)
			if ((Object)(object)_contextMenu == (Object)null || (Object)(object)_contextMenuLabel != (Object)null)
			{
				return;
			}
			Transform val = ((Transform)_contextMenu).Find("UnpinButton");
			if (!((Object)(object)val == (Object)null))
			{
				TextMeshProUGUI componentInChildren = ((Component)val).GetComponentInChildren<TextMeshProUGUI>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					_contextMenuLabel = componentInChildren;
					return;
				}
				_contextMenuLabel = UiUtils.NewText(val, "Label", 20f, (TextAlignmentOptions)514);
				UiUtils.SetAnchors(((TMP_Text)_contextMenuLabel).rectTransform, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f));
				((TMP_Text)_contextMenuLabel).rectTransform.offsetMin = Vector2.zero;
				((TMP_Text)_contextMenuLabel).rectTransform.offsetMax = Vector2.zero;
				((TMP_Text)_contextMenuLabel).text = "Unpin";
				((TMP_Text)_contextMenuLabel).textWrappingMode = (TextWrappingModes)0;
				((TMP_Text)_contextMenuLabel).overflowMode = (TextOverflowModes)0;
				((TMP_Text)_contextMenuLabel).outlineWidth = 0.2f;
				((TMP_Text)_contextMenuLabel).outlineColor = new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue);
				((TMP_Text)_contextMenuLabel).fontStyle = (FontStyles)1;
				((Graphic)_contextMenuLabel).raycastTarget = false;
			}
		}

		private void StyleContextMenuLabel()
		{
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			EnsureContextMenuLabel();
			if ((Object)(object)_contextMenuLabel == (Object)null)
			{
				return;
			}
			TMP_FontAsset val = UiUtils.ResolveFont();
			if ((Object)(object)val != (Object)null)
			{
				((TMP_Text)_contextMenuLabel).font = val;
			}
			string text = null;
			if (!string.IsNullOrEmpty(_contextMenuPrefab))
			{
				ResolvedRecipe resolvedRecipe = (_contextMenuIsBuild ? PieceResolver.Resolve(_contextMenuPrefab) : RecipeResolver.Resolve(_contextMenuPrefab));
				if (resolvedRecipe != null && !string.IsNullOrEmpty(resolvedRecipe.DisplayName))
				{
					text = resolvedRecipe.DisplayName;
				}
			}
			((TMP_Text)_contextMenuLabel).text = (string.IsNullOrEmpty(text) ? "Unpin" : ("Unpin " + text));
			((Graphic)_contextMenuLabel).color = new Color(1f, 0.88f, 0.35f, 1f);
			((Behaviour)_contextMenuLabel).enabled = true;
			((Component)_contextMenuLabel).gameObject.SetActive(true);
			((TMP_Text)_contextMenuLabel).ForceMeshUpdate(true, false);
			if ((Object)(object)_contextMenu != (Object)null)
			{
				float num = Mathf.Clamp(((TMP_Text)_contextMenuLabel).preferredWidth + 28f, 150f, 420f);
				_contextMenu.sizeDelta = new Vector2(num, 42f);
			}
		}

		private void ApplyScale()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_content != (Object)null && PinConfig.PanelScale != null)
			{
				float num = Mathf.Clamp(PinConfig.PanelScale.Value, 0.25f, 3f);
				((Transform)_content).localScale = new Vector3(num, num, 1f);
			}
		}

		private void DestroyRoot()
		{
			_entries.Clear();
			HideContextMenu();
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_root).gameObject);
			}
			_root = null;
			_content = null;
			_canvasGroup = null;
			_boundParent = null;
			_contextMenu = null;
			_contextMenuPrefab = null;
			_contextMenuIsBuild = false;
			_contextMenuLabel = null;
		}

		private bool ShouldShowPanel()
		{
			if (PinConfig.Enabled != null && !PinConfig.Enabled.Value)
			{
				return false;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).InCutscene())
			{
				return false;
			}
			if ((Object)(object)Hud.instance == (Object)null || Hud.IsUserHidden())
			{
				return false;
			}
			if ((Object)(object)Minimap.instance == (Object)null || Minimap.IsOpen())
			{
				return false;
			}
			if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
			{
				return false;
			}
			if (Console.IsVisible() || Menu.IsVisible())
			{
				return false;
			}
			if (_entries.Count <= 0)
			{
				return PinStore.TotalPinCount > 0;
			}
			return true;
		}

		private void RebuildPanel()
		{
			_dirty = false;
			_hasUnresolved = false;
			if ((Object)(object)_content == (Object)null)
			{
				return;
			}
			foreach (EntryView entry in _entries)
			{
				if ((Object)(object)entry.Root != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)entry.Root).gameObject);
				}
			}
			_entries.Clear();
			List<(ResolvedRecipe, bool)> list = new List<(ResolvedRecipe, bool)>();
			foreach (string pin in PinStore.GetPins())
			{
				ResolvedRecipe resolvedRecipe = RecipeResolver.Resolve(pin);
				if (resolvedRecipe == null)
				{
					_hasUnresolved = true;
				}
				else
				{
					list.Add((resolvedRecipe, false));
				}
			}
			foreach (string buildPin in PinStore.GetBuildPins())
			{
				ResolvedRecipe resolvedRecipe2 = PieceResolver.Resolve(buildPin);
				if (resolvedRecipe2 == null)
				{
					_hasUnresolved = true;
				}
				else
				{
					list.Add((resolvedRecipe2, true));
				}
			}
			int num = Mathf.Min((PinConfig.MaxPinnedRecipes != null) ? PinConfig.MaxPinnedRecipes.Value : 8, list.Count);
			for (int i = 0; i < num; i++)
			{
				_entries.Add(BuildEntry(list[i].Item1, list[i].Item2));
			}
			int num2 = list.Count - num;
			if (num2 > 0)
			{
				BuildMoreRow(num2);
			}
			if (_hasUnresolved)
			{
				_nextUnresolvedRetry = Time.unscaledTime + 1f;
			}
			RefreshCounts();
		}

		private EntryView BuildEntry(ResolvedRecipe recipe, bool isBuild)
		{
			//IL_008c: 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_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Expected O, but got Unknown
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			EntryView entryView = new EntryView
			{
				PrefabName = recipe.PrefabName,
				IsBuild = isBuild,
				CollapseKey = (isBuild ? PinStore.BuildCollapseKey(recipe.PrefabName) : PinStore.CraftCollapseKey(recipe.PrefabName))
			};
			RectTransform val = (entryView.Root = UiUtils.NewRect((Transform)(object)_content, "Entry_" + (isBuild ? "B_" : "C_") + recipe.PrefabName));
			Image obj = ((Component)val).gameObject.AddComponent<Image>();
			((Graphic)obj).color = new Color(1f, 1f, 1f, 0f);
			((Graphic)obj).raycastTarget = true;
			PanelEntryInteraction panelEntryInteraction = ((Component)val).gameObject.AddComponent<PanelEntryInteraction>();
			panelEntryInteraction.PrefabName = recipe.PrefabName;
			panelEntryInteraction.IsBuild = isBuild;
			panelEntryInteraction.Owner = this;
			VerticalLayoutGroup obj2 = ((Component)val).gameObject.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj2).spacing = 2f;
			((LayoutGroup)obj2).childAlignment = (TextAnchor)0;
			RectTransform val2 = UiUtils.NewRect((Transform)(object)val, "Header");
			HorizontalLayoutGroup obj3 = ((Component)val2).gameObject.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj3).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj3).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj3).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj3).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj3).spacing = 4f;
			((LayoutGroup)obj3).childAlignment = (TextAnchor)3;
			TextMeshProUGUI val3 = UiUtils.NewText((Transform)(object)val2, "Arrow", 18f, (TextAlignmentOptions)4098);
			((Graphic)val3).raycastTarget = true;
			AddLayoutSize(((Component)val3).gameObject, 22f, 40f);
			((Component)val3).gameObject.AddComponent<ExpandArrowInteraction>().Entry = entryView;
			entryView.ArrowLabel = val3;
			AddLayoutSize(((Component)UiUtils.NewImage((Transform)(object)val2, "Icon", recipe.Icon)).gameObject, 40f, 40f);
			if (isBuild)
			{
				AddLayoutSize(((Component)UiUtils.NewImage((Transform)(object)val2, "BuildBadge", PinIcon.BuildSprite)).gameObject, 16f, 16f);
			}
			string text = recipe.DisplayName;
			if (!isBuild && !recipe.IsUpgrade && recipe.OutputAmount > 1)
			{
				text = text + " x" + recipe.OutputAmount;
			}
			TextMeshProUGUI obj4 = UiUtils.NewText((Transform)(object)val2, "Name", 18f, (TextAlignmentOptions)4097);
			((TMP_Text)obj4).text = text;
			((Graphic)obj4).color = PinConfig.NameColor;
			AddLayoutHeight(((Component)obj4).gameObject, 40f);
			RectTransform val4 = (entryView.ReqsRoot = UiUtils.NewRect((Transform)(object)val, "Requirements"));
			VerticalLayoutGroup obj5 = ((Component)val4).gameObject.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj5).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj5).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj5).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj5).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj5).spacing = 2f;
			((LayoutGroup)obj5).childAlignment = (TextAnchor)0;
			((LayoutGroup)obj5).padding = new RectOffset(26, 0, 0, 0);
			foreach (ResolvedRequirement requirement in recipe.Requirements)
			{
				RectTransform obj6 = UiUtils.NewRect((Transform)(object)val4, "Req");
				Image obj7 = ((Component)obj6).gameObject.AddComponent<Image>();
				((Graphic)obj7).color = new Color(1f, 1f, 1f, 0f);
				((Graphic)obj7).raycastTarget = true;
				string topic = ((!string.IsNullOrEmpty(requirement.DisplayName)) ? requirement.DisplayName : (requirement.SharedName ?? ""));
				((Component)obj6).gameObject.AddComponent<ReqHoverProbe>().Topic = topic;
				HorizontalLayoutGroup obj8 = ((Component)obj6).gameObject.AddComponent<HorizontalLayoutGroup>();
				((HorizontalOrVerticalLayoutGroup)obj8).childControlWidth = true;
				((HorizontalOrVerticalLayoutGroup)obj8).childControlHeight = true;
				((HorizontalOrVerticalLayoutGroup)obj8).childForceExpandWidth = false;
				((HorizontalOrVerticalLayoutGroup)obj8).childForceExpandHeight = false;
				((HorizontalOrVerticalLayoutGroup)obj8).spacing = 4f;
				((LayoutGroup)obj8).childAlignment = (TextAnchor)3;
				AddLayoutSize(((Component)UiUtils.NewImage((Transform)(object)obj6, "MatIcon", requirement.Icon)).gameObject, 26f, 26f);
				TextMeshProUGUI val5 = UiUtils.NewText((Transform)(object)obj6, "Count", 16f, (TextAlignmentOptions)4097);
				AddLayoutHeight(((Component)val5).gameObject, 26f);
				entryView.Reqs.Add(new ReqView
				{
					SharedName = requirement.SharedName,
					Need = requirement.Need,
					Label = val5
				});
			}
			entryView.ApplyExpanded(!PinStore.IsCollapsed(entryView.CollapseKey));
			return entryView;
		}

		private void BuildMoreRow(int extra)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = UiUtils.NewRect((Transform)(object)_content, "MoreRow");
			HorizontalLayoutGroup obj = ((Component)val).gameObject.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			((LayoutGroup)obj).childAlignment = (TextAnchor)5;
			TextMeshProUGUI obj2 = UiUtils.NewText((Transform)(object)val, "MoreLabel", 15f, (TextAlignmentOptions)4100);
			((TMP_Text)obj2).text = "+" + extra + " more";
			((Graphic)obj2).color = new Color(0.8f, 0.8f, 0.8f, 1f);
			AddLayoutHeight(((Component)obj2).gameObject, 20f);
			_entries.Add(new EntryView
			{
				Root = val,
				PrefabName = null
			});
		}

		private void RefreshCounts()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			Inventory val = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Humanoid)Player.m_localPlayer).GetInventory() : null);
			Color satisfiedColor = PinConfig.SatisfiedColor;
			Color missingColor = PinConfig.MissingColor;
			foreach (EntryView entry in _entries)
			{
				foreach (ReqView req in entry.Reqs)
				{
					if (!((Object)(object)req.Label == (Object)null))
					{
						int num = ((val != null) ? val.CountItems(req.SharedName, -1, true) : 0);
						((TMP_Text)req.Label).text = num + "/" + req.Need;
						((Graphic)req.Label).color = ((num >= req.Need) ? satisfiedColor : missingColor);
					}
				}
			}
		}

		internal static void ToggleCollapsed(EntryView view)
		{
			if (view != null && !string.IsNullOrEmpty(view.PrefabName))
			{
				bool expanded = PinStore.ToggleCollapsed(view.CollapseKey, view.IsBuild, view.PrefabName);
				view.ApplyExpanded(expanded);
			}
		}

		internal void ShowContextMenu(string prefabName, bool isBuild, Vector2 screenPos)
		{
			//IL_0081: 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_0092: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_contextMenu == (Object)null) && !((Object)(object)_root == (Object)null) && !string.IsNullOrEmpty(prefabName))
			{
				_contextMenuPrefab = prefabName;
				_contextMenuIsBuild = isBuild;
				((Component)_contextMenu).gameObject.SetActive(true);
				((Transform)_contextMenu).SetAsLastSibling();
				StyleContextMenuLabel();
				Camera val = null;
				Canvas componentInParent = ((Component)_root).GetComponentInParent<Canvas>();
				if ((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0)
				{
					val = componentInParent.worldCamera;
				}
				Vector2 anchoredPosition = default(Vector2);
				if (RectTransformUtility.ScreenPointToLocalPointInRectangle(_root, screenPos, val, ref anchoredPosition))
				{
					_contextMenu.anchoredPosition = anchoredPosition;
				}
			}
		}

		internal void HideContextMenu()
		{
			_contextMenuPrefab = null;
			_contextMenuIsBuild = false;
			if ((Object)(object)_contextMenu != (Object)null)
			{
				((Component)_contextMenu).gameObject.SetActive(false);
			}
		}

		internal void ConfirmUnpinFromMenu()
		{
			string contextMenuPrefab = _contextMenuPrefab;
			bool contextMenuIsBuild = _contextMenuIsBuild;
			HideContextMenu();
			if (string.IsNullOrEmpty(contextMenuPrefab) || !PinStore.HasActiveProfile)
			{
				return;
			}
			if (contextMenuIsBuild)
			{
				if (!PinStore.IsBuildPinned(contextMenuPrefab))
				{
					return;
				}
				PinStore.ToggleBuild(contextMenuPrefab);
			}
			else
			{
				if (!PinStore.IsPinned(contextMenuPrefab))
				{
					return;
				}
				PinStore.Toggle(contextMenuPrefab);
			}
			PinSound.Play();
		}

		private void UpdatePosition()
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: 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)
			if ((Object)(object)_root == (Object)null || (Object)(object)_boundParent == (Object)null)
			{
				return;
			}
			Transform boundParent = _boundParent;
			RectTransform val = (RectTransform)(object)((boundParent is RectTransform) ? boundParent : null);
			Minimap instance = Minimap.instance;
			RectTransform val2 = (RectTransform)(((Object)(object)instance != (Object)null && (Object)(object)instance.m_smallRoot != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
			float num = ((PinConfig.OffsetX != null) ? PinConfig.OffsetX.Value : (-14f));
			float num2 = ((PinConfig.ExtraOffsetBelowMinimap != null) ? PinConfig.ExtraOffsetBelowMinimap.Value : (-14f));
			float num3 = -260f;
			if ((Object)(object)val != (Object)null && ((Transform)val).lossyScale.y != 0f)
			{
				val.GetWorldCorners(_cornersParent);
				float y = _cornersParent[1].y;
				float num4 = float.NaN;
				if ((Object)(object)val2 != (Object)null && instance.m_smallRoot.activeInHierarchy)
				{
					val2.GetWorldCorners(_cornersMinimap);
					num4 = _cornersMinimap[0].y;
				}
				RectTransform activeShipHudRect = GetActiveShipHudRect();
				if ((Object)(object)activeShipHudRect != (Object)null)
				{
					activeShipHudRect.GetWorldCorners(_cornersShipHud);
					float y2 = _cornersShipHud[0].y;
					if (float.IsNaN(num4) || y2 < num4)
					{
						num4 = y2;
					}
				}
				if (!float.IsNaN(num4))
				{
					num3 = 0f - (y - num4) / ((Transform)val).lossyScale.y;
				}
			}
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(num, num3 + num2);
			if (IsInventoryOpen())
			{
				float num5 = ((PinConfig.InventoryOpenShiftLeft != null) ? PinConfig.InventoryOpenShiftLeft.Value : 640f);
				float num6 = ((PinConfig.InventoryOpenShiftDown != null) ? PinConfig.InventoryOpenShiftDown.Value : 170f);
				val3 += new Vector2(0f - num5, 0f - num6);
			}
			_root.anchoredPosition = val3;
		}

		private static RectTransform GetActiveShipHudRect()
		{
			Hud instance = Hud.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			if ((Object)(object)instance.m_shipHudRoot != (Object)null && instance.m_shipHudRoot.activeInHierarchy)
			{
				Transform transform = instance.m_shipHudRoot.transform;
				RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				if (val != null)
				{
					return val;
				}
			}
			if ((Object)(object)instance.m_shipWindIndicatorRoot != (Object)null && ((Component)instance.m_shipWindIndicatorRoot).gameObject.activeInHierarchy)
			{
				return instance.m_shipWindIndicatorRoot;
			}
			return null;
		}

		private static bool IsInventoryOpen()
		{
			return InventoryGui.IsVisible();
		}

		private void OnGUI()
		{
			//IL_0014: 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_001a: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_0071: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_00a2: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: 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_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			if (_hoverTipVisible && !string.IsNullOrEmpty(_hoverTipText))
			{
				Vector3 mousePosition = Input.mousePosition;
				float num = mousePosition.x + 16f;
				float num2 = (float)Screen.height - mousePosition.y + 16f;
				GUIStyle val = new GUIStyle(GUI.skin.box)
				{
					fontSize = 16,
					alignment = (TextAnchor)3,
					wordWrap = false,
					padding = new RectOffset(10, 10, 6, 6)
				};
				val.normal.textColor = new Color(1f, 0.88f, 0.35f, 1f);
				GUIContent val2 = new GUIContent(_hoverTipText);
				Vector2 val3 = val.CalcSize(val2);
				val3.x = Mathf.Clamp(val3.x + 4f, 60f, 420f);
				val3.y = Mathf.Max(val3.y, 28f);
				GUI.Box(new Rect(num, num2, val3.x, val3.y), val2, val);
			}
		}

		internal static void ShowMaterialTip(string displayName)
		{
			if (!string.IsNullOrEmpty(displayName))
			{
				_hoverTipText = displayName;
				_hoverTipVisible = true;
			}
		}

		internal static void HideMaterialTip()
		{
			_hoverTipVisible = false;
			_hoverTipText = null;
		}

		private static void AddLayoutSize(GameObject go, float w, float h)
		{
			LayoutElement obj = go.AddComponent<LayoutElement>();
			obj.preferredWidth = w;
			obj.preferredHeight = h;
			obj.minWidth = w;
			obj.minHeight = h;
		}

		private static void AddLayoutHeight(GameObject go, float h)
		{
			LayoutElement obj = go.AddComponent<LayoutElement>();
			obj.preferredHeight = h;
			obj.minHeight = h;
		}
	}
	internal sealed class ReqHoverProbe : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler
	{
		internal string Topic;

		public void OnPointerEnter(PointerEventData eventData)
		{
			PinnedPanelController.ShowMaterialTip(Topic);
		}

		public void OnPointerExit(PointerEventData eventData)
		{
			PinnedPanelController.HideMaterialTip();
		}
	}
	internal sealed class ExpandArrowInteraction : MonoBehaviour, IPointerClickHandler, IEventSystemHandler
	{
		internal PinnedPanelController.EntryView Entry;

		public void OnPointerClick(PointerEventData eventData)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			if (eventData != null && (int)eventData.button == 0 && Entry != null && !string.IsNullOrEmpty(Entry.PrefabName))
			{
				PinnedPanelController.ToggleCollapsed(Entry);
				((AbstractEventData)eventData).Use();
			}
		}
	}
	internal sealed class PanelEntryInteraction : MonoBehaviour, IPointerClickHandler, IEventSystemHandler
	{
		internal string PrefabName;

		internal bool IsBuild;

		internal PinnedPanelController Owner;

		public void OnPointerClick(PointerEventData eventData)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Invalid comparison between Unknown and I4
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if (eventData != null && (int)eventData.button == 1 && !string.IsNullOrEmpty(PrefabName) && !((Object)(object)Owner == (Object)null) && PinStore.HasActiveProfile)
			{
				Owner.ShowContextMenu(PrefabName, IsBuild, eventData.position);
				((AbstractEventData)eventData).Use();
			}
		}
	}
	internal sealed class UnpinMenuItem : MonoBehaviour
	{
		internal PinnedPanelController Owner;

		public void OnClick()
		{
			Owner?.ConfirmUnpinFromMenu();
		}
	}
}
namespace PinThoseRecipes.Patches
{
	internal static class InventoryGuiRecipeListPatches
	{
		[HarmonyPatch]
		private static class AddRecipeToListPatch
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(InventoryGui), "AddRecipeToList", new Type[4]
				{
					typeof(Player),
					typeof(Recipe),
					typeof(ItemData),
					typeof(bool)
				}, (Type[])null);
			}

			private static bool Prepare()
			{
				if (TargetMethod() == null)
				{
					ManualLogSource log = PinThoseRecipesPlugin.Log;
					if (log != null)
					{
						log.LogError((object)"InventoryGui.AddRecipeToList(Player,Recipe,ItemData,bool) not found; pin icons in the craft list are disabled.");
					}
					return false;
				}
				return true;
			}

			private static void Prefix(InventoryGui __instance, out int __state)
			{
				__state = GetAvailableRecipes(__instance)?.Count ?? 0;
			}

			private static void Postfix(InventoryGui __instance, Recipe recipe, ItemData item, int __state)
			{
				if (PinConfig.Enabled != null && !PinConfig.Enabled.Value)
				{
					return;
				}
				IList availableRecipes = GetAvailableRecipes(__instance);
				if (availableRecipes == null || availableRecipes.Count <= __state)
				{
					return;
				}
				EnsurePairAccessors(availableRecipes[__state]);
				GameObject val = null;
				for (int i = __state; i < availableRecipes.Count; i++)
				{
					if (_getPairRecipe(availableRecipes[i]) == recipe)
					{
						val = _getPairElement(availableRecipes[i]);
						break;
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					val = _getPairElement(availableRecipes[__state]);
				}
				if (!((Object)(object)val == (Object)null))
				{
					AttachPin(__instance, val, recipe, item);
				}
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")]
		private static class UpdateRecipeListPatch
		{
			private static void Postfix(InventoryGui __instance)
			{
				if (PinConfig.Enabled == null || PinConfig.Enabled.Value)
				{
					ReorderPinned(__instance);
				}
			}
		}

		private struct Entry
		{
			public object Pair;

			public GameObject Element;

			public string PinKey;

			public int Original;
		}

		internal static bool IsReordering;

		private static FieldInfo _fAvailableRecipes;

		private static Func<object, Recipe> _getPairRecipe;

		private static Func<object, ItemData> _getPairItemData;

		private static Func<object, GameObject> _getPairElement;

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

		private static IList GetAvailableRecipes(InventoryGui gui)
		{
			return AvailableRecipesField?.GetValue(gui) as IList;
		}

		private static void EnsurePairAccessors(object samplePair)
		{
			if (_getPairRecipe != null || samplePair == null)
			{
				return;
			}
			Type type = samplePair.GetType();
			PropertyInfo pRecipe = AccessTools.Property(type, "Recipe");
			if (pRecipe != null)
			{
				_getPairRecipe = delegate(object o)
				{
					object? value = pRecipe.GetValue(o, null);
					return (Recipe)((value is Recipe) ? value : null);
				};
			}
			else
			{
				FieldInfo f = AccessTools.Field(type, "Recipe") ?? AccessTools.Field(type, "<Recipe>k__BackingField");
				_getPairRecipe = delegate(object o)
				{
					object? obj = f?.GetValue(o);
					return (Recipe)((obj is Recipe) ? obj : null);
				};
			}
			PropertyInfo pItem = AccessTools.Property(type, "ItemData");
			if (pItem != null)
			{
				_getPairItemData = delegate(object o)
				{
					object? value = pItem.GetValue(o, null);
					return (ItemData)((value is ItemData) ? value : null);
				};
			}
			else
			{
				FieldInfo f2 = AccessTools.Field(type, "ItemData") ?? AccessTools.Field(type, "<ItemData>k__BackingField");
				_getPairItemData = delegate(object o)
				{
					object? obj = f2?.GetValue(o);
					return (ItemData)((obj is ItemData) ? obj : null);
				};
			}
			PropertyInfo pElement = AccessTools.Property(type, "InterfaceElement");
			if (pElement != null)
			{
				_getPairElement = delegate(object o)
				{
					object? value = pElement.GetValue(o, null);
					return (GameObject)((value is GameObject) ? value : null);
				};
				return;
			}
			FieldInfo f3 = AccessTools.Field(type, "InterfaceElement") ?? AccessTools.Field(type, "<InterfaceElement>k__BackingField");
			_getPairElement = delegate(object o)
			{
				object? obj = f3?.GetValue(o);
				return (GameObject)((obj is GameObject) ? obj : null);
			};
		}

		private static string PrefabNameOf(Recipe recipe)
		{
			if (!((Object)(object)recipe != (Object)null) || !((Object)(object)recipe.m_item != (Object)null))
			{
				return null;
			}
			return ((Object)((Component)recipe.m_item).gameObject).name;
		}

		internal static string PinKeyFor(Recipe recipe, ItemData upgradeItem)
		{
			string text = PrefabNameOf(recipe);
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			if (upgradeItem != null && upgradeItem.m_quality >= 1)
			{
				int num = upgradeItem.m_quality + 1;
				if (num >= 2)
				{
					return PinStore.UpgradePinKey(text, num);
				}
			}
			return PinStore.CraftPinKey(text);
		}

		private static void AttachPin(InventoryGui gui, GameObject element, Recipe recipe, ItemData upgradeItem)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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)
			RecipePinInteraction recipePinInteraction = element.GetComponent<RecipePinInteraction>() ?? element.AddComponent<RecipePinInteraction>();
			recipePinInteraction.Owner = gui;
			recipePinInteraction.Recipe = recipe;
			recipePinInteraction.PinKey = PinKeyFor(recipe, upgradeItem);
			if ((Object)(object)recipePinInteraction.PinImage == (Object)null)
			{
				Image val = UiUtils.NewImage(element.transform, "PinThoseRecipes_PinIcon", PinIcon.Sprite);
				RectTransform rectTransform = ((Graphic)val).rectTransform;
				UiUtils.SetAnchors(rectTransform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(1f, 1f));
				rectTransform.sizeDelta = new Vector2(20f, 20f);
				rectTransform.anchoredPosition = new Vector2(-4f, -4f);
				((Transform)rectTransform).SetAsLastSibling();
				recipePinInteraction.PinImage = val;
			}
			else if ((Object)(object)recipePinInteraction.PinImage.sprite == (Object)null)
			{
				recipePinInteraction.PinImage.sprite = PinIcon.Sprite;
			}
			recipePinInteraction.RefreshPinVisual();
		}

		private static void ReorderPinned(InventoryGui gui)
		{
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			if (IsReordering)
			{
				return;
			}
			IList availableRecipes = GetAvailableRecipes(gui);
			if (availableRecipes == null || availableRecipes.Count == 0)
			{
				return;
			}
			IsReordering = true;
			try
			{
				EnsurePairAccessors(availableRecipes[0]);
				float recipeListSpace = gui.m_recipeListSpace;
				int count = availableRecipes.Count;
				List<Entry> list = new List<Entry>(count);
				for (int i = 0; i < count; i++)
				{
					object obj = availableRecipes[i];
					Recipe recipe = _getPairRecipe(obj);
					ItemData upgradeItem = ((_getPairItemData != null) ? _getPairItemData(obj) : null);
					list.Add(new Entry
					{
						Pair = obj,
						Element = _getPairElement(obj),
						PinKey = PinKeyFor(recipe, upgradeItem),
						Original = i
					});
				}
				List<Entry> list2 = (from e in list
					orderby (!PinStore.IsPinned(e.PinKey)) ? 1 : 0, (!PinStore.IsPinned(e.PinKey)) ? e.Original : PinStore.GetPinIndex(e.PinKey)
					select e).ToList();
				for (int num = 0; num < list2.Count; num++)
				{
					Entry entry = list2[num];
					availableRecipes[num] = entry.Pair;
					if ((Object)(object)entry.Element != (Object)null)
					{
						entry.Element.transform.SetSiblingIndex(num);
						Transform transform = entry.Element.transform;
						RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
						if (val != null)
						{
							val.anchoredPosition = new Vector2(0f, (float)num * (0f - recipeListSpace));
						}
						entry.Element.GetComponent<RecipePinInteraction>()?.RefreshPinVisual();
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = PinThoseRecipesPlugin.Log;
				if (log != null)
				{
					log.LogError((object)("Reorder failed: " + ex));
				}
			}
			finally
			{
				IsReordering = false;
			}
		}
	}
	internal static class ProfileLifecyclePatches
	{
		[HarmonyPatch(typeof(Game), "Start")]
		private static class GameStartPatch
		{
			private static void Postfix()
			{
				SyncProfile();
			}
		}

		[HarmonyPatch(typeof(Game), "Logout")]
		private static class GameLogoutPatch
		{
			private static bool Prepare()
			{
				return AccessTools.Method(typeof(Game), "Logout", (Type[])null, (Type[])null) != null;
			}

			private static void Prefix()
			{
				PinStore.ClearActive();
				RecipeResolver.InvalidateCache();
			}
		}

		internal static void SyncProfile()
		{
			Game instance = Game.instance;
			if ((Object)(object)instance == (Object)null)
			{
				PinStore.ClearActive();
				return;
			}
			PlayerProfile playerProfile = instance.GetPlayerProfile();
			string text = ((playerProfile != null) ? playerProfile.GetFilename() : null);
			if (string.IsNullOrEmpty(text))
			{
				text = ((playerProfile != null) ? playerProfile.GetName() : null);
			}
			if (!string.IsNullOrEmpty(text))
			{
				PinStore.LoadForProfile(text);
			}
		}
	}
	internal static class ObjectDbPatches
	{
		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class ObjectDbAwakePatch
		{
			private static void Postfix()
			{
				RecipeResolver.InvalidateCache();
				PieceResolver.InvalidateCache();
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		private static class ObjectDbCopyOtherPatch
		{
			private static bool Prepare()
			{
				return AccessTools.Method(typeof(ObjectDB), "CopyOtherDB", (Type[])null, (Type[])null) != null;
			}

			private static void Postfix()
			{
				RecipeResolver.InvalidateCache();
				PieceResolver.InvalidateCache();
			}
		}
	}
	internal static class HudPiecePatches
	{
		[HarmonyPatch(typeof(Hud), "OnRightClickPiece")]
		private static class OnRightClickPiecePatch
		{
			private static bool Prepare()
			{
				return AccessTools.Method(typeof(Hud), "OnRightClickPiece", (Type[])null, (Type[])null) != null;
			}

			private static bool Prefix(Hud __instance, UIInputHandler ih)
			{
				//IL_0071: Unknown result type (might be due to invalid IL or missing references)
				//IL_0076: Unknown result type (might be due to invalid IL or missing references)
				//IL_0088: Unknown result type (might be due to invalid IL or missing references)
				if (PinConfig.Enabled != null && !PinConfig.Enabled.Value)
				{
					return true;
				}
				if ((Object)(object)ih == (Object)null || !PinStore.HasActiveProfile || (Object)(object)Player.m_localPlayer == (Object)null)
				{
					return true;
				}
				MethodInfo methodInfo = AccessTools.Method(typeof(Hud), "GetSelectedGrid", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					return true;
				}
				if (!(methodInfo.Invoke(__instance, new object[1] { ih }) is Vector2Int val) || ((Vector2Int)(ref val)).x < 0)
				{
					return true;
				}
				Piece piece = Player.m_localPlayer.GetPiece(val);
				if ((Object)(object)piece == (Object)null)
				{
					return true;
				}
				string text = Utils.GetPrefabName(((Component)piece).gameObject);
				if (string.IsNullOrEmpty(text))
				{
					text = ((Object)((Component)piece).gameObject).name;
				}
				PinStore.ToggleBuild(text);
				PinSound.Play();
				PiecePinOverlay.Refresh(((Component)ih).gameObject, text);
				return false;
			}
		}

		[HarmonyPatch(typeof(Hud), "UpdatePieceList")]
		private static class UpdatePieceListPatch
		{
			private static bool Prepare()
			{
				return AccessTools.Method(typeof(Hud), "UpdatePieceList", (Type[])null, (Type[])null) != null;
			}

			private static void Postfix(Hud __instance)
			{
				if (PinConfig.Enabled != null && !PinConfig.Enabled.Value)
				{
					return;
				}
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || !(PieceIconsField?.GetValue(__instance) is IList { Count: not 0 } list))
				{
					return;
				}
				if (_fGo == null && list.Count > 0)
				{
					_fGo = AccessTools.Field(list[0].GetType(), "m_go");
				}
				List<Piece> buildPieces = localPlayer.GetBuildPieces();
				for (int i = 0; i < list.Count; i++)
				{
					object? obj = _fGo?.GetValue(list[i]);
					GameObject val = (GameObject)((obj is GameObject) ? obj : null);
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					if (buildPieces != null && i < buildPieces.Count && (Object)(object)buildPieces[i] != (Object)null)
					{
						string text = Utils.GetPrefabName(((Component)buildPieces[i]).gameObject);
						if (string.IsNullOrEmpty(text))
						{
							text = ((Object)((Component)buildPieces[i]).gameObject).name;
						}
						PiecePinOverlay.Refresh(val, text);
					}
					else
					{
						PiecePinOverlay.Hide(val);
					}
				}
			}
		}

		private static FieldInfo _fPieceIcons;

		private static FieldInfo _fGo;

		private static FieldInfo PieceIconsField => _fPieceIcons ?? (_fPieceIcons = AccessTools.Field(typeof(Hud), "m_pieceIcons"));
	}
	internal static class PiecePinOverlay
	{
		private const string ChildName = "PinThoseRecipes_BuildPin";

		internal static void Refresh(GameObject pieceIconGo, string prefabName)
		{
			if (!((Object)(object)pieceIconGo == (Object)null))
			{
				Image val = EnsureImage(pieceIconGo);
				bool flag = PinStore.IsBuildPinned(prefabName);
				if (((Component)val).gameObject.activeSelf != flag)
				{
					((Component)val).gameObject.SetActive(flag);
				}
				if (flag && (Object)(object)val.sprite == (Object)null)
				{
					val.sprite = PinIcon.BuildSprite;
				}
			}
		}

		internal static void Hide(GameObject pieceIconGo)
		{
			if (!((Object)(object)pieceIconGo == (Object)null))
			{
				Transform val = pieceIconGo.transform.Find("PinThoseRecipes_BuildPin");
				if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf)
				{
					((Component)val).gameObject.SetActive(false);
				}
			}
		}

		private static Image EnsureImage(GameObject pieceIconGo)
		{
			//IL_004a: 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_0092: Unknown result type (might be due to invalid IL or missing references)
			Transform val = pieceIconGo.transform.Find("PinThoseRecipes_BuildPin");
			Image val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Image>() : UiUtils.NewImage(pieceIconGo.transform, "PinThoseRecipes_BuildPin", PinIcon.Buil