Decompiled source of DoYouCatchMyDrift v1.1.0

DoYouCatchMyDrift_IL2Cpp_1.1.0.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.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using DoYouCatchMyDrift;
using DoYouCatchMyDrift.Rally;
using DoYouCatchMyDrift.TemplateUtils;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Persistence;
using Il2CppScheduleOne.PlayerScripts;
using Il2CppScheduleOne.Vehicles;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Core), "Do You Catch My Drift", "1.1.0", "OverweightUnicorn", null)]
[assembly: MelonColor(255, 191, 0, 255)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("DoYouCatchMyDrift_IL2Cpp_1.1.0")]
[assembly: AssemblyConfiguration("IL2CPP")]
[assembly: AssemblyFileVersion("1.1.0")]
[assembly: AssemblyInformationalVersion("1.0.0+a726d37961d63920903a5730415253b0c9f17281")]
[assembly: AssemblyProduct("DoYouCatchMyDrift_IL2Cpp_1.1.0")]
[assembly: AssemblyTitle("DoYouCatchMyDrift_IL2Cpp_1.1.0")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DoYouCatchMyDrift
{
	public static class BuildInfo
	{
		public const string Name = "Do You Catch My Drift";

		public const string Description = "Vehicle physics tuner – balanced vs drift presets.";

		public const string Author = "OverweightUnicorn";

		public const string Company = "UnicornsCanMod";

		public const string Version = "1.1.0";

		public const string DownloadLink = null;
	}
	public class Core : MelonMod
	{
		public static string gameObjectString = "Map/Hyland Point/Region_Northtown/Waterfront/Mooring Bollard (8)";

		public override void OnInitializeMelon()
		{
			AssetBundleUtils.Initialize(this);
			RallyConfig.Init();
			if (!AssetBundleUtils.EnsureBundleCached("doyoucatchmydrift"))
			{
				((MelonBase)this).Unregister("Failed to cache required asset bundle doyoucatchmydrift.", false);
			}
			Utility.Log("Do You Catch My Drift v1.1.0 initialised. F6 toggles rally physics, F5 toggles the HUD.");
		}

		public override void OnUpdate()
		{
			if (Input.GetKeyDown((KeyCode)287))
			{
				RallyConfig.ToggleEnabled();
			}
			if (Input.GetKeyDown((KeyCode)286))
			{
				RallyConfig.ToggleHud();
			}
		}

		public override void OnGUI()
		{
			RallyHud.Draw();
		}

		public override void OnLateInitializeMelon()
		{
			Singleton<LoadManager>.Instance.onLoadComplete.AddListener(UnityAction.op_Implicit((Action)InitMod));
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			((MelonMod)this).OnSceneWasLoaded(buildIndex, sceneName);
			RallyManager.Clear();
			if (sceneName == "Main")
			{
				OverpassModule.OnSceneLoaded(sceneName);
			}
			else
			{
				OverpassModule.OnMenuLoaded();
			}
		}

		public void InitMod()
		{
			Utility.Log("Game has loaded!");
		}

		public void PlaceCubeAt(string targetName)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GameObject.Find(targetName);
			if ((Object)(object)val == (Object)null)
			{
				MelonLogger.Msg("Could not find GameObject named '" + targetName + "'");
				return;
			}
			Vector3 position = val.transform.position;
			GameObject val2 = GameObject.CreatePrimitive((PrimitiveType)3);
			val2.transform.position = new Vector3(-46.75f, -8f, 198f);
			val2.transform.localScale = new Vector3(67f, 5.99f, 70f);
			((Object)val2).name = "PlacedCube_BOIIIIIIIIIIIIIIIII";
			Renderer component = val2.GetComponent<Renderer>();
			if ((Object)(object)component != (Object)null)
			{
				Shader val3 = Shader.Find("Universal Render Pipeline/Lit");
				if ((Object)(object)val3 == (Object)null)
				{
					MelonLogger.Msg(ConsoleColor.Red, "URP shader not found. The cube may still be invisible.");
				}
				else
				{
					Material val4 = new Material(val3);
					val4.color = Color.gray;
					component.material = val4;
					MelonLogger.Msg(ConsoleColor.Green, "Applied URP material to cube.");
				}
			}
			val2.layer = LayerMask.NameToLayer("Default");
		}
	}
	public static class OverpassModule
	{
		private const string OverpassRootPath = "Map/Hyland Point/Overpass";

		private const string IslandPath = "Map/Island";

		private const string OceanPath = "Map/StylizedWater2_Ocean";

		private const string OverpassRampParentForCollider = "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/Ramp";

		private const string OverpassRampCube = "Map/Hyland Point/Overpass/Overpass Ramp/Cube";

		private const string SkyboxPath = "EnvironmentManager/Skybox";

		private static string[] OverpassRamp = new string[6] { "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/RampSupport", "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/RampSupport (1)", "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/RampSupport (2)", "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/RampSupport (3)", "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/RampSupport (4)", "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/CurvedBeams" };

		private static GameObject overpassFillInstance;

		private const string OverpassBundleName = "doyoucatchmydrift";

		private const string OverpassPrefabAssetName = "overpassfill";

		private const string URPLitShaderName = "Universal Render Pipeline/Lit";

		private static GameObject overpassPrefab;

		private static readonly string[] RampKeepNames = new string[3] { "Ramp", "Block", "CurvedBeams" };

		private static readonly string[] RoadblockerTargets = new string[5] { "Map/Hyland Point/Overpass/Overpass Ramp/RoadBlocker4_LOD (1)", "Map/Hyland Point/Overpass/Overpass Ramp/RoadBlocker4_LOD (2)", "Map/Hyland Point/Overpass/Overpass Ramp/RoadBlocker4_LOD (3)", "Map/Hyland Point/Overpass/Overpass Ramp/RoadBlocker4_LOD (4)", "@Businesses/Car Wash/Dumpster" };

		private static bool built = false;

		private static void LoadOverpassAssetBundle()
		{
			Il2CppAssetBundle val = AssetBundleUtils.LoadAssetBundle("doyoucatchmydrift");
			if (val == null)
			{
				MelonLogger.Warning("[Overpass] AssetBundle 'doyoucatchmydrift' failed to load -- falling back to in-scene mesh recovery.");
				return;
			}
			GameObject val2 = AssetBundleUtils.LoadAssetFromBundle<GameObject>("overpassfill.prefab", "doyoucatchmydrift");
			if ((Object)(object)val2 != (Object)null)
			{
				overpassPrefab = val2;
				MelonLogger.Msg("[Overpass] Loaded prefab 'overpassfill' from bundle 'doyoucatchmydrift'.");
			}
			else
			{
				MelonLogger.Warning("[Overpass] Prefab 'overpassfill' not found in bundle 'doyoucatchmydrift' -- falling back to in-scene mesh recovery.");
			}
		}

		public static void OnSceneLoaded(string sceneName)
		{
			if (!(sceneName != "Main") && !built)
			{
				built = true;
				MelonCoroutines.Start(RunAfterDelay(1f));
			}
		}

		public static void OnMenuLoaded()
		{
			built = false;
			overpassPrefab = null;
			overpassFillInstance = null;
			AssetBundleUtils.ClearCache();
		}

		private static IEnumerator WaitForPath(string path, Action<GameObject> onFound, float timeout = 10f)
		{
			float elapsed = 0f;
			GameObject found = null;
			while ((Object)(object)found == (Object)null && elapsed < timeout)
			{
				found = FindByFullPathIncludingInactive(path);
				if ((Object)(object)found != (Object)null)
				{
					break;
				}
				yield return null;
				elapsed += Time.deltaTime;
			}
			if ((Object)(object)found == (Object)null)
			{
				MelonLogger.Warning($"[Overpass] Timed out after {timeout}s waiting for '{path}'.");
			}
			else
			{
				onFound(found);
			}
		}

		private static IEnumerator RunAfterDelay(float delay)
		{
			yield return (object)new WaitForSeconds(delay);
			LoadOverpassAssetBundle();
			MelonCoroutines.Start(StripOriginalColliders());
			MelonCoroutines.Start(DisableIsland());
			MelonCoroutines.Start(ScaleTheOcean());
			MelonCoroutines.Start(DisableRoadblockers());
			MelonCoroutines.Start(DisableOverpassRampObjects());
			MelonCoroutines.Start(FixSkybox());
			MelonCoroutines.Start(FixRampMeshCollider());
			MelonCoroutines.Start(SpawnOverpassFill());
			MelonCoroutines.Start(RemoveTreesUnderOverpass());
			MelonCoroutines.Start(EnforceInactive("Map/Hyland Point/Overpass/Overpass Ramp/Cube"));
		}

		private static IEnumerator StripOriginalColliders()
		{
			GameObject overpassRoot = null;
			yield return WaitForPath("Map/Hyland Point/Overpass", delegate(GameObject o)
			{
				overpassRoot = o;
			});
			if (Object.op_Implicit((Object)(object)overpassRoot))
			{
				int removed = 0;
				foreach (BoxCollider col in overpassRoot.GetComponentsInChildren<BoxCollider>(true))
				{
					if (Mathf.Approximately(col.size.z, 0.65f))
					{
						Vector3 newSize = col.size;
						newSize.z = 0f;
						col.size = newSize;
						Vector3 newCenter = col.center;
						newCenter.z = 0.1f;
						col.center = newCenter;
						removed++;
					}
				}
				MelonLogger.Msg("[Overpass] Removed " + removed + " original colliders.");
			}
			else
			{
				MelonLogger.Warning("[Overpass] 'Map/Hyland Point/Overpass' never appeared -- skipping collider strip.");
			}
		}

		private static IEnumerator DisableIsland()
		{
			GameObject obj = null;
			yield return WaitForPath("Map/Island", delegate(GameObject o)
			{
				obj = o;
			});
			if (Object.op_Implicit((Object)(object)obj))
			{
				obj.SetActive(false);
			}
			else
			{
				MelonLogger.Warning("[Overpass] Island 'Map/Island' never appeared -- not disabled.");
			}
		}

		private static IEnumerator ScaleTheOcean()
		{
			GameObject obj = null;
			yield return WaitForPath("Map/StylizedWater2_Ocean", delegate(GameObject o)
			{
				obj = o;
			});
			if (Object.op_Implicit((Object)(object)obj))
			{
				obj.transform.localScale = new Vector3(3f, 3f, 3f);
			}
			else
			{
				MelonLogger.Warning("[Overpass] Ocean 'Map/StylizedWater2_Ocean' never appeared -- not disabled.");
			}
		}

		private static IEnumerator DisableRoadblockers()
		{
			string[] roadblockerTargets = RoadblockerTargets;
			foreach (string path in roadblockerTargets)
			{
				GameObject obj = null;
				yield return WaitForPath(path, delegate(GameObject o)
				{
					obj = o;
				});
				if (Object.op_Implicit((Object)(object)obj))
				{
					obj.SetActive(false);
				}
				else
				{
					MelonLogger.Warning("[Overpass] Roadblocker '" + path + "' never appeared -- not disabled.");
				}
			}
		}

		private static IEnumerator DisableOverpassRampObjects()
		{
			string[] overpassRamp = OverpassRamp;
			foreach (string path in overpassRamp)
			{
				GameObject obj = null;
				yield return WaitForPath(path, delegate(GameObject o)
				{
					obj = o;
				});
				if (Object.op_Implicit((Object)(object)obj))
				{
					obj.SetActive(false);
				}
				else
				{
					MelonLogger.Warning("[Overpass] Ramp object '" + path + "' never appeared -- not disabled.");
				}
			}
		}

		private static IEnumerator FixRampMeshCollider()
		{
			GameObject rampRefParent = null;
			yield return WaitForPath("Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/Ramp", delegate(GameObject o)
			{
				rampRefParent = o;
			});
			if (Object.op_Implicit((Object)(object)rampRefParent))
			{
				MeshCollider collider = rampRefParent.GetComponent<MeshCollider>();
				if ((Object)(object)collider != (Object)null)
				{
					collider.convex = false;
				}
			}
			else
			{
				MelonLogger.Warning("[Overpass] 'Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/Ramp' never appeared -- ramp collider left untouched.");
			}
		}

		private static IEnumerator FixSkybox()
		{
			GameObject skyboxRef = null;
			yield return WaitForPath("EnvironmentManager/Skybox", delegate(GameObject o)
			{
				skyboxRef = o;
			});
			if (Object.op_Implicit((Object)(object)skyboxRef))
			{
				Transform transform = skyboxRef.transform;
				transform.localScale *= 2f;
			}
			else
			{
				MelonLogger.Warning("[Overpass] 'Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/Ramp' never appeared -- ramp collider left untouched.");
			}
		}

		private static IEnumerator SpawnOverpassFill()
		{
			if ((Object)(object)overpassPrefab == (Object)null)
			{
				MelonLogger.Warning("[Overpass] overpassPrefab is null -- overpass fill not spawned (bundle failed to load).");
				yield break;
			}
			GameObject overpassRoot = null;
			yield return WaitForPath("Map/Hyland Point/Overpass", delegate(GameObject o)
			{
				overpassRoot = o;
			});
			if ((Object)(object)overpassRoot == (Object)null)
			{
				MelonLogger.Warning("[Overpass] 'Map/Hyland Point/Overpass' never appeared -- cannot parent the overpass fill.");
				yield break;
			}
			GameObject instance = Object.Instantiate<GameObject>(overpassPrefab, overpassRoot.transform, true);
			((Object)instance).name = "OverpassFill";
			overpassFillInstance = instance;
			FixBundleMaterials(instance, overpassRoot);
			MelonLogger.Msg("[Overpass] Instantiated 'overpassfill' under 'Map/Hyland Point/Overpass'.");
		}

		private static void FixBundleMaterials(GameObject instance, GameObject overpassRoot)
		{
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
			Shader val = Shader.Find("Universal Render Pipeline/Lit");
			if ((Object)(object)val == (Object)null)
			{
				MelonLogger.Warning("[Overpass] 'Universal Render Pipeline/Lit' not found -- materials may render magenta.");
			}
			Dictionary<string, Material> dictionary = new Dictionary<string, Material>(StringComparer.OrdinalIgnoreCase);
			foreach (Renderer componentsInChild in overpassRoot.GetComponentsInChildren<Renderer>(true))
			{
				if (((Component)componentsInChild).transform.IsChildOf(instance.transform))
				{
					continue;
				}
				Il2CppReferenceArray<Material> sharedMaterials = componentsInChild.sharedMaterials;
				if (sharedMaterials == null)
				{
					continue;
				}
				foreach (Material item in (Il2CppArrayBase<Material>)(object)sharedMaterials)
				{
					if ((Object)(object)item != (Object)null && !dictionary.ContainsKey(((Object)item).name))
					{
						dictionary[((Object)item).name] = item;
					}
				}
			}
			MelonLogger.Msg($"[Overpass] Harvested {dictionary.Count} live overpass material(s) for matching.");
			Texture2D val2 = AssetBundleUtils.LoadTextureContaining("doyoucatchmydrift", "asphalt", "diff");
			Texture2D val3 = AssetBundleUtils.LoadTextureContaining("doyoucatchmydrift", "asphalt", "norm");
			Texture2D val4 = AssetBundleUtils.LoadTextureContaining("doyoucatchmydrift", "grass", "diff");
			Texture2D val5 = AssetBundleUtils.LoadTextureContaining("doyoucatchmydrift", "grass", "norm");
			foreach (Renderer componentsInChild2 in instance.GetComponentsInChildren<Renderer>(true))
			{
				Il2CppReferenceArray<Material> sharedMaterials2 = componentsInChild2.sharedMaterials;
				if (sharedMaterials2 == null)
				{
					continue;
				}
				bool flag = false;
				for (int i = 0; i < ((Il2CppArrayBase<Material>)(object)sharedMaterials2).Length; i++)
				{
					if ((Object)(object)((Il2CppArrayBase<Material>)(object)sharedMaterials2)[i] == (Object)null)
					{
						continue;
					}
					Material val6 = FindLiveMaterial(((Object)((Il2CppArrayBase<Material>)(object)sharedMaterials2)[i]).name, dictionary);
					if ((Object)(object)val6 != (Object)null)
					{
						MelonLogger.Msg($"[Overpass] Material '{((Object)((Il2CppArrayBase<Material>)(object)sharedMaterials2)[i]).name}' -> live '{((Object)val6).name}'.");
						((Il2CppArrayBase<Material>)(object)sharedMaterials2)[i] = val6;
						flag = true;
						continue;
					}
					Material val7 = ((Il2CppArrayBase<Material>)(object)sharedMaterials2)[i];
					if ((Object)(object)val != (Object)null)
					{
						val7.shader = val;
					}
					string text = ((Object)val7).name.ToLowerInvariant();
					Texture val8 = null;
					Texture val9 = null;
					if (text.Contains("asphalt") || text.Contains("road"))
					{
						val8 = (Texture)(object)val2;
						val9 = (Texture)(object)val3;
					}
					else if (text.Contains("grass") || text.Contains("wild"))
					{
						val8 = (Texture)(object)val4;
						val9 = (Texture)(object)val5;
						val7.mainTextureScale = new Vector2(200f, 200f);
					}
					if ((Object)(object)val8 != (Object)null)
					{
						val7.SetTexture("_BaseMap", val8);
					}
					if ((Object)(object)val9 != (Object)null)
					{
						val7.SetTexture("_BumpMap", val9);
						val7.EnableKeyword("_NORMALMAP");
					}
					if (val7.HasProperty("_BaseColor"))
					{
						val7.SetColor("_BaseColor", Color.white);
					}
					MelonLogger.Msg($"[Overpass] Material '{((Object)val7).name}': no live match -- custom baseMap={(Object)(object)val8 != (Object)null}, normal={(Object)(object)val9 != (Object)null}.");
				}
				if (flag)
				{
					componentsInChild2.sharedMaterials = sharedMaterials2;
				}
			}
		}

		private static Material FindLiveMaterial(string fillName, Dictionary<string, Material> liveMats)
		{
			if (liveMats.TryGetValue(fillName, out var value))
			{
				return value;
			}
			string text = NormalizeMatName(fillName);
			if (text.Length < 3)
			{
				return null;
			}
			Material val = null;
			foreach (KeyValuePair<string, Material> liveMat in liveMats)
			{
				string text2 = NormalizeMatName(liveMat.Key);
				if (text2 == text)
				{
					return liveMat.Value;
				}
				if ((Object)(object)val == (Object)null && (text2.Contains(text) || text.Contains(text2)))
				{
					val = liveMat.Value;
				}
			}
			return val;
		}

		private static string NormalizeMatName(string name)
		{
			string text = name.ToLowerInvariant().Trim();
			if (text.StartsWith("overpass_segment_"))
			{
				text = text.Substring("overpass_segment_".Length);
			}
			if (text.EndsWith(" mat"))
			{
				text = text.Substring(0, text.Length - 4);
			}
			else if (text.EndsWith("mat"))
			{
				text = text.Substring(0, text.Length - 3);
			}
			return text.Trim();
		}

		private static IEnumerator RemoveTreesUnderOverpass(int maxAttempts = 5, float retryDelay = 3f)
		{
			GameObject overpassRoot = null;
			yield return WaitForPath("Map/Hyland Point/Overpass", delegate(GameObject o)
			{
				overpassRoot = o;
			});
			if (!Object.op_Implicit((Object)(object)overpassRoot))
			{
				MelonLogger.Warning("[Overpass] Overpass root never appeared -- skipping tree removal.");
				yield break;
			}
			for (int attempt = 1; attempt <= maxAttempts; attempt++)
			{
				Il2CppReferenceArray<Terrain> terrains = Terrain.activeTerrains;
				if (terrains == null || ((Il2CppArrayBase<Terrain>)(object)terrains).Length == 0)
				{
					MelonLogger.Warning($"[Overpass] No active terrains yet (attempt {attempt}/{maxAttempts}) -- retrying in {retryDelay}s.");
					yield return (object)new WaitForSeconds(retryDelay);
					continue;
				}
				Bounds overpassBounds = ComputeWorldBounds(overpassRoot, overpassFillInstance);
				((Bounds)(ref overpassBounds)).Expand(new Vector3(2f, 0f, 2f));
				int totalRemoved = 0;
				foreach (Terrain terrain in (Il2CppArrayBase<Terrain>)(object)terrains)
				{
					TerrainData data = terrain.terrainData;
					if ((Object)(object)data == (Object)null)
					{
						continue;
					}
					Vector3 terrainOrigin = ((Component)terrain).transform.position;
					Il2CppStructArray<TreeInstance> instances = data.treeInstances;
					List<TreeInstance> kept = new List<TreeInstance>(((Il2CppArrayBase<TreeInstance>)(object)instances).Length);
					int removedTrees = 0;
					for (int i = 0; i < ((Il2CppArrayBase<TreeInstance>)(object)instances).Length; i++)
					{
						TreeInstance inst = ((Il2CppArrayBase<TreeInstance>)(object)instances)[i];
						Vector3 worldPos = terrainOrigin + Vector3.Scale(inst.position, data.size);
						if (worldPos.x >= ((Bounds)(ref overpassBounds)).min.x && worldPos.x <= ((Bounds)(ref overpassBounds)).max.x && worldPos.z >= ((Bounds)(ref overpassBounds)).min.z && worldPos.z <= ((Bounds)(ref overpassBounds)).max.z)
						{
							removedTrees++;
						}
						else
						{
							kept.Add(inst);
						}
					}
					if (removedTrees > 0)
					{
						data.treeInstances = Il2CppStructArray<TreeInstance>.op_Implicit(kept.ToArray());
						TerrainCollider tc = ((Component)terrain).GetComponent<TerrainCollider>();
						if ((Object)(object)tc != (Object)null)
						{
							((Collider)tc).enabled = false;
							((Collider)tc).enabled = true;
						}
						terrain.Flush();
						totalRemoved += removedTrees;
						MelonLogger.Msg($"[Overpass] Terrain '{((Object)terrain).name}': removed {removedTrees} tree(s) under the overpass (kept {kept.Count}).");
					}
				}
				MelonLogger.Msg($"[Overpass] Tree removal done: {totalRemoved} removed across {((Il2CppArrayBase<Terrain>)(object)terrains).Length} terrain(s), bounds={overpassBounds}.");
				yield break;
			}
			MelonLogger.Error($"[Overpass] No active terrain found after {maxAttempts} attempts -- tree removal skipped.");
		}

		private static IEnumerator EnforceInactive(string path, float recheckInterval = 5f)
		{
			while (true)
			{
				GameObject obj = FindByFullPathIncludingInactive(path);
				if ((Object)(object)obj != (Object)null && obj.activeSelf)
				{
					obj.SetActive(false);
					MelonLogger.Msg("[Overpass] Re-disabled '" + path + "' (was reactivated).");
				}
				yield return (object)new WaitForSeconds(recheckInterval);
			}
		}

		private static IEnumerator EnforceHiddenRampChildren(float recheckInterval = 5f)
		{
			while (true)
			{
				GameObject rampRefParent = FindByFullPathIncludingInactive("Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/Ramp");
				if ((Object)(object)rampRefParent != (Object)null && (Object)(object)rampRefParent.transform.parent != (Object)null)
				{
					HideChildrenExceptNamed(rampRefParent.transform.parent, RampKeepNames);
				}
				yield return (object)new WaitForSeconds(recheckInterval);
			}
		}

		private static void HideChildrenExceptNamed(Transform parent, string[] keepNameContains)
		{
			int num = 0;
			for (int i = 0; i < parent.childCount; i++)
			{
				Transform child = parent.GetChild(i);
				bool flag = false;
				foreach (string value in keepNameContains)
				{
					if (((Object)child).name.Contains(value))
					{
						flag = true;
						break;
					}
				}
				if (!flag && ((Component)child).gameObject.activeSelf)
				{
					((Component)child).gameObject.SetActive(false);
					num++;
				}
			}
			if (num > 0)
			{
				MelonLogger.Msg($"[Overpass] HideChildrenExceptNamed('{((Object)parent).name}'): hid {num} child object(s).");
			}
		}

		private static Bounds ComputeWorldBounds(GameObject go, params GameObject[] exclude)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			Il2CppArrayBase<Renderer> componentsInChildren = go.GetComponentsInChildren<Renderer>(true);
			Bounds val = default(Bounds);
			bool flag = false;
			foreach (Renderer item in componentsInChildren)
			{
				if (!((Object)(object)item == (Object)null) && !IsDescendantOfAny(((Component)item).transform, exclude))
				{
					if (!flag)
					{
						val = item.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref val)).Encapsulate(item.bounds);
					}
				}
			}
			return (Bounds)(flag ? val : new Bounds(go.transform.position, Vector3.zero));
		}

		private static bool IsDescendantOfAny(Transform t, GameObject[] roots)
		{
			if (roots == null)
			{
				return false;
			}
			foreach (GameObject val in roots)
			{
				if ((Object)(object)val != (Object)null && t.IsChildOf(val.transform))
				{
					return true;
				}
			}
			return false;
		}

		private static GameObject FindByFullPathIncludingInactive(string fullPath)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(fullPath))
			{
				return null;
			}
			string[] array = fullPath.Split('/');
			for (int i = 0; i < SceneManager.sceneCount; i++)
			{
				Scene sceneAt = SceneManager.GetSceneAt(i);
				if (!((Scene)(ref sceneAt)).isLoaded)
				{
					continue;
				}
				foreach (GameObject item in (Il2CppArrayBase<GameObject>)(object)((Scene)(ref sceneAt)).GetRootGameObjects())
				{
					if (((Object)item).name != array[0])
					{
						continue;
					}
					Transform val = item.transform;
					for (int j = 1; j < array.Length; j++)
					{
						if (!((Object)(object)val != (Object)null))
						{
							break;
						}
						val = val.Find(array[j]);
					}
					if (!((Object)(object)val != (Object)null))
					{
						continue;
					}
					return ((Component)val).gameObject;
				}
			}
			return null;
		}
	}
	public static class VehicleDumper
	{
		public static IEnumerator DumpAfterDelay(float seconds)
		{
			yield return (object)new WaitForSeconds(seconds);
			try
			{
				DumpAll();
			}
			catch (Exception ex)
			{
				Exception e = ex;
				Utility.PrintException(e);
			}
		}

		public static void DumpAll()
		{
			VehicleManager instance = NetworkSingleton<VehicleManager>.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				Utility.Error("VehicleDumper: VehicleManager instance not found.");
				return;
			}
			List<string> list = new List<string>();
			for (int i = 0; i < instance.VehiclePrefabs.Count; i++)
			{
				list.Add(DumpVehicleSafe(instance.VehiclePrefabs[i], isPrefab: true));
			}
			List<string> list2 = new List<string>();
			for (int j = 0; j < instance.AllVehicles.Count; j++)
			{
				list2.Add(DumpVehicleSafe(instance.AllVehicles[j], isPrefab: false));
			}
			StringBuilder stringBuilder = new StringBuilder(65536);
			stringBuilder.Append("{\n");
			stringBuilder.Append("  \"fixedDeltaTime\": ").Append(F(Time.fixedDeltaTime)).Append(",\n");
			stringBuilder.Append("  \"prefabs\": [\n").Append(string.Join(",\n", list)).Append("\n  ],\n");
			stringBuilder.Append("  \"spawned\": [\n").Append(string.Join(",\n", list2)).Append("\n  ]\n");
			stringBuilder.Append("}\n");
			string text = Path.Combine(MelonEnvironment.UserDataDirectory, "DYCMD_VehicleDump.json");
			File.WriteAllText(text, stringBuilder.ToString());
			Utility.Success($"VehicleDumper: wrote {list.Count} prefabs + {list2.Count} spawned vehicles to {text}");
		}

		private static string DumpVehicleSafe(LandVehicle v, bool isPrefab)
		{
			try
			{
				if ((Object)(object)v == (Object)null)
				{
					return "    { \"error\": \"null vehicle entry\" }";
				}
				return DumpVehicle(v, isPrefab);
			}
			catch (Exception ex)
			{
				string s = "unknown";
				try
				{
					s = ((Object)((Component)v).gameObject).name;
				}
				catch
				{
				}
				return $"    {{ \"name\": {J(s)}, \"error\": {J(ex.GetType().Name + ": " + ex.Message)} }}";
			}
		}

		private static string DumpVehicle(LandVehicle v, bool isPrefab)
		{
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0439: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder(4096);
			stringBuilder.Append("    {\n");
			stringBuilder.Append("      \"name\": ").Append(J(((Object)((Component)v).gameObject).name)).Append(",\n");
			stringBuilder.Append("      \"vehicleName\": ").Append(J(v.VehicleName)).Append(",\n");
			stringBuilder.Append("      \"vehicleCode\": ").Append(J(v.vehicleCode)).Append(",\n");
			stringBuilder.Append("      \"layer\": ").Append(J(LayerMask.LayerToName(((Component)v).gameObject.layer))).Append(",\n");
			Rigidbody rb = v.Rb;
			if ((Object)(object)rb != (Object)null)
			{
				stringBuilder.Append("      \"rigidbody\": {\n");
				stringBuilder.Append("        \"mass\": ").Append(F(rb.mass)).Append(",\n");
				stringBuilder.Append("        \"drag\": ").Append(F(rb.drag)).Append(",\n");
				stringBuilder.Append("        \"angularDrag\": ").Append(F(rb.angularDrag)).Append(",\n");
				stringBuilder.Append("        \"centerOfMass\": ").Append(V(rb.centerOfMass)).Append(",\n");
				stringBuilder.Append("        \"inertiaTensor\": ").Append(V(rb.inertiaTensor)).Append("\n");
				stringBuilder.Append("      },\n");
			}
			if ((Object)(object)v.centerOfMass != (Object)null)
			{
				stringBuilder.Append("      \"centerOfMassTransformLocal\": ").Append(V(((Component)v).transform.InverseTransformPoint(v.centerOfMass.position))).Append(",\n");
			}
			stringBuilder.Append("      \"drive\": {\n");
			stringBuilder.Append("        \"topSpeed\": ").Append(F(v.TopSpeed)).Append(",\n");
			stringBuilder.Append("        \"diffGearing\": ").Append(F(v.diffGearing)).Append(",\n");
			stringBuilder.Append("        \"handBrakeForce\": ").Append(F(v.handBrakeForce)).Append(",\n");
			stringBuilder.Append("        \"brakeForceMultiplier\": ").Append(F(v.BrakeForceMultiplier)).Append(",\n");
			stringBuilder.Append("        \"downforce\": ").Append(F(v.downforce)).Append(",\n");
			stringBuilder.Append("        \"reverseMultiplier\": ").Append(F(v.reverseMultiplier)).Append(",\n");
			stringBuilder.Append("        \"maxSteeringAngle\": ").Append(F(v.maxSteeringAngle)).Append(",\n");
			stringBuilder.Append("        \"steerRate\": ").Append(F(v.steerRate)).Append(",\n");
			stringBuilder.Append("        \"flipSteer\": ").Append(v.flipSteer ? "true" : "false").Append(",\n");
			stringBuilder.Append("        \"motorTorqueCurve\": ").Append(Curve(v.motorTorque)).Append(",\n");
			stringBuilder.Append("        \"brakeForceCurve\": ").Append(Curve(v.brakeForce)).Append("\n");
			stringBuilder.Append("      },\n");
			Il2CppArrayBase<Wheel> componentsInChildren = ((Component)v).GetComponentsInChildren<Wheel>(true);
			List<string> list = new List<string>();
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				list.Add(DumpWheel(v, componentsInChildren[i]));
			}
			stringBuilder.Append("      \"wheels\": [\n").Append(string.Join(",\n", list)).Append("\n      ]");
			if (!isPrefab)
			{
				stringBuilder.Append(",\n      \"worldPosition\": ").Append(V(((Component)v).transform.position));
				stringBuilder.Append(",\n      \"ground\": ").Append(DumpGround(v));
			}
			stringBuilder.Append("\n    }");
			return stringBuilder.ToString();
		}

		private static string DumpWheel(LandVehicle v, Wheel w)
		{
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02de: Unknown result type (might be due to invalid IL or missing references)
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0306: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Unknown result type (might be due to invalid IL or missing references)
			//IL_0349: Unknown result type (might be due to invalid IL or missing references)
			//IL_036f: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder(1024);
			WheelCollider wheelCollider = w.wheelCollider;
			stringBuilder.Append("        {\n");
			stringBuilder.Append("          \"name\": ").Append(J(((Object)((Component)w).gameObject).name)).Append(",\n");
			if ((Object)(object)wheelCollider != (Object)null)
			{
				bool flag = false;
				bool flag2 = false;
				bool flag3 = false;
				Il2CppReferenceArray<WheelCollider> driveWheels = v.driveWheels;
				if (driveWheels != null)
				{
					for (int i = 0; i < ((Il2CppArrayBase<WheelCollider>)(object)driveWheels).Length; i++)
					{
						if ((Object)(object)((Il2CppArrayBase<WheelCollider>)(object)driveWheels)[i] == (Object)(object)wheelCollider)
						{
							flag = true;
							break;
						}
					}
				}
				Il2CppReferenceArray<WheelCollider> steerWheels = v.steerWheels;
				if (steerWheels != null)
				{
					for (int j = 0; j < ((Il2CppArrayBase<WheelCollider>)(object)steerWheels).Length; j++)
					{
						if ((Object)(object)((Il2CppArrayBase<WheelCollider>)(object)steerWheels)[j] == (Object)(object)wheelCollider)
						{
							flag2 = true;
							break;
						}
					}
				}
				Il2CppReferenceArray<WheelCollider> handbrakeWheels = v.handbrakeWheels;
				if (handbrakeWheels != null)
				{
					for (int k = 0; k < ((Il2CppArrayBase<WheelCollider>)(object)handbrakeWheels).Length; k++)
					{
						if ((Object)(object)((Il2CppArrayBase<WheelCollider>)(object)handbrakeWheels)[k] == (Object)(object)wheelCollider)
						{
							flag3 = true;
							break;
						}
					}
				}
				stringBuilder.Append("          \"isDrive\": ").Append(flag ? "true" : "false").Append(",\n");
				stringBuilder.Append("          \"isSteer\": ").Append(flag2 ? "true" : "false").Append(",\n");
				stringBuilder.Append("          \"isHandbrake\": ").Append(flag3 ? "true" : "false").Append(",\n");
				stringBuilder.Append("          \"localPosition\": ").Append(V(((Component)v).transform.InverseTransformPoint(((Component)wheelCollider).transform.position))).Append(",\n");
				stringBuilder.Append("          \"radius\": ").Append(F(wheelCollider.radius)).Append(",\n");
				stringBuilder.Append("          \"mass\": ").Append(F(wheelCollider.mass)).Append(",\n");
				stringBuilder.Append("          \"center\": ").Append(V(wheelCollider.center)).Append(",\n");
				stringBuilder.Append("          \"suspensionDistance\": ").Append(F(wheelCollider.suspensionDistance)).Append(",\n");
				stringBuilder.Append("          \"wheelDampingRate\": ").Append(F(wheelCollider.wheelDampingRate)).Append(",\n");
				stringBuilder.Append("          \"forceAppPointDistance\": ").Append(F(wheelCollider.forceAppPointDistance)).Append(",\n");
				JointSpring suspensionSpring = wheelCollider.suspensionSpring;
				stringBuilder.Append("          \"suspensionSpring\": { \"spring\": ").Append(F(suspensionSpring.spring)).Append(", \"damper\": ")
					.Append(F(suspensionSpring.damper))
					.Append(", \"targetPosition\": ")
					.Append(F(suspensionSpring.targetPosition))
					.Append(" },\n");
				stringBuilder.Append("          \"forwardFriction\": ").Append(Friction(wheelCollider.forwardFriction)).Append(",\n");
				stringBuilder.Append("          \"sidewaysFriction\": ").Append(Friction(wheelCollider.sidewaysFriction));
			}
			else
			{
				stringBuilder.Append("          \"error\": \"no wheelCollider\"");
			}
			stringBuilder.Append("\n        }");
			return stringBuilder.ToString();
		}

		private static string DumpGround(LandVehicle v)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			int num = (1 << LayerMask.NameToLayer("Default")) | (1 << LayerMask.NameToLayer("Terrain"));
			RaycastHit val = default(RaycastHit);
			if (Physics.Raycast(((Component)v).transform.position + Vector3.up * 0.5f, Vector3.down, ref val, 20f, num))
			{
				string text = (((Object)(object)((RaycastHit)(ref val)).collider.sharedMaterial != (Object)null) ? ((Object)((RaycastHit)(ref val)).collider.sharedMaterial).name : null);
				return "{ \"collider\": " + J(((Object)((Component)((RaycastHit)(ref val)).collider).gameObject).name) + ", \"layer\": " + J(LayerMask.LayerToName(((Component)((RaycastHit)(ref val)).collider).gameObject.layer)) + ", \"physicMaterial\": " + ((text != null) ? J(text) : "null") + ", \"distance\": " + F(((RaycastHit)(ref val)).distance) + " }";
			}
			return "null";
		}

		private static string Friction(WheelFrictionCurve c)
		{
			return "{ \"extremumSlip\": " + F(((WheelFrictionCurve)(ref c)).extremumSlip) + ", \"extremumValue\": " + F(((WheelFrictionCurve)(ref c)).extremumValue) + ", \"asymptoteSlip\": " + F(((WheelFrictionCurve)(ref c)).asymptoteSlip) + ", \"asymptoteValue\": " + F(((WheelFrictionCurve)(ref c)).asymptoteValue) + ", \"stiffness\": " + F(((WheelFrictionCurve)(ref c)).stiffness) + " }";
		}

		private static string Curve(AnimationCurve curve)
		{
			//IL_003a: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			if (curve == null)
			{
				return "null";
			}
			Il2CppStructArray<Keyframe> keys = curve.keys;
			List<string> list = new List<string>();
			for (int i = 0; i < ((Il2CppArrayBase<Keyframe>)(object)keys).Length; i++)
			{
				string[] obj = new string[5] { "{ \"time\": ", null, null, null, null };
				Keyframe val = ((Il2CppArrayBase<Keyframe>)(object)keys)[i];
				obj[1] = F(((Keyframe)(ref val)).time);
				obj[2] = ", \"value\": ";
				val = ((Il2CppArrayBase<Keyframe>)(object)keys)[i];
				obj[3] = F(((Keyframe)(ref val)).value);
				obj[4] = " }";
				list.Add(string.Concat(obj));
			}
			return "[ " + string.Join(", ", list) + " ]";
		}

		private static string F(float f)
		{
			return f.ToString("R", CultureInfo.InvariantCulture);
		}

		private static string V(Vector3 v)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			return "{ \"x\": " + F(v.x) + ", \"y\": " + F(v.y) + ", \"z\": " + F(v.z) + " }";
		}

		private static string J(string s)
		{
			if (s == null)
			{
				return "null";
			}
			return "\"" + s.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"";
		}
	}
}
namespace DoYouCatchMyDrift.TemplateUtils
{
	public static class AssetBundleUtils
	{
		private static Core mod;

