Decompiled source of WagonR v1.4.0

LCWagonR.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
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: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
namespace LCWagonR;

[Serializable]
public class MatInfo
{
	public string name;

	public Color color;

	public string texture;

	public string mode;
}
[Serializable]
public class PartInfo
{
	public string name;

	public Vector3 pos;

	public Vector3 size;

	public Vector3 axis;
}
[Serializable]
public class Meta
{
	public Vector3 boundsMin;

	public Vector3 boundsMax;

	public List<PartInfo> parts = new List<PartInfo>();

	public List<PartInfo> wheels = new List<PartInfo>();

	public List<PartInfo> seats = new List<PartInfo>();

	public List<MatInfo> materials = new List<MatInfo>();

	public PartInfo Part(string n)
	{
		return parts.FirstOrDefault((PartInfo p) => p.name == n);
	}

	public PartInfo Wheel(string n)
	{
		return wheels.FirstOrDefault((PartInfo p) => p.name == n);
	}
}
internal static class WagonAssets
{
	public static AssetBundle Bundle;

	public static GameObject ModelPrefab;

	public static Meta Meta;

	public static string Error;

	private static Dictionary<string, Material> cache = new Dictionary<string, Material>();

	private static Dictionary<string, Texture2D> texCache;

	public static Material Charred;

	public static bool Load()
	{
		//IL_0150: 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)ModelPrefab != (Object)null)
		{
			return true;
		}
		try
		{
			string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Plugin.Instance).Info.Location), "wagonr.bundle");
			if (!File.Exists(text))
			{
				Error = "wagonr.bundle not found next to the plugin dll (" + text + ")";
				return false;
			}
			Bundle = AssetBundle.LoadFromFile(text);
			if ((Object)(object)Bundle == (Object)null)
			{
				Error = "AssetBundle.LoadFromFile failed for " + text;
				return false;
			}
			ModelPrefab = Bundle.LoadAsset<GameObject>("WagonR_Model");
			TextAsset val = Bundle.LoadAsset<TextAsset>("wagonr_meta");
			if ((Object)(object)ModelPrefab == (Object)null || (Object)(object)val == (Object)null)
			{
				Error = "bundle is missing WagonR_Model / wagonr_meta";
				return false;
			}
			Meta = ParseMeta(val.text);
			Plugin.Log.LogInfo((object)$"Loaded wagonr.bundle: {Meta.parts.Count} parts, {Meta.wheels.Count} wheels, {Meta.seats.Count} seat parts, {Meta.materials.Count} materials, bounds {Meta.boundsMin} .. {Meta.boundsMax}");
			if (Meta.wheels.Count != 4 || Meta.Part("SteeringWheel") == null || Meta.materials.Count == 0)
			{
				Error = "wagonr_meta is incomplete (rebuild the bundle)";
				return false;
			}
			return true;
		}
		catch (Exception ex)
		{
			Error = ex.ToString();
			return false;
		}
	}

	private static float F(string s)
	{
		return float.Parse(s, CultureInfo.InvariantCulture);
	}

	private static Vector3 V3(string[] f, int i)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		return new Vector3(F(f[i]), F(f[i + 1]), F(f[i + 2]));
	}

	public static Meta ParseMeta(string text)
	{
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: 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_00e2: 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_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)
		Meta meta = new Meta();
		string[] array = text.Split(new char[1] { '\n' });
		for (int i = 0; i < array.Length; i++)
		{
			string text2 = array[i].Trim();
			if (text2.Length != 0)
			{
				string[] array2 = text2.Split('|');
				switch (array2[0])
				{
				case "bounds":
					meta.boundsMin = V3(array2, 1);
					meta.boundsMax = V3(array2, 4);
					break;
				case "part":
				case "wheel":
				case "seat":
				{
					PartInfo item = new PartInfo
					{
						name = array2[1],
						pos = V3(array2, 2),
						size = V3(array2, 5),
						axis = V3(array2, 8)
					};
					((array2[0] == "part") ? meta.parts : ((array2[0] == "wheel") ? meta.wheels : meta.seats)).Add(item);
					break;
				}
				case "mat":
					meta.materials.Add(new MatInfo
					{
						name = array2[1],
						color = new Color(F(array2[2]), F(array2[3]), F(array2[4]), F(array2[5])),
						texture = ((array2[6].Length > 0) ? array2[6] : null),
						mode = array2[7]
					});
					break;
				}
			}
		}
		return meta;
	}

	private static Texture TextureByName(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return null;
		}
		if (texCache == null)
		{
			texCache = new Dictionary<string, Texture2D>();
			Texture2D[] array = Bundle.LoadAllAssets<Texture2D>();
			foreach (Texture2D val in array)
			{
				texCache[((Object)val).name] = val;
			}
			Plugin.Log.LogInfo((object)("[Mat] bundle textures: " + string.Join(", ", texCache.Keys)));
		}
		if (texCache.TryGetValue(name, out var value))
		{
			return (Texture)(object)value;
		}
		value = Bundle.LoadAsset<Texture2D>(name);
		if ((Object)(object)value == (Object)null)
		{
			Plugin.Log.LogWarning((object)("[Mat] texture '" + name + "' NOT FOUND in the bundle"));
		}
		return (Texture)(object)value;
	}

	private static Material Template(GameObject cruiser, string namePrefix)
	{
		Renderer[] componentsInChildren = cruiser.GetComponentsInChildren<Renderer>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			Material[] sharedMaterials = componentsInChildren[i].sharedMaterials;
			foreach (Material val in sharedMaterials)
			{
				if ((Object)(object)val != (Object)null && ((Object)val).name.StartsWith(namePrefix))
				{
					return val;
				}
			}
		}
		return null;
	}

	public static void DescribeTemplate(Material m, string tag)
	{
		//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)
		if ((Object)(object)m == (Object)null)
		{
			Plugin.Log.LogWarning((object)("[Mat] template " + tag + ": NOT FOUND"));
			return;
		}
		Plugin.Log.LogInfo((object)(string.Format("[Mat] template {0} '{1}' shader={2} queue={3} keywords=[{4}] ", tag, ((Object)m).name, ((Object)m.shader).name, m.renderQueue, string.Join(" ", m.shaderKeywords)) + string.Format("baseMap={0} mask={1} normal={2} ", m.HasProperty("_BaseColorMap") && (Object)(object)m.GetTexture("_BaseColorMap") != (Object)null, m.HasProperty("_MaskMap") && (Object)(object)m.GetTexture("_MaskMap") != (Object)null, m.HasProperty("_NormalMap") && (Object)(object)m.GetTexture("_NormalMap") != (Object)null) + string.Format("base={0} smooth={1} metal={2} ", m.HasProperty("_BaseColor") ? ((object)m.GetColor("_BaseColor")/*cast due to .constrained prefix*/).ToString() : "-", m.HasProperty("_Smoothness") ? m.GetFloat("_Smoothness") : (-1f), m.HasProperty("_Metallic") ? m.GetFloat("_Metallic") : (-1f)) + string.Format("surface={0}", m.HasProperty("_SurfaceType") ? m.GetFloat("_SurfaceType") : (-1f))));
	}

	public static void BuildMaterials(GameObject cruiser)
	{
		//IL_0170: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: Expected O, but got Unknown
		//IL_0276: Unknown result type (might be due to invalid IL or missing references)
		//IL_027b: Unknown result type (might be due to invalid IL or missing references)
		//IL_028b: Expected O, but got Unknown
		//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: 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_00a8: Expected O, but got Unknown
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: 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_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cf: 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_0103: 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)
		//IL_0121: 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)
		cache.Clear();
		Material val = Template(cruiser, "TruckTexture");
		Material val2 = Template(cruiser, "WindshieldGlass");
		DescribeTemplate(val, "opaque");
		DescribeTemplate(val2, "glass");
		if ((Object)(object)val == (Object)null)
		{
			throw new Exception("Cruiser opaque template material not found");
		}
		foreach (MatInfo material in Meta.materials)
		{
			Material val3;
			if (material.mode == "glass" && (Object)(object)val2 != (Object)null)
			{
				val3 = new Material(val2)
				{
					name = "WagonR_" + material.name
				};
				Color color = material.color;
				if (color.r + color.g + color.b < 0.01f)
				{
					((Color)(ref color))..ctor(0.08f, 0.1f, 0.12f, 1f);
				}
				if (val3.HasProperty("_BaseColor"))
				{
					Color color2 = val3.GetColor("_BaseColor");
					val3.SetColor("_BaseColor", new Color(color.r, color.g, color.b, Mathf.Min(color2.a, 0.45f)));
				}
				val3.DisableKeyword("_MATERIAL_FEATURE_IRIDESCENCE");
				if (val3.HasProperty("_MaterialID"))
				{
					val3.SetFloat("_MaterialID", 1f);
				}
			}
			else
			{
				val3 = new Material(val)
				{
					name = "WagonR_" + material.name
				};
				ResetMaps(val3);
				Texture val4 = TextureByName(material.texture);
				Color val5 = (((Object)(object)val4 != (Object)null) ? Color.white : material.color);
				if (val3.HasProperty("_BaseColor"))
				{
					val3.SetColor("_BaseColor", val5);
				}
				if (val3.HasProperty("_BaseColorMap"))
				{
					val3.SetTexture("_BaseColorMap", val4);
				}
				if (val3.HasProperty("_Metallic"))
				{
					val3.SetFloat("_Metallic", ((Object)(object)val4 != (Object)null) ? 0.15f : 0.05f);
				}
				if (val3.HasProperty("_Smoothness"))
				{
					val3.SetFloat("_Smoothness", Smoothness(material.name));
				}
			}
			cache[material.name] = val3;
		}
		Charred = new Material(val)
		{
			name = "WagonR_Charred"
		};
		ResetMaps(Charred);
		if (Charred.HasProperty("_BaseColor"))
		{
			Charred.SetColor("_BaseColor", new Color(0.04f, 0.035f, 0.03f, 1f));
		}
		if (Charred.HasProperty("_Smoothness"))
		{
			Charred.SetFloat("_Smoothness", 0.1f);
		}
		if (Charred.HasProperty("_Metallic"))
		{
			Charred.SetFloat("_Metallic", 0f);
		}
		Plugin.Log.LogInfo((object)$"[Mat] built {cache.Count} materials (glass template: {(Object)(object)val2 != (Object)null})");
	}

	private static float Smoothness(string n)
	{
		if (n == "primary" || n.StartsWith("boot_ok") || n.StartsWith("door"))
		{
			return 0.45f;
		}
		if (n.StartsWith("wheel"))
		{
			return 0.35f;
		}
		if (n.StartsWith("interior"))
		{
			return 0.15f;
		}
		return 0.3f;
	}

	private static void ResetMaps(Material m)
	{
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		string[] array = new string[6] { "_NormalMap", "_MaskMap", "_EmissiveColorMap", "_DetailMap", "_BentNormalMap", "_HeightMap" };
		foreach (string text in array)
		{
			if (m.HasProperty(text))
			{
				m.SetTexture(text, (Texture)null);
			}
		}
		array = new string[7] { "_NORMALMAP", "_MASKMAP", "_EMISSIVE_COLOR_MAP", "_DETAIL_MAP", "_BENTNORMALMAP", "_HEIGHTMAP", "_NORMALMAP_TANGENT_SPACE" };
		foreach (string text2 in array)
		{
			m.DisableKeyword(text2);
		}
		if (m.HasProperty("_EmissiveColor"))
		{
			m.SetColor("_EmissiveColor", Color.black);
		}
		if (m.HasProperty("_BaseColorMap_ST"))
		{
			m.SetVector("_BaseColorMap_ST", new Vector4(1f, 1f, 0f, 0f));
		}
	}

	public static Material ByName(string materialName)
	{
		if (!cache.TryGetValue(materialName, out var value))
		{
			return null;
		}
		return value;
	}

	public static void Apply(GameObject model)
	{
		Renderer[] componentsInChildren = model.GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val in componentsInChildren)
		{
			Material[] sharedMaterials = val.sharedMaterials;
			Material[] array = (Material[])(object)new Material[sharedMaterials.Length];
			for (int j = 0; j < sharedMaterials.Length; j++)
			{
				array[j] = (((Object)(object)sharedMaterials[j] != (Object)null) ? ByName(((Object)sharedMaterials[j]).name) : null) ?? Charred;
			}
			val.sharedMaterials = array;
			val.shadowCastingMode = (ShadowCastingMode)1;
			val.receiveShadows = true;
		}
	}
}
public class WagonRunner : MonoBehaviour
{
}
internal static class WagonAudio
{
	private static readonly Dictionary<string, AudioClip> clips = new Dictionary<string, AudioClip>();

