Decompiled source of HaldorOverhaul v1.0.5
HaldorOverhaul.dll
Decompiled 6 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using GUIFramework; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Haldor Overhaul")] [assembly: AssemblyDescription("Custom buy/sell trading for Haldor")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("HaldorOverhaul")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("b7e4f347-ccbb-4fa9-9a20-b39679b6d0f7")] [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")] [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 HaldorOverhaul { public static class ConfigLoader { private static string BuyFile => Path.Combine(Paths.ConfigPath, "HaldorOverhaul.haldor.buy.json"); private static string SellFile => Path.Combine(Paths.ConfigPath, "HaldorOverhaul.haldor.sell.json"); public static List<TradeEntry> BuyEntries { get; private set; } = new List<TradeEntry>(); public static List<TradeEntry> SellEntries { get; private set; } = new List<TradeEntry>(); public static void Initialize() { EnsureConfigFilesExist(); LoadBuyConfig(); LoadSellConfig(); ValidateAndLogStats(); } private static void ValidateAndLogStats() { int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; foreach (TradeEntry item in BuyEntries.ToList()) { if (!ValidateEntry(item, "BUY")) { BuyEntries.Remove(item); num2++; } else { num++; } } foreach (TradeEntry item2 in SellEntries.ToList()) { if (!ValidateEntry(item2, "SELL")) { SellEntries.Remove(item2); num4++; } else { num3++; } } if (num2 > 0 || num4 > 0) { HaldorOverhaul.Log.LogWarning((object)string.Format("{0} Removed {1} invalid BUY entries and {2} invalid SELL entries.", "[ConfigLoader]", num2, num4)); } HaldorOverhaul.Log.LogInfo((object)string.Format("{0} Config validated: {1} BUY entries, {2} SELL entries.", "[ConfigLoader]", num, num3)); } private static bool ValidateEntry(TradeEntry entry, string type) { if (entry == null) { HaldorOverhaul.Log.LogWarning((object)("[ConfigLoader] " + type + " entry is null, skipping.")); return false; } if (string.IsNullOrWhiteSpace(entry.prefab)) { HaldorOverhaul.Log.LogWarning((object)("[ConfigLoader] " + type + " entry has empty prefab name, skipping.")); return false; } if (entry.price < 0) { HaldorOverhaul.Log.LogWarning((object)string.Format("{0} {1} entry '{2}' has negative price ({3}), setting to 0.", "[ConfigLoader]", type, entry.prefab, entry.price)); entry.price = 0; } if (entry.stack <= 0) { HaldorOverhaul.Log.LogWarning((object)string.Format("{0} {1} entry '{2}' has invalid stack ({3}), setting to 1.", "[ConfigLoader]", type, entry.prefab, entry.stack)); entry.stack = 1; } return true; } private static void EnsureConfigFilesExist() { if (!File.Exists(BuyFile)) { File.WriteAllText(BuyFile, DefaultBuyJson()); HaldorOverhaul.Log.LogInfo((object)"[ConfigLoader] Created default BUY config."); } if (!File.Exists(SellFile)) { File.WriteAllText(SellFile, DefaultSellJson()); HaldorOverhaul.Log.LogInfo((object)"[ConfigLoader] Created default SELL config."); } } private static void LoadBuyConfig() { try { BuyEntries = JsonConvert.DeserializeObject<List<TradeEntry>>(File.ReadAllText(BuyFile)) ?? new List<TradeEntry>(); HaldorOverhaul.Log.LogInfo((object)$"[ConfigLoader] Loaded {BuyEntries.Count} BUY entries."); } catch (Exception arg) { HaldorOverhaul.Log.LogError((object)$"[ConfigLoader] Error loading BUY config: {arg}"); BuyEntries = new List<TradeEntry>(); } } private static void LoadSellConfig() { try { SellEntries = JsonConvert.DeserializeObject<List<TradeEntry>>(File.ReadAllText(SellFile)) ?? new List<TradeEntry>(); HaldorOverhaul.Log.LogInfo((object)$"[ConfigLoader] Loaded {SellEntries.Count} SELL entries."); } catch (Exception arg) { HaldorOverhaul.Log.LogError((object)$"[ConfigLoader] Error loading SELL config: {arg}"); SellEntries = new List<TradeEntry>(); } } private static string DefaultBuyJson() { return JsonConvert.SerializeObject((object)new List<TradeEntry> { new TradeEntry { prefab = "Wood", stack = 50, price = 25, requiredGlobalKey = "defeated_eikthyr" } }, (Formatting)1); } private static string DefaultSellJson() { return JsonConvert.SerializeObject((object)new List<TradeEntry> { new TradeEntry { prefab = "Wood", stack = 1, price = 1 } }, (Formatting)1); } } public class TradeEntry { [JsonProperty("item_prefab")] public string prefab = ""; [JsonProperty("item_quantity")] public int stack = 1; [JsonProperty("item_price")] public int price = 1; [JsonProperty("must_defeated_boss")] public string requiredGlobalKey = ""; } public static class Constants { public static class UIElements { public const string Selected = "selected"; public const string Icon = "icon"; public const string Name = "name"; public const string Price = "price"; public const string Stack = "stack"; public const string CoinBackground = "coin_bkg"; public const string Quality = "quality"; public const string ItemList = "ItemList"; public const string Items = "Items"; public const string ListRoot = "ListRoot"; public const string ItemElement = "ItemElement"; public const string Topic = "topic"; public const string Coins = "coins"; public const string Border = "border (1)"; public const string BuyButton = "BuyButton"; public const string SellPanel = "SellPanel"; public const string Background = "bkg"; public const string Text = "Text"; } public const float BuyPanelPositionOffset = 35f; public const float SellPanelHorizontalOffset = 250f; public const float DefaultItemListBaseSize = 300f; public const float DefaultViewportHeight = 300f; public static readonly Color CategoryHeaderBackground = new Color(0.3f, 0.25f, 0.15f, 0.85f); public static readonly Color CategoryHeaderTextColor = new Color(1f, 0.9f, 0.5f, 1f); public static readonly Color CategoryIconTint = new Color(1f, 0.9f, 0.6f, 1f); public const float CategoryHeaderFontSizeMultiplier = 1.2f; public static readonly Color AffordableItemColor = Color.white; public static readonly Color UnaffordableItemColor = new Color(1f, 0f, 1f, 1f); public const float GamepadHintVerticalOffset = -30f; public const float GamepadHintFontSizeReduction = 2f; public const float MinGamepadHintFontSize = 14f; public const float InputDebounceTime = 0.15f; public const int HashMultiplier = 397; public const string LogPrefixUIManager = "[UIManager]"; public const string LogPrefixPatches = "[StoreGuiPatches]"; public const string LogPrefixConfig = "[ConfigLoader]"; public static readonly string[] CategoryBuckets = new string[10] { "Weapons", "Armor", "Shields", "Ammo", "Consumables", "Materials", "Utility", "Trophies", "Crafting", "Misc" }; public static string GetCategoryDisplayName(string category) { if (string.IsNullOrEmpty(category)) { return "Misc"; } return category; } } public static class UIElementFinder { public static Transform FindChild(Transform parent, string name, bool recursive = false) { if ((Object)(object)parent == (Object)null || string.IsNullOrEmpty(name)) { return null; } Transform val = parent.Find(name); if ((Object)(object)val != (Object)null) { return val; } if (!recursive) { return null; } return FindChildRecursive(parent, name); } public static Transform FindChildRecursive(Transform root, string name) { if ((Object)(object)root == (Object)null || string.IsNullOrEmpty(name)) { return null; } for (int i = 0; i < root.childCount; i++) { Transform child = root.GetChild(i); if (string.Equals(((Object)child).name, name, StringComparison.OrdinalIgnoreCase)) { return child; } Transform val = FindChildRecursive(child, name); if ((Object)(object)val != (Object)null) { return val; } } return null; } public static Image GetIcon(Transform parent) { Transform val = FindChild(parent, "icon"); if ((Object)(object)val != (Object)null) { return ((Component)val).GetComponent<Image>(); } Image[] componentsInChildren = ((Component)parent).GetComponentsInChildren<Image>(true); if (componentsInChildren == null || componentsInChildren.Length == 0) { return null; } return componentsInChildren[0]; } public static TMP_Text GetText(Transform parent, string childName) { Transform val = FindChild(parent, childName); if ((Object)(object)val != (Object)null) { TMP_Text component = ((Component)val).GetComponent<TMP_Text>(); if ((Object)(object)component != (Object)null) { return component; } return (TMP_Text)(object)((Component)val).GetComponent<TextMeshProUGUI>(); } TMP_Text[] componentsInChildren = ((Component)parent).GetComponentsInChildren<TMP_Text>(true); if (componentsInChildren != null) { TMP_Text[] array = componentsInChildren; foreach (TMP_Text val2 in array) { if ((Object)(object)val2 != (Object)null && string.Equals(((Object)((Component)val2).gameObject).name, childName, StringComparison.OrdinalIgnoreCase)) { return val2; } } } return null; } public static TMP_Text GetNameText(Transform parent) { return GetText(parent, "name"); } public static TMP_Text GetPriceText(Transform parent) { TMP_Text text = GetText(parent, "price"); if ((Object)(object)text != (Object)null) { return text; } Transform val = FindChild(parent, "coin_bkg"); if ((Object)(object)val != (Object)null) { return GetText(val, "price"); } return null; } public static TMP_Text GetStackText(Transform parent) { return GetText(parent, "stack"); } public static Transform GetSelectedIndicator(Transform parent) { return FindChild(parent, "selected"); } public static void SetSelected(Transform parent, bool selected) { Transform selectedIndicator = GetSelectedIndicator(parent); if ((Object)(object)selectedIndicator != (Object)null) { ((Component)selectedIndicator).gameObject.SetActive(selected); } } public static Button GetButton(GameObject go) { if (go == null) { return null; } return go.GetComponent<Button>(); } public static void DestroyChild(Transform parent, string name) { if (!((Object)(object)parent == (Object)null) && !string.IsNullOrEmpty(name)) { Transform val = parent.Find(name); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } } } public static T GetOrAddComponent<T>(GameObject go) where T : Component { if ((Object)(object)go == (Object)null) { return default(T); } T component = go.GetComponent<T>(); if (!((Object)(object)component != (Object)null)) { return go.AddComponent<T>(); } return component; } } public static class GamepadInputHandler { private static readonly object _lock = new object(); private static readonly Dictionary<string, float> _lastInputTime = new Dictionary<string, float>(); private static readonly float _debounceTime = 0.15f; public static void Reset() { lock (_lock) { _lastInputTime.Clear(); } } private static bool GetButtonDownDebounced(string buttonName) { if (string.IsNullOrEmpty(buttonName)) { return false; } if (!ZInput.GetButtonDown(buttonName)) { return false; } float unscaledTime = Time.unscaledTime; lock (_lock) { if (_lastInputTime.TryGetValue(buttonName, out var value) && unscaledTime - value < _debounceTime) { return false; } _lastInputTime[buttonName] = unscaledTime; } return true; } public static bool GetLeftBumper() { if (!GetButtonDownDebounced("JoyLB") && !GetButtonDownDebounced("JoyLBumper") && !GetButtonDownDebounced("JoyButtonLB")) { return GetButtonDownDebounced("JoyL1"); } return true; } public static bool GetRightBumper() { if (!GetButtonDownDebounced("JoyRB") && !GetButtonDownDebounced("JoyRBumper") && !GetButtonDownDebounced("JoyButtonRB")) { return GetButtonDownDebounced("JoyR1"); } return true; } public static bool GetMoveDown() { if (!GetButtonDownDebounced("JoyLStickDown")) { return GetButtonDownDebounced("JoyDPadDown"); } return true; } public static bool GetMoveUp() { if (!GetButtonDownDebounced("JoyLStickUp")) { return GetButtonDownDebounced("JoyDPadUp"); } return true; } public static bool GetMoveLeft() { if (!GetButtonDownDebounced("JoyDPadLeft")) { return GetButtonDownDebounced("JoyRStickLeft"); } return true; } public static bool GetMoveRight() { if (!GetButtonDownDebounced("JoyDPadRight")) { return GetButtonDownDebounced("JoyRStickRight"); } return true; } public static bool GetButtonX() { return GetButtonDownDebounced("JoyButtonX"); } public static bool GetButtonA() { return GetButtonDownDebounced("JoyButtonA"); } public static bool IsGamepadActive() { return ZInput.IsGamepadActive(); } } [BepInPlugin("com.haldor.overhaul", "Haldor Overhaul", "1.0.0")] public class HaldorOverhaul : BaseUnityPlugin { public const string PluginGUID = "com.haldor.overhaul"; public const string PluginName = "Haldor Overhaul"; public const string PluginVersion = "1.0.0"; private static Harmony _harmony; internal static ManualLogSource Log; private void Awake() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"Haldor Overhaul v1.0.0 loading..."); ConfigLoader.Initialize(); _harmony = new Harmony("com.haldor.overhaul"); _harmony.PatchAll(typeof(StoreGuiPatches)); Log.LogInfo((object)"Haldor Overhaul loaded successfully!"); } private void OnDestroy() { UIManager.ClearAllCaches(); GamepadInputHandler.Reset(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } Log.LogInfo((object)"Haldor Overhaul unloaded."); } } [HarmonyPatch] public static class StoreGuiPatches { private static readonly object _reflectionLock = new object(); private static FieldInfo _itemListField; private static FieldInfo _traderField; private static FieldInfo _itemsField; private static FieldInfo _listRootField; private static FieldInfo _selectedItemField; private static MethodInfo _getPlayerCoinsMethod; private static MethodInfo _selectItemMethod; private static MethodInfo _buySelectedItemMethod; private static volatile bool _reflectionCached; internal static bool SuppressSellDeselect = false; private static volatile bool _fullyInitialized = false; private static string _pendingBuyItemName; private static int _pendingBuyPrice; private static int _pendingBuyStack; private static Sprite _pendingBuyIcon; private static int _pendingBuyCoins = -1; private static bool _buyAttempted = false; internal static bool SuppressNextBuyMessage = false; [HarmonyPatch(typeof(Chat), "HasFocus")] [HarmonyPostfix] private static void Chat_HasFocus_Postfix(ref bool __result) { if (!__result && UIManager.IsOpen() && UIManager.IsSearchInputFocused) { __result = true; } } [HarmonyPatch(typeof(StoreGui), "Show")] [HarmonyPostfix] private static void StoreGui_Show_Postfix(StoreGui __instance) { try { if (!((Object)(object)__instance == (Object)null)) { CacheReflection(); Trader trader = GetTrader(__instance); UIManager.OnStoreOpened(__instance); } } catch (Exception ex) { HaldorOverhaul.Log.LogError((object)("[StoreGuiPatches] Show error: " + ex.Message)); } } [HarmonyPatch(typeof(StoreGui), "Hide")] [HarmonyPostfix] private static void StoreGui_Hide_Postfix() { UIManager.OnStoreClosed(); } [HarmonyPatch(typeof(StoreGui), "Update")] [HarmonyPostfix] private static void StoreGui_Update_Postfix(StoreGui __instance) { if (UIManager.IsOpen()) { UIManager.UpdateCoinsDisplay(__instance); UIManager.RefreshSellListIfInventoryChanged(); UIManager.UpdateGamepadHints(__instance); UIManager.UpdateSearchFocusState(); } } [HarmonyPatch(typeof(StoreGui), "GetSelectedItemIndex")] [HarmonyPrefix] private static bool StoreGui_GetSelectedItemIndex_Prefix(StoreGui __instance, ref int __result) { if (!UIManager.IsOpen()) { return true; } List<GameObject> itemList = GetItemList(__instance); if (itemList == null) { return true; } __result = -1; for (int i = 0; i < itemList.Count; i++) { if (UIManager.IsBuyIndexVisible(i, itemList.Count) && !((Object)(object)itemList[i] == (Object)null)) { Transform val = itemList[i].transform.Find("selected"); if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf) { __result = i; break; } } } return false; } [HarmonyPatch(typeof(StoreGui), "SelectItem")] [HarmonyPrefix] private static void StoreGui_SelectItem_Prefix(StoreGui __instance, ref int index, ref bool center) { if (!UIManager.IsOpen()) { return; } List<GameObject> itemList = GetItemList(__instance); if (itemList == null) { return; } if (index >= 0) { if (itemList.Count == 0) { index = -1; } else { index = Mathf.Clamp(index, 0, itemList.Count - 1); index = UIManager.FindNearestVisibleBuyIndex(index, itemList.Count); } center = center || index == 0; if (index >= 0 && !SuppressSellDeselect) { UIManager.DeselectSellSide(); } } UIManager.OnBuyItemSelected(index); } [HarmonyPatch(typeof(StoreGui), "UpdateRecipeGamepadInput")] [HarmonyPrefix] private static bool StoreGui_UpdateRecipeGamepadInput_Prefix(StoreGui __instance) { if (!UIManager.IsOpen()) { return true; } List<GameObject> itemList = GetItemList(__instance); if (itemList == null) { return true; } UIManager.EnsureTabFocus(__instance, itemList); if (GamepadInputHandler.GetLeftBumper()) { UIManager.SwitchToBuyTab(__instance, itemList, centerOnSelect: true); return false; } if (GamepadInputHandler.GetRightBumper()) { UIManager.SwitchToSellTab(centerOnSelect: true); return false; } if (GamepadInputHandler.GetMoveDown()) { UIManager.MoveActiveFocus(__instance, itemList, 1); return false; } if (GamepadInputHandler.GetMoveUp()) { UIManager.MoveActiveFocus(__instance, itemList, -1); return false; } if (GamepadInputHandler.GetMoveLeft()) { UIManager.SwitchToBuyTab(__instance, itemList, centerOnSelect: true); return false; } if (GamepadInputHandler.GetMoveRight()) { UIManager.SwitchToSellTab(centerOnSelect: true); return false; } if (GamepadInputHandler.GetButtonX()) { UIManager.ToggleFocusedCategory(__instance, itemList); return false; } if (GamepadInputHandler.GetButtonA()) { UIManager.ActivateFocused(__instance); return false; } return false; } private static void ManualFillList(StoreGui instance, List<TradeItem> items, List<GameObject> itemList, RectTransform listRoot) { //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Unknown result type (might be due to invalid IL or missing references) //IL_03ef: Unknown result type (might be due to invalid IL or missing references) //IL_053a: Unknown result type (might be due to invalid IL or missing references) //IL_0544: Expected O, but got Unknown if ((Object)(object)instance == (Object)null || items == null || itemList == null) { return; } if (items.Count == 0) { for (int i = 0; i < itemList.Count; i++) { if ((Object)(object)itemList[i] != (Object)null) { itemList[i].SetActive(false); } } } else { if (itemList.Count == 0) { return; } int num = Mathf.Min(items.Count, itemList.Count); int playerCoins = GetPlayerCoins(instance); for (int j = 0; j < itemList.Count; j++) { if (!((Object)(object)itemList[j] == (Object)null)) { Transform val = itemList[j].transform.Find("selected"); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(false); } } } for (int k = 0; k < num && k < items.Count && k < itemList.Count; k++) { TradeItem val2 = items[k]; GameObject val3 = itemList[k]; if (val2 == null || (Object)(object)val3 == (Object)null) { continue; } if (!string.IsNullOrEmpty(val2.m_requiredGlobalKey) && (!((Object)(object)ZoneSystem.instance != (Object)null) || !ZoneSystem.instance.GetGlobalKey(val2.m_requiredGlobalKey))) { val3.SetActive(false); continue; } try { val3.SetActive(true); Transform transform = val3.transform; RectTransform val4 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val4 != (Object)null && (Object)(object)listRoot != (Object)null && (Object)(object)((Transform)val4).parent != (Object)(object)listRoot) { ((Transform)val4).SetParent((Transform)(object)listRoot, false); } Image val5 = null; Transform val6 = val3.transform.Find("icon"); if ((Object)(object)val6 != (Object)null) { val5 = ((Component)val6).GetComponent<Image>(); } if ((Object)(object)val5 == (Object)null) { Image[] componentsInChildren = val3.GetComponentsInChildren<Image>(true); if (componentsInChildren != null && componentsInChildren.Length != 0) { val5 = componentsInChildren[0]; } } if ((Object)(object)val5 != (Object)null && (Object)(object)val2.m_prefab != (Object)null && val2.m_prefab.m_itemData != null) { Sprite icon = val2.m_prefab.m_itemData.GetIcon(); if ((Object)(object)icon != (Object)null) { val5.sprite = icon; ((Graphic)val5).color = Color.white; } } TextMeshProUGUI val7 = null; Transform val8 = val3.transform.Find("name"); if ((Object)(object)val8 != (Object)null) { val7 = ((Component)val8).GetComponent<TextMeshProUGUI>(); } if ((Object)(object)val7 == (Object)null) { TextMeshProUGUI[] componentsInChildren2 = val3.GetComponentsInChildren<TextMeshProUGUI>(true); if (componentsInChildren2 != null) { TextMeshProUGUI[] array = componentsInChildren2; foreach (TextMeshProUGUI val9 in array) { if ((Object)(object)val9 != (Object)null && ((Object)((Component)val9).gameObject).name == "name") { val7 = val9; break; } } } } if ((Object)(object)val7 != (Object)null && (Object)(object)val2.m_prefab != (Object)null && val2.m_prefab.m_itemData != null) { string text = UIManager.Localize(val2.m_prefab.m_itemData.m_shared.m_name); if (val2.m_stack > 1) { text = text + " x" + val2.m_stack; } ((TMP_Text)val7).text = text; } TextMeshProUGUI val10 = null; Transform val11 = val3.transform.Find("price"); if ((Object)(object)val11 != (Object)null) { val10 = ((Component)val11).GetComponent<TextMeshProUGUI>(); } if ((Object)(object)val10 == (Object)null) { TextMeshProUGUI[] componentsInChildren3 = val3.GetComponentsInChildren<TextMeshProUGUI>(true); if (componentsInChildren3 != null) { TextMeshProUGUI[] array2 = componentsInChildren3; foreach (TextMeshProUGUI val12 in array2) { if ((Object)(object)val12 != (Object)null && ((Object)((Component)val12).gameObject).name == "price") { val10 = val12; break; } } } } if ((Object)(object)val10 != (Object)null) { ((TMP_Text)val10).text = val2.m_price.ToString(); ((Graphic)val10).color = ((playerCoins >= val2.m_price) ? Constants.AffordableItemColor : Constants.UnaffordableItemColor); } TextMeshProUGUI val13 = null; Transform val14 = val3.transform.Find("stack"); if ((Object)(object)val14 != (Object)null) { val13 = ((Component)val14).GetComponent<TextMeshProUGUI>(); } if ((Object)(object)val13 == (Object)null) { TextMeshProUGUI[] componentsInChildren4 = val3.GetComponentsInChildren<TextMeshProUGUI>(true); if (componentsInChildren4 != null) { TextMeshProUGUI[] array3 = componentsInChildren4; foreach (TextMeshProUGUI val15 in array3) { if ((Object)(object)val15 != (Object)null && ((Object)((Component)val15).gameObject).name == "stack") { val13 = val15; break; } } } } if ((Object)(object)val13 != (Object)null) { if (val2.m_stack > 1) { ((Component)val13).gameObject.SetActive(true); ((TMP_Text)val13).text = val2.m_stack.ToString(); } else { ((Component)val13).gameObject.SetActive(false); } } Transform val16 = val3.transform.Find("selected"); if ((Object)(object)val16 != (Object)null) { ((Component)val16).gameObject.SetActive(false); } Button component = val3.GetComponent<Button>(); if ((Object)(object)component != (Object)null) { ((UnityEventBase)component.onClick).RemoveAllListeners(); int capturedIndex = k; ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { InvokeSelectItem(instance, capturedIndex, center: false); }); } Component component2 = val3.GetComponent("UITooltip"); if ((Object)(object)component2 != (Object)null && (Object)(object)val2.m_prefab != (Object)null && val2.m_prefab.m_itemData != null) { ItemData itemData = val2.m_prefab.m_itemData; string value = UIManager.Localize(itemData.m_shared.m_name); string value2 = UIManager.Localize(itemData.m_shared.m_description); Type type = ((object)component2).GetType(); FieldInfo field = type.GetField("m_topic", BindingFlags.Instance | BindingFlags.Public); FieldInfo field2 = type.GetField("m_text", BindingFlags.Instance | BindingFlags.Public); field?.SetValue(component2, value); field2?.SetValue(component2, value2); } } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)string.Format("{0} ManualFillList item {1} failed: {2}", "[StoreGuiPatches]", k, ex.Message)); } } for (int num2 = num; num2 < itemList.Count; num2++) { try { if ((Object)(object)itemList[num2] != (Object)null) { itemList[num2].SetActive(false); } } catch (Exception ex2) { HaldorOverhaul.Log.LogWarning((object)string.Format("{0} ManualFillList cleanup {1} failed: {2}", "[StoreGuiPatches]", num2, ex2.Message)); } } } } [HarmonyPatch(typeof(StoreGui), "FillList")] [HarmonyPrefix] private static bool StoreGui_FillList_Prefix(StoreGui __instance) { //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Expected O, but got Unknown //IL_0252: 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_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) try { CacheReflection(); List<TradeItem> list = GetTradeItems(__instance); List<GameObject> list2 = GetItemList(__instance); RectTransform val = GetListRoot(__instance); Trader trader = GetTrader(__instance); if (list == null) { list = new List<TradeItem>(); if (_itemsField != null) { _itemsField.SetValue(__instance, list); } } if (list2 == null) { list2 = new List<GameObject>(); if (_itemListField != null) { _itemListField.SetValue(__instance, list2); } } if ((Object)(object)val == (Object)null && (Object)(object)__instance != (Object)null) { Transform val2 = ((Component)__instance).transform.Find("ItemList/Items/ListRoot"); RectTransform val3 = (RectTransform)(object)((val2 is RectTransform) ? val2 : null); if ((Object)(object)val3 == (Object)null) { val3 = ((IEnumerable<RectTransform>)((Component)__instance).GetComponentsInChildren<RectTransform>(true)).FirstOrDefault((Func<RectTransform, bool>)((RectTransform r) => string.Equals(((Object)r).name, "ListRoot", StringComparison.OrdinalIgnoreCase))); } if ((Object)(object)val3 == (Object)null && (Object)(object)__instance.m_listElement != (Object)null) { Transform parent = __instance.m_listElement.transform.parent; val3 = (RectTransform)(object)((parent is RectTransform) ? parent : null); } if ((Object)(object)val3 == (Object)null) { Transform val4 = ((Component)__instance).transform.Find("ItemList/Items"); if ((Object)(object)val4 == (Object)null) { val4 = ((Component)__instance).transform.Find("ItemList"); } GameObject val5 = new GameObject("ListRoot", new Type[1] { typeof(RectTransform) }); RectTransform component = val5.GetComponent<RectTransform>(); val5.transform.SetParent(((Object)(object)val4 != (Object)null) ? val4 : ((Component)__instance).transform, false); component.anchorMin = new Vector2(0f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(0.5f, 1f); val3 = component; } if ((Object)(object)val3 != (Object)null) { val = val3; if (_listRootField != null) { _listRootField.SetValue(__instance, val3); } } } int count = list.Count; int count2 = list2.Count; if ((Object)(object)val == (Object)null) { GameObject val6 = new GameObject("ListRoot_Fallback", new Type[1] { typeof(RectTransform) }); RectTransform component2 = val6.GetComponent<RectTransform>(); val6.transform.SetParent(((Component)__instance).transform, false); component2.anchorMin = new Vector2(0f, 1f); component2.anchorMax = new Vector2(1f, 1f); component2.pivot = new Vector2(0.5f, 1f); val = component2; if (_listRootField != null) { _listRootField.SetValue(__instance, component2); } } if (count2 < count) { int num = count - count2; GameObject val7 = __instance.m_listElement; Transform val8 = (Transform)(object)__instance.m_listRoot; if ((Object)(object)val7 == (Object)null && list2.Count > 0) { val7 = list2[0]; } if ((Object)(object)val8 == (Object)null) { val8 = (Transform)(object)val; } if ((Object)(object)val8 == (Object)null && (Object)(object)val7 != (Object)null) { val8 = val7.transform.parent; } if ((Object)(object)val7 != (Object)null && (Object)(object)val8 != (Object)null) { int count3 = list2.Count; for (int i = 0; i < num; i++) { try { GameObject val9 = Object.Instantiate<GameObject>(val7, val8); ((Object)val9).name = $"StoreElement_Clone_{count3 + i}"; val9.SetActive(false); Transform val10 = val9.transform.Find("selected"); if ((Object)(object)val10 != (Object)null) { ((Component)val10).gameObject.SetActive(false); } list2.Add(val9); } catch (Exception ex) { HaldorOverhaul.Log.LogError((object)$"[StoreGuiPatches] Failed to clone element {i}: {ex.Message}"); break; } } count2 = list2.Count; if (_itemListField != null) { _itemListField.SetValue(__instance, list2); List<GameObject> itemList = GetItemList(__instance); if (itemList == null || itemList.Count != count2) { HaldorOverhaul.Log.LogWarning((object)$"[StoreGuiPatches] Pool expanded but reflection update failed (local={count2}, actual={itemList?.Count ?? (-1)})"); list2 = itemList ?? list2; count2 = list2.Count; } } else { HaldorOverhaul.Log.LogWarning((object)"[StoreGuiPatches] Pool expanded locally but _itemListField is null, cannot update StoreGui"); } } else { HaldorOverhaul.Log.LogError((object)$"[StoreGuiPatches] CANNOT EXPAND POOL (template={(Object)(object)val7 != (Object)null}, parent={(Object)(object)val8 != (Object)null}). Will display first {count2} of {count} items."); } } List<GameObject> list3 = GetItemList(__instance); RectTransform listRoot = GetListRoot(__instance); int num2 = list3?.Count ?? 0; HaldorOverhaul.Log.LogInfo((object)string.Format("[StoreGuiPatches] FillList_Prefix final state: trader={0}, items={1}, itemList={2}, root={3}, initialized={4}", trader?.m_name ?? "NULL", count, num2, ((listRoot != null) ? ((Object)listRoot).name : null) ?? "NULL", _fullyInitialized)); if (list3 != list2) { HaldorOverhaul.Log.LogWarning((object)$"[StoreGuiPatches] List reference mismatch! local={list2?.Count ?? (-1)}, fromReflection={num2}"); list3 = list2; num2 = list2?.Count ?? 0; } ManualFillList(__instance, list ?? new List<TradeItem>(), list3 ?? new List<GameObject>(), listRoot); return false; } catch (Exception ex2) { HaldorOverhaul.Log.LogError((object)("[StoreGuiPatches] FillList_Prefix failed: " + ex2.Message + "\n" + ex2.StackTrace)); return true; } } [HarmonyPatch(typeof(StoreGui), "FillList")] [HarmonyPostfix] private static void StoreGui_FillList_Postfix(StoreGui __instance) { try { if ((Object)(object)__instance == (Object)null) { return; } CacheReflection(); List<GameObject> itemList = GetItemList(__instance); if (itemList == null) { return; } List<TradeItem> tradeItems = GetTradeItems(__instance); for (int i = 0; i < itemList.Count; i++) { if (tradeItems == null) { break; } if (i >= tradeItems.Count) { break; } GameObject val = itemList[i]; if (!((Object)(object)val == (Object)null)) { Component component = val.GetComponent("UITooltip"); TradeItem val2 = tradeItems[i]; if ((Object)(object)component != (Object)null && val2?.m_prefab?.m_itemData != null) { ItemData itemData = val2.m_prefab.m_itemData; string value = UIManager.Localize(itemData.m_shared.m_name); string value2 = UIManager.Localize(itemData.m_shared.m_description); Type type = ((object)component).GetType(); FieldInfo field = type.GetField("m_topic", BindingFlags.Instance | BindingFlags.Public); FieldInfo field2 = type.GetField("m_text", BindingFlags.Instance | BindingFlags.Public); field?.SetValue(component, value); field2?.SetValue(component, value2); } } } if (_fullyInitialized) { RectTransform listRoot = GetListRoot(__instance); Vector2? buyScrollPosition = UIManager.GetBuyScrollPosition(__instance); float? buyScrollNormalized = UIManager.GetBuyScrollNormalized(__instance); UIManager.ApplyBuyCategories(__instance, itemList, tradeItems, listRoot); if (buyScrollPosition.HasValue || buyScrollNormalized.HasValue) { UIManager.RestoreBuyScroll(__instance, buyScrollPosition, buyScrollNormalized); } } if (_fullyInitialized && UIManager.IsOpen()) { UIManager.FillSellableList(); } } catch (Exception ex) { HaldorOverhaul.Log.LogError((object)("[StoreGuiPatches] FillList error: " + ex.Message)); } } [HarmonyPatch(typeof(StoreGui), "FillList")] [HarmonyFinalizer] private static Exception StoreGui_FillList_Finalizer(StoreGui __instance, Exception __exception) { if (__exception != null) { List<TradeItem> tradeItems = GetTradeItems(__instance); List<GameObject> itemList = GetItemList(__instance); RectTransform listRoot = GetListRoot(__instance); Trader trader = GetTrader(__instance); HaldorOverhaul.Log.LogError((object)string.Format("[StoreGuiPatches] FillList exception: trader={0}, items={1}, itemList={2}, root={3}, message={4}", trader?.m_name ?? "NULL", tradeItems?.Count ?? (-1), itemList?.Count ?? (-1), ((listRoot != null) ? ((Object)listRoot).name : null) ?? "NULL", __exception.Message)); HaldorOverhaul.Log.LogError((object)__exception.StackTrace); return null; } return null; } internal static void ResetInitialization() { _fullyInitialized = false; } internal static void MarkInitialized() { _fullyInitialized = true; } internal static bool IsInitialized() { return _fullyInitialized; } private static void CacheReflection() { if (_reflectionCached) { return; } lock (_reflectionLock) { if (_reflectionCached) { return; } try { Type typeFromHandle = typeof(StoreGui); BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic; _itemListField = typeFromHandle.GetField("m_itemList", bindingFlags); _traderField = typeFromHandle.GetField("m_trader", bindingFlags); _itemsField = typeFromHandle.GetField("m_items", bindingFlags); _listRootField = typeFromHandle.GetField("m_listRoot", bindingFlags); _selectedItemField = typeFromHandle.GetField("m_selectedItem", bindingFlags); _getPlayerCoinsMethod = typeFromHandle.GetMethod("GetPlayerCoins", bindingFlags); _selectItemMethod = typeFromHandle.GetMethod("SelectItem", bindingFlags, null, new Type[2] { typeof(int), typeof(bool) }, null); _buySelectedItemMethod = typeFromHandle.GetMethod("BuySelectedItem", bindingFlags); if (_itemsField == null) { _itemsField = FindFieldByType(typeFromHandle, typeof(List<TradeItem>), bindingFlags); } if (_itemListField == null) { _itemListField = FindFieldByType(typeFromHandle, typeof(List<GameObject>), bindingFlags); } if (_listRootField == null) { _listRootField = FindFieldByType(typeFromHandle, typeof(RectTransform), bindingFlags); } if (_buySelectedItemMethod == null) { _buySelectedItemMethod = typeFromHandle.GetMethods(bindingFlags).FirstOrDefault((MethodInfo m) => m.Name.IndexOf("BuySelected", StringComparison.OrdinalIgnoreCase) >= 0 && m.GetParameters().Length == 0); } _reflectionCached = true; } catch (Exception ex) { HaldorOverhaul.Log.LogError((object)("[StoreGuiPatches] Reflection cache failed: " + ex.Message)); _reflectionCached = true; } } } private static FieldInfo FindFieldByType(Type type, Type targetType, BindingFlags flags) { try { return type.GetFields(flags).FirstOrDefault((FieldInfo f) => targetType.IsAssignableFrom(f.FieldType)); } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)("[StoreGuiPatches] FindFieldByType failed for " + targetType.Name + ": " + ex.Message)); return null; } } internal static List<GameObject> GetItemList(StoreGui gui) { if (_itemListField == null || (Object)(object)gui == (Object)null) { return null; } try { return _itemListField.GetValue(gui) as List<GameObject>; } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)("[StoreGuiPatches] GetItemList failed: " + ex.Message)); return null; } } internal static List<TradeItem> GetTradeItems(StoreGui gui) { if ((Object)(object)gui == (Object)null) { return null; } CacheReflection(); List<TradeItem> list = null; if (_itemsField != null) { try { list = _itemsField.GetValue(gui) as List<TradeItem>; } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)("[StoreGuiPatches] GetTradeItems reflection failed: " + ex.Message)); } } if (list == null || list.Count == 0) { Trader trader = GetTrader(gui); if ((Object)(object)trader != (Object)null) { try { List<TradeItem> availableItems = trader.GetAvailableItems(); if (availableItems != null && availableItems.Count > 0) { list = availableItems; _itemsField?.SetValue(gui, availableItems); } } catch (Exception ex2) { HaldorOverhaul.Log.LogWarning((object)("[StoreGuiPatches] GetTradeItems fallback failed: " + ex2.Message)); } } } return list; } internal static Trader GetTrader(StoreGui gui) { if (_traderField == null || (Object)(object)gui == (Object)null) { return null; } try { object? value = _traderField.GetValue(gui); return (Trader)((value is Trader) ? value : null); } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)("[StoreGuiPatches] GetTrader failed: " + ex.Message)); return null; } } internal static RectTransform GetListRoot(StoreGui gui) { if ((Object)(object)gui == (Object)null) { return null; } CacheReflection(); RectTransform val = null; if (_listRootField != null) { try { object? value = _listRootField.GetValue(gui); val = (RectTransform)((value is RectTransform) ? value : null); } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)("[StoreGuiPatches] GetListRoot reflection failed: " + ex.Message)); } } if ((Object)(object)val == (Object)null) { Transform val2 = ((Component)gui).transform.Find("ItemList/Items/ListRoot"); RectTransform val3 = (RectTransform)(object)((val2 is RectTransform) ? val2 : null); if (val3 != null) { val = val3; _listRootField?.SetValue(gui, val3); } } return val; } internal static int GetPlayerCoins(StoreGui gui) { if (_getPlayerCoinsMethod == null || (Object)(object)gui == (Object)null) { return 0; } try { return (int)_getPlayerCoinsMethod.Invoke(gui, null); } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)("[StoreGuiPatches] GetPlayerCoins failed: " + ex.Message)); return 0; } } internal static void InvokeSelectItem(StoreGui gui, int index, bool center) { if (_selectItemMethod == null || (Object)(object)gui == (Object)null) { return; } SuppressSellDeselect = true; try { _selectItemMethod.Invoke(gui, new object[2] { index, center }); } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)("[StoreGuiPatches] InvokeSelectItem failed: " + ex.Message)); } finally { SuppressSellDeselect = false; } } internal static void InvokeBuySelectedItem(StoreGui gui) { if ((Object)(object)gui == (Object)null) { return; } CacheReflection(); if (!(_buySelectedItemMethod != null)) { return; } try { _buySelectedItemMethod.Invoke(gui, null); } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)("[StoreGuiPatches] InvokeBuySelectedItem failed: " + ex.Message)); } } internal static int GetSelectedItemIndex(StoreGui gui) { if ((Object)(object)gui == (Object)null) { return -1; } CacheReflection(); if (_selectedItemField != null) { try { return (int)_selectedItemField.GetValue(gui); } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)("[StoreGuiPatches] GetSelectedItemIndex failed: " + ex.Message)); } } return -1; } [HarmonyPatch(typeof(Trader), "GetAvailableItems")] [HarmonyPostfix] private static void Trader_GetAvailableItems_Postfix(Trader __instance, List<TradeItem> __result) { //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Expected O, but got Unknown try { if ((Object)(object)__instance == (Object)null || __result == null || (Object)(object)ObjectDB.instance == (Object)null) { return; } string text = __instance.m_name ?? ""; if ((text.IndexOf("Haldor", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("$npc_haldor", StringComparison.OrdinalIgnoreCase) < 0) || ConfigLoader.BuyEntries == null || ConfigLoader.BuyEntries.Count == 0) { return; } List<TradeItem> list = new List<TradeItem>(); int num = 0; foreach (TradeEntry buyEntry in ConfigLoader.BuyEntries) { if (buyEntry == null || string.IsNullOrEmpty(buyEntry.prefab)) { continue; } if (!string.IsNullOrEmpty(buyEntry.requiredGlobalKey) && (!((Object)(object)ZoneSystem.instance != (Object)null) || !ZoneSystem.instance.GetGlobalKey(buyEntry.requiredGlobalKey))) { num++; continue; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(buyEntry.prefab); if (!((Object)(object)itemPrefab == (Object)null)) { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && component.m_itemData != null) { TradeItem item = new TradeItem { m_prefab = component, m_price = buyEntry.price, m_stack = buyEntry.stack, m_requiredGlobalKey = "" }; list.Add(item); } } } if (list.Count > 0) { __result.Clear(); __result.AddRange(list); } else { HaldorOverhaul.Log.LogWarning((object)$"[GetAvailableItems] No items to add (skippedByKey={num}), using vanilla items ({__result.Count}) for trader={text}"); } } catch (Exception ex) { HaldorOverhaul.Log.LogError((object)("[StoreGuiPatches] GetAvailableItems error: " + ex.Message)); } } [HarmonyPatch(typeof(StoreGui), "SellItem")] [HarmonyPrefix] private static bool StoreGui_SellItem_Prefix(StoreGui __instance) { if (UIManager.IsOpen()) { UIManager.SellSelectedItem(__instance); return false; } return true; } [HarmonyPatch(typeof(StoreGui), "OnSelectedItem")] [HarmonyPostfix] private static void StoreGui_OnSelectedItem_Postfix() { if (!SuppressSellDeselect && UIManager.IsOpen()) { UIManager.DeselectSellSide(); } } private static int CountPlayerCoins(StoreGui gui) { if ((Object)(object)gui == (Object)null || (Object)(object)gui.m_coinPrefab == (Object)null) { return -1; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return -1; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory == null) { return -1; } string text = gui.m_coinPrefab.m_itemData?.m_shared?.m_name; if (string.IsNullOrEmpty(text)) { return -1; } return inventory.CountItems(text, -1, true); } [HarmonyPatch(typeof(Character), "Message")] [HarmonyPrefix] private static bool Character_Message_Prefix(Character __instance) { if (SuppressNextBuyMessage && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { SuppressNextBuyMessage = false; return false; } return true; } [HarmonyPatch(typeof(StoreGui), "BuySelectedItem")] [HarmonyPrefix] private static void StoreGui_BuySelectedItem_Prefix(StoreGui __instance) { _pendingBuyItemName = null; _pendingBuyPrice = 0; _pendingBuyStack = 0; _pendingBuyIcon = null; _pendingBuyCoins = -1; _buyAttempted = false; SuppressNextBuyMessage = false; try { _pendingBuyCoins = CountPlayerCoins(__instance); List<TradeItem> tradeItems = GetTradeItems(__instance); if (tradeItems == null || tradeItems.Count == 0) { HaldorOverhaul.Log.LogWarning((object)"[Buy_Prefix] No trade items"); return; } int selectedItemIndex = GetSelectedItemIndex(__instance); if (selectedItemIndex < 0 || selectedItemIndex >= tradeItems.Count) { HaldorOverhaul.Log.LogWarning((object)$"[Buy_Prefix] Invalid index: {selectedItemIndex}, items: {tradeItems.Count}"); return; } TradeItem val = tradeItems[selectedItemIndex]; if (val?.m_prefab?.m_itemData?.m_shared == null) { HaldorOverhaul.Log.LogWarning((object)"[Buy_Prefix] Invalid item data"); return; } _pendingBuyItemName = UIManager.Localize(val.m_prefab.m_itemData.m_shared.m_name); _pendingBuyPrice = val.m_price; _pendingBuyStack = val.m_stack; _buyAttempted = true; SuppressNextBuyMessage = true; try { _pendingBuyIcon = val.m_prefab.m_itemData.GetIcon(); } catch { _pendingBuyIcon = null; } HaldorOverhaul.Log.LogInfo((object)$"[Buy_Prefix] Attempting: {_pendingBuyItemName} x{_pendingBuyStack} for {_pendingBuyPrice}, coins before: {_pendingBuyCoins}"); } catch (Exception ex) { SuppressNextBuyMessage = false; HaldorOverhaul.Log.LogError((object)("[Buy_Prefix] Error: " + ex.Message)); } } [HarmonyPatch(typeof(StoreGui), "BuySelectedItem")] [HarmonyPostfix] private static void StoreGui_BuySelectedItem_Postfix(StoreGui __instance) { //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) try { if (!_buyAttempted || string.IsNullOrEmpty(_pendingBuyItemName)) { HaldorOverhaul.Log.LogWarning((object)"[Buy_Postfix] No buy was attempted"); return; } int num = CountPlayerCoins(__instance); bool flag = false; if (_pendingBuyCoins >= 0 && num >= 0) { int pendingBuyPrice = _pendingBuyPrice; int num2 = _pendingBuyCoins - num; flag = num2 >= pendingBuyPrice; HaldorOverhaul.Log.LogInfo((object)$"[Buy_Postfix] Coins: {_pendingBuyCoins} -> {num} (expected -{pendingBuyPrice}, actual -{num2})"); } else { flag = true; HaldorOverhaul.Log.LogWarning((object)"[Buy_Postfix] Could not verify coins, assuming success"); } if (flag) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { bool flag2 = false; try { if (__instance.m_sellEffects != null && __instance.m_sellEffects.m_effectPrefabs != null && __instance.m_sellEffects.m_effectPrefabs.Length != 0) { __instance.m_sellEffects.Create(((Component)__instance).transform.position, Quaternion.identity, (Transform)null, 1f, -1); flag2 = true; HaldorOverhaul.Log.LogInfo((object)"[Buy_Postfix] Played m_sellEffects"); } else { HaldorOverhaul.Log.LogWarning((object)"[Buy_Postfix] m_sellEffects is null or empty"); } } catch (Exception ex) { HaldorOverhaul.Log.LogError((object)("[Buy_Postfix] Sound error: " + ex.Message)); } string arg = ((_pendingBuyStack > 1) ? $"{_pendingBuyStack} {_pendingBuyItemName}" : _pendingBuyItemName); string text = $"Bought {arg} from Haldor for {_pendingBuyPrice} coins"; ((Character)localPlayer).Message((MessageType)1, text, 0, _pendingBuyIcon); HaldorOverhaul.Log.LogInfo((object)$"[Buy_Postfix] Purchase complete: {arg}, sound={flag2}"); } } else { HaldorOverhaul.Log.LogWarning((object)"[Buy_Postfix] Purchase failed or insufficient funds"); } } catch (Exception ex2) { HaldorOverhaul.Log.LogError((object)("[Buy_Postfix] Error: " + ex2.Message + "\n" + ex2.StackTrace)); } finally { _pendingBuyItemName = null; _pendingBuyIcon = null; _buyAttempted = false; } if (UIManager.IsOpen()) { UIManager.FillSellableList(); } } } internal static class UIManager { private enum ActiveTab { Buy, Sell } internal sealed class SellRow { public ItemData Item; public int TotalPrice; public int Stack; public int ConfigStack; public GameObject RowObject; public string Category; } private sealed class SellCandidate { public ItemData Item; public int Price; public int ConfigStack; public string Category; } private sealed class NavigationEntry { public string Category; public int Index; public bool IsHeader; } [CompilerGenerated] private static class <>O { public static UnityAction <0>__OnSellButtonClicked; public static UnityAction<string> <1>__OnBuySearchChanged; public static UnityAction<string> <2>__OnSellSearchChanged; } private static readonly float PositionDelta = 35f; private static readonly float SellPanelOffset = 250f; private static readonly HashSet<int> AdjustedStores = new HashSet<int>(); private static GameObject _sellPanel; private static Button _sellButton; private static RectTransform _sellListRoot; private static GameObject _sellListElementPrefab; private static ScrollRectEnsureVisible _sellItemEnsureVisible; private static TMP_Text _playerCoinsText; private static TMP_Text _gamepadHintText; private static StoreGui _currentStore; private static TMP_InputField _buySearchInput; private static TMP_InputField _sellSearchInput; private static string _buySearchFilter = ""; private static string _sellSearchFilter = ""; internal static bool IsSearchInputFocused = false; private static float _itemSpacing; private static float _itemListBaseSize; private static int _lastInventoryHash = -1; internal static readonly List<GameObject> SellItemList = new List<GameObject>(); internal static readonly List<SellRow> SellRows = new List<SellRow>(); private static readonly List<GameObject> _sellCategoryHeaders = new List<GameObject>(); private static readonly List<string> _sellCategoryOrder = new List<string>(); private static readonly Dictionary<string, GameObject> _sellHeaderLookup = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, bool> _sellCategoryCollapsed = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); private static int _selectedSellIndex = -1; private static bool _sellInProgress = false; private static readonly List<GameObject> _buyCategoryHeaders = new List<GameObject>(); private static readonly List<string> _buyCategoryOrder = new List<string>(); private static readonly Dictionary<string, GameObject> _buyHeaderLookup = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, bool> _buyCategoryCollapsed = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<int> _hiddenBuyIndices = new HashSet<int>(); private static readonly List<string> _buyIndexCategories = new List<string>(); private static bool _buyInProgress = false; private static readonly string[] _categoryBuckets = Constants.CategoryBuckets; private static readonly Dictionary<string, string[]> _categoryIconPrefabs = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase) { { "Weapons", new string[5] { "SwordBronze", "SwordIron", "AxeBronze", "Bow", "Club" } }, { "Armor", new string[4] { "ArmorBronzeChest", "ArmorIronChest", "HelmetBronze", "ArmorLeatherChest" } }, { "Shields", new string[4] { "ShieldWood", "ShieldBronzeBuckler", "ShieldIronSquare", "ShieldBanded" } }, { "Ammo", new string[4] { "ArrowWood", "ArrowFire", "ArrowFlint", "ArrowBronze" } }, { "Consumables", new string[4] { "MeadHealthMinor", "MeadStaminaMinor", "Honey", "Raspberry" } }, { "Materials", new string[5] { "Wood", "Stone", "Coal", "Bronze", "Iron" } }, { "Utility", new string[4] { "Hammer", "Hoe", "FishingRod", "Torch" } }, { "Trophies", new string[4] { "TrophyBoar", "TrophyDeer", "TrophyNeck", "TrophyGreydwarf" } }, { "Crafting", new string[4] { "Hammer", "SurtlingCore", "GreydwarfEye", "Resin" } }, { "Misc", new string[4] { "Coins", "Ruby", "Amber", "AmberPearl" } } }; private static readonly Dictionary<string, Sprite> _categoryIconCache = new Dictionary<string, Sprite>(StringComparer.OrdinalIgnoreCase); private static ActiveTab _activeTab = ActiveTab.Buy; private static string _focusedBuyHeader; private static int _focusedBuyIndex = -1; private static string _focusedSellHeader; private static MethodInfo _localizeMethod; private static object _localizationInstance; private static bool _localizationCached; private static void ClearCategoryHeaders(List<GameObject> headers, Dictionary<string, GameObject> lookup) { if (headers != null) { foreach (GameObject header in headers) { if ((Object)(object)header != (Object)null) { Object.Destroy((Object)(object)header); } } headers.Clear(); } lookup?.Clear(); } private static string GetItemCategory(ItemData item) { if (item == null || item.m_shared == null) { return "Misc"; } string text = ((object)(ItemType)(ref item.m_shared.m_itemType)).ToString(); if (string.IsNullOrEmpty(text)) { return "Misc"; } string text2 = text switch { "OneHandedWeapon" => "Weapons", "TwoHandedWeapon" => "Weapons", "Bow" => "Weapons", "Tool" => "Utility", "Shield" => "Shields", "Helmet" => "Armor", "Chest" => "Armor", "Legs" => "Armor", "Shoulder" => "Armor", "Utility" => "Utility", "Ammo" => "Ammo", "Consumable" => "Consumables", "Trophie" => "Trophies", _ => text, }; if (text2.IndexOf("Weapon", StringComparison.OrdinalIgnoreCase) >= 0) { text2 = "Weapons"; } else if (text2.IndexOf("Armor", StringComparison.OrdinalIgnoreCase) >= 0) { text2 = "Armor"; } else if (text2.IndexOf("Shield", StringComparison.OrdinalIgnoreCase) >= 0) { text2 = "Shields"; } else if (text2.IndexOf("Ammo", StringComparison.OrdinalIgnoreCase) >= 0) { text2 = "Ammo"; } else if (text2.IndexOf("Consum", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("Food", StringComparison.OrdinalIgnoreCase) >= 0) { text2 = "Consumables"; } else if (text2.IndexOf("Material", StringComparison.OrdinalIgnoreCase) >= 0) { text2 = "Materials"; } else if (text2.IndexOf("Utility", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("Tool", StringComparison.OrdinalIgnoreCase) >= 0) { text2 = "Utility"; } else if (text2.IndexOf("Troph", StringComparison.OrdinalIgnoreCase) >= 0) { text2 = "Trophies"; } string[] categoryBuckets = _categoryBuckets; foreach (string text3 in categoryBuckets) { if (string.Equals(text2, text3, StringComparison.OrdinalIgnoreCase)) { return text3; } } return "Misc"; } private static string GetCategoryHeaderLabel(string category, bool collapsed) { string categoryDisplayName = Constants.GetCategoryDisplayName(category); return (collapsed ? "[+] " : "[-] ") + categoryDisplayName; } private static Sprite GetCategoryIcon(string category) { if (string.IsNullOrEmpty(category)) { return null; } if (_categoryIconCache.TryGetValue(category, out var value)) { return value; } if ((Object)(object)ObjectDB.instance == (Object)null) { return null; } if (_categoryIconPrefabs.TryGetValue(category, out var value2)) { string[] array = value2; foreach (string text in array) { try { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text); if ((Object)(object)itemPrefab == (Object)null) { continue; } ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if (component?.m_itemData != null) { Sprite icon = component.m_itemData.GetIcon(); if ((Object)(object)icon != (Object)null) { _categoryIconCache[category] = icon; return icon; } } } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)("[UIManager] GetCategoryIcon failed for " + text + ": " + ex.Message)); } } } return null; } private static GameObject CreateCategoryHeader(GameObject prefab, Transform parent, string category, bool collapsed, bool isSell) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Expected O, but got Unknown //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Expected O, but got Unknown if ((Object)(object)prefab == (Object)null || (Object)(object)parent == (Object)null) { return null; } GameObject val = Object.Instantiate<GameObject>(prefab, parent); val.SetActive(true); ((Object)val).name = (isSell ? "SellCategory_" : "BuyCategory_") + category; Image val2 = val.GetComponent<Image>(); if ((Object)(object)val2 == (Object)null) { val2 = val.AddComponent<Image>(); } ((Graphic)val2).color = Constants.CategoryHeaderBackground; Transform val3 = val.transform.Find("icon"); if ((Object)(object)val3 != (Object)null) { Sprite categoryIcon = GetCategoryIcon(category); if ((Object)(object)categoryIcon != (Object)null) { ((Component)val3).gameObject.SetActive(true); Image component = ((Component)val3).GetComponent<Image>(); if ((Object)(object)component != (Object)null) { component.sprite = categoryIcon; ((Graphic)component).color = Constants.CategoryIconTint; } } else { ((Component)val3).gameObject.SetActive(false); } } Transform val4 = val.transform.Find("coin_bkg"); if ((Object)(object)val4 != (Object)null) { ((Component)val4).gameObject.SetActive(false); } Transform val5 = val.transform.Find("quality"); if ((Object)(object)val5 != (Object)null) { ((Component)val5).gameObject.SetActive(false); } Transform val6 = val.transform.Find("selected"); if ((Object)(object)val6 != (Object)null) { ((Component)val6).gameObject.SetActive(false); } Transform obj = val.transform.Find("name"); TMP_Text val7 = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null); if ((Object)(object)val7 != (Object)null) { val7.text = GetCategoryHeaderLabel(category, collapsed); ((Graphic)val7).color = Constants.CategoryHeaderTextColor; val7.fontStyle = (FontStyles)1; val7.fontSize *= 1.2f; } else { HaldorOverhaul.Log.LogWarning((object)("[UIManager] CreateCategoryHeader: name text missing for " + category)); } Button component2 = val.GetComponent<Button>(); if ((Object)(object)component2 != (Object)null) { ((UnityEventBase)component2.onClick).RemoveAllListeners(); if (isSell) { ((UnityEvent)component2.onClick).AddListener((UnityAction)delegate { ToggleSellCategory(category); }); } else { ((UnityEvent)component2.onClick).AddListener((UnityAction)delegate { ToggleBuyCategory(category); }); } ((Selectable)component2).interactable = true; } return val; } private static void UpdateHeaderLabel(GameObject header, string category, bool collapsed) { object obj; if (header == null) { obj = null; } else { Transform obj2 = header.transform.Find("name"); obj = ((obj2 != null) ? ((Component)obj2).GetComponent<TMP_Text>() : null); } TMP_Text val = (TMP_Text)obj; if ((Object)(object)val != (Object)null) { val.text = GetCategoryHeaderLabel(category, collapsed); } } private static void PositionRow(RectTransform rect, float offset, int visualIndex) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rect == (Object)null)) { rect.anchoredPosition = new Vector2(offset, (float)visualIndex * (0f - _itemSpacing) - offset); } } private static void MarkHeaderSelected(Dictionary<string, GameObject> lookup, string category) { if (lookup == null) { return; } foreach (KeyValuePair<string, GameObject> item in lookup) { GameObject value = item.Value; if (!((Object)(object)value == (Object)null)) { Transform val = value.transform.Find("selected"); if ((Object)(object)val != (Object)null) { bool active = !string.IsNullOrEmpty(category) && string.Equals(category, item.Key, StringComparison.OrdinalIgnoreCase); ((Component)val).gameObject.SetActive(active); } } } } private static void ToggleSellCategory(string category) { //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(category)) { return; } bool value; bool flag = _sellCategoryCollapsed.TryGetValue(category, out value) && value; _sellCategoryCollapsed[category] = !flag; SetActiveTab(ActiveTab.Sell); _selectedSellIndex = -1; _focusedSellHeader = category; MarkHeaderSelected(_sellHeaderLookup, category); RelayoutSellList(rebuildHeaders: false); GameObject sellPanel = _sellPanel; object obj; if (sellPanel == null) { obj = null; } else { Transform obj2 = sellPanel.transform.Find("ItemList/Items"); obj = ((obj2 != null) ? ((Component)obj2).GetComponent<ScrollRect>() : null); } ScrollRect val = (ScrollRect)obj; if ((Object)(object)val != (Object)null && (Object)(object)val.content != (Object)null && _sellHeaderLookup.TryGetValue(category, out var value2) && (Object)(object)value2 != (Object)null) { LayoutRebuilder.ForceRebuildLayoutImmediate(val.content); Canvas.ForceUpdateCanvases(); Transform transform = value2.transform; RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null); Rect rect = val.content.rect; float height = ((Rect)(ref rect)).height; float num; if (!((Object)(object)val.viewport != (Object)null)) { num = 300f; } else { rect = val.viewport.rect; num = ((Rect)(ref rect)).height; } float num2 = num; if (height > num2 && (Object)(object)val2 != (Object)null) { float num3 = Mathf.Abs(val2.anchoredPosition.y); float num4 = height - num2; num3 = Mathf.Clamp(num3, 0f, num4); float num5 = 1f - num3 / num4; val.verticalNormalizedPosition = Mathf.Clamp01(num5); } else if (height <= num2) { val.verticalNormalizedPosition = 1f; } val.velocity = Vector2.zero; } } private static void ToggleBuyCategory(string category) { //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(category) || (Object)(object)_currentStore == (Object)null) { return; } bool value; bool flag = _buyCategoryCollapsed.TryGetValue(category, out value) && value; _buyCategoryCollapsed[category] = !flag; SetActiveTab(ActiveTab.Buy); _focusedBuyHeader = category; _focusedBuyIndex = -1; List<GameObject> itemList = StoreGuiPatches.GetItemList(_currentStore); List<TradeItem> tradeItems = StoreGuiPatches.GetTradeItems(_currentStore); MarkHeaderSelected(_buyHeaderLookup, category); ClearBuyItemSelection(itemList); ApplyBuyCategories(_currentStore, itemList, tradeItems); GameObject rootPanel = _currentStore.m_rootPanel; object obj; if (rootPanel == null) { obj = null; } else { Transform obj2 = rootPanel.transform.Find("ItemList/Items"); obj = ((obj2 != null) ? ((Component)obj2).GetComponent<ScrollRect>() : null); } ScrollRect val = (ScrollRect)obj; if ((Object)(object)val != (Object)null && (Object)(object)val.content != (Object)null && _buyHeaderLookup.TryGetValue(category, out var value2) && (Object)(object)value2 != (Object)null) { LayoutRebuilder.ForceRebuildLayoutImmediate(val.content); Canvas.ForceUpdateCanvases(); Transform transform = value2.transform; RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null); Rect rect = val.content.rect; float height = ((Rect)(ref rect)).height; float num; if (!((Object)(object)val.viewport != (Object)null)) { num = 300f; } else { rect = val.viewport.rect; num = ((Rect)(ref rect)).height; } float num2 = num; if (height > num2 && (Object)(object)val2 != (Object)null) { float num3 = Mathf.Abs(val2.anchoredPosition.y); float num4 = height - num2; num3 = Mathf.Clamp(num3, 0f, num4); float num5 = 1f - num3 / num4; val.verticalNormalizedPosition = Mathf.Clamp01(num5); } else if (height <= num2) { val.verticalNormalizedPosition = 1f; } val.velocity = Vector2.zero; } } private static string GetBuyCategoryForIndex(int index) { if (index < 0 || !(index < _buyIndexCategories?.Count)) { return null; } return _buyIndexCategories[index]; } internal static bool IsBuyIndexVisible(int index, int totalCount) { if (index >= 0 && index < totalCount) { return !_hiddenBuyIndices.Contains(index); } return false; } internal static int FindNearestVisibleBuyIndex(int index, int totalCount) { if (totalCount <= 0) { return -1; } index = Mathf.Clamp(index, 0, totalCount - 1); if (!_hiddenBuyIndices.Contains(index)) { return index; } for (int i = 1; i < totalCount; i++) { int num = index + i; if (num < totalCount && !_hiddenBuyIndices.Contains(num)) { return num; } int num2 = index - i; if (num2 >= 0 && !_hiddenBuyIndices.Contains(num2)) { return num2; } } return -1; } internal static int GetNextVisibleBuyIndex(int index, int direction, int totalCount) { if (totalCount <= 0) { return -1; } if (direction == 0) { direction = 1; } int num = index; int num2 = Math.Sign(direction); if (num < 0) { num = ((num2 > 0) ? (-1) : totalCount); } for (int i = num + num2; i >= 0 && i < totalCount; i += num2) { if (!_hiddenBuyIndices.Contains(i)) { return i; } } return -1; } internal static void RefreshBuyCategories(StoreGui gui) { if (!((Object)(object)gui == (Object)null)) { List<GameObject> itemList = StoreGuiPatches.GetItemList(gui); List<TradeItem> tradeItems = StoreGuiPatches.GetTradeItems(gui); ApplyBuyCategories(gui, itemList, tradeItems); } } internal static void ApplyBuyCategories(StoreGui gui, List<GameObject> buyList, List<TradeItem> tradeItems, RectTransform explicitListRoot = null) { //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_0911: Unknown result type (might be due to invalid IL or missing references) //IL_0916: Unknown result type (might be due to invalid IL or missing references) //IL_091f: Unknown result type (might be due to invalid IL or missing references) //IL_098d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)gui == (Object)null) { return; } if (buyList == null || buyList.Count == 0) { HaldorOverhaul.Log.LogWarning((object)"[UIManager] ApplyBuyCategories skipped: buyList empty"); return; } if (_itemSpacing <= 0f) { _itemSpacing = gui.m_itemSpacing; if (_itemSpacing <= 0f) { HaldorOverhaul.Log.LogWarning((object)"[UIManager] Buy category build skipped: itemSpacing is 0"); return; } } RectTransform val = explicitListRoot ?? gui.m_listRoot; if ((Object)(object)val == (Object)null) { GameObject obj = buyList[0]; object obj2; if (obj == null) { obj2 = null; } else { Transform transform = obj.transform; obj2 = ((transform != null) ? transform.parent : null); } Transform val2 = (Transform)obj2; val = (RectTransform)(object)((val2 is RectTransform) ? val2 : null); } if ((Object)(object)val == (Object)null) { HaldorOverhaul.Log.LogWarning((object)"[UIManager] Buy category build skipped: no list root found."); return; } if (tradeItems == null || tradeItems.Count != buyList.Count) { Trader trader = StoreGuiPatches.GetTrader(gui); if ((Object)(object)trader != (Object)null) { try { List<TradeItem> availableItems = trader.GetAvailableItems(); if (availableItems != null && availableItems.Count > 0) { tradeItems = availableItems; } } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)("[UIManager] Failed to fetch fallback trade items: " + ex.Message)); } } } VerticalLayoutGroup component = ((Component)val).GetComponent<VerticalLayoutGroup>(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } ContentSizeFitter component2 = ((Component)val).GetComponent<ContentSizeFitter>(); if ((Object)(object)component2 != (Object)null) { ((Behaviour)component2).enabled = false; } Transform parent = ((Transform)val).parent; if ((Object)(object)parent != (Object)null) { VerticalLayoutGroup component3 = ((Component)parent).GetComponent<VerticalLayoutGroup>(); if ((Object)(object)component3 != (Object)null) { ((Behaviour)component3).enabled = false; } ContentSizeFitter component4 = ((Component)parent).GetComponent<ContentSizeFitter>(); if ((Object)(object)component4 != (Object)null) { ((Behaviour)component4).enabled = false; } } ClearCategoryHeaders(_buyCategoryHeaders, _buyHeaderLookup); _buyCategoryOrder.Clear(); _hiddenBuyIndices.Clear(); int num = tradeItems?.Count ?? buyList.Count; string[] array = new string[num]; string[] array2 = new string[num]; string[] categoryBuckets = _categoryBuckets; foreach (string text in categoryBuckets) { if (!_buyCategoryCollapsed.ContainsKey(text)) { _buyCategoryCollapsed[text] = true; } _buyCategoryOrder.Add(text); } for (int j = 0; j < num; j++) { ItemData val3 = ((tradeItems == null || j >= tradeItems.Count) ? null : tradeItems[j]?.m_prefab?.m_itemData); string key = (array[j] = GetItemCategory(val3)); array2[j] = ((val3?.m_shared != null) ? Localize(val3.m_shared.m_name) : ""); if (!_buyCategoryCollapsed.ContainsKey(key)) { _buyCategoryCollapsed[key] = true; } } _buyIndexCategories.Clear(); _buyIndexCategories.AddRange(array); Rect rect = val.rect; float width = ((Rect)(ref rect)).width; GameObject val4 = (((Object)(object)gui.m_listElement != (Object)null) ? gui.m_listElement : buyList[0]); RectTransform val5 = (((Object)(object)val4 != (Object)null) ? val4.GetComponent<RectTransform>() : null); float num2; if (!((Object)(object)val5 != (Object)null)) { num2 = width; } else { rect = val5.rect; num2 = ((Rect)(ref rect)).width; } float num3 = num2; float offset = (width - num3) / 2f; LayoutElement val6 = (((Object)(object)val4 != (Object)null) ? val4.GetComponent<LayoutElement>() : null); bool flag = IsSearchActive(isBuy: true); if (!flag) { foreach (string item in _buyCategoryOrder) { bool value; bool collapsed = _buyCategoryCollapsed.TryGetValue(item, out value) && value; GameObject val7 = CreateCategoryHeader(val4, (Transform)(object)val, item, collapsed, isSell: false); if ((Object)(object)val7 != (Object)null) { if ((Object)(object)val6 != (Object)null) { LayoutElement val8 = val7.GetComponent<LayoutElement>() ?? val7.AddComponent<LayoutElement>(); val8.minHeight = val6.minHeight; val8.preferredHeight = val6.preferredHeight; val8.flexibleHeight = val6.flexibleHeight; val8.minWidth = val6.minWidth; val8.preferredWidth = val6.preferredWidth; val8.flexibleWidth = val6.flexibleWidth; val8.ignoreLayout = true; } else { LayoutElement val9 = val7.GetComponent<LayoutElement>() ?? val7.AddComponent<LayoutElement>(); val9.ignoreLayout = true; } _buyCategoryHeaders.Add(val7); _buyHeaderLookup[item] = val7; } else { HaldorOverhaul.Log.LogWarning((object)("[UIManager] Failed to create buy header for: " + item)); } } } int num4 = FindSelectedBuyIndex(buyList); int num5 = 0; List<GameObject> list = new List<GameObject>(); for (int k = 0; k < buyList.Count; k++) { if (!((Object)(object)buyList[k] == (Object)null)) { Transform val10 = buyList[k].transform.Find("selected"); if ((Object)(object)val10 != (Object)null) { ((Component)val10).gameObject.SetActive(false); } } } if (flag) { for (int l = 0; l < buyList.Count; l++) { if (l >= num) { GameObject obj3 = buyList[l]; if (obj3 != null) { obj3.SetActive(false); } continue; } GameObject val11 = buyList[l]; if ((Object)(object)val11 == (Object)null) { continue; } LayoutElement val12 = val11.GetComponent<LayoutElement>() ?? val11.AddComponent<LayoutElement>(); val12.ignoreLayout = true; bool flag2 = ItemMatchesSearch(array2[l], isBuy: true); val11.SetActive(flag2); Button component5 = val11.GetComponent<Button>(); if ((Object)(object)component5 != (Object)null) { ((Selectable)component5).interactable = flag2; } Transform val13 = val11.transform.Find("selected"); if (!flag2) { if ((Object)(object)val13 != (Object)null) { ((Component)val13).gameObject.SetActive(false); } _hiddenBuyIndices.Add(l); } else { if ((Object)(object)val13 != (Object)null) { ((Component)val13).gameObject.SetActive(l == num4); } list.Add(val11); } } } else { foreach (string item2 in _buyCategoryOrder) { bool value2; bool flag3 = _buyCategoryCollapsed.TryGetValue(item2, out value2) && value2; if (_buyHeaderLookup.TryGetValue(item2, out var value3) && (Object)(object)value3 != (Object)null) { value3.SetActive(true); UpdateHeaderLabel(value3, item2, flag3); list.Add(value3); } for (int m = 0; m < buyList.Count; m++) { if (m >= num) { GameObject obj4 = buyList[m]; if (obj4 != null) { obj4.SetActive(false); } } else { if (!string.Equals(array[m], item2, StringComparison.OrdinalIgnoreCase)) { continue; } GameObject val14 = buyList[m]; if ((Object)(object)val14 == (Object)null) { continue; } LayoutElement val15 = val14.GetComponent<LayoutElement>() ?? val14.AddComponent<LayoutElement>(); val15.ignoreLayout = true; bool flag4 = !flag3; val14.SetActive(flag4); Button component6 = val14.GetComponent<Button>(); if ((Object)(object)component6 != (Object)null) { ((Selectable)component6).interactable = flag4; } Transform val16 = val14.transform.Find("selected"); if (!flag4 && (Object)(object)val16 != (Object)null) { ((Component)val16).gameObject.SetActive(false); } if (!flag4) { _hiddenBuyIndices.Add(m); continue; } if ((Object)(object)val16 != (Object)null) { ((Component)val16).gameObject.SetActive(m == num4); } list.Add(val14); } } } } for (int n = 0; n < list.Count; n++) { GameObject val17 = list[n]; if (!((Object)(object)val17 == (Object)null)) { val17.transform.SetSiblingIndex(n); Transform transform2 = val17.transform; PositionRow((RectTransform)(object)((transform2 is RectTransform) ? transform2 : null), offset, n); val17.SetActive(true); } } num5 = list.Count; HashSet<GameObject> hashSet = new HashSet<GameObject>(list); for (int num6 = 0; num6 < ((Transform)val).childCount; num6++) { Transform child = ((Transform)val).GetChild(num6); if (!((Object)(object)child == (Object)null) && !hashSet.Contains(((Component)child).gameObject)) { ((Component)child).gameObject.SetActive(false); child.SetSiblingIndex(((Transform)val).childCount - 1); } } float num7 = (float)num5 * _itemSpacing + _itemSpacing * 0.1f; float num8 = Mathf.Max(_itemListBaseSize, num7); val.SetSizeWithCurrentAnchors((Axis)1, num8); LayoutRebuilder.ForceRebuildLayoutImmediate(val); Transform parent2 = ((Transform)val).parent; ScrollRect val18 = ((parent2 != null) ? ((Component)parent2).GetComponent<ScrollRect>() : null); if ((Object)(object)val18 != (Object)null) { val18.movementType = (MovementType)2; val18.vertical = true; val18.horizontal = false; } if (string.IsNullOrEmpty(_focusedBuyHeader)) { EnsureBuySelectionVisible(gui, buyList); NormalizeBuySelection(buyList, _focusedBuyIndex); Transform parent3 = ((Transform)val).parent; ScrollRect val19 = ((parent3 != null) ? ((Component)parent3).GetComponent<ScrollRect>() : null); if ((Object)(object)val19 != (Object)null && (Object)(object)val19.content == (Object)(object)val) { Vector2 anchoredPosition = val19.content.anchoredPosition; val19.content.anchoredPosition = anchoredPosition; } NormalizeBuySelection(buyList, FindSelectedBuyIndex(buyList)); } else { MarkHeaderSelected(_buyHeaderLookup, _focusedBuyHeader); ClearBuyItemSelection(buyList); } try { int childCount = ((Transform)val).childCount; int num9 = Math.Min(childCount, 15); for (int num10 = 0; num10 < num9; num10++) { Transform child2 = ((Transform)val).GetChild(num10); RectTransform val20 = (RectTransform)(object)((child2 is RectTransform) ? child2 : null); string text2 = (((Object)(object)val20 != (Object)null) ? $"pos={val20.anchoredPosition}" : ""); } } catch (Exception ex2) { HaldorOverhaul.Log.LogWarning((object)("[UIManager] Failed to list buy children: " + ex2.Message)); } } private static int FindSelectedBuyIndex(List<GameObject> buyList) { if (buyList == null) { return -1; } for (int i = 0; i < buyList.Count; i++) { GameObject obj = buyList[i]; if (obj != null) { Transform obj2 = obj.transform.Find("selected"); if (((obj2 != null) ? new bool?(((Component)obj2).gameObject.activeSelf) : null).GetValueOrDefault()) { return i; } } } return -1; } private static void EnsureBuySelectionVisible(StoreGui gui, List<GameObject> buyList) { if ((Object)(object)gui == (Object)null || buyList == null) { return; } if (!string.IsNullOrEmpty(_focusedBuyHeader)) { MarkHeaderSelected(_buyHeaderLookup, _focusedBuyHeader); ClearBuyItemSelection(buyList); return; } int num = FindSelectedBuyIndex(buyList); int num2 = FindNearestVisibleBuyIndex((num >= 0) ? num : 0, buyList.Count); if (_focusedBuyIndex >= 0 && IsBuyIndexVisible(_focusedBuyIndex, buyList.Count)) { StoreGuiPatches.InvokeSelectItem(gui, _focusedBuyIndex, center: false); return; } if (num >= 0 && IsBuyIndexVisible(num, buyList.Count)) { _focusedBuyIndex = num; return; } StoreGuiPatches.InvokeSelectItem(gui, num2, center: false); _focusedBuyIndex = num2; } private static void CacheLocalization() { if (_localizationCached) { return; } _localizationCached = true; try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type type = assembly.GetType("Localization"); if (!(type == null)) { _localizationInstance = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) ?? type.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null); if (_localizationInstance != null) { _localizeMethod = type.GetMethod("Localize", new Type[1] { typeof(string) }); break; } } } } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)("[UIManager] CacheLocalization failed: " + ex.Message)); } } internal static string Localize(string token) { if (string.IsNullOrEmpty(token)) { return token; } CacheLocalization(); if (_localizeMethod != null && _localizationInstance != null) { try { if (_localizeMethod.Invoke(_localizationInstance, new object[1] { token }) is string text && !string.IsNullOrEmpty(text)) { return text; } } catch (Exception ex) { HaldorOverhaul.Log.LogWarning((object)("[UIManager] Localize failed for '" + token + "': " + ex.Message)); } } return (token.StartsWith("$") ? token.Substring(1) : token).Replace('_', ' '); } private static int CalculateInventoryHash(List<ItemData> items) { if (items == null || items.Count == 0) { return 0; } int num = items.Count; for (int i = 0; i < items.Count; i++) { ItemData val = items[i]; if (val != null) { num = (num * 397) ^ val.m_stack.GetHashCode(); num = (num * 397) ^ val.m_quality.GetHashCode(); if ((Object)(object)val.m_dropPrefab != (Object)null) { num = (num * 397) ^ ((Object)val.m_dropPrefab).name.GetHashCode(); } else if (val.m_shared != null && val.m_shared.m_name != null) { num = (num * 397) ^ val.m_shared.m_name.GetHashCode(); } } } return num; } private static void DestroyChildSafe(Transform parent, string name) { Transform obj = parent.Find(name); Object.Destroy((Object)(object)((obj != null) ? ((Component)obj).gameObject : null)); } private static void ClearBuyItemSelection(List<GameObject> buyList = null) { if (buyList == null) { buyList = (((Object)(object)_currentStore != (Object)null) ? StoreGuiPatches.GetItemList(_currentStore) : null); } if (buyList == null) { return; } foreach (GameObject buy in buyList) { if (buy != null) { Transform obj = buy.transform.Find("selected"); if (obj != null) { ((Component)obj).gameObject.SetActive(false); } } } } private static void NormalizeBuySelection(List<GameObject> buyList, int targetIndex = -1) { if (buyList == null) { return; } if (targetIndex < 0) { targetIndex = FindSelectedBuyIndex(buyList); } for (int i = 0; i < buyList.Count; i++) { GameObject obj = buyList[i]; if (obj != null) { Transform obj2 = obj.transform.Find("selected"); if (obj2 != null) { ((Component)obj2).gameObject.SetActive(i == targetIndex); } } } } private static Transform FindChildRecursive(Transform root, string name) { if ((Object)(object)root == (Object)null || string.IsNullOrEmpty(name)) { return null; } for (int i = 0; i < root.childCount; i++) { Transform child = root.GetChild(i); if (string.Equals(((Object)child).name, name, StringComparison.OrdinalIgnoreCase)) { return child; } Transform val = FindChildRecursive(child, name); if ((Object)(object)val != (Object)null) { return val; } } return null; } internal static void ResetBuyListScroll(StoreGui gui) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) ScrollRect buyScrollRect = GetBuyScrollRect(gui); if ((Object)(object)buyScrollRect != (Object)null) { buyScrollRect.velocity = Vector2.zero; } } internal static Vector2? GetBuyScrollPosition(StoreGui gui) { return GetScrollPosition(GetBuyScrollRect(gui)); } internal static float? GetBuyScrollNormalized(StoreGui gui) { ScrollRect buyScrollRect = GetBuyScrollRect(gui); if (buyScrollRect == null) { return null; } return buyScrollRect.verticalNormalizedPosition; } internal static void RestoreBuyScroll(StoreGui gui, Vector2? anchored, float? normalized) { RestoreScroll(GetBuyScrollRect(gui), anchored, normalized); } private static ScrollRect GetBuyScrollRect(StoreGui gui) { if (gui == null) { return null; } GameObject rootPanel = gui.m_rootPanel; if (rootPanel == null) { return null; } Transform obj = rootPanel.transform.Find("ItemList/Items"); if (obj == null) { return null; } return ((Component)obj).GetComponent<ScrollRect>(); } private static ScrollRect GetSellScrollRect() { RectTransform sellListRoot = _sellListRoot; object obj; if (sellListRoot == null) { obj = null; } else { Transform parent = ((Transform)sellListRoot).parent; obj = ((parent != null) ? ((Component)parent).GetComponent<ScrollRect>() : null); } if (obj == null) { ScrollRectEnsureVisible sellItemEnsureVisible = _sellItemEnsureVisible; if (sellItemEnsureVisible == null) { return null; } obj = ((Component)sellItemEnsureVisible).GetComponent<ScrollRect>(); } return (ScrollRect)obj; } private static Vector2? GetSellScrollPosition() { return GetScrollPosition(GetSellScrollRect()); } private static float? GetSellScrollNormalized() { ScrollRect sellScrollRect = GetSellScrollRect(); if (sellScrollRect == null) { return null; } return sellScrollRect.verticalNormalizedPosition; } private static void RestoreSellScroll(Vector2? anchored, float? normalized) { RestoreScroll(GetSellScrollRect(), anchored, normalized); } private static Vector2? GetScrollPosition(ScrollRect scroll) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (scroll == null) { return null; } RectTransform content = scroll.content; if (content == null) { return null; } return content.anchoredPosition; } private static void RestoreScroll(ScrollRect scroll, Vector2? anchored, float? normalized) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)scroll == (Object)null)) { if (anchored.HasValue && (Object)(object)scroll.content != (Object)null) { scroll.content.anchoredPosition = anchored.Value; } if (normalized.HasValue) { scroll.verticalNormalizedPosition = normalized.Value; } } } internal static void EnsureBuyHeaderVisible(StoreGui gui, string category) { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)gui == (Object)null || string.IsNullOrEmpty(category) || !_buyHeaderLookup.TryGetValue(category, out var value) || (Object)(object)value == (Object)null) { return; } Transform transform = value.transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val == (Object)null) { return; } ScrollRect buyScrollRect = GetBuyScrollRect(gui); if (!((Object)(object)buyScrollRect == (Object)null) && !((Object)(object)buyScrollRect.content == (Object)null)) { int siblingIndex = value.transform.GetSiblingIndex(); float num = (float)siblingIndex * _itemSpacing; if ((Object)(object)buyScrollRect.viewport != (Object)null) { Rect rect = buyScrollRect.content.rect; float height = ((Rect)(ref rect)).height; rect = buyScrollRect.viewport.rect; float num2 = Mathf.Max(0f, height - ((Rect)(ref rect)).height); num = Mathf.Clamp(num, 0f, num2); } buyScrollRect.content.anchoredPosition = new Vector2(buyScrollRect.content.anchoredPosition.x, num); buyScrollRect.velocity = Vector2.zero; } } internal static void EnsureSellHeaderVisible(string category) { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(category) || !_sellHeaderLookup.TryGetValue(category, out var value) || (Object)(object)value == (Object)null) { return; } Transform transform = value.transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val == (Object)null) { return; } ScrollRect sellScrollRect = GetSellScrollRect(); if (!((Object)(object)sellScrollRect == (Object)null) && !((Object)(object)sellScrollRect.content == (Object)null)) { int siblingIndex = value.transform.GetSiblingIndex(); float num = (float)siblingIndex * _itemSpacing; if ((Object)(object)sellScrollRect.viewport != (Object)null) { Rect rect = sellScrollRect.content.rect; float height = ((Rect)(ref rect)).height; rect = sellScrollRect.viewport.rect; float num2 = Mathf.Max(0f, height - ((Rect)(ref rect)).height); num = Mathf.Clamp(num, 0f, num2); } sellScrollRect.content.anchoredPosition = new Vector2(sellScrollRect.content.anchoredPosition.x, num); sellScrollRect.velocity = Vector2.zero; } } internal static void UpdateGamepadHints(StoreGui gui) { if ((Object)(object)gui == (Object)null) { return; } if (!UsingGamepad()) { if ((Object)(object)_gamepadHintText != (Object)null) { ((Component)_gamepadHintText).gameObject.SetActive(false); } return; } EnsureGamepadHint(gui); if (!((Object)(object)_gamepadHintText == (Object)null)) { ((Component)_gamepadHintText).gameObject.SetActive(true); string text = ((_activeTab == ActiveTab.Sell) ? "A: Sell" : "A: Buy"); _gamepadHintText.text = "LB: Buy tab RB: Sell tab LS/D-Pad: Navigate X: Toggle category " + text; } } private static void EnsureGamepadHint(StoreGui gui) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: 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) if (!((Object)(object)_gamepadHintText != (Object)null) && !((Object)(object)gui == (Object)null) && !((Object)(object)gui.m_rootPanel == (Object)null)) { Transform obj = gui.m_rootPanel.transform.Find("topic"); TMP_Text val = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null); if ((Object)(object)val == (Object)null && (Object)(object)_sellPanel != (Object)null) { Transform obj2 = _sellPanel.transform.Find("topic"); val = ((obj2 != null) ? ((Component)obj2).GetComponent<TMP_Text>() : null); } if (!((Object)(object)val == (Object)null)) { _gamepadHintText = Object.Instantiate<TMP_Text>(val, gui.m_rootPanel.transform); ((Object)_gamepadHintText).name = "GamepadHints"; _gamepadHintText.text = ""; RectTransform rectTransform = _gamepadHintText.rectTransform; rectTransform.anchorMin = new Vector2(0.5f, 0f); rectTransform.anchorMax = new Vector2(0.5f, 0f); rectTransform.pivot = new Vector2(0.5f, 0f); rectTransform.anchoredPosition = new Vector2(0f, -30f); _gamepadHintText.alignment = (TextAlignmentOptions)514; _gamepadHintText.fontSize = Mathf.Max(14f, val.fontSize - 2f); } } } private static List<NavigationEntry> BuildBuyNavigation(List<GameObject> buyList) { List<NavigationEntry> list = new List<NavigationEntry>(); if (buyList == null || buyList.Count == 0) { return list; } foreach (string item in _buyCategoryOrder) { if (_buyHeaderLookup.TryGetValue(item, out var value) && (Object)(object)value != (Object)null) { list.Add(new NavigationEntry { Category = item, Index = -1, IsHeader = true }); } if (_buyCategoryCollapsed.TryGetValue(item, out var value2) && value2) { continue; } for (int i = 0; i < buyList.Count; i++) { if (IsBuyIndexVisible(i, buyList.Count)) { string buyCategoryForIndex = GetBuyCategoryForIndex(i); if (string.Equals(buyCategoryForIndex, item, StringComparison.OrdinalIgnoreCase)) { list.Add(new NavigationEntry { Category = item, Index = i, IsHeader = false }); } } } } return list; } private static List<NavigationEntry> BuildSellNavigation() { List<NavigationEntry> list = new List<NavigationEntry>(); foreach (string item in _sellCategoryOrder) { if (_sellHeaderLookup.TryGetValue(item, out var value) && (Object)(object)value != (Object)null) { list.Add(new NavigationEntry { Category = item, Index = -1, IsHeader = true }); } bool value2; bool flag = _sellCategoryCollapsed.TryGetValue(item, out value2) && value2; for (int i = 0; i < SellRows.Count; i++) { SellRow sellRow = SellRows[i]; if (sellRow != null && str