		private static MelonAssembly melonAssembly;

		private static Dictionary<string, Il2CppAssetBundle> assetBundles = new Dictionary<string, Il2CppAssetBundle>();

		private static string GetBundleCachePath(string bundleFileName)
		{
			string text = Path.Combine(MelonEnvironment.UserDataDirectory, "DoYouCatchMyDrift", "AssetBundles");
			Directory.CreateDirectory(text);
			return Path.Combine(text, bundleFileName + "_1.1.0");
		}

		private static byte[] ReadEmbeddedBundleBytes(string bundleFileName)
		{
			string text = typeof(Core).Namespace + ".Assets." + bundleFileName;
			using Stream stream = melonAssembly.Assembly.GetManifestResourceStream(text);
			if (stream == null)
			{
				MelonLogger.Error($"AssetBundle resource '{text}' not found. Check EmbeddedResource entry for '{bundleFileName}'.");
				return null;
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			byte[] array = memoryStream.ToArray();
			if (array.Length == 0)
			{
				MelonLogger.Error("AssetBundle '" + bundleFileName + "' is empty.");
				return null;
			}
			return array;
		}

		public static bool EnsureBundleCached(string bundleFileName)
		{
			string bundleCachePath = GetBundleCachePath(bundleFileName);
			if (File.Exists(bundleCachePath))
			{
				return true;
			}
			byte[] array = ReadEmbeddedBundleBytes(bundleFileName);
			if (array == null)
			{
				return false;
			}
			try
			{
				File.WriteAllBytes(bundleCachePath, array);
				MelonLogger.Msg($"Cached AssetBundle '{bundleFileName}' to '{bundleCachePath}'.");
				return true;
			}
			catch (Exception value)
			{
				MelonLogger.Error($"Failed writing AssetBundle cache '{bundleFileName}': {value}");
				return false;
			}
		}

		private static Il2CppAssetBundle LoadBundleFromFile(string bundleFileName)
		{
			string bundleCachePath = GetBundleCachePath(bundleFileName);
			try
			{
				Il2CppAssetBundle val = Il2CppAssetBundleManager.LoadFromFile(bundleCachePath);
				if (val == null)
				{
					MelonLogger.Error($"LoadFromFile returned null for bundle '{bundleFileName}' at '{bundleCachePath}'.");
				}
				return val;
			}
			catch (Exception value)
			{
				MelonLogger.Error($"LoadFromFile failed for bundle '{bundleFileName}' at '{bundleCachePath}': {value}");
				return null;
			}
		}

		public static void Initialize(Core coreMod)
		{
			mod = coreMod;
			melonAssembly = ((MelonBase)mod).MelonAssembly;
		}

		public static Il2CppAssetBundle LoadAssetBundle(string bundleFileName)
		{
			if (assetBundles.TryGetValue(bundleFileName, out var value) && value != null)
			{
				return value;
			}
			try
			{
				string bundleCachePath = GetBundleCachePath(bundleFileName);
				Il2CppAssetBundle val;
				if (File.Exists(bundleCachePath))
				{
					val = LoadBundleFromFile(bundleFileName);
				}
				else
				{
					byte[] array = ReadEmbeddedBundleBytes(bundleFileName);
					if (array == null)
					{
						((MelonBase)mod).Unregister("Failed to read required AssetBundle '" + bundleFileName + "' from embedded resources.", false);
						return null;
					}
					try
					{
						Il2CppAssetBundle val2 = Il2CppAssetBundleManager.LoadFromMemory(Il2CppStructArray<byte>.op_Implicit(array));
						if (val2 == null)
						{
							((MelonBase)mod).Unregister("Failed to validate required AssetBundle '" + bundleFileName + "' from memory.", false);
							return null;
						}
						try
						{
							File.WriteAllBytes(bundleCachePath, array);
							MelonLogger.Msg($"Cached AssetBundle '{bundleFileName}' to '{bundleCachePath}'.");
						}
						catch (Exception value2)
						{
							try
							{
								val2.Unload(false);
							}
							catch
							{
							}
							((MelonBase)mod).Unregister($"Failed writing required AssetBundle '{bundleFileName}' to cache: {value2}", false);
							return null;
						}
						try
						{
							val2.Unload(false);
						}
						catch (Exception ex)
						{
							MelonLogger.Warning("Failed to unload memory validation bundle '" + bundleFileName + "': " + ex.Message);
						}
						val = LoadBundleFromFile(bundleFileName);
					}
					catch (Exception value3)
					{
						((MelonBase)mod).Unregister($"Failed to validate required AssetBundle '{bundleFileName}' from memory: {value3}", false);
						return null;
					}
				}
				if (val == null)
				{
					((MelonBase)mod).Unregister("Failed to load required AssetBundle '" + bundleFileName + "'.", false);
					return null;
				}
				assetBundles[bundleFileName] = val;
				return val;
			}
			catch (Exception value4)
			{
				((MelonBase)mod).Unregister($"Failed to load required AssetBundle '{bundleFileName}': {value4}", false);
				return null;
			}
		}

		public static void ClearCache()
		{
			assetBundles.Clear();
		}

		public static Il2CppAssetBundle GetLoadedAssetBundle(string bundleName)
		{
			if (assetBundles.ContainsKey(bundleName))
			{
				return assetBundles[bundleName];
			}
			MelonLogger.Warning("Asset bundle '" + bundleName + "' is not loaded.");
			return null;
		}

		public static T LoadAssetFromBundle<T>(string assetName, string bundleName) where T : Object
		{
			Il2CppAssetBundle loadedAssetBundle = GetLoadedAssetBundle(bundleName);
			if (loadedAssetBundle == null)
			{
				MelonLogger.Error("Couldn't find loaded bundle '" + bundleName + "'.");
				return default(T);
			}
			T val = default(T);
			try
			{
				val = loadedAssetBundle.LoadAsset<T>(assetName);
			}
			catch (Exception value)
			{
				MelonLogger.Error($"Failed loading asset '{assetName}' from bundle '{bundleName}'. The bundle may be invalid or have a zero internal pointer. {value}");
				return default(T);
			}
			if ((Object)(object)val == (Object)null)
			{
				MelonLogger.Error($"Asset '{assetName}' not found in bundle '{bundleName}'.");
				return default(T);
			}
			return val;
		}

		public static Texture2D LoadTextureContaining(string bundleName, params string[] contains)
		{
			Il2CppAssetBundle loadedAssetBundle = GetLoadedAssetBundle(bundleName);
			if (loadedAssetBundle == null)
			{
				return null;
			}
			Il2CppStringArray allAssetNames = loadedAssetBundle.GetAllAssetNames();
			for (int i = 0; i < ((Il2CppArrayBase<string>)(object)allAssetNames).Length; i++)
			{
				string text = ((Il2CppArrayBase<string>)(object)allAssetNames)[i].ToLowerInvariant();
				bool flag = true;
				for (int j = 0; j < contains.Length; j++)
				{
					if (!text.Contains(contains[j].ToLowerInvariant()))
					{
						flag = false;
						break;
					}
				}
				if (!flag)
				{
					continue;
				}
				try
				{
					Texture2D val = loadedAssetBundle.LoadAsset<Texture2D>(((Il2CppArrayBase<string>)(object)allAssetNames)[i]);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
				catch (Exception ex)
				{
					MelonLogger.Warning($"Failed loading texture '{((Il2CppArrayBase<string>)(object)allAssetNames)[i]}' from bundle '{bundleName}': {ex.Message}");
				}
			}
			return null;
		}
	}
	public static class Utility
	{
		public static void PrintException(Exception e)
		{
			MelonLogger.Msg(ConsoleColor.Red, e.GetType().FullName);
			MelonLogger.Msg(ConsoleColor.DarkRed, e.Message);
			if (!string.IsNullOrWhiteSpace(e.StackTrace))
			{
				MelonLogger.Msg(ConsoleColor.DarkRed, "StackTrace:");
				string[] array = e.StackTrace.Split('\n');
				foreach (string text in array)
				{
					MelonLogger.Msg(ConsoleColor.DarkRed, "    " + text.Trim());
				}
			}
			if (e.InnerException != null)
			{
				MelonLogger.Msg(ConsoleColor.DarkRed, "  -- Inner Exception --");
				PrintException(e.InnerException);
			}
		}

		public static void Error(string msg)
		{
			MelonLogger.Msg(ConsoleColor.Red, msg);
		}

		public static void Log(string msg)
		{
			MelonLogger.Msg(ConsoleColor.DarkMagenta, msg);
		}

		public static void Success(string msg)
		{
			MelonLogger.Msg(ConsoleColor.Green, msg);
		}
	}
}
namespace DoYouCatchMyDrift.Rally
{
	public class RallyProfile
	{
		public string Code = "default";

