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; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("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.3.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.3.0.0")] namespace ValheimRecipePinner; public class ContainerScanner { public static List AllContainers = new List(); private static readonly HashSet _containerSet = new HashSet(); internal static readonly object ContainerLock = new object(); public Dictionary ContainerCache = new Dictionary(); private static readonly HashSet _processedIDs = new HashSet(); private readonly List _snapshotBuffer = new List(); private Vector3 _lastScanPos; private int _lastItemCount; private float _scanTimer; private float _moveScanCooldown; private const float MovementThresholdSqr = 4f; private const float MinMoveScanCooldown = 1f; private static volatile bool _isInitializing = false; public void InitializeContainers() { if (!RecipePinnerPlugin.EnableChestScanning.Value) { DebugLogger.Verbose("InitializeContainers skipped — chest scanning disabled"); return; } if (_isInitializing) { DebugLogger.Verbose("InitializeContainers skipped — already initializing"); return; } _isInitializing = true; try { DebugLogger.Verbose("Init containers"); lock (ContainerLock) { if (AllContainers.Count > 0) { DebugLogger.Verbose($"InitializeContainers: list already populated ({AllContainers.Count}), skipping scan"); return; } Container[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Container val in array) { if ((Object)(object)val != (Object)null && _containerSet.Add(val)) { AllContainers.Add(val); if ((Object)(object)((Component)val).GetComponent() == (Object)null) { ((Component)val).gameObject.AddComponent().MyContainer = val; } } } DebugLogger.Verbose($"Tracking {AllContainers.Count} containers"); } } finally { _isInitializing = false; } } public static void ClearAll() { lock (ContainerLock) { AllContainers.Clear(); _containerSet.Clear(); DebugLogger.Verbose("ContainerScanner: all container references cleared"); } } public void UpdateScanning() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } _scanTimer += Time.deltaTime; _moveScanCooldown += Time.deltaTime; bool flag = Vector3.SqrMagnitude(((Component)Player.m_localPlayer).transform.position - _lastScanPos) > 4f && _moveScanCooldown >= 1f; float num = (((Object)(object)InventoryGui.instance != (Object)null && (Object)(object)ReflectionHelper.GetCurrentContainer(InventoryGui.instance) != (Object)null) ? 0.5f : RecipePinnerPlugin.ChestScanInterval.Value); bool flag2 = _scanTimer >= num; if (!flag && !flag2) { return; } int num2 = 0; foreach (ItemData allItem in ((Humanoid)Player.m_localPlayer).GetInventory().GetAllItems()) { num2 += allItem.m_stack; } bool flag3 = num2 != _lastItemCount; _lastItemCount = num2; DebugLogger.Verbose($"Scanning containers - Moved: {flag}, InvChanged: {flag3}, Interval: {flag2}"); _scanTimer = 0f; if (flag) { _moveScanCooldown = 0f; } UpdateContainerCache(); } private void UpdateContainerCache() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: 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; } if (Vector3.SqrMagnitude(((Component)item).transform.position - position) > 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 || !RecipePinnerPlugin.EnableChestScanning.Value) { return; } lock (ContainerLock) { if (_containerSet.Add(__instance)) { AllContainers.Add(__instance); (((Component)__instance).gameObject.GetComponent() ?? ((Component)__instance).gameObject.AddComponent()).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 recipeManager = RecipePinnerPlugin.Instance?.RecipeMgr; if (recipeManager == null) { DebugLogger.Warning("Cannot save - RecipeMgr is null"); return; } List list = new List(); HashSet hashSet = new HashSet(); foreach (string item in recipeManager.PinnedRecipeOrder) { int value3; if (!hashSet.Add(item)) { DebugLogger.Warning("Skipping duplicate pin order entry while saving: " + item); } else if (item.StartsWith("GROUP:")) { string text = item.Substring(6); if (!recipeManager.PinGroups.TryGetValue(text, out var value)) { continue; } List list2 = new List(); foreach (string memberRecipeKey in value.MemberRecipeKeys) { int value2; int num = ((!value.MemberCounts.TryGetValue(memberRecipeKey, out value2)) ? 1 : value2); list2.Add($"{EscapeSaveValue(memberRecipeKey)}:{num}"); } string text2 = string.Join(",", list2); list.Add("GROUP:" + EscapeSaveValue(text) + "|" + text2); DebugLogger.Verbose($"Saved group: {text} with {value.MemberRecipeKeys.Count} members"); } else if (recipeManager.PinnedRecipes.TryGetValue(item, out value3)) { list.Add($"{EscapeSaveValue(item)}:{value3}"); } } WriteAllLinesAtomically(savePath, list); int count = recipeManager.PinGroups.Count; DebugLogger.Log($"Saved {list.Count} entries ({list.Count - count} pins, {count} groups) 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 recipeManager = RecipePinnerPlugin.Instance?.RecipeMgr; if (recipeManager == null) { DebugLogger.Warning("Cannot load - RecipeMgr is null"); return; } if (!File.Exists(savePath)) { DebugLogger.Log("No save file found at: " + savePath); return; } try { string[] array = File.ReadAllLines(savePath); Dictionary dictionary = new Dictionary(); Dictionary dictionary2 = new Dictionary(); List list = new List(); HashSet hashSet = new HashSet(); int num = 0; int num2 = 0; int num3 = 0; string[] array2 = array; foreach (string text in array2) { if (string.IsNullOrWhiteSpace(text)) { continue; } if (text.StartsWith("GROUP:")) { string text2 = text.Substring(6); int num4 = FindGroupSeparator(text2); if (num4 > 0 && num4 < text2.Length - 1) { string text3 = UnescapeSaveValue(text2.Substring(0, num4).Trim()); string[] array3 = text2.Substring(num4 + 1).Trim().Split(new char[1] { ',' }); if (!string.IsNullOrEmpty(text3) && array3.Length >= 2) { PinGroupData pinGroupData = new PinGroupData { GroupName = text3 }; string[] array4 = array3; for (int j = 0; j < array4.Length; j++) { string text4 = array4[j].Trim(); if (string.IsNullOrEmpty(text4)) { continue; } int num5 = text4.LastIndexOf(':'); if (num5 > 0 && num5 < text4.Length - 1) { string text5 = UnescapeSaveValue(text4.Substring(0, num5)); int result = 1; int.TryParse(text4.Substring(num5 + 1), out result); if (result < 1) { result = 1; } pinGroupData.MemberRecipeKeys.Add(text5); pinGroupData.MemberCounts[text5] = result; } else { string text6 = UnescapeSaveValue(text4); pinGroupData.MemberRecipeKeys.Add(text6); pinGroupData.MemberCounts[text6] = 1; } } if (pinGroupData.MemberRecipeKeys.Count >= 2) { string item = "GROUP:" + text3; if (!hashSet.Add(item)) { DebugLogger.Warning("Duplicate group entry in save file, keeping first order position and latest data: " + text3); num3++; } else { list.Add(item); } dictionary2[text3] = pinGroupData; num2++; DebugLogger.Verbose($"Loaded group: {text3} with {pinGroupData.MemberRecipeKeys.Count} members"); } else { DebugLogger.Warning("Group '" + text3 + "' has less than 2 members, skipping"); num3++; } } else { DebugLogger.Warning("Invalid group format: " + text); num3++; } } else { DebugLogger.Warning("Invalid group line (missing pipe): " + text); num3++; } continue; } int num6 = text.LastIndexOf(':'); if (num6 > 0 && num6 < text.Length - 1) { string text7 = UnescapeSaveValue(text.Substring(0, num6).Trim()); if (int.TryParse(text.Substring(num6 + 1).Trim(), out var result2)) { if (!hashSet.Add(text7)) { DebugLogger.Warning("Duplicate pin entry in save file, keeping first order position and latest count: " + text7); num3++; } else { list.Add(text7); } dictionary[text7] = result2; num++; } else { DebugLogger.Warning("Invalid count value in save file: " + text); num3++; } } else { string text8 = UnescapeSaveValue(text.Trim()); if (!hashSet.Add(text8)) { DebugLogger.Warning("Duplicate legacy pin entry in save file, keeping first order position and latest count: " + text8); num3++; } else { list.Add(text8); } dictionary[text8] = 1; num++; } } recipeManager.PinnedRecipes.Clear(); recipeManager.PinGroups.Clear(); recipeManager.PinnedRecipeOrder.Clear(); foreach (KeyValuePair item2 in dictionary) { recipeManager.PinnedRecipes[item2.Key] = item2.Value; } foreach (KeyValuePair item3 in dictionary2) { recipeManager.PinGroups[item3.Key] = item3.Value; } recipeManager.PinnedRecipeOrder.AddRange(list); int effectivePinCount = recipeManager.GetEffectivePinCount(); if (effectivePinCount > RecipePinnerPlugin.MaximumPins.Value) { int num7 = recipeManager.TrimToMaximumPins(RecipePinnerPlugin.MaximumPins.Value); DebugLogger.Warning($"Loaded save exceeded max effective pins ({effectivePinCount} > {RecipePinnerPlugin.MaximumPins.Value}) - trimmed {num7} effective pin(s)"); } DebugLogger.Log($"Loaded {num} pins and {num2} groups from: {savePath} (Errors: {num3})"); } catch (Exception ex) { DebugLogger.Error("Failed to load pins", ex); } } private void WriteAllLinesAtomically(string savePath, List lines) { string? directoryName = Path.GetDirectoryName(savePath); if (string.IsNullOrEmpty(directoryName)) { throw new IOException("Invalid save directory for path: " + savePath); } string fileName = Path.GetFileName(savePath); string text = Path.Combine(directoryName, $"{fileName}.{Guid.NewGuid():N}.tmp"); string text2 = savePath + ".bak"; try { File.WriteAllLines(text, lines); if (File.Exists(savePath)) { if (File.Exists(text2)) { File.Delete(text2); } File.Replace(text, savePath, text2, ignoreMetadataErrors: true); } else { File.Move(text, savePath); } } catch { try { if (File.Exists(text)) { File.Delete(text); } } catch (Exception ex) { DebugLogger.Warning("Failed to delete temp save file '" + text + "': " + ex.Message); } throw; } } private static int FindGroupSeparator(string groupContent) { int num = groupContent.LastIndexOf('|'); if (num >= 0) { return num; } return -1; } private static string EscapeSaveValue(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } return value.Replace("%", "%25").Replace("|", "%7C").Replace(",", "%2C") .Replace("\r", "%0D") .Replace("\n", "%0A"); } private static string UnescapeSaveValue(string value) { if (string.IsNullOrEmpty(value) || value.IndexOf('%') < 0) { return value; } StringBuilder stringBuilder = new StringBuilder(value.Length); for (int i = 0; i < value.Length; i++) { if (value[i] == '%' && i + 2 < value.Length && IsHexDigit(value[i + 1]) && IsHexDigit(value[i + 2])) { string value2 = value.Substring(i + 1, 2); stringBuilder.Append((char)Convert.ToInt32(value2, 16)); i += 2; } else { stringBuilder.Append(value[i]); } } return stringBuilder.ToString(); } private static bool IsHexDigit(char c) { if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) { if (c >= 'A') { return c <= 'F'; } return false; } return true; } 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() { if ((Object)(object)RecipePinnerPlugin.Instance != (Object)null && RecipePinnerPlugin.EnableDebugLogging != null) { return RecipePinnerPlugin.EnableDebugLogging.Value; } return false; } } public class LocalizationManager { private readonly RecipePinnerPlugin _plugin; private readonly Dictionary _localizedText = new Dictionary(); private static readonly Dictionary _defaultEnglish = new Dictionary { { "pinned", "Recipe Pinned!" }, { "unpinned", "Pin Removed" }, { "list_full", "List Full!" }, { "added_more", "Added More: {0}x" }, { "decreased", "Decreased: {0}x" }, { "cleared", "Pinned Recipes Cleared" }, { "clear_confirm_hotkey", "Press again to clear all pins" }, { "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}" }, { "mypins_title", "MY PINS" }, { "mypins_button", "Pins" }, { "mypins_empty", "No Recipes Pinned" }, { "group_button", "Group" }, { "group_confirm", "Confirm" }, { "group_cancel", "Cancel" }, { "group_name_prompt", "Enter group name:" }, { "group_created", "Group Created: {0}" }, { "group_disbanded", "Group Disbanded: {0}" }, { "group_select_hint", "Select pins to group" }, { "group_min_select", "Select at least 2 pins" }, { "group_need_more", "At least 2 pins needed to create a group" }, { "group_create_failed", "Group could not be created" }, { "group_name_exists", "Group '{0}' already exists" }, { "disband_button", "Disband" }, { "confirm_delete_group", "Delete group \"{0}\" and all member pins?" }, { "confirm_delete_pin", "Delete \"{0}\"?" }, { "confirm_remove_member", "Remove \"{0}\" from group \"{1}\"?" }, { "confirm_button", "Confirm" }, { "cancel_button", "Cancel" }, { "confirm_disband_group", "Disband group \"{0}\"? Members will become individual pins." }, { "clear_button", "Clear" }, { "clear_confirm_msg", "Remove all pins?" }, { "clear_no_pins", "No pins to clear" }, { "group_no_pins", "Not enough pins to group" }, { "close_button", "Close" }, { "controls_title", "CONTROLS" }, { "controls_config_note", "Controls can be changed in\nthe config file." }, { "controls_config_note_single", "Controls can be changed in the config file." }, { "howto_header", "HOW TO USE" }, { "howto_pin", "Hover over a recipe in the crafting menu and press [{0}] to pin it." }, { "howto_unpin", "Hold [{0}] and press [{1}] to unpin a recipe." }, { "howto_toggle_hud", "Press [{0}] to show or hide the pinned recipe overlay." }, { "howto_gathering", "Press [{0}] to open or close the gathering list." }, { "howto_next_page", "Press [{0}] to cycle through HUD pages." }, { "howto_clear_all", "Press [{0}] to remove all pinned recipes." }, { "keybindings_header", "KEY BINDINGS" }, { "ctrl_pin", "Pin Recipe" }, { "ctrl_unpin", "Unpin (hold + Pin Recipe key)" }, { "ctrl_toggle_hud", "Toggle HUD Visibility" }, { "ctrl_gathering", "Toggle Gathering List" }, { "ctrl_next_page", "Next HUD Page" }, { "ctrl_clear_all", "Clear All Pins" } }; 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 text2 = text; char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { text2 = text2.Replace(oldChar, '_'); } string text3 = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)_plugin).Info.Location), "RecipePinner_languages", text2 + ".json"); if (!File.Exists(text3)) { DebugLogger.Log("Language file not found: " + text3 + " - Using default English"); return; } try { string text4 = File.ReadAllText(text3); int num = 0; string[] array = text4.Split(new string[3] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text5 = array[i].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 text7 = array2[1].Trim(',', '"', ' ', '\t', '\r'); text7 = text7.Replace("\\\"", "\"").Replace("\\n", "\n").Replace("\\t", "\t") .Replace("\\\\", "\\"); if (!string.IsNullOrEmpty(text6) && !string.IsNullOrEmpty(text7)) { _localizedText[text6] = text7; num++; } } } DebugLogger.Log($"Loaded {num} translations from: {text}.json"); } catch (Exception ex) { DebugLogger.Error("Failed to load language file: " + text3, 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 Sprite Icon; public int StackCount; public List Resources = new List(); public bool IsDirty = true; public bool IsGroup; public PinGroupData GroupRef; } public class PinnedResData { public string ItemName; public string CachedName; public Sprite Icon; public int RequiredAmount; public int LastKnownAmount; public int LastKnownInvAmount; public string CachedAmountString; } public class PinGroupData { public string GroupName; public List MemberRecipeKeys = new List(); public Dictionary MemberCounts = new Dictionary(); public List MemberPins = new List(); public List MergedResources = new List(); public List MemberIcons = new List(); public bool IsDirty = true; } public class RecipeManager { public Dictionary PinnedRecipes = new Dictionary(); public List PinnedRecipeOrder = new List(); public List CachedPins = new List(); public Dictionary PinGroups = new Dictionary(); private readonly Dictionary _fakeRecipeCache = new Dictionary(); private static readonly Regex CleanNameRegex = new Regex("<.*?>", 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 _cachedRecipeFields = new Dictionary(); private static readonly Dictionary _cachedRecipeProps = new Dictionary(); private static readonly Dictionary _cachedItemFields = new Dictionary(); private static readonly Dictionary _cachedItemProps = new Dictionary(); private static readonly Dictionary _cachedElementProps = new Dictionary(); private static readonly Dictionary _cachedElementFields = new Dictionary(); private static readonly HashSet _elementLookupFailed = new HashSet(); 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"); PinGroups.Clear(); _cachedRecipeFields.Clear(); _cachedRecipeProps.Clear(); _cachedItemFields.Clear(); _cachedItemProps.Clear(); _cachedElementProps.Clear(); _cachedElementFields.Clear(); _elementLookupFailed.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; Dictionary dictionary = new Dictionary(); foreach (PinGroupData value9 in PinGroups.Values) { foreach (string memberRecipeKey in value9.MemberRecipeKeys) { int value; int num3 = ((!value9.MemberCounts.TryGetValue(memberRecipeKey, out value)) ? 1 : value); if (dictionary.ContainsKey(memberRecipeKey)) { dictionary[memberRecipeKey] += num3; } else { dictionary[memberRecipeKey] = num3; } } } Dictionary dictionary2 = new Dictionary(); int num4 = 0; foreach (KeyValuePair pinGroup in PinGroups) { PinGroupData value2 = pinGroup.Value; value2.MemberPins.Clear(); value2.MergedResources.Clear(); value2.MemberIcons.Clear(); value2.IsDirty = true; Dictionary dictionary3 = new Dictionary(); foreach (string memberRecipeKey2 in value2.MemberRecipeKeys) { int value3; int count = ((!value2.MemberCounts.TryGetValue(memberRecipeKey2, out value3)) ? 1 : value3); Recipe recipeByName = GetRecipeByName(memberRecipeKey2); if ((Object)(object)recipeByName == (Object)null) { DebugLogger.Warning("Group '" + value2.GroupName + "' member not found: " + memberRecipeKey2); continue; } PinnedRecipeData pinnedRecipeData = BuildPinnedRecipeData(recipeByName, memberRecipeKey2, count); if (pinnedRecipeData == null) { continue; } value2.MemberPins.Add(pinnedRecipeData); if ((Object)(object)pinnedRecipeData.Icon != (Object)null && value2.MemberIcons.Count < 4) { value2.MemberIcons.Add(pinnedRecipeData.Icon); } foreach (PinnedResData resource in pinnedRecipeData.Resources) { if (dictionary3.TryGetValue(resource.ItemName, out var value4)) { value4.RequiredAmount += resource.RequiredAmount; continue; } dictionary3[resource.ItemName] = new PinnedResData { ItemName = resource.ItemName, CachedName = resource.CachedName, Icon = resource.Icon, RequiredAmount = resource.RequiredAmount, LastKnownAmount = -1, LastKnownInvAmount = -1 }; } } foreach (PinnedResData value10 in dictionary3.Values) { value2.MergedResources.Add(value10); } PinnedRecipeData value5 = new PinnedRecipeData { IsDirty = true, RecipeRef = null, RawName = value2.GroupName, CachedHeader = value2.GroupName, Icon = ((value2.MemberIcons.Count > 0) ? value2.MemberIcons[0] : null), StackCount = 1, Resources = value2.MergedResources, IsGroup = true, GroupRef = value2 }; dictionary2[pinGroup.Key] = value5; num4++; DebugLogger.Verbose($"Group pin built: {value2.GroupName} ({value2.MemberPins.Count} members, {value2.MergedResources.Count} resources)"); } foreach (string item in GetDisplayPinOrder()) { if (item.StartsWith("GROUP:")) { string key = item.Substring(6); if (dictionary2.TryGetValue(key, out var value6)) { CachedPins.Add(value6); } } else { if (!PinnedRecipes.TryGetValue(item, out var value7)) { continue; } if (dictionary.TryGetValue(item, out var value8)) { int num5 = value7 - value8; if (num5 <= 0) { DebugLogger.Verbose($"Skipping grouped recipe (no excess): {item} (claims={value8})"); continue; } value7 = num5; DebugLogger.Verbose($"Grouped recipe excess for overlay: {item} x{num5} (claims={value8})"); } Recipe recipeByName2 = GetRecipeByName(item); if ((Object)(object)recipeByName2 != (Object)null) { PinnedRecipeData pinnedRecipeData2 = BuildPinnedRecipeData(recipeByName2, item, value7); if (pinnedRecipeData2 != null) { CachedPins.Add(pinnedRecipeData2); num++; } else { num2++; } } else { DebugLogger.Warning("Recipe not found: " + item); num2++; } } } DebugLogger.Log($"Cache refreshed: {num} pins, {num4} groups, {num2} failed"); if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)RecipePinnerPlugin.Instance != (Object)null) { RecipePinnerPlugin.Instance.UIMgr.UpdateUI(RecipePinnerPlugin.IsUiVisible); RecipePinnerPlugin.Instance.UIMgr.RefreshMyPinsList(); } } public List GetDisplayPinOrder() { List list = new List(); List list2 = new List(); HashSet hashSet = new HashSet(); Dictionary dictionary = new Dictionary(); foreach (string item in PinnedRecipeOrder) { if (!item.StartsWith("GROUP:")) { continue; } string text = item.Substring(6); if (!PinGroups.TryGetValue(text, out var value)) { continue; } foreach (string memberRecipeKey in value.MemberRecipeKeys) { dictionary[memberRecipeKey] = text; } } foreach (string item2 in PinnedRecipeOrder) { if (item2.StartsWith("GROUP:")) { string text2 = item2.Substring(6); if (PinGroups.ContainsKey(text2)) { list.Add(item2); AppendDeferredExcessForGroup(list, list2, hashSet, dictionary, text2); } } else { if (!PinnedRecipes.TryGetValue(item2, out var value2)) { continue; } int groupClaimCount = GetGroupClaimCount(item2); if (groupClaimCount <= 0) { list.Add(item2); } else { if (value2 <= groupClaimCount) { continue; } if (dictionary.ContainsKey(item2)) { if (hashSet.Add(item2)) { list2.Add(item2); } } else { list.Add(item2); } } } } foreach (string item3 in list2) { list.Add(item3); } return list; } private static void AppendDeferredExcessForGroup(List displayOrder, List deferredExcess, HashSet deferredSet, Dictionary lastClaimingGroup, string groupName) { int num = 0; while (num < deferredExcess.Count) { string text = deferredExcess[num]; if (lastClaimingGroup.TryGetValue(text, out var value) && value == groupName) { displayOrder.Add(text); deferredSet.Remove(text); deferredExcess.RemoveAt(num); } else { num++; } } } 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(); if (!int.TryParse(match.Groups[1].Value, out var result)) { DebugLogger.Warning("Invalid upgrade level in recipe key: " + name); return null; } Recipe recipeByName = GetRecipeByName(name2); if ((Object)(object)recipeByName != (Object)null) { if (!IsValidUpgradeTarget(recipeByName, result, name)) { return null; } Recipe val = CreateFakeUpgradeRecipe(recipeByName, result, name); if ((Object)(object)val != (Object)null) { return val; } } } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(name); ItemDrop val2 = ((itemPrefab != null) ? itemPrefab.GetComponent() : 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(); if ((Object)(object)component != (Object)null && component.m_resources != null && component.m_resources.Length != 0) { Recipe val5 = ScriptableObject.CreateInstance(); ((Object)val5).hideFlags = (HideFlags)61; ((Object)val5).name = name; val5.m_item = val4.GetComponent(); 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown if ((Object)(object)baseRecipe == (Object)null) { return null; } if (!IsValidUpgradeTarget(baseRecipe, targetLevel, customName)) { return null; } Recipe val = ScriptableObject.CreateInstance(); ((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 list = new List(); 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; } private bool IsValidUpgradeTarget(Recipe baseRecipe, int targetLevel, string customName) { if (targetLevel < 2) { DebugLogger.Warning("Invalid upgrade level for '" + customName + "': target level must be at least 2"); return false; } SharedData val = (baseRecipe.m_item?.m_itemData)?.m_shared; if (val == null) { DebugLogger.Warning("Cannot validate upgrade level for '" + customName + "' - item data is missing"); return false; } int maxQuality = val.m_maxQuality; if (maxQuality < 2 || targetLevel > maxQuality) { DebugLogger.Warning($"Invalid upgrade level for '{customName}': target={targetLevel}, max={maxQuality}"); return false; } return true; } 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 list = new List(); 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); PinnedRecipeOrder.Remove(item); DebugLogger.Warning("Removed invalid recipe: " + item); } DebugLogger.Log($"Removed {list.Count} invalid pins"); } else { DebugLogger.Log("All individual pins valid"); } int num = CleanInvalidGroupMembers(); if (list.Count > 0 || num > 0) { RecipePinnerPlugin.Instance?.DataMgr.SavePins(); } } private int CleanInvalidGroupMembers() { int num = 0; List list = new List(); foreach (KeyValuePair pinGroup in PinGroups) { string key = pinGroup.Key; PinGroupData value = pinGroup.Value; List list2 = new List(); foreach (string memberRecipeKey in value.MemberRecipeKeys) { if ((Object)(object)GetRecipeByName(memberRecipeKey) == (Object)null) { value.MemberCounts.Remove(memberRecipeKey); num++; DebugLogger.Warning("Removed invalid group member: " + memberRecipeKey + " from group '" + key + "'"); } else { list2.Add(memberRecipeKey); } } if (list2.Count != value.MemberRecipeKeys.Count) { value.MemberRecipeKeys.Clear(); value.MemberRecipeKeys.AddRange(list2); } List list3 = new List(); foreach (string key2 in value.MemberCounts.Keys) { if (!value.MemberRecipeKeys.Contains(key2)) { list3.Add(key2); } } foreach (string item in list3) { value.MemberCounts.Remove(item); } if (value.MemberRecipeKeys.Count < 2) { list.Add(key); } } foreach (string item2 in list) { PinGroups.Remove(item2); PinnedRecipeOrder.Remove("GROUP:" + item2); DebugLogger.Warning("Removed group '" + item2 + "' because it has less than 2 valid members"); } if (num > 0 || list.Count > 0) { DebugLogger.Log($"Removed {num} invalid group member(s) and {list.Count} invalid group(s)"); } return num + list.Count; } public void TryPinHoveredRecipe(InventoryGui gui) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown 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(); 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, logHit: false)) { 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(); int num = -1; for (int i = 0; i < list.Count; i++) { GameObject interfaceElementFromObject = GetInterfaceElementFromObject(list[i]); if (!((Object)(object)interfaceElementFromObject == (Object)null) && ((Object)(object)interfaceElementFromObject == (Object)(object)((Component)val).gameObject || interfaceElementFromObject.transform.IsChildOf(val))) { num = i; break; } } int num2 = -1; foreach (object item2 in list) { num2++; if (num >= 0 && num2 != num) { continue; } Recipe recipeFromObject = GetRecipeFromObject(item2); if (!((Object)(object)recipeFromObject != (Object)null)) { continue; } bool flag2 = num >= 0; if (!flag2) { 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", ""); flag2 = text4.Equals(text3, StringComparison.OrdinalIgnoreCase) || text4.Equals(text2, StringComparison.OrdinalIgnoreCase); } if (!flag2) { continue; } if (flag) { ItemData val3 = GetItemDataFromObject(item2) ?? ReflectionHelper.GetCraftUpgradeItem(gui); if (val3 != null) { int quality = val3.m_quality; int num3 = 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} ★{num3}"; if (IsUnpinHotkeyHeld() && !PinnedRecipes.ContainsKey(text6)) { return; } DebugLogger.Verbose("Attempting to pin hovered recipe..."); DebugLogger.Verbose($"Hovered: '{text3}' (UpgradeTab: {flag})"); 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 if (!IsUnpinHotkeyHeld() || PinnedRecipes.ContainsKey(((Object)recipeFromObject).name)) { DebugLogger.Verbose("Attempting to pin hovered recipe..."); DebugLogger.Verbose($"Hovered: '{text3}' (UpgradeTab: {flag})"); DebugLogger.Log("Matched recipe: " + ((Object)recipeFromObject).name); TogglePin(((Object)recipeFromObject).name); } return; } } } public void TryPinHoveredPiece() { 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 && (!IsUnpinHotkeyHeld() || PinnedRecipes.ContainsKey(((Object)hoveredPiece).name))) { DebugLogger.Verbose("Attempting to pin hovered piece..."); DebugLogger.Log("Pinning piece: " + ((Object)hoveredPiece).name); TogglePin(((Object)hoveredPiece).name); } } } private bool IsUnpinHotkeyHeld() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) ConfigEntry hotkeyUnpin = RecipePinnerPlugin.HotkeyUnpin; KeyCode val = (KeyCode)((hotkeyUnpin == null) ? 304 : ((int)hotkeyUnpin.Value)); if ((int)val != 0) { return Input.GetKey(val); } return false; } private void TogglePin(string recipeName) { bool flag = IsUnpinHotkeyHeld(); LocalizationManager localizationMgr = RecipePinnerPlugin.Instance.LocalizationMgr; if (PinnedRecipes.TryGetValue(recipeName, out var value)) { if (flag) { int groupClaimCount = GetGroupClaimCount(recipeName); int num = groupClaimCount; value--; if (value < num) { if (groupClaimCount > 0) { value = groupClaimCount; PinnedRecipes[recipeName] = value; string groupContainingRecipe = GetGroupContainingRecipe(recipeName); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "Cannot remove: in group \"" + groupContainingRecipe + "\"", 0, (Sprite)null); } DebugLogger.Log($"Hotkey unpin blocked: {recipeName} min={groupClaimCount}"); } else { PinnedRecipes.Remove(recipeName); PinnedRecipeOrder.Remove(recipeName); Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)2, localizationMgr.GetText("unpinned"), 0, (Sprite)null); } DebugLogger.Log("Unpinned: " + recipeName); } } else if (value == 0) { PinnedRecipes.Remove(recipeName); PinnedRecipeOrder.Remove(recipeName); Player localPlayer3 = Player.m_localPlayer; if (localPlayer3 != null) { ((Character)localPlayer3).Message((MessageType)2, localizationMgr.GetText("unpinned"), 0, (Sprite)null); } DebugLogger.Log("Unpinned: " + recipeName); } else { PinnedRecipes[recipeName] = value; int num2 = value - groupClaimCount; if (num2 > 0) { string text = string.Format(localizationMgr.GetText("decreased"), num2); Player localPlayer4 = Player.m_localPlayer; if (localPlayer4 != null) { ((Character)localPlayer4).Message((MessageType)2, text, 0, (Sprite)null); } } else { Player localPlayer5 = Player.m_localPlayer; if (localPlayer5 != null) { ((Character)localPlayer5).Message((MessageType)2, localizationMgr.GetText("unpinned"), 0, (Sprite)null); } } DebugLogger.Log($"Decreased pin count: {recipeName} = {value}"); } } else { value++; PinnedRecipes[recipeName] = value; int groupClaimCount2 = GetGroupClaimCount(recipeName); if (groupClaimCount2 > 0) { int num3 = value - groupClaimCount2; if (num3 == 1) { if (!PinnedRecipeOrder.Contains(recipeName)) { PinnedRecipeOrder.Add(recipeName); } Player localPlayer6 = Player.m_localPlayer; if (localPlayer6 != null) { ((Character)localPlayer6).Message((MessageType)2, localizationMgr.GetText("pinned"), 0, (Sprite)null); } } else { string text2 = string.Format(localizationMgr.GetText("added_more"), num3); Player localPlayer7 = Player.m_localPlayer; if (localPlayer7 != null) { ((Character)localPlayer7).Message((MessageType)2, text2, 0, (Sprite)null); } } } else { string text3 = string.Format(localizationMgr.GetText("added_more"), value); Player localPlayer8 = Player.m_localPlayer; if (localPlayer8 != null) { ((Character)localPlayer8).Message((MessageType)2, text3, 0, (Sprite)null); } } DebugLogger.Log($"Increased pin count: {recipeName} = {value}"); } } else { if (flag) { return; } if (GetEffectivePinCount() < RecipePinnerPlugin.MaximumPins.Value) { PinnedRecipes.Add(recipeName, 1); if (!PinnedRecipeOrder.Contains(recipeName)) { PinnedRecipeOrder.Add(recipeName); } Player localPlayer9 = Player.m_localPlayer; if (localPlayer9 != null) { ((Character)localPlayer9).Message((MessageType)2, localizationMgr.GetText("pinned"), 0, (Sprite)null); } DebugLogger.Log("Pinned new recipe: " + recipeName); } else { Player localPlayer10 = Player.m_localPlayer; if (localPlayer10 != null) { ((Character)localPlayer10).Message((MessageType)2, localizationMgr.GetText("list_full"), 0, (Sprite)null); } DebugLogger.Warning($"Cannot pin {recipeName} - max pins reached ({RecipePinnerPlugin.MaximumPins.Value})"); } } RefreshRecipeCache(); RecipePinnerPlugin.Instance?.DataMgr.SavePins(); } 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 GameObject GetInterfaceElementFromObject(object data) { if (data == null) { return null; } Type type = data.GetType(); if (_elementLookupFailed.Contains(type)) { return null; } if (_cachedElementProps.TryGetValue(type, out var value)) { object? value2 = value.GetValue(data, null); return (GameObject)((value2 is GameObject) ? value2 : null); } if (_cachedElementFields.TryGetValue(type, out var value3)) { object? value4 = value3.GetValue(data); return (GameObject)((value4 is GameObject) ? value4 : null); } PropertyInfo property = type.GetProperty("InterfaceElement", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.PropertyType == typeof(GameObject) && property.CanRead) { _cachedElementProps[type] = property; object? value5 = property.GetValue(data, null); return (GameObject)((value5 is GameObject) ? value5 : null); } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.PropertyType == typeof(GameObject) && propertyInfo.CanRead) { _cachedElementProps[type] = propertyInfo; object? value6 = propertyInfo.GetValue(data, null); return (GameObject)((value6 is GameObject) ? value6 : null); } } FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(GameObject)) { _cachedElementFields[type] = fieldInfo; object? value7 = fieldInfo.GetValue(data); return (GameObject)((value7 is GameObject) ? value7 : null); } } _elementLookupFailed.Add(type); DebugLogger.Warning("GetInterfaceElementFromObject: no GameObject member on '" + type.Name + "' - falling back to name matching"); return null; } private string ExtractTextFromUI(Transform child) { Text componentInChildren = ((Component)child).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { return componentInChildren.text; } Component[] componentsInChildren = ((Component)child).GetComponentsInChildren(true); foreach (Component val in componentsInChildren) { 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(); if ((Object)(object)component != (Object)null) { return component.m_itemData.m_shared.m_name; } Piece component2 = val.GetComponent(); if ((Object)(object)component2 != (Object)null) { return component2.m_name; } } return null; } private bool IsVisibleInScroll(RectTransform item, ScrollRect scrollRect) { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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); } private PinnedRecipeData BuildPinnedRecipeData(Recipe r, string recipeName, int count) { if ((Object)(object)r == (Object)null) { return null; } PinnedRecipeData pinnedRecipeData = new PinnedRecipeData { IsDirty = true, RecipeRef = r, StackCount = count }; if ((Object)(object)r.m_item != (Object)null && r.m_item.m_itemData != null) { pinnedRecipeData.Icon = r.m_item.m_itemData.GetIcon(); pinnedRecipeData.RawName = r.m_item.m_itemData.m_shared?.m_name; } else if ((Object)(object)r.m_item != (Object)null) { DebugLogger.Warning("BuildPinnedRecipeData: recipe '" + recipeName + "' has item without itemData, using fallback name"); } else { ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab(((Object)r).name) : null); if ((Object)(object)val != (Object)null) { Piece component = val.GetComponent(); if ((Object)(object)component != (Object)null) { pinnedRecipeData.Icon = component.m_icon; pinnedRecipeData.RawName = component.m_name; } } } if (string.IsNullOrEmpty(pinnedRecipeData.RawName)) { pinnedRecipeData.RawName = ((Object)r).name; } string text = pinnedRecipeData.RawName; if (Localization.instance != null) { Match match = UpgradeStarRegex.Match(recipeName); text = ((!match.Success) ? Localization.instance.Localize(pinnedRecipeData.RawName) : (Localization.instance.Localize(pinnedRecipeData.RawName) + match.Value)); } text = text.Replace("\r", "").Replace("\n", ""); if (r.m_amount > 1) { text += $" (x{r.m_amount})"; } if (count > 1) { text = $"{count}x {text}"; } pinnedRecipeData.CachedHeader = text; if (r.m_resources == null) { DebugLogger.Warning("BuildPinnedRecipeData: recipe '" + recipeName + "' has null resources, skipping"); return null; } Requirement[] resources = r.m_resources; foreach (Requirement val2 in resources) { if (val2 == null || val2.m_amount <= 0) { continue; } if ((Object)(object)val2.m_resItem == (Object)null || val2.m_resItem.m_itemData == null) { DebugLogger.Warning("BuildPinnedRecipeData: skipping invalid resource in '" + recipeName + "'"); continue; } 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 * count, LastKnownAmount = -1, LastKnownInvAmount = -1 }; if (string.IsNullOrEmpty(pinnedResData.ItemName)) { DebugLogger.Warning("BuildPinnedRecipeData: skipping resource with empty item name in '" + recipeName + "'"); continue; } string text2 = pinnedResData.ItemName; if (Localization.instance != null) { text2 = Localization.instance.Localize(pinnedResData.ItemName); } text2 = text2.Replace("\r", "").Replace("\n", ""); pinnedResData.CachedName = text2; pinnedRecipeData.Resources.Add(pinnedResData); } return pinnedRecipeData; } public bool CreateGroup(string groupName, List selectedKeys) { if (string.IsNullOrWhiteSpace(groupName)) { DebugLogger.Warning("CreateGroup: group name is empty"); return false; } if (selectedKeys == null || selectedKeys.Count < 2) { DebugLogger.Warning($"CreateGroup: need at least 2 pins, got {selectedKeys?.Count ?? 0}"); return false; } if (PinGroups.ContainsKey(groupName)) { DebugLogger.Warning("CreateGroup: group '" + groupName + "' already exists"); return false; } PinGroupData pinGroupData = new PinGroupData { GroupName = groupName }; foreach (string selectedKey in selectedKeys) { if (PinnedRecipes.TryGetValue(selectedKey, out var value)) { int groupClaimCount = GetGroupClaimCount(selectedKey); int num = value - groupClaimCount; if (num <= 0) { DebugLogger.Warning($"CreateGroup: recipe key '{selectedKey}' has no ungrouped excess to claim (total={value}, claims={groupClaimCount}), skipping"); continue; } pinGroupData.MemberRecipeKeys.Add(selectedKey); pinGroupData.MemberCounts[selectedKey] = num; DebugLogger.Verbose($"CreateGroup: added member '{selectedKey}' to group '{groupName}' (claim={num}, total={value}, previousClaims={groupClaimCount})"); } else { DebugLogger.Warning("CreateGroup: recipe key '" + selectedKey + "' not found in PinnedRecipes, skipping"); } } if (pinGroupData.MemberRecipeKeys.Count < 2) { DebugLogger.Warning($"CreateGroup: only {pinGroupData.MemberRecipeKeys.Count} valid members, need at least 2"); return false; } PinGroups[groupName] = pinGroupData; string item = "GROUP:" + groupName; PinnedRecipeOrder.Add(item); DebugLogger.Log($"Group created: '{groupName}' with {pinGroupData.MemberRecipeKeys.Count} members (added to end)"); RefreshRecipeCache(); RecipePinnerPlugin.Instance?.DataMgr.SavePins(); return true; } public bool DisbandGroup(string groupName) { if (!PinGroups.TryGetValue(groupName, out var value)) { DebugLogger.Warning("DisbandGroup: group '" + groupName + "' not found"); return false; } PinGroups.Remove(groupName); PinnedRecipeOrder.Remove("GROUP:" + groupName); DebugLogger.Log($"Group disbanded: '{groupName}' ({value.MemberRecipeKeys.Count} members restored)"); RefreshRecipeCache(); RecipePinnerPlugin.Instance?.DataMgr.SavePins(); return true; } public void DecrementGroupMemberCounts(string recipeKey) { string text = null; foreach (string item in PinnedRecipeOrder) { if (item.StartsWith("GROUP:")) { string text2 = item.Substring(6); if (PinGroups.TryGetValue(text2, out var value) && value.MemberCounts.ContainsKey(recipeKey)) { text = text2; } } } if (text == null) { return; } string text3 = text; if (!PinGroups.TryGetValue(text3, out var value2)) { return; } int num = value2.MemberCounts[recipeKey]; int num2 = num - 1; if (num2 <= 0) { value2.MemberCounts.Remove(recipeKey); value2.MemberRecipeKeys.Remove(recipeKey); DebugLogger.Log($"AutoUnpin: '{recipeKey}' claim reached 0, removed from group '{text3}' ({value2.MemberRecipeKeys.Count} remaining)"); if (value2.MemberRecipeKeys.Count < 2) { DebugLogger.Log($"AutoUnpin: Group '{text3}' auto-disbanded ({value2.MemberRecipeKeys.Count} members)"); PinGroups.Remove(text3); PinnedRecipeOrder.Remove("GROUP:" + text3); } } else { value2.MemberCounts[recipeKey] = num2; DebugLogger.Log($"AutoUnpin: '{recipeKey}' claim decremented in group '{text3}': {num}->{num2}"); } } public void RemoveMemberFromGroup(string groupName, string recipeKey) { if (!PinGroups.TryGetValue(groupName, out var value)) { DebugLogger.Warning("RemoveMemberFromGroup: group '" + groupName + "' not found"); return; } if (!value.MemberRecipeKeys.Remove(recipeKey)) { DebugLogger.Warning("RemoveMemberFromGroup: '" + recipeKey + "' not in group '" + groupName + "'"); return; } int value2; int num = ((!value.MemberCounts.TryGetValue(recipeKey, out value2)) ? 1 : value2); value.MemberCounts.Remove(recipeKey); DebugLogger.Log($"Removed '{recipeKey}' from group '{groupName}' (claim={num}, {value.MemberRecipeKeys.Count} remaining)"); if (PinnedRecipes.TryGetValue(recipeKey, out var value3)) { int num2 = value3 - num; if (num2 <= 0) { PinnedRecipes.Remove(recipeKey); PinnedRecipeOrder.Remove(recipeKey); } else { PinnedRecipes[recipeKey] = num2; } } if (value.MemberRecipeKeys.Count < 2) { DebugLogger.Log($"Group '{groupName}' has {value.MemberRecipeKeys.Count} member(s), auto-disbanding"); PinGroups.Remove(groupName); PinnedRecipeOrder.Remove("GROUP:" + groupName); } RefreshRecipeCache(); RecipePinnerPlugin.Instance?.DataMgr.SavePins(); if (GetEffectivePinCount() < 2) { RecipePinnerPlugin.Instance?.UIMgr.CloseGatheringList(); } } public void RemovePinFromMyPinsPanel(string key) { if (PinGroups.TryGetValue(key, out var value)) { foreach (string memberRecipeKey in value.MemberRecipeKeys) { if (PinnedRecipes.TryGetValue(memberRecipeKey, out var value2)) { int value3; int num = ((!value.MemberCounts.TryGetValue(memberRecipeKey, out value3)) ? 1 : value3); int num2 = value2 - num; if (num2 <= 0) { PinnedRecipes.Remove(memberRecipeKey); PinnedRecipeOrder.Remove(memberRecipeKey); DebugLogger.Verbose("Removed group member pin entirely: " + memberRecipeKey); } else { PinnedRecipes[memberRecipeKey] = num2; DebugLogger.Verbose($"Group member pin kept as individual: {memberRecipeKey} x{num2}"); } } } PinGroups.Remove(key); PinnedRecipeOrder.Remove("GROUP:" + key); DebugLogger.Log("Removed group: " + key + " (member excess pins preserved)"); } else { int groupClaimCount = GetGroupClaimCount(key); if (groupClaimCount > 0) { if (PinnedRecipes.TryGetValue(key, out var value4) && value4 > groupClaimCount) { PinnedRecipes[key] = groupClaimCount; DebugLogger.Log($"Removed individual excess for grouped pin: {key} (kept {groupClaimCount} for groups)"); } else { DebugLogger.Log($"No individual excess to remove for: {key} (claims={groupClaimCount})"); } } else { PinnedRecipes.Remove(key); PinnedRecipeOrder.Remove(key); DebugLogger.Log("Removed pin: " + key); } } RefreshRecipeCache(); RecipePinnerPlugin.Instance?.DataMgr.SavePins(); if (GetEffectivePinCount() < 2) { RecipePinnerPlugin.Instance?.UIMgr.CloseGatheringList(); } } public void AdjustPinCount(string key, int delta, bool showMessage = true) { if (!PinnedRecipes.TryGetValue(key, out var value)) { DebugLogger.Warning("AdjustPinCount: recipe '" + key + "' not found"); return; } int groupClaimCount = GetGroupClaimCount(key); int num = ((groupClaimCount <= 0) ? 1 : (groupClaimCount + 1)); value += delta; DebugLogger.Log($"AdjustPinCount: {key} -> {value} (min={num}, claims={groupClaimCount})"); if (value < num) { value = num; DebugLogger.Log($"AdjustPinCount: clamped to minimum {num} for '{key}'"); return; } PinnedRecipes[key] = value; if (showMessage) { LocalizationManager localizationManager = RecipePinnerPlugin.Instance?.LocalizationMgr; if (localizationManager != null) { int num2 = value - groupClaimCount; if (delta > 0) { string text = string.Format(localizationManager.GetText("added_more"), num2); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null); } } else { string text2 = string.Format(localizationManager.GetText("decreased"), num2); Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)2, text2, 0, (Sprite)null); } } } } RefreshRecipeCache(); RecipePinnerPlugin.Instance?.DataMgr.SavePins(); } public string GetGroupContainingRecipe(string recipeKey) { foreach (KeyValuePair pinGroup in PinGroups) { if (pinGroup.Value.MemberRecipeKeys.Contains(recipeKey)) { return pinGroup.Key; } } return null; } public int GetGroupClaimCount(string recipeKey) { int num = 0; foreach (PinGroupData value2 in PinGroups.Values) { if (value2.MemberCounts.TryGetValue(recipeKey, out var value)) { num += value; } else if (value2.MemberRecipeKeys.Contains(recipeKey)) { num++; } } return num; } public int TrimToMaximumPins(int maxEffectivePins) { int num = 0; while (GetEffectivePinCount() > maxEffectivePins) { bool flag = false; for (int num2 = PinnedRecipeOrder.Count - 1; num2 >= 0; num2--) { string text = PinnedRecipeOrder[num2]; if (text.StartsWith("GROUP:")) { string key = text.Substring(6); PinnedRecipeOrder.RemoveAt(num2); if (PinGroups.TryGetValue(key, out var value)) { foreach (string memberRecipeKey in value.MemberRecipeKeys) { if (PinnedRecipes.TryGetValue(memberRecipeKey, out var value2)) { int value3; int num3 = ((!value.MemberCounts.TryGetValue(memberRecipeKey, out value3)) ? 1 : value3); int num4 = value2 - num3; if (num4 > 0) { PinnedRecipes[memberRecipeKey] = num4; continue; } PinnedRecipes.Remove(memberRecipeKey); PinnedRecipeOrder.Remove(memberRecipeKey); } } PinGroups.Remove(key); } num++; flag = true; break; } if (PinnedRecipes.TryGetValue(text, out var value4)) { int groupClaimCount = GetGroupClaimCount(text); if (value4 > groupClaimCount) { if (groupClaimCount > 0) { PinnedRecipes[text] = groupClaimCount; } else { PinnedRecipes.Remove(text); PinnedRecipeOrder.RemoveAt(num2); } num++; flag = true; break; } } } if (!flag) { break; } } return num; } public int GetEffectivePinCount() { int num = PinGroups.Count; foreach (KeyValuePair pinnedRecipe in PinnedRecipes) { int groupClaimCount = GetGroupClaimCount(pinnedRecipe.Key); if (pinnedRecipe.Value > groupClaimCount) { num++; } } return num; } } [BepInPlugin("com.Kadrio.RecipePinner", "Recipe Pinner", "1.3.0")] public class RecipePinnerPlugin : BaseUnityPlugin { public enum PinLayoutMode { AutoDetect, ForceVertical, ForceHorizontal, ForceBottomRightHorizontal } public class ConfigurationManagerAttributes { public bool? ShowRangeAsPercent; public Action 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 ObjToStr; public Func StrToObj; } public static RecipePinnerPlugin Instance; public LocalizationManager LocalizationMgr; public RecipeManager RecipeMgr; public ContainerScanner ContainerMgr; public UIManager UIMgr; public DataPersistence DataMgr; internal bool _mluiMapListEnabled; internal bool _mluiNoMapListEnabled; internal bool _mluiInstalled; private bool _startupInitialized; private string _lastLanguage = ""; private string _currentSessionPlayer; private const float ClearAllConfirmWindow = 2f; private float _clearAllArmedUntil; private static bool _isUiVisible = true; public static ConfigEntry EnableMod; public static ConfigEntry LanguageOverride; public static ConfigEntry LayoutModeConfig; public static ConfigEntry MaximumPins; public static ConfigEntry PinsPerPage; public static ConfigEntry AutoUnpinAfterCrafting; public static ConfigEntry AutoUnpinAfterBuilding; public static ConfigEntry HotkeyPin; public static ConfigEntry HotkeyUnpin; public static ConfigEntry HotkeyClearAll; public static ConfigEntry HotkeyToggleVisibility; public static ConfigEntry HotkeyPageSwitch; public static ConfigEntry HotkeyGatheringList; public static ConfigEntry EnableChestScanning; public static ConfigEntry ChestScanRange; public static ConfigEntry ChestScanInterval; public static ConfigEntry UIScale; public static ConfigEntry BackgroundOpacity; public static ConfigEntry FontSizeRecipeName; public static ConfigEntry FontSizeMaterials; public static ConfigEntry HudRecipeIconSize; public static ConfigEntry HudMaterialIconSize; public static ConfigEntry HudGroupIconSize; public static ConfigEntry EnableCraftReadiness; public static ConfigEntry ColorHeader; public static ConfigEntry ColorEnoughInInventory; public static ConfigEntry ColorEnoughWithChests; public static ConfigEntry ColorMissing; public static ConfigEntry ColorCraftReady; public static ConfigEntry ColorCraftNotReady; public static ConfigEntry ColorPaginationActive; public static ConfigEntry PaginationInactiveOpacity; public static ConfigEntry PaginationDotSize; public static ConfigEntry PaginationDotSpacing; public static ConfigEntry EnableGatheringList; public static ConfigEntry AutoOpenGatheringList; public static ConfigEntry GatheringListColumns; public static ConfigEntry GatheringListFontSizeTitle; public static ConfigEntry GatheringListFontSizeMaterials; public static ConfigEntry ContainerGatheringListPosition; public static ConfigEntry GroupCompactThreshold; public static ConfigEntry GroupCompactMaxRows; public static ConfigEntry GroupIconFontSize; public static ConfigEntry MyPinsPanelWidth; public static ConfigEntry MyPinsPanelHeight; public static ConfigEntry MyPinsPanelPosition; public static ConfigEntry ButtonTextColor; public static ConfigEntry MyPinsButtonPosition; public static ConfigEntry MyPinsButtonSize; public static ConfigEntry VerticalListWidth; public static ConfigEntry VerticalPinSpacing; public static ConfigEntry VerticalPosition; public static ConfigEntry HorizontalColumnWidth; public static ConfigEntry HorizontalPinSpacing; public static ConfigEntry HorizontalPosition; public static ConfigEntry BottomRightColumnWidth; public static ConfigEntry BottomRightPinSpacing; public static ConfigEntry BottomRightPosition; public static ConfigEntry EnableDebugLogging; public static bool IsUiVisible => _isUiVisible; internal bool IsPinDataLoaded => _startupInitialized; 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) 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 Start() { DebugLogger.Log("Start()"); LocalizationMgr.LoadTranslations(); ReadMyLittleUIConfig(); ContainerMgr.InitializeContainers(); DebugLogger.Log("Start done"); } private void OnDestroy() { DebugLogger.Log("OnDestroy"); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && !string.IsNullOrEmpty(localPlayer.GetPlayerName())) { if (EnableMod == null || !EnableMod.Value) { DebugLogger.Verbose("OnDestroy save skipped - mod is disabled"); } else if (!_startupInitialized) { DebugLogger.Verbose("OnDestroy save skipped - pin data not loaded yet"); } else { DataMgr.SavePins(); } } RecipeMgr.Cleanup(); } private void OnApplicationFocus(bool hasFocus) { if (hasFocus) { UIMgr?.ResetMyPinsInputState("application focus restored"); } } private void Update() { //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_041d: 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(); } bool flag = (Input.GetKeyDown(HotkeyToggleVisibility.Value) || Input.GetKeyDown(HotkeyPin.Value) || Input.GetKeyDown(HotkeyClearAll.Value) || Input.GetKeyDown(HotkeyPageSwitch.Value) || Input.GetKeyDown(HotkeyGatheringList.Value)) && AreRecipePinnerHotkeysBlocked(); if (Input.GetKeyDown(HotkeyToggleVisibility.Value) && !flag) { _isUiVisible = !_isUiVisible; DebugLogger.Log($"UI visibility toggled: {_isUiVisible}"); } if ((Object)(object)Player.m_localPlayer != (Object)null) { UpdatePlayerSession(); } bool flag2 = (Object)(object)Player.m_localPlayer != (Object)null && ((Character)Player.m_localPlayer).InPlaceMode(); if (Input.GetKeyDown(HotkeyPin.Value) && !flag) { if ((Object)(object)InventoryGui.instance != (Object)null && InventoryGui.IsVisible()) { RecipeMgr.TryPinHoveredRecipe(InventoryGui.instance); } else if (flag2) { RecipeMgr.TryPinHoveredPiece(); } } if (Input.GetKeyDown(HotkeyClearAll.Value) && !flag && (RecipeMgr.PinnedRecipes.Count > 0 || RecipeMgr.PinGroups.Count > 0)) { if (Time.unscaledTime <= _clearAllArmedUntil) { _clearAllArmedUntil = 0f; int count = RecipeMgr.PinnedRecipes.Count; int count2 = RecipeMgr.PinGroups.Count; RecipeMgr.PinnedRecipes.Clear(); RecipeMgr.PinnedRecipeOrder.Clear(); RecipeMgr.PinGroups.Clear(); _isUiVisible = true; RecipeMgr.RefreshRecipeCache(); UIMgr.CloseGatheringList(); UIMgr.RefreshMyPinsList(); DataMgr.SavePins(); 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 and {count2} groups"); } else { _clearAllArmedUntil = Time.unscaledTime + 2f; Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)2, LocalizationMgr.GetText("clear_confirm_hotkey"), 0, (Sprite)null); } DebugLogger.Log("Clear-all armed - press again to confirm"); } } 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(); UIMgr?.DestroyMyPinsUI(); } } if (Input.GetKeyDown(HotkeyPageSwitch.Value) && _isUiVisible && !flag) { UIMgr?.CyclePage(); } if (Input.GetKeyDown(HotkeyGatheringList.Value) && !flag && EnableGatheringList.Value) { UIMgr?.ToggleGatheringList(); } if ((Object)(object)Player.m_localPlayer != (Object)null) { UIMgr?.UpdateMyPinsInventoryState(); } } private bool AreRecipePinnerHotkeysBlocked() { if (InputHelper.IsInputBlocked()) { return true; } if (UIMgr != null && UIMgr.IsMyPinsPanelOpen) { return true; } if (ControlsInfoPanel.IsOpen) { return true; } InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance != (Object)null && InventoryGui.IsVisible() && ReflectionHelper.IsBlockingInventoryPanelOpen(instance)) { return true; } return false; } private void UpdatePlayerSession() { if ((Object)(object)Player.m_localPlayer == (Object)null || ((Character)Player.m_localPlayer).IsDead()) { UIMgr?.UpdateUI(isVisible: false); 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.PinnedRecipeOrder.Clear(); RecipeMgr.CachedPins.Clear(); RecipeMgr.PinGroups.Clear(); UIMgr.DestroyUI(); UIMgr.DestroyMyPinsUI(); _currentSessionPlayer = playerName; DataMgr.LoadPins(); if ((Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_recipes.Count > 0) { RecipeMgr.ValidateAndCleanPins(); } 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; for (int i = 0; i < array2.Length; i++) { string text2 = array2[i].Trim(); bool value; if (text2.StartsWith("[") && text2.EndsWith("]")) { text = text2; } else if (TryReadBoolConfigValue(text2, "Enable", out value)) { if (text == "[Status effects - Map - List]") { _mluiMapListEnabled = value; } else if (text == "[Status effects - Nomap - List]") { _mluiNoMapListEnabled = value; } } } DebugLogger.Log($"MyLittleUI Config: MapList={_mluiMapListEnabled}, NoMapList={_mluiNoMapListEnabled}"); } catch (Exception ex) { DebugLogger.Error("Error reading MyLittleUI config", ex); } } private static bool TryReadBoolConfigValue(string line, string key, out bool value) { value = false; int num = line.IndexOf('='); if (num <= 0) { return false; } if (!string.Equals(line.Substring(0, num).Trim(), key, StringComparison.OrdinalIgnoreCase)) { return false; } string text = line.Substring(num + 1); int num2 = text.IndexOf('#'); if (num2 >= 0) { text = text.Substring(0, num2); } return bool.TryParse(text.Trim(), out value); } [HarmonyPatch(typeof(Game), "SavePlayerProfile")] [HarmonyPostfix] public static void AutoSavePinsHook() { if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)Instance == (Object)null)) { if (EnableMod == null || !EnableMod.Value) { DebugLogger.Verbose("Auto-save skipped - mod is disabled"); return; } if (!Instance.IsPinDataLoaded) { DebugLogger.Verbose("Auto-save skipped - pin data not loaded yet"); return; } 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)) { bool num2 = value > Instance.RecipeMgr.GetGroupClaimCount(text); value--; DebugLogger.Log($"Auto-unpin: {text}, remaining count: {value}"); if (num2) { DebugLogger.Verbose("Auto-unpin: consumed ungrouped copy of '" + text + "', group claims untouched"); } else { Instance.RecipeMgr.DecrementGroupMemberCounts(text); } if (value <= 0) { Instance.RecipeMgr.PinnedRecipes.Remove(text); Instance.RecipeMgr.PinnedRecipeOrder.Remove(text); DebugLogger.Log("Recipe " + text + " fully unpinned"); } else { Instance.RecipeMgr.PinnedRecipes[text] = value; } Instance.RecipeMgr.RefreshRecipeCache(); Instance.DataMgr.SavePins(); if (Instance.RecipeMgr.GetEffectivePinCount() < 2) { Instance.UIMgr.CloseGatheringList(); } } else if (text != null) { string text2 = string.Join(", ", Instance.RecipeMgr.PinnedRecipes.Keys); DebugLogger.Verbose("Auto-unpin: '" + text + "' not found in PinnedRecipes. Current keys: [" + text2 + "]"); } } [HarmonyPatch(typeof(Player), "PlacePiece")] [HarmonyPostfix] public static void AutoUnpinBuildHook(Piece piece) { DebugLogger.Verbose("AutoUnpinBuildHook fired (PlacePiece postfix)"); if ((Object)(object)Instance == (Object)null || !EnableMod.Value || !AutoUnpinAfterBuilding.Value) { DebugLogger.Verbose($"AutoUnpinBuildHook early exit: Instance={(Object)(object)Instance != (Object)null}, EnableMod={EnableMod?.Value}, AutoUnpin={AutoUnpinAfterBuilding?.Value}"); return; } if ((Object)(object)piece == (Object)null) { DebugLogger.Verbose("AutoUnpinBuildHook: piece is null"); return; } string text = ((Object)piece).name.Replace("(Clone)", "").Trim(); if (!Instance.RecipeMgr.PinnedRecipes.TryGetValue(text, out var value)) { DebugLogger.Verbose("AutoUnpinBuildHook: '" + text + "' not pinned"); return; } bool num = value > Instance.RecipeMgr.GetGroupClaimCount(text); value--; DebugLogger.Log($"Auto-unpin (Build): {text}, remaining count: {value}"); if (num) { DebugLogger.Verbose("Auto-unpin (Build): consumed ungrouped copy of '" + text + "', group claims untouched"); } else { Instance.RecipeMgr.DecrementGroupMemberCounts(text); } if (value <= 0) { Instance.RecipeMgr.PinnedRecipes.Remove(text); Instance.RecipeMgr.PinnedRecipeOrder.Remove(text); DebugLogger.Log("Build recipe " + text + " fully unpinned"); } else { Instance.RecipeMgr.PinnedRecipes[text] = value; } Instance.RecipeMgr.RefreshRecipeCache(); Instance.DataMgr.SavePins(); if (Instance.RecipeMgr.GetEffectivePinCount() < 2) { Instance.UIMgr.CloseGatheringList(); } } [HarmonyPatch(typeof(Player), "TakeInput")] [HarmonyPrefix] public static bool Player_TakeInput_BlockDuringDialog() { if (GroupNameDialog.IsDialogOpen || ConfirmDialog.IsDialogOpen) { return false; } if (Instance?.UIMgr != null && Instance.UIMgr.IsMyPinsPanelOpen) { return false; } return true; } [HarmonyPatch(typeof(InventoryGui), "Hide")] [HarmonyPrefix] public static bool InventoryGui_Hide_BlockDuringDialog() { if (GroupNameDialog.IsDialogOpen) { DebugLogger.Verbose("InventoryGui.Hide blocked - GroupNameDialog is open"); return false; } if (ConfirmDialog.IsDialogOpen) { DebugLogger.Verbose("InventoryGui.Hide blocked - ConfirmDialog is open"); return false; } if (ControlsInfoPanel.IsOpen) { ControlsInfoPanel.Instance?.Hide(); DebugLogger.Log("InventoryGui.Hide intercepted (ESC) - closing ControlsInfoPanel only"); return false; } if (Instance?.UIMgr != null && Instance.UIMgr.IsMyPinsPanelOpen) { bool keyDown = Input.GetKeyDown((KeyCode)27); Instance.UIMgr.ToggleMyPinsPanel(); if (keyDown) { DebugLogger.Log("InventoryGui.Hide intercepted (ESC) - closing My Pins panel only"); return false; } DebugLogger.Log("InventoryGui.Hide intercepted (Tab) - closing My Pins panel + inventory"); return true; } return true; } private void BindConfigs() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Expected O, but got Unknown //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Expected O, but got Unknown //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Expected O, but got Unknown //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Expected O, but got Unknown //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Expected O, but got Unknown //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Expected O, but got Unknown //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Expected O, but got Unknown //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Expected O, but got Unknown //IL_034d: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Expected O, but got Unknown //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_039c: Expected O, but got Unknown //IL_03d4: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Expected O, but got Unknown //IL_0415: Unknown result type (might be due to invalid IL or missing references) //IL_041f: Expected O, but got Unknown //IL_047e: Unknown result type (might be due to invalid IL or missing references) //IL_0488: Expected O, but got Unknown //IL_04d1: Unknown result type (might be due to invalid IL or missing references) //IL_04db: Expected O, but got Unknown //IL_0524: Unknown result type (might be due to invalid IL or missing references) //IL_052e: Expected O, but got Unknown //IL_058d: Unknown result type (might be due to invalid IL or missing references) //IL_0597: Expected O, but got Unknown //IL_05d6: Unknown result type (might be due to invalid IL or missing references) //IL_05e0: Expected O, but got Unknown //IL_0635: Unknown result type (might be due to invalid IL or missing references) //IL_063f: Expected O, but got Unknown //IL_0695: Unknown result type (might be due to invalid IL or missing references) //IL_069f: 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_0755: Unknown result type (might be due to invalid IL or missing references) //IL_075f: Expected O, but got Unknown //IL_07ac: Unknown result type (might be due to invalid IL or missing references) //IL_07b6: Expected O, but got Unknown //IL_07f5: Unknown result type (might be due to invalid IL or missing references) //IL_081b: Unknown result type (might be due to invalid IL or missing references) //IL_0825: Expected O, but got Unknown //IL_0864: Unknown result type (might be due to invalid IL or missing references) //IL_088a: Unknown result type (might be due to invalid IL or missing references) //IL_0894: Expected O, but got Unknown //IL_08d3: Unknown result type (might be due to invalid IL or missing references) //IL_08f9: Unknown result type (might be due to invalid IL or missing references) //IL_0903: Expected O, but got Unknown //IL_0942: Unknown result type (might be due to invalid IL or missing references) //IL_0968: Unknown result type (might be due to invalid IL or missing references) //IL_0972: Expected O, but got Unknown //IL_09b1: Unknown result type (might be due to invalid IL or missing references) //IL_09d7: Unknown result type (might be due to invalid IL or missing references) //IL_09e1: Expected O, but got Unknown //IL_0a20: Unknown result type (might be due to invalid IL or missing references) //IL_0a46: Unknown result type (might be due to invalid IL or missing references) //IL_0a50: Expected O, but got Unknown //IL_0a8f: Unknown result type (might be due to invalid IL or missing references) //IL_0ab5: Unknown result type (might be due to invalid IL or missing references) //IL_0abf: Expected O, but got Unknown //IL_0b1e: Unknown result type (might be due to invalid IL or missing references) //IL_0b28: Expected O, but got Unknown //IL_0b7d: Unknown result type (might be due to invalid IL or missing references) //IL_0b87: Expected O, but got Unknown //IL_0bdb: Unknown result type (might be due to invalid IL or missing references) //IL_0be5: Expected O, but got Unknown //IL_0c32: Unknown result type (might be due to invalid IL or missing references) //IL_0c3c: Expected O, but got Unknown //IL_0c89: Unknown result type (might be due to invalid IL or missing references) //IL_0c93: Expected O, but got Unknown //IL_0cd1: Unknown result type (might be due to invalid IL or missing references) //IL_0cdb: Expected O, but got Unknown //IL_0d30: Unknown result type (might be due to invalid IL or missing references) //IL_0d3a: Expected O, but got Unknown //IL_0d8f: Unknown result type (might be due to invalid IL or missing references) //IL_0d99: Expected O, but got Unknown //IL_0dce: Unknown result type (might be due to invalid IL or missing references) //IL_0df4: Unknown result type (might be due to invalid IL or missing references) //IL_0dfe: Expected O, but got Unknown //IL_0e3c: Unknown result type (might be due to invalid IL or missing references) //IL_0e46: Expected O, but got Unknown //IL_0e9a: Unknown result type (might be due to invalid IL or missing references) //IL_0ea4: Expected O, but got Unknown //IL_0ef9: Unknown result type (might be due to invalid IL or missing references) //IL_0f03: Expected O, but got Unknown //IL_0f62: Unknown result type (might be due to invalid IL or missing references) //IL_0f6c: Expected O, but got Unknown //IL_0fcb: Unknown result type (might be due to invalid IL or missing references) //IL_0fd5: Expected O, but got Unknown //IL_1000: Unknown result type (might be due to invalid IL or missing references) //IL_1026: Unknown result type (might be due to invalid IL or missing references) //IL_1030: Expected O, but got Unknown //IL_106f: Unknown result type (might be due to invalid IL or missing references) //IL_1095: Unknown result type (might be due to invalid IL or missing references) //IL_109f: Expected O, but got Unknown //IL_10d4: Unknown result type (might be due to invalid IL or missing references) //IL_10fa: Unknown result type (might be due to invalid IL or missing references) //IL_1104: Expected O, but got Unknown //IL_115d: Unknown result type (might be due to invalid IL or missing references) //IL_1167: Expected O, but got Unknown //IL_11b8: Unknown result type (might be due to invalid IL or missing references) //IL_11c2: Expected O, but got Unknown //IL_11fd: Unknown result type (might be due to invalid IL or missing references) //IL_1207: Expected O, but got Unknown //IL_1226: Unknown result type (might be due to invalid IL or missing references) //IL_124c: Unknown result type (might be due to invalid IL or missing references) //IL_1256: Expected O, but got Unknown //IL_1291: Unknown result type (might be due to invalid IL or missing references) //IL_129b: Expected O, but got Unknown //IL_12d6: Unknown result type (might be due to invalid IL or missing references) //IL_12e0: Expected O, but got Unknown //IL_12ff: Unknown result type (might be due to invalid IL or missing references) //IL_1325: Unknown result type (might be due to invalid IL or missing references) //IL_132f: Expected O, but got Unknown //IL_136a: Unknown result type (might be due to invalid IL or missing references) //IL_1374: Expected O, but got Unknown //IL_13af: Unknown result type (might be due to invalid IL or missing references) //IL_13b9: Expected O, but got Unknown //IL_13d8: Unknown result type (might be due to invalid IL or missing references) //IL_13fe: Unknown result type (might be due to invalid IL or missing references) //IL_1408: Expected O, but got Unknown //IL_143f: Unknown result type (might be due to invalid IL or missing references) //IL_1449: Expected O, but got Unknown EnableMod = ((BaseUnityPlugin)this).Config.Bind("01 - 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(); UIMgr?.DestroyMyPinsUI(); } }; LanguageOverride = ((BaseUnityPlugin)this).Config.Bind("01 - General", "LanguageOverride", "Auto", new ConfigDescription("Force a specific language (e.g., 'German', 'Turkish'). 'Auto' uses the game language.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); LanguageOverride.SettingChanged += delegate { LocalizationMgr?.LoadTranslations(); RecipeMgr?.RefreshRecipeCache(); UIMgr?.DestroyUI(); UIMgr?.DestroyMyPinsUI(); }; LayoutModeConfig = ((BaseUnityPlugin)this).Config.Bind("01 - General", "LayoutMode", PinLayoutMode.AutoDetect, new ConfigDescription("HUD pin layout position.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); LayoutModeConfig.SettingChanged += delegate { ReadMyLittleUIConfig(); UIMgr?.DestroyUI(); }; MaximumPins = ((BaseUnityPlugin)this).Config.Bind("01 - General", "MaximumPins", 10, new ConfigDescription("Maximum number of pins allowed at once.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 20), new object[1] { new ConfigurationManagerAttributes { Order = 96 } })); MaximumPins.SettingChanged += delegate { int num = RecipeMgr?.TrimToMaximumPins(MaximumPins.Value) ?? 0; if (num > 0) { DebugLogger.Log($"MaximumPins reduced, trimmed {num} effective pin(s)"); RecipeMgr?.RefreshRecipeCache(); } UIMgr?.ResetPage(); UIMgr?.DestroyUI(); if (num > 0) { DataMgr?.SavePins(); } }; PinsPerPage = ((BaseUnityPlugin)this).Config.Bind("01 - General", "PinsPerPage", 5, new ConfigDescription("How many pins to show per HUD page.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 10), new object[1] { new ConfigurationManagerAttributes { Order = 95 } })); PinsPerPage.SettingChanged += delegate { UIMgr?.ResetPage(); UIMgr?.DestroyUI(); }; AutoUnpinAfterCrafting = ((BaseUnityPlugin)this).Config.Bind("01 - General", "AutoUnpinAfterCrafting", true, new ConfigDescription("Automatically unpin a recipe after it is crafted.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 94 } })); AutoUnpinAfterBuilding = ((BaseUnityPlugin)this).Config.Bind("01 - General", "AutoUnpinAfterBuilding", true, new ConfigDescription("Automatically unpin a recipe after placing a building piece.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 93 } })); HotkeyPin = ((BaseUnityPlugin)this).Config.Bind("02 - Controls", "HotkeyPin", (KeyCode)325, new ConfigDescription("Hotkey to pin the currently viewed recipe.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); HotkeyUnpin = ((BaseUnityPlugin)this).Config.Bind("02 - Controls", "HotkeyUnpin", (KeyCode)304, new ConfigDescription("Hold this key + press the Pin hotkey over a recipe or build piece to decrease/remove that pin.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); HotkeyToggleVisibility = ((BaseUnityPlugin)this).Config.Bind("02 - Controls", "HotkeyToggleVisibility", (KeyCode)288, new ConfigDescription("Hotkey to show/hide the HUD pin overlay.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); HotkeyGatheringList = ((BaseUnityPlugin)this).Config.Bind("02 - Controls", "HotkeyGatheringList", (KeyCode)289, new ConfigDescription("Hotkey to toggle the Gathering List panel.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 96 } })); HotkeyPageSwitch = ((BaseUnityPlugin)this).Config.Bind("02 - Controls", "HotkeyPageSwitch", (KeyCode)308, new ConfigDescription("Press this key to cycle through HUD pages.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 95 } })); HotkeyClearAll = ((BaseUnityPlugin)this).Config.Bind("02 - Controls", "HotkeyClearAll", (KeyCode)112, new ConfigDescription("Hotkey to clear all pinned recipes.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 94 } })); EnableChestScanning = ((BaseUnityPlugin)this).Config.Bind("03 - Chest Scanner", "EnableChestScanning", false, new ConfigDescription("Count materials found in nearby chests towards requirements.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); EnableChestScanning.SettingChanged += delegate { if (EnableChestScanning.Value) { ContainerScanner.ClearAll(); ContainerMgr?.InitializeContainers(); } else { ContainerScanner.ClearAll(); } RecipeMgr?.RefreshRecipeCache(); }; ChestScanRange = ((BaseUnityPlugin)this).Config.Bind("03 - Chest Scanner", "ChestScanRange", 20f, new ConfigDescription("Radius (meters) in which chests are scanned.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 100f), new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); ChestScanInterval = ((BaseUnityPlugin)this).Config.Bind("03 - Chest Scanner", "ChestScanInterval", 3f, new ConfigDescription("How often (seconds) chests are re-scanned.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 10f), new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); UIScale = ((BaseUnityPlugin)this).Config.Bind("04 - HUD Appearance", "UIScale", 0.75f, new ConfigDescription("Global UI scale multiplier.", (AcceptableValueBase)(object)new AcceptableValueRange(0.3f, 3f), new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); UIScale.SettingChanged += delegate { UIMgr?.DestroyUI(); }; BackgroundOpacity = ((BaseUnityPlugin)this).Config.Bind("04 - HUD Appearance", "BackgroundOpacity", 0.5f, new ConfigDescription("Background panel opacity (0 = transparent, 1 = opaque).", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); FontSizeRecipeName = ((BaseUnityPlugin)this).Config.Bind("04 - HUD Appearance", "FontSizeRecipeName", 16, new ConfigDescription("Font size for recipe/group name in HUD pins.", (AcceptableValueBase)(object)new AcceptableValueRange(8, 40), new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); FontSizeRecipeName.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; FontSizeMaterials = ((BaseUnityPlugin)this).Config.Bind("04 - HUD Appearance", "FontSizeMaterials", 15, new ConfigDescription("Font size for material names and amounts in HUD pins.", (AcceptableValueBase)(object)new AcceptableValueRange(8, 40), new object[1] { new ConfigurationManagerAttributes { Order = 96 } })); FontSizeMaterials.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; HudRecipeIconSize = ((BaseUnityPlugin)this).Config.Bind("04 - HUD Appearance", "HudRecipeIconSize", 28, new ConfigDescription("Size (px) of the recipe icon in the HUD pin header.", (AcceptableValueBase)(object)new AcceptableValueRange(12, 64), new object[1] { new ConfigurationManagerAttributes { Order = 95 } })); HudRecipeIconSize.SettingChanged += delegate { UIMgr?.DestroyUI(); }; HudMaterialIconSize = ((BaseUnityPlugin)this).Config.Bind("04 - HUD Appearance", "HudMaterialIconSize", 20, new ConfigDescription("Size (px) of the material icons in the HUD pin resource list.", (AcceptableValueBase)(object)new AcceptableValueRange(10, 48), new object[1] { new ConfigurationManagerAttributes { Order = 94 } })); HudMaterialIconSize.SettingChanged += delegate { UIMgr?.DestroyUI(); }; HudGroupIconSize = ((BaseUnityPlugin)this).Config.Bind("04 - HUD Appearance", "HudGroupIconSize", 28, new ConfigDescription("Size (px) of the group icon (stacked cards) in the HUD pin header.", (AcceptableValueBase)(object)new AcceptableValueRange(12, 64), new object[1] { new ConfigurationManagerAttributes { Order = 93 } })); HudGroupIconSize.SettingChanged += delegate { UIMgr?.DestroyUI(); }; EnableCraftReadiness = ((BaseUnityPlugin)this).Config.Bind("04 - HUD Appearance", "EnableCraftReadiness", true, new ConfigDescription("Show a colored accent bar indicating whether a recipe can be crafted.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 92 } })); EnableCraftReadiness.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; ColorHeader = ((BaseUnityPlugin)this).Config.Bind("05 - Colors", "ColorHeader", new Color(1f, 0.808f, 0f, 1f), new ConfigDescription("Recipe/group name text color.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); ColorHeader.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); UIMgr?.DestroyUI(); }; ColorEnoughInInventory = ((BaseUnityPlugin)this).Config.Bind("05 - Colors", "ColorEnoughInInventory", new Color(0f, 1f, 0f, 1f), new ConfigDescription("Material amount color when you have enough in your inventory.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); ColorEnoughInInventory.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; ColorEnoughWithChests = ((BaseUnityPlugin)this).Config.Bind("05 - Colors", "ColorEnoughWithChests", new Color(1f, 1f, 0f, 1f), new ConfigDescription("Material amount color when enough only if chests are counted.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); ColorEnoughWithChests.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; ColorMissing = ((BaseUnityPlugin)this).Config.Bind("05 - Colors", "ColorMissing", new Color(1f, 0.33f, 0.33f, 1f), new ConfigDescription("Material amount color when materials are missing.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 96 } })); ColorMissing.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; ColorCraftReady = ((BaseUnityPlugin)this).Config.Bind("05 - 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("05 - 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("06 - Pagination", "ColorPaginationActive", new Color(1f, 0.717f, 0.368f, 1f), new ConfigDescription("Active page indicator dot color.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); ColorPaginationActive.SettingChanged += delegate { UIMgr?.UpdateUI(isVisible: true); }; PaginationInactiveOpacity = ((BaseUnityPlugin)this).Config.Bind("06 - Pagination", "PaginationInactiveOpacity", 0.3f, new ConfigDescription("Opacity of inactive page indicator dots.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 1f), new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); PaginationInactiveOpacity.SettingChanged += delegate { UIMgr?.UpdateUI(isVisible: true); }; PaginationDotSize = ((BaseUnityPlugin)this).Config.Bind("06 - Pagination", "PaginationDotSize", 10, new ConfigDescription("Size of the pagination dot squares.", (AcceptableValueBase)(object)new AcceptableValueRange(5, 20), new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); PaginationDotSize.SettingChanged += delegate { UIMgr?.UpdateUI(isVisible: true); }; PaginationDotSpacing = ((BaseUnityPlugin)this).Config.Bind("06 - Pagination", "PaginationDotSpacing", 8, new ConfigDescription("Space between pagination squares.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 20), new object[1] { new ConfigurationManagerAttributes { Order = 96 } })); PaginationDotSpacing.SettingChanged += delegate { UIMgr?.UpdateUI(isVisible: true); }; EnableGatheringList = ((BaseUnityPlugin)this).Config.Bind("07 - Gathering List", "EnableGatheringList", true, new ConfigDescription("Enable the Gathering List (aggregated material overview).", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); EnableGatheringList.SettingChanged += delegate { UIMgr?.DestroyUI(); }; AutoOpenGatheringList = ((BaseUnityPlugin)this).Config.Bind("07 - Gathering List", "AutoOpenGatheringList", true, new ConfigDescription("Automatically open the Gathering List when 2+ recipes are pinned.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); GatheringListColumns = ((BaseUnityPlugin)this).Config.Bind("07 - Gathering List", "GatheringListColumns", 4, new ConfigDescription("Number of columns in the Gathering List grid (horizontal modes only). Panel width adjusts proportionally.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 10), new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); GatheringListColumns.SettingChanged += delegate { UIMgr?.DestroyUI(); }; GatheringListFontSizeTitle = ((BaseUnityPlugin)this).Config.Bind("07 - Gathering List", "GatheringListFontSizeTitle", 20, new ConfigDescription("Font size for the Gathering List title.", (AcceptableValueBase)(object)new AcceptableValueRange(8, 40), new object[1] { new ConfigurationManagerAttributes { Order = 96 } })); GatheringListFontSizeTitle.SettingChanged += delegate { UIMgr?.DestroyUI(); }; GatheringListFontSizeMaterials = ((BaseUnityPlugin)this).Config.Bind("07 - Gathering List", "GatheringListFontSizeMaterials", 15, new ConfigDescription("Font size for material amounts in the Gathering List.", (AcceptableValueBase)(object)new AcceptableValueRange(8, 40), new object[1] { new ConfigurationManagerAttributes { Order = 95 } })); GatheringListFontSizeMaterials.SettingChanged += delegate { UIMgr?.DestroyUI(); }; ContainerGatheringListPosition = ((BaseUnityPlugin)this).Config.Bind("07 - Gathering List", "ContainerGatheringListPosition", new Vector2(-400f, 320f), new ConfigDescription("Gathering List position offset (X, Y) when a container (chest/inventory) is open.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 94 } })); GroupCompactThreshold = ((BaseUnityPlugin)this).Config.Bind("08 - Groups", "GroupCompactThreshold", 4, new ConfigDescription("Number of unique materials above which a group pin switches to compact (grid) layout. Default: 4 (triggers at 5+).", (AcceptableValueBase)(object)new AcceptableValueRange(1, 10), new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); GroupCompactThreshold.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; GroupCompactMaxRows = ((BaseUnityPlugin)this).Config.Bind("08 - Groups", "GroupCompactMaxRows", 3, new ConfigDescription("Number of grid rows a compact group pin shows before collapsing the rest into a \"+N\" cell. Default: 3.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 10), new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); GroupCompactMaxRows.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; GroupIconFontSize = ((BaseUnityPlugin)this).Config.Bind("08 - Groups", "GroupIconFontSize", 16, new ConfigDescription("Font size of the member count number displayed on the group icon.", (AcceptableValueBase)(object)new AcceptableValueRange(8, 32), new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); GroupIconFontSize.SettingChanged += delegate { UIMgr?.DestroyUI(); }; MyPinsPanelWidth = ((BaseUnityPlugin)this).Config.Bind("09 - My Pins Panel", "PanelWidth", 375f, new ConfigDescription("Width of the My Pins panel.", (AcceptableValueBase)(object)new AcceptableValueRange(200f, 600f), new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); MyPinsPanelWidth.SettingChanged += delegate { UIMgr?.DestroyMyPinsUI(); }; MyPinsPanelHeight = ((BaseUnityPlugin)this).Config.Bind("09 - My Pins Panel", "PanelHeight", 480f, new ConfigDescription("Height of the My Pins panel.", (AcceptableValueBase)(object)new AcceptableValueRange(200f, 800f), new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); MyPinsPanelHeight.SettingChanged += delegate { UIMgr?.DestroyMyPinsUI(); }; MyPinsPanelPosition = ((BaseUnityPlugin)this).Config.Bind("09 - My Pins Panel", "PanelPosition", Vector2.zero, new ConfigDescription("Position offset (X, Y) of the My Pins panel from the screen center. (0, 0) = perfectly centered.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); MyPinsPanelPosition.SettingChanged += delegate { UIMgr?.DestroyMyPinsUI(); }; ButtonTextColor = ((BaseUnityPlugin)this).Config.Bind("09 - My Pins Panel", "ButtonIconColor", new Color(1f, 0.631f, 0.239f, 1f), new ConfigDescription("Tint color of the pin icon on the My Pins button (default: #ffa13d).", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 96 } })); ButtonTextColor.SettingChanged += delegate { UIMgr?.DestroyMyPinsUI(); }; MyPinsButtonPosition = ((BaseUnityPlugin)this).Config.Bind("09 - My Pins Panel", "MyPinsButtonPosition", new Vector2(-500f, 570f), new ConfigDescription("Position offset (X, Y) of the My Pins button from the inventory.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 95 } })); MyPinsButtonPosition.SettingChanged += delegate { UIMgr?.DestroyMyPinsUI(); }; MyPinsButtonSize = ((BaseUnityPlugin)this).Config.Bind("09 - My Pins Panel", "MyPinsButtonSize", 40, new ConfigDescription("Size of the My Pins icon button in pixels.", (AcceptableValueBase)(object)new AcceptableValueRange(20, 200), new object[1] { new ConfigurationManagerAttributes { Order = 94 } })); MyPinsButtonSize.SettingChanged += delegate { UIMgr?.DestroyMyPinsUI(); }; VerticalListWidth = ((BaseUnityPlugin)this).Config.Bind("10 - Layout (Vertical Mode)", "ListWidth", 265f, new ConfigDescription("Width of the pin list panel.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); VerticalPinSpacing = ((BaseUnityPlugin)this).Config.Bind("10 - Layout (Vertical Mode)", "PinSpacing", 10f, new ConfigDescription("Vertical spacing between pin cards.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); VerticalPosition = ((BaseUnityPlugin)this).Config.Bind("10 - Layout (Vertical Mode)", "Position", new Vector2(-40f, -250f), new ConfigDescription("Anchor position offset (X, Y).", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); HorizontalColumnWidth = ((BaseUnityPlugin)this).Config.Bind("11 - Layout (Horizontal - Map Side)", "ColumnWidth", 265f, new ConfigDescription("Width of each pin column.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); HorizontalPinSpacing = ((BaseUnityPlugin)this).Config.Bind("11 - Layout (Horizontal - Map Side)", "PinSpacing", 10f, new ConfigDescription("Spacing between pin cards in the column.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); HorizontalPosition = ((BaseUnityPlugin)this).Config.Bind("11 - Layout (Horizontal - Map Side)", "Position", new Vector2(-250f, -40f), new ConfigDescription("Anchor position offset (X, Y).", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); BottomRightColumnWidth = ((BaseUnityPlugin)this).Config.Bind("12 - Layout (Horizontal - Bottom Right)", "ColumnWidth", 265f, new ConfigDescription("Width of each pin column.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); BottomRightPinSpacing = ((BaseUnityPlugin)this).Config.Bind("12 - Layout (Horizontal - Bottom Right)", "PinSpacing", 10f, new ConfigDescription("Spacing between pin cards in the column.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); BottomRightPosition = ((BaseUnityPlugin)this).Config.Bind("12 - Layout (Horizontal - Bottom Right)", "Position", new Vector2(-40f, 40f), new ConfigDescription("Anchor position offset (X, Y).", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); EnableDebugLogging = ((BaseUnityPlugin)this).Config.Bind("13 - Debug", "EnableDebugLogging", false, new ConfigDescription("Enable verbose debug logging to the BepInEx console.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); DebugLogger.Log("Config loaded"); } } public static class ReflectionHelper { private static Func _getGuiScale; private static Func _getRecipeListRoot; private static Func _getAvailableRecipes; private static Func _getCurrentContainer; private static Func _getCraftRecipe; private static Func _getCraftUpgradeItem; private static Func _getHoveredPiece; private static FieldInfo[] _blockingInventoryPanelFields; public static Func CheckContainerAccess; 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>(Expression.Field(null, fieldInfo), Array.Empty()).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>(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>(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>(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>(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>(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>(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>(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"); } } 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 bool IsBlockingInventoryPanelOpen(InventoryGui gui) { if ((Object)(object)gui == (Object)null) { return false; } FieldInfo[] blockingInventoryPanelFields = GetBlockingInventoryPanelFields(); for (int i = 0; i < blockingInventoryPanelFields.Length; i++) { if (IsActiveUnityObject(blockingInventoryPanelFields[i].GetValue(gui))) { return true; } } return false; } private static FieldInfo[] GetBlockingInventoryPanelFields() { if (_blockingInventoryPanelFields != null) { return _blockingInventoryPanelFields; } List list = new List(); FieldInfo[] fields = typeof(InventoryGui).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { string text = fieldInfo.Name.ToLowerInvariant(); if ((text.Contains("troph") || text.Contains("skill") || text.Contains("compendium") || text.Contains("texts")) && (typeof(GameObject).IsAssignableFrom(fieldInfo.FieldType) || typeof(Component).IsAssignableFrom(fieldInfo.FieldType))) { list.Add(fieldInfo); } } _blockingInventoryPanelFields = list.ToArray(); return _blockingInventoryPanelFields; } private static bool IsActiveUnityObject(object value) { GameObject val = (GameObject)((value is GameObject) ? value : null); if (val != null) { return val.activeInHierarchy; } Component val2 = (Component)((value is Component) ? value : null); if (val2 != null) { if ((Object)(object)val2.gameObject != (Object)null) { return val2.gameObject.activeInHierarchy; } return false; } return false; } } public static class InputHelper { public static bool IsInputBlocked() { if (GroupNameDialog.IsDialogOpen) { return true; } if (ConfirmDialog.IsDialogOpen) { return true; } if (Console.IsVisible()) { return true; } if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return true; } if (TextInput.IsVisible()) { return true; } return false; } public static bool IsMouseOverRect(RectTransform rect, bool logHit = true) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)rect == (Object)null) { DebugLogger.Verbose("IsMouseOverRect: rect is null"); return false; } bool num = RectTransformUtility.RectangleContainsScreenPoint(rect, Vector2.op_Implicit(Input.mousePosition)); if (num && logHit) { DebugLogger.Verbose("Mouse over rect: " + ((Object)((Component)rect).gameObject).name); } return num; } } public class GatheringItemUI : MonoBehaviour { public Image Icon; public Text AmountText; public void SetActive(bool active) { ((Component)this).gameObject.SetActive(active); } } public class GatheringListUI : MonoBehaviour { public RectTransform PanelRect; public Image BgImage; public Text TitleText; public Transform ItemListRoot; public Text HintText; public List ItemSlots = new List(); 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; } private IEnumerator FixLayout() { yield return null; if ((Object)(object)ItemListRoot != (Object)null) { Transform itemListRoot = ItemListRoot; LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)(object)((itemListRoot is RectTransform) ? itemListRoot : null)); } if ((Object)(object)PanelRect != (Object)null) { LayoutRebuilder.ForceRebuildLayoutImmediate(PanelRect); } } public void ApplyColumns(int configCols) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ItemListRoot == (Object)null) { return; } GridLayoutGroup component = ((Component)ItemListRoot).GetComponent(); if (!((Object)(object)component == (Object)null)) { VerticalLayoutGroup component2 = ((Component)this).GetComponent(); float num = (((Object)(object)component2 != (Object)null) ? ((float)(((LayoutGroup)component2).padding.left + ((LayoutGroup)component2).padding.right)) : 22f); float x = component.cellSize.x; float x2 = component.spacing.x; if (configCols > 0) { component.constraintCount = configCols; float num2 = (float)configCols * (x + x2) - x2 + num; PanelRect.sizeDelta = new Vector2(num2, PanelRect.sizeDelta.y); } else { float num3 = PanelRect.sizeDelta.x - num; int constraintCount = Mathf.Max(1, Mathf.FloorToInt((num3 + x2) / (x + x2))); component.constraintCount = constraintCount; } } } public static float CalculateWidthForColumns(int cols) { float num = 58f; float num2 = 4f; float num3 = 22f; return (float)cols * (num + num2) - num2 + num3; } } 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 DividerColor = new Color(1f, 1f, 1f, 0.1f); private static Sprite _cachedUiSprite; private static bool _spriteSearchDone = false; private static Font _cachedNorseFont; private static Sprite _cachedPinIcon; private static bool _pinIconLoadAttempted = false; private static bool _vanillaBtnCached = false; private static Sprite _vanillaBtnSprite; private static Material _vanillaBtnMaterial; private static ColorBlock _vanillaBtnColors; private static Font _vanillaBtnFont; private static int _vanillaBtnFontSize; private static FontStyle _vanillaBtnFontStyle; private static Color _vanillaBtnTextColor; private static bool _vanillaBtnHasOutline; private static Color _vanillaBtnOutlineColor; private static Vector2 _vanillaBtnOutlineDistance; private static Transition _vanillaBtnTransition; private static SpriteState _vanillaBtnSpriteState; private static AnimationTriggers _vanillaBtnAnimTriggers; private static GameObject _cachedButtonSfxPrefab; private static Color ValheimOrange => (Color)(((??)RecipePinnerPlugin.ButtonTextColor?.Value) ?? new Color(1f, 0.631f, 0.239f, 1f)); private static Sprite LoadPinIconSprite() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) if (_pinIconLoadAttempted) { return _cachedPinIcon; } _pinIconLoadAttempted = true; try { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("RecipePinner.pinIcon.png"); if (stream == null) { DebugLogger.Warning("pin.png embedded resource not found"); return null; } byte[] array; using (MemoryStream memoryStream = new MemoryStream()) { stream.CopyTo(memoryStream); array = memoryStream.ToArray(); } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); ((Texture)val).filterMode = (FilterMode)1; bool flag = false; MethodInfo method = typeof(Texture2D).GetMethod("LoadImage", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(byte[]) }, null); if (method != null) { flag = (bool)(method.Invoke(val, new object[1] { array }) ?? ((object)false)); } else { Type type = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule"); if (type != null) { MethodInfo method2 = type.GetMethod("LoadImage", new Type[2] { typeof(Texture2D), typeof(byte[]) }); if (method2 != null) { flag = (bool)(method2.Invoke(null, new object[2] { val, array }) ?? ((object)false)); } } } if (!flag) { DebugLogger.Warning("pin.png: LoadImage failed (reflection)"); return null; } _cachedPinIcon = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f); DebugLogger.Log($"pin.png loaded: {((Texture)val).width}x{((Texture)val).height}"); return _cachedPinIcon; } catch (Exception ex) { DebugLogger.Warning("pin.png load error: " + ex.Message); return null; } } private static bool TryGetTrophiesPanelBackground(out Sprite sprite, out Material material) { sprite = null; material = null; if ((Object)(object)InventoryGui.instance == (Object)null || (Object)(object)InventoryGui.instance.m_trophiesPanel == (Object)null) { DebugLogger.Warning("TryGetTrophiesPanelBackground: InventoryGui or m_trophiesPanel is null"); return false; } GameObject trophiesPanel = InventoryGui.instance.m_trophiesPanel; Image component = trophiesPanel.GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.sprite != (Object)null) { sprite = component.sprite; material = ((Graphic)component).material; DebugLogger.Verbose("TryGetTrophiesPanelBackground: Found on root - sprite=" + ((Object)sprite).name + ", material=" + (((Object)(object)material != (Object)null) ? ((Object)material).name : "null")); return true; } Transform val = trophiesPanel.transform.Find("background") ?? trophiesPanel.transform.Find("Background") ?? trophiesPanel.transform.Find("bg"); if ((Object)(object)val != (Object)null) { Image component2 = ((Component)val).GetComponent(); if ((Object)(object)component2 != (Object)null && (Object)(object)component2.sprite != (Object)null) { sprite = component2.sprite; material = ((Graphic)component2).material; DebugLogger.Verbose("TryGetTrophiesPanelBackground: Found on child '" + ((Object)val).name + "' - sprite=" + ((Object)sprite).name); return true; } } Image[] componentsInChildren = trophiesPanel.GetComponentsInChildren(true); foreach (Image val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null && (Object)(object)val2.sprite != (Object)null && (Object)(object)((Component)val2).gameObject != (Object)(object)trophiesPanel) { sprite = val2.sprite; material = ((Graphic)val2).material; DebugLogger.Verbose("TryGetTrophiesPanelBackground: Found on child '" + ((Object)((Component)val2).gameObject).name + "' via iteration"); return true; } } DebugLogger.Warning("TryGetTrophiesPanelBackground: No suitable background found"); return false; } private static bool TryGetVanillaInputFieldStyle(out Sprite sprite, out Material material, out Color color, out Selectable source) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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) sprite = null; material = null; color = Color.white; source = null; TextInput instance = TextInput.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_inputField == (Object)null) { DebugLogger.Verbose("TryGetVanillaInputFieldStyle: TextInput or m_inputField is null"); return false; } Graphic targetGraphic = ((Selectable)instance.m_inputField).targetGraphic; Image val = (Image)(object)((targetGraphic is Image) ? targetGraphic : null); if ((Object)(object)val == (Object)null) { val = ((Component)instance.m_inputField).GetComponent(); } if ((Object)(object)val == (Object)null || (Object)(object)val.sprite == (Object)null) { DebugLogger.Verbose("TryGetVanillaInputFieldStyle: no background Image with a sprite"); return false; } sprite = val.sprite; material = ((Graphic)val).material; color = ((Graphic)val).color; source = (Selectable)(object)instance.m_inputField; DebugLogger.Verbose("TryGetVanillaInputFieldStyle: sprite=" + ((Object)sprite).name + ", material=" + (((Object)(object)material != (Object)null) ? ((Object)material).name : "null")); return true; } private static Sprite GetBackgroundSprite() { if ((Object)(object)_cachedUiSprite != (Object)null) { return _cachedUiSprite; } if (_spriteSearchDone) { return null; } Sprite[] array = Resources.FindObjectsOfTypeAll(); 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 Sprite GetUISpritePublic() { return GetBackgroundSprite(); } public static void PlayButtonSFX() { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_cachedButtonSfxPrefab == (Object)null) { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance != (Object)null) { EffectList setActiveGroupEffects = instance.m_setActiveGroupEffects; if (setActiveGroupEffects != null && setActiveGroupEffects.m_effectPrefabs?.Length > 0) { _cachedButtonSfxPrefab = instance.m_setActiveGroupEffects.m_effectPrefabs[0].m_prefab; if ((Object)(object)_cachedButtonSfxPrefab != (Object)null) { DebugLogger.Log("Cached button SFX prefab: " + ((Object)_cachedButtonSfxPrefab).name); } } } } if ((Object)(object)_cachedButtonSfxPrefab != (Object)null) { Player localPlayer = Player.m_localPlayer; Vector3 val = (((Object)(object)localPlayer != (Object)null) ? ((Component)localPlayer).transform.position : Vector3.zero); Object.Instantiate(_cachedButtonSfxPrefab, val, Quaternion.identity); } } private static void CacheVanillaButtonStyle() { //IL_0091: 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_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) if (_vanillaBtnCached) { return; } _vanillaBtnCached = true; if ((Object)(object)InventoryGui.instance == (Object)null) { return; } FieldInfo fieldInfo = AccessTools.Field(typeof(InventoryGui), "m_craftButton"); if (fieldInfo == null) { DebugLogger.Warning("Vanilla button style: m_craftButton field not found"); return; } object? value = fieldInfo.GetValue(InventoryGui.instance); Button val = (Button)((value is Button) ? value : null); if ((Object)(object)val == (Object)null) { DebugLogger.Warning("Vanilla button style: m_craftButton value is null"); return; } Image component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { _vanillaBtnSprite = component.sprite; _vanillaBtnMaterial = ((Graphic)component).material; } _vanillaBtnColors = ((Selectable)val).colors; Text componentInChildren = ((Component)val).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { _vanillaBtnFont = componentInChildren.font; _vanillaBtnFontSize = componentInChildren.fontSize; _vanillaBtnFontStyle = componentInChildren.fontStyle; _vanillaBtnTextColor = ((Graphic)componentInChildren).color; } Outline componentInChildren2 = ((Component)val).GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { _vanillaBtnHasOutline = true; _vanillaBtnOutlineColor = ((Shadow)componentInChildren2).effectColor; _vanillaBtnOutlineDistance = ((Shadow)componentInChildren2).effectDistance; } _vanillaBtnTransition = ((Selectable)val).transition; _vanillaBtnSpriteState = ((Selectable)val).spriteState; _vanillaBtnAnimTriggers = ((Selectable)val).animationTriggers; Sprite vanillaBtnSprite = _vanillaBtnSprite; string arg = ((vanillaBtnSprite != null) ? ((Object)vanillaBtnSprite).name : null); Font vanillaBtnFont = _vanillaBtnFont; DebugLogger.Log($"Vanilla button style cached (sprite={arg}, font={((vanillaBtnFont != null) ? ((Object)vanillaBtnFont).name : null)}, transition={_vanillaBtnTransition})"); } public static Button CreateVanillaButton(Transform parent, string label, float minWidth = -1f, float minHeight = 35f) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Invalid comparison between Unknown and I4 //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Invalid comparison between Unknown and I4 //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Expected O, but got Unknown //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_0302: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_037b: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Expected O, but got Unknown CacheVanillaButtonStyle(); GameObject val = new GameObject("Btn_" + label, new Type[1] { typeof(RectTransform) }) { layer = 5 }; val.transform.SetParent(parent, false); Image val2 = val.AddComponent(); if ((Object)(object)_vanillaBtnSprite != (Object)null) { val2.sprite = _vanillaBtnSprite; ((Graphic)val2).material = _vanillaBtnMaterial; val2.type = (Type)((_vanillaBtnSprite.border != Vector4.zero) ? 1 : 0); ((Graphic)val2).color = Color.white; } else { Sprite val3 = (val2.sprite = GetBackgroundSprite()); if ((Object)(object)val3 != (Object)null && val3.border != Vector4.zero) { val2.type = (Type)1; } ((Graphic)val2).color = new Color(0.2f, 0.2f, 0.2f, 0.9f); } ((Graphic)val2).raycastTarget = true; Button val4 = val.AddComponent