	private static readonly Dictionary<AudioClip, AudioClip> pitched = new Dictionary<AudioClip, AudioClip>();

	private static bool loaded;

	private static readonly FieldInfo HoodOpen = AccessTools.Field(typeof(VehicleController), "carHoodOpenSFX");

	private static readonly FieldInfo HoodClose = AccessTools.Field(typeof(VehicleController), "carHoodCloseSFX");

	private static AudioClip cdoorOpen;

	private static AudioClip cdoorClose;

	private static AudioClip chatchOpen;

	private static AudioClip chatchClose;

	private static AudioClip choodOpen;

	private static AudioClip choodClose;

	public static int Count => clips.Count;

	public static int PitchedCount => pitched.Count;

	public static void Load()
	{
		if (loaded)
		{
			return;
		}
		loaded = true;
		string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)Plugin.Instance).Info.Location), "audio");
		if (!Directory.Exists(text))
		{
			Plugin.Log.LogWarning((object)("[Audio] no audio folder at " + text + ": the Wagon R keeps the Cruiser's engine sounds"));
			return;
		}
		string[] files = Directory.GetFiles(text, "*.wav");
		foreach (string path in files)
		{
			try
			{
				AudioClip val = Read(path);
				if ((Object)(object)val != (Object)null)
				{
					clips[((Object)val).name] = val;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[Audio] " + Path.GetFileName(path) + ": " + ex.Message));
			}
		}
		Plugin.Log.LogInfo((object)$"[Audio] loaded {clips.Count} clips from {text}");
	}

	public static AudioClip Get(string name)
	{
		if (!clips.TryGetValue(name, out var value))
		{
			return null;
		}
		return value;
	}

	private static void Put(ref AudioClip field, string name)
	{
		AudioClip val = Get(name);
		if ((Object)(object)val != (Object)null)
		{
			field = val;
		}
		else
		{
			Plugin.Log.LogWarning((object)("[Audio] missing clip " + name));
		}
	}

	private static AudioClip Read(string path)
	{
		byte[] array = File.ReadAllBytes(path);
		if (array.Length < 44 || array[0] != 82 || array[1] != 73 || array[2] != 70 || array[3] != 70)
		{
			throw new Exception("not a RIFF file");
		}
		int i = 12;
		int num = 1;
		int num2 = 44100;
		int num3 = 16;
		int num4 = -1;
		int num5 = 0;
		int num6;
		for (; i + 8 <= array.Length; i += 8 + num6 + (num6 & 1))
		{
			string text = Encoding.ASCII.GetString(array, i, 4);
			num6 = BitConverter.ToInt32(array, i + 4);
			if (text == "fmt ")
			{
				num = BitConverter.ToInt16(array, i + 10);
				num2 = BitConverter.ToInt32(array, i + 12);
				num3 = BitConverter.ToInt16(array, i + 22);
			}
			else if (text == "data")
			{
				num4 = i + 8;
				num5 = Math.Min(num6, array.Length - num4);
				break;
			}
		}
		if (num4 < 0 || num3 != 16)
		{
			throw new Exception("need 16-bit PCM");
		}
		int num7 = num5 / 2;
		float[] array2 = new float[num7];
		for (int j = 0; j < num7; j++)
		{
			array2[j] = (float)BitConverter.ToInt16(array, num4 + j * 2) / 32768f;
		}
		AudioClip obj = AudioClip.Create(Path.GetFileNameWithoutExtension(path), num7 / num, num, num2, false);
		obj.SetData(array2, 0);
		((Object)obj).hideFlags = (HideFlags)61;
		return obj;
	}

	private static List<KeyValuePair<AudioClip, bool>> ReusedClips(VehicleController c)
	{
		List<KeyValuePair<AudioClip, bool>> l = new List<KeyValuePair<AudioClip, bool>>();
		Action<AudioClip, bool> action = delegate(AudioClip a, bool loop)
		{
			if ((Object)(object)a != (Object)null)
			{
				l.Add(new KeyValuePair<AudioClip, bool>(a, loop));
			}
		};
		action(c.insertKey, arg2: false);
		action(c.twistKey, arg2: false);
		action(c.removeKey, arg2: false);
		action(c.sitDown, arg2: false);
		action(c.engineRev, arg2: false);
		action(c.revEngineStart, arg2: true);
		action(c.headlightsToggleSFX, arg2: false);
		action(c.turboBoostSFX, arg2: false);
		action(c.jumpInCarSFX, arg2: false);
		action(c.windshieldBreak, arg2: false);
		AudioClip[][] array = new AudioClip[5][] { c.gearStickAudios, c.minCollisions, c.medCollisions, c.maxCollisions, c.obstacleCollisions };
		foreach (AudioClip[] array2 in array)
		{
			if (array2 != null)
			{
				AudioClip[] array3 = array2;
				foreach (AudioClip arg in array3)
				{
					action(arg, arg2: false);
				}
			}
		}
		if ((Object)(object)c.hornAudio != (Object)null)
		{
			action(c.hornAudio.clip, arg2: true);
		}
		if ((Object)(object)c.rollingAudio != (Object)null)
		{
			action(c.rollingAudio.clip, arg2: true);
		}
		if ((Object)(object)c.skiddingAudio != (Object)null)
		{
			action(c.skiddingAudio.clip, arg2: true);
		}
		return l;
	}

	public static void PrepareCruiser(VehicleController cruiser)
	{
		//IL_0028: 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_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Expected O, but got Unknown
		float value = Cfg.CruiserPitch.Value;
		if ((Object)(object)cruiser == (Object)null || Mathf.Approximately(value, 1f))
		{
			return;
		}
		try
		{
			GameObject val = new GameObject("WagonR_AudioPrep")
			{
				hideFlags = (HideFlags)61
			};
			Object.DontDestroyOnLoad((Object)val);
			((MonoBehaviour)val.AddComponent<WagonRunner>()).StartCoroutine(PitchAll(ReusedClips(cruiser), value));
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("[Audio] pitched Cruiser sounds unavailable (the Cruiser's originals are used): " + ex.Message));
		}
	}

	private static IEnumerator PitchAll(List<KeyValuePair<AudioClip, bool>> list, float k)
	{
		int ok = 0;
		int failed = 0;
		foreach (KeyValuePair<AudioClip, bool> kv in list)
		{
			AudioClip src = kv.Key;
			if (!((Object)(object)src == (Object)null) && !pitched.ContainsKey(src))
			{
				if ((int)src.loadState == 0)
				{
					src.LoadAudioData();
				}
				float waited = 0f;
				while ((int)src.loadState == 1 && waited < 8f)
				{
					waited += Time.unscaledDeltaTime;
					yield return null;
				}
				string why;
				AudioClip val = Resample(src, k, kv.Value, out why);
				if ((Object)(object)val != (Object)null)
				{
					pitched[src] = val;
					ok++;
				}
				else
				{
					failed++;
					Plugin.Log.LogWarning((object)$"[Audio] cannot pitch '{((Object)src).name}' ({why}; loadType={src.loadType}, state={src.loadState}): it keeps the Cruiser's original");
				}
				yield return null;
			}
		}
		Plugin.Log.LogInfo((object)$"[Audio] pitched {ok} Cruiser clips up by {k:0.00}x ({failed} could not be read)");
	}

	private static AudioClip Resample(AudioClip src, float k, bool loop, out string why)
	{
		why = null;
		try
		{
			int channels = src.channels;
			int samples = src.samples;
			if (samples < 2)
			{
				why = "empty";
				return null;
			}
			float[] array = new float[samples * channels];
			if (!src.GetData(array, 0))
			{
				why = "GetData refused";
				return null;
			}
			int num = Mathf.Max(2, Mathf.RoundToInt((float)samples / k));
			float num2 = (float)samples / (float)num;
			float[] array2 = new float[num * channels];
			for (int i = 0; i < num; i++)
			{
				float num3 = (float)i * num2;
				int num4 = Mathf.Min((int)num3, samples - 1);
				float num5 = num3 - (float)num4;
				int num6 = num4 + 1;
				if (num6 >= samples)
				{
					num6 = ((!loop) ? (samples - 1) : 0);
				}
				for (int j = 0; j < channels; j++)
				{
					array2[i * channels + j] = Mathf.Lerp(array[num4 * channels + j], array[num6 * channels + j], num5);
				}
			}
			AudioClip obj = AudioClip.Create(((Object)src).name + "_p", num, channels, src.frequency, false);
			obj.SetData(array2, 0);
			((Object)obj).hideFlags = (HideFlags)61;
			return obj;
		}
		catch (Exception ex)
		{
			why = ex.Message;
			return null;
		}
	}

	private static AudioClip Map(AudioClip c)
	{
		if (!((Object)(object)c != (Object)null) || !pitched.TryGetValue(c, out var value))
		{
			return c;
		}
		return value;
	}

	private static AudioClip[] Map(AudioClip[] a)
	{
		return a?.Select(Map).ToArray();
	}

	public static void ApplyRuntime(VehicleController vc)
	{
		vc.insertKey = Map(vc.insertKey);
		vc.twistKey = Map(vc.twistKey);
		vc.removeKey = Map(vc.removeKey);
		vc.sitDown = Map(vc.sitDown);
		vc.engineRev = Map(vc.engineRev);
		vc.revEngineStart = Map(vc.revEngineStart);
		vc.headlightsToggleSFX = Map(vc.headlightsToggleSFX);
		vc.turboBoostSFX = Map(vc.turboBoostSFX);
		vc.jumpInCarSFX = Map(vc.jumpInCarSFX);
		vc.windshieldBreak = Map(vc.windshieldBreak);
		vc.gearStickAudios = Map(vc.gearStickAudios);
		vc.minCollisions = Map(vc.minCollisions);
		vc.medCollisions = Map(vc.medCollisions);
		vc.maxCollisions = Map(vc.maxCollisions);
		vc.obstacleCollisions = Map(vc.obstacleCollisions);
		if ((Object)(object)vc.hornAudio != (Object)null)
		{
			vc.hornAudio.clip = Map(vc.hornAudio.clip);
		}
		if ((Object)(object)vc.rollingAudio != (Object)null)
		{
			vc.rollingAudio.clip = Map(vc.rollingAudio.clip);
		}
		if ((Object)(object)vc.skiddingAudio != (Object)null)
		{
			vc.skiddingAudio.clip = Map(vc.skiddingAudio.clip);
		}
		AudioClip val = Get("silent");
		if ((Object)(object)val != (Object)null)
		{
			if (HoodOpen != null)
			{
				HoodOpen.SetValue(vc, val);
			}
			if (HoodClose != null)
			{
				HoodClose.SetValue(vc, val);
			}
		}
	}

	public static void CaptureCruiserBody(GameObject car, AnimatedObjectTrigger backDoor)
	{
		Func<string, PlayAudioAnimationEvent> func = delegate(string path)
		{
			Transform val3 = car.transform.Find(path);
			return (!((Object)(object)val3 != (Object)null)) ? null : ((Component)val3).GetComponent<PlayAudioAnimationEvent>();
		};
		PlayAudioAnimationEvent val = func("Meshes/DoorLeftContainer") ?? func("Meshes/DoorRightContainer");
		if ((Object)(object)val != (Object)null)
		{
			cdoorClose = val.audioClip;
			cdoorOpen = val.audioClip2;
		}
		PlayAudioAnimationEvent val2 = func("Meshes/CarHoodContainer/CarHoodMesh");
		if ((Object)(object)val2 != (Object)null)
		{
			choodClose = val2.audioClip;
			choodOpen = val2.audioClip2;
		}
		if ((Object)(object)backDoor != (Object)null)
		{
			if (backDoor.boolTrueAudios != null && backDoor.boolTrueAudios.Length != 0)
			{
				chatchOpen = backDoor.boolTrueAudios[0];
			}
			if (backDoor.boolFalseAudios != null && backDoor.boolFalseAudios.Length != 0)
			{
				chatchClose = backDoor.boolFalseAudios[0];
			}
		}
		Plugin.Log.LogInfo((object)("[Audio] Cruiser body clips: door " + Nm(cdoorOpen) + "/" + Nm(cdoorClose) + ", hatch " + Nm(chatchOpen) + "/" + Nm(chatchClose) + ", bonnet " + Nm(choodOpen) + "/" + Nm(choodClose)));
	}

	private static string Nm(AudioClip c)
	{
		if (!((Object)(object)c != (Object)null))
		{
			return "null";
		}
		return ((Object)c).name;
	}

	public static void BodySounds(WagonHinge h, AudioSource template, string kind)
	{
		if (clips.Count == 0)
		{
			return;
		}
		AudioClip val = null;
		AudioClip val2 = null;
		switch (kind)
		{
		case "door":
			val = cdoorOpen;
			val2 = cdoorClose;
			break;
		case "hatch":
			val = chatchOpen;
			val2 = chatchClose;
			break;
		case "hood":
			val = choodOpen;
			val2 = choodClose;
			break;
		}
		if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null) && !((Object)(object)h == (Object)null))
		{
			AudioSource val3 = ((Component)h).gameObject.AddComponent<AudioSource>();
			val3.playOnAwake = false;
			val3.loop = false;
			val3.spatialBlend = 1f;
			val3.minDistance = 2f;
			val3.maxDistance = 30f;
			val3.rolloffMode = (AudioRolloffMode)1;
			val3.dopplerLevel = 0f;
			val3.pitch = Cfg.CruiserPitch.Value;
			if ((Object)(object)template != (Object)null)
			{
				val3.outputAudioMixerGroup = template.outputAudioMixerGroup;
			}
			h.sfx = val3;
			h.openClips = (AudioClip[])(object)new AudioClip[1] { val };
			h.closeClips = (AudioClip[])(object)new AudioClip[1] { val2 };
		}
	}

	public static void Apply(VehicleController vc, GameObject car)
	{
		if (clips.Count == 0)
		{
			return;
		}
		Put(ref vc.engineStartSuccessful, "engine_start");
		Put(ref vc.engineRun, "engine_idle");
		Put(ref vc.engineRun2, "engine_high");
		Put(ref vc.turboBoostSFX2, "engine_blip");
		if ((Object)(object)vc.engineAudio2 != (Object)null && (Object)(object)Get("engine_high") != (Object)null)
		{
			vc.engineAudio2.clip = Get("engine_high");
		}
		AudioClip val = Get("silent");
		if ((Object)(object)val != (Object)null)
		{
			PlayAudioAnimationEvent[] componentsInChildren = car.GetComponentsInChildren<PlayAudioAnimationEvent>(true);
			foreach (PlayAudioAnimationEvent val2 in componentsInChildren)
			{
				if ((Object)(object)val2.audioClip != (Object)null)
				{
					val2.audioClip = val;
				}
				if ((Object)(object)val2.audioClip2 != (Object)null)
				{
					val2.audioClip2 = val;
				}
				if ((Object)(object)val2.audioClip3 != (Object)null)
				{
					val2.audioClip3 = val;
				}
				if (val2.randomClips != null && val2.randomClips.Length != 0)
				{
					val2.randomClips = (AudioClip[])(object)new AudioClip[1] { val };
				}
				if (val2.randomClips2 != null && val2.randomClips2.Length != 0)
				{
					val2.randomClips2 = (AudioClip[])(object)new AudioClip[1] { val };
				}
			}
		}
		Plugin.Log.LogInfo((object)"[Audio] Wagon R engine sounds applied");
	}
}
internal static class WagonSound
{
	private const float Idle = 900f;

