Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of FavoriteItems v1.2.0
plugins/FavoriteItems.dll
Decompiled a month agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FavoriteItems.API; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("FavoriteItems")] [assembly: AssemblyDescription("Favorite inventory stacks and an integration API for Valheim")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("FavoriteItems")] [assembly: AssemblyCopyright("Copyright (c) 2026 Ronaldo Donizeti")] [assembly: ComVisible(false)] [assembly: Guid("8c58b898-40d5-4c74-912d-cd2af0abf1f0")] [assembly: AssemblyFileVersion("1.2.0.0")] [assembly: AssemblyInformationalVersion("1.2.0")] [assembly: AssemblyVersion("1.2.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FavoriteItems { internal static class EquipmentAndQuickSlotsIntegration { internal const string PluginGuid = "randyknapp.mods.equipmentandquickslots"; private static MethodInfo _isSlotCell; private static bool _warned; internal static void Initialize() { Type type = Type.GetType("EquipmentAndQuickSlots.API, EquipmentAndQuickSlots", throwOnError: false); if (type == null) { FavoriteItemsPlugin.Log.LogInfo((object)"EquipmentAndQuickSlots was not detected; favorite protection remains active."); return; } _isSlotCell = type.GetMethod("IsSlotCell", BindingFlags.Static | BindingFlags.Public, null, new Type[3] { typeof(int), typeof(int), typeof(string).MakeByRefType() }, null); if (_isSlotCell == null) { FavoriteItemsPlugin.Log.LogWarning((object)"EquipmentAndQuickSlots was detected, but the IsSlotCell API is unavailable."); return; } FavoriteItemsApi.RegisterProtectionProvider("randyknapp.mods.equipmentandquickslots", IsProtectedSlot); FavoriteItemsPlugin.Log.LogInfo((object)"Automatic EquipmentAndQuickSlots slot protection is active."); } internal static void Shutdown() { FavoriteItemsApi.UnregisterProtectionProvider("randyknapp.mods.equipmentandquickslots"); _isSlotCell = null; _warned = false; } internal static bool IsProtectedSlot(ItemData item) { if (item == null || _isSlotCell == null || !FavoriteItemsPlugin.ProtectEquipmentAndQuickSlots.Value) { return false; } try { object[] parameters = new object[3] { item.m_gridPos.x, item.m_gridPos.y, null }; return (bool)_isSlotCell.Invoke(null, parameters); } catch (Exception ex) { if (!_warned) { _warned = true; FavoriteItemsPlugin.Log.LogWarning((object)("Failed to query the EquipmentAndQuickSlots API: " + ex.Message)); } return false; } } } internal static class ExtraSlotsIntegration { internal const string PluginGuid = "shudnal.ExtraSlots"; private static MethodInfo _isItemInSlot; private static bool _warned; internal static void Initialize() { Type type = Type.GetType("ExtraSlots.API, ExtraSlots", throwOnError: false); if (type == null) { FavoriteItemsPlugin.Log.LogInfo((object)"ExtraSlots was not detected; favorite protection remains active."); return; } _isItemInSlot = type.GetMethod("IsItemInSlot", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(ItemData) }, null); if (_isItemInSlot == null || _isItemInSlot.ReturnType != typeof(bool)) { FavoriteItemsPlugin.Log.LogWarning((object)"ExtraSlots was detected, but the IsItemInSlot API is unavailable."); return; } FavoriteItemsApi.RegisterProtectionProvider("shudnal.ExtraSlots", IsProtectedSlot); FavoriteItemsPlugin.Log.LogInfo((object)"Optional ExtraSlots slot protection is available."); } internal static void Shutdown() { FavoriteItemsApi.UnregisterProtectionProvider("shudnal.ExtraSlots"); _isItemInSlot = null; _warned = false; } internal static bool IsProtectedSlot(ItemData item) { if (item == null || _isItemInSlot == null || !FavoriteItemsPlugin.ProtectExtraSlots.Value) { return false; } try { return (bool)_isItemInSlot.Invoke(null, new object[1] { item }); } catch (Exception ex) { if (!_warned) { _warned = true; FavoriteItemsPlugin.Log.LogWarning((object)("Failed to query the ExtraSlots API: " + ex.Message)); } return false; } } } [HarmonyPatch(typeof(InventoryGrid), "OnLeftDown")] internal static class FavoriteInputPatches { private static readonly MethodInfo GetButtonPosMethod = AccessTools.Method(typeof(InventoryGrid), "GetButtonPos", new Type[1] { typeof(GameObject) }, (Type[])null); [HarmonyPriority(800)] private static bool Prefix(InventoryGrid __instance, UIInputHandler __0) { //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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) if (!FavoriteItemsPlugin.Enabled.Value || !AltIsPressed() || (Object)(object)__0 == (Object)null) { return true; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || __instance.GetInventory() != ((Humanoid)localPlayer).GetInventory() || GetButtonPosMethod == null) { return true; } try { Vector2i val = (Vector2i)GetButtonPosMethod.Invoke(__instance, new object[1] { ((Component)__0).gameObject }); ItemData itemAt = __instance.GetInventory().GetItemAt(val.x, val.y); if (itemAt == null) { return true; } if (!FavoriteItemsApi.TryToggleFavorite(itemAt, out var isFavorite)) { return true; } FavoriteManager.Notify(localPlayer, itemAt, isFavorite); return false; } catch (Exception ex) { FavoriteItemsPlugin.Log.LogWarning((object)("Could not toggle favorite state: " + ex.Message)); return true; } } private static bool AltIsPressed() { if (!ZInput.GetKey((KeyCode)308, true)) { return ZInput.GetKey((KeyCode)307, true); } return true; } } [BepInPlugin("com.ronaldo.valheim.favoriteitems", "FavoriteItems", "1.2.0")] [BepInProcess("valheim.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class FavoriteItemsPlugin : BaseUnityPlugin { public const string PluginGuid = "com.ronaldo.valheim.favoriteitems"; public const string PluginName = "FavoriteItems"; public const string PluginVersion = "1.2.0"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> ShowMarker; internal static ConfigEntry<bool> ShowMessages; internal static ConfigEntry<bool> ProtectEquipmentAndQuickSlots; internal static ConfigEntry<bool> ProtectExtraSlots; private Harmony _harmony; private void Awake() { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enables Alt-click favorite toggling, the visual marker, and quick-stack protection."); ShowMarker = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "ShowMarker", true, "Shows a subtle star in the corner of favorite stacks."); ShowMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "ShowMessages", true, "Shows a short message when an item is favorited or unfavorited."); ProtectEquipmentAndQuickSlots = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "ProtectEquipmentAndQuickSlots", false, "Automatically protects items in EquipmentAndQuickSlots special slots, even when they are not favorited."); ProtectExtraSlots = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "ProtectExtraSlots", false, "Automatically protects items in ExtraSlots special slots, even when they are not favorited."); _harmony = new Harmony("com.ronaldo.valheim.favoriteitems"); _harmony.PatchAll(typeof(FavoriteItemsPlugin).Assembly); EquipmentAndQuickSlotsIntegration.Initialize(); ExtraSlotsIntegration.Initialize(); GorilaChestIntegration.Initialize(_harmony); Log.LogInfo((object)"FavoriteItems 1.2.0 loaded. Use Alt+left-click to toggle favorites."); } private void OnDestroy() { EquipmentAndQuickSlotsIntegration.Shutdown(); ExtraSlotsIntegration.Shutdown(); ProtectionRegistry.Clear(); FavoriteItemsApi.Shutdown(); FavoriteVisualPatches.Cleanup(); if (_harmony != null) { _harmony.UnpatchSelf(); } } } internal static class FavoriteManager { internal const string FavoriteKey = "com.ronaldo.valheim.favoriteitems.favorite"; internal static bool IsFavorite(ItemData item) { if (item != null && item.m_customData != null && item.m_customData.TryGetValue("com.ronaldo.valheim.favoriteitems.favorite", out var value)) { return value == "1"; } return false; } internal static bool SetFavorite(ItemData item, bool favorite) { if (item == null) { return false; } if (item.m_customData == null) { item.m_customData = new Dictionary<string, string>(); } if (favorite) { item.m_customData["com.ronaldo.valheim.favoriteitems.favorite"] = "1"; } else { item.m_customData.Remove("com.ronaldo.valheim.favoriteitems.favorite"); } return favorite; } internal static void Notify(Player player, ItemData item, bool favorite) { if (!((Object)(object)player == (Object)null) && item != null && FavoriteItemsPlugin.ShowMessages.Value) { string text = ((item.m_shared != null) ? item.m_shared.m_name : "item"); if (Localization.instance != null) { text = Localization.instance.Localize(text); } ((Character)player).Message((MessageType)2, favorite ? ("★ Favorite: " + text) : ("☆ Removed from favorites: " + text), 0, (Sprite)null, false); } } } [HarmonyPatch(typeof(InventoryGrid), "UpdateGui")] internal static class FavoriteVisualPatches { private const string MarkerName = "FavoriteItems_Star"; private static readonly List<GameObject> CreatedMarkers = new List<GameObject>(); [HarmonyPriority(0)] private static void Postfix(InventoryGrid __instance, List<InventoryElement> ___m_elements) { if (___m_elements == null) { return; } Inventory inventory = __instance.GetInventory(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || inventory != ((Humanoid)localPlayer).GetInventory()) { return; } for (int i = 0; i < ___m_elements.Count; i++) { InventoryElement val = ___m_elements[i]; if (!((Object)(object)val == (Object)null)) { Transform val2 = ((Component)val).transform.Find("FavoriteItems_Star"); if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(false); } } } if (!FavoriteItemsPlugin.Enabled.Value || !FavoriteItemsPlugin.ShowMarker.Value) { return; } int width = inventory.GetWidth(); List<ItemData> allItems = inventory.GetAllItems(); for (int j = 0; j < allItems.Count; j++) { ItemData val3 = allItems[j]; if (!FavoriteManager.IsFavorite(val3)) { continue; } int num = val3.m_gridPos.y * width + val3.m_gridPos.x; if (num < 0 || num >= ___m_elements.Count) { continue; } InventoryElement val4 = ___m_elements[num]; if (!((Object)(object)val4 == (Object)null)) { GameObject val5 = EnsureMarker(val4); if ((Object)(object)val5 != (Object)null) { val5.SetActive(true); } } } } private static GameObject EnsureMarker(InventoryElement element) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_00f0: Unknown result type (might be due to invalid IL or missing references) Transform val = ((Component)element).transform.Find("FavoriteItems_Star"); if ((Object)(object)val != (Object)null) { return ((Component)val).gameObject; } GameObject val2 = new GameObject("FavoriteItems_Star", new Type[1] { typeof(RectTransform) }); val2.transform.SetParent(((Component)element).transform, false); val2.transform.SetAsLastSibling(); RectTransform component = val2.GetComponent<RectTransform>(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; TextMeshProUGUI val3 = val2.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val3).text = "★"; ((TMP_Text)val3).fontSize = 14f; ((TMP_Text)val3).fontStyle = (FontStyles)1; ((TMP_Text)val3).alignment = (TextAlignmentOptions)260; ((TMP_Text)val3).margin = new Vector4(0f, 1f, 2f, 0f); ((Graphic)val3).color = new Color(1f, 0.78f, 0.15f, 0.95f); ((Graphic)val3).raycastTarget = false; if ((Object)(object)element.m_amount != (Object)null) { ((TMP_Text)val3).font = element.m_amount.font; ((TMP_Text)val3).fontSharedMaterial = element.m_amount.fontSharedMaterial; } CreatedMarkers.Add(val2); return val2; } internal static void Cleanup() { for (int i = 0; i < CreatedMarkers.Count; i++) { if ((Object)(object)CreatedMarkers[i] != (Object)null) { Object.Destroy((Object)(object)CreatedMarkers[i]); } } CreatedMarkers.Clear(); } } internal static class GorilaChestIntegration { internal const string PluginGuid = "dev.trentini.gorilachestmod"; internal static void Initialize(Harmony harmony) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown Type type = Type.GetType("GorilaChestMod.QuickStack, GorilaChestMod", throwOnError: false); if (type == null) { FavoriteItemsPlugin.Log.LogInfo((object)"GorilaChestMod was not detected; favorites and the visual marker remain active."); return; } MethodInfo methodInfo = AccessTools.Method(type, "CanMove", new Type[3] { typeof(Player), typeof(Inventory), typeof(ItemData) }, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(GorilaChestIntegration), "CanMovePostfix", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null || methodInfo.ReturnType != typeof(bool)) { FavoriteItemsPlugin.Log.LogWarning((object)"GorilaChestMod was detected, but QuickStack.CanMove has changed; protection was not applied."); return; } harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); FavoriteItemsPlugin.Log.LogInfo((object)"GorilaChestMod quick-stack protection is active."); } public static void CanMovePostfix(ItemData __2, ref bool __result) { if (__result && __2 != null && FavoriteItemsApi.ShouldPreventAutomaticMove(__2)) { __result = false; } } } internal static class ProtectionRegistry { private sealed class ProviderEntry { internal string OwnerId; internal Func<ItemData, bool> Provider; } private static readonly object SyncRoot = new object(); private static readonly Dictionary<string, Func<ItemData, bool>> Providers = new Dictionary<string, Func<ItemData, bool>>(StringComparer.Ordinal); private static readonly HashSet<string> WarnedProviders = new HashSet<string>(StringComparer.Ordinal); internal static bool Register(string ownerId, Func<ItemData, bool> provider) { if (string.IsNullOrWhiteSpace(ownerId) || provider == null) { return false; } lock (SyncRoot) { Providers[ownerId] = provider; WarnedProviders.Remove(ownerId); } return true; } internal static bool Unregister(string ownerId) { if (string.IsNullOrWhiteSpace(ownerId)) { return false; } lock (SyncRoot) { WarnedProviders.Remove(ownerId); return Providers.Remove(ownerId); } } internal static bool IsProtected(ItemData item) { if (item == null) { return false; } ProviderEntry[] array; lock (SyncRoot) { array = new ProviderEntry[Providers.Count]; int num = 0; foreach (KeyValuePair<string, Func<ItemData, bool>> provider in Providers) { ProviderEntry providerEntry = new ProviderEntry(); providerEntry.OwnerId = provider.Key; providerEntry.Provider = provider.Value; array[num++] = providerEntry; } } for (int i = 0; i < array.Length; i++) { try { if (array[i].Provider(item)) { return true; } } catch (Exception ex) { bool flag; lock (SyncRoot) { flag = WarnedProviders.Add(array[i].OwnerId); } if (flag) { LogWarning("Protection provider '" + array[i].OwnerId + "' failed: " + ex.Message); } } } return false; } internal static void Clear() { lock (SyncRoot) { Providers.Clear(); WarnedProviders.Clear(); } } private static void LogWarning(string message) { if (FavoriteItemsPlugin.Log != null) { FavoriteItemsPlugin.Log.LogWarning((object)message); } } } internal static class StackSafetyPatches { internal sealed class StackSnapshot { internal readonly List<StackEntry> Entries = new List<StackEntry>(); } internal sealed class StackEntry { internal ItemData Item; internal int Amount; } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] internal static class AddItemWholePatch { private static void Prefix(Inventory __instance, ItemData __0, out StackSnapshot __state) { __state = null; if (!FavoriteItemsPlugin.Enabled.Value || !FavoriteManager.IsFavorite(__0) || __instance == null || __0.m_shared == null) { return; } StackSnapshot stackSnapshot = new StackSnapshot(); List<ItemData> allItems = __instance.GetAllItems(); for (int i = 0; i < allItems.Count; i++) { ItemData val = allItems[i]; if (val != null && val.m_shared != null && val.IsSameType(__0) && val.m_stack < val.m_shared.m_maxStackSize) { StackEntry stackEntry = new StackEntry(); stackEntry.Item = val; stackEntry.Amount = val.m_stack; stackSnapshot.Entries.Add(stackEntry); } } __state = stackSnapshot; } private static void Postfix(StackSnapshot __state) { if (__state == null) { return; } for (int i = 0; i < __state.Entries.Count; i++) { StackEntry stackEntry = __state.Entries[i]; if (stackEntry.Item != null && stackEntry.Item.m_stack > stackEntry.Amount) { FavoriteManager.SetFavorite(stackEntry.Item, favorite: true); } } } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool) })] internal static class AddItemAtPositionPatch { private static void Prefix(Inventory __instance, ItemData __0, int __1, int __2, int __3) { if (FavoriteItemsPlugin.Enabled.Value && FavoriteManager.IsFavorite(__0) && __instance != null && __0 != null && __0.m_shared != null && __1 > 0) { ItemData itemAt = __instance.GetItemAt(__2, __3); if (itemAt != null && itemAt.IsSameType(__0) && itemAt.m_shared != null && itemAt.m_stack < itemAt.m_shared.m_maxStackSize) { FavoriteManager.SetFavorite(itemAt, favorite: true); } } } } } } namespace FavoriteItems.API { public static class FavoriteItemsApi { public const int ApiVersion = 1; public static bool IsEnabled { get { if (FavoriteItemsPlugin.Enabled != null) { return FavoriteItemsPlugin.Enabled.Value; } return false; } } public static event Action<ItemData, bool> FavoriteChanged; public static bool IsFavorite(ItemData item) { return FavoriteManager.IsFavorite(item); } public static bool ShouldPreventAutomaticMove(ItemData item) { if (!IsEnabled || item == null) { return false; } if (!FavoriteManager.IsFavorite(item)) { return ProtectionRegistry.IsProtected(item); } return true; } public static bool TrySetFavorite(ItemData item, bool favorite) { if (!IsEnabled || item == null) { return false; } if (FavoriteManager.IsFavorite(item) == favorite) { return true; } FavoriteManager.SetFavorite(item, favorite); RaiseFavoriteChanged(item, favorite); return true; } public static bool TryToggleFavorite(ItemData item, out bool isFavorite) { isFavorite = FavoriteManager.IsFavorite(item); if (!IsEnabled || item == null) { return false; } isFavorite = !isFavorite; FavoriteManager.SetFavorite(item, isFavorite); RaiseFavoriteChanged(item, isFavorite); return true; } public static bool RegisterProtectionProvider(string ownerId, Func<ItemData, bool> provider) { return ProtectionRegistry.Register(ownerId, provider); } public static bool UnregisterProtectionProvider(string ownerId) { return ProtectionRegistry.Unregister(ownerId); } internal static void Shutdown() { FavoriteItemsApi.FavoriteChanged = null; } private static void RaiseFavoriteChanged(ItemData item, bool favorite) { Action<ItemData, bool> favoriteChanged = FavoriteItemsApi.FavoriteChanged; if (favoriteChanged == null) { return; } Delegate[] invocationList = favoriteChanged.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { try { ((Action<ItemData, bool>)invocationList[i])(item, favorite); } catch (Exception ex) { if (FavoriteItemsPlugin.Log != null) { FavoriteItemsPlugin.Log.LogWarning((object)("A FavoriteChanged subscriber failed: " + ex.Message)); } } } } } }