using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using HutongGames.PlayMaker; using HutongGames.PlayMaker.Actions; using InControl; using MerchantStacker.Patches; using Microsoft.CodeAnalysis; using TMProOld; using UnityEngine; using UnityEngine.SceneManagement; 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("MerchantStacker")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+8f0fc1654bce926d1feb93021b9281f4d06d4a57")] [assembly: AssemblyProduct("MerchantStacker")] [assembly: AssemblyTitle("MerchantStacker")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/RaincloudTheDragon/MerchantStacker")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] [Embedded] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] [Embedded] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace Microsoft.CodeAnalysis { [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace MerchantStacker { internal static class Eligibility { public static bool IsStackableShopOffer(ShopItem? item) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)item == (Object)null) { return false; } if (item.IsAvailableNotInfinite) { return false; } if (!IsBulkTypeFlags(item.GetTypeFlags())) { return false; } if (item.Cost <= 0) { return false; } return item.Item is CollectableItem; } private static bool IsBulkTypeFlags(TypeFlags flags) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Invalid comparison between Unknown and I4 if ((int)flags != 0) { return (int)flags == 1; } return true; } public static bool StockHasStackableOffers(ShopMenuStock? stock) { if ((Object)(object)stock == (Object)null) { return false; } try { foreach (ShopItem item in ((ShopMenuStock)(((Object)(object)stock.MasterList != (Object)null) ? ((object)stock.MasterList) : ((object)stock))).EnumerateStock()) { if (IsStackableShopOffer(item)) { return true; } } int itemCount = stock.GetItemCount(); for (int i = 0; i < itemCount; i++) { GameObject itemGameObject = stock.GetItemGameObject(i); ShopItemStats obj = ((itemGameObject != null) ? itemGameObject.GetComponent() : null); if (IsStackableShopOffer((obj != null) ? obj.Item : null)) { return true; } } } catch { } return false; } public static bool IsBulkEligible(ShopItem? item) { if ((Object)(object)item == (Object)null || !item.IsAvailable) { return false; } if (!IsStackableShopOffer(item)) { return false; } SavedItem item2 = item.Item; CollectableItem val = (CollectableItem)(object)((item2 is CollectableItem) ? item2 : null); if (val == null) { return false; } if (((SavedItem)val).CanGetMore()) { return !val.IsAtMax(); } return false; } public static bool ShouldOfferBulkQty(ShopItem? item) { if (!IsBulkEligible(item) || (Object)(object)item == (Object)null) { return false; } return GetMaxQuantity(item) >= 2; } public static int GetMaxQuantity(ShopItem item) { SavedItem item2 = item.Item; CollectableItem val = (CollectableItem)(object)((item2 is CollectableItem) ? item2 : null); if (val == null) { return 0; } int roomUntilCap = GetRoomUntilCap(val); int affordableCount = GetAffordableCount(item); return Math.Max(0, Math.Min(roomUntilCap, affordableCount)); } public static int GetRoomUntilCap(CollectableItem item) { if (!((SavedItem)item).CanGetMore() || item.IsAtMax()) { return 0; } int collectedAmount = item.CollectedAmount; int num = TryGetCap(item); if (num > 0) { return Math.Max(0, num - collectedAmount); } return 99; } public static int GetAffordableCount(ShopItem item) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 int cost = item.Cost; if (cost <= 0) { return 0; } CurrencyType currencyType = item.CurrencyType; if ((int)currencyType != 0) { if ((int)currencyType == 1) { return PlayerData.instance.ShellShards / cost; } return 0; } return PlayerData.instance.geo / cost; } public static int GetAffordableCount(int unitCost, CurrencyType currency) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 if (unitCost <= 0) { return 0; } if ((int)currency != 0) { if ((int)currency == 1) { return PlayerData.instance.ShellShards / unitCost; } return 0; } return PlayerData.instance.geo / unitCost; } private static int TryGetCap(CollectableItem item) { try { FieldInfo field = typeof(CollectableItem).GetField("customMaxAmount", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { int num = (int)field.GetValue(item); if (num > 0) { return num; } } } catch { } return 20; } } [BepInPlugin("io.github.raincloudthedragon.merchantstacker", "MerchantStacker", "1.0.0")] public class MerchantStackerPlugin : BaseUnityPlugin { public const string PluginGuid = "io.github.raincloudthedragon.merchantstacker"; public const string PluginName = "MerchantStacker"; public const string PluginVersion = "1.0.0"; internal static ConfigEntry Enabled; internal static ConfigEntry DpadStep; internal static ConfigEntry StickStep; internal static ConfigEntry HoldInitialDelay; internal static ConfigEntry HoldMinRepeat; internal static ConfigEntry HoldAccel; private Harmony? _harmony; private GameObject? _pickerGo; internal static MerchantStackerPlugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } private void Awake() { //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Enable bulk-buy quantity submenu."); DpadStep = ((BaseUnityPlugin)this).Config.Bind("Controls", "DpadStep", 1, "Quantity change for d-pad up/down."); StickStep = ((BaseUnityPlugin)this).Config.Bind("Controls", "StickStep", 5, "Quantity change for right stick up/down."); HoldInitialDelay = ((BaseUnityPlugin)this).Config.Bind("Controls", "HoldInitialDelay", 0.35f, "Seconds before hold-repeat starts."); HoldMinRepeat = ((BaseUnityPlugin)this).Config.Bind("Controls", "HoldMinRepeat", 0.05f, "Fastest hold-repeat interval in seconds."); HoldAccel = ((BaseUnityPlugin)this).Config.Bind("Controls", "HoldAccel", 0.85f, "Multiply repeat interval by this each step while held (< 1 accelerates)."); _pickerGo = new GameObject("MerchantStacker_QuantityPicker"); Object.DontDestroyOnLoad((Object)(object)_pickerGo); ((Object)_pickerGo).hideFlags = (HideFlags)61; _pickerGo.AddComponent(); _harmony = new Harmony("io.github.raincloudthedragon.merchantstacker"); TryPatch(typeof(ShopSelectionCachePatches)); TryPatch(typeof(ShopConfirmShowPatches)); TryPatch(typeof(ShopConfirmListPatches)); TryPatch(typeof(ConfirmDialogPatches)); TryPatch(typeof(InventoryPaneInputPatches)); TryPatch(typeof(ShopPurchasePatches)); TryPatch(typeof(SimpleShopPatches)); TryPatch(typeof(MachinePurchasePatches)); Log.LogInfo((object)"MerchantStacker v1.0.0 loaded (F6 ScriptEngine reload supported)."); } private void TryPatch(Type type) { try { _harmony.PatchAll(type); Log.LogInfo((object)("Patched " + type.Name)); } catch (Exception arg) { Log.LogError((object)$"Failed to patch {type.Name}: {arg}"); } } private void OnDestroy() { try { QuantityPicker.Instance?.ShutdownForReload(); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("QuantityPicker unload: " + ex.Message)); } } if ((Object)(object)_pickerGo != (Object)null) { Object.DestroyImmediate((Object)(object)_pickerGo); _pickerGo = null; } PurchaseBatcher.ClearPendingQuantity(); PurchaseBatcher.ExpectingFsmPurchase = false; PurchaseBatcher.ClearShopPurchaseSuppression(); try { InventoryPaneInput.IsInputBlocked = false; } catch { } Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)"MerchantStacker unloaded."); } } } internal static class PurchaseBatcher { internal static int PendingQuantity { get; set; } internal static bool IsBatching { get; private set; } internal static bool BlockShopPurchases { get; private set; } internal static bool ExpectingFsmPurchase { get; set; } internal static void ClearPendingQuantity() { PendingQuantity = 0; } internal static int ConsumePendingQuantity() { int pendingQuantity = PendingQuantity; PendingQuantity = 0; return pendingQuantity; } internal static void BeginShopPurchaseBlock() { BlockShopPurchases = true; } internal static void EndShopPurchaseBlock() { BlockShopPurchases = false; } internal static void ClearShopPurchaseSuppression() { BlockShopPurchases = false; } internal static void BuyShopItem(ShopItemStats stats, int quantity, int subItemIndex, Action? onComplete) { if ((Object)(object)stats == (Object)null || (Object)(object)stats.Item == (Object)null) { onComplete?.Invoke(); return; } IsBatching = true; try { int num = 0; for (int i = 0; i < quantity; i++) { if (!stats.CanBuy()) { break; } if (stats.IsAtMax()) { break; } if (!stats.Item.IsAvailable) { break; } stats.SetPurchased((Action)null, subItemIndex); num++; } MerchantStackerPlugin.Log.LogInfo((object)$"Bought {num}x {stats.Item.DisplayName}"); if (num > 0) { BeginShopPurchaseBlock(); } } finally { IsBatching = false; } onComplete?.Invoke(); } internal static void BuyShopItem(ShopItem item, int quantity, int subItemIndex, Action? onComplete) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 if ((Object)(object)item == (Object)null) { onComplete?.Invoke(); return; } IsBatching = true; try { int num = 0; for (int i = 0; i < quantity; i++) { if (!item.IsAvailable) { break; } if (item.IsAtMax()) { break; } CurrencyType currencyType = item.CurrencyType; if (!(((int)currencyType == 0) ? (PlayerData.instance.geo >= item.Cost) : ((int)currencyType == 1 && PlayerData.instance.ShellShards >= item.Cost))) { break; } item.SetPurchased((Action)null, subItemIndex); num++; } MerchantStackerPlugin.Log.LogInfo((object)$"Bought {num}x {item.DisplayName}"); if (num > 0) { BeginShopPurchaseBlock(); } } finally { IsBatching = false; } onComplete?.Invoke(); } } internal sealed class QuantityPicker : MonoBehaviour { private static readonly FieldInfo InstanceField = AccessTools.Field(typeof(DialogueYesNoBox), "_instance"); private static readonly FieldInfo CurrentYesField = AccessTools.Field(typeof(YesNoBox), "currentYes"); private static readonly FieldInfo CurrentNoField = AccessTools.Field(typeof(YesNoBox), "currentNo"); private static readonly FieldInfo SelectedStateField = AccessTools.Field(typeof(YesNoBox), "selectedState"); private static readonly MethodInfo DoEndMethod = AccessTools.Method(typeof(YesNoBox), "DoEnd", (Type[])null, (Type[])null); private static readonly FieldInfo RequiredCurrencyAmountField = AccessTools.Field(typeof(DialogueYesNoBox), "requiredCurrencyAmount"); private static readonly FieldInfo CurrencyTextField = AccessTools.Field(typeof(DialogueYesNoBox), "currencyText"); private static readonly FieldInfo InstantiatedItemsField = AccessTools.Field(typeof(DialogueYesNoBox), "instantiatedItems"); private static readonly FieldInfo WillGetItemField = AccessTools.Field(typeof(DialogueYesNoBox), "willGetItem"); private static readonly FieldInfo ItemTemplateField = AccessTools.Field(typeof(DialogueYesNoBox), "itemTemplate"); private static readonly FieldInfo ItemsLayoutField = AccessTools.Field(typeof(DialogueYesNoBox), "itemsLayout"); private static readonly FieldInfo ItemCostTextField = AccessTools.Field(typeof(ShopItemStats), "itemCostText"); private static readonly FieldInfo TitleTextField = AccessTools.Field(typeof(ShopMenuStock), "titleText"); private static readonly FieldInfo CostSpriteField = AccessTools.Field(typeof(ShopItemStats), "costSprite"); private static readonly FieldInfo ItemSpriteField = AccessTools.Field(typeof(ShopItemStats), "itemSprite"); private static readonly FieldInfo ScrollUpArrowField = AccessTools.Field(typeof(ScrollView), "upArrow"); private static readonly FieldInfo ScrollDownArrowField = AccessTools.Field(typeof(ScrollView), "downArrow"); private static Mesh? _cachedCaretMesh; private static Material? _cachedCaretMaterial; private static int _cachedCaretSortLayer; private static int _cachedCaretSortOrder; private static Sprite? _cachedArrowSprite; private static bool _arrowAssetsSearched; private bool _active; private bool _hijacked; private bool _inShop; private int _quantity = 1; private int _min = 1; private int _max = 1; private int _unitCost; private CurrencyType _currency; private string _title = "Buy"; private CollectableItem? _item; private ShopItemStats? _shopStats; private Transform? _shopRoot; private string? _originalCostText; private TextMeshPro? _boundCostText; private ShopMenuStock? _boundStock; private string? _originalShopTitle; private GameObject? _hudRoot; private TextMeshPro? _hudQty; private TextMeshPro? _hudCost; private GameObject? _hudArrowUp; private GameObject? _hudArrowDown; private SpriteRenderer? _hudCurrencyIcon; private GameObject? _hiddenConfirmList; private GameObject? _hiddenConfirmMsg; private readonly List _hiddenNativeCost = new List(); private readonly List _chromeToKeepOff = new List(); private GameObject? _itemArtSprite; private GameObject? _itemArtName; private SpriteRenderer? _itemArtSpriteSr; private TextMeshPro? _itemArtNameTmp; private int _lastHudQty = int.MinValue; private int _lastHudTotal = int.MinValue; private bool _lastArrowUpOn; private bool _lastArrowDownOn; private float _maxRecalcTimer; private int _chromeKeepOffFrame; private MeshRenderer? _itemArtNameMr; private Vector3 _qtyLocal; private Vector3 _costLocal; private Vector3 _currencyLocal; private Vector3 _arrowUpLocal; private Vector3 _arrowDownLocal; private Vector3 _arrowUpBaseLocalScale = Vector3.one; private Vector3 _arrowDownBaseLocalScale = Vector3.one; private Quaternion _arrowUpLocalRot = Quaternion.identity; private Quaternion _arrowDownLocalRot = Quaternion.identity; private Action? _originalYes; private Action? _originalNo; private Action? _onConfirm; private Action? _onCancel; private Action? _purchaseDone; private Action? _shopCancel; private float _upTimer; private float _downTimer; private float _upInterval; private float _downInterval; private bool _upHeld; private bool _downHeld; private int _heldStep; internal static QuantityPicker? Instance { get; private set; } internal static bool SuppressHijack { get; private set; } public bool IsOpen => _active; public int CurrentQuantity => _quantity; private void Awake() { Instance = this; } internal void ShutdownForReload() { SuppressHijack = false; try { if (_active) { if (_inShop) { AbortInShopSession(resetShopWindow: true); } else { Finish(confirmed: false); } } else { DestroyInShopHud(restoreTexts: true); } } catch (Exception ex) { MerchantStackerPlugin.Log.LogWarning((object)("QuantityPicker shutdown: " + ex.Message)); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } public void OpenInShop(Transform shopRoot, string title, CollectableItem? item, int unitCost, CurrencyType currency, int maxQuantity, ShopItemStats stats) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (MerchantStackerPlugin.Enabled.Value && maxQuantity >= 1 && !_active && !((Object)(object)((stats != null) ? stats.Item : null) == (Object)null)) { BeginSession(title, item, unitCost, currency, maxQuantity, hijacked: false); _inShop = true; _shopRoot = shopRoot; _shopStats = stats; _onConfirm = null; _onCancel = null; BindShopCostText(stats); BuildConfirmQtyHud(); if ((Object)(object)_hudRoot == (Object)null) { MerchantStackerPlugin.Log.LogWarning((object)"OpenInShop: HUD failed — restoring vanilla confirm"); _active = false; _inShop = false; _shopRoot = null; _shopStats = null; InventoryPaneInput.IsInputBlocked = false; ShopConfirmListPatches.RestoreConfirmChromePublic(); } else { RefreshInShopHud(); ((MonoBehaviour)this).StartCoroutine(EnsureHudSoon()); MerchantStackerPlugin.Log.LogInfo((object)$"In-shop qty: {_title} max={_max} cost={_unitCost} hud=True"); } } } public void ArmShopPurchaseSession(Action? onPurchaseComplete, Action? onCancelPurchase) { _purchaseDone = onPurchaseComplete; _shopCancel = onCancelPurchase; } private void LateUpdate() { if (_active && _inShop) { if ((++_chromeKeepOffFrame & 0xF) == 0) { KeepCachedItemArtVisible(); } Transform val = (Transform)(((Object)(object)_shopRoot != (Object)null) ? ((object)_shopRoot) : ((object)(((Object)(object)_shopStats != (Object)null) ? ((Component)_shopStats).transform.root : null))); if ((Object)(object)val == (Object)null || !Object.op_Implicit((Object)(object)val) || !((Component)val).gameObject.activeInHierarchy) { AbortInShopSession(resetShopWindow: true); } } } private void KeepCachedChromeOff() { for (int i = 0; i < _chromeToKeepOff.Count; i++) { GameObject val = _chromeToKeepOff[i]; if ((Object)(object)val != (Object)null && val.activeSelf) { val.SetActive(false); } } } private void KeepCachedItemArtVisible() { if ((Object)(object)_itemArtSprite != (Object)null && !_itemArtSprite.activeSelf) { _itemArtSprite.SetActive(true); } if ((Object)(object)_itemArtSpriteSr != (Object)null && !((Renderer)_itemArtSpriteSr).enabled) { ((Renderer)_itemArtSpriteSr).enabled = true; } if ((Object)(object)_itemArtName != (Object)null && !_itemArtName.activeSelf) { _itemArtName.SetActive(true); } if ((Object)(object)_itemArtNameTmp != (Object)null) { if (!((Behaviour)_itemArtNameTmp).enabled) { ((Behaviour)_itemArtNameTmp).enabled = true; } if ((Object)(object)_itemArtNameMr != (Object)null && !((Renderer)_itemArtNameMr).enabled) { ((Renderer)_itemArtNameMr).enabled = true; } } } private void CacheChromeToKeepOff(Transform confirm, Transform? confirmGroup) { _chromeToKeepOff.Clear(); Transform[] componentsInChildren = ((Component)(((Object)(object)confirmGroup != (Object)null) ? ((object)confirmGroup) : ((object)confirm))).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } string name = ((Object)val).name; if (!(name == "UI List") || !((Object)(object)val.parent != (Object)null) || !(((Object)val.parent).name == "Confirm")) { switch (name) { case "Yes": case "No": if (!ShopConfirmListPatches.IsUnderShopConfirmPublic(val)) { continue; } break; case "Confirm msg": case "Costs": case "Thankyou": case "Money": case "Item cost": case "Geo Sprite": break; default: continue; } } _chromeToKeepOff.Add(((Component)val).gameObject); } ShopConfirmShowPatches.LockConfirmChrome = true; KeepCachedChromeOff(); } private void CacheItemArt(Transform layoutParent) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown _itemArtSprite = null; _itemArtName = null; _itemArtSpriteSr = null; _itemArtNameTmp = null; foreach (Transform item in layoutParent) { Transform val = item; if (!((Object)(object)val == (Object)null)) { if (((Object)val).name == "Item Sprite") { _itemArtSprite = ((Component)val).gameObject; _itemArtSpriteSr = ((Component)val).GetComponent(); } else if (((Object)val).name == "Item name") { _itemArtName = ((Component)val).gameObject; _itemArtNameTmp = ((Component)val).GetComponent(); _itemArtNameMr = (((Object)(object)_itemArtNameTmp != (Object)null) ? ((Component)_itemArtNameTmp).GetComponent() : null); } } } } private Transform? GetShopMenuRoot() { try { if ((Object)(object)_shopStats != (Object)null) { return ((Component)_shopStats).transform.root; } if ((Object)(object)_shopRoot != (Object)null) { return _shopRoot.root; } } catch { } return null; } private void EnsureConfirmItemArtVisible() { Transform shopMenuRoot = GetShopMenuRoot(); if ((Object)(object)shopMenuRoot == (Object)null) { return; } Transform val = FindNamedTransform(shopMenuRoot, "Item Confirm Group"); if (!((Object)(object)val == (Object)null)) { if (!((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(true); } CacheItemArt(val); KeepCachedItemArtVisible(); } } private void AbortInShopSession(bool resetShopWindow) { if (_active) { MerchantStackerPlugin.Log.LogInfo((object)"In-shop qty aborted (shop closed/disabled)"); PurchaseBatcher.ClearPendingQuantity(); PurchaseBatcher.ExpectingFsmPurchase = false; PurchaseBatcher.ClearShopPurchaseSuppression(); Action shopCancel = _shopCancel; _purchaseDone = null; _shopCancel = null; _active = false; _hijacked = false; _inShop = false; _shopRoot = null; _shopStats = null; DestroyInShopHud(restoreTexts: true); ResetHoldState(); InventoryPaneInput.IsInputBlocked = false; if (resetShopWindow) { shopCancel?.Invoke(); } } } public void Hijack(DialogueYesNoBox box, string title, CollectableItem? item, int unitCost, CurrencyType currency, int maxQuantity, Action? originalYes, Action? originalNo) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (MerchantStackerPlugin.Enabled.Value && maxQuantity >= 1 && !_active) { BeginSession(title, item, unitCost, currency, maxQuantity, hijacked: true); _inShop = false; _originalYes = originalYes; _originalNo = originalNo; _onConfirm = null; _onCancel = null; _purchaseDone = null; _shopCancel = null; CurrentYesField.SetValue(box, (Action)delegate { Finish(confirmed: true); }); CurrentNoField.SetValue(box, (Action)delegate { Finish(confirmed: false); }); EnsureWillGetDisplay(box, item); ((MonoBehaviour)this).StartCoroutine(RefreshAfterOpen()); MerchantStackerPlugin.Log.LogInfo((object)$"Hijack DialogueYesNoBox: {_title} max={_max} cost={_unitCost}"); } } public void Open(string title, CollectableItem? item, int unitCost, CurrencyType currency, int maxQuantity, Action onConfirm, Action onCancel) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) if (!MerchantStackerPlugin.Enabled.Value || maxQuantity < 1) { onConfirm(1); return; } BeginSession(title, item, unitCost, currency, maxQuantity, hijacked: false); _inShop = false; _onConfirm = onConfirm; _onCancel = onCancel; _originalYes = null; _originalNo = null; _purchaseDone = null; _shopCancel = null; SuppressHijack = true; try { DialogueYesNoBox.Open((Action)delegate { Finish(confirmed: true); }, (Action)delegate { Finish(confirmed: false); }, true, _title, _currency, _unitCost, (IReadOnlyList)null, (IReadOnlyList)null, true, false, (SavedItem)(object)_item, (TakeItemTypes)0, (DisplayType)1); } finally { SuppressHijack = false; } ((MonoBehaviour)this).StartCoroutine(RefreshAfterOpen()); MerchantStackerPlugin.Log.LogInfo((object)$"Native qty open: {_title} max={_max} cost={_unitCost}"); } private void BeginSession(string title, CollectableItem? item, int unitCost, CurrencyType currency, int maxQuantity, bool hijacked) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) _title = (string.IsNullOrEmpty(title) ? "Buy" : title); _item = item; _unitCost = Math.Max(1, unitCost); _currency = currency; _min = 1; _max = Math.Max(1, maxQuantity); _quantity = 1; _hijacked = hijacked; _inShop = false; _shopRoot = null; _shopStats = null; _active = true; ResetHoldState(); InventoryPaneInput.IsInputBlocked = true; } private void BindShopCostText(ShopItemStats stats) { _boundCostText = null; _originalCostText = null; _boundStock = null; _originalShopTitle = null; try { object? obj = ItemCostTextField?.GetValue(stats); TextMeshPro val = (TextMeshPro)((obj is TextMeshPro) ? obj : null); if (val != null) { _boundCostText = val; _originalCostText = ((TMP_Text)val).text; } _boundStock = ((Component)stats).GetComponentInParent(); if ((Object)(object)_boundStock != (Object)null) { _originalShopTitle = _boundStock.Title; } } catch { } } private void BuildConfirmQtyHud() { //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0125: 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_0177: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_hudRoot != (Object)null || (Object)(object)_shopStats == (Object)null) { return; } ShopConfirmShowPatches.BuildingQtyHud = true; try { Transform root = ((Component)_shopStats).transform.root; Transform val = FindNamedTransform(root, "Item Confirm Group"); Transform val2 = FindNamedTransform(root, "Confirm"); if ((Object)(object)val2 == (Object)null) { MerchantStackerPlugin.Log.LogWarning((object)"Qty HUD: Confirm transform not found"); return; } TextMeshPro val3 = FindTemplateTmp(val2) ?? _boundCostText; if ((Object)(object)val3 == (Object)null) { MerchantStackerPlugin.Log.LogWarning((object)"Qty HUD: no TMP template"); return; } Transform val4 = (((Object)(object)val != (Object)null) ? val : val2); EnsureConfirmItemArtVisible(); GetQtyRowLocal(val4, out _qtyLocal, out _arrowUpLocal, out _arrowDownLocal, out _currencyLocal, out _costLocal); _hudRoot = new GameObject("MerchantStacker_QtyHud"); _hudRoot.layer = ((Component)val4).gameObject.layer; _hudRoot.transform.SetParent(val4, false); _hudRoot.transform.localPosition = Vector3.zero; _hudRoot.transform.localRotation = Quaternion.identity; _hudRoot.transform.localScale = Vector3.one; TextMeshPro val5 = FindConfirmCostTmp(val2) ?? val3; float num = Math.Max(5f, ((TMP_Text)val5).fontSize); _hudQty = CloneTmpLocal(val3, _hudRoot.transform, "Qty", _qtyLocal, "1", num * 2.7f, (TextAlignmentOptions)5); _hudCost = CloneTmpLocal(val5, _hudRoot.transform, "TotalCost", _costLocal, "0", num * 2f, (TextAlignmentOptions)4); object? obj = CostSpriteField?.GetValue(_shopStats); SpriteRenderer val6 = (SpriteRenderer)((obj is SpriteRenderer) ? obj : null); if ((Object)(object)val6 != (Object)null) { _hudCurrencyIcon = CloneSpriteLocal(val6, _hudRoot.transform, "CurrencyIcon", _currencyLocal, 2f); Sprite currencySprite = _shopStats.GetCurrencySprite(); if ((Object)(object)currencySprite != (Object)null) { _hudCurrencyIcon.sprite = currencySprite; } } if (TryCreateVerticalArrowsLocal(val2, val4, _hudRoot.transform, _arrowUpLocal, _arrowDownLocal)) { MerchantStackerPlugin.Log.LogInfo((object)"Qty HUD arrows: ok"); } else { MerchantStackerPlugin.Log.LogWarning((object)"Qty HUD: no arrow templates found"); } HideConfirmChrome(val2, val); HideNativeConfirmCosts(val2); CacheChromeToKeepOff(val2, val); _lastHudQty = int.MinValue; _lastHudTotal = int.MinValue; _lastArrowUpOn = _quantity >= _max; _lastArrowDownOn = _quantity <= _min; MatchArrowDrawOrderToQty(); RefreshInShopHud(); ReassertHudPoses(); ForceArrowRenderersOn(); MerchantStackerPlugin.Log.LogInfo((object)($"Qty HUD row qty={_qtyLocal} cost={_costLocal} arrows={(Object)(object)_hudArrowUp != (Object)null} " + $"arrowSize={MeshSize(_hudArrowUp)}")); } catch (Exception ex) { MerchantStackerPlugin.Log.LogWarning((object)("Qty HUD build failed: " + ex.Message)); DestroyInShopHud(restoreTexts: true); } finally { ShopConfirmShowPatches.BuildingQtyHud = false; } } private void HideNativeConfirmCosts(Transform confirm) { _hiddenNativeCost.Clear(); Transform[] componentsInChildren = ((Component)confirm).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } string name = ((Object)val).name; if (!(name != "Costs") || !(name != "Money") || !(name != "Item cost") || !(name != "Geo Sprite")) { MeshRenderer[] componentsInChildren2 = ((Component)val).GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren2.Length; j++) { ((Renderer)componentsInChildren2[j]).enabled = false; } SpriteRenderer[] componentsInChildren3 = ((Component)val).GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren3.Length; j++) { ((Renderer)componentsInChildren3[j]).enabled = false; } if (((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(false); } _hiddenNativeCost.Add(((Component)val).gameObject); } } } private static TextMeshPro? FindConfirmCostTmp(Transform confirm) { TextMeshPro[] componentsInChildren = ((Component)confirm).GetComponentsInChildren(true); foreach (TextMeshPro val in componentsInChildren) { if ((Object)(object)val != (Object)null && ((Object)((Component)val).gameObject).name == "Item cost") { return val; } } return null; } private static void GetQtyRowLocal(Transform layoutParent, out Vector3 qtyLocal, out Vector3 upLocal, out Vector3 downLocal, out Vector3 currencyLocal, out Vector3 costLocal) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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_00e7: 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_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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) Vector3 localPosition = default(Vector3); ((Vector3)(ref localPosition))..ctor(-3.4f, 1.5f, -3f); foreach (Transform item in layoutParent) { Transform val = item; if ((Object)(object)val != (Object)null && ((Object)val).name == "Item Sprite") { localPosition = val.localPosition; break; } } float z = localPosition.z; float num = localPosition.y - 3.5f; float num2 = localPosition.x - 0.85f; qtyLocal = new Vector3(num2 - 0.9f, num, z); upLocal = new Vector3(qtyLocal.x, num + 1.1f, z); downLocal = new Vector3(qtyLocal.x, num - 1.1f, z); currencyLocal = new Vector3(num2 + 0.85f, num, z); costLocal = new Vector3(num2 + 1.55f, num, z); } private bool TryCreateVerticalArrowsLocal(Transform confirm, Transform layoutParent, Transform arrowParent, Vector3 upLocal, Vector3 downLocal) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: 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_011a: 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_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) SpriteRenderer val = (SpriteRenderer)(((object)_hudCurrencyIcon) ?? ((object)/*isinst with value type is only supported in some contexts*/)); EnsureArrowSpriteCached(); if ((Object)(object)_cachedArrowSprite != (Object)null && (Object)(object)val != (Object)null) { _hudArrowUp = CloneSpriteCaret(val, arrowParent, "ArrowUp", upLocal, 90f, 2f); _hudArrowDown = CloneSpriteCaret(val, arrowParent, "ArrowDown", downLocal, 270f, 2f); ApplySprite(_hudArrowUp, _cachedArrowSprite); ApplySprite(_hudArrowDown, _cachedArrowSprite); RememberArrowBaseScales(); MerchantStackerPlugin.Log.LogInfo((object)$"Arrows from sprite '{((Object)_cachedArrowSprite).name}' size={SpriteSize(_hudArrowUp)}"); return true; } if ((Object)(object)val != (Object)null && (Object)(object)val.sprite != (Object)null) { _hudArrowUp = CloneSpriteCaret(val, arrowParent, "ArrowUp", upLocal, 90f, 2f); _hudArrowDown = CloneSpriteCaret(val, arrowParent, "ArrowDown", downLocal, 270f, 2f); RememberArrowBaseScales(); MerchantStackerPlugin.Log.LogInfo((object)$"Arrows from currency sprite '{((Object)val.sprite).name}' size={SpriteSize(_hudArrowUp)}"); return true; } EnsureCaretMeshCached(confirm, layoutParent); if ((Object)(object)_cachedCaretMesh != (Object)null && (Object)(object)_cachedCaretMaterial != (Object)null) { _hudArrowUp = CreateMeshCaret(arrowParent, "ArrowUp", upLocal, 90f, 0.9f); _hudArrowDown = CreateMeshCaret(arrowParent, "ArrowDown", downLocal, -90f, 0.9f); RememberArrowBaseScales(); MerchantStackerPlugin.Log.LogInfo((object)$"Arrows from tk2d caret mesh size={MeshSize(_hudArrowUp)}"); if ((Object)(object)_hudArrowUp != (Object)null) { return (Object)(object)_hudArrowDown != (Object)null; } return false; } return false; } private static void EnsureCaretMeshCached(Transform confirm, Transform layoutParent) { //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0222: 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_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_cachedCaretMesh != (Object)null && (Object)(object)_cachedCaretMaterial != (Object)null) { return; } GameObject val = null; Transform val2 = FindNamedTransform(confirm, "Pointer L") ?? FindNamedTransform(layoutParent, "Pointer L"); if ((Object)(object)val2 != (Object)null) { Transform parent = val2.parent; while ((Object)(object)parent != (Object)null && ((Object)parent).name != "Yes" && ((Object)parent).name != "Confirm") { parent = parent.parent; } if ((Object)(object)parent != (Object)null && ((Object)parent).name == "Yes" && !((Component)parent).gameObject.activeSelf) { ((Component)parent).gameObject.SetActive(true); } ((Component)val2).gameObject.SetActive(true); InvAnimateUpAndDown component = ((Component)val2).GetComponent(); if (component != null) { component.Show(); } val = ((Component)val2).gameObject; } if ((Object)(object)val == (Object)null) { ScrollView[] array = Resources.FindObjectsOfTypeAll(); foreach (ScrollView val3 in array) { if ((Object)(object)val3 == (Object)null) { continue; } Scene scene = ((Component)val3).gameObject.scene; if (!((Scene)(ref scene)).IsValid()) { continue; } object? obj = ScrollUpArrowField?.GetValue(val3); Component val4 = (Component)((obj is Component) ? obj : null); if (!((Object)(object)val4 == (Object)null)) { val4.gameObject.SetActive(true); Component obj2 = ((val4 is InvAnimateUpAndDown) ? val4 : null); if (obj2 != null) { ((InvAnimateUpAndDown)obj2).Show(); } val = val4.gameObject; break; } } } if ((Object)(object)val == (Object)null) { return; } MeshFilter componentInChildren = val.GetComponentInChildren(true); MeshRenderer componentInChildren2 = val.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren2 == (Object)null || (Object)(object)componentInChildren.sharedMesh == (Object)null || (Object)(object)((Renderer)componentInChildren2).sharedMaterial == (Object)null) { MerchantStackerPlugin.Log.LogWarning((object)$"Caret bake skipped: mesh={(Object)(object)((componentInChildren != null) ? componentInChildren.sharedMesh : null) != (Object)null} mat={(Object)(object)((componentInChildren2 != null) ? ((Renderer)componentInChildren2).sharedMaterial : null) != (Object)null}"); return; } Bounds bounds = componentInChildren.sharedMesh.bounds; Vector3 size = ((Bounds)(ref bounds)).size; if (Mathf.Max(size.x, size.y) < 0.08f) { MerchantStackerPlugin.Log.LogInfo((object)$"Caret bake skipped: mesh too small ({size})"); return; } _cachedCaretMesh = Object.Instantiate(componentInChildren.sharedMesh); ((Object)_cachedCaretMesh).name = "MerchantStacker_CaretMesh"; _cachedCaretMaterial = ((Renderer)componentInChildren2).sharedMaterial; _cachedCaretSortLayer = ((Renderer)componentInChildren2).sortingLayerID; _cachedCaretSortOrder = Math.Max(((Renderer)componentInChildren2).sortingOrder + 20, 260); ManualLogSource log = MerchantStackerPlugin.Log; string text = $"Cached tk2d caret mesh verts={_cachedCaretMesh.vertexCount} "; bounds = _cachedCaretMesh.bounds; log.LogInfo((object)(text + $"bounds={((Bounds)(ref bounds)).size}")); } private static void EnsureArrowSpriteCached() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) if (_arrowAssetsSearched) { return; } _arrowAssetsSearched = true; InventoryArrowContainer[] array = Resources.FindObjectsOfTypeAll(); foreach (InventoryArrowContainer val in array) { if ((Object)(object)val == (Object)null) { continue; } Scene scene = ((Component)val).gameObject.scene; if (!((Scene)(ref scene)).IsValid()) { continue; } SpriteRenderer[] componentsInChildren = ((Component)val).GetComponentsInChildren(true); foreach (SpriteRenderer val2 in componentsInChildren) { if ((Object)(object)((val2 != null) ? val2.sprite : null) != (Object)null && LooksLikeArrowSprite(((Object)val2.sprite).name, ((Object)((Component)val2).gameObject).name)) { _cachedArrowSprite = val2.sprite; MerchantStackerPlugin.Log.LogInfo((object)("Cached arrow sprite from InventoryArrowContainer '" + ((Object)val2.sprite).name + "'")); return; } } } Sprite[] array2 = Resources.FindObjectsOfTypeAll(); foreach (Sprite val3 in array2) { if ((Object)(object)val3 == (Object)null || !LooksLikeArrowSprite(((Object)val3).name, ((Object)val3).name)) { continue; } Rect rect = val3.rect; if (!(((Rect)(ref rect)).width > 128f)) { rect = val3.rect; if (!(((Rect)(ref rect)).height > 128f)) { _cachedArrowSprite = val3; ManualLogSource log = MerchantStackerPlugin.Log; string name = ((Object)val3).name; rect = val3.rect; object arg = ((Rect)(ref rect)).width; rect = val3.rect; log.LogInfo((object)$"Cached arrow sprite '{name}' ({arg}x{((Rect)(ref rect)).height})"); break; } } } } private static bool LooksLikeArrowSprite(string spriteName, string goName) { string haystack = spriteName + " " + goName; if (ContainsIgnore(haystack, "map") || ContainsIgnore(haystack, "quest") || ContainsIgnore(haystack, "pan") || ContainsIgnore(haystack, "compass") || ContainsIgnore(haystack, "marker")) { return false; } if (!ContainsIgnore(haystack, "arrow") && !ContainsIgnore(haystack, "caret") && !ContainsIgnore(haystack, "chevron")) { return ContainsIgnore(haystack, "pointer"); } return true; } private static GameObject CreateMeshCaret(Transform parent, string name, Vector3 localPos, float rotationZ, float scale) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.layer = ((Component)parent).gameObject.layer; val.transform.SetParent(parent, false); val.transform.localPosition = localPos; val.transform.localRotation = Quaternion.Euler(0f, 0f, rotationZ); val.transform.localScale = Vector3.one * scale; val.AddComponent().sharedMesh = _cachedCaretMesh; MeshRenderer val2 = val.AddComponent(); ((Renderer)val2).sharedMaterial = _cachedCaretMaterial; ((Renderer)val2).sortingLayerID = _cachedCaretSortLayer; ((Renderer)val2).sortingOrder = _cachedCaretSortOrder; ((Renderer)val2).enabled = true; Bounds bounds = ((Renderer)val2).bounds; float x = ((Bounds)(ref bounds)).size.x; bounds = ((Renderer)val2).bounds; float num = Mathf.Max(x, ((Bounds)(ref bounds)).size.y); if (num > 0.0001f && num < 0.5f) { Transform transform = val.transform; transform.localScale *= 0.8f / num; } return val; } private static void ApplySprite(GameObject? go, Sprite sprite) { if (!((Object)(object)go == (Object)null)) { SpriteRenderer component = go.GetComponent(); if ((Object)(object)component != (Object)null) { component.sprite = sprite; ((Renderer)component).enabled = true; } } } private void RememberArrowBaseScales() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_hudArrowUp != (Object)null) { _arrowUpBaseLocalScale = _hudArrowUp.transform.localScale; _arrowUpLocalRot = _hudArrowUp.transform.localRotation; } if ((Object)(object)_hudArrowDown != (Object)null) { _arrowDownBaseLocalScale = _hudArrowDown.transform.localScale; _arrowDownLocalRot = _hudArrowDown.transform.localRotation; } } private void DestroyArrowPair() { if ((Object)(object)_hudArrowUp != (Object)null) { Object.Destroy((Object)(object)_hudArrowUp); _hudArrowUp = null; } if ((Object)(object)_hudArrowDown != (Object)null) { Object.Destroy((Object)(object)_hudArrowDown); _hudArrowDown = null; } } private static Vector3 MeshSize(GameObject? go) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)go == (Object)null) { return Vector3.zero; } MeshRenderer componentInChildren = go.GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { Bounds bounds = ((Renderer)componentInChildren).bounds; return ((Bounds)(ref bounds)).size; } return SpriteSize(go); } private static Vector3 SpriteSize(GameObject? go) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)go == (Object)null) { return Vector3.zero; } SpriteRenderer componentInChildren = go.GetComponentInChildren(true); if (!((Object)(object)componentInChildren != (Object)null)) { return Vector3.zero; } Bounds bounds = ((Renderer)componentInChildren).bounds; return ((Bounds)(ref bounds)).size; } private static GameObject CloneSpriteCaret(SpriteRenderer template, Transform parent, string name, Vector3 localPos, float rotationZ, float scaleMul) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate(((Component)template).gameObject, parent); ((Object)val).name = name; val.layer = ((Component)parent).gameObject.layer; val.SetActive(true); val.transform.localPosition = localPos; val.transform.localRotation = Quaternion.Euler(0f, 0f, rotationZ); val.transform.localScale = Vector3.one * scaleMul; for (int num = val.transform.childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)val.transform.GetChild(num)).gameObject); } SpriteRenderer component = val.GetComponent(); if ((Object)(object)component != (Object)null) { ((Renderer)component).enabled = true; Color color = component.color; color.a = 1f; component.color = color; ((Renderer)component).sortingOrder = Math.Max(((Renderer)template).sortingOrder + 10, 260); ((Renderer)component).sortingLayerID = ((Renderer)template).sortingLayerID; } return val; } private void MatchArrowDrawOrderToQty() { if ((Object)(object)_hudQty == (Object)null) { return; } MeshRenderer component = ((Component)_hudQty).GetComponent(); if ((Object)(object)component == (Object)null) { return; } GameObject[] array = (GameObject[])(object)new GameObject[2] { _hudArrowUp, _hudArrowDown }; foreach (GameObject val in array) { if (!((Object)(object)val == (Object)null)) { MeshRenderer[] componentsInChildren = val.GetComponentsInChildren(true); foreach (MeshRenderer obj in componentsInChildren) { ((Renderer)obj).enabled = true; ((Renderer)obj).sortingLayerID = ((Renderer)component).sortingLayerID; ((Renderer)obj).sortingOrder = ((Renderer)component).sortingOrder + 5; } SpriteRenderer[] componentsInChildren2 = val.GetComponentsInChildren(true); foreach (SpriteRenderer obj2 in componentsInChildren2) { ((Renderer)obj2).enabled = true; ((Renderer)obj2).sortingLayerID = ((Renderer)component).sortingLayerID; ((Renderer)obj2).sortingOrder = ((Renderer)component).sortingOrder + 5; } } } } private void ForceArrowRenderersOn() { GameObject[] array = (GameObject[])(object)new GameObject[2] { _hudArrowUp, _hudArrowDown }; foreach (GameObject val in array) { if (!((Object)(object)val == (Object)null)) { if (!val.activeSelf) { val.SetActive(true); } MeshRenderer[] componentsInChildren = val.GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren.Length; j++) { ((Renderer)componentsInChildren[j]).enabled = true; } SpriteRenderer[] componentsInChildren2 = val.GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren2.Length; j++) { ((Renderer)componentsInChildren2[j]).enabled = true; } } } } private void HideConfirmChrome(Transform confirm, Transform? confirmGroup) { object obj = (((Object)(object)confirmGroup != (Object)null) ? ((object)confirmGroup) : ((object)confirm)); ShopConfirmListPatches.SuppressConfirmChromePublic((Transform)obj); Transform val = confirm.Find("UI List"); if ((Object)(object)val != (Object)null) { _hiddenConfirmList = ((Component)val).gameObject; if (((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(false); } } Transform val2 = FindNamedTransform(confirm, "Confirm msg"); if ((Object)(object)val2 != (Object)null) { _hiddenConfirmMsg = ((Component)val2).gameObject; if (((Component)val2).gameObject.activeSelf) { ((Component)val2).gameObject.SetActive(false); } } Transform[] componentsInChildren = ((Component)obj).GetComponentsInChildren(true); foreach (Transform val3 in componentsInChildren) { if (!((Object)(object)val3 == (Object)null)) { string name = ((Object)val3).name; if ((ContainsIgnore(name, "cursor") || ContainsIgnore(name, "selector")) && ((Component)val3).gameObject.activeSelf) { ((Component)val3).gameObject.SetActive(false); } } } } private static bool ContainsIgnore(string haystack, string needle) { return haystack.IndexOf(needle, StringComparison.OrdinalIgnoreCase) >= 0; } private IEnumerator EnsureHudSoon() { yield return null; if (_active && _inShop) { ReassertHudPoses(); MatchArrowDrawOrderToQty(); ForceArrowRenderersOn(); RefreshInShopHud(); } } private void RefreshInShopHud() { //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) int num = _unitCost * _quantity; bool flag = _quantity != _lastHudQty; bool flag2 = num != _lastHudTotal; _lastHudQty = _quantity; _lastHudTotal = num; ApplyTmp(_hudQty, _quantity.ToString(CultureInfo.InvariantCulture), flag || flag2); ApplyTmp(_hudCost, num.ToString(CultureInfo.InvariantCulture), force: true); if ((Object)(object)_shopStats != (Object)null && (_hiddenNativeCost.Count > 0 || flag2)) { KeepNativeConfirmCostsHidden(); } bool flag3 = _quantity < _max; bool flag4 = _quantity > _min; if (flag3 != _lastArrowUpOn || flag) { _lastArrowUpOn = flag3; SetArrowVisible(_hudArrowUp, flag3, _arrowUpBaseLocalScale); } if (flag4 != _lastArrowDownOn || flag) { _lastArrowDownOn = flag4; SetArrowVisible(_hudArrowDown, flag4, _arrowDownBaseLocalScale); } if ((Object)(object)_hudCurrencyIcon != (Object)null) { ((Renderer)_hudCurrencyIcon).enabled = true; if (!((Component)_hudCurrencyIcon).gameObject.activeSelf) { ((Component)_hudCurrencyIcon).gameObject.SetActive(true); } } } private static void SetArrowVisible(GameObject? arrow, bool fullyVisible, Vector3 baseLocalScale) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)arrow == (Object)null)) { arrow.transform.localScale = baseLocalScale * (fullyVisible ? 1f : 0.55f); MeshRenderer[] componentsInChildren = arrow.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { ((Renderer)componentsInChildren[i]).enabled = true; } SpriteRenderer[] componentsInChildren2 = arrow.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren2.Length; i++) { ((Renderer)componentsInChildren2[i]).enabled = true; } } } private void ReassertHudPoses() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: 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_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_hudQty != (Object)null) { SetLocalPose(_hudQty.transform, _qtyLocal, Vector3.one); } if ((Object)(object)_hudCost != (Object)null) { SetLocalPose(_hudCost.transform, _costLocal, Vector3.one); } if ((Object)(object)_hudCurrencyIcon != (Object)null) { SetLocalPose(((Component)_hudCurrencyIcon).transform, _currencyLocal, ((Component)_hudCurrencyIcon).transform.localScale); } if ((Object)(object)_hudArrowUp != (Object)null) { float num = (_lastArrowUpOn ? 1f : 0.55f); _hudArrowUp.transform.localPosition = _arrowUpLocal; _hudArrowUp.transform.localRotation = _arrowUpLocalRot; _hudArrowUp.transform.localScale = _arrowUpBaseLocalScale * num; } if ((Object)(object)_hudArrowDown != (Object)null) { float num2 = (_lastArrowDownOn ? 1f : 0.55f); _hudArrowDown.transform.localPosition = _arrowDownLocal; _hudArrowDown.transform.localRotation = _arrowDownLocalRot; _hudArrowDown.transform.localScale = _arrowDownBaseLocalScale * num2; } } private static void ApplyTmp(TextMeshPro? tmp, string value, bool force = false) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)tmp == (Object)null) { return; } try { if (!((Component)tmp).gameObject.activeSelf) { ((Component)tmp).gameObject.SetActive(true); } ((Behaviour)tmp).enabled = true; Color color = ((Graphic)tmp).color; if (color.a < 0.9f) { color.a = 1f; ((Graphic)tmp).color = color; } string text = ((TMP_Text)tmp).text ?? string.Empty; if (force || !string.Equals(text.Trim(), value, StringComparison.Ordinal)) { ((TMP_Text)tmp).text = value; ((TMP_Text)tmp).SetText(value); ((TMP_Text)tmp).ForceMeshUpdate(true); } MeshRenderer component = ((Component)tmp).GetComponent(); if ((Object)(object)component != (Object)null) { ((Renderer)component).enabled = true; } } catch { } } private void KeepNativeConfirmCostsHidden() { for (int i = 0; i < _hiddenNativeCost.Count; i++) { GameObject val = _hiddenNativeCost[i]; if ((Object)(object)val != (Object)null && val.activeSelf) { val.SetActive(false); } } } private static void SetTmpAlpha(TextMeshPro? tmp, float alpha) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)tmp == (Object)null)) { Color color = ((Graphic)tmp).color; color.a = alpha; ((Graphic)tmp).color = color; } } private static void SetLocalPose(Transform t, Vector3 localPos, Vector3 localScale) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) t.localPosition = localPos; t.localRotation = Quaternion.identity; t.localScale = localScale; RectTransform val = (RectTransform)(object)((t is RectTransform) ? t : null); if (val != null) { val.anchoredPosition3D = localPos; } } private static TextMeshPro CloneTmpLocal(TextMeshPro template, Transform parent, string name, Vector3 localPos, string text, float fontSize, TextAlignmentOptions alignment = (TextAlignmentOptions)5) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate(((Component)template).gameObject); ((Object)val).name = name; val.transform.SetParent(parent, false); val.SetActive(true); SetLocalPose(val.transform, localPos, Vector3.one); for (int num = val.transform.childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)val.transform.GetChild(num)).gameObject); } TextMeshPro component = val.GetComponent(); ((Behaviour)component).enabled = true; ((TMP_Text)component).fontSize = fontSize; ((TMP_Text)component).alignment = alignment; ((TMP_Text)component).enableWordWrapping = false; Color color = ((Graphic)template).color; color.a = 1f; ((Graphic)component).color = color; ((TMP_Text)component).SetText(text); ((TMP_Text)component).ForceMeshUpdate(true); MeshRenderer component2 = val.GetComponent(); if ((Object)(object)component2 != (Object)null) { ((Renderer)component2).enabled = true; ((Renderer)component2).sortingOrder = Math.Max(((Renderer)component2).sortingOrder, 250); } return component; } private static SpriteRenderer CloneSpriteLocal(SpriteRenderer template, Transform parent, string name, Vector3 localPos, float scaleMul) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) GameObject obj = Object.Instantiate(((Component)template).gameObject); ((Object)obj).name = name; obj.transform.SetParent(parent, false); obj.SetActive(true); SetLocalPose(obj.transform, localPos, ((Component)template).transform.localScale * scaleMul); SpriteRenderer component = obj.GetComponent(); ((Renderer)component).enabled = true; Color color = component.color; color.a = 1f; component.color = color; ((Renderer)component).sortingOrder = Math.Max(((Renderer)component).sortingOrder, 250); return component; } private static Transform? FindNamedTransform(Transform root, string name) { Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if ((Object)(object)val != (Object)null && ((Object)val).name == name) { return val; } } return null; } private static TextMeshPro? FindTemplateTmp(Transform confirm) { TextMeshPro[] componentsInChildren = ((Component)confirm).GetComponentsInChildren(true); foreach (TextMeshPro val in componentsInChildren) { if ((Object)(object)val != (Object)null && ((Object)((Component)val).gameObject).name == "Text" && (Object)(object)val.transform.parent != (Object)null && ((Object)val.transform.parent).name == "Yes") { return val; } } componentsInChildren = ((Component)confirm).GetComponentsInChildren(true); foreach (TextMeshPro val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null) { return val2; } } return null; } private static void ForceHideConfirmChromeOnly(ShopItemStats? stats) { Transform val = null; try { if ((Object)(object)stats != (Object)null) { val = ((Component)stats).transform.root; } } catch { val = null; } if ((Object)(object)val == (Object)null) { ShopMenuStock[] array = Object.FindObjectsByType((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (ShopMenuStock val2 in array) { if (ShopConfirmListPatches.IsLiveOpenShopStock(val2)) { val = ((Component)val2).transform.root; break; } } } if ((Object)(object)val == (Object)null) { return; } ShopConfirmListPatches.RestoreConfirmChromePublic(); ShopConfirmListPatches.ForceEnableConfirmYesNoPublic(); Transform[] componentsInChildren = ((Component)val).GetComponentsInChildren(true); foreach (Transform val3 in componentsInChildren) { if ((Object)(object)val3 == (Object)null || ((Object)val3).name != "Item Confirm Group") { continue; } Transform[] componentsInChildren2 = ((Component)val3).GetComponentsInChildren(true); foreach (Transform val4 in componentsInChildren2) { if ((Object)(object)val4 == (Object)null) { continue; } string name = ((Object)val4).name; if (!(name == "UI List") || !((Object)(object)val4.parent != (Object)null) || !(((Object)val4.parent).name == "Confirm")) { switch (name) { case "Yes": case "No": case "Confirm msg": case "Costs": break; default: continue; } } if (!((Component)val4).gameObject.activeSelf) { ((Component)val4).gameObject.SetActive(true); } } if (((Component)val3).gameObject.activeSelf) { ((Component)val3).gameObject.SetActive(false); } } } private void DestroyInShopHud(bool restoreTexts, bool restoreConfirmChrome = true) { if (restoreTexts) { if ((Object)(object)_boundStock != (Object)null && _originalShopTitle != null) { _boundStock.Title = _originalShopTitle; } if ((Object)(object)_boundCostText != (Object)null && _originalCostText != null) { ApplyTmp(_boundCostText, _originalCostText); } } ClearTmpVisual(_hudQty); ClearTmpVisual(_hudCost); if ((Object)(object)_hudRoot != (Object)null) { Object.DestroyImmediate((Object)(object)_hudRoot); } _hudRoot = null; _hudQty = null; _hudArrowUp = null; _hudArrowDown = null; _hudCost = null; _hudCurrencyIcon = null; _chromeToKeepOff.Clear(); ShopConfirmShowPatches.LockConfirmChrome = false; _itemArtSprite = null; _itemArtName = null; _itemArtSpriteSr = null; _itemArtNameTmp = null; _itemArtNameMr = null; ScrubLeftoverQtyHud(); if (restoreConfirmChrome) { if ((Object)(object)_hiddenConfirmList != (Object)null) { _hiddenConfirmList.SetActive(true); _hiddenConfirmList = null; } if ((Object)(object)_hiddenConfirmMsg != (Object)null) { _hiddenConfirmMsg.SetActive(true); _hiddenConfirmMsg = null; } foreach (GameObject item in _hiddenNativeCost) { if ((Object)(object)item == (Object)null) { continue; } TextMeshPro[] componentsInChildren = item.GetComponentsInChildren(true); foreach (TextMeshPro val in componentsInChildren) { if ((Object)(object)val != (Object)null && ((Object)((Component)val).gameObject).name == "Item cost" && _unitCost > 0) { ApplyTmp(val, _unitCost.ToString(CultureInfo.InvariantCulture)); } } item.SetActive(true); } _hiddenNativeCost.Clear(); } else { _hiddenConfirmList = null; _hiddenConfirmMsg = null; _hiddenNativeCost.Clear(); } if (restoreTexts) { _boundStock = null; _originalShopTitle = null; _boundCostText = null; _originalCostText = null; } } private static void ClearTmpVisual(TextMeshPro? tmp) { if ((Object)(object)tmp == (Object)null) { return; } try { ((TMP_Text)tmp).SetText(string.Empty); ((TMP_Text)tmp).ForceMeshUpdate(true); MeshRenderer component = ((Component)tmp).GetComponent(); if ((Object)(object)component != (Object)null) { ((Renderer)component).enabled = false; } } catch { } } private static void ScrubLeftoverQtyHud() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 GameObject[] array = Resources.FindObjectsOfTypeAll(); foreach (GameObject val in array) { if ((Object)(object)val == (Object)null) { continue; } bool flag; try { Scene scene = val.scene; flag = ((Scene)(ref scene)).IsValid() || (int)((Object)val).hideFlags > 0; } catch { continue; } if ((flag || !(((Object)val).name != "MerchantStacker_QtyHud")) && (((Object)val).name == "MerchantStacker_QtyHud" || ((Object)val).name == "TotalCost" || ((Object)val).name == "Qty" || ((Object)val).name == "ArrowUp" || ((Object)val).name == "ArrowDown" || ((Object)val).name == "CurrencyIcon") && (((Object)val).name == "MerchantStacker_QtyHud" || ((Object)(object)val.transform.parent != (Object)null && (((Object)val.transform.parent).name == "MerchantStacker_QtyHud" || ((Object)val.transform.parent).name == "MerchantStacker_QuantityPicker" || (((Object)val).name == "MerchantStacker_QtyHud" && ((Object)val.transform.parent).name == "Item Confirm Group"))))) { TextMeshPro[] componentsInChildren = val.GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren.Length; j++) { ClearTmpVisual(componentsInChildren[j]); } Object.DestroyImmediate((Object)(object)val); } } } private void ClearPickerHudChildren() { for (int num = ((Component)this).transform.childCount - 1; num >= 0; num--) { Transform child = ((Component)this).transform.GetChild(num); if (!((Object)(object)child == (Object)null)) { TextMeshPro[] componentsInChildren = ((Component)child).GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { ClearTmpVisual(componentsInChildren[i]); } Object.DestroyImmediate((Object)(object)((Component)child).gameObject); } } _hudRoot = null; _hudQty = null; _hudArrowUp = null; _hudArrowDown = null; _hudCost = null; _hudCurrencyIcon = null; } private IEnumerator RefreshAfterOpen() { yield return null; yield return null; if (_active && !_inShop) { RefreshNativeDisplay(); } } private static void CompleteInShopPurchase(ShopItemStats shopStats, int qty, Action? purchaseDone) { ShopMenuStock stock = ((Component)shopStats).GetComponentInParent(); PurchaseBatcher.ClearPendingQuantity(); PurchaseBatcher.ExpectingFsmPurchase = false; PurchaseBatcher.BuyShopItem(shopStats, qty, 0, delegate { ShopMenuStock obj = stock; if (obj != null) { obj.SetWasItemPurchased(true); } ShowPurchaseFeedback(shopStats); ScrubLeftoverQtyHud(); ForceHideConfirmChromeOnly(shopStats); purchaseDone?.Invoke(); EventRegister.SendEvent(EventRegisterEvents.ResetShopWindow, (GameObject)null); PurchaseBatcher.EndShopPurchaseBlock(); ShopConfirmListPatches.ClearPendingQtyOpen(); GameCameras instance = GameCameras.instance; if (instance != null) { instance.HUDIn(); } MerchantStackerPlugin.Log.LogInfo((object)"Bulk buy done → popup + ResetShopWindow"); }); } internal static void ShowPurchaseFeedback(ShopItemStats? stats) { try { object obj; if (stats == null) { obj = null; } else { ShopItem item = stats.Item; obj = ((item != null) ? item.Item : null); } CollectableItem val = (CollectableItem)((obj is CollectableItem) ? obj : null); if (val != null) { CollectableUIMsg.Spawn((ICollectableUIMsgItem)(object)val, (CollectableUIMsg)null, false); CollectableItemHeroReaction.DoReaction(); } } catch (Exception ex) { MerchantStackerPlugin.Log.LogWarning((object)("ShowPurchaseFeedback: " + ex.Message)); } } private void Finish(bool confirmed) { if (!_active) { return; } int num = Math.Max(1, _quantity); bool hijacked = _hijacked; bool inShop = _inShop; Action originalYes = _originalYes; Action originalNo = _originalNo; Action onConfirm = _onConfirm; Action onCancel = _onCancel; ShopItemStats shopStats = _shopStats; Action purchaseDone = _purchaseDone; Action shopCancel = _shopCancel; _active = false; _hijacked = false; _inShop = false; _shopRoot = null; _shopStats = null; _originalYes = null; _originalNo = null; _onConfirm = null; _onCancel = null; _purchaseDone = null; _shopCancel = null; _item = null; DestroyInShopHud(restoreTexts: true, !confirmed); ForceHideConfirmChromeOnly(shopStats); ClearPickerHudChildren(); ResetHoldState(); InventoryPaneInput.IsInputBlocked = false; ShopConfirmListPatches.ClearPendingQtyOpen(); if (confirmed) { PurchaseBatcher.PendingQuantity = num; MerchantStackerPlugin.Log.LogInfo((object)$"Qty confirmed: {num}"); } else { PurchaseBatcher.ClearPendingQuantity(); PurchaseBatcher.ExpectingFsmPurchase = false; PurchaseBatcher.EndShopPurchaseBlock(); } if (inShop) { if (confirmed && (Object)(object)shopStats != (Object)null) { CompleteInShopPurchase(shopStats, num, purchaseDone); return; } ScrubLeftoverQtyHud(); ClearPickerHudChildren(); ForceHideConfirmChromeOnly(shopStats); ShopSelectionCache.Clear(); ShopConfirmListPatches.BeginUiConfirmGrace(); shopCancel?.Invoke(); ScrubLeftoverQtyHud(); ClearPickerHudChildren(); MerchantStackerPlugin.Log.LogInfo((object)"Qty cancelled → ResetShopWindow"); } else if (hijacked) { object? value = InstanceField.GetValue(null); YesNoBox val = (YesNoBox)((value is YesNoBox) ? value : null); if ((Object)(object)val != (Object)null) { CurrentYesField.SetValue(val, originalYes); CurrentNoField.SetValue(val, originalNo); SelectedStateField.SetValue(val, confirmed); DoEndMethod.Invoke(val, null); } else if (confirmed) { originalYes?.Invoke(); } else { originalNo?.Invoke(); } } else { ClearBoxHandlersAndClose(); if (confirmed) { onConfirm?.Invoke(num); } else { onCancel?.Invoke(); } } } private static void ClearBoxHandlersAndClose() { object? value = InstanceField.GetValue(null); DialogueYesNoBox val = (DialogueYesNoBox)((value is DialogueYesNoBox) ? value : null); if ((Object)(object)val != (Object)null) { CurrentYesField.SetValue(val, null); CurrentNoField.SetValue(val, null); } try { DialogueYesNoBox.ForceClose(); } catch { } } private void ResetHoldState() { _upTimer = 0f; _downTimer = 0f; _upHeld = false; _downHeld = false; _upInterval = MerchantStackerPlugin.HoldInitialDelay.Value; _downInterval = MerchantStackerPlugin.HoldInitialDelay.Value; _heldStep = MerchantStackerPlugin.DpadStep.Value; } private void Update() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: 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_0104: Invalid comparison between Unknown and I4 //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Invalid comparison between Unknown and I4 ShopConfirmListPatches.PumpBlankConfirmWatchdog(); if (!_active) { return; } _maxRecalcTimer -= Time.unscaledDeltaTime; if (_maxRecalcTimer <= 0f) { _maxRecalcTimer = 0.25f; int affordableCount = Eligibility.GetAffordableCount(_unitCost, _currency); int val = (((Object)(object)_item != (Object)null) ? Eligibility.GetRoomUntilCap(_item) : _max); int num = Math.Max(1, Math.Min(Math.Max(1, affordableCount), Math.Max(1, val))); if (num != _max) { _max = num; if (_quantity > _max) { _quantity = _max; } if (_inShop) { _lastHudQty = int.MinValue; RefreshInShopHud(); } else { RefreshDisplay(); } } } InputHandler instance = ManagerSingleton.Instance; if ((Object)(object)instance == (Object)null) { return; } HeroActions inputActions = instance.inputActions; MenuActions menuAction = Platform.Current.GetMenuAction(inputActions, false, false); if ((int)menuAction != 1) { if ((int)menuAction == 2) { Finish(confirmed: false); return; } int value = MerchantStackerPlugin.DpadStep.Value; int value2 = MerchantStackerPlugin.StickStep.Value; bool num2 = ((OneAxisInputControl)inputActions.Up).WasPressed || ((OneAxisInputControl)inputActions.RsUp).WasPressed; bool flag = ((OneAxisInputControl)inputActions.Down).WasPressed || ((OneAxisInputControl)inputActions.RsDown).WasPressed; bool upHeld = ((OneAxisInputControl)inputActions.Up).IsPressed || ((OneAxisInputControl)inputActions.RsUp).IsPressed; bool downHeld = ((OneAxisInputControl)inputActions.Down).IsPressed || ((OneAxisInputControl)inputActions.RsDown).IsPressed; int num3 = ((((OneAxisInputControl)inputActions.RsUp).WasPressed || ((OneAxisInputControl)inputActions.RsDown).WasPressed) ? value2 : value); int step = ((((OneAxisInputControl)inputActions.RsUp).IsPressed || ((OneAxisInputControl)inputActions.RsDown).IsPressed) ? value2 : value); if (num2) { Adjust(num3); BeginHold(up: true, step); } else if (flag) { Adjust(-num3); BeginHold(up: false, step); } else { TickHold(upHeld, downHeld, step); } } else { Finish(confirmed: true); } } private void BeginHold(bool up, int step) { _heldStep = step; float value = MerchantStackerPlugin.HoldInitialDelay.Value; if (up) { _upHeld = true; _downHeld = false; _upTimer = value; _upInterval = value; } else { _downHeld = true; _upHeld = false; _downTimer = value; _downInterval = value; } } private void TickHold(bool upHeld, bool downHeld, int step) { _heldStep = step; float value = MerchantStackerPlugin.HoldMinRepeat.Value; float value2 = MerchantStackerPlugin.HoldAccel.Value; float unscaledDeltaTime = Time.unscaledDeltaTime; if (upHeld && _upHeld) { _upTimer -= unscaledDeltaTime; if (_upTimer <= 0f) { Adjust(_heldStep); _upInterval = Math.Max(value, _upInterval * value2); _upTimer = _upInterval; } } else { _upHeld = false; _upInterval = MerchantStackerPlugin.HoldInitialDelay.Value; } if (downHeld && _downHeld) { _downTimer -= unscaledDeltaTime; if (_downTimer <= 0f) { Adjust(-_heldStep); _downInterval = Math.Max(value, _downInterval * value2); _downTimer = _downInterval; } } else { _downHeld = false; _downInterval = MerchantStackerPlugin.HoldInitialDelay.Value; } } private void Adjust(int delta) { int num = Math.Clamp(_quantity + delta, _min, _max); if (num != _quantity) { _quantity = num; PurchaseBatcher.PendingQuantity = _quantity; RefreshDisplay(); MerchantStackerPlugin.Log.LogInfo((object)$"Qty -> {_quantity}"); } } private void RefreshDisplay() { if (_inShop) { RefreshInShopHud(); } else { RefreshNativeDisplay(); } } private void RefreshNativeDisplay() { object? value = InstanceField.GetValue(null); DialogueYesNoBox val = (DialogueYesNoBox)((value is DialogueYesNoBox) ? value : null); if ((Object)(object)val == (Object)null) { return; } int num = _unitCost * _quantity; RequiredCurrencyAmountField.SetValue(val, num); object? value2 = CurrencyTextField.GetValue(val); TMP_Text val2 = (TMP_Text)((value2 is TMP_Text) ? value2 : null); if (val2 != null) { val2.text = num.ToString(CultureInfo.InvariantCulture); } if ((Object)(object)_item == (Object)null) { return; } WillGetItemField.SetValue(val, _item); EnsureWillGetDisplay(val, _item); if (!(InstantiatedItemsField.GetValue(val) is List list)) { return; } foreach (SavedItemDisplay item in list) { if (!((Object)(object)item == (Object)null) && ((Component)item).gameObject.activeSelf) { item.Setup((SavedItem)(object)_item, _quantity); ForceAmountText(item, _quantity); break; } } } private static void EnsureWillGetDisplay(DialogueYesNoBox box, CollectableItem? item) { if ((Object)(object)item == (Object)null) { return; } try { if (!(InstantiatedItemsField.GetValue(box) is List list)) { return; } object? value = ItemTemplateField.GetValue(box); SavedItemDisplay val = (SavedItemDisplay)((value is SavedItemDisplay) ? value : null); if (val != null) { while (list.Count < 1) { list.Add(Object.Instantiate(val, ((Component)val).transform.parent)); } ((Component)list[0]).gameObject.SetActive(true); list[0].Setup((SavedItem)(object)item, 1); object? value2 = ItemsLayoutField.GetValue(box); Behaviour val2 = (Behaviour)((value2 is Behaviour) ? value2 : null); if (val2 != null) { ((Component)val2).gameObject.SetActive(true); } } } catch { } } private static void ForceAmountText(SavedItemDisplay display, int qty) { try { object? obj = AccessTools.Field(typeof(SavedItemDisplay), "amountText")?.GetValue(display); TMP_Text val = (TMP_Text)((obj is TMP_Text) ? obj : null); if (val != null) { ((Component)val).gameObject.SetActive(true); val.text = qty.ToString(CultureInfo.InvariantCulture); } } catch { } } } internal static class ShopSelectionCache { private static readonly string[] ItemObjectVarNames = new string[3] { "Shop Item", "Item", "Selected Item" }; private static readonly string[] ItemIndexVarNames = new string[3] { "Current Item", "Item Number", "Selected Index" }; internal static ShopItemStats? Stats { get; private set; } internal static ShopItem? Item { get { if (!((Object)(object)Stats != (Object)null)) { return null; } return Stats.Item; } } internal static void Remember(ShopItemStats? stats) { if (!((Object)(object)stats == (Object)null) && !((Object)(object)stats.Item == (Object)null)) { Stats = stats; } } internal static void Clear() { Stats = null; } internal static ShopItemStats? ResolveStats() { ShopItemStats? stats = Stats; if ((Object)(object)((stats != null) ? stats.Item : null) != (Object)null) { return Stats; } if (TryResolveFromFsm(out ShopItemStats stats2)) { Remember(stats2); return stats2; } InventoryItemManager[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (InventoryItemManager val in array) { if (!((Object)(object)((val != null) ? val.CurrentSelected : null) == (Object)null)) { ShopItemStats statsFromGameObject = GetStatsFromGameObject(((Component)val.CurrentSelected).gameObject); if ((Object)(object)((statsFromGameObject != null) ? statsFromGameObject.Item : null) != (Object)null) { Remember(statsFromGameObject); return statsFromGameObject; } } } return null; } private static bool TryResolveFromFsm(out ShopItemStats? stats) { stats = null; PlayMakerFSM[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (PlayMakerFSM val in array) { if ((Object)(object)val == (Object)null || !((Behaviour)val).isActiveAndEnabled || val.FsmVariables == null) { continue; } string[] itemObjectVarNames = ItemObjectVarNames; foreach (string text in itemObjectVarNames) { FsmGameObject val2 = val.FsmVariables.FindFsmGameObject(text); if (!((Object)(object)((val2 != null) ? val2.Value : null) == (Object)null)) { stats = GetStatsFromGameObject(val2.Value); ShopItemStats? obj = stats; if ((Object)(object)((obj != null) ? obj.Item : null) != (Object)null) { MerchantStackerPlugin.Log.LogDebug((object)("Shop item from FSM '" + text + "': " + stats.Item.DisplayName)); return true; } } } itemObjectVarNames = ItemIndexVarNames; foreach (string text2 in itemObjectVarNames) { FsmInt val3 = val.FsmVariables.FindFsmInt(text2); if (val3 == null) { continue; } ShopMenuStock val4 = ((Component)val).GetComponent() ?? ((Component)val).GetComponentInParent() ?? ((Component)val).GetComponentInChildren(true); if ((Object)(object)val4 == (Object)null) { continue; } int value = val3.Value; if (value >= 0 && value < val4.GetItemCount()) { stats = GetStatsFromGameObject(val4.GetItemGameObject(value)); ShopItemStats? obj2 = stats; if ((Object)(object)((obj2 != null) ? obj2.Item : null) != (Object)null) { MerchantStackerPlugin.Log.LogDebug((object)$"Shop item from FSM '{text2}'={value}: {stats.Item.DisplayName}"); return true; } } } } stats = null; return false; } internal static ShopItemStats? GetStatsFromGameObject(GameObject? go) { if ((Object)(object)go == (Object)null) { return null; } return go.GetComponent() ?? go.GetComponentInChildren(true) ?? go.GetComponentInParent(); } } [HarmonyPatch] internal static class ShopSelectionCachePatches { [HarmonyPostfix] [HarmonyPatch(typeof(InventoryItemSelectable), "Select")] private static void SelectPostfix(InventoryItemSelectable __instance) { ShopSelectionCache.Remember(ShopSelectionCache.GetStatsFromGameObject((__instance != null) ? ((Component)__instance).gameObject : null)); } [HarmonyPostfix] [HarmonyPatch(typeof(InventoryItemManager), "SetSelected", new Type[] { typeof(GameObject), typeof(bool) })] private static void SetSelectedGoPostfix(GameObject selectedGameObject) { ShopSelectionCache.Remember(ShopSelectionCache.GetStatsFromGameObject(selectedGameObject)); } [HarmonyPostfix] [HarmonyPatch(typeof(InventoryItemManager), "SetSelected", new Type[] { typeof(InventoryItemSelectable), typeof(SelectionDirection?), typeof(bool) })] private static void SetSelectedSelectablePostfix(InventoryItemSelectable selectable) { ShopSelectionCache.Remember(ShopSelectionCache.GetStatsFromGameObject((selectable != null) ? ((Component)selectable).gameObject : null)); } [HarmonyPrefix] [HarmonyPatch(typeof(SetShopItemPurchased), "OnEnter")] private static void CachePurchaseTargetV1(SetShopItemPurchased __instance) { ShopSelectionCache.Remember(ShopSelectionCache.GetStatsFromGameObject(FSMUtility.GetSafe(__instance.Target, (FsmStateAction)(object)__instance))); } [HarmonyPrefix] [HarmonyPatch(typeof(SetShopItemPurchasedV2), "OnEnter")] private static void CachePurchaseTargetV2(SetShopItemPurchasedV2 __instance) { ShopSelectionCache.Remember(ShopSelectionCache.GetStatsFromGameObject(FSMUtility.GetSafe(__instance.Target, (FsmStateAction)(object)__instance))); } [HarmonyPostfix] [HarmonyPatch(typeof(ShopMenuStock), "GetItemGameObject")] private static void GetItemGameObjectPostfix(ShopMenuStock __instance, int itemNum, GameObject __result) { ShopSelectionCache.Remember(ShopSelectionCache.GetStatsFromGameObject(__result)); } [HarmonyPostfix] [HarmonyPatch(typeof(ShopMenuStock), "GetCost")] private static void GetCostPostfix(ShopMenuStock __instance, int itemNum) { try { ShopSelectionCache.Remember(ShopSelectionCache.GetStatsFromGameObject(__instance.GetItemGameObject(itemNum))); } catch { } } } } namespace MerchantStacker.Patches { [HarmonyPatch] internal static class ConfirmDialogPatches { private static readonly FieldInfo CurrentYesField = AccessTools.Field(typeof(YesNoBox), "currentYes"); private static readonly FieldInfo CurrentNoField = AccessTools.Field(typeof(YesNoBox), "currentNo"); private static readonly FieldInfo RequiredCurrencyAmountField = AccessTools.Field(typeof(DialogueYesNoBox), "requiredCurrencyAmount"); private static readonly FieldInfo WillGetItemField = AccessTools.Field(typeof(DialogueYesNoBox), "willGetItem"); [HarmonyPostfix] [HarmonyPatch(typeof(YesNoBox), "InternalOpen")] private static void InternalOpenPostfix(YesNoBox __instance) { //IL_0117: Unknown result type (might be due to invalid IL or missing references) try { if (!MerchantStackerPlugin.Enabled.Value || PurchaseBatcher.IsBatching || QuantityPicker.SuppressHijack || (Object)(object)QuantityPicker.Instance == (Object)null || QuantityPicker.Instance.IsOpen) { return; } DialogueYesNoBox val = (DialogueYesNoBox)(object)((__instance is DialogueYesNoBox) ? __instance : null); if (val == null || IsMerchantShopOpen()) { return; } int valueOrDefault = (RequiredCurrencyAmountField?.GetValue(val) as int?).GetValueOrDefault(); if (valueOrDefault <= 0) { return; } ShopItemStats val2 = ShopSelectionCache.ResolveStats(); if (!((Object)(object)((val2 != null) ? val2.Item : null) == (Object)null) && Eligibility.ShouldOfferBulkQty(val2.Item)) { object? obj = WillGetItemField?.GetValue(val); CollectableItem val3 = (CollectableItem)(((obj is CollectableItem) ? obj : null) ?? ((object)/*isinst with value type is only supported in some contexts*/)); if (!((Object)(object)val3 == (Object)null)) { QuantityPicker.Instance.Hijack(val, val2.Item.DisplayName, val3, (val2.Item.Cost > 0) ? val2.Item.Cost : valueOrDefault, val2.Item.CurrencyType, Eligibility.GetMaxQuantity(val2.Item), CurrentYesField?.GetValue(val) as Action, CurrentNoField?.GetValue(val) as Action); } } } catch (Exception arg) { MerchantStackerPlugin.Log.LogError((object)$"ConfirmDialogPatches: {arg}"); } } private static bool IsMerchantShopOpen() { try { ShopMenuStock[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (ShopMenuStock val in array) { if ((Object)(object)val != (Object)null && ((Behaviour)val).isActiveAndEnabled) { return true; } } } catch { } return false; } } [HarmonyPatch(typeof(InventoryPaneInput), "Update")] internal static class InventoryPaneInputPatches { [HarmonyPrefix] private static bool Prefix() { if (!((Object)(object)QuantityPicker.Instance == (Object)null)) { return !QuantityPicker.Instance.IsOpen; } return true; } } [HarmonyPatch] internal static class MachinePurchasePatches { private static int _pendingCollectQty; private static bool _skipNextCollect; private static bool _waitingForConfirm; private static CollectableItem? _pendingItem; private static TakeCurrency? _heldTakeAction; private static int _heldUnitCost; private static CurrencyType _heldCurrency; [HarmonyPrefix] [HarmonyPatch(typeof(TakeCurrency), "OnEnter")] private static bool TakeCurrencyPrefix(TakeCurrency __instance) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011b: 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) if (!MerchantStackerPlugin.Enabled.Value || PurchaseBatcher.IsBatching || _waitingForConfirm) { return true; } if (((NamedVariable)__instance.CurrencyType).IsNone || __instance.Amount.Value <= 0) { return true; } int num = PurchaseBatcher.ConsumePendingQuantity(); if (num > 1 && IsRosaryStringMachine((FsmStateAction)(object)__instance)) { CurrencyType val = (CurrencyType)(object)__instance.CurrencyType.Value; CurrencyManager.TakeCurrency(__instance.Amount.Value * num, val, true); _pendingCollectQty = num; _skipNextCollect = false; ((FsmStateAction)__instance).Finish(); return false; } if (num == 1) { return true; } if (!IsRosaryStringMachine((FsmStateAction)(object)__instance)) { return true; } CurrencyType val2 = (CurrencyType)(object)__instance.CurrencyType.Value; int value = __instance.Amount.Value; CollectableItem val3 = FindMachineCollectable((FsmStateAction)(object)__instance); int val4 = (((Object)(object)val3 != (Object)null) ? Eligibility.GetRoomUntilCap(val3) : 20); int affordableCount = Eligibility.GetAffordableCount(value, val2); int num2 = Math.Max(1, Math.Min(val4, affordableCount)); if (num2 <= 1 || (Object)(object)val3 == (Object)null) { return true; } if ((Object)(object)QuantityPicker.Instance == (Object)null) { return true; } _waitingForConfirm = true; _heldTakeAction = __instance; _heldUnitCost = value; _heldCurrency = val2; _pendingItem = val3; QuantityPicker.Instance.Open(((SavedItem)val3).GetPopupName(), val3, value, val2, num2, delegate(int qty) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) _waitingForConfirm = false; TakeCurrency heldTakeAction = _heldTakeAction; _heldTakeAction = null; if (heldTakeAction != null) { CurrencyManager.TakeCurrency(_heldUnitCost * qty, _heldCurrency, true); _pendingCollectQty = qty; _skipNextCollect = false; ((FsmStateAction)heldTakeAction).Finish(); } }, delegate { _waitingForConfirm = false; PurchaseBatcher.ClearPendingQuantity(); _skipNextCollect = true; _pendingCollectQty = 0; TakeCurrency? heldTakeAction = _heldTakeAction; _heldTakeAction = null; if (heldTakeAction != null) { ((FsmStateAction)heldTakeAction).Finish(); } }); return false; } [HarmonyPrefix] [HarmonyPatch(typeof(CollectableItemAction), "OnEnter")] private static bool CollectOnEnterPrefix(CollectableItemAction __instance) { CollectableItemCollect val = (CollectableItemCollect)(object)((__instance is CollectableItemCollect) ? __instance : null); if (val == null) { return true; } if (_skipNextCollect) { _skipNextCollect = false; _pendingCollectQty = 0; ((FsmStateAction)val).Finish(); return false; } if (_pendingCollectQty > 0) { int pendingCollectQty = _pendingCollectQty; _pendingCollectQty = 0; Object value = ((CollectableItemAction)val).Item.Value; CollectableItem val2 = (CollectableItem)(object)((value is CollectableItem) ? value : null); if ((Object)(object)val2 != (Object)null) { val2.Collect(pendingCollectQty, true); MerchantStackerPlugin.Log.LogInfo((object)$"Machine collected {pendingCollectQty}x {((Object)val2).name}"); } ((FsmStateAction)val).Finish(); return false; } return true; } private static bool IsRosaryStringMachine(FsmStateAction action) { GameObject owner = action.Owner; if ((Object)(object)owner == (Object)null) { return false; } Transform val = owner.transform; while ((Object)(object)val != (Object)null) { string name = ((Object)val).name; if (name.Contains("rosary_string_machine", StringComparison.OrdinalIgnoreCase) || name.Contains("rosary_stringer", StringComparison.OrdinalIgnoreCase)) { return true; } val = val.parent; } return false; } private static CollectableItem? FindMachineCollectable(FsmStateAction action) { if ((Object)(object)_pendingItem != (Object)null) { return _pendingItem; } try { Fsm fsm = action.Fsm; if (fsm == null) { return null; } FsmObject[] objectVariables = fsm.Variables.ObjectVariables; for (int i = 0; i < objectVariables.Length; i++) { Object value = objectVariables[i].Value; CollectableItem val = (CollectableItem)(object)((value is CollectableItem) ? value : null); if (val != null) { return val; } } FsmState[] states = fsm.States; for (int i = 0; i < states.Length; i++) { FsmStateAction[] actions = states[i].Actions; foreach (FsmStateAction obj in actions) { CollectableItemAction val2 = (CollectableItemAction)(object)((obj is CollectableItemAction) ? obj : null); if (val2 != null) { CollectableItem val3 = TryGetCollectableFromAction(val2); if ((Object)(object)val3 != (Object)null) { return val3; } } } } } catch (Exception ex) { MerchantStackerPlugin.Log.LogDebug((object)("FindMachineCollectable: " + ex.Message)); } return CollectableItemManager.GetItemByName("Rosary_Set_Small"); } private static CollectableItem? TryGetCollectableFromAction(CollectableItemAction action) { FieldInfo[] fields = ((object)action).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (typeof(CollectableItem).IsAssignableFrom(fieldInfo.FieldType)) { object? value = fieldInfo.GetValue(action); return (CollectableItem?)((value is CollectableItem) ? value : null); } if (fieldInfo.FieldType == typeof(FsmObject)) { object? value2 = fieldInfo.GetValue(action); object? obj = ((value2 is FsmObject) ? value2 : null); Object obj2 = ((obj != null) ? ((FsmObject)obj).Value : null); CollectableItem val = (CollectableItem)(object)((obj2 is CollectableItem) ? obj2 : null); if (val != null) { return val; } } } return null; } } [HarmonyPatch] internal static class ShopConfirmShowPatches { internal static bool LockConfirmChrome; internal static bool BuildingQtyHud; [HarmonyPrefix] [HarmonyPatch(typeof(ActivateGameObject), "OnEnter")] private static bool ActivateGameObjectPrefix(ActivateGameObject __instance) { if (!LockConfirmChrome || BuildingQtyHud || __instance.activate == null || !__instance.activate.Value) { return true; } try { Fsm fsm = ((FsmStateAction)__instance).Fsm; GameObject val = ((fsm != null) ? fsm.GetOwnerDefaultTarget(__instance.gameObject) : null); if ((Object)(object)val != (Object)null && IsConfirmChromeName(((Object)val).name)) { return false; } } catch { } return true; } private static bool IsConfirmChromeName(string n) { switch (n) { default: return n == "Geo Sprite"; case "UI List": case "Yes": case "No": case "Confirm msg": case "Costs": case "Thankyou": case "Money": case "Item cost": return true; } } [HarmonyPostfix] [HarmonyPatch(typeof(ActivateGameObject), "OnEnter")] private static void ActivateGameObjectPostfix(ActivateGameObject __instance) { try { if (BuildingQtyHud || LockConfirmChrome || __instance.activate == null || !__instance.activate.Value || ((FsmStateAction)__instance).Fsm == null) { return; } GameObject ownerDefaultTarget = ((FsmStateAction)__instance).Fsm.GetOwnerDefaultTarget(__instance.gameObject); if ((Object)(object)ownerDefaultTarget == (Object)null) { return; } GameObject val = ShopConfirmListPatches.FindConfirmGroupObjectPublic(ownerDefaultTarget); if (!((Object)(object)val == (Object)null) && ShopConfirmListPatches.IsStackableMerchantConfirmPublic(val) && (!((Object)(object)QuantityPicker.Instance != (Object)null) || !QuantityPicker.Instance.IsOpen)) { if (ShopConfirmListPatches.TryOpenQtyForConfirmGroupPublic(val, "ActivateGameObject") && (Object)(object)QuantityPicker.Instance != (Object)null && QuantityPicker.Instance.IsOpen) { ShopConfirmListPatches.SuppressConfirmChromePublic(val.transform); } else { ShopConfirmListPatches.RestoreConfirmChromePublic(); } } } catch (Exception ex) { MerchantStackerPlugin.Log.LogWarning((object)("ActivateGameObject hook: " + ex.Message)); } } [HarmonyPostfix] [HarmonyPatch(typeof(CallMethodProper), "OnEnter")] private static void CallMethodProperPostfix(CallMethodProper __instance) { try { if (__instance.methodName == null || ((FsmStateAction)__instance).Fsm == null || BuildingQtyHud || LockConfirmChrome || !string.Equals(__instance.methodName.Value, "SetActive", StringComparison.Ordinal)) { return; } GameObject ownerDefaultTarget = ((FsmStateAction)__instance).Fsm.GetOwnerDefaultTarget(__instance.gameObject); if (!((Object)(object)ownerDefaultTarget == (Object)null)) { GameObject val = ShopConfirmListPatches.FindConfirmGroupObjectPublic(ownerDefaultTarget); if (!((Object)(object)val == (Object)null) && ShopConfirmListPatches.IsStackableMerchantConfirmPublic(val)) { ShopConfirmListPatches.TryOpenQtyForConfirmGroupPublic(val, "CallMethodProper.SetActive"); } } } catch (Exception ex) { MerchantStackerPlugin.Log.LogWarning((object)("CallMethodProper hook: " + ex.Message)); } } } [HarmonyPatch] internal static class ShopConfirmListPatches { private static bool _pendingQtyOpen; private static ShopItemStats? _armedBulkStats; private static readonly HashSet HiddenChrome = new HashSet(); private static int _blankConfirmFrames; private const int BlankConfirmRestoreFrames = 20; private static float _ignoreUiConfirmUntil; private static Coroutine? _confirmWaitCo; internal static void PumpBlankConfirmWatchdog() { if (_pendingQtyOpen || ((Object)(object)QuantityPicker.Instance != (Object)null && QuantityPicker.Instance.IsOpen) || PurchaseBatcher.IsBatching || PurchaseBatcher.BlockShopPurchases || PurchaseBatcher.ExpectingFsmPurchase || HiddenChrome.Count == 0) { _blankConfirmFrames = 0; } else if (++_blankConfirmFrames >= 20) { _blankConfirmFrames = 0; MerchantStackerPlugin.Log.LogWarning((object)"Blank confirm watchdog → restoring vanilla Yes/No (qty never opened)"); RestoreConfirmChrome(); ForceEnableConfirmYesNo(); ClearPendingQtyOpen(); } } internal static void BeginUiConfirmGrace(float seconds = 0.4f) { _ignoreUiConfirmUntil = Time.unscaledTime + Math.Max(0.05f, seconds); StopConfirmWaitCoroutine(); _pendingQtyOpen = false; _armedBulkStats = null; } private static void ForceEnableConfirmYesNo() { ShopMenuStock[] array = Object.FindObjectsByType((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (ShopMenuStock val in array) { if ((Object)(object)val == (Object)null || !Eligibility.StockHasStackableOffers(val)) { continue; } Transform[] componentsInChildren = ((Component)((Component)val).transform.root).GetComponentsInChildren(true); foreach (Transform val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null) && !(((Object)val2).name != "Item Confirm Group")) { ForceEnableConfirmChildren(val2); } } } } private static void ForceEnableConfirmChildren(Transform confirmGroup) { Transform[] componentsInChildren = ((Component)confirmGroup).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { string name = ((Object)val).name; if (((name == "UI List" && (Object)(object)val.parent != (Object)null && ((Object)val.parent).name == "Confirm") || !(name != "Yes") || !(name != "No") || !(name != "Confirm msg") || !(name != "Costs")) && ((!(name == "Yes") && !(name == "No")) || IsUnderShopConfirmTransform(val)) && !((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(true); } } } } private static void StopConfirmWaitCoroutine() { if (_confirmWaitCo != null && (Object)(object)QuantityPicker.Instance != (Object)null) { try { ((MonoBehaviour)QuantityPicker.Instance).StopCoroutine(_confirmWaitCo); } catch { } } _confirmWaitCo = null; } internal static void ClearPendingQtyOpen() { StopConfirmWaitCoroutine(); _pendingQtyOpen = false; _armedBulkStats = null; } internal static bool CanInterceptConfirmInputPublic() { return CanInterceptConfirmInput(); } internal static GameObject? FindConfirmGroupObjectPublic(GameObject go) { return FindConfirmGroupObject(go); } internal static bool TryOpenQtyForConfirmGroupPublic(GameObject confirmGroup, string reason) { return TryOpenQtyForConfirmGroup(confirmGroup, reason); } internal static void SuppressConfirmChromePublic(Transform root) { PreHideConfirmChrome(root); } internal static void RestoreConfirmChromePublic() { RestoreConfirmChrome(); } internal static void ForceEnableConfirmYesNoPublic() { ForceEnableConfirmYesNo(); } internal static bool IsUnderShopConfirmPublic(Transform? t) { return IsUnderShopConfirmTransform(t); } internal static bool IsStackableMerchantConfirmPublic(GameObject confirmGroup) { return Eligibility.StockHasStackableOffers(confirmGroup.GetComponentInParent(true)); } [HarmonyPrefix] [HarmonyPatch(typeof(FSMUtility), "SendEventToGameObject", new Type[] { typeof(GameObject), typeof(string), typeof(bool) })] private static bool SendEventToGameObjectPrefix(GameObject go, string eventName) { if (!IsShopConfirmRelevantEvent(eventName)) { return true; } return HandleConfirmEvent(go, eventName); } [HarmonyPrefix] [HarmonyPatch(typeof(PlayMakerFSM), "SendEvent", new Type[] { typeof(string) })] private static void PlayMakerSendEventPrefix(PlayMakerFSM __instance, string eventName) { if (IsShopConfirmRelevantEvent(eventName)) { HandleConfirmEvent(((Object)(object)__instance != (Object)null) ? ((Component)__instance).gameObject : null, eventName); } } [HarmonyPrefix] [HarmonyPatch(typeof(Fsm), "ProcessEvent", new Type[] { typeof(FsmEvent), typeof(FsmEventData) })] private static bool FsmProcessEventPrefix(Fsm __instance, FsmEvent fsmEvent) { if (fsmEvent == null || string.IsNullOrEmpty(fsmEvent.Name)) { return true; } string name = fsmEvent.Name; bool flag = (Object)(object)QuantityPicker.Instance != (Object)null && QuantityPicker.Instance.IsOpen; if (flag) { if (!IsConfirmNavOrSubmitEvent(name)) { return true; } } else if (name != "UI CONFIRM" && name != "TO CONFIRM" && name != "UI SELECTION MADE") { return true; } GameObject val = null; try { val = __instance.GameObject; } catch { return true; } if ((Object)(object)val == (Object)null || !IsShopFsmOwner(val)) { return true; } if (flag && IsConfirmNavOrSubmitEvent(name)) { return false; } return HandleConfirmEvent(val, name); } private static bool IsShopConfirmRelevantEvent(string? eventName) { if (string.IsNullOrEmpty(eventName)) { return false; } switch (eventName) { default: return eventName == "RESET SHOP"; case "UI CONFIRM": case "TO CONFIRM": case "UI SELECTION MADE": case "RESET SHOP WINDOW": return true; } } private static bool IsConfirmNavOrSubmitEvent(string eventName) { switch (eventName) { default: return eventName == "UI MOVE HORIZONTAL"; case "UI CONFIRM": case "TO CONFIRM": case "UI SELECTION MADE": case "UI CANCEL": case "CANCEL": case "UI UP": case "UI DOWN": case "UI LEFT": case "UI RIGHT": case "UP": case "DOWN": case "LEFT": case "RIGHT": case "UI MOVE VERTICAL": return true; } } [HarmonyPrefix] [HarmonyPatch(typeof(UISelectionList), "Update")] private static bool ConfirmListUpdatePrefix(UISelectionList __instance) { if ((Object)(object)QuantityPicker.Instance == (Object)null || !QuantityPicker.Instance.IsOpen) { return true; } Transform parent = ((Component)__instance).transform.parent; if ((Object)(object)parent == (Object)null || ((Object)parent).name != "Confirm") { return true; } return false; } [HarmonyPrefix] [HarmonyPatch(typeof(UISelectionListItem), "Submit")] private static bool ConfirmYesSubmitPrefix(UISelectionListItem __instance) { if ((Object)(object)QuantityPicker.Instance != (Object)null && QuantityPicker.Instance.IsOpen && IsShopConfirmYes(__instance)) { return false; } if (!CanInterceptConfirmInput()) { return true; } if (!IsShopConfirmYes(__instance)) { return true; } ShopItemStats val = ResolveBulkStatsNear(null) ?? ResolveBulkStatsNear(((Component)__instance).gameObject); if ((Object)(object)val == (Object)null || (Object)(object)val.Item == (Object)null) { return true; } if (QuantityPicker.Instance.IsOpen) { return false; } MerchantStackerPlugin.Log.LogInfo((object)("Confirm Yes Submit → qty: " + val.Item.DisplayName)); OpenQtyForStats(val, "YesSubmit"); return false; } [HarmonyPostfix] [HarmonyPatch(typeof(UISelectionList), "SetActive")] private static void ConfirmListSetActivePostfix(UISelectionList __instance, bool value) { if (!value || !CanInterceptConfirmInput() || QuantityPicker.Instance.IsOpen || !IsUnderShopConfirmList(__instance)) { return; } ShopMenuStock val = ((Component)__instance).GetComponentInParent(true); if ((Object)(object)val != (Object)null && !IsLiveOpenShopStock(val)) { return; } if (val == null) { val = FindActiveShopStock(); } ShopItemStats val2 = ResolveStatsFromStock(val) ?? ResolveBulkStatsNear(((Component)__instance).gameObject); if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.Item == (Object)null) && Eligibility.ShouldOfferBulkQty(val2.Item)) { MerchantStackerPlugin.Log.LogInfo((object)("Confirm UI List SetActive → open qty: " + val2.Item.DisplayName)); if (OpenQtyForStats(val2, "SetActive(UI List)", val) && (Object)(object)QuantityPicker.Instance != (Object)null && QuantityPicker.Instance.IsOpen) { ((Component)__instance).gameObject.SetActive(false); PreHideConfirmChrome(((Component)__instance).transform.root); } } } [HarmonyPostfix] [HarmonyPatch(typeof(ShopMenuStock), "BuildItemList")] private static void BuildItemListPostfix(ShopMenuStock __instance) { if (Eligibility.StockHasStackableOffers(__instance) && IsLiveOpenShopStock(__instance)) { PurchaseBatcher.EndShopPurchaseBlock(); ClearPendingQtyOpen(); RestoreConfirmChrome(); MerchantStackerPlugin.Log.LogInfo((object)("Shop stock built (stackable): '" + ((Object)__instance).name + "' items=" + DescribeStock(__instance))); } } private static string DescribeStock(ShopMenuStock stock) { List list = new List(); int itemCount = stock.GetItemCount(); for (int i = 0; i < itemCount; i++) { list.Add(stock.GetName(i)); } return string.Join(", ", list); } private static bool HandleConfirmEvent(GameObject? go, string? eventName) { if (string.IsNullOrEmpty(eventName)) { return true; } if (eventName == "RESET SHOP WINDOW" || eventName == "RESET SHOP") { PurchaseBatcher.EndShopPurchaseBlock(); ClearPendingQtyOpen(); ShopSelectionCache.Clear(); } if (!CanInterceptConfirmInput()) { if ((Object)(object)QuantityPicker.Instance != (Object)null && QuantityPicker.Instance.IsOpen) { switch (eventName) { case "UI CONFIRM": case "TO CONFIRM": case "UI SELECTION MADE": return false; } } return true; } ShopItemStats val = ResolveBulkStatsNear(go) ?? ResolveBulkStatsNear(null); if (eventName == "UI SELECTION MADE") { if ((Object)(object)QuantityPicker.Instance != (Object)null && QuantityPicker.Instance.IsOpen) { return false; } if ((Object)(object)val != (Object)null && (Object)(object)val.Item != (Object)null && Eligibility.ShouldOfferBulkQty(val.Item) && HasActiveConfirmGroup()) { MerchantStackerPlugin.Log.LogInfo((object)("UI SELECTION MADE → qty (block Yes): " + val.Item.DisplayName)); OpenQtyForStats(val, "UI SELECTION MADE"); return false; } return true; } if (eventName != "TO CONFIRM" && eventName != "UI CONFIRM") { return true; } if (eventName == "UI CONFIRM") { if (Time.unscaledTime < _ignoreUiConfirmUntil) { return true; } ShopItemStats val2 = ResolveCurrentShopStats(go); if ((Object)(object)((val2 != null) ? val2.Item : null) != (Object)null && Eligibility.ShouldOfferBulkQty(val2.Item)) { ShopSelectionCache.Remember(val2); _armedBulkStats = val2; MerchantStackerPlugin.Log.LogInfo((object)("Fsm 'UI CONFIRM' → armed qty for " + val2.Item.DisplayName)); ScheduleOpenWhenConfirmShows(val2); } else { _armedBulkStats = null; StopConfirmWaitCoroutine(); _pendingQtyOpen = false; } return true; } if (eventName == "TO CONFIRM" && (Object)(object)val != (Object)null && (Object)(object)val.Item != (Object)null && Eligibility.ShouldOfferBulkQty(val.Item)) { _armedBulkStats = val; MerchantStackerPlugin.Log.LogInfo((object)("Fsm '" + eventName + "' → arm qty for " + val.Item.DisplayName)); OpenQtyForStats(val, eventName); } return true; } private static bool TryOpenQtyForConfirmGroup(GameObject confirmGroup, string reason) { if (!CanInterceptConfirmInput() || (Object)(object)QuantityPicker.Instance == (Object)null || QuantityPicker.Instance.IsOpen) { if ((Object)(object)QuantityPicker.Instance != (Object)null) { return QuantityPicker.Instance.IsOpen; } return false; } ShopMenuStock val = confirmGroup.GetComponentInParent(true); if ((Object)(object)val != (Object)null && !IsLiveOpenShopStock(val)) { return false; } if (val == null) { val = FindActiveShopStock(); } ShopItemStats val2 = ResolveCurrentShopStats(confirmGroup) ?? ResolveStatsFromStock(val) ?? _armedBulkStats; if ((Object)(object)((val2 != null) ? val2.Item : null) != (Object)null) { ShopSelectionCache.Remember(val2); } if ((Object)(object)val2 == (Object)null || (Object)(object)val2.Item == (Object)null) { if (!_pendingQtyOpen && (Object)(object)_armedBulkStats != (Object)null && (Object)(object)QuantityPicker.Instance != (Object)null) { _pendingQtyOpen = true; ((MonoBehaviour)QuantityPicker.Instance).StartCoroutine(RetryOpenFromConfirmGroup(confirmGroup, val)); } return false; } if (!Eligibility.ShouldOfferBulkQty(val2.Item)) { _pendingQtyOpen = false; _armedBulkStats = null; return false; } MerchantStackerPlugin.Log.LogInfo((object)(reason + " → open qty: " + val2.Item.DisplayName)); bool num = OpenQtyForStats(val2, reason, val); if (num) { _armedBulkStats = null; } return num; } private static GameObject? FindConfirmGroupObject(GameObject go) { if ((Object)(object)go == (Object)null) { return null; } if (((Object)go).name == "Item Confirm Group") { return go; } Transform[] componentsInChildren = go.GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if ((Object)(object)val != (Object)null && ((Object)val).name == "Item Confirm Group") { return ((Component)val).gameObject; } } Transform parent = go.transform.parent; int num = 0; while ((Object)(object)parent != (Object)null && num++ < 12) { if (((Object)parent).name == "Item Confirm Group") { return ((Component)parent).gameObject; } parent = parent.parent; } return null; } private static bool IsShopFsmOwner(GameObject ownerGo) { if ((Object)(object)ownerGo == (Object)null) { return false; } if ((Object)(object)ownerGo.GetComponentInParent(true) != (Object)null) { return true; } return ((Object)ownerGo).name.IndexOf("Shop", StringComparison.OrdinalIgnoreCase) >= 0; } internal static bool TryOpenQtyImmediate(ShopItemStats stats) { return OpenQtyForStats(stats, "immediate"); } internal static bool TryOpenQtyFromVisibleConfirm(GameObject? eventSource, string reason) { ShopItemStats val = ResolveBulkStatsNear(eventSource) ?? ResolveBulkStatsNear(null); if ((Object)(object)((val != null) ? val.Item : null) != (Object)null) { return OpenQtyForStats(val, reason); } return false; } private static bool CanInterceptConfirmInput() { if (MerchantStackerPlugin.Enabled.Value && !PurchaseBatcher.IsBatching && !PurchaseBatcher.BlockShopPurchases && !PurchaseBatcher.ExpectingFsmPurchase && (Object)(object)QuantityPicker.Instance != (Object)null) { return !QuantityPicker.Instance.IsOpen; } return false; } private static bool OpenQtyForStats(ShopItemStats? stats, string reason, ShopMenuStock? stockHint = null) { if ((Object)(object)QuantityPicker.Instance == (Object)null || QuantityPicker.Instance.IsOpen) { return false; } if ((Object)(object)stats == (Object)null) { return false; } ShopItem item = stats.Item; if ((Object)(object)item == (Object)null || !Eligibility.ShouldOfferBulkQty(item)) { return false; } ShopMenuStock val = stockHint; if ((Object)(object)val == (Object)null) { try { val = ((Component)stats).GetComponentInParent(true); } catch { val = null; } } if ((Object)(object)val != (Object)null && !IsLiveOpenShopStock(val)) { val = null; } if (val == null) { val = FindActiveShopStock(); } if ((Object)(object)val == (Object)null || !IsLiveOpenShopStock(val)) { MerchantStackerPlugin.Log.LogWarning((object)("Confirm qty: no live ShopMenuStock (via '" + reason + "') for " + item.DisplayName)); return false; } if (!reason.StartsWith("SetActive", StringComparison.Ordinal) && !reason.StartsWith("Activate", StringComparison.Ordinal) && !reason.StartsWith("CallMethod", StringComparison.Ordinal)) { switch (reason) { default: if (reason.IndexOf("retry", StringComparison.OrdinalIgnoreCase) < 0) { return false; } break; case "TO CONFIRM": case "UI SELECTION MADE": case "YesSubmit": case "immediate": case "ConfirmWait": break; } } ShopSelectionCache.Remember(stats); _pendingQtyOpen = false; OpenQtyReplacingConfirm(val, stats); if (QuantityPicker.Instance.IsOpen) { PreHideConfirmChrome(((Component)val).transform.root); MerchantStackerPlugin.Log.LogInfo((object)("Confirm → qty replace: " + item.DisplayName + " (via '" + reason + "')")); return true; } MerchantStackerPlugin.Log.LogWarning((object)("Confirm qty: OpenInShop failed for " + item.DisplayName + " (via '" + reason + "') — left vanilla confirm")); return false; } private static void ScheduleOpenWhenConfirmShows(ShopItemStats stats) { if (!((Object)(object)QuantityPicker.Instance == (Object)null) && !((Object)(object)((stats != null) ? stats.Item : null) == (Object)null)) { ShopMenuStock val = null; try { val = ((Component)stats).GetComponentInParent(true); } catch { val = null; } if ((Object)(object)val != (Object)null && !IsLiveOpenShopStock(val)) { val = null; } if (val == null) { val = FindActiveShopStock(); } if ((Object)(object)val == (Object)null || !IsLiveOpenShopStock(val)) { MerchantStackerPlugin.Log.LogWarning((object)("Confirm qty wait: no live stock for " + stats.Item.DisplayName)); return; } StopConfirmWaitCoroutine(); _pendingQtyOpen = true; _confirmWaitCo = ((MonoBehaviour)QuantityPicker.Instance).StartCoroutine(OpenQtyAfterConfirmShows(val, stats, "UI CONFIRM.wait")); } } private static IEnumerator OpenQtyAfterConfirmShows(ShopMenuStock stock, ShopItemStats stats, string reason) { for (int i = 0; i < 90; i++) { yield return null; if ((Object)(object)QuantityPicker.Instance == (Object)null || QuantityPicker.Instance.IsOpen) { _pendingQtyOpen = false; _confirmWaitCo = null; yield break; } if ((Object)(object)stats == (Object)null) { _pendingQtyOpen = false; _confirmWaitCo = null; yield break; } ShopMenuStock val = FindActiveShopStock() ?? stock; if ((Object)(object)val != (Object)null && !IsLiveOpenShopStock(val)) { val = FindActiveShopStock(); } Transform val2 = null; if ((Object)(object)val != (Object)null) { val2 = FindActiveConfirmGroup(((Component)val).transform.root); } if ((Object)(object)val2 == (Object)null) { using List.Enumerator enumerator = FindActiveConfirmGroups().GetEnumerator(); if (enumerator.MoveNext()) { val2 = enumerator.Current; } } if ((Object)(object)val2 == (Object)null) { continue; } if (val == null) { val = ((Component)val2).GetComponentInParent(true) ?? stock; } if (!((Object)(object)val == (Object)null)) { PreHideConfirmChrome(val2); MerchantStackerPlugin.Log.LogInfo((object)(reason + " → open qty: " + stats.Item.DisplayName)); OpenQtyReplacingConfirm(val, stats); _pendingQtyOpen = false; _confirmWaitCo = null; if ((Object)(object)QuantityPicker.Instance != (Object)null && QuantityPicker.Instance.IsOpen) { _armedBulkStats = null; yield break; } RestoreConfirmChrome(); ForceEnableConfirmYesNo(); MerchantStackerPlugin.Log.LogWarning((object)("Confirm qty: OpenInShop failed mid-wait (via '" + reason + "') — restored vanilla confirm")); yield break; } } _pendingQtyOpen = false; _confirmWaitCo = null; RestoreConfirmChrome(); ForceEnableConfirmYesNo(); MerchantStackerPlugin.Log.LogWarning((object)("Confirm qty: failed to open (via '" + reason + "') — restored vanilla confirm")); } private static IEnumerator WaitForConfirmThenOpen(GameObject? eventSource) { for (int i = 0; i < 45; i++) { yield return null; if ((Object)(object)QuantityPicker.Instance == (Object)null || QuantityPicker.Instance.IsOpen) { _pendingQtyOpen = false; yield break; } Transform val = null; using (List.Enumerator enumerator = FindActiveConfirmGroups().GetEnumerator()) { if (enumerator.MoveNext()) { val = enumerator.Current; } } if (!((Object)(object)val == (Object)null)) { ShopItemStats val2 = ResolveBulkStatsNear(eventSource) ?? ResolveBulkStatsNear(((Component)val).gameObject); if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.Item == (Object)null)) { _pendingQtyOpen = false; MerchantStackerPlugin.Log.LogInfo((object)("Confirm wait → open qty: " + val2.Item.DisplayName)); OpenQtyForStats(val2, "ConfirmWait", FindShopStock(((Component)val).gameObject)); yield break; } } } _pendingQtyOpen = false; } private static void OpenQtyReplacingConfirm(ShopMenuStock stock, ShopItemStats stats) { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)QuantityPicker.Instance == (Object)null || QuantityPicker.Instance.IsOpen || (Object)(object)((stats != null) ? stats.Item : null) == (Object)null) { return; } Transform transform = ((Component)stock).transform; InventoryPaneBase componentInParent = ((Component)stock).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { transform = ((Component)componentInParent).transform; } MerchantStackerPlugin.Log.LogInfo((object)("Replace confirm with qty: " + stats.Item.DisplayName)); QuantityPicker.Instance.ArmShopPurchaseSession(null, delegate { PurchaseBatcher.EndShopPurchaseBlock(); EventRegister.SendEvent(EventRegisterEvents.ResetShopWindow, (GameObject)null); GameCameras instance2 = GameCameras.instance; if (instance2 != null) { instance2.HUDIn(); } }); QuantityPicker? instance = QuantityPicker.Instance; Transform shopRoot = transform; string displayName = stats.Item.DisplayName; SavedItem item = stats.Item.Item; instance.OpenInShop(shopRoot, displayName, (CollectableItem?)(object)((item is CollectableItem) ? item : null), stats.Item.Cost, stats.Item.CurrencyType, Eligibility.GetMaxQuantity(stats.Item), stats); } private static IEnumerator RetryOpenFromConfirmGroup(GameObject confirmGroup, ShopMenuStock? stockHint) { ShopMenuStock stock = stockHint; for (int i = 0; i < 12; i++) { yield return null; if ((Object)(object)QuantityPicker.Instance == (Object)null || QuantityPicker.Instance.IsOpen) { _pendingQtyOpen = false; yield break; } if ((Object)(object)confirmGroup == (Object)null || !confirmGroup.activeInHierarchy) { _pendingQtyOpen = false; _armedBulkStats = null; yield break; } if (stock == null) { stock = confirmGroup.GetComponentInParent(true) ?? FindActiveShopStock(); } if ((Object)(object)stock != (Object)null && !IsLiveOpenShopStock(stock)) { stock = FindActiveShopStock(); } ShopItemStats val = ResolveCurrentShopStats(confirmGroup) ?? ResolveStatsFromStock(stock) ?? _armedBulkStats; if (!((Object)(object)val == (Object)null) && !((Object)(object)val.Item == (Object)null) && Eligibility.ShouldOfferBulkQty(val.Item)) { _pendingQtyOpen = false; MerchantStackerPlugin.Log.LogInfo((object)("Item Confirm Group retry → open qty: " + val.Item.DisplayName)); if (OpenQtyForStats(val, "ItemConfirmGroup.retry", stock)) { _armedBulkStats = null; PreHideConfirmChrome(confirmGroup.transform); yield break; } } } _pendingQtyOpen = false; if ((Object)(object)confirmGroup != (Object)null) { RestoreConfirmChrome(); ForceEnableConfirmYesNo(); } } private static void PreHideConfirmChrome(Transform root) { if ((Object)(object)root == (Object)null || !RootHasStackableMerchant(root)) { return; } Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeSelf) { continue; } string name = ((Object)val).name; if (!(name == "UI List") || !((Object)(object)val.parent != (Object)null) || !(((Object)val.parent).name == "Confirm")) { switch (name) { case "Confirm msg": case "Costs": case "Yes": case "No": case "Thankyou": break; default: continue; } } if ((!(name == "Yes") && !(name == "No")) || IsUnderShopConfirmTransform(val)) { HiddenChrome.Add(((Component)val).gameObject); ((Component)val).gameObject.SetActive(false); } } } private static void RestoreConfirmChrome() { if (HiddenChrome.Count == 0) { return; } foreach (GameObject item in HiddenChrome) { if ((Object)(object)item != (Object)null && !item.activeSelf) { item.SetActive(true); } } HiddenChrome.Clear(); } private static bool RootHasStackableMerchant(Transform root) { ShopMenuStock[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { if (Eligibility.StockHasStackableOffers(componentsInChildren[i])) { return true; } } return false; } private static bool HasActiveConfirmGroup() { return FindActiveConfirmGroups().Count > 0; } private static List FindActiveConfirmGroups() { List list = new List(); ShopMenuStock[] array = Object.FindObjectsByType((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (ShopMenuStock val in array) { if (IsLiveOpenShopStock(val)) { Transform val2 = FindActiveConfirmGroup(((Component)val).transform.root); if ((Object)(object)val2 != (Object)null) { list.Add(val2); } } } return list; } private static Transform? FindActiveConfirmGroup(Transform root) { if ((Object)(object)root == (Object)null) { return null; } Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if ((Object)(object)val != (Object)null && ((Object)val).name == "Item Confirm Group" && ((Component)val).gameObject.activeInHierarchy) { return val; } } return null; } private static ShopItemStats? ResolveCurrentShopStats(GameObject? go) { ShopMenuStock val = FindShopStock(go); if ((Object)(object)val != (Object)null && !IsLiveOpenShopStock(val)) { val = null; } if (val == null) { val = FindActiveShopStock(); } ShopItemStats val2 = ResolveStatsFromStock(val); if ((Object)(object)val2 == (Object)null && (Object)(object)go != (Object)null) { val2 = ShopSelectionCache.GetStatsFromGameObject(go); } if (val2 == null) { val2 = FindHighlightedBulk(); } if ((Object)(object)val2 == (Object)null || (Object)(object)val2.Item == (Object)null) { return null; } return val2; } private static ShopItemStats? ResolveBulkStatsNear(GameObject? go) { ShopItemStats val = ResolveCurrentShopStats(go); if ((Object)(object)((val != null) ? val.Item : null) != (Object)null) { ShopSelectionCache.Remember(val); if (!Eligibility.ShouldOfferBulkQty(val.Item)) { return null; } return val; } return null; } private static ShopItemStats? ResolveStatsFromStock(ShopMenuStock? stock) { if ((Object)(object)stock == (Object)null) { return null; } try { PlayMakerFSM[] componentsInChildren = ((Component)((Component)stock).transform.root).GetComponentsInChildren(true); foreach (PlayMakerFSM val in componentsInChildren) { if (((val != null) ? val.FsmVariables : null) == null) { continue; } string[] array = new string[3] { "Shop Item", "Item", "Selected Item" }; foreach (string text in array) { FsmGameObject obj = val.FsmVariables.FindFsmGameObject(text); ShopItemStats statsFromGameObject = ShopSelectionCache.GetStatsFromGameObject((obj != null) ? obj.Value : null); if ((Object)(object)((statsFromGameObject != null) ? statsFromGameObject.Item : null) != (Object)null) { return statsFromGameObject; } } array = new string[4] { "Current Item", "Item Number", "Selected Index", "Initial Item" }; foreach (string text2 in array) { FsmInt val2 = val.FsmVariables.FindFsmInt(text2); if (val2 != null) { ShopItemStats val3 = StatsFromStockIndex(stock, val2.Value); if ((Object)(object)((val3 != null) ? val3.Item : null) != (Object)null) { return val3; } } } } } catch { } return null; } private static ShopItemStats? StatsFromStockIndex(ShopMenuStock stock, int index) { int itemCount = stock.GetItemCount(); int[] array = new int[2] { index, index - 1 }; foreach (int num in array) { if (num >= 0 && num < itemCount) { ShopItemStats statsFromGameObject = ShopSelectionCache.GetStatsFromGameObject(stock.GetItemGameObject(num)); if ((Object)(object)((statsFromGameObject != null) ? statsFromGameObject.Item : null) != (Object)null) { return statsFromGameObject; } } } return null; } private static bool IsShopConfirmYes(UISelectionListItem item) { if ((Object)(object)item == (Object)null) { return false; } string name = ((Object)((Component)item).gameObject).name; if (!name.Equals("Yes", StringComparison.OrdinalIgnoreCase) && name.IndexOf("yes", StringComparison.OrdinalIgnoreCase) < 0) { return false; } return IsUnderShopConfirmTransform(((Component)item).transform); } private static bool IsUnderShopConfirmList(UISelectionList list) { if ((Object)(object)list != (Object)null) { return IsUnderShopConfirmTransform(((Component)list).transform); } return false; } private static bool IsUnderShopConfirmTransform(Transform? t) { bool flag = false; while ((Object)(object)t != (Object)null) { if ((((Object)t).name == "Confirm" || ((Object)t).name == "Item Confirm Group" || ((Object)t).name == "UI List") && (((Object)t).name == "Confirm" || ((Object)t).name == "Item Confirm Group")) { flag = true; } if (flag && ((Object)(object)((Component)t).GetComponent() != (Object)null || (Object)(object)((Component)t).GetComponentInParent(true) != (Object)null || (Object)(object)((Component)t).GetComponent() != (Object)null || (Object)(object)((Component)t).GetComponentInParent(true) != (Object)null)) { return true; } t = t.parent; } if (flag) { return (Object)(object)FindActiveShopStock() != (Object)null; } return false; } private static ShopMenuStock? FindShopStock(GameObject? go) { if ((Object)(object)go == (Object)null) { return null; } return go.GetComponent() ?? go.GetComponentInChildren(true) ?? go.GetComponentInParent() ?? ((Component)go.transform.root).GetComponentInChildren(true); } private static ShopMenuStock? FindActiveShopStock() { ShopMenuStock[] array = Object.FindObjectsByType((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (ShopMenuStock val in array) { if (IsLiveOpenShopStock(val)) { return val; } } return null; } internal static bool IsLiveOpenShopStock(ShopMenuStock? stock) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)stock == (Object)null || !Eligibility.StockHasStackableOffers(stock)) { return false; } try { Scene scene = ((Component)stock).gameObject.scene; if (!((Scene)(ref scene)).IsValid()) { return false; } if (stock.GetItemCount() <= 0) { return false; } InventoryPaneBase componentInParent = ((Component)stock).GetComponentInParent(true); if ((Object)(object)componentInParent != (Object)null) { return ((Component)componentInParent).gameObject.activeInHierarchy; } if (((Component)stock).gameObject.activeInHierarchy) { return true; } return (Object)(object)FindActiveConfirmGroup(((Component)stock).transform.root) != (Object)null; } catch { return false; } } private static ShopMenuStock? FindAnyShopStock() { return FindActiveShopStock(); } private static ShopItemStats? FindHighlightedBulk() { InventoryItemManager[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (InventoryItemManager val in array) { if ((Object)(object)((val != null) ? val.CurrentSelected : null) == (Object)null || !((Behaviour)val).isActiveAndEnabled) { continue; } ShopMenuStock componentInParent = ((Component)val).GetComponentInParent(true); if (!((Object)(object)componentInParent != (Object)null) || IsLiveOpenShopStock(componentInParent)) { ShopItemStats statsFromGameObject = ShopSelectionCache.GetStatsFromGameObject(((Component)val.CurrentSelected).gameObject); if ((Object)(object)((statsFromGameObject != null) ? statsFromGameObject.Item : null) != (Object)null && Eligibility.ShouldOfferBulkQty(statsFromGameObject.Item)) { return statsFromGameObject; } } } return null; } } [HarmonyPatch] internal static class ShopPurchasePatches { [HarmonyPrefix] [HarmonyPatch(typeof(SetShopItemPurchased), "OnEnter")] private static bool SetPurchasedPrefix(SetShopItemPurchased __instance) { return TryIntercept(FSMUtility.GetSafe(__instance.Target, (FsmStateAction)(object)__instance), 0, (Action)delegate(bool v) { if (__instance.IsWaitingBool != null) { __instance.IsWaitingBool.Value = v; } }, (Action)((FsmStateAction)__instance).Finish, (Action)delegate { if (__instance.IsWaitingBool != null) { __instance.IsWaitingBool.Value = false; } GameCameras instance = GameCameras.instance; if (instance != null) { instance.HUDIn(); } }); } [HarmonyPrefix] [HarmonyPatch(typeof(SetShopItemPurchasedV2), "OnEnter")] private static bool SetPurchasedV2Prefix(SetShopItemPurchasedV2 __instance) { int subItemIndex = ((__instance.SubItemIndex != null) ? __instance.SubItemIndex.Value : 0); return TryIntercept(FSMUtility.GetSafe(__instance.Target, (FsmStateAction)(object)__instance), subItemIndex, (Action)delegate(bool v) { if (__instance.IsWaitingBool != null) { __instance.IsWaitingBool.Value = v; } }, (Action)((FsmStateAction)__instance).Finish, (Action)delegate { if (__instance.IsWaitingBool != null) { __instance.IsWaitingBool.Value = false; } GameCameras instance = GameCameras.instance; if (instance != null) { instance.HUDIn(); } }); } private static bool TryIntercept(GameObject? target, int subItemIndex, Action setWaiting, Action finish, Action onPurchaseComplete) { if (!MerchantStackerPlugin.Enabled.Value || PurchaseBatcher.IsBatching) { return true; } if ((Object)(object)QuantityPicker.Instance != (Object)null && QuantityPicker.Instance.IsOpen) { MerchantStackerPlugin.Log.LogInfo((object)"SetShopItemPurchased blocked (qty picker open)"); setWaiting(obj: false); finish(); return false; } if (PurchaseBatcher.BlockShopPurchases) { MerchantStackerPlugin.Log.LogInfo((object)"SetShopItemPurchased blocked (bulk already done)"); setWaiting(obj: false); finish(); return false; } if ((Object)(object)target == (Object)null) { return true; } ShopItemStats stats = target.GetComponent(); ManualLogSource log = MerchantStackerPlugin.Log; ShopItemStats obj = stats; log.LogInfo((object)$"SetShopItemPurchased: {(((Object)(object)((obj != null) ? obj.Item : null) != (Object)null) ? stats.Item.DisplayName : ((Object)target).name)} pending={PurchaseBatcher.PendingQuantity}"); ShopItemStats obj2 = stats; if ((Object)(object)((obj2 != null) ? obj2.Item : null) == (Object)null) { return true; } ShopSelectionCache.Remember(stats); PurchaseBatcher.ExpectingFsmPurchase = false; int num = PurchaseBatcher.ConsumePendingQuantity(); if (num >= 1) { setWaiting(obj: true); PurchaseBatcher.BuyShopItem(stats, num, subItemIndex, delegate { QuantityPicker.ShowPurchaseFeedback(stats); onPurchaseComplete(); }); finish(); return false; } if (Eligibility.ShouldOfferBulkQty(stats.Item)) { setWaiting(obj: true); if (ShopConfirmListPatches.TryOpenQtyImmediate(stats)) { MerchantStackerPlugin.Log.LogInfo((object)"SetShopItemPurchased → opened late qty UI (Yes beat confirm hook)"); QuantityPicker.Instance.ArmShopPurchaseSession(onPurchaseComplete, delegate { setWaiting(obj: false); PurchaseBatcher.EndShopPurchaseBlock(); EventRegister.SendEvent(EventRegisterEvents.ResetShopWindow, (GameObject)null); GameCameras instance = GameCameras.instance; if (instance != null) { instance.HUDIn(); } }); finish(); return false; } MerchantStackerPlugin.Log.LogWarning((object)"SetShopItemPurchased: qty UI unavailable — allowing single vanilla buy"); setWaiting(obj: false); return true; } return true; } } [HarmonyPatch] internal static class SimpleShopPatches { private static readonly FieldInfo SelectedIndexField = AccessTools.Field(typeof(SimpleShopMenu), "selectedIndex"); private static readonly FieldInfo OwnerField = AccessTools.Field(typeof(SimpleShopMenu), "owner"); private static readonly FieldInfo StateField = AccessTools.Field(typeof(SimpleShopMenu), "state"); private static readonly FieldInfo OpenTimeField = AccessTools.Field(typeof(SimpleShopMenu), "openTime"); private static readonly FieldInfo DidPurchaseField = AccessTools.Field(typeof(SimpleShopMenu), "didPurchase"); private static readonly FieldInfo PurchasedIndexField = AccessTools.Field(typeof(SimpleShopMenu), "purchasedIndex"); private static bool _handlingBulk; private static SimpleShopMenu? _pendingMenu; private static SimpleShopMenuOwner? _pendingOwner; private static int _pendingIndex; private static int _pendingUnitCost; [HarmonyPrefix] [HarmonyPatch(typeof(SimpleShopMenu), "OnSubmitPressed")] private static bool OnSubmitPressedPrefix(SimpleShopMenu __instance) { //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_00f9: Unknown result type (might be due to invalid IL or missing references) if (!MerchantStackerPlugin.Enabled.Value || _handlingBulk || (Object)(object)QuantityPicker.Instance == (Object)null || QuantityPicker.Instance.IsOpen) { return true; } if ((float)OpenTimeField.GetValue(__instance) > Time.time) { return true; } if ((int)StateField.GetValue(__instance) != 0) { return true; } ISimpleShopItem currentSelectedItem = __instance.GetCurrentSelectedItem(); if (currentSelectedItem == null) { return true; } int cost = currentSelectedItem.GetCost(); if (cost <= 0 || PlayerData.instance.geo < cost) { return true; } if (!TryExtractShopItem(currentSelectedItem, out ShopItem shopItem) || (Object)(object)shopItem == (Object)null || !Eligibility.ShouldOfferBulkQty(shopItem)) { return true; } int maxQuantity = Eligibility.GetMaxQuantity(shopItem); if (maxQuantity < 1) { return true; } int pendingIndex = (int)SelectedIndexField.GetValue(__instance); SimpleShopMenuOwner val = (SimpleShopMenuOwner)OwnerField.GetValue(__instance); _pendingMenu = __instance; _pendingOwner = val; _pendingIndex = pendingIndex; _pendingUnitCost = cost; QuantityPicker? instance = QuantityPicker.Instance; string displayName = shopItem.DisplayName; SavedItem item = shopItem.Item; instance.Open(displayName, (CollectableItem?)(object)((item is CollectableItem) ? item : null), cost, shopItem.CurrencyType, maxQuantity, delegate(int qty) { SimpleShopMenu pendingMenu = _pendingMenu; SimpleShopMenuOwner pendingOwner = _pendingOwner; int pendingIndex2 = _pendingIndex; int pendingUnitCost = _pendingUnitCost; ClearPending(); if ((Object)(object)pendingMenu != (Object)null && (Object)(object)pendingOwner != (Object)null) { ApplySimpleShopBulk(pendingMenu, pendingOwner, pendingIndex2, pendingUnitCost, qty); } }, ClearPending); return false; } private static void ClearPending() { _pendingMenu = null; _pendingOwner = null; _pendingIndex = -1; _pendingUnitCost = 0; } private static void ApplySimpleShopBulk(SimpleShopMenu menu, SimpleShopMenuOwner owner, int selectedIndex, int unitCost, int quantity) { _handlingBulk = true; try { DidPurchaseField.SetValue(menu, true); PurchasedIndexField.SetValue(menu, selectedIndex); int num = 0; for (int i = 0; i < quantity; i++) { if (PlayerData.instance.geo < unitCost) { break; } CurrencyManager.TakeCurrency(unitCost, (CurrencyType)0, true); owner.PurchaseNoClose(selectedIndex); num++; if (!owner.HasStockLeft()) { break; } owner.RefreshStock(); } MerchantStackerPlugin.Log.LogInfo((object)$"Simple shop bulk bought {num}"); PurchasedIndexField.SetValue(menu, -1); if (owner.HasStockLeft()) { owner.RefreshStock(); AccessTools.Method(typeof(SimpleShopMenu), "ScrollTo", new Type[2] { typeof(int), typeof(bool) }, (Type[])null)?.Invoke(menu, new object[2] { 0, true }); menu.ConfirmNo(true); } else { object? value = AccessTools.Field(typeof(SimpleShopMenu), "pane").GetValue(menu); object? obj = ((value is InventoryPaneStandalone) ? value : null); if (obj != null) { ((InventoryPaneBase)obj).PaneEnd(); } } } finally { _handlingBulk = false; } } private static bool TryExtractShopItem(ISimpleShopItem item, out ShopItem? shopItem) { shopItem = null; FieldInfo[] fields = ((object)item).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (typeof(ShopItem).IsAssignableFrom(fieldInfo.FieldType)) { object? value = fieldInfo.GetValue(item); shopItem = (ShopItem?)((value is ShopItem) ? value : null); return (Object)(object)shopItem != (Object)null; } } return false; } } }