	private const float Limit = 5600f;

	private const float IdleClipRpm = 880f;

	private const float HighClipRpm = 3000f;

	private static readonly float[] GearTop = new float[5] { 0.3f, 0.5f, 0.7f, 0.9f, 1.15f };

	private static float RpmAt(int gear, float g)
	{
		return Mathf.Clamp(900f + g / (GearTop[gear - 1] * Cfg.TopSpeed.Value) * 4700f, 900f, 7000f);
	}

	public static void Engine(VehicleController v, WagonTag t)
	{
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Invalid comparison between Unknown and I4
		if (!v.ignitionStarted || (Object)(object)v.engineAudio1 == (Object)null || WagonAudio.Count == 0)
		{
			return;
		}
		float deltaTime = Time.deltaTime;
		if (deltaTime <= 0f)
		{
			return;
		}
		float num = Mathf.Abs(v.EngineRPM) / 60f * 2f * MathF.PI * Mathf.Max(0.05f, v.FrontLeftWheel.radius);
		t.sndAccel = Mathf.Lerp(t.sndAccel, (num - t.sndLastSpeed) / deltaTime, 1f - Mathf.Exp((0f - deltaTime) / 0.25f));
		t.sndLastSpeed = num;
		bool num2 = (int)v.gear == 2;
		bool flag = ((NetworkBehaviour)v).IsOwner && v.localPlayerInControl && v.drivePedalPressed;
		if (num2 || num < 1.2f)
		{
			t.sndGear = 1;
		}
		else if (t.sndShift <= 0f)
		{
			if (t.sndGear < 5 && RpmAt(t.sndGear, num) > 4700f)
			{
				t.sndGear++;
				t.sndShift = 0.35f;
			}
			else if (t.sndGear > 1 && RpmAt(t.sndGear, num) < 1700f)
			{
				t.sndGear--;
				t.sndShift = 0.3f;
			}
		}
		float num3 = RpmAt(t.sndGear, num);
		if (num < 1.2f)
		{
			num3 = (flag ? 2600f : (900f + 12f * Mathf.Sin(Time.time * 9f)));
		}
		if (t.sndShift > 0f)
		{
			t.sndShift -= deltaTime;
			num3 = Mathf.Max(900f, num3 - 900f * Mathf.Sin(Mathf.Clamp01(t.sndShift / 0.35f) * MathF.PI));
		}
		t.sndRpm += (num3 - t.sndRpm) * (1f - Mathf.Exp((0f - deltaTime) / ((num3 > t.sndRpm) ? 0.09f : 0.2f)));
		float num4 = Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(1300f, 2000f, t.sndRpm));
		float num5 = Mathf.Clamp01(0.35f + t.sndAccel / 3f);
		if (flag)
		{
			num5 = Mathf.Max(num5, 0.9f);
		}
		float num6 = Mathf.Lerp(0.6f, 1f, num5);
		v.engineAudio1.pitch = Mathf.Clamp(t.sndRpm / 880f, 0.7f, 2.6f);
		v.engineAudio1.volume = (1f - num4) * num6 * 0.9f;
		if ((Object)(object)v.engineAudio2 != (Object)null)
		{
			v.engineAudio2.pitch = Mathf.Clamp(t.sndRpm / 3000f, 0.45f, 2f);
			v.engineAudio2.volume = num4 * num6 * 0.9f;
		}
	}
}
[HarmonyPatch(typeof(VehicleController), "SetCarEffects")]
internal static class P_Vehicle_Effects
{
	private static void Postfix(VehicleController __instance)
	{
		WagonTag wagonTag = Tuning.Of(__instance);
		if ((Object)(object)wagonTag != (Object)null)
		{
			WagonSound.Engine(__instance, wagonTag);
		}
	}
}
public class WagonCabinGuard : MonoBehaviour
{
	public VehicleController vc;

	public Transform[] ejectPoints;

	private Transform rig;

	private float lastTip = -99f;

	private float poseCheck;

	private float poseFix;

	private const float HalfWidth = 0.66f;

	private const float ZMin = -1.97f;

	private const float ZMax = 0.85f;

	private const float FeetMin = 0.48f;

	private const float FeetMax = 1.8f;

	private const int VehicleLayerMask = 1073741824;

	private float lastSeatLog = -99f;

	private void Awake()
	{
		rig = ((Component)this).transform.Find("Rig");
	}

	private void LateUpdate()
	{
		//IL_007b: 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_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: 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)
		if (!Cfg.SeatedNoCollide.Value || (Object)(object)vc == (Object)null)
		{
			return;
		}
		GameNetworkManager instance = GameNetworkManager.Instance;
		PlayerControllerB val = (((Object)(object)instance != (Object)null) ? instance.localPlayerController : null);
		if ((Object)(object)val == (Object)null || !val.isPlayerControlled || !val.inSpecialInteractAnimation || (!vc.localPlayerInPassengerSeat && !vc.localPlayerInControl))
		{
			return;
		}
		CharacterController thisController = val.thisController;
		if ((Object)(object)thisController == (Object)null)
		{
			return;
		}
		LayerMask excludeLayers = ((Collider)thisController).excludeLayers;
		if ((((LayerMask)(ref excludeLayers)).value & 0x40000000) != 1073741824)
		{
			excludeLayers = ((Collider)thisController).excludeLayers;
			((Collider)thisController).excludeLayers = LayerMask.op_Implicit(((LayerMask)(ref excludeLayers)).value | 0x40000000);
			if (Time.time - lastSeatLog > 2f)
			{
				lastSeatLog = Time.time;
				Plugin.Log.LogInfo((object)"[Seat] the game re-enabled car collisions for the seated local player: switched off again");
			}
		}
	}

	public bool IsInside(Vector3 worldFeet)
	{
		//IL_0016: 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_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: 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_003e: 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_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)rig == (Object)null)
		{
			return false;
		}
		Vector3 val = rig.InverseTransformPoint(worldFeet) / Cfg.ModelScale.Value;
		if (Mathf.Abs(val.x) < 0.66f && val.z > -1.97f && val.z < 0.85f && val.y > 0.48f)
		{
			return val.y < 1.8f;
		}
		return false;
	}

	private void CheckPassengerPose(PlayerControllerB me)
	{
		if (!Cfg.PassengerPoseFix.Value || Time.time < poseCheck)
		{
			return;
		}
		poseCheck = Time.time + 0.25f;
		if (!vc.localPlayerInPassengerSeat || vc.localPlayerInControl || !me.inSpecialInteractAnimation)
		{
			return;
		}
		Animator playerBodyAnimator = me.playerBodyAnimator;
		if (!((Object)(object)playerBodyAnimator == (Object)null))
		{
			if (playerBodyAnimator.GetInteger("SA_CarAnim") != 0)
			{
				playerBodyAnimator.SetInteger("SA_CarAnim", 0);
			}
			int layerIndex = playerBodyAnimator.GetLayerIndex("SpecialAnimations");
			if (layerIndex >= 0 && !(Time.time < poseFix) && playerBodyAnimator.GetCurrentAnimatorClipInfo(layerIndex).Any((AnimatorClipInfo c) => (Object)(object)((AnimatorClipInfo)(ref c)).clip != (Object)null && ((Object)((AnimatorClipInfo)(ref c)).clip).name == "SitAndSteer"))
			{
				poseFix = Time.time + 1.5f;
				playerBodyAnimator.ResetTrigger("SA_Truck");
				playerBodyAnimator.SetTrigger("SA_Truck");
				Plugin.Log.LogInfo((object)"[Cabin] passenger was in the driver's hands-on-the-wheel pose: put back");
			}
		}
	}