		public float PowerCV = 200f;

		public float MaxPowerRPM = 5500f;

		public float TorqueNm = 300f;

		public float MaxTorqueRPM = 3500f;

		public float MinRPM = 950f;

		public float MaxRPM = 6500f;

		public int GearCount = 5;

		public float TopSpeedKmh = 110f;

		public float FirstGearTopKmh;

		public float BrakeG = 1f;

		public float CdA = 0.85f;

		public float DownforceCoeff = 0.6f;

		public float ComLowerY = 0.12f;

		public float DragCoefficient = 0.2f;

		public float ShiftTime = 0.3f;

		public float RevLimiterTime = 0.08f;

		public float ClutchFullyClosedKmh = 10f;

		public float ShiftUpRPM;

		public float ShiftDownRPM;

		public float[] GearRatios;

		public float FinalDrive;

		public const float RPM2W = (float)Math.PI / 30f;

		public void ComputeGearing(float wheelRadius, float massKg)
		{
			float num = TopSpeedKmh / 3.6f;
			float num2 = 1f;
			FinalDrive = MaxRPM * ((float)Math.PI / 30f) * wheelRadius / (num * num2);
			if (FirstGearTopKmh <= 0f)
			{
				FirstGearTopKmh = TopSpeedKmh * 0.32f;
			}
			float num3 = Mathf.Max(FirstGearTopKmh, 20f) / 3.6f;
			float num4 = MaxRPM * ((float)Math.PI / 30f) * wheelRadius / (num3 * FinalDrive);
			num4 = Mathf.Max(num4, 1.6f);
			GearRatios = new float[GearCount + 2];
			GearRatios[0] = (0f - num4) * 0.9f;
			GearRatios[1] = 0f;
			for (int i = 0; i < GearCount; i++)
			{
				float num5 = ((GearCount == 1) ? 1f : ((float)i / (float)(GearCount - 1)));
				GearRatios[i + 2] = num4 * Mathf.Pow(num2 / num4, num5);
			}
			ShiftUpRPM = Mathf.Min(MaxPowerRPM * 1.05f, MaxRPM * 0.97f);
			ShiftDownRPM = MaxTorqueRPM * 0.75f;
		}
	}
	public static class RallyProfiles
	{
		private static readonly Dictionary<string, RallyProfile> table = new Dictionary<string, RallyProfile>
		{
			["shitbox"] = new RallyProfile
			{
				Code = "shitbox",
				PowerCV = 120f,
				MaxPowerRPM = 6200f,
				TorqueNm = 165f,
				MaxTorqueRPM = 4200f,
				MaxRPM = 7000f,
				GearCount = 5,
				TopSpeedKmh = 100f,
				CdA = 0.75f,
				BrakeG = 0.95f
			},
			["shitbox_police"] = new RallyProfile
			{
				Code = "shitbox_police",
				PowerCV = 135f,
				MaxPowerRPM = 6200f,
				TorqueNm = 180f,
				MaxTorqueRPM = 4200f,
				MaxRPM = 7000f,
				GearCount = 5,
				TopSpeedKmh = 105f,
				CdA = 0.75f,
				BrakeG = 0.95f
			},
			["bruiser"] = new RallyProfile
			{
				Code = "bruiser",
				PowerCV = 210f,
				MaxPowerRPM = 5200f,
				TorqueNm = 320f,
				MaxTorqueRPM = 3200f,
				MaxRPM = 6200f,
				GearCount = 5,
				TopSpeedKmh = 115f,
				CdA = 0.95f,
				ComLowerY = 0.15f
			},
			["bruiser_police"] = new RallyProfile
			{
				Code = "bruiser_police",
				PowerCV = 225f,
				MaxPowerRPM = 5200f,
				TorqueNm = 330f,
				MaxTorqueRPM = 3200f,
				MaxRPM = 6200f,
				GearCount = 5,
				TopSpeedKmh = 120f,
				CdA = 0.95f,
				ComLowerY = 0.15f
			},
			["hounddog"] = new RallyProfile
			{
				Code = "hounddog",
				PowerCV = 250f,
				MaxPowerRPM = 5600f,
				TorqueNm = 350f,
				MaxTorqueRPM = 3800f,
				MaxRPM = 6600f,
				GearCount = 5,
				TopSpeedKmh = 130f,
				CdA = 0.8f,
				BrakeG = 1.05f
			},
			["dinkler"] = new RallyProfile
			{
				Code = "dinkler",
				PowerCV = 230f,
				MaxPowerRPM = 4800f,
				TorqueNm = 390f,
				MaxTorqueRPM = 2800f,
				MaxRPM = 5800f,
				GearCount = 5,
				TopSpeedKmh = 115f,
				CdA = 1f,
				ComLowerY = 0.2f
			},
			["cheetah"] = new RallyProfile
			{
				Code = "cheetah",
				PowerCV = 340f,
				MaxPowerRPM = 6800f,
				TorqueNm = 300f,
				MaxTorqueRPM = 4600f,
				MaxRPM = 7600f,
				GearCount = 6,
				TopSpeedKmh = 130f,
				FirstGearTopKmh = 46f,
				CdA = 0.7f,
				BrakeG = 1.1f,
				DragCoefficient = 0.02f
			},
			["veeper"] = new RallyProfile
			{
				Code = "veeper",
				PowerCV = 190f,
				MaxPowerRPM = 4800f,
				TorqueNm = 320f,
				MaxTorqueRPM = 2800f,
				MaxRPM = 5600f,
				GearCount = 5,
				TopSpeedKmh = 105f,
				CdA = 1.05f,
				ComLowerY = 0.22f
			},
			["hotbox"] = new RallyProfile
			{
				Code = "hotbox",
				PowerCV = 240f,
				MaxPowerRPM = 5200f,
				TorqueNm = 350f,
				MaxTorqueRPM = 3200f,
				MaxRPM = 6200f,
				GearCount = 5,
				TopSpeedKmh = 120f,
				CdA = 1f,
				ComLowerY = 0.15f
			}
		};

		public static RallyProfile Get(string vehicleCode, float wheelRadius, float massKg)
		{
			if (vehicleCode == null || !table.TryGetValue(vehicleCode.ToLowerInvariant(), out var value))
			{
				value = new RallyProfile
				{
					Code = (vehicleCode ?? "unknown"),
					PowerCV = Mathf.Clamp(massKg * 0.125f, 100f, 300f),
					TorqueNm = Mathf.Clamp(massKg * 0.19f, 150f, 420f)
				};
			}
			if (value.GearRatios == null)
			{
				value.ComputeGearing(wheelRadius, massKg);
			}
			return value;
		}
	}
	public class EngineSim
	{
		private const float RPM2W = (float)Math.PI / 30f;

		private const float CV2KW = 0.7358f;

		private const float DrivelineEfficiency = 0.9f;

		private const float EngineFrictionFactor = 0.25f;

		private readonly RallyProfile p;

		private readonly float maxPowerEngineTorque;

		private const float ShiftCooldownTime = 0.4f;

		public int gear;

		public float rpm;

		public float clutch;

		public bool shifting;

		private float shiftTimer;

		private int nextGear;

		private float limiterTimer;

		private float shiftCooldown;

		public int ReverseIndex => 0;

		public int NeutralIndex => 1;

		public int FirstIndex => 2;

		public bool InReverse => gear == ReverseIndex;

		public bool LimiterActive => limiterTimer > 0f;

		public string GearLabel => (gear == 0) ? "R" : ((gear == 1) ? "N" : (gear - 1).ToString());

		public float MaxWheelRpm
		{
			get
			{
				float num = Mathf.Abs(p.GearRatios[gear]) * p.FinalDrive;
				return (num > 0.01f) ? (p.MaxRPM / num) : float.MaxValue;
			}
		}

		public EngineSim(RallyProfile profile)
		{
			p = profile;
			gear = FirstIndex;
			rpm = p.MinRPM;
			maxPowerEngineTorque = CalcEngineTorque(p.MaxPowerRPM);
		}

		public float CalcEngineTorque(float atRpm)
		{
			float num2;
			if (atRpm < p.MaxTorqueRPM)
			{
				float num = atRpm / p.MaxTorqueRPM - 1f;
				num2 = p.TorqueNm * (1f - num * num);
			}
			else
			{
				float num3 = p.MaxPowerRPM * ((float)Math.PI / 30f);
				float num4 = p.PowerCV * 0.7358f * 1000f / num3;
				float num5 = 2f * p.MaxTorqueRPM * p.MaxPowerRPM - p.MaxPowerRPM * p.MaxPowerRPM - p.MaxTorqueRPM * p.MaxTorqueRPM;
				float num6 = atRpm - p.MaxTorqueRPM;
				num2 = (p.TorqueNm - num4) / num5 * (num6 * num6) + p.TorqueNm;
			}
			return Mathf.Max(num2, 0f);
		}

		public float CalcFrictionTorque(float atRpm)
		{
			float num = 0.1f;
			if (atRpm < p.MinRPM)
			{
				num = 1f - 0.9f * (atRpm / p.MinRPM);
			}
			return maxPowerEngineTorque * 0.25f * (num + (1f - num) * atRpm / p.MaxRPM);
		}

		public void EnsureReverse()
		{
			if (gear != ReverseIndex)
			{
				gear = ReverseIndex;
				shifting = false;
			}
		}

		public void EnsureFirst()
		{
			if (InReverse || gear == NeutralIndex)
			{
				gear = FirstIndex;
				shifting = false;
			}
		}

		private void BeginShift(int target)
		{
			shifting = true;
			shiftTimer = 0f;
			nextGear = target;
		}

		public void Step(float throttle, float brake, float avgDriveWheelRpm, float speedKmh, bool grounded, bool wheelspin, float dt)
		{
			if (shifting)
			{
				shiftTimer += dt;
				if (shiftTimer >= p.ShiftTime)
				{
					gear = nextGear;
					shifting = false;
					shiftCooldown = 0.4f;
				}
			}
			if (shiftCooldown > 0f)
			{
				shiftCooldown -= dt;
			}
			if (!shifting && gear > FirstIndex && Mathf.Abs(speedKmh) < 5f)
			{
				gear = FirstIndex;
			}
			float num = Mathf.Abs(p.GearRatios[gear]) * p.FinalDrive;
			float num2 = Mathf.Abs(avgDriveWheelRpm) * num;
			bool flag = gear == FirstIndex || gear == ReverseIndex;
			clutch = (shifting ? 0f : (flag ? Mathf.Clamp01(Mathf.Abs(speedKmh) / p.ClutchFullyClosedKmh) : 1f));
			float num3;
			if (clutch > 0.99f)
			{
				num3 = num2;
			}
			else
			{
				float num4 = p.MinRPM + throttle * (p.MaxPowerRPM * 0.5f);
				num3 = Mathf.Max(num2, num4 * (1f - clutch) + num2 * clutch);
			}
			num3 = Mathf.Clamp(num3, p.MinRPM, p.MaxRPM);
			float num5 = ((clutch > 0.99f) ? 30000f : ((num3 > rpm) ? 9000f : 6000f));
			rpm = Mathf.MoveTowards(rpm, num3, num5 * dt);
			if (rpm >= p.MaxRPM - 1f)
			{
				limiterTimer = p.RevLimiterTime;
			}
			else if (limiterTimer > 0f)
			{
				limiterTimer -= dt;
			}
			if (!shifting && shiftCooldown <= 0f && grounded && gear >= FirstIndex)
			{
				if (rpm >= p.ShiftUpRPM && gear < p.GearRatios.Length - 1)
				{
					BeginShift(gear + 1);
				}
				else if ((throttle > 0.05f || brake > 0.05f) && rpm <= p.ShiftDownRPM && gear > FirstIndex)
				{
					BeginShift(gear - 1);
				}
			}
		}

		public float DriveWheelTorque(float throttle)
		{
			if (shifting)
			{
				return 0f;
			}
			float num = p.GearRatios[gear] * p.FinalDrive;
			if (num == 0f)
			{
				return 0f;
			}
			float num2 = (LimiterActive ? 0f : throttle);
			float num3 = 0.6f + 0.4f * clutch;
			return CalcEngineTorque(rpm) * num2 * num * num3 * 0.9f;
		}

		public float EngineBrakeWheelTorque(float throttle)
		{
			if (shifting)
			{
				return 0f;
			}
			float num = Mathf.Abs(p.GearRatios[gear]) * p.FinalDrive;
			if (num == 0f)
			{
				return 0f;
			}
			float num2 = (LimiterActive ? 0f : throttle);
			return CalcFrictionTorque(rpm) * (1f - num2) * num * clutch * 0.9f;
		}
	}
	public enum SurfaceKind
	{
		Tarmac,
		Dirt
	}
	public class RallyVehicle
	{
		private const float RPM2RadS = (float)Math.PI / 30f;

		private const float DiffLockNmPerRpm = 7f;

		private const float LatAccelLimit = 9f;

		private const float SlipAngleMarginDeg = 8f;

		public readonly LandVehicle Vehicle;

		public readonly RallyProfile Profile;

		public readonly EngineSim Engine;

		private readonly HashSet<int> handbrakeColliderIds = new HashSet<int>();

		private readonly int terrainLayer;

		private readonly int groundMask;

		private readonly Vector3 originalCenterOfMass;

		private readonly float wheelbase;

		private bool comLowered;

		private readonly float originalDrag;

		private bool dragLowered;

		private readonly CollisionDetectionMode originalCollisionMode;

		private bool collisionModeChanged;

		public bool HandbrakeActive;

		public float Throttle;

		public float Brake;

		public float AvgWheelRpm;

		public SurfaceKind Surface;

		private float logTimer;

		private int lastLoggedGear = -1;

		private float filteredFrontRpm;

		private float filteredRearRpm;

		private static float ClampWheelRpm(float rawRpm, float speedKmh, float radius)
		{
			float num = speedKmh / 3.6f / radius * 9.549297f;
			if (num > 60f)
			{
				return Mathf.Max(rawRpm, num * 0.85f);
			}
			if (num < -60f)
			{
				return Mathf.Min(rawRpm, num * 0.85f);
			}
			return rawRpm;
		}

		private static string GearName(int g)
		{
			return g switch
			{
				1 => "N", 
				0 => "R", 
				_ => (g - 1).ToString(), 
			};
		}

		public RallyVehicle(LandVehicle vehicle)
		{
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: 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)
			Vehicle = vehicle;
			terrainLayer = LayerMask.NameToLayer("Terrain");
			groundMask = (1 << LayerMask.NameToLayer("Default")) | (1 << terrainLayer);
			float num = 0.5f;
			if (vehicle.wheels.Count > 0 && (Object)(object)vehicle.wheels[0].wheelCollider != (Object)null)
			{
				num = vehicle.wheels[0].wheelCollider.radius;
			}
			Profile = RallyProfiles.Get(vehicle.vehicleCode, num, vehicle.Rb.mass);
			Engine = new EngineSim(Profile);
			originalDrag = vehicle.Rb.drag;
			vehicle.Rb.drag = Profile.DragCoefficient;
			dragLowered = true;
			originalCollisionMode = vehicle.Rb.collisionDetectionMode;
			vehicle.Rb.collisionDetectionMode = (CollisionDetectionMode)2;
			collisionModeChanged = true;
			Il2CppReferenceArray<WheelCollider> handbrakeWheels = vehicle.handbrakeWheels;
			if (handbrakeWheels != null)
			{
				for (int i = 0; i < ((Il2CppArrayBase<WheelCollider>)(object)handbrakeWheels).Length; i++)
				{
					if ((Object)(object)((Il2CppArrayBase<WheelCollider>)(object)handbrakeWheels)[i] != (Object)null)
					{
						handbrakeColliderIds.Add(((Object)((Il2CppArrayBase<WheelCollider>)(object)handbrakeWheels)[i]).GetInstanceID());
					}
				}
			}
			originalCenterOfMass = vehicle.Rb.centerOfMass;
			float num2 = float.MinValue;
			float num3 = float.MaxValue;
			for (int j = 0; j < vehicle.wheels.Count; j++)
			{
				WheelCollider val = (((Object)(object)vehicle.wheels[j] != (Object)null) ? vehicle.wheels[j].wheelCollider : null);
				if (!((Object)(object)val == (Object)null))
				{
					float z = ((Component)vehicle).transform.InverseTransformPoint(((Component)val).transform.position).z;
					if (z > num2)
					{
						num2 = z;
					}
					if (z < num3)
					{
						num3 = z;
					}
				}
			}
			wheelbase = ((num2 > num3) ? Mathf.Max(num2 - num3, 1.5f) : 2.8f);
			lastLoggedGear = Engine.gear;
			StringBuilder stringBuilder = new StringBuilder();
			for (int k = 0; k < Profile.GearRatios.Length; k++)
			{
				if (k > 0)
				{
					stringBuilder.Append(", ");
				}
				stringBuilder.Append(Profile.GearRatios[k].ToString("0.00"));
			}
			Utility.Log($"[RALLY] Bound '{Profile.Code}' mass={vehicle.Rb.mass:0}kg wheelR={num:0.00}m wheels={vehicle.wheels.Count} hbWheels={handbrakeColliderIds.Count} | FD={Profile.FinalDrive:0.00} gears=[{stringBuilder}] | shiftUp={Profile.ShiftUpRPM:0} shiftDown={Profile.ShiftDownRPM:0} maxRPM={Profile.MaxRPM:0} | top={Profile.TopSpeedKmh:0}km/h 1st-top={Profile.FirstGearTopKmh:0}km/h");
		}

