Decompiled source of RecipePinner v1.1.2

plugins/RecipePinner.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("PinRecipe")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PinRecipe")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("b7fff297-caca-412c-8cb0-52556a76bd3f")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace RecipePinner
{
	public class PinnedRecipe
	{
		public Recipe Recipe { get; private set; }

		public int CurrentCount { get; set; }

		public string OverrideName { get; private set; }

		public List<Requirement> OverrideRequirements { get; private set; }

		public int TargetQuality { get; private set; }

		public PinnedRecipe(Recipe recipe, int count = 1, string overrideName = null, List<Requirement> overrideRequirements = null, int targetQuality = 0)
		{
			Recipe = recipe;
			CurrentCount = count;
			OverrideName = overrideName;
			OverrideRequirements = overrideRequirements;
			TargetQuality = targetQuality;
		}

		public string GetName()
		{
			if (!string.IsNullOrEmpty(OverrideName))
			{
				return OverrideName;
			}
			return Recipe.m_item.m_itemData.m_shared.m_name;
		}

		public Sprite GetIcon()
		{
			return Recipe.m_item.m_itemData.m_shared.m_icons[0];
		}

		public Requirement[] GetRequirements()
		{
			if (OverrideRequirements != null)
			{
				return OverrideRequirements.ToArray();
			}
			return Recipe.m_resources;
		}
	}
}
namespace ValheimRecipePinner
{
	public class ContainerScanner
	{
		public static List<Container> AllContainers = new List<Container>();

		internal static readonly object ContainerLock = new object();

		public Dictionary<string, int> ContainerCache = new Dictionary<string, int>();

		private static HashSet<int> _processedIDs = new HashSet<int>();

		private Vector3 _lastScanPos;

		private int _lastItemCount = 0;

		private float _scanTimer = 0f;

		private const float MovementThresholdSqr = 4f;

		public void InitializeContainers()
		{
			DebugLogger.Log("Initializing container tracking...");
			lock (ContainerLock)
			{
				if (AllContainers.Count != 0)
				{
					return;
				}
				Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
				Container[] array2 = array;
				foreach (Container val in array2)
				{
					if ((Object)(object)val != (Object)null && !AllContainers.Contains(val))
					{
						AllContainers.Add(val);
						if ((Object)(object)((Component)val).GetComponent<ContainerTracker>() == (Object)null)
						{
							ContainerTracker containerTracker = ((Component)val).gameObject.AddComponent<ContainerTracker>();
							containerTracker.MyContainer = val;
						}
					}
				}
				DebugLogger.Log($"Initialized tracking for {AllContainers.Count} existing containers");
			}
		}

		public void UpdateScanning()
		{
			//IL_0033: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			_scanTimer += Time.deltaTime;
			float num = Vector3.SqrMagnitude(((Component)Player.m_localPlayer).transform.position - _lastScanPos);
			bool flag = num > 4f;
			int num2 = 0;
			foreach (ItemData allItem in ((Humanoid)Player.m_localPlayer).GetInventory().GetAllItems())
			{
				num2 += allItem.m_stack;
			}
			bool flag2 = num2 != _lastItemCount;
			bool flag3 = false;
			if ((Object)(object)InventoryGui.instance != (Object)null)
			{
				flag3 = (Object)(object)ReflectionHelper.GetCurrentContainer(InventoryGui.instance) != (Object)null;
			}
			float num3 = (flag3 ? 0.5f : RecipePinnerPlugin.ChestScanInterval.Value);
			if (flag || flag2 || _scanTimer >= num3)
			{
				_scanTimer = 0f;
				_lastItemCount = num2;
				DebugLogger.Verbose($"Scanning containers - Moved: {flag}, InvChanged: {flag2}, Interval: {_scanTimer >= num3}");
				UpdateContainerCache();
			}
		}

