Decompiled source of CartTrain v1.3.1

plugins\GrimDinosaurs-CartTrain\CartTrain.dll

Decompiled 13 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using 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: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("GrimDinosaurs")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.1.0")]
[assembly: AssemblyInformationalVersion("1.3.1")]
[assembly: AssemblyProduct("CartTrain")]
[assembly: AssemblyTitle("CartTrain")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.1.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;
		}
	}
}
namespace CartTrain
{
	[BepInPlugin("GrimDinosaurs.CartTrain", "CartTrain", "1.3.1")]
	public sealed class CartTrain : BaseUnityPlugin
	{
		public const string PluginGuid = "GrimDinosaurs.CartTrain";

		public const string PluginName = "CartTrain";

		public const string PluginVersion = "1.3.1";

		private readonly Dictionary<int, CartTrainLink> _links = new Dictionary<int, CartTrainLink>();

		private Harmony? _harmony;

		private float _rescanTimer;

		private float _autoConnectTimer;

		private string? _hudMessage;

		private float _hudMessageUntil;

		private GUIStyle? _hudStyle;

		private int _heldCartHudLinkId;

		private float _heldCartHudUntil;

		internal static CartTrain Instance { get; private set; }

		internal static ManualLogSource Logger => Instance._logger;

		internal static ConfigEntry<float> SnapDistance { get; private set; }

		internal static ConfigEntry<float> CartSpacing { get; private set; }

		internal static ConfigEntry<float> MaximumSnapAngle { get; private set; }

		internal static ConfigEntry<float> FollowSpeed { get; private set; }

		internal static ConfigEntry<float> TurnSpeed { get; private set; }

		internal static ConfigEntry<float> TetherSpring { get; private set; }

		internal static ConfigEntry<float> TetherDamper { get; private set; }

		internal static ConfigEntry<float> PathStrength { get; private set; }

		internal static ConfigEntry<float> PathDamping { get; private set; }

		internal static ConfigEntry<float> RotationStrength { get; private set; }

		internal static ConfigEntry<KeyCode> DetachKey { get; private set; }

		internal static ConfigEntry<bool> ConnectedCartCollisions { get; private set; }

		internal static ConfigEntry<float> PullDisconnectStretch { get; private set; }

		internal static ConfigEntry<float> VanillaGrabDisconnectDelay { get; private set; }

		internal static ConfigEntry<bool> DisconnectWhenFalling { get; private set; }

		internal static ConfigEntry<float> FallDisconnectDelay { get; private set; }

