Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of DoYouCatchMyDrift v1.1.0
DoYouCatchMyDrift_IL2Cpp_1.1.0.dll
Decompiled a day agousing 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 agousing 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) { } } }