		private void UpdateContainerCache()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: 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)
			ContainerCache.Clear();
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				DebugLogger.Verbose("Cannot scan - player is null");
				return;
			}
			Vector3 position = ((Component)Player.m_localPlayer).transform.position;
			float value = RecipePinnerPlugin.ChestScanRange.Value;
			float num = value * value;
			List<Container> list;
			lock (ContainerLock)
			{
				list = new List<Container>(AllContainers);
			}
			_processedIDs.Clear();
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			foreach (Container item in list)
			{
				if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).transform == (Object)null)
				{
					num3++;
					continue;
				}
				int instanceID = ((Object)item).GetInstanceID();
				if (_processedIDs.Contains(instanceID))
				{
					num3++;
					continue;
				}
				_processedIDs.Add(instanceID);
				float num5 = Vector3.SqrMagnitude(((Component)item).transform.position - position);
				if (num5 > num)
				{
					num3++;
					continue;
				}
				bool flag = true;
				if (ReflectionHelper.CheckContainerAccess != null)
				{
					flag = ReflectionHelper.CheckContainerAccess(item, Player.m_localPlayer.GetPlayerID());
				}
				if (!flag)
				{
					num4++;
					continue;
				}
				Inventory inventory = item.GetInventory();
				if (inventory == null)
				{
					continue;
				}
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					string name = allItem.m_shared.m_name;
					if (ContainerCache.TryGetValue(name, out var value2))
					{
						ContainerCache[name] = value2 + allItem.m_stack;
					}
					else
					{
						ContainerCache[name] = allItem.m_stack;
					}
				}
				num2++;
			}
			_lastScanPos = position;
			DebugLogger.Verbose($"Container scan complete - Scanned: {num2}, Skipped: {num3}, AccessDenied: {num4}, UniqueItems: {ContainerCache.Count}");
		}

		[HarmonyPatch(typeof(Container), "Awake")]
		[HarmonyPostfix]
		public static void TrackContainerAwake(Container __instance)
		{
			if (!((Object)(object)__instance != (Object)null))
			{
				return;
			}
			lock (ContainerLock)
			{
				if (!AllContainers.Contains(__instance))
				{
					AllContainers.Add(__instance);
					ContainerTracker containerTracker = ((Component)__instance).gameObject.GetComponent<ContainerTracker>();
					if ((Object)(object)containerTracker == (Object)null)
					{
						containerTracker = ((Component)__instance).gameObject.AddComponent<ContainerTracker>();
					}
					containerTracker.MyContainer = __instance;
					DebugLogger.Verbose($"New container tracked: {((Object)__instance).name} (Total: {AllContainers.Count})");
				}
			}
		}
	}
	public class ContainerTracker : MonoBehaviour
	{
		public Container MyContainer;

		private void OnDestroy()
		{
			if (ContainerScanner.AllContainers != null && (Object)(object)MyContainer != (Object)null)
			{
				lock (ContainerScanner.ContainerLock)
				{
					ContainerScanner.AllContainers.Remove(MyContainer);
					DebugLogger.Verbose($"Container removed: {((Object)MyContainer).name} (Remaining: {ContainerScanner.AllContainers.Count})");
				}
			}
		}
	}
	public class DataPersistence
	{
		public void SavePins()
		{
			try
			{
				string savePath = GetSavePath();
				if (string.IsNullOrEmpty(savePath))
				{
					DebugLogger.Warning("Cannot save - save path is invalid");
					return;
				}
				RecipeManager recipeMgr = RecipePinnerPlugin.Instance.RecipeMgr;
				List<string> list = new List<string>();
				foreach (KeyValuePair<string, int> pinnedRecipe in recipeMgr.PinnedRecipes)
				{
					list.Add($"{pinnedRecipe.Key}:{pinnedRecipe.Value}");
				}
				File.WriteAllLines(savePath, list);
				DebugLogger.Log($"Saved {list.Count} pinned recipes to: {savePath}");
			}
			catch (Exception ex)
			{
				DebugLogger.Error("Failed to save pins", ex);
			}
		}

		public void LoadPins()
		{
			string savePath = GetSavePath();
			if (string.IsNullOrEmpty(savePath))
			{
				DebugLogger.Warning("Cannot load - save path is invalid");
				return;
			}
			RecipeManager recipeMgr = RecipePinnerPlugin.Instance.RecipeMgr;
			if (!File.Exists(savePath))
			{
				DebugLogger.Log("No save file found at: " + savePath);
				return;
			}
			try
			{
				recipeMgr.PinnedRecipes.Clear();
				string[] array = File.ReadAllLines(savePath);
				int num = 0;
				int num2 = 0;
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (string.IsNullOrWhiteSpace(text))
					{
						continue;
					}
					if (text.Contains(":"))
					{
						string[] array3 = text.Split(new char[1] { ':' });
						if (array3.Length == 2)
						{
							string key = array3[0].Trim();
							string s = array3[1].Trim();
							if (int.TryParse(s, out var result))
							{
								recipeMgr.PinnedRecipes[key] = result;
								num++;
							}
							else
							{
								DebugLogger.Warning("Invalid count value in save file: " + text);
								num2++;
							}
						}
					}
					else if (!recipeMgr.PinnedRecipes.ContainsKey(text))
					{
						recipeMgr.PinnedRecipes[text] = 1;
						num++;
					}
				}
				if (recipeMgr.PinnedRecipes.Count > RecipePinnerPlugin.MaximumPins.Value)
				{
					int count = recipeMgr.PinnedRecipes.Count;
					recipeMgr.PinnedRecipes = recipeMgr.PinnedRecipes.Take(RecipePinnerPlugin.MaximumPins.Value).ToDictionary((KeyValuePair<string, int> k) => k.Key, (KeyValuePair<string, int> v) => v.Value);
					DebugLogger.Warning($"Exceeded max pins limit - trimmed from {count} to {recipeMgr.PinnedRecipes.Count}");
				}
				DebugLogger.Log($"Loaded {num} recipes from: {savePath} (Errors: {num2})");
			}
			catch (Exception ex)
			{
				DebugLogger.Error("Failed to load pins", ex);
			}
		}

		private string GetSavePath()
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				DebugLogger.Verbose("Cannot get save path - local player is null");
				return null;
			}
			string playerName = Player.m_localPlayer.GetPlayerName();
			if (string.IsNullOrWhiteSpace(playerName))
			{
				DebugLogger.Warning("Cannot get save path - player name is empty");
				return null;
			}
			string text = Path.Combine(Paths.ConfigPath, "RecipePinner_Data");
			if (!Directory.Exists(text))
			{
				try
				{
					Directory.CreateDirectory(text);
					DebugLogger.Log("Created save directory: " + text);
				}
				catch (Exception ex)
				{
					DebugLogger.Error("Failed to create save directory: " + text, ex);
					return null;
				}
			}
			string text2 = playerName;
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char oldChar in invalidFileNameChars)
			{
				text2 = text2.Replace(oldChar, '_');
			}
			string text3 = Path.Combine(text, text2 + ".txt");
			DebugLogger.Verbose("Save path: " + text3);
			return text3;
		}
	}
	public static class DebugLogger
	{
		private const string Prefix = "[RecipePinner]";

		public static void Log(string message)
		{
			if (IsDebugEnabled())
			{
				Debug.Log((object)("[RecipePinner] " + message));
			}
		}

		public static void Warning(string message)
		{
			Debug.LogWarning((object)("[RecipePinner] " + message));
		}

		public static void Error(string message)
		{
			Debug.LogError((object)("[RecipePinner] " + message));
		}

		public static void Error(string message, Exception ex)
		{
			Debug.LogError((object)("[RecipePinner] " + message + "\nException: " + ex.Message + "\nStackTrace: " + ex.StackTrace));
		}

		public static void Verbose(string message)
		{
			if (IsDebugEnabled())
			{
				Debug.Log((object)("[RecipePinner] [VERBOSE] " + message));
			}
		}

		private static bool IsDebugEnabled()
		{
			return (Object)(object)RecipePinnerPlugin.Instance != (Object)null && RecipePinnerPlugin.EnableDebugLogging != null && RecipePinnerPlugin.EnableDebugLogging.Value;
		}
	}
	public class LocalizationManager
	{
		private RecipePinnerPlugin _plugin;

		private Dictionary<string, string> _localizedText = new Dictionary<string, string>();

		private static readonly Dictionary<string, string> _defaultEnglish = new Dictionary<string, string>
		{
			{ "pinned", "Recipe Pinned!" },
			{ "unpinned", "Pin Removed" },
			{ "list_full", "List Full!" },
			{ "added_more", "Added More: {0}x" },
			{ "decreased", "Decreased: {0}x" },
			{ "cleared", "Pinned Recipes Cleared" },
			{ "max_level", "Max Level Reached" },
			{ "no_upgrade_cost", "No upgrade cost found" }
		};

		public LocalizationManager(RecipePinnerPlugin plugin)
		{
			_plugin = plugin;
			DebugLogger.Log("LocalizationManager initialized");
		}

		public void LoadTranslations()
		{
			_localizedText.Clear();
			string text = RecipePinnerPlugin.LanguageOverride.Value.Trim();
			if (string.IsNullOrEmpty(text) || text.ToLower() == "auto")
			{
				text = ((Localization.instance == null) ? "English" : Localization.instance.GetSelectedLanguage());
				DebugLogger.Log("Auto-detected language: " + text);
			}
			else
			{
				DebugLogger.Log("Using forced language: " + text);
			}
			string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)_plugin).Info.Location);
			string text2 = Path.Combine(directoryName, "RecipePinner_languages", text + ".json");
			if (!File.Exists(text2))
			{
				DebugLogger.Log("Language file not found: " + text2 + " - Using default English");
				return;
			}
			try
			{
				string text3 = File.ReadAllText(text2);
				int num = 0;
				string[] array = text3.Split(new char[1] { '\n' });
				foreach (string text4 in array)
				{
					if (!text4.Contains(":"))
					{
						continue;
					}
					string[] array2 = text4.Split(new char[1] { ':' }, 2);
					if (array2.Length == 2)
					{
						string text5 = array2[0].Trim().Trim(',', '"', ' ', '\t', '\r');
						string value = array2[1].Trim().Trim(',', '"', ' ', '\t', '\r');
						if (!string.IsNullOrEmpty(text5) && !string.IsNullOrEmpty(value))
						{
							_localizedText[text5] = value;
							num++;
						}
					}
				}
				DebugLogger.Log($"Loaded {num} translations from: {text}.json");
			}
			catch (Exception ex)
			{
				DebugLogger.Error("Failed to load language file: " + text2, ex);
			}
		}

		public string GetText(string key)
		{
			if (_localizedText.TryGetValue(key, out var value))
			{
				DebugLogger.Verbose("Translation found for '" + key + "': " + value);
				return value;
			}
			if (_defaultEnglish.TryGetValue(key, out var value2))
			{
				DebugLogger.Verbose("Using default English for '" + key + "': " + value2);
				return value2;
			}
			DebugLogger.Warning("No translation found for key: " + key);
			return key;
		}
	}
	public class PinnedRecipeData
	{
		public Recipe RecipeRef;

		public string RawName;

		public string CachedHeader;

		public string CachedShadowHeader;

		public Sprite Icon;

		public int StackCount;

		public List<PinnedResData> Resources = new List<PinnedResData>();

		public bool IsDirty = true;
	}
	public class PinnedResData
	{
		public string ItemName;

		public string CachedName;

		public string CachedShadowName;

		public Sprite Icon;

		public int RequiredAmount;

		public int LastKnownAmount;

		public int LastKnownInvAmount;

		public string CachedAmountString;
	}
	public class RecipeManager
	{
		public Dictionary<string, int> PinnedRecipes = new Dictionary<string, int>();

		public List<PinnedRecipeData> CachedPins = new List<PinnedRecipeData>();

		private Dictionary<string, Recipe> _fakeRecipeCache = new Dictionary<string, Recipe>();

		private static readonly Regex CleanNameRegex = new Regex("<.*?>", RegexOptions.Compiled);

		private static readonly Regex ShadowCleanRegex = new Regex("<color=.*?>|</color>", RegexOptions.Compiled);

		private static readonly Regex AmountSuffixRegex = new Regex("\\s*[xX]?\\s*\\d+$", RegexOptions.Compiled);

		private static readonly Regex UpgradeStarRegex = new Regex("\\s*★(\\d+)$", RegexOptions.Compiled);

		private static Dictionary<Type, FieldInfo> _cachedRecipeFields = new Dictionary<Type, FieldInfo>();

		private static Dictionary<Type, PropertyInfo> _cachedRecipeProps = new Dictionary<Type, PropertyInfo>();

		private static Dictionary<Type, FieldInfo> _cachedItemFields = new Dictionary<Type, FieldInfo>();

		private static Dictionary<Type, PropertyInfo> _cachedItemProps = new Dictionary<Type, PropertyInfo>();

		public void Cleanup()
		{
			DebugLogger.Log("RecipeManager cleanup started");
			if (_fakeRecipeCache != null)
			{
				int count = _fakeRecipeCache.Count;
				foreach (Recipe value in _fakeRecipeCache.Values)
				{
					if ((Object)(object)value != (Object)null)
					{
						Object.Destroy((Object)(object)value);
					}
				}
				_fakeRecipeCache.Clear();
				DebugLogger.Log($"Cleaned up {count} fake recipes");
			}
			_cachedRecipeFields.Clear();
			_cachedRecipeProps.Clear();
			_cachedItemFields.Clear();
			_cachedItemProps.Clear();
		}

		public void RefreshRecipeCache()
		{
			DebugLogger.Verbose("Refreshing recipe cache...");
			CachedPins.Clear();
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				DebugLogger.Warning("Cannot refresh recipe cache - ObjectDB.instance is null");
				return;
			}
			int num = 0;
			int num2 = 0;
			foreach (KeyValuePair<string, int> pinnedRecipe in PinnedRecipes)
			{
				string key = pinnedRecipe.Key;
				int value = pinnedRecipe.Value;
				Recipe recipeByName = GetRecipeByName(key);
				if ((Object)(object)recipeByName != (Object)null)
				{
					PinnedRecipeData pinnedRecipeData = new PinnedRecipeData
					{
						IsDirty = true,
						RecipeRef = recipeByName,
						StackCount = value
					};
					if ((Object)(object)recipeByName.m_item != (Object)null)
					{
						pinnedRecipeData.Icon = recipeByName.m_item.m_itemData.GetIcon();
						pinnedRecipeData.RawName = recipeByName.m_item.m_itemData.m_shared.m_name;
					}
					else
					{
						ZNetScene instance = ZNetScene.instance;
						GameObject val = ((instance != null) ? instance.GetPrefab(((Object)recipeByName).name) : null);
						if ((Object)(object)val != (Object)null)
						{
							Piece component = val.GetComponent<Piece>();
							if ((Object)(object)component != (Object)null)
							{
								pinnedRecipeData.Icon = component.m_icon;
								pinnedRecipeData.RawName = component.m_name;
							}
						}
						else if (UpgradeStarRegex.IsMatch(key) && (Object)(object)recipeByName.m_item != (Object)null)
						{
							pinnedRecipeData.Icon = recipeByName.m_item.m_itemData.GetIcon();
							pinnedRecipeData.RawName = ((Object)recipeByName).name;
						}
					}
					if (string.IsNullOrEmpty(pinnedRecipeData.RawName))
					{
						pinnedRecipeData.RawName = ((Object)recipeByName).name;
					}
					string text = pinnedRecipeData.RawName;
					if (Localization.instance != null)
					{
						Match match = UpgradeStarRegex.Match(key);
						if (match.Success)
						{
							string text2 = pinnedRecipeData.RawName;
							if (Localization.instance != null)
							{
								text2 = Localization.instance.Localize(pinnedRecipeData.RawName);
							}
							text = text2 + match.Value;
						}
						else
						{
							text = Localization.instance.Localize(pinnedRecipeData.RawName);
						}
					}
					text = text.Replace("\r", "").Replace("\n", "");
					if (recipeByName.m_amount > 1)
					{
						text += $" (x{recipeByName.m_amount})";
					}
					if (value > 1)
					{
						text = $"{value}x {text}";
					}
					pinnedRecipeData.CachedHeader = text;
					pinnedRecipeData.CachedShadowHeader = text;
					Requirement[] resources = recipeByName.m_resources;
					foreach (Requirement val2 in resources)
					{
						if (val2 != null && !((Object)(object)val2.m_resItem == (Object)null) && val2.m_amount > 0)
						{
							PinnedResData pinnedResData = new PinnedResData
							{
								ItemName = val2.m_resItem.m_itemData.m_shared.m_name,
								Icon = val2.m_resItem.m_itemData.GetIcon(),
								RequiredAmount = val2.m_amount * value,
								LastKnownAmount = -1,
								LastKnownInvAmount = -1
							};
							string text3 = pinnedResData.ItemName;
							if (Localization.instance != null)
							{
								text3 = Localization.instance.Localize(pinnedResData.ItemName);
							}
							text3 = (pinnedResData.CachedName = text3.Replace("\r", "").Replace("\n", ""));
							pinnedResData.CachedShadowName = ShadowCleanRegex.Replace(text3, string.Empty);
							pinnedRecipeData.Resources.Add(pinnedResData);
						}
					}
					CachedPins.Add(pinnedRecipeData);
					num++;
				}
				else
				{
					DebugLogger.Warning("Recipe not found: " + key);
					num2++;
				}
			}
			DebugLogger.Log($"Recipe cache refreshed: {num} successful, {num2} failed");
			if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)RecipePinnerPlugin.Instance != (Object)null)
			{
				RecipePinnerPlugin.Instance.UIMgr.UpdateUI(isVisible: true);
			}
		}

		public Recipe GetRecipeByName(string name)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			if (_fakeRecipeCache.TryGetValue(name, out var value))
			{
				DebugLogger.Verbose("Found cached fake recipe: " + name);
				return value;
			}
			Match match = UpgradeStarRegex.Match(name);
			if (match.Success)
			{
				string name2 = name.Substring(0, match.Index).Trim();
				int targetLevel = int.Parse(match.Groups[1].Value);
				Recipe recipeByName = GetRecipeByName(name2);
				if ((Object)(object)recipeByName != (Object)null)
				{
					Recipe val = CreateFakeUpgradeRecipe(recipeByName, targetLevel, name);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(name);
			ItemDrop val2 = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val2 != (Object)null)
			{
				Recipe recipe = ObjectDB.instance.GetRecipe(val2.m_itemData);
				if ((Object)(object)recipe != (Object)null)
				{
					DebugLogger.Verbose("Found standard recipe: " + name);
					return recipe;
				}
			}
			Recipe val3 = ((IEnumerable<Recipe>)ObjectDB.instance.m_recipes).FirstOrDefault((Func<Recipe, bool>)((Recipe r) => ((Object)r).name == name));
			if ((Object)(object)val3 != (Object)null)
			{
				DebugLogger.Verbose("Found recipe in ObjectDB: " + name);
				return val3;
			}
			ZNetScene instance = ZNetScene.instance;
			GameObject val4 = ((instance != null) ? instance.GetPrefab(name) : null);
			if ((Object)(object)val4 != (Object)null)
			{
				Piece component = val4.GetComponent<Piece>();
				if ((Object)(object)component != (Object)null && component.m_resources != null && component.m_resources.Length != 0)
				{
					Recipe val5 = ScriptableObject.CreateInstance<Recipe>();
					((Object)val5).hideFlags = (HideFlags)61;
					((Object)val5).name = name;
					val5.m_item = val4.GetComponent<ItemDrop>();
					List<Requirement> list = component.m_resources.ToList();
					val5.m_resources = list.ToArray();
					_fakeRecipeCache[name] = val5;
					DebugLogger.Verbose("Created fake recipe for piece: " + name);
					return val5;
				}
			}
			DebugLogger.Warning("Recipe not found anywhere: " + name);
			return null;
		}

		private Recipe CreateFakeUpgradeRecipe(Recipe baseRecipe, int targetLevel, string customName)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			if ((Object)(object)baseRecipe == (Object)null)
			{
				return null;
			}
			Recipe val = ScriptableObject.CreateInstance<Recipe>();
			((Object)val).hideFlags = (HideFlags)61;
			((Object)val).name = customName;
			val.m_item = baseRecipe.m_item;
			val.m_amount = 1;
			List<Requirement> list = new List<Requirement>();
			Requirement[] resources = baseRecipe.m_resources;
			foreach (Requirement val2 in resources)
			{
				if (val2.m_amountPerLevel > 0)
				{
					Requirement item = new Requirement
					{
						m_resItem = val2.m_resItem,
						m_amount = val2.m_amountPerLevel,
						m_amountPerLevel = 0,
						m_recover = val2.m_recover
					};
					list.Add(item);
				}
			}
			if (list.Count == 0)
			{
				Object.Destroy((Object)(object)val);
				return null;
			}
			val.m_resources = list.ToArray();
			_fakeRecipeCache[customName] = val;
			DebugLogger.Verbose("Created fake upgrade recipe: " + customName);
			return val;
		}

		public void ValidateAndCleanPins()
		{
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				DebugLogger.Warning("Cannot validate pins - ObjectDB.instance is null");
				return;
			}
			DebugLogger.Log("Validating pinned recipes...");
			List<string> list = new List<string>();
			foreach (string key in PinnedRecipes.Keys)
			{
				if ((Object)(object)GetRecipeByName(key) == (Object)null)
				{
					list.Add(key);
				}
			}
			if (list.Count > 0)
			{
				foreach (string item in list)
				{
					PinnedRecipes.Remove(item);
					DebugLogger.Warning("Removed invalid recipe: " + item);
				}
				if ((Object)(object)RecipePinnerPlugin.Instance != (Object)null)
				{
					RecipePinnerPlugin.Instance.DataMgr.SavePins();
				}
				DebugLogger.Log($"Validation complete: {list.Count} invalid recipes removed");
			}
			else
			{
				DebugLogger.Log("All pinned recipes are valid");
			}
		}

		public void TryPinHoveredRecipe(InventoryGui gui)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			DebugLogger.Verbose("Attempting to pin hovered recipe...");
			Transform recipeListRoot = ReflectionHelper.GetRecipeListRoot(gui);
			object availableRecipes = ReflectionHelper.GetAvailableRecipes(gui);
			IList list = availableRecipes as IList;
			if ((Object)(object)recipeListRoot == (Object)null || list == null)
			{
				DebugLogger.Verbose("Cannot pin - listRoot or availableRecipes is null");
				return;
			}
			ScrollRect componentInParent = ((Component)recipeListRoot).GetComponentInParent<ScrollRect>();
			bool flag = !((Selectable)gui.m_tabUpgrade).interactable;
			foreach (Transform item in recipeListRoot)
			{
				Transform val = item;
				if (!((Component)val).gameObject.activeInHierarchy)
				{
					continue;
				}
				RectTransform val2 = (RectTransform)(object)((val is RectTransform) ? val : null);
				if ((Object)(object)val2 == (Object)null || !IsVisibleInScroll(val2, componentInParent) || !InputHelper.IsMouseOverRect(val2))
				{
					continue;
				}
				string text = ExtractTextFromUI(val);
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				string text2 = CleanNameRegex.Replace(text, string.Empty).Trim();
				text2 = text2.Replace("\r", "").Replace("\n", "");
				string text3 = AmountSuffixRegex.Replace(text2, "").Trim();
				DebugLogger.Verbose($"Hovered: '{text3}' (UpgradeTab: {flag})");
				foreach (object item2 in list)
				{
					Recipe recipeFromObject = GetRecipeFromObject(item2);
					if (!((Object)(object)recipeFromObject != (Object)null))
					{
						continue;
					}
					string rawRecipeName = GetRawRecipeName(recipeFromObject);
					if (string.IsNullOrEmpty(rawRecipeName))
					{
						continue;
					}
					string text4 = rawRecipeName;
					if (Localization.instance != null)
					{
						text4 = Localization.instance.Localize(rawRecipeName);
					}
					text4 = text4.Replace("\r", "").Replace("\n", "");
					if (!text4.Equals(text3, StringComparison.OrdinalIgnoreCase) && !text4.Equals(text2, StringComparison.OrdinalIgnoreCase))
					{
						continue;
					}
					if (flag)
					{
						ItemData val3 = GetItemDataFromObject(item2);
						if (val3 == null)
						{
							val3 = ReflectionHelper.GetCraftUpgradeItem(gui);
						}
						if (val3 != null)
						{
							int quality = val3.m_quality;
							int num = quality + 1;
							int maxQuality = val3.m_shared.m_maxQuality;
							if (quality >= maxQuality)
							{
								string text5 = RecipePinnerPlugin.Instance.LocalizationMgr.GetText("max_level");
								Player localPlayer = Player.m_localPlayer;
								if (localPlayer != null)
								{
									((Character)localPlayer).Message((MessageType)2, text5, 0, (Sprite)null);
								}
								return;
							}
							string name = ((Object)recipeFromObject.m_item).name;
							string text6 = $"{name} ★{num}";
							DebugLogger.Log("Attempting to pin upgrade: " + text6 + " (Base: " + name + ")");
							if ((Object)(object)GetRecipeByName(text6) != (Object)null)
							{
								TogglePin(text6);
								return;
							}
							string text7 = RecipePinnerPlugin.Instance.LocalizationMgr.GetText("no_upgrade_cost");
							Player localPlayer2 = Player.m_localPlayer;
							if (localPlayer2 != null)
							{
								((Character)localPlayer2).Message((MessageType)2, text7, 0, (Sprite)null);
							}
						}
						else
						{
							DebugLogger.Warning("Matched name but could not get ItemData for upgrade.");
						}
					}
					else
					{
						DebugLogger.Log("Matched recipe: " + ((Object)recipeFromObject).name);
						TogglePin(((Object)recipeFromObject).name);
					}
					return;
				}
			}
		}

		public void TryPinHoveredPiece()
		{
			DebugLogger.Verbose("Attempting to pin hovered piece...");
			if (!((Object)(object)Hud.instance == (Object)null))
			{
				Piece hoveredPiece = ReflectionHelper.GetHoveredPiece(Hud.instance);
				if ((Object)(object)hoveredPiece != (Object)null && hoveredPiece.m_resources != null && hoveredPiece.m_resources.Length != 0)
				{
					DebugLogger.Log("Pinning piece: " + ((Object)hoveredPiece).name);
					TogglePin(((Object)hoveredPiece).name);
				}
				else
				{
					DebugLogger.Verbose("No valid piece to pin (Mouse must be over a recipe icon)");
				}
			}
		}

		private void TogglePin(string recipeName)
		{
			bool flag = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
			LocalizationManager localizationMgr = RecipePinnerPlugin.Instance.LocalizationMgr;
			if (PinnedRecipes.ContainsKey(recipeName))
			{
				if (flag)
				{
					PinnedRecipes[recipeName]--;
					if (PinnedRecipes[recipeName] <= 0)
					{
						PinnedRecipes.Remove(recipeName);
						Player localPlayer = Player.m_localPlayer;
						if (localPlayer != null)
						{
							((Character)localPlayer).Message((MessageType)2, localizationMgr.GetText("unpinned"), 0, (Sprite)null);
						}
						DebugLogger.Log("Unpinned: " + recipeName);
					}
					else
					{
						string text = string.Format(localizationMgr.GetText("decreased"), PinnedRecipes[recipeName]);
						Player localPlayer2 = Player.m_localPlayer;
						if (localPlayer2 != null)
						{
							((Character)localPlayer2).Message((MessageType)2, text, 0, (Sprite)null);
						}
						DebugLogger.Log($"Decreased pin count: {recipeName} = {PinnedRecipes[recipeName]}");
					}
				}
				else
				{
					PinnedRecipes[recipeName]++;
					string text2 = string.Format(localizationMgr.GetText("added_more"), PinnedRecipes[recipeName]);
					Player localPlayer3 = Player.m_localPlayer;
					if (localPlayer3 != null)
					{
						((Character)localPlayer3).Message((MessageType)2, text2, 0, (Sprite)null);
					}
					DebugLogger.Log($"Increased pin count: {recipeName} = {PinnedRecipes[recipeName]}");
				}
			}
			else
			{
				if (flag)
				{
					return;
				}
				if (PinnedRecipes.Count < RecipePinnerPlugin.MaximumPins.Value)
				{
					PinnedRecipes.Add(recipeName, 1);
					Player localPlayer4 = Player.m_localPlayer;
					if (localPlayer4 != null)
					{
						((Character)localPlayer4).Message((MessageType)2, localizationMgr.GetText("pinned"), 0, (Sprite)null);
					}
					DebugLogger.Log("Pinned new recipe: " + recipeName);
				}
				else
				{
					Player localPlayer5 = Player.m_localPlayer;
					if (localPlayer5 != null)
					{
						((Character)localPlayer5).Message((MessageType)2, localizationMgr.GetText("list_full"), 0, (Sprite)null);
					}
					DebugLogger.Warning($"Cannot pin {recipeName} - max pins reached ({RecipePinnerPlugin.MaximumPins.Value})");
				}
			}
			RefreshRecipeCache();
		}

		private Recipe GetRecipeFromObject(object data)
		{
			if (data == null)
			{
				return null;
			}
			Recipe val = (Recipe)((data is Recipe) ? data : null);
			if (val != null)
			{
				return val;
			}
			Type type = data.GetType();
			if (_cachedRecipeFields.TryGetValue(type, out var value))
			{
				object? value2 = value.GetValue(data);
				return (Recipe)((value2 is Recipe) ? value2 : null);
			}
			if (_cachedRecipeProps.TryGetValue(type, out var value3))
			{
				object? value4 = value3.GetValue(data, null);
				return (Recipe)((value4 is Recipe) ? value4 : null);
			}
			PropertyInfo property = type.GetProperty("Key");
			if (property != null)
			{
				object? value5 = property.GetValue(data, null);
				Recipe val2 = (Recipe)((value5 is Recipe) ? value5 : null);
				if (val2 != null)
				{
					_cachedRecipeProps[type] = property;
					return val2;
				}
			}
			FieldInfo field = type.GetField("m_recipe", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				object? value6 = field.GetValue(data);
				Recipe val3 = (Recipe)((value6 is Recipe) ? value6 : null);
				if (val3 != null)
				{
					_cachedRecipeFields[type] = field;
					return val3;
				}
			}
			FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.FieldType == typeof(Recipe))
				{
					_cachedRecipeFields[type] = fieldInfo;
					object? value7 = fieldInfo.GetValue(data);
					return (Recipe)((value7 is Recipe) ? value7 : null);
				}
			}
			PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (PropertyInfo propertyInfo in properties)
			{
				if (propertyInfo.PropertyType == typeof(Recipe) && propertyInfo.CanRead)
				{
					_cachedRecipeProps[type] = propertyInfo;
					object? value8 = propertyInfo.GetValue(data, null);
					return (Recipe)((value8 is Recipe) ? value8 : null);
				}
			}
			return null;
		}

		private ItemData GetItemDataFromObject(object data)
		{
			if (data == null)
			{
				return null;
			}
			Type type = data.GetType();
			if (_cachedItemFields.TryGetValue(type, out var value))
			{
				object? value2 = value.GetValue(data);
				return (ItemData)((value2 is ItemData) ? value2 : null);
			}
			if (_cachedItemProps.TryGetValue(type, out var value3))
			{
				object? value4 = value3.GetValue(data, null);
				return (ItemData)((value4 is ItemData) ? value4 : null);
			}
			PropertyInfo property = type.GetProperty("Value");
			if (property != null)
			{
				object? value5 = property.GetValue(data, null);
				ItemData val = (ItemData)((value5 is ItemData) ? value5 : null);
				if (val != null)
				{
					_cachedItemProps[type] = property;
					return val;
				}
			}
			PropertyInfo property2 = type.GetProperty("Item2");
			if (property2 != null)
			{
				object? value6 = property2.GetValue(data, null);
				ItemData val2 = (ItemData)((value6 is ItemData) ? value6 : null);
				if (val2 != null)
				{
					_cachedItemProps[type] = property2;
					return val2;
				}
			}
			FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.FieldType == typeof(ItemData))
				{
					_cachedItemFields[type] = fieldInfo;
					object? value7 = fieldInfo.GetValue(data);
					return (ItemData)((value7 is ItemData) ? value7 : null);
				}
			}
			PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (PropertyInfo propertyInfo in properties)
			{
				if (propertyInfo.PropertyType == typeof(ItemData) && propertyInfo.CanRead)
				{
					_cachedItemProps[type] = propertyInfo;
					object? value8 = propertyInfo.GetValue(data, null);
					return (ItemData)((value8 is ItemData) ? value8 : null);
				}
			}
			return null;
		}

		private string ExtractTextFromUI(Transform child)
		{
			Text componentInChildren = ((Component)child).GetComponentInChildren<Text>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				return componentInChildren.text;
			}
			Component[] componentsInChildren = ((Component)child).GetComponentsInChildren<Component>(true);
			Component[] array = componentsInChildren;
			foreach (Component val in array)
			{
				if (!((object)val).GetType().Name.Contains("TextMeshPro") && !((object)val).GetType().Name.Contains("TMP_Text"))
				{
					continue;
				}
				PropertyInfo property = ((object)val).GetType().GetProperty("text");
				if (property != null)
				{
					string text = property.GetValue(val, null) as string;
					if (!string.IsNullOrEmpty(text))
					{
						return text;
					}
				}
			}
			return null;
		}

		private string GetRawRecipeName(Recipe r)
		{
			if ((Object)(object)r.m_item != (Object)null && r.m_item.m_itemData != null)
			{
				return r.m_item.m_itemData.m_shared.m_name;
			}
			ZNetScene instance = ZNetScene.instance;
			GameObject val = ((instance != null) ? instance.GetPrefab(((Object)r).name) : null);
			if ((Object)(object)val != (Object)null)
			{
				ItemDrop component = val.GetComponent<ItemDrop>();
				if ((Object)(object)component != (Object)null)
				{
					return component.m_itemData.m_shared.m_name;
				}
				Piece component2 = val.GetComponent<Piece>();
				if ((Object)(object)component2 != (Object)null)
				{
					return component2.m_name;
				}
			}
			return null;
		}

		private bool IsVisibleInScroll(RectTransform item, ScrollRect scrollRect)
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)item == (Object)null || !((Component)item).gameObject.activeInHierarchy)
			{
				return false;
			}
			if ((Object)(object)scrollRect == (Object)null || (Object)(object)scrollRect.viewport == (Object)null)
			{
				return true;
			}
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			scrollRect.viewport.GetWorldCorners(array);
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(array[0].x, array[0].y, array[2].x - array[0].x, array[2].y - array[0].y);
			Vector3[] array2 = (Vector3[])(object)new Vector3[4];
			item.GetWorldCorners(array2);
			Vector3 val2 = (array2[0] + array2[2]) / 2f;
			return ((Rect)(ref val)).Contains(val2);
		}
	}
	[BepInPlugin("com.Kadrio.RecipePinner", "Recipe Pinner", "1.1.2")]
	public class RecipePinnerPlugin : BaseUnityPlugin
	{
		public enum PinLayoutMode
		{
			AutoDetect,
			ForceVertical,
			ForceHorizontal,
			ForceBottomRightHorizontal
		}

		public class ConfigurationManagerAttributes
		{
			public bool? ShowRangeAsPercent;

			public Action<ConfigEntryBase> CustomDrawer;

			public bool? Browsable;

			public string Category;

			public object DefaultValue;

			public bool? HideDefaultButton;

			public bool? HideSettingName;

			public string Description;

			public string DispName;

			public int? Order;

			public bool? ReadOnly;

			public bool? IsAdvanced;

			public Func<object, string> ObjToStr;

			public Func<string, object> StrToObj;
		}

		public static RecipePinnerPlugin Instance;

		public static ConfigEntry<bool> EnableMod;

		public static ConfigEntry<string> LanguageOverride;

		public static ConfigEntry<PinLayoutMode> LayoutModeConfig;

		public static ConfigEntry<int> MaximumPins;

		public static ConfigEntry<int> PinsPerPage;

		public static ConfigEntry<bool> AutoUnpinAfterCrafting;

		public static ConfigEntry<KeyCode> HotkeyPin;

		public static ConfigEntry<KeyCode> HotkeyClearAll;

		public static ConfigEntry<KeyCode> HotkeyToggleVisibility;

		public static ConfigEntry<KeyCode> HotkeyPageSwitch;

		public static ConfigEntry<bool> EnableChestScanning;

		public static ConfigEntry<float> ChestScanRange;

		public static ConfigEntry<float> ChestScanInterval;

		public static ConfigEntry<float> UIScale;

		public static ConfigEntry<int> FontSizeRecipeName;

		public static ConfigEntry<int> FontSizeMaterials;

		public static ConfigEntry<float> BackgroundOpacity;

		public static ConfigEntry<Color> ColorHeader;

		public static ConfigEntry<Color> ColorEnoughInInventory;

		public static ConfigEntry<Color> ColorEnoughWithChests;

		public static ConfigEntry<Color> ColorMissing;

		public static ConfigEntry<Color> ColorPaginationActive;

		public static ConfigEntry<float> PaginationInactiveOpacity;

		public static ConfigEntry<int> PaginationDotSize;

		public static ConfigEntry<int> PaginationDotSpacing;

		public static ConfigEntry<float> VerticalListWidth;

		public static ConfigEntry<float> VerticalPinSpacing;

		public static ConfigEntry<Vector2> VerticalPosition;

		public static ConfigEntry<float> HorizontalColumnWidth;

		public static ConfigEntry<float> HorizontalPinSpacing;

		public static ConfigEntry<Vector2> HorizontalPosition;

		public static ConfigEntry<float> BottomRightColumnWidth;

		public static ConfigEntry<float> BottomRightPinSpacing;

		public static ConfigEntry<Vector2> BottomRightPosition;

		public static ConfigEntry<bool> EnableDebugLogging;

		public LocalizationManager LocalizationMgr;

		public RecipeManager RecipeMgr;

		public ContainerScanner ContainerMgr;

		public UIManager UIMgr;

		public DataPersistence DataMgr;

		internal bool _mluiMapListEnabled = false;

		internal bool _mluiNoMapListEnabled = false;

		internal bool _mluiInstalled = false;

		private bool _startupInitialized = false;

		private string _lastLanguage = "";

		private string _currentSessionPlayer = null;

		private static bool _isUiVisible = true;

		public bool IsHorizontalMode
		{
			get
			{
				if (LayoutModeConfig.Value == PinLayoutMode.ForceBottomRightHorizontal)
				{
					return true;
				}
				if (LayoutModeConfig.Value == PinLayoutMode.ForceHorizontal)
				{
					return true;
				}
				if (LayoutModeConfig.Value == PinLayoutMode.ForceVertical)
				{
					return false;
				}
				if (!_mluiInstalled)
				{
					return false;
				}
				if (Game.m_noMap)
				{
					return _mluiNoMapListEnabled;
				}
				return _mluiMapListEnabled;
			}
		}

		private void Awake()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			Instance = this;
			BindConfigs();
			DebugLogger.Log("RecipePinner plugin initializing...");
			LocalizationMgr = new LocalizationManager(this);
			RecipeMgr = new RecipeManager();
			ContainerMgr = new ContainerScanner();
			UIMgr = new UIManager();
			DataMgr = new DataPersistence();
			DebugLogger.Log("All managers initialized successfully");
			Harmony val = new Harmony("com.Kadrio.RecipePinner");
			val.PatchAll(typeof(RecipePinnerPlugin));
			val.PatchAll(typeof(ContainerScanner));
			DebugLogger.Log("Harmony patches applied successfully");
		}

		private void BindConfigs()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Expected O, but got Unknown
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Expected O, but got Unknown
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Expected O, but got Unknown
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Expected O, but got Unknown
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Expected O, but got Unknown
			//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d6: Expected O, but got Unknown
			//IL_0336: Unknown result type (might be due to invalid IL or missing references)
			//IL_0340: Expected O, but got Unknown
			//IL_0389: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Expected O, but got Unknown
			//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d0: Expected O, but got Unknown
			//IL_041a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0424: Expected O, but got Unknown
			//IL_04bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_050c: Unknown result type (might be due to invalid IL or missing references)
			//IL_055b: Unknown result type (might be due to invalid IL or missing references)
			//IL_05aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0652: Unknown result type (might be due to invalid IL or missing references)
			//IL_065c: Expected O, but got Unknown
			//IL_069c: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a6: Expected O, but got Unknown
			//IL_06e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ef: Expected O, but got Unknown
			//IL_076d: Unknown result type (might be due to invalid IL or missing references)
			//IL_07e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0859: Unknown result type (might be due to invalid IL or missing references)
			EnableMod = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "EnableMod", true, new ConfigDescription("Enable or disable the mod completely.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 99
				}
			}));
			EnableMod.SettingChanged += delegate
			{
				if (!EnableMod.Value)
				{
					UIMgr?.DestroyUI();
				}
			};
			LanguageOverride = ((BaseUnityPlugin)this).Config.Bind<string>("1 - General", "LanguageOverride", "Auto", new ConfigDescription("Force a specific language (e.g., 'German', 'Turkish').", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 98
				}
			}));
			LanguageOverride.SettingChanged += delegate
			{
				LocalizationMgr?.LoadTranslations();
				RecipeMgr?.RefreshRecipeCache();
			};
			LayoutModeConfig = ((BaseUnityPlugin)this).Config.Bind<PinLayoutMode>("1 - General", "LayoutMode", PinLayoutMode.AutoDetect, new ConfigDescription("Choose layout position.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 97
				}
			}));
			LayoutModeConfig.SettingChanged += delegate
			{
				UIMgr?.DestroyUI();
			};
			MaximumPins = ((BaseUnityPlugin)this).Config.Bind<int>("1 - General", "MaximumPins", 10, new ConfigDescription("Max pins allowed.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 96
				}
			}));
			MaximumPins.SettingChanged += delegate
			{
				UIMgr?.DestroyUI();
			};
			PinsPerPage = ((BaseUnityPlugin)this).Config.Bind<int>("1 - General", "PinsPerPage", 5, new ConfigDescription("How many pins to show per page.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 95
				}
			}));
			PinsPerPage.SettingChanged += delegate
			{
				UIMgr?.ResetPage();
				UIMgr?.DestroyUI();
			};
			AutoUnpinAfterCrafting = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "AutoUnpinAfterCrafting", true, new ConfigDescription("Unpin after crafting.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 95
				}
			}));
			HotkeyPin = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2 - Controls", "HotkeyPin", (KeyCode)325, "Key to pin recipe.");
			HotkeyClearAll = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2 - Controls", "HotkeyClearAll", (KeyCode)112, "Key to clear all pins.");
			HotkeyToggleVisibility = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2 - Controls", "HotkeyToggleVisibility", (KeyCode)288, "Key to toggle overlay.");
			HotkeyPageSwitch = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2 - Controls", "HotkeyPageSwitch", (KeyCode)308, "Key to cycle through pin pages.");
			EnableChestScanning = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Chest Scanner", "EnableChestScanning", false, new ConfigDescription("Count materials in nearby chests.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 99
				}
			}));
			EnableChestScanning.SettingChanged += delegate
			{
				RecipeMgr?.RefreshRecipeCache();
			};
			ChestScanRange = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Chest Scanner", "ChestScanRange", 20f, new ConfigDescription("Scan radius.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 98
				}
			}));
			ChestScanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Chest Scanner", "ChestScanInterval", 3f, new ConfigDescription("Scan frequency.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 97
				}
			}));
			UIScale = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Visual Settings", "UIScale", 0.75f, new ConfigDescription("Global UI scale.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 3f), Array.Empty<object>()));
			UIScale.SettingChanged += delegate
			{
				UIMgr?.DestroyUI();
			};
			BackgroundOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Visual Settings", "BackgroundOpacity", 0.45f, new ConfigDescription("Background opacity.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			FontSizeRecipeName = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Visual Settings", "FontSizeRecipeName", 15, "Recipe name font size.");
			FontSizeRecipeName.SettingChanged += delegate
			{
				RecipeMgr?.RefreshRecipeCache();
			};
			FontSizeMaterials = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Visual Settings", "FontSizeMaterials", 15, "Material font size.");
			FontSizeMaterials.SettingChanged += delegate
			{
				RecipeMgr?.RefreshRecipeCache();
			};
			ColorHeader = ((BaseUnityPlugin)this).Config.Bind<Color>("4 - Visual Settings", "ColorHeader", new Color(1f, 0.717f, 0.368f, 1f), "Recipe title color.");
			ColorHeader.SettingChanged += delegate
			{
				RecipeMgr?.RefreshRecipeCache();
			};
			ColorEnoughInInventory = ((BaseUnityPlugin)this).Config.Bind<Color>("4 - Visual Settings", "ColorEnoughInInventory", new Color(0f, 1f, 0f, 1f), "Color: Enough in inventory.");
			ColorEnoughInInventory.SettingChanged += delegate
			{
				RecipeMgr?.RefreshRecipeCache();
			};
			ColorEnoughWithChests = ((BaseUnityPlugin)this).Config.Bind<Color>("4 - Visual Settings", "ColorEnoughWithChests", new Color(1f, 1f, 0f, 1f), "Color: Enough with chests.");
			ColorEnoughWithChests.SettingChanged += delegate
			{
				RecipeMgr?.RefreshRecipeCache();
			};
			ColorMissing = ((BaseUnityPlugin)this).Config.Bind<Color>("4 - Visual Settings", "ColorMissing", new Color(1f, 0.33f, 0.33f, 1f), "Color: Missing materials.");
			ColorMissing.SettingChanged += delegate
			{
				RecipeMgr?.RefreshRecipeCache();
			};
			ColorPaginationActive = ((BaseUnityPlugin)this).Config.Bind<Color>("4 - Visual Settings", "ColorPaginationActive", new Color(1f, 0.717f, 0.368f, 1f), "Active page dot color (Orange).");
			ColorPaginationActive.SettingChanged += delegate
			{
				UIMgr?.UpdateUI(isVisible: true);
			};
			PaginationInactiveOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Visual Settings", "PaginationInactiveOpacity", 0.3f, new ConfigDescription("Opacity of inactive page dots (0.0 to 1.0).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			PaginationInactiveOpacity.SettingChanged += delegate
			{
				UIMgr?.UpdateUI(isVisible: true);
			};
			PaginationDotSize = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Visual Settings", "PaginationDotSize", 10, new ConfigDescription("Size of the pagination squares.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 20), Array.Empty<object>()));
			PaginationDotSize.SettingChanged += delegate
			{
				UIMgr?.UpdateUI(isVisible: true);
			};
			PaginationDotSpacing = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Visual Settings", "PaginationDotSpacing", 8, new ConfigDescription("Space between pagination squares.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>()));
			PaginationDotSpacing.SettingChanged += delegate
			{
				UIMgr?.UpdateUI(isVisible: true);
			};
			VerticalListWidth = ((BaseUnityPlugin)this).Config.Bind<float>("5 - Layout (Vertical Mode)", "ListWidth", 265f, "List width.");
			VerticalPinSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("5 - Layout (Vertical Mode)", "PinSpacing", 10f, "Spacing between pins.");
			VerticalPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("5 - Layout (Vertical Mode)", "Position", new Vector2(-40f, -250f), "Position (X, Y).");
			HorizontalColumnWidth = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Layout (Horizontal - Map Side)", "ColumnWidth", 250f, "Column width.");
			HorizontalPinSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Layout (Horizontal - Map Side)", "PinSpacing", 10f, "Spacing between pins.");
			HorizontalPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("6 - Layout (Horizontal - Map Side)", "Position", new Vector2(-250f, -40f), "Position (X, Y).");
			BottomRightColumnWidth = ((BaseUnityPlugin)this).Config.Bind<float>("7 - Layout (Horizontal - Bottom Right)", "ColumnWidth", 250f, "Column width.");
			BottomRightPinSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("7 - Layout (Horizontal - Bottom Right)", "PinSpacing", 10f, "Spacing between pins.");
			BottomRightPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("7 - Layout (Horizontal - Bottom Right)", "Position", new Vector2(-40f, 40f), "Position (X, Y).");
			EnableDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("8 - Debug", "EnableDebugLogging", false, "Enable debug logs.");
			DebugLogger.Log("Configuration loaded successfully");
		}

		private void Start()
		{
			DebugLogger.Log("Start() called - Loading translations and initializing containers");
			LocalizationMgr.LoadTranslations();
			ReadMyLittleUIConfig();
			ContainerMgr.InitializeContainers();
			DebugLogger.Log("Start() completed successfully");
		}

		private void OnDestroy()
		{
			DebugLogger.Log("Plugin destroyed - Cleaning up");
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				DataMgr.SavePins();
			}
			RecipeMgr.Cleanup();
		}

		private void Update()
		{
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Unknown result type (might be due to invalid IL or missing references)
			if (!EnableMod.Value)
			{
				return;
			}
			ReflectionHelper.UpdateGuiScale();
			if (!_startupInitialized && (Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_recipes.Count > 0)
			{
				DebugLogger.Log("First-time initialization triggered");
				_lastLanguage = Localization.instance.GetSelectedLanguage();
				DataMgr.LoadPins();
				RecipeMgr.ValidateAndCleanPins();
				RecipeMgr.RefreshRecipeCache();
				_startupInitialized = true;
				DebugLogger.Log($"Initialization complete - {RecipeMgr.PinnedRecipes.Count} recipes loaded");
			}
			if (EnableChestScanning.Value && (Object)(object)Player.m_localPlayer != (Object)null && RecipeMgr.CachedPins.Count > 0)
			{
				ContainerMgr.UpdateScanning();
			}
			if (Input.GetKeyDown(HotkeyToggleVisibility.Value) && !InputHelper.IsInputBlocked())
			{
				_isUiVisible = !_isUiVisible;
				DebugLogger.Log($"UI visibility toggled: {_isUiVisible}");
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				UpdatePlayerSession();
			}
			if (Input.GetKeyDown(HotkeyPin.Value))
			{
				if ((Object)(object)InventoryGui.instance != (Object)null && InventoryGui.IsVisible())
				{
					RecipeMgr.TryPinHoveredRecipe(InventoryGui.instance);
				}
				else if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null && ((Character)Player.m_localPlayer).InPlaceMode())
				{
					RecipeMgr.TryPinHoveredPiece();
				}
			}
			if (Input.GetKeyDown(HotkeyClearAll.Value))
			{
				if (InputHelper.IsInputBlocked())
				{
					return;
				}
				if (RecipeMgr.PinnedRecipes.Count > 0)
				{
					int count = RecipeMgr.PinnedRecipes.Count;
					RecipeMgr.PinnedRecipes.Clear();
					RecipeMgr.RefreshRecipeCache();
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)2, LocalizationMgr.GetText("cleared"), 0, (Sprite)null);
					}
					DebugLogger.Log($"Cleared {count} pinned recipes");
				}
			}
			if (Localization.instance != null)
			{
				string selectedLanguage = Localization.instance.GetSelectedLanguage();
				if (_lastLanguage != selectedLanguage)
				{
					DebugLogger.Log("Language changed from " + _lastLanguage + " to " + selectedLanguage);
					_lastLanguage = selectedLanguage;
					LocalizationMgr.LoadTranslations();
					if ((Object)(object)ObjectDB.instance != (Object)null)
					{
						RecipeMgr.RefreshRecipeCache();
					}
				}
			}
			if (Input.GetKeyDown(HotkeyPageSwitch.Value) && _isUiVisible && !InputHelper.IsInputBlocked())
			{
				UIMgr?.CyclePage();
			}
		}

		private void UpdatePlayerSession()
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null || ((Character)Player.m_localPlayer).IsDead())
			{
				return;
			}
			string playerName = Player.m_localPlayer.GetPlayerName();
			if (string.IsNullOrEmpty(playerName))
			{
				return;
			}
			if (_currentSessionPlayer != playerName)
			{
				DebugLogger.Log("Player session changed from '" + _currentSessionPlayer + "' to '" + playerName + "'");
				RecipeMgr.PinnedRecipes.Clear();
				RecipeMgr.CachedPins.Clear();
				UIMgr.DestroyUI();
				_currentSessionPlayer = playerName;
				if (!string.IsNullOrEmpty(playerName))
				{
					DataMgr.LoadPins();
					RecipeMgr.RefreshRecipeCache();
				}
			}
			UIMgr.UpdateUI(_isUiVisible);
		}

		private void ReadMyLittleUIConfig()
		{
			if (!Chainloader.PluginInfos.ContainsKey("shudnal.MyLittleUI"))
			{
				_mluiInstalled = false;
				DebugLogger.Log("MyLittleUI not detected");
				return;
			}
			_mluiInstalled = true;
			_mluiMapListEnabled = true;
			_mluiNoMapListEnabled = true;
			string path = Path.Combine(Paths.ConfigPath, "shudnal.MyLittleUI.cfg");
			if (!File.Exists(path))
			{
				DebugLogger.Log("MyLittleUI installed but config not found");
				return;
			}
			try
			{
				string[] array = File.ReadAllLines(path);
				string text = "";
				string[] array2 = array;
				foreach (string text2 in array2)
				{
					string text3 = text2.Trim();
					if (text3.StartsWith("[") && text3.EndsWith("]"))
					{
						text = text3;
					}
					else if (text3.StartsWith("Enable"))
					{
						bool flag = text3.ToLower().Contains("true");
						if (text == "[Status effects - Map - List]")
						{
							_mluiMapListEnabled = flag;
						}
						else if (text == "[Status effects - Nomap - List]")
						{
							_mluiNoMapListEnabled = flag;
						}
					}
				}
				DebugLogger.Log($"MyLittleUI Config: MapList={_mluiMapListEnabled}, NoMapList={_mluiNoMapListEnabled}");
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[RecipePinner] Error reading MyLittleUI config: " + ex.Message));
			}
		}

		[HarmonyPatch(typeof(Game), "SavePlayerProfile")]
		[HarmonyPostfix]
		public static void AutoSavePinsHook()
		{
			if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)Instance != (Object)null)
			{
				DebugLogger.Log("Auto-saving pins on profile save");
				Instance.DataMgr.SavePins();
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
		[HarmonyPostfix]
		public static void AutoUnpinHook(InventoryGui __instance)
		{
			if (!EnableMod.Value || !AutoUnpinAfterCrafting.Value || (Object)(object)Instance == (Object)null)
			{
				return;
			}
			Recipe craftRecipe = ReflectionHelper.GetCraftRecipe(__instance);
			if (!((Object)(object)craftRecipe != (Object)null))
			{
				return;
			}
			string text = null;
			if (!((Selectable)__instance.m_tabUpgrade).interactable)
			{
				ItemData craftUpgradeItem = ReflectionHelper.GetCraftUpgradeItem(__instance);
				if (craftUpgradeItem != null)
				{
					string name = ((Object)craftRecipe.m_item).name;
					int quality = craftUpgradeItem.m_quality;
					int num = quality + 1;
					text = $"{name} ★{num}";
					DebugLogger.Log($"Upgrade crafted: Unpinning target {text} (Base Level: {quality})");
				}
			}
			else
			{
				text = ((Object)craftRecipe).name;
			}
			if (text != null && Instance.RecipeMgr.PinnedRecipes.ContainsKey(text))
			{
				Instance.RecipeMgr.PinnedRecipes[text]--;
				DebugLogger.Log($"Auto-unpin: {text}, remaining count: {Instance.RecipeMgr.PinnedRecipes[text]}");
				if (Instance.RecipeMgr.PinnedRecipes[text] <= 0)
				{
					Instance.RecipeMgr.PinnedRecipes.Remove(text);
					DebugLogger.Log("Recipe " + text + " fully unpinned");
				}
				Instance.RecipeMgr.RefreshRecipeCache();
			}
		}

		[HarmonyPatch(typeof(Player), "ConsumeResources")]
		[HarmonyPostfix]
		public static void AutoUnpinBuildHook()
		{
			if ((Object)(object)Instance == (Object)null || !EnableMod.Value || !AutoUnpinAfterCrafting.Value)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			PieceTable pieceTable = ReflectionHelper.GetPieceTable(localPlayer);
			if ((Object)(object)pieceTable == (Object)null)
			{
				return;
			}
			Piece selectedPiece = pieceTable.GetSelectedPiece();
			if ((Object)(object)selectedPiece == (Object)null)
			{
				return;
			}
			string text = ((Object)selectedPiece).name.Replace("(Clone)", "").Trim();
			if (Instance.RecipeMgr.PinnedRecipes.ContainsKey(text))
			{
				Instance.RecipeMgr.PinnedRecipes[text]--;
				DebugLogger.Log($"Auto-unpin (Build): {text}, remaining count: {Instance.RecipeMgr.PinnedRecipes[text]}");
				if (Instance.RecipeMgr.PinnedRecipes[text] <= 0)
				{
					Instance.RecipeMgr.PinnedRecipes.Remove(text);
					DebugLogger.Log("Build recipe " + text + " fully unpinned");
				}
				Instance.RecipeMgr.RefreshRecipeCache();
			}
		}
	}
	public static class ReflectionHelper
	{
		private static Func<float> _getGuiScale;

		private static Func<InventoryGui, Transform> _getRecipeListRoot;

		private static Func<InventoryGui, object> _getAvailableRecipes;

		private static Func<InventoryGui, Container> _getCurrentContainer;

		private static Func<InventoryGui, Recipe> _getCraftRecipe;

		private static Func<InventoryGui, ItemData> _getCraftUpgradeItem;

		private static Func<Hud, Piece> _getHoveredPiece;

		public static Func<Container, long, bool> CheckContainerAccess;

		private static FieldInfo _f_buildPieces;

		public static float currentGuiScaleValue;

		static ReflectionHelper()
		{
			currentGuiScaleValue = 1f;
			InitializeReflection();
		}

		public static void InitializeReflection()
		{
			DebugLogger.Log("Initializing reflection helpers...");
			int num = 0;
			int num2 = 0;
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(GuiScaler), "m_largeGuiScale");
				if (fieldInfo != null && fieldInfo.IsStatic)
				{
					_getGuiScale = Expression.Lambda<Func<float>>(Expression.Field(null, fieldInfo), Array.Empty<ParameterExpression>()).Compile();
					num++;
					DebugLogger.Verbose("✓ GuiScaler.m_largeGuiScale");
				}
				else
				{
					num2++;
					DebugLogger.Warning("✗ GuiScaler.m_largeGuiScale not found");
				}
				FieldInfo fieldInfo2 = AccessTools.Field(typeof(InventoryGui), "m_recipeListRoot");
				if (fieldInfo2 != null)
				{
					ParameterExpression parameterExpression = Expression.Parameter(typeof(InventoryGui), "arg");
					_getRecipeListRoot = Expression.Lambda<Func<InventoryGui, Transform>>(Expression.Field(parameterExpression, fieldInfo2), new ParameterExpression[1] { parameterExpression }).Compile();
					num++;
					DebugLogger.Verbose("✓ InventoryGui.m_recipeListRoot");
				}
				else
				{
					num2++;
					DebugLogger.Warning("✗ InventoryGui.m_recipeListRoot not found");
				}
				FieldInfo fieldInfo3 = AccessTools.Field(typeof(InventoryGui), "m_availableRecipes");
				if (fieldInfo3 != null)
				{
					ParameterExpression parameterExpression2 = Expression.Parameter(typeof(InventoryGui), "arg");
					_getAvailableRecipes = Expression.Lambda<Func<InventoryGui, object>>(Expression.Field(parameterExpression2, fieldInfo3), new ParameterExpression[1] { parameterExpression2 }).Compile();
					num++;
					DebugLogger.Verbose("✓ InventoryGui.m_availableRecipes");
				}
				else
				{
					num2++;
					DebugLogger.Warning("✗ InventoryGui.m_availableRecipes not found");
				}
				FieldInfo fieldInfo4 = AccessTools.Field(typeof(InventoryGui), "m_currentContainer");
				if (fieldInfo4 != null)
				{
					ParameterExpression parameterExpression3 = Expression.Parameter(typeof(InventoryGui), "arg");
					_getCurrentContainer = Expression.Lambda<Func<InventoryGui, Container>>(Expression.Field(parameterExpression3, fieldInfo4), new ParameterExpression[1] { parameterExpression3 }).Compile();
					num++;
					DebugLogger.Verbose("✓ InventoryGui.m_currentContainer");
				}
				else
				{
					num2++;
					DebugLogger.Warning("✗ InventoryGui.m_currentContainer not found");
				}
				FieldInfo fieldInfo5 = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe");
				if (fieldInfo5 != null)
				{
					ParameterExpression parameterExpression4 = Expression.Parameter(typeof(InventoryGui), "arg");
					_getCraftRecipe = Expression.Lambda<Func<InventoryGui, Recipe>>(Expression.Field(parameterExpression4, fieldInfo5), new ParameterExpression[1] { parameterExpression4 }).Compile();
					num++;
					DebugLogger.Verbose("✓ InventoryGui.m_craftRecipe");
				}
				else
				{
					num2++;
					DebugLogger.Warning("✗ InventoryGui.m_craftRecipe not found");
				}
				FieldInfo fieldInfo6 = AccessTools.Field(typeof(InventoryGui), "m_craftUpgradeItem");
				if (fieldInfo6 != null)
				{
					ParameterExpression parameterExpression5 = Expression.Parameter(typeof(InventoryGui), "arg");
					_getCraftUpgradeItem = Expression.Lambda<Func<InventoryGui, ItemData>>(Expression.Field(parameterExpression5, fieldInfo6), new ParameterExpression[1] { parameterExpression5 }).Compile();
					num++;
					DebugLogger.Verbose("✓ InventoryGui.m_craftUpgradeItem");
				}
				else
				{
					num2++;
					DebugLogger.Warning("✗ InventoryGui.m_craftUpgradeItem not found");
				}
				FieldInfo fieldInfo7 = AccessTools.Field(typeof(Hud), "m_hoveredPiece");
				if (fieldInfo7 != null)
				{
					ParameterExpression parameterExpression6 = Expression.Parameter(typeof(Hud), "arg");
					_getHoveredPiece = Expression.Lambda<Func<Hud, Piece>>(Expression.Field(parameterExpression6, fieldInfo7), new ParameterExpression[1] { parameterExpression6 }).Compile();
					num++;
					DebugLogger.Verbose("✓ Hud.m_hoveredPiece");
				}
				else
				{
					num2++;
					DebugLogger.Warning("✗ Hud.m_hoveredPiece not found");
				}
				MethodInfo methodInfo = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null);
				if (methodInfo != null)
				{
					CheckContainerAccess = AccessTools.MethodDelegate<Func<Container, long, bool>>(methodInfo, (object)null, true);
					num++;
					DebugLogger.Verbose("✓ Container.CheckAccess");
				}
				else
				{
					num2++;
					DebugLogger.Warning("✗ Container.CheckAccess not found");
				}
				DebugLogger.Log($"Reflection initialization complete: {num} successful, {num2} failed");
				if (num2 > 0)
				{
					DebugLogger.Warning("Some reflection targets failed - mod may not work correctly!");
				}
				_f_buildPieces = AccessTools.Field(typeof(Player), "m_buildPieces");
				if (_f_buildPieces != null)
				{
					num++;
					DebugLogger.Verbose("✓ Player.m_buildPieces");
				}
				else
				{
					num2++;
					DebugLogger.Warning("✗ Player.m_buildPieces not found");
				}
			}
			catch (Exception ex)
			{
				DebugLogger.Error("Critical error during reflection initialization", ex);
			}
		}

		public static void UpdateGuiScale()
		{
			if (_getGuiScale != null)
			{
				currentGuiScaleValue = _getGuiScale();
			}
			else
			{
				currentGuiScaleValue = 1f;
			}
		}

		public static Transform GetRecipeListRoot(InventoryGui gui)
		{
			if (_getRecipeListRoot == null)
			{
				DebugLogger.Warning("GetRecipeListRoot delegate is null");
				return null;
			}
			return _getRecipeListRoot(gui);
		}

		public static object GetAvailableRecipes(InventoryGui gui)
		{
			if (_getAvailableRecipes == null)
			{
				DebugLogger.Warning("GetAvailableRecipes delegate is null");
				return null;
			}
			return _getAvailableRecipes(gui);
		}

		public static Container GetCurrentContainer(InventoryGui gui)
		{
			if (_getCurrentContainer == null)
			{
				DebugLogger.Verbose("GetCurrentContainer delegate is null");
				return null;
			}
			return _getCurrentContainer(gui);
		}

		public static Recipe GetCraftRecipe(InventoryGui gui)
		{
			if (_getCraftRecipe == null)
			{
				DebugLogger.Warning("GetCraftRecipe delegate is null");
				return null;
			}
			return _getCraftRecipe(gui);
		}

		public static ItemData GetCraftUpgradeItem(InventoryGui gui)
		{
			return _getCraftUpgradeItem?.Invoke(gui);
		}

		public static Piece GetHoveredPiece(Hud hud)
		{
			if (_getHoveredPiece == null)
			{
				DebugLogger.Verbose("GetHoveredPiece delegate is null");
				return null;
			}
			return _getHoveredPiece(hud);
		}

		public static PieceTable GetPieceTable(Player player)
		{
			if (_f_buildPieces == null || (Object)(object)player == (Object)null)
			{
				return null;
			}
			object? value = _f_buildPieces.GetValue(player);
			return (PieceTable)((value is PieceTable) ? value : null);
		}
	}
	public static class InputHelper
	{
		public static bool IsInputBlocked()
		{
			if (Console.IsVisible())
			{
				return true;
			}
			if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
			{
				return true;
			}
			if (TextInput.IsVisible())
			{
				DebugLogger.Verbose("Input blocked: TextInput is visible");
				return true;
			}
			return false;
		}

		public static bool IsMouseOverRect(RectTransform rect)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rect == (Object)null)
			{
				DebugLogger.Verbose("IsMouseOverRect: rect is null");
				return false;
			}
			bool flag = RectTransformUtility.RectangleContainsScreenPoint(rect, Vector2.op_Implicit(Input.mousePosition));
			if (flag)
			{
				DebugLogger.Verbose("Mouse over rect: " + ((Object)((Component)rect).gameObject).name);
			}
			return flag;
		}
	}
	public static class UIBuilder
	{
		private static Color ValheimOrange = new Color(1f, 0.77f, 0.31f, 1f);

		private static Color DividerColor = new Color(1f, 1f, 1f, 0.1f);

		private static Sprite _cachedUiSprite;

		private static bool _spriteSearchDone = false;

		private static Sprite GetBackgroundSprite()
		{
			if ((Object)(object)_cachedUiSprite != (Object)null)
			{
				return _cachedUiSprite;
			}
			if (_spriteSearchDone)
			{
				return null;
			}
			Sprite[] source = Resources.FindObjectsOfTypeAll<Sprite>();
			_cachedUiSprite = ((IEnumerable<Sprite>)source).FirstOrDefault((Func<Sprite, bool>)((Sprite x) => ((Object)x).name == "UISprite")) ?? ((IEnumerable<Sprite>)source).FirstOrDefault((Func<Sprite, bool>)((Sprite x) => ((Object)x).name == "Knob"));
			_spriteSearchDone = true;
			if ((Object)(object)_cachedUiSprite != (Object)null)
			{
				DebugLogger.Verbose("Found background sprite: " + ((Object)_cachedUiSprite).name);
			}
			else
			{
				DebugLogger.Warning("No suitable background sprite found");
			}
			return _cachedUiSprite;
		}

		public static PinSlotUI CreatePinSlot(Transform parent, Font font)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Expected O, but got Unknown
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Expected O, but got Unknown
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Expected O, but got Unknown
			//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0348: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Expected O, but got Unknown
			//IL_038c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0391: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0416: Unknown result type (might be due to invalid IL or missing references)
			//IL_041d: Expected O, but got Unknown
			GameObject val = new GameObject("PinSlot", new Type[1] { typeof(RectTransform) });
			val.layer = 5;
			val.transform.SetParent(parent, false);
			PinSlotUI pinSlotUI = val.AddComponent<PinSlotUI>();
			pinSlotUI.Rect = val.GetComponent<RectTransform>();
			Image val2 = val.AddComponent<Image>();
			Sprite val3 = (val2.sprite = GetBackgroundSprite());
			if ((Object)(object)val3 != (Object)null && val3.border != Vector4.zero)
			{
				val2.type = (Type)1;
			}
			else
			{
				val2.type = (Type)0;
			}
			float num = ((RecipePinnerPlugin.BackgroundOpacity != null) ? RecipePinnerPlugin.BackgroundOpacity.Value : 0.45f);
			((Graphic)val2).color = new Color(0f, 0f, 0f, num);
			((Graphic)val2).raycastTarget = false;
			VerticalLayoutGroup val4 = val.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)val4).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)val4).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)val4).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)val4).spacing = 5f;
			((LayoutGroup)val4).padding = new RectOffset(8, 8, 8, 8);
			ContentSizeFitter val5 = val.AddComponent<ContentSizeFitter>();
			val5.horizontalFit = (FitMode)0;
			val5.verticalFit = (FitMode)2;
			GameObject val6 = new GameObject("HeaderRow", new Type[1] { typeof(RectTransform) });
			val6.layer = 5;
			val6.transform.SetParent(val.transform, false);
			HorizontalLayoutGroup val7 = val6.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)val7).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)val7).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)val7).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)val7).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)val7).spacing = 8f;
			LayoutElement val8 = val6.AddComponent<LayoutElement>();
			val8.minHeight = 30f;
			val8.flexibleHeight = 0f;
			val8.flexibleWidth = 1f;
			GameObject val9 = new GameObject("Icon", new Type[1] { typeof(RectTransform) });
			val9.layer = 5;
			val9.transform.SetParent(val6.transform, false);
			Image val10 = val9.AddComponent<Image>();
			((Graphic)val10).raycastTarget = false;
			val10.preserveAspect = true;
			pinSlotUI.IconImage = val10;
			LayoutElement val11 = val9.AddComponent<LayoutElement>();
			val11.minWidth = 28f;
			val11.minHeight = 28f;
			val11.preferredWidth = 28f;
			val11.preferredHeight = 28f;
			val11.flexibleWidth = 0f;
			GameObject val12 = new GameObject("Title", new Type[1] { typeof(RectTransform) });
			val12.layer = 5;
			val12.transform.SetParent(val6.transform, false);
			Text val13 = val12.AddComponent<Text>();
			((Graphic)val13).raycastTarget = false;
			val13.font = font;
			val13.fontSize = 18;
			val13.alignment = (TextAnchor)3;
			val13.horizontalOverflow = (HorizontalWrapMode)0;
			val13.verticalOverflow = (VerticalWrapMode)1;
			((Graphic)val13).color = ValheimOrange;
			pinSlotUI.HeaderText = val13;
			LayoutElement val14 = val12.AddComponent<LayoutElement>();
			val14.minHeight = 24f;
			val14.flexibleWidth = 1f;
			GameObject val15 = new GameObject("Divider", new Type[1] { typeof(RectTransform) });
			val15.layer = 5;
			val15.transform.SetParent(val.transform, false);
			Image val16 = val15.AddComponent<Image>();
			val16.sprite = GetBackgroundSprite();
			if ((Object)(object)val3 != (Object)null && val3.border != Vector4.zero)
			{
				val16.type = (Type)1;
			}
			else
			{
				val16.type = (Type)0;
			}
			((Graphic)val16).color = DividerColor;
			((Graphic)val16).raycastTarget = false;
			LayoutElement val17 = val15.AddComponent<LayoutElement>();
			val17.minHeight = 2f;
			val17.preferredHeight = 2f;
			val17.flexibleWidth = 1f;
			GameObject val18 = new GameObject("ResourceList", new Type[1] { typeof(RectTransform) });
			val18.layer = 5;
			val18.transform.SetParent(val.transform, false);
			VerticalLayoutGroup val19 = val18.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)val19).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)val19).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)val19).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)val19).spacing = 3f;
			ContentSizeFitter val20 = val18.AddComponent<ContentSizeFitter>();
			val20.verticalFit = (FitMode)2;
			pinSlotUI.ResourceListRoot = val18.transform;
			DebugLogger.Verbose("Created pin slot UI");
			return pinSlotUI;
		}

		public static ResourceSlotUI CreateResourceSlot(Transform parent, Font font)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Expected O, but got Unknown
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Expected O, but got Unknown
			GameObject val = new GameObject("ResSlot", new Type[1] { typeof(RectTransform) });
			val.layer = 5;
			val.transform.SetParent(parent, false);
			ResourceSlotUI resourceSlotUI = val.AddComponent<ResourceSlotUI>();
			HorizontalLayoutGroup val2 = val.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)val2).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)val2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)val2).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)val2).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)val2).spacing = 6f;
			LayoutElement val3 = val.AddComponent<LayoutElement>();
			val3.minHeight = 22f;
			val3.flexibleHeight = 0f;
			GameObject val4 = new GameObject("Icon", new Type[1] { typeof(RectTransform) });
			val4.layer = 5;
			val4.transform.SetParent(val.transform, false);
			resourceSlotUI.ResIcon = val4.AddComponent<Image>();
			((Graphic)resourceSlotUI.ResIcon).raycastTarget = false;
			resourceSlotUI.ResIcon.preserveAspect = true;
			LayoutElement val5 = val4.AddComponent<LayoutElement>();
			val5.minWidth = 20f;
			val5.minHeight = 20f;
			val5.preferredWidth = 20f;
			val5.preferredHeight = 20f;
			val5.flexibleWidth = 0f;
			GameObject val6 = new GameObject("Name", new Type[1] { typeof(RectTransform) });
			val6.layer = 5;
			val6.transform.SetParent(val.transform, false);
			resourceSlotUI.ResName = val6.AddComponent<Text>();
			((Graphic)resourceSlotUI.ResName).raycastTarget = false;
			resourceSlotUI.ResName.font = font;
			resourceSlotUI.ResName.fontSize = 15;
			resourceSlotUI.ResName.alignment = (TextAnchor)3;
			resourceSlotUI.ResName.horizontalOverflow = (HorizontalWrapMode)0;
			((Graphic)resourceSlotUI.ResName).color = new Color(0.9f, 0.9f, 0.9f, 1f);
			LayoutElement val7 = val6.AddComponent<LayoutElement>();
			val7.flexibleWidth = 1f;
			GameObject val8 = new GameObject("Amount", new Type[1] { typeof(RectTransform) });
			val8.layer = 5;
			val8.transform.SetParent(val.transform, false);
			resourceSlotUI.ResAmount = val8.AddComponent<Text>();
			((Graphic)resourceSlotUI.ResAmount).raycastTarget = false;
			resourceSlotUI.ResAmount.font = font;
			resourceSlotUI.ResAmount.fontSize = 15;
			resourceSlotUI.ResAmount.alignment = (TextAnchor)5;
			LayoutElement val9 = val8.AddComponent<LayoutElement>();
			val9.minWidth = 40f;
			DebugLogger.Verbose("Created resource slot UI");
			return resourceSlotUI;
		}

		public static GameObject CreatePaginationContainer(Transform parent)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			GameObject val = new GameObject("PaginationDots", new Type[1] { typeof(RectTransform) });
			val.layer = 5;
			val.transform.SetParent(parent, false);
			LayoutElement val2 = val.AddComponent<LayoutElement>();
			val2.ignoreLayout = true;
			HorizontalLayoutGroup val3 = val.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)val3).childControlHeight = false;
			((HorizontalOrVerticalLayoutGroup)val3).childControlWidth = false;
			((HorizontalOrVerticalLayoutGroup)val3).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)val3).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)val3).spacing = RecipePinnerPlugin.PaginationDotSpacing.Value;
			((LayoutGroup)val3).childAlignment = (TextAnchor)4;
			ContentSizeFitter val4 = val.AddComponent<ContentSizeFitter>();
			val4.horizontalFit = (FitMode)2;
			val4.verticalFit = (FitMode)2;
			return val;
		}

		public static Image CreatePageDot(Transform parent)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("PageDot", new Type[1] { typeof(RectTransform) });
			val.layer = 5;
			val.transform.SetParent(parent, false);
			Image val2 = val.AddComponent<Image>();
			val2.type = (Type)0;
			((Graphic)val2).raycastTarget = false;
			int value = RecipePinnerPlugin.PaginationDotSize.Value;
			RectTransform component = val.GetComponent<RectTransform>();
			component.sizeDelta = new Vector2((float)value, (float)value);
			((Transform)component).localRotation = Quaternion.Euler(0f, 0f, 45f);
			return val2;
		}
	}
	public class PinSlotUI : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <FixLayout>d__16 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public PinSlotUI <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <FixLayout>d__16(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if ((Object)(object)<>4__this.ResourceListRoot != (Object)null)
					{
						Transform resourceListRoot = <>4__this.ResourceListRoot;
						LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)(object)((resourceListRoot is RectTransform) ? resourceListRoot : null));
					}
					if ((Object)(object)<>4__this.Rect != (Object)null)
					{
						LayoutRebuilder.ForceRebuildLayoutImmediate(<>4__this.Rect);
					}
					if ((Object)(object)((Component)<>4__this).transform.parent != (Object)null)
					{
						Transform parent = ((Component)<>4__this).transform.parent;
						LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)(object)((parent is RectTransform) ? parent : null));
					}
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		public RectTransform Rect;

		public Image IconImage;

		public Text HeaderText;

		public Transform ResourceListRoot;

		public PinnedRecipeData CurrentData;

		private Coroutine _layoutCoroutine;

		private Image _cachedBg;

		private ContentSizeFitter _cachedCsf;

		public List<ResourceSlotUI> ResourceSlots = new List<ResourceSlotUI>();

		public Image BgImage => Object.op_Implicit((Object)(object)_cachedBg) ? _cachedBg : (_cachedBg = ((Component)this).GetComponent<Image>());

		public ContentSizeFitter Csf => Object.op_Implicit((Object)(object)_cachedCsf) ? _cachedCsf : (_cachedCsf = ((Component)this).GetComponent<ContentSizeFitter>());

		public void SetActive(bool active)
		{
			((Component)this).gameObject.SetActive(active);
		}

		public void UpdateData(PinnedRecipeData data, Font font)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			IconImage.sprite = data.Icon;
			HeaderText.text = data.CachedHeader;
			HeaderText.font = font;
			HeaderText.fontSize = RecipePinnerPlugin.FontSizeRecipeName.Value;
			((Graphic)HeaderText).color = RecipePinnerPlugin.ColorHeader.Value;
			int count = data.Resources.Count;
			while (ResourceSlots.Count < count)
			{
				ResourceSlots.Add(UIBuilder.CreateResourceSlot(ResourceListRoot, font));
			}
			for (int i = 0; i < ResourceSlots.Count; i++)
			{
				if (i < count)
				{
					ResourceSlots[i].SetActive(active: true);
					ResourceSlots[i].UpdateResource(data.Resources[i]);
				}
				else
				{
					ResourceSlots[i].SetActive(active: false);
				}
			}
			if (((Component)this).gameObject.activeInHierarchy)
			{
				if (_layoutCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_layoutCoroutine);
				}
				_layoutCoroutine = ((MonoBehaviour)this).StartCoroutine(FixLayout());
			}
		}

		private void OnDisable()
		{
			_layoutCoroutine = null;
		}

		[IteratorStateMachine(typeof(<FixLayout>d__16))]
		private IEnumerator FixLayout()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <FixLayout>d__16(0)
			{
				<>4__this = this
			};
		}
	}
	public class ResourceSlotUI : MonoBehaviour
	{
		public Image ResIcon;

		public Text ResName;

		public Text ResAmount;

		public void SetActive(bool active)
		{
			((Component)this).gameObject.SetActive(active);
		}

		public void UpdateResource(PinnedResData res)
		{
			ResIcon.sprite = res.Icon;
			ResName.text = res.CachedName;
			ResAmount.text = res.CachedAmountString;
			if (RecipePinnerPlugin.FontSizeMaterials != null)
			{
				int value = RecipePinnerPlugin.FontSizeMaterials.Value;
				ResName.fontSize = value;
				ResAmount.fontSize = value;
			}
		}
	}
	public class UIManager
	{
		private Transform _pinListRoot;

		private List<PinSlotUI> _pinPool = new List<PinSlotUI>();

		private Font _cachedFont;

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

		private int _currentPage = 0;

		private GameObject _paginationRoot;

		public void DestroyUI()
		{
			DebugLogger.Verbose("Destroying UI...");
			if ((Object)(object)_pinListRoot != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_pinListRoot).gameObject);
				_pinListRoot = null;
			}
			if (_pinPool != null)
			{
				_pinPool.Clear();
			}
			DebugLogger.Log("UI destroyed successfully");
		}

		public void ResetPage()
		{
			_currentPage = 0;
		}

		public void CyclePage()
		{
			RecipeManager recipeMgr = RecipePinnerPlugin.Instance.RecipeMgr;
			int count = recipeMgr.CachedPins.Count;
			int value = RecipePinnerPlugin.PinsPerPage.Value;
			if (count > value)
			{
				int num = Mathf.CeilToInt((float)count / (float)value);
				_currentPage++;
				if (_currentPage >= num)
				{
					_currentPage = 0;
				}
				DebugLogger.Log($"Switched to Page: {_currentPage + 1}/{num}");
				UpdateUI(isVisible: true);
			}
		}

		public void UpdateUI(bool isVisible)
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null || ((Character)Player.m_localPlayer).IsDead())
			{
				return;
			}
			Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
			if (inventory == null)
			{
				return;
			}
			RecipePinnerPlugin instance = RecipePinnerPlugin.Instance;
			RecipeManager recipeMgr = instance.RecipeMgr;
			ContainerScanner containerMgr = instance.ContainerMgr;
			if (_pinPool.Count < RecipePinnerPlugin.MaximumPins.Value)
			{
				DebugLogger.Log($"Pin limit changed (Pool: {_pinPool.Count}, Config: {RecipePinnerPlugin.MaximumPins.Value}). Rebuilding UI...");
				DestroyUI();
			}
			if ((Object)(object)_pinListRoot == (Object)null)
			{
				_pinPool.Clear();
				CreateCanvasUI();
				if ((Object)(object)_pinListRoot == (Object)null)
				{
					return;
				}
				foreach (PinnedRecipeData cachedPin in recipeMgr.CachedPins)
				{
					cachedPin.IsDirty = true;
				}
			}
			UpdateLayout();
			if ((Object)(object)_pinListRoot == (Object)null)
			{
				return;
			}
			bool flag = isVisible && !InputHelper.IsInputBlocked() && recipeMgr.CachedPins.Count > 0;
			if (((Component)_pinListRoot).gameObject.activeSelf != flag)
			{
				((Component)_pinListRoot).gameObject.SetActive(flag);
			}
			if (!flag)
			{
				return;
			}
			_reusableInvCounts.Clear();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				string name = allItem.m_shared.m_name;
				if (_reusableInvCounts.ContainsKey(name))
				{
					_reusableInvCounts[name] += allItem.m_stack;
				}
				else
				{
					_reusableInvCounts[name] = allItem.m_stack;
				}
			}
			int count = recipeMgr.CachedPins.Count;
			int value = RecipePinnerPlugin.PinsPerPage.Value;
			int num = _currentPage * value;
			if (num >= count && _currentPage > 0)
			{
				_currentPage--;
				num = _currentPage * value;
			}
			int num2 = Mathf.Min(num + value, count);
			for (int i = 0; i < _pinPool.Count; i++)
			{
				if (!((Object)(object)_pinPool[i] == (Object)null))
				{
					int num3 = num + i;
					if (num3 < num2)
					{
						UpdatePinSlot(i, recipeMgr.CachedPins[num3], containerMgr);
					}
					else if (((Component)_pinPool[i]).gameObject.activeSelf)
					{
						_pinPool[i].SetActive(active: false);
					}
				}
			}
			int totalPages = 1;
			if (count > 0)
			{
				totalPages = Mathf.CeilToInt((float)count / (float)value);
			}
			UpdatePageDots(totalPages);
		}

		private void UpdatePageDots(int totalPages)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: 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_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_paginationRoot == (Object)null)
			{
				return;
			}
			HorizontalLayoutGroup component = _paginationRoot.GetComponent<HorizontalLayoutGroup>();
			if ((Object)(object)component != (Object)null)
			{
				((HorizontalOrVerticalLayoutGroup)component).spacing = RecipePinnerPlugin.PaginationDotSpacing.Value;
			}
			if (totalPages <= 1)
			{
				if (_paginationRoot.activeSelf)
				{
					_paginationRoot.SetActive(false);
				}
				return;
			}
			if (!_paginationRoot.activeSelf)
			{
				_paginationRoot.SetActive(true);
			}
			foreach (Transform item in _paginationRoot.transform)
			{
				Transform val = item;
				Object.Destroy((Object)(object)((Component)val).gameObject);
			}
			int value = RecipePinnerPlugin.PaginationDotSize.Value;
			Color value2 = RecipePinnerPlugin.ColorPaginationActive.Value;
			for (int i = 0; i < totalPages; i++)
			{
				Image val2 = UIBuilder.CreatePageDot(_paginationRoot.transform);
				if (i == _currentPage)
				{
					((Graphic)val2).color = value2;
					((Graphic)val2).rectTransform.sizeDelta = new Vector2((float)value * 1.2f, (float)value * 1.2f);
					continue;
				}
				Color color = value2;
				color.a = RecipePinnerPlugin.PaginationInactiveOpacity.Value;
				((Graphic)val2).color = color;
				((Graphic)val2).rectTransform.sizeDelta = new Vector2((float)value, (float)value);
			}
		}

		private void UpdateDotsPosition()
		{
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_paginationRoot == (Object)null || (Object)(object)_pinListRoot == (Object)null)
			{
				return;
			}
			RectTransform component = _paginationRoot.GetComponent<RectTransform>();
			bool flag = RecipePinnerPlugin.LayoutModeConfig.Value == RecipePinnerPlugin.PinLayoutMode.ForceBottomRightHorizontal;
			bool flag2 = (Object)(object)Player.m_localPlayer.GetControlledShip() != (Object)null;
			if (RecipePinnerPlugin.Instance.IsHorizontalMode || flag2)
			{
				float num = ((flag || flag2) ? RecipePinnerPlugin.BottomRightColumnWidth.Value : RecipePinnerPlugin.HorizontalColumnWidth.Value);
				float num2 = 0f - num / 2f;
				if (flag || flag2)
				{
					component.anchorMin = new Vector2(1f, 0f);
					component.anchorMax = new Vector2(1f, 0f);
					component.pivot = new Vector2(0.5f, 1f);
					component.anchoredPosition = new Vector2(num2, -15f);
				}
				else
				{
					component.anchorMin = new Vector2(1f, 1f);
					component.anchorMax = new Vector2(1f, 1f);
					component.pivot = new Vector2(0.5f, 0f);
					component.anchoredPosition = new Vector2(num2, 15f);
				}
			}
			else
			{
				component.anchorMin = new Vector2(0.5f, 1f);
				component.anchorMax = new Vector2(0.5f, 1f);
				component.pivot = new Vector2(0.5f, 0f);
				component.anchoredPosition = new Vector2(0f, 20f);
			}
		}

		private void UpdatePinSlot(int index, PinnedRecipeData pinData, ContainerScanner containerMgr)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			PinSlotUI pinSlotUI = _pinPool[index];
			if ((Object)(object)pinSlotUI == (Object)null || (Object)(object)((Component)pinSlotUI).gameObject == (Object)null)
			{
				return;
			}
			if (!((Component)pinSlotUI).gameObject.activeSelf)
			{
				pinSlotUI.SetActive(active: true);
			}
			if ((Object)(object)pinSlotUI.BgImage != (Object)null)
			{
				float a = ((Graphic)pinSlotUI.BgImage).color.a;
				if (Mathf.Abs(a - RecipePinnerPlugin.BackgroundOpacity.Value) > 0.01f)
				{
					((Graphic)pinSlotUI.BgImage).color = new Color(0f, 0f, 0f, RecipePinnerPlugin.BackgroundOpacity.Value);
				}
			}
			bool flag = RecipePinnerPlugin.LayoutModeConfig.Value == RecipePinnerPlugin.PinLayoutMode.ForceBottomRightHorizontal;
			bool flag2 = (Object)(object)Player.m_localPlayer.GetControlledShip() != (Object)null;
			if (RecipePinnerPlugin.Instance.IsHorizontalMode || flag2)
			{
				RectTransform val = pinSlotUI.Rect ?? ((Component)pinSlotUI).GetComponent<RectTransform>();
				float num = ((flag || flag2) ? RecipePinnerPlugin.BottomRightColumnWidth.Value : RecipePinnerPlugin.HorizontalColumnWidth.Value);
				if (Mathf.Abs(val.sizeDelta.x - num) > 1f)
				{
					val.sizeDelta = new Vector2(num, val.sizeDelta.y);
				}
			}
			bool flag3 = pinSlotUI.CurrentData != pinData;
			pinSlotUI.CurrentData = pinData;
			foreach (PinnedResData resource in pinData.Resources)
			{
				int value = 0;
				_reusableInvCounts.TryGetValue(resource.ItemName, out value);
				int num2 = 0;
				if (RecipePinnerPlugin.EnableChestScanning.Value && containerMgr.ContainerCache.ContainsKey(resource.ItemName))
				{
					num2 = containerMgr.ContainerCache[resource.ItemName];
				}
				int num3 = value + num2;
				if (num3 != resource.LastKnownAmount || value != resource.LastKnownInvAmount || resource.CachedAmountString == null)
				{
					resource.LastKnownAmount = num3;
					resource.LastKnownInvAmount = value;
					Color val2 = ((value >= resource.RequiredAmount) ? RecipePinnerPlugin.ColorEnoughInInventory.Value : ((num3 >= resource.RequiredAmount) ? RecipePinnerPlugin.ColorEnoughWithChests.Value : RecipePinnerPlugin.ColorMissing.Value));
					string arg = "#" + ColorUtility.ToHtmlStringRGBA(val2);
					string text = $"<color={arg}>{num3}/{resource.RequiredAmount}</color>";
					if (resource.CachedAmountString != text)
					{
						resource.CachedAmountString = text;
						pinData.IsDirty = true;
					}
				}
			}
			if (flag3 || pinData.IsDirty)
			{
				pinSlotUI.UpdateData(pinData, _cachedFont);
				pinData.IsDirty = false;
			}
		}

		private void CreateCanvasUI()