		internal static ConfigEntry<float> FallSpeedThreshold { get; private set; }

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		private void Awake()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Expected O, but got Unknown
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Expected O, but got Unknown
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Expected O, but got Unknown
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Expected O, but got Unknown
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Expected O, but got Unknown
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Expected O, but got Unknown
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Expected O, but got Unknown
			//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fa: Expected O, but got Unknown
			//IL_032d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Expected O, but got Unknown
			//IL_038a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0394: Expected O, but got Unknown
			//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d1: Expected O, but got Unknown
			//IL_03dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e6: Expected O, but got Unknown
			Instance = this;
			((Component)this).transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			SnapDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Connection", "AutomaticSnapDistance", 0.45f, new ConfigDescription("How close a cart's front must be to another cart's back before they snap together.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), Array.Empty<object>()));
			CartSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("Connection", "ConnectedGap", 1f, new ConfigDescription("Gap in metres between connected cart bodies.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>()));
			MaximumSnapAngle = ((BaseUnityPlugin)this).Config.Bind<float>("Connection", "MaximumSnapAngle", 70f, new ConfigDescription("Maximum angle between two carts when their front/back connection is allowed to snap.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 120f), Array.Empty<object>()));
			FollowSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Following", "MaximumFollowSpeed", 8f, new ConfigDescription("Maximum movement speed used by following carts.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			TurnSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Following", "MaximumTurnSpeed", 240f, new ConfigDescription("Maximum turning speed in degrees per second.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 720f), Array.Empty<object>()));
			TetherSpring = ((BaseUnityPlugin)this).Config.Bind<float>("Following", "TetherSpring", 1800f, new ConfigDescription("Strength of the physical one-metre tether between connected carts.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 6000f), Array.Empty<object>()));
			TetherDamper = ((BaseUnityPlugin)this).Config.Bind<float>("Following", "TetherDamper", 180f, new ConfigDescription("Damping applied to the physical tether.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 800f), Array.Empty<object>()));
			PathStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Following", "PathStrength", 8f, new ConfigDescription("How strongly a follower returns to the recorded route.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>()));
			PathDamping = ((BaseUnityPlugin)this).Config.Bind<float>("Following", "PathDamping", 8f, new ConfigDescription("Velocity damping used by the virtual track constraint.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>()));
			RotationStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Following", "RotationStrength", 30f, new ConfigDescription("How strongly a follower aligns its rotation to the recorded route.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 80f), Array.Empty<object>()));
			DetachKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "DetachKey", (KeyCode)120, "While actively grabbing a large cart, press this key to detach both its front and rear train links.");
			ConnectedCartCollisions = ((BaseUnityPlugin)this).Config.Bind<bool>("Collision", "ConnectedCartCollisions", true, "Allows directly connected carts to collide instead of passing through each other.");
			PullDisconnectStretch = ((BaseUnityPlugin)this).Config.Bind<float>("Connection", "PullDisconnectStretch", 0.3f, new ConfigDescription("Extra tether stretch in metres that disconnects a cart while it is being grabbed and pulled away.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>()));
			VanillaGrabDisconnectDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Connection", "VanillaGrabDisconnectDelay", 0.6f, new ConfigDescription("How long a connected follower may be grabbed before its incoming link releases. This lets vanilla players disconnect carts without installing the mod.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.15f, 2f), Array.Empty<object>()));
			DisconnectWhenFalling = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "DisconnectWhenFalling", true, "Disconnects a falling cart from both sides so it cannot pull the rest of the train over an edge.");
			FallDisconnectDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "FallDisconnectDelay", 0.25f, new ConfigDescription("How long an unsupported cart must keep falling before it disconnects.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>()));
			FallSpeedThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "FallSpeedThreshold", 1.5f, new ConfigDescription("Minimum downward speed in metres per second required to count as falling.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 10f), Array.Empty<object>()));
			_harmony = new Harmony("GrimDinosaurs.CartTrain");
			_harmony.PatchAll();
			Logger.LogInfo((object)"GrimDinosaurs.CartTrain v1.3.1 loaded in host-only mode. Vanilla clients are supported; no CartTrain RPCs will be sent.");
		}

		private void OnDestroy()
		{
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void Update()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			_rescanTimer -= Time.unscaledDeltaTime;
			if (_rescanTimer <= 0f)
			{
				_rescanTimer = 1f;
				AttachToExistingCarts();
				RemoveDestroyedLinks();
			}
			UpdateHeldCartHudTimer();
			if (TryGetHeldCartLink(out CartTrainLink link))
			{
				if (Input.GetKeyDown(DetachKey.Value))
				{
					link.RequestDetachAll();
				}
				else
				{
					link.RequestPullDisconnectIfStretched();
				}
			}
			_autoConnectTimer -= Time.unscaledDeltaTime;
			if (_autoConnectTimer <= 0f)
			{
				_autoConnectTimer = 0.15f;
				TryAutoConnectCarts();
			}
		}

		private void OnGUI()
		{
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: 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_00ab: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			string text = null;
			if (TryGetHeldCartLink(out CartTrainLink link) && link.LinkId == _heldCartHudLinkId && Time.unscaledTime < _heldCartHudUntil)
			{
				if (link.IsConnected)
				{
					link.GetTrainPosition(out var position, out var total);
					text = $"CART TRAIN\nConnected - Cart {position} of {total}\nPress {DetachKey.Value} to detach this cart";
				}
				else
				{
					text = "CART TRAIN\nNot connected\nPush this cart's front into another cart's back";
				}
			}
			else if (_hudMessage != null && Time.unscaledTime < _hudMessageUntil)
			{
				text = _hudMessage;
			}
			if (text != null)
			{
				if (_hudStyle == null)
				{
					_hudStyle = new GUIStyle(GUI.skin.box)
					{
						alignment = (TextAnchor)4,
						fontSize = 18,
						fontStyle = (FontStyle)1,
						wordWrap = true
					};
				}
				_hudStyle.normal.textColor = Color.white;
				float num = Mathf.Min(460f, (float)Screen.width - 24f);
				float num2 = Mathf.Max(12f, (float)Screen.height - 175f - 112f);
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(((float)Screen.width - num) * 0.5f, num2, num, 112f);
				GUI.Box(val, text, _hudStyle);
			}
		}

		private void UpdateHeldCartHudTimer()
		{
			if (!TryGetHeldCartLink(out CartTrainLink link))
			{
				_heldCartHudLinkId = 0;
			}
			else if (_heldCartHudLinkId != link.LinkId)
			{
				_heldCartHudLinkId = link.LinkId;
				_heldCartHudUntil = Time.unscaledTime + 5f;
			}
		}

		internal void ShowHudMessage(string message, float duration = 2.5f)
		{
			_hudMessage = message;
			_hudMessageUntil = Time.unscaledTime + duration;
		}

		internal bool TryGetHeldCartLink(out CartTrainLink link)
		{
			link = null;
			PlayerController instance = PlayerController.instance;
			if ((Object)(object)instance == (Object)null || !instance.physGrabActive || (Object)(object)instance.physGrabObject == (Object)null)
			{
				return false;
			}
			GameObject physGrabObject = instance.physGrabObject;
			PhysGrabCart val = physGrabObject.GetComponentInParent<PhysGrabCart>();
			if ((Object)(object)val == (Object)null)
			{
				PhysGrabObject componentInParent = physGrabObject.GetComponentInParent<PhysGrabObject>();
				val = (((Object)(object)componentInParent != (Object)null) ? componentInParent.physGrabCart : null);
			}
			if ((Object)(object)val == (Object)null || val.isSmallCart)
			{
				return false;
			}
			link = Attach(val);
			return (Object)(object)link != (Object)null;
		}

		private void FixedUpdate()
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			foreach (CartTrainLink value in _links.Values)
			{
				value.HostUpdateFallingDisconnect();
			}
			foreach (CartTrainLink value2 in _links.Values)
			{
				value2.RecordPose();
			}
			foreach (CartTrainLink value3 in _links.Values)
			{
				value3.DriveFollower();
			}
		}

		internal void Register(CartTrainLink link)
		{
			int linkId = link.LinkId;
			if (linkId == 0)
			{
				return;
			}
			List<int> list = null;
			foreach (KeyValuePair<int, CartTrainLink> link2 in _links)
			{
				if (!((Object)(object)link2.Value != (Object)(object)link) && link2.Key != linkId)
				{
					if (list == null)
					{
						list = new List<int>();
					}
					list.Add(link2.Key);
				}
			}
			if (list != null)
			{
				foreach (int item in list)
				{
					_links.Remove(item);
				}
			}
			_links[linkId] = link;
		}

		internal CartTrainLink? FindLink(int viewId)
		{
			_links.TryGetValue(viewId, out CartTrainLink value);
			return value;
		}

		internal static CartTrainLink Attach(PhysGrabCart cart)
		{
			CartTrainLink component = ((Component)cart).GetComponent<CartTrainLink>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			CartTrainLink cartTrainLink = ((Component)cart).gameObject.AddComponent<CartTrainLink>();
			cartTrainLink.Initialize(cart);
			return cartTrainLink;
		}

		private void AttachToExistingCarts()
		{
			PhysGrabCart[] array = Object.FindObjectsOfType<PhysGrabCart>();
			foreach (PhysGrabCart val in array)
			{
				if ((Object)(object)val != (Object)null && !val.isSmallCart)
				{
					Attach(val);
				}
			}
		}

		private void RemoveDestroyedLinks()
		{
			List<int> list = null;
			foreach (KeyValuePair<int, CartTrainLink> link in _links)
			{
				if (!((Object)(object)link.Value != (Object)null))
				{
					if (list == null)
					{
						list = new List<int>();
					}
					list.Add(link.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (int item in list)
			{
				_links.Remove(item);
			}
		}

		private void TryAutoConnectCarts()
		{
			//IL_0091: 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_00af: 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)
			foreach (CartTrainLink value in _links.Values)
			{
				if ((Object)(object)value == (Object)null || (Object)(object)value.Follower != (Object)null)
				{
					continue;
				}
				CartTrainLink cartTrainLink = null;
				float num = float.MaxValue;
				foreach (CartTrainLink value2 in _links.Values)
				{
					if (!((Object)(object)value2 == (Object)null) && !((Object)(object)value2 == (Object)(object)value) && !((Object)(object)value2.Leader != (Object)null) && !value.WouldCreateCycle(value2) && !(Vector3.Angle(value.FrontDirection, value2.FrontDirection) > MaximumSnapAngle.Value))
					{
						float num2 = Vector3.Distance(value.HandleConnector, value2.FrontConnector);
						if (num2 <= SnapDistance.Value && num2 < num)
						{
							cartTrainLink = value2;
							num = num2;
						}
					}
				}
				if ((Object)(object)cartTrainLink != (Object)null)
				{
					value.HostAutoConnect(cartTrainLink.LinkId);
				}
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabCart), "FixedUpdate")]
	internal static class PhysGrabCartFixedUpdatePatch
	{
		[HarmonyPostfix]
		private static void Postfix(PhysGrabCart __instance, ref bool ___cartBeingPulled)
		{
			if ((Object)(object)__instance != (Object)null && !__instance.isSmallCart && SemiFunc.IsMasterClientOrSingleplayer())
			{
				CartTrainLink cartTrainLink = CartTrain.Attach(__instance);
				cartTrainLink.SetBeingPulled(___cartBeingPulled);
			}
		}
	}
	public sealed class CartTrainLink : MonoBehaviour
	{
		private readonly struct PathPose
		{
			internal Vector3 Position { get; }

			internal Quaternion Rotation { get; }

			internal float Distance { get; }

			internal PathPose(Vector3 position, Quaternion rotation, float distance)
			{
				//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;
				Distance = distance;
			}
		}

		private const float PathSampleDistance = 0.04f;

		private const float PathSampleAngle = 1f;

		private const float MaximumStoredPath = 80f;

		private const int TetherVisualSegments = 14;

		private readonly List<PathPose> _path = new List<PathPose>();

		private PhysGrabCart _cart;

		private int _followerId;

		private int _leaderId;

		private bool _initialized;

		private bool _isBeingPulled;

		private float _releaseDampingUntil;

		private SpringJoint? _tether;

		private int _tetherLeaderId;

		private GameObject? _tetherVisualObject;

		private LineRenderer? _tetherVisual;

		private Material? _tetherVisualMaterial;

		private bool _tetherVisualShaderWarningLogged;

		private float _nextPullDisconnectRequestTime;

		private float _awayPullTimer;

		private float _vanillaGrabDisconnectTimer;

		private float _fallingTimer;

		internal int NetworkViewId
		{
			get
			{
				if (!((Object)(object)_cart != (Object)null) || !((Object)(object)_cart.photonView != (Object)null))
				{
					return 0;
				}
				return _cart.photonView.ViewID;
			}
		}

		internal int LinkId
		{
			get
			{
				if (NetworkViewId == 0)
				{
					return ((Object)this).GetInstanceID();
				}
				return NetworkViewId;
			}
		}

		internal float HalfLength { get; private set; } = 0.75f;

		internal Vector3 HandleConnector
		{
			get
			{
				//IL_0027: 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_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_0053: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)_cart != (Object)null) || !((Object)(object)_cart.handlePoint != (Object)null))
				{
					return ((Component)this).transform.position - ((Component)this).transform.forward * HalfLength;
				}
				return _cart.handlePoint.position;
			}
		}

		internal Vector3 FrontDirection
		{
			get
			{
				//IL_006e: 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_0037: Unknown result type (might be due to invalid IL or missing references)
				//IL_003c: 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_0062: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_cart != (Object)null && (Object)(object)_cart.handlePoint != (Object)null)
				{
					Vector3 val = Vector3.ProjectOnPlane(((Component)this).transform.position - _cart.handlePoint.position, ((Component)this).transform.up);
					if (((Vector3)(ref val)).sqrMagnitude > 0.01f)
					{
						return ((Vector3)(ref val)).normalized;
					}
				}
				return ((Component)this).transform.forward;
			}
		}

		internal Vector3 FrontConnector => ((Component)this).transform.position + FrontDirection * HalfLength;

		internal CartTrainLink? Follower
		{
			get
			{
				if (_followerId != 0)
				{
					return CartTrain.Instance.FindLink(_followerId);
				}
				return null;
			}
		}

		internal CartTrainLink? Leader
		{
			get
			{
				if (_leaderId != 0)
				{
					return CartTrain.Instance.FindLink(_leaderId);
				}
				return null;
			}
		}

		internal bool IsConnected
		{
			get
			{
				if (!((Object)(object)Leader != (Object)null))
				{
					return (Object)(object)Follower != (Object)null;
				}
				return true;
			}
		}

		internal void Initialize(PhysGrabCart cart)
		{
			if (!_initialized)
			{
				_initialized = true;
				_cart = cart;
				ConfigureCollisionBody();
				HalfLength = CalculateHalfLength(cart);
				CartTrain.Instance.Register(this);
				ResetPath(1.5f);
				CartTrain.Logger.LogInfo((object)$"Tracking large cart '{((Object)((Component)cart).gameObject).name}' with link ID {LinkId}.");
			}
		}

		private void Start()
		{
			if (!_initialized)
			{
				PhysGrabCart component = ((Component)this).GetComponent<PhysGrabCart>();
				if ((Object)(object)component != (Object)null && !component.isSmallCart)
				{
					Initialize(component);
				}
			}
			CartTrain.Instance.Register(this);
		}

		private void Update()
		{
			if (_initialized)
			{
				CartTrain.Instance.Register(this);
			}
		}

		private void LateUpdate()
		{
			CartTrainLink follower = Follower;
			if (!_initialized || (Object)(object)follower == (Object)null)
			{
				RemoveTetherVisual();
				return;
			}
			EnsureTetherVisual();
			UpdateTetherVisual(follower);
		}

		private void OnDestroy()
		{
			RemoveTether();
			RemoveTetherVisual();
		}

		internal bool WouldCreateCycle(CartTrainLink candidate)
		{
			CartTrainLink cartTrainLink = candidate;
			int num = 0;
			while ((Object)(object)cartTrainLink != (Object)null && num++ < 128)
			{
				if ((Object)(object)cartTrainLink == (Object)(object)this)
				{
					return true;
				}
				cartTrainLink = cartTrainLink.Follower;
			}
			return false;
		}

		internal void SetBeingPulled(bool isBeingPulled)
		{
			bool flag = isBeingPulled || ((Object)(object)_cart.physGrabObject != (Object)null && _cart.physGrabObject.grabbed);
			if (_isBeingPulled && !flag)
			{
				_releaseDampingUntil = Time.unscaledTime + 2.5f;
				CancelTetherClosingVelocity();
			}
			_isBeingPulled = flag;
		}

		private void CancelTetherClosingVelocity()
		{
			//IL_0040: 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)
			//IL_004b: 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_0062: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			if (!SemiFunc.IsMasterClientOrSingleplayer() || (Object)(object)_cart.rb == (Object)null)
			{
				return;
			}
			CartTrainLink leader = Leader;
			if ((Object)(object)leader == (Object)null || (Object)(object)leader._cart.rb == (Object)null)
			{
				return;
			}
			Vector3 val = leader.HandleConnector - FrontConnector;
			if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f))
			{
				Vector3 normalized = ((Vector3)(ref val)).normalized;
				float num = Vector3.Dot(_cart.rb.velocity - leader._cart.rb.velocity, normalized);
				if (num > 0f)
				{
					Rigidbody rb = _cart.rb;
					rb.velocity -= normalized * num;
				}
			}
		}

		internal void HostAutoConnect(int followerViewId)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Follower != (Object)null)
			{
				return;
			}
			CartTrainLink cartTrainLink = CartTrain.Instance.FindLink(followerViewId);
			if (!((Object)(object)cartTrainLink == (Object)null) && !((Object)(object)cartTrainLink == (Object)(object)this) && !((Object)(object)cartTrainLink.Leader != (Object)null) && !WouldCreateCycle(cartTrainLink))
			{
				float num = Vector3.Distance(HandleConnector, cartTrainLink.FrontConnector);
				if (!(num > CartTrain.SnapDistance.Value))
				{
					HostSetFollower(followerViewId);
				}
			}
		}

		internal void RequestDetachAll()
		{
			if (!IsConnected)
			{
				CartTrain.Instance.ShowHudMessage("CART TRAIN\nThis cart is not connected");
			}
			else if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				HostDetachAll();
			}
		}

		internal void RequestPullDisconnectIfStretched()
		{
			//IL_0033: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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_005a: 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_00a2: 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_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_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			CartTrainLink leader = Leader;
			if ((Object)(object)leader == (Object)null)
			{
				_awayPullTimer = 0f;
				return;
			}
			float num = CartTrain.CartSpacing.Value + CartTrain.PullDisconnectStretch.Value;
			Vector3 val = FrontConnector - leader.HandleConnector;
			float magnitude = ((Vector3)(ref val)).magnitude;
			Vector3 val2 = ((magnitude > 0.001f) ? (val / magnitude) : (-FrontDirection));
			float num2 = 0f;
			if ((Object)(object)_cart.rb != (Object)null && (Object)(object)leader._cart.rb != (Object)null)
			{
				num2 = Vector3.Dot(_cart.rb.velocity - leader._cart.rb.velocity, val2);
			}
			bool flag = false;
			PhysGrabber instance = PhysGrabber.instance;
			if ((Object)(object)instance != (Object)null && ((Vector3)(ref instance.currentGrabForce)).sqrMagnitude > 0.01f)
			{
				flag = Vector3.Dot(((Vector3)(ref instance.currentGrabForce)).normalized, val2) > 0.35f;
			}
			if (flag || num2 > 0.12f)
			{
				_awayPullTimer += Time.unscaledDeltaTime;
			}
			else
			{
				_awayPullTimer = Mathf.Max(0f, _awayPullTimer - Time.unscaledDeltaTime * 2f);
			}
			bool flag2 = _awayPullTimer >= 0.12f;
			bool flag3 = magnitude >= num;
			if ((flag2 || flag3) && !(Time.unscaledTime < _nextPullDisconnectRequestTime))
			{
				_awayPullTimer = 0f;
				_nextPullDisconnectRequestTime = Time.unscaledTime + 0.4f;
				if (SemiFunc.IsMasterClientOrSingleplayer())
				{
					HostBreakFromLeader();
				}
			}
		}

		private void HostBreakFromLeader()
		{
			CartTrainLink leader = Leader;
			if (!((Object)(object)leader == (Object)null))
			{
				leader.HostSetFollower(0);
				CartTrain.Logger.LogInfo((object)$"Pull-released cart {LinkId} from leader {leader.LinkId}.");
				CartTrain.Instance.ShowHudMessage("CART TRAIN\nTether released by pulling");
			}
		}

		private void HostDetachAll()
		{
			CartTrainLink leader = Leader;
			if ((Object)(object)leader != (Object)null)
			{
				leader.HostSetFollower(0);
			}
			if ((Object)(object)Follower != (Object)null)
			{
				HostSetFollower(0);
			}
			CartTrain.Instance.ShowHudMessage("CART TRAIN\nCart detached on both sides");
		}

		private void HostSetFollower(int followerViewId)
		{
			ApplyLink(followerViewId);
		}

		private void ApplyLink(int followerViewId)
		{
			if (_followerId == followerViewId)
			{
				CartTrainLink follower = Follower;
				if (followerViewId == 0 || ((Object)(object)follower != (Object)null && follower._leaderId == LinkId))
				{
					return;
				}
			}
			CartTrainLink follower2 = Follower;
			if ((Object)(object)follower2 != (Object)null && follower2._leaderId == LinkId)
			{
				follower2._leaderId = 0;
				follower2.RemoveTether();
			}
			_followerId = followerViewId;
			CartTrainLink follower3 = Follower;
			if ((Object)(object)follower3 != (Object)null)
			{
				CartTrainLink leader = follower3.Leader;
				if ((Object)(object)leader != (Object)null && (Object)(object)leader != (Object)(object)this)
				{
					leader._followerId = 0;
				}
				follower3._leaderId = LinkId;
				ResetPath(GetCenterSpacing(follower3));
				CartTrain.Logger.LogInfo((object)$"Connected cart {LinkId} handle to cart {follower3.LinkId} front.");
				if (CartTrain.Instance.TryGetHeldCartLink(out CartTrainLink link) && ((Object)(object)link == (Object)(object)this || (Object)(object)link == (Object)(object)follower3))
				{
					CartTrain.Instance.ShowHudMessage("CART TRAIN\nCart connected");
				}
			}
			else
			{
				CartTrain.Logger.LogInfo((object)$"Disconnected the follower from cart {LinkId}.");
				RemoveTetherVisual();
				if ((Object)(object)follower2 != (Object)null && CartTrain.Instance.TryGetHeldCartLink(out CartTrainLink link2) && ((Object)(object)link2 == (Object)(object)this || (Object)(object)link2 == (Object)(object)follower2))
				{
					CartTrain.Instance.ShowHudMessage("CART TRAIN\nCart detached");
				}
			}
		}

		internal void GetTrainPosition(out int position, out int total)
		{
			CartTrainLink cartTrainLink = this;
			int num = 0;
			while ((Object)(object)cartTrainLink.Leader != (Object)null && num++ < 128)
			{
				cartTrainLink = cartTrainLink.Leader;
			}
			position = 1;
			total = 0;
			CartTrainLink cartTrainLink2 = cartTrainLink;
			num = 0;
			while ((Object)(object)cartTrainLink2 != (Object)null && num++ < 128)
			{
				total++;
				if ((Object)(object)cartTrainLink2 == (Object)(object)this)
				{
					position = total;
				}
				cartTrainLink2 = cartTrainLink2.Follower;
			}
			if (total == 0)
			{
				total = 1;
			}
		}

		internal void RecordPose()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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_0051: 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_00b1: 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)
			if (!_initialized || (Object)(object)_cart.rb == (Object)null)
			{
				return;
			}
			Vector3 position = _cart.rb.position;
			Quaternion rotation = _cart.rb.rotation;
			if (_path.Count == 0)
			{
				_path.Add(new PathPose(position, rotation, 0f));
				return;
			}
			PathPose pathPose = _path[_path.Count - 1];
			float num = Vector3.Distance(pathPose.Position, position);
			float num2 = Quaternion.Angle(pathPose.Rotation, rotation);
			if (!(num < 0.04f) || !(num2 < 1f))
			{
				_path.Add(new PathPose(position, rotation, pathPose.Distance + num));
				TrimPath();
			}
		}

		internal void HostUpdateFallingDisconnect()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if (!CartTrain.DisconnectWhenFalling.Value || !IsConnected || (Object)(object)_cart.rb == (Object)null)
			{
				_fallingTimer = 0f;
				return;
			}
			if (!(_cart.rb.velocity.y <= 0f - CartTrain.FallSpeedThreshold.Value) || HasGroundSupport())
			{
				_fallingTimer = 0f;
				return;
			}
			_fallingTimer += Time.fixedDeltaTime;
			if (!(_fallingTimer < CartTrain.FallDisconnectDelay.Value))
			{
				_fallingTimer = 0f;
				HostDetachAll();
				CartTrain.Logger.LogInfo((object)$"Disconnected falling cart {LinkId} from both sides.");
				CartTrain.Instance.ShowHudMessage("CART TRAIN\nFalling cart disconnected");
			}
		}

		private bool HasGroundSupport()
		{
			//IL_000b: 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)
			//IL_0077: 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_0086: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: 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_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_0046: 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_00f3: Unknown result type (might be due to invalid IL or missing references)
			Collider[] componentsInChildren = ((Component)this).GetComponentsInChildren<Collider>();
			bool flag = false;
			Bounds val = default(Bounds);
			Collider[] array = componentsInChildren;
			foreach (Collider val2 in array)
			{
				if (!((Object)(object)val2 == (Object)null) && val2.enabled && !val2.isTrigger)
				{
					if (!flag)
					{
						val = val2.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref val)).Encapsulate(val2.bounds);
					}
				}
			}
			Vector3 val3 = _cart.rb.worldCenterOfMass + Vector3.up * 0.1f;
			float num = (flag ? ((Bounds)(ref val)).min.y : (((Component)this).transform.position.y - 0.75f));
			float num2 = Mathf.Max(0.6f, val3.y - num + 0.35f);
			RaycastHit[] array2 = Physics.RaycastAll(val3, Vector3.down, num2, -1, (QueryTriggerInteraction)1);
			RaycastHit[] array3 = array2;
			for (int j = 0; j < array3.Length; j++)
			{
				RaycastHit val4 = array3[j];
				if (!((Object)(object)((RaycastHit)(ref val4)).collider == (Object)null))
				{
					Transform transform = ((Component)((RaycastHit)(ref val4)).collider).transform;
					if (!((Object)(object)transform == (Object)(object)((Component)this).transform) && !transform.IsChildOf(((Component)this).transform))
					{
						return true;
					}
				}
			}
			return false;
		}

		internal void DriveFollower()
		{
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: 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_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: 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_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: 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)
			//IL_0152: 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_0159: 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_015d: 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_0164: 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_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: 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_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: 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_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: 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_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_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			CartTrainLink follower = Follower;
			if ((Object)(object)follower == (Object)null || (Object)(object)follower._cart.rb == (Object)null || (Object)(object)_cart.rb == (Object)null)
			{
				return;
			}
			follower.EnsureTether(this);
			if (follower._isBeingPulled)
			{
				follower.HostUpdateVanillaGrabDisconnect(this);
				return;
			}
			follower._vanillaGrabDisconnectTimer = 0f;
			float centerSpacing = GetCenterSpacing(follower);
			PathPose poseBehind = GetPoseBehind(centerSpacing);
			Rigidbody rb = follower._cart.rb;
			bool flag = Time.unscaledTime < follower._releaseDampingUntil;
			float num = (flag ? Mathf.Min(CartTrain.FollowSpeed.Value, 2.5f) : CartTrain.FollowSpeed.Value);
			float num2 = (flag ? Mathf.Min(CartTrain.PathStrength.Value, 3f) : CartTrain.PathStrength.Value);
			float num3 = (flag ? Mathf.Max(CartTrain.PathDamping.Value, 10f) : CartTrain.PathDamping.Value);
			Vector3 val = poseBehind.Position - rb.position;
			float num4 = Vector3.Dot(_cart.rb.velocity, ((Component)this).transform.forward);
			Vector3 val2 = poseBehind.Rotation * Vector3.forward * num4;
			Vector3 val3 = Vector3.ClampMagnitude(val * num2, num);
			Vector3 val4 = val2 + val3;
			Vector3 val5 = (val4 - rb.velocity) * num3;
			float num5 = (flag ? 18f : 60f);
			rb.AddForce(Vector3.ClampMagnitude(val5, num5), (ForceMode)5);
			Quaternion val6 = poseBehind.Rotation * Quaternion.Inverse(rb.rotation);
			float num6 = default(float);
			Vector3 val7 = default(Vector3);
			((Quaternion)(ref val6)).ToAngleAxis(ref num6, ref val7);
			if (num6 > 180f)
			{
				num6 -= 360f;
			}
			if (((Vector3)(ref val7)).sqrMagnitude > 0.0001f)
			{
				float num7 = (flag ? Mathf.Min(CartTrain.TurnSpeed.Value, 120f) : CartTrain.TurnSpeed.Value);
				float num8 = (flag ? Mathf.Min(CartTrain.RotationStrength.Value, 10f) : CartTrain.RotationStrength.Value);
				float num9 = num7 * (MathF.PI / 180f);
				Vector3 val8 = Vector3.ClampMagnitude(((Vector3)(ref val7)).normalized * num6 * (MathF.PI / 180f) * num8, num9);
				Vector3 val9 = (val8 - rb.angularVelocity) * num3;
				rb.AddTorque(Vector3.ClampMagnitude(val9, num9 * 4f), (ForceMode)5);
			}
			if (((Vector3)(ref val)).sqrMagnitude > 0.0025f)
			{
				rb.WakeUp();
			}
		}

		private bool HostUpdateVanillaGrabDisconnect(CartTrainLink leader)
		{
			if ((Object)(object)Leader != (Object)(object)leader)
			{
				_vanillaGrabDisconnectTimer = 0f;
				return false;
			}
			if (CartTrain.Instance.TryGetHeldCartLink(out CartTrainLink link) && (Object)(object)link == (Object)(object)this)
			{
				_vanillaGrabDisconnectTimer = 0f;
				return false;
			}
			_vanillaGrabDisconnectTimer += Time.fixedDeltaTime;
			if (_vanillaGrabDisconnectTimer < CartTrain.VanillaGrabDisconnectDelay.Value)
			{
				return false;
			}
			_vanillaGrabDisconnectTimer = 0f;
			leader.HostSetFollower(0);
			CartTrain.Logger.LogInfo((object)$"Released grabbed cart {LinkId} for vanilla-client compatibility.");
			CartTrain.Instance.ShowHudMessage("CART TRAIN\nGrabbed follower released");
			return true;
		}

		private void EnsureTether(CartTrainLink leader)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer() && !((Object)(object)leader._cart.rb == (Object)null) && !((Object)(object)_cart.rb == (Object)null))
			{
				if ((Object)(object)_tether != (Object)null && _tetherLeaderId == leader.LinkId && (Object)(object)((Joint)_tether).connectedBody == (Object)(object)leader._cart.rb)
				{
					ConfigureTether(_tether, leader);
					return;
				}
				RemoveTether();
				_tether = ((Component)this).gameObject.AddComponent<SpringJoint>();
				_tetherLeaderId = leader.LinkId;
				ConfigureTether(_tether, leader);
				CartTrain.Logger.LogInfo((object)$"Created one-metre tether from cart {leader.LinkId} to cart {LinkId}.");
			}
		}

		private void ConfigureTether(SpringJoint tether, CartTrainLink leader)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			((Joint)tether).connectedBody = leader._cart.rb;
			((Joint)tether).autoConfigureConnectedAnchor = false;
			((Joint)tether).anchor = ((Component)this).transform.InverseTransformPoint(FrontConnector);
			((Joint)tether).connectedAnchor = ((Component)leader).transform.InverseTransformPoint(leader.HandleConnector);
			tether.minDistance = CartTrain.CartSpacing.Value;
			tether.maxDistance = CartTrain.CartSpacing.Value;
			tether.spring = CartTrain.TetherSpring.Value;
			float num = Mathf.Max(leader._cart.rb.mass, 0.01f);
			float num2 = Mathf.Max(_cart.rb.mass, 0.01f);
			float num3 = num * num2 / (num + num2);
			float num4 = 2f * Mathf.Sqrt(tether.spring * num3);
			float num5 = Mathf.Max(CartTrain.TetherDamper.Value, num4 * 1.1f);
			if (_isBeingPulled || Time.unscaledTime < _releaseDampingUntil)
			{
				num5 *= 1.25f;
			}
			tether.damper = num5;
			tether.tolerance = 0.02f;
			((Joint)tether).enableCollision = CartTrain.ConnectedCartCollisions.Value;
			((Joint)tether).enablePreprocessing = true;
			((Joint)tether).massScale = 1f;
			((Joint)tether).connectedMassScale = 1f;
		}

		private void RemoveTether()
		{
			if ((Object)(object)_tether != (Object)null)
			{
				Object.Destroy((Object)(object)_tether);
			}
			_tether = null;
			_tetherLeaderId = 0;
		}

		private void ConfigureCollisionBody()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Invalid comparison between Unknown and I4
			if (!((Object)(object)_cart.rb == (Object)null))
			{
				Rigidbody rb = _cart.rb;
				if (!rb.isKinematic && ((int)rb.collisionDetectionMode == 0 || (int)rb.collisionDetectionMode == 1))
				{
					rb.collisionDetectionMode = (CollisionDetectionMode)2;
				}
				rb.solverIterations = Mathf.Max(rb.solverIterations, 12);
				rb.solverVelocityIterations = Mathf.Max(rb.solverVelocityIterations, 6);
				rb.maxDepenetrationVelocity = Mathf.Max(rb.maxDepenetrationVelocity, 6f);
			}
		}

		private void EnsureTetherVisual()
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: 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_00bd: Expected O, but got Unknown
			//IL_013e: 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)
			if ((Object)(object)_tetherVisualObject != (Object)null && (Object)(object)_tetherVisual != (Object)null)
			{
				return;
			}
			RemoveTetherVisual();
			Shader val = Shader.Find("Sprites/Default");
			if ((Object)(object)val == (Object)null)
			{
				val = Shader.Find("Unlit/Color");
			}
			if ((Object)(object)val == (Object)null)
			{
				if (!_tetherVisualShaderWarningLogged)
				{
					_tetherVisualShaderWarningLogged = true;
					CartTrain.Logger.LogWarning((object)"Could not find a shader for the light-red tether visual.");
				}
				return;
			}
			_tetherVisualObject = new GameObject("CartTrain Light Red Tether");
			((Object)_tetherVisualObject).hideFlags = (HideFlags)52;
			_tetherVisualObject.transform.SetParent(((Component)this).transform, false);
			_tetherVisualMaterial = new Material(val)
			{
				hideFlags = (HideFlags)52,
				color = Color.white
			};
			_tetherVisual = _tetherVisualObject.AddComponent<LineRenderer>();
			((Renderer)_tetherVisual).sharedMaterial = _tetherVisualMaterial;
			_tetherVisual.positionCount = 14;
			_tetherVisual.useWorldSpace = true;
			_tetherVisual.loop = false;
			_tetherVisual.startWidth = 0.045f;
			_tetherVisual.endWidth = 0.045f;
			_tetherVisual.startColor = new Color(1f, 0.55f, 0.58f, 0.95f);
			_tetherVisual.endColor = new Color(1f, 0.55f, 0.58f, 0.95f);
			_tetherVisual.numCornerVertices = 4;
			_tetherVisual.numCapVertices = 4;
			_tetherVisual.alignment = (LineAlignment)0;
			_tetherVisual.textureMode = (LineTextureMode)0;
			((Renderer)_tetherVisual).receiveShadows = false;
			((Renderer)_tetherVisual).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)_tetherVisual).sortingOrder = 10;
		}

		private void UpdateTetherVisual(CartTrainLink follower)
		{
			//IL_0010: 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_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_0047: 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_004b: 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_0052: 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_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_0072: 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)
			if (!((Object)(object)_tetherVisual == (Object)null))
			{
				Vector3 handleConnector = HandleConnector;
				Vector3 frontConnector = follower.FrontConnector;
				float num = Vector3.Distance(handleConnector, frontConnector);
				float num2 = Mathf.Min(0.12f, num * 0.1f);
				for (int i = 0; i < 14; i++)
				{
					float num3 = (float)i / 13f;
					Vector3 val = Vector3.Lerp(handleConnector, frontConnector, num3);
					val += Vector3.down * (Mathf.Sin(num3 * MathF.PI) * num2);
					_tetherVisual.SetPosition(i, val);
				}
			}
		}

		private void RemoveTetherVisual()
		{
			if ((Object)(object)_tetherVisualObject != (Object)null)
			{
				Object.Destroy((Object)(object)_tetherVisualObject);
			}
			if ((Object)(object)_tetherVisualMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)_tetherVisualMaterial);
			}
			_tetherVisualObject = null;
			_tetherVisual = null;
			_tetherVisualMaterial = null;
		}

		private float GetCenterSpacing(CartTrainLink follower)
		{
			//IL_0001: 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_001c: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_004b: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = Vector3.ProjectOnPlane(HandleConnector - ((Component)this).transform.position, ((Component)this).transform.up);
			float magnitude = ((Vector3)(ref val)).magnitude;
			val = Vector3.ProjectOnPlane(follower.FrontConnector - ((Component)follower).transform.position, ((Component)follower).transform.up);
			float magnitude2 = ((Vector3)(ref val)).magnitude;
			return magnitude + magnitude2 + CartTrain.CartSpacing.Value;
		}

		private PathPose GetPoseBehind(float spacing)
		{
			//IL_0014: 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_0025: 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_0035: 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_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: 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_0113: 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)
			if (_path.Count < 2)
			{
				return new PathPose(((Component)this).transform.position - ((Component)this).transform.forward * spacing, ((Component)this).transform.rotation, 0f);
			}
			float num = _path[_path.Count - 1].Distance - spacing;
			if (num <= _path[0].Distance)
			{
				return _path[0];
			}
			for (int num2 = _path.Count - 1; num2 > 0; num2--)
			{
				PathPose pathPose = _path[num2 - 1];
				PathPose pathPose2 = _path[num2];
				if (!(pathPose.Distance > num))
				{
					float num3 = pathPose2.Distance - pathPose.Distance;
					float num4 = ((num3 <= 0.0001f) ? 0f : ((num - pathPose.Distance) / num3));
					return new PathPose(Vector3.Lerp(pathPose.Position, pathPose2.Position, num4), Quaternion.Slerp(pathPose.Rotation, pathPose2.Rotation, num4), num);
				}
			}
			return _path[0];
		}

		private void ResetPath(float spacing)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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_0071: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			_path.Clear();
			Vector3 val = (((Object)(object)_cart != (Object)null && (Object)(object)_cart.rb != (Object)null) ? _cart.rb.position : ((Component)this).transform.position);
			Quaternion val2 = (((Object)(object)_cart != (Object)null && (Object)(object)_cart.rb != (Object)null) ? _cart.rb.rotation : ((Component)this).transform.rotation);
			_path.Add(new PathPose(val - val2 * Vector3.forward * spacing, val2, 0f));
			_path.Add(new PathPose(val, val2, spacing));
		}

		private void TrimPath()
		{
			if (_path.Count >= 3)
			{
				float num = _path[_path.Count - 1].Distance - 80f;
				int i;
				for (i = 0; i + 1 < _path.Count && _path[i + 1].Distance < num; i++)
				{
				}
				if (i > 0)
				{
					_path.RemoveRange(0, i);
				}
			}
		}

		private float CalculateHalfLength(PhysGrabCart cart)
		{
			//IL_0050: 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_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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_007a: 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_0093: 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)
			if ((Object)(object)cart.physGrabObject != (Object)null && cart.physGrabObject.itemLengthZ > 0.2f)
			{
				return Mathf.Max(0.5f, cart.physGrabObject.itemLengthZ * 0.5f);
			}
			if ((Object)(object)cart.cartMesh != (Object)null)
			{
				Bounds bounds = ((Renderer)cart.cartMesh).bounds;
				Vector3 forward = ((Component)this).transform.forward;
				float num = Mathf.Abs(forward.x) * ((Bounds)(ref bounds)).extents.x + Mathf.Abs(forward.y) * ((Bounds)(ref bounds)).extents.y + Mathf.Abs(forward.z) * ((Bounds)(ref bounds)).extents.z;
				return Mathf.Max(0.5f, num);
			}
			return 0.75f;
		}
	}
}