Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Big Cart Fix v1.0.5
BigCartFix.dll
Decompiled 5 hours agousing 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.5")] 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.5"; 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<bool> debugLogging; private static ConfigEntry<int> maxBigCartsForPurchase; private static ConfigEntry<string> 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 string ValueTextPath { get { if (valueTextPath == null || valueTextPath.Value == null) { return string.Empty; } return valueTextPath.Value.Trim(); } } public static void Bind(ConfigFile config) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown debugLogging = config.Bind<bool>("General", "DebugLogging", false, "Enable extra logs for Big Cart Fix."); valueTextPath = config.Bind<string>("Display", "ValueTextPath", string.Empty, "Optional Transform path to the Big Cart TMP value text. Leave empty to use the built-in paths."); maxBigCartsForPurchase = config.Bind<int>("Shop", "MaxBigCartsForPurchase", 4, new ConfigDescription("Maximum amount of Big Carts that can be offered/purchased during a run.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), 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<int, TMP_Text> FallbackTexts = new Dictionary<int, TMP_Text>(); private static readonly HashSet<int> BoundScreens = new HashSet<int>(); private static readonly HashSet<int> MissingDisplayWarnings = new HashSet<int>(); private static readonly HashSet<int> LoggedTextPaths = new HashSet<int>(); 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<ValueScreen>(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<ValueScreen>(); if ((Object)(object)val3 == (Object)null) { val3 = ((Component)val2).gameObject.AddComponent<ValueScreen>(); } 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>(); 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<TMP_Text>(); if ((Object)(object)component != (Object)null) { return component; } return ((Component)val).GetComponentInChildren<TMP_Text>(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<TMP_Text>(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<TMP_Text>(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<TMP_Text>(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<string> list = new List<string>(); 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 = "<color=#bd4300>$</color>" + SemiFunc.DollarGetString(num); } } internal static class CartPurchaseSettings { private static readonly string[] BigCartItemNames = new string[4] { "Cart Big", "Big Cart", "B.I.G. C.A.R.T.", "Big_Cart" }; private static readonly HashSet<int> AppliedItems = new HashSet<int>(); public static void Apply() { if ((Object)(object)StatsManager.instance == (Object)null || StatsManager.instance.itemDictionary == null) { return; } foreach (KeyValuePair<string, Item> item in StatsManager.instance.itemDictionary) { if (IsBigCartItem(item.Key, item.Value)) { ApplyToItem(item.Value); } } } 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) { int num = (item.maxPurchaseAmount = (item.maxAmountInShop = (item.maxAmount = ModConfig.MaxBigCartsForPurchase))); 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 + "."); } } } [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(); } } [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<int> MissingInCartWarnings = new HashSet<int>(); 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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> 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<CodeInstruction> codes = new List<CodeInstruction>(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); } }