Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of Chassis v1.3.0
Chassis.dll
Decompiled 4 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using Chassis; using Chassis.Api; using Chassis.Compat; using Chassis.Debugging; using Chassis.Instruments; using Chassis.Interior; using Chassis.Net; using Chassis.Seating; using Chassis.Tune; using Chassis.Vehicles; using HarmonyLib; using Il2CppFishNet; using Il2CppFishNet.Connection; using Il2CppFishNet.Object; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppRootMotion.FinalIK; using Il2CppScheduleOne; using Il2CppScheduleOne.Audio; using Il2CppScheduleOne.AvatarFramework; using Il2CppScheduleOne.AvatarFramework.Animation; using Il2CppScheduleOne.Core.Audio; using Il2CppScheduleOne.Core.Weather; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.Dialogue; using Il2CppScheduleOne.Equipping; using Il2CppScheduleOne.GameTime; using Il2CppScheduleOne.Interaction; using Il2CppScheduleOne.Money; using Il2CppScheduleOne.NPCs; using Il2CppScheduleOne.NPCs.Behaviour; using Il2CppScheduleOne.NPCs.CharacterClasses; using Il2CppScheduleOne.Persistence; using Il2CppScheduleOne.PlayerScripts; using Il2CppScheduleOne.Quests; using Il2CppScheduleOne.UI; using Il2CppScheduleOne.UI.Input; using Il2CppScheduleOne.Vehicles; using Il2CppScheduleOne.Vehicles.Modification; using Il2CppScheduleOne.Vehicles.Sound; using Il2CppScheduleOne.Weather; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.Reflection; using Il2CppTMPro; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UnityEngine; using UnityEngine.Audio; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Core), "Chassis", "1.3.0", "DooDesch", "https://github.com/DooDesch-Mods/ScheduleOne-Chassis")] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("DooDesch")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © DooDesch")] [assembly: AssemblyFileVersion("1.3.0.0")] [assembly: AssemblyInformationalVersion("1.3.0+9e107530daa51cef80c7382e7dd8644ad1c7798d")] [assembly: AssemblyProduct("Chassis")] [assembly: AssemblyTitle("Chassis")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially created with the assistance of Generative AI.")] [assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic/OpenAI")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.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 DooDesch { internal static class ModVersion { internal const string Current = "1.3.0"; } } namespace Chassis { public sealed class Core : MelonMod { private bool _inWorld; private static readonly Dictionary<string, float> _lastError = new Dictionary<string, float>(); private static readonly Dictionary<string, int> _muted = new Dictionary<string, int>(); public static Core Instance { get; private set; } public static Instance Log { get; private set; } public override void OnInitializeMelon() { Instance = this; Log = ((MelonBase)this).LoggerInstance; int num = 0; foreach (MethodBase patchedMethod in ((MelonBase)this).HarmonyInstance.GetPatchedMethods()) { _ = patchedMethod; num++; } Log.Msg("[Core] Harmony patches applied by MelonLoader on " + num + " method(s)."); ChassisNet.Apply(((MelonBase)this).HarmonyInstance); ChassisSync.Init(); RepaintPatch.Apply(((MelonBase)this).HarmonyInstance); BaseInterior.LoadSetting(); Sources.LoadSetting(); ChassisApi.Changed += delegate { Gauges.ForgetAll(); Controls.ForgetAll(); }; Log.Msg("Chassis loaded."); } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { _inWorld = sceneName == "Main"; if (!_inWorld) { ResetWorld(); } } public override void OnSceneWasUnloaded(int buildIndex, string sceneName) { _inWorld = false; ResetWorld(); } private static void ResetWorld() { FirstPersonView.Reset(); Occupants.Reset(); Registry.Reset(); Fleet.Reset(); Indicators.Reset(); KeyHints.Reset(); Parts.Reset(); AutoLights.Reset(); BaseInterior.Reset(); NpcRiders.Reset(); SeatedTargets.Reset(); Store.Reset(); Odometer.Reset(); Pedals.Reset(); Sources.Reset(); Gauges.Reset(); Controls.Reset(); Cockpit.Reset(); Watch.Reset(); State.Reset(); Apply.Reset(); } public override void OnLateUpdate() { if (_inWorld) { Guard("parts", Parts.LateTick); } } public override void OnUpdate() { if (_inWorld) { Guard("vehicles", Registry.Tick); Guard("interior", BaseInterior.Tick); Guard("npc riders", NpcRiders.Tick); Guard("seated targets", SeatedTargets.Tick); Guard("owners", Fleet.Tick); Guard("horn", Horn.Tick); Guard("indicators", Indicators.Tick); Guard("auto lights", AutoLights.Tick); Guard("view", FirstPersonView.Tick); Guard("key hints", KeyHints.Tick); Guard("pedals", Pedals.Tick); Guard("odometer", Odometer.Tick); Guard("gauges", Gauges.Tick); Guard("switches", Controls.Tick); Guard("cockpit", Cockpit.Tick); Guard("driving events", Watch.Tick); Guard("tuning state", State.Tick); Guard("tuning", Apply.Tick); Guard("sweep", Occupants.Sweep); ConsoleRegistration.EnsureRegistered(); DebugConsolePatch.FlushToConsole(); } } private static void Guard(string what, Action a) { try { a(); } catch (Exception ex) { if (_lastError.TryGetValue(what, out var value) && Time.time - value < 5f) { _muted.TryGetValue(what, out var value2); _muted[what] = value2 + 1; return; } _lastError[what] = Time.time; _muted.TryGetValue(what, out var value3); _muted[what] = 0; Log.Error("[Core] " + what + " failed this frame" + ((value3 > 0) ? (" (and " + value3 + " more time(s) in the last 5 s)") : "") + ": " + ex); } } } } namespace Chassis.Vehicles { internal static class AutoLights { private const int OnFrom = 1730; private const int OffFrom = 600; private const float CheckEvery = 1f; private const float MovingKmh = 3f; private const float AfterglowSeconds = 60f; private static readonly Dictionary<int, bool> Wanted = new Dictionary<int, bool>(); private static readonly Dictionary<int, bool> WasDriven = new Dictionary<int, bool>(); private static readonly Dictionary<int, float> GlowUntil = new Dictionary<int, float>(); private static float _next; private static bool _warned; internal static void Tick() { if (!ChassisNet.IsHost) { return; } float time = Time.time; if (time < _next) { return; } _next = time + 1f; if (!NetworkSingleton<TimeManager>.InstanceExists) { if (!_warned) { Core.Log.Warning("[Lights] no clock yet - automatic headlights wait."); } _warned = true; return; } _warned = false; bool flag = NetworkSingleton<TimeManager>.Instance.IsCurrentTimeWithinRange(1730, 600); foreach (LandVehicle item in Registry.Profiled) { if ((Object)(object)item == (Object)null) { continue; } ChassisProfile chassisProfile = ChassisApi.For(item); if (chassisProfile != null && chassisProfile.AutoLights) { int instanceID = ((Object)item).GetInstanceID(); bool flag2 = item.IsOccupied || ((Object)(object)item.Agent != (Object)null && item.Agent.AutoDriving) || Mathf.Abs(item.Speed_Kmh) > 3f; bool flag3 = Afterglow(item, instanceID, time, flag, flag2); bool flag4 = flag && (flag2 || flag3); if (!Wanted.TryGetValue(instanceID, out var value) || value != flag4) { Wanted[instanceID] = flag4; Apply(item, flag4, flag, flag2, flag3); } } } } private static bool Afterglow(LandVehicle lv, int key, float now, bool night, bool driven) { WasDriven.TryGetValue(key, out var value); WasDriven[key] = driven; if (driven) { GlowUntil.Remove(key); return false; } if (value && night) { GlowUntil[key] = now + 60f; Core.Log.Msg("[Lights] " + Registry.Name(lv) + " parked at night - headlights stay on for " + 60f.ToString("0") + " s or until it is locked."); } if (!GlowUntil.TryGetValue(key, out var value2)) { return false; } object obj; if (night) { Fleet.Rec rec = Fleet.For(lv); obj = ((rec != null && rec.Locked) ? "it was locked" : ((now >= value2) ? "the minute is up" : null)); } else { obj = "the day broke"; } string text = (string)obj; if (text == null) { return true; } GlowUntil.Remove(key); Core.Log.Msg("[Lights] " + Registry.Name(lv) + " afterglow ends: " + text + "."); return false; } private static void Apply(LandVehicle lv, bool on, bool night, bool driven, bool glow) { VehicleLights lights = lv.lights; if ((Object)(object)lights == (Object)null) { Core.Log.Warning("[Lights] " + Registry.Name(lv) + " has no lights to switch."); } else if (lights.HeadlightsOn != on) { try { lights.HeadlightsOn = on; Core.Log.Msg("[Lights] " + Registry.Name(lv) + " headlights " + (on ? "on" : "off") + " (" + (night ? "night" : "day") + ", " + (driven ? "driven" : (glow ? "afterglow" : "parked")) + ")."); } catch (Exception ex) { Core.Log.Error("[Lights] switching the headlights of " + Registry.Name(lv) + " failed: " + ex.Message); } } } internal static void Reset() { Wanted.Clear(); WasDriven.Clear(); GlowUntil.Clear(); _next = 0f; _warned = false; } } internal static class Fleet { internal sealed class Rec { internal string Owner = string.Empty; internal bool Locked; internal bool Same(Rec o) { if (o != null && o.Owner == Owner) { return o.Locked == Locked; } return false; } } private struct Announcement { internal string Buyer; internal string Code; internal float At; } [HarmonyPatch(typeof(LandVehicle), "Hovered")] private static class HoveredPatch { private static void Postfix(LandVehicle __instance) { try { if (__instance.IsPlayerOwned && !CanEnter(__instance, out var _)) { __instance.intObj.SetMessage("Locked"); __instance.intObj.SetInteractableState((EInteractableState)1); } } catch (Exception ex) { Core.Log.Error("[Fleet] could not show the locked prompt: " + ex.Message); } } } [HarmonyPatch(typeof(LandVehicle), "Interacted")] private static class InteractedPatch { private static bool Prefix(LandVehicle __instance) { try { if (CanEnter(__instance, out var why)) { return true; } if (Time.time - _lastRefusalLog > 2f) { _lastRefusalLog = Time.time; Core.Log.Msg("[Fleet] " + Registry.Name(__instance) + " is " + why + " - not entering."); } return false; } catch (Exception ex) { Core.Log.Error("[Fleet] lock check failed, letting the player in: " + ex.Message); return true; } } } [HarmonyPatch(typeof(DialogueHandler_VehicleSalesman), "ChoiceCallback")] private static class PurchasePatch { private static void Prefix(DialogueHandler_VehicleSalesman __instance, string choiceLabel) { try { if (choiceLabel != "BUY_CASH" && choiceLabel != "BUY_ONLINE") { return; } DealershipListing selectedVehicle = __instance.selectedVehicle; if (selectedVehicle == null) { Core.Log.Warning("[Fleet] purchase without a selected vehicle - not announced."); return; } MoneyManager instance = NetworkSingleton<MoneyManager>.Instance; float num = ((choiceLabel == "BUY_CASH") ? instance.cashBalance : instance.onlineBalance); if (num < selectedVehicle.price) { Core.Log.Msg("[Fleet] '" + selectedVehicle.vehicleCode + "' purchase with " + choiceLabel + " will fail (balance " + num + " < " + selectedVehicle.price + ") - not announced."); return; } string text = MyCode(); if (text.Length == 0) { Core.Log.Warning("[Fleet] no local player code - the new vehicle will belong to the host."); return; } ChassisNet.ToHost("req.bought", text, selectedVehicle.vehicleCode); Core.Log.Msg("[Fleet] announced a '" + selectedVehicle.vehicleCode + "' purchase by " + text + " (" + choiceLabel + ")."); } catch (Exception ex) { Core.Log.Error("[Fleet] could not announce the purchase - the vehicle will belong to the host: " + ex); } } } internal const string FileName = "Chassis.vehicles"; private const float ScanSeconds = 1f; private const float ClaimSeconds = 10f; private static readonly Dictionary<string, Rec> ByGuid = new Dictionary<string, Rec>(); private static readonly Dictionary<int, Rec> ById = new Dictionary<int, Rec>(); private static string _loadedFrom; private static float _nextScan; private static string _lastBroadcast = string.Empty; private static bool _askedForSync; private static bool _scanned; private static readonly List<Announcement> Pending = new List<Announcement>(); private static readonly Dictionary<string, float> FirstSeen = new Dictionary<string, float>(); private static float _lastRefusalLog = -100f; internal static Rec For(LandVehicle lv) { if ((Object)(object)lv == (Object)null) { return null; } int num = Ids.Of((Component)(object)lv); if (num < 0 || !ById.TryGetValue(num, out var value)) { return null; } return value; } internal static string MyCode() { Player local = Player.Local; if (!((Object)(object)local != (Object)null)) { return string.Empty; } return local.PlayerCode ?? string.Empty; } internal static bool IsOwner(LandVehicle lv, string playerCode) { Rec rec = For(lv); if (rec != null && !string.IsNullOrEmpty(playerCode)) { return rec.Owner == playerCode; } return false; } internal static bool IsMine(LandVehicle lv) { return IsOwner(lv, MyCode()); } internal static bool CanEnter(LandVehicle lv, out string why) { why = null; Rec rec = For(lv); if (rec == null || !rec.Locked) { return true; } if (rec.Owner == MyCode()) { return true; } Il2CppReferenceArray<VehicleSeat> seats = lv.Seats; if (seats != null) { foreach (VehicleSeat item in (Il2CppArrayBase<VehicleSeat>)(object)seats) { if ((Object)(object)item != (Object)null && (Object)(object)item.Occupant != (Object)null && item.Occupant.PlayerCode == rec.Owner) { return true; } } } why = "locked by its owner " + rec.Owner; return false; } internal static void RequestLock(LandVehicle lv, bool locked) { if ((Object)(object)lv == (Object)null) { Core.Log.Warning("[Fleet] lock asked for a vehicle that is gone."); return; } string text = MyCode(); if (text.Length == 0) { Core.Log.Warning("[Fleet] lock asked before the local player exists."); return; } ChassisNet.ToHost("req.lock", Ids.Of((Component)(object)lv).ToString(CultureInfo.InvariantCulture), text, locked ? "1" : "0"); } internal static void HostLock(int vehicle, string requester, bool locked) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) LandVehicle val = Registry.ById(vehicle); if ((Object)(object)val == (Object)null) { Core.Log.Warning("[Fleet] lock for net " + vehicle + ", which is not here - dropped."); return; } EnsureLoaded(); if (!ByGuid.TryGetValue(((object)val.GUID/*cast due to .constrained prefix*/).ToString(), out var value)) { Core.Log.Msg("[Fleet] " + Registry.Name(val) + " has no owner record - lock refused."); return; } if (value.Owner != requester) { Core.Log.Msg("[Fleet] " + requester + " does not own " + Registry.Name(val) + " - lock refused."); return; } if (value.Locked == locked) { Core.Log.Msg("[Fleet] " + Registry.Name(val) + " is already " + (locked ? "locked" : "unlocked") + "."); return; } value.Locked = locked; Core.Log.Msg("[Fleet] " + Registry.Name(val) + " " + (locked ? "locked" : "unlocked") + " by " + requester + "."); ChassisNet.ToAll("chirp", vehicle.ToString(CultureInfo.InvariantCulture), locked ? "1" : "0"); Save(); Broadcast(force: true); } internal static bool HostSetOwner(LandVehicle lv, string owner, string why) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (!ChassisNet.IsHost) { Core.Log.Warning("[Fleet] owner change on a client ignored - the host records owners."); return false; } if ((Object)(object)lv == (Object)null || !lv.IsPlayerOwned || string.IsNullOrEmpty(owner)) { Core.Log.Warning("[Fleet] owner change without a player vehicle or owner (" + why + ") ignored."); return false; } EnsureLoaded(); string text = ((object)lv.GUID/*cast due to .constrained prefix*/).ToString(); if (!ByGuid.TryGetValue(text, out var value)) { value = (ByGuid[text] = new Rec()); } value.Owner = owner; Core.Log.Msg("[Fleet] " + Registry.Name(lv) + " " + Short(text) + " now belongs to " + owner + " (" + why + ")."); Save(); Broadcast(force: true); return true; } internal static void Announce(string buyer, string code) { if (string.IsNullOrEmpty(buyer)) { Core.Log.Warning("[Fleet] purchase announcement without a buyer ignored."); return; } Pending.Add(new Announcement { Buyer = buyer, Code = (code ?? string.Empty), At = Time.unscaledTime }); Core.Log.Msg("[Fleet] " + buyer + " is buying a '" + code + "' - the next new one is theirs."); _nextScan = 0f; } internal static void Tick() { //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) if (!ChassisNet.IsHost) { if (!_askedForSync && (Object)(object)Player.Local != (Object)null && (Object)(object)NetworkSingleton<QuestManager>.Instance != (Object)null) { _askedForSync = true; ChassisNet.ToHost("req.sync", MyCode()); } return; } float unscaledTime = Time.unscaledTime; if (unscaledTime < _nextScan) { return; } _nextScan = unscaledTime + 1f; EnsureLoaded(); string text = MyCode(); if (text.Length == 0) { return; } for (int num = Pending.Count - 1; num >= 0; num--) { if (!(unscaledTime - Pending[num].At <= 10f)) { Core.Log.Warning("[Fleet] the '" + Pending[num].Code + "' purchase announced by " + Pending[num].Buyer + " never showed up as a vehicle - announcement dropped."); Pending.RemoveAt(num); } } bool flag = false; foreach (LandVehicle v in Registry.All) { if ((Object)(object)v == (Object)null || !v.IsPlayerOwned) { continue; } string text2 = ((object)v.GUID/*cast due to .constrained prefix*/).ToString(); if (ByGuid.ContainsKey(text2)) { continue; } int num2 = Pending.FindIndex((Announcement a) => string.Equals(a.Code, v.vehicleCode, StringComparison.OrdinalIgnoreCase)); if (num2 >= 0) { ByGuid[text2] = new Rec { Owner = Pending[num2].Buyer }; Core.Log.Msg("[Fleet] " + Registry.Name(v) + " " + Short(text2) + " bought at the dealership by " + Pending[num2].Buyer + "."); Pending.RemoveAt(num2); flag = true; continue; } if (_scanned) { if (!FirstSeen.TryGetValue(text2, out var value)) { FirstSeen[text2] = unscaledTime; continue; } if (unscaledTime - value < 10f) { continue; } } ByGuid[text2] = Unknown(v, text); flag = true; } _scanned = true; if (flag) { Save(); } Broadcast(force: false); } private static Rec Unknown(LandVehicle v, string host) { //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: 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) Func<LandVehicle, OwnerRecord> resolveUnknownOwner = ChassisApi.ResolveUnknownOwner; if (resolveUnknownOwner != null) { try { OwnerRecord ownerRecord = resolveUnknownOwner(v); if (ownerRecord != null && !string.IsNullOrEmpty(ownerRecord.Owner)) { Core.Log.Msg("[Fleet] " + Registry.Name(v) + " " + Short(((object)v.GUID/*cast due to .constrained prefix*/).ToString()) + " imported from a mod: owner " + ownerRecord.Owner + (ownerRecord.Locked ? ", locked" : "") + "."); return new Rec { Owner = ownerRecord.Owner, Locked = ownerRecord.Locked }; } } catch (Exception ex) { Core.Log.Error("[Fleet] a ResolveUnknownOwner handler failed for " + Registry.Name(v) + ", it belongs to the host: " + ex); } } Core.Log.Msg("[Fleet] " + Registry.Name(v) + " " + Short(((object)v.GUID/*cast due to .constrained prefix*/).ToString()) + " had no owner record - it belongs to the host (" + host + ")."); return new Rec { Owner = host }; } internal static void Broadcast(bool force) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); foreach (LandVehicle item in Registry.All) { if (!((Object)(object)item == (Object)null) && item.IsPlayerOwned && ByGuid.TryGetValue(((object)item.GUID/*cast due to .constrained prefix*/).ToString(), out var value)) { if (stringBuilder.Length > 0) { stringBuilder.Append(';'); } stringBuilder.Append(Ids.Of((Component)(object)item).ToString(CultureInfo.InvariantCulture)).Append(',').Append(value.Owner) .Append(',') .Append(value.Locked ? 1 : 0); } } string text = stringBuilder.ToString(); if (force || !(text == _lastBroadcast)) { _lastBroadcast = text; ChassisNet.ToAll("own", text); } } internal static void ApplyState(string payload) { Dictionary<int, Rec> dictionary = new Dictionary<int, Rec>(); string[] array = (payload ?? string.Empty).Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { string[] array2 = text.Split(','); if (array2.Length < 3 || !int.TryParse(array2[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { Core.Log.Warning("[Fleet] unreadable record '" + text + "' skipped."); continue; } dictionary[result] = new Rec { Owner = array2[1], Locked = (array2[2] == "1") }; } bool flag = dictionary.Count != ById.Count; if (!flag) { foreach (KeyValuePair<int, Rec> item in dictionary) { if (!ById.TryGetValue(item.Key, out var value) || !value.Same(item.Value)) { flag = true; break; } } } if (!flag) { return; } ById.Clear(); foreach (KeyValuePair<int, Rec> item2 in dictionary) { ById[item2.Key] = item2.Value; } Core.Log.Msg("[Fleet] owner records: " + ((payload.Length > 0) ? payload : "none") + " (I am " + MyCode() + ")."); ChassisApi.RaiseOwners(); } internal static void EnsureLoaded() { string text = SavePath(); if (text == null || text == _loadedFrom) { return; } _loadedFrom = text; ByGuid.Clear(); if (!File.Exists(text)) { Core.Log.Msg("[Fleet] no Chassis.vehicles in this save yet."); return; } try { string[] array = File.ReadAllLines(text); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split('|'); if (array2.Length >= 3 && array2[0].Length != 0) { ByGuid[array2[0]] = new Rec { Owner = array2[1], Locked = (array2[2] == "1") }; } } Core.Log.Msg("[Fleet] loaded " + ByGuid.Count + " owner record(s) from " + text + "."); } catch (Exception ex) { Core.Log.Error("[Fleet] could not read " + text + ", owners start empty: " + ex.Message); } } internal static void SaveWithGame() { if (ChassisNet.IsHost) { EnsureLoaded(); Save(); Core.Log.Msg("[Fleet] saved " + ByGuid.Count + " owner record(s) with the game."); } } private static void Save() { string text = SavePath(); if (text == null) { Core.Log.Warning("[Fleet] no save folder - the owner records stay in memory."); return; } try { StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, Rec> item in ByGuid) { stringBuilder.Append(item.Key).Append('|').Append(item.Value.Owner) .Append('|') .Append(item.Value.Locked ? 1 : 0) .Append('\n'); } File.WriteAllText(text, stringBuilder.ToString()); _loadedFrom = text; } catch (Exception ex) { Core.Log.Error("[Fleet] could not write " + text + ": " + ex.Message); } } private static string SavePath() { if (!Singleton<LoadManager>.InstanceExists) { return null; } string loadedGameFolderPath = Singleton<LoadManager>.Instance.LoadedGameFolderPath; if (!string.IsNullOrEmpty(loadedGameFolderPath) && Directory.Exists(loadedGameFolderPath)) { return Path.Combine(loadedGameFolderPath, "Chassis.vehicles"); } return null; } private static string Short(string guid) { if (guid.Length <= 6) { return guid; } return guid.Substring(0, 6); } internal static string Describe() { StringBuilder stringBuilder = new StringBuilder("owners me=" + MyCode() + " host=" + ChassisNet.IsHost); foreach (KeyValuePair<int, Rec> item in ById) { stringBuilder.Append(" [net ").Append(item.Key).Append(" owner=") .Append(item.Value.Owner) .Append(" locked=") .Append(item.Value.Locked) .Append(']'); } return stringBuilder.ToString(); } internal static void Reset() { ByGuid.Clear(); ById.Clear(); _loadedFrom = null; _lastBroadcast = string.Empty; _askedForSync = false; _scanned = false; Pending.Clear(); FirstSeen.Clear(); _nextScan = 0f; } } internal static class Frames { internal static Transform Resolve(Transform root, string path) { if ((Object)(object)root == (Object)null) { return null; } if (string.IsNullOrEmpty(path)) { return root; } Transform val = root.Find(path); if ((Object)(object)val != (Object)null) { return val; } int num = path.LastIndexOf('/'); return Deep(root, (num >= 0) ? path.Substring(num + 1) : path); } internal static Transform Deep(Transform root, string name) { if ((Object)(object)root == (Object)null || string.IsNullOrEmpty(name)) { return null; } foreach (Transform componentsInChild in ((Component)root).GetComponentsInChildren<Transform>(true)) { if ((Object)(object)componentsInChild != (Object)null && ((Object)componentsInChild).name == name) { return componentsInChild; } } return null; } internal static bool InGroup(string name, string group) { if (name == null || string.IsNullOrEmpty(group)) { return false; } if (!(name == group)) { if (name.Length > group.Length && name[group.Length] == '.') { return name.StartsWith(group, StringComparison.Ordinal); } return false; } return true; } } internal static class Horn { internal const KeyCode Key = (KeyCode)103; private const float FlashStep = 0.35f; private const int Flashes = 2; internal static void Tick() { if (Input.GetKeyDown((KeyCode)103) && !GameInput.IsTyping) { Player local = Player.Local; LandVehicle val = (((Object)(object)local != (Object)null && (Object)(object)local.CurrentVehicle != (Object)null) ? ((Component)local.CurrentVehicle).GetComponent<LandVehicle>() : null); if (!((Object)(object)val == (Object)null) && val.LocalPlayerIsDriver && (!((Object)(object)val.State != (Object)null) || val.State.IsAcceptingInput)) { Request(val, flash: false); } } } internal static void Request(LandVehicle lv, bool flash) { if ((Object)(object)lv == (Object)null) { Core.Log.Warning("[Horn] honk asked for a vehicle that is gone."); return; } Player local = Player.Local; if ((Object)(object)local == (Object)null) { Core.Log.Warning("[Horn] honk asked before the local player exists."); return; } ChassisNet.ToHost("req.honk", Ids.Of((Component)(object)lv).ToString(CultureInfo.InvariantCulture), Ids.Of((Component)(object)local).ToString(CultureInfo.InvariantCulture), flash ? "1" : "0"); } internal static void HostHonk(int vehicle, int player, bool flash) { LandVehicle val = Registry.ById(vehicle); if ((Object)(object)val == (Object)null) { Core.Log.Warning("[Horn] honk for net " + vehicle + ", which is not here - dropped."); return; } Player val2 = Ids.PlayerById(player); if ((Object)(object)val2 == (Object)null) { Core.Log.Warning("[Horn] honk from player net " + player + ", who is not here - dropped."); } else if (!Inside(val, val2) && !Fleet.IsOwner(val, val2.PlayerCode)) { Core.Log.Msg("[Horn] " + val2.PlayerName + " neither sits in nor owns " + Registry.Name(val) + " - no honk."); } else { ChassisNet.ToAll("honk", vehicle.ToString(CultureInfo.InvariantCulture)); if (flash) { Flash(val); } } } private static bool Inside(LandVehicle lv, Player p) { Il2CppReferenceArray<VehicleSeat> seats = lv.Seats; if (seats == null) { return false; } foreach (VehicleSeat item in (Il2CppArrayBase<VehicleSeat>)(object)seats) { if ((Object)(object)item != (Object)null && (Object)(object)item.Occupant == (Object)(object)p) { return true; } } return false; } internal static void OnHonk(int vehicle) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) LandVehicle val = Registry.ById(vehicle); if ((Object)(object)val == (Object)null) { Core.Log.Warning("[Horn] honk for net " + vehicle + ", which is not here."); return; } HornProfile hornProfile = ChassisApi.For(val)?.Horn; try { string text = State.Get(val, "horn"); AudioClip val2 = Sounds.Get((!string.IsNullOrEmpty(text)) ? text : hornProfile?.Clip, "chassis.horn"); Sounds.On(val, "ChassisHorn", (Vector3)(((??)hornProfile?.Position) ?? new Vector3(0f, 0.6f, 1.8f))).PlayOneShot(val2, (hornProfile?.Volume ?? 0.8f) * Sounds.Volume()); Instance log = Core.Log; string[] obj = new string[5] { "[Horn] honked ", Registry.Name(val), " at ", null, null }; Vector3 position = ((Component)val).transform.position; obj[3] = ((Vector3)(ref position)).ToString("F0"); obj[4] = "."; log.Msg(string.Concat(obj)); } catch (Exception ex) { Core.Log.Error("[Horn] could not play the horn of " + Registry.Name(val) + ": " + ex); } } internal static void OnChirp(int vehicle, bool locked) { LandVehicle val = Registry.ById(vehicle); if ((Object)(object)val == (Object)null) { Core.Log.Warning("[Horn] lock chirp for net " + vehicle + ", which is not here."); } else if (PlayChirp(val)) { if (locked) { MelonCoroutines.Start(SecondChirp(val)); } Core.Log.Msg("[Horn] " + (locked ? "lock" : "unlock") + " chirp on " + Registry.Name(val) + "."); } } private static IEnumerator SecondChirp(LandVehicle lv) { yield return (object)new WaitForSeconds(0.22f); if ((Object)(object)lv == (Object)null) { Core.Log.Warning("[Horn] second lock chirp: the vehicle is gone."); } else { PlayChirp(lv); } } private static bool PlayChirp(LandVehicle lv) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) HornProfile hornProfile = ChassisApi.For(lv)?.Horn; try { AudioClip val = Sounds.Get(hornProfile?.Chirp, "chassis.chirp"); Sounds.On(lv, "ChassisHorn", (Vector3)(((??)hornProfile?.Position) ?? new Vector3(0f, 0.6f, 1.8f))).PlayOneShot(val, 0.7f * Sounds.Volume()); return true; } catch (Exception ex) { Core.Log.Error("[Horn] could not play the lock chirp of " + Registry.Name(lv) + ": " + ex.Message); return false; } } internal static void Flash(LandVehicle lv) { if ((Object)(object)lv == (Object)null || (Object)(object)lv.lights == (Object)null) { Core.Log.Warning("[Horn] " + Registry.Name(lv) + " has no lights to flash."); } else { MelonCoroutines.Start(FlashRoutine(lv)); } } private static IEnumerator FlashRoutine(LandVehicle lv) { VehicleLights lights = lv.lights; bool was = lights.HeadlightsOn; for (int i = 0; i < 4; i++) { if ((Object)(object)lv == (Object)null || (Object)(object)lights == (Object)null) { Core.Log.Warning("[Horn] the flashing vehicle is gone."); yield break; } bool flag = false; try { lights.HeadlightsOn = i % 2 == 0 != was; } catch (Exception ex) { Core.Log.Error("[Horn] flashing the lights failed: " + ex.Message); flag = true; } if (flag) { yield break; } yield return (object)new WaitForSeconds(0.35f); } if (!((Object)(object)lv == (Object)null) && !((Object)(object)lights == (Object)null)) { lights.HeadlightsOn = was; Core.Log.Msg("[Horn] flashed the headlights of " + Registry.Name(lv) + "."); } } } internal static class Indicators { private sealed class Lamp { internal Renderer R; internal Material[] Off; internal Material[] On; } private sealed class Lamps { internal readonly List<Lamp> Left = new List<Lamp>(); internal readonly List<Lamp> Right = new List<Lamp>(); internal bool Lit; internal int Shown; } internal const KeyCode LeftKey = (KeyCode)276; internal const KeyCode RightKey = (KeyCode)275; internal const int Hazard = 2; private const float Period = 0.66f; private const float InTurnDegrees = 12f; private const float OutOfTurnDegrees = 3f; private static readonly Dictionary<int, int> _sides = new Dictionary<int, int>(); private static readonly Dictionary<int, Lamps> _lamps = new Dictionary<int, Lamps>(); private static readonly Dictionary<string, Material> _glow = new Dictionary<string, Material>(); private static LandVehicle _keyVehicle; private static int _keySide; private static bool _inTurn; private static float _sentAt = -1f; private static bool _relayPhase; private static bool _relayFailed; private static bool _relayLogged; private static bool _noCamera; internal static bool Phase => Time.time % 0.66f < 0.33f; internal static int SideOf(LandVehicle lv) { if ((Object)(object)lv == (Object)null) { return 0; } if (!_sides.TryGetValue(Ids.Of((Component)(object)lv), out var value)) { return 0; } return value; } internal static void Request(LandVehicle lv, int side, bool fromKey) { if ((Object)(object)lv == (Object)null) { Core.Log.Warning("[Indicators] signal asked for a vehicle that is gone."); return; } side = Clean(side); if (fromKey) { _keyVehicle = ((side != 0) ? lv : null); _keySide = side; _inTurn = false; } else if ((Object)(object)_keyVehicle == (Object)(object)lv) { _keyVehicle = null; _keySide = 0; } _sentAt = Time.time; ChassisNet.ToHost("req.blink", Ids.Of((Component)(object)lv).ToString(CultureInfo.InvariantCulture), side.ToString(CultureInfo.InvariantCulture)); } internal static int Clean(int side) { if (side != 2) { return Math.Sign(side); } return 2; } private static string Word(int side) { if (side != 2) { if (side >= 0) { if (side <= 0) { return "off"; } return "right"; } return "left"; } return "hazard"; } internal static void OnBlink(int vehicle, int side) { _sides.TryGetValue(vehicle, out var value); if (side == 0) { _sides.Remove(vehicle); } else { _sides[vehicle] = side; } if (value != side) { LandVehicle val = Registry.ById(vehicle); Core.Log.Msg("[Indicators] " + (((Object)(object)val != (Object)null) ? Registry.Name(val) : ("net " + vehicle)) + " signals " + Word(side) + "."); if ((Object)(object)val != (Object)null) { ChassisApi.RaiseIndicator(val, side); } } } internal static void HostReplay() { foreach (KeyValuePair<int, int> side in _sides) { ChassisNet.ToAll("blink", side.Key.ToString(CultureInfo.InvariantCulture), side.Value.ToString(CultureInfo.InvariantCulture)); } } internal static void Tick() { Keys(); AutoCancel(); Flash(); Relay(); } private static void Relay() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) bool phase = Phase; if (phase == _relayPhase) { return; } _relayPhase = phase; Player local = Player.Local; LandVehicle val = (((Object)(object)local != (Object)null && (Object)(object)local.CurrentVehicle != (Object)null) ? ((Component)local.CurrentVehicle).GetComponent<LandVehicle>() : null); if ((Object)(object)val == (Object)null || SideOf(val) == 0) { return; } Camera main = Camera.main; if ((Object)(object)main == (Object)null) { if (!_noCamera) { Core.Log.Warning("[Indicators] no main camera - the relay does not tick."); } _noCamera = true; return; } _noCamera = false; try { Sounds.InCabin(val, ((Component)main).transform.position + ((Component)val).transform.forward * 0.5f - ((Component)val).transform.up * 0.2f, phase ? "chassis.tick" : "chassis.tock", 0.45f); if (_relayFailed || !_relayLogged) { Core.Log.Msg("[Indicators] relay tick played in " + Registry.Name(val) + "; later ticks are not logged."); } _relayFailed = false; _relayLogged = true; } catch (Exception ex) { if (!_relayFailed) { Core.Log.Error("[Indicators] the relay tick could not play, logged once until one plays: " + ex); } _relayFailed = true; } } private static void Keys() { bool keyDown = Input.GetKeyDown((KeyCode)276); bool keyDown2 = Input.GetKeyDown((KeyCode)275); if ((!keyDown && !keyDown2) || GameInput.IsTyping) { return; } Player local = Player.Local; LandVehicle val = (((Object)(object)local != (Object)null && (Object)(object)local.CurrentVehicle != (Object)null) ? ((Component)local.CurrentVehicle).GetComponent<LandVehicle>() : null); if (!((Object)(object)val == (Object)null) && val.LocalPlayerIsDriver && (!((Object)(object)val.State != (Object)null) || val.State.IsAcceptingInput)) { if (ChassisApi.For(val)?.Lamps == null) { Core.Log.Msg("[Indicators] " + Registry.Name(val) + " has no indicator lamps in its profile - the arrow key does nothing."); return; } int num = ((!keyDown) ? 1 : (-1)); Request(val, (SideOf(val) != num) ? num : 0, fromKey: true); } } private static void AutoCancel() { if ((Object)(object)_keyVehicle == (Object)null || _keySide == 0) { return; } if (SideOf(_keyVehicle) != _keySide) { if (Time.time - _sentAt > 2f) { _keyVehicle = null; _keySide = 0; } return; } float num = _keyVehicle.CurrentSteerAngle * (float)_keySide; if (!_inTurn) { if (num > 12f) { _inTurn = true; } } else if (!(num > 3f)) { Core.Log.Msg("[Indicators] " + Registry.Name(_keyVehicle) + " is out of the turn - signal off."); LandVehicle keyVehicle = _keyVehicle; _keyVehicle = null; _keySide = 0; _inTurn = false; Request(keyVehicle, 0, fromKey: false); } } private static void Flash() { bool phase = Phase; foreach (LandVehicle item in Registry.Profiled) { if ((Object)(object)item == (Object)null) { continue; } ChassisProfile chassisProfile = ChassisApi.For(item); if (chassisProfile?.Lamps != null) { Lamps lamps = LampsOf(item, chassisProfile.Lamps); int num = SideOf(item); bool flag = num != 0 && phase; if (flag != lamps.Lit || num != lamps.Shown) { lamps.Lit = flag; lamps.Shown = num; Paint(lamps.Left, flag && (num < 0 || num == 2)); Paint(lamps.Right, flag && (num == 1 || num == 2)); } } } foreach (KeyValuePair<int, int> side in _sides) { LandVehicle val = Registry.ById(side.Key); if ((Object)(object)val != (Object)null && ChassisApi.For(val)?.Lamps == null) { VanillaLamps.Ensure(val); } } VanillaLamps.Paint(phase); } private static void Paint(List<Lamp> lamps, bool on) { foreach (Lamp lamp in lamps) { if (!((Object)(object)lamp.R == (Object)null)) { try { lamp.R.sharedMaterials = Il2CppReferenceArray<Material>.op_Implicit(on ? lamp.On : lamp.Off); } catch (Exception ex) { Core.Log.Error("[Indicators] could not switch a lamp on " + ((Object)lamp.R).name + ", it stays as it is: " + ex.Message); lamp.R = null; } } } } private static Lamps LampsOf(LandVehicle lv, LampProfile profile) { int instanceID = ((Object)lv).GetInstanceID(); if (_lamps.TryGetValue(instanceID, out var value)) { return value; } value = new Lamps(); Collect(((Component)lv).transform, profile.Left, profile, value.Left); Collect(((Component)lv).transform, profile.Right, profile, value.Right); if (profile.Left.Count + profile.Right.Count == 0) { Core.Log.Msg("[Indicators] " + Registry.Name(lv) + ": no lamps in the profile, the mod draws its own."); } else if (value.Left.Count == 0 && value.Right.Count == 0) { Core.Log.Warning("[Indicators] " + Registry.Name(lv) + ": none of the lamps " + string.Join(", ", profile.Left) + " / " + string.Join(", ", profile.Right) + " found - the signal stays dark."); } else { Core.Log.Msg("[Indicators] " + Registry.Name(lv) + ": " + value.Left.Count + " left and " + value.Right.Count + " right lamp renderer(s)."); } _lamps[instanceID] = value; return value; } private static void Collect(Transform root, List<string> groups, LampProfile profile, List<Lamp> into) { if (groups == null || groups.Count == 0) { return; } HashSet<int> hashSet = new HashSet<int>(); foreach (Transform componentsInChild in ((Component)root).GetComponentsInChildren<Transform>(true)) { bool flag = false; foreach (string group in groups) { if (Frames.InGroup(((Object)componentsInChild).name, group)) { flag = true; break; } } if (!flag) { continue; } foreach (Renderer componentsInChild2 in ((Component)componentsInChild).GetComponentsInChildren<Renderer>(true)) { if (!((Object)(object)componentsInChild2 == (Object)null) && hashSet.Add(((Object)componentsInChild2).GetInstanceID())) { Il2CppReferenceArray<Material> sharedMaterials = componentsInChild2.sharedMaterials; Material[] array = (Material[])(object)new Material[((Il2CppArrayBase<Material>)(object)sharedMaterials).Length]; Material[] array2 = (Material[])(object)new Material[((Il2CppArrayBase<Material>)(object)sharedMaterials).Length]; for (int i = 0; i < ((Il2CppArrayBase<Material>)(object)sharedMaterials).Length; i++) { array[i] = ((Il2CppArrayBase<Material>)(object)sharedMaterials)[i]; array2[i] = (((Object)(object)((Il2CppArrayBase<Material>)(object)sharedMaterials)[i] != (Object)null) ? Glow(((Il2CppArrayBase<Material>)(object)sharedMaterials)[i], profile) : null); } into.Add(new Lamp { R = componentsInChild2, Off = array, On = array2 }); } } } } private unsafe static Material Glow(Material baseMat, LampProfile profile) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_008c: 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_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) string[] obj = new string[5] { ((Object)baseMat).GetInstanceID().ToString(), "|", null, null, null }; Color color = profile.Color; obj[2] = ((object)(*(Color*)(&color))/*cast due to .constrained prefix*/).ToString(); obj[3] = "|"; obj[4] = profile.UseTexture.ToString(); string key = string.Concat(obj); if (_glow.TryGetValue(key, out var value) && (Object)(object)value != (Object)null) { return value; } value = new Material(baseMat) { name = ((Object)baseMat).name + "_ChassisIndicator" }; Material obj2 = value; ((Object)obj2).hideFlags = (HideFlags)(((Object)obj2).hideFlags | 0x20); Texture val = ((profile.UseTexture && baseMat.HasProperty("_BaseMap")) ? baseMat.GetTexture("_BaseMap") : null); if ((Object)(object)val != (Object)null) { value.SetTexture("_EmissionMap", val); } else { value.SetColor("_BaseColor", profile.Color); if (value.HasProperty("_EmissionMap")) { value.SetTexture("_EmissionMap", (Texture)null); } } value.EnableKeyword("_EMISSION"); value.SetColor("_EmissionColor", profile.Color * 4f); value.globalIlluminationFlags = (MaterialGlobalIlluminationFlags)0; _glow[key] = value; return value; } internal static string Describe() { if (_sides.Count == 0) { return "indicators: all off"; } List<string> list = new List<string>(); foreach (KeyValuePair<int, int> side in _sides) { list.Add("net " + side.Key + " " + Word(side.Value)); } return "indicators: " + string.Join(", ", list) + (((Object)(object)_keyVehicle != (Object)null) ? (" (key signal" + (_inTurn ? ", in the turn)" : ")")) : ""); } internal static void Reset() { _sides.Clear(); _lamps.Clear(); VanillaLamps.Reset(); _keyVehicle = null; _keySide = 0; _inTurn = false; } } internal static class KeyHints { private sealed class Entry { internal string Id; internal string Label; internal Func<KeyHintContext, bool> Show; internal Func<KeyCode[]> Keys; internal string[] Buttons; internal bool Last; } private const string ModuleId = "chassis_vehicle_keys"; private static readonly List<Entry> _builtIn = new List<Entry> { new Entry { Id = "chassis.firstperson", Label = "First Person", Keys = () => (KeyCode[])(object)new KeyCode[1] { (KeyCode)118 }, Show = (KeyHintContext c) => !c.Pushing && Occupants.Of(Player.Local) != null }, new Entry { Id = "chassis.horn", Label = "Horn", Keys = () => (KeyCode[])(object)new KeyCode[1] { (KeyCode)103 }, Show = (KeyHintContext c) => c.Driver }, new Entry { Id = "chassis.indicators", Label = "Indicators", Keys = () => (KeyCode[])(object)new KeyCode[2] { (KeyCode)276, (KeyCode)275 }, Show = (KeyHintContext c) => c.Driver && c.Profile?.Lamps != null, Last = true }, new Entry { Id = "chassis.push", Label = "Push", Show = (KeyHintContext c) => c.Pushing, Buttons = new string[1] { "Motion" } }, new Entry { Id = "chassis.letgo", Label = "Let Go", Show = (KeyHintContext c) => c.Pushing, Buttons = new string[1] { "Interact" } } }; private static readonly List<Entry> _mods = new List<Entry>(); private static readonly HashSet<string> _failed = new HashSet<string>(); private static readonly KeyHintContext _context = new KeyHintContext(); private static readonly List<Entry> _shown = new List<Entry>(); private static readonly List<KeyCode[]> _shownKeys = new List<KeyCode[]>(); private static readonly StringBuilder _sig = new StringBuilder(); private static readonly Dictionary<string, InputPromptsData> _cache = new Dictionary<string, InputPromptsData>(); private static readonly Dictionary<string, List<string[]>> _labels = new Dictionary<string, List<string[]>>(); private static string _shownSig; private static bool _loaded; internal static string ArrowFormat = "<b><size=250%>{0}</size></b>"; internal static void Add(string id, string label, Func<KeyHintContext, bool> show, Func<KeyCode[]> keys) { if (string.IsNullOrEmpty(id)) { throw new ArgumentException("a key hint needs an id"); } if (string.IsNullOrEmpty(label)) { throw new ArgumentException("a key hint needs a label"); } _mods.RemoveAll((Entry e) => e.Id == id); _mods.Add(new Entry { Id = id, Label = label, Show = show, Keys = keys }); _failed.Remove(id); Core.Log.Msg("[KeyHints] a mod added '" + label + "' (" + id + ")."); } internal static bool Remove(string id) { bool num = _mods.RemoveAll((Entry e) => e.Id == id) > 0; if (num) { Core.Log.Msg("[KeyHints] a mod removed " + id + "."); } return num; } internal static void Rebuild() { _cache.Clear(); _labels.Clear(); _shownSig = null; } internal static void Reset() { _shownSig = null; _loaded = false; } internal static void Tick() { string text = (Collect() ? Signature() : null); InputPromptsManager val = (Singleton<InputPromptsManager>.InstanceExists ? Singleton<InputPromptsManager>.Instance : null); if (text != null && text == _shownSig && (Object)(object)val != (Object)null && _loaded && Time.frameCount % 30 == 0 && !val.HasActivePrompt("chassis_vehicle_keys")) { Core.Log.Msg("[KeyHints] the game removed the vehicle key hints - showing them again."); _shownSig = null; } else if (text != null && text == _shownSig && _loaded && Time.frameCount % 30 == 0) { ShortLabels(val, text, log: false); } if (text == _shownSig) { return; } _shownSig = text; _loaded = false; if ((Object)(object)val == (Object)null) { Core.Log.Warning("[KeyHints] no InputPromptsManager - the vehicle keys are not shown."); return; } if (val.HasActivePrompt("chassis_vehicle_keys")) { val.UnloadModule("chassis_vehicle_keys"); } if (text == null) { return; } if (text.Length == 0) { Core.Log.Msg("[KeyHints] this seat offers no Chassis keys."); return; } InputPromptsData val2 = Data(text); if (!((Object)(object)val2 == (Object)null)) { val.LoadModule(val2, (EInputPromptPosition)0, (string)null); _loaded = val.HasActivePrompt("chassis_vehicle_keys"); if (_loaded) { Core.Log.Msg("[KeyHints] shown in the key hints: " + text + "."); ShortLabels(val, text, log: true); } else { Core.Log.Warning("[KeyHints] the key hints refused the vehicle keys (" + text + ")."); } } } private static bool Collect() { _shown.Clear(); _shownKeys.Clear(); Player local = Player.Local; if ((Object)(object)local == (Object)null) { return false; } LandVehicle val = null; LandVehicle val2 = (((Object)(object)local.CurrentVehicle == (Object)null) ? ChassisApi.PushedBy(local) : null); bool flag = (Object)(object)val2 != (Object)null; if (flag) { val = val2; } else if ((Object)(object)local.CurrentVehicle != (Object)null) { val = ((Component)local.CurrentVehicle).GetComponent<LandVehicle>(); } if ((Object)(object)val == (Object)null) { return false; } _context.Vehicle = val; _context.Profile = ChassisApi.For(val); _context.Pushing = flag; _context.Driver = !flag && val.LocalPlayerIsDriver; _context.Seat = (flag ? (-1) : SeatOf(val, local)); foreach (Entry item in _builtIn) { if (!item.Last) { Consider(item); } } foreach (Entry mod in _mods) { Consider(mod); } foreach (Entry item2 in _builtIn) { if (item2.Last) { Consider(item2); } } return true; } private static void Consider(Entry e) { if (_failed.Contains(e.Id)) { return; } try { if (e.Show == null || e.Show(_context)) { KeyCode[] array = ((e.Keys != null) ? e.Keys() : null); if (e.Buttons != null || (array != null && array.Length != 0)) { _shown.Add(e); _shownKeys.Add(array); } } } catch (Exception ex) { _failed.Add(e.Id); Core.Log.Error("[KeyHints] the key hint '" + e.Label + "' (" + e.Id + ") failed and is hidden until it is added again: " + ex); } } private static int SeatOf(LandVehicle lv, Player p) { Il2CppReferenceArray<VehicleSeat> seats = lv.Seats; if (seats == null) { return -1; } for (int i = 0; i < ((Il2CppArrayBase<VehicleSeat>)(object)seats).Length; i++) { if ((Object)(object)((Il2CppArrayBase<VehicleSeat>)(object)seats)[i] != (Object)null && (Object)(object)((Il2CppArrayBase<VehicleSeat>)(object)seats)[i].Occupant == (Object)(object)p) { return i; } } return -1; } private static string Signature() { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) _sig.Clear(); for (int i = 0; i < _shown.Count; i++) { if (i > 0) { _sig.Append(", "); } Entry entry = _shown[i]; if (entry.Buttons != null) { string[] buttons = entry.Buttons; foreach (string value in buttons) { _sig.Append(value).Append(' '); } } else { KeyCode[] array = _shownKeys[i]; foreach (KeyCode val in array) { _sig.Append(val).Append(' '); } } _sig.Append(entry.Label); } return _sig.ToString(); } private static InputPromptsData Data(string sig) { //IL_00de: Unknown result type (might be due to invalid IL or missing references) if (_cache.TryGetValue(sig, out var value) && (Object)(object)value != (Object)null) { return value; } InputActionMap map = InputActionSetupExtensions.AddActionMap(Create<InputActionAsset>(), "Chassis"); InputPromptsData val = Create<InputPromptsData>(); val.Id = "chassis_vehicle_keys"; val.Position = (EInputPromptPosition)0; val.Descriptors = new List<InputPromptsDescriptorData>(); List<string[]> list = new List<string[]>(); for (int i = 0; i < _shown.Count; i++) { Entry entry = _shown[i]; List<InputActionReference> val2 = new List<InputActionReference>(); if (entry.Buttons != null) { GameActions(entry, val2); } else { KeyActions(entry, _shownKeys[i], map, i, val2); } if (val2.Count == 0) { Core.Log.Warning("[KeyHints] '" + entry.Label + "' has no key the hints can draw - not shown."); continue; } InputPromptsDescriptorData val3 = Create<InputPromptsDescriptorData>(); val3.DisplayName = entry.Label; val3.DisplayColor = Color.white; val3.Actions = val2; val.Descriptors.Add(val3); list.Add((entry.Buttons == null && val2.Count == _shownKeys[i].Length) ? ShortNames(_shownKeys[i]) : null); } if (val.Descriptors.Count == 0) { Core.Log.Warning("[KeyHints] none of the vehicle keys can be drawn (" + sig + ")."); return null; } _cache[sig] = val; _labels[sig] = list; return val; } private static void KeyActions(Entry e, KeyCode[] keys, InputActionMap map, int index, List<InputActionReference> into) { for (int i = 0; i < keys.Length; i++) { string text = ControlPath(keys[i]); if (text == null) { Core.Log.Warning("[KeyHints] no keyboard path for " + ((object)Unsafe.As<KeyCode, KeyCode>(ref keys[i])/*cast due to .constrained prefix*/).ToString() + " - left out of '" + e.Label + "'."); } else { InputAction val = InputActionSetupExtensions.AddAction(map, "k" + index + "_" + i, (InputActionType)1, text, (string)null, (string)null, (string)null, (string)null); into.Add(InputActionReference.Create(val)); } } } private static void GameActions(Entry e, List<InputActionReference> into) { if (!Singleton<GameInput>.InstanceExists) { Core.Log.Warning("[KeyHints] no GameInput - '" + e.Label + "' is not shown."); return; } GameInput instance = Singleton<GameInput>.Instance; string[] buttons = e.Buttons; InputAction val = default(InputAction); foreach (string text in buttons) { if (!instance.TryGetAction(text, ref val) || val == null) { Core.Log.Warning("[KeyHints] the game has no input action " + text + " - left out of '" + e.Label + "'."); } else { into.Add(InputActionReference.Create(val)); } } } private static string[] ShortNames(KeyCode[] keys) { string[] array = null; for (int i = 0; i < keys.Length; i++) { string text = (((int)keys[i] == 276) ? "←" : (((int)keys[i] == 275) ? "→" : (((int)keys[i] == 273) ? "↑" : (((int)keys[i] == 274) ? "↓" : null)))); if (text != null) { text = string.Format(ArrowFormat, text); if (array == null) { array = new string[keys.Length]; } array[i] = text; } } return array; } private static void ShortLabels(InputPromptsManager prompts, string sig, bool log) { if (!_labels.TryGetValue(sig, out var value)) { return; } bool flag = false; foreach (string[] item in value) { if (item != null) { flag = true; break; } } if (!flag) { return; } try { InputPromptsUI inputPromptsUI = prompts._inputPromptsUI; if ((Object)(object)inputPromptsUI == (Object)null || !inputPromptsUI._activePrompts.ContainsKey("chassis_vehicle_keys")) { if (log) { Core.Log.Warning("[KeyHints] no panel chassis_vehicle_keys in the hint strip - the arrow keys keep the game's labels."); } return; } List<InputPromptsItemUI> activePrompts = inputPromptsUI._activePrompts["chassis_vehicle_keys"]._activePrompts; int num = 0; for (int i = 0; i < activePrompts.Count && i < value.Count; i++) { if (value[i] == null) { continue; } List<PromptImage> promptImages = activePrompts[i]._promptImages; for (int j = 0; j < value[i].Length && j < promptImages.Count; j++) { if (value[i][j] != null && !((Object)(object)promptImages[j].Label == (Object)null) && !(((TMP_Text)promptImages[j].Label).text == value[i][j])) { ((TMP_Text)promptImages[j].Label).text = value[i][j]; ((Component)promptImages[j].Label).gameObject.SetActive(true); num++; } } } if (log || num > 0) { Core.Log.Msg("[KeyHints] " + num + " key picture label(s) shortened."); } } catch (Exception ex) { Core.Log.Error("[KeyHints] could not shorten the arrow key labels, the game's stay: " + ex.Message); } } private static T Create<T>() where T : ScriptableObject { T val = ((Il2CppObjectBase)ScriptableObject.CreateInstance(Il2CppType.Of<T>())).Cast<T>(); ((Object)(object)val).hideFlags = (HideFlags)32; return val; } internal unsafe static string ControlPath(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Invalid comparison between Unknown and I4 //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Invalid comparison between Unknown and I4 //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Invalid comparison between Unknown and I4 //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Invalid comparison between Unknown and I4 //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected I4, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Invalid comparison between Unknown and I4 //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Invalid comparison between Unknown and I4 //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Expected I4, but got Unknown //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Invalid comparison between Unknown and I4 //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Invalid comparison between Unknown and I4 //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Expected I4, but got Unknown //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected I4, but got Unknown //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Invalid comparison between Unknown and I4 if ((int)key >= 97 && (int)key <= 122) { string text = ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(); Keyboard current = Keyboard.current; KeyControl val = ((current != null) ? current.FindKeyOnCurrentKeyboardLayout(text) : null); if (val != null) { return "<Keyboard>/" + ((InputControl)val).name; } Core.Log.Warning("[KeyHints] no key on the current keyboard layout types '" + text + "' - the hint uses the US position."); return "<Keyboard>/" + text; } if ((int)key >= 48 && (int)key <= 57) { return "<Keyboard>/" + (key - 48); } if ((int)key >= 282 && (int)key <= 293) { return "<Keyboard>/" + ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(); } if ((int)key <= 13) { if ((int)key == 8) { return "<Keyboard>/backspace"; } if ((int)key == 9) { return "<Keyboard>/tab"; } if ((int)key == 13) { return "<Keyboard>/enter"; } } else if ((int)key <= 276) { if ((int)key == 32) { return "<Keyboard>/space"; } switch (key - 273) { case 3: return "<Keyboard>/leftArrow"; case 2: return "<Keyboard>/rightArrow"; case 0: return "<Keyboard>/upArrow"; case 1: return "<Keyboard>/downArrow"; } } else { switch (key - 301) { default: switch (key - 323) { case 0: return "<Mouse>/leftButton"; case 1: return "<Mouse>/rightButton"; case 2: return "<Mouse>/middleButton"; } break; case 0: return "<Keyboard>/capsLock"; case 3: return "<Keyboard>/leftShift"; case 2: return "<Keyboard>/rightShift"; case 5: return "<Keyboard>/leftCtrl"; case 4: return "<Keyboard>/rightCtrl"; case 7: return "<Keyboard>/leftAlt"; case 6: return "<Keyboard>/rightAlt"; case 1: break; } } return null; } internal static string Describe() { return "key hints: " + ((_shownSig == null) ? "not in a vehicle" : ((_shownSig.Length == 0) ? "none" : _shownSig)) + (_loaded ? "" : " (not shown)") + ", " + _mods.Count + " from mods"; } } internal static class Parts { private sealed class Pivot { internal PartProfile P; internal Transform T; internal Quaternion Rest; internal Vector3 Axis; internal float Dir = 1f; internal float Open; internal float Phase; internal float Turned; internal bool TurnedSet; internal bool Logged; } private sealed class Rig { internal LandVehicle V; internal ChassisProfile Profile; internal Transform Frame; internal Pivot Steering; internal readonly List<Pivot> Lids = new List<Pivot>(); internal readonly List<Pivot> Wipers = new List<Pivot>(); internal bool Wiping; internal float Rain; internal float NextRain; } private const float PoseRange = 80f; private const float RainPollSeconds = 1f; private const float RunningKmh = 2f; private const float WheelSpeed = 200f; internal static bool? RainOverride; private static readonly Dictionary<int, Rig> _rigs = new Dictionary<int, Rig>(); private static readonly Dictionary<int, HashSet<string>> _open = new Dictionary<int, HashSet<string>>(); private static readonly Dictionary<int, bool> _storageFront = new Dictionary<int, bool>(); private static bool _rainBroken; internal static bool Wheel(LandVehicle lv, out Transform pivot, out Vector3 axis, out float angle) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) pivot = null; axis = Vector3.forward; angle = 0f; Rig rig = RigOf(lv); if (rig?.Steering == null || (Object)(object)rig.Steering.T == (Object)null) { return false; } Pivot steering = rig.Steering; pivot = steering.T; axis = (((Object)(object)steering.T.parent != (Object)null) ? (steering.T.parent.rotation * (steering.Rest * steering.Axis)) : (steering.Rest * steering.Axis)); angle = (steering.TurnedSet ? steering.Turned : SteerAngle(lv, rig.Profile.Steering)) * steering.Dir; return true; } private static float Turn(Rig rig, LandVehicle lv, float dt) { Pivot steering = rig.Steering; float num = SteerAngle(lv, rig.Profile.Steering); steering.Turned = (steering.TurnedSet ? Mathf.MoveTowards(steering.Turned, num, 200f * dt) : num); steering.TurnedSet = true; return steering.Turned; } private static float SteerAngle(LandVehicle lv, SteeringProfile s) { return Mathf.Clamp(lv.CurrentSteerAngle * s.Ratio, 0f - s.Max, s.Max); } internal static void Request(LandVehicle lv, string part, bool open) { if ((Object)(object)lv == (Object)null) { Core.Log.Warning("[Parts] '" + part + "' asked for a vehicle that is gone."); } else if (ChassisApi.For(lv)?.Part(part) == null) { Core.Log.Warning("[Parts] " + Registry.Name(lv) + " has no part '" + part + "' in its profile."); } else { ChassisNet.ToHost("req.part", Ids.Of((Component)(object)lv).ToString(CultureInfo.InvariantCulture), part, open ? "1" : "0"); } } internal static void RequestStorageEnd(LandVehicle lv, bool front) { if ((Object)(object)lv == (Object)null) { Core.Log.Warning("[Parts] storage end asked for a vehicle that is gone."); return; } ChassisNet.ToHost("req.side", Ids.Of((Component)(object)lv).ToString(CultureInfo.InvariantCulture), front ? "1" : "0"); } internal static bool IsOpen(LandVehicle lv, string part) { if ((Object)(object)lv != (Object)null && _open.TryGetValue(Ids.Of((Component)(object)lv), out var value)) { return value.Contains(part.ToLowerInvariant()); } return false; } internal static void OnPart(int vehicle, string part, bool open) { if (!_open.TryGetValue(vehicle, out var value)) { value = (_open[vehicle] = new HashSet<string>()); } if (open ? value.Add(part.ToLowerInvariant()) : value.Remove(part.ToLowerInvariant())) { Core.Log.Msg("[Parts] net " + vehicle + ": " + part + (open ? " opened" : " closed") + " on request."); } } internal static void OnStorageEnd(int vehicle, bool front) { _storageFront.TryGetValue(vehicle, out var value); _storageFront[vehicle] = front; if (value != front) { Core.Log.Msg("[Parts] net " + vehicle + ": storage opened at the " + (front ? "front" : "rear") + "."); } } internal static void HostReplay() { foreach (KeyValuePair<int, HashSet<string>> item in _open) { foreach (string item2 in item.Value) { ChassisNet.ToAll("part", item.Key.ToString(CultureInfo.InvariantCulture), item2, "1"); } } foreach (KeyValuePair<int, bool> item3 in _storageFront) { if (item3.Value) { ChassisNet.ToAll("side", item3.Key.ToString(CultureInfo.InvariantCulture), "1"); } } } internal static void LateTick() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) if (Registry.Profiled.Count == 0) { return; } float time = Time.time; float deltaTime = Time.deltaTime; Camera main = Camera.main; Vector3 val = (((Object)(object)main != (Object)null) ? ((Component)main).transform.position : Vector3.zero); foreach (LandVehicle item in Registry.Profiled) { if ((Object)(object)item == (Object)null) { continue; } Rig rig = RigOf(item); if (rig == null) { continue; } MoveLids(rig, deltaTime); if ((Object)(object)main != (Object)null) { Vector3 val2 = ((Component)item).transform.position - val; if (((Vector3)(ref val2)).sqrMagnitude > 6400f) { continue; } } if (rig.Steering != null && (Object)(object)rig.Steering.T != (Object)null) { Pose(rig.Steering, Turn(rig, item, deltaTime)); } Wipe(rig, time, deltaTime); } } internal static void Forget(LandVehicle lv) { if (!((Object)(object)lv == (Object)null) && _rigs.TryGetValue(((Object)lv).GetInstanceID(), out var value)) { if (value != null) { Restore(value); } _rigs.Remove(((Object)lv).GetInstanceID()); } } private static Rig RigOf(LandVehicle lv) { if ((Object)(object)lv == (Object)null) { return null; } ChassisProfile chassisProfile = ChassisApi.For(lv); int instanceID = ((Object)lv).GetInstanceID(); if (_rigs.TryGetValue(instanceID, out var value) && value != null && value.Profile == chassisProfile && (Object)(object)value.Frame != (Object)null) { return value; } if (value != null) { Restore(value); } if (chassisProfile == null) { _rigs.Remove(instanceID); return null; } value = Build(lv, chassisProfile); _rigs[instanceID] = value; return value; } private static Rig Build(LandVehicle lv, ChassisProfile profile) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) Rig rig = new Rig { V = lv, Profile = profile, Frame = Frames.Resolve(((Component)lv).transform, profile.Frame) }; if ((Object)(object)rig.Frame == (Object)null) { Core.Log.Warning("[Parts] " + Registry.Name(lv) + ": frame '" + profile.Frame + "' not found - nothing moves on it yet."); return rig; } List<string> list = new List<string>(); if (profile.Steering != null && !string.IsNullOrEmpty(profile.Steering.Path)) { Pivot pivot = MakePivot(rig, profile.Steering.Path, profile.Steering.Axis); if (pivot == null) { list.Add("steering wheel '" + profile.Steering.Path + "'"); } else { pivot.Dir = ((profile.Steering.Sign != 0f) ? Mathf.Sign(profile.Steering.Sign) : TopRight(rig, pivot)); rig.Steering = pivot; } } foreach (PartProfile part in profile.Parts) { if (part == null || string.IsNullOrEmpty(part.Path)) { continue; } Pivot pivot2 = MakePivot(rig, part.Path, part.Axis); if (pivot2 == null) { list.Add(part.Kind + " '" + part.Path + "'"); continue; } pivot2.P = part; pivot2.Dir = ((part.Sign != 0f) ? Mathf.Sign(part.Sign) : Lifts(rig, pivot2)); if (part.Kind == "wiper") { rig.Wipers.Add(pivot2); } else { rig.Lids.Add(pivot2); } } Core.Log.Msg("[Parts] " + Registry.Name(lv) + " (" + profile.Code + "): " + ((rig.Steering != null) ? ("steering wheel turning " + ((rig.Steering.Dir > 0f) ? "+" : "-")) : "no steering wheel") + ", " + rig.Lids.Count + " lid(s), " + rig.Wipers.Count + " wiper(s)" + ((list.Count > 0) ? ("; not in the model: " + string.Join(", ", list)) : "") + "."); return rig; } private static Pivot MakePivot(Rig rig, string path, Vector3 axis) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) Transform val = Frames.Resolve(rig.Frame, path); if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)rig.Frame) { return null; } return new Pivot { T = val, Rest = val.localRotation, Axis = ((((Vector3)(ref axis)).sqrMagnitude > 0f) ? ((Vector3)(ref axis)).normalized : Vector3.forward) }; } private static Vector3 AxisWorld(Pivot p) { //IL_0030: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)p.T.parent != (Object)null)) { return p.Rest * p.Axis; } return p.T.parent.rotation * (p.Rest * p.Axis); } private static float TopRight(Rig rig, Pivot p) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_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_0026: 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_002c: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) Vector3 val = AxisWorld(p); Vector3 val2 = Vector3.ProjectOnPlane(rig.Frame.up, val); Vector3 normalized = ((Vector3)(ref val2)).normalized; if (!(Vector3.Dot(Quaternion.AngleAxis(10f, val) * normalized - normalized, rig.Frame.right) >= 0f)) { return -1f; } return 1f; } private static float Lifts(Rig rig, Pivot p) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_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_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_0044: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.zero; int num = 0; foreach (Renderer componentsInChild in ((Component)p.T).GetComponentsInChildren<Renderer>(true)) { if (!((Object)(object)componentsInChild == (Object)null)) { Vector3 val2 = val; Bounds bounds = componentsInChild.bounds; val = val2 + ((Bounds)(ref bounds)).center; num++; } } if (num == 0) { return 1f; } Vector3 val3 = val / (float)num - p.T.position; if (!(Vector3.Dot(Quaternion.AngleAxis(10f, AxisWorld(p)) * val3 - val3, rig.Frame.up) >= 0f)) { return -1f; } return 1f; } private static void Pose(Pivot p, float degrees) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) p.T.localRotation = p.Rest * Quaternion.AngleAxis(degrees * p.Dir, p.Axis); } private static void MoveLids(Rig rig, float dt) { if (rig.Lids.Count == 0) { return; } int key = Ids.Of((Component)(object)rig.V); _open.TryGetValue(key, out var value); bool flag = false; try { flag = (Object)(object)rig.V.Storage != (Object)null && rig.V.Storage.IsOpened; } catch (Exception ex) { Core.Log.Warning("[Parts] could not read the storage of " + Registry.Name(rig.V) + ": " + ex.Message); } _storageFront.TryGetValue(key, out var value2); foreach (Pivot lid in rig.Lids) { bool flag2 = (value != null && value.Contains(lid.P.Name.ToLowerInvariant())) || (flag && ((lid.P.Storage == "front" && value2) || (lid.P.Storage == "rear" && !value2))); if (flag2 != lid.Logged) { lid.Logged = flag2; Core.Log.Msg("[Parts] " + Registry.Name(rig.V) + " " + lid.P.Name + (flag2 ? " opens" : " closes") + "."); } lid.Open = Mathf.MoveTowards(lid.Open, flag2 ? 1f : 0f, dt / Mathf.Max(0.05f, lid.P.Seconds)); float num = lid.Open * lid.Open * (3f - 2f * lid.Open); Pose(lid, lid.P.Angle * num); } } private static void Wipe(Rig rig, float now, float dt) { if (rig.Wipers.Count == 0) { return; } if (now >= rig.NextRain) { rig.NextRain = now + 1f; float num = RainAt(rig.V); bool flag = rig.V.IsOccupied || Mathf.Abs(rig.V.Speed_Kmh) > 2f; bool flag2 = num > 0f && !rig.V.IsUnderCover; bool flag3 = flag2 && flag; if (flag3 != rig.Wiping) { Core.Log.Msg("[Parts] " + Registry.Name(rig.V) + " wipers " + (flag3 ? ("on (rain " + num.ToString("F2") + ")") : ("off (" + (flag2 ? "not running" : (rig.V.IsUnderCover ? "under cover" : "no rain")) + ")")) + "."); } rig.Wiping = flag3; rig.Rain = num; } foreach (Pivot wiper in rig.Wipers) { if (rig.Wiping || wiper.Phase != 0f) { float num2 = Mathf.Lerp(wiper.P.Seconds, wiper.P.FastSeconds, Mathf.Clamp01(rig.Rain)); wiper.Phase += dt / Mathf.Max(0.1f, num2); if (wiper.Phase >= 1f) { wiper.Phase = (rig.Wiping ? (wiper.Phase - 1f) : 0f); } Pose(wiper, wiper.P.Angle * (0.5f - 0.5f * Mathf.Cos(wiper.Phase * 2f * (float)Math.PI))); } } } private static float RainAt(LandVehicle lv) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (RainOverride.HasValue) { if (!RainOverride.Value) { return 0f; } return 1f; } if (_rainBroken) { return 0f; } try { if (!NetworkSingleton<EnvironmentManager>.InstanceExists) { return 0f; } EnvironmentManager instance = NetworkSingleton<EnvironmentManager>.Instance; if (!instance.HasActiveWeatherVolumes()) { return 0f; } WeatherProfile weatherProfileFromPosition = instance.GetWeatherProfileFromPosition(((Component)lv).transform.position); return ((Object)(object)weatherProfileFromPosition != (Object)null && weatherProfileFromPosition.Conditions != null) ? weatherProfileFromPosition.Conditions.Rainy : 0f; } catch (Exception ex) { _rainBroken = true; Core.Log.Error("[Parts] reading the weather failed, the wipers stay still from now on: " + ex.Message); return 0f; } } private static void Restore(Rig rig) { //IL_002b: 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_00ae: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)rig.Steering?.T != (Object)null) { rig.Steering.T.localRotation = rig.Steering.Rest; } foreach (Pivot lid in rig.Lids) { if ((Object)(object)lid.T != (Object)null) { lid.T.localRotation = lid.Rest; } } foreach (Pivot wiper in rig.Wipers) { if ((Object)(object)wiper.T != (Object)null) { wiper.T.localRotation = wiper.Rest; } } } internal static string Describe(LandVehicle lv) { Rig rig = RigOf(lv); if (rig == null) { return "parts: " + Registry.Name(lv) + " has no profile"; } StringBuilder stringBuilder = new StringBuilder("parts of " + Registry.Name(lv) + ": steer " + lv.CurrentSteerAngle.ToString("F1")); if (rig.Steering != null) { stringBuilder.Append(" wheel ").Append(Rot(rig.Steering.T)).Append(" dir ") .Append(rig.Steering.Dir); } foreach (Pivot lid in rig.Lids) { stringBuilder.Append(" | ").Append(lid.P.Name).Append(' ') .Append(lid.Open.ToString("F2")) .Append(' ') .Append(Rot(lid.T)); } stringBuilder.Append(" | wipers ").Append(rig.Wiping ? "on" : "off"); foreach (Pivot wiper in rig.Wipers) { stringBuilder.Append(' ').Append(wiper.P.Name).Append(' ') .Append(Rot(wiper.T)); } return stringBuilder.ToString(); static string Rot(Transform t) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)t == (Object)null)) { Vector3 localEulerAngles = t.localEulerAngles; return ((Vector3)(ref localEulerAngles)).ToString("F0"); } return "-"; } } internal static void Reset() { _rigs.Clear(); _open.Clear(); _storageFront.Clear(); _rainBroken = false; RainOverride = null; } } internal static class Registry { private const float RefreshSeconds = 1f; private static readonly List<LandVehicle> _all = new List<LandVehicle>(); private static readonly List<LandVehicle> _profiled = new List<LandVehicle>(); private static readonly HashSet<int> _alive = new HashSet<int>(); private static readonly List<LandVehicle> _unlisted = new List<LandVehicle>(); private static float _next; private static bool _warned; internal static List<LandVehicle> All => _all; internal static List<LandVehicle> Profiled => _profiled; internal static void Touch() { _next = 0f; } internal static void Tick() { float unscaledTime = Time.unscaledTime; if (unscaledTime < _next) { return; } _next = unscaledTime + 1f; _all.Clear(); _profiled.Clear(); if (!NetworkSingleton<VehicleManager>.InstanceExists) { if (!_warned) { Core.Log.Warning("[Vehicles] no vehicle manager yet - horn, indicators and parts wait."); } _warned = true; return; } _warned = false; List<LandVehicle> allVehicles = NetworkSingleton<VehicleManager>.Instance.AllVehicles; _alive.Clear(); for (int i = 0; i < allVehicles.Count; i++) { LandVehicle val = allVehicles[i]; if (!((Object)(object)val == (Object)null)) { _all.Add(val); _alive.Add(((Object)val).GetInstanceID()); if (ChassisApi.For(val) != null) { _profiled.Add(val); } } } _unlisted.Clear(); ChassisApi.PruneAssignments(_alive, _unlisted); foreach (LandVehicle item in _unlisted) { _all.Add(item); if (ChassisApi.For(item) != null) { _profiled.Add(item); } } } internal static LandVehicle ById(int id) { if (id < 0) { return null; } foreach (LandVehicle item in _all) { if ((Object)(object)item != (Object)null && Ids.Of((Component)(object)item) == id) { return item; } } return Ids.Find<LandVehicle>(id); } internal static string Name(LandVehicle v) { if (!((Object)(object)v == (Object)null)) { return v.vehicleCode + " net " + Ids.Of((Component)(object)v); } return "a vehicle that is gone"; } internal static void Reset() { _all.Clear(); _profiled.Clear(); _next = 0f; _warned = false; } } [HarmonyPatch(typeof(SaveManager), "Save", new Type[] { typeof(string) })] internal static class SaveFiles { private static void Prefix() { if (!ChassisNet.IsHost) { return; } try { Fleet.EnsureLoaded(); Store.EnsureLoaded(); State.EnsureLoaded(); } catch (Exception ex) { Core.Log.Error("[Save] reading Chassis' files before the save failed: " + ex); } } private static void Postfix(SaveManager __instance) { if (!ChassisNet.IsHost) { return; } try { List<string> approvedBaseLevelPaths = __instance.ApprovedBaseLevelPaths; if (approvedBaseLevelPaths == null) { Core.Log.Warning("[Save] the game has no list of kept files - Chassis' files may be deleted at the end of the save."); } else { if (!approvedBaseLevelPaths.Contains("Chassis.vehicles")) { approvedBaseLevelPaths.Add("Chassis.vehicles"); } if (!approvedBaseLevelPaths.Contains("Chassis.instruments")) { approvedBaseLevelPaths.Add("Chassis.instruments"); } if (!approvedBaseLevelPaths.Contains("Chassis.tuning")) { approvedBaseLevelPaths.Add("Chassis.tuning"); } } } catch (Exception ex) { Core.Log.Error("[Save] could not mark Chassis' files as kept: " + ex); } try { Fleet.SaveWithGame(); } catch (Exception ex2) { Core.Log.Error("[Save] saving the owner records failed: " + ex2); } try { Store.Save(); } catch (Exception ex3) { Core.Log.Error("[Save] saving the cockpits failed: " + ex3); } try { State.Save(); } catch (Exception ex4) { Core.Log.Error("[Save] saving the tuning failed: " + ex4); } } } internal static class Sounds { internal const string Horn = "chassis.horn"; internal const string Chirp = "chassis.chirp"; internal const string Click = "chassis.click"; internal const string Tick = "chassis.tick"; internal const string Tock = "chassis.tock"; private const int Rate = 44100; private static readonly Dictionary<string, AudioClip> _clips = new Dictionary<string, AudioClip>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> _missing = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static AudioMixerGroup _group; private static bool _noManager; private const string CabinName = "ChassisCabin"; internal static void Register(string name, AudioClip clip) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(name)) { throw new ArgumentException("a sound needs a name"); } if ((Object)(object)clip == (Object)null) { throw new ArgumentNullException("clip"); } ((Object)clip).hideFlags = (HideFlags)(((Object)clip).hideFlags | 0x20); _clips[name] = clip; _missing.Remove(name); Core.Log.Msg("[Sound] '" + name + "' registered, " + clip.length.ToString("F2") + " s."); } internal static bool Has(string name) { if (!string.IsNullOrEmpty(name) && _clips.TryGetValue(name, out var value)) { return (Object)(object)value != (Object)null; } return false; } internal static AudioClip Get(string name, string fallback) { if (!string.IsNullOrEmpty(name)) { if (_clips.TryGetValue(name, out var value) && (Object)(object)value != (Object)null) { return value; } if (_missing.Add(name)) { Core.Log.Warning("[Sound] '" + name + "' is not registered, the built-in " + fallback + " plays instead."); } } if (_clips.TryGetValue(fallback, out var value2) && (Object)(object)value2 != (Object)null) { return value2; } value2 = (AudioClip)(fallback switch { "chassis.chirp" => Synth("chassis.chirp", 0.09f, new float[1] { 1760f }, 3), "chassis.click" => Knock("chassis.click", 0.035f, 2900f, 0.55f, 1), "chassis.tick" => Knock("chassis.tick", 0.025f, 2200f, 0.35f, 2), "chassis.tock" => Knock("chassis.tock", 0.025f, 1500f, 0.35f, 3), _ => Synth("chassis.horn", 0.55f, new float[2] { 415f, 494f }, 7), }); _clips[fallback] = value2; return value2; } private static AudioClip Knock(string name, float length, float tone, float noise, int seed) { int num = Mathf.RoundToInt(length * 44100f); float[] array = new float[num]; Random random = new Random(seed); float num2 = (float)Math.PI * 2f; for (int i = 0; i < num; i++) { float num3 = (float)i / 44100f; float num4 = Mathf.Exp((0f - num3) / (length * 0.22f)); float num5 = Mathf.Exp((0f - num3) / 0.002f); float num6 = (float)(random.NextDouble() * 2.0 - 1.0); array[i] = 0.5f * (num4 * Mathf.Sin(num2 * tone * num3) + noise * num5 * num6); } AudioClip val = AudioClip.Create(name, num, 1, 44100, false); val.SetData(Il2CppStructArray<float>.op_Implicit(array), 0); ((Object)val).hideFlags = (HideFlags)32; Core.Log.Msg("[Sound] built-in '" + name + "' made, " + length.ToString("F3") + " s."); return val; } internal static void InCabin(LandVehicle v, Vector3 world, string clip, float level) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) Transform val = ((Component)v).transform.Find("ChassisCabin"); AudioSource val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<AudioSource>() : null); if ((Object)(object)val2 == (Object)null) { val2 = On(v, "ChassisCabin", Vector3.zero); val2.minDistance = 0.6f; val2.maxDistance = 7f; } ((Component)val2).transform.position = world; val2.PlayOneShot(Get(null, clip), level * Volume()); } private static AudioClip Synth(string name, float length, float[] tones, int harmonics) { int num = Mathf.RoundToInt(length * 44100f); float[] array = new float[num]; float num2 = (float)Math.PI * 2f; for (int i = 0; i < num; i++) { float num3 = (float)i / 44100f; float num4 = Mathf.Clamp01(num3 / 0.01f) * Mathf.Clamp01((length - num3) / Mathf.Min(0.06f, length * 0.4f)); float num5 = 0f; foreach (float num6 in tones) { for (int k = 1; k <= harmonics; k += 2) { num5 += Mathf.Sin(num2 * num6 * (float)k * num3) / (float)k; } } array[i] = 0.22f * num4 * num5; } AudioClip val = AudioClip.Create(name, num, 1, 44100, false); val.SetData(Il2CppStructArray<float>.op_Implicit(array), 0); ((Object)val).hideFlags = (HideFlags)32; Core.Log.Msg("[Sound] built-in '" + name + "' made, " + length.ToString("F2") + " s."); return val; } internal static float Volume() { try { if (Singleton<AudioManager>.InstanceExists) { _noManager = false; return Mathf.Clamp01(Singleton<AudioManager>.Instance.GetVolume((EAudioType)2, true)); } } catch (Exception ex) { if (!_noManager) { Core.Log.Warning("[Sound] could not read the game's volume: " + ex.Message); } _noManager = true; return 1f; } if (!_noManager) { Core.Log.Warning("[Sound] no audio manager - vehicle sounds play at full volume."); } _noManager = true; return 1f; } internal static AudioSource On(LandVehicle v, string name, Vector3 local) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) Transform val = ((Component)v).transform.Find(name); if ((Object)(object)val != (Object)null) { AudioSource component = ((Component)val).GetComponent<AudioSource>(); if ((Object)(object)component != (Object)null) { return component; } } object obj = (((Object)(object)val != (Object)null) ? ((object)((Component)val).gameObject) : ((object)new GameObject(name))); ((GameObject)obj).transform.SetParent(((Component)v).transform, false); ((GameObject)obj).transform.localPosition = local; AudioSource obj2 = ((GameObject)obj).AddComponent<AudioSource>(); obj2.spatialBlend = 1f; obj2.minDistance = 6f; obj2.maxDistance = 120f; obj2.rolloffMode = (AudioRolloffMode)1; obj2.playOnAwake = false; obj2.dopplerLevel = 0f; obj2.outputAudioMixerGroup = Group(); return obj2; } private static AudioMixerGroup Group() { if ((Object)(object)_group != (Object)null) { return _group; } try { if (Singleton<AudioManager>.InstanceExists) { _group = Singleton<AudioManager>.Instance.MainGameMixer; } if ((Object)(object)_group == (Object)null) { Core.Log.Warning("[Sound] no game mixer group yet - this sound skips the mixer."); } } catch (Exception ex) { Core.Log.Warning("[Sound] could not read the game mixer group: " + ex.Message); } return _group; } } internal static class VanillaLamps { private sealed class Set { internal LandVehicle V; internal readonly List<GameObject> Left = new List<GameObject>(); internal readonly List<GameObject> Right = new List<GameObject>(); internal bool Lit; internal int Shown; } private sealed class Source { internal MeshRenderer R; internal int Sub; internal int End; internal bool Brake; internal List<Bounds> Skip; internal string What; } private sealed class Half { internal Mesh Left; internal Mesh Right; internal bool Front; internal bool Rear; } private const float Lift = 0.004f; private const float CentreGap = 0.05f; private const float SameEdge = 0.03f; private static readonly Color Amber = new Color(1f, 0.42f, 0f); private static readonly Color Red = new Color(1f, 0.04f, 0.02f); private static readonly Dictionary<int, Set> _sets = new Dictionary<int, Set>(); private static readonly Dictionary<string, Half> _meshes = new Dictionary<string, Half>(); private static Material _amber; private static Material _red; internal static void Ensure(LandVehicle lv) { if ((Object)(object)lv == (Object)null) { return; } int instanceID = ((Object)lv).GetInstanceID(); if (_sets.ContainsKey(instanceID)) { return; } Set set = new Set { V = lv }; _sets[instanceID] = set; try { Build(set); } catch (Exception ex) { Core.Log.Error("[Indicators] " + Registry.Name(lv) + ": building the turn signal lamps failed, the signal stays dark outside: " + ex); } } internal static void Paint(bool phase) { List<int> list = null; foreach (KeyValuePair<int, Set> set in _sets) { Set value = set.Value; if ((Object)(object)value.V == (Object)null) { (list ?? (list = new List<int>())).Add(set.Key); continue; } int num = Indicators.SideOf(value.V); bool flag = num != 0 && phase