Decompiled source of PrincessUsasBikeGangMod v1.3.0

PrincessUsasBikeGangMod.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[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("PrincessUsasBikeGangMod")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PrincessUsasBikeGangMod")]
[assembly: AssemblyTitle("PrincessUsasBikeGangMod")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 PrincessUsasBikeGangMod
{
	[BepInPlugin("CandyKane.PrincessUsasBikeGangMod", "Princess Usa's Bike Gang Mod", "1.3.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "CandyKane.PrincessUsasBikeGangMod";

		public const string PluginName = "Princess Usa's Bike Gang Mod";

		public const string PluginVersion = "1.3.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<int> MaxCopies;

		internal static ConfigEntry<int> ShopPoolCopies;

		internal static ConfigEntry<bool> DebugLogging;

		internal static ConfigEntry<bool> EnableVehicleColors;

		internal static ConfigEntry<KeyCode> PaletteKey;

		internal static ConfigEntry<bool> ExteriorPaintOnly;

		private Harmony _harmony;

		private PhysGrabObject? _paletteTarget;

		private bool _paletteOpen;

		private CursorLockMode _previousCursorLock;

		private bool _previousCursorVisible;

		private void Awake()
		{
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			MaxCopies = ((BaseUnityPlugin)this).Config.Bind<int>("Shop", "ScoutHaulerMaxCopies", 10, "Maximum number of Scouts or Haulers allowed by their Item shop/purchase limits. Minimum effective value is 2.");
			ShopPoolCopies = ((BaseUnityPlugin)this).Config.Bind<int>("Shop", "ScoutHaulerShopPoolCopies", 5, "How many copies of each Scout/Hauler Item are placed in the normal shop candidate pool. Range 1-10. This makes Bike Gang self-contained for vehicle duplicates.");
			EnableVehicleColors = ((BaseUnityPlugin)this).Config.Bind<bool>("Vehicle Colors", "EnableVehicleColors", true, "Enable the per-vehicle Scout/Hauler color palette.");
			PaletteKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Vehicle Colors", "PaletteKey", (KeyCode)99, "Key used to open/close the color palette while holding a Scout or Hauler.");
			ExteriorPaintOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Vehicle Colors", "ExteriorPaintOnly", true, "When true, recolor only likely exterior/body paint renderers. Set false to use the broader 1.2.0-style recoloring.");
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLogging", false, "Enable verbose logging.");
			_harmony = new Harmony("CandyKane.PrincessUsasBikeGangMod");
			_harmony.PatchAll();
			((MonoBehaviour)this).StartCoroutine(ApplyWhenReady());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Princess Usa's Bike Gang Mod 1.3.0 loaded. Multi-buy shop logic and per-vehicle color palette enabled.");
		}

		private void Update()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			MultiplayerColorSync.Tick();
			if (!EnableVehicleColors.Value)
			{
				if (_paletteOpen)
				{
					ClosePalette();
				}
			}
			else if (Input.GetKeyDown((KeyCode)27) && _paletteOpen)
			{
				ClosePalette();
			}
			else
			{
				if (!Input.GetKeyDown(PaletteKey.Value))
				{
					return;
				}
				if (_paletteOpen)
				{
					ClosePalette();
					return;
				}
				PhysGrabObject val = VehicleColorSystem.FindHeldVehicle();
				if ((Object)(object)val == (Object)null)
				{
					if (DebugLogging.Value)
					{
						Log.LogDebug((object)$"Palette key {PaletteKey.Value} pressed, but no held Scout/Hauler was found.");
					}
					return;
				}
				_paletteTarget = val;
				_paletteOpen = true;
				_previousCursorLock = Cursor.lockState;
				_previousCursorVisible = Cursor.visible;
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
				Log.LogInfo((object)("Opened color palette for " + ((Object)val).name + "."));
			}
		}

		private void OnGUI()
		{
			if (_paletteOpen && !((Object)(object)_paletteTarget == (Object)null))
			{
				VehicleColorPalette.Draw(_paletteTarget, ClosePalette);
			}
		}

		private void ClosePalette()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (_paletteOpen)
			{
				_paletteOpen = false;
				_paletteTarget = null;
				Cursor.lockState = _previousCursorLock;
				Cursor.visible = _previousCursorVisible;
			}
		}

		private IEnumerator ApplyWhenReady()
		{
			for (int i = 0; i < 120; i++)
			{
				if (VehicleShopLimits.Apply())
				{
					yield break;
				}
				yield return (object)new WaitForSeconds(0.5f);
			}
			Log.LogWarning((object)"Could not find StatsManager/itemDictionary during startup. Shop patches will retry when the shop initializes.");
		}
	}
	internal static class VehicleIdentity
	{
		internal static bool IsScoutOrHauler(string key, Item item)
		{
			if (LooksLikeVehicleName(key) || LooksLikeVehicleName(((Object)item).name))
			{
				return true;
			}
			FieldInfo[] fields = ((object)item).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				try
				{
					object value = fieldInfo.GetValue(item);
					if (value is string text && LooksLikeVehicleName(text))
					{
						return true;
					}
					Object val = (Object)((value is Object) ? value : null);
					if (val != null && val != (Object)null && LooksLikeVehicleName(val.name))
					{
						return true;
					}
				}
				catch
				{
				}
			}
			return false;
		}

		internal static bool IsScoutOrHauler(Item item)
		{
			if ((Object)(object)item != (Object)null)
			{
				return IsScoutOrHauler(((Object)item).name, item);
			}
			return false;
		}

		private static bool LooksLikeVehicleName(string? text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			string text2 = text.ToLowerInvariant();
			if (!text2.Contains("semiscooter") && !text2.Contains("hauler"))
			{
				return text2.Contains("scout");
			}
			return true;
		}
	}
	internal static class VehicleShopLimits
	{
		internal static bool Apply()
		{
			if ((Object)(object)StatsManager.instance == (Object)null || StatsManager.instance.itemDictionary == null)
			{
				return false;
			}
			if (SemiFunc.IsMultiplayer() && !SemiFunc.IsMasterClient())
			{
				return true;
			}
			int num = Math.Max(2, Plugin.MaxCopies.Value);
			int num2 = 0;
			foreach (KeyValuePair<string, Item> item in StatsManager.instance.itemDictionary)
			{
				Item value = item.Value;
				if (!((Object)(object)value == (Object)null) && VehicleIdentity.IsScoutOrHauler(item.Key, value))
				{
					bool num3 = value.maxAmountInShop != num || value.maxAmount != num || value.maxPurchase || value.maxPurchaseAmount != 0;
					value.maxAmountInShop = num;
					value.maxAmount = num;
					value.maxPurchase = false;
					value.maxPurchaseAmount = 0;
					if (num3)
					{
						num2++;
						Plugin.Log.LogInfo((object)$"Enabled repeat purchase for '{item.Key}' / '{((Object)value).name}' (max copies: {num}).");
					}
					else if (Plugin.DebugLogging.Value)
					{
						Plugin.Log.LogDebug((object)("Scout/Hauler limits already patched for '" + item.Key + "' / '" + ((Object)value).name + "'."));
					}
				}
			}
			if (Plugin.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)$"Vehicle shop limit pass complete; {num2} item(s) changed.");
			}
			return true;
		}
	}
	internal static class VehicleShopPool
	{
		private const string PotentialItemsField = "potentialItems";

		internal static void Apply(ShopManager manager, string source)
		{
			if ((Object)(object)manager == (Object)null || (SemiFunc.IsMultiplayer() && !SemiFunc.IsMasterClient()))
			{
				return;
			}
			VehicleShopLimits.Apply();
			FieldInfo field = ((object)manager).GetType().GetField("potentialItems", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null)
			{
				if (Plugin.DebugLogging.Value)
				{
					Plugin.Log.LogWarning((object)("Could not find ShopManager.potentialItems during " + source + "."));
				}
				return;
			}
			if (!(field.GetValue(manager) is IList list))
			{
				if (Plugin.DebugLogging.Value)
				{
					Plugin.Log.LogWarning((object)("ShopManager.potentialItems was not an IList during " + source + "."));
				}
				return;
			}
			if (list.IsReadOnly || list.IsFixedSize)
			{
				Plugin.Log.LogWarning((object)"ShopManager.potentialItems cannot be expanded; Bike Gang vehicle duplicates were not added.");
				return;
			}
			int num = Math.Max(1, Math.Min(10, Plugin.ShopPoolCopies.Value));
			List<Item> list2 = new List<Item>();
			Dictionary<Item, int> dictionary = new Dictionary<Item, int>();
			for (int i = 0; i < list.Count; i++)
			{
				object? obj = list[i];
				Item val = (Item)((obj is Item) ? obj : null);
				if (val != null && !((Object)(object)val == (Object)null) && VehicleIdentity.IsScoutOrHauler(val))
				{
					if (!dictionary.ContainsKey(val))
					{
						dictionary[val] = 0;
						list2.Add(val);
					}
					dictionary[val]++;
				}
			}
			int num2 = 0;
			foreach (Item item in list2)
			{
				int num3 = dictionary[item];
				int num4 = Math.Max(0, num - num3);
				for (int j = 0; j < num4; j++)
				{
					list.Add(item);
					num2++;
				}
				if (num4 > 0 || Plugin.DebugLogging.Value)
				{
					Plugin.Log.LogInfo((object)$"Bike Gang shop pool: '{((Object)item).name}' {num3} -> {num3 + num4} candidate copies (target {num}).");
				}
			}
			if (list2.Count == 0 && Plugin.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)("No Scout/Hauler Item was present in potentialItems during " + source + "; nothing to top up."));
			}
			else if (Plugin.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)$"Bike Gang {source} pool pass complete; added {num2} vehicle candidate entries.");
			}
		}
	}
	internal static class VehicleColorSystem
	{
		private static readonly Dictionary<int, Dictionary<Material, Color>> OriginalColors = new Dictionary<int, Dictionary<Material, Color>>();

		private static readonly string[] NonPaintWords = new string[48]
		{
			"wheel", "tire", "tyre", "glass", "window", "light", "lamp", "emiss", "seat", "screen",
			"display", "handle", "grip", "steer", "pedal", "brake", "suspension", "spring", "shock", "axle",
			"hub", "bearing", "motor", "engine", "battery", "cable", "wire", "chain", "gear", "drive",
			"drivetrain", "exhaust", "pipe", "hose", "bolt", "screw", "joint", "collider", "trigger", "audio",
			"shadow", "decal", "logo", "text", "ui", "icon", "floor", "ground"
		};

		private static readonly string[] PaintWords = new string[18]
		{
			"body", "paint", "shell", "panel", "fairing", "fender", "mudguard", "cover", "cowling", "chassis",
			"frame", "hood", "bonnet", "bumper", "trim", "scooter", "semiscooter", "vehicle"
		};

		internal static PhysGrabObject? FindHeldVehicle()
		{
			PhysGrabObject[] array = Object.FindObjectsOfType<PhysGrabObject>();
			foreach (PhysGrabObject val in array)
			{
				if (!((Object)(object)val == (Object)null) && IsHeldByLocalPlayer(val) && IsVehicleObject(val))
				{
					return val;
				}
			}
			return null;
		}

		private static bool IsVehicleObject(PhysGrabObject pgo)
		{
			Transform val = ((Component)pgo).transform;
			int num = 0;
			while ((Object)(object)val != (Object)null && num < 5)
			{
				if (LooksLikeVehicleName(((Object)val).name ?? string.Empty))
				{
					return true;
				}
				num++;
				val = val.parent;
			}
			return false;
		}

		private static bool LooksLikeVehicleName(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			string text2 = text.ToLowerInvariant();
			if (!text2.Contains("semiscooter") && !text2.Contains("hauler"))
			{
				return text2.Contains("scout");
			}
			return true;
		}

		private static bool IsHeldByLocalPlayer(PhysGrabObject pgo)
		{
			try
			{
				Type type = ((object)pgo).GetType();
				FieldInfo fieldInfo = AccessTools.Field(type, "grabbedLocal") ?? AccessTools.Field(type, "isGrabbedLocal");
				if (fieldInfo != null && fieldInfo.FieldType == typeof(bool))
				{
					return (bool)fieldInfo.GetValue(pgo);
				}
				PropertyInfo propertyInfo = AccessTools.Property(type, "grabbedLocal") ?? AccessTools.Property(type, "isGrabbedLocal");
				if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool))
				{
					return (bool)propertyInfo.GetValue(pgo, null);
				}
			}
			catch (Exception ex)
			{
				if (Plugin.DebugLogging.Value)
				{
					Plugin.Log.LogDebug((object)("Could not inspect held state for " + ((Object)pgo).name + ": " + ex.Message));
				}
			}
			return false;
		}

		internal static void ApplyColor(PhysGrabObject vehicle, Color chosen)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)vehicle == (Object)null)
			{
				return;
			}
			Transform val = ResolveVehicleRootTransform(((Component)vehicle).transform);
			int instanceID = ((Object)val).GetInstanceID();
			if (!OriginalColors.TryGetValue(instanceID, out Dictionary<Material, Color> value))
			{
				value = new Dictionary<Material, Color>();
				OriginalColors[instanceID] = value;
			}
			Renderer[] componentsInChildren = ((Component)val).GetComponentsInChildren<Renderer>(true);
			float num = 0f;
			Renderer[] array = componentsInChildren;
			foreach (Renderer val2 in array)
			{
				if (!((Object)(object)val2 == (Object)null) && !ShouldSkipRenderer(val2))
				{
					Bounds bounds = val2.bounds;
					Vector3 size = ((Bounds)(ref bounds)).size;
					float sqrMagnitude = ((Vector3)(ref size)).sqrMagnitude;
					if (sqrMagnitude > num)
					{
						num = sqrMagnitude;
					}
				}
			}
			int num2 = 0;
			int num3 = 0;
			array = componentsInChildren;
			Color val6 = default(Color);
			foreach (Renderer val3 in array)
			{
				if ((Object)(object)val3 == (Object)null || ShouldSkipRenderer(val3))
				{
					num3++;
					continue;
				}
				Material[] materials;
				try
				{
					materials = val3.materials;
				}
				catch
				{
					num3++;
					continue;
				}
				Material[] array2 = materials;
				foreach (Material val4 in array2)
				{
					if ((Object)(object)val4 == (Object)null || ShouldSkipMaterial(val4))
					{
						num3++;
						continue;
					}
					if (!val4.HasProperty("_Color") && !val4.HasProperty("_BaseColor"))
					{
						num3++;
						continue;
					}
					if (Plugin.ExteriorPaintOnly.Value && !LooksLikeExteriorPaint(val3, val4, num))
					{
						num3++;
						continue;
					}
					if (!value.ContainsKey(val4))
					{
						Color value2 = (val4.HasProperty("_BaseColor") ? val4.GetColor("_BaseColor") : val4.color);
						value[val4] = value2;
					}
					Color val5 = value[val4];
					((Color)(ref val6))..ctor(chosen.r, chosen.g, chosen.b, val5.a);
					if (val4.HasProperty("_BaseColor"))
					{
						val4.SetColor("_BaseColor", val6);
					}
					if (val4.HasProperty("_Color"))
					{
						val4.SetColor("_Color", val6);
					}
					num2++;
					if (Plugin.DebugLogging.Value)
					{
						Plugin.Log.LogDebug((object)("Painted renderer '" + GetTransformPath(((Component)val3).transform) + "' material '" + ((Object)val4).name + "'."));
					}
				}
			}
			Plugin.Log.LogInfo((object)$"Recolored {((Object)val).name}: updated {num2} exterior material slot(s), skipped {num3} non-paint slot(s). ExteriorPaintOnly={Plugin.ExteriorPaintOnly.Value}. RootResolvedFrom={((Object)vehicle).name}.");
			if (num2 == 0 && Plugin.ExteriorPaintOnly.Value)
			{
				Plugin.Log.LogWarning((object)("No likely exterior paint renderers were found on " + ((Object)val).name + ". Set Vehicle Colors / ExteriorPaintOnly = false to use broad recoloring and send the log so the body filter can be refined."));
			}
		}

		internal static void RestoreOriginal(PhysGrabObject vehicle)
		{
			//IL_0057: 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_0073: 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 ((Object)(object)vehicle == (Object)null)
			{
				return;
			}
			Transform val = ResolveVehicleRootTransform(((Component)vehicle).transform);
			int instanceID = ((Object)val).GetInstanceID();
			if (!OriginalColors.TryGetValue(instanceID, out Dictionary<Material, Color> value))
			{
				return;
			}
			int num = 0;
			foreach (KeyValuePair<Material, Color> item in value)
			{
				Material key = item.Key;
				if (!((Object)(object)key == (Object)null))
				{
					Color value2 = item.Value;
					if (key.HasProperty("_BaseColor"))
					{
						key.SetColor("_BaseColor", value2);
					}
					if (key.HasProperty("_Color"))
					{
						key.SetColor("_Color", value2);
					}
					num++;
				}
			}
			Plugin.Log.LogInfo((object)$"Restored original colors for {((Object)val).name}: {num} material slot(s). RootResolvedFrom={((Object)vehicle).name}.");
		}

		private static Transform ResolveVehicleRootTransform(Transform start)
		{
			Transform val = start;
			Transform val2 = null;
			int num = 0;
			while ((Object)(object)val != (Object)null && num++ < 16)
			{
				if (LooksLikeVehicleName(((Object)val).name ?? string.Empty))
				{
					val2 = val;
				}
				val = val.parent;
			}
			if ((Object)(object)val2 != (Object)null)
			{
				if (Plugin.DebugLogging.Value && (Object)(object)val2 != (Object)(object)start)
				{
					Plugin.Log.LogDebug((object)("Resolved held object '" + ((Object)start).name + "' to vehicle root '" + ((Object)val2).name + "'."));
				}
				return val2;
			}
			if (Plugin.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)("Could not resolve a named vehicle root above '" + ((Object)start).name + "'; using held object as root."));
			}
			return start;
		}

		private static bool LooksLikeExteriorPaint(Renderer renderer, Material mat, float largestRendererSize)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			string text = ((Object)((Component)renderer).gameObject).name.ToLowerInvariant();
			string text2 = ((Object)mat).name.ToLowerInvariant();
			string text3 = GetTransformPath(((Component)renderer).transform).ToLowerInvariant();
			if (ContainsAny(text, NonPaintWords) || ContainsAny(text2, NonPaintWords))
			{
				return false;
			}
			if (ContainsAny(text, PaintWords) || ContainsAny(text2, PaintWords))
			{
				return true;
			}
			bool num = text3.Contains("semiscooter") || text3.Contains("hauler") || text3.Contains("scout");
			Bounds bounds = renderer.bounds;
			Vector3 size = ((Bounds)(ref bounds)).size;
			float sqrMagnitude = ((Vector3)(ref size)).sqrMagnitude;
			bool flag = largestRendererSize > 0f && sqrMagnitude >= largestRendererSize * 0.16f;
			return num && flag;
		}

		private static bool ShouldSkipRenderer(Renderer renderer)
		{
			return ContainsAny(((Object)((Component)renderer).gameObject).name.ToLowerInvariant(), NonPaintWords);
		}

		private static bool ShouldSkipMaterial(Material mat)
		{
			return ContainsAny(((Object)mat).name.ToLowerInvariant(), NonPaintWords);
		}

		private static bool ContainsAny(string text, string[] words)
		{
			foreach (string value in words)
			{
				if (text.Contains(value))
				{
					return true;
				}
			}
			return false;
		}

		private static string GetTransformPath(Transform t)
		{
			string text = ((Object)t).name;
			Transform parent = t.parent;
			int num = 0;
			while ((Object)(object)parent != (Object)null && num++ < 8)
			{
				text = ((Object)parent).name + "/" + text;
				parent = parent.parent;
			}
			return text;
		}
	}
	internal static class MultiplayerColorSync
	{
		private sealed class PendingColor
		{
			internal readonly int ViewId;

			internal readonly int SwatchIndex;

			internal readonly float ExpiresAt;

			internal PendingColor(int viewId, int swatchIndex, float expiresAt)
			{
				ViewId = viewId;
				SwatchIndex = swatchIndex;
				ExpiresAt = expiresAt;
			}
		}

		private const byte EventCode = 196;

		private const int ProtocolMagic = 1112885059;

		private const int ProtocolVersion = 1;

		private const int ActionRestore = -1;

		private static readonly List<PendingColor> Pending = new List<PendingColor>();

		private static float _nextRetry;

		internal static void BroadcastColor(PhysGrabObject vehicle, int swatchIndex)
		{
			if (SemiFunc.IsMultiplayer())
			{
				int num = FindVehicleViewId(vehicle);
				if (num <= 0)
				{
					Plugin.Log.LogWarning((object)("Could not find a PhotonView ID for " + ((Object)vehicle).name + "; color changed locally but was not synchronized."));
				}
				else
				{
					Send(num, swatchIndex);
				}
			}
		}

		internal static void BroadcastRestore(PhysGrabObject vehicle)
		{
			if (SemiFunc.IsMultiplayer())
			{
				int num = FindVehicleViewId(vehicle);
				if (num <= 0)
				{
					Plugin.Log.LogWarning((object)("Could not find a PhotonView ID for " + ((Object)vehicle).name + "; restore happened locally but was not synchronized."));
				}
				else
				{
					Send(num, -1);
				}
			}
		}

		private static void Send(int viewId, int swatchIndex)
		{
			try
			{
				Type type = AccessTools.TypeByName("Photon.Pun.PhotonNetwork");
				Type type2 = AccessTools.TypeByName("Photon.Realtime.RaiseEventOptions");
				Type type3 = AccessTools.TypeByName("Photon.Realtime.ReceiverGroup");
				Type type4 = AccessTools.TypeByName("Photon.Realtime.EventCaching");
				Type type5 = AccessTools.TypeByName("ExitGames.Client.Photon.SendOptions");
				if (type == null || type2 == null || type3 == null || type5 == null)
				{
					throw new InvalidOperationException("Photon networking types were not found.");
				}
				MethodInfo methodInfo = null;
				MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public);
				foreach (MethodInfo methodInfo2 in methods)
				{
					if (!(methodInfo2.Name != "RaiseEvent"))
					{
						ParameterInfo[] parameters = methodInfo2.GetParameters();
						if (parameters.Length == 4 && parameters[0].ParameterType == typeof(byte))
						{
							methodInfo = methodInfo2;
							break;
						}
					}
				}
				if (methodInfo == null)
				{
					throw new MissingMethodException("PhotonNetwork.RaiseEvent");
				}
				object obj = Activator.CreateInstance(type2);
				PropertyInfo property = type2.GetProperty("Receivers", BindingFlags.Instance | BindingFlags.Public);
				if (property != null)
				{
					property.SetValue(obj, Enum.Parse(type3, "All"), null);
				}
				if (type4 != null)
				{
					PropertyInfo property2 = type2.GetProperty("CachingOption", BindingFlags.Instance | BindingFlags.Public);
					if (property2 != null)
					{
						object obj2 = null;
						try
						{
							obj2 = Enum.Parse(type4, "AddToRoomCacheGlobal");
						}
						catch
						{
							try
							{
								obj2 = Enum.Parse(type4, "AddToRoomCache");
							}
							catch
							{
							}
						}
						if (obj2 != null)
						{
							property2.SetValue(obj, obj2, null);
						}
					}
				}
				object obj5 = Activator.CreateInstance(type5);
				PropertyInfo property3 = type5.GetProperty("Reliability", BindingFlags.Instance | BindingFlags.Public);
				if (property3 != null && property3.CanWrite)
				{
					property3.SetValue(obj5, true, null);
				}
				else
				{
					PropertyInfo property4 = type5.GetProperty("SendReliable", BindingFlags.Static | BindingFlags.Public);
					if (property4 != null)
					{
						obj5 = property4.GetValue(null, null);
					}
				}
				object[] array = new object[4] { 1112885059, 1, viewId, swatchIndex };
				object obj6 = methodInfo.Invoke(null, new object[4]
				{
					(byte)196,
					array,
					obj,
					obj5
				});
				Plugin.Log.LogInfo((object)string.Format("Synced vehicle color choice to room: viewID={0}, choice={1}, sent={2}.", viewId, (swatchIndex == -1) ? "RestoreOriginal" : swatchIndex.ToString(), obj6 ?? ((object)true)));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Vehicle color sync send failed: " + ex.GetBaseException().Message));
			}
		}

		internal static void HandlePhotonEvent(object? photonEvent)
		{
			if (photonEvent == null)
			{
				return;
			}
			try
			{
				Type type = photonEvent.GetType();
				PropertyInfo property = type.GetProperty("Code", BindingFlags.Instance | BindingFlags.Public);
				if (property == null || Convert.ToByte(property.GetValue(photonEvent, null)) != 196 || !(type.GetProperty("CustomData", BindingFlags.Instance | BindingFlags.Public)?.GetValue(photonEvent, null) is object[] array) || array.Length < 4 || Convert.ToInt32(array[0]) != 1112885059 || Convert.ToInt32(array[1]) != 1)
				{
					return;
				}
				int viewId = Convert.ToInt32(array[2]);
				int swatchIndex = Convert.ToInt32(array[3]);
				if (!TryApplyRemote(viewId, swatchIndex))
				{
					Pending.RemoveAll((PendingColor p) => p.ViewId == viewId);
					Pending.Add(new PendingColor(viewId, swatchIndex, Time.realtimeSinceStartup + 20f));
					if (Plugin.DebugLogging.Value)
					{
						Plugin.Log.LogDebug((object)$"Queued synchronized vehicle color until viewID {viewId} finishes spawning.");
					}
				}
			}
			catch (Exception ex)
			{
				if (Plugin.DebugLogging.Value)
				{
					Plugin.Log.LogDebug((object)("Ignored malformed Bike Gang color event: " + ex.GetBaseException().Message));
				}
			}
		}

		internal static void Tick()
		{
			if (Pending.Count == 0 || Time.realtimeSinceStartup < _nextRetry)
			{
				return;
			}
			_nextRetry = Time.realtimeSinceStartup + 0.35f;
			for (int num = Pending.Count - 1; num >= 0; num--)
			{
				PendingColor pendingColor = Pending[num];
				if (Time.realtimeSinceStartup > pendingColor.ExpiresAt || TryApplyRemote(pendingColor.ViewId, pendingColor.SwatchIndex))
				{
					Pending.RemoveAt(num);
				}
			}
		}

		private static bool TryApplyRemote(int viewId, int swatchIndex)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			PhysGrabObject val = FindVehicleByViewId(viewId);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if (swatchIndex == -1)
			{
				VehicleColorSystem.RestoreOriginal(val);
				Plugin.Log.LogInfo((object)$"Applied synchronized Restore Original to {((Object)val).name} (viewID={viewId}).");
				return true;
			}
			if (!VehicleColorPalette.TryGetSwatchColor(swatchIndex, out var color))
			{
				return true;
			}
			VehicleColorSystem.ApplyColor(val, color);
			Plugin.Log.LogInfo((object)$"Applied synchronized vehicle color to {((Object)val).name} (viewID={viewId}, swatch={swatchIndex}).");
			return true;
		}

		private static int FindVehicleViewId(PhysGrabObject vehicle)
		{
			if ((Object)(object)vehicle == (Object)null)
			{
				return 0;
			}
			Transform val = ResolveNamedVehicleRoot(((Component)vehicle).transform);
			int num = FindPhotonViewId(((Component)val).gameObject, includeChildren: true);
			if (num > 0)
			{
				return num;
			}
			Transform parent = val.parent;
			int num2 = 0;
			while ((Object)(object)parent != (Object)null && num2 < 4)
			{
				num = FindPhotonViewId(((Component)parent).gameObject, includeChildren: false);
				if (num > 0)
				{
					return num;
				}
				num2++;
				parent = parent.parent;
			}
			return 0;
		}

		private static PhysGrabObject? FindVehicleByViewId(int viewId)
		{
			PhysGrabObject[] array = Object.FindObjectsOfType<PhysGrabObject>();
			foreach (PhysGrabObject val in array)
			{
				if (!((Object)(object)val == (Object)null) && LooksLikeVehicleHierarchy(((Component)val).transform) && FindVehicleViewId(val) == viewId)
				{
					return val;
				}
			}
			return null;
		}

		private static int FindPhotonViewId(GameObject go, bool includeChildren)
		{
			Component[] array = (includeChildren ? go.GetComponentsInChildren<Component>(true) : go.GetComponents<Component>());
			foreach (Component val in array)
			{
				if (!((Object)(object)val == (Object)null) && !(((object)val).GetType().FullName != "Photon.Pun.PhotonView"))
				{
					PropertyInfo property = ((object)val).GetType().GetProperty("ViewID", BindingFlags.Instance | BindingFlags.Public);
					if (property != null)
					{
						return Convert.ToInt32(property.GetValue(val, null));
					}
				}
			}
			return 0;
		}

		private static Transform ResolveNamedVehicleRoot(Transform start)
		{
			Transform val = start;
			Transform val2 = null;
			int num = 0;
			while ((Object)(object)val != (Object)null && num < 16)
			{
				string text = (((Object)val).name ?? string.Empty).ToLowerInvariant();
				if (text.Contains("semiscooter") || text.Contains("hauler") || text.Contains("scout"))
				{
					val2 = val;
				}
				num++;
				val = val.parent;
			}
			return val2 ?? start;
		}

		private static bool LooksLikeVehicleHierarchy(Transform start)
		{
			Transform val = start;
			int num = 0;
			while ((Object)(object)val != (Object)null && num < 12)
			{
				string text = (((Object)val).name ?? string.Empty).ToLowerInvariant();
				if (text.Contains("semiscooter") || text.Contains("hauler") || text.Contains("scout"))
				{
					return true;
				}
				num++;
				val = val.parent;
			}
			return false;
		}
	}
	[HarmonyPatch]
	internal static class PhotonColorEventPatch
	{
		private static MethodBase? TargetMethod()
		{
			Type type = AccessTools.TypeByName("Photon.Realtime.LoadBalancingClient");
			if (type == null)
			{
				Plugin.Log.LogWarning((object)"Photon LoadBalancingClient type was not found; multiplayer vehicle colors will be local-only.");
				return null;
			}
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!(methodInfo.Name != "OnEvent") && methodInfo.GetParameters().Length == 1)
				{
					return methodInfo;
				}
			}
			Plugin.Log.LogWarning((object)"Photon LoadBalancingClient.OnEvent was not found; multiplayer vehicle colors will be local-only.");
			return null;
		}

		private static void Postfix(object __0)
		{
			MultiplayerColorSync.HandlePhotonEvent(__0);
		}
	}
	internal static class VehicleColorPalette
	{
		private readonly struct Swatch
		{
			internal readonly string Name;

			internal readonly Color Color;

			internal Swatch(string name, Color color)
			{
				//IL_0008: 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)
				Name = name;
				Color = color;
			}
		}

		private static readonly Swatch[] Swatches = new Swatch[12]
		{
			new Swatch("Pink", new Color(1f, 0.25f, 0.62f)),
			new Swatch("Purple", new Color(0.62f, 0.28f, 1f)),
			new Swatch("Blue", new Color(0.18f, 0.48f, 1f)),
			new Swatch("Cyan", new Color(0.1f, 0.88f, 1f)),
			new Swatch("Green", new Color(0.2f, 0.85f, 0.35f)),
			new Swatch("Yellow", new Color(1f, 0.86f, 0.15f)),
			new Swatch("Orange", new Color(1f, 0.45f, 0.1f)),
			new Swatch("Red", new Color(0.95f, 0.15f, 0.15f)),
			new Swatch("White", new Color(0.95f, 0.95f, 0.95f)),
			new Swatch("Black", new Color(0.08f, 0.08f, 0.08f)),
			new Swatch("Gold", new Color(1f, 0.65f, 0.1f)),
			new Swatch("Mint", new Color(0.45f, 1f, 0.72f))
		};

		private static Type? _guiType;

		private static MethodInfo? _box;

		private static MethodInfo? _button;

		private static PropertyInfo? _guiColor;

		private static bool _reflectionReady;

		internal static void Draw(PhysGrabObject vehicle, Action close)
		{
			//IL_003a: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			if (!EnsureGuiReflection())
			{
				return;
			}
			float num = ((float)Screen.width - 430f) * 0.5f;
			float num2 = ((float)Screen.height - 310f) * 0.5f;
			Box(new Rect(num, num2, 430f, 310f), "Princess Usa's Bike Gang - Vehicle Colors");
			float num3 = 92f;
			float num4 = 48f;
			float num5 = num + 20f;
			float num6 = num2 + 45f;
			float num7 = 8f;
			float num8 = 10f;
			for (int i = 0; i < Swatches.Length; i++)
			{
				int num9 = i / 4;
				int num10 = i % 4;
				Rect rect = new Rect(num5 + (float)num10 * (num3 + num7), num6 + (float)num9 * (num4 + num8), num3, num4);
				Color guiColor = GetGuiColor();
				SetGuiColor(Swatches[i].Color);
				bool num11 = Button(rect, Swatches[i].Name);
				SetGuiColor(guiColor);
				if (num11)
				{
					VehicleColorSystem.ApplyColor(vehicle, Swatches[i].Color);
					MultiplayerColorSync.BroadcastColor(vehicle, i);
					close();
					return;
				}
			}
			SetGuiColor(Color.white);
			if (Button(new Rect(num + 20f, num2 + 230f, 190f, 45f), "Restore Original"))
			{
				VehicleColorSystem.RestoreOriginal(vehicle);
				MultiplayerColorSync.BroadcastRestore(vehicle);
				close();
			}
			else if (Button(new Rect(num + 220f, num2 + 230f, 190f, 45f), "Cancel"))
			{
				close();
			}
		}

		internal static bool TryGetSwatchColor(int index, out Color color)
		{
			//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_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 (index >= 0 && index < Swatches.Length)
			{
				color = Swatches[index].Color;
				return true;
			}
			color = Color.white;
			return false;
		}

		private static bool EnsureGuiReflection()
		{
			if (_reflectionReady)
			{
				return _guiType != null;
			}
			_reflectionReady = true;
			try
			{
				_guiType = Type.GetType("UnityEngine.GUI, UnityEngine.IMGUIModule");
				if (_guiType == null)
				{
					Plugin.Log.LogError((object)"Unity IMGUI module was not available; vehicle color palette cannot be drawn.");
					return false;
				}
				_box = _guiType.GetMethod("Box", new Type[2]
				{
					typeof(Rect),
					typeof(string)
				});
				_button = _guiType.GetMethod("Button", new Type[2]
				{
					typeof(Rect),
					typeof(string)
				});
				_guiColor = _guiType.GetProperty("color", BindingFlags.Static | BindingFlags.Public);
				if (_box == null || _button == null || _guiColor == null)
				{
					Plugin.Log.LogError((object)"Could not bind Unity GUI methods for Bike Gang color palette.");
					return false;
				}
				return true;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to initialize Bike Gang color palette: {arg}");
				return false;
			}
		}

		private static void Box(Rect rect, string text)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			_box.Invoke(null, new object[2] { rect, text });
		}

		private static bool Button(Rect rect, string text)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			return (bool)_button.Invoke(null, new object[2] { rect, text });
		}

		private static Color GetGuiColor()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			return (Color)_guiColor.GetValue(null, null);
		}

		private static void SetGuiColor(Color color)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			_guiColor.SetValue(null, color, null);
		}
	}
	[HarmonyPatch(typeof(ShopManager), "ShopInitialize")]
	internal static class ShopInitializePatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			VehicleShopLimits.Apply();
		}

		[HarmonyPostfix]
		private static void Postfix(ShopManager __instance)
		{
			VehicleShopLimits.Apply();
			VehicleShopPool.Apply(__instance, "ShopInitialize");
		}
	}
	[HarmonyPatch(typeof(ShopManager), "GetAllItemsFromStatsManager")]
	internal static class ShopItemPoolPatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			VehicleShopLimits.Apply();
		}

		[HarmonyPostfix]
		private static void Postfix(ShopManager __instance)
		{
			VehicleShopLimits.Apply();
			VehicleShopPool.Apply(__instance, "GetAllItemsFromStatsManager");
		}
	}
}