Decompiled source of RecipePinner v1.2.1

plugins/RecipePinner.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
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 ValheimRecipePinner;

public class ContainerScanner
{
	public static List<Container> AllContainers = new List<Container>();

	private static readonly HashSet<Container> _containerSet = new HashSet<Container>();

	internal static readonly object ContainerLock = new object();

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

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

	private readonly List<Container> _snapshotBuffer = new List<Container>();

	private Vector3 _lastScanPos;

	private int _lastItemCount = 0;

	private float _scanTimer = 0f;

	private float _moveScanCooldown = 0f;

	private const float MovementThresholdSqr = 4f;

	private const float MinMoveScanCooldown = 1f;

	public void InitializeContainers()
	{
		DebugLogger.Log("Init containers");
		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 && _containerSet.Add(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($"Tracking {AllContainers.Count} containers");
		}
	}

	public void UpdateScanning()
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			return;
		}
		_scanTimer += Time.deltaTime;
		_moveScanCooldown += Time.deltaTime;
		float num = Vector3.SqrMagnitude(((Component)Player.m_localPlayer).transform.position - _lastScanPos);
		bool flag = num > 4f && _moveScanCooldown >= 1f;
		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);
		bool flag4 = _scanTimer >= num3;
		if (flag || flag2 || flag4)
		{
			DebugLogger.Verbose($"Scanning containers - Moved: {flag}, InvChanged: {flag2}, Interval: {flag4}");
			_scanTimer = 0f;
			if (flag)
			{
				_moveScanCooldown = 0f;
			}
			_lastItemCount = num2;
			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_0250: Unknown result type (might be due to invalid IL or missing references)
		//IL_0251: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		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;
		_snapshotBuffer.Clear();
		lock (ContainerLock)
		{
			_snapshotBuffer.AddRange(AllContainers);
		}
		_processedIDs.Clear();
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		foreach (Container item in _snapshotBuffer)
		{
			if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).transform == (Object)null)
			{
				num3++;
				continue;
			}
			int instanceID = ((Object)item).GetInstanceID();
			if (!_processedIDs.Add(instanceID))
			{
				num3++;
				continue;
			}
			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 (_containerSet.Add(__instance))
			{
				AllContainers.Add(__instance);
				ContainerTracker containerTracker = ((Component)__instance).gameObject.GetComponent<ContainerTracker>() ?? ((Component)__instance).gameObject.AddComponent<ContainerTracker>();
				containerTracker.MyContainer = __instance;
				DebugLogger.Verbose($"New container tracked: {((Object)__instance).name} (Total: {AllContainers.Count})");
			}
		}
	}

	public static void RemoveFromSet(Container c)
	{
		_containerSet.Remove(c);
	}
}
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);
				ContainerScanner.RemoveFromSet(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;
				}
				int num3 = text.LastIndexOf(':');
				if (num3 > 0 && num3 < text.Length - 1)
				{
					string key = text.Substring(0, num3).Trim();
					string s = text.Substring(num3 + 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;
				Dictionary<string, int> dictionary = new Dictionary<string, int>();
				int num4 = 0;
				foreach (KeyValuePair<string, int> pinnedRecipe in recipeMgr.PinnedRecipes)
				{
					if (num4 >= RecipePinnerPlugin.MaximumPins.Value)
					{
						break;
					}
					dictionary[pinnedRecipe.Key] = pinnedRecipe.Value;
					num4++;
				}
				recipeMgr.PinnedRecipes = dictionary;
				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 readonly RecipePinnerPlugin _plugin;

	private readonly 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" },
		{ "gathering_title", "GATHERING LIST" },
		{ "gathering_opened", "Gathering List Opened" },
		{ "gathering_closed", "Gathering List Closed" },
		{ "gathering_empty", "No Recipes Pinned" },
		{ "gathering_hint", "Open/Close: {0}" }
	};

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

	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)
			{
				string text5 = text4.Trim();
				if (string.IsNullOrEmpty(text5) || text5 == "{" || text5 == "}" || !text5.Contains(":"))
				{
					continue;
				}
				string[] array2 = text5.Split(new char[1] { ':' }, 2);
				if (array2.Length == 2)
				{
					string text6 = array2[0].Trim(',', '"', ' ', '\t', '\r');
					string value = array2[1].Trim(',', '"', ' ', '\t', '\r');
					if (!string.IsNullOrEmpty(text6) && !string.IsNullOrEmpty(value))
					{
						_localizedText[text6] = 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 readonly 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 readonly Dictionary<Type, FieldInfo> _cachedRecipeFields = new Dictionary<Type, FieldInfo>();

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

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

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

	public void Cleanup()
	{
		DebugLogger.Log("RecipeManager cleanup");
		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 {count} fake recipes");
		_cachedRecipeFields.Clear();
		_cachedRecipeProps.Clear();
		_cachedItemFields.Clear();
		_cachedItemProps.Clear();
	}

	public void RefreshRecipeCache()
	{
		DebugLogger.Verbose("Refreshing cache");
		CachedPins.Clear();
		if ((Object)(object)ObjectDB.instance == (Object)null)
		{
			DebugLogger.Warning("ObjectDB null, can't refresh");
			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;
						}
					}
				}
				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($"Cache refreshed: {num} ok, {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 = null;
		foreach (Recipe recipe2 in ObjectDB.instance.m_recipes)
		{
			if (((Object)recipe2).name == name)
			{
				val3 = recipe2;
				break;
			}
		}
		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>();
				val5.m_resources = (Requirement[])component.m_resources.Clone();
				_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_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: 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;
		int num = Mathf.Max(1, targetLevel - 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 * num,
					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 pins");
		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);
			}
			RecipePinnerPlugin.Instance?.DataMgr.SavePins();
			DebugLogger.Log($"Removed {list.Count} invalid pins");
		}
		else
		{
			DebugLogger.Log("All pins valid");
		}
	}

	public void TryPinHoveredRecipe(InventoryGui gui)
	{
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Expected O, but got Unknown
		DebugLogger.Verbose("Attempting to pin hovered recipe...");
		Transform recipeListRoot = ReflectionHelper.GetRecipeListRoot(gui);
		if (!(ReflectionHelper.GetAvailableRecipes(gui) is IList list) || (Object)(object)recipeListRoot == (Object)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) ?? 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.TryGetValue(recipeName, out var value))
		{
			if (flag)
			{
				value--;
				if (value <= 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
				{
					PinnedRecipes[recipeName] = value;
					string text = string.Format(localizationMgr.GetText("decreased"), value);
					Player localPlayer2 = Player.m_localPlayer;
					if (localPlayer2 != null)
					{
						((Character)localPlayer2).Message((MessageType)2, text, 0, (Sprite)null);
					}
					DebugLogger.Log($"Decreased pin count: {recipeName} = {value}");
				}
			}
			else
			{
				value++;
				PinnedRecipes[recipeName] = value;
				string text2 = string.Format(localizationMgr.GetText("added_more"), value);
				Player localPlayer3 = Player.m_localPlayer;
				if (localPlayer3 != null)
				{
					((Character)localPlayer3).Message((MessageType)2, text2, 0, (Sprite)null);
				}
				DebugLogger.Log($"Increased pin count: {recipeName} = {value}");
			}
		}
		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.2.1")]
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<bool> EnableGatheringList;

	public static ConfigEntry<bool> AutoOpenGatheringList;

	public static ConfigEntry<KeyCode> HotkeyPin;

	public static ConfigEntry<KeyCode> HotkeyClearAll;

	public static ConfigEntry<KeyCode> HotkeyToggleVisibility;

	public static ConfigEntry<KeyCode> HotkeyPageSwitch;

	public static ConfigEntry<KeyCode> HotkeyGatheringList;

	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<bool> EnableCraftReadiness;

	public static ConfigEntry<Color> ColorCraftReady;

	public static ConfigEntry<Color> ColorCraftNotReady;

	public static ConfigEntry<int> GatheringListFontSizeTitle;

	public static ConfigEntry<int> GatheringListFontSizeMaterials;

	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 static ConfigEntry<Vector2> InventoryGatheringListPosition;

	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("Plugin init");
		LocalizationMgr = new LocalizationManager(this);
		RecipeMgr = new RecipeManager();
		ContainerMgr = new ContainerScanner();
		UIMgr = new UIManager();
		DataMgr = new DataPersistence();
		DebugLogger.Log("Managers ready");
		Harmony val = new Harmony("com.Kadrio.RecipePinner");
		val.PatchAll(typeof(RecipePinnerPlugin));
		val.PatchAll(typeof(ContainerScanner));
		DebugLogger.Log("Patches applied");
	}

	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_023f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0249: Expected O, but got Unknown
		//IL_0297: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a1: Expected O, but got Unknown
		//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e6: Expected O, but got Unknown
		//IL_0321: Unknown result type (might be due to invalid IL or missing references)
		//IL_032b: Expected O, but got Unknown
		//IL_0366: Unknown result type (might be due to invalid IL or missing references)
		//IL_0370: Expected O, but got Unknown
		//IL_03ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b5: Expected O, but got Unknown
		//IL_03ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f7: Expected O, but got Unknown
		//IL_042e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0438: Expected O, but got Unknown
		//IL_0498: Unknown result type (might be due to invalid IL or missing references)
		//IL_04a2: Expected O, but got Unknown
		//IL_04eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_04f5: Expected O, but got Unknown
		//IL_053e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0548: Expected O, but got Unknown
		//IL_05a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_05b2: Expected O, but got Unknown
		//IL_05ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_05f4: Expected O, but got Unknown
		//IL_0643: Unknown result type (might be due to invalid IL or missing references)
		//IL_064d: 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_06f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_06ff: Expected O, but got Unknown
		//IL_074d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0757: Expected O, but got Unknown
		//IL_0797: Unknown result type (might be due to invalid IL or missing references)
		//IL_07bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_07c7: Expected O, but got Unknown
		//IL_0807: Unknown result type (might be due to invalid IL or missing references)
		//IL_082d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0837: Expected O, but got Unknown
		//IL_0877: Unknown result type (might be due to invalid IL or missing references)
		//IL_089d: Unknown result type (might be due to invalid IL or missing references)
		//IL_08a7: Expected O, but got Unknown
		//IL_08e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_090d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0917: Expected O, but got Unknown
		//IL_0957: Unknown result type (might be due to invalid IL or missing references)
		//IL_097d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0987: Expected O, but got Unknown
		//IL_09c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_09ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_09f7: Expected O, but got Unknown
		//IL_0a37: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a5d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a67: Expected O, but got Unknown
		//IL_0ac7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ad1: Expected O, but got Unknown
		//IL_0b27: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b31: Expected O, but got Unknown
		//IL_0b86: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b90: Expected O, but got Unknown
		//IL_0be2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0bec: Expected O, but got Unknown
		//IL_0c27: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c31: Expected O, but got Unknown
		//IL_0c50: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c76: Unknown result type (might be due to invalid IL or missing references)
		//IL_0c80: Expected O, but got Unknown
		//IL_0cbb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0cc5: Expected O, but got Unknown
		//IL_0d00: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d0a: Expected O, but got Unknown
		//IL_0d29: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d4f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d59: Expected O, but got Unknown
		//IL_0d94: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d9e: Expected O, but got Unknown
		//IL_0dd9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0de3: Expected O, but got Unknown
		//IL_0e02: Unknown result type (might be due to invalid IL or missing references)
		//IL_0e28: Unknown result type (might be due to invalid IL or missing references)
		//IL_0e32: Expected O, but got Unknown
		//IL_0e51: Unknown result type (might be due to invalid IL or missing references)
		//IL_0e77: Unknown result type (might be due to invalid IL or missing references)
		//IL_0e81: Expected O, but got Unknown
		//IL_0eb8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ec2: Expected O, but got Unknown
		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();
			UIMgr?.DestroyUI();
		};
		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 = 94
			}
		}));
		EnableGatheringList = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "EnableGatheringList", true, new ConfigDescription("Enable the gathering list feature.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 93
			}
		}));
		EnableGatheringList.SettingChanged += delegate
		{
			UIMgr?.DestroyUI();
		};
		AutoOpenGatheringList = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "AutoOpenGatheringList", true, new ConfigDescription("Automatically open gathering list when 2+ recipes are pinned.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 92
			}
		}));
		HotkeyPin = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2 - Controls", "HotkeyPin", (KeyCode)325, new ConfigDescription("Key to pin recipe.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 99
			}
		}));
		HotkeyToggleVisibility = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2 - Controls", "HotkeyToggleVisibility", (KeyCode)288, new ConfigDescription("Key to toggle overlay.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 98
			}
		}));
		HotkeyGatheringList = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2 - Controls", "HotkeyGatheringList", (KeyCode)289, new ConfigDescription("Key to toggle gathering list panel.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 97
			}
		}));
		HotkeyPageSwitch = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2 - Controls", "HotkeyPageSwitch", (KeyCode)308, new ConfigDescription("Key to cycle through pin pages.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 96
			}
		}));
		HotkeyClearAll = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2 - Controls", "HotkeyClearAll", (KeyCode)112, new ConfigDescription("Key to clear all pins.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 95
			}
		}));
		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 (seconds).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 97
			}
		}));
		UIScale = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Appearance", "UIScale", 0.75f, new ConfigDescription("Global UI scale.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 3f), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 99
			}
		}));
		UIScale.SettingChanged += delegate
		{
			UIMgr?.DestroyUI();
		};
		BackgroundOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Appearance", "BackgroundOpacity", 0.5f, new ConfigDescription("Background opacity.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 98
			}
		}));
		FontSizeRecipeName = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Appearance", "FontSizeRecipeName", 16, new ConfigDescription("Recipe name font size.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 97
			}
		}));
		FontSizeRecipeName.SettingChanged += delegate
		{
			RecipeMgr?.RefreshRecipeCache();
		};
		FontSizeMaterials = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Appearance", "FontSizeMaterials", 15, new ConfigDescription("Material font size.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 96
			}
		}));
		FontSizeMaterials.SettingChanged += delegate
		{
			RecipeMgr?.RefreshRecipeCache();
		};
		GatheringListFontSizeTitle = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Appearance", "GatheringListFontSizeTitle", 20, new ConfigDescription("Gathering list title font size.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 95
			}
		}));
		GatheringListFontSizeTitle.SettingChanged += delegate
		{
			UIMgr?.DestroyUI();
		};
		GatheringListFontSizeMaterials = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Appearance", "GatheringListFontSizeMaterials", 15, new ConfigDescription("Gathering list material font size.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 94
			}
		}));
		GatheringListFontSizeMaterials.SettingChanged += delegate
		{
			UIMgr?.DestroyUI();
		};
		EnableCraftReadiness = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Appearance", "EnableCraftReadiness", true, new ConfigDescription("Show a colored accent bar indicating craft readiness.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 93
			}
		}));
		EnableCraftReadiness.SettingChanged += delegate
		{
			RecipeMgr?.RefreshRecipeCache();
		};
		ColorHeader = ((BaseUnityPlugin)this).Config.Bind<Color>("5 - Colors", "ColorHeader", new Color(1f, 0.717f, 0.368f, 1f), new ConfigDescription("Recipe title color.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 99
			}
		}));
		ColorHeader.SettingChanged += delegate
		{
			RecipeMgr?.RefreshRecipeCache();
		};
		ColorEnoughInInventory = ((BaseUnityPlugin)this).Config.Bind<Color>("5 - Colors", "ColorEnoughInInventory", new Color(0f, 1f, 0f, 1f), new ConfigDescription("Color: Enough in inventory.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 98
			}
		}));
		ColorEnoughInInventory.SettingChanged += delegate
		{
			RecipeMgr?.RefreshRecipeCache();
		};
		ColorEnoughWithChests = ((BaseUnityPlugin)this).Config.Bind<Color>("5 - Colors", "ColorEnoughWithChests", new Color(1f, 1f, 0f, 1f), new ConfigDescription("Color: Enough with chests.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 97
			}
		}));
		ColorEnoughWithChests.SettingChanged += delegate
		{
			RecipeMgr?.RefreshRecipeCache();
		};
		ColorMissing = ((BaseUnityPlugin)this).Config.Bind<Color>("5 - Colors", "ColorMissing", new Color(1f, 0.33f, 0.33f, 1f), new ConfigDescription("Color: Missing materials.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 96
			}
		}));
		ColorMissing.SettingChanged += delegate
		{
			RecipeMgr?.RefreshRecipeCache();
		};
		ColorCraftReady = ((BaseUnityPlugin)this).Config.Bind<Color>("5 - Colors", "ColorCraftReady", new Color(0.2f, 0.9f, 0.3f, 0.85f), new ConfigDescription("Accent bar color when all materials are available.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 95
			}
		}));
		ColorCraftReady.SettingChanged += delegate
		{
			RecipeMgr?.RefreshRecipeCache();
		};
		ColorCraftNotReady = ((BaseUnityPlugin)this).Config.Bind<Color>("5 - Colors", "ColorCraftNotReady", new Color(0.9f, 0.25f, 0.25f, 0.5f), new ConfigDescription("Accent bar color when materials are missing.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 94
			}
		}));
		ColorCraftNotReady.SettingChanged += delegate
		{
			RecipeMgr?.RefreshRecipeCache();
		};
		ColorPaginationActive = ((BaseUnityPlugin)this).Config.Bind<Color>("6 - Pagination", "ColorPaginationActive", new Color(1f, 0.717f, 0.368f, 1f), new ConfigDescription("Active page dot color.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 99
			}
		}));
		ColorPaginationActive.SettingChanged += delegate
		{
			UIMgr?.UpdateUI(isVisible: true);
		};
		PaginationInactiveOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Pagination", "PaginationInactiveOpacity", 0.3f, new ConfigDescription("Opacity of inactive page dots.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 98
			}
		}));
		PaginationInactiveOpacity.SettingChanged += delegate
		{
			UIMgr?.UpdateUI(isVisible: true);
		};
		PaginationDotSize = ((BaseUnityPlugin)this).Config.Bind<int>("6 - Pagination", "PaginationDotSize", 10, new ConfigDescription("Size of the pagination squares.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 20), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 97
			}
		}));
		PaginationDotSize.SettingChanged += delegate
		{
			UIMgr?.UpdateUI(isVisible: true);
		};
		PaginationDotSpacing = ((BaseUnityPlugin)this).Config.Bind<int>("6 - Pagination", "PaginationDotSpacing", 8, new ConfigDescription("Space between pagination squares.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 96
			}
		}));
		PaginationDotSpacing.SettingChanged += delegate
		{
			UIMgr?.UpdateUI(isVisible: true);
		};
		VerticalListWidth = ((BaseUnityPlugin)this).Config.Bind<float>("7 - Layout (Vertical Mode)", "ListWidth", 265f, new ConfigDescription("List width.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 99
			}
		}));
		VerticalPinSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("7 - Layout (Vertical Mode)", "PinSpacing", 10f, new ConfigDescription("Spacing between pins.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 98
			}
		}));
		VerticalPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("7 - Layout (Vertical Mode)", "Position", new Vector2(-40f, -250f), new ConfigDescription("Position (X, Y).", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 97
			}
		}));
		HorizontalColumnWidth = ((BaseUnityPlugin)this).Config.Bind<float>("8 - Layout (Horizontal - Map Side)", "ColumnWidth", 265f, new ConfigDescription("Column width.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 99
			}
		}));
		HorizontalPinSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("8 - Layout (Horizontal - Map Side)", "PinSpacing", 10f, new ConfigDescription("Spacing between pins.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 98
			}
		}));
		HorizontalPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("8 - Layout (Horizontal - Map Side)", "Position", new Vector2(-250f, -40f), new ConfigDescription("Position (X, Y).", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 97
			}
		}));
		BottomRightColumnWidth = ((BaseUnityPlugin)this).Config.Bind<float>("9 - Layout (Horizontal - Bottom Right)", "ColumnWidth", 265f, new ConfigDescription("Column width.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 99
			}
		}));
		BottomRightPinSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("9 - Layout (Horizontal - Bottom Right)", "PinSpacing", 10f, new ConfigDescription("Spacing between pins.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 98
			}
		}));
		BottomRightPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("9 - Layout (Horizontal - Bottom Right)", "Position", new Vector2(-40f, 40f), new ConfigDescription("Position (X, Y).", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 97
			}
		}));
		InventoryGatheringListPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("1 - General", "InventoryGatheringListPosition", new Vector2(-1680f, 1150f), new ConfigDescription("Gathering list position offset (X, Y) when inventory/chest is open.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 91
			}
		}));
		EnableDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("10 - Debug", "EnableDebugLogging", false, new ConfigDescription("Enable debug logs.", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 99
			}
		}));
		DebugLogger.Log("Config loaded");
	}

	private void Start()
	{
		DebugLogger.Log("Start()");
		LocalizationMgr.LoadTranslations();
		ReadMyLittleUIConfig();
		ContainerMgr.InitializeContainers();
		DebugLogger.Log("Start done");
	}

	private void OnDestroy()
	{
		DebugLogger.Log("OnDestroy");
		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_0339: Unknown result type (might be due to invalid IL or missing references)
		//IL_037c: 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 init");
			_lastLanguage = Localization.instance.GetSelectedLanguage();
			DataMgr.LoadPins();
			RecipeMgr.ValidateAndCleanPins();
			RecipeMgr.RefreshRecipeCache();
			_startupInitialized = true;
			DebugLogger.Log($"Init done - {RecipeMgr.PinnedRecipes.Count} pins 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) && !InputHelper.IsInputBlocked() && RecipeMgr.PinnedRecipes.Count > 0)
		{
			int count = RecipeMgr.PinnedRecipes.Count;
			RecipeMgr.PinnedRecipes.Clear();
			RecipeMgr.RefreshRecipeCache();
			UIMgr.CloseGatheringList();
			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();
				}
				UIMgr?.DestroyUI();
			}
		}
		if (Input.GetKeyDown(HotkeyPageSwitch.Value) && _isUiVisible && !InputHelper.IsInputBlocked())
		{
			UIMgr?.CyclePage();
		}
		if (Input.GetKeyDown(HotkeyGatheringList.Value) && !InputHelper.IsInputBlocked() && EnableGatheringList.Value)
		{
			UIMgr?.ToggleGatheringList();
		}
	}

	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");
			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.TryGetValue(text, out var value))
		{
			value--;
			DebugLogger.Log($"Auto-unpin: {text}, remaining count: {value}");
			if (value <= 0)
			{
				Instance.RecipeMgr.PinnedRecipes.Remove(text);
				DebugLogger.Log("Recipe " + text + " fully unpinned");
			}
			else
			{
				Instance.RecipeMgr.PinnedRecipes[text] = value;
			}
			Instance.RecipeMgr.RefreshRecipeCache();
			if (Instance.RecipeMgr.PinnedRecipes.Count < 2)
			{
				Instance.UIMgr.CloseGatheringList();
			}
		}
	}

	[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.TryGetValue(text, out var value))
		{
			value--;
			DebugLogger.Log($"Auto-unpin (Build): {text}, remaining count: {value}");
			if (value <= 0)
			{
				Instance.RecipeMgr.PinnedRecipes.Remove(text);
				DebugLogger.Log("Build recipe " + text + " fully unpinned");
			}
			else
			{
				Instance.RecipeMgr.PinnedRecipes[text] = value;
			}
			Instance.RecipeMgr.RefreshRecipeCache();
			if (Instance.RecipeMgr.PinnedRecipes.Count < 2)
			{
				Instance.UIMgr.CloseGatheringList();
			}
		}
	}
}
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("Reflection init");
		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 done: {num} ok, {num2} failed");
			if (num2 > 0)
			{
				DebugLogger.Warning("Some reflection targets failed");
			}
			_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("Reflection init failed", 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 class GatheringItemUI : MonoBehaviour
{
	public Image Icon;

	public Text AmountText;

	public void SetActive(bool active)
	{
		((Component)this).gameObject.SetActive(active);
	}
}
public class GatheringListUI : MonoBehaviour
{
	[CompilerGenerated]
	private sealed class <FixLayout>d__10 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public GatheringListUI <>4__this;

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

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

		[DebuggerHidden]
		public <FixLayout>d__10(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.ItemListRoot != (Object)null)
				{
					Transform itemListRoot = <>4__this.ItemListRoot;
					LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)(object)((itemListRoot is RectTransform) ? itemListRoot : null));
				}
				if ((Object)(object)<>4__this.PanelRect != (Object)null)
				{
					LayoutRebuilder.ForceRebuildLayoutImmediate(<>4__this.PanelRect);
				}
				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 PanelRect;

	public Image BgImage;

	public Text TitleText;

	public Transform ItemListRoot;

	public Text HintText;

	public List<GatheringItemUI> ItemSlots = new List<GatheringItemUI>();

	private Coroutine _layoutCoroutine;

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

	public void RefreshLayout()
	{
		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__10))]
	private IEnumerator FixLayout()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <FixLayout>d__10(0)
		{
			<>4__this = this
		};
	}
}
public class GatheringItemData
{
	public string ItemName;