	private void Update()
	{
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: 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_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: Unknown result type (might be due to invalid IL or missing references)
		if (!Cfg.KeepOutOfCabin.Value || (Object)(object)vc == (Object)null)
		{
			return;
		}
		GameNetworkManager instance = GameNetworkManager.Instance;
		PlayerControllerB me = (((Object)(object)instance != (Object)null) ? instance.localPlayerController : null);
		if ((Object)(object)me == (Object)null || !me.isPlayerControlled || me.isPlayerDead)
		{
			return;
		}
		CheckPassengerPose(me);
		if (me.inSpecialInteractAnimation || me.isCrouching || !IsInside(((Component)me).transform.position))
		{
			return;
		}
		Transform val = ((ejectPoints != null) ? ejectPoints.Where((Transform p) => (Object)(object)p != (Object)null).OrderBy(delegate(Transform p)
		{
			//IL_0001: 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_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val2 = p.position - ((Component)me).transform.position;
			return ((Vector3)(ref val2)).sqrMagnitude;
		}).FirstOrDefault() : null);
		if (!((Object)(object)val == (Object)null))
		{
			Plugin.Log.LogInfo((object)$"[Cabin] moved {me.playerUsername} out of the Wagon R body (was at {rig.InverseTransformPoint(((Component)me).transform.position) / Cfg.ModelScale.Value}) -> {((Object)val).name}");
			me.TeleportPlayer(val.position, false, 0f, false, true);
			if (Time.time - lastTip > 4f && (Object)(object)HUDManager.Instance != (Object)null)
			{
				lastTip = Time.time;
				HUDManager.Instance.DisplayTip("Wagon R", "No room to stand inside. Use the seats.", false, false, "LC_WagonRStand");
			}
		}
	}
}
internal static class Cfg
{
	public static ConfigEntry<int> Price;

	public static ConfigEntry<string> DisplayName;

	public static ConfigEntry<int> Hitpoints;

	public static ConfigEntry<float> Mass;

	public static ConfigEntry<float> TopSpeed;

	public static ConfigEntry<float> ReverseSpeed;

	public static ConfigEntry<float> Power;

	public static ConfigEntry<float> ComHeight;

	public static ConfigEntry<float> HighSpeedLock;

	public static ConfigEntry<float> LockFadeSpeed;

	public static ConfigEntry<float> SteerReturn;

	public static ConfigEntry<float> ModelScale;

	public static ConfigEntry<float> SeatBack;

	public static ConfigEntry<float> WheelTowardDriver;

	public static ConfigEntry<bool> LightTuning;

	public static ConfigEntry<float> CruiserPitch;

	public static ConfigEntry<float> TyreGrip;

	public static ConfigEntry<float> TyrePeakSlip;

	public static ConfigEntry<float> TyreTraction;

	public static ConfigEntry<float> YawInertia;

	public static ConfigEntry<float> SteeringSpeed;

	public static ConfigEntry<float> RollingDrag;

	public static ConfigEntry<float> AngularDrag;

	public static ConfigEntry<float> EngineBraking;

	public static ConfigEntry<float> BrakeTorque;

	public static ConfigEntry<float> MaxLateralAccel;

	public static ConfigEntry<bool> AutoGearbox;

	public static ConfigEntry<Key> HandbrakeKey;

	public static ConfigEntry<bool> DriverSeatOnRight;

	public static ConfigEntry<bool> KeepOutOfCabin;

	public static ConfigEntry<bool> PassengerPoseFix;

	public static ConfigEntry<bool> SeatedNoCollide;

	public static ConfigEntry<bool> TowEnabled;

	public static ConfigEntry<bool> VehiclesCollide;

	public static ConfigEntry<bool> CrashScaling;

	public static ConfigEntry<bool> SoftLandings;

	public static ConfigEntry<float> RopeLength;

	public static ConfigEntry<float> RopeStiffness;

	public static ConfigEntry<float> RopeDamping;

	public static ConfigEntry<float> RopeSnap;

	public static ConfigEntry<bool> Debug;

	public static void Bind(ConfigFile c)
	{
		Price = c.Bind<int>("Shop", "Price", 250, "Terminal price in credits (the Company Cruiser costs 370).");
		DisplayName = c.Bind<string>("Shop", "DisplayName", "Wagon R", "Name shown in the terminal store.");
		Hitpoints = c.Bind<int>("Car", "Hitpoints", 20, "Car health (the Cruiser has 30). It regenerates slowly above 16 like the Cruiser.");
		CrashScaling = c.Bind<bool>("Car", "CrashScaling", true, "The crash force that counts as a bump / a hard hit is scaled with the car's mass (LightMass / 200), so the same speed into a wall hurts the Wagon R as much as it hurts the Cruiser. A hit's force grows with the car's mass, so without this the lighter Wagon would be harder to damage. false = the Cruiser's thresholds as they are.");
		SoftLandings = c.Bind<bool>("Car", "SoftLandings", true, "Landing on a floor / ramp / the ground (a surface that faces up) is judged by the speed INTO the surface instead of the car's whole speed, and one landing counts once. Without it a jump that lands at 30 m/s forwards + 13 m/s down is above the Cruiser's 31 m/s 'near-destruction' limit and wrecks a small car in one go. A fall adds damage by its speed instead: nothing up to 14 m/s into the ground, 2 hit points at 20 m/s, 4 at 25 m/s, at most 6. Walls are not affected. false = the Cruiser's rules.");
		Mass = c.Bind<float>("Car", "LightMass", 140f, "Rigidbody mass (the Cruiser is 200). Lighter = zippier. The suspension is scaled with it. Must be the same for every player in the lobby.");
		ModelScale = c.Bind<float>("Car", "ModelScale", 1.6f, "Size of the car relative to the model file (1.0 = a real hatchback). The seated pose is a baked animation made for the Cruiser's big cab, so values around 1.5-1.7 fit best. Must be the same for every player in the lobby.");
		SeatBack = c.Bind<float>("Car", "SeatBackOffset", 0.2f, "Metres the seated player is moved back from the Cruiser-derived position (more room to the wheel; the baked hand animation then reaches slightly).");
		WheelTowardDriver = c.Bind<float>("Car", "WheelTowardDriver", 0.08f, "Metres the steering wheel is moved towards the driver so the hands still reach it after SeatBackOffset.");
		CruiserPitch = c.Bind<float>("Sound", "CruiserPitch", 1.2f, "Every sound except the engine is the Company Cruiser's own recording, pitched up by this factor (1.0 = unchanged, 1.2 = 20% higher).");
		LightTuning = c.Bind<bool>("Handling", "SmallCarTuning", true, "Handling of a small light hatchback instead of the Cruiser's big soft truck: tyres that bite (the Cruiser's peak at a 35 degree slip angle), a lower yaw inertia, quicker steering, rolling resistance and engine braking. false = the Cruiser's tuning.");
		TyreGrip = c.Bind<float>("Handling", "TyreGrip", 1.3f, "Maximum side grip of the tyres (about 1.3 g). The Cruiser's tyres give 2.0 g, which is why it corners like a rail and tips over if you push a lighter body.");
		TyrePeakSlip = c.Bind<float>("Handling", "TyrePeakSlip", 0.25f, "Slip at which the tyres give their maximum side force. Lower = crisper turn-in, but twitchier (the Cruiser: 0.7).");
		TyreTraction = c.Bind<float>("Handling", "TyreTraction", 1.5f, "Forward (drive/brake) tyre stiffness, all four wheels (the Cruiser: 2.0 front, 0.75 rear).");
		YawInertia = c.Bind<float>("Handling", "YawInertia", 0.65f, "Multiplier on the car's resistance to turning about its vertical axis. Lower = changes direction faster.");
		SteeringSpeed = c.Bind<float>("Handling", "SteeringSpeed", 6.5f, "How fast the steering wheel winds while a steering key is held (the Cruiser: 4).");
		RollingDrag = c.Bind<float>("Handling", "RollingDrag", 0.01f, "Air + rolling resistance (linear drag). 0.01 is the Cruiser's: it coasts almost forever. A higher value (0.05) makes the car slow down quicker when you lift off.");
		AngularDrag = c.Bind<float>("Handling", "AngularDrag", 1f, "Damping of the car's rotation (the Cruiser: 0.5).");
		EngineBraking = c.Bind<float>("Handling", "EngineBraking", 40f, "Brake torque per wheel (Nm) applied in Drive/Reverse when no pedal is pressed, so the car slows down when you lift off.");
		MaxLateralAccel = c.Bind<float>("Handling", "MaxCornerAccel", 22f, "The steering lock is reduced with speed so that the turn it asks for never needs more sideways acceleration than this (m/s2). The Cruiser's wheels never slip, so an unlimited lock at speed rolls a light car over (tested: it flips at 26 m/s with 13 degrees).");
		BrakeTorque = c.Bind<float>("Handling", "BrakeTorque", 450f, "Brake torque per wheel (Nm) while braking: about 20 m/s2, stopping from 22 m/s in roughly a second (the world's gravity is 32 m/s2, so this is a firm but not instant stop). The Cruiser uses 2000, which stops it in under half a second.");
		AutoGearbox = c.Bind<bool>("Handling", "AutomaticGearbox", true, "W selects Drive from Park, S brakes and then reverses (S = gas while in Reverse). The gear lever on the console still works. false = Cruiser behaviour (lever only).");
		TopSpeed = c.Bind<float>("Handling", "TopSpeed", 100f, "Soft top speed in m/s: the motors fade out over the last quarter of it. 100 means no limit of its own: the game caps every vehicle at 50 m/s (180 km/h) anyway. Use 30 (108 km/h) for a slower, more hatchback-like car. It also stretches the gears of the engine sound.");
		ReverseSpeed = c.Bind<float>("Handling", "ReverseSpeed", 9f, "Top reverse speed in m/s.");
		Power = c.Bind<float>("Handling", "Power", 0.45f, "Multiplier on the Cruiser's motor torque (it is clamped to 325..1000 Nm per wheel in vanilla). Lower = slower acceleration.");
		ComHeight = c.Bind<float>("Handling", "CenterOfMassHeight", 0.5f, "Centre of mass height above the ground in metres. Lower = harder to roll.");
		HighSpeedLock = c.Bind<float>("Handling", "HighSpeedSteerLimit", 0.9f, "Steering input limit at high speed (3 = full 45 degrees lock, 0.9 = 13.5 degrees).");
		LockFadeSpeed = c.Bind<float>("Handling", "SteerLockFadeSpeed", 22f, "Speed in m/s at which the high-speed steering limit is fully applied.");
		SteerReturn = c.Bind<float>("Handling", "SteerReturnRate", 5f, "How fast the steering re-centres when no steering key is held (the Cruiser never re-centres).");
		HandbrakeKey = c.Bind<Key>("Controls", "HandbrakeKey", (Key)30, "Keyboard key that pulls / releases the handbrake while you drive (the lever between the seats can be clicked too). Use a Key name of the Unity Input System: P, H, Space, LeftAlt ...");
		DriverSeatOnRight = c.Bind<bool>("Car", "DriverSeatOnRight", true, "India-spec Wagon R is right-hand drive, like the model. false mirrors the seats (the car model itself is not mirrored).");
		KeepOutOfCabin = c.Bind<bool>("Car", "KeepOutOfCabin", true, "A player who stands (not seated) inside the car body is moved out next to the nearest door. The Cruiser is an open truck; the Wagon has a roof and floor, and standing in it makes the car's physics fight the player and throws the car into the air.");
		PassengerPoseFix = c.Bind<bool>("Car", "PassengerPoseFix", true, "A passenger never sits with the driver's hands-on-the-wheel pose: if the game puts the driver's hand animation on a passenger it is taken off again.");
		SeatedNoCollide = c.Bind<bool>("Car", "SeatedPlayerNoCollide", true, "A player sitting in the car never collides with its body. The game re-enables that collision for a seated passenger whenever the car's physics moves to their machine (for example when the driver gets out); the Wagon has a solid body, so the squeeze launches the car into the air.");
		VehiclesCollide = c.Bind<bool>("Car", "VehiclesCollide", true, "Vehicles collide with each other (a Wagon R with a Cruiser or another Wagon R), through duplicates of their hulls on a private physics layer. Must be the same for every player.");
		TowEnabled = c.Bind<bool>("Tow", "Enabled", true, "Tow hitch on the Wagon R: hook a rope to another Wagon R or a Company Cruiser. Must be the same for every player.");
		RopeLength = c.Bind<float>("Tow", "RopeLength", 6f, "Length of the tow rope in metres, between the two tie points.");
		RopeStiffness = c.Bind<float>("Tow", "RopeStiffness", 9000f, "How hard the rope pulls once it is stretched (N per metre). Higher = less bounce, harder jerks.");
		RopeDamping = c.Bind<float>("Tow", "RopeDamping", 100f, "Damping of the rope's pull (N s per metre). Lower = bouncier.");
		RopeSnap = c.Bind<float>("Tow", "SnapStretch", 0f, "The rope snaps when it is stretched this many metres beyond its length. 0 = it never snaps (tug of war between two vehicles).");
		Debug = c.Bind<bool>("Debug", "VerboseBuildLog", false, "Log every node the vehicle builder touches.");
	}
}
public class WagonHandbrake : MonoBehaviour
{
	public VehicleController vc;

