using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace BigCartFix; [BepInPlugin("bigcart.fix.runtime", "Big Cart Fix", "1.0.6")] public sealed class Plugin : BaseUnityPlugin { internal const string PluginGuid = "bigcart.fix.runtime"; internal const string PluginName = "Big Cart Fix"; internal const string PluginVersion = "1.0.6"; private void Awake() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) ModLog.Source = ((BaseUnityPlugin)this).Logger; ModConfig.Bind(((BaseUnityPlugin)this).Config); new Harmony("bigcart.fix.runtime").PatchAll(); CartPurchaseSettings.Apply(); ModLog.Info("Patched Big Cart display, null guard, and shop purchase limits."); } } internal static class ModConfig { private const string DisplaySection = "Display"; private const string ShopSection = "Shop"; private const string GeneralSection = "General"; private static ConfigEntry debugLogging; private static ConfigEntry enableShopSettings; private static ConfigEntry maxBigCartsForPurchase; private static ConfigEntry guaranteedBigCartsInShop; private static ConfigEntry bigCartPriceOverride; private static ConfigEntry bigCartVolumeOverride; private static ConfigEntry valueTextPath; public static bool DebugLogging { get { if (debugLogging != null) { return debugLogging.Value; } return false; } } public static int MaxBigCartsForPurchase { get { if (maxBigCartsForPurchase == null) { return 4; } return Mathf.Clamp(maxBigCartsForPurchase.Value, 1, 20); } } public static bool EnableShopSettings { get { if (enableShopSettings != null) { return enableShopSettings.Value; } return true; } } public static int GuaranteedBigCartsInShop { get { if (guaranteedBigCartsInShop == null) { return 1; } return Mathf.Clamp(guaranteedBigCartsInShop.Value, 0, 20); } } public static int BigCartPriceOverride { get { if (bigCartPriceOverride == null) { return 0; } return Mathf.Clamp(bigCartPriceOverride.Value, 0, 1000000); } } public static string BigCartVolumeOverride { get { if (bigCartVolumeOverride == null || bigCartVolumeOverride.Value == null) { return "Default"; } return bigCartVolumeOverride.Value.Trim(); } } public static string ValueTextPath { get { if (valueTextPath == null || valueTextPath.Value == null) { return string.Empty; } return valueTextPath.Value.Trim(); } } public static void Bind(ConfigFile config) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Expected O, but got Unknown debugLogging = config.Bind("General", "DebugLogging", false, "Enable extra logs for Big Cart Fix."); valueTextPath = config.Bind("Display", "ValueTextPath", string.Empty, "Optional Transform path to the Big Cart TMP value text. Leave empty to use the built-in paths."); enableShopSettings = config.Bind("Shop", "EnableShopSettings", true, "Enable Big Cart shop limit, guarantee, price, and volume settings."); maxBigCartsForPurchase = config.Bind("Shop", "MaxBigCartsForPurchase", 4, new ConfigDescription("Maximum amount of Big Carts that can be offered/purchased during a run.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 20), new object[0])); guaranteedBigCartsInShop = config.Bind("Shop", "GuaranteedBigCartsInShop", 1, new ConfigDescription("How many Big Carts should be forced into the regular shop candidate list. Set to 0 to only use vanilla/random shop selection.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 20), new object[0])); bigCartPriceOverride = config.Bind("Shop", "BigCartPriceOverride", 0, new ConfigDescription("Override Big Cart item value. Set to 0 to keep the original mod value.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 1000000), new object[0])); bigCartVolumeOverride = config.Bind("Shop", "BigCartVolumeOverride", "Default", new ConfigDescription("Override the shop slot volume used by Big Cart. Use Default, small, medium, large, large_wide, large_high, large_plus, or vehicle.", (AcceptableValueBase)(object)new AcceptableValueList(new string[8] { "Default", "small", "medium", "large", "large_wide", "large_high", "large_plus", "vehicle" }), new object[0])); } } internal static class ModLog { public static ManualLogSource Source; public static void Info(string message) { if (Source != null) { Source.LogInfo((object)message); } } public static void Warning(string message) { if (Source != null) { Source.LogWarning((object)message); } else { Debug.LogWarning((object)("[Big Cart Fix] " + message)); } } public static void DebugInfo(string message) { if (ModConfig.DebugLogging && Source != null) { Source.LogInfo((object)("[Debug] " + message)); } } } internal static class CartValueDisplay { private static readonly FieldInfo HaulCurrentField = AccessTools.Field(typeof(PhysGrabCart), "haulCurrent"); private static readonly FieldInfo OriginalColorField = AccessTools.Field(typeof(ValueScreen), "originalColor"); private static readonly FieldInfo ValuePreviousField = AccessTools.Field(typeof(ValueScreen), "valuePrevious"); private static readonly FieldInfo ValueCurrentField = AccessTools.Field(typeof(ValueScreen), "valueCurrent"); private static readonly FieldInfo ResetTextField = AccessTools.Field(typeof(ValueScreen), "resetText"); private static readonly FieldInfo SoundSourceField = AccessTools.Field(typeof(Sound), "Source"); private static readonly FieldInfo[] SoundFields = typeof(Sound).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly string[] ValueTextPathCandidates = new string[12] { "Value Screen/Value Text", "Value Screen/Text", "Value Screen/Text (TMP)", "ValueScreen/ValueText", "ValueScreen/Text", "Screen/Value Text", "Screen/Text", "Canvas/Value Text", "Canvas/Text", "Canvas/Text (TMP)", "Value Text", "Text (TMP)" }; private static readonly Dictionary FallbackTexts = new Dictionary(); private static readonly HashSet BoundScreens = new HashSet(); private static readonly HashSet MissingDisplayWarnings = new HashSet(); private static readonly HashSet LoggedTextPaths = new HashSet(); private static Sound sourceIncreaseSound; private static Sound sourceDecreaseSound; private static bool loggedSoundAttached; private static bool loggedSoundMissing; public static void EnsureBound(PhysGrabCart cart) { if ((Object)(object)cart == (Object)null) { return; } int instanceID = ((Object)cart).GetInstanceID(); if (FallbackTexts.TryGetValue(instanceID, out var value) && (Object)(object)value != (Object)null) { return; } LogTextPaths(cart); if ((Object)(object)cart.valueScreen != (Object)null && (Object)(object)cart.valueScreen.displayText != (Object)null) { BindScreen(cart, cart.valueScreen, cart.valueScreen.displayText); return; } ValueScreen componentInChildren = ((Component)cart).GetComponentInChildren(true); TMP_Text val = FindDisplayText(cart, componentInChildren); TextMeshPro val2 = (TextMeshPro)(object)((val is TextMeshPro) ? val : null); if ((Object)(object)componentInChildren != (Object)null) { if ((Object)(object)componentInChildren.displayText == (Object)null && (Object)(object)val2 != (Object)null) { componentInChildren.displayText = val2; } if ((Object)(object)componentInChildren.displayText != (Object)null) { BindScreen(cart, componentInChildren, componentInChildren.displayText); return; } } if ((Object)(object)val2 != (Object)null) { ValueScreen val3 = ((Component)val2).gameObject.GetComponent(); if ((Object)(object)val3 == (Object)null) { val3 = ((Component)val2).gameObject.AddComponent(); } BindScreen(cart, val3, val2); } else if ((Object)(object)val != (Object)null) { FallbackTexts[instanceID] = val; SetText(val, ReadHaul(cart)); } else if (MissingDisplayWarnings.Add(instanceID)) { ModLog.Warning("Could not find a value text display on this cart prefab."); } } private static void BindScreen(PhysGrabCart cart, ValueScreen screen, TextMeshPro displayText) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)cart == (Object)null) && !((Object)(object)screen == (Object)null) && !((Object)(object)displayText == (Object)null)) { int instanceID = ((Object)cart).GetInstanceID(); screen.displayText = displayText; cart.valueScreen = screen; OriginalColorField.SetValue(screen, ((Graphic)displayText).color); TryAttachSounds(screen); if (BoundScreens.Add(instanceID)) { SetScreenBaseline(screen, ReadHaul(cart)); } } } private static bool TryAttachSounds(ValueScreen screen) { if ((Object)(object)screen == (Object)null) { return false; } CacheSourceSounds(screen); if (!HasPlayableSound(screen.soundValueIncrease) && HasPlayableSound(sourceIncreaseSound)) { screen.soundValueIncrease = CloneSound(sourceIncreaseSound); } if (!HasPlayableSound(screen.soundValueDecrease) && HasPlayableSound(sourceDecreaseSound)) { screen.soundValueDecrease = CloneSound(sourceDecreaseSound); } bool flag = HasPlayableSound(screen.soundValueIncrease) && HasPlayableSound(screen.soundValueDecrease); if (flag && !loggedSoundAttached) { ModLog.Info("Attached vanilla cart value sounds to Big Cart value display."); loggedSoundAttached = true; } return flag; } private static void CacheSourceSounds(ValueScreen skipScreen) { if (HasPlayableSound(sourceIncreaseSound) && HasPlayableSound(sourceDecreaseSound)) { return; } ValueScreen[] array = Resources.FindObjectsOfTypeAll(); ValueScreen[] array2 = array; foreach (ValueScreen val in array2) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)skipScreen) && HasPlayableSound(val.soundValueIncrease) && HasPlayableSound(val.soundValueDecrease)) { sourceIncreaseSound = val.soundValueIncrease; sourceDecreaseSound = val.soundValueDecrease; break; } } } private static bool HasPlayableSound(Sound sound) { if (sound != null && sound.Sounds != null) { return sound.Sounds.Length > 0; } return false; } private static Sound CloneSound(Sound source) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown if (source == null) { return null; } Sound val = new Sound(); FieldInfo[] soundFields = SoundFields; foreach (FieldInfo fieldInfo in soundFields) { if (!fieldInfo.IsInitOnly && !(fieldInfo.Name == "Source") && !(fieldInfo.Name == "LowPassLogic") && !(fieldInfo.Name == "HasLowPassLogic")) { fieldInfo.SetValue(val, fieldInfo.GetValue(source)); } } SoundSourceField.SetValue(val, null); return val; } private static void SetScreenBaseline(ValueScreen screen, int value) { int num = Mathf.Max(0, value); ValuePreviousField.SetValue(screen, num); ValueCurrentField.SetValue(screen, num); ResetTextField.SetValue(screen, true); SetText((TMP_Text)(object)screen.displayText, num); } public static void Refresh(PhysGrabCart cart) { EnsureBound(cart); if ((Object)(object)cart != (Object)null && FallbackTexts.TryGetValue(((Object)cart).GetInstanceID(), out var value) && (Object)(object)value != (Object)null) { SetText(value, ReadHaul(cart)); } } public static bool TryHandleValueScreenUpdate(ValueScreen screen, int value) { if ((Object)(object)screen == (Object)null) { return false; } if ((Object)(object)screen.displayText == (Object)null) { return false; } if (TryAttachSounds(screen)) { return true; } if (!loggedSoundMissing) { ModLog.Warning("No playable vanilla cart value sounds found yet; updating Big Cart value display without audio."); loggedSoundMissing = true; } SetText((TMP_Text)(object)screen.displayText, value); return false; } private static int ReadHaul(PhysGrabCart cart) { object value = HaulCurrentField.GetValue(cart); if (value is int) { return (int)value; } return 0; } private static TMP_Text FindDisplayText(PhysGrabCart cart, ValueScreen existingScreen) { if ((Object)(object)existingScreen != (Object)null && (Object)(object)existingScreen.displayText != (Object)null) { return (TMP_Text)(object)existingScreen.displayText; } TMP_Text val = FindTextAtPath(cart, ModConfig.ValueTextPath); if ((Object)(object)val != (Object)null) { ModLog.DebugInfo("Found value text from config path: " + GetPath(((Component)cart).transform, val.transform)); return val; } string[] valueTextPathCandidates = ValueTextPathCandidates; foreach (string path in valueTextPathCandidates) { TMP_Text val2 = FindTextAtPath(cart, path); if ((Object)(object)val2 != (Object)null) { ModLog.DebugInfo("Found value text from built-in path: " + GetPath(((Component)cart).transform, val2.transform)); return val2; } } TMP_Text val3 = FindTextByInitialValue(cart); if ((Object)(object)val3 != (Object)null) { ModLog.DebugInfo("Found value text by initial value: " + GetPath(((Component)cart).transform, val3.transform)); return val3; } return FindOnlyWorldText(cart); } private static TMP_Text FindTextAtPath(PhysGrabCart cart, string path) { if ((Object)(object)cart == (Object)null || string.IsNullOrEmpty(path)) { return null; } Transform val = FindTransformByPath(((Component)cart).transform, path); if ((Object)(object)val == (Object)null) { return null; } TMP_Text component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } return ((Component)val).GetComponentInChildren(true); } private static Transform FindTransformByPath(Transform root, string path) { if ((Object)(object)root == (Object)null || string.IsNullOrEmpty(path)) { return null; } string text = path.Trim().Replace('\\', '/').Trim(new char[1] { '/' }); if (text.Length == 0) { return null; } if (string.Equals(text, ((Object)root).name, StringComparison.OrdinalIgnoreCase)) { return root; } string text2 = ((Object)root).name + "/"; if (text.StartsWith(text2, StringComparison.OrdinalIgnoreCase)) { text = text.Substring(text2.Length); } Transform val = root.Find(text); if ((Object)(object)val != (Object)null) { return val; } return FindTransformByPathIgnoreCase(root, text); } private static Transform FindTransformByPathIgnoreCase(Transform root, string path) { string[] array = path.Split(new char[1] { '/' }); Transform val = root; string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim(); if (text2.Length == 0) { continue; } Transform val2 = null; for (int j = 0; j < val.childCount; j++) { Transform child = val.GetChild(j); if (string.Equals(((Object)child).name, text2, StringComparison.OrdinalIgnoreCase)) { val2 = child; break; } } if ((Object)(object)val2 == (Object)null) { return null; } val = val2; } return val; } private static TMP_Text FindTextByInitialValue(PhysGrabCart cart) { TMP_Text[] componentsInChildren = ((Component)cart).GetComponentsInChildren(true); TMP_Text[] array = componentsInChildren; foreach (TMP_Text val in array) { if (!((Object)(object)val == (Object)null) && val.text != null && val.text.Contains("100000")) { return val; } } return null; } private static TMP_Text FindOnlyWorldText(PhysGrabCart cart) { TMP_Text[] componentsInChildren = ((Component)cart).GetComponentsInChildren(true); TMP_Text val = null; int num = 0; TMP_Text[] array = componentsInChildren; foreach (TMP_Text val2 in array) { if (val2 is TextMeshPro) { val = val2; num++; } } if (num == 1) { ModLog.DebugInfo("Found the only world TMP text: " + GetPath(((Component)cart).transform, val.transform)); return val; } return null; } private static void LogTextPaths(PhysGrabCart cart) { if (!ModConfig.DebugLogging || (Object)(object)cart == (Object)null) { return; } int instanceID = ((Object)cart).GetInstanceID(); if (!LoggedTextPaths.Add(instanceID)) { return; } TMP_Text[] componentsInChildren = ((Component)cart).GetComponentsInChildren(true); TMP_Text[] array = componentsInChildren; foreach (TMP_Text val in array) { if (!((Object)(object)val == (Object)null)) { string text = ((val.text == null) ? string.Empty : val.text); ModLog.DebugInfo("TMP path: " + GetPath(((Component)cart).transform, val.transform) + " | text: " + text); } } } private static string GetPath(Transform root, Transform target) { if ((Object)(object)root == (Object)null || (Object)(object)target == (Object)null) { return string.Empty; } List list = new List(); Transform val = target; while ((Object)(object)val != (Object)null) { list.Insert(0, ((Object)val).name); if ((Object)(object)val == (Object)(object)root) { break; } val = val.parent; } return string.Join("/", list.ToArray()); } private static void SetText(TMP_Text text, int value) { int num = Mathf.Max(0, value); text.text = "$" + SemiFunc.DollarGetString(num); } } internal static class CartPurchaseSettings { private static readonly string[] BigCartItemNames = new string[5] { "Cart Big", "Item Cart Big", "Big Cart", "B.I.G. C.A.R.T.", "Big_Cart" }; private static readonly HashSet AppliedItems = new HashSet(); private static readonly HashSet GuaranteedManagers = new HashSet(); private static bool loggedMissingBigCartItem; public static void Apply() { if (!ModConfig.EnableShopSettings || (Object)(object)StatsManager.instance == (Object)null || StatsManager.instance.itemDictionary == null) { return; } foreach (KeyValuePair item in StatsManager.instance.itemDictionary) { if (IsBigCartItem(item.Key, item.Value)) { ApplyToItem(item.Value); } } } public static void GuaranteeShopCandidates(ShopManager shopManager) { if (!ModConfig.EnableShopSettings || ModConfig.GuaranteedBigCartsInShop <= 0 || (Object)(object)shopManager == (Object)null || shopManager.potentialItems == null) { return; } Item val = FindBigCartItem(); if ((Object)(object)val == (Object)null) { if (!loggedMissingBigCartItem) { ModLog.Warning("Could not find the Big Cart item in StatsManager.itemDictionary; shop guarantee skipped."); loggedMissingBigCartItem = true; } return; } ApplyToItem(val); RemoveBigCartCandidates(shopManager.potentialItems); int num = SemiFunc.StatGetItemsPurchased(((Object)val).name); int num2 = Mathf.Max(0, val.maxAmountInShop - num); int num3 = Mathf.Min(ModConfig.GuaranteedBigCartsInShop, num2); if (num3 > 0) { for (int i = 0; i < num3; i++) { shopManager.potentialItems.Add(val); } shopManager.itemSpawnTargetAmount = Mathf.Max(shopManager.itemSpawnTargetAmount, num3); if (GuaranteedManagers.Add(((Object)shopManager).GetInstanceID())) { ModLog.Info("Guaranteed " + num3 + " Big Cart shop candidate(s)."); } else { ModLog.DebugInfo("Refreshed guaranteed Big Cart shop candidates: " + num3 + "."); } } } private static Item FindBigCartItem() { if ((Object)(object)StatsManager.instance == (Object)null || StatsManager.instance.itemDictionary == null) { return null; } foreach (KeyValuePair item in StatsManager.instance.itemDictionary) { if (IsBigCartItem(item.Key, item.Value)) { return item.Value; } } return null; } private static void RemoveBigCartCandidates(List items) { for (int num = items.Count - 1; num >= 0; num--) { if (IsBigCartItem(string.Empty, items[num])) { items.RemoveAt(num); } } } private static bool IsBigCartItem(string dictionaryKey, Item item) { if ((Object)(object)item == (Object)null) { return false; } if (!MatchesKnownName(dictionaryKey) && !MatchesKnownName(((Object)item).name)) { return MatchesKnownName(item.itemName); } return true; } private static bool MatchesKnownName(string value) { if (string.IsNullOrEmpty(value)) { return false; } string a = value.Replace("(Clone)", string.Empty).Trim(); string[] bigCartItemNames = BigCartItemNames; foreach (string b in bigCartItemNames) { if (string.Equals(a, b, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static void ApplyToItem(Item item) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) int num = (item.maxPurchaseAmount = (item.maxAmountInShop = (item.maxAmount = ModConfig.MaxBigCartsForPurchase))); int bigCartPriceOverride = ModConfig.BigCartPriceOverride; if (bigCartPriceOverride > 0 && (Object)(object)item.value != (Object)null) { item.value.valueMin = bigCartPriceOverride; item.value.valueMax = bigCartPriceOverride; } if (TryParseVolumeOverride(out var volume)) { item.itemVolume = volume; } if (AppliedItems.Add(((Object)item).GetInstanceID())) { ModLog.Info("Big Cart shop limit set to " + num + "."); } else { ModLog.DebugInfo("Big Cart shop limit refreshed to " + num + "."); } } private static bool TryParseVolumeOverride(out itemVolume volume) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected I4, but got Unknown volume = (itemVolume)1; string bigCartVolumeOverride = ModConfig.BigCartVolumeOverride; if (string.IsNullOrEmpty(bigCartVolumeOverride) || string.Equals(bigCartVolumeOverride, "Default", StringComparison.OrdinalIgnoreCase)) { return false; } try { volume = (itemVolume)(int)(itemVolume)Enum.Parse(typeof(itemVolume), bigCartVolumeOverride, ignoreCase: true); return true; } catch { ModLog.Warning("Invalid BigCartVolumeOverride value: " + bigCartVolumeOverride + ". Keeping original item volume."); return false; } } } [HarmonyPatch(typeof(StatsManager), "LoadItemsFromFolder")] internal static class StatsManagerLoadItemsFromFolderPatch { private static void Postfix() { CartPurchaseSettings.Apply(); } } [HarmonyPatch(typeof(ShopManager), "GetAllItemsFromStatsManager")] internal static class ShopManagerGetAllItemsFromStatsManagerPatch { private static void Prefix() { CartPurchaseSettings.Apply(); } private static void Postfix(ShopManager __instance) { CartPurchaseSettings.GuaranteeShopCandidates(__instance); } } [HarmonyPatch(typeof(ItemManager), "GetPurchasedItems")] internal static class ItemManagerGetPurchasedItemsPatch { private static void Prefix() { CartPurchaseSettings.Apply(); } } [HarmonyPatch(typeof(PhysGrabCart), "Start")] internal static class PhysGrabCartStartPatch { private static void Postfix(PhysGrabCart __instance) { CartValueDisplay.EnsureBound(__instance); } } [HarmonyPatch(typeof(PhysGrabCart), "ObjectsInCart")] internal static class PhysGrabCartObjectsInCartPatch { private static readonly FieldInfo InCartField = AccessTools.Field(typeof(PhysGrabCart), "inCart"); private static readonly HashSet MissingInCartWarnings = new HashSet(); private static bool Prefix(PhysGrabCart __instance) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown CartValueDisplay.EnsureBound(__instance); Transform val = (Transform)InCartField.GetValue(__instance); if ((Object)(object)val != (Object)null) { return true; } int instanceID = ((Object)__instance).GetInstanceID(); if (MissingInCartWarnings.Add(instanceID)) { ModLog.Warning("Skipping cart item scan because this cart prefab has no 'In Cart' transform."); } return false; } private static void Postfix(PhysGrabCart __instance) { CartValueDisplay.Refresh(__instance); } private static Exception Finalizer(Exception __exception) { if (__exception is NullReferenceException) { return null; } return __exception; } private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) { FieldInfo valueScreenField = AccessTools.Field(typeof(PhysGrabCart), "valueScreen"); FieldInfo haulCurrentField = AccessTools.Field(typeof(PhysGrabCart), "haulCurrent"); MethodInfo updateValueMethod = AccessTools.Method(typeof(ValueScreen), "UpdateValue", new Type[1] { typeof(int) }, (Type[])null); List codes = new List(instructions); bool patched = false; for (int i = 0; i < codes.Count; i++) { CodeInstruction code = codes[i]; if (!patched && CodeInstructionExtensions.LoadsField(code, valueScreenField, false) && i + 3 < codes.Count && codes[i + 1].opcode == OpCodes.Ldarg_0 && CodeInstructionExtensions.LoadsField(codes[i + 2], haulCurrentField, false) && CodeInstructionExtensions.Calls(codes[i + 3], updateValueMethod)) { Label hasValueScreenLabel = generator.DefineLabel(); codes[i + 1].labels.Add(hasValueScreenLabel); yield return code; yield return new CodeInstruction(OpCodes.Dup, (object)null); yield return new CodeInstruction(OpCodes.Brtrue_S, (object)hasValueScreenLabel); yield return new CodeInstruction(OpCodes.Pop, (object)null); yield return new CodeInstruction(OpCodes.Ret, (object)null); patched = true; } else { yield return code; } } if (!patched) { ModLog.Warning("Could not find ValueScreen.UpdateValue in PhysGrabCart.ObjectsInCart."); } } } [HarmonyPatch(typeof(ValueScreen), "UpdateValue")] internal static class ValueScreenUpdateValuePatch { private static bool Prefix(ValueScreen __instance, int __0) { return CartValueDisplay.TryHandleValueScreenUpdate(__instance, __0); } }