Decompiled source of Chassis v1.3.0

Chassis.dll

Decompiled 4 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.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