Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of RecipePinner v1.1.4
plugins/RecipePinner.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("PinRecipe")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("PinRecipe")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("b7fff297-caca-412c-8cb0-52556a76bd3f")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace ValheimRecipePinner; public class ContainerScanner { public static List<Container> AllContainers = new List<Container>(); private static readonly HashSet<Container> _containerSet = new HashSet<Container>(); internal static readonly object ContainerLock = new object(); public Dictionary<string, int> ContainerCache = new Dictionary<string, int>(); private static readonly HashSet<int> _processedIDs = new HashSet<int>(); private readonly List<Container> _snapshotBuffer = new List<Container>(); private Vector3 _lastScanPos; private int _lastItemCount = 0; private float _scanTimer = 0f; private float _moveScanCooldown = 0f; private const float MovementThresholdSqr = 4f; private const float MinMoveScanCooldown = 1f; public void InitializeContainers() { DebugLogger.Log("Initializing container tracking..."); lock (ContainerLock) { if (AllContainers.Count != 0) { return; } Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0); Container[] array2 = array; foreach (Container val in array2) { if ((Object)(object)val != (Object)null && _containerSet.Add(val)) { AllContainers.Add(val); if ((Object)(object)((Component)val).GetComponent<ContainerTracker>() == (Object)null) { ContainerTracker containerTracker = ((Component)val).gameObject.AddComponent<ContainerTracker>(); containerTracker.MyContainer = val; } } } DebugLogger.Log($"Initialized tracking for {AllContainers.Count} existing containers"); } } public void UpdateScanning() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } _scanTimer += Time.deltaTime; _moveScanCooldown += Time.deltaTime; float num = Vector3.SqrMagnitude(((Component)Player.m_localPlayer).transform.position - _lastScanPos); bool flag = num > 4f && _moveScanCooldown >= 1f; int num2 = 0; foreach (ItemData allItem in ((Humanoid)Player.m_localPlayer).GetInventory().GetAllItems()) { num2 += allItem.m_stack; } bool flag2 = num2 != _lastItemCount; bool flag3 = false; if ((Object)(object)InventoryGui.instance != (Object)null) { flag3 = (Object)(object)ReflectionHelper.GetCurrentContainer(InventoryGui.instance) != (Object)null; } float num3 = (flag3 ? 0.5f : RecipePinnerPlugin.ChestScanInterval.Value); bool flag4 = _scanTimer >= num3; if (flag || flag2 || flag4) { DebugLogger.Verbose($"Scanning containers - Moved: {flag}, InvChanged: {flag2}, Interval: {flag4}"); _scanTimer = 0f; if (flag) { _moveScanCooldown = 0f; } _lastItemCount = num2; UpdateContainerCache(); } } private void UpdateContainerCache() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) ContainerCache.Clear(); if ((Object)(object)Player.m_localPlayer == (Object)null) { DebugLogger.Verbose("Cannot scan - player is null"); return; } Vector3 position = ((Component)Player.m_localPlayer).transform.position; float value = RecipePinnerPlugin.ChestScanRange.Value; float num = value * value; _snapshotBuffer.Clear(); lock (ContainerLock) { _snapshotBuffer.AddRange(AllContainers); } _processedIDs.Clear(); int num2 = 0; int num3 = 0; int num4 = 0; foreach (Container item in _snapshotBuffer) { if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).transform == (Object)null) { num3++; continue; } int instanceID = ((Object)item).GetInstanceID(); if (!_processedIDs.Add(instanceID)) { num3++; continue; } float num5 = Vector3.SqrMagnitude(((Component)item).transform.position - position); if (num5 > num) { num3++; continue; } bool flag = true; if (ReflectionHelper.CheckContainerAccess != null) { flag = ReflectionHelper.CheckContainerAccess(item, Player.m_localPlayer.GetPlayerID()); } if (!flag) { num4++; continue; } Inventory inventory = item.GetInventory(); if (inventory == null) { continue; } foreach (ItemData allItem in inventory.GetAllItems()) { string name = allItem.m_shared.m_name; if (ContainerCache.TryGetValue(name, out var value2)) { ContainerCache[name] = value2 + allItem.m_stack; } else { ContainerCache[name] = allItem.m_stack; } } num2++; } _lastScanPos = position; DebugLogger.Verbose($"Container scan complete - Scanned: {num2}, Skipped: {num3}, AccessDenied: {num4}, UniqueItems: {ContainerCache.Count}"); } [HarmonyPatch(typeof(Container), "Awake")] [HarmonyPostfix] public static void TrackContainerAwake(Container __instance) { if (!((Object)(object)__instance != (Object)null)) { return; } lock (ContainerLock) { if (_containerSet.Add(__instance)) { AllContainers.Add(__instance); ContainerTracker containerTracker = ((Component)__instance).gameObject.GetComponent<ContainerTracker>() ?? ((Component)__instance).gameObject.AddComponent<ContainerTracker>(); containerTracker.MyContainer = __instance; DebugLogger.Verbose($"New container tracked: {((Object)__instance).name} (Total: {AllContainers.Count})"); } } } public static void RemoveFromSet(Container c) { _containerSet.Remove(c); } } public class ContainerTracker : MonoBehaviour { public Container MyContainer; private void OnDestroy() { if (ContainerScanner.AllContainers != null && (Object)(object)MyContainer != (Object)null) { lock (ContainerScanner.ContainerLock) { ContainerScanner.AllContainers.Remove(MyContainer); ContainerScanner.RemoveFromSet(MyContainer); DebugLogger.Verbose($"Container removed: {((Object)MyContainer).name} (Remaining: {ContainerScanner.AllContainers.Count})"); } } } } public class DataPersistence { public void SavePins() { try { string savePath = GetSavePath(); if (string.IsNullOrEmpty(savePath)) { DebugLogger.Warning("Cannot save - save path is invalid"); return; } RecipeManager recipeMgr = RecipePinnerPlugin.Instance.RecipeMgr; List<string> list = new List<string>(); foreach (KeyValuePair<string, int> pinnedRecipe in recipeMgr.PinnedRecipes) { list.Add($"{pinnedRecipe.Key}:{pinnedRecipe.Value}"); } File.WriteAllLines(savePath, list); DebugLogger.Log($"Saved {list.Count} pinned recipes to: {savePath}"); } catch (Exception ex) { DebugLogger.Error("Failed to save pins", ex); } } public void LoadPins() { string savePath = GetSavePath(); if (string.IsNullOrEmpty(savePath)) { DebugLogger.Warning("Cannot load - save path is invalid"); return; } RecipeManager recipeMgr = RecipePinnerPlugin.Instance.RecipeMgr; if (!File.Exists(savePath)) { DebugLogger.Log("No save file found at: " + savePath); return; } try { recipeMgr.PinnedRecipes.Clear(); string[] array = File.ReadAllLines(savePath); int num = 0; int num2 = 0; string[] array2 = array; foreach (string text in array2) { if (string.IsNullOrWhiteSpace(text)) { continue; } int num3 = text.LastIndexOf(':'); if (num3 > 0 && num3 < text.Length - 1) { string key = text.Substring(0, num3).Trim(); string s = text.Substring(num3 + 1).Trim(); if (int.TryParse(s, out var result)) { recipeMgr.PinnedRecipes[key] = result; num++; } else { DebugLogger.Warning("Invalid count value in save file: " + text); num2++; } } else if (!recipeMgr.PinnedRecipes.ContainsKey(text)) { recipeMgr.PinnedRecipes[text] = 1; num++; } } if (recipeMgr.PinnedRecipes.Count > RecipePinnerPlugin.MaximumPins.Value) { int count = recipeMgr.PinnedRecipes.Count; Dictionary<string, int> dictionary = new Dictionary<string, int>(); int num4 = 0; foreach (KeyValuePair<string, int> pinnedRecipe in recipeMgr.PinnedRecipes) { if (num4 >= RecipePinnerPlugin.MaximumPins.Value) { break; } dictionary[pinnedRecipe.Key] = pinnedRecipe.Value; num4++; } recipeMgr.PinnedRecipes = dictionary; DebugLogger.Warning($"Exceeded max pins limit - trimmed from {count} to {recipeMgr.PinnedRecipes.Count}"); } DebugLogger.Log($"Loaded {num} recipes from: {savePath} (Errors: {num2})"); } catch (Exception ex) { DebugLogger.Error("Failed to load pins", ex); } } private string GetSavePath() { if ((Object)(object)Player.m_localPlayer == (Object)null) { DebugLogger.Verbose("Cannot get save path - local player is null"); return null; } string playerName = Player.m_localPlayer.GetPlayerName(); if (string.IsNullOrWhiteSpace(playerName)) { DebugLogger.Warning("Cannot get save path - player name is empty"); return null; } string text = Path.Combine(Paths.ConfigPath, "RecipePinner_Data"); if (!Directory.Exists(text)) { try { Directory.CreateDirectory(text); DebugLogger.Log("Created save directory: " + text); } catch (Exception ex) { DebugLogger.Error("Failed to create save directory: " + text, ex); return null; } } string text2 = playerName; char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { text2 = text2.Replace(oldChar, '_'); } string text3 = Path.Combine(text, text2 + ".txt"); DebugLogger.Verbose("Save path: " + text3); return text3; } } public static class DebugLogger { private const string Prefix = "[RecipePinner]"; public static void Log(string message) { if (IsDebugEnabled()) { Debug.Log((object)("[RecipePinner] " + message)); } } public static void Warning(string message) { Debug.LogWarning((object)("[RecipePinner] " + message)); } public static void Error(string message) { Debug.LogError((object)("[RecipePinner] " + message)); } public static void Error(string message, Exception ex) { Debug.LogError((object)("[RecipePinner] " + message + "\nException: " + ex.Message + "\nStackTrace: " + ex.StackTrace)); } public static void Verbose(string message) { if (IsDebugEnabled()) { Debug.Log((object)("[RecipePinner] [VERBOSE] " + message)); } } private static bool IsDebugEnabled() { return (Object)(object)RecipePinnerPlugin.Instance != (Object)null && RecipePinnerPlugin.EnableDebugLogging != null && RecipePinnerPlugin.EnableDebugLogging.Value; } } public class LocalizationManager { private RecipePinnerPlugin _plugin; private Dictionary<string, string> _localizedText = new Dictionary<string, string>(); private static readonly Dictionary<string, string> _defaultEnglish = new Dictionary<string, string> { { "pinned", "Recipe Pinned!" }, { "unpinned", "Pin Removed" }, { "list_full", "List Full!" }, { "added_more", "Added More: {0}x" }, { "decreased", "Decreased: {0}x" }, { "cleared", "Pinned Recipes Cleared" }, { "max_level", "Max Level Reached" }, { "no_upgrade_cost", "No upgrade cost found" } }; public LocalizationManager(RecipePinnerPlugin plugin) { _plugin = plugin; DebugLogger.Log("LocalizationManager initialized"); } public void LoadTranslations() { _localizedText.Clear(); string text = RecipePinnerPlugin.LanguageOverride.Value.Trim(); if (string.IsNullOrEmpty(text) || text.ToLower() == "auto") { text = ((Localization.instance == null) ? "English" : Localization.instance.GetSelectedLanguage()); DebugLogger.Log("Auto-detected language: " + text); } else { DebugLogger.Log("Using forced language: " + text); } string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)_plugin).Info.Location); string text2 = Path.Combine(directoryName, "RecipePinner_languages", text + ".json"); if (!File.Exists(text2)) { DebugLogger.Log("Language file not found: " + text2 + " - Using default English"); return; } try { string text3 = File.ReadAllText(text2); int num = 0; string[] array = text3.Split(new char[1] { '\n' }); foreach (string text4 in array) { if (!text4.Contains(":")) { continue; } string[] array2 = text4.Split(new char[1] { ':' }, 2); if (array2.Length == 2) { string text5 = array2[0].Trim().Trim(',', '"', ' ', '\t', '\r'); string value = array2[1].Trim().Trim(',', '"', ' ', '\t', '\r'); if (!string.IsNullOrEmpty(text5) && !string.IsNullOrEmpty(value)) { _localizedText[text5] = value; num++; } } } DebugLogger.Log($"Loaded {num} translations from: {text}.json"); } catch (Exception ex) { DebugLogger.Error("Failed to load language file: " + text2, ex); } } public string GetText(string key) { if (_localizedText.TryGetValue(key, out var value)) { DebugLogger.Verbose("Translation found for '" + key + "': " + value); return value; } if (_defaultEnglish.TryGetValue(key, out var value2)) { DebugLogger.Verbose("Using default English for '" + key + "': " + value2); return value2; } DebugLogger.Warning("No translation found for key: " + key); return key; } } public class PinnedRecipeData { public Recipe RecipeRef; public string RawName; public string CachedHeader; public string CachedShadowHeader; public Sprite Icon; public int StackCount; public List<PinnedResData> Resources = new List<PinnedResData>(); public bool IsDirty = true; } public class PinnedResData { public string ItemName; public string CachedName; public string CachedShadowName; public Sprite Icon; public int RequiredAmount; public int LastKnownAmount; public int LastKnownInvAmount; public string CachedAmountString; } public class RecipeManager { public Dictionary<string, int> PinnedRecipes = new Dictionary<string, int>(); public List<PinnedRecipeData> CachedPins = new List<PinnedRecipeData>(); private readonly Dictionary<string, Recipe> _fakeRecipeCache = new Dictionary<string, Recipe>(); private static readonly Regex CleanNameRegex = new Regex("<.*?>", RegexOptions.Compiled); private static readonly Regex ShadowCleanRegex = new Regex("<color=.*?>|</color>", RegexOptions.Compiled); private static readonly Regex AmountSuffixRegex = new Regex("\\s*[xX]?\\s*\\d+$", RegexOptions.Compiled); private static readonly Regex UpgradeStarRegex = new Regex("\\s*★(\\d+)$", RegexOptions.Compiled); private static readonly Dictionary<Type, FieldInfo> _cachedRecipeFields = new Dictionary<Type, FieldInfo>(); private static readonly Dictionary<Type, PropertyInfo> _cachedRecipeProps = new Dictionary<Type, PropertyInfo>(); private static readonly Dictionary<Type, FieldInfo> _cachedItemFields = new Dictionary<Type, FieldInfo>(); private static readonly Dictionary<Type, PropertyInfo> _cachedItemProps = new Dictionary<Type, PropertyInfo>(); public void Cleanup() { DebugLogger.Log("RecipeManager cleanup started"); if (_fakeRecipeCache != null) { int count = _fakeRecipeCache.Count; foreach (Recipe value in _fakeRecipeCache.Values) { if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } } _fakeRecipeCache.Clear(); DebugLogger.Log($"Cleaned up {count} fake recipes"); } _cachedRecipeFields.Clear(); _cachedRecipeProps.Clear(); _cachedItemFields.Clear(); _cachedItemProps.Clear(); } public void RefreshRecipeCache() { DebugLogger.Verbose("Refreshing recipe cache..."); CachedPins.Clear(); if ((Object)(object)ObjectDB.instance == (Object)null) { DebugLogger.Warning("Cannot refresh recipe cache - ObjectDB.instance is null"); return; } int num = 0; int num2 = 0; foreach (KeyValuePair<string, int> pinnedRecipe in PinnedRecipes) { string key = pinnedRecipe.Key; int value = pinnedRecipe.Value; Recipe recipeByName = GetRecipeByName(key); if ((Object)(object)recipeByName != (Object)null) { PinnedRecipeData pinnedRecipeData = new PinnedRecipeData { IsDirty = true, RecipeRef = recipeByName, StackCount = value }; if ((Object)(object)recipeByName.m_item != (Object)null) { pinnedRecipeData.Icon = recipeByName.m_item.m_itemData.GetIcon(); pinnedRecipeData.RawName = recipeByName.m_item.m_itemData.m_shared.m_name; } else { ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab(((Object)recipeByName).name) : null); if ((Object)(object)val != (Object)null) { Piece component = val.GetComponent<Piece>(); if ((Object)(object)component != (Object)null) { pinnedRecipeData.Icon = component.m_icon; pinnedRecipeData.RawName = component.m_name; } } } if (string.IsNullOrEmpty(pinnedRecipeData.RawName)) { pinnedRecipeData.RawName = ((Object)recipeByName).name; } string text = pinnedRecipeData.RawName; if (Localization.instance != null) { Match match = UpgradeStarRegex.Match(key); if (match.Success) { string text2 = pinnedRecipeData.RawName; if (Localization.instance != null) { text2 = Localization.instance.Localize(pinnedRecipeData.RawName); } text = text2 + match.Value; } else { text = Localization.instance.Localize(pinnedRecipeData.RawName); } } text = text.Replace("\r", "").Replace("\n", ""); if (recipeByName.m_amount > 1) { text += $" (x{recipeByName.m_amount})"; } if (value > 1) { text = $"{value}x {text}"; } pinnedRecipeData.CachedHeader = text; pinnedRecipeData.CachedShadowHeader = text; Requirement[] resources = recipeByName.m_resources; foreach (Requirement val2 in resources) { if (val2 != null && !((Object)(object)val2.m_resItem == (Object)null) && val2.m_amount > 0) { PinnedResData pinnedResData = new PinnedResData { ItemName = val2.m_resItem.m_itemData.m_shared.m_name, Icon = val2.m_resItem.m_itemData.GetIcon(), RequiredAmount = val2.m_amount * value, LastKnownAmount = -1, LastKnownInvAmount = -1 }; string text3 = pinnedResData.ItemName; if (Localization.instance != null) { text3 = Localization.instance.Localize(pinnedResData.ItemName); } text3 = (pinnedResData.CachedName = text3.Replace("\r", "").Replace("\n", "")); pinnedResData.CachedShadowName = ShadowCleanRegex.Replace(text3, string.Empty); pinnedRecipeData.Resources.Add(pinnedResData); } } CachedPins.Add(pinnedRecipeData); num++; } else { DebugLogger.Warning("Recipe not found: " + key); num2++; } } DebugLogger.Log($"Recipe cache refreshed: {num} successful, {num2} failed"); if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)RecipePinnerPlugin.Instance != (Object)null) { RecipePinnerPlugin.Instance.UIMgr.UpdateUI(isVisible: true); } } public Recipe GetRecipeByName(string name) { if ((Object)(object)ObjectDB.instance == (Object)null) { return null; } if (_fakeRecipeCache.TryGetValue(name, out var value)) { DebugLogger.Verbose("Found cached fake recipe: " + name); return value; } Match match = UpgradeStarRegex.Match(name); if (match.Success) { string name2 = name.Substring(0, match.Index).Trim(); int targetLevel = int.Parse(match.Groups[1].Value); Recipe recipeByName = GetRecipeByName(name2); if ((Object)(object)recipeByName != (Object)null) { Recipe val = CreateFakeUpgradeRecipe(recipeByName, targetLevel, name); if ((Object)(object)val != (Object)null) { return val; } } } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(name); ItemDrop val2 = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null); if ((Object)(object)val2 != (Object)null) { Recipe recipe = ObjectDB.instance.GetRecipe(val2.m_itemData); if ((Object)(object)recipe != (Object)null) { DebugLogger.Verbose("Found standard recipe: " + name); return recipe; } } Recipe val3 = null; foreach (Recipe recipe2 in ObjectDB.instance.m_recipes) { if (((Object)recipe2).name == name) { val3 = recipe2; break; } } if ((Object)(object)val3 != (Object)null) { DebugLogger.Verbose("Found recipe in ObjectDB: " + name); return val3; } ZNetScene instance = ZNetScene.instance; GameObject val4 = ((instance != null) ? instance.GetPrefab(name) : null); if ((Object)(object)val4 != (Object)null) { Piece component = val4.GetComponent<Piece>(); if ((Object)(object)component != (Object)null && component.m_resources != null && component.m_resources.Length != 0) { Recipe val5 = ScriptableObject.CreateInstance<Recipe>(); ((Object)val5).hideFlags = (HideFlags)61; ((Object)val5).name = name; val5.m_item = val4.GetComponent<ItemDrop>(); val5.m_resources = (Requirement[])component.m_resources.Clone(); _fakeRecipeCache[name] = val5; DebugLogger.Verbose("Created fake recipe for piece: " + name); return val5; } } DebugLogger.Warning("Recipe not found anywhere: " + name); return null; } private Recipe CreateFakeUpgradeRecipe(Recipe baseRecipe, int targetLevel, string customName) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown if ((Object)(object)baseRecipe == (Object)null) { return null; } Recipe val = ScriptableObject.CreateInstance<Recipe>(); ((Object)val).hideFlags = (HideFlags)61; ((Object)val).name = customName; val.m_item = baseRecipe.m_item; val.m_amount = 1; int num = Mathf.Max(1, targetLevel - 1); List<Requirement> list = new List<Requirement>(); Requirement[] resources = baseRecipe.m_resources; foreach (Requirement val2 in resources) { if (val2.m_amountPerLevel > 0) { Requirement item = new Requirement { m_resItem = val2.m_resItem, m_amount = val2.m_amountPerLevel * num, m_amountPerLevel = 0, m_recover = val2.m_recover }; list.Add(item); } } if (list.Count == 0) { Object.Destroy((Object)(object)val); return null; } val.m_resources = list.ToArray(); _fakeRecipeCache[customName] = val; DebugLogger.Verbose("Created fake upgrade recipe: " + customName); return val; } public void ValidateAndCleanPins() { if ((Object)(object)ObjectDB.instance == (Object)null) { DebugLogger.Warning("Cannot validate pins - ObjectDB.instance is null"); return; } DebugLogger.Log("Validating pinned recipes..."); List<string> list = new List<string>(); foreach (string key in PinnedRecipes.Keys) { if ((Object)(object)GetRecipeByName(key) == (Object)null) { list.Add(key); } } if (list.Count > 0) { foreach (string item in list) { PinnedRecipes.Remove(item); DebugLogger.Warning("Removed invalid recipe: " + item); } RecipePinnerPlugin.Instance?.DataMgr.SavePins(); DebugLogger.Log($"Validation complete: {list.Count} invalid recipes removed"); } else { DebugLogger.Log("All pinned recipes are valid"); } } public void TryPinHoveredRecipe(InventoryGui gui) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown DebugLogger.Verbose("Attempting to pin hovered recipe..."); Transform recipeListRoot = ReflectionHelper.GetRecipeListRoot(gui); if (!(ReflectionHelper.GetAvailableRecipes(gui) is IList list) || (Object)(object)recipeListRoot == (Object)null) { DebugLogger.Verbose("Cannot pin - listRoot or availableRecipes is null"); return; } ScrollRect componentInParent = ((Component)recipeListRoot).GetComponentInParent<ScrollRect>(); bool flag = !((Selectable)gui.m_tabUpgrade).interactable; foreach (Transform item in recipeListRoot) { Transform val = item; if (!((Component)val).gameObject.activeInHierarchy) { continue; } RectTransform val2 = (RectTransform)(object)((val is RectTransform) ? val : null); if ((Object)(object)val2 == (Object)null || !IsVisibleInScroll(val2, componentInParent) || !InputHelper.IsMouseOverRect(val2)) { continue; } string text = ExtractTextFromUI(val); if (string.IsNullOrEmpty(text)) { continue; } string text2 = CleanNameRegex.Replace(text, string.Empty).Trim(); text2 = text2.Replace("\r", "").Replace("\n", ""); string text3 = AmountSuffixRegex.Replace(text2, "").Trim(); DebugLogger.Verbose($"Hovered: '{text3}' (UpgradeTab: {flag})"); foreach (object item2 in list) { Recipe recipeFromObject = GetRecipeFromObject(item2); if (!((Object)(object)recipeFromObject != (Object)null)) { continue; } string rawRecipeName = GetRawRecipeName(recipeFromObject); if (string.IsNullOrEmpty(rawRecipeName)) { continue; } string text4 = rawRecipeName; if (Localization.instance != null) { text4 = Localization.instance.Localize(rawRecipeName); } text4 = text4.Replace("\r", "").Replace("\n", ""); if (!text4.Equals(text3, StringComparison.OrdinalIgnoreCase) && !text4.Equals(text2, StringComparison.OrdinalIgnoreCase)) { continue; } if (flag) { ItemData val3 = GetItemDataFromObject(item2) ?? ReflectionHelper.GetCraftUpgradeItem(gui); if (val3 != null) { int quality = val3.m_quality; int num = quality + 1; int maxQuality = val3.m_shared.m_maxQuality; if (quality >= maxQuality) { string text5 = RecipePinnerPlugin.Instance.LocalizationMgr.GetText("max_level"); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, text5, 0, (Sprite)null); } return; } string name = ((Object)recipeFromObject.m_item).name; string text6 = $"{name} ★{num}"; DebugLogger.Log("Attempting to pin upgrade: " + text6 + " (Base: " + name + ")"); if ((Object)(object)GetRecipeByName(text6) != (Object)null) { TogglePin(text6); return; } string text7 = RecipePinnerPlugin.Instance.LocalizationMgr.GetText("no_upgrade_cost"); Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)2, text7, 0, (Sprite)null); } } else { DebugLogger.Warning("Matched name but could not get ItemData for upgrade."); } } else { DebugLogger.Log("Matched recipe: " + ((Object)recipeFromObject).name); TogglePin(((Object)recipeFromObject).name); } return; } } } public void TryPinHoveredPiece() { DebugLogger.Verbose("Attempting to pin hovered piece..."); if (!((Object)(object)Hud.instance == (Object)null)) { Piece hoveredPiece = ReflectionHelper.GetHoveredPiece(Hud.instance); if ((Object)(object)hoveredPiece != (Object)null && hoveredPiece.m_resources != null && hoveredPiece.m_resources.Length != 0) { DebugLogger.Log("Pinning piece: " + ((Object)hoveredPiece).name); TogglePin(((Object)hoveredPiece).name); } else { DebugLogger.Verbose("No valid piece to pin (Mouse must be over a recipe icon)"); } } } private void TogglePin(string recipeName) { bool flag = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303); LocalizationManager localizationMgr = RecipePinnerPlugin.Instance.LocalizationMgr; if (PinnedRecipes.TryGetValue(recipeName, out var value)) { if (flag) { value--; if (value <= 0) { PinnedRecipes.Remove(recipeName); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, localizationMgr.GetText("unpinned"), 0, (Sprite)null); } DebugLogger.Log("Unpinned: " + recipeName); } else { PinnedRecipes[recipeName] = value; string text = string.Format(localizationMgr.GetText("decreased"), value); Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)2, text, 0, (Sprite)null); } DebugLogger.Log($"Decreased pin count: {recipeName} = {value}"); } } else { value++; PinnedRecipes[recipeName] = value; string text2 = string.Format(localizationMgr.GetText("added_more"), value); Player localPlayer3 = Player.m_localPlayer; if (localPlayer3 != null) { ((Character)localPlayer3).Message((MessageType)2, text2, 0, (Sprite)null); } DebugLogger.Log($"Increased pin count: {recipeName} = {value}"); } } else { if (flag) { return; } if (PinnedRecipes.Count < RecipePinnerPlugin.MaximumPins.Value) { PinnedRecipes.Add(recipeName, 1); Player localPlayer4 = Player.m_localPlayer; if (localPlayer4 != null) { ((Character)localPlayer4).Message((MessageType)2, localizationMgr.GetText("pinned"), 0, (Sprite)null); } DebugLogger.Log("Pinned new recipe: " + recipeName); } else { Player localPlayer5 = Player.m_localPlayer; if (localPlayer5 != null) { ((Character)localPlayer5).Message((MessageType)2, localizationMgr.GetText("list_full"), 0, (Sprite)null); } DebugLogger.Warning($"Cannot pin {recipeName} - max pins reached ({RecipePinnerPlugin.MaximumPins.Value})"); } } RefreshRecipeCache(); } private Recipe GetRecipeFromObject(object data) { if (data == null) { return null; } Recipe val = (Recipe)((data is Recipe) ? data : null); if (val != null) { return val; } Type type = data.GetType(); if (_cachedRecipeFields.TryGetValue(type, out var value)) { object? value2 = value.GetValue(data); return (Recipe)((value2 is Recipe) ? value2 : null); } if (_cachedRecipeProps.TryGetValue(type, out var value3)) { object? value4 = value3.GetValue(data, null); return (Recipe)((value4 is Recipe) ? value4 : null); } PropertyInfo property = type.GetProperty("Key"); if (property != null) { object? value5 = property.GetValue(data, null); Recipe val2 = (Recipe)((value5 is Recipe) ? value5 : null); if (val2 != null) { _cachedRecipeProps[type] = property; return val2; } } FieldInfo field = type.GetField("m_recipe", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object? value6 = field.GetValue(data); Recipe val3 = (Recipe)((value6 is Recipe) ? value6 : null); if (val3 != null) { _cachedRecipeFields[type] = field; return val3; } } FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(Recipe)) { _cachedRecipeFields[type] = fieldInfo; object? value7 = fieldInfo.GetValue(data); return (Recipe)((value7 is Recipe) ? value7 : null); } } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.PropertyType == typeof(Recipe) && propertyInfo.CanRead) { _cachedRecipeProps[type] = propertyInfo; object? value8 = propertyInfo.GetValue(data, null); return (Recipe)((value8 is Recipe) ? value8 : null); } } return null; } private ItemData GetItemDataFromObject(object data) { if (data == null) { return null; } Type type = data.GetType(); if (_cachedItemFields.TryGetValue(type, out var value)) { object? value2 = value.GetValue(data); return (ItemData)((value2 is ItemData) ? value2 : null); } if (_cachedItemProps.TryGetValue(type, out var value3)) { object? value4 = value3.GetValue(data, null); return (ItemData)((value4 is ItemData) ? value4 : null); } PropertyInfo property = type.GetProperty("Value"); if (property != null) { object? value5 = property.GetValue(data, null); ItemData val = (ItemData)((value5 is ItemData) ? value5 : null); if (val != null) { _cachedItemProps[type] = property; return val; } } PropertyInfo property2 = type.GetProperty("Item2"); if (property2 != null) { object? value6 = property2.GetValue(data, null); ItemData val2 = (ItemData)((value6 is ItemData) ? value6 : null); if (val2 != null) { _cachedItemProps[type] = property2; return val2; } } FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(ItemData)) { _cachedItemFields[type] = fieldInfo; object? value7 = fieldInfo.GetValue(data); return (ItemData)((value7 is ItemData) ? value7 : null); } } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.PropertyType == typeof(ItemData) && propertyInfo.CanRead) { _cachedItemProps[type] = propertyInfo; object? value8 = propertyInfo.GetValue(data, null); return (ItemData)((value8 is ItemData) ? value8 : null); } } return null; } private string ExtractTextFromUI(Transform child) { Text componentInChildren = ((Component)child).GetComponentInChildren<Text>(); if ((Object)(object)componentInChildren != (Object)null) { return componentInChildren.text; } Component[] componentsInChildren = ((Component)child).GetComponentsInChildren<Component>(true); Component[] array = componentsInChildren; foreach (Component val in array) { if (!((object)val).GetType().Name.Contains("TextMeshPro") && !((object)val).GetType().Name.Contains("TMP_Text")) { continue; } PropertyInfo property = ((object)val).GetType().GetProperty("text"); if (property != null) { string text = property.GetValue(val, null) as string; if (!string.IsNullOrEmpty(text)) { return text; } } } return null; } private string GetRawRecipeName(Recipe r) { if ((Object)(object)r.m_item != (Object)null && r.m_item.m_itemData != null) { return r.m_item.m_itemData.m_shared.m_name; } ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab(((Object)r).name) : null); if ((Object)(object)val != (Object)null) { ItemDrop component = val.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null) { return component.m_itemData.m_shared.m_name; } Piece component2 = val.GetComponent<Piece>(); if ((Object)(object)component2 != (Object)null) { return component2.m_name; } } return null; } private bool IsVisibleInScroll(RectTransform item, ScrollRect scrollRect) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)item == (Object)null || !((Component)item).gameObject.activeInHierarchy) { return false; } if ((Object)(object)scrollRect == (Object)null || (Object)(object)scrollRect.viewport == (Object)null) { return true; } Vector3[] array = (Vector3[])(object)new Vector3[4]; scrollRect.viewport.GetWorldCorners(array); Rect val = default(Rect); ((Rect)(ref val))..ctor(array[0].x, array[0].y, array[2].x - array[0].x, array[2].y - array[0].y); Vector3[] array2 = (Vector3[])(object)new Vector3[4]; item.GetWorldCorners(array2); Vector3 val2 = (array2[0] + array2[2]) / 2f; return ((Rect)(ref val)).Contains(val2); } } [BepInPlugin("com.Kadrio.RecipePinner", "Recipe Pinner", "1.1.4")] public class RecipePinnerPlugin : BaseUnityPlugin { public enum PinLayoutMode { AutoDetect, ForceVertical, ForceHorizontal, ForceBottomRightHorizontal } public class ConfigurationManagerAttributes { public bool? ShowRangeAsPercent; public Action<ConfigEntryBase> CustomDrawer; public bool? Browsable; public string Category; public object DefaultValue; public bool? HideDefaultButton; public bool? HideSettingName; public string Description; public string DispName; public int? Order; public bool? ReadOnly; public bool? IsAdvanced; public Func<object, string> ObjToStr; public Func<string, object> StrToObj; } public static RecipePinnerPlugin Instance; public static ConfigEntry<bool> EnableMod; public static ConfigEntry<string> LanguageOverride; public static ConfigEntry<PinLayoutMode> LayoutModeConfig; public static ConfigEntry<int> MaximumPins; public static ConfigEntry<int> PinsPerPage; public static ConfigEntry<bool> AutoUnpinAfterCrafting; public static ConfigEntry<KeyCode> HotkeyPin; public static ConfigEntry<KeyCode> HotkeyClearAll; public static ConfigEntry<KeyCode> HotkeyToggleVisibility; public static ConfigEntry<KeyCode> HotkeyPageSwitch; public static ConfigEntry<bool> EnableChestScanning; public static ConfigEntry<float> ChestScanRange; public static ConfigEntry<float> ChestScanInterval; public static ConfigEntry<float> UIScale; public static ConfigEntry<int> FontSizeRecipeName; public static ConfigEntry<int> FontSizeMaterials; public static ConfigEntry<float> BackgroundOpacity; public static ConfigEntry<Color> ColorHeader; public static ConfigEntry<Color> ColorEnoughInInventory; public static ConfigEntry<Color> ColorEnoughWithChests; public static ConfigEntry<Color> ColorMissing; public static ConfigEntry<Color> ColorPaginationActive; public static ConfigEntry<float> PaginationInactiveOpacity; public static ConfigEntry<int> PaginationDotSize; public static ConfigEntry<int> PaginationDotSpacing; public static ConfigEntry<float> VerticalListWidth; public static ConfigEntry<float> VerticalPinSpacing; public static ConfigEntry<Vector2> VerticalPosition; public static ConfigEntry<float> HorizontalColumnWidth; public static ConfigEntry<float> HorizontalPinSpacing; public static ConfigEntry<Vector2> HorizontalPosition; public static ConfigEntry<float> BottomRightColumnWidth; public static ConfigEntry<float> BottomRightPinSpacing; public static ConfigEntry<Vector2> BottomRightPosition; public static ConfigEntry<bool> EnableDebugLogging; public LocalizationManager LocalizationMgr; public RecipeManager RecipeMgr; public ContainerScanner ContainerMgr; public UIManager UIMgr; public DataPersistence DataMgr; internal bool _mluiMapListEnabled = false; internal bool _mluiNoMapListEnabled = false; internal bool _mluiInstalled = false; private bool _startupInitialized = false; private string _lastLanguage = ""; private string _currentSessionPlayer = null; private static bool _isUiVisible = true; public bool IsHorizontalMode { get { if (LayoutModeConfig.Value == PinLayoutMode.ForceBottomRightHorizontal) { return true; } if (LayoutModeConfig.Value == PinLayoutMode.ForceHorizontal) { return true; } if (LayoutModeConfig.Value == PinLayoutMode.ForceVertical) { return false; } if (!_mluiInstalled) { return false; } if (Game.m_noMap) { return _mluiNoMapListEnabled; } return _mluiMapListEnabled; } } private void Awake() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown Instance = this; BindConfigs(); DebugLogger.Log("RecipePinner plugin initializing..."); LocalizationMgr = new LocalizationManager(this); RecipeMgr = new RecipeManager(); ContainerMgr = new ContainerScanner(); UIMgr = new UIManager(); DataMgr = new DataPersistence(); DebugLogger.Log("All managers initialized successfully"); Harmony val = new Harmony("com.Kadrio.RecipePinner"); val.PatchAll(typeof(RecipePinnerPlugin)); val.PatchAll(typeof(ContainerScanner)); DebugLogger.Log("Harmony patches applied successfully"); } private void BindConfigs() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Expected O, but got Unknown //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Expected O, but got Unknown //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Expected O, but got Unknown //IL_0336: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Expected O, but got Unknown //IL_0389: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Expected O, but got Unknown //IL_03c6: Unknown result type (might be due to invalid IL or missing references) //IL_03d0: Expected O, but got Unknown //IL_041a: Unknown result type (might be due to invalid IL or missing references) //IL_0424: Expected O, but got Unknown //IL_04bd: Unknown result type (might be due to invalid IL or missing references) //IL_050c: Unknown result type (might be due to invalid IL or missing references) //IL_055b: Unknown result type (might be due to invalid IL or missing references) //IL_05aa: Unknown result type (might be due to invalid IL or missing references) //IL_05f9: Unknown result type (might be due to invalid IL or missing references) //IL_0652: Unknown result type (might be due to invalid IL or missing references) //IL_065c: Expected O, but got Unknown //IL_069c: Unknown result type (might be due to invalid IL or missing references) //IL_06a6: Expected O, but got Unknown //IL_06e5: Unknown result type (might be due to invalid IL or missing references) //IL_06ef: Expected O, but got Unknown //IL_076d: Unknown result type (might be due to invalid IL or missing references) //IL_07e3: Unknown result type (might be due to invalid IL or missing references) //IL_0859: Unknown result type (might be due to invalid IL or missing references) EnableMod = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "EnableMod", true, new ConfigDescription("Enable or disable the mod completely.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); EnableMod.SettingChanged += delegate { if (!EnableMod.Value) { UIMgr?.DestroyUI(); } }; LanguageOverride = ((BaseUnityPlugin)this).Config.Bind<string>("1 - General", "LanguageOverride", "Auto", new ConfigDescription("Force a specific language (e.g., 'German', 'Turkish').", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); LanguageOverride.SettingChanged += delegate { LocalizationMgr?.LoadTranslations(); RecipeMgr?.RefreshRecipeCache(); }; LayoutModeConfig = ((BaseUnityPlugin)this).Config.Bind<PinLayoutMode>("1 - General", "LayoutMode", PinLayoutMode.AutoDetect, new ConfigDescription("Choose layout position.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); LayoutModeConfig.SettingChanged += delegate { UIMgr?.DestroyUI(); }; MaximumPins = ((BaseUnityPlugin)this).Config.Bind<int>("1 - General", "MaximumPins", 10, new ConfigDescription("Max pins allowed.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), new object[1] { new ConfigurationManagerAttributes { Order = 96 } })); MaximumPins.SettingChanged += delegate { UIMgr?.DestroyUI(); }; PinsPerPage = ((BaseUnityPlugin)this).Config.Bind<int>("1 - General", "PinsPerPage", 5, new ConfigDescription("How many pins to show per page.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), new object[1] { new ConfigurationManagerAttributes { Order = 95 } })); PinsPerPage.SettingChanged += delegate { UIMgr?.ResetPage(); UIMgr?.DestroyUI(); }; AutoUnpinAfterCrafting = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "AutoUnpinAfterCrafting", true, new ConfigDescription("Unpin after crafting.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 95 } })); HotkeyPin = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2 - Controls", "HotkeyPin", (KeyCode)325, "Key to pin recipe."); HotkeyClearAll = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2 - Controls", "HotkeyClearAll", (KeyCode)112, "Key to clear all pins."); HotkeyToggleVisibility = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2 - Controls", "HotkeyToggleVisibility", (KeyCode)288, "Key to toggle overlay."); HotkeyPageSwitch = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("2 - Controls", "HotkeyPageSwitch", (KeyCode)308, "Key to cycle through pin pages."); EnableChestScanning = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Chest Scanner", "EnableChestScanning", false, new ConfigDescription("Count materials in nearby chests.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 99 } })); EnableChestScanning.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; ChestScanRange = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Chest Scanner", "ChestScanRange", 20f, new ConfigDescription("Scan radius.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), new object[1] { new ConfigurationManagerAttributes { Order = 98 } })); ChestScanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Chest Scanner", "ChestScanInterval", 3f, new ConfigDescription("Scan frequency.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), new object[1] { new ConfigurationManagerAttributes { Order = 97 } })); UIScale = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Visual Settings", "UIScale", 0.75f, new ConfigDescription("Global UI scale.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 3f), Array.Empty<object>())); UIScale.SettingChanged += delegate { UIMgr?.DestroyUI(); }; BackgroundOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Visual Settings", "BackgroundOpacity", 0.45f, new ConfigDescription("Background opacity.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); FontSizeRecipeName = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Visual Settings", "FontSizeRecipeName", 15, "Recipe name font size."); FontSizeRecipeName.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; FontSizeMaterials = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Visual Settings", "FontSizeMaterials", 15, "Material font size."); FontSizeMaterials.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; ColorHeader = ((BaseUnityPlugin)this).Config.Bind<Color>("4 - Visual Settings", "ColorHeader", new Color(1f, 0.717f, 0.368f, 1f), "Recipe title color."); ColorHeader.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; ColorEnoughInInventory = ((BaseUnityPlugin)this).Config.Bind<Color>("4 - Visual Settings", "ColorEnoughInInventory", new Color(0f, 1f, 0f, 1f), "Color: Enough in inventory."); ColorEnoughInInventory.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; ColorEnoughWithChests = ((BaseUnityPlugin)this).Config.Bind<Color>("4 - Visual Settings", "ColorEnoughWithChests", new Color(1f, 1f, 0f, 1f), "Color: Enough with chests."); ColorEnoughWithChests.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; ColorMissing = ((BaseUnityPlugin)this).Config.Bind<Color>("4 - Visual Settings", "ColorMissing", new Color(1f, 0.33f, 0.33f, 1f), "Color: Missing materials."); ColorMissing.SettingChanged += delegate { RecipeMgr?.RefreshRecipeCache(); }; ColorPaginationActive = ((BaseUnityPlugin)this).Config.Bind<Color>("4 - Visual Settings", "ColorPaginationActive", new Color(1f, 0.717f, 0.368f, 1f), "Active page dot color (Orange)."); ColorPaginationActive.SettingChanged += delegate { UIMgr?.UpdateUI(isVisible: true); }; PaginationInactiveOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Visual Settings", "PaginationInactiveOpacity", 0.3f, new ConfigDescription("Opacity of inactive page dots (0.0 to 1.0).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>())); PaginationInactiveOpacity.SettingChanged += delegate { UIMgr?.UpdateUI(isVisible: true); }; PaginationDotSize = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Visual Settings", "PaginationDotSize", 10, new ConfigDescription("Size of the pagination squares.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 20), Array.Empty<object>())); PaginationDotSize.SettingChanged += delegate { UIMgr?.UpdateUI(isVisible: true); }; PaginationDotSpacing = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Visual Settings", "PaginationDotSpacing", 8, new ConfigDescription("Space between pagination squares.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>())); PaginationDotSpacing.SettingChanged += delegate { UIMgr?.UpdateUI(isVisible: true); }; VerticalListWidth = ((BaseUnityPlugin)this).Config.Bind<float>("5 - Layout (Vertical Mode)", "ListWidth", 265f, "List width."); VerticalPinSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("5 - Layout (Vertical Mode)", "PinSpacing", 10f, "Spacing between pins."); VerticalPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("5 - Layout (Vertical Mode)", "Position", new Vector2(-40f, -250f), "Position (X, Y)."); HorizontalColumnWidth = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Layout (Horizontal - Map Side)", "ColumnWidth", 250f, "Column width."); HorizontalPinSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Layout (Horizontal - Map Side)", "PinSpacing", 10f, "Spacing between pins."); HorizontalPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("6 - Layout (Horizontal - Map Side)", "Position", new Vector2(-250f, -40f), "Position (X, Y)."); BottomRightColumnWidth = ((BaseUnityPlugin)this).Config.Bind<float>("7 - Layout (Horizontal - Bottom Right)", "ColumnWidth", 250f, "Column width."); BottomRightPinSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("7 - Layout (Horizontal - Bottom Right)", "PinSpacing", 10f, "Spacing between pins."); BottomRightPosition = ((BaseUnityPlugin)this).Config.Bind<Vector2>("7 - Layout (Horizontal - Bottom Right)", "Position", new Vector2(-40f, 40f), "Position (X, Y)."); EnableDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("8 - Debug", "EnableDebugLogging", false, "Enable debug logs."); DebugLogger.Log("Configuration loaded successfully"); } private void Start() { DebugLogger.Log("Start() called - Loading translations and initializing containers"); LocalizationMgr.LoadTranslations(); ReadMyLittleUIConfig(); ContainerMgr.InitializeContainers(); DebugLogger.Log("Start() completed successfully"); } private void OnDestroy() { DebugLogger.Log("Plugin destroyed - Cleaning up"); if ((Object)(object)Player.m_localPlayer != (Object)null) { DataMgr.SavePins(); } RecipeMgr.Cleanup(); } private void Update() { //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) if (!EnableMod.Value) { return; } ReflectionHelper.UpdateGuiScale(); if (!_startupInitialized && (Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_recipes.Count > 0) { DebugLogger.Log("First-time initialization triggered"); _lastLanguage = Localization.instance.GetSelectedLanguage(); DataMgr.LoadPins(); RecipeMgr.ValidateAndCleanPins(); RecipeMgr.RefreshRecipeCache(); _startupInitialized = true; DebugLogger.Log($"Initialization complete - {RecipeMgr.PinnedRecipes.Count} recipes loaded"); } if (EnableChestScanning.Value && (Object)(object)Player.m_localPlayer != (Object)null && RecipeMgr.CachedPins.Count > 0) { ContainerMgr.UpdateScanning(); } if (Input.GetKeyDown(HotkeyToggleVisibility.Value) && !InputHelper.IsInputBlocked()) { _isUiVisible = !_isUiVisible; DebugLogger.Log($"UI visibility toggled: {_isUiVisible}"); } if ((Object)(object)Player.m_localPlayer != (Object)null) { UpdatePlayerSession(); } if (Input.GetKeyDown(HotkeyPin.Value)) { if ((Object)(object)InventoryGui.instance != (Object)null && InventoryGui.IsVisible()) { RecipeMgr.TryPinHoveredRecipe(InventoryGui.instance); } else if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null && ((Character)Player.m_localPlayer).InPlaceMode()) { RecipeMgr.TryPinHoveredPiece(); } } if (Input.GetKeyDown(HotkeyClearAll.Value) && !InputHelper.IsInputBlocked() && RecipeMgr.PinnedRecipes.Count > 0) { int count = RecipeMgr.PinnedRecipes.Count; RecipeMgr.PinnedRecipes.Clear(); RecipeMgr.RefreshRecipeCache(); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, LocalizationMgr.GetText("cleared"), 0, (Sprite)null); } DebugLogger.Log($"Cleared {count} pinned recipes"); } if (Localization.instance != null) { string selectedLanguage = Localization.instance.GetSelectedLanguage(); if (_lastLanguage != selectedLanguage) { DebugLogger.Log("Language changed from " + _lastLanguage + " to " + selectedLanguage); _lastLanguage = selectedLanguage; LocalizationMgr.LoadTranslations(); if ((Object)(object)ObjectDB.instance != (Object)null) { RecipeMgr.RefreshRecipeCache(); } } } if (Input.GetKeyDown(HotkeyPageSwitch.Value) && _isUiVisible && !InputHelper.IsInputBlocked()) { UIMgr?.CyclePage(); } } private void UpdatePlayerSession() { if ((Object)(object)Player.m_localPlayer == (Object)null || ((Character)Player.m_localPlayer).IsDead()) { return; } string playerName = Player.m_localPlayer.GetPlayerName(); if (string.IsNullOrEmpty(playerName)) { return; } if (_currentSessionPlayer != playerName) { DebugLogger.Log("Player session changed from '" + _currentSessionPlayer + "' to '" + playerName + "'"); RecipeMgr.PinnedRecipes.Clear(); RecipeMgr.CachedPins.Clear(); UIMgr.DestroyUI(); _currentSessionPlayer = playerName; if (!string.IsNullOrEmpty(playerName)) { DataMgr.LoadPins(); RecipeMgr.RefreshRecipeCache(); } } UIMgr.UpdateUI(_isUiVisible); } private void ReadMyLittleUIConfig() { if (!Chainloader.PluginInfos.ContainsKey("shudnal.MyLittleUI")) { _mluiInstalled = false; DebugLogger.Log("MyLittleUI not detected"); return; } _mluiInstalled = true; _mluiMapListEnabled = true; _mluiNoMapListEnabled = true; string path = Path.Combine(Paths.ConfigPath, "shudnal.MyLittleUI.cfg"); if (!File.Exists(path)) { DebugLogger.Log("MyLittleUI installed but config not found"); return; } try { string[] array = File.ReadAllLines(path); string text = ""; string[] array2 = array; foreach (string text2 in array2) { string text3 = text2.Trim(); if (text3.StartsWith("[") && text3.EndsWith("]")) { text = text3; } else if (text3.StartsWith("Enable")) { bool flag = text3.ToLower().Contains("true"); if (text == "[Status effects - Map - List]") { _mluiMapListEnabled = flag; } else if (text == "[Status effects - Nomap - List]") { _mluiNoMapListEnabled = flag; } } } DebugLogger.Log($"MyLittleUI Config: MapList={_mluiMapListEnabled}, NoMapList={_mluiNoMapListEnabled}"); } catch (Exception ex) { Debug.LogWarning((object)("[RecipePinner] Error reading MyLittleUI config: " + ex.Message)); } } [HarmonyPatch(typeof(Game), "SavePlayerProfile")] [HarmonyPostfix] public static void AutoSavePinsHook() { if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)Instance != (Object)null) { DebugLogger.Log("Auto-saving pins on profile save"); Instance.DataMgr.SavePins(); } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] [HarmonyPostfix] public static void AutoUnpinHook(InventoryGui __instance) { if (!EnableMod.Value || !AutoUnpinAfterCrafting.Value || (Object)(object)Instance == (Object)null) { return; } Recipe craftRecipe = ReflectionHelper.GetCraftRecipe(__instance); if (!((Object)(object)craftRecipe != (Object)null)) { return; } string text = null; if (!((Selectable)__instance.m_tabUpgrade).interactable) { ItemData craftUpgradeItem = ReflectionHelper.GetCraftUpgradeItem(__instance); if (craftUpgradeItem != null) { string name = ((Object)craftRecipe.m_item).name; int quality = craftUpgradeItem.m_quality; int num = quality + 1; text = $"{name} ★{num}"; DebugLogger.Log($"Upgrade crafted: Unpinning target {text} (Base Level: {quality})"); } } else { text = ((Object)craftRecipe).name; } if (text != null && Instance.RecipeMgr.PinnedRecipes.TryGetValue(text, out var value)) { value--; DebugLogger.Log($"Auto-unpin: {text}, remaining count: {value}"); if (value <= 0) { Instance.RecipeMgr.PinnedRecipes.Remove(text); DebugLogger.Log("Recipe " + text + " fully unpinned"); } else { Instance.RecipeMgr.PinnedRecipes[text] = value; } Instance.RecipeMgr.RefreshRecipeCache(); } } [HarmonyPatch(typeof(Player), "ConsumeResources")] [HarmonyPostfix] public static void AutoUnpinBuildHook() { if ((Object)(object)Instance == (Object)null || !EnableMod.Value || !AutoUnpinAfterCrafting.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } PieceTable pieceTable = ReflectionHelper.GetPieceTable(localPlayer); if ((Object)(object)pieceTable == (Object)null) { return; } Piece selectedPiece = pieceTable.GetSelectedPiece(); if ((Object)(object)selectedPiece == (Object)null) { return; } string text = ((Object)selectedPiece).name.Replace("(Clone)", "").Trim(); if (Instance.RecipeMgr.PinnedRecipes.TryGetValue(text, out var value)) { value--; DebugLogger.Log($"Auto-unpin (Build): {text}, remaining count: {value}"); if (value <= 0) { Instance.RecipeMgr.PinnedRecipes.Remove(text); DebugLogger.Log("Build recipe " + text + " fully unpinned"); } else { Instance.RecipeMgr.PinnedRecipes[text] = value; } Instance.RecipeMgr.RefreshRecipeCache(); } } } public static class ReflectionHelper { private static Func<float> _getGuiScale; private static Func<InventoryGui, Transform> _getRecipeListRoot; private static Func<InventoryGui, object> _getAvailableRecipes; private static Func<InventoryGui, Container> _getCurrentContainer; private static Func<InventoryGui, Recipe> _getCraftRecipe; private static Func<InventoryGui, ItemData> _getCraftUpgradeItem; private static Func<Hud, Piece> _getHoveredPiece; public static Func<Container, long, bool> CheckContainerAccess; private static FieldInfo _f_buildPieces; public static float currentGuiScaleValue; static ReflectionHelper() { currentGuiScaleValue = 1f; InitializeReflection(); } public static void InitializeReflection() { DebugLogger.Log("Initializing reflection helpers..."); int num = 0; int num2 = 0; try { FieldInfo fieldInfo = AccessTools.Field(typeof(GuiScaler), "m_largeGuiScale"); if (fieldInfo != null && fieldInfo.IsStatic) { _getGuiScale = Expression.Lambda<Func<float>>(Expression.Field(null, fieldInfo), Array.Empty<ParameterExpression>()).Compile(); num++; DebugLogger.Verbose("✓ GuiScaler.m_largeGuiScale"); } else { num2++; DebugLogger.Warning("✗ GuiScaler.m_largeGuiScale not found"); } FieldInfo fieldInfo2 = AccessTools.Field(typeof(InventoryGui), "m_recipeListRoot"); if (fieldInfo2 != null) { ParameterExpression parameterExpression = Expression.Parameter(typeof(InventoryGui), "arg"); _getRecipeListRoot = Expression.Lambda<Func<InventoryGui, Transform>>(Expression.Field(parameterExpression, fieldInfo2), new ParameterExpression[1] { parameterExpression }).Compile(); num++; DebugLogger.Verbose("✓ InventoryGui.m_recipeListRoot"); } else { num2++; DebugLogger.Warning("✗ InventoryGui.m_recipeListRoot not found"); } FieldInfo fieldInfo3 = AccessTools.Field(typeof(InventoryGui), "m_availableRecipes"); if (fieldInfo3 != null) { ParameterExpression parameterExpression2 = Expression.Parameter(typeof(InventoryGui), "arg"); _getAvailableRecipes = Expression.Lambda<Func<InventoryGui, object>>(Expression.Field(parameterExpression2, fieldInfo3), new ParameterExpression[1] { parameterExpression2 }).Compile(); num++; DebugLogger.Verbose("✓ InventoryGui.m_availableRecipes"); } else { num2++; DebugLogger.Warning("✗ InventoryGui.m_availableRecipes not found"); } FieldInfo fieldInfo4 = AccessTools.Field(typeof(InventoryGui), "m_currentContainer"); if (fieldInfo4 != null) { ParameterExpression parameterExpression3 = Expression.Parameter(typeof(InventoryGui), "arg"); _getCurrentContainer = Expression.Lambda<Func<InventoryGui, Container>>(Expression.Field(parameterExpression3, fieldInfo4), new ParameterExpression[1] { parameterExpression3 }).Compile(); num++; DebugLogger.Verbose("✓ InventoryGui.m_currentContainer"); } else { num2++; DebugLogger.Warning("✗ InventoryGui.m_currentContainer not found"); } FieldInfo fieldInfo5 = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe"); if (fieldInfo5 != null) { ParameterExpression parameterExpression4 = Expression.Parameter(typeof(InventoryGui), "arg"); _getCraftRecipe = Expression.Lambda<Func<InventoryGui, Recipe>>(Expression.Field(parameterExpression4, fieldInfo5), new ParameterExpression[1] { parameterExpression4 }).Compile(); num++; DebugLogger.Verbose("✓ InventoryGui.m_craftRecipe"); } else { num2++; DebugLogger.Warning("✗ InventoryGui.m_craftRecipe not found"); } FieldInfo fieldInfo6 = AccessTools.Field(typeof(InventoryGui), "m_craftUpgradeItem"); if (fieldInfo6 != null) { ParameterExpression parameterExpression5 = Expression.Parameter(typeof(InventoryGui), "arg"); _getCraftUpgradeItem = Expression.Lambda<Func<InventoryGui, ItemData>>(Expression.Field(parameterExpression5, fieldInfo6), new ParameterExpression[1] { parameterExpression5 }).Compile(); num++; DebugLogger.Verbose("✓ InventoryGui.m_craftUpgradeItem"); } else { num2++; DebugLogger.Warning("✗ InventoryGui.m_craftUpgradeItem not found"); } FieldInfo fieldInfo7 = AccessTools.Field(typeof(Hud), "m_hoveredPiece"); if (fieldInfo7 != null) { ParameterExpression parameterExpression6 = Expression.Parameter(typeof(Hud), "arg"); _getHoveredPiece = Expression.Lambda<Func<Hud, Piece>>(Expression.Field(parameterExpression6, fieldInfo7), new ParameterExpression[1] { parameterExpression6 }).Compile(); num++; DebugLogger.Verbose("✓ Hud.m_hoveredPiece"); } else { num2++; DebugLogger.Warning("✗ Hud.m_hoveredPiece not found"); } MethodInfo methodInfo = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null); if (methodInfo != null) { CheckContainerAccess = AccessTools.MethodDelegate<Func<Container, long, bool>>(methodInfo, (object)null, true); num++; DebugLogger.Verbose("✓ Container.CheckAccess"); } else { num2++; DebugLogger.Warning("✗ Container.CheckAccess not found"); } DebugLogger.Log($"Reflection initialization complete: {num} successful, {num2} failed"); if (num2 > 0) { DebugLogger.Warning("Some reflection targets failed - mod may not work correctly!"); } _f_buildPieces = AccessTools.Field(typeof(Player), "m_buildPieces"); if (_f_buildPieces != null) { num++; DebugLogger.Verbose("✓ Player.m_buildPieces"); } else { num2++; DebugLogger.Warning("✗ Player.m_buildPieces not found"); } } catch (Exception ex) { DebugLogger.Error("Critical error during reflection initialization", ex); } } public static void UpdateGuiScale() { if (_getGuiScale != null) { currentGuiScaleValue = _getGuiScale(); } else { currentGuiScaleValue = 1f; } } public static Transform GetRecipeListRoot(InventoryGui gui) { if (_getRecipeListRoot == null) { DebugLogger.Warning("GetRecipeListRoot delegate is null"); return null; } return _getRecipeListRoot(gui); } public static object GetAvailableRecipes(InventoryGui gui) { if (_getAvailableRecipes == null) { DebugLogger.Warning("GetAvailableRecipes delegate is null"); return null; } return _getAvailableRecipes(gui); } public static Container GetCurrentContainer(InventoryGui gui) { if (_getCurrentContainer == null) { DebugLogger.Verbose("GetCurrentContainer delegate is null"); return null; } return _getCurrentContainer(gui); } public static Recipe GetCraftRecipe(InventoryGui gui) { if (_getCraftRecipe == null) { DebugLogger.Warning("GetCraftRecipe delegate is null"); return null; } return _getCraftRecipe(gui); } public static ItemData GetCraftUpgradeItem(InventoryGui gui) { return _getCraftUpgradeItem?.Invoke(gui); } public static Piece GetHoveredPiece(Hud hud) { if (_getHoveredPiece == null) { DebugLogger.Verbose("GetHoveredPiece delegate is null"); return null; } return _getHoveredPiece(hud); } public static PieceTable GetPieceTable(Player player) { if (_f_buildPieces == null || (Object)(object)player == (Object)null) { return null; } object? value = _f_buildPieces.GetValue(player); return (PieceTable)((value is PieceTable) ? value : null); } } public static class InputHelper { public static bool IsInputBlocked() { if (Console.IsVisible()) { return true; } if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return true; } if (TextInput.IsVisible()) { DebugLogger.Verbose("Input blocked: TextInput is visible"); return true; } return false; } public static bool IsMouseOverRect(RectTransform rect) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)rect == (Object)null) { DebugLogger.Verbose("IsMouseOverRect: rect is null"); return false; } bool flag = RectTransformUtility.RectangleContainsScreenPoint(rect, Vector2.op_Implicit(Input.mousePosition)); if (flag) { DebugLogger.Verbose("Mouse over rect: " + ((Object)((Component)rect).gameObject).name); } return flag; } } public static class UIBuilder { private static Color ValheimOrange = new Color(1f, 0.77f, 0.31f, 1f); private static Color DividerColor = new Color(1f, 1f, 1f, 0.1f); private static Sprite _cachedUiSprite; private static bool _spriteSearchDone = false; private static Sprite GetBackgroundSprite() { if ((Object)(object)_cachedUiSprite != (Object)null) { return _cachedUiSprite; } if (_spriteSearchDone) { return null; } Sprite[] array = Resources.FindObjectsOfTypeAll<Sprite>(); Sprite val = null; Sprite[] array2 = array; foreach (Sprite val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { if (((Object)val2).name == "UISprite") { _cachedUiSprite = val2; break; } if ((Object)(object)val == (Object)null && ((Object)val2).name == "Knob") { val = val2; } } } if ((Object)(object)_cachedUiSprite == (Object)null) { _cachedUiSprite = val; } _spriteSearchDone = true; if ((Object)(object)_cachedUiSprite != (Object)null) { DebugLogger.Verbose("Found background sprite: " + ((Object)_cachedUiSprite).name); } else { DebugLogger.Warning("No suitable background sprite found"); } return _cachedUiSprite; } public static PinSlotUI CreatePinSlot(Transform parent, Font font) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Expected O, but got Unknown //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Expected O, but got Unknown //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Expected O, but got Unknown //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Expected O, but got Unknown //IL_038c: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_03ba: Unknown result type (might be due to invalid IL or missing references) //IL_0416: Unknown result type (might be due to invalid IL or missing references) //IL_041d: Expected O, but got Unknown GameObject val = new GameObject("PinSlot", new Type[1] { typeof(RectTransform) }); val.layer = 5; val.transform.SetParent(parent, false); PinSlotUI pinSlotUI = val.AddComponent<PinSlotUI>(); pinSlotUI.Rect = val.GetComponent<RectTransform>(); Image val2 = val.AddComponent<Image>(); Sprite val3 = (val2.sprite = GetBackgroundSprite()); if ((Object)(object)val3 != (Object)null && val3.border != Vector4.zero) { val2.type = (Type)1; } else { val2.type = (Type)0; } float num = ((RecipePinnerPlugin.BackgroundOpacity != null) ? RecipePinnerPlugin.BackgroundOpacity.Value : 0.45f); ((Graphic)val2).color = new Color(0f, 0f, 0f, num); ((Graphic)val2).raycastTarget = false; VerticalLayoutGroup val4 = val.AddComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)val4).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val4).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val4).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val4).spacing = 5f; ((LayoutGroup)val4).padding = new RectOffset(8, 8, 8, 8); ContentSizeFitter val5 = val.AddComponent<ContentSizeFitter>(); val5.horizontalFit = (FitMode)0; val5.verticalFit = (FitMode)2; GameObject val6 = new GameObject("HeaderRow", new Type[1] { typeof(RectTransform) }); val6.layer = 5; val6.transform.SetParent(val.transform, false); HorizontalLayoutGroup val7 = val6.AddComponent<HorizontalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)val7).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val7).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val7).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val7).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)val7).spacing = 8f; LayoutElement val8 = val6.AddComponent<LayoutElement>(); val8.minHeight = 30f; val8.flexibleHeight = 0f; val8.flexibleWidth = 1f; GameObject val9 = new GameObject("Icon", new Type[1] { typeof(RectTransform) }); val9.layer = 5; val9.transform.SetParent(val6.transform, false); Image val10 = val9.AddComponent<Image>(); ((Graphic)val10).raycastTarget = false; val10.preserveAspect = true; pinSlotUI.IconImage = val10; LayoutElement val11 = val9.AddComponent<LayoutElement>(); val11.minWidth = 28f; val11.minHeight = 28f; val11.preferredWidth = 28f; val11.preferredHeight = 28f; val11.flexibleWidth = 0f; GameObject val12 = new GameObject("Title", new Type[1] { typeof(RectTransform) }); val12.layer = 5; val12.transform.SetParent(val6.transform, false); Text val13 = val12.AddComponent<Text>(); ((Graphic)val13).raycastTarget = false; val13.font = font; val13.fontSize = 18; val13.alignment = (TextAnchor)3; val13.horizontalOverflow = (HorizontalWrapMode)0; val13.verticalOverflow = (VerticalWrapMode)1; ((Graphic)val13).color = ValheimOrange; pinSlotUI.HeaderText = val13; LayoutElement val14 = val12.AddComponent<LayoutElement>(); val14.minHeight = 24f; val14.flexibleWidth = 1f; GameObject val15 = new GameObject("Divider", new Type[1] { typeof(RectTransform) }); val15.layer = 5; val15.transform.SetParent(val.transform, false); Image val16 = val15.AddComponent<Image>(); val16.sprite = GetBackgroundSprite(); if ((Object)(object)val3 != (Object)null && val3.border != Vector4.zero) { val16.type = (Type)1; } else { val16.type = (Type)0; } ((Graphic)val16).color = DividerColor; ((Graphic)val16).raycastTarget = false; LayoutElement val17 = val15.AddComponent<LayoutElement>(); val17.minHeight = 2f; val17.preferredHeight = 2f; val17.flexibleWidth = 1f; GameObject val18 = new GameObject("ResourceList", new Type[1] { typeof(RectTransform) }); val18.layer = 5; val18.transform.SetParent(val.transform, false); VerticalLayoutGroup val19 = val18.AddComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)val19).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val19).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val19).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val19).spacing = 3f; ContentSizeFitter val20 = val18.AddComponent<ContentSizeFitter>(); val20.verticalFit = (FitMode)2; pinSlotUI.ResourceListRoot = val18.transform; DebugLogger.Verbose("Created pin slot UI"); return pinSlotUI; } public static ResourceSlotUI CreateResourceSlot(Transform parent, Font font) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Expected O, but got Unknown //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Expected O, but got Unknown GameObject val = new GameObject("ResSlot", new Type[1] { typeof(RectTransform) }); val.layer = 5; val.transform.SetParent(parent, false); ResourceSlotUI resourceSlotUI = val.AddComponent<ResourceSlotUI>(); HorizontalLayoutGroup val2 = val.AddComponent<HorizontalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)val2).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val2).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)val2).spacing = 6f; LayoutElement val3 = val.AddComponent<LayoutElement>(); val3.minHeight = 22f; val3.flexibleHeight = 0f; GameObject val4 = new GameObject("Icon", new Type[1] { typeof(RectTransform) }); val4.layer = 5; val4.transform.SetParent(val.transform, false); resourceSlotUI.ResIcon = val4.AddComponent<Image>(); ((Graphic)resourceSlotUI.ResIcon).raycastTarget = false; resourceSlotUI.ResIcon.preserveAspect = true; LayoutElement val5 = val4.AddComponent<LayoutElement>(); val5.minWidth = 20f; val5.minHeight = 20f; val5.preferredWidth = 20f; val5.preferredHeight = 20f; val5.flexibleWidth = 0f; GameObject val6 = new GameObject("Name", new Type[1] { typeof(RectTransform) }); val6.layer = 5; val6.transform.SetParent(val.transform, false); resourceSlotUI.ResName = val6.AddComponent<Text>(); ((Graphic)resourceSlotUI.ResName).raycastTarget = false; resourceSlotUI.ResName.font = font; resourceSlotUI.ResName.fontSize = 15; resourceSlotUI.ResName.alignment = (TextAnchor)3; resourceSlotUI.ResName.horizontalOverflow = (HorizontalWrapMode)0; ((Graphic)resourceSlotUI.ResName).color = new Color(0.9f, 0.9f, 0.9f, 1f); LayoutElement val7 = val6.AddComponent<LayoutElement>(); val7.flexibleWidth = 1f; GameObject val8 = new GameObject("Amount", new Type[1] { typeof(RectTransform) }); val8.layer = 5; val8.transform.SetParent(val.transform, false); resourceSlotUI.ResAmount = val8.AddComponent<Text>(); ((Graphic)resourceSlotUI.ResAmount).raycastTarget = false; resourceSlotUI.ResAmount.font = font; resourceSlotUI.ResAmount.fontSize = 15; resourceSlotUI.ResAmount.alignment = (TextAnchor)5; LayoutElement val9 = val8.AddComponent<LayoutElement>(); val9.minWidth = 40f; DebugLogger.Verbose("Created resource slot UI"); return resourceSlotUI; } public static GameObject CreatePaginationContainer(Transform parent) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown GameObject val = new GameObject("PaginationDots", new Type[1] { typeof(RectTransform) }); val.layer = 5; val.transform.SetParent(parent, false); LayoutElement val2 = val.AddComponent<LayoutElement>(); val2.ignoreLayout = true; HorizontalLayoutGroup val3 = val.AddComponent<HorizontalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)val3).childControlHeight = false; ((HorizontalOrVerticalLayoutGroup)val3).childControlWidth = false; ((HorizontalOrVerticalLayoutGroup)val3).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val3).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)val3).spacing = RecipePinnerPlugin.PaginationDotSpacing.Value; ((LayoutGroup)val3).childAlignment = (TextAnchor)4; ContentSizeFitter val4 = val.AddComponent<ContentSizeFitter>(); val4.horizontalFit = (FitMode)2; val4.verticalFit = (FitMode)2; return val; } public static Image CreatePageDot(Transform parent) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("PageDot", new Type[1] { typeof(RectTransform) }); val.layer = 5; val.transform.SetParent(parent, false); Image val2 = val.AddComponent<Image>(); val2.type = (Type)0; ((Graphic)val2).raycastTarget = false; int value = RecipePinnerPlugin.PaginationDotSize.Value; RectTransform component = val.GetComponent<RectTransform>(); component.sizeDelta = new Vector2((float)value, (float)value); ((Transform)component).localRotation = Quaternion.Euler(0f, 0f, 45f); return val2; } } public class PinSlotUI : MonoBehaviour { [CompilerGenerated] private sealed class <FixLayout>d__16 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public PinSlotUI <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <FixLayout>d__16(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)<>4__this.ResourceListRoot != (Object)null) { Transform resourceListRoot = <>4__this.ResourceListRoot; LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)(object)((resourceListRoot is RectTransform) ? resourceListRoot : null)); } if ((Object)(object)<>4__this.Rect != (Object)null) { LayoutRebuilder.ForceRebuildLayoutImmediate(<>4__this.Rect); } if ((Object)(object)((Component)<>4__this).transform.parent != (Object)null) { Transform parent = ((Component)<>4__this).transform.parent; LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)(object)((parent is RectTransform) ? parent : null)); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public RectTransform Rect; public Image IconImage; public Text HeaderText; public Transform ResourceListRoot; public PinnedRecipeData CurrentData; private Coroutine _layoutCoroutine; private Image _cachedBg; private ContentSizeFitter _cachedCsf; public List<ResourceSlotUI> ResourceSlots = new List<ResourceSlotUI>(); public Image BgImage => Object.op_Implicit((Object)(object)_cachedBg) ? _cachedBg : (_cachedBg = ((Component)this).GetComponent<Image>()); public ContentSizeFitter Csf => Object.op_Implicit((Object)(object)_cachedCsf) ? _cachedCsf : (_cachedCsf = ((Component)this).GetComponent<ContentSizeFitter>()); public void SetActive(bool active) { ((Component)this).gameObject.SetActive(active); } public void UpdateData(PinnedRecipeData data, Font font) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) IconImage.sprite = data.Icon; HeaderText.text = data.CachedHeader; HeaderText.font = font; HeaderText.fontSize = RecipePinnerPlugin.FontSizeRecipeName.Value; ((Graphic)HeaderText).color = RecipePinnerPlugin.ColorHeader.Value; int count = data.Resources.Count; while (ResourceSlots.Count < count) { ResourceSlots.Add(UIBuilder.CreateResourceSlot(ResourceListRoot, font)); } for (int i = 0; i < ResourceSlots.Count; i++) { if (i < count) { ResourceSlots[i].SetActive(active: true); ResourceSlots[i].UpdateResource(data.Resources[i]); } else { ResourceSlots[i].SetActive(active: false); } } if (((Component)this).gameObject.activeInHierarchy) { if (_layoutCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_layoutCoroutine); } _layoutCoroutine = ((MonoBehaviour)this).StartCoroutine(FixLayout()); } } private void OnDisable() { _layoutCoroutine = null; } [IteratorStateMachine(typeof(<FixLayout>d__16))] private IEnumerator FixLayout() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <FixLayout>d__16(0) { <>4__this = this }; } } public class ResourceSlotUI : MonoBehaviour { public Image ResIcon; public Text ResName; public Text ResAmount; public void SetActive(bool active) { ((Component)this).gameObject.SetActive(active); } public void UpdateResource(PinnedResData res) { ResIcon.sprite = res.Icon; ResName.text = res.CachedName; ResAmount.text = res.CachedAmountString; if (RecipePinnerPlugin.FontSizeMaterials != null) { int value = RecipePinnerPlugin.FontSizeMaterials.Value; ResName.fontSize = value; ResAmount.fontSize = value; } } } public class UIManager { private Transform _pinListRoot; private List<PinSlotUI> _pinPool = new List<PinSlotUI>(); private Font _cachedFont; private static Dictionary<string, int> _reusableInvCounts = new Dictionary<string, int>(); private int _currentPage = 0; private GameObject _paginationRoot; private List<Image> _pageDots = new List<Image>(); public void DestroyUI() { DebugLogger.Verbose("Destroying UI..."); if ((Object)(object)_pinListRoot != (Object)null) { Object.Destroy((Object)(object)((Component)_pinListRoot).gameObject); _pinListRoot = null; } if (_pinPool != null) { _pinPool.Clear(); } _pageDots.Clear(); _paginationRoot = null; DebugLogger.Log("UI destroyed successfully"); } public void ResetPage() { _currentPage = 0; } public void CyclePage() { RecipeManager recipeMgr = RecipePinnerPlugin.Instance.RecipeMgr; int count = recipeMgr.CachedPins.Count; int value = RecipePinnerPlugin.PinsPerPage.Value; if (count > value) { int num = Mathf.CeilToInt((float)count / (float)value); _currentPage++; if (_currentPage >= num) { _currentPage = 0; } DebugLogger.Log($"Switched to Page: {_currentPage + 1}/{num}"); UpdateUI(isVisible: true); } } public void UpdateUI(bool isVisible) { if ((Object)(object)Player.m_localPlayer == (Object)null || ((Character)Player.m_localPlayer).IsDead()) { return; } Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); if (inventory == null) { return; } RecipePinnerPlugin instance = RecipePinnerPlugin.Instance; RecipeManager recipeMgr = instance.RecipeMgr; ContainerScanner containerMgr = instance.ContainerMgr; if (_pinPool.Count < RecipePinnerPlugin.MaximumPins.Value) { DebugLogger.Log($"Pin limit changed (Pool: {_pinPool.Count}, Config: {RecipePinnerPlugin.MaximumPins.Value}). Rebuilding UI..."); DestroyUI(); } if ((Object)(object)_pinListRoot == (Object)null) { _pinPool.Clear(); CreateCanvasUI(); if ((Object)(object)_pinListRoot == (Object)null) { return; } foreach (PinnedRecipeData cachedPin in recipeMgr.CachedPins) { cachedPin.IsDirty = true; } } UpdateLayout(); if ((Object)(object)_pinListRoot == (Object)null) { return; } bool flag = isVisible && !InputHelper.IsInputBlocked() && recipeMgr.CachedPins.Count > 0; if (((Component)_pinListRoot).gameObject.activeSelf != flag) { ((Component)_pinListRoot).gameObject.SetActive(flag); } if (!flag) { return; } _reusableInvCounts.Clear(); foreach (ItemData allItem in inventory.GetAllItems()) { string name = allItem.m_shared.m_name; if (_reusableInvCounts.TryGetValue(name, out var value)) { _reusableInvCounts[name] = value + allItem.m_stack; } else { _reusableInvCounts[name] = allItem.m_stack; } } int count = recipeMgr.CachedPins.Count; int value2 = RecipePinnerPlugin.PinsPerPage.Value; int num = _currentPage * value2; if (num >= count && _currentPage > 0) { _currentPage--; num = _currentPage * value2; } int num2 = Mathf.Min(num + value2, count); for (int i = 0; i < _pinPool.Count; i++) { if (!((Object)(object)_pinPool[i] == (Object)null)) { int num3 = num + i; if (num3 < num2) { UpdatePinSlot(i, recipeMgr.CachedPins[num3], containerMgr); } else if (((Component)_pinPool[i]).gameObject.activeSelf) { _pinPool[i].SetActive(active: false); } } } int totalPages = 1; if (count > 0) { totalPages = Mathf.CeilToInt((float)count / (float)value2); } UpdatePageDots(totalPages); } private void UpdatePageDots(int totalPages) { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_0166: 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_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_paginationRoot == (Object)null) { return; } HorizontalLayoutGroup component = _paginationRoot.GetComponent<HorizontalLayoutGroup>(); if ((Object)(object)component != (Object)null) { ((HorizontalOrVerticalLayoutGroup)component).spacing = RecipePinnerPlugin.PaginationDotSpacing.Value; } if (totalPages <= 1) { if (_paginationRoot.activeSelf) { _paginationRoot.SetActive(false); } return; } if (!_paginationRoot.activeSelf) { _paginationRoot.SetActive(true); } while (_pageDots.Count < totalPages) { _pageDots.Add(UIBuilder.CreatePageDot(_paginationRoot.transform)); } int value = RecipePinnerPlugin.PaginationDotSize.Value; Color value2 = RecipePinnerPlugin.ColorPaginationActive.Value; for (int i = 0; i < _pageDots.Count; i++) { if (i < totalPages) { ((Component)_pageDots[i]).gameObject.SetActive(true); if (i == _currentPage) { ((Graphic)_pageDots[i]).color = value2; ((Graphic)_pageDots[i]).rectTransform.sizeDelta = new Vector2((float)value * 1.2f, (float)value * 1.2f); continue; } Color color = value2; color.a = RecipePinnerPlugin.PaginationInactiveOpacity.Value; ((Graphic)_pageDots[i]).color = color; ((Graphic)_pageDots[i]).rectTransform.sizeDelta = new Vector2((float)value, (float)value); } else { ((Component)_pageDots[i]).gameObject.SetActive(false); } } } private void UpdateDotsPosition() { //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_paginationRoot == (Object)null || (Object)(object)_pinListRoot == (Object)null) { return; } RectTransform component = _paginationRoot.GetComponent<RectTransform>(); bool flag = RecipePinnerPlugin.LayoutModeConfig.Value == RecipePinnerPlugin.PinLayoutMode.ForceBottomRightHorizontal; bool flag2 = (Object)(object)Player.m_localPlayer.GetControlledShip() != (Object)null; if (RecipePinnerPlugin.Instance.IsHorizontalMode || flag2) { float num = ((flag || flag2) ? RecipePinnerPlugin.BottomRightColumnWidth.Value : RecipePinnerPlugin.HorizontalColumnWidth.Value); float num2 = 0f - num / 2f; if (flag || flag2) { component.anchorMin = new Vector2(1f, 0f); component.anchorMax = new Vector2(1f, 0f); component.pivot = new Vector2(0.5f, 1f); component.anchoredPosition = new Vector2(num2, -15f); } else { component.anchorMin = new Vector2(1f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(0.5f, 0f); component.anchoredPosition = new Vector2(num2, 15f); } } else { component.anchorMin = new Vector2(0.5f, 1f); component.anchorMax = new Vector2(0.5f, 1f); component.pivot = new Vector2(0.5f, 0f); component.anchoredPosition = new Vector2(0f, 20f); } } private void UpdatePinSlot(int index, PinnedRecipeData pinData, ContainerScanner containerMgr) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) PinSlotUI pinSlotUI = _pinPool[index]; if ((Object)(object)pinSlotUI == (Object)null || (Object)(object)((Component)pinSlotUI).gameObject == (Object)null) { return; } if (!((Component)pinSlotUI).gameObject.activeSelf) { pinSlotUI.SetActive(active: true); } if ((Object)(object)pinSlotUI.BgImage != (Object)null) { float a = ((Graphic)pinSlotUI.BgImage).color.a; if (Mathf.Abs(a - RecipePinnerPlugin.BackgroundOpacity.Value) > 0.01f) { ((Graphic)pinSlotUI.BgImage).color = new Color(0f, 0f, 0f, RecipePinnerPlugin.BackgroundOpacity.Value); } } bool flag = RecipePinnerPlugin.LayoutModeConfig.Value == RecipePinnerPlugin.PinLayoutMode.ForceBottomRightHorizontal; bool flag2 = (Object)(object)Player.m_localPlayer.GetControlledShip() != (Object)null; if (RecipePinnerPlugin.Instance.IsHorizontalMode || flag2) { RectTransform val = pinSlotUI.Rect ?? ((Component)pinSlotUI).GetComponent<RectTransform>(); float num = ((flag || flag2) ? RecipePinnerPlugin.BottomRightColumnWidth.Value : RecipePinnerPlugin.HorizontalColumnWidth.Value); if (Mathf.Abs(val.sizeDelta.x - num) > 1f) { val.sizeDelta = new Vector2(num, val.sizeDelta.y); } } bool flag3 = pinSlotUI.CurrentData != pinData; pinSlotUI.CurrentData = pinData; foreach (PinnedResData resource in pinData.Resources) { int value = 0; _reusableInvCounts.TryGetValue(resource.ItemName, out value); int value2 = 0; if (RecipePinnerPlugin.EnableChestScanning.Value) { containerMgr.ContainerCache.TryGetValue(resource.ItemName, out value2); } int num2 = value + value2; if (num2 != resource.LastKnownAmount || value != resource.LastKnownInvAmount || resource.CachedAmountString == null) { resource.LastKnownAmount = num2; resource.LastKnownInvAmount = value; Color val2 = ((value >= resource.RequiredAmount) ? RecipePinnerPlugin.ColorEnoughInInventory.Value : ((num2 >= resource.RequiredAmount) ? RecipePinnerPlugin.ColorEnoughWithChests.Value : RecipePinnerPlugin.ColorMissing.Value)); string arg = "#" + ColorUtility.ToHtmlStringRGBA(val2); string text = $"<color={arg}>{num2}/{resource.RequiredAmount}</color>"; if (resource.CachedAmountString != text) { resource.CachedAmountString = text; pinData.IsDirty = true; } } } if (flag3 || pinData.IsDirty) { pinSlotUI.UpdateData(pinData, _cachedFont); pinData.IsDirty = false; } } private void CreateCanvasUI() { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_pinListRoot != (Object)null) { return; } if ((Object)(object)Hud.instance == (Object)null || (Object)(object)Hud.instance.m_rootObject == (Object)null) { DebugLogger.Warning("Cannot create canvas - Hud.instance is null"); return; } if ((Object)(object)_cachedFont == (Object)null) { _cachedFont = GetGameFont(); } if ((Object)(object)_cachedFont == (Object)null) { DebugLogger.Error("Cannot create UI - no valid font found"); return; } DebugLogger.Log("Creating canvas UI..."); Transform transform = Hud.instance.m_rootObject.transform; GameObject val = new GameObject("PinListRoot", new Type[1] { typeof(RectTransform) }); val.layer = 5; val.transform.SetParent(transform, false); _pinListRoot = val.transform; _pinListRoot.localScale = Vector3.one * RecipePinnerPlugin.UIScale.Value; RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = new Vector2(1f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(1f, 1f); bool flag = (Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)Player.m_localPlayer.GetControlledShip() != (Object)null; bool flag2 = RecipePinnerPlugin.LayoutModeConfig.Value == RecipePinnerPlugin.PinLayoutMode.ForceBottomRightHorizontal; bool flag3 = flag || flag2; bool flag4 = RecipePinnerPlugin.Instance.IsHorizontalMode || flag; DebugLogger.Verbose($"UI Layout - Horizontal: {flag4}, BottomRight: {flag3}, Sailing: {flag}"); if (flag4) { HorizontalLayoutGroup val2 = val.AddComponent<HorizontalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)val2).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val2).childControlWidth = false; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandWidth = false; ((LayoutGroup)val2).childAlignment = (TextAnchor)(flag3 ? 8 : 2); ((HorizontalOrVerticalLayoutGroup)val2).spacing = RecipePinnerPlugin.HorizontalPinSpacing.Value; ContentSizeFitter val3 = val.AddComponent<ContentSizeFitter>(); val3.horizontalFit = (FitMode)2; val3.verticalFit = (FitMode)2; } else { VerticalLayoutGroup val4 = val.AddComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)val4).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val4).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val4).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val4).spacing = 8f; ContentSizeFitter val5 = val.AddComponent<ContentSizeFitter>(); val5.verticalFit = (FitMode)2; } for (int i = 0; i < RecipePinnerPlugin.MaximumPins.Value; i++) { PinSlotUI pinSlotUI = UIBuilder.CreatePinSlot(_pinListRoot, _cachedFont); if ((Object)(object)pinSlotUI != (Object)null) { pinSlotUI.SetActive(active: false); _pinPool.A