	public string DisplayName;

	public Sprite Icon;

	public int TotalRequired;

	public int TotalHave;

	public bool IsComplete;
}
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[] array = Resources.FindObjectsOfTypeAll<Sprite>();
		Sprite val = null;
		Sprite[] array2 = array;
		foreach (Sprite val2 in array2)
		{
			if (!((Object)(object)val2 == (Object)null))
			{
				if (((Object)val2).name == "UISprite")
				{
					_cachedUiSprite = val2;
					break;
				}
				if ((Object)(object)val == (Object)null && ((Object)val2).name == "Knob")
				{
					val = val2;
				}
			}
		}
		if ((Object)(object)_cachedUiSprite == (Object)null)
		{
			_cachedUiSprite = val;
		}
		_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_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Expected O, but got Unknown
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0181: Unknown result type (might be due to invalid IL or missing references)
		//IL_0198: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0201: Unknown result type (might be due to invalid IL or missing references)
		//IL_020b: Expected O, but got Unknown
		//IL_023e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0243: Unknown result type (might be due to invalid IL or missing references)
		//IL_024d: Expected O, but got Unknown
		//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f2: 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_039b: Expected O, but got Unknown
		//IL_03f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0440: Unknown result type (might be due to invalid IL or missing references)
		//IL_0445: Unknown result type (might be due to invalid IL or missing references)
		//IL_044f: Expected O, but got Unknown
		//IL_0483: Unknown result type (might be due to invalid IL or missing references)
		//IL_0488: Unknown result type (might be due to invalid IL or missing references)
		//IL_04b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_050d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0512: Unknown result type (might be due to invalid IL or missing references)
		//IL_051c: Expected O, but got Unknown
		GameObject val = new GameObject("PinSlot", new Type[1] { typeof(RectTransform) })
		{
			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;
		GameObject val4 = new GameObject("AccentBar", new Type[1] { typeof(RectTransform) })
		{
			layer = 5
		};
		val4.transform.SetParent(val.transform, false);
		Image val5 = val4.AddComponent<Image>();
		((Graphic)val5).raycastTarget = false;
		((Graphic)val5).color = new Color(0.9f, 0.25f, 0.25f, 0.5f);
		RectTransform component = val4.GetComponent<RectTransform>();
		component.anchorMin = new Vector2(0f, 0f);
		component.anchorMax = new Vector2(0f, 1f);
		component.pivot = new Vector2(0f, 0.5f);
		component.sizeDelta = new Vector2(4f, 0f);
		component.anchoredPosition = Vector2.zero;
		LayoutElement val6 = val4.AddComponent<LayoutElement>();
		val6.ignoreLayout = true;
		pinSlotUI.AccentBar = val5;
		VerticalLayoutGroup val7 = val.AddComponent<VerticalLayoutGroup>();
		((HorizontalOrVerticalLayoutGroup)val7).childControlHeight = true;
		((HorizontalOrVerticalLayoutGroup)val7).childControlWidth = true;
		((HorizontalOrVerticalLayoutGroup)val7).childForceExpandHeight = false;
		((HorizontalOrVerticalLayoutGroup)val7).spacing = 5f;
		((LayoutGroup)val7).padding = new RectOffset(14, 8, 8, 8);
		ContentSizeFitter val8 = val.AddComponent<ContentSizeFitter>();
		val8.horizontalFit = (FitMode)0;
		val8.verticalFit = (FitMode)2;
		GameObject val9 = new GameObject("HeaderRow", new Type[1] { typeof(RectTransform) })
		{
			layer = 5
		};
		val9.transform.SetParent(val.transform, false);
		HorizontalLayoutGroup val10 = val9.AddComponent<HorizontalLayoutGroup>();
		((HorizontalOrVerticalLayoutGroup)val10).childControlHeight = true;
		((HorizontalOrVerticalLayoutGroup)val10).childControlWidth = true;
		((HorizontalOrVerticalLayoutGroup)val10).childForceExpandHeight = false;
		((HorizontalOrVerticalLayoutGroup)val10).childForceExpandWidth = false;
		((HorizontalOrVerticalLayoutGroup)val10).spacing = 8f;
		LayoutElement val11 = val9.AddComponent<LayoutElement>();
		val11.minHeight = 30f;
		val11.flexibleHeight = 0f;
		val11.flexibleWidth = 1f;
		GameObject val12 = new GameObject("Icon", new Type[1] { typeof(RectTransform) })
		{
			layer = 5
		};
		val12.transform.SetParent(val9.transform, false);
		Image val13 = val12.AddComponent<Image>();
		((Graphic)val13).raycastTarget = false;
		val13.preserveAspect = true;
		pinSlotUI.IconImage = val13;
		LayoutElement val14 = val12.AddComponent<LayoutElement>();
		val14.minWidth = 28f;
		val14.minHeight = 28f;
		val14.preferredWidth = 28f;
		val14.preferredHeight = 28f;
		val14.flexibleWidth = 0f;
		GameObject val15 = new GameObject("Title", new Type[1] { typeof(RectTransform) })
		{
			layer = 5
		};
		val15.transform.SetParent(val9.transform, false);
		Text val16 = val15.AddComponent<Text>();
		((Graphic)val16).raycastTarget = false;
		val16.font = font;
		val16.fontSize = 18;
		val16.alignment = (TextAnchor)3;
		val16.horizontalOverflow = (HorizontalWrapMode)0;
		val16.verticalOverflow = (VerticalWrapMode)1;
		((Graphic)val16).color = ValheimOrange;
		pinSlotUI.HeaderText = val16;
		LayoutElement val17 = val15.AddComponent<LayoutElement>();
		val17.minHeight = 24f;
		val17.flexibleWidth = 1f;
		GameObject val18 = new GameObject("Divider", new Type[1] { typeof(RectTransform) })
		{
			layer = 5
		};
		val18.transform.SetParent(val.transform, false);
		Image val19 = val18.AddComponent<Image>();
		val19.sprite = GetBackgroundSprite();
		if ((Object)(object)val3 != (Object)null && val3.border != Vector4.zero)
		{
			val19.type = (Type)1;
		}
		else
		{
			val19.type = (Type)0;
		}
		((Graphic)val19).color = DividerColor;
		((Graphic)val19).raycastTarget = false;
		LayoutElement val20 = val18.AddComponent<LayoutElement>();
		val20.minHeight = 2f;
		val20.preferredHeight = 2f;
		val20.flexibleWidth = 1f;
		GameObject val21 = new GameObject("ResourceList", new Type[1] { typeof(RectTransform) })
		{
			layer = 5
		};
		val21.transform.SetParent(val.transform, false);
		VerticalLayoutGroup val22 = val21.AddComponent<VerticalLayoutGroup>();
		((HorizontalOrVerticalLayoutGroup)val22).childControlHeight = true;
		((HorizontalOrVerticalLayoutGroup)val22).childControlWidth = true;
		((HorizontalOrVerticalLayoutGroup)val22).childForceExpandHeight = false;
		((HorizontalOrVerticalLayoutGroup)val22).spacing = 3f;
		ContentSizeFitter val23 = val21.AddComponent<ContentSizeFitter>();
		val23.verticalFit = (FitMode)2;
		pinSlotUI.ResourceListRoot = val21.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_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Expected O, but got Unknown
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Expected O, but got Unknown
		//IL_015a: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Expected O, but got Unknown
		//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0224: Unknown result type (might be due to invalid IL or missing references)
		//IL_022e: Expected O, but got Unknown
		GameObject val = new GameObject("ResSlot", new Type[1] { typeof(RectTransform) })
		{
			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;
		((LayoutGroup)val2).childAlignment = (TextAnchor)3;
		((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) })
		{
			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) })
		{
			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) })
		{
			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_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Expected O, but got Unknown
		GameObject val = new GameObject("PaginationDots", new Type[1] { typeof(RectTransform) })
		{
			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_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: 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) })
		{
			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 static GatheringListUI CreateGatheringListPanel(Transform parent, Font font, string title)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: 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_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Expected O, but got Unknown
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: Expected O, but got Unknown
		//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_020c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0211: Unknown result type (might be due to invalid IL or missing references)
		//IL_021b: Expected O, but got Unknown
		//IL_024f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0254: Unknown result type (might be due to invalid IL or missing references)
		//IL_027d: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02de: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e8: Expected O, but got Unknown
		//IL_0311: Unknown result type (might be due to invalid IL or missing references)
		//IL_0328: Unknown result type (might be due to invalid IL or missing references)
		//IL_0385: Unknown result type (might be due to invalid IL or missing references)
		//IL_038a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0394: Expected O, but got Unknown
		//IL_03ec: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("GatheringListPanel", new Type[1] { typeof(RectTransform) })
		{
			layer = 5
		};
		val.transform.SetParent(parent, false);
		GatheringListUI gatheringListUI = val.AddComponent<GatheringListUI>();
		gatheringListUI.PanelRect = 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,