		public void RestoreCenterOfMass()
		{
			//IL_003c: 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)
			if (comLowered && (Object)(object)Vehicle != (Object)null && (Object)(object)Vehicle.Rb != (Object)null)
			{
				Vehicle.Rb.centerOfMass = originalCenterOfMass;
				comLowered = false;
			}
			if (dragLowered)
			{
				Vehicle.Rb.drag = originalDrag;
				dragLowered = false;
			}
			if (collisionModeChanged)
			{
				Vehicle.Rb.collisionDetectionMode = originalCollisionMode;
				collisionModeChanged = false;
			}
		}

		private void UpdateSurface()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			RaycastHit val = default(RaycastHit);
			if (Physics.Raycast(((Component)Vehicle).transform.position + Vector3.up * 0.5f, Vector3.down, ref val, 3f, groundMask))
			{
				Surface = (((Object)(object)((RaycastHit)(ref val)).collider != (Object)null && ((Component)((RaycastHit)(ref val)).collider).gameObject.layer == terrainLayer) ? SurfaceKind.Dirt : SurfaceKind.Tarmac);
			}
		}

		public void HandleThrottle()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_080c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0811: Unknown result type (might be due to invalid IL or missing references)
			//IL_0816: Unknown result type (might be due to invalid IL or missing references)
			//IL_0818: Unknown result type (might be due to invalid IL or missing references)
			//IL_07e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_07f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0837: Unknown result type (might be due to invalid IL or missing references)
			//IL_0842: Unknown result type (might be due to invalid IL or missing references)
			//IL_0858: Unknown result type (might be due to invalid IL or missing references)
			//IL_07a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_07bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_09b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_09b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_09c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_09c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_09c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_09ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a01: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a16: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a31: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a49: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a4e: Unknown result type (might be due to invalid IL or missing references)
			LandVehicle vehicle = Vehicle;
			float fixedDeltaTime = Time.fixedDeltaTime;
			float num = vehicle.currentThrottle;
			float speed_Kmh = vehicle.Speed_Kmh;
			float x = GameInput.MotionAxis.x;
			if (Mathf.Abs(num) > 0.01f && Mathf.Abs(x) > 0.01f)
			{
				num = Mathf.Clamp(num / Mathf.Sqrt(Mathf.Max(1f - x * x, 0.05f)), -1f, 1f);
			}
			if (!comLowered)
			{
				vehicle.Rb.centerOfMass = originalCenterOfMass + Vector3.down * Profile.ComLowerY;
				comLowered = true;
			}
			if (vehicle.Rb.IsSleeping())
			{
				vehicle.Rb.WakeUp();
			}
			HandbrakeActive = GameInput.GetButton((ButtonCode)16);
			float num2 = 0f;
			float num3 = 0f;
			bool flag = false;
			if (num > 0.01f)
			{
				if (speed_Kmh > -2f)
				{
					num2 = num;
				}
				else
				{
					num3 = num;
				}
			}
			else if (num < -0.01f)
			{
				if (speed_Kmh > 4f)
				{
					num3 = 0f - num;
				}
				else
				{
					flag = true;
					num2 = 0f - num;
				}
			}
			if (flag)
			{
				Engine.EnsureReverse();
			}
			else if (num2 > 0f)
			{
				Engine.EnsureFirst();
			}
			float num4 = 0f;
			int num5 = 0;
			float num6 = 0f;
			float num7 = 0f;
			int num8 = 0;
			int num9 = 0;
			bool flag2 = false;
			float num10 = 0f;
			float num11 = 0.5f;
			List<Wheel> wheels = vehicle.wheels;
			for (int i = 0; i < wheels.Count; i++)
			{
				Wheel val = wheels[i];
				WheelCollider val2 = (((Object)(object)val != (Object)null) ? val.wheelCollider : null);
				if (!((Object)(object)val2 == (Object)null))
				{
					float num12 = ClampWheelRpm(val2.rpm, speed_Kmh, val2.radius);
					num4 += num12;
					num5++;
					if (val.IsSteerWheel)
					{
						num6 += num12;
						num8++;
					}
					else
					{
						num7 += num12;
						num9++;
					}
					num11 = val2.radius;
					if (val2.isGrounded)
					{
						flag2 = true;
					}
					float num13 = Mathf.Abs(num12) * ((float)Math.PI / 30f) * val2.radius;
					if (num13 > num10)
					{
						num10 = num13;
					}
				}
			}
			UpdateSurface();
			float num14 = Mathf.Abs(speed_Kmh) / 3.6f;
			bool flag3 = num10 > num14 + 4f && num10 > num14 * 1.5f;
			AvgWheelRpm = ((num5 > 0) ? (num4 / (float)num5) : 0f);
			float avgDriveWheelRpm = Mathf.Abs(speed_Kmh) / 3.6f / num11 * 9.549297f;
			Engine.Step(num2, num3, avgDriveWheelRpm, speed_Kmh, flag2, flag3, fixedDeltaTime);
			bool flag4 = false;
			if (RallyConfig.VerboseLogging != null && RallyConfig.VerboseLogging.Value)
			{
				if (Engine.gear != lastLoggedGear)
				{
					Utility.Log($"[SHIFT] {GearName(lastLoggedGear)} -> {GearName(Engine.gear)} @ {Engine.rpm:0} RPM, {speed_Kmh:0.0} km/h");
					lastLoggedGear = Engine.gear;
				}
				logTimer += fixedDeltaTime;
				if (logTimer >= 0.5f)
				{
					logTimer = 0f;
					flag4 = true;
				}
			}
			int num15 = Mathf.Max(num5, 1);
			float num16 = Engine.DriveWheelTorque(num2) * RallyConfig.PowerMult.Value / (float)num15;
			float num17 = ((Surface == SurfaceKind.Dirt) ? (1f * RallyConfig.DirtGrip.Value) : (1.9f * RallyConfig.TarmacGrip.Value));
			float num18 = num17 * (vehicle.Rb.mass * 9.81f / (float)num15) * num11 * 0.85f;
			num16 = Mathf.Clamp(num16, 0f - num18, num18);
			float num19 = 0f;
			if (flag2 && Mathf.Abs(speed_Kmh) > 2f)
			{
				num19 = Mathf.Min(Engine.EngineBrakeWheelTorque(num2) * 0.5f, 1000f) / num11;
			}
			float num20 = vehicle.Rb.mass * Profile.BrakeG * 9.81f * num11;
			float num21 = vehicle.handBrakeForce * 2f;
			float num22 = Mathf.Clamp01(fixedDeltaTime / 0.15f);
			filteredFrontRpm += (((num8 > 0) ? (num6 / (float)num8) : 0f) - filteredFrontRpm) * num22;
			filteredRearRpm += (((num9 > 0) ? (num7 / (float)num9) : 0f) - filteredRearRpm) * num22;
			float num23 = (filteredFrontRpm + filteredRearRpm) * 0.5f;
			float num24 = Mathf.Abs(num16) * 1.5f + 500f;
			float num25 = Mathf.Clamp((num23 - filteredFrontRpm) * 7f, 0f - num24, num24);
			float num26 = 0f - num25;
			StringBuilder stringBuilder = (flag4 ? new StringBuilder("[WHEELS] ") : null);
			for (int j = 0; j < wheels.Count; j++)
			{
				Wheel val3 = wheels[j];
				WheelCollider val4 = (((Object)(object)val3 != (Object)null) ? val3.wheelCollider : null);
				if (!((Object)(object)val4 == (Object)null))
				{
					bool flag5 = handbrakeColliderIds.Contains(((Object)val4).GetInstanceID());
					float num27 = (val3.IsSteerWheel ? 0.65f : 0.35f);
					float num28 = ((num3 > 0.01f) ? (num3 * num20 * num27 * 0.5f) : 0f);
					float num29 = num16 + (val3.IsSteerWheel ? num25 : num26);
					num29 = Mathf.Clamp(num29, 0f - num18, num18) + 0.0001f;
					if (HandbrakeActive && flag5)
					{
						num29 = 0.0001f;
						num28 = Mathf.Max(num28, num21);
					}
					val4.motorTorque = num29;
					val4.brakeTorque = num28;
					stringBuilder?.AppendFormat("W{0}({1}{2}{3}) rpm={4:0} mt={5:0} bt={6:0} sa={7:0.0}  ", j, val3.IsSteerWheel ? "S" : "-", flag5 ? "H" : "-", val4.isGrounded ? "G" : "-", val4.rpm, num29, num28, val4.steerAngle);
				}
			}
			if (num2 > 0.2f && Mathf.Abs(speed_Kmh) < 4f)
			{
				float num30 = (Engine.InReverse ? (-1f) : 1f);
				float num31 = 1f - Mathf.Abs(speed_Kmh) / 4f;
				vehicle.Rb.AddForce(((Component)vehicle).transform.forward * (num30 * num2 * num31 * vehicle.Rb.mass * 3f));
			}
			if (num19 > 0f)
			{
				vehicle.Rb.AddForce(((Component)vehicle).transform.forward * ((0f - Mathf.Sign(speed_Kmh)) * num19));
			}
			Vector3 val5 = ((Component)vehicle).transform.InverseTransformDirection(vehicle.Rb.velocity);
			if (Mathf.Abs(val5.x) > 2f)
			{
				vehicle.Rb.AddTorque(Vector3.up * ((0f - vehicle.Rb.angularVelocity.y) * RallyConfig.YawDamping.Value), (ForceMode)5);
			}
			Throttle = num2;
			Brake = num3;
			if (flag4)
			{
				Utility.Log(string.Format("[TELEM] v={0:0.0}km/h axis={1:0.00} thr={2:0.00} brk={3:0.00} hb={4} | gear={5} rpm={6:0} wheelAvg={7:0} clutch={8:0.00} {9}{10}| drv/whl={11:0}Nm engBrkN={12:0} diffF={13:0}Nm | spin={14} gnd={15} surf={16} steer={17:0.0}", speed_Kmh, num, num2, num3, HandbrakeActive ? 1 : 0, Engine.GearLabel, Engine.rpm, AvgWheelRpm, Engine.clutch, Engine.LimiterActive ? "[LIMIT] " : "", Engine.shifting ? "[SHIFTING] " : "", num16, num19, num25, flag3 ? 1 : 0, flag2 ? 1 : 0, Surface, vehicle.CurrentSteerAngle));
				Vector3 velocity = vehicle.Rb.velocity;
				Vector3 val6 = ((Component)vehicle).transform.InverseTransformDirection(velocity);
				Utility.Log($"[MOTION] vMag={((Vector3)(ref velocity)).magnitude * 3.6f:0.0}km/h vFwd={val6.z * 3.6f:0.0} vLat={val6.x * 3.6f:0.0} vUp={val6.y * 3.6f:0.0} | yawRate={vehicle.Rb.angularVelocity.y:0.00}rad/s rollDeg={Vector3.Angle(((Component)vehicle).transform.up, Vector3.up):0.0}");
				if (stringBuilder != null)
				{
					Utility.Log(stringBuilder.ToString());
				}
			}
			vehicle.SetIsBraking(num3 > 0.05f || (HandbrakeActive && Mathf.Abs(speed_Kmh) > 4f));
			vehicle.SetIsReversing(Engine.InReverse && speed_Kmh < -0.1f);
			if (HandbrakeActive && !vehicle.HandbrakeApplied && (Delegate)(object)vehicle.onHandbrakeApplied != (Delegate)null)
			{
				vehicle.onHandbrakeApplied.Invoke();
			}
			vehicle.HandbrakeApplied = HandbrakeActive;
		}