	public InteractTrigger trigger;

	public Transform lever;

	public const float ReleasedAngle = 14f;

	private const float Speed = 110f;

	private Quaternion baseRot;

	private float angle;

	private bool init;

	private bool lastOn;

	private bool Engaged => HandbrakeNet.On(vc);

	private float Target
	{
		get
		{
			if (!Engaged)
			{
				return 14f;
			}
			return 0f;
		}
	}

	public float Angle => angle;

	private void Update()
	{
		//IL_0092: 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_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: 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)
		if ((Object)(object)vc == (Object)null)
		{
			return;
		}
		bool engaged = Engaged;
		if ((Object)(object)lever != (Object)null)
		{
			if (!init)
			{
				baseRot = lever.localRotation;
				angle = Target;
				lastOn = engaged;
				init = true;
			}
			angle = Mathf.MoveTowards(angle, Target, 110f * Time.deltaTime);
			lever.localRotation = Quaternion.Euler(angle, 0f, 0f) * baseRot;
		}
		if (engaged != lastOn)
		{
			lastOn = engaged;
			try
			{
				if ((Object)(object)vc.gearStickAudio != (Object)null && vc.gearStickAudios != null && vc.gearStickAudios.Length != 0)
				{
					vc.gearStickAudio.PlayOneShot(vc.gearStickAudios[0]);
				}
			}
			catch
			{
			}
		}
		if ((Object)(object)trigger != (Object)null)
		{
			string text = (vc.carDestroyed ? "" : (engaged ? "Release handbrake : [LMB]" : "Pull handbrake : [LMB]"));
			if (trigger.hoverTip != text)
			{
				trigger.hoverTip = text;
			}
		}
	}

	private void FixedUpdate()
	{
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Invalid comparison between Unknown and I4
		if (!((Object)(object)vc == (Object)null) && ((NetworkBehaviour)vc).IsOwner && !vc.localPlayerInControl && !((Object)(object)vc.currentDriver != (Object)null) && !vc.carDestroyed && !vc.magnetedToShip && vc.hasBeenSpawned && (!((Object)(object)TowManager.Instance != (Object)null) || TowManager.Instance.LinkOf(vc) == null))
		{
			bool flag = (int)vc.gear == 3;
			bool engaged = Engaged;
			Hold(vc.FrontLeftWheel, flag, handbrake: false);
			Hold(vc.FrontRightWheel, flag, handbrake: false);
			Hold(vc.BackLeftWheel, flag || engaged, engaged);
			Hold(vc.BackRightWheel, flag || engaged, engaged);
		}
	}

	private static void Hold(WheelCollider w, bool locked, bool handbrake)
	{
		if (!((Object)(object)w == (Object)null))
		{
			w.brakeTorque = (handbrake ? 20000f : (locked ? 2000f : 0f));
			w.motorTorque = (locked ? 0f : 0.0001f);
		}
	}

	public bool Use(PlayerControllerB who)
	{
		PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
		if ((Object)(object)val == (Object)null || (Object)(object)who != (Object)(object)val || (Object)(object)vc == (Object)null || vc.carDestroyed || (Object)(object)HandbrakeNet.Instance == (Object)null)
		{
			return true;
		}
		HandbrakeNet.Instance.Request(vc, !Engaged);
		return true;
	}

	public static void PollKey(VehicleController v)
	{
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		Keyboard current = Keyboard.current;
		PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
		if (current != null && !((Object)(object)val == (Object)null) && !((Object)(object)HandbrakeNet.Instance == (Object)null) && !v.carDestroyed && ((ButtonControl)current[Cfg.HandbrakeKey.Value]).wasPressedThisFrame && !val.isTypingChat && !val.inTerminalMenu && (!((Object)(object)val.quickMenuManager != (Object)null) || !val.quickMenuManager.isMenuOpen))
		{
			HandbrakeNet.Instance.Request(v, !HandbrakeNet.On(v));
		}
	}
}
public class HandbrakeNet : MonoBehaviour
{
	public static HandbrakeNet Instance;

	public const float LockTorque = 20000f;

	private const string ReqName = "um.lcwagonr.hb.req";

	private const string SyncName = "um.lcwagonr.hb.sync";

	private readonly Dictionary<ulong, bool> state = new Dictionary<ulong, bool>();

	private object registeredFor;

	private Action<ulong> connectedHandler;

	private float nextPrune;

	private static NetworkManager NM => NetworkManager.Singleton;

	private static bool IsServer
	{
		get
		{
			NetworkManager nM = NM;
			if ((Object)(object)nM != (Object)null && nM.IsListening)
			{
				return nM.IsServer;
			}
			return false;
		}
	}

