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