		public void ApplyWheelFriction(Wheel w)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			WheelCollider wheelCollider = w.wheelCollider;
			if (!((Object)(object)wheelCollider == (Object)null) && ((Collider)wheelCollider).enabled)
			{
				bool flag = Surface == SurfaceKind.Dirt;
				float num = (flag ? RallyConfig.DirtGrip.Value : RallyConfig.TarmacGrip.Value);
				bool flag2 = HandbrakeActive && !w.IsSteerWheel;
				float num2 = (flag2 ? (0.4f / Mathf.Max(RallyConfig.HandbrakeLooseness.Value, 0.1f)) : 1f);
				WheelFrictionCurve forwardFriction = wheelCollider.forwardFriction;
				WheelFrictionCurve sidewaysFriction = wheelCollider.sidewaysFriction;
				if (flag)
				{
					((WheelFrictionCurve)(ref forwardFriction)).extremumSlip = 0.5f;
					((WheelFrictionCurve)(ref forwardFriction)).extremumValue = 0.95f;
					((WheelFrictionCurve)(ref forwardFriction)).asymptoteSlip = 1.2f;
					((WheelFrictionCurve)(ref forwardFriction)).asymptoteValue = 0.55f;
					((WheelFrictionCurve)(ref forwardFriction)).stiffness = 1.05f * num;
					((WheelFrictionCurve)(ref sidewaysFriction)).extremumSlip = 0.45f;
					((WheelFrictionCurve)(ref sidewaysFriction)).extremumValue = 0.9f;
					((WheelFrictionCurve)(ref sidewaysFriction)).asymptoteSlip = 0.9f;
					((WheelFrictionCurve)(ref sidewaysFriction)).asymptoteValue = 0.6f;
					((WheelFrictionCurve)(ref sidewaysFriction)).stiffness = 0.8f * num * num2;
				}
				else
				{
					((WheelFrictionCurve)(ref forwardFriction)).extremumSlip = 0.35f;
					((WheelFrictionCurve)(ref forwardFriction)).extremumValue = 1f;
					((WheelFrictionCurve)(ref forwardFriction)).asymptoteSlip = 0.8f;
					((WheelFrictionCurve)(ref forwardFriction)).asymptoteValue = 0.75f;
					((WheelFrictionCurve)(ref forwardFriction)).stiffness = 1.9f * num;
					((WheelFrictionCurve)(ref sidewaysFriction)).extremumSlip = 0.3f;
					((WheelFrictionCurve)(ref sidewaysFriction)).extremumValue = 1f;
					((WheelFrictionCurve)(ref sidewaysFriction)).asymptoteSlip = 0.65f;
					((WheelFrictionCurve)(ref sidewaysFriction)).asymptoteValue = 0.8f;
					float num3 = Mathf.Lerp(RallyConfig.RearGrip.Value, 0.9f, Mathf.Clamp01((Mathf.Abs(Vehicle.Speed_Kmh) - 50f) / 50f));
					float num4 = (w.IsSteerWheel ? 1f : num3);
					((WheelFrictionCurve)(ref sidewaysFriction)).stiffness = 1.4f * num * num4 * num2;
				}
				if (flag2)
				{
					((WheelFrictionCurve)(ref forwardFriction)).stiffness = ((WheelFrictionCurve)(ref forwardFriction)).stiffness * 0.55f;
				}
				wheelCollider.forwardFriction = forwardFriction;
				wheelCollider.sidewaysFriction = sidewaysFriction;
			}
		}

		public void HandleSteering()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_003d: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			LandVehicle vehicle = Vehicle;
			float deltaTime = Time.deltaTime;
			float num = Mathf.Abs(vehicle.Speed_Kmh);
			float x = GameInput.MotionAxis.x;
			Vector3 val = ((Component)vehicle).transform.InverseTransformDirection(vehicle.Rb.velocity);
			bool flag = Mathf.Abs(val.x) > 1f && Mathf.Abs(x) > 0.01f && Mathf.Sign(x) == Mathf.Sign(val.x);
			float num2 = vehicle.ActualMaxSteeringAngle;
			float num3 = num / 3.6f;
			if (flag)
			{
				float num4 = Mathf.Atan2(Mathf.Abs(val.x), Mathf.Max(Mathf.Abs(val.z), 0.5f)) * 57.29578f;
				num2 = Mathf.Min(vehicle.ActualMaxSteeringAngle, num4 + 10f);
			}
			else if (num3 > 3f)
			{
				float num5 = Mathf.Atan(wheelbase * 9f / (num3 * num3)) * 57.29578f + 8f;
				num2 = Mathf.Clamp(num5, 8f, num2);
			}
			num2 = Mathf.Min(num2, Mathf.Lerp(12f, vehicle.ActualMaxSteeringAngle, Mathf.Clamp01(num / 12f)));
			float lastFrameSteerAngle = vehicle.lastFrameSteerAngle;
			lastFrameSteerAngle = ((!(Mathf.Abs(x) > 0.001f)) ? Mathf.MoveTowards(lastFrameSteerAngle, 0f, vehicle.steerRate * 1.6f * deltaTime) : Mathf.Clamp(lastFrameSteerAngle + x * vehicle.steerRate * 1.1f * deltaTime, 0f - num2, num2));
			lastFrameSteerAngle = (vehicle.CurrentSteerAngle = Mathf.Clamp(lastFrameSteerAngle, 0f - num2, num2));
			if (Mathf.Abs(vehicle.lastReplicatedSteerAngle - lastFrameSteerAngle) > 3f)
			{
				vehicle.lastReplicatedSteerAngle = lastFrameSteerAngle;
				vehicle.SetSteeringAngle(lastFrameSteerAngle);
			}
			vehicle.lastFrameSteerAngle = lastFrameSteerAngle;
		}

		public void HandleAero()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_0066: 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)
			Rigidbody rb = Vehicle.Rb;
			Vector3 velocity = rb.velocity;
			float sqrMagnitude = ((Vector3)(ref velocity)).sqrMagnitude;
			if (!(sqrMagnitude < 0.01f))
			{
				rb.AddForce(-((Vector3)(ref velocity)).normalized * (0.61f * Profile.CdA * sqrMagnitude));
				rb.AddForce(-((Component)Vehicle).transform.up * (Profile.DownforceCoeff * sqrMagnitude));
			}
		}
	}
	public static class RallyConfig
	{
		public static MelonPreferences_Category Category;

		public static MelonPreferences_Entry<bool> Enabled;

		public static MelonPreferences_Entry<bool> DebugHud;

		public static MelonPreferences_Entry<bool> VerboseLogging;

		public static MelonPreferences_Entry<float> TarmacGrip;

		public static MelonPreferences_Entry<float> DirtGrip;

		public static MelonPreferences_Entry<float> PowerMult;

		public static MelonPreferences_Entry<float> HandbrakeLooseness;

		public static MelonPreferences_Entry<float> RearGrip;

		public static MelonPreferences_Entry<float> YawDamping;

		public static void Init()
		{
			Category = MelonPreferences.CreateCategory("DoYouCatchMyDrift");
			Enabled = Category.CreateEntry<bool>("RallyModeEnabled", true, (string)null, "Master toggle for rally physics (F6 in game)", false, false, (ValueValidator)null, (string)null);
			DebugHud = Category.CreateEntry<bool>("DebugHud", true, (string)null, "Show gear/RPM/surface HUD while driving (F7 in game)", false, false, (ValueValidator)null, (string)null);
			VerboseLogging = Category.CreateEntry<bool>("VerboseLogging", false, (string)null, "Log rally telemetry to the Melon console once per second", false, false, (ValueValidator)null, (string)null);
			TarmacGrip = Category.CreateEntry<float>("TarmacGrip", 1f, (string)null, "Grip multiplier on roads/built surfaces", false, false, (ValueValidator)null, (string)null);
			DirtGrip = Category.CreateEntry<float>("DirtGrip", 1f, (string)null, "Grip multiplier on terrain (dirt/grass)", false, false, (ValueValidator)null, (string)null);
			PowerMult = Category.CreateEntry<float>("PowerMultiplier", 1f, (string)null, "Engine power multiplier for all profiles. Set it to 1.5f to have more fun!", false, false, (ValueValidator)null, (string)null);
			HandbrakeLooseness = Category.CreateEntry<float>("HandbrakeLooseness", 1f, (string)null, "Higher = rear steps out more on handbrake", false, false, (ValueValidator)null, (string)null);
			RearGrip = Category.CreateEntry<float>("RearGrip", 0.7f, (string)null, "REAR sideways grip as a fraction of front, at ALL speeds. 0.67 = 60/40 front/rear split. Lower = tail-happier, 1.0 = planted. Fronts always keep full grip for steering/countersteer", false, false, (ValueValidator)null, (string)null);
			YawDamping = Category.CreateEntry<float>("YawDamping", 0.78f, (string)null, "Counter-torque per rad/s of rotation while sliding. Settles the drift-catch-swing pendulum. 0 = off, 0.6 = default, 1.0+ = very settled/harder to rotate", false, false, (ValueValidator)null, (string)null);
		}

		public static void ToggleEnabled()
		{
			Enabled.Value = !Enabled.Value;
			MelonPreferences.Save();
			if (!Enabled.Value)
			{
				RallyManager.RestoreAll();
			}
			Utility.Log("Rally mode " + (Enabled.Value ? "ENABLED" : "DISABLED"));
		}

		public static void ToggleHud()
		{
			DebugHud.Value = !DebugHud.Value;
			MelonPreferences.Save();
		}
	}
	public static class RallyManager
	{
		private static readonly Dictionary<int, RallyVehicle> sims = new Dictionary<int, RallyVehicle>();

		public static RallyVehicle CurrentDriven;

		public static bool IsActiveFor(LandVehicle v)
		{
			return RallyConfig.Enabled != null && RallyConfig.Enabled.Value && (Object)(object)v != (Object)null && v.LocalPlayerIsDriver && !v.overrideControls;
		}

		public static RallyVehicle For(LandVehicle v)
		{
			int instanceID = ((Object)v).GetInstanceID();
			if (!sims.TryGetValue(instanceID, out var value))
			{
				value = new RallyVehicle(v);
				sims[instanceID] = value;
			}
			CurrentDriven = value;
			return value;
		}

		public static void RestoreAll()
		{
			foreach (RallyVehicle value in sims.Values)
			{
				value.RestoreCenterOfMass();
			}
		}

		public static void Clear()
		{
			sims.Clear();
			CurrentDriven = null;
		}
	}
	public static class RallyHud
	{
		public static void Draw()
		{
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			if (RallyConfig.DebugHud != null && RallyConfig.DebugHud.Value)
			{
				RallyVehicle currentDriven = RallyManager.CurrentDriven;
				if (currentDriven != null && !((Object)(object)currentDriven.Vehicle == (Object)null) && currentDriven.Vehicle.LocalPlayerIsDriver)
				{
					string text = string.Format("RALLY {0}   {1,4:0} km/h   Gear {2}   {3,5:0} RPM{4}   Wheel {5,4:0}\nSurface: {6}   Clutch {7:0.00}   Thr {8:0.00}  Brk {9:0.00}{10}", RallyConfig.Enabled.Value ? "ON" : "OFF", currentDriven.Vehicle.Speed_Kmh, currentDriven.Engine.GearLabel, currentDriven.Engine.rpm, currentDriven.Engine.LimiterActive ? " [LIMIT]" : (currentDriven.Engine.shifting ? " [SHIFT]" : ""), currentDriven.AvgWheelRpm, currentDriven.Surface, currentDriven.Engine.clutch, currentDriven.Throttle, currentDriven.Brake, currentDriven.HandbrakeActive ? "   HANDBRAKE" : "");
					GUI.Box(new Rect(10f, (float)Screen.height - 78f, 430f, 62f), "");
					GUI.Label(new Rect(20f, (float)Screen.height - 72f, 420f, 55f), text);
				}
			}
		}
	}
	[HarmonyPatch(typeof(LandVehicle), "ApplyThrottle")]
	internal static class Patch_LandVehicle_ApplyThrottle
	{
		private static bool Prefix(LandVehicle __instance)
		{
			if (!RallyManager.IsActiveFor(__instance))
			{
				return true;
			}
			try
			{
				RallyManager.For(__instance).HandleThrottle();
				return false;
			}
			catch (Exception e)
			{
				Utility.PrintException(e);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(LandVehicle), "UpdateSteerAngle")]
	internal static class Patch_LandVehicle_UpdateSteerAngle
	{
		private static bool Prefix(LandVehicle __instance)
		{
			if (!RallyManager.IsActiveFor(__instance))
			{
				return true;
			}
			try
			{
				if ((Object)(object)__instance.DriverPlayer != (Object)null && __instance.DriverPlayer.IsTased)
				{
					return true;
				}
				if ((Object)(object)Player.Local != (Object)null && (Player.Local.Seizure || Player.Local.Disoriented))
				{
					return true;
				}
				RallyManager.For(__instance).HandleSteering();
				return false;
			}
			catch (Exception e)
			{
				Utility.PrintException(e);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Wheel), "ApplyFriction")]
	internal static class Patch_Wheel_ApplyFriction
	{
		private static bool Prefix(Wheel __instance)
		{
			LandVehicle vehicle = __instance.vehicle;
			if (!RallyManager.IsActiveFor(vehicle))
			{
				return true;
			}
			try
			{
				RallyManager.For(vehicle).ApplyWheelFriction(__instance);
				return false;
			}
			catch (Exception e)
			{
				Utility.PrintException(e);
				return true;
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		internal IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}

DoYouCatchMyDrift_Mono_1.1.0.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.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using DoYouCatchMyDrift;
using DoYouCatchMyDrift.Rally;
using DoYouCatchMyDrift.TemplateUtils;
using HarmonyLib;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using ScheduleOne;
using ScheduleOne.DevUtilities;
using ScheduleOne.Persistence;
using ScheduleOne.PlayerScripts;
using ScheduleOne.Vehicles;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Core), "Do You Catch My Drift", "1.1.0", "OverweightUnicorn", null)]
[assembly: MelonColor(255, 191, 0, 255)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyCompany("DoYouCatchMyDrift_Mono_1.1.0")]
[assembly: AssemblyConfiguration("MONO")]
[assembly: AssemblyFileVersion("1.1.0")]
[assembly: AssemblyInformationalVersion("1.0.0+a726d37961d63920903a5730415253b0c9f17281")]
[assembly: AssemblyProduct("DoYouCatchMyDrift_Mono_1.1.0")]
[assembly: AssemblyTitle("DoYouCatchMyDrift_Mono_1.1.0")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DoYouCatchMyDrift
{
	public static class BuildInfo
	{
		public const string Name = "Do You Catch My Drift";

		public const string Description = "Vehicle physics tuner – balanced vs drift presets.";

		public const string Author = "OverweightUnicorn";

		public const string Company = "UnicornsCanMod";

		public const string Version = "1.1.0";

		public const string DownloadLink = null;
	}
	public class Core : MelonMod
	{
		public static string gameObjectString = "Map/Hyland Point/Region_Northtown/Waterfront/Mooring Bollard (8)";

		public override void OnInitializeMelon()
		{
			AssetBundleUtils.Initialize(this);
			RallyConfig.Init();
			Utility.Log("Do You Catch My Drift v1.1.0 initialised. F6 toggles rally physics, F5 toggles the HUD.");
		}

		public override void OnUpdate()
		{
			if (Input.GetKeyDown((KeyCode)287))
			{
				RallyConfig.ToggleEnabled();
			}
			if (Input.GetKeyDown((KeyCode)286))
			{
				RallyConfig.ToggleHud();
			}
		}

		public override void OnGUI()
		{
			RallyHud.Draw();
		}

		public override void OnLateInitializeMelon()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			Singleton<LoadManager>.Instance.onLoadComplete.AddListener(new UnityAction(InitMod));
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			((MelonMod)this).OnSceneWasLoaded(buildIndex, sceneName);
			RallyManager.Clear();
			if (sceneName == "Main")
			{
				OverpassModule.OnSceneLoaded(sceneName);
			}
			else
			{
				OverpassModule.OnMenuLoaded();
			}
		}

		public void InitMod()
		{
			Utility.Log("Game has loaded!");
		}

		public void PlaceCubeAt(string targetName)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GameObject.Find(targetName);
			if ((Object)(object)val == (Object)null)
			{
				MelonLogger.Msg("Could not find GameObject named '" + targetName + "'");
				return;
			}
			Vector3 position = val.transform.position;
			GameObject val2 = GameObject.CreatePrimitive((PrimitiveType)3);
			val2.transform.position = new Vector3(-46.75f, -8f, 198f);
			val2.transform.localScale = new Vector3(67f, 5.99f, 70f);
			((Object)val2).name = "PlacedCube_BOIIIIIIIIIIIIIIIII";
			Renderer component = val2.GetComponent<Renderer>();
			if ((Object)(object)component != (Object)null)
			{
				Shader val3 = Shader.Find("Universal Render Pipeline/Lit");
				if ((Object)(object)val3 == (Object)null)
				{
					MelonLogger.Msg(ConsoleColor.Red, "URP shader not found. The cube may still be invisible.");
				}
				else
				{
					Material val4 = new Material(val3);
					val4.color = Color.gray;
					component.material = val4;
					MelonLogger.Msg(ConsoleColor.Green, "Applied URP material to cube.");
				}
			}
			val2.layer = LayerMask.NameToLayer("Default");
		}
	}
	public static class OverpassModule
	{
		private const string OverpassRootPath = "Map/Hyland Point/Overpass";

		private const string IslandPath = "Map/Island";

		private const string OceanPath = "Map/StylizedWater2_Ocean";

		private const string OverpassRampParentForCollider = "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/Ramp";

		private const string OverpassRampCube = "Map/Hyland Point/Overpass/Overpass Ramp/Cube";

		private const string SkyboxPath = "EnvironmentManager/Skybox";

		private static string[] OverpassRamp = new string[6] { "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/RampSupport", "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/RampSupport (1)", "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/RampSupport (2)", "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/RampSupport (3)", "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/RampSupport (4)", "Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/CurvedBeams" };

		private static GameObject overpassFillInstance;

		private const string OverpassBundleName = "doyoucatchmydrift";

		private const string OverpassPrefabAssetName = "overpassfill";

		private const string URPLitShaderName = "Universal Render Pipeline/Lit";

		private static GameObject overpassPrefab;

		private static readonly string[] RampKeepNames = new string[3] { "Ramp", "Block", "CurvedBeams" };

		private static readonly string[] RoadblockerTargets = new string[5] { "Map/Hyland Point/Overpass/Overpass Ramp/RoadBlocker4_LOD (1)", "Map/Hyland Point/Overpass/Overpass Ramp/RoadBlocker4_LOD (2)", "Map/Hyland Point/Overpass/Overpass Ramp/RoadBlocker4_LOD (3)", "Map/Hyland Point/Overpass/Overpass Ramp/RoadBlocker4_LOD (4)", "@Businesses/Car Wash/Dumpster" };

		private static bool built = false;

		private static void LoadOverpassAssetBundle()
		{
			AssetBundle val = AssetBundleUtils.LoadAssetBundle("doyoucatchmydrift");
			if ((Object)(object)val == (Object)null)
			{
				MelonLogger.Warning("[Overpass] AssetBundle 'doyoucatchmydrift' failed to load -- falling back to in-scene mesh recovery.");
				return;
			}
			GameObject val2 = AssetBundleUtils.LoadAssetFromBundle<GameObject>("overpassfill.prefab", "doyoucatchmydrift");
			if ((Object)(object)val2 != (Object)null)
			{
				overpassPrefab = val2;
				MelonLogger.Msg("[Overpass] Loaded prefab 'overpassfill' from bundle 'doyoucatchmydrift'.");
			}
			else
			{
				MelonLogger.Warning("[Overpass] Prefab 'overpassfill' not found in bundle 'doyoucatchmydrift' -- falling back to in-scene mesh recovery.");
			}
		}

		public static void OnSceneLoaded(string sceneName)
		{
			if (!(sceneName != "Main") && !built)
			{
				built = true;
				MelonCoroutines.Start(RunAfterDelay(1f));
			}
		}

		public static void OnMenuLoaded()
		{
			built = false;
			overpassPrefab = null;
			overpassFillInstance = null;
			AssetBundleUtils.ClearCache();
		}

		private static IEnumerator WaitForPath(string path, Action<GameObject> onFound, float timeout = 10f)
		{
			float elapsed = 0f;
			GameObject found = null;
			while ((Object)(object)found == (Object)null && elapsed < timeout)
			{
				found = FindByFullPathIncludingInactive(path);
				if ((Object)(object)found != (Object)null)
				{
					break;
				}
				yield return null;
				elapsed += Time.deltaTime;
			}
			if ((Object)(object)found == (Object)null)
			{
				MelonLogger.Warning($"[Overpass] Timed out after {timeout}s waiting for '{path}'.");
			}
			else
			{
				onFound(found);
			}
		}

		private static IEnumerator RunAfterDelay(float delay)
		{
			yield return (object)new WaitForSeconds(delay);
			LoadOverpassAssetBundle();
			MelonCoroutines.Start(StripOriginalColliders());
			MelonCoroutines.Start(DisableIsland());
			MelonCoroutines.Start(ScaleTheOcean());
			MelonCoroutines.Start(DisableRoadblockers());
			MelonCoroutines.Start(DisableOverpassRampObjects());
			MelonCoroutines.Start(FixSkybox());
			MelonCoroutines.Start(FixRampMeshCollider());
			MelonCoroutines.Start(SpawnOverpassFill());
			MelonCoroutines.Start(RemoveTreesUnderOverpass());
			MelonCoroutines.Start(EnforceInactive("Map/Hyland Point/Overpass/Overpass Ramp/Cube"));
		}

		private static IEnumerator StripOriginalColliders()
		{
			GameObject overpassRoot = null;
			yield return WaitForPath("Map/Hyland Point/Overpass", delegate(GameObject o)
			{
				overpassRoot = o;
			});
			if (Object.op_Implicit((Object)(object)overpassRoot))
			{
				int removed = 0;
				BoxCollider[] componentsInChildren = overpassRoot.GetComponentsInChildren<BoxCollider>(true);
				foreach (BoxCollider col in componentsInChildren)
				{
					if (Mathf.Approximately(col.size.z, 0.65f))
					{
						Vector3 newSize = col.size;
						newSize.z = 0f;
						col.size = newSize;
						Vector3 newCenter = col.center;
						newCenter.z = 0.1f;
						col.center = newCenter;
						removed++;
					}
				}
				MelonLogger.Msg("[Overpass] Removed " + removed + " original colliders.");
			}
			else
			{
				MelonLogger.Warning("[Overpass] 'Map/Hyland Point/Overpass' never appeared -- skipping collider strip.");
			}
		}

		private static IEnumerator DisableIsland()
		{
			GameObject obj = null;
			yield return WaitForPath("Map/Island", delegate(GameObject o)
			{
				obj = o;
			});
			if (Object.op_Implicit((Object)(object)obj))
			{
				obj.SetActive(false);
			}
			else
			{
				MelonLogger.Warning("[Overpass] Island 'Map/Island' never appeared -- not disabled.");
			}
		}

		private static IEnumerator ScaleTheOcean()
		{
			GameObject obj = null;
			yield return WaitForPath("Map/StylizedWater2_Ocean", delegate(GameObject o)
			{
				obj = o;
			});
			if (Object.op_Implicit((Object)(object)obj))
			{
				obj.transform.localScale = new Vector3(3f, 3f, 3f);
			}
			else
			{
				MelonLogger.Warning("[Overpass] Ocean 'Map/StylizedWater2_Ocean' never appeared -- not disabled.");
			}
		}

		private static IEnumerator DisableRoadblockers()
		{
			string[] roadblockerTargets = RoadblockerTargets;
			foreach (string path in roadblockerTargets)
			{
				GameObject obj = null;
				yield return WaitForPath(path, delegate(GameObject o)
				{
					obj = o;
				});
				if (Object.op_Implicit((Object)(object)obj))
				{
					obj.SetActive(false);
				}
				else
				{
					MelonLogger.Warning("[Overpass] Roadblocker '" + path + "' never appeared -- not disabled.");
				}
			}
		}

		private static IEnumerator DisableOverpassRampObjects()
		{
			string[] overpassRamp = OverpassRamp;
			foreach (string path in overpassRamp)
			{
				GameObject obj = null;
				yield return WaitForPath(path, delegate(GameObject o)
				{
					obj = o;
				});
				if (Object.op_Implicit((Object)(object)obj))
				{
					obj.SetActive(false);
				}
				else
				{
					MelonLogger.Warning("[Overpass] Ramp object '" + path + "' never appeared -- not disabled.");
				}
			}
		}

		private static IEnumerator FixRampMeshCollider()
		{
			GameObject rampRefParent = null;
			yield return WaitForPath("Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/Ramp", delegate(GameObject o)
			{
				rampRefParent = o;
			});
			if (Object.op_Implicit((Object)(object)rampRefParent))
			{
				MeshCollider collider = rampRefParent.GetComponent<MeshCollider>();
				if ((Object)(object)collider != (Object)null)
				{
					collider.convex = false;
				}
			}
			else
			{
				MelonLogger.Warning("[Overpass] 'Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/Ramp' never appeared -- ramp collider left untouched.");
			}
		}

		private static IEnumerator FixSkybox()
		{
			GameObject skyboxRef = null;
			yield return WaitForPath("EnvironmentManager/Skybox", delegate(GameObject o)
			{
				skyboxRef = o;
			});
			if (Object.op_Implicit((Object)(object)skyboxRef))
			{
				Transform transform = skyboxRef.transform;
				transform.localScale *= 2f;
			}
			else
			{
				MelonLogger.Warning("[Overpass] 'Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/Ramp' never appeared -- ramp collider left untouched.");
			}
		}

		private static IEnumerator SpawnOverpassFill()
		{
			if ((Object)(object)overpassPrefab == (Object)null)
			{
				MelonLogger.Warning("[Overpass] overpassPrefab is null -- overpass fill not spawned (bundle failed to load).");
				yield break;
			}
			GameObject overpassRoot = null;
			yield return WaitForPath("Map/Hyland Point/Overpass", delegate(GameObject o)
			{
				overpassRoot = o;
			});
			if ((Object)(object)overpassRoot == (Object)null)
			{
				MelonLogger.Warning("[Overpass] 'Map/Hyland Point/Overpass' never appeared -- cannot parent the overpass fill.");
				yield break;
			}
			GameObject instance = Object.Instantiate<GameObject>(overpassPrefab, overpassRoot.transform, true);
			((Object)instance).name = "OverpassFill";
			overpassFillInstance = instance;
			FixBundleMaterials(instance, overpassRoot);
			MelonLogger.Msg("[Overpass] Instantiated 'overpassfill' under 'Map/Hyland Point/Overpass'.");
		}

		private static void FixBundleMaterials(GameObject instance, GameObject overpassRoot)
		{
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			Shader val = Shader.Find("Universal Render Pipeline/Lit");
			if ((Object)(object)val == (Object)null)
			{
				MelonLogger.Warning("[Overpass] 'Universal Render Pipeline/Lit' not found -- materials may render magenta.");
			}
			Dictionary<string, Material> dictionary = new Dictionary<string, Material>(StringComparer.OrdinalIgnoreCase);
			Renderer[] componentsInChildren = overpassRoot.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val2 in componentsInChildren)
			{
				if (((Component)val2).transform.IsChildOf(instance.transform))
				{
					continue;
				}
				Material[] sharedMaterials = val2.sharedMaterials;
				if (sharedMaterials == null)
				{
					continue;
				}
				Material[] array = sharedMaterials;
				foreach (Material val3 in array)
				{
					if ((Object)(object)val3 != (Object)null && !dictionary.ContainsKey(((Object)val3).name))
					{
						dictionary[((Object)val3).name] = val3;
					}
				}
			}
			MelonLogger.Msg($"[Overpass] Harvested {dictionary.Count} live overpass material(s) for matching.");
			Texture2D val4 = AssetBundleUtils.LoadTextureContaining("doyoucatchmydrift", "asphalt", "diff");
			Texture2D val5 = AssetBundleUtils.LoadTextureContaining("doyoucatchmydrift", "asphalt", "norm");
			Texture2D val6 = AssetBundleUtils.LoadTextureContaining("doyoucatchmydrift", "grass", "diff");
			Texture2D val7 = AssetBundleUtils.LoadTextureContaining("doyoucatchmydrift", "grass", "norm");
			Renderer[] componentsInChildren2 = instance.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val8 in componentsInChildren2)
			{
				Material[] sharedMaterials2 = val8.sharedMaterials;
				if (sharedMaterials2 == null)
				{
					continue;
				}
				bool flag = false;
				for (int l = 0; l < sharedMaterials2.Length; l++)
				{
					if ((Object)(object)sharedMaterials2[l] == (Object)null)
					{
						continue;
					}
					Material val9 = FindLiveMaterial(((Object)sharedMaterials2[l]).name, dictionary);
					if ((Object)(object)val9 != (Object)null)
					{
						MelonLogger.Msg("[Overpass] Material '" + ((Object)sharedMaterials2[l]).name + "' -> live '" + ((Object)val9).name + "'.");
						sharedMaterials2[l] = val9;
						flag = true;
						continue;
					}
					Material val10 = sharedMaterials2[l];
					if ((Object)(object)val != (Object)null)
					{
						val10.shader = val;
					}
					string text = ((Object)val10).name.ToLowerInvariant();
					Texture val11 = null;
					Texture val12 = null;
					if (text.Contains("asphalt") || text.Contains("road"))
					{
						val11 = (Texture)(object)val4;
						val12 = (Texture)(object)val5;
					}
					else if (text.Contains("grass") || text.Contains("wild"))
					{
						val11 = (Texture)(object)val6;
						val12 = (Texture)(object)val7;
						val10.mainTextureScale = new Vector2(200f, 200f);
					}
					if ((Object)(object)val11 != (Object)null)
					{
						val10.SetTexture("_BaseMap", val11);
					}
					if ((Object)(object)val12 != (Object)null)
					{
						val10.SetTexture("_BumpMap", val12);
						val10.EnableKeyword("_NORMALMAP");
					}
					if (val10.HasProperty("_BaseColor"))
					{
						val10.SetColor("_BaseColor", Color.white);
					}
					MelonLogger.Msg($"[Overpass] Material '{((Object)val10).name}': no live match -- custom baseMap={(Object)(object)val11 != (Object)null}, normal={(Object)(object)val12 != (Object)null}.");
				}
				if (flag)
				{
					val8.sharedMaterials = sharedMaterials2;
				}
			}
		}

		private static Material FindLiveMaterial(string fillName, Dictionary<string, Material> liveMats)
		{
			if (liveMats.TryGetValue(fillName, out var value))
			{
				return value;
			}
			string text = NormalizeMatName(fillName);
			if (text.Length < 3)
			{
				return null;
			}
			Material val = null;
			foreach (KeyValuePair<string, Material> liveMat in liveMats)
			{
				string text2 = NormalizeMatName(liveMat.Key);
				if (text2 == text)
				{
					return liveMat.Value;
				}
				if ((Object)(object)val == (Object)null && (text2.Contains(text) || text.Contains(text2)))
				{
					val = liveMat.Value;
				}
			}
			return val;
		}

		private static string NormalizeMatName(string name)
		{
			string text = name.ToLowerInvariant().Trim();
			if (text.StartsWith("overpass_segment_"))
			{
				text = text.Substring("overpass_segment_".Length);
			}
			if (text.EndsWith(" mat"))
			{
				text = text.Substring(0, text.Length - 4);
			}
			else if (text.EndsWith("mat"))
			{
				text = text.Substring(0, text.Length - 3);
			}
			return text.Trim();
		}

		private static IEnumerator RemoveTreesUnderOverpass(int maxAttempts = 5, float retryDelay = 3f)
		{
			GameObject overpassRoot = null;
			yield return WaitForPath("Map/Hyland Point/Overpass", delegate(GameObject o)
			{
				overpassRoot = o;
			});
			if (!Object.op_Implicit((Object)(object)overpassRoot))
			{
				MelonLogger.Warning("[Overpass] Overpass root never appeared -- skipping tree removal.");
				yield break;
			}
			for (int attempt = 1; attempt <= maxAttempts; attempt++)
			{
				Terrain[] terrains = Terrain.activeTerrains;
				if (terrains == null || terrains.Length == 0)
				{
					MelonLogger.Warning($"[Overpass] No active terrains yet (attempt {attempt}/{maxAttempts}) -- retrying in {retryDelay}s.");
					yield return (object)new WaitForSeconds(retryDelay);
					continue;
				}
				Bounds overpassBounds = ComputeWorldBounds(overpassRoot, overpassFillInstance);
				((Bounds)(ref overpassBounds)).Expand(new Vector3(2f, 0f, 2f));
				int totalRemoved = 0;
				Terrain[] array = terrains;
				foreach (Terrain terrain in array)
				{
					TerrainData data = terrain.terrainData;
					if ((Object)(object)data == (Object)null)
					{
						continue;
					}
					Vector3 terrainOrigin = ((Component)terrain).transform.position;
					TreeInstance[] instances = data.treeInstances;
					List<TreeInstance> kept = new List<TreeInstance>(instances.Length);
					int removedTrees = 0;
					for (int i = 0; i < instances.Length; i++)
					{
						TreeInstance inst = instances[i];
						Vector3 worldPos = terrainOrigin + Vector3.Scale(inst.position, data.size);
						if (worldPos.x >= ((Bounds)(ref overpassBounds)).min.x && worldPos.x <= ((Bounds)(ref overpassBounds)).max.x && worldPos.z >= ((Bounds)(ref overpassBounds)).min.z && worldPos.z <= ((Bounds)(ref overpassBounds)).max.z)
						{
							removedTrees++;
						}
						else
						{
							kept.Add(inst);
						}
					}
					if (removedTrees > 0)
					{
						data.treeInstances = kept.ToArray();
						TerrainCollider tc = ((Component)terrain).GetComponent<TerrainCollider>();
						if ((Object)(object)tc != (Object)null)
						{
							((Collider)tc).enabled = false;
							((Collider)tc).enabled = true;
						}
						terrain.Flush();
						totalRemoved += removedTrees;
						MelonLogger.Msg($"[Overpass] Terrain '{((Object)terrain).name}': removed {removedTrees} tree(s) under the overpass (kept {kept.Count}).");
					}
				}
				MelonLogger.Msg($"[Overpass] Tree removal done: {totalRemoved} removed across {terrains.Length} terrain(s), bounds={overpassBounds}.");
				yield break;
			}
			MelonLogger.Error($"[Overpass] No active terrain found after {maxAttempts} attempts -- tree removal skipped.");
		}

		private static IEnumerator EnforceInactive(string path, float recheckInterval = 5f)
		{
			while (true)
			{
				GameObject obj = FindByFullPathIncludingInactive(path);
				if ((Object)(object)obj != (Object)null && obj.activeSelf)
				{
					obj.SetActive(false);
					MelonLogger.Msg("[Overpass] Re-disabled '" + path + "' (was reactivated).");
				}
				yield return (object)new WaitForSeconds(recheckInterval);
			}
		}

		private static IEnumerator EnforceHiddenRampChildren(float recheckInterval = 5f)
		{
			while (true)
			{
				GameObject rampRefParent = FindByFullPathIncludingInactive("Map/Hyland Point/Overpass/Overpass Ramp/overpass_ramp/Ramp");
				if ((Object)(object)rampRefParent != (Object)null && (Object)(object)rampRefParent.transform.parent != (Object)null)
				{
					HideChildrenExceptNamed(rampRefParent.transform.parent, RampKeepNames);
				}
				yield return (object)new WaitForSeconds(recheckInterval);
			}
		}

		private static void HideChildrenExceptNamed(Transform parent, string[] keepNameContains)
		{
			int num = 0;
			for (int i = 0; i < parent.childCount; i++)
			{
				Transform child = parent.GetChild(i);
				bool flag = false;
				foreach (string value in keepNameContains)
				{
					if (((Object)child).name.Contains(value))
					{
						flag = true;
						break;
					}
				}
				if (!flag && ((Component)child).gameObject.activeSelf)
				{
					((Component)child).gameObject.SetActive(false);
					num++;
				}
			}
			if (num > 0)
			{
				MelonLogger.Msg($"[Overpass] HideChildrenExceptNamed('{((Object)parent).name}'): hid {num} child object(s).");
			}
		}

		private static Bounds ComputeWorldBounds(GameObject go, params GameObject[] exclude)
		{
			//IL_000b: 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_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_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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_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)
			Renderer[] componentsInChildren = go.GetComponentsInChildren<Renderer>(true);
			Bounds val = default(Bounds);
			bool flag = false;
			Renderer[] array = componentsInChildren;
			foreach (Renderer val2 in array)
			{
				if (!((Object)(object)val2 == (Object)null) && !IsDescendantOfAny(((Component)val2).transform, exclude))
				{
					if (!flag)
					{
						val = val2.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref val)).Encapsulate(val2.bounds);
					}
				}
			}
			return (Bounds)(flag ? val : new Bounds(go.transform.position, Vector3.zero));
		}

		private static bool IsDescendantOfAny(Transform t, GameObject[] roots)
		{
			if (roots == null)
			{
				return false;
			}
			foreach (GameObject val in roots)
			{
				if ((Object)(object)val != (Object)null && t.IsChildOf(val.transform))
				{
					return true;
				}
			}
			return false;
		}

		private static GameObject FindByFullPathIncludingInactive(string fullPath)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(fullPath))
			{
				return null;
			}
			string[] array = fullPath.Split('/');
			for (int i = 0; i < SceneManager.sceneCount; i++)
			{
				Scene sceneAt = SceneManager.GetSceneAt(i);
				if (!((Scene)(ref sceneAt)).isLoaded)
				{
					continue;
				}
				GameObject[] rootGameObjects = ((Scene)(ref sceneAt)).GetRootGameObjects();
				foreach (GameObject val in rootGameObjects)
				{
					if (((Object)val).name != array[0])
					{
						continue;
					}
					Transform val2 = val.transform;
					for (int k = 1; k < array.Length; k++)
					{
						if (!((Object)(object)val2 != (Object)null))
						{
							break;
						}
						val2 = val2.Find(array[k]);
					}
					if ((Object)(object)val2 != (Object)null)
					{
						return ((Component)val2).gameObject;
					}
				}
			}
			return null;
		}
	}
	public static class VehicleDumper
	{
		public static IEnumerator DumpAfterDelay(float seconds)
		{
			yield return (object)new WaitForSeconds(seconds);
			try
			{
				DumpAll();
			}
			catch (Exception ex)
			{
				Exception e = ex;
				Utility.PrintException(e);
			}
		}

		public static void DumpAll()
		{
			VehicleManager instance = NetworkSingleton<VehicleManager>.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				Utility.Error("VehicleDumper: VehicleManager instance not found.");
				return;
			}
			List<string> list = new List<string>();
			for (int i = 0; i < instance.VehiclePrefabs.Count; i++)
			{
				list.Add(DumpVehicleSafe(instance.VehiclePrefabs[i], isPrefab: true));
			}
			List<string> list2 = new List<string>();
			for (int j = 0; j < instance.AllVehicles.Count; j++)
			{
				list2.Add(DumpVehicleSafe(instance.AllVehicles[j], isPrefab: false));
			}
			StringBuilder stringBuilder = new StringBuilder(65536);
			stringBuilder.Append("{\n");
			stringBuilder.Append("  \"fixedDeltaTime\": ").Append(F(Time.fixedDeltaTime)).Append(",\n");
			stringBuilder.Append("  \"prefabs\": [\n").Append(string.Join(",\n", list)).Append("\n  ],\n");
			stringBuilder.Append("  \"spawned\": [\n").Append(string.Join(",\n", list2)).Append("\n  ]\n");
			stringBuilder.Append("}\n");
			string text = Path.Combine(MelonEnvironment.UserDataDirectory, "DYCMD_VehicleDump.json");
			File.WriteAllText(text, stringBuilder.ToString());
			Utility.Success($"VehicleDumper: wrote {list.Count} prefabs + {list2.Count} spawned vehicles to {text}");
		}

		private static string DumpVehicleSafe(LandVehicle v, bool isPrefab)
		{
			try
			{
				if ((Object)(object)v == (Object)null)
				{
					return "    { \"error\": \"null vehicle entry\" }";
				}
				return DumpVehicle(v, isPrefab);
			}
			catch (Exception ex)
			{
				string s = "unknown";
				try
				{
					s = ((Object)((Component)v).gameObject).name;
				}
				catch
				{
				}
				return "    { \"name\": " + J(s) + ", \"error\": " + J(ex.GetType().Name + ": " + ex.Message) + " }";
			}
		}

		private static string DumpVehicle(LandVehicle v, bool isPrefab)
		{
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0432: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder(4096);
			stringBuilder.Append("    {\n");
			stringBuilder.Append("      \"name\": ").Append(J(((Object)((Component)v).gameObject).name)).Append(",\n");
			stringBuilder.Append("      \"vehicleName\": ").Append(J(v.VehicleName)).Append(",\n");
			stringBuilder.Append("      \"vehicleCode\": ").Append(J(v.vehicleCode)).Append(",\n");
			stringBuilder.Append("      \"layer\": ").Append(J(LayerMask.LayerToName(((Component)v).gameObject.layer))).Append(",\n");
			Rigidbody rb = v.Rb;
			if ((Object)(object)rb != (Object)null)
			{
				stringBuilder.Append("      \"rigidbody\": {\n");
				stringBuilder.Append("        \"mass\": ").Append(F(rb.mass)).Append(",\n");
				stringBuilder.Append("        \"drag\": ").Append(F(rb.drag)).Append(",\n");
				stringBuilder.Append("        \"angularDrag\": ").Append(F(rb.angularDrag)).Append(",\n");
				stringBuilder.Append("        \"centerOfMass\": ").Append(V(rb.centerOfMass)).Append(",\n");
				stringBuilder.Append("        \"inertiaTensor\": ").Append(V(rb.inertiaTensor)).Append("\n");
				stringBuilder.Append("      },\n");
			}
			if ((Object)(object)v.centerOfMass != (Object)null)
			{
				stringBuilder.Append("      \"centerOfMassTransformLocal\": ").Append(V(((Component)v).transform.InverseTransformPoint(v.centerOfMass.position))).Append(",\n");
			}
			stringBuilder.Append("      \"drive\": {\n");
			stringBuilder.Append("        \"topSpeed\": ").Append(F(v.TopSpeed)).Append(",\n");
			stringBuilder.Append("        \"diffGearing\": ").Append(F(v.diffGearing)).Append(",\n");
			stringBuilder.Append("        \"handBrakeForce\": ").Append(F(v.handBrakeForce)).Append(",\n");
			stringBuilder.Append("        \"brakeForceMultiplier\": ").Append(F(v.BrakeForceMultiplier)).Append(",\n");
			stringBuilder.Append("        \"downforce\": ").Append(F(v.downforce)).Append(",\n");
			stringBuilder.Append("        \"reverseMultiplier\": ").Append(F(v.reverseMultiplier)).Append(",\n");
			stringBuilder.Append("        \"maxSteeringAngle\": ").Append(F(v.maxSteeringAngle)).Append(",\n");
			stringBuilder.Append("        \"steerRate\": ").Append(F(v.steerRate)).Append(",\n");
			stringBuilder.Append("        \"flipSteer\": ").Append(v.flipSteer ? "true" : "false").Append(",\n");
			stringBuilder.Append("        \"motorTorqueCurve\": ").Append(Curve(v.motorTorque)).Append(",\n");
			stringBuilder.Append("        \"brakeForceCurve\": ").Append(Curve(v.brakeForce)).Append("\n");
			stringBuilder.Append("      },\n");
			Wheel[] componentsInChildren = ((Component)v).GetComponentsInChildren<Wheel>(true);
			List<string> list = new List<string>();
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				list.Add(DumpWheel(v, componentsInChildren[i]));
			}
			stringBuilder.Append("      \"wheels\": [\n").Append(string.Join(",\n", list)).Append("\n      ]");
			if (!isPrefab)
			{
				stringBuilder.Append(",\n      \"worldPosition\": ").Append(V(((Component)v).transform.position));
				stringBuilder.Append(",\n      \"ground\": ").Append(DumpGround(v));
			}
			stringBuilder.Append("\n    }");
			return stringBuilder.ToString();
		}

		private static string DumpWheel(LandVehicle v, Wheel w)
		{
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: 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_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_030c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder(1024);
			WheelCollider wheelCollider = w.wheelCollider;
			stringBuilder.Append("        {\n");
			stringBuilder.Append("          \"name\": ").Append(J(((Object)((Component)w).gameObject).name)).Append(",\n");
			if ((Object)(object)wheelCollider != (Object)null)
			{
				bool flag = false;
				bool flag2 = false;
				bool flag3 = false;
				WheelCollider[] driveWheels = v.driveWheels;
				if (driveWheels != null)
				{
					for (int i = 0; i < driveWheels.Length; i++)
					{
						if ((Object)(object)driveWheels[i] == (Object)(object)wheelCollider)
						{
							flag = true;
							break;
						}
					}
				}
				WheelCollider[] steerWheels = v.steerWheels;
				if (steerWheels != null)
				{
					for (int j = 0; j < steerWheels.Length; j++)
					{
						if ((Object)(object)steerWheels[j] == (Object)(object)wheelCollider)
						{
							flag2 = true;
							break;
						}
					}
				}
				WheelCollider[] handbrakeWheels = v.handbrakeWheels;
				if (handbrakeWheels != null)
				{
					for (int k = 0; k < handbrakeWheels.Length; k++)
					{
						if ((Object)(object)handbrakeWheels[k] == (Object)(object)wheelCollider)
						{
							flag3 = true;
							break;
						}
					}
				}
				stringBuilder.Append("          \"isDrive\": ").Append(flag ? "true" : "false").Append(",\n");
				stringBuilder.Append("          \"isSteer\": ").Append(flag2 ? "true" : "false").Append(",\n");
				stringBuilder.Append("          \"isHandbrake\": ").Append(flag3 ? "true" : "false").Append(",\n");
				stringBuilder.Append("          \"localPosition\": ").Append(V(((Component)v).transform.InverseTransformPoint(((Component)wheelCollider).transform.position))).Append(",\n");
				stringBuilder.Append("          \"radius\": ").Append(F(wheelCollider.radius)).Append(",\n");
				stringBuilder.Append("          \"mass\": ").Append(F(wheelCollider.mass)).Append(",\n");
				stringBuilder.Append("          \"center\": ").Append(V(wheelCollider.center)).Append(",\n");
				stringBuilder.Append("          \"suspensionDistance\": ").Append(F(wheelCollider.suspensionDistance)).Append(",\n");
				stringBuilder.Append("          \"wheelDampingRate\": ").Append(F(wheelCollider.wheelDampingRate)).Append(",\n");
				stringBuilder.Append("          \"forceAppPointDistance\": ").Append(F(wheelCollider.forceAppPointDistance)).Append(",\n");
				JointSpring suspensionSpring = wheelCollider.suspensionSpring;
				stringBuilder.Append("          \"suspensionSpring\": { \"spring\": ").Append(F(suspensionSpring.spring)).Append(", \"damper\": ")
					.Append(F(suspensionSpring.damper))
					.Append(", \"targetPosition\": ")
					.Append(F(suspensionSpring.targetPosition))
					.Append(" },\n");
				stringBuilder.Append("          \"forwardFriction\": ").Append(Friction(wheelCollider.forwardFriction)).Append(",\n");
				stringBuilder.Append("          \"sidewaysFriction\": ").Append(Friction(wheelCollider.sidewaysFriction));
			}
			else
			{
				stringBuilder.Append("          \"error\": \"no wheelCollider\"");
			}
			stringBuilder.Append("\n        }");
			return stringBuilder.ToString();
		}

		private static string DumpGround(LandVehicle v)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			int num = (1 << LayerMask.NameToLayer("Default")) | (1 << LayerMask.NameToLayer("Terrain"));
			RaycastHit val = default(RaycastHit);
			if (Physics.Raycast(((Component)v).transform.position + Vector3.up * 0.5f, Vector3.down, ref val, 20f, num))
			{
				string text = (((Object)(object)((RaycastHit)(ref val)).collider.sharedMaterial != (Object)null) ? ((Object)((RaycastHit)(ref val)).collider.sharedMaterial).name : null);
				return "{ \"collider\": " + J(((Object)((Component)((RaycastHit)(ref val)).collider).gameObject).name) + ", \"layer\": " + J(LayerMask.LayerToName(((Component)((RaycastHit)(ref val)).collider).gameObject.layer)) + ", \"physicMaterial\": " + ((text != null) ? J(text) : "null") + ", \"distance\": " + F(((RaycastHit)(ref val)).distance) + " }";
			}
			return "null";
		}

		private static string Friction(WheelFrictionCurve c)
		{
			return "{ \"extremumSlip\": " + F(((WheelFrictionCurve)(ref c)).extremumSlip) + ", \"extremumValue\": " + F(((WheelFrictionCurve)(ref c)).extremumValue) + ", \"asymptoteSlip\": " + F(((WheelFrictionCurve)(ref c)).asymptoteSlip) + ", \"asymptoteValue\": " + F(((WheelFrictionCurve)(ref c)).asymptoteValue) + ", \"stiffness\": " + F(((WheelFrictionCurve)(ref c)).stiffness) + " }";
		}

		private static string Curve(AnimationCurve curve)
		{
			if (curve == null)
			{
				return "null";
			}
			Keyframe[] keys = curve.keys;
			List<string> list = new List<string>();
			for (int i = 0; i < keys.Length; i++)
			{
				list.Add("{ \"time\": " + F(((Keyframe)(ref keys[i])).time) + ", \"value\": " + F(((Keyframe)(ref keys[i])).value) + " }");
			}
			return "[ " + string.Join(", ", list) + " ]";
		}

		private static string F(float f)
		{
			return f.ToString("R", CultureInfo.InvariantCulture);
		}

		private static string V(Vector3 v)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			return "{ \"x\": " + F(v.x) + ", \"y\": " + F(v.y) + ", \"z\": " + F(v.z) + " }";
		}

		private static string J(string s)
		{
			if (s == null)
			{
				return "null";
			}
			return "\"" + s.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"";
		}
	}
}
namespace DoYouCatchMyDrift.TemplateUtils
{
	public static class AssetBundleUtils
	{
		private static Core mod;

