Decompiled source of MoreCustomisations v0.5.0

plugins/MoreCustomisations.dll

Decompiled 5 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.Mono;
using Effects;
using Game;
using Game.UnlockableContent;
using HarmonyLib;
using Menus.UI;
using Microsoft.CodeAnalysis;
using Player;
using Sounds.SplitScreenAudio;
using TMPro;
using UI.TabsSystem;
using UI.UnlockableContentGrid;
using UI.VectorGraphics;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using Utils;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MoreCustomisations")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0+66c6863e256e719fb60338ff72cca19afbcaaaa4")]
[assembly: AssemblyProduct("MoreCustomisations")]
[assembly: AssemblyTitle("MoreCustomisations")]
[assembly: AssemblyVersion("0.5.0.0")]
[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 MoreCustomisations
{
	internal static class Art
	{
		public static Texture2D Load(string file)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			using Stream stream = typeof(Art).Assembly.GetManifestResourceStream(file);
			if (stream == null)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(39, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Image '");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(file);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' isn't embedded; is it in art/?");
				}
				log.LogWarning(val);
				return null;
			}
			byte[] array = new byte[stream.Length];
			stream.Read(array, 0, array.Length);
			return Decode(array, file);
		}

		public static Texture2D LoadFile(string path)
		{
			if (!File.Exists(path))
			{
				return null;
			}
			return Decode(File.ReadAllBytes(path), Path.GetFileName(path));
		}

		private static Texture2D Decode(byte[] bytes, string name)
		{
			//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_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
			{
				name = name,
				wrapMode = (TextureWrapMode)1
			};
			if (ImageConversion.LoadImage(val, bytes))
			{
				return val;
			}
			ManualLogSource log = Plugin.Log;
			bool flag = default(bool);
			BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(30, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Image '");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(name);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' isn't a readable PNG.");
			}
			log.LogWarning(val2);
			return null;
		}
	}
	internal sealed class CarData
	{
		public string Name;

		public List<CarSlot> Slots;

		public List<CarPiece> Pieces;
	}
	internal sealed class CarSlot
	{
		public string Name;

		public float R;

		public float G;

		public float B;
	}
	internal sealed class CarPiece
	{
		public string Name;

		public List<CarMesh> Meshes;
	}
	internal sealed class CarMesh
	{
		public int Slot;

		public float[] Positions;

		public float[] Normals;

		public int[] Triangles;
	}
	internal static class CarFile
	{
		public static readonly string[] Pieces = new string[9] { "Body", "Hood", "DoorLeft", "DoorRight", "Hatch", "WheelFL", "WheelFR", "WheelBL", "WheelBR" };

		public static readonly string[] Required = new string[5] { "Body", "WheelFL", "WheelFR", "WheelBL", "WheelBR" };

		private const string Magic = "MCCAR";

		private const int Version = 1;

		private const int MaxBytes = 4194304;

		private const int MaxTriangles = 20000;

		private const int MaxVertices = 65535;

		public static byte[] Encode(CarData car)
		{
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8))
			{
				binaryWriter.Write(Encoding.ASCII.GetBytes("MCCAR"));
				binaryWriter.Write(1);
				WriteString(binaryWriter, car.Name);
				binaryWriter.Write(car.Slots.Count);
				foreach (CarSlot slot in car.Slots)
				{
					WriteString(binaryWriter, slot.Name);
					binaryWriter.Write(slot.R);
					binaryWriter.Write(slot.G);
					binaryWriter.Write(slot.B);
				}
				binaryWriter.Write(car.Pieces.Count);
				foreach (CarPiece piece in car.Pieces)
				{
					WriteString(binaryWriter, piece.Name);
					binaryWriter.Write(piece.Meshes.Count);
					foreach (CarMesh mesh in piece.Meshes)
					{
						binaryWriter.Write(mesh.Slot);
						int num = mesh.Positions.Length / 3;
						binaryWriter.Write(num);
						for (int i = 0; i < num; i++)
						{
							for (int j = 0; j < 3; j++)
							{
								binaryWriter.Write(mesh.Positions[i * 3 + j]);
							}
							for (int k = 0; k < 3; k++)
							{
								binaryWriter.Write(mesh.Normals[i * 3 + k]);
							}
						}
						binaryWriter.Write(mesh.Triangles.Length);
						int[] triangles = mesh.Triangles;
						foreach (int value in triangles)
						{
							binaryWriter.Write(value);
						}
					}
				}
			}
			return memoryStream.ToArray();
		}

		public static CarData Decode(byte[] bytes, out string error)
		{
			try
			{
				error = null;
				CarData carData = Read(bytes);
				error = Problem(carData);
				return (error == null) ? carData : null;
			}
			catch (InvalidDataException ex)
			{
				error = ex.Message;
				return null;
			}
			catch (Exception ex2) when (ex2 is EndOfStreamException || ex2 is IOException || ex2 is ArgumentException)
			{
				error = "truncated or malformed (" + ex2.Message + ")";
				return null;
			}
		}

		private static CarData Read(byte[] bytes)
		{
			if (bytes == null || bytes.Length > 4194304)
			{
				throw new InvalidDataException("missing or over 4 MB");
			}
			using BinaryReader binaryReader = new BinaryReader(new MemoryStream(bytes), Encoding.UTF8);
			if (Encoding.ASCII.GetString(binaryReader.ReadBytes("MCCAR".Length)) != "MCCAR")
			{
				throw new InvalidDataException("not a car file");
			}
			int num = binaryReader.ReadInt32();
			if (num != 1)
			{
				throw new InvalidDataException($"version {num}, expected {1}");
			}
			CarData carData = new CarData
			{
				Name = ReadString(binaryReader),
				Slots = new List<CarSlot>(),
				Pieces = new List<CarPiece>()
			};
			int num2 = Count(binaryReader, 1, 16, "slots");
			for (int i = 0; i < num2; i++)
			{
				carData.Slots.Add(new CarSlot
				{
					Name = ReadString(binaryReader),
					R = ReadFloat(binaryReader),
					G = ReadFloat(binaryReader),
					B = ReadFloat(binaryReader)
				});
			}
			int num3 = Count(binaryReader, 1, 16, "pieces");
			int num4 = 0;
			for (int j = 0; j < num3; j++)
			{
				CarPiece carPiece = new CarPiece
				{
					Name = ReadString(binaryReader),
					Meshes = new List<CarMesh>()
				};
				int num5 = Count(binaryReader, 1, 16, "meshes in " + carPiece.Name);
				for (int k = 0; k < num5; k++)
				{
					int num6 = binaryReader.ReadInt32();
					if (num6 < 0 || num6 >= num2)
					{
						throw new InvalidDataException($"{carPiece.Name}: slot {num6} out of range");
					}
					int num7 = Count(binaryReader, 1, 65535, "vertices in " + carPiece.Name);
					CarMesh carMesh = new CarMesh
					{
						Slot = num6,
						Positions = new float[num7 * 3],
						Normals = new float[num7 * 3]
					};
					for (int l = 0; l < num7; l++)
					{
						for (int m = 0; m < 3; m++)
						{
							carMesh.Positions[l * 3 + m] = ReadFloat(binaryReader);
						}
						for (int n = 0; n < 3; n++)
						{
							carMesh.Normals[l * 3 + n] = ReadFloat(binaryReader);
						}
					}
					int num8 = Count(binaryReader, 3, 60000, "indices in " + carPiece.Name);
					if (num8 % 3 != 0)
					{
						throw new InvalidDataException($"{carPiece.Name}: {num8} indices isn't whole triangles");
					}
					num4 += num8 / 3;
					if (num4 > 20000)
					{
						throw new InvalidDataException($"over {20000} triangles");
					}
					carMesh.Triangles = new int[num8];
					for (int num9 = 0; num9 < num8; num9++)
					{
						int num10 = binaryReader.ReadInt32();
						if (num10 < 0 || num10 >= num7)
						{
							throw new InvalidDataException($"{carPiece.Name}: index {num10} out of range");
						}
						carMesh.Triangles[num9] = num10;
					}
					carPiece.Meshes.Add(carMesh);
				}
				carData.Pieces.Add(carPiece);
			}
			if (binaryReader.BaseStream.Position != binaryReader.BaseStream.Length)
			{
				throw new InvalidDataException("bytes left over");
			}
			return carData;
		}

		private static string Problem(CarData car)
		{
			if (string.IsNullOrEmpty(car.Name) || car.Name.Length > 64)
			{
				return "name must be 1 to 64 characters";
			}
			if (car.Slots.Any((CarSlot s) => string.IsNullOrEmpty(s.Name)))
			{
				return "a slot has no name";
			}
			List<string> names = car.Pieces.Select((CarPiece p) => p.Name).ToList();
			string text = names.FirstOrDefault((string n) => !Pieces.Contains(n));
			if (text != null)
			{
				return "unknown piece '" + text + "' (pieces: " + string.Join(", ", Pieces) + ")";
			}
			IGrouping<string, string> grouping = (from n in names
				group n by n).FirstOrDefault((IGrouping<string, string> g) => g.Count() > 1);
			if (grouping != null)
			{
				return "piece '" + grouping.Key + "' appears twice";
			}
			string text2 = Required.FirstOrDefault((string n) => !names.Contains(n));
			if (text2 == null)
			{
				return null;
			}
			return "missing required piece '" + text2 + "'";
		}

		private static int Count(BinaryReader r, int min, int max, string what)
		{
			int num = r.ReadInt32();
			if (num < min || num > max)
			{
				throw new InvalidDataException($"{num} {what}, expected {min} to {max}");
			}
			return num;
		}

		private static float ReadFloat(BinaryReader r)
		{
			float num = r.ReadSingle();
			if (float.IsNaN(num) || float.IsInfinity(num))
			{
				throw new InvalidDataException("non-finite number");
			}
			return num;
		}

		private static string ReadString(BinaryReader r)
		{
			int num = Count(r, 0, 256, "string bytes");
			byte[] array = r.ReadBytes(num);
			if (array.Length != num)
			{
				throw new EndOfStreamException();
			}
			return Encoding.UTF8.GetString(array);
		}

		private static void WriteString(BinaryWriter w, string value)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(value);
			w.Write(bytes.Length);
			w.Write(bytes);
		}
	}
	internal static class CarPatches
	{
		private static readonly Dictionary<UnlockablePlayerCustomizationSelector, PlayerInLobby> Lobbies = new Dictionary<UnlockablePlayerCustomizationSelector, PlayerInLobby>();

		public static CustomTab CreateTab()
		{
			Cars.Choices.Changed += CarView.RefreshAll;
			return new CustomTab("Car", "TabCar.png", Cars.Choices, () => Cars.All, delegate(PlayerData player, string car)
			{
				Cars.Choices.SetLocal(player, car);
			}, delegate(UnlockablePlayerCustomizationSelector selector, PlayerData player, string car)
			{
				Lobby(selector)?.Show(car);
			}, delegate(UnlockablePlayerCustomizationSelector selector, PlayerData player)
			{
				Lobby(selector)?.Refresh();
			});
		}

		private static CarView Lobby(UnlockablePlayerCustomizationSelector selector)
		{
			if (!Lobbies.TryGetValue(selector, out var value) || (Object)(object)value == (Object)null)
			{
				PlayerInLobby[] array = Object.FindObjectsOfType<PlayerInLobby>();
				foreach (PlayerInLobby val in array)
				{
					if ((Object)(object)val.customizationSelector == (Object)(object)selector)
					{
						value = (Lobbies[selector] = val);
					}
				}
			}
			if (!((Object)(object)value != (Object)null))
			{
				return null;
			}
			return CarView.ForLobby(value);
		}

		[HarmonyPatch(typeof(CarEffects), "SetAllContent", new Type[] { typeof(PlayerData) })]
		[HarmonyPostfix]
		private static void DressRaceCar(CarEffects __instance, PlayerData playerData)
		{
			Safely(delegate
			{
				CarView.ForRace(__instance, playerData)?.Refresh();
			});
		}

		[HarmonyPatch(typeof(PlayerInLobby), "SetAllContent")]
		[HarmonyPostfix]
		private static void DressLobbyCar(PlayerInLobby __instance)
		{
			Safely(delegate
			{
				CarView.ForLobby(__instance)?.Refresh();
			});
		}

		[HarmonyPatch(typeof(CarSkin), "Add", new Type[]
		{
			typeof(CarEffects),
			typeof(int)
		})]
		[HarmonyPostfix]
		private static void HatOnRaceCar(CarSkin __instance)
		{
			Rehide(__instance);
		}

		[HarmonyPatch(typeof(CarSkin), "Add", new Type[]
		{
			typeof(CarSkinPartsReferences),
			typeof(int)
		})]
		[HarmonyPostfix]
		private static void HatOnLobbyCar(CarSkin __instance)
		{
			Rehide(__instance);
		}

		private static void Rehide(CarSkin hat)
		{
			Safely(delegate
			{
				if ((Object)(object)hat != (Object)null)
				{
					((Component)hat).GetComponentInParent<CarView>()?.Rehide();
				}
			});
		}

		private static void Safely(Action action)
		{
			try
			{
				action();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Car swap failed; the car stays as it was. " + ex));
			}
		}
	}
	internal static class Cars
	{
		public const string Stock = "Stock";

		public static readonly Choices Choices = new Choices("Car", "Cars", "Stock");

		private static List<CarData> _loaded;

		private static List<ChoiceContent> _all;

		private static Texture2D _defaultIcon;

		private static string Folder => Path.Combine(Path.GetDirectoryName(typeof(Cars).Assembly.Location), "cars");

		public static IReadOnlyList<CarData> Loaded => _loaded ?? (_loaded = Load());

		public static IReadOnlyList<ChoiceContent> All => _all ?? (_all = (from n in new string[1] { "Stock" }.Concat(Loaded.Select((CarData c) => c.Name))
			select ChoiceContent.Create("Car", n, Icon(n))).ToList());

		public static CarData Get(string name)
		{
			return Loaded.FirstOrDefault((CarData c) => c.Name == name);
		}

		private static Texture2D Icon(string name)
		{
			return ((name != "Stock") ? Art.LoadFile(Path.Combine(Folder, name + ".png")) : null) ?? _defaultIcon ?? (_defaultIcon = Art.Load("TabCar.png"));
		}

		private static List<CarData> Load()
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Expected O, but got Unknown
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Expected O, but got Unknown
			List<CarData> list = new List<CarData>();
			if (!Directory.Exists(Folder))
			{
				Plugin.Log.LogInfo((object)"No cars folder next to the DLL; only the stock car is available.");
				return list;
			}
			bool flag = default(bool);
			foreach (string item in Directory.GetFiles(Folder, "*.car").OrderBy<string, string>((string f) => f, StringComparer.Ordinal))
			{
				CarData car;
				string error;
				try
				{
					car = CarFile.Decode(File.ReadAllBytes(item), out error);
				}
				catch (IOException ex)
				{
					car = null;
					error = ex.Message;
				}
				if (car == null)
				{
					ManualLogSource log = Plugin.Log;
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(14, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Skipped car ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(Path.GetFileName(item));
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(error);
					}
					log.LogWarning(val);
				}
				else if (car.Name == "Stock" || list.Any((CarData c) => c.Name == car.Name))
				{
					ManualLogSource log2 = Plugin.Log;
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(35, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Skipped car ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(Path.GetFileName(item));
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": the name '");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(car.Name);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' is taken.");
					}
					log2.LogWarning(val);
				}
				else
				{
					list.Add(car);
				}
			}
			ManualLogSource log3 = Plugin.Log;
			BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(13, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Cars loaded: ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>((list.Count > 0) ? string.Join(", ", list.Select((CarData c) => c.Name)) : "none");
			}
			log3.LogInfo(val2);
			return list;
		}
	}
	internal sealed class CarView : MonoBehaviour
	{
		private sealed class LiftingHatch : MonoBehaviour
		{
			private const float LiftPerSwing = 0.55f;

			private Transform _door;

			private Quaternion _doorRest;

			private Quaternion _rest;

			public static Transform Create(Transform door, CarPiece piece)
			{
				//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ad: Expected O, but got Unknown
				//IL_00da: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
				//IL_0118: Unknown result type (might be due to invalid IL or missing references)
				//IL_011d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0123: Unknown result type (might be due to invalid IL or missing references)
				//IL_0128: Unknown result type (might be due to invalid IL or missing references)
				//IL_0132: Unknown result type (might be due to invalid IL or missing references)
				//IL_0137: Unknown result type (might be due to invalid IL or missing references)
				//IL_0148: 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)
				//IL_0161: Unknown result type (might be due to invalid IL or missing references)
				//IL_0163: Unknown result type (might be due to invalid IL or missing references)
				float num = float.MaxValue;
				float num2 = float.MinValue;
				float num3 = 0f;
				int num4 = 0;
				foreach (CarMesh mesh in piece.Meshes)
				{
					for (int i = 0; i < mesh.Positions.Length; i += 3)
					{
						num3 += mesh.Positions[i];
						num = Mathf.Min(num, mesh.Positions[i + 1]);
						num2 = Mathf.Max(num2, mesh.Positions[i + 2]);
						num4++;
					}
				}
				Vector3 val = default(Vector3);
				((Vector3)(ref val))..ctor(num3 / (float)num4, num, num2);
				GameObject val2 = new GameObject("MoreCustomisations_Car_HatchHinge");
				val2.layer = ((Component)door).gameObject.layer;
				val2.transform.SetParent(door.parent, false);
				val2.transform.localPosition = door.localPosition + door.localRotation * val;
				val2.transform.localRotation = door.localRotation;
				LiftingHatch liftingHatch = val2.AddComponent<LiftingHatch>();
				liftingHatch._door = door;
				liftingHatch._doorRest = door.localRotation;
				liftingHatch._rest = door.localRotation;
				GameObject val3 = new GameObject("MoreCustomisations_Car_HatchLid")
				{
					layer = ((Component)door).gameObject.layer
				};
				val3.transform.SetParent(val2.transform, false);
				val3.transform.localPosition = -val;
				return val3.transform;
			}

			private void LateUpdate()
			{
				//IL_0010: 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_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0043: Unknown result type (might be due to invalid IL or missing references)
				//IL_0048: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)_door == (Object)null))
				{
					float num = Quaternion.Angle(_doorRest, _door.localRotation);
					((Component)this).transform.localRotation = _rest * Quaternion.Euler(num * 0.55f, 0f, 0f);
				}
			}
		}

		private const string PiecePrefix = "MoreCustomisations_Car_";

		private static readonly string[] KeepUnder = new string[3] { "Boost", "SurfaceEffects", "Particles" };

		private static readonly HashSet<CarView> Live = new HashSet<CarView>();

		private static readonly Dictionary<CarMesh, Mesh> Meshes = new Dictionary<CarMesh, Mesh>();

		private static readonly Dictionary<(CarData, int), Material> Materials = new Dictionary<(CarData, int), Material>();

		private static Material _template;

		private Transform _root;

		private Transform[] _targets;

		private Func<PlayerData> _player;

		private bool _lobby;

		private CarData _shown;

		private readonly List<GameObject> _pieces = new List<GameObject>();

		private readonly List<Renderer> _hidden = new List<Renderer>();

		public static CarView ForRace(CarEffects effects, PlayerData player)
		{
			return For(((Object)(object)effects.hood != (Object)null) ? effects.hood.parent : effects.body, (Transform[])(object)new Transform[8] { effects.hood, effects.doorLeft, effects.doorRight, effects.doorBack, effects.wheelFL, effects.wheelFR, effects.wheelBL, effects.wheelBR }, () => player, lobby: false);
		}

		public static CarView ForLobby(PlayerInLobby lobby)
		{
			CarSkinPartsReferences carRendererAccessoryHolder = lobby.carRendererAccessoryHolder;
			if ((Object)(object)carRendererAccessoryHolder == (Object)null)
			{
				return null;
			}
			return For(((Object)(object)carRendererAccessoryHolder.hood != (Object)null) ? carRendererAccessoryHolder.hood.parent : (((Object)(object)lobby.carBody != (Object)null) ? lobby.carBody.transform : null), (Transform[])(object)new Transform[8] { carRendererAccessoryHolder.hood, carRendererAccessoryHolder.doorLeft, carRendererAccessoryHolder.doorRight, carRendererAccessoryHolder.doorBack, carRendererAccessoryHolder.wheelFL, carRendererAccessoryHolder.wheelFR, carRendererAccessoryHolder.wheelBL, carRendererAccessoryHolder.wheelBR }, () => Traverse.Create((object)lobby).Field("_player").GetValue<PlayerData>(), lobby: true);
		}

		private static CarView For(Transform body, Transform[] rest, Func<PlayerData> player, bool lobby)
		{
			Transform val = rest.Skip(4).FirstOrDefault((Func<Transform, bool>)((Transform w) => (Object)(object)w != (Object)null));
			if ((Object)(object)body == (Object)null || (Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"A car without a body or wheels; leaving it stock.");
				return null;
			}
			CarView carView = ((Component)body).GetComponent<CarView>() ?? ((Component)body).gameObject.AddComponent<CarView>();
			carView._root = CommonParent(body, val);
			carView._targets = ((IEnumerable<Transform>)(object)new Transform[1] { body }).Concat(rest).ToArray();
			carView._player = player;
			carView._lobby = lobby;
			Live.Add(carView);
			return carView;
		}

		public void Refresh()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			PlayerData val = _player();
			if (!((Object)(object)val == (Object)null))
			{
				Show((_lobby && (int)val.type == 0) ? Cars.Choices.Saved(val) : Cars.Choices.Get(val));
			}
		}

		public void Show(string name)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			CarData carData = Cars.Get(name);
			try
			{
				if (carData != _shown)
				{
					Clear();
					if (carData != null)
					{
						Build(carData);
					}
					_shown = carData;
				}
				if (carData != null)
				{
					Hide();
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(45, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Couldn't show car '");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(name);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'; showing the stock car. ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
				Clear();
				_shown = null;
			}
		}

		public void Rehide()
		{
			if (_shown != null)
			{
				Hide();
			}
		}

		public static void RefreshAll()
		{
			foreach (CarView item in Live.ToList())
			{
				if ((Object)(object)item == (Object)null)
				{
					Live.Remove(item);
				}
				else
				{
					item.Refresh();
				}
			}
		}

		private void Build(CarData car)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Expected O, but got Unknown
			Material template = Template();
			int i;
			bool flag = default(bool);
			for (i = 0; i < CarFile.Pieces.Length; i++)
			{
				CarPiece carPiece = car.Pieces.FirstOrDefault((CarPiece p) => p.Name == CarFile.Pieces[i]);
				Transform val = _targets[i];
				if (carPiece == null)
				{
					continue;
				}
				if ((Object)(object)val == (Object)null)
				{
					ManualLogSource log = Plugin.Log;
					BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(45, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(car.Name);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": this car has no ");
						((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(carPiece.Name);
						((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" part to carry it; skipped.");
					}
					log.LogWarning(val2);
					continue;
				}
				Transform val3 = val;
				if (carPiece.Name == "Hatch")
				{
					val3 = LiftingHatch.Create(val, carPiece);
					_pieces.Add(((Component)val3.parent).gameObject);
				}
				foreach (CarMesh mesh in carPiece.Meshes)
				{
					GameObject val4 = new GameObject("MoreCustomisations_Car_" + carPiece.Name);
					val4.layer = ((Component)val).gameObject.layer;
					val4.transform.SetParent(val3, false);
					val4.AddComponent<MeshFilter>().sharedMesh = MeshFor(car, mesh);
					((Renderer)val4.AddComponent<MeshRenderer>()).sharedMaterial = MaterialFor(car, mesh.Slot, template);
					_pieces.Add(val4);
				}
			}
		}

		private void Hide()
		{
			MeshRenderer[] componentsInChildren = ((Component)_root).GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val in componentsInChildren)
			{
				if (((Renderer)val).enabled && !((Object)val).name.StartsWith("MoreCustomisations_Car_") && !Kept(((Component)val).transform))
				{
					((Renderer)val).enabled = false;
					_hidden.Add((Renderer)(object)val);
				}
			}
		}

		private void Clear()
		{
			foreach (GameObject piece in _pieces)
			{
				if ((Object)(object)piece != (Object)null)
				{
					Object.Destroy((Object)(object)piece);
				}
			}
			_pieces.Clear();
			foreach (Renderer item in _hidden)
			{
				if ((Object)(object)item != (Object)null)
				{
					item.enabled = true;
				}
			}
			_hidden.Clear();
		}

		private bool Kept(Transform t)
		{
			while ((Object)(object)t != (Object)null && (Object)(object)t != (Object)(object)_root)
			{
				if (KeepUnder.Any((string k) => ((Object)t).name.Contains(k)))
				{
					return true;
				}
				t = t.parent;
			}
			return false;
		}

		private Material Template()
		{
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected O, but got Unknown
			if ((Object)(object)_template != (Object)null)
			{
				return _template;
			}
			List<Material> source = (from m in ((Component)_root).GetComponentsInChildren<MeshRenderer>(true).SelectMany((MeshRenderer r) => ((Renderer)r).sharedMaterials)
				where (Object)(object)m != (Object)null
				select m).ToList();
			_template = ((IEnumerable<Material>)source).FirstOrDefault((Func<Material, bool>)((Material m) => m.HasProperty("_RampTint") && m.HasProperty("_Color"))) ?? ((IEnumerable<Material>)source).FirstOrDefault((Func<Material, bool>)((Material m) => m.HasProperty("_Color")));
			if ((Object)(object)_template == (Object)null)
			{
				throw new InvalidOperationException("no stock material with a _Color to copy");
			}
			ManualLogSource log = Plugin.Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(25, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Car materials copy '");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((Object)_template).name);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' (");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((Object)_template.shader).name);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(").");
			}
			log.LogInfo(val);
			return _template;
		}

		private static Material MaterialFor(CarData car, int slot, Material template)
		{
			//IL_004c: 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_0073: Expected O, but got Unknown
			//IL_0079: 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)
			if (Materials.TryGetValue((car, slot), out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			CarSlot carSlot = car.Slots[slot];
			Color val = default(Color);
			((Color)(ref val))..ctor(carSlot.R, carSlot.G, carSlot.B, 1f);
			value = new Material(template)
			{
				name = "MoreCustomisations_" + car.Name + "_" + carSlot.Name
			};
			value.SetColor("_Color", val);
			if (value.HasProperty("_RampTint"))
			{
				value.SetColor("_RampTint", new Color(carSlot.R * 0.75f, carSlot.G * 0.75f, carSlot.B * 0.75f, 1f));
			}
			return Materials[(car, slot)] = value;
		}

		private static Mesh MeshFor(CarData car, CarMesh source)
		{
			//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)
			//IL_0036: Expected O, but got Unknown
			if (Meshes.TryGetValue(source, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			value = new Mesh
			{
				name = "MoreCustomisations_" + car.Name
			};
			value.vertices = Vectors(source.Positions);
			value.normals = Vectors(source.Normals);
			value.triangles = source.Triangles;
			return Meshes[source] = value;
		}

		private static Vector3[] Vectors(float[] xyz)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Vector3[] array = (Vector3[])(object)new Vector3[xyz.Length / 3];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = new Vector3(xyz[i * 3], xyz[i * 3 + 1], xyz[i * 3 + 2]);
			}
			return array;
		}

		private static Transform CommonParent(Transform a, Transform b)
		{
			HashSet<Transform> hashSet = new HashSet<Transform>();
			Transform val = a;
			while ((Object)(object)val != (Object)null)
			{
				hashSet.Add(val);
				val = val.parent;
			}
			Transform val2 = b;
			while ((Object)(object)val2 != (Object)null)
			{
				if (hashSet.Contains(val2))
				{
					return val2;
				}
				val2 = val2.parent;
			}
			return a.root;
		}

		private void OnDestroy()
		{
			Live.Remove(this);
		}
	}
	public class ChoiceContent : UnlockableContent
	{
		public string Kind;

		public string ChoiceName;

		public override UnlockableContentType Type()
		{
			return (UnlockableContentType)1;
		}

		public static ChoiceContent Create(string kind, string choiceName, Texture2D preview)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			ChoiceContent choiceContent = ScriptableObject.CreateInstance<ChoiceContent>();
			((Object)choiceContent).name = "MoreCustomisations_" + kind + "_" + choiceName;
			((Object)choiceContent).hideFlags = (HideFlags)32;
			choiceContent.Kind = kind;
			choiceContent.ChoiceName = choiceName;
			((UnlockableContent)choiceContent).unlockedByDefault = true;
			using (MD5 mD = MD5.Create())
			{
				((UnlockableContent)choiceContent).id = new SerializableGuid(new Guid(mD.ComputeHash(Encoding.UTF8.GetBytes("MoreCustomisations." + kind + "." + choiceName))));
			}
			((UnlockableContent)choiceContent).preview = preview;
			return choiceContent;
		}
	}
	internal sealed class Choices
	{
		private readonly string _kind;

		private readonly string _section;

		private readonly string _default;

		private readonly string _messageName;

		private readonly Func<PlayerData, string, string> _resolve;

		private readonly Dictionary<ulong, string> _table = new Dictionary<ulong, string>();

		private readonly Dictionary<int, ConfigEntry<string>> _saved = new Dictionary<int, ConfigEntry<string>>();

		private readonly Dictionary<int, PlayerData> _localPlayers = new Dictionary<int, PlayerData>();

		private CustomMessagingManager _registeredOn;

		private bool _toldAboutHost;

		public IReadOnlyCollection<PlayerData> LocalPlayers => _localPlayers.Values;

		public event Action Changed;

		public Choices(string kind, string section, string defaultValue, Func<PlayerData, string, string> resolve = null)
		{
			_kind = kind;
			_section = section;
			_default = defaultValue;
			_messageName = "MoreCustomisations." + section;
			_resolve = resolve ?? ((Func<PlayerData, string, string>)((PlayerData _, string saved) => saved));
		}

		public string Get(PlayerData player)
		{
			if (!((Object)(object)player != (Object)null) || !_table.TryGetValue(((NetworkBehaviour)player).NetworkObjectId, out var value))
			{
				return _default;
			}
			return value;
		}

		public string Saved(PlayerData player)
		{
			return Entry(player).Value;
		}

		public void SetLocal(PlayerData player, string value)
		{
			Entry(player).Value = value;
			Announce(player);
		}

		public void Announce(PlayerData player)
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Expected O, but got Unknown
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsListening || (Object)(object)player == (Object)null || !((NetworkBehaviour)player).IsSpawned)
			{
				return;
			}
			_localPlayers[player.LocalIndex] = player;
			Dictionary<ulong, string> dictionary = new Dictionary<ulong, string> { [((NetworkBehaviour)player).NetworkObjectId] = _resolve(player, Entry(player).Value) };
			if (singleton.IsServer)
			{
				Merge(dictionary);
				Broadcast();
				return;
			}
			Send(ChoiceTable.Encode(dictionary), 0uL);
			if (!_toldAboutHost)
			{
				_toldAboutHost = true;
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(91, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Sent ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(_kind.ToLowerInvariant());
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" choice to the host. If the host doesn't have MoreCustomisations, everyone gets stock.");
				}
				log.LogInfo(val);
			}
		}

		public void Tick()
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			NetworkManager singleton = NetworkManager.Singleton;
			CustomMessagingManager val = (((Object)(object)singleton != (Object)null) ? singleton.CustomMessagingManager : null);
			if (val != _registeredOn)
			{
				_registeredOn = val;
				_table.Clear();
				_localPlayers.Clear();
				_toldAboutHost = false;
				if (val != null)
				{
					val.RegisterNamedMessageHandler(_messageName, new HandleNamedMessageDelegate(OnMessage));
				}
			}
		}

		private ConfigEntry<string> Entry(PlayerData player)
		{
			int num = player.LocalIndex + 1;
			if (!_saved.TryGetValue(num, out var value))
			{
				value = (_saved[num] = Plugin.Cfg.Bind<string>(_section, $"Player{num}{_kind}", _default, $"{_kind} used by local player {num}."));
			}
			return value;
		}

		private void OnMessage(ulong sender, FastBufferReader reader)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected O, but got Unknown
			Dictionary<ulong, string> dictionary;
			bool flag = default(bool);
			try
			{
				int num = default(int);
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
				if (num < 0 || num > 16384)
				{
					throw new OverflowException("length " + num);
				}
				byte[] bytes = new byte[num];
				((FastBufferReader)(ref reader)).ReadBytesSafe(ref bytes, num, 0);
				dictionary = ChoiceTable.Decode(bytes);
			}
			catch (OverflowException ex)
			{
				ManualLogSource log = Plugin.Log;
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(43, 3, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Ignored a malformed ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(_kind.ToLowerInvariant());
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" message from client ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<ulong>(sender);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogWarning(val);
				return;
			}
			if (dictionary == null)
			{
				ManualLogSource log2 = Plugin.Log;
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(42, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Ignored a malformed ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(_kind.ToLowerInvariant());
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" message from client ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<ulong>(sender);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
				}
				log2.LogWarning(val);
			}
			else if (NetworkManager.Singleton.IsServer)
			{
				Merge(ChoiceTable.OwnedBy(sender, dictionary, OwnerOf));
				Broadcast();
			}
			else if (sender == 0L)
			{
				_table.Clear();
				Merge(dictionary);
			}
		}

		private static ulong? OwnerOf(ulong playerObjectId)
		{
			if (!NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(playerObjectId, out var value) || !((Object)(object)((Component)value).GetComponent<PlayerData>() != (Object)null))
			{
				return null;
			}
			return value.OwnerClientId;
		}

		private void Merge(IReadOnlyDictionary<ulong, string> entries)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			foreach (KeyValuePair<ulong, string> entry in entries)
			{
				_table[entry.Key] = entry.Value;
			}
			try
			{
				this.Changed?.Invoke();
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(26, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Applying ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(_kind.ToLowerInvariant());
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" choices failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
				}
				log.LogError(val);
			}
		}

		private void Broadcast()
		{
			byte[] bytes = ChoiceTable.Encode(_table);
			foreach (ulong connectedClientsId in NetworkManager.Singleton.ConnectedClientsIds)
			{
				if (connectedClientsId != 0L)
				{
					Send(bytes, connectedClientsId);
				}
			}
		}

		private unsafe void Send(byte[] bytes, ulong to)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(bytes.Length + 4, (Allocator)2, -1);
			try
			{
				int num = bytes.Length;
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives));
				((FastBufferWriter)(ref val)).WriteBytesSafe(bytes, -1, 0);
				NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage(_messageName, to, val, (NetworkDelivery)4);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}
	}
	internal static class ChoiceTable
	{
		public const int MaxNameLength = 64;

		public const int MaxEntries = 64;

		public const int MaxBytes = 16384;

		public static byte[] Encode(IReadOnlyDictionary<ulong, string> table)
		{
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8))
			{
				binaryWriter.Write(table.Count);
				foreach (KeyValuePair<ulong, string> item in table)
				{
					binaryWriter.Write(item.Key);
					binaryWriter.Write(item.Value);
				}
			}
			return memoryStream.ToArray();
		}

		public static Dictionary<ulong, string> Decode(byte[] bytes)
		{
			if (bytes == null || bytes.Length > 16384)
			{
				return null;
			}
			try
			{
				using BinaryReader binaryReader = new BinaryReader(new MemoryStream(bytes), Encoding.UTF8);
				int num = binaryReader.ReadInt32();
				if (num < 0 || num > 64)
				{
					return null;
				}
				Dictionary<ulong, string> dictionary = new Dictionary<ulong, string>(num);
				for (int i = 0; i < num; i++)
				{
					ulong key = binaryReader.ReadUInt64();
					string text = binaryReader.ReadString();
					if (text.Length == 0 || text.Length > 64)
					{
						return null;
					}
					dictionary[key] = text;
				}
				return (binaryReader.BaseStream.Position == binaryReader.BaseStream.Length) ? dictionary : null;
			}
			catch (Exception ex) when (ex is EndOfStreamException || ex is IOException || ex is FormatException || ex is ArgumentException)
			{
				return null;
			}
		}

		public static string PickRandom(IReadOnlyList<string> horns, string previous, Random rng)
		{
			if (horns.Count == 0)
			{
				return null;
			}
			if (horns.Count == 1)
			{
				return horns[0];
			}
			int num = -1;
			for (int i = 0; i < horns.Count; i++)
			{
				if (horns[i] == previous)
				{
					num = i;
				}
			}
			if (num < 0)
			{
				return horns[rng.Next(horns.Count)];
			}
			int num2 = rng.Next(horns.Count - 1);
			return horns[(num2 >= num) ? (num2 + 1) : num2];
		}

		public static Dictionary<ulong, string> OwnedBy(ulong sender, IReadOnlyDictionary<ulong, string> claimed, Func<ulong, ulong?> ownerOf)
		{
			Dictionary<ulong, string> dictionary = new Dictionary<ulong, string>();
			foreach (KeyValuePair<ulong, string> item in claimed)
			{
				if (ownerOf(item.Key) == sender)
				{
					dictionary[item.Key] = item.Value;
				}
			}
			return dictionary;
		}
	}
	internal sealed class CustomTab
	{
		private class State
		{
			public CustomTab Tab;

			public int TabIndex;

			public UnlockableContentGridUi Grid;

			public PlayerData Player;

			public bool Initialising;
		}

		public static readonly List<CustomTab> All = new List<CustomTab>();

		private readonly string _title;

		private readonly string _iconFile;

		private readonly Choices _choices;

		private readonly Func<IReadOnlyList<ChoiceContent>> _entries;

		private readonly Action<PlayerData, string> _select;

		private readonly Action<UnlockablePlayerCustomizationSelector, PlayerData, string> _hover;

		private readonly Action<UnlockablePlayerCustomizationSelector, PlayerData> _leave;

		private Sprite _icon;

		private static readonly Dictionary<UnlockablePlayerCustomizationSelector, List<State>> States = new Dictionary<UnlockablePlayerCustomizationSelector, List<State>>();

		private static readonly Dictionary<Tab, string> OurTabs = new Dictionary<Tab, string>();

		public CustomTab(string title, string iconFile, Choices choices, Func<IReadOnlyList<ChoiceContent>> entries, Action<PlayerData, string> select, Action<UnlockablePlayerCustomizationSelector, PlayerData, string> hover, Action<UnlockablePlayerCustomizationSelector, PlayerData> leave)
		{
			_title = title;
			_iconFile = iconFile;
			_choices = choices;
			_entries = entries;
			_select = select;
			_hover = hover;
			_leave = leave;
		}

		private ChoiceContent Find(string name)
		{
			IReadOnlyList<ChoiceContent> readOnlyList = _entries();
			return readOnlyList.FirstOrDefault((ChoiceContent e) => e.ChoiceName == name) ?? readOnlyList[0];
		}

		private static List<State> StatesOf(UnlockablePlayerCustomizationSelector selector)
		{
			if (!States.TryGetValue(selector, out var value))
			{
				return new List<State>();
			}
			return value;
		}

		private static State Active(UnlockablePlayerCustomizationSelector selector)
		{
			return StatesOf(selector).FirstOrDefault((State s) => (Object)(object)s.Grid != (Object)null && selector.tabs.CurrentTabIndex == s.TabIndex);
		}

		[HarmonyPatch(typeof(UnlockablePlayerCustomizationSelector), "Init")]
		[HarmonyPrefix]
		private static void BeforeInit(UnlockablePlayerCustomizationSelector __instance, PlayerData playerData)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)playerData == (Object)null || (int)playerData.type != 0)
			{
				return;
			}
			if (!States.TryGetValue(__instance, out var value))
			{
				value = (States[__instance] = new List<State>());
			}
			foreach (CustomTab tab in All)
			{
				State state = value.FirstOrDefault((State s) => s.Tab == tab && (Object)(object)s.Grid != (Object)null);
				if (state != null)
				{
					state.Player = playerData;
					continue;
				}
				try
				{
					value.Add(tab.Build(__instance, playerData));
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Couldn't add the " + tab._title + " tab; saved choices still apply. " + ex));
				}
			}
		}

		[HarmonyPatch(typeof(UnlockablePlayerCustomizationSelector), "Init")]
		[HarmonyPostfix]
		private static void AfterInit(UnlockablePlayerCustomizationSelector __instance, PlayerData playerData)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)playerData == (Object)null || (int)playerData.type != 0)
			{
				return;
			}
			foreach (CustomTab item in All)
			{
				item._choices.Announce(playerData);
			}
			foreach (State item2 in from s in StatesOf(__instance)
				where (Object)(object)s.Grid != (Object)null
				select s)
			{
				item2.Initialising = true;
				try
				{
					item2.Grid.Init(item2.Tab._entries().Cast<UnlockableContent>().ToList(), (UnlockableContent)(object)item2.Tab.Find(item2.Tab._choices.Saved(playerData)), 0);
					LabelTiles(item2.Grid);
				}
				finally
				{
					item2.Initialising = false;
				}
			}
		}

		private State Build(UnlockablePlayerCustomizationSelector selector, PlayerData player)
		{
			Tab val = selector.tabs.tabs[selector.carAccessoryGridTabIndex];
			GameObject val2 = (((Component)val.container).transform.IsChildOf(((Component)val.containerLayout).transform) ? ((Component)val.containerLayout).gameObject : ((Component)val.container).gameObject);
			GameObject val3 = Object.Instantiate<GameObject>(val2, val2.transform.parent);
			((Object)val3).name = "MoreCustomisations_" + _title + "Tab";
			GameObject val4 = Object.Instantiate<GameObject>(val.root, val.root.transform.parent);
			((Object)val4).name = "MoreCustomisations_" + _title + "Grid";
			try
			{
				return Wire(selector, player, val, val2, val3, val4);
			}
			catch
			{
				Object.Destroy((Object)(object)val3);
				Object.Destroy((Object)(object)val4);
				throw;
			}
		}

		private State Wire(UnlockablePlayerCustomizationSelector selector, PlayerData player, Tab source, GameObject headerSource, GameObject header, GameObject root)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Expected O, but got Unknown
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Expected O, but got Unknown
			UnlockableContentGridUi componentInChildren = root.GetComponentInChildren<UnlockableContentGridUi>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				throw new InvalidOperationException("no grid under the Accessory tab's root");
			}
			Tab val = new Tab();
			FieldInfo[] fields = typeof(Tab).GetFields(BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				fieldInfo.SetValue(val, fieldInfo.GetValue(source));
			}
			val.container = Twin<GradientImageWithBorderRadius>(source.container, headerSource, header);
			val.containerLayout = Twin<LayoutElement>(source.containerLayout, headerSource, header);
			val.headerLabel = Twin<TMP_Text>(source.headerLabel, headerSource, header);
			val.iconHolder = Twin<Image>(source.iconHolder, headerSource, header);
			val.root = root;
			val.icon = Icon();
			val.onSelect = new UnityEvent();
			val.onDeselect = new UnityEvent();
			OurTabs[val] = _title;
			selector.tabs.tabs = selector.tabs.tabs.Append(val).ToArray();
			componentInChildren.type = (UnlockableContentType)3;
			componentInChildren.onSelected = new UnityEvent<UnlockableContent, UnlockableContentGridElementUi>();
			componentInChildren.onHovered = new UnityEvent<UnlockableContent, UnlockableContentGridElementUi>();
			UnlockableContentInfoDisplay info = Nearest<UnlockableContentInfoDisplay>(((Component)componentInChildren).transform);
			State state = new State
			{
				Tab = this,
				TabIndex = selector.tabs.tabs.Length - 1,
				Grid = componentInChildren,
				Player = player
			};
			componentInChildren.onSelected.AddListener((UnityAction<UnlockableContent, UnlockableContentGridElementUi>)delegate(UnlockableContent content, UnlockableContentGridElementUi _)
			{
				if (!state.Initialising && content is ChoiceContent choiceContent)
				{
					_select(state.Player, choiceContent.ChoiceName);
				}
			});
			componentInChildren.onHovered.AddListener((UnityAction<UnlockableContent, UnlockableContentGridElementUi>)delegate(UnlockableContent content, UnlockableContentGridElementUi element)
			{
				if (selector.tabs.CurrentTabIndex == state.TabIndex && content is ChoiceContent choiceContent)
				{
					if ((Object)(object)info != (Object)null)
					{
						info.UpdateInfos(content, element);
					}
					if (!state.Initialising)
					{
						_hover(selector, state.Player, choiceContent.ChoiceName);
					}
				}
			});
			ManualLogSource log = Plugin.Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(53, 4, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(_title);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" tab added for local player ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(player.LocalIndex + 1);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" (");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(_entries().Count);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" entries, info panel ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(((Object)(object)info != (Object)null) ? "found" : "not found");
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(").");
			}
			log.LogInfo(val2);
			return state;
		}

		private static T Twin<T>(T original, GameObject sourceRoot, GameObject cloneRoot) where T : Component
		{
			if ((Object)(object)original == (Object)null)
			{
				return default(T);
			}
			List<int> list = new List<int>();
			Transform val = ((Component)original).transform;
			while ((Object)(object)val != (Object)(object)sourceRoot.transform)
			{
				if ((Object)(object)val == (Object)null)
				{
					throw new InvalidOperationException(((Object)(object)original).name + " isn't under the tab header " + ((Object)sourceRoot).name);
				}
				list.Insert(0, val.GetSiblingIndex());
				val = val.parent;
			}
			Transform val2 = cloneRoot.transform;
			foreach (int item in list)
			{
				val2 = val2.GetChild(item);
			}
			return ((Component)val2).GetComponent<T>();
		}

		private static void LabelTiles(UnlockableContentGridUi grid)
		{
			UnlockableContentGridElementUi[] componentsInChildren = ((Component)grid.elementContainer).GetComponentsInChildren<UnlockableContentGridElementUi>(true);
			foreach (UnlockableContentGridElementUi val in componentsInChildren)
			{
				if (Traverse.Create((object)val).Field("_unlockableContent").GetValue() is ChoiceContent choiceContent)
				{
					val.indexLabel.text = choiceContent.ChoiceName;
					val.indexLabel.enableAutoSizing = true;
					val.indexLabel.fontSizeMin = 8f;
					val.indexLabel.fontSizeMax = Mathf.Max(val.indexLabel.fontSize, 8f);
				}
			}
		}

		private Sprite Icon()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_icon != (Object)null)
			{
				return _icon;
			}
			Texture2D val = Art.Load(_iconFile);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			_icon = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
			((Object)_icon).name = "MoreCustomisations_Tab" + _title;
			return _icon;
		}

		private static T Nearest<T>(Transform from) where T : Component
		{
			Transform val = from;
			while ((Object)(object)val != (Object)null)
			{
				T componentInChildren = ((Component)val).GetComponentInChildren<T>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					return componentInChildren;
				}
				val = val.parent;
			}
			return default(T);
		}

		private static void Leave(UnlockablePlayerCustomizationSelector selector)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			bool flag = default(bool);
			foreach (State item in from s in StatesOf(selector)
				where (Object)(object)s.Grid != (Object)null
				select s)
			{
				try
				{
					item.Tab._leave(selector, item.Player);
				}
				catch (Exception ex)
				{
					ManualLogSource log = Plugin.Log;
					BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(25, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Leaving the ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(item.Tab._title);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" tab failed: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
					}
					log.LogError(val);
				}
			}
		}

		[HarmonyPatch(typeof(Tab), "Init")]
		[HarmonyPostfix]
		private static void LabelOurTab(Tab __instance)
		{
			if (OurTabs.TryGetValue(__instance, out var value))
			{
				__instance.headerLabel.text = value;
			}
		}

		[HarmonyPatch(typeof(UnlockablePlayerCustomizationSelector), "GetGridFromTab")]
		[HarmonyPrefix]
		private static bool GridFromTab(UnlockablePlayerCustomizationSelector __instance, ref GridUI __result)
		{
			State state = Active(__instance);
			if (state == null)
			{
				return true;
			}
			__result = (GridUI)(object)state.Grid;
			return false;
		}

		[HarmonyPatch(typeof(UnlockablePlayerCustomizationSelector), "UpdateGridHoveredView")]
		[HarmonyPrefix]
		private static bool HoveredView(UnlockablePlayerCustomizationSelector __instance, ref (UnlockableContentType, object content) __result)
		{
			Leave(__instance);
			State state = Active(__instance);
			if (state == null)
			{
				return true;
			}
			state.Initialising = true;
			try
			{
				state.Grid.HoverSelected();
			}
			finally
			{
				state.Initialising = false;
			}
			__result = ((UnlockableContentType)1, content: state.Grid.HoveredContent);
			return false;
		}

		[HarmonyPatch(typeof(UnlockablePlayerCustomizationSelector), "LockView")]
		[HarmonyPostfix]
		private static void Lock(UnlockablePlayerCustomizationSelector __instance)
		{
			foreach (State item in from s in StatesOf(__instance)
				where (Object)(object)s.Grid != (Object)null
				select s)
			{
				item.Grid.Lock();
			}
			Leave(__instance);
		}

		[HarmonyPatch(typeof(UnlockablePlayerCustomizationSelector), "Unlock")]
		[HarmonyPostfix]
		private static void Unlock(UnlockablePlayerCustomizationSelector __instance)
		{
			foreach (State item in from s in StatesOf(__instance)
				where (Object)(object)s.Grid != (Object)null
				select s)
			{
				item.Grid.Unlock();
			}
		}

		[HarmonyPatch(typeof(UnlockableContentGridUi), "Init", new Type[] { typeof(UnlockableContentType) })]
		[HarmonyPrefix]
		private static bool SkipSelfInit(UnlockableContentGridUi __instance)
		{
			return !States.Values.Any((List<State> states) => states.Any((State s) => (Object)(object)s.Grid == (Object)(object)__instance));
		}

		[HarmonyPatch(typeof(UnlockableContentManager), "HasUnlockedContent")]
		[HarmonyPostfix]
		private static void ChoicesAreUnlocked(UnlockableContent content, ref bool __result)
		{
			if (content is ChoiceContent)
			{
				__result = true;
			}
		}

		[HarmonyPatch(typeof(UnlockableContentInfoDisplay), "UpdateInfos")]
		[HarmonyPostfix]
		private static void ShowChoiceName(UnlockableContentInfoDisplay __instance, UnlockableContent content)
		{
			if (content is ChoiceContent choiceContent)
			{
				__instance.unlockInfosLabel.text = choiceContent.ChoiceName;
			}
		}
	}
	internal static class HornChoices
	{
		public static readonly Choices Choices = new Choices("Horn", "Horns", "Stock", Resolved);

		private static readonly Dictionary<int, string> Rolls = new Dictionary<int, string>();

		private static readonly Random Rng = new Random();

		private static GameManager _roundsHookedTo;

		public static string Get(PlayerData player)
		{
			return Choices.Get(player);
		}

		public static void SetLocal(PlayerData player, string horn)
		{
			if (horn == "Random")
			{
				Reroll(player);
			}
			Choices.SetLocal(player, horn);
		}

		private static string Resolved(PlayerData player, string saved)
		{
			if (saved != "Random")
			{
				return saved;
			}
			int localIndex = player.LocalIndex;
			if (!Rolls.ContainsKey(localIndex))
			{
				Reroll(player);
			}
			return Rolls[localIndex];
		}

		private static void Reroll(PlayerData player)
		{
			int localIndex = player.LocalIndex;
			Rolls.TryGetValue(localIndex, out var value);
			Rolls[localIndex] = ChoiceTable.PickRandom(Horns.Customs, value, Rng) ?? "Stock";
		}

		private static void OnStateChange(GameState state)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			if ((int)state != 4)
			{
				return;
			}
			foreach (PlayerData item in new List<PlayerData>(Choices.LocalPlayers))
			{
				if (!((Object)(object)item == (Object)null) && !(Choices.Saved(item) != "Random"))
				{
					Reroll(item);
					Choices.Announce(item);
				}
			}
		}

		public static void Tick()
		{
			GameManager instance = GameManager.instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)instance != (Object)(object)_roundsHookedTo)
			{
				_roundsHookedTo = instance;
				instance.onStateChange.AddListener((UnityAction<GameState>)OnStateChange);
			}
			Choices.Tick();
		}
	}
	internal static class HornPlayback
	{
		[HarmonyPatch(typeof(CarEffects), "Honk")]
		[HarmonyPostfix]
		[HarmonyPriority(600)]
		private static void OnHonk(CarEffects __instance)
		{
			AudioClip val = Horns.Clip(HornChoices.Get(((Object)(object)__instance.car != (Object)null) ? __instance.car.player : null));
			if (!((Object)(object)val == (Object)null))
			{
				SplitScreenAudioSource honkAudioSource = __instance.honkAudioSource;
				honkAudioSource.SwitchClip(val);
				honkAudioSource.SetVolume(Plugin.HornVolume.Value);
				honkAudioSource.SetPitch(1f);
				__instance.splitScreenAudioPlayers.Play(honkAudioSource);
			}
		}
	}
	internal static class Horns
	{
		public const string Stock = "Stock";

		public const string Random = "Random";

		private static List<ChoiceContent> _all;

		private static List<string> _customs;

		private static Texture2D _defaultIcon;

		private static Texture2D _randomIcon;

		public static IReadOnlyList<ChoiceContent> All => _all ?? (_all = (from n in new string[2] { "Stock", "Random" }.Concat(Customs)
			select ChoiceContent.Create("Horn", n, Icon(n))).ToList());

		public static IReadOnlyList<string> Customs => _customs ?? (_customs = (from c in Sounds.Clips
			select ((Object)c).name into n
			where n != "Stock" && n != "Random"
			select n).ToList());

		private static Texture2D Icon(string name)
		{
			object obj = Sounds.Texture(name);
			if (obj == null)
			{
				if (!(name == "Random"))
				{
					obj = _defaultIcon;
					if (obj == null)
					{
						return _defaultIcon = Art.Load("Horn.png");
					}
				}
				else
				{
					obj = _randomIcon ?? (_randomIcon = Art.Load("Random.png"));
				}
			}
			return (Texture2D)obj;
		}

		public static AudioClip Clip(string name)
		{
			if (!(name == "Stock") && !(name == "Random"))
			{
				return ((IEnumerable<AudioClip>)Sounds.Clips).FirstOrDefault((Func<AudioClip, bool>)((AudioClip c) => ((Object)c).name == name));
			}
			return null;
		}
	}
	internal static class HornTab
	{
		private static AudioSource _preview;

		private static readonly Random PreviewRng = new Random();

		public static CustomTab Create()
		{
			return new CustomTab("Horn", "TabHorn.png", HornChoices.Choices, () => Horns.All, HornChoices.SetLocal, delegate(UnlockablePlayerCustomizationSelector selector, PlayerData player, string horn)
			{
				Preview(horn);
			}, delegate
			{
				StopPreview();
			});
		}

		private static void Preview(string hornName)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			if (hornName == "Random")
			{
				hornName = ChoiceTable.PickRandom(Horns.Customs, null, PreviewRng);
			}
			AudioClip val = Horns.Clip(hornName);
			if (!((Object)(object)val == (Object)null))
			{
				if ((Object)(object)_preview == (Object)null)
				{
					GameObject val2 = new GameObject("MoreCustomisations_HornPreview");
					Object.DontDestroyOnLoad((Object)val2);
					_preview = val2.AddComponent<AudioSource>();
					_preview.spatialBlend = 0f;
				}
				_preview.Stop();
				_preview.PlayOneShot(val, Plugin.HornVolume.Value);
			}
		}

		private static void StopPreview()
		{
			if ((Object)(object)_preview != (Object)null)
			{
				_preview.Stop();
			}
		}
	}
	[BepInPlugin("onlynothing.morecustomisations", "MoreCustomisations", "0.5.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		internal static ConfigFile Cfg;

		internal static ConfigEntry<float> HornVolume;

		private static Harmony _harmony;

		private void Awake()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = ((BaseUnityPlugin)this).Config;
			HornVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Horns", "Volume", 1f, "Volume of custom horn sounds, 0 to 1.");
			_harmony = new Harmony("onlynothing.morecustomisations");
			Sounds.Load();
			_ = Cars.Loaded;
			_harmony.PatchAll(typeof(HornPlayback));
			CustomTab.All.Add(HornTab.Create());
			CustomTab.All.Add(CarPatches.CreateTab());
			_harmony.PatchAll(typeof(CustomTab));
			_harmony.PatchAll(typeof(CarPatches));
			Log.LogInfo((object)"MoreCustomisations loaded.");
		}

		private void Update()
		{
			HornChoices.Tick();
			Cars.Choices.Tick();
		}
	}
	internal static class Sounds
	{
		private const string BundleFile = "morecustomisations.bundle";

		private static Texture2D[] _textures = (Texture2D[])(object)new Texture2D[0];

		public static AudioClip[] Clips { get; private set; } = (AudioClip[])(object)new AudioClip[0];

		public static void Load()
		{
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Expected O, but got Unknown
			string text = Path.Combine(Path.GetDirectoryName(typeof(Sounds).Assembly.Location), "morecustomisations.bundle");
			if (!File.Exists(text))
			{
				Plugin.Log.LogInfo((object)"No sound bundle next to the DLL; only the stock horn is available.");
				return;
			}
			AssetBundle assetBundle = AssetBundle.LoadFromFileAsync(text).assetBundle;
			if ((Object)(object)assetBundle == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Sound bundle failed to load; was it built with Unity 2022.3 for Windows?");
				return;
			}
			Clips = (from c in assetBundle.LoadAllAssetsAsync<AudioClip>().allAssets.OfType<AudioClip>()
				orderby ((Object)c).name
				select c).ToArray();
			_textures = assetBundle.LoadAllAssetsAsync<Texture2D>().allAssets.OfType<Texture2D>().ToArray();
			ManualLogSource log = Plugin.Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(25, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Horns loaded: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(string.Join(", ", Clips.Select((AudioClip c) => ((Object)c).name)));
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" (");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_textures.Length);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" icon(s))");
			}
			log.LogInfo(val);
		}

		public static Texture2D Texture(string name)
		{
			return ((IEnumerable<Texture2D>)_textures).FirstOrDefault((Func<Texture2D, bool>)((Texture2D t) => ((Object)t).name == name));
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "onlynothing.morecustomisations";

		public const string PLUGIN_NAME = "MoreCustomisations";

		public const string PLUGIN_VERSION = "0.5.0";
	}
}