using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("TrinketAndBindingFramework")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("TrinketAndBindingFramework")] [assembly: AssemblyTitle("TrinketAndBindingFramework")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 TrinketAndBindingFramework { [BepInPlugin("com.cicismods.trinketandbindingframework", "CiCi's Trinket & Binding Framework", "1.3.0")] public class Plugin : BaseUnityPlugin { public const string GUID = "com.cicismods.trinketandbindingframework"; public const string NAME = "CiCi's Trinket & Binding Framework"; public const string VERSION = "1.3.0"; public static ManualLogSource Log; private void Awake() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; new Harmony("com.cicismods.trinketandbindingframework").PatchAll(); Log.LogInfo((object)"CiCi's Trinket & Binding Framework 1.3.0 loaded"); } } public static class TrinketRegistry { public enum Bucket { Vanilla, Parasite, Chimney } public enum Category { Consumable, Tool, Other, Artifact, Perk } internal class Entry { public string id; public string title; public string description; public string flavorText; public bool isBinding; public int cost; public float scoreMultiplierBonus; public float scoreBonus; public Sprite icon; public Func> itemsToGrantFactory; public Func> perksToGrantFactory; public int pouchesToGrant; public Trinket runtime; public int stackCount = 1; public int maxStacks = 1; public string titleColor; public Bucket bucket; public Category category = Category.Other; public int tier; public bool isFrameworkRegistered; public bool pinToVanilla; public bool disablesLeaderboards = true; } internal static readonly Dictionary _registry = new Dictionary(); internal static readonly Dictionary> _mutexGroups = new Dictionary>(); internal static Bucket BucketFromColor(string colorHex) { if (string.IsNullOrEmpty(colorHex)) { return Bucket.Vanilla; } string text = colorHex.TrimStart('#').ToUpperInvariant(); if (text == "18420F") { return Bucket.Parasite; } if (text == "8DDDF5") { return Bucket.Chimney; } return Bucket.Vanilla; } internal static bool IsGrey(string colorHex) { if (string.IsNullOrEmpty(colorHex)) { return true; } return colorHex.TrimStart('#').ToUpperInvariant() == "D3D3D3"; } internal static Entry FindEntryForTrinket(Trinket t) { if ((Object)(object)t == (Object)null) { return null; } foreach (KeyValuePair item in _registry) { if ((Object)(object)item.Value.runtime == (Object)(object)t) { return item.Value; } } return null; } public static void RegisterMutexGroup(string groupId, params string[] entryIds) { if (!string.IsNullOrEmpty(groupId) && entryIds != null) { List value = new List(entryIds); _mutexGroups[groupId] = value; } } public static Trinket GetRuntimeTrinket(string id) { if (string.IsNullOrEmpty(id)) { return null; } if (!_registry.TryGetValue(id, out var value)) { return null; } return value.runtime; } public static void RegisterTrinket(string id, string title, string description, string flavorText = "", int cost = 1, float scoreMultiplierBonus = 0f, float scoreBonus = 0f, Sprite icon = null, Func> itemsToGrantFactory = null, Func> perksToGrantFactory = null, int pouchesToGrant = 0) { Register(id, title, description, flavorText, isBinding: false, cost, scoreMultiplierBonus, scoreBonus, icon, itemsToGrantFactory, perksToGrantFactory, pouchesToGrant); } public static void RegisterBinding(string id, string title, string description, string flavorText = "", int cost = 1, float scoreMultiplierBonus = 0f, float scoreBonus = 0f, Sprite icon = null, Func> itemsToGrantFactory = null, Func> perksToGrantFactory = null, int pouchesToGrant = 0) { Register(id, title, description, flavorText, isBinding: true, cost, scoreMultiplierBonus, scoreBonus, icon, itemsToGrantFactory, perksToGrantFactory, pouchesToGrant); } private static void Register(string id, string title, string description, string flavorText, bool isBinding, int cost, float scoreMultiplierBonus, float scoreBonus, Sprite icon, Func> itemsToGrantFactory, Func> perksToGrantFactory, int pouchesToGrant) { if (string.IsNullOrEmpty(id)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Registry] empty id, skipped"); } return; } _registry[id] = new Entry { id = id, title = title, description = description, flavorText = (flavorText ?? ""), isBinding = isBinding, cost = cost, scoreMultiplierBonus = scoreMultiplierBonus, scoreBonus = scoreBonus, icon = icon, itemsToGrantFactory = itemsToGrantFactory, perksToGrantFactory = perksToGrantFactory, pouchesToGrant = pouchesToGrant }; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[Registry] " + (isBinding ? "binding" : "trinket") + ": " + id)); } } } [HarmonyPatch(typeof(Trinket), "IsUnlocked")] public static class TrinketUnlockBypassPatch { [HarmonyPostfix] public static void Postfix(ref bool __result) { __result = true; } } [HarmonyPatch(typeof(UI_TrinketPicker), "SelectTrinket")] public static class MutexEnforcePatch { [HarmonyPrefix] public static void Prefix(UI_TrinketPicker __instance, Trinket t) { if ((Object)(object)__instance == (Object)null || __instance.selectedTrinkets == null || (Object)(object)t == (Object)null || TrinketRegistry._mutexGroups.Count == 0) { return; } string text = null; foreach (KeyValuePair item in TrinketRegistry._registry) { if ((Object)(object)item.Value.runtime == (Object)(object)t) { text = item.Value.id; break; } } if (text == null) { return; } List list = null; foreach (KeyValuePair> mutexGroup in TrinketRegistry._mutexGroups) { if (mutexGroup.Value.Contains(text)) { list = mutexGroup.Value; break; } } if (list == null || __instance.selectedTrinkets.Contains(t)) { return; } for (int num = __instance.selectedTrinkets.Count - 1; num >= 0; num--) { Trinket val = __instance.selectedTrinkets[num]; foreach (KeyValuePair item2 in TrinketRegistry._registry) { if ((Object)(object)item2.Value.runtime == (Object)(object)val && list.Contains(item2.Value.id)) { __instance.selectedTrinkets.RemoveAt(num); break; } } } } } [HarmonyPatch(typeof(Trinket), "GetDescription")] public static class TrinketCustomColorPatch { [HarmonyPostfix] public static void Postfix(Trinket __instance, ref string __result) { if ((Object)(object)__instance == (Object)null) { return; } TrinketRegistry.Entry entry = null; foreach (KeyValuePair item in TrinketRegistry._registry) { if ((Object)(object)item.Value.runtime == (Object)(object)__instance) { entry = item.Value; break; } } if (entry != null && !string.IsNullOrEmpty(entry.titleColor)) { string text = (__instance.isBinding ? "Binding" : "Trinket"); string text2 = (__instance.isBinding ? ("" + __instance.description + "") : __instance.description); __result = "" + text + ": " + __instance.title + ". " + text2 + "\n" + __instance.flavorText + ""; } } } [HarmonyPatch(typeof(Trinket), "GetLockedDescription")] public static class LockedDescriptionNullGuardPatch { [HarmonyPrefix] public static bool Prefix(Trinket __instance, ref string __result) { if ((Object)(object)__instance == (Object)null) { return true; } if ((Object)(object)__instance.progressionUnlock == (Object)null && !__instance.comingSoon) { __result = __instance.GetDescription() + "\n(Locked by current selection.)"; return false; } return true; } } [HarmonyPatch(typeof(CL_AssetManager), "GetTrinketAsset")] public static class TrinketAssetLookupPatch { [HarmonyPostfix] public static void Postfix(string id, ref Trinket __result) { if ((Object)(object)__result != (Object)null || string.IsNullOrEmpty(id)) { return; } string value = id.ToLowerInvariant(); foreach (KeyValuePair item in TrinketRegistry._registry) { TrinketRegistry.Entry value2 = item.Value; if (!((Object)(object)value2.runtime == (Object)null) && ((Object)value2.runtime).name != null && ((Object)value2.runtime).name.ToLowerInvariant().Contains(value)) { __result = value2.runtime; break; } } } } [HarmonyPatch(typeof(UI_TrinketPicker), "ReloadTrinkets")] public static class TrinketInjectionPatch { private static readonly FieldInfoCache CurrentGamemodeField = new FieldInfoCache(typeof(UI_TrinketPicker), "currentGamemode"); private static List _orderedTrinkets; private static List _orderedBindings; private static int _cachedRegistryCount = -1; [HarmonyPrefix] public static void Prefix(UI_TrinketPicker __instance) { object obj = CurrentGamemodeField.Get(__instance); InjectInto((M_Gamemode)(((obj is M_Gamemode) ? obj : null) ?? CL_GameManager.GetBaseGamemode())); } internal static void InjectInto(M_Gamemode gamemode) { if ((Object)(object)gamemode == (Object)null) { return; } PerkBindingAutoRegistrar.TryRun(); ItemTrinketAutoRegistrar.TryRun(); ArtifactTrinketAutoRegistrar.TryRun(); ConsumableTrinketAutoRegistrar.TryRun(); ClimbingItemTrinketAutoRegistrar.TryRun(); MiscTrinketAutoRegistrar.TryRun(); UnusedVanillaTrinketAutoRegistrar.TryRun(); NoHammerBindingRegistrar.TryRun(); if (TrinketRegistry._registry.Count == 0) { return; } if ((Object)(object)gamemode.availableTrinkets == (Object)null) { TrinketList val = ScriptableObject.CreateInstance(); ((Object)val).name = "TrinketList_CiCi_Auto_" + (gamemode.gamemodeName ?? "unknown"); val.trinkets = new List(); val.bindings = new List(); Object.DontDestroyOnLoad((Object)(object)val); gamemode.availableTrinkets = val; } if (gamemode.availableTrinkets.trinkets == null) { gamemode.availableTrinkets.trinkets = new List(); } if (gamemode.availableTrinkets.bindings == null) { gamemode.availableTrinkets.bindings = new List(); } gamemode.useGamemodeSettings = true; BuildRegistryRuntimes(gamemode); RebuildMasterListsIfStale(); List trinkets = gamemode.availableTrinkets.trinkets; List bindings = gamemode.availableTrinkets.bindings; trinkets.Clear(); bindings.Clear(); foreach (Trinket orderedTrinket in _orderedTrinkets) { trinkets.Add(orderedTrinket); } foreach (Trinket orderedBinding in _orderedBindings) { bindings.Add(orderedBinding); } foreach (KeyValuePair item in TrinketRegistry._registry) { TrinketRegistry.Entry value = item.Value; if (!((Object)(object)value.runtime == (Object)null) && value.pinToVanilla) { List list = (value.isBinding ? bindings : trinkets); if (!list.Contains(value.runtime)) { list.Add(value.runtime); } } } List list2 = new List(); List list3 = new List(); foreach (KeyValuePair item2 in TrinketRegistry._registry) { TrinketRegistry.Entry value2 = item2.Value; if (!((Object)(object)value2.runtime == (Object)null) && !value2.pinToVanilla) { (value2.isBinding ? list3 : list2).Add(value2.runtime); } } list2.Sort(CompareTrinkets); list3.Sort(CompareTrinkets); foreach (Trinket item3 in list2) { if (!trinkets.Contains(item3)) { trinkets.Add(item3); } } foreach (Trinket item4 in list3) { if (!bindings.Contains(item4)) { bindings.Add(item4); } } } internal static int CompareTrinkets(Trinket a, Trinket b) { TrinketRegistry.Entry entry = TrinketRegistry.FindEntryForTrinket(a); TrinketRegistry.Entry entry2 = TrinketRegistry.FindEntryForTrinket(b); bool flag = entry?.isFrameworkRegistered ?? false; bool flag2 = entry2?.isFrameworkRegistered ?? false; if (flag != flag2) { if (!flag) { return -1; } return 1; } int num = (int)(entry?.bucket ?? TrinketRegistry.Bucket.Vanilla); int num2 = (int)(entry2?.bucket ?? TrinketRegistry.Bucket.Vanilla); if (num != num2) { return num - num2; } int num3 = (int)(entry?.category ?? TrinketRegistry.Category.Other); int num4 = (int)(entry2?.category ?? TrinketRegistry.Category.Other); if (num3 != num4) { return num3 - num4; } bool flag3 = TrinketRegistry.IsGrey(entry?.titleColor); bool flag4 = TrinketRegistry.IsGrey(entry2?.titleColor); if (flag3 != flag4) { if (!flag3) { return 1; } return -1; } int num5 = entry?.tier ?? 0; int num6 = entry2?.tier ?? 0; if (num5 != num6) { return num5 - num6; } return string.Compare(a?.title ?? "", b?.title ?? "", StringComparison.OrdinalIgnoreCase); } private static void BuildRegistryRuntimes(M_Gamemode gamemode) { Sprite val = FindBorrowableIcon(gamemode.availableTrinkets?.trinkets); Sprite val2 = FindBorrowableIcon(gamemode.availableTrinkets?.bindings); foreach (KeyValuePair item in TrinketRegistry._registry) { TrinketRegistry.Entry value = item.Value; if ((Object)(object)value.runtime == (Object)null) { value.runtime = BuildTrinketSO(value); } if ((Object)(object)value.runtime == (Object)null) { continue; } if ((Object)(object)value.runtime.icon == (Object)null) { Sprite val3 = (value.isBinding ? val2 : val); value.runtime.icon = value.icon ?? val3; value.runtime.lockIcon = value.runtime.icon; } if (value.itemsToGrantFactory != null) { try { value.runtime.itemsToGrant = value.itemsToGrantFactory() ?? new List(); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Registry] " + value.id + " items factory: " + ex.Message)); } } } if (value.perksToGrantFactory == null) { continue; } try { value.runtime.perksToGrant = value.perksToGrantFactory() ?? new List(); } catch (Exception ex2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[Registry] " + value.id + " perks factory: " + ex2.Message)); } } } } private static void RebuildMasterListsIfStale() { if (_orderedTrinkets != null && _orderedBindings != null && _cachedRegistryCount == TrinketRegistry._registry.Count) { return; } List list = new List(); List list2 = new List(); HashSet hashSet = new HashSet(); M_Gamemode val = FindOrderingSourceGamemode(); if ((Object)(object)val != (Object)null && (Object)(object)val.availableTrinkets != (Object)null) { if (val.availableTrinkets.trinkets != null) { foreach (Trinket trinket in val.availableTrinkets.trinkets) { if (!((Object)(object)trinket == (Object)null) && hashSet.Add(trinket) && TrinketRegistry.FindEntryForTrinket(trinket) == null) { list.Add(trinket); } } } if (val.availableTrinkets.bindings != null) { foreach (Trinket binding in val.availableTrinkets.bindings) { if (!((Object)(object)binding == (Object)null) && hashSet.Add(binding) && TrinketRegistry.FindEntryForTrinket(binding) == null) { list2.Add(binding); } } } } Trinket[] array = Resources.FindObjectsOfTypeAll(); if (array != null) { foreach (Trinket val2 in array) { if (!((Object)(object)val2 == (Object)null) && hashSet.Add(val2) && TrinketRegistry.FindEntryForTrinket(val2) == null) { if (val2.isBinding) { list2.Add(val2); } else { list.Add(val2); } } } } _orderedTrinkets = list; _orderedBindings = list2; _cachedRegistryCount = TrinketRegistry._registry.Count; } private static M_Gamemode FindOrderingSourceGamemode() { M_Gamemode[] array = Resources.FindObjectsOfTypeAll(); if (array == null) { return null; } foreach (M_Gamemode val in array) { if ((Object)(object)val != (Object)null && (Object)(object)val.availableTrinkets != (Object)null && val.gamemodeName == "Campaign") { return val; } } M_Gamemode result = null; int num = -1; foreach (M_Gamemode val2 in array) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.availableTrinkets == (Object)null)) { int num2 = (val2.availableTrinkets.trinkets?.Count ?? 0) + (val2.availableTrinkets.bindings?.Count ?? 0); if (num2 > num) { num = num2; result = val2; } } } return result; } private static Sprite FindBorrowableIcon(List source) { if (source == null) { return null; } foreach (Trinket item in source) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item.icon == (Object)null) && !item.comingSoon && item.IsUnlocked()) { return item.icon; } } foreach (Trinket item2 in source) { if ((Object)(object)item2 != (Object)null && (Object)(object)item2.icon != (Object)null) { return item2.icon; } } return null; } private static Trinket BuildTrinketSO(TrinketRegistry.Entry e) { Trinket obj = ScriptableObject.CreateInstance(); ((Object)obj).name = "Trinket_" + e.id; obj.title = e.title; obj.description = e.description; obj.flavorText = e.flavorText; obj.isBinding = e.isBinding; obj.cost = e.cost; obj.scoreMultiplierBonus = e.scoreMultiplierBonus; obj.scoreBonus = e.scoreBonus; obj.comingSoon = false; obj.settingBlacklist = new List(); obj.itemsToGrant = new List(); obj.perksToGrant = new List(); obj.pouchesToGrant = e.pouchesToGrant; obj.progressionUnlock = null; obj.icon = e.icon; obj.lockIcon = e.icon; Object.DontDestroyOnLoad((Object)(object)obj); return obj; } } [HarmonyPatch(typeof(UI_GamemodeScreen), "Initialize")] public static class GamemodeScreenInitInjectPatch { [HarmonyPrefix] public static void Prefix(M_Gamemode mode) { if (!((Object)(object)mode == (Object)null)) { TrinketInjectionPatch.InjectInto(mode); } } [HarmonyPostfix] public static void Postfix(UI_GamemodeScreen __instance) { if (__instance?.activePanels == null) { return; } foreach (KeyValuePair activePanel in __instance.activePanels) { UI_GamemodeScreen_Panel value = activePanel.Value; if ((Object)(object)value == (Object)null) { continue; } UI_TrinketPicker[] componentsInChildren = ((Component)value).GetComponentsInChildren(true); foreach (UI_TrinketPicker val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } if ((Object)(object)val.parentPanel == (Object)null) { val.parentPanel = value; } try { val.Initialize(); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("picker init failed on " + ((Object)value).name + ": " + ex.Message)); } } } } } } internal sealed class FieldInfoCache { private readonly FieldInfo _field; public FieldInfoCache(Type type, string name) { _field = AccessTools.Field(type, name); } public object Get(object instance) { return _field?.GetValue(instance); } } internal static class PickerReflection { public static readonly FieldInfoCache DescriptionTextField = new FieldInfoCache(typeof(UI_TrinketPicker), "descriptionText"); public static readonly FieldInfoCache ParentPickerField = new FieldInfoCache(typeof(UI_TrinketPicker_Icon), "parentPicker"); public static bool HasDescriptionText(UI_TrinketPicker picker) { if ((Object)(object)picker == (Object)null) { return false; } object obj = DescriptionTextField.Get(picker); Object val = (Object)((obj is Object) ? obj : null); if (val != null) { return val != (Object)null; } return false; } public static UI_TrinketPicker GetParentPicker(UI_TrinketPicker_Icon icon) { if (!((Object)(object)icon == (Object)null)) { object obj = ParentPickerField.Get(icon); return (UI_TrinketPicker)((obj is UI_TrinketPicker) ? obj : null); } return null; } } [HarmonyPatch(typeof(UI_TrinketPicker), "HideDescription")] public static class HideDescriptionNullGuardPatch { [HarmonyPrefix] public static bool Prefix(UI_TrinketPicker __instance) { return PickerReflection.HasDescriptionText(__instance); } } [HarmonyPatch(typeof(UI_TrinketPicker), "ShowDescription")] public static class ShowDescriptionNullGuardPatch { [HarmonyPrefix] public static bool Prefix(UI_TrinketPicker __instance) { return PickerReflection.HasDescriptionText(__instance); } } [HarmonyPatch(typeof(UI_TrinketPicker), "ShowTrinketDescription")] public static class ShowTrinketDescriptionNullGuardPatch { [HarmonyPrefix] public static bool Prefix(UI_TrinketPicker __instance, Trinket t) { if ((Object)(object)t != (Object)null) { return PickerReflection.HasDescriptionText(__instance); } return false; } } [HarmonyPatch(typeof(UI_TrinketPicker_Icon), "OnPointerEnter")] public static class IconPointerEnterNullGuardPatch { [HarmonyPrefix] public static bool Prefix(UI_TrinketPicker_Icon __instance) { if ((Object)(object)PickerReflection.GetParentPicker(__instance) != (Object)null) { return (Object)(object)__instance.trinket != (Object)null; } return false; } } [HarmonyPatch(typeof(UI_TrinketPicker_Icon), "OnPointerExit")] public static class IconPointerExitNullGuardPatch { [HarmonyPrefix] public static bool Prefix(UI_TrinketPicker_Icon __instance) { return (Object)(object)PickerReflection.GetParentPicker(__instance) != (Object)null; } } [HarmonyPatch(typeof(UI_TrinketPicker_Icon), "OnSelect")] public static class IconSelectNullGuardPatch { [HarmonyPrefix] public static bool Prefix(UI_TrinketPicker_Icon __instance) { if ((Object)(object)PickerReflection.GetParentPicker(__instance) != (Object)null) { return (Object)(object)__instance.trinket != (Object)null; } return false; } } [HarmonyPatch(typeof(UI_TrinketPicker_Icon), "OnDeselect")] public static class IconDeselectNullGuardPatch { [HarmonyPrefix] public static bool Prefix(UI_TrinketPicker_Icon __instance) { return (Object)(object)PickerReflection.GetParentPicker(__instance) != (Object)null; } } [HarmonyPatch(typeof(UI_TrinketPicker_Icon), "OnClick")] public static class IconClickNullGuardPatch { [HarmonyPrefix] public static bool Prefix(UI_TrinketPicker_Icon __instance) { if ((Object)(object)PickerReflection.GetParentPicker(__instance) != (Object)null) { return (Object)(object)__instance.trinket != (Object)null; } return false; } } public class PerkBindingClickOverride : MonoBehaviour, IPointerDownHandler, IEventSystemHandler { public string bindingId; public TextMeshProUGUI stackText; private int _lastProcessedFrame = -1; private static readonly MethodInfo _updateTrinketActivation = AccessTools.Method(typeof(UI_TrinketPicker), "UpdateTrinketActivation", (Type[])null, (Type[])null); public void OnPointerDown(PointerEventData eventData) { int frameCount = Time.frameCount; if (frameCount == _lastProcessedFrame) { return; } _lastProcessedFrame = frameCount; bool mouseButtonDown = Input.GetMouseButtonDown(0); bool mouseButtonDown2 = Input.GetMouseButtonDown(1); bool flag = mouseButtonDown2 && !mouseButtonDown; bool flag2 = mouseButtonDown && !mouseButtonDown2; if ((!flag && !flag2) || !TrinketRegistry._registry.TryGetValue(bindingId, out var value)) { return; } UI_TrinketPicker_Icon component = ((Component)this).GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component.trinket == (Object)null) { return; } UI_TrinketPicker parentPicker = PickerReflection.GetParentPicker(component); if ((Object)(object)parentPicker == (Object)null) { return; } bool flag3 = parentPicker.selectedTrinkets != null && parentPicker.selectedTrinkets.Contains(component.trinket); bool flag4 = false; if (flag2) { if (!flag3) { value.stackCount = 1; parentPicker.SelectTrinket(component.trinket); } else if (value.stackCount < value.maxStacks) { value.stackCount++; flag4 = true; } } else if (flag && flag3) { if (value.stackCount > 1) { value.stackCount--; flag4 = true; } else { value.stackCount = 1; parentPicker.SelectTrinket(component.trinket); } } if (flag4) { try { _updateTrinketActivation?.Invoke(parentPicker, null); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Stack] activation refresh failed: " + ex.Message)); } } } if ((Object)(object)value.runtime != (Object)null) { if (value.perksToGrantFactory != null) { try { value.runtime.perksToGrant = value.perksToGrantFactory(); } catch (Exception ex2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[Stack] perksToGrant rebuild failed for " + value.id + ": " + ex2.Message)); } } } if (value.itemsToGrantFactory != null) { try { value.runtime.itemsToGrant = value.itemsToGrantFactory(); } catch (Exception ex3) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("[Stack] itemsToGrant rebuild failed for " + value.id + ": " + ex3.Message)); } } } } UpdateStackVisual(value); } private void UpdateStackVisual(TrinketRegistry.Entry entry) { if (!((Object)(object)stackText == (Object)null)) { ((TMP_Text)stackText).text = ((entry.stackCount > 1) ? $"x{entry.stackCount}" : ""); } } } [HarmonyPatch(typeof(UI_TrinketPicker_Icon), "SetTrinket")] public static class PerkBindingIconAttachPatch { [HarmonyPostfix] public static void Postfix(UI_TrinketPicker_Icon __instance, Trinket t) { if ((Object)(object)__instance == (Object)null || (Object)(object)t == (Object)null || string.IsNullOrEmpty(((Object)t).name)) { return; } TrinketRegistry.Entry entry = null; foreach (KeyValuePair item in TrinketRegistry._registry) { if ((Object)(object)item.Value.runtime == (Object)(object)t) { entry = item.Value; break; } } if (entry != null && entry.maxStacks > 1) { Button component = ((Component)__instance).GetComponent