		private static MelonAssembly melonAssembly;

		private static Dictionary<string, AssetBundle> assetBundles = new Dictionary<string, AssetBundle>();

		public static void Initialize(Core coreMod)
		{
			mod = coreMod;
			melonAssembly = ((MelonBase)mod).MelonAssembly;
		}

		public static AssetBundle LoadAssetBundle(string bundleFileName)
		{
			if (assetBundles.TryGetValue(bundleFileName, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			try
			{
				string text = typeof(Core).Namespace + ".Assets." + bundleFileName;
				Stream manifestResourceStream = melonAssembly.Assembly.GetManifestResourceStream(text ?? "");
				if (manifestResourceStream == null)
				{
					((MelonBase)mod).Unregister("AssetBundle resource '" + text + "' not found.", false);
					return null;
				}
				byte[] array;
				using (manifestResourceStream)
				{
					using MemoryStream memoryStream = new MemoryStream();
					manifestResourceStream.CopyTo(memoryStream);
					array = memoryStream.ToArray();
				}
				if (array.Length == 0)
				{
					((MelonBase)mod).Unregister("AssetBundle '" + bundleFileName + "' is empty.", false);
					return null;
				}
				AssetBundle val = AssetBundle.LoadFromMemory(array);
				if ((Object)(object)val == (Object)null)
				{
					((MelonBase)mod).Unregister("Failed to load required AssetBundle '" + bundleFileName + "'.", false);
					return null;
				}
				assetBundles[bundleFileName] = val;
				return val;
			}
			catch (Exception arg)
			{
				((MelonBase)mod).Unregister($"Failed to load required AssetBundle '{bundleFileName}': {arg}", false);
				return null;
			}
		}

		public static void ClearCache()
		{
			assetBundles.Clear();
		}

		public static AssetBundle GetLoadedAssetBundle(string bundleName)
		{
			if (assetBundles.ContainsKey(bundleName))
			{
				return assetBundles[bundleName];
			}
			MelonLogger.Warning("Asset bundle '" + bundleName + "' is not loaded.");
			return null;
		}

		public static T LoadAssetFromBundle<T>(string assetName, string bundleName) where T : Object
		{
			AssetBundle loadedAssetBundle = GetLoadedAssetBundle(bundleName);
			if ((Object)(object)loadedAssetBundle == (Object)null)
			{
				MelonLogger.Error("Couldn't find loaded bundle '" + bundleName + "'.");
				return default(T);
			}
			T val = default(T);
			try
			{
				val = loadedAssetBundle.LoadAsset<T>(assetName);
			}
			catch (Exception arg)
			{
				MelonLogger.Error($"Failed loading asset '{assetName}' from bundle '{bundleName}'. The bundle may be invalid or have a zero internal pointer. {arg}");
				return default(T);
			}
			if ((Object)(object)val == (Object)null)
			{
				MelonLogger.Error("Asset '" + assetName + "' not found in bundle '" + bundleName + "'.");
				return default(T);
			}
			return val;
		}

		public static Texture2D LoadTextureContaining(string bundleName, params string[] contains)
		{
			AssetBundle loadedAssetBundle = GetLoadedAssetBundle(bundleName);
			if ((Object)(object)loadedAssetBundle == (Object)null)
			{
				return null;
			}
			string[] allAssetNames = loadedAssetBundle.GetAllAssetNames();
			for (int i = 0; i < allAssetNames.Length; i++)
			{
				string text = allAssetNames[i].ToLowerInvariant();
				bool flag = true;
				for (int j = 0; j < contains.Length; j++)
				{
					if (!text.Contains(contains[j].ToLowerInvariant()))
					{
						flag = false;
						break;
					}
				}
				if (!flag)
				{
					continue;
				}
				try
				{
					Texture2D val = loadedAssetBundle.LoadAsset<Texture2D>(allAssetNames[i]);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("Failed loading texture '" + allAssetNames[i] + "' from bundle '" + bundleName + "': " + ex.Message);
				}
			}
			return null;
		}
	}
	public static class Utility
	{
		public static void PrintException(Exception e)
		{
			MelonLogger.Msg(ConsoleColor.Red, e.GetType().FullName);
			MelonLogger.Msg(ConsoleColor.DarkRed, e.Message);
			if (!string.IsNullOrWhiteSpace(e.StackTrace))
			{
				MelonLogger.Msg(ConsoleColor.DarkRed, "StackTrace:");
				string[] array = e.StackTrace.Split('\n');
				foreach (string text in array)
				{
					MelonLogger.Msg(ConsoleColor.DarkRed, "    " + text.Trim());
				}
			}
			if (e.InnerException != null)
			{
				MelonLogger.Msg(ConsoleColor.DarkRed, "  -- Inner Exception --");
				PrintException(e.InnerException);
			}
		}

		public static void Error(string msg)
		{
			MelonLogger.Msg(ConsoleColor.Red, msg);
		}

		public static void Log(string msg)
		{
			MelonLogger.Msg(ConsoleColor.DarkMagenta, msg);
		}

		public static void Success(string msg)
		{
			MelonLogger.Msg(ConsoleColor.Green, msg);
		}
	}
}
namespace DoYouCatchMyDrift.Rally
{
	public class RallyProfile
	{
		public string Code = "default";

