Decompiled source of CartTrainLocomotives v0.3.4

plugins/CartTrainLocomotives/CartTrainLocomotives.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CartTrainLocomotives")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("CartTrainLocomotives")]
[assembly: AssemblyTitle("CartTrainLocomotives")]
[assembly: AssemblyVersion("1.0.0.0")]
[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;
		}
	}
}
namespace CartTrainLocomotives
{
	[BepInPlugin("CandyKane.CartTrainLocomotives", "CartTrain Locomotives", "0.3.4")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "CandyKane.CartTrainLocomotives";

		public const string PluginName = "CartTrain Locomotives";

		public const string PluginVersion = "0.3.4";

		internal static ManualLogSource Log;

		internal static ConfigEntry<float> SnapDistance;

		internal static ConfigEntry<float> TowGap;

		internal static ConfigEntry<float> GrabStrength;

		internal static ConfigEntry<float> GrabDamping;

		internal static ConfigEntry<float> MaxGrabAcceleration;

		internal static ConfigEntry<float> MaxVerticalAcceleration;

		internal static ConfigEntry<float> CenteringStrength;

		internal static ConfigEntry<float> CenteringDamping;

		internal static ConfigEntry<float> MaxCenteringAcceleration;

		internal static ConfigEntry<float> YawAlignmentStrength;

		internal static ConfigEntry<float> MaxYawAcceleration;

		internal static ConfigEntry<float> PathSampleSpacing;

		internal static ConfigEntry<float> PathGuideStrength;

		internal static ConfigEntry<float> MaxPathGuideVelocityChange;

		internal static ConfigEntry<float> PoweredCatchupGain;

		internal static ConfigEntry<float> MaxPoweredCatchupSpeed;

		internal static ConfigEntry<float> MaxPoweredVelocityChange;

		internal static ConfigEntry<bool> IgnoreCoupledCollisions;

		internal static ConfigEntry<float> VehicleRearPadding;

		internal static ConfigEntry<float> CartEndPadding;

		internal static ConfigEntry<float> MaxSnapAngle;

		internal static ConfigEntry<float> EmergencyBreakawayDistance;

		internal static ConfigEntry<float> CouplingGraceSeconds;

		internal static ConfigEntry<KeyCode> DetachKey;

		internal static ConfigEntry<bool> DebugLogging;

		internal static ConfigEntry<bool> ShowConnectionBeam;

		internal static ConfigEntry<float> BeamWidth;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_03ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d8: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			SnapDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Coupling", "ManualAttachDistance", 1.5f, "Maximum rear-of-vehicle to nearest cart-end distance when Shift+X is pressed while holding either object.");
			TowGap = ((BaseUnityPlugin)this).Config.Bind<float>("Coupling", "TowGap", 0.18f, "Extra gap between the vehicle rear grab point and the cart handle/end.");
			MaxSnapAngle = ((BaseUnityPlugin)this).Config.Bind<float>("Coupling", "MaximumSnapAngle", 85f, "Maximum alignment error allowed when snapping. Either end of the cart may attach.");
			EmergencyBreakawayDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Coupling", "EmergencyBreakawayDistance", 40f, "Safety-only distance at which a coupled cart is released. Normal driving should never reach this.");
			CouplingGraceSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Coupling", "CouplingGraceSeconds", 2.5f, "Seconds after coupling during which distance-based emergency breakaway is disabled.");
			VehicleRearPadding = ((BaseUnityPlugin)this).Config.Bind<float>("Anchors", "VehicleRearPadding", 0.1f, "Moves the cached vehicle rear anchor farther behind the vehicle.");
			CartEndPadding = ((BaseUnityPlugin)this).Config.Bind<float>("Anchors", "CartEndPadding", 0.08f, "Moves the cached cart coupling end outward.");
			GrabStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "GrabStrength", 18f, "Spring acceleration pulling the cart's selected end toward the vehicle's rear grab point.");
			GrabDamping = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "GrabDamping", 5.5f, "Damping against relative velocity at the virtual hand/cart handle connection.");
			MaxGrabAcceleration = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "MaximumGrabAcceleration", 28f, "Maximum horizontal acceleration applied to the cart by the virtual hand.");
			MaxVerticalAcceleration = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "MaximumVerticalAcceleration", 1.5f, "Maximum vertical correction. Kept intentionally low so the tow point cannot lift the cart or vehicle into ceilings.");
			CenteringStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "BehindCenteringStrength", 20f, "Sideways spring strength that keeps the first cart centered behind the Scout/Hauler instead of swinging into walls.");
			CenteringDamping = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "BehindCenteringDamping", 7f, "Sideways damping used by the behind-centering controller.");
			MaxCenteringAcceleration = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "MaximumCenteringAcceleration", 20f, "Maximum sideways acceleration used to keep the first cart behind the locomotive.");
			YawAlignmentStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "YawAlignmentStrength", 5f, "Gentle yaw-only alignment that encourages the cart to point the same direction as the Scout/Hauler without rigidly locking it.");
			MaxYawAcceleration = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "MaximumYawAcceleration", 8f, "Maximum yaw angular acceleration used by the soft alignment controller.");
			PathSampleSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "PathSampleSpacing", 0.1f, "Distance the locomotive rear must travel before another breadcrumb point is recorded for corner-following.");
			PathGuideStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "PathGuideStrength", 4.5f, "Velocity guidance toward the locomotive's recent rear path. Higher values make the first cart follow corners more tightly.");
			MaxPathGuideVelocityChange = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "MaximumPathGuideVelocityChangePerTick", 0.65f, "Maximum horizontal velocity correction per physics tick used by the breadcrumb path guide.");
			PoweredCatchupGain = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "PoweredCatchupGain", 4f, "Extra forward catch-up speed per meter the first cart falls behind. This assist is mass-independent so heavy cargo cannot anchor the locomotive.");
			MaxPoweredCatchupSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "MaximumPoweredCatchupSpeed", 8f, "Maximum extra horizontal catch-up speed added to the first cart when it lags behind the Scout/Hauler.");
			MaxPoweredVelocityChange = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "MaximumPoweredVelocityChangePerTick", 0.75f, "Maximum horizontal velocity change applied to the first cart each physics tick by the powered tow assist.");
			IgnoreCoupledCollisions = ((BaseUnityPlugin)this).Config.Bind<bool>("Physics", "IgnoreCoupledVehicleCartCollisions", true, "Ignore collisions between the connected Scout/Hauler and first cart while attached, preventing the loaded cart from physically braking or wedging the locomotive.");
			DetachKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "DetachKey", (KeyCode)120, "Detach key. Hold Left Shift while pressing this key near a coupled locomotive/cart. This prevents accidental detaches from normal gameplay inputs.");
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLogging", false, "Enable verbose logging.");
			ShowConnectionBeam = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "ShowConnectionBeam", true, "Show a visible beam between the Scout/Hauler rear tow point and the connected cart end.");
			BeamWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "ConnectionBeamWidth", 0.05f, "Width of the visible tow/hand-hold beam.");
			_harmony = new Harmony("CandyKane.CartTrainLocomotives");
			_harmony.PatchAll();
			((Component)this).gameObject.AddComponent<LocomotiveManager>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"CartTrain Locomotives 0.3.4 loaded. Optimized event-driven discovery enabled.");
		}
	}
	[HarmonyPatch(typeof(PhysGrabCart), "Start")]
	internal static class CartStartPatch
	{
		[HarmonyPostfix]
		private static void Postfix(PhysGrabCart __instance)
		{
			LocomotiveManager.RegisterCart(__instance);
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "Start")]
	internal static class PhysGrabObjectStartPatch
	{
		[HarmonyPostfix]
		private static void Postfix(PhysGrabObject __instance)
		{
			LocomotiveManager.RegisterPotentialVehicle(__instance);
		}
	}
	internal sealed class LocomotiveManager : MonoBehaviour
	{
		private sealed class VehicleState
		{
			private sealed class ColliderPair
			{
				internal readonly Collider A;

				internal readonly Collider B;

				internal ColliderPair(Collider a, Collider b)
				{
					A = a;
					B = b;
				}
			}

			internal readonly PhysGrabObject Vehicle;

			internal readonly Rigidbody Body;

			internal readonly string DisplayName;

			internal readonly float HalfLength;

			internal PhysGrabCart? Cart;

			internal Rigidbody? CartBody;

			internal float CartEndSign = 1f;

			internal float CoupledAt;

			private GameObject? _beamObject;

			private LineRenderer? _beam;

			private static Material? _beamMaterial;

			private readonly List<ColliderPair> _ignoredCollisionPairs = new List<ColliderPair>();

			private readonly List<Vector3> _rearPath = new List<Vector3>();

			internal Vector3 RearAnchor => ((Component)Vehicle).transform.position - ((Component)Vehicle).transform.forward * (HalfLength + Plugin.VehicleRearPadding.Value);

			internal VehicleState(PhysGrabObject vehicle, Rigidbody body, string prefabName)
			{
				Vehicle = vehicle;
				Body = body;
				DisplayName = (prefabName.EndsWith(" Small", StringComparison.Ordinal) ? "Scout" : "Hauler");
				HalfLength = ComputeHalfLength(((Component)vehicle).gameObject, ((Component)vehicle).transform, 0.45f);
			}

			internal void ResetRearPath()
			{
				_rearPath.Clear();
			}

			internal void RecordRearPath(Vector3 rearPoint)
			{
				//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_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0061: Unknown result type (might be due to invalid IL or missing references)
				rearPoint.y = 0f;
				if (_rearPath.Count == 0)
				{
					_rearPath.Add(rearPoint);
				}
				else if (!(Vector3.Distance(_rearPath[_rearPath.Count - 1], rearPoint) < Mathf.Max(0.02f, Plugin.PathSampleSpacing.Value)))
				{
					_rearPath.Add(rearPoint);
					if (_rearPath.Count > 160)
					{
						_rearPath.RemoveAt(0);
					}
				}
			}

			internal void GetRearPathTarget(Vector3 currentRear, Vector3 fallbackForward, float distanceBack, out Vector3 target, out Vector3 tangent)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_0067: 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_0040: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				//IL_0043: Unknown result type (might be due to invalid IL or missing references)
				//IL_0048: Unknown result type (might be due to invalid IL or missing references)
				//IL_004d: 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_0055: Unknown result type (might be due to invalid IL or missing references)
				//IL_0082: Unknown result type (might be due to invalid IL or missing references)
				//IL_0087: 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_008a: Unknown result type (might be due to invalid IL or missing references)
				//IL_008c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0091: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d9: 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_0100: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b2: 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_00b7: 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_00c3: Unknown result type (might be due to invalid IL or missing references)
				//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_010f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0110: Unknown result type (might be due to invalid IL or missing references)
				//IL_0115: Unknown result type (might be due to invalid IL or missing references)
				//IL_011a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0129: Unknown result type (might be due to invalid IL or missing references)
				//IL_012a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0136: Unknown result type (might be due to invalid IL or missing references)
				//IL_0137: Unknown result type (might be due to invalid IL or missing references)
				//IL_0139: Unknown result type (might be due to invalid IL or missing references)
				//IL_013e: 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_014a: Unknown result type (might be due to invalid IL or missing references)
				//IL_014b: Unknown result type (might be due to invalid IL or missing references)
				currentRear.y = 0f;
				fallbackForward = Flat(fallbackForward);
				if (((Vector3)(ref fallbackForward)).sqrMagnitude < 0.0001f)
				{
					fallbackForward = Vector3.forward;
				}
				((Vector3)(ref fallbackForward)).Normalize();
				if (_rearPath.Count < 2)
				{
					target = currentRear - fallbackForward * distanceBack;
					tangent = fallbackForward;
					return;
				}
				float num = Mathf.Max(0.05f, distanceBack);
				Vector3 val = currentRear;
				for (int num2 = _rearPath.Count - 1; num2 >= 0; num2--)
				{
					Vector3 val2 = _rearPath[num2];
					Vector3 val3 = val - val2;
					float magnitude = ((Vector3)(ref val3)).magnitude;
					if (magnitude > 0.0001f)
					{
						if (num <= magnitude)
						{
							float num3 = num / magnitude;
							target = Vector3.Lerp(val, val2, num3);
							tangent = val3 / magnitude;
							return;
						}
						num -= magnitude;
					}
					val = val2;
				}
				Vector3 val4 = _rearPath[0];
				Vector3 val5 = Flat(((_rearPath.Count > 1) ? _rearPath[1] : currentRear) - val4);
				if (((Vector3)(ref val5)).sqrMagnitude < 0.0001f)
				{
					val5 = fallbackForward;
				}
				else
				{
					((Vector3)(ref val5)).Normalize();
				}
				target = val4 - val5 * num;
				tangent = val5;
			}

			internal void EnableCoupledCollisionIgnore(PhysGrabCart cart)
			{
				RestoreCoupledCollisions();
				if (!Plugin.IgnoreCoupledCollisions.Value || (Object)(object)cart == (Object)null)
				{
					return;
				}
				Collider[] componentsInChildren = ((Component)Vehicle).GetComponentsInChildren<Collider>(true);
				Collider[] componentsInChildren2 = ((Component)cart).GetComponentsInChildren<Collider>(true);
				Collider[] array = componentsInChildren;
				foreach (Collider val in array)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					Collider[] array2 = componentsInChildren2;
					foreach (Collider val2 in array2)
					{
						if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val == (Object)(object)val2))
						{
							Physics.IgnoreCollision(val, val2, true);
							_ignoredCollisionPairs.Add(new ColliderPair(val, val2));
						}
					}
				}
				if (Plugin.DebugLogging.Value)
				{
					Plugin.Log.LogDebug((object)$"Ignoring {_ignoredCollisionPairs.Count} vehicle/cart collider pair(s) for {DisplayName} while coupled.");
				}
			}

			internal void RestoreCoupledCollisions()
			{
				foreach (ColliderPair ignoredCollisionPair in _ignoredCollisionPairs)
				{
					if ((Object)(object)ignoredCollisionPair.A != (Object)null && (Object)(object)ignoredCollisionPair.B != (Object)null)
					{
						Physics.IgnoreCollision(ignoredCollisionPair.A, ignoredCollisionPair.B, false);
					}
				}
				_ignoredCollisionPairs.Clear();
			}

			internal void EnsureBeam()
			{
				//IL_0056: Unknown result type (might be due to invalid IL or missing references)
				//IL_0060: Expected O, but got Unknown
				//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
				//IL_0143: Unknown result type (might be due to invalid IL or missing references)
				//IL_014d: Expected O, but got Unknown
				if ((Object)(object)_beam != (Object)null)
				{
					((Renderer)_beam).enabled = Plugin.ShowConnectionBeam.Value && (Object)(object)Cart != (Object)null;
					return;
				}
				_beamObject = new GameObject($"CartTrainBeam_{DisplayName}_{((Object)Vehicle).GetInstanceID()}");
				_beam = _beamObject.AddComponent<LineRenderer>();
				_beam.positionCount = 2;
				_beam.useWorldSpace = true;
				((Renderer)_beam).shadowCastingMode = (ShadowCastingMode)0;
				((Renderer)_beam).receiveShadows = false;
				((Renderer)_beam).motionVectorGenerationMode = (MotionVectorGenerationMode)2;
				_beam.textureMode = (LineTextureMode)0;
				_beam.alignment = (LineAlignment)0;
				_beam.numCapVertices = 8;
				_beam.numCornerVertices = 4;
				_beam.startWidth = Plugin.BeamWidth.Value;
				_beam.endWidth = Plugin.BeamWidth.Value * 0.7f;
				if ((Object)(object)_beamMaterial == (Object)null)
				{
					Shader val = Shader.Find("Sprites/Default");
					if ((Object)(object)val == (Object)null)
					{
						val = Shader.Find("Unlit/Color");
					}
					if ((Object)(object)val != (Object)null)
					{
						_beamMaterial = new Material(val);
					}
				}
				if ((Object)(object)_beamMaterial != (Object)null)
				{
					((Renderer)_beam).sharedMaterial = _beamMaterial;
				}
				Color startColor = default(Color);
				((Color)(ref startColor))..ctor(0.55f, 0.95f, 1f, 0.95f);
				Color endColor = default(Color);
				((Color)(ref endColor))..ctor(0.95f, 1f, 1f, 0.55f);
				_beam.startColor = startColor;
				_beam.endColor = endColor;
				((Renderer)_beam).enabled = Plugin.ShowConnectionBeam.Value && (Object)(object)Cart != (Object)null;
			}

			internal void DisableBeam()
			{
				if ((Object)(object)_beam != (Object)null)
				{
					((Renderer)_beam).enabled = false;
				}
			}

			internal void DestroyBeam()
			{
				RestoreCoupledCollisions();
				if ((Object)(object)_beamObject != (Object)null)
				{
					Object.Destroy((Object)(object)_beamObject);
					_beamObject = null;
					_beam = null;
				}
			}

			internal void UpdateBeam()
			{
				//IL_0094: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a4: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
				//IL_00cf: 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_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_00e7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e8: 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_00fa: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
				//IL_0101: Unknown result type (might be due to invalid IL or missing references)
				//IL_0106: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)_beam == (Object)null))
				{
					if (!Plugin.ShowConnectionBeam.Value || (Object)(object)Cart == (Object)null)
					{
						((Renderer)_beam).enabled = false;
						return;
					}
					((Renderer)_beam).enabled = true;
					float num = 0.92f + Mathf.Sin(Time.time * 8f) * 0.08f;
					_beam.startWidth = Plugin.BeamWidth.Value * num;
					_beam.endWidth = Plugin.BeamWidth.Value * 0.7f * num;
					Vector3 val = RearAnchor - ((Component)Vehicle).transform.forward * Plugin.TowGap.Value;
					Vector3 val2 = CartEndAnchor(Cart, CartEndSign);
					Vector3 val3 = Vector3.up * 0.06f;
					_beam.SetPosition(0, val + val3);
					_beam.SetPosition(1, val2 + val3 * 0.5f);
				}
			}
		}

		private static LocomotiveManager? _instance;

		private static readonly HashSet<PhysGrabCart> Carts = new HashSet<PhysGrabCart>();

		private static readonly HashSet<PhysGrabCart> ClaimedFollowers = new HashSet<PhysGrabCart>();

		private static readonly Dictionary<PhysGrabCart, float> CartHalfLengths = new Dictionary<PhysGrabCart, float>();

		private static readonly HashSet<int> RejectedCartIds = new HashSet<int>();

		private const string SupportedCartName = "Item Cart Medium";

		private readonly Dictionary<int, VehicleState> _vehicles = new Dictionary<int, VehicleState>();

		private float _nextFallbackScan;

		private float _nextCleanup;

		private void Awake()
		{
			_instance = this;
		}

		internal static void RegisterCart(PhysGrabCart cart)
		{
			if ((Object)(object)cart == (Object)null)
			{
				return;
			}
			if (!string.Equals(CleanName(((Object)cart).name), "Item Cart Medium", StringComparison.Ordinal))
			{
				int instanceID = ((Object)((Component)cart).gameObject).GetInstanceID();
				if (Plugin.DebugLogging.Value && RejectedCartIds.Add(instanceID))
				{
					Plugin.Log.LogDebug((object)$"Ignoring unsupported PhysGrabCart '{((Object)cart).name}' ({instanceID}).");
				}
			}
			else
			{
				Carts.Add(cart);
				if (!CartHalfLengths.ContainsKey(cart))
				{
					CartHalfLengths[cart] = ComputeHalfLength(((Component)cart).gameObject, ((Component)cart).transform, 0.35f);
				}
			}
		}

		internal static void RegisterPotentialVehicle(PhysGrabObject pgo)
		{
			if (!((Object)(object)_instance == (Object)null) && !((Object)(object)pgo == (Object)null))
			{
				_instance.TryRegisterVehicle(pgo);
			}
		}

		private void Update()
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			if (!IsAuthority())
			{
				return;
			}
			if (Time.unscaledTime >= _nextFallbackScan)
			{
				_nextFallbackScan = Time.unscaledTime + 5f;
				if (_vehicles.Count == 0)
				{
					FallbackDiscover();
				}
			}
			if (Time.unscaledTime >= _nextCleanup)
			{
				_nextCleanup = Time.unscaledTime + 3f;
				Cleanup();
			}
			if (Input.GetKey((KeyCode)304) && Input.GetKeyDown(Plugin.DetachKey.Value))
			{
				TryToggleHeldConnection();
			}
			UpdateVisuals();
		}

		private void FixedUpdate()
		{
			if (!IsAuthority())
			{
				return;
			}
			foreach (VehicleState value in _vehicles.Values)
			{
				if (!((Object)(object)value.Vehicle == (Object)null) && !((Object)(object)value.Body == (Object)null) && (Object)(object)value.Cart != (Object)null)
				{
					DriveFollower(value);
				}
			}
		}

		private void TryRegisterVehicle(PhysGrabObject pgo)
		{
			string text = CleanName(((Object)pgo).name);
			if (text != "Item Vehicle Semiscooter" && text != "Item Vehicle Semiscooter Small")
			{
				return;
			}
			int instanceID = ((Object)((Component)pgo).gameObject).GetInstanceID();
			if (!_vehicles.ContainsKey(instanceID))
			{
				Rigidbody val = (((Object)(object)pgo.rb != (Object)null) ? pgo.rb : ((Component)pgo).GetComponent<Rigidbody>());
				if (!((Object)(object)val == (Object)null))
				{
					_vehicles[instanceID] = new VehicleState(pgo, val, text);
					Plugin.Log.LogInfo((object)$"Tracking locomotive: {text} ({instanceID})");
				}
			}
		}

		private void FallbackDiscover()
		{
			PhysGrabObject[] array = Object.FindObjectsOfType<PhysGrabObject>();
			foreach (PhysGrabObject pgo in array)
			{
				TryRegisterVehicle(pgo);
			}
			PhysGrabCart[] array2 = Object.FindObjectsOfType<PhysGrabCart>();
			for (int i = 0; i < array2.Length; i++)
			{
				RegisterCart(array2[i]);
			}
		}

		private void Cleanup()
		{
			Carts.RemoveWhere((PhysGrabCart c) => (Object)(object)c == (Object)null || !IsSupportedCart(c));
			ClaimedFollowers.RemoveWhere((PhysGrabCart c) => (Object)(object)c == (Object)null);
			Dictionary<PhysGrabCart, float> dictionary = new Dictionary<PhysGrabCart, float>();
			foreach (KeyValuePair<PhysGrabCart, float> cartHalfLength in CartHalfLengths)
			{
				if ((Object)(object)cartHalfLength.Key != (Object)null)
				{
					dictionary[cartHalfLength.Key] = cartHalfLength.Value;
				}
			}
			CartHalfLengths.Clear();
			foreach (KeyValuePair<PhysGrabCart, float> item in dictionary)
			{
				CartHalfLengths[item.Key] = item.Value;
			}
			List<int> list = new List<int>();
			foreach (KeyValuePair<int, VehicleState> vehicle in _vehicles)
			{
				if ((Object)(object)vehicle.Value.Vehicle == (Object)null)
				{
					list.Add(vehicle.Key);
				}
			}
			foreach (int item2 in list)
			{
				if (_vehicles.TryGetValue(item2, out VehicleState value))
				{
					value.DestroyBeam();
				}
				_vehicles.Remove(item2);
			}
		}

		private void UpdateVisuals()
		{
			foreach (VehicleState value in _vehicles.Values)
			{
				value.UpdateBeam();
			}
		}

		private void TryToggleHeldConnection()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			foreach (VehicleState value in _vehicles.Values)
			{
				if (!((Object)(object)value.Cart == (Object)null) && (IsHeldByLocalPlayer(value.Vehicle) || IsHeldByLocalPlayer(GetCartGrabObject(value.Cart))))
				{
					Detach(value, "manual held-object Shift+" + ((object)Plugin.DetachKey.Value/*cast due to .constrained prefix*/).ToString());
					return;
				}
			}
			foreach (VehicleState value2 in _vehicles.Values)
			{
				if (IsHeldByLocalPlayer(value2.Vehicle) && TryAttachNearestCartToVehicle(value2))
				{
					return;
				}
			}
			foreach (PhysGrabCart cart in Carts)
			{
				if (IsSupportedCart(cart) && IsHeldByLocalPlayer(GetCartGrabObject(cart)) && TryAttachHeldCartToNearestVehicle(cart))
				{
					return;
				}
			}
			if (Plugin.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)"Shift+X pressed, but no held Scout/Hauler or supported large cart was in attach range.");
			}
		}

		private static bool TryAttachNearestCartToVehicle(VehicleState state)
		{
			//IL_0011: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)state.Cart != (Object)null)
			{
				return false;
			}
			Vector3 rearAnchor = state.RearAnchor;
			PhysGrabCart val = null;
			float num = Plugin.SnapDistance.Value;
			float cartEndSign = 1f;
			foreach (PhysGrabCart cart in Carts)
			{
				if (IsSupportedCart(cart) && !ClaimedFollowers.Contains(cart) && TryGetAttachCandidate(state, cart, rearAnchor, out var distance, out var sign) && !(distance > num))
				{
					val = cart;
					num = distance;
					cartEndSign = sign;
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Attach(state, val, cartEndSign);
			return true;
		}

		private bool TryAttachHeldCartToNearestVehicle(PhysGrabCart cart)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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)
			if (ClaimedFollowers.Contains(cart))
			{
				return false;
			}
			VehicleState vehicleState = null;
			float num = Plugin.SnapDistance.Value;
			float cartEndSign = 1f;
			foreach (VehicleState value in _vehicles.Values)
			{
				if (!((Object)(object)value.Cart != (Object)null) && !((Object)(object)value.Vehicle == (Object)null))
				{
					Vector3 rearAnchor = value.RearAnchor;
					if (TryGetAttachCandidate(value, cart, rearAnchor, out var distance, out var sign) && !(distance > num))
					{
						vehicleState = value;
						num = distance;
						cartEndSign = sign;
					}
				}
			}
			if (vehicleState == null)
			{
				return false;
			}
			Attach(vehicleState, cart, cartEndSign);
			return true;
		}

		private static bool TryGetAttachCandidate(VehicleState state, PhysGrabCart cart, Vector3 rear, out float distance, out float sign)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: 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_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			distance = float.MaxValue;
			sign = 1f;
			if ((Object)(object)((Component)cart).GetComponent<Rigidbody>() == (Object)null)
			{
				return false;
			}
			float cartHalfLength = GetCartHalfLength(cart);
			Vector3 val = ((Component)cart).transform.position + ((Component)cart).transform.forward * (cartHalfLength + Plugin.CartEndPadding.Value);
			Vector3 val2 = ((Component)cart).transform.position - ((Component)cart).transform.forward * (cartHalfLength + Plugin.CartEndPadding.Value);
			float num = Vector3.Distance(rear, val);
			float num2 = Vector3.Distance(rear, val2);
			sign = ((num <= num2) ? 1f : (-1f));
			distance = Mathf.Min(num, num2);
			Vector3 val3 = Flat(((Component)cart).transform.forward * sign);
			return Vector3.Angle(Flat(((Component)state.Vehicle).transform.forward), val3) <= Plugin.MaxSnapAngle.Value;
		}

		private static PhysGrabObject? GetCartGrabObject(PhysGrabCart cart)
		{
			if ((Object)(object)cart == (Object)null)
			{
				return null;
			}
			PhysGrabObject component = ((Component)cart).GetComponent<PhysGrabObject>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			component = ((Component)cart).GetComponentInParent<PhysGrabObject>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			return ((Component)cart).GetComponentInChildren<PhysGrabObject>();
		}

		private static bool IsHeldByLocalPlayer(PhysGrabObject? pgo)
		{
			if ((Object)(object)pgo == (Object)null)
			{
				return false;
			}
			try
			{
				Type type = ((object)pgo).GetType();
				FieldInfo fieldInfo = AccessTools.Field(type, "grabbedLocal") ?? AccessTools.Field(type, "isGrabbedLocal");
				if (fieldInfo != null && fieldInfo.FieldType == typeof(bool))
				{
					return (bool)fieldInfo.GetValue(pgo);
				}
				PropertyInfo propertyInfo = AccessTools.Property(type, "grabbedLocal") ?? AccessTools.Property(type, "isGrabbedLocal");
				if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool))
				{
					return (bool)propertyInfo.GetValue(pgo, null);
				}
			}
			catch (Exception ex)
			{
				if (Plugin.DebugLogging.Value)
				{
					Plugin.Log.LogDebug((object)("Could not read local grab state for " + ((Object)pgo).name + ": " + ex.Message));
				}
			}
			return false;
		}

		private static void Attach(VehicleState state, PhysGrabCart cart, float cartEndSign)
		{
			if (IsSupportedCart(cart))
			{
				Rigidbody component = ((Component)cart).GetComponent<Rigidbody>();
				if (!((Object)(object)component == (Object)null))
				{
					state.Cart = cart;
					state.CartBody = component;
					state.CartEndSign = cartEndSign;
					state.CoupledAt = Time.time;
					state.ResetRearPath();
					component.WakeUp();
					state.EnableCoupledCollisionIgnore(cart);
					state.EnsureBeam();
					ClaimedFollowers.Add(cart);
					Plugin.Log.LogInfo((object)$"Coupled {state.DisplayName} -> {((Object)cart).name} (end sign {cartEndSign:+0;-0}).");
				}
			}
		}

		private static void Detach(VehicleState state, string reason = "unspecified")
		{
			if ((Object)(object)state.Cart != (Object)null)
			{
				PhysGrabCart cart = state.Cart;
				ClaimedFollowers.Remove(cart);
				state.RestoreCoupledCollisions();
				Plugin.Log.LogInfo((object)("Detached " + state.DisplayName + " -> " + ((Object)cart).name + " (reason: " + reason + ")"));
			}
			state.Cart = null;
			state.CartBody = null;
			state.DisableBeam();
			state.CartEndSign = 1f;
			state.CoupledAt = 0f;
			state.ResetRearPath();
		}

		private static void DriveFollower(VehicleState state)
		{
			//IL_009c: 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_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: 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_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: 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_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: 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_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: 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_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_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: 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_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: 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_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: 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_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_0343: 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_0359: Unknown result type (might be due to invalid IL or missing references)
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0360: Unknown result type (might be due to invalid IL or missing references)
			//IL_036c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0371: Unknown result type (might be due to invalid IL or missing references)
			//IL_0374: 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_037e: 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_03b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03de: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e0: 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_03e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0410: Unknown result type (might be due to invalid IL or missing references)
			//IL_041c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0421: Unknown result type (might be due to invalid IL or missing references)
			//IL_0426: Unknown result type (might be due to invalid IL or missing references)
			//IL_044b: Unknown result type (might be due to invalid IL or missing references)
			//IL_044d: Unknown result type (might be due to invalid IL or missing references)
			//IL_044f: Unknown result type (might be due to invalid IL or missing references)
			//IL_046b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0499: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a0: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)state.Cart == (Object)null)
			{
				Detach(state, "cart destroyed/unloaded");
				return;
			}
			if (!IsSupportedCart(state.Cart))
			{
				Detach(state, "unsupported cart type");
				return;
			}
			if ((Object)(object)state.CartBody == (Object)null)
			{
				state.CartBody = ((Component)state.Cart).GetComponent<Rigidbody>();
				if ((Object)(object)state.CartBody == (Object)null)
				{
					if (Plugin.DebugLogging.Value)
					{
						Plugin.Log.LogDebug((object)("Waiting for Rigidbody to return on " + ((Object)state.Cart).name + "; coupling retained."));
					}
					return;
				}
			}
			float num = Vector3.Distance(((Component)state.Vehicle).transform.position, ((Component)state.Cart).transform.position);
			if (Time.time - state.CoupledAt >= Plugin.CouplingGraceSeconds.Value && num > Plugin.EmergencyBreakawayDistance.Value)
			{
				Plugin.Log.LogWarning((object)$"Emergency breakaway: {state.DisplayName} -> {((Object)state.Cart).name} at {num:0.0}m.");
				Detach(state, $"emergency breakaway at {num:0.0}m");
				return;
			}
			Rigidbody cartBody = state.CartBody;
			Vector3 val = CartEndAnchor(state.Cart, state.CartEndSign);
			Vector3 val2 = state.RearAnchor - ((Component)state.Vehicle).transform.forward * Plugin.TowGap.Value;
			Vector3 val3 = val2 - val;
			Vector3 pointVelocity = cartBody.GetPointVelocity(val);
			Vector3 val4 = state.Body.GetPointVelocity(val2) - pointVelocity;
			Vector3 val5 = new Vector3(val3.x, 0f, val3.z);
			Vector3 val6 = default(Vector3);
			((Vector3)(ref val6))..ctor(val4.x, 0f, val4.z);
			Vector3 val7 = Vector3.ClampMagnitude(val5 * Plugin.GrabStrength.Value + val6 * Plugin.GrabDamping.Value, Plugin.MaxGrabAcceleration.Value);
			float num2 = Mathf.Clamp(val3.y * (Plugin.GrabStrength.Value * 0.25f) + val4.y * (Plugin.GrabDamping.Value * 0.25f), 0f - Plugin.MaxVerticalAcceleration.Value, Plugin.MaxVerticalAcceleration.Value);
			Vector3 val8 = val7 + Vector3.up * num2;
			cartBody.AddForceAtPosition(val8, val, (ForceMode)5);
			Vector3 val9 = Flat(((Component)state.Vehicle).transform.forward);
			Vector3 normalized = ((Vector3)(ref val9)).normalized;
			float cartHalfLength = GetCartHalfLength(state.Cart);
			state.RecordRearPath(val2);
			state.GetRearPathTarget(val2, normalized, cartHalfLength + Plugin.CartEndPadding.Value, out var target, out var tangent);
			val9 = Vector3.Cross(Vector3.up, tangent);
			Vector3 normalized2 = ((Vector3)(ref val9)).normalized;
			Vector3 val10 = Flat(target - cartBody.worldCenterOfMass);
			float num3 = Vector3.Dot(val10, normalized2);
			float num4 = Vector3.Dot(state.Body.velocity - cartBody.velocity, normalized2);
			float num5 = Mathf.Clamp(num3 * Plugin.CenteringStrength.Value + num4 * Plugin.CenteringDamping.Value, 0f - Plugin.MaxCenteringAcceleration.Value, Plugin.MaxCenteringAcceleration.Value);
			cartBody.AddForce(normalized2 * num5, (ForceMode)5);
			Vector3 val11 = val10 * Plugin.PathGuideStrength.Value * Time.fixedDeltaTime;
			val11 = Vector3.ClampMagnitude(val11, Plugin.MaxPathGuideVelocityChange.Value);
			cartBody.AddForce(val11, (ForceMode)2);
			float num6 = Mathf.Clamp(Vector3.Dot(val10, tangent) * Plugin.PoweredCatchupGain.Value, 0f - Plugin.MaxPoweredCatchupSpeed.Value, Plugin.MaxPoweredCatchupSpeed.Value);
			val9 = Flat(state.Body.velocity);
			float magnitude = ((Vector3)(ref val9)).magnitude;
			Vector3 val12 = tangent * (magnitude + num6);
			Vector3 val13 = Flat(cartBody.velocity);
			Vector3 val14 = val12 - val13;
			val14 = Vector3.ClampMagnitude(val14, Plugin.MaxPoweredVelocityChange.Value);
			cartBody.AddForce(val14, (ForceMode)2);
			Vector3 val15 = Flat(((Component)state.Cart).transform.forward * (0f - state.CartEndSign));
			if (((Vector3)(ref val15)).sqrMagnitude > 0.0001f && ((Vector3)(ref tangent)).sqrMagnitude > 0.0001f)
			{
				((Vector3)(ref val15)).Normalize();
				float num7 = Mathf.Clamp(Vector3.SignedAngle(val15, tangent, Vector3.up) * (MathF.PI / 180f) * Plugin.YawAlignmentStrength.Value - cartBody.angularVelocity.y * 1.5f, 0f - Plugin.MaxYawAcceleration.Value, Plugin.MaxYawAcceleration.Value);
				cartBody.AddTorque(Vector3.up * num7, (ForceMode)5);
			}
			if (Plugin.DebugLogging.Value && Time.frameCount % 120 == 0)
			{
				Plugin.Log.LogDebug((object)$"Virtual hand {state.DisplayName}: handle={((Vector3)(ref val3)).magnitude:0.00}m, path={((Vector3)(ref val10)).magnitude:0.00}m, lateral={num3:0.00}m, poweredDV={((Vector3)(ref val14)).magnitude:0.00}, sign={state.CartEndSign:+0;-0}");
			}
		}

		private static float GetCartHalfLength(PhysGrabCart cart)
		{
			if (!CartHalfLengths.TryGetValue(cart, out var value))
			{
				value = ComputeHalfLength(((Component)cart).gameObject, ((Component)cart).transform, 0.35f);
				CartHalfLengths[cart] = value;
			}
			return value;
		}

		private static Vector3 CartEndAnchor(PhysGrabCart cart, float sign)
		{
			//IL_0006: 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_0017: 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)
			return ((Component)cart).transform.position + ((Component)cart).transform.forward * sign * (GetCartHalfLength(cart) + Plugin.CartEndPadding.Value);
		}

		private static float ComputeHalfLength(GameObject go, Transform t, float fallback)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_0060: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			Collider[] componentsInChildren = go.GetComponentsInChildren<Collider>();
			if (componentsInChildren.Length == 0)
			{
				return fallback;
			}
			float num = 0f;
			float num2 = 0f;
			Vector3 position = t.position;
			Vector3 forward = t.forward;
			Vector3 normalized = ((Vector3)(ref forward)).normalized;
			Collider[] array = componentsInChildren;
			foreach (Collider val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					Bounds bounds = val.bounds;
					Vector3 extents = ((Bounds)(ref bounds)).extents;
					float num3 = Vector3.Dot(((Bounds)(ref bounds)).center - position, normalized);
					float num4 = Mathf.Abs(normalized.x) * extents.x + Mathf.Abs(normalized.y) * extents.y + Mathf.Abs(normalized.z) * extents.z;
					num = Mathf.Min(num, num3 - num4);
					num2 = Mathf.Max(num2, num3 + num4);
				}
			}
			return Mathf.Max(fallback, Mathf.Max(Mathf.Abs(num), Mathf.Abs(num2)));
		}

		private static bool IsAuthority()
		{
			try
			{
				return SemiFunc.IsMasterClientOrSingleplayer();
			}
			catch
			{
				return true;
			}
		}

		private static bool IsSupportedCart(PhysGrabCart cart)
		{
			if ((Object)(object)cart != (Object)null)
			{
				return string.Equals(CleanName(((Object)cart).name), "Item Cart Medium", StringComparison.Ordinal);
			}
			return false;
		}

		private static string CleanName(string name)
		{
			if (!string.IsNullOrEmpty(name))
			{
				return name.Replace("Valuable ", string.Empty).Replace("(Clone)", string.Empty).Trim();
			}
			return string.Empty;
		}

		private static Vector3 Flat(Vector3 v)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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)
			return new Vector3(v.x, 0f, v.z);
		}
	}
}