	public static void Create()
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		if (!((Object)(object)Instance != (Object)null))
		{
			GameObject val = new GameObject("WagonR.Handbrake");
			Object.DontDestroyOnLoad((Object)val);
			Instance = val.AddComponent<HandbrakeNet>();
		}
	}

	public static ulong IdOf(VehicleController v)
	{
		if (!((Object)(object)v != (Object)null) || !((Object)(object)((NetworkBehaviour)v).NetworkObject != (Object)null) || !((NetworkBehaviour)v).NetworkObject.IsSpawned)
		{
			return 0uL;
		}
		return ((NetworkBehaviour)v).NetworkObject.NetworkObjectId;
	}

	public static bool On(VehicleController v)
	{
		HandbrakeNet instance = Instance;
		ulong num = IdOf(v);
		bool value = default(bool);
		return (Object)(object)instance != (Object)null && num != 0L && instance.state.TryGetValue(num, out value) && value;
	}

	public unsafe void Request(VehicleController v, bool on)
	{
		//IL_004f: 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_0061: 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_007a: Unknown result type (might be due to invalid IL or missing references)
		ulong num = IdOf(v);
		if (num == 0L)
		{
			return;
		}
		state[num] = on;
		if (IsServer)
		{
			Broadcast();
			return;
		}
		NetworkManager nM = NM;
		if ((Object)(object)nM == (Object)null || nM.CustomMessagingManager == null)
		{
			return;
		}
		FastBufferWriter val = default(FastBufferWriter);
		((FastBufferWriter)(ref val))..ctor(32, (Allocator)2, -1);
		try
		{
			((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref num, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref on, default(ForPrimitives));
			nM.CustomMessagingManager.SendNamedMessage("um.lcwagonr.hb.req", 0uL, val, (NetworkDelivery)3);
		}
		finally
		{
			((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
		}
	}

	private void OnRequest(ulong sender, FastBufferReader r)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		if (IsServer)
		{
			ulong key = default(ulong);
			((FastBufferReader)(ref r)).ReadValueSafe<ulong>(ref key, default(ForPrimitives));
			bool value = default(bool);
			((FastBufferReader)(ref r)).ReadValueSafe<bool>(ref value, default(ForPrimitives));
			state[key] = value;
			Broadcast();
		}
	}

	private unsafe void Broadcast()
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: 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_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: 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_00c2: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager nM = NM;
		if ((Object)(object)nM == (Object)null || nM.CustomMessagingManager == null)
		{
			return;
		}
		FastBufferWriter val = default(FastBufferWriter);
		((FastBufferWriter)(ref val))..ctor(16 + state.Count * 16, (Allocator)2, -1);
		try
		{
			int count = state.Count;
			((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref count, default(ForPrimitives));
			foreach (KeyValuePair<ulong, bool> item in state)
			{
				ulong key = item.Key;
				((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref key, default(ForPrimitives));
				bool value = item.Value;
				((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref value, default(ForPrimitives));
			}
			nM.CustomMessagingManager.SendNamedMessageToAll("um.lcwagonr.hb.sync", val, (NetworkDelivery)3);
		}
		finally
		{
			((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
		}
	}

	private void OnSync(ulong sender, FastBufferReader r)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: 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_0035: 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_0047: Unknown result type (might be due to invalid IL or missing references)
		if (!IsServer)
		{
			int num = default(int);
			((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num, default(ForPrimitives));
			state.Clear();
			ulong key = default(ulong);
			bool value = default(bool);
			for (int i = 0; i < num; i++)
			{
				((FastBufferReader)(ref r)).ReadValueSafe<ulong>(ref key, default(ForPrimitives));
				((FastBufferReader)(ref r)).ReadValueSafe<bool>(ref value, default(ForPrimitives));
				state[key] = value;
			}
		}
	}

	private void Register()
	{
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Expected O, but got Unknown
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Expected O, but got Unknown
		NetworkManager nM = NM;
		if ((Object)(object)nM == (Object)null || !nM.IsListening || nM.CustomMessagingManager == null)
		{
			if (registeredFor != null)
			{
				registeredFor = null;
				state.Clear();
			}
		}
		else
		{
			if (registeredFor == nM.CustomMessagingManager)
			{
				return;
			}
			registeredFor = nM.CustomMessagingManager;
			state.Clear();
			nM.CustomMessagingManager.RegisterNamedMessageHandler("um.lcwagonr.hb.req", new HandleNamedMessageDelegate(OnRequest));
			nM.CustomMessagingManager.RegisterNamedMessageHandler("um.lcwagonr.hb.sync", new HandleNamedMessageDelegate(OnSync));
			if (nM.IsServer)
			{
				connectedHandler = delegate
				{
					if (state.Count > 0)
					{
						Broadcast();
					}
				};
				nM.OnClientConnectedCallback += connectedHandler;
			}
			Plugin.Log.LogInfo((object)("[Handbrake] network messages registered (" + (nM.IsServer ? "host" : "client") + ")"));
		}
	}

	private void Update()
	{
		Register();
		if (!IsServer || !(Time.time > nextPrune))
		{
			return;
		}
		nextPrune = Time.time + 5f;
		NetworkManager nM = NM;
		List<ulong> list = new List<ulong>();
		foreach (ulong key in state.Keys)
		{
			if (nM.SpawnManager == null || !nM.SpawnManager.SpawnedObjects.TryGetValue(key, out var value) || (Object)(object)value == (Object)null)
			{
				list.Add(key);
			}
		}
		if (list.Count <= 0)
		{
			return;
		}
		foreach (ulong item in list)
		{
			state.Remove(item);
		}
		Broadcast();
	}
}
[HarmonyPatch(typeof(VehicleController), "OnCollisionEnter")]
internal static class P_Vehicle_Landing
{
	public static bool Trace;

	private static readonly MethodInfo Damage = AccessTools.Method(typeof(VehicleController), "DealPermanentDamage", (Type[])null, (Type[])null);

	public static int FallDamage(float into)
	{
		return Mathf.Clamp(Mathf.RoundToInt((into - 14f) / 3f), 0, 6);
	}

	private static bool Prefix(VehicleController __instance, Collision collision, out Vector3 __state)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: 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_0056: 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_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: 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_007f: 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_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_0159: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0202: Unknown result type (might be due to invalid IL or missing references)
		//IL_0208: Unknown result type (might be due to invalid IL or missing references)
		__state = __instance.averageVelocity;
		WagonTag wagonTag = Tuning.Of(__instance);
		if ((Object)(object)wagonTag == (Object)null || !Cfg.SoftLandings.Value || !((NetworkBehaviour)__instance).IsOwner || ((Component)collision.collider).gameObject.layer != 8)
		{
			return true;
		}
		int contactCount = collision.contactCount;
		if (contactCount == 0)
		{
			return true;
		}
		Vector3 val = Vector3.zero;
		for (int i = 0; i < contactCount; i++)
		{
			Vector3 val2 = val;
			ContactPoint contact = collision.GetContact(i);
			val = val2 + ((ContactPoint)(ref contact)).normal;
		}
		val /= (float)contactCount;
		if (val.y < 0.6f)
		{
			return true;
		}
		((Vector3)(ref val)).Normalize();
		float num = ((wagonTag.Airborne || Time.time - wagonTag.touchdown < 0.5f) ? wagonTag.fallPeak : 0f);
		float num2 = Mathf.Max(Mathf.Abs(Vector3.Dot(collision.relativeVelocity, val)), num);
		if (Time.time - wagonTag.lastLanding < 0.6f)
		{
			return false;
		}
		if (num2 >= 4f)
		{
			wagonTag.lastLanding = Time.time;
		}
		__instance.averageVelocity = ((Vector3)(ref __state)).normalized * Mathf.Min(((Vector3)(ref __state)).magnitude, num2);
		int num3 = FallDamage(num2);
		if (Trace && num2 >= 6f)
		{
			Plugin.Log.LogInfo((object)$"[Landing] speed into the surface {Mathf.Abs(Vector3.Dot(collision.relativeVelocity, val)):0.0} at the first touch, fastest fall since take-off {num:0.0} (average velocity {__state.x:0.0}, {__state.y:0.0}, {__state.z:0.0}) -> judged {num2:0.0}, fall damage {num3}, hp {__instance.carHP}");
		}
		if (num3 > 0 && Damage != null && !__instance.carDestroyed)
		{
			try
			{
				Damage.Invoke(__instance, new object[2]
				{
					num3,
					(object)default(Vector3)
				});
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[Landing] fall damage failed: " + ex.Message));
			}
		}
		return true;
	}

	private static void Postfix(VehicleController __instance, Vector3 __state)
	{
		//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)
		__instance.averageVelocity = __state;
	}
}
public class WagonHinge : MonoBehaviour
{
	public AnimatedObjectTrigger trigger;

	public VehicleController hoodOf;

	public GameObject showWhenClosed;

	public GameObject showWhenOpen;

	public Collider enableWhenClosed;

	public Vector3 axis = Vector3.up;

	public float openAngle = 68f;

	public float speed = 200f;

	public AudioSource sfx;

	public AudioClip[] openClips;

	public AudioClip[] closeClips;

	private float cur;

	private int last = -1;

	private bool closing;

	private static readonly FieldInfo CarHoodOpen = AccessTools.Field(typeof(VehicleController), "carHoodOpen");

	private bool IsOpen()
	{
		if ((Object)(object)hoodOf != (Object)null)
		{
			return (bool)CarHoodOpen.GetValue(hoodOf);
		}
		if ((Object)(object)trigger != (Object)null)
		{
			return trigger.boolValue;
		}
		return false;
	}

	private void Update()
	{
		//IL_00e3: 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)
		bool flag = IsOpen();
		if ((flag ? 1 : 0) != last)
		{
			bool num = last < 0;
			last = (flag ? 1 : 0);
			if ((Object)(object)showWhenClosed != (Object)null)
			{
				showWhenClosed.SetActive(!flag);
			}
			if ((Object)(object)showWhenOpen != (Object)null)
			{
				showWhenOpen.SetActive(flag);
			}
			if ((Object)(object)enableWhenClosed != (Object)null)
			{
				enableWhenClosed.enabled = !flag;
			}
			if (!num)
			{
				closing = !flag;
				if (flag)
				{
					Play(openClips);
				}
			}
		}
		float num2 = (flag ? openAngle : 0f);
		if (!Mathf.Approximately(cur, num2))
		{
			cur = Mathf.MoveTowards(cur, num2, speed * Time.deltaTime);
			((Component)this).transform.localRotation = Quaternion.AngleAxis(cur, axis);
			if (closing && Mathf.Approximately(cur, 0f))
			{
				closing = false;
				Play(closeClips);
			}
		}
	}

	private void Play(AudioClip[] set)
	{
		if ((Object)(object)sfx != (Object)null && set != null && set.Length != 0)
		{
			sfx.PlayOneShot(set[Random.Range(0, set.Length)]);
		}
	}
}
public class WagonSteering : MonoBehaviour
{
	public VehicleController vc;

	public Quaternion baseRot;

	public float sign = 1f;

	public float lockToLock = 720f;

	private void LateUpdate()
	{
		//IL_0054: 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)
		if (!((Object)(object)vc == (Object)null) && !((Object)(object)vc.steeringWheelAnimator == (Object)null))
		{
			float num = (0f - (vc.steeringWheelAnimator.GetFloat("steeringWheelTurnSpeed") - 0.5f)) * lockToLock * sign;
			((Component)this).transform.localRotation = baseRot * Quaternion.Euler(0f, num, 0f);
		}
	}
}
public class WagonTag : MonoBehaviour
{
	public VehicleController vc;

	public GameObject intact;

	public GameObject wreck;

	public float lastAutoShift;

	public float lastLanding = -99f;

	public float fallPeak;

	public float touchdown = -99f;

	private bool airborne;

	public float wheelbase = 4f;

	public float sndRpm = 900f;

	public float sndLastSpeed;

	public float sndAccel;

	public float sndShift;

	public int sndGear = 1;

	private bool shown;

	private static readonly FieldInfo AnimFloat = AccessTools.Field(typeof(VehicleController), "steeringWheelAnimFloat");

	public readonly float[] roll = new float[4];

	public readonly float[] rollRate = new float[4];

	private Vector3 lastPos;

	private bool hasLast;

	private float speedFiltered;

	public bool Airborne => airborne;

	private void FixedUpdate()
	{
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)vc == (Object)null || !((NetworkBehaviour)vc).IsOwner || (Object)(object)vc.mainRigidbody == (Object)null)
		{
			return;
		}
		if (!vc.FrontLeftWheel.isGrounded && !vc.FrontRightWheel.isGrounded && !vc.BackLeftWheel.isGrounded && !vc.BackRightWheel.isGrounded)
		{
			if (!airborne)
			{
				airborne = true;
				fallPeak = 0f;
			}
			fallPeak = Mathf.Max(fallPeak, 0f - vc.mainRigidbody.velocity.y);
		}
		else if (airborne)
		{
			airborne = false;
			touchdown = Time.time;
		}
	}

	private void LateUpdate()
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: 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_00c6: 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_0076: 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_007d: 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_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0211: Unknown result type (might be due to invalid IL or missing references)
		//IL_0216: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)vc == (Object)null)
		{
			return;
		}
		float deltaTime = Time.deltaTime;
		if (deltaTime <= 0f)
		{
			return;
		}
		Vector3 position = ((Component)vc).transform.position;
		float num = ((((NetworkBehaviour)vc).IsOwner && (Object)(object)vc.mainRigidbody != (Object)null && !vc.mainRigidbody.isKinematic) ? Vector3.Dot(vc.mainRigidbody.velocity, ((Component)vc).transform.forward) : (hasLast ? Vector3.Dot((position - lastPos) / deltaTime, ((Component)vc).transform.forward) : 0f));
		lastPos = position;
		hasLast = true;
		speedFiltered = Mathf.Lerp(speedFiltered, num, 1f - Mathf.Exp((0f - deltaTime) / 0.08f));
		float num2 = (float)AnimFloat.GetValue(vc) * 45f;
		MeshRenderer[] array = (MeshRenderer[])(object)new MeshRenderer[4] { vc.leftWheelMesh, vc.rightWheelMesh, vc.backLeftWheelMesh, vc.backRightWheelMesh };
		float num3 = Mathf.Max(0.05f, vc.FrontLeftWheel.radius);
		float num4 = speedFiltered / (MathF.PI * 2f * num3) * 60f;
		for (int i = 0; i < 4; i++)
		{
			if (!((Object)(object)array[i] == (Object)null))
			{
				rollRate[i] = num4 * 6f;
				roll[i] = Mathf.Repeat(roll[i] + rollRate[i] * deltaTime, 360f);
				((Component)array[i]).transform.localRotation = Quaternion.Euler(0f, (i < 2) ? num2 : 0f, 0f) * Quaternion.Euler(roll[i], 0f, 0f);
			}
		}
	}

	private void Update()
	{
		if (!shown && !((Object)(object)vc == (Object)null) && vc.carDestroyed)
		{
			shown = true;
			if ((Object)(object)intact != (Object)null)
			{
				intact.SetActive(false);
			}
			if ((Object)(object)wreck != (Object)null)
			{
				wreck.SetActive(true);
			}
		}
	}
}
[BepInPlugin("um.lcwagonr", "Wagon R", "1.4.0")]
public class Plugin : BaseUnityPlugin
{
	public const string Guid = "um.lcwagonr";

	internal static ManualLogSource Log;

	internal static Plugin Instance;

	private void Awake()
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		Log = ((BaseUnityPlugin)this).Logger;
		Instance = this;
		Cfg.Bind(((BaseUnityPlugin)this).Config);
		new Harmony("um.lcwagonr").PatchAll();
		Log.LogInfo((object)"Wagon R loaded");
	}
}
public class WagonRearDoor : MonoBehaviour
{
	public VehicleController vc;

	public InteractTrigger trigger;

	public InteractTrigger seat;

	public AnimatedObjectTrigger state;

	public Transform[] exitPoints;

	private string useTip;

	private static readonly FieldInfo ExitMask = AccessTools.Field(typeof(VehicleController), "exitCarLayerMask");

	private void Awake()
	{
		if ((Object)(object)trigger != (Object)null)
		{
			useTip = trigger.hoverTip;
		}
	}

	public bool Use(PlayerControllerB who)
	{
		PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
		if ((Object)(object)val == (Object)null || (Object)(object)who != (Object)(object)val || !val.inSpecialInteractAnimation || (Object)(object)val.currentTriggerInAnimationWith == (Object)null || (Object)(object)state == (Object)null || (Object)(object)trigger == (Object)null)
		{
			return false;
		}
		if (!trigger.interactable)
		{
			return true;
		}
		if (trigger.interactCooldown)
		{
			if (trigger.currentCooldownValue >= 0f)
			{
				return true;
			}
			trigger.currentCooldownValue = trigger.cooldownTime;
		}
		bool num = (Object)(object)val.currentTriggerInAnimationWith == (Object)(object)seat && !state.boolValue;
		state.TriggerAnimation(val);
		if (num)
		{
			Exit(val);
		}
		return true;
	}