		public float PowerCV = 200f;

		public float MaxPowerRPM = 5500f;

		public float TorqueNm = 300f;

		public float MaxTorqueRPM = 3500f;

		public float MinRPM = 950f;

		public float MaxRPM = 6500f;

		public int GearCount = 5;

		public float TopSpeedKmh = 110f;

		public float FirstGearTopKmh;

		public float BrakeG = 1f;

		public float CdA = 0.85f;

		public float DownforceCoeff = 0.6f;

		public float ComLowerY = 0.12f;

		public float DragCoefficient = 0.2f;

		public float ShiftTime = 0.3f;

		public float RevLimiterTime = 0.08f;

		public float ClutchFullyClosedKmh = 10f;

		public float ShiftUpRPM;

		public float ShiftDownRPM;

		public float[] GearRatios;

		public float FinalDrive;

		public const float RPM2W = MathF.PI / 30f;

		public void ComputeGearing(float wheelRadius, float massKg)
		{
			float num = TopSpeedKmh / 3.6f;
			float num2 = 1f;
			FinalDrive = MaxRPM * (MathF.PI / 30f) * wheelRadius / (num * num2);
			if (FirstGearTopKmh <= 0f)
			{
				FirstGearTopKmh = TopSpeedKmh * 0.32f;
			}
			float num3 = Mathf.Max(FirstGearTopKmh, 20f) / 3.6f;
			float num4 = MaxRPM * (MathF.PI / 30f) * wheelRadius / (num3 * FinalDrive);
			num4 = Mathf.Max(num4, 1.6f);
			GearRatios = new float[GearCount + 2];
			GearRatios[0] = (0f - num4) * 0.9f;
			GearRatios[1] = 0f;
			for (int i = 0; i < GearCount; i++)
			{
				float num5 = ((GearCount == 1) ? 1f : ((float)i / (float)(GearCount - 1)));
				GearRatios[i + 2] = num4 * Mathf.Pow(num2 / num4, num5);
			}
			ShiftUpRPM = Mathf.Min(MaxPowerRPM * 1.05f, MaxRPM * 0.97f);
			ShiftDownRPM = MaxTorqueRPM * 0.75f;
		}
	}
	public static class RallyProfiles
	{
		private static readonly Dictionary<string, RallyProfile> table = new Dictionary<string, RallyProfile>
		{
			["shitbox"] = new RallyProfile
			{
				Code = "shitbox",
				PowerCV = 120f,
				MaxPowerRPM = 6200f,
				TorqueNm = 165f,
				MaxTorqueRPM = 4200f,
				MaxRPM = 7000f,
				GearCount = 5,
				TopSpeedKmh = 100f,
				CdA = 0.75f,
				BrakeG = 0.95f
			},
			["shitbox_police"] = new RallyProfile
			{
				Code = "shitbox_police",
				PowerCV = 135f,
				MaxPowerRPM = 6200f,
				TorqueNm = 180f,
				MaxTorqueRPM = 4200f,
				MaxRPM = 7000f,
				GearCount = 5,
				TopSpeedKmh = 105f,
				CdA = 0.75f,
				BrakeG = 0.95f
			},
			["bruiser"] = new RallyProfile
			{
				Code = "bruiser",
				PowerCV = 210f,
				MaxPowerRPM = 5200f,
				TorqueNm = 320f,
				MaxTorqueRPM = 3200f,
				MaxRPM = 6200f,
				GearCount = 5,
				TopSpeedKmh = 115f,
				CdA = 0.95f,
				ComLowerY = 0.15f
			},
			["bruiser_police"] = new RallyProfile
			{
				Code = "bruiser_police",
				PowerCV = 225f,
				MaxPowerRPM = 5200f,
				TorqueNm = 330f,
				MaxTorqueRPM = 3200f,
				MaxRPM = 6200f,
				GearCount = 5,
				TopSpeedKmh = 120f,
				CdA = 0.95f,
				ComLowerY = 0.15f
			},
			["hounddog"] = new RallyProfile
			{
				Code = "hounddog",
				PowerCV = 250f,
				MaxPowerRPM = 5600f,
				TorqueNm = 350f,
				MaxTorqueRPM = 3800f,
				MaxRPM = 6600f,
				GearCount = 5,
				TopSpeedKmh = 130f,
				CdA = 0.8f,
				BrakeG = 1.05f
			},
			["dinkler"] = new RallyProfile
			{
				Code = "dinkler",
				PowerCV = 230f,
				MaxPowerRPM = 4800f,
				TorqueNm = 390f,
				MaxTorqueRPM = 2800f,
				MaxRPM = 5800f,
				GearCount = 5,
				TopSpeedKmh = 115f,
				CdA = 1f,
				ComLowerY = 0.2f
			},
			["cheetah"] = new RallyProfile
			{
				Code = "cheetah",
				PowerCV = 340f,
				MaxPowerRPM = 6800f,
				TorqueNm = 300f,
				MaxTorqueRPM = 4600f,
				MaxRPM = 7600f,
				GearCount = 6,
				TopSpeedKmh = 130f,
				FirstGearTopKmh = 46f,
				CdA = 0.7f,
				BrakeG = 1.1f,
				DragCoefficient = 0.02f
			},
			["veeper"] = new RallyProfile
			{
				Code = "veeper",
				PowerCV = 190f,
				MaxPowerRPM = 4800f,
				TorqueNm = 320f,
				MaxTorqueRPM = 2800f,
				MaxRPM = 5600f,
				GearCount = 5,
				TopSpeedKmh = 105f,
				CdA = 1.05f,
				ComLowerY = 0.22f
			},
			["hotbox"] = new RallyProfile
			{
				Code = "hotbox",
				PowerCV = 240f,
				MaxPowerRPM = 5200f,
				TorqueNm = 350f,
				MaxTorqueRPM = 3200f,
				MaxRPM = 6200f,
				GearCount = 5,
				TopSpeedKmh = 120f,
				CdA = 1f,
				ComLowerY = 0.15f
			}
		};

		public static RallyProfile Get(string vehicleCode, float wheelRadius, float massKg)
		{
			if (vehicleCode == null || !table.TryGetValue(vehicleCode.ToLowerInvariant(), out var value))
			{
				value = new RallyProfile
				{
					Code = (vehicleCode ?? "unknown"),
					PowerCV = Mathf.Clamp(massKg * 0.125f, 100f, 300f),
					TorqueNm = Mathf.Clamp(massKg * 0.19f, 150f, 420f)
				};
			}
			if (value.GearRatios == null)
			{
				value.ComputeGearing(wheelRadius, massKg);
			}
			return value;
		}
	}
	public class EngineSim
	{
		private const float RPM2W = MathF.PI / 30f;

		private const float CV2KW = 0.7358f;

		private const float DrivelineEfficiency = 0.9f;

		private const float EngineFrictionFactor = 0.25f;

		private readonly RallyProfile p;

		private readonly float maxPowerEngineTorque;

		private const float ShiftCooldownTime = 0.4f;

		public int gear;

		public float rpm;

		public float clutch;

		public bool shifting;

		private float shiftTimer;

		private int nextGear;

		private float limiterTimer;

		private float shiftCooldown;

		public int ReverseIndex => 0;

		public int NeutralIndex => 1;

		public int FirstIndex => 2;

		public bool InReverse => gear == ReverseIndex;

		public bool LimiterActive => limiterTimer > 0f;

		public string GearLabel => (gear == 0) ? "R" : ((gear == 1) ? "N" : (gear - 1).ToString());

		public float MaxWheelRpm
		{
			get
			{
				float num = Mathf.Abs(p.GearRatios[gear]) * p.FinalDrive;
				return (num > 0.01f) ? (p.MaxRPM / num) : float.MaxValue;
			}
		}

		public EngineSim(RallyProfile profile)
		{
			p = profile;
			gear = FirstIndex;
			rpm = p.MinRPM;
			maxPowerEngineTorque = CalcEngineTorque(p.MaxPowerRPM);
		}

		public float CalcEngineTorque(float atRpm)
		{
			float num2;
			if (atRpm < p.MaxTorqueRPM)
			{
				float num = atRpm / p.MaxTorqueRPM - 1f;
				num2 = p.TorqueNm * (1f - num * num);
			}
			else
			{
				float num3 = p.MaxPowerRPM * (MathF.PI / 30f);
				float num4 = p.PowerCV * 0.7358f * 1000f / num3;
				float num5 = 2f * p.MaxTorqueRPM * p.MaxPowerRPM - p.MaxPowerRPM * p.MaxPowerRPM - p.MaxTorqueRPM * p.MaxTorqueRPM;
				float num6 = atRpm - p.MaxTorqueRPM;
				num2 = (p.TorqueNm - num4) / num5 * (num6 * num6) + p.TorqueNm;
			}
			return Mathf.Max(num2, 0f);
		}

		public float CalcFrictionTorque(float atRpm)
		{
			float num = 0.1f;
			if (atRpm < p.MinRPM)
			{
				num = 1f - 0.9f * (atRpm / p.MinRPM);
			}
			return maxPowerEngineTorque * 0.25f * (num + (1f - num) * atRpm / p.MaxRPM);
		}

		public void EnsureReverse()
		{
			if (gear != ReverseIndex)
			{
				gear = ReverseIndex;
				shifting = false;
			}
		}

		public void EnsureFirst()
		{
			if (InReverse || gear == NeutralIndex)
			{
				gear = FirstIndex;
				shifting = false;
			}
		}

		private void BeginShift(int target)
		{
			shifting = true;
			shiftTimer = 0f;
			nextGear = target;
		}

		public void Step(float throttle, float brake, float avgDriveWheelRpm, float speedKmh, bool grounded, bool wheelspin, float dt)
		{
			if (shifting)
			{
				shiftTimer += dt;
				if (shiftTimer >= p.ShiftTime)
				{
					gear = nextGear;
					shifting = false;
					shiftCooldown = 0.4f;
				}
			}
			if (shiftCooldown > 0f)
			{
				shiftCooldown -= dt;
			}
			if (!shifting && gear > FirstIndex && Mathf.Abs(speedKmh) < 5f)
			{
				gear = FirstIndex;
			}
			float num = Mathf.Abs(p.GearRatios[gear]) * p.FinalDrive;
			float num2 = Mathf.Abs(avgDriveWheelRpm) * num;
			bool flag = gear == FirstIndex || gear == ReverseIndex;
			clutch = (shifting ? 0f : (flag ? Mathf.Clamp01(Mathf.Abs(speedKmh) / p.ClutchFullyClosedKmh) : 1f));
			float num3;
			if (clutch > 0.99f)
			{
				num3 = num2;
			}
			else
			{
				float num4 = p.MinRPM + throttle * (p.MaxPowerRPM * 0.5f);
				num3 = Mathf.Max(num2, num4 * (1f - clutch) + num2 * clutch);
			}
			num3 = Mathf.Clamp(num3, p.MinRPM, p.MaxRPM);
			float num5 = ((clutch > 0.99f) ? 30000f : ((num3 > rpm) ? 9000f : 6000f));
			rpm = Mathf.MoveTowards(rpm, num3, num5 * dt);
			if (rpm >= p.MaxRPM - 1f)
			{
				limiterTimer = p.RevLimiterTime;
			}
			else if (limiterTimer > 0f)
			{
				limiterTimer -= dt;
			}
			if (!shifting && shiftCooldown <= 0f && grounded && gear >= FirstIndex)
			{
				if (rpm >= p.ShiftUpRPM && gear < p.GearRatios.Length - 1)
				{
					BeginShift(gear + 1);
				}
				else if ((throttle > 0.05f || brake > 0.05f) && rpm <= p.ShiftDownRPM && gear > FirstIndex)
				{
					BeginShift(gear - 1);
				}
			}
		}

		public float DriveWheelTorque(float throttle)
		{
			if (shifting)
			{
				return 0f;
			}
			float num = p.GearRatios[gear] * p.FinalDrive;
			if (num == 0f)
			{
				return 0f;
			}
			float num2 = (LimiterActive ? 0f : throttle);
			float num3 = 0.6f + 0.4f * clutch;
			return CalcEngineTorque(rpm) * num2 * num * num3 * 0.9f;
		}

		public float EngineBrakeWheelTorque(float throttle)
		{
			if (shifting)
			{
				return 0f;
			}
			float num = Mathf.Abs(p.GearRatios[gear]) * p.FinalDrive;
			if (num == 0f)
			{
				return 0f;
			}
			float num2 = (LimiterActive ? 0f : throttle);
			return CalcFrictionTorque(rpm) * (1f - num2) * num * clutch * 0.9f;
		}
	}
	public enum SurfaceKind
	{
		Tarmac,
		Dirt
	}
	public class RallyVehicle
	{
		private const float RPM2RadS = MathF.PI / 30f;

		private const float DiffLockNmPerRpm = 7f;

		private const float LatAccelLimit = 9f;

		private const float SlipAngleMarginDeg = 8f;

		public readonly LandVehicle Vehicle;

		public readonly RallyProfile Profile;

		public readonly EngineSim Engine;

		private readonly HashSet<int> handbrakeColliderIds = new HashSet<int>();

		private readonly int terrainLayer;

		private readonly int groundMask;

		private readonly Vector3 originalCenterOfMass;

		private readonly float wheelbase;

		private bool comLowered;

		private readonly float originalDrag;

		private bool dragLowered;

		private readonly CollisionDetectionMode originalCollisionMode;

		private bool collisionModeChanged;

		public bool HandbrakeActive;

		public float Throttle;

		public float Brake;

		public float AvgWheelRpm;

		public SurfaceKind Surface;

		private float logTimer;

		private int lastLoggedGear = -1;

		private float filteredFrontRpm;

		private float filteredRearRpm;

		private static float ClampWheelRpm(float rawRpm, float speedKmh, float radius)
		{
			float num = speedKmh / 3.6f / radius * 9.549297f;
			if (num > 60f)
			{
				return Mathf.Max(rawRpm, num * 0.85f);
			}
			if (num < -60f)
			{
				return Mathf.Min(rawRpm, num * 0.85f);
			}
			return rawRpm;
		}

		private static string GearName(int g)
		{
			return g switch
			{
				1 => "N", 
				0 => "R", 
				_ => (g - 1).ToString(), 
			};
		}

		public RallyVehicle(LandVehicle vehicle)
		{
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			Vehicle = vehicle;
			terrainLayer = LayerMask.NameToLayer("Terrain");
			groundMask = (1 << LayerMask.NameToLayer("Default")) | (1 << terrainLayer);
			float num = 0.5f;
			if (vehicle.wheels.Count > 0 && (Object)(object)vehicle.wheels[0].wheelCollider != (Object)null)
			{
				num = vehicle.wheels[0].wheelCollider.radius;
			}
			Profile = RallyProfiles.Get(vehicle.vehicleCode, num, vehicle.Rb.mass);
			Engine = new EngineSim(Profile);
			originalDrag = vehicle.Rb.drag;
			vehicle.Rb.drag = Profile.DragCoefficient;
			dragLowered = true;
			originalCollisionMode = vehicle.Rb.collisionDetectionMode;
			vehicle.Rb.collisionDetectionMode = (CollisionDetectionMode)2;
			collisionModeChanged = true;
			WheelCollider[] handbrakeWheels = vehicle.handbrakeWheels;
			if (handbrakeWheels != null)
			{
				for (int i = 0; i < handbrakeWheels.Length; i++)
				{
					if ((Object)(object)handbrakeWheels[i] != (Object)null)
					{
						handbrakeColliderIds.Add(((Object)handbrakeWheels[i]).GetInstanceID());
					}
				}
			}
			originalCenterOfMass = vehicle.Rb.centerOfMass;
			float num2 = float.MinValue;
			float num3 = float.MaxValue;
			for (int j = 0; j < vehicle.wheels.Count; j++)
			{
				WheelCollider val = (((Object)(object)vehicle.wheels[j] != (Object)null) ? vehicle.wheels[j].wheelCollider : null);
				if (!((Object)(object)val == (Object)null))
				{
					float z = ((Component)vehicle).transform.InverseTransformPoint(((Component)val).transform.position).z;
					if (z > num2)
					{
						num2 = z;
					}
					if (z < num3)
					{
						num3 = z;
					}
				}
			}
			wheelbase = ((num2 > num3) ? Mathf.Max(num2 - num3, 1.5f) : 2.8f);
			lastLoggedGear = Engine.gear;
			StringBuilder stringBuilder = new StringBuilder();
			for (int k = 0; k < Profile.GearRatios.Length; k++)
			{
				if (k > 0)
				{
					stringBuilder.Append(", ");
				}
				stringBuilder.Append(Profile.GearRatios[k].ToString("0.00"));
			}
			Utility.Log($"[RALLY] Bound '{Profile.Code}' mass={vehicle.Rb.mass:0}kg wheelR={num:0.00}m wheels={vehicle.wheels.Count} hbWheels={handbrakeColliderIds.Count} | FD={Profile.FinalDrive:0.00} gears=[{stringBuilder}] | shiftUp={Profile.ShiftUpRPM:0} shiftDown={Profile.ShiftDownRPM:0} maxRPM={Profile.MaxRPM:0} | top={Profile.TopSpeedKmh:0}km/h 1st-top={Profile.FirstGearTopKmh:0}km/h");
		}

		public void RestoreCenterOfMass()
		{
			//IL_003c: 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)
			if (comLowered && (Object)(object)Vehicle != (Object)null && (Object)(object)Vehicle.Rb != (Object)null)
			{
				Vehicle.Rb.centerOfMass = originalCenterOfMass;
				comLowered = false;
			}
			if (dragLowered)
			{
				Vehicle.Rb.drag = originalDrag;
				dragLowered = false;
			}
			if (collisionModeChanged)
			{
				Vehicle.Rb.collisionDetectionMode = originalCollisionMode;
				collisionModeChanged = false;
			}
		}

		private void UpdateSurface()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			RaycastHit val = default(RaycastHit);
			if (Physics.Raycast(((Component)Vehicle).transform.position + Vector3.up * 0.5f, Vector3.down, ref val, 3f, groundMask))
			{
				Surface = (((Object)(object)((RaycastHit)(ref val)).collider != (Object)null && ((Component)((RaycastHit)(ref val)).collider).gameObject.layer == terrainLayer) ? SurfaceKind.Dirt : SurfaceKind.Tarmac);
			}
		}

		public void HandleThrottle()
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_080c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0811: Unknown result type (might be due to invalid IL or missing references)
			//IL_0816: Unknown result type (might be due to invalid IL or missing references)
			//IL_0818: Unknown result type (might be due to invalid IL or missing references)
			//IL_07e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_07f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0837: Unknown result type (might be due to invalid IL or missing references)
			//IL_0842: Unknown result type (might be due to invalid IL or missing references)
			//IL_0858: Unknown result type (might be due to invalid IL or missing references)
			//IL_07a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_07bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_09b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_09b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_09c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_09c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_09c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_09ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a01: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a16: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a31: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a49: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a4e: Unknown result type (might be due to invalid IL or missing references)
			LandVehicle vehicle = Vehicle;
			float fixedDeltaTime = Time.fixedDeltaTime;
			float num = vehicle.currentThrottle;
			float speed_Kmh = vehicle.Speed_Kmh;
			float x = GameInput.MotionAxis.x;
			if (Mathf.Abs(num) > 0.01f && Mathf.Abs(x) > 0.01f)
			{
				num = Mathf.Clamp(num / Mathf.Sqrt(Mathf.Max(1f - x * x, 0.05f)), -1f, 1f);
			}
			if (!comLowered)
			{
				vehicle.Rb.centerOfMass = originalCenterOfMass + Vector3.down * Profile.ComLowerY;
				comLowered = true;
			}
			if (vehicle.Rb.IsSleeping())
			{
				vehicle.Rb.WakeUp();
			}
			HandbrakeActive = GameInput.GetButton((ButtonCode)16);
			float num2 = 0f;
			float num3 = 0f;
			bool flag = false;
			if (num > 0.01f)
			{
				if (speed_Kmh > -2f)
				{
					num2 = num;
				}
				else
				{
					num3 = num;
				}
			}
			else if (num < -0.01f)
			{
				if (speed_Kmh > 4f)
				{
					num3 = 0f - num;
				}
				else
				{
					flag = true;
					num2 = 0f - num;
				}
			}
			if (flag)
			{
				Engine.EnsureReverse();
			}
			else if (num2 > 0f)
			{
				Engine.EnsureFirst();
			}
			float num4 = 0f;
			int num5 = 0;
			float num6 = 0f;
			float num7 = 0f;
			int num8 = 0;
			int num9 = 0;
			bool flag2 = false;
			float num10 = 0f;
			float num11 = 0.5f;
			List<Wheel> wheels = vehicle.wheels;
			for (int i = 0; i < wheels.Count; i++)
			{
				Wheel val = wheels[i];
				WheelCollider val2 = (((Object)(object)val != (Object)null) ? val.wheelCollider : null);
				if (!((Object)(object)val2 == (Object)null))
				{
					float num12 = ClampWheelRpm(val2.rpm, speed_Kmh, val2.radius);
					num4 += num12;
					num5++;
					if (val.IsSteerWheel)
					{
						num6 += num12;
						num8++;
					}
					else
					{
						num7 += num12;
						num9++;
					}
					num11 = val2.radius;
					if (val2.isGrounded)
					{
						flag2 = true;
					}
					float num13 = Mathf.Abs(num12) * (MathF.PI / 30f) * val2.radius;
					if (num13 > num10)
					{
						num10 = num13;
					}
				}
			}
			UpdateSurface();
			float num14 = Mathf.Abs(speed_Kmh) / 3.6f;
			bool flag3 = num10 > num14 + 4f && num10 > num14 * 1.5f;
			AvgWheelRpm = ((num5 > 0) ? (num4 / (float)num5) : 0f);
			float avgDriveWheelRpm = Mathf.Abs(speed_Kmh) / 3.6f / num11 * 9.549297f;
			Engine.Step(num2, num3, avgDriveWheelRpm, speed_Kmh, flag2, flag3, fixedDeltaTime);
			bool flag4 = false;
			if (RallyConfig.VerboseLogging != null && RallyConfig.VerboseLogging.Value)
			{
				if (Engine.gear != lastLoggedGear)
				{
					Utility.Log($"[SHIFT] {GearName(lastLoggedGear)} -> {GearName(Engine.gear)} @ {Engine.rpm:0} RPM, {speed_Kmh:0.0} km/h");
					lastLoggedGear = Engine.gear;
				}
				logTimer += fixedDeltaTime;
				if (logTimer >= 0.5f)
				{
					logTimer = 0f;
					flag4 = true;
				}
			}
			int num15 = Mathf.Max(num5, 1);
			float num16 = Engine.DriveWheelTorque(num2) * RallyConfig.PowerMult.Value / (float)num15;
			float num17 = ((Surface == SurfaceKind.Dirt) ? (1f * RallyConfig.DirtGrip.Value) : (1.9f * RallyConfig.TarmacGrip.Value));
			float num18 = num17 * (vehicle.Rb.mass * 9.81f / (float)num15) * num11 * 0.85f;
			num16 = Mathf.Clamp(num16, 0f - num18, num18);
			float num19 = 0f;
			if (flag2 && Mathf.Abs(speed_Kmh) > 2f)
			{
				num19 = Mathf.Min(Engine.EngineBrakeWheelTorque(num2) * 0.5f, 1000f) / num11;
			}
			float num20 = vehicle.Rb.mass * Profile.BrakeG * 9.81f * num11;
			float num21 = vehicle.handBrakeForce * 2f;
			float num22 = Mathf.Clamp01(fixedDeltaTime / 0.15f);
			filteredFrontRpm += (((num8 > 0) ? (num6 / (float)num8) : 0f) - filteredFrontRpm) * num22;
			filteredRearRpm += (((num9 > 0) ? (num7 / (float)num9) : 0f) - filteredRearRpm) * num22;
			float num23 = (filteredFrontRpm + filteredRearRpm) * 0.5f;
			float num24 = Mathf.Abs(num16) * 1.5f + 500f;
			float num25 = Mathf.Clamp((num23 - filteredFrontRpm) * 7f, 0f - num24, num24);
			float num26 = 0f - num25;
			StringBuilder stringBuilder = (flag4 ? new StringBuilder("[WHEELS] ") : null);
			for (int j = 0; j < wheels.Count; j++)
			{
				Wheel val3 = wheels[j];
				WheelCollider val4 = (((Object)(object)val3 != (Object)null) ? val3.wheelCollider : null);
				if (!((Object)(object)val4 == (Object)null))
				{
					bool flag5 = handbrakeColliderIds.Contains(((Object)val4).GetInstanceID());
					float num27 = (val3.IsSteerWheel ? 0.65f : 0.35f);
					float num28 = ((num3 > 0.01f) ? (num3 * num20 * num27 * 0.5f) : 0f);
					float num29 = num16 + (val3.IsSteerWheel ? num25 : num26);
					num29 = Mathf.Clamp(num29, 0f - num18, num18) + 0.0001f;
					if (HandbrakeActive && flag5)
					{
						num29 = 0.0001f;
						num28 = Mathf.Max(num28, num21);
					}
					val4.motorTorque = num29;
					val4.brakeTorque = num28;
					stringBuilder?.AppendFormat("W{0}({1}{2}{3}) rpm={4:0} mt={5:0} bt={6:0} sa={7:0.0}  ", j, val3.IsSteerWheel ? "S" : "-", flag5 ? "H" : "-", val4.isGrounded ? "G" : "-", val4.rpm, num29, num28, val4.steerAngle);
				}
			}
			if (num2 > 0.2f && Mathf.Abs(speed_Kmh) < 4f)
			{
				float num30 = (Engine.InReverse ? (-1f) : 1f);
				float num31 = 1f - Mathf.Abs(speed_Kmh) / 4f;
				vehicle.Rb.AddForce(((Component)vehicle).transform.forward * (num30 * num2 * num31 * vehicle.Rb.mass * 3f));
			}
			if (num19 > 0f)
			{
				vehicle.Rb.AddForce(((Component)vehicle).transform.forward * ((0f - Mathf.Sign(speed_Kmh)) * num19));
			}
			Vector3 val5 = ((Component)vehicle).transform.InverseTransformDirection(vehicle.Rb.velocity);
			if (Mathf.Abs(val5.x) > 2f)
			{
				vehicle.Rb.AddTorque(Vector3.up * ((0f - vehicle.Rb.angularVelocity.y) * RallyConfig.YawDamping.Value), (ForceMode)5);
			}
			Throttle = num2;
			Brake = num3;
			if (flag4)
			{
				Utility.Log(string.Format("[TELEM] v={0:0.0}km/h axis={1:0.00} thr={2:0.00} brk={3:0.00} hb={4} | gear={5} rpm={6:0} wheelAvg={7:0} clutch={8:0.00} {9}{10}| drv/whl={11:0}Nm engBrkN={12:0} diffF={13:0}Nm | spin={14} gnd={15} surf={16} steer={17:0.0}", speed_Kmh, num, num2, num3, HandbrakeActive ? 1 : 0, Engine.GearLabel, Engine.rpm, AvgWheelRpm, Engine.clutch, Engine.LimiterActive ? "[LIMIT] " : "", Engine.shifting ? "[SHIFTING] " : "", num16, num19, num25, flag3 ? 1 : 0, flag2 ? 1 : 0, Surface, vehicle.CurrentSteerAngle));
				Vector3 velocity = vehicle.Rb.velocity;
				Vector3 val6 = ((Component)vehicle).transform.InverseTransformDirection(velocity);
				Utility.Log($"[MOTION] vMag={((Vector3)(ref velocity)).magnitude * 3.6f:0.0}km/h vFwd={val6.z * 3.6f:0.0} vLat={val6.x * 3.6f:0.0} vUp={val6.y * 3.6f:0.0} | yawRate={vehicle.Rb.angularVelocity.y:0.00}rad/s rollDeg={Vector3.Angle(((Component)vehicle).transform.up, Vector3.up):0.0}");
				if (stringBuilder != null)
				{
					Utility.Log(stringBuilder.ToString());
				}
			}
			vehicle.SetIsBraking(num3 > 0.05f || (HandbrakeActive && Mathf.Abs(speed_Kmh) > 4f));
			vehicle.SetIsReversing(Engine.InReverse && speed_Kmh < -0.1f);
			if (HandbrakeActive && !vehicle.HandbrakeApplied && vehicle.onHandbrakeApplied != null)
			{
				vehicle.onHandbrakeApplied();
			}
			vehicle.HandbrakeApplied = HandbrakeActive;
		}

		public void ApplyWheelFriction(Wheel w)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			WheelCollider wheelCollider = w.wheelCollider;
			if (!((Object)(object)wheelCollider == (Object)null) && ((Collider)wheelCollider).enabled)
			{
				bool flag = Surface == SurfaceKind.Dirt;
				float num = (flag ? RallyConfig.DirtGrip.Value : RallyConfig.TarmacGrip.Value);
				bool flag2 = HandbrakeActive && !w.IsSteerWheel;
				float num2 = (flag2 ? (0.4f / Mathf.Max(RallyConfig.HandbrakeLooseness.Value, 0.1f)) : 1f);
				WheelFrictionCurve forwardFriction = wheelCollider.forwardFriction;
				WheelFrictionCurve sidewaysFriction = wheelCollider.sidewaysFriction;
				if (flag)
				{
					((WheelFrictionCurve)(ref forwardFriction)).extremumSlip = 0.5f;
					((WheelFrictionCurve)(ref forwardFriction)).extremumValue = 0.95f;
					((WheelFrictionCurve)(ref forwardFriction)).asymptoteSlip = 1.2f;
					((WheelFrictionCurve)(ref forwardFriction)).asymptoteValue = 0.55f;
					((WheelFrictionCurve)(ref forwardFriction)).stiffness = 1.05f * num;
					((WheelFrictionCurve)(ref sidewaysFriction)).extremumSlip = 0.45f;
					((WheelFrictionCurve)(ref sidewaysFriction)).extremumValue = 0.9f;
					((WheelFrictionCurve)(ref sidewaysFriction)).asymptoteSlip = 0.9f;
					((WheelFrictionCurve)(ref sidewaysFriction)).asymptoteValue = 0.6f;
					((WheelFrictionCurve)(ref sidewaysFriction)).stiffness = 0.8f * num * num2;
				}
				else
				{
					((WheelFrictionCurve)(ref forwardFriction)).extremumSlip = 0.35f;
					((WheelFrictionCurve)(ref forwardFriction)).extremumValue = 1f;
					((WheelFrictionCurve)(ref forwardFriction)).asymptoteSlip = 0.8f;
					((WheelFrictionCurve)(ref forwardFriction)).asymptoteValue = 0.75f;
					((WheelFrictionCurve)(ref forwardFriction)).stiffness = 1.9f * num;
					((WheelFrictionCurve)(ref sidewaysFriction)).extremumSlip = 0.3f;
					((WheelFrictionCurve)(ref sidewaysFriction)).extremumValue = 1f;
					((WheelFrictionCurve)(ref sidewaysFriction)).asymptoteSlip = 0.65f;
					((WheelFrictionCurve)(ref sidewaysFriction)).asymptoteValue = 0.8f;
					float num3 = Mathf.Lerp(RallyConfig.RearGrip.Value, 0.9f, Mathf.Clamp01((Mathf.Abs(Vehicle.Speed_Kmh) - 50f) / 50f));
					float num4 = (w.IsSteerWheel ? 1f : num3);
					((WheelFrictionCurve)(ref sidewaysFriction)).stiffness = 1.4f * num * num4 * num2;
				}
				if (flag2)
				{
					((WheelFrictionCurve)(ref forwardFriction)).stiffness = ((WheelFrictionCurve)(ref forwardFriction)).stiffness * 0.55f;
				}
				wheelCollider.forwardFriction = forwardFriction;
				wheelCollider.sidewaysFriction = sidewaysFriction;
			}
		}

		public void HandleSteering()
		{
			//IL_0031: 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)
			//IL_003d: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			LandVehicle vehicle = Vehicle;
			float deltaTime = Time.deltaTime;
			float num = Mathf.Abs(vehicle.Speed_Kmh);
			float x = GameInput.MotionAxis.x;
			Vector3 val = ((Component)vehicle).transform.InverseTransformDirection(vehicle.Rb.velocity);
			bool flag = Mathf.Abs(val.x) > 1f && Mathf.Abs(x) > 0.01f && Mathf.Sign(x) == Mathf.Sign(val.x);
			float num2 = vehicle.ActualMaxSteeringAngle;
			float num3 = num / 3.6f;
			if (flag)
			{
				float num4 = Mathf.Atan2(Mathf.Abs(val.x), Mathf.Max(Mathf.Abs(val.z), 0.5f)) * 57.29578f;
				num2 = Mathf.Min(vehicle.ActualMaxSteeringAngle, num4 + 10f);
			}
			else if (num3 > 3f)
			{
				float num5 = Mathf.Atan(wheelbase * 9f / (num3 * num3)) * 57.29578f + 8f;
				num2 = Mathf.Clamp(num5, 8f, num2);
			}
			num2 = Mathf.Min(num2, Mathf.Lerp(12f, vehicle.ActualMaxSteeringAngle, Mathf.Clamp01(num / 12f)));
			float lastFrameSteerAngle = vehicle.lastFrameSteerAngle;
			lastFrameSteerAngle = ((!(Mathf.Abs(x) > 0.001f)) ? Mathf.MoveTowards(lastFrameSteerAngle, 0f, vehicle.steerRate * 1.6f * deltaTime) : Mathf.Clamp(lastFrameSteerAngle + x * vehicle.steerRate * 1.1f * deltaTime, 0f - num2, num2));
			lastFrameSteerAngle = (vehicle.CurrentSteerAngle = Mathf.Clamp(lastFrameSteerAngle, 0f - num2, num2));
			if (Mathf.Abs(vehicle.lastReplicatedSteerAngle - lastFrameSteerAngle) > 3f)
			{
				vehicle.lastReplicatedSteerAngle = lastFrameSteerAngle;
				vehicle.SetSteeringAngle(lastFrameSteerAngle);
			}
			vehicle.lastFrameSteerAngle = lastFrameSteerAngle;
		}

		public void HandleAero()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_0066: 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)
			Rigidbody rb = Vehicle.Rb;
			Vector3 velocity = rb.velocity;
			float sqrMagnitude = ((Vector3)(ref velocity)).sqrMagnitude;
			if (!(sqrMagnitude < 0.01f))
			{
				rb.AddForce(-((Vector3)(ref velocity)).normalized * (0.61f * Profile.CdA * sqrMagnitude));
				rb.AddForce(-((Component)Vehicle).transform.up * (Profile.DownforceCoeff * sqrMagnitude));
			}
		}
	}
	public static class RallyConfig
	{
		public static MelonPreferences_Category Category;

		public static MelonPreferences_Entry<bool> Enabled;

		public static MelonPreferences_Entry<bool> DebugHud;

		public static MelonPreferences_Entry<bool> VerboseLogging;

		public static MelonPreferences_Entry<float> TarmacGrip;

		public static MelonPreferences_Entry<float> DirtGrip;

		public static MelonPreferences_Entry<float> PowerMult;

		public static MelonPreferences_Entry<float> HandbrakeLooseness;

		public static MelonPreferences_Entry<float> RearGrip;

		public static MelonPreferences_Entry<float> YawDamping;

		public static void Init()
		{
			Category = MelonPreferences.CreateCategory("DoYouCatchMyDrift");
			Enabled = Category.CreateEntry<bool>("RallyModeEnabled", true, (string)null, "Master toggle for rally physics (F6 in game)", false, false, (ValueValidator)null, (string)null);
			DebugHud = Category.CreateEntry<bool>("DebugHud", true, (string)null, "Show gear/RPM/surface HUD while driving (F7 in game)", false, false, (ValueValidator)null, (string)null);
			VerboseLogging = Category.CreateEntry<bool>("VerboseLogging", false, (string)null, "Log rally telemetry to the Melon console once per second", false, false, (ValueValidator)null, (string)null);
			TarmacGrip = Category.CreateEntry<float>("TarmacGrip", 1f, (string)null, "Grip multiplier on roads/built surfaces", false, false, (ValueValidator)null, (string)null);
			DirtGrip = Category.CreateEntry<float>("DirtGrip", 1f, (string)null, "Grip multiplier on terrain (dirt/grass)", false, false, (ValueValidator)null, (string)null);
			PowerMult = Category.CreateEntry<float>("PowerMultiplier", 1f, (string)null, "Engine power multiplier for all profiles. Set it to 1.5f to have more fun!", false, false, (ValueValidator)null, (string)null);
			HandbrakeLooseness = Category.CreateEntry<float>("HandbrakeLooseness", 1f, (string)null, "Higher = rear steps out more on handbrake", false, false, (ValueValidator)null, (string)null);
			RearGrip = Category.CreateEntry<float>("RearGrip", 0.7f, (string)null, "REAR sideways grip as a fraction of front, at ALL speeds. 0.67 = 60/40 front/rear split. Lower = tail-happier, 1.0 = planted. Fronts always keep full grip for steering/countersteer", false, false, (ValueValidator)null, (string)null);
			YawDamping = Category.CreateEntry<float>("YawDamping", 0.78f, (string)null, "Counter-torque per rad/s of rotation while sliding. Settles the drift-catch-swing pendulum. 0 = off, 0.6 = default, 1.0+ = very settled/harder to rotate", false, false, (ValueValidator)null, (string)null);
		}

		public static void ToggleEnabled()
		{
			Enabled.Value = !Enabled.Value;
			MelonPreferences.Save();
			if (!Enabled.Value)
			{
				RallyManager.RestoreAll();
			}
			Utility.Log("Rally mode " + (Enabled.Value ? "ENABLED" : "DISABLED"));
		}

		public static void ToggleHud()
		{
			DebugHud.Value = !DebugHud.Value;
			MelonPreferences.Save();
		}
	}
	public static class RallyManager
	{
		private static readonly Dictionary<int, RallyVehicle> sims = new Dictionary<int, RallyVehicle>();

		public static RallyVehicle CurrentDriven;

		public static bool IsActiveFor(LandVehicle v)
		{
			return RallyConfig.Enabled != null && RallyConfig.Enabled.Value && (Object)(object)v != (Object)null && v.LocalPlayerIsDriver && !v.overrideControls;
		}

		public static RallyVehicle For(LandVehicle v)
		{
			int instanceID = ((Object)v).GetInstanceID();
			if (!sims.TryGetValue(instanceID, out var value))
			{
				value = new RallyVehicle(v);
				sims[instanceID] = value;
			}
			CurrentDriven = value;
			return value;
		}

		public static void RestoreAll()
		{
			foreach (RallyVehicle value in sims.Values)
			{
				value.RestoreCenterOfMass();
			}
		}

		public static void Clear()
		{
			sims.Clear();
			CurrentDriven = null;
		}
	}
	public static class RallyHud
	{
		public static void Draw()
		{
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			if (RallyConfig.DebugHud != null && RallyConfig.DebugHud.Value)
			{
				RallyVehicle currentDriven = RallyManager.CurrentDriven;
				if (currentDriven != null && !((Object)(object)currentDriven.Vehicle == (Object)null) && currentDriven.Vehicle.LocalPlayerIsDriver)
				{
					string text = string.Format("RALLY {0}   {1,4:0} km/h   Gear {2}   {3,5:0} RPM{4}   Wheel {5,4:0}\nSurface: {6}   Clutch {7:0.00}   Thr {8:0.00}  Brk {9:0.00}{10}", RallyConfig.Enabled.Value ? "ON" : "OFF", currentDriven.Vehicle.Speed_Kmh, currentDriven.Engine.GearLabel, currentDriven.Engine.rpm, currentDriven.Engine.LimiterActive ? " [LIMIT]" : (currentDriven.Engine.shifting ? " [SHIFT]" : ""), currentDriven.AvgWheelRpm, currentDriven.Surface, currentDriven.Engine.clutch, currentDriven.Throttle, currentDriven.Brake, currentDriven.HandbrakeActive ? "   HANDBRAKE" : "");
					GUI.Box(new Rect(10f, (float)Screen.height - 78f, 430f, 62f), "");
					GUI.Label(new Rect(20f, (float)Screen.height - 72f, 420f, 55f), text);
				}
			}
		}
	}
	[HarmonyPatch(typeof(LandVehicle), "ApplyThrottle")]
	internal static class Patch_LandVehicle_ApplyThrottle
	{
		private static bool Prefix(LandVehicle __instance)
		{
			if (!RallyManager.IsActiveFor(__instance))
			{
				return true;
			}
			try
			{
				RallyManager.For(__instance).HandleThrottle();
				return false;
			}
			catch (Exception e)
			{
				Utility.PrintException(e);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(LandVehicle), "UpdateSteerAngle")]
	internal static class Patch_LandVehicle_UpdateSteerAngle
	{
		private static bool Prefix(LandVehicle __instance)
		{
			if (!RallyManager.IsActiveFor(__instance))
			{
				return true;
			}
			try
			{
				if ((Object)(object)__instance.DriverPlayer != (Object)null && __instance.DriverPlayer.IsTased)
				{
					return true;
				}
				if ((Object)(object)Player.Local != (Object)null && (Player.Local.Seizure || Player.Local.Disoriented))
				{
					return true;
				}
				RallyManager.For(__instance).HandleSteering();
				return false;
			}
			catch (Exception e)
			{
				Utility.PrintException(e);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Wheel), "ApplyFriction")]
	internal static class Patch_Wheel_ApplyFriction
	{
		private static bool Prefix(Wheel __instance)
		{
			LandVehicle vehicle = __instance.vehicle;
			if (!RallyManager.IsActiveFor(vehicle))
			{
				return true;
			}
			try
			{
				RallyManager.For(vehicle).ApplyWheelFriction(__instance);
				return false;
			}
			catch (Exception e)
			{
				Utility.PrintException(e);
				return true;
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		internal IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}