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 ShoppingCart v1.0.4
ShoppingCart.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; 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 ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using ShoppingCart; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("Tolga")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.4.0")] [assembly: AssemblyInformationalVersion("1.0.4")] [assembly: AssemblyProduct("ShoppingCart")] [assembly: AssemblyTitle("ShoppingCart")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.4.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } 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; } } } public class ShopCartComponent : MonoBehaviour { private List<ItemAttributes> itemsInCart = new List<ItemAttributes>(); private Transform? inCartTransform; private Collider? inCartTrigger; private float rescanTimer; private const float RESCAN_INTERVAL = 0.2f; private float nextValueDebugLogTime; private string? lastSetDisplayText; private float lastSetDisplayTime; private static FieldInfo? ShopItemField; private int totalCost; public TextMeshPro? costDisplay; private PhotonView? photonView; private PhysGrabObject? physGrabObject; private MeshRenderer? cartMesh; private float cleanupTimer; private const float CLEANUP_INTERVAL = 0.5f; private void Awake() { photonView = ((Component)this).GetComponent<PhotonView>(); physGrabObject = ((Component)this).GetComponent<PhysGrabObject>(); inCartTransform = ((Component)this).transform.Find("In Cart"); if ((Object)(object)inCartTransform != (Object)null) { inCartTrigger = ((Component)inCartTransform).GetComponent<Collider>(); if ((Object)(object)inCartTrigger == (Object)null) { global::ShoppingCart.ShoppingCart.LogWarning("ShopCartComponent: 'In Cart' child has no Collider!"); } else if (!inCartTrigger.isTrigger) { global::ShoppingCart.ShoppingCart.LogWarning("ShopCartComponent: 'In Cart' collider is not marked as Trigger. Value scanning will still work, but trigger events won't."); } if ((Object)(object)((Component)inCartTransform).GetComponent<ShopCartTrigger>() == (Object)null) { global::ShoppingCart.ShoppingCart.LogWarning("ShopCartComponent: 'In Cart' child needs ShopCartTrigger component!"); } } else { global::ShoppingCart.ShoppingCart.LogWarning("ShopCartComponent: 'In Cart' child GameObject not found!"); } if ((Object)(object)costDisplay == (Object)null) { Transform val = ((Component)this).transform.Find("Screen/Text"); if ((Object)(object)val != (Object)null) { costDisplay = ((Component)val).GetComponent<TextMeshPro>(); } if ((Object)(object)costDisplay == (Object)null) { TextMeshPro[] componentsInChildren = ((Component)this).GetComponentsInChildren<TextMeshPro>(true); costDisplay = ((IEnumerable<TextMeshPro>)componentsInChildren).FirstOrDefault((Func<TextMeshPro, bool>)((TextMeshPro t) => (Object)(object)t.transform.parent != (Object)null && ((Object)t.transform.parent).name == "Screen" && ((Object)t).name == "Text")) ?? ((IEnumerable<TextMeshPro>)componentsInChildren).FirstOrDefault((Func<TextMeshPro, bool>)((TextMeshPro t) => ((Object)t).name.Contains("Screen") || ((Object)(object)t.transform.parent != (Object)null && ((Object)t.transform.parent).name.Contains("Screen")))) ?? componentsInChildren.FirstOrDefault(); } } cartMesh = ((IEnumerable<MeshRenderer>)((Component)this).GetComponentsInChildren<MeshRenderer>(true)).FirstOrDefault((Func<MeshRenderer, bool>)((MeshRenderer m) => (Object)(object)((Component)m).transform != (Object)(object)((Component)this).transform)); } private void Start() { try { CartMaterialRepair.RepairCartMaterials(((Component)this).gameObject); } catch { } UpdateCostDisplay(); if (global::ShoppingCart.ShoppingCart.CartValueDebugLogging.Value) { global::ShoppingCart.ShoppingCart.LogInfo("ShopCartComponent: Start on '" + ((Object)((Component)this).gameObject).name + "'. costDisplay=" + (((Object)(object)costDisplay != (Object)null) ? ((Object)costDisplay).name : "<null>") + ", inCartTrigger=" + (((Object)(object)inCartTrigger != (Object)null) ? ((object)inCartTrigger).GetType().Name : "<null>")); } } private void Update() { if (SemiFunc.RunIsShop() && SemiFunc.IsMasterClientOrSingleplayer()) { if (global::ShoppingCart.ShoppingCart.CartValueDebugLogging.Value && (Object)(object)costDisplay != (Object)null && lastSetDisplayText != null && Time.unscaledTime - lastSetDisplayTime < 1f && ((TMP_Text)costDisplay).text != lastSetDisplayText) { global::ShoppingCart.ShoppingCart.LogWarning("ShopCartComponent: Screen text overwritten. Expected '" + lastSetDisplayText + "', actual '" + ((TMP_Text)costDisplay).text + "'. Another script is writing to the same TMP."); lastSetDisplayTime = -999f; } rescanTimer -= Time.deltaTime; if (rescanTimer <= 0f) { RescanAndRecalculate(); rescanTimer = 0.2f; } cleanupTimer -= Time.deltaTime; if (cleanupTimer <= 0f) { CleanupDestroyedItems(); cleanupTimer = 0.5f; } } } public void OnItemEnter(ItemAttributes itemAttributes) { if (SemiFunc.IsMasterClientOrSingleplayer() && SemiFunc.RunIsShop()) { RescanAndRecalculate(); } } public void OnItemExit(ItemAttributes itemAttributes) { if (SemiFunc.IsMasterClientOrSingleplayer() && SemiFunc.RunIsShop()) { RescanAndRecalculate(); } } private bool IsPurchasableShopItem(ItemAttributes itemAttributes) { if ((Object)(object)itemAttributes == (Object)null) { return false; } if (!SemiFunc.RunIsShop()) { return false; } if ((object)ShopItemField == null) { ShopItemField = typeof(ItemAttributes).GetField("shopItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (ShopItemField != null && ShopItemField.FieldType == typeof(bool)) { try { return (bool)ShopItemField.GetValue(itemAttributes); } catch { } } return (Object)(object)itemAttributes.item != (Object)null; } private void RescanAndRecalculate() { //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: 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_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } if ((Object)(object)inCartTransform == (Object)null || (Object)(object)inCartTrigger == (Object)null) { if (global::ShoppingCart.ShoppingCart.CartValueDebugLogging.Value && Time.unscaledTime >= nextValueDebugLogTime) { global::ShoppingCart.ShoppingCart.LogWarning($"ShopCartComponent: Missing inCartTransform/inCartTrigger. inCartTransform={(Object)(object)inCartTransform != (Object)null}, inCartTrigger={(Object)(object)inCartTrigger != (Object)null}"); nextValueDebugLogTime = Time.unscaledTime + 1f; } return; } Collider? obj = inCartTrigger; BoxCollider val = (BoxCollider)(object)((obj is BoxCollider) ? obj : null); Vector3 val2; Vector3 val3; Quaternion val4; if (val != null) { val2 = ((Component)val).transform.TransformPoint(val.center); Vector3 lossyScale = ((Component)val).transform.lossyScale; ((Vector3)(ref lossyScale))..ctor(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.y), Mathf.Abs(lossyScale.z)); val3 = Vector3.Scale(val.size * 0.5f, lossyScale); val4 = ((Component)val).transform.rotation; } else { Bounds bounds = inCartTrigger.bounds; val2 = ((Bounds)(ref bounds)).center; val3 = ((Bounds)(ref bounds)).extents; val4 = Quaternion.identity; } Collider[] array = Physics.OverlapBox(val2, val3, val4, -1, (QueryTriggerInteraction)1); if (array == null) { return; } itemsInCart.Clear(); int num = 0; int num2 = 0; Collider[] array2 = array; foreach (Collider val5 in array2) { if ((Object)(object)val5 == (Object)null || ((Component)val5).transform.IsChildOf(((Component)this).transform)) { continue; } ItemAttributes componentInParent = ((Component)val5).GetComponentInParent<ItemAttributes>(); if ((Object)(object)componentInParent == (Object)null || !IsPurchasableShopItem(componentInParent) || itemsInCart.Contains(componentInParent)) { continue; } itemsInCart.Add(componentInParent); num += componentInParent.value; num2++; if (global::ShoppingCart.ShoppingCart.CartValueDebugLogging.Value && Time.unscaledTime >= nextValueDebugLogTime) { bool flag = IsPurchasableShopItem(componentInParent); int num3 = 0; PhotonView component = ((Component)componentInParent).GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null) { num3 = component.ViewID; } global::ShoppingCart.ShoppingCart.LogInfo($"ShopCartComponent: Found item '{((Object)((Component)componentInParent).gameObject).name}' value={componentInParent.value} shopItem={flag} pv={num3} at {((Component)componentInParent).transform.position}"); } } if (global::ShoppingCart.ShoppingCart.CartValueDebugLogging.Value && Time.unscaledTime >= nextValueDebugLogTime) { global::ShoppingCart.ShoppingCart.LogInfo($"ShopCartComponent: OverlapBox center={val2} ext={val3} rotY={((Quaternion)(ref val4)).eulerAngles.y:F1} overlaps={array.Length} uniqueItems={num2} newTotal={num}"); nextValueDebugLogTime = Time.unscaledTime + 1f; } if (num != totalCost) { totalCost = num; UpdateCostDisplay(); if (SemiFunc.IsMultiplayer() && (Object)(object)photonView != (Object)null) { photonView.RPC("UpdateCartDisplayRPC", (RpcTarget)1, new object[1] { totalCost }); } } } private void CalculateTotalCost() { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } int num = 0; foreach (ItemAttributes item in itemsInCart) { if ((Object)(object)item != (Object)null) { num += item.value; } } if (num != totalCost) { totalCost = num; UpdateCostDisplay(); if (SemiFunc.IsMultiplayer() && (Object)(object)photonView != (Object)null) { photonView.RPC("UpdateCartDisplayRPC", (RpcTarget)1, new object[1] { totalCost }); } } } private void UpdateCostDisplay() { if ((Object)(object)costDisplay == (Object)null) { if (global::ShoppingCart.ShoppingCart.CartValueDebugLogging.Value && Time.unscaledTime >= nextValueDebugLogTime) { global::ShoppingCart.ShoppingCart.LogWarning("ShopCartComponent: costDisplay is null; cannot update screen text."); nextValueDebugLogTime = Time.unscaledTime + 1f; } return; } string text = SemiFunc.DollarGetString(Mathf.Max(0, totalCost)); string text2 = "$" + text; ((TMP_Text)costDisplay).text = text2; ((Component)costDisplay).gameObject.SetActive(true); lastSetDisplayText = text2; lastSetDisplayTime = Time.unscaledTime; if (global::ShoppingCart.ShoppingCart.CartValueDebugLogging.Value && Time.unscaledTime >= nextValueDebugLogTime) { global::ShoppingCart.ShoppingCart.LogInfo("ShopCartComponent: Set screen text to '" + text2 + "' on '" + ((Object)costDisplay).name + "'"); nextValueDebugLogTime = Time.unscaledTime + 1f; } } [PunRPC] private void UpdateCartDisplayRPC(int syncedTotalCost, PhotonMessageInfo info = default(PhotonMessageInfo)) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (SemiFunc.MasterOnlyRPC(info)) { totalCost = syncedTotalCost; UpdateCostDisplay(); } } private void CleanupDestroyedItems() { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } bool flag = false; for (int num = itemsInCart.Count - 1; num >= 0; num--) { if ((Object)(object)itemsInCart[num] == (Object)null || (Object)(object)((Component)itemsInCart[num]).gameObject == (Object)null) { itemsInCart.RemoveAt(num); flag = true; } } if (flag) { CalculateTotalCost(); } } } public class ShopCartTrigger : MonoBehaviour { public ShopCartComponent? parentCart; private static readonly int[] ALLOWED_ITEM_LAYERS = new int[4] { 9, 16, 20, 23 }; private void Awake() { parentCart = ((Component)this).GetComponentInParent<ShopCartComponent>(); if ((Object)(object)parentCart == (Object)null) { Debug.LogError((object)"ShopCartTrigger: Parent ShopCartComponent not found!"); } else { Debug.Log((object)("ShopCartTrigger: Found parent cart at '" + ((Object)((Component)parentCart).gameObject).name + "'")); } } private void OnTriggerEnter(Collider other) { if ((Object)(object)parentCart == (Object)null || (Object)(object)((Component)other).transform.root == (Object)(object)((Component)this).transform.root) { return; } int otherLayer = ((Component)other).gameObject.layer; if (Array.Exists(ALLOWED_ITEM_LAYERS, (int layer) => layer == otherLayer)) { ItemAttributes componentInParent = ((Component)other).GetComponentInParent<ItemAttributes>(); if ((Object)(object)componentInParent != (Object)null) { parentCart.OnItemEnter(componentInParent); } } } private void OnTriggerExit(Collider other) { if ((Object)(object)parentCart == (Object)null) { return; } int otherLayer = ((Component)other).gameObject.layer; if (Array.Exists(ALLOWED_ITEM_LAYERS, (int layer) => layer == otherLayer)) { ItemAttributes componentInParent = ((Component)other).GetComponentInParent<ItemAttributes>(); if ((Object)(object)componentInParent != (Object)null) { parentCart.OnItemExit(componentInParent); } } } } namespace ShoppingCart { internal static class CartMaterialRepair { internal static void RepairCartMaterials(GameObject cartRoot) { if ((Object)(object)cartRoot == (Object)null) { return; } Renderer[] componentsInChildren; try { componentsInChildren = cartRoot.GetComponentsInChildren<Renderer>(true); } catch { return; } foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } Material[] materials; try { materials = val.materials; } catch { continue; } bool flag = IsBasketLike(((Object)((Component)val).gameObject).name); foreach (Material val2 in materials) { if (!((Object)(object)val2 == (Object)null)) { if (IsBrokenOrErrorShader(val2)) { TryRepairMaterialShader(val2, flag); } else if (flag) { TryEnableAlphaClip(val2); } } } try { val.materials = materials; } catch { } } } private static bool IsBasketLike(string name) { if (string.IsNullOrEmpty(name)) { return false; } string text = name.ToLowerInvariant(); if (!text.Contains("basket") && !text.Contains("wire") && !text.Contains("grid") && !text.Contains("net") && !text.Contains("mesh")) { return text.Contains("cage"); } return true; } private static bool IsBrokenOrErrorShader(Material mat) { Shader shader = mat.shader; if ((Object)(object)shader == (Object)null) { return true; } string text = ((Object)shader).name ?? string.Empty; if (text.Contains("InternalErrorShader", StringComparison.OrdinalIgnoreCase)) { return true; } if (text.Contains("Hidden/InternalErrorShader", StringComparison.OrdinalIgnoreCase)) { return true; } return false; } private static void TryRepairMaterialShader(Material mat, bool basketLike) { //IL_003d: 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_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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) Texture val = null; if (mat.HasProperty("_BaseMap")) { val = mat.GetTexture("_BaseMap"); } if ((Object)(object)val == (Object)null && mat.HasProperty("_MainTex")) { val = mat.GetTexture("_MainTex"); } Color val2 = Color.white; if (mat.HasProperty("_BaseColor")) { val2 = mat.GetColor("_BaseColor"); } else if (mat.HasProperty("_Color")) { val2 = mat.GetColor("_Color"); } Shader val3 = Shader.Find("Universal Render Pipeline/Lit"); if ((Object)(object)val3 == (Object)null) { val3 = Shader.Find("Standard"); } if ((Object)(object)val3 == (Object)null) { return; } mat.shader = val3; if (mat.HasProperty("_BaseColor")) { mat.SetColor("_BaseColor", val2); } if (mat.HasProperty("_Color")) { mat.SetColor("_Color", val2); } if ((Object)(object)val != (Object)null) { if (mat.HasProperty("_BaseMap")) { mat.SetTexture("_BaseMap", val); } if (mat.HasProperty("_MainTex")) { mat.SetTexture("_MainTex", val); } } if (basketLike) { TryEnableAlphaClip(mat); } } private static void TryEnableAlphaClip(Material mat) { if (mat.HasProperty("_AlphaClip")) { mat.SetFloat("_AlphaClip", 1f); } if (mat.HasProperty("_Cutoff")) { float num = mat.GetFloat("_Cutoff"); if (num <= 0f) { num = 0.5f; } mat.SetFloat("_Cutoff", num); } try { mat.EnableKeyword("_ALPHATEST_ON"); } catch { } } } [HarmonyPatch(typeof(PlayerController))] internal static class ExamplePlayerControllerPatch { [HarmonyPrefix] [HarmonyPatch("Start")] private static void Start_Prefix(PlayerController __instance) { ShoppingCart.Logger.LogDebug((object)$"{__instance} Start Prefix"); } [HarmonyPostfix] [HarmonyPatch("Start")] private static void Start_Postfix(PlayerController __instance) { ShoppingCart.Logger.LogDebug((object)$"{__instance} Start Postfix"); } } [HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "DestroyObject")] internal static class ImpactDestroyPatch { private const float FALL_OUT_Y = -50f; private static float _nextBlockedLogTime; private static bool _missingSpawnPointLogged; [HarmonyPrefix] private static bool Prefix(PhysGrabObjectImpactDetector __instance) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_00c6: 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_0099: Unknown result type (might be due to invalid IL or missing references) if (!ShopCartSpawner.IsModCart(((Component)__instance).gameObject)) { return true; } if (!SemiFunc.IsMasterClientOrSingleplayer()) { return false; } if (((Component)__instance).transform.position.y < -50f) { if ((Object)(object)TruckSafetySpawnPoint.instance == (Object)null) { if (!_missingSpawnPointLogged) { _missingSpawnPointLogged = true; ShoppingCart.LogWarning("Mod cart fell out of the map and this level has no TruckSafetySpawnPoint; cannot recover it."); } return false; } PhysGrabObject component = ((Component)__instance).GetComponent<PhysGrabObject>(); if ((Object)(object)component != (Object)null) { if ((Object)(object)component.rb != (Object)null && !component.rb.isKinematic) { component.rb.velocity = Vector3.zero; component.rb.angularVelocity = Vector3.zero; } Transform transform = ((Component)TruckSafetySpawnPoint.instance).transform; component.Teleport(transform.position, transform.rotation); ShoppingCart.LogWarning($"Mod cart fell out of the map; teleported to truck safety spawn {transform.position}"); } return false; } if (Time.unscaledTime >= _nextBlockedLogTime) { _nextBlockedLogTime = Time.unscaledTime + 1f; ShoppingCart.LogInfo("Blocked DestroyObject on the mod cart"); } return false; } } [HarmonyPatch(typeof(ItemAttributes), "ShowInfo")] internal static class ItemAttributesShowInfoPatch { [HarmonyPrefix] private static bool Prefix(ItemAttributes __instance) { if ((Object)(object)__instance == (Object)null) { return false; } return !ShopCartSpawner.IsModCart(((Component)__instance).gameObject); } } [HarmonyPatch(typeof(PhysGrabCart), "CartSteer")] internal static class PhysGrabCartDistancePatch { private static float _nextCartSteerLogTime; private static float _lastA; private static float _lastB; private static float _lastD; private static float _lastTurnMultiplier; private static float _lastTurnMinFactor; private static float _lastTurnMaxClamp; [HarmonyPrefix] private static bool Prefix(PhysGrabCart __instance) { int spawnedCartInstanceID = ShopCartSpawner.SpawnedCartInstanceID; if (spawnedCartInstanceID == 0 || ((Object)((Component)__instance).gameObject).GetInstanceID() != spawnedCartInstanceID) { return true; } CustomCartSteer(__instance); return false; } private static void CustomCartSteer(PhysGrabCart cart) { //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: 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_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0359: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Unknown result type (might be due to invalid IL or missing references) //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_036e: Unknown result type (might be due to invalid IL or missing references) //IL_0373: Unknown result type (might be due to invalid IL or missing references) //IL_0375: Unknown result type (might be due to invalid IL or missing references) //IL_037c: Unknown result type (might be due to invalid IL or missing references) //IL_0381: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Unknown result type (might be due to invalid IL or missing references) //IL_039a: Unknown result type (might be due to invalid IL or missing references) //IL_03a4: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03ca: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Unknown result type (might be due to invalid IL or missing references) //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_03f8: Unknown result type (might be due to invalid IL or missing references) //IL_0403: Unknown result type (might be due to invalid IL or missing references) //IL_0408: Unknown result type (might be due to invalid IL or missing references) //IL_040d: Unknown result type (might be due to invalid IL or missing references) //IL_0412: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Unknown result type (might be due to invalid IL or missing references) //IL_041b: Unknown result type (might be due to invalid IL or missing references) //IL_0430: Unknown result type (might be due to invalid IL or missing references) //IL_043c: Unknown result type (might be due to invalid IL or missing references) //IL_0441: Unknown result type (might be due to invalid IL or missing references) //IL_0445: Unknown result type (might be due to invalid IL or missing references) //IL_0454: Unknown result type (might be due to invalid IL or missing references) //IL_0459: Unknown result type (might be due to invalid IL or missing references) //IL_045b: Unknown result type (might be due to invalid IL or missing references) //IL_045d: Unknown result type (might be due to invalid IL or missing references) //IL_0462: Unknown result type (might be due to invalid IL or missing references) //IL_0467: Unknown result type (might be due to invalid IL or missing references) //IL_04bd: Unknown result type (might be due to invalid IL or missing references) //IL_04c2: Unknown result type (might be due to invalid IL or missing references) //IL_04c9: Unknown result type (might be due to invalid IL or missing references) //IL_04ce: Unknown result type (might be due to invalid IL or missing references) //IL_04d0: Unknown result type (might be due to invalid IL or missing references) //IL_04d4: Unknown result type (might be due to invalid IL or missing references) //IL_04d9: Unknown result type (might be due to invalid IL or missing references) //IL_04df: Unknown result type (might be due to invalid IL or missing references) //IL_04e4: Unknown result type (might be due to invalid IL or missing references) //IL_04e8: Unknown result type (might be due to invalid IL or missing references) //IL_04f6: Unknown result type (might be due to invalid IL or missing references) //IL_04fd: Unknown result type (might be due to invalid IL or missing references) float num = ShoppingCart.CartWalkDistance.Value; float num2 = ShoppingCart.CartSprintDistance.Value; float num3 = ShoppingCart.CartTurnMultiplier.Value; float num4 = ShoppingCart.CartTurnMinFactor.Value; float num5 = ShoppingCart.CartTurnMaxClamp.Value; if (float.IsNaN(num) || num < 0f) { num = 0f; } if (float.IsNaN(num2) || num2 < 0f) { num2 = 0f; } num = Mathf.Clamp(num, 0.7f, 4f); num2 = Mathf.Clamp(num2, 0.7f, 4f); if (num3 < 0f) { num3 = 0f; } if (num4 < 0f) { num4 = 0f; } if (num5 < 0f) { num5 = 0f; } PhysGrabObjectGrabArea component = ((Component)cart).GetComponent<PhysGrabObjectGrabArea>(); if ((Object)(object)component == (Object)null) { return; } List<PhysGrabber> listOfAllGrabbers = component.listOfAllGrabbers; foreach (PhysGrabber item in listOfAllGrabbers) { if (Object.op_Implicit((Object)(object)item)) { if (item.isLocal) { TutorialDirector.instance.playerUsedCart = true; } item.OverrideGrabPoint(cart.cartGrabPoint); } } if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } BoxCollider component2 = ((Component)cart.inCart).GetComponent<BoxCollider>(); float num10 = default(float); Vector3 val10 = default(Vector3); foreach (PhysGrabber item2 in listOfAllGrabbers) { if (!Object.op_Implicit((Object)(object)item2)) { continue; } Vector3 val = ((Component)item2.playerAvatar).transform.position + Vector3.up * 0.25f; if (item2.playerAvatar.isTumbling) { val = item2.playerAvatar.tumble.physGrabObject.centerPoint; } if (!(Vector3.Distance(((Collider)component2).ClosestPoint(val), val) >= 0.01f)) { continue; } Rigidbody component3 = ((Component)cart).GetComponent<Rigidbody>(); ((object)cart).GetType().GetMethod("CartMassOverride", BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(cart, new object[1] { 4f }); if ((Object)(object)item2 == (Object)(object)PhysGrabber.instance) { SemiFunc.PhysGrabberLocalChangeAlpha(0.1f); } if (!cart.cartActive && item2.initialPressTimer > 0f) { cart.cartActive = true; } if (!cart.cartActive || (Object)(object)item2 != (Object)(object)listOfAllGrabbers[0]) { break; } cart.cartBeingPulled = true; item2.OverridePhysGrabForcesDisable(0.1f); float num6 = num; float num7 = num2; if (cart.isSmallCart) { num6 = 1.75f; num7 = 2f; } Vector3 val2 = PlayerController.instance.rb.velocity; if (!item2.isLocal) { val2 = item2.playerAvatar.rbVelocityRaw; } bool flag = Vector3.Dot(val2, ((Component)cart).transform.forward) > 0f; float num8 = ((item2.playerAvatar.isSprinting && flag) ? num7 : num6); Vector3 val3 = ((Component)item2).transform.rotation * Vector3.back; Vector3 val4 = ((Component)item2.playerAvatar).transform.position - val3 * num8; float num9 = Mathf.Clamp(Vector3.Distance(((Component)cart).transform.position, val4 / 1f), 0f, 1f); Vector3 val5 = val4 - ((Component)cart).transform.position; Vector3 val6 = ((Vector3)(ref val5)).normalized * 5f * num9; val6 = Vector3.ClampMagnitude(val6, 5f); float y = component3.velocity.y; component3.velocity = Vector3.MoveTowards(component3.velocity, val6, num9 * 2f); component3.velocity = new Vector3(component3.velocity.x, y, component3.velocity.z); component3.velocity = Vector3.ClampMagnitude(component3.velocity, 5f); Quaternion val7 = Quaternion.LookRotation(((Component)item2).transform.position - ((Component)cart).transform.position, Vector3.up); Quaternion val8 = Quaternion.Euler(0f, ((Quaternion)(ref val7)).eulerAngles.y + 180f, 0f); val7 = component3.rotation; Quaternion val9 = Quaternion.Euler(0f, ((Quaternion)(ref val7)).eulerAngles.y, 0f); val7 = val8 * Quaternion.Inverse(val9); ((Quaternion)(ref val7)).ToAngleAxis(ref num10, ref val10); if (num10 > 180f) { num10 -= 360f; } float num11 = Mathf.Clamp(Mathf.Abs(num10) / 180f, num4, 1f) * num3; num11 = Mathf.Clamp(num11, 0f, num5); Vector3 val11 = MathF.PI / 180f * num10 * ((Vector3)(ref val10)).normalized * num11; val11 = Vector3.ClampMagnitude(val11, num5); component3.angularVelocity = Vector3.MoveTowards(component3.angularVelocity, val11, num11); component3.angularVelocity = Vector3.ClampMagnitude(component3.angularVelocity, num5); if (ShoppingCart.CartSteerDebugLogging.Value && Time.unscaledTime >= _nextCartSteerLogTime) { if (Mathf.Abs(_lastA - num6) > 0.001f || Mathf.Abs(_lastB - num7) > 0.001f || Mathf.Abs(_lastD - num8) > 0.001f || Mathf.Abs(_lastTurnMultiplier - num3) > 0.001f || Mathf.Abs(_lastTurnMinFactor - num4) > 0.001f || Mathf.Abs(_lastTurnMaxClamp - num5) > 0.001f) { ShoppingCart.LogInfo($"[CartSteer] dist walk={num6:F2} sprint={num7:F2} active={num8:F2} | turn mult={num3:F1} min={num4:F2} clamp={num5:F1}"); _lastA = num6; _lastB = num7; _lastD = num8; _lastTurnMultiplier = num3; _lastTurnMinFactor = num4; _lastTurnMaxClamp = num5; } _nextCartSteerLogTime = Time.unscaledTime + 1f; } } } } [HarmonyPatch(typeof(PhysGrabCart), "ObjectsInCart")] internal static class PhysGrabCartShopTotalPatch { private sealed class CartScanState { public readonly Dictionary<int, float> LastSeenTime = new Dictionary<int, float>(); public readonly Dictionary<int, int> LastValueDollars = new Dictionary<int, int>(); } private static readonly FieldRef<PhysGrabCart, Transform>? InCartRef; private static readonly FieldRef<PhysGrabCart, int>? HaulCurrentRef; private static readonly FieldRef<PhysGrabCart, float>? ObjectInCartCheckTimerRef; private static readonly FieldRef<PhysGrabCart, List<PhysGrabObject>>? ItemsInCartRef; private static readonly FieldRef<PhysGrabCart, int>? ItemsInCartCountRef; private static readonly bool Disabled; private static float _nextDebugTime; private const float SCAN_CULL_DISTANCE = 12f; private const float SCAN_INTERVAL_SECONDS = 0.5f; private const int SHOP_ITEM_K_TO_DOLLARS = 1000; private const float MISSING_GRACE_SECONDS = 1f; private static readonly Dictionary<int, CartScanState> StateByCart; private static readonly HashSet<int> AdoptedCarts; private static readonly HashSet<int> AttachFailed; private static readonly HashSet<int> ScreenFailed; static PhysGrabCartShopTotalPatch() { StateByCart = new Dictionary<int, CartScanState>(); AdoptedCarts = new HashSet<int>(); AttachFailed = new HashSet<int>(); ScreenFailed = new HashSet<int>(); try { InCartRef = AccessTools.FieldRefAccess<PhysGrabCart, Transform>("inCart"); HaulCurrentRef = AccessTools.FieldRefAccess<PhysGrabCart, int>("haulCurrent"); ObjectInCartCheckTimerRef = AccessTools.FieldRefAccess<PhysGrabCart, float>("objectInCartCheckTimer"); ItemsInCartRef = AccessTools.FieldRefAccess<PhysGrabCart, List<PhysGrabObject>>("itemsInCart"); ItemsInCartCountRef = AccessTools.FieldRefAccess<PhysGrabCart, int>("itemsInCartCount"); } catch (Exception ex) { Disabled = true; ShoppingCart.LogError("PhysGrabCartShopTotalPatch disabled, PhysGrabCart layout changed: " + ex.GetType().Name + ": " + ex.Message); } } internal static void ResetState() { StateByCart.Clear(); AdoptedCarts.Clear(); AttachFailed.Clear(); ScreenFailed.Clear(); } [HarmonyPrefix] private static bool Prefix(PhysGrabCart __instance) { //IL_00a8: 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_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: 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) //IL_0177: 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_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) if (Disabled) { return true; } int instanceID = ((Object)__instance).GetInstanceID(); if ((Object)(object)__instance.valueScreen == (Object)null) { if (!ShopCartSpawner.IsModCart(((Component)__instance).gameObject)) { return true; } if (AttachFailed.Contains(instanceID)) { return false; } if (!TryAttachValueScreen(__instance)) { AttachFailed.Add(instanceID); return false; } AdoptedCarts.Add(instanceID); ShopCartSpawner.ApplyModCartProtection(((Component)__instance).gameObject); } if (!AdoptedCarts.Contains(instanceID)) { return true; } if (!SemiFunc.RunIsShop()) { return true; } if (!StateByCart.TryGetValue(instanceID, out CartScanState value)) { value = new CartScanState(); StateByCart[instanceID] = value; } if (SemiFunc.PlayerNearestDistance(((Component)__instance).transform.position) > 12f) { return false; } Transform val = InCartRef.Invoke(__instance); if ((Object)(object)val == (Object)null) { if (!ScreenFailed.Contains(instanceID)) { ScreenFailed.Add(instanceID); ShoppingCart.LogError("PhysGrabCartShopTotalPatch: '" + ((Object)((Component)__instance).gameObject).name + "' has no 'In Cart' transform; scan disabled for this cart."); } return false; } int num = HaulCurrentRef.Invoke(__instance); float num2 = ObjectInCartCheckTimerRef.Invoke(__instance); if (num2 > 0f) { ObjectInCartCheckTimerRef.Invoke(__instance) = Mathf.Max(0f, num2 - Time.deltaTime); return false; } PhysGrabObject component = ((Component)__instance).GetComponent<PhysGrabObject>(); Vector3 val2 = val.position; Vector3 val3 = val.localScale / 2f; Quaternion rotation = val.rotation; BoxCollider component2 = ((Component)val).GetComponent<BoxCollider>(); if ((Object)(object)component2 != (Object)null) { val2 = ((Component)component2).transform.TransformPoint(component2.center); Vector3 lossyScale = ((Component)component2).transform.lossyScale; ((Vector3)(ref lossyScale))..ctor(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.y), Mathf.Abs(lossyScale.z)); val3 = Vector3.Scale(component2.size * 0.5f, lossyScale); rotation = ((Component)component2).transform.rotation; } Collider[] array = Physics.OverlapBox(val2, val3, rotation, -1, (QueryTriggerInteraction)2); HashSet<PhysGrabObject> hashSet = new HashSet<PhysGrabObject>(); HashSet<ItemAttributes> hashSet2 = new HashSet<ItemAttributes>(); List<PhysGrabObject> list = ItemsInCartRef.Invoke(__instance); list?.Clear(); int num3 = 0; float unscaledTime = Time.unscaledTime; HashSet<int> hashSet3 = new HashSet<int>(); int num4 = 0; int num5 = 0; int num6 = 0; Collider[] array2 = array; foreach (Collider val4 in array2) { if ((Object)(object)val4 == (Object)null || (Object)(object)((Component)val4).GetComponentInParent<PlayerAvatar>() != (Object)null || (Object)(object)((Component)val4).GetComponentInParent<PlayerTumble>() != (Object)null) { continue; } PhysGrabObject componentInParent = ((Component)val4).GetComponentInParent<PhysGrabObject>(); ItemAttributes val5 = null; ValuableObject val6 = null; if ((Object)(object)componentInParent != (Object)null) { val5 = ((Component)componentInParent).GetComponent<ItemAttributes>(); if ((Object)(object)val5 == (Object)null) { val5 = ((Component)componentInParent).GetComponentInChildren<ItemAttributes>(true); } val6 = ((Component)componentInParent).GetComponent<ValuableObject>(); if ((Object)(object)val6 == (Object)null) { val6 = ((Component)componentInParent).GetComponentInChildren<ValuableObject>(true); } } else { val5 = ((Component)val4).GetComponentInParent<ItemAttributes>(); val6 = ((Component)val4).GetComponentInParent<ValuableObject>(); } if ((Object)(object)componentInParent != (Object)null && (Object)(object)component != (Object)null && (Object)(object)componentInParent == (Object)(object)component) { continue; } int instanceID2; if ((Object)(object)componentInParent != (Object)null) { if (!hashSet.Add(componentInParent)) { continue; } instanceID2 = ((Object)componentInParent).GetInstanceID(); list?.Add(componentInParent); num3++; } else { if (!((Object)(object)val5 != (Object)null) || hashSet2.Contains(val5)) { continue; } hashSet2.Add(val5); instanceID2 = ((Object)val5).GetInstanceID(); } int? num7 = null; if ((Object)(object)val5 != (Object)null) { num7 = Mathf.Max(0, val5.value) * 1000; num4++; } else if ((Object)(object)val6 != (Object)null) { num7 = Mathf.RoundToInt(val6.dollarValueCurrent); num5++; } if (num7.HasValue) { value.LastSeenTime[instanceID2] = unscaledTime; value.LastValueDollars[instanceID2] = num7.Value; hashSet3.Add(instanceID2); if (ShoppingCart.CartValueDebugLogging.Value && Time.unscaledTime >= _nextDebugTime && num6 < 12) { string text = LayerMask.LayerToName(((Component)val4).gameObject.layer); string text2 = (((Object)(object)componentInParent != (Object)null) ? ((Object)((Component)componentInParent).gameObject).name : "<null>"); string text3 = (((Object)(object)val5 != (Object)null) ? ((Object)((Component)val5).gameObject).name : "<null>"); int num8 = (((Object)(object)val5 != (Object)null) ? val5.value : 0); bool flag = (Object)(object)val6 != (Object)null; ShoppingCart.LogInfo($"[CartValueHit] col='{((Object)val4).name}' layer={((Component)val4).gameObject.layer}('{text}') trig={val4.isTrigger} pgo='{text2}' ia='{text3}' iaValue={num8} hasVal={flag}"); num6++; } } } int num9 = 0; if (value.LastSeenTime.Count > 0) { List<int> list2 = null; foreach (KeyValuePair<int, float> item in value.LastSeenTime) { int key = item.Key; float value2 = item.Value; if (!hashSet3.Contains(key) && unscaledTime - value2 > 1f) { if (list2 == null) { list2 = new List<int>(); } list2.Add(key); } } if (list2 != null) { foreach (int item2 in list2) { value.LastSeenTime.Remove(item2); value.LastValueDollars.Remove(item2); } } foreach (KeyValuePair<int, int> lastValueDollar in value.LastValueDollars) { num9 += lastValueDollar.Value; } } ItemsInCartCountRef.Invoke(__instance) = num3; ObjectInCartCheckTimerRef.Invoke(__instance) = 0.5f; HaulCurrentRef.Invoke(__instance) = num9; bool flag2 = num9 != num; try { __instance.valueScreen.UpdateValue(num9); } catch (Exception ex) { if (!ScreenFailed.Contains(instanceID)) { ScreenFailed.Add(instanceID); ShoppingCart.LogError("PhysGrabCartShopTotalPatch: ValueScreen.UpdateValue failed on '" + ((Object)((Component)__instance).gameObject).name + "': " + ex.GetType().Name + ": " + ex.Message); } } if (ShoppingCart.CartValueDebugLogging.Value && Time.unscaledTime >= _nextDebugTime) { if (flag2) { ShoppingCart.LogInfo($"[CartValue] Updated haulCurrent from {num} to {num9}. itemsSeen={num4} valuablesSeen={num5} overlap={array.Length} retained={value.LastValueDollars.Count} grace={1f:F1}s"); } _nextDebugTime = Time.unscaledTime + 1f; } return false; } private static bool TryAttachValueScreen(PhysGrabCart cart) { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Expected O, but got Unknown Transform val = ((Component)cart).transform.Find("Screen/Text"); TextMeshPro val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<TextMeshPro>() : null); if ((Object)(object)val2 == (Object)null) { string text = (((Object)(object)val == (Object)null) ? "child missing" : string.Join(",", Array.ConvertAll(((Component)val).GetComponents<Component>(), (Component comp) => ((object)comp).GetType().Name))); ShoppingCart.LogError("PhysGrabCartShopTotalPatch: no TextMeshPro at 'Screen/Text' on '" + ((Object)((Component)cart).gameObject).name + "' (" + text + "); cart screen stays static."); return false; } ValueScreen val3 = ((Component)cart).gameObject.AddComponent<ValueScreen>(); val3.displayText = val2; val3.soundValueIncrease = new Sound { Sounds = Array.Empty<AudioClip>() }; val3.soundValueDecrease = new Sound { Sounds = Array.Empty<AudioClip>() }; ValueScreen val4 = FindSoundDonor(val3); if ((Object)(object)val4 != (Object)null) { Sound.CopySound(val4.soundValueIncrease, val3.soundValueIncrease); Sound.CopySound(val4.soundValueDecrease, val3.soundValueDecrease); val3.soundValueIncrease.Source = null; val3.soundValueDecrease.Source = null; } cart.valueScreen = val3; ShoppingCart.LogInfo("PhysGrabCartShopTotalPatch: attached ValueScreen to '" + ((Object)((Component)cart).gameObject).name + "' (sounds=" + (((Object)(object)val4 != (Object)null) ? "vanilla" : "none") + ")."); return true; } private static ValueScreen? FindSoundDonor(ValueScreen self) { ValueScreen[] array = Resources.FindObjectsOfTypeAll<ValueScreen>(); foreach (ValueScreen val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)self) && val.soundValueIncrease?.Sounds != null && val.soundValueIncrease.Sounds.Length != 0 && val.soundValueDecrease?.Sounds != null && val.soundValueDecrease.Sounds.Length != 0) { return val; } } return null; } } [HarmonyPatch(typeof(ShopManager), "ShopInitialize")] internal static class ShopCartSpawner { [HarmonyPatch(typeof(RunManager), "ChangeLevel")] private static class CleanupCartOnLevelChange { [HarmonyPostfix] private static void Postfix(bool _completedLevel, bool _levelFailed, ChangeLevelType _changeLevelType) { PhysGrabCartShopTotalPatch.ResetState(); if (!SemiFunc.RunIsShop() && (Object)(object)spawnedCart != (Object)null) { if (SemiFunc.IsMultiplayer() && SemiFunc.IsMasterClientOrSingleplayer()) { PhotonNetwork.Destroy(spawnedCart); } else if (!SemiFunc.IsMultiplayer()) { Object.Destroy((Object)(object)spawnedCart); } spawnedCart = null; spawnedCartInstanceID = 0; spawnedCartPhotonViewID = 0; spawnedCartPhysGrabObjectID = 0; } } } private static GameObject? cartPrefab; private static GameObject? spawnedCart; private static int spawnedCartInstanceID = 0; private static int spawnedCartPhotonViewID = 0; private static int spawnedCartPhysGrabObjectID = 0; private const string PrefabReadyRoomPropKey = "Tolga.ShoppingCart.PrefabReady"; private const string PhotonPrefabKey = "Shopping Cart"; private const string ModItemAssetName = "Item Shopping Cart"; private static readonly Vector3 FallbackSpawnPosition = new Vector3(0.49f, 0.97f, -1.28f); private const float SLOT_OCCUPIED_RADIUS = 0.75f; private const float PLACE_GROUND_CLEARANCE = 0.02f; private static readonly float[] PlaceSearchRadii = new float[4] { 0f, 0.75f, 1.5f, 2.5f }; private const int PLACE_SEARCH_DIRECTIONS = 8; internal static GameObject? LatestSpawnedCart => spawnedCart; internal static int SpawnedCartInstanceID => spawnedCartInstanceID; internal static int SpawnedCartPhotonViewID => spawnedCartPhotonViewID; internal static int SpawnedCartPhysGrabObjectID => spawnedCartPhysGrabObjectID; private static int GroundMask => LayerMask.GetMask(new string[2] { "Default", "StaticGrabObject" }); private static int SolidMask => LayerMask.GetMask(new string[5] { "Default", "StaticGrabObject", "PhysGrabObject", "PhysGrabObjectCart", "PhysGrabObjectHinge" }); [HarmonyPostfix] private static void Postfix() { if (SemiFunc.RunIsShop()) { ((MonoBehaviour)ShoppingCart.Instance).StartCoroutine(LoadRegisterAndMaybeSpawnCart()); } } private static IEnumerator LoadRegisterAndMaybeSpawnCart() { if ((Object)(object)spawnedCart != (Object)null) { yield break; } float timeout = 10f; while (timeout > 0f && ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated)) { timeout -= 0.1f; yield return (object)new WaitForSeconds(0.1f); } if ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated) { ShoppingCart.LogError("Level did not finish generating within 10s; not spawning cart."); yield break; } if ((Object)(object)cartPrefab == (Object)null) { string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)ShoppingCart.Instance).Info.Location) ?? string.Empty, "shoppingcart"); AssetBundle bundle = null; if (File.Exists(text)) { AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(text); yield return request; bundle = request.assetBundle; } else { Assembly assembly = typeof(ShopCartSpawner).GetTypeInfo().Assembly; string text2 = null; string[] manifestResourceNames = assembly.GetManifestResourceNames(); foreach (string text3 in manifestResourceNames) { if (text3.EndsWith(".shoppingcart") || text3.EndsWith("shoppingcart")) { text2 = text3; break; } } if (text2 == null) { ShoppingCart.LogError("Cart asset bundle not found at: " + text + " and no embedded resource available"); yield break; } using Stream s = assembly.GetManifestResourceStream(text2); if (s == null) { ShoppingCart.LogError("Embedded resource '" + text2 + "' could not be opened"); yield break; } byte[] array = new byte[s.Length]; int num = s.Read(array, 0, array.Length); if (num != array.Length) { ShoppingCart.LogWarning($"Read {num} bytes from embedded '{text2}' but expected {array.Length}"); } AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(array); yield return request; bundle = request.assetBundle; } if ((Object)(object)bundle == (Object)null) { ShoppingCart.LogError("Failed to load cart asset bundle!"); yield break; } AssetBundleRequest allAssetsRequest = bundle.LoadAllAssetsAsync(); yield return allAssetsRequest; cartPrefab = SelectCartPrefabDeterministically(bundle, allAssetsRequest.allAssets); if ((Object)(object)cartPrefab == (Object)null) { ShoppingCart.LogError("Cart prefab not found in asset bundle!"); yield break; } } RegisterPrefabForPhoton(cartPrefab); if (SemiFunc.IsMultiplayer() && PhotonNetwork.InRoom) { MarkLocalPrefabReady(); } if (!SemiFunc.IsMasterClientOrSingleplayer()) { yield break; } if (SemiFunc.IsMultiplayer() && PhotonNetwork.InRoom) { float waitBudget = 8f; while (waitBudget > 0f && !AreAllPlayersPrefabReady()) { waitBudget -= 0.25f; yield return (object)new WaitForSeconds(0.25f); } if (!AreAllPlayersPrefabReady()) { ShoppingCart.LogWarning("Not all players reported ShoppingCart prefab ready before spawn; spawning anyway."); } } if (!SemiFunc.RunIsShop() || (Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated) { ShoppingCart.LogWarning("Shop left before the cart could spawn; skipping."); } else { SpawnCart(); } } private static GameObject? SelectCartPrefabDeterministically(AssetBundle bundle, Object[] allAssets) { string[] array = new string[4] { "Shopping Cart", "shopping_cart", "ShoppingCart", "Cart" }; foreach (string text in array) { try { GameObject val = bundle.LoadAsset<GameObject>(text); if ((Object)(object)val != (Object)null) { ShoppingCart.LogInfo("Selected cart prefab by name '" + text + "'"); return val; } } catch { } } GameObject[] array2 = (from g in allAssets.OfType<GameObject>() orderby ((Object)g).name select g).ToArray(); if (array2.Length == 0) { return null; } GameObject val2 = null; int num = int.MinValue; GameObject[] array3 = array2; foreach (GameObject val3 in array3) { int num3 = 0; string text2 = ((Object)val3).name.ToLowerInvariant(); if (text2.Contains("cart")) { num3 += 10; } if (text2.Contains("shopping")) { num3 += 5; } if ((Object)(object)val3.transform.Find("In Cart") != (Object)null) { num3 += 25; } if ((Object)(object)val3.transform.Find("Screen") != (Object)null) { num3 += 10; } if ((Object)(object)val3.transform.Find("Screen/Text") != (Object)null) { num3 += 10; } if ((Object)(object)val3.GetComponentInChildren<PhysGrabObject>(true) != (Object)null) { num3 += 10; } if ((Object)(object)val3.GetComponentInChildren<ShopCartComponent>(true) != (Object)null) { num3 += 10; } if ((Object)(object)val3.GetComponentInChildren<PhotonView>(true) != (Object)null) { num3 += 5; } if ((Object)(object)val2 == (Object)null || num3 > num) { val2 = val3; num = num3; } } string text3 = string.Join(", ", array2.Select((GameObject c) => ((Object)c).name)); ShoppingCart.LogInfo("Cart bundle prefab candidates (sorted): " + text3); ShoppingCart.LogInfo(string.Format("Selected cart prefab by heuristic: '{0}' (score={1})", ((val2 != null) ? ((Object)val2).name : null) ?? "<null>", num)); return val2; } private static void RegisterPrefabForPhoton(GameObject prefab) { if (!SemiFunc.IsMultiplayer()) { return; } IPunPrefabPool prefabPool = PhotonNetwork.PrefabPool; DefaultPool val = (DefaultPool)(object)((prefabPool is DefaultPool) ? prefabPool : null); if (val != null) { val.ResourceCache["Shopping Cart"] = prefab; if (!string.IsNullOrEmpty(((Object)prefab).name)) { val.ResourceCache[((Object)prefab).name] = prefab; } } else { ShoppingCart.LogWarning("PhotonNetwork.PrefabPool is '" + (((object)PhotonNetwork.PrefabPool)?.GetType().FullName ?? "<null>") + "', expected DefaultPool; cart may fail to spawn on some clients."); } } private static void MarkLocalPrefabReady() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0017: Expected O, but got Unknown try { Hashtable val = new Hashtable(); ((Dictionary<object, object>)val).Add((object)"Tolga.ShoppingCart.PrefabReady", (object)true); Hashtable val2 = val; PhotonNetwork.LocalPlayer.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null); } catch { } } private static bool AreAllPlayersPrefabReady() { Player[] playerList = PhotonNetwork.PlayerList; foreach (Player val in playerList) { if (val.CustomProperties == null) { return false; } if (!((Dictionary<object, object>)(object)val.CustomProperties).TryGetValue((object)"Tolga.ShoppingCart.PrefabReady", out object value)) { return false; } if (!(value is bool) || !(bool)value) { return false; } } return true; } private static Camera? GetBestCamera() { if ((Object)(object)Camera.main != (Object)null && ((Behaviour)Camera.main).isActiveAndEnabled) { return Camera.main; } Camera[] array = Object.FindObjectsOfType<Camera>(); foreach (Camera val in array) { if (((Behaviour)val).isActiveAndEnabled && ((Component)val).CompareTag("MainCamera")) { return val; } } array = Object.FindObjectsOfType<Camera>(); foreach (Camera val2 in array) { if (((Behaviour)val2).isActiveAndEnabled) { return val2; } } ShoppingCart.LogWarning("No active camera found for cart spawn"); return null; } private static bool TryGetCameraSpawn(out Vector3 spawnPos, out Quaternion spawnRot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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) //IL_0032: 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_003e: 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) //IL_004d: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) spawnPos = Vector3.zero; spawnRot = Quaternion.identity; Camera bestCamera = GetBestCamera(); if ((Object)(object)bestCamera == (Object)null) { return false; } Vector3 position = ((Component)bestCamera).transform.position; Vector3 forward = ((Component)bestCamera).transform.forward; float num = 2f; float num2 = 3f; RaycastHit val = default(RaycastHit); if (Physics.Raycast(position, forward, ref val, num2, -1, (QueryTriggerInteraction)1)) { num = Mathf.Clamp(((RaycastHit)(ref val)).distance - 0.4f, 0.8f, 2f); ShoppingCart.LogInfo($"F8: Raycast hit '{((Object)((RaycastHit)(ref val)).collider).name}' at {((RaycastHit)(ref val)).distance:F2}m, placing at {num:F2}m"); } spawnPos = position + forward * num; spawnRot = Quaternion.Euler(0f, ((Component)bestCamera).transform.eulerAngles.y, 0f); ShoppingCart.LogInfo($"Spawned cart in air at: {spawnPos} (2m away, let it fall naturally)"); return true; } private static void SpawnCart() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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) if ((Object)(object)cartPrefab == (Object)null) { return; } string text = "Shopping Cart"; GetDesiredSpawn(out var position, out var rotation); ShoppingCart.LogInfo($"Spawning cart near {position} (rotation {((Quaternion)(ref rotation)).eulerAngles}); free-space placement runs next frame"); if (SemiFunc.IsMultiplayer()) { RegisterPrefabForPhoton(cartPrefab); spawnedCart = PhotonNetwork.InstantiateRoomObject(text, position, rotation, (byte)0, (object[])null); } else { spawnedCart = Object.Instantiate<GameObject>(cartPrefab, position, rotation); } if ((Object)(object)spawnedCart == (Object)null) { ShoppingCart.LogError("Failed to spawn cart! Instantiate returned null (prefab key unresolved?)."); return; } if ((Object)(object)LevelGenerator.Instance != (Object)null && (Object)(object)LevelGenerator.Instance.ItemParent != (Object)null) { spawnedCart.transform.parent = LevelGenerator.Instance.ItemParent.transform; } spawnedCartInstanceID = ((Object)spawnedCart).GetInstanceID(); PhotonView component = spawnedCart.GetComponent<PhotonView>(); spawnedCartPhotonViewID = (((Object)(object)component != (Object)null) ? component.ViewID : 0); PhysGrabObject componentInChildren = spawnedCart.GetComponentInChildren<PhysGrabObject>(true); spawnedCartPhysGrabObjectID = (((Object)(object)componentInChildren != (Object)null) ? ((Object)componentInChildren).GetInstanceID() : 0); DisableVanillaCostUI(spawnedCart); ApplyModCartProtection(spawnedCart); try { ShopCartComponent componentInChildren2 = spawnedCart.GetComponentInChildren<ShopCartComponent>(true); int num = 0; MonoBehaviour[] componentsInChildren = spawnedCart.GetComponentsInChildren<MonoBehaviour>(true); for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] == (Object)null) { num++; } } string text2 = "<no ShopCartComponent>"; string text3 = "<no ShopCartComponent>"; if ((Object)(object)componentInChildren2 != (Object)null) { Transform val = ((Component)componentInChildren2).transform.Find("In Cart"); Collider val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Collider>() : null); text2 = (((Object)(object)val2 != (Object)null) ? $"In Cart collider={((object)val2).GetType().Name} isTrigger={val2.isTrigger}" : "In Cart collider=<null>"); Transform val3 = ((Component)componentInChildren2).transform.Find("Screen/Text"); TextMeshPro val4 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).GetComponent<TextMeshPro>() : null); text3 = (((Object)(object)val4 != (Object)null) ? ("Screen TMP='" + ((Object)val4).name + "'") : "Screen TMP=<null>"); } ShoppingCart.LogInfo($"Spawn sanity: ShopCartComponent={(Object)(object)componentInChildren2 != (Object)null}, {text2}, {text3}, missingMonoBehaviours={num}"); ShoppingCart.LogInfo($"Spawn sanity IDs: cartInst={spawnedCartInstanceID}, cartPv={spawnedCartPhotonViewID}, cartPhysGrabObjectInst={spawnedCartPhysGrabObjectID}"); } catch (Exception ex) { ShoppingCart.LogWarning("Spawn sanity failed: " + ex.GetType().Name + ": " + ex.Message); } InitializeCart(spawnedCart); ((MonoBehaviour)ShoppingCart.Instance).StartCoroutine(FixCartPositionAfterSpawn(spawnedCart, position, rotation)); ShoppingCart.LogInfo($"Cart instantiated (InstanceID: {spawnedCartInstanceID}). Will position via gravity + bounds-fix."); } internal static bool IsModCart(GameObject? go) { if ((Object)(object)go == (Object)null) { return false; } PhysGrabObject componentInParent = go.GetComponentInParent<PhysGrabObject>(); GameObject val = (GameObject)(((Object)(object)componentInParent != (Object)null) ? ((object)((Component)componentInParent).gameObject) : ((object)go)); if (spawnedCartInstanceID != 0 && ((Object)val).GetInstanceID() == spawnedCartInstanceID) { return true; } ItemAttributes component = val.GetComponent<ItemAttributes>(); if ((Object)(object)component != (Object)null && (Object)(object)component.item != (Object)null) { return ((Object)component.item).name == "Item Shopping Cart"; } return false; } internal static void ApplyModCartProtection(GameObject cart) { FixWheels(cart); PhysGrabObjectImpactDetector component = cart.GetComponent<PhysGrabObjectImpactDetector>(); if ((Object)(object)component == (Object)null) { ShoppingCart.LogWarning("No PhysGrabObjectImpactDetector on '" + ((Object)cart).name + "'"); } else if (!component.playerHurtDisable) { component.playerHurtDisable = true; ShoppingCart.LogInfo("Set playerHurtDisable on '" + ((Object)cart).name + "' to match vanilla carts"); } } private static void FixWheels(GameObject cart) { Rigidbody component = cart.GetComponent<Rigidbody>(); int num = LayerMask.NameToLayer("PhysGrabObject"); int num2 = ((num >= 0) ? num : cart.layer); int num3 = 0; Rigidbody[] componentsInChildren = cart.GetComponentsInChildren<Rigidbody>(true); foreach (Rigidbody val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)component)) { GameObject gameObject = ((Component)val).gameObject; val.isKinematic = true; val.detectCollisions = false; Collider[] components = gameObject.GetComponents<Collider>(); for (int j = 0; j < components.Length; j++) { Object.Destroy((Object)(object)components[j]); } Object.Destroy((Object)(object)val); gameObject.layer = num2; RestorePrefabLocalPose(cart, gameObject); num3++; } } if (num3 > 0) { ShoppingCart.LogInfo($"Fixed {num3} wheel(s) on '{((Object)cart).name}': removed child rigidbodies/colliders, layer -> {LayerMask.LayerToName(num2)}"); } } private static void RestorePrefabLocalPose(GameObject cart, GameObject child) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)cartPrefab == (Object)null)) { Transform val = cartPrefab.transform.Find(((Object)child).name); if (!((Object)(object)val == (Object)null)) { child.transform.localPosition = val.localPosition; child.transform.localRotation = val.localRotation; } } } private static void GetDesiredSpawn(out Vector3 position, out Quaternion rotation) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_0045: 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) //IL_0054: 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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: 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_0225: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) Quaternion val = Quaternion.identity; ItemAttributes val2 = (((Object)(object)cartPrefab != (Object)null) ? cartPrefab.GetComponent<ItemAttributes>() : null); if ((Object)(object)val2 != (Object)null && (Object)(object)val2.item != (Object)null) { Quaternion spawnRotationOffset = val2.item.spawnRotationOffset; val = ((spawnRotationOffset.x * spawnRotationOffset.x + spawnRotationOffset.y * spawnRotationOffset.y + spawnRotationOffset.z * spawnRotationOffset.z + spawnRotationOffset.w * spawnRotationOffset.w < 1E-06f) ? Quaternion.identity : Quaternion.Normalize(spawnRotationOffset)); } int mask = LayerMask.GetMask(new string[3] { "PhysGrabObject", "PhysGrabObjectCart", "PhysGrabObjectHinge" }); foreach (ShopSlotCapturePatch.Slot cartSlot in ShopSlotCapturePatch.CartSlots) { Collider[] array = Physics.OverlapSphere(cartSlot.Position + Vector3.up * 0.5f, 0.75f, mask, (QueryTriggerInteraction)1); bool flag = false; Collider[] array2 = array; foreach (Collider val3 in array2) { if ((Object)(object)val3 != (Object)null && (Object)(object)((Component)val3).GetComponentInParent<PhysGrabObject>() != (Object)null) { flag = true; break; } } if (!flag) { position = cartSlot.Position; rotation = cartSlot.Rotation * val; ShoppingCart.LogInfo($"Using free shop cart slot at {position}"); return; } } if (ShopSlotCapturePatch.CartSlots.Count > 0) { ShoppingCart.LogInfo($"All {ShopSlotCapturePatch.CartSlots.Count} shop cart slot(s) occupied; spawning near the player"); } else { ShoppingCart.LogWarning("No shop cart slots captured on this map; spawning near the player"); } PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance != (Object)null) { Vector3 forward = ((Component)instance).transform.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.001f) { forward = Vector3.forward; } ((Vector3)(ref forward)).Normalize(); position = ((Component)instance).transform.position + forward * 2f + Vector3.up * 0.5f; rotation = Quaternion.LookRotation(-forward, Vector3.up); } else { ShoppingCart.LogWarning("No local PlayerAvatar at spawn time; using fallback coordinates"); position = FallbackSpawnPosition; rotation = Quaternion.identity; } } private static IEnumerator FixCartPositionAfterSpawn(GameObject cart, Vector3 targetPos, Quaternion targetRot) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) yield return null; if (!((Object)(object)cart == (Object)null) && !((Object)(object)LevelGenerator.Instance == (Object)null) && LevelGenerator.Instance.Generated) { PlaceCartSafely(cart, targetPos, targetRot, "spawn"); } } internal static void PlaceCartSafely(GameObject cart, Vector3 desired, Quaternion rotation, string reason) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_00c9: 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_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: 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) //IL_0131: 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_014c: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) PhysGrabObject component = cart.GetComponent<PhysGrabObject>(); if ((Object)(object)component == (Object)null) { ShoppingCart.LogWarning($"PlaceCartSafely({reason}): no PhysGrabObject on cart; leaving it at {desired}"); cart.transform.SetPositionAndRotation(desired, rotation); return; } Vector3 halfExtents = component.boundingBox * 0.45f; Vector3 val = rotation * component.midPointOffset; float num = component.midPointOffset.y - component.boundingBox.y * 0.5f; Vector3? val2 = null; int num2 = 0; float[] placeSearchRadii = PlaceSearchRadii; RaycastHit val4 = default(RaycastHit); Vector3 val5 = default(Vector3); foreach (float num3 in placeSearchRadii) { int num4 = ((num3 <= 0f) ? 1 : 8); for (int j = 0; j < num4; j++) { if (val2.HasValue) { break; } float num5 = 360f * (float)j / (float)num4; Vector3 val3 = desired + Quaternion.Euler(0f, num5, 0f) * (Vector3.forward * num3); num2++; if ((!(num3 > 0f) || !Physics.Linecast(desired, val3, GroundMask, (QueryTriggerInteraction)1)) && Physics.Raycast(val3 + Vector3.up * 1f, Vector3.down, ref val4, 6f, GroundMask, (QueryTriggerInteraction)1)) { ((Vector3)(ref val5))..ctor(val3.x, ((RaycastHit)(ref val4)).point.y + 0.02f - num, val3.z); if (!OverlapsForeignSolid(cart.transform, val5 + val, halfExtents, rotation)) { val2 = val5; } } } if (val2.HasValue) { break; } } if (!val2.HasValue) { ShoppingCart.LogWarning($"PlaceCartSafely({reason}): no free spot among {num2} candidates around {desired}; using it anyway"); val2 = desired; } else { ShoppingCart.LogInfo($"PlaceCartSafely({reason}): placed at {val2.Value} after {num2} candidate(s), desired {desired}"); } component.Teleport(val2.Value, rotation); } private static bool OverlapsForeignSolid(Transform self, Vector3 center, Vector3 halfExtents, Quaternion rotation) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) Collider[] array = Physics.OverlapBox(center, halfExtents, rotation, SolidMask, (QueryTriggerInteraction)1); foreach (Collider val in array) { if (!((Object)(object)val == (Object)null) && !val.isTrigger && !((Component)val).transform.IsChildOf(self) && !((Object)(object)((Component)val).GetComponentInParent<PlayerController>() != (Object)null) && !((Object)(object)((Component)val).GetComponentInParent<PlayerAvatar>() != (Object)null)) { return true; } } return false; } private static void InitializeCart(GameObject cart) { } private static void DisableVanillaCostUI(GameObject cart) { int num = 0; MonoBehaviour[] componentsInChildren = cart.GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { switch (((object)val).GetType().Name) { case "WorldSpaceUIValue": case "ShopCostUI": case "CurrencyUI": ((Behaviour)val).enabled = false; num++; break; } } } if (num > 0) { ShoppingCart.LogInfo($"Disabled {num} vanilla cost UI component(s) on spawned cart"); } } } [BepInPlugin("Tolga.ShoppingCart", "ShoppingCart", "1.0.4")] public class ShoppingCart : BaseUnityPlugin { private float _nextMoveTime; internal static ShoppingCart Instance { get; private set; } internal static ManualLogSource Logger => Instance._logger; private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger; internal Harmony? Harmony { get; set; } internal static ConfigEntry<float> CartWalkDistance { get; private set; } internal static ConfigEntry<float> CartSprintDistance { get; private set; } internal static ConfigEntry<float> CartTurnMultiplier { get; private set; } internal static ConfigEntry<float> CartTurnMinFactor { get; private set; } internal static ConfigEntry<float> CartTurnMaxClamp { get; private set; } internal static ConfigEntry<bool> CartDebugLogging { get; private set; } internal static ConfigEntry<bool> CartPriceDebugLogging { get; private set; } internal static ConfigEntry<bool> CartValueDebugLogging { get; private set; } internal static ConfigEntry<bool> CartCostPopupDebugLogging { get; private set; } internal static ConfigEntry<bool> CartSteerDebugLogging { get; private set; } internal static ConfigEntry<bool> CartInfoLogging { get; private set; } internal static ConfigEntry<bool> CartWarningLogging { get; private set; } internal static ConfigEntry<bool> CartErrorLogging { get; private set; } internal static ConfigEntry<KeyboardShortcut> MoveCartKey { get; private set; } internal static bool AnyInfoLoggingEnabled { get { if (!IsEnabled(CartInfoLogging) && !IsEnabled(CartDebugLogging) && !IsEnabled(CartPriceDebugLogging) && !IsEnabled(CartValueDebugLogging) && !IsEnabled(CartCostPopupDebugLogging)) { return IsEnabled(CartSteerDebugLogging); } return true; } } internal static bool AnyWarningLoggingEnabled { get { if (!IsEnabled(CartWarningLogging)) { return IsEnabled(CartDebugLogging); } return true; } } internal static bool AnyErrorLoggingEnabled { get { if (!IsEnabled(CartErrorLogging)) { return IsEnabled(CartDebugLogging); } return true; } } private void Awake() { Instance = this; ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; BindConfig(); LogInfo($"Config: InfoLogging={CartInfoLogging.Value}, WarningLogging={CartWarningLogging.Value}, ErrorLogging={CartErrorLogging.Value}, DebugLogging={CartDebugLogging.Value}, PriceDebugLogging={CartPriceDebugLogging.Value}, ValueDebugLogging={CartValueDebugLogging.Value}, CostPopupDebugLogging={CartCostPopupDebugLogging.Value}, SteerDebugLogging={CartSteerDebugLogging.Value}"); Patch(); LogInfo($"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!"); } private void BindConfig() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_0214: Unknown result type (might be due to invalid IL or missing references) AcceptableValueRange<float> val = new AcceptableValueRange<float>(0.7f, 4f); ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes { Increment = 0.01f }; CartWalkDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Cart", "WalkDistance", 1.1f, new ConfigDescription("How far the cart stays from you while walking.", (AcceptableValueBase)(object)val, new object[1] { configurationManagerAttributes })); CartSprintDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Cart", "SprintDistance", 1.75f, new ConfigDescription("How far the cart stays from you while sprinting (and moving forward with the cart).", (AcceptableValueBase)(object)val, new object[1] { configurationManagerAttributes })); CartTurnMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Cart", "TurnMultiplier", 20f, "Turn speed multiplier used when steering the cart."); CartTurnMinFactor = ((BaseUnityPlugin)this).Config.Bind<float>("Cart", "TurnMinFactor", 0.2f, "Minimum turning factor clamp (vanilla is 0.2). Higher = more responsive at small angles."); CartTurnMaxClamp = ((BaseUnityPlugin)this).Config.Bind<float>("Cart", "TurnMaxClamp", 4f, "Maximum turn clamp used for angular velocity (vanilla is 4). Higher = faster snap turns."); CartDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Cart", "DebugLogging", false, "Enable verbose debug logging for cart patches."); CartPriceDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Cart", "PriceDebugLogging", false, "Enable verbose debug logging for cart price display."); CartValueDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Cart", "ValueDebugLogging", false, "Enable debug logging for cart item scanning and total calculation."); CartCostPopupDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Cart", "CostPopupDebugLogging", false, "Enable debug logging for WorldSpaceUIValue (the '-$...K' popup) suppression."); CartSteerDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Cart", "SteerDebugLogging", false, "Enable debug logging for CartSteer distance/turn calculations."); CartInfoLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Cart", "InfoLogging", false, "Enable informational logs from the mod."); CartWarningLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Cart", "WarningLogging", false, "Enable warning logs from the mod."); CartErrorLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Cart", "ErrorLogging", false, "Enable error logs from the mod."); MoveCartKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Cart", "MoveCartKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Brings the shopping cart to the spot you are looking at (shop only). Set to None to disable."); } private static bool IsEnabled(ConfigEntry<bool>? entry) { return entry?.Value ?? false; } internal static void LogInfo(string message) { if (AnyInfoLoggingEnabled) { Logger.LogInfo((object)message); } } internal static void LogWarning(string message) { if (AnyWarningLoggingEnabled) { Logger.LogWarning((object)message); } } internal static void LogError(string message) { if (AnyErrorLoggingEnabled) { Logger.LogError((object)message); } } internal void Patch() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0025: Expected O, but got Unknown if (Harmony == null) { Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); Harmony val2 = val; Harmony = val; } int num = 0; int num2 = 0; Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(Assembly.GetExecutingAssembly()); foreach (Type type in typesFromAssembly) { if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0) { try { Harmony.CreateClassProcessor(type).Patch(); num++; } catch (Exception arg) { num2++; Logger.LogError((object)$"Harmony patch class {type.FullName} failed to apply: {arg}"); } } } if (num2 > 0) { Logger.LogError((object)$"Harmony: {num} patch class(es) applied, {num2} FAILED. The mod is partially active."); } } internal void Unpatch() { Harmony? harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void Update() { //IL_001b: 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)RunManager.instance == (Object)null) && SemiFunc.RunIsShop()) { KeyboardShortcut value = MoveCartKey.Value; if (((KeyboardShortcut)(ref value)).IsDown() && Time.time >= _nextMoveTime) { _nextMoveTime = Time.time + 0.25f; LogInfo("MoveCartKey pressed - triggering MoveCartToLook()"); MoveCartToLook(); } } } private Camera? GetBestCamera() { if (Object.op_Implicit((Object)(object)Camera.main) && ((Behaviour)Camera.main).isActiveAndEnabled) { return Camera.main; } Camera val = ((IEnumerable<Camera>)Object.FindObjectsOfType<Camera>()).FirstOrDefault((Func<Camera, bool>)((Camera c) => ((Behaviour)c).isActiveAndEnabled && ((Component)c).CompareTag("MainCamera"))); if ((Object)(object)val != (Object)null) { return val; } Camera? obj = ((IEnumerable<Camera>)Object.FindObjectsOfType<Camera>()).FirstOrDefault((Func<Camera, bool>)((Camera c) => ((Behaviour)c).isActiveAndEnabled)); if ((Object)(object)obj == (Object)null) { LogWarning("No active camera found!"); } return obj; } private void MoveCartToLook() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: 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) Camera bestCamera = GetBestCamera(); if ((Object)(object)bestCamera == (Object)null) { LogWarning("F8: No active camera found"); return; } GameObject val = ShopCartSpawner.LatestSpawnedCart; if ((Object)(object)val == (Object)null) { val = GameObject.Find("Shopping Cart(Clone)") ?? GameObject.Find("shopping_cart(Clone)"); } if ((Object)(object)val == (Object)null) { LogWarning("F8 pressed but shopping_cart(Clone) not found. Cart may not have spawned yet."); return; } Vector3 position = ((Component)bestCamera).transform.position; Vector3 forward = ((Component)bestCamera).transform.forward; float num = 2f; float num2 = 3f; RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(position, forward, ref val2, num2, -1, (QueryTriggerInteraction)1)) { num = Mathf.Clamp(((RaycastHit)(ref val2)).distance - 0.4f, 0.8f, 2f); LogInfo($"F8: Raycast hit '{((Object)((RaycastHit)(ref val2)).collider).name}' at {((RaycastHit)(ref val2)).distance:F2}m, placing at {num:F2}m"); } Vector3 desired = position + forward * num; ShopCartSpawner.PlaceCartSafely(val, desired, Quaternion.Euler(0f, ((Component)bestCamera).transform.eulerAngles.y, 0f), "F8"); } } internal sealed class ConfigurationManagerAttributes { public float? Increment { get; set; } } [HarmonyPatch(typeof(ShopManager), "ShoppingListItemAdd")] internal static class ShoppingListPatch { [HarmonyPrefix] private static bool Prefix(ItemAttributes item) { if ((Object)(object)item == (Object)null || !ShopCartSpawner.IsModCart(((Component)item).gameObject)) { return true; } ShoppingCart.LogInfo("Mod cart entered the extraction area; not adding it to the shopping list"); return false; } } [HarmonyPatch(typeof(PunManager), "ShopPopulateItemVolumes")] internal static class ShopSlotCapturePatch { internal readonly struct Slot { public readonly Vector3 Position; public readonly Quaternion Rotation; public Slot(Vector3 position, Quaternion rotation) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_0009: Unknown result type (might be due to invalid IL or missing references) Position = position; Rotation = rotation; } } internal const itemVolume CartVolumeType = (itemVolume)3; internal static readonly List<Slot> CartSlots = new List<Slot>(); [HarmonyPrefix] private static void Prefix() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Invalid comparison between Unknown and I4 //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) try { CartSlots.Clear(); if ((Object)(object)ShopManager.instance == (Object)null || ShopManager.instance.itemVolumes == null) { return; } foreach (ItemVolume itemVolume in ShopManager.instance.itemVolumes) { if (!((Object)(object)itemVolume == (Object)null) && (int)itemVolume.itemVolume == 3 && (int)itemVolume.itemSecretShopType == 0) { CartSlots.Add(new Slot(((Component)itemVolume).transform.position, ((Component)itemVolume).transform.rotation)); } } ShoppingCart.LogInfo($"Captured {CartSlots.Count} shop cart slot(s) of type {(object)(itemVolume)3}"); } catch (Exception ex) { CartSlots.Clear(); ShoppingCart.LogError("ShopSlotCapturePatch failed: " + ex.GetType().Name + ": " + ex.Message); } } } [HarmonyPatch(typeof(WorldSpaceUIValue), "Show", new Type[] { typeof(PhysGrabObject), typeof(int), typeof(bool), typeof(Vector3) })] internal static class WorldSpaceUIValuePatch { private static float _nextCostPopupDebugTime; [HarmonyPrefix] private static bool Prefix(PhysGrabObject _grabObject, int _value, bool _cost) { if ((Object)(object)_grabObject == (Object)null) { return true; } bool flag = ShopCartSpawner.IsModCart(((Component)_grabObject).gameObject); if (ShoppingCart.CartCostPopupDebugLogging.Value && Time.unscaledTime >= _nextCostPopupDebugTime) { ShoppingCart.LogInfo(string.Format("[CostPopup] Show(value={0}, cost={1}) grab='{2}' => {3}", _value, _cost, ((Object)((Component)_grabObject).gameObject).name, flag ? "BLOCK" : "ALLOW")); _nextCostPopupDebugTime = Time.unscaledTime + 1f; } return !flag; } } }