	private void Exit(PlayerControllerB me)
	{
		//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)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		if (exitPoints != null && exitPoints.Length != 0)
		{
			int mask = ((ExitMask != null) ? ((int)ExitMask.GetValue(vc)) : 2305);
			Vector3 cam = ((Component)me.gameplayCamera).transform.position;
			Transform val = ((IEnumerable<Transform>)exitPoints).FirstOrDefault((Func<Transform, bool>)((Transform p) => (Object)(object)p != (Object)null && !Physics.Linecast(cam, p.position, mask, (QueryTriggerInteraction)1))) ?? ((IEnumerable<Transform>)exitPoints).FirstOrDefault((Func<Transform, bool>)((Transform p) => (Object)(object)p != (Object)null));
			if (!((Object)(object)val == (Object)null))
			{
				me.TeleportPlayer(val.position, false, 0f, false, true);
			}
		}
	}

	private void Update()
	{
		PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
		if (!((Object)(object)val == (Object)null) && !((Object)(object)trigger == (Object)null) && !((Object)(object)state == (Object)null))
		{
			string text = useTip;
			if (val.inSpecialInteractAnimation && (Object)(object)val.currentTriggerInAnimationWith == (Object)(object)seat)
			{
				text = (state.boolValue ? "Close door : [LMB]" : "Exit : [LMB]");
			}
			if (trigger.hoverTip != text && !string.IsNullOrEmpty(text))
			{
				trigger.hoverTip = text;
			}
		}
	}
}
public class WagonRearSeat : MonoBehaviour
{
	public VehicleController vc;

	public InteractTrigger seat;

	public AnimatedObjectTrigger door;

	private bool was;

	private void Update()
	{
		if (!((Object)(object)seat == (Object)null))
		{
			bool isPlayingSpecialAnimation = seat.isPlayingSpecialAnimation;
			if (isPlayingSpecialAnimation && !was && (Object)(object)door != (Object)null && door.boolValue)
			{
				door.SetBoolOnClientOnly(false);
			}
			was = isPlayingSpecialAnimation;
			InteractTrigger val = (((Object)(object)vc != (Object)null) ? vc.passengerSeatTrigger : null);
			if ((Object)(object)val != (Object)null && !val.interactable && !val.isPlayingSpecialAnimation && !vc.carDestroyed)
			{
				val.interactable = true;
			}
		}
	}
}
[HarmonyPatch(typeof(InteractTrigger), "Interact")]
internal static class P_Interact_RearDoor
{
	private static bool Prefix(InteractTrigger __instance, Transform playerTransform)
	{
		if ((Object)(object)TowManager.Instance != (Object)null && (Object)(object)TowManager.RopeFrom != (Object)null && TowManager.IsPushSpot(__instance))
		{
			PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
			if ((Object)(object)val != (Object)null && (Object)(object)playerTransform == (Object)(object)((Component)val).transform && TowManager.Instance.TryAttachAtPushSpot(__instance))
			{
				return false;
			}
		}
		WagonHandbrake component = ((Component)__instance).GetComponent<WagonHandbrake>();
		if ((Object)(object)component != (Object)null)
		{
			component.Use(((Object)(object)playerTransform != (Object)null) ? ((Component)playerTransform).GetComponent<PlayerControllerB>() : null);
			return false;
		}
		WagonHitch component2 = ((Component)__instance).GetComponent<WagonHitch>();
		if ((Object)(object)component2 != (Object)null)
		{
			PlayerControllerB who = (((Object)(object)playerTransform != (Object)null) ? ((Component)playerTransform).GetComponent<PlayerControllerB>() : null);
			component2.Use(who);
			return false;
		}
		WagonRearDoor component3 = ((Component)__instance).GetComponent<WagonRearDoor>();
		if (Cfg.Debug.Value && ((Object)(object)component3 != (Object)null || (Object)(object)((Component)__instance).GetComponent<WagonRearSeat>() != (Object)null))
		{
			PlayerControllerB val2 = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
			StackFrame[] frames = new StackTrace(1, fNeedFileInfo: false).GetFrames();
			string text = string.Join(" < ", from f in frames.Take(4)
				select f.GetMethod().DeclaringType?.Name + "." + f.GetMethod().Name);
			Plugin.Log.LogInfo((object)string.Format("[Interact] {0}: seated={1} inSeat={2} called from {3}", ((Object)__instance).name, (Object)(object)val2 != (Object)null && val2.inSpecialInteractAnimation, ((Object)(object)val2 != (Object)null && (Object)(object)val2.currentTriggerInAnimationWith != (Object)null) ? ((Object)val2.currentTriggerInAnimationWith).name : "-", text));
		}
		if ((Object)(object)component3 == (Object)null)
		{
			return true;
		}
		PlayerControllerB who2 = (((Object)(object)playerTransform != (Object)null) ? ((Component)playerTransform).GetComponent<PlayerControllerB>() : null);
		return !component3.Use(who2);
	}
}
internal static class Registration
{
	public static GameObject Prefab;

	public static bool Failed;

	public static VehicleController PrefabController
	{
		get
		{
			if (!((Object)(object)Prefab != (Object)null))
			{
				return null;
			}
			return Prefab.GetComponent<VehicleController>();
		}
	}

	public static void BuildAndRegister(NetworkManager nm)
	{
		if ((Object)(object)Prefab != (Object)null || Failed)
		{
			return;
		}
		try
		{
			if (!WagonAssets.Load())
			{
				Plugin.Log.LogError((object)("Wagon R disabled: " + WagonAssets.Error));
				Failed = true;
				return;
			}
			GameObject val = nm.NetworkConfig.Prefabs.Prefabs.Select((NetworkPrefab p) => p.Prefab).FirstOrDefault((Func<GameObject, bool>)((GameObject g) => (Object)(object)g != (Object)null && (Object)(object)g.GetComponent<VehicleController>() != (Object)null));
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogError((object)"Wagon R disabled: no vehicle prefab among the network prefabs");
				Failed = true;
				return;
			}
			WagonAssets.BuildMaterials(val);
			WagonAudio.Load();
			WagonAudio.PrepareCruiser(val.GetComponent<VehicleController>());
			Prefab = VehicleBuilder.Build(val);
			nm.AddNetworkPrefab(Prefab);
			Plugin.Log.LogInfo((object)"Wagon R network prefab registered");
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Wagon R disabled, build failed: " + ex));
			Failed = true;
			Prefab = null;
		}
	}

	public static void AddToVehiclesList(StartOfRound sor)
	{
		if (!((Object)(object)Prefab == (Object)null) && !sor.VehiclesList.Contains(Prefab))
		{
			int num = sor.VehiclesList.Length;
			sor.VehiclesList = sor.VehiclesList.Concat((IEnumerable<GameObject>)(object)new GameObject[1] { Prefab }).ToArray();
			Plugin.Log.LogInfo((object)$"VehiclesList: Wagon R at index {num}");
			AssignId(num);
		}
	}

	private static void AssignId(int idx)
	{
		VehicleController prefabController = PrefabController;
		if (!((Object)(object)prefabController == (Object)null))
		{
			if (prefabController.vehicleID != -1 && prefabController.vehicleID != idx)
			{
				Plugin.Log.LogWarning((object)$"Wagon R id mismatch: prefab has {prefabController.vehicleID}, list index is {idx} (another mod added vehicles?)");
			}
			prefabController.vehicleID = idx;
		}
	}

	public static void AddToTerminal(Terminal t)
	{
		//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)
		//IL_0063: 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_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Expected O, but got Unknown
		if ((Object)(object)Prefab == (Object)null || t.buyableVehicles.Any((BuyableVehicle b) => b != null && (Object)(object)b.vehiclePrefab == (Object)(object)Prefab))
		{
			return;
		}
		int num = t.buyableVehicles.Length;
		t.buyableVehicles = t.buyableVehicles.Concat((IEnumerable<BuyableVehicle>)(object)new BuyableVehicle[1]
		{
			new BuyableVehicle
			{
				vehiclePrefab = Prefab,
				secondaryPrefab = null,
				vehicleDisplayName = Cfg.DisplayName.Value,
				creditsWorth = Cfg.Price.Value
			}
		}).ToArray();
		Plugin.Log.LogInfo((object)$"Terminal.buyableVehicles: {Cfg.DisplayName.Value} at index {num}, ${Cfg.Price.Value}");
		AssignId(num);
		if (t.itemSalesPercentages != null && t.itemSalesPercentages.Length != 0)
		{
			int[] itemSalesPercentages = t.itemSalesPercentages;
			int[] array = Enumerable.Repeat(100, t.buyableItemsList.Length + t.buyableVehicles.Length).ToArray();
			Array.Copy(itemSalesPercentages, array, Math.Min(itemSalesPercentages.Length, array.Length));
			t.itemSalesPercentages = array;
		}
		try
		{
			AddKeywords(t, num);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("terminal keyword setup failed: " + ex));
		}
	}

	private static void AddKeywords(Terminal t, int idx)
	{
		//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cc: Expected O, but got Unknown
		//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ef: Expected O, but got Unknown
		TerminalNodesList terminalNodes = t.terminalNodes;
		TerminalKeyword val = terminalNodes.allKeywords.First((TerminalKeyword k) => k.word == "buy");
		TerminalNode result = val.compatibleNouns.First((CompatibleNoun n) => (Object)(object)n.noun != (Object)null && n.noun.word == "cruiser").result;
		CompatibleNoun val2 = result.terminalOptions.First((CompatibleNoun o) => o.noun.word == "confirm");
		CompatibleNoun val3 = result.terminalOptions.First((CompatibleNoun o) => o.noun.word == "deny");
		if (!terminalNodes.allKeywords.Any((TerminalKeyword k) => k.word == "wagonr"))
		{
			TerminalKeyword val4 = ScriptableObject.CreateInstance<TerminalKeyword>();
			((Object)val4).name = "WagonR";
			val4.word = "wagonr";
			val4.isVerb = false;
			val4.defaultVerb = val;
			TerminalNode val5 = Object.Instantiate<TerminalNode>(val2.result);
			((Object)val5).name = "buyWagonR2";
			val5.buyVehicleIndex = idx;
			val5.itemCost = Cfg.Price.Value;
			val5.displayText = "Ordered the " + Cfg.DisplayName.Value + ". Your new balance is [playerCredits].\n\nIt is delivered by the dropship, like the Cruiser. Only one vehicle can be on the moon at a time. Items cannot be purchased while the vehicle is en route.\n\n";
			TerminalNode val6 = Object.Instantiate<TerminalNode>(result);
			((Object)val6).name = "buyWagonR";
			val6.buyVehicleIndex = idx;
			val6.itemCost = Cfg.Price.Value;
			val6.displayText = "You have requested to order the " + Cfg.DisplayName.Value + ". \n[warranty] Total cost of items: [totalCost].\n\nPlease CONFIRM or DENY.\n\n";
			val6.terminalOptions = (CompatibleNoun[])(object)new CompatibleNoun[2]
			{
				new CompatibleNoun(val2.noun, val5),
				val3
			};
			val.compatibleNouns = val.compatibleNouns.Concat((IEnumerable<CompatibleNoun>)(object)new CompatibleNoun[1]
			{
				new CompatibleNoun(val4, val6)
			}).ToArray();
			terminalNodes.allKeywords = terminalNodes.allKeywords.Concat((IEnumerable<TerminalKeyword>)(object)new TerminalKeyword[1] { val4 }).ToArray();
			Plugin.Log.LogInfo((object)"Terminal: 'buy wagonr' added");
		}
	}
}
[HarmonyPatch(typeof(GameNetworkManager), "Start")]
internal static class P_GameNetworkManager_Start
{
	private static void Postfix(GameNetworkManager __instance)
	{
		NetworkManager component = ((Component)__instance).GetComponent<NetworkManager>();
		if ((Object)(object)component != (Object)null)
		{
			Registration.BuildAndRegister(component);
		}
		TowManager.Create();
		HandbrakeNet.Create();
		try
		{
			VehicleShell.Setup();
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("[Car] vehicle-vehicle collisions unavailable: " + ex.Message));
		}
	}
}
[HarmonyPatch(typeof(StartOfRound), "Awake")]
internal static class P_StartOfRound_Awake
{
	private static void Prefix(StartOfRound __instance)
	{
		Registration.AddToVehiclesList(__instance);
	}
}
[HarmonyPatch(typeof(Terminal), "Awake")]
internal static class P_Terminal_Awake
{
	private static void Prefix(Terminal __instance)
	{
		Registration.AddToTerminal(__instance);
	}
}
public class WagonHitch : MonoBehaviour
{
	public VehicleController vc;

	public InteractTrigger trigger;

	public Transform eye;

	public Vector3 AnchorLocal => ((Component)vc).transform.InverseTransformPoint(((Object)(object)eye != (Object)null) ? eye.position : ((Component)this).transform.position);

	public bool TiedHere(TowManager.Link l)
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		if (l == null)
		{
			return false;
		}
		ulong networkObjectId = ((NetworkBehaviour)vc).NetworkObject.NetworkObjectId;
		return Vector3.Distance((l.a == networkObjectId) ? l.la : l.lb, AnchorLocal) < 0.5f;
	}

	private void Update()
	{
		if (!((Object)(object)trigger == (Object)null) && !((Object)(object)vc == (Object)null) && !((Object)(object)TowManager.Instance == (Object)null))
		{
			string text = ((!Cfg.TowEnabled.Value) ? "Towing is switched off" : ((TowManager.Instance.LinkOf(vc) != null) ? (TiedHere(TowManager.Instance.LinkOf(vc)) ? "Release tow rope : [LMB]" : "The tow rope is on the other hitch") : (((Object)(object)TowManager.RopeFrom == (Object)(object)vc) ? "Put the tow rope away : [LMB]" : ((!((Object)(object)TowManager.RopeFrom != (Object)null)) ? "Hook up tow rope : [LMB]" : "Attach tow rope here : [LMB]"))));
			if (trigger.hoverTip != text)
			{
				trigger.hoverTip = text;
			}
		}
	}

	public bool Use(PlayerControllerB who)
	{
		//IL_00fd: 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_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
		if ((Object)(object)val == (Object)null || (Object)(object)who != (Object)(object)val || (Object)(object)TowManager.Instance == (Object)null || !Cfg.TowEnabled.Value)
		{
			return true;
		}
		TowManager instance = TowManager.Instance;
		TowManager.Link link = instance.LinkOf(vc);
		if (link != null)
		{
			if (TiedHere(link))
			{
				instance.RequestClear(vc);
			}
			else if ((Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.DisplayTip("Tow rope", "The tow rope is on the other hitch.", false, false, "LC_Tip1");
			}
			return true;
		}
		if ((Object)(object)TowManager.RopeFrom == (Object)(object)vc)
		{
			TowManager.Drop("You put the tow rope away.");
			return true;
		}
		if ((Object)(object)TowManager.RopeFrom != (Object)null)
		{
			VehicleController ropeFrom = TowManager.RopeFrom;
			Vector3 ropeLocal = TowManager.RopeLocal;
			TowManager.Drop(null);
			instance.RequestLink(ropeFrom, ropeLocal, vc, AnchorLocal);
			return true;
		}
		TowManager.PickUp(vc, AnchorLocal);
		return true;
	}
}
public class TowManager : MonoBehaviour
{
	public class Link
	{
		public ulong a;

		public ulong b;

		public Vector3 la;

		public Vector3 lb;

		public float len;

		public ConfigurableJoint joint;

		public LineRenderer line;

		public bool sim;

		public int mode;
	}

	public static TowManager Instance;

	public static VehicleController RopeFrom;

	public static Vector3 RopeLocal;

	public const float ReleaseRadius = 1f;

	private const string ReqName = "um.lcwagonr.tow.req";

	private const string SyncName = "um.lcwagonr.tow.sync";

	public readonly List<Link> links = new List<Link>();

	private LineRenderer handLine;

	private object registeredFor;

	private Action<ulong> connectedHandler;

	private float nextTick;

	private float nextWarn;

	private Material ropeMat;

	private static readonly int Mask = -1;

	public static VehicleController TestRemote;

	public static bool TestClick;

	private static NetworkManager NM => NetworkManager.Singleton;

	private static bool IsServer
	{
		get
		{
			NetworkManager nM = NM;
			if ((Object)(object)nM != (Object)null && nM.IsListening)
			{
				return nM.IsServer;
			}
			return false;
		}
	}

	private static PlayerControllerB Me
	{
		get
		{
			if (!((Object)(object)GameNetworkManager.Instance != (Object)null))
			{
				return null;
			}
			return GameNetworkManager.Instance.localPlayerController;
		}
	}

	public static void Create()
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		if (!((Object)(object)Instance != (Object)null))
		{
			GameObject val = new GameObject("WagonR.Tow");
			Object.DontDestroyOnLoad((Object)val);
			Instance = val.AddComponent<TowManager>();
		}
	}

	private static VehicleController Find(ulong id)
	{
		NetworkManager nM = NM;
		if ((Object)(object)nM == (Object)null || nM.SpawnManager == null || !nM.SpawnManager.SpawnedObjects.TryGetValue(id, out var value) || (Object)(object)value == (Object)null)
		{
			return null;
		}
		return ((Component)value).GetComponent<VehicleController>();
	}

	private static ulong IdOf(VehicleController v)
	{
		return ((NetworkBehaviour)v).NetworkObject.NetworkObjectId;
	}

	public Link LinkOf(VehicleController v)
	{
		if ((Object)(object)v == (Object)null || (Object)(object)((NetworkBehaviour)v).NetworkObject == (Object)null || !((NetworkBehaviour)v).NetworkObject.IsSpawned)
		{
			return null;
		}
		ulong id = IdOf(v);
		return links.FirstOrDefault((Link l) => l.a == id || l.b == id);
	}

	public static void PickUp(VehicleController v, Vector3 local)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		RopeFrom = v;
		RopeLocal = local;
		Tip("Tow rope", "Look at the other vehicle and click on it. Click the hitch again to put the rope away.");
	}

	public static void Drop(string message, string why = null)
	{
		if ((Object)(object)RopeFrom != (Object)null && why != null)
		{
			Plugin.Log.LogInfo((object)("[Tow] rope dropped: " + why));
		}
		RopeFrom = null;
		if (!string.IsNullOrEmpty(message))
		{
			Tip("Tow rope", message);
		}
	}

	private static void Tip(string head, string body)
	{
		if ((Object)(object)HUDManager.Instance != (Object)null)
		{
			HUDManager.Instance.DisplayTip(head, body, false, false, "LC_Tip1");
		}
	}

	public static Vector3 EndAnchorLocal(VehicleController v, Vector3 near)
	{
		//IL_0006: 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_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_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_018b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e9: 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_01f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_020f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0211: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: 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)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: 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_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		Vector3 forward = ((Component)v).transform.forward;
		float num = float.MaxValue;
		float num2 = float.MinValue;
		float num3 = float.MaxValue;
		int num4 = LayerMask.NameToLayer("InteractableObject");
		Collider[] componentsInChildren = ((Component)v).GetComponentsInChildren<Collider>();
		Vector3 val2 = default(Vector3);
		foreach (Collider val in componentsInChildren)
		{
			if (!val.enabled || val.isTrigger || val is WheelCollider || ((Component)val).gameObject.layer == num4 || (Object)(object)val.attachedRigidbody != (Object)(object)v.mainRigidbody)
			{
				continue;
			}
			Bounds bounds = val.bounds;
			for (int j = 0; j < 8; j++)
			{
				((Vector3)(ref val2))..ctor(((j & 1) == 0) ? ((Bounds)(ref bounds)).min.x : ((Bounds)(ref bounds)).max.x, ((j & 2) == 0) ? ((Bounds)(ref bounds)).min.y : ((Bounds)(ref bounds)).max.y, ((j & 4) == 0) ? ((Bounds)(ref bounds)).min.z : ((Bounds)(ref bounds)).max.z);
				float num5 = Vector3.Dot(val2, forward);
				if (num5 < num)
				{
					num = num5;
				}
				if (num5 > num2)
				{
					num2 = num5;
				}
				if (val2.y < num3)
				{
					num3 = val2.y;
				}
			}
		}
		if (num > num2)
		{
			Bounds bounds2 = ((Collider)v.boundsCollider).bounds;
			float num6 = Vector3.Dot(((Bounds)(ref bounds2)).center, forward);
			Vector3 extents = ((Bounds)(ref bounds2)).extents;
			num = num6 - ((Vector3)(ref extents)).magnitude * 0.5f;
			float num7 = num;
			extents = ((Bounds)(ref bounds2)).extents;
			num2 = num7 + ((Vector3)(ref extents)).magnitude;
			num3 = ((Bounds)(ref bounds2)).min.y;
		}
		float num8 = Vector3.Dot(((Component)v).transform.position, forward);
		float num9 = Vector3.Dot(near, forward);
		float num10 = ((Mathf.Abs(num9 - num2) < Mathf.Abs(num9 - num)) ? num2 : num);
		Vector3 val3 = ((Component)v).transform.position + forward * (num10 - num8);
		val3.y = num3 + 0.55f;
		return ((Component)v).transform.InverseTransformPoint(val3);
	}

	public void RequestLink(VehicleController a, Vector3 la, VehicleController b, Vector3 lb)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: 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_0067: 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)
		if ((Object)(object)a == (Object)null || (Object)(object)b == (Object)null || (Object)(object)a == (Object)(object)b)
		{
			return;
		}
		if (IsServer)
		{
			ServerLink(IdOf(a), la, IdOf(b), lb, 0uL);
			return;
		}
		SendToServer(delegate(FastBufferWriter w)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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_002c: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			byte b2 = 1;
			((FastBufferWriter)(ref w)).WriteValueSafe<byte>(ref b2, default(ForPrimitives));
			ulong num = IdOf(a);
			((FastBufferWriter)(ref w)).WriteValueSafe<ulong>(ref num, default(ForPrimitives));
			((FastBufferWriter)(ref w)).WriteValueSafe(ref la);
			num = IdOf(b);
			((FastBufferWriter)(ref w)).WriteValueSafe<ulong>(ref num, default(ForPrimitives));
			((FastBufferWriter)(ref w)).WriteValueSafe(ref lb);
		});
	}

	public void RequestClear(VehicleController v)
	{
		if ((Object)(object)v == (Object)null)
		{
			return;
		}
		if (IsServer)
		{
			ServerClear(IdOf(v));
			return;
		}
		SendToServer(delegate(FastBufferWriter w)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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_002c: Unknown result type (might be due to invalid IL or missing references)
			byte b = 2;
			((FastBufferWriter)(ref w)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
			ulong num = IdOf(v);
			((FastBufferWriter)(ref w)).WriteValueSafe<ulong>(ref num, default(ForPrimitives));
		});
	}

	private unsafe static void SendToServer(Action<FastBufferWriter> fill)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		NetworkManager nM = NM;
		if ((Object)(object)nM == (Object)null || nM.CustomMessagingManager == null)
		{
			return;
		}
		FastBufferWriter val = default(FastBufferWriter);
		((FastBufferWriter)(ref val))..ctor(128, (Allocator)2, -1);
		try
		{
			fill(val);
			nM.CustomMessagingManager.SendNamedMessage("um.lcwagonr.tow.req", 0uL, val, (NetworkDelivery)3);
		}
		finally
		{
			((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
		}
	}

	private void OnRequest(ulong sender, FastBufferReader r)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: 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_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: 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_006c: 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)
		if (IsServer)
		{
			byte b = default(byte);
			((FastBufferReader)(ref r)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
			switch (b)
			{
			case 1:
			{
				ulong aId = default(ulong);
				((FastBufferReader)(ref r)).ReadValueSafe<ulong>(ref aId, default(ForPrimitives));
				Vector3 la = default(Vector3);
				((FastBufferReader)(ref r)).ReadValueSafe(ref la);
				ulong bId = default(ulong);
				((FastBufferReader)(