Decompiled source of LocalGRACE v1.1.3

LocalGRACE.dll

Decompiled 21 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using CommonAPI.Phone;
using HarmonyLib;
using Reptile;
using Reptile.Phone;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("LocalGRACE")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("LocalGRACE")]
[assembly: AssemblyTitle("LocalGRACE")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace LocalGRACE
{
	public static class CompressionUtil
	{
		private static readonly MD5 Hasher = MD5.Create();

		public static int HashString(string s)
		{
			return BitConverter.ToInt32(Hasher.ComputeHash(Encoding.UTF8.GetBytes(s)), 0);
		}
	}
	public static class GameAPI
	{
		private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;

		private static MethodInfo _cachedSceneNameToStageMethod;

		public static object GetSceneObjectsRegister(WorldHandler worldHandler)
		{
			if ((Object)(object)worldHandler == (Object)null)
			{
				return null;
			}
			return GetFieldOrProp(worldHandler, "sceneObjectsRegister", "SceneObjectsRegister");
		}

		public static IList GetStageChunks(WorldHandler worldHandler)
		{
			object sceneObjectsRegister = GetSceneObjectsRegister(worldHandler);
			if (sceneObjectsRegister == null)
			{
				return null;
			}
			return GetFieldOrProp(sceneObjectsRegister, "stageChunks", "StageChunks") as IList;
		}

		public static List<GraffitiSpot> GetGrafSpots(WorldHandler worldHandler)
		{
			if ((Object)(object)worldHandler == (Object)null)
			{
				return null;
			}
			object sceneObjectsRegister = GetSceneObjectsRegister(worldHandler);
			if (sceneObjectsRegister == null)
			{
				return null;
			}
			object obj = GetFieldOrProp(sceneObjectsRegister, "grafSpots", "GrafSpots") ?? GetFieldOrProp(sceneObjectsRegister, "graffitiSpots", "GraffitiSpots") ?? GetGrafSpotsFromRegisterFallback(sceneObjectsRegister);
			if (obj == null)
			{
				return null;
			}
			if (obj is List<GraffitiSpot> result)
			{
				return result;
			}
			if (obj is Array array)
			{
				List<GraffitiSpot> list = new List<GraffitiSpot>();
				{
					foreach (object item in array)
					{
						GraffitiSpot val = (GraffitiSpot)((item is GraffitiSpot) ? item : null);
						if (val != null)
						{
							list.Add(val);
						}
					}
					return list;
				}
			}
			if (obj is IList list2)
			{
				List<GraffitiSpot> list3 = new List<GraffitiSpot>();
				for (int i = 0; i < list2.Count; i++)
				{
					object? obj2 = list2[i];
					GraffitiSpot val2 = (GraffitiSpot)((obj2 is GraffitiSpot) ? obj2 : null);
					if (val2 != null)
					{
						list3.Add(val2);
					}
				}
				return list3;
			}
			if (obj is IEnumerable enumerable)
			{
				List<GraffitiSpot> list4 = new List<GraffitiSpot>();
				{
					foreach (object item2 in enumerable)
					{
						GraffitiSpot val3 = (GraffitiSpot)((item2 is GraffitiSpot) ? item2 : null);
						if (val3 != null)
						{
							list4.Add(val3);
						}
					}
					return list4;
				}
			}
			return null;
		}

		public static bool IsValidGraffitiSpotForRace(GraffitiSpot x)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Invalid comparison between Unknown and I4
			try
			{
				if ((Object)(object)x == (Object)null)
				{
					return false;
				}
				if (x is GraffitiSpotFinisher)
				{
					return false;
				}
				if (GetGraffitiSpotBool(x, "deactivateDuringEncounters", "DeactivateDuringEncounters", defaultVal: false))
				{
					return false;
				}
				if ((int)GetGraffitiSpotEnum<AttachType>(x, "attachedTo", "AttachedTo", (AttachType)0) != 0)
				{
					return false;
				}
				if ((int)GetGraffitiSpotEnum<PlayerType>(x, "notAllowedToPaint", "NotAllowedToPaint", (PlayerType)(-1)) == 0)
				{
					return false;
				}
				if ((Object)(object)((Component)x).GetComponentInParent<Rigidbody>(true) != (Object)null)
				{
					return false;
				}
				if ((int)GetGraffitiSpotEnum<ObjectiveID>(x, "beTargetForObjective", "BeTargetForObjective", (ObjectiveID)(-1)) != -1)
				{
					return false;
				}
				if ((Object)(object)((Component)x).GetComponentInParent<ActiveOnChapter>(true) != (Object)null)
				{
					return false;
				}
				return true;
			}
			catch
			{
				return true;
			}
		}

		private static object GetGrafSpotsFromRegisterFallback(object register)
		{
			if (register == null)
			{
				return null;
			}
			Type type = register.GetType();
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			PropertyInfo[] properties = type.GetProperties(bindingAttr);
			foreach (PropertyInfo propertyInfo in properties)
			{
				if (propertyInfo.PropertyType == typeof(List<GraffitiSpot>))
				{
					return propertyInfo.GetValue(register);
				}
				if (propertyInfo.PropertyType.IsGenericType)
				{
					Type[] genericArguments = propertyInfo.PropertyType.GetGenericArguments();
					if (genericArguments.Length == 1 && (genericArguments[0] == typeof(GraffitiSpot) || typeof(GraffitiSpot).IsAssignableFrom(genericArguments[0])))
					{
						return propertyInfo.GetValue(register);
					}
				}
			}
			FieldInfo[] fields = type.GetFields(bindingAttr);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.FieldType == typeof(List<GraffitiSpot>))
				{
					return fieldInfo.GetValue(register);
				}
				if (fieldInfo.FieldType.IsGenericType)
				{
					Type[] genericArguments2 = fieldInfo.FieldType.GetGenericArguments();
					if (genericArguments2.Length == 1 && (genericArguments2[0] == typeof(GraffitiSpot) || typeof(GraffitiSpot).IsAssignableFrom(genericArguments2[0])))
					{
						return fieldInfo.GetValue(register);
					}
				}
			}
			return null;
		}

		private static bool GetGraffitiSpotBool(GraffitiSpot spot, string name1, string name2, bool defaultVal)
		{
			return GetFieldOrProp(spot, name1, name2, defaultVal);
		}

		private static T GetGraffitiSpotEnum<T>(GraffitiSpot spot, string name1, string name2, T defaultVal) where T : struct
		{
			object fieldOrProp = GetFieldOrProp(spot, name1, name2);
			if (fieldOrProp is T)
			{
				return (T)fieldOrProp;
			}
			if (fieldOrProp is int value && typeof(T).IsEnum)
			{
				try
				{
					return (T)Enum.ToObject(typeof(T), value);
				}
				catch
				{
				}
			}
			if (fieldOrProp != null && fieldOrProp.GetType().IsValueType)
			{
				try
				{
					return (T)fieldOrProp;
				}
				catch
				{
				}
			}
			return defaultVal;
		}

		public static GraffitiSpot GetGraffitiGameSpot(GraffitiGame game)
		{
			if ((Object)(object)game == (Object)null)
			{
				return null;
			}
			return GetFieldOrProp<GraffitiSpot>(game, "gSpot", "GSpot");
		}

		public static List<GraffitiSpot> GetPlayerGraffitiSpotsAvailable(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			return GetFieldOrProp<List<GraffitiSpot>>(player, "graffitiSpotsAvailable", "GraffitiSpotsAvailable");
		}

		public static bool GetPlayerUserInputEnabled(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return true;
			}
			return GetFieldOrProp(player, "userInputEnabled", "UserInputEnabled", defaultVal: true);
		}

		public static void SetPlayerUserInputEnabled(Player player, bool value)
		{
			if (!((Object)(object)player == (Object)null))
			{
				SetFieldOrProp(player, "userInputEnabled", "UserInputEnabled", value);
			}
		}

		public static bool GetPlayerIsAI(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			return GetFieldOrProp(player, "isAI", "IsAI", defaultVal: false);
		}

		public static bool PlayerIsBusyOrDead(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return true;
			}
			if (player.IsBusyWithSequence() || player.IsDead())
			{
				return true;
			}
			object fieldOrProp = GetFieldOrProp(player, "ability", "Ability");
			object fieldOrProp2 = GetFieldOrProp(player, "danceAbility", "DanceAbility");
			if (fieldOrProp != null && fieldOrProp2 != null && fieldOrProp == fieldOrProp2)
			{
				return true;
			}
			return GetPlayerIsAI(player);
		}

		public static void SetPlayerGraffitiSpotsAvailableClear(Player player)
		{
			GetPlayerGraffitiSpotsAvailable(player)?.Clear();
		}

		public static GraffitiArt GetRandomGraffitiArt(GraffitiSpot spot, Player player)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)spot == (Object)null || (Object)(object)player == (Object)null)
			{
				return null;
			}
			if ((int)spot.size == 0)
			{
				object fieldOrProp = GetFieldOrProp(spot, "GraffitiArtInfo", "graffitiArtInfo");
				if (fieldOrProp == null)
				{
					return null;
				}
				MethodInfo method = fieldOrProp.GetType().GetMethod("FindByCharacter", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new Type[1] { typeof(Characters) }, null);
				if (method != null && GetFieldOrProp(player, "character", "Character") is Characters val)
				{
					object? obj = method.Invoke(fieldOrProp, new object[1] { val });
					return (GraffitiArt)((obj is GraffitiArt) ? obj : null);
				}
			}
			object fieldOrProp2 = GetFieldOrProp(spot, "GraffitiArtInfo", "graffitiArtInfo");
			if (fieldOrProp2 == null)
			{
				return null;
			}
			if (!(GetFieldOrProp(fieldOrProp2, "graffitiArt", "GraffitiArt") is IEnumerable source))
			{
				return null;
			}
			List<GraffitiArt> list = source.Cast<GraffitiArt>().ToList();
			if (list.Count == 0)
			{
				return null;
			}
			GraffitiSize size = spot.size;
			List<GraffitiArt> list2 = new List<GraffitiArt>();
			foreach (GraffitiArt item in list)
			{
				object fieldOrProp3 = GetFieldOrProp(item, "graffitiSize", "GraffitiSize");
				if (fieldOrProp3 != null && (fieldOrProp3.Equals(size) || (fieldOrProp3 is GraffitiSize val2 && val2 == size)))
				{
					list2.Add(item);
				}
			}
			if (list2.Count == 0)
			{
				return list[Random.Range(0, list.Count)];
			}
			return list2[Random.Range(0, list2.Count)];
		}

		public static void GraffitiSpotPaint(GraffitiSpot spot, Crew crew, GraffitiArt art, object optional = null)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)spot == (Object)null))
			{
				MethodInfo methodInfo = typeof(GraffitiSpot).GetMethod("Paint", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new Type[3]
				{
					typeof(Crew),
					typeof(GraffitiArt),
					typeof(object)
				}, null) ?? typeof(GraffitiSpot).GetMethod("Paint", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
				if (methodInfo != null)
				{
					methodInfo.Invoke(spot, (methodInfo.GetParameters().Length != 3) ? new object[2] { crew, art } : new object[3] { crew, art, optional });
				}
			}
		}

		public static void GraffitiSpotClearPaint(GraffitiSpot spot)
		{
			if (!((Object)(object)spot == (Object)null))
			{
				InvokeMethod(spot, "ClearPaint", "clearPaint");
			}
		}

		public static void PlayerPlaySfxGraffitiComplete(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			object fieldOrProp = GetFieldOrProp(player, "audioManager", "AudioManager");
			if (fieldOrProp == null)
			{
				return;
			}
			MethodInfo methodInfo = fieldOrProp.GetType().GetMethod("PlaySfxGameplay", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new Type[3]
			{
				typeof(SfxCollectionID),
				typeof(AudioClipID),
				typeof(float)
			}, null) ?? fieldOrProp.GetType().GetMethod("PlaySfxGameplay", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			if (methodInfo != null)
			{
				if (methodInfo.GetParameters().Length == 3)
				{
					methodInfo.Invoke(fieldOrProp, new object[3]
					{
						(object)(SfxCollectionID)13,
						(object)(AudioClipID)559,
						0f
					});
				}
				else
				{
					methodInfo.Invoke(fieldOrProp, new object[2]
					{
						(object)(SfxCollectionID)13,
						(object)(AudioClipID)559
					});
				}
			}
		}

		public static void PlayerGrafSpotVisualizationStop(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				InvokeMethod(player, "GrafSpotVisualizationStop", "grafSpotVisualizationStop");
			}
		}

		public static void PlayerStoryObjectVisualizationStop(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				InvokeMethod(player, "StoryObjectVisualizationStop", "storyObjectVisualizationStop");
			}
		}

		public static void SetPlayerGraffitiContextAvailable(Player player, int value)
		{
			if (!((Object)(object)player == (Object)null))
			{
				SetFieldOrProp(player, "graffitiContextAvailable", "GraffitiContextAvailable", value);
			}
		}

		public static PinType GetMapPinType(MapPin pin)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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)pin == (Object)null)
			{
				return (PinType)(-1);
			}
			object fieldOrProp = GetFieldOrProp(pin, "m_pinType", "PinType");
			if (fieldOrProp == null)
			{
				fieldOrProp = GetFieldOrProp(pin, "pinType", null);
			}
			if (fieldOrProp is PinType)
			{
				return (PinType)fieldOrProp;
			}
			if (fieldOrProp == null || !Enum.IsDefined(typeof(PinType), fieldOrProp))
			{
				return (PinType)(-1);
			}
			return (PinType)fieldOrProp;
		}

		public static void MapPinDisable(MapPin pin)
		{
			if (!((Object)(object)pin == (Object)null))
			{
				InvokeMethod(pin, "DisableMapPinGameObject", "disableMapPinGameObject");
			}
		}

		public static object GetProgressableData(AProgressable progressable)
		{
			if ((Object)(object)progressable == (Object)null)
			{
				return null;
			}
			return GetFieldOrProp(progressable, "progressableData", "ProgressableData");
		}

		public static void SetProgressableData(AProgressable progressable, object value)
		{
			if (!((Object)(object)progressable == (Object)null))
			{
				SetFieldOrProp(progressable, "progressableData", "ProgressableData", value);
			}
		}

		public static MapPin MapControllerCreatePin(Mapcontroller mapController, PinType pinType)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)mapController == (Object)null)
			{
				return null;
			}
			object? obj = typeof(Mapcontroller).GetMethod("CreatePin", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new Type[1] { typeof(PinType) }, null)?.Invoke(mapController, new object[1] { pinType });
			return (MapPin)((obj is MapPin) ? obj : null);
		}

		public static ICollection<MapPin> GetMapControllerPins(Mapcontroller mapController)
		{
			if ((Object)(object)mapController == (Object)null)
			{
				return null;
			}
			return GetFieldOrProp<ICollection<MapPin>>(mapController, "m_MapPins", "MapPins");
		}

		public static void SetMapPinValid(MapPin pin, bool value)
		{
			if (!((Object)(object)pin == (Object)null))
			{
				SetFieldOrProp(pin, "isMapPinValid", "IsMapPinValid", value);
			}
		}

		public static float GetPlayerMaxBoostCharge(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 0f;
			}
			return GetFieldOrProp(player, "maxBoostCharge", "MaxBoostCharge", 1f);
		}

		public static Camera GetGameplayCameraCam()
		{
			object gameplayCamera = GetGameplayCamera();
			if (gameplayCamera == null)
			{
				return null;
			}
			return GetFieldOrProp<Camera>(gameplayCamera, "cam", "Cam");
		}

		public static object GetGameplayCamera()
		{
			Type typeFromHandle = typeof(GameplayCamera);
			return (typeFromHandle.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public) ?? typeFromHandle.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(null);
		}

		public static object GetUIManagerGameplay(UIManager uiManager)
		{
			if ((Object)(object)uiManager == (Object)null)
			{
				return null;
			}
			return GetFieldOrProp(uiManager, "gameplay", "Gameplay");
		}

		public static bool IsGameplayScreenActive(UIManager uiManager)
		{
			object uIManagerGameplay = GetUIManagerGameplay(uiManager);
			if (uIManagerGameplay == null)
			{
				return false;
			}
			object fieldOrProp = GetFieldOrProp(uIManagerGameplay, "gameplayScreen", "GameplayScreen");
			object obj = ((fieldOrProp is Component) ? fieldOrProp : null);
			GameObject val = ((obj != null) ? ((Component)obj).gameObject : null);
			if ((Object)(object)val != (Object)null)
			{
				return val.activeInHierarchy;
			}
			return false;
		}

		private static T GetFieldOrProp<T>(object obj, string name1, string name2, T defaultVal = default(T))
		{
			object fieldOrProp = GetFieldOrProp(obj, name1, name2, (object)defaultVal);
			if (fieldOrProp == null && default(T) == null)
			{
				return defaultVal;
			}
			if (fieldOrProp is T)
			{
				return (T)fieldOrProp;
			}
			try
			{
				return (T)Convert.ChangeType(fieldOrProp, typeof(T));
			}
			catch
			{
				return defaultVal;
			}
		}

		private static object GetFieldOrProp(object obj, string name1, string name2, object defaultVal = null)
		{
			if (obj == null)
			{
				return defaultVal;
			}
			Type type2 = ((obj is Type type) ? type : obj.GetType());
			BindingFlags bindingAttr = ((obj is Type) ? (BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
			for (int i = 0; i < 2; i++)
			{
				string text = ((i == 0) ? name1 : name2);
				if (!string.IsNullOrEmpty(text))
				{
					FieldInfo field = type2.GetField(text, bindingAttr);
					if (field != null)
					{
						return field.GetValue((obj is Type) ? null : obj);
					}
					PropertyInfo property = type2.GetProperty(text, bindingAttr);
					if (property != null)
					{
						return property.GetValue((obj is Type) ? null : obj);
					}
				}
			}
			return defaultVal;
		}

		private static void SetFieldOrProp(object obj, string name1, string name2, object value)
		{
			if (obj == null)
			{
				return;
			}
			Type type = obj.GetType();
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			for (int i = 0; i < 2; i++)
			{
				string text = ((i == 0) ? name1 : name2);
				if (!string.IsNullOrEmpty(text))
				{
					FieldInfo field = type.GetField(text, bindingAttr);
					if (field != null)
					{
						field.SetValue(obj, value);
						break;
					}
					PropertyInfo property = type.GetProperty(text, bindingAttr);
					if (property != null && property.CanWrite)
					{
						property.SetValue(obj, value);
						break;
					}
				}
			}
		}

		private static void InvokeMethod(object obj, string name1, string name2)
		{
			if (obj == null)
			{
				return;
			}
			Type type = obj.GetType();
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			for (int i = 0; i < 2; i++)
			{
				string text = ((i == 0) ? name1 : name2);
				if (!string.IsNullOrEmpty(text))
				{
					MethodInfo method = type.GetMethod(text, bindingAttr, null, Type.EmptyTypes, null);
					if (method != null)
					{
						method.Invoke(obj, null);
						break;
					}
				}
			}
		}

		public static int GetCurrentStageId()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Scene activeScene = SceneManager.GetActiveScene();
				string sceneName = ((Scene)(ref activeScene)).name ?? "";
				int currentStageIdFromSceneName = GetCurrentStageIdFromSceneName();
				if (currentStageIdFromSceneName >= 0)
				{
					return currentStageIdFromSceneName;
				}
				return GetCurrentStageIdFromBombRushMP(sceneName);
			}
			catch
			{
				return -1;
			}
		}

		public static bool IsInBombRushMPLobby()
		{
			if (!LocalGRACEPlugin.HasBombRushMP)
			{
				return false;
			}
			try
			{
				Type typeFromAnyAssembly = GetTypeFromAnyAssembly("BombRushMP.Plugin.ClientController");
				if (typeFromAnyAssembly == null)
				{
					return false;
				}
				PropertyInfo property = typeFromAnyAssembly.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);
				if (property == null)
				{
					return false;
				}
				object value = property.GetValue(null);
				if (value == null)
				{
					return false;
				}
				PropertyInfo property2 = typeFromAnyAssembly.GetProperty("ClientLobbyManager", BindingFlags.Instance | BindingFlags.Public);
				if (property2 == null)
				{
					return false;
				}
				object value2 = property2.GetValue(value);
				if (value2 == null)
				{
					return false;
				}
				PropertyInfo property3 = value2.GetType().GetProperty("CurrentLobby", BindingFlags.Instance | BindingFlags.Public);
				if (property3 == null)
				{
					return false;
				}
				return property3.GetValue(value2) != null;
			}
			catch
			{
				return false;
			}
		}

		private static int GetCurrentStageIdFromBombRushMP(string sceneName)
		{
			if (string.IsNullOrEmpty(sceneName))
			{
				return -1;
			}
			try
			{
				MethodInfo methodInfo = FindSceneNameToStageMethod();
				if (methodInfo == null)
				{
					return -1;
				}
				object obj = methodInfo.Invoke(null, new object[1] { sceneName });
				if (obj == null)
				{
					return -1;
				}
				if (obj is Enum value)
				{
					return Convert.ToInt32(value);
				}
				if (obj is int result)
				{
					return result;
				}
				return Convert.ToInt32(obj);
			}
			catch
			{
				return -1;
			}
		}

		private static MethodInfo FindSceneNameToStageMethod()
		{
			if (_cachedSceneNameToStageMethod != null)
			{
				return _cachedSceneNameToStageMethod;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					Type[] exportedTypes = assembly.GetExportedTypes();
					foreach (Type type in exportedTypes)
					{
						try
						{
							MethodInfo method = type.GetMethod("SceneNameToStage", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
							if (method != null && method.ReturnType != typeof(void))
							{
								_cachedSceneNameToStageMethod = method;
								return method;
							}
						}
						catch
						{
						}
					}
				}
				catch
				{
				}
			}
			return null;
		}

		private static Type GetTypeFromAnyAssembly(string typeName)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					Type type = assembly.GetType(typeName);
					if (type != null)
					{
						return type;
					}
				}
				catch
				{
				}
			}
			return null;
		}

		private static int GetCurrentStageIdFromSceneName()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			string text = ((Scene)(ref activeScene)).name ?? "";
			if (string.IsNullOrEmpty(text))
			{
				return -1;
			}
			Type type = typeof(WorldHandler).Assembly.GetType("Reptile.Stage") ?? typeof(WorldHandler).Assembly.GetType("Stage");
			if (type == null || !type.IsEnum)
			{
				return -1;
			}
			string text2 = text.ToLowerInvariant();
			string[] names = Enum.GetNames(type);
			Array values = Enum.GetValues(type);
			int result = -1;
			int num = 0;
			for (int i = 0; i < names.Length; i++)
			{
				string text3 = names[i].ToLowerInvariant();
				if (text3.Length != 0 && (text2.Equals(text3, StringComparison.OrdinalIgnoreCase) || text2.Contains(text3)) && text3.Length > num)
				{
					num = text3.Length;
					result = (int)values.GetValue(i);
				}
			}
			return result;
		}
	}
	public static class GraceStats
	{
		private static string _recordsFilePath;

		private static string _mapStatsFolder;

		private static Dictionary<int, double> _bestTimes = new Dictionary<int, double>();

		public static void Initialize()
		{
			string text = Path.Combine(Paths.BepInExRootPath, "GRACEPlus");
			Directory.CreateDirectory(text);
			_recordsFilePath = Path.Combine(text, "grace_records.txt");
			_mapStatsFolder = Path.Combine(text, "Map Stats");
			Directory.CreateDirectory(_mapStatsFolder);
			LoadRecords();
		}

		private static void LoadRecords()
		{
			if (!File.Exists(_recordsFilePath))
			{
				return;
			}
			try
			{
				string[] array = File.ReadAllLines(_recordsFilePath);
				foreach (string text in array)
				{
					if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("#"))
					{
						string[] array2 = text.Split(new char[1] { '=' });
						if (array2.Length == 2 && int.TryParse(array2[0].Trim(), out var result) && double.TryParse(array2[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
						{
							_bestTimes[result] = result2;
						}
					}
				}
			}
			catch
			{
			}
		}

		private static void SaveRecords()
		{
			try
			{
				List<string> list = new List<string> { "# GRACEPlus Best Times (Solo Mode)", "# Format: StageID=TimeInSeconds", "# Generated automatically - do not edit manually", "" };
				foreach (KeyValuePair<int, double> bestTime in _bestTimes)
				{
					list.Add(string.Format("{0}={1}", bestTime.Key, bestTime.Value.ToString("F1", CultureInfo.InvariantCulture)));
				}
				File.WriteAllLines(_recordsFilePath, list);
			}
			catch
			{
			}
		}

		public static bool CheckAndUpdateBestTime(int stageId, double timeInSeconds, out double? previousBest)
		{
			previousBest = null;
			if (stageId < 0)
			{
				return false;
			}
			bool flag = false;
			if (!_bestTimes.ContainsKey(stageId))
			{
				_bestTimes[stageId] = timeInSeconds;
				flag = true;
			}
			else if (timeInSeconds < _bestTimes[stageId])
			{
				previousBest = _bestTimes[stageId];
				_bestTimes[stageId] = timeInSeconds;
				flag = true;
			}
			if (flag)
			{
				SaveRecords();
			}
			return flag;
		}

		public static void SaveMapStats(int stageId, double timeInSeconds, bool won)
		{
			if (stageId < 0)
			{
				return;
			}
			try
			{
				string path = Path.Combine(_mapStatsFolder, $"Stage_{stageId}.txt");
				string text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
				string text2 = (won ? "WIN" : "LOSS");
				string text3 = string.Format("{0},{1:F1},{2},{3},{4}", text, timeInSeconds, text2, "SOLO", "NORMAL");
				if (!File.Exists(path))
				{
					List<string> contents = new List<string>
					{
						"# GRACEPlus Map Statistics",
						$"# Stage: {stageId}",
						"# Format: Timestamp,Time,Result,GameType,ModeType",
						"# Result: WIN or LOSS",
						"# GameType: MULTIPLAYER or SOLO",
						"# ModeType: TEAM or NORMAL",
						""
					};
					File.WriteAllLines(path, contents);
				}
				File.AppendAllText(path, text3 + Environment.NewLine);
			}
			catch
			{
			}
		}

		public static double? GetBestTime(int stageId)
		{
			if (stageId < 0)
			{
				return null;
			}
			LoadRecords();
			if (!_bestTimes.ContainsKey(stageId))
			{
				return null;
			}
			return _bestTimes[stageId];
		}

		public static double? GetAverageTime(int stageId)
		{
			if (stageId < 0)
			{
				return null;
			}
			string path = Path.Combine(_mapStatsFolder, $"Stage_{stageId}.txt");
			if (!File.Exists(path))
			{
				return null;
			}
			try
			{
				string[] array = File.ReadAllLines(path);
				List<double> list = new List<double>();
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (string.IsNullOrWhiteSpace(text) || text.StartsWith("#"))
					{
						continue;
					}
					string[] array3 = text.Split(new char[1] { ',' });
					if (array3.Length >= 3 && double.TryParse(array3[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && array3[2] == "WIN")
					{
						if (array3.Length >= 5 && array3[4] == "NORMAL")
						{
							list.Add(result);
						}
						else if (array3.Length < 5)
						{
							list.Add(result);
						}
					}
				}
				return (list.Count > 0) ? new double?(list.Average()) : null;
			}
			catch
			{
				return null;
			}
		}

		public static bool ClearBestTime(int stageId)
		{
			if (stageId < 0)
			{
				return false;
			}
			if (!_bestTimes.ContainsKey(stageId))
			{
				return false;
			}
			_bestTimes.Remove(stageId);
			SaveRecords();
			return true;
		}

		public static bool ClearMapStats(int stageId)
		{
			if (stageId < 0)
			{
				return false;
			}
			string path = Path.Combine(_mapStatsFolder, $"Stage_{stageId}.txt");
			if (!File.Exists(path))
			{
				return false;
			}
			try
			{
				File.Delete(path);
				return true;
			}
			catch
			{
				return false;
			}
		}

		public static int GetGamesPlayed(int stageId)
		{
			if (stageId < 0)
			{
				return 0;
			}
			string path = Path.Combine(_mapStatsFolder, $"Stage_{stageId}.txt");
			if (!File.Exists(path))
			{
				return 0;
			}
			try
			{
				string[] array = File.ReadAllLines(path);
				int num = 0;
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("#") && text.Split(new char[1] { ',' }).Length >= 3)
					{
						num++;
					}
				}
				return num;
			}
			catch
			{
				return 0;
			}
		}

		public static int GetSoloGamesCount(int stageId)
		{
			if (stageId < 0)
			{
				return 0;
			}
			string path = Path.Combine(_mapStatsFolder, $"Stage_{stageId}.txt");
			if (!File.Exists(path))
			{
				return 0;
			}
			try
			{
				string[] array = File.ReadAllLines(path);
				int num = 0;
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("#"))
					{
						string[] array3 = text.Split(new char[1] { ',' });
						if (array3.Length >= 4 && array3[3] == "SOLO")
						{
							num++;
						}
					}
				}
				return num;
			}
			catch
			{
				return 0;
			}
		}
	}
	public enum GraffitiSpotIndicatorStyle
	{
		Normal,
		PinkCan,
		RainbowCan,
		RedCan,
		WhiteCan,
		PurpleCan,
		OrangeCan,
		GreenCan,
		BlackCan
	}
	public enum GraffitiOutlineColor
	{
		Green,
		Red,
		Yellow,
		Blue,
		Pink,
		Purple,
		Orange,
		White,
		Black
	}
	[BepInPlugin("com.localgrace", "LocalGRACE", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class LocalGRACEPlugin : BaseUnityPlugin
	{
		private static Harmony _harmony;

		internal static LocalGRACEPlugin Instance;

		internal static bool HasBombRushMP;

		private static StandaloneGraffitiRace _race;

		public static ConfigEntry<int> DefaultSpotCount;

		public static ConfigEntry<int> CountdownSeconds;

		public static ConfigEntry<bool> QuickGraffiti;

		public static ConfigEntry<bool> AutoGraffiti;

		public static ConfigEntry<GraffitiSpotIndicatorStyle> GraffitiSpotIndicator;

		public static ConfigEntry<bool> UseCustomGraffitiIndicator;

		public static ConfigEntry<GraffitiOutlineColor> GraffitiOutlineColorPreset;

		public static ConfigEntry<bool> UseGraffitiOutlineColorHexCode;

		public static ConfigEntry<string> GraffitiOutlineColorHexCode;

		public static ConfigEntry<bool> AutoStart;

		public static ConfigEntry<int> AutoStartDelay;

		public static ConfigEntry<bool> SaveToStats;

		public static ConfigEntry<bool> ShowUICountdownTimer;

		public static ConfigEntry<bool> ShowUIRaceTimeTracker;

		public static ConfigEntry<bool> ShowUITagCounter;

		public static ConfigEntry<bool> ShowUIPB;

		public static ConfigEntry<bool> ShowUIAVG;

		public static ConfigEntry<string> UICountdownTimerColorHex;

		public static ConfigEntry<string> UIRaceTimeTrackerColorHex;

		public static ConfigEntry<string> UITagCounterColorHex;

		public static ConfigEntry<string> UIPBColorHex;

		public static ConfigEntry<string> UIAVGColorHex;

		private float _autoStartAtTime;

		private Coroutine _autoStartCoroutine;

		public static string ModFolder { get; private set; }

		public static StandaloneGraffitiRace Race => _race ?? (_race = new StandaloneGraffitiRace());

		public static string GetCustomGraffitiSignPath()
		{
			if (UseCustomGraffitiIndicator == null || !UseCustomGraffitiIndicator.Value)
			{
				return null;
			}
			try
			{
				string text = Path.Combine(Paths.BepInExRootPath, "GRACEPlus");
				string text2 = Path.Combine(text, "Textures");
				string path = Path.Combine(text2, "GraffitiSign");
				Directory.CreateDirectory(text);
				Directory.CreateDirectory(text2);
				Directory.CreateDirectory(path);
				string[] files = Directory.GetFiles(path, "*.png", SearchOption.TopDirectoryOnly);
				return (files.Length != 0) ? files[0] : null;
			}
			catch
			{
				return null;
			}
		}

		public static string GetBackendTexturePath(string filename)
		{
			if (!string.IsNullOrEmpty(ModFolder))
			{
				return Path.Combine(ModFolder, "backendtextures", filename);
			}
			return null;
		}

		public static string GetAppIconPath()
		{
			if (string.IsNullOrEmpty(ModFolder))
			{
				return null;
			}
			try
			{
				string path = Path.Combine(ModFolder, "backendtextures", "AppIcon");
				if (!Directory.Exists(path))
				{
					return null;
				}
				string[] files = Directory.GetFiles(path, "*.png", SearchOption.TopDirectoryOnly);
				return (files.Length != 0) ? files[0] : null;
			}
			catch
			{
				return null;
			}
		}

		public static string GetGraffitiIndicatorFilename(GraffitiSpotIndicatorStyle style)
		{
			return style switch
			{
				GraffitiSpotIndicatorStyle.PinkCan => "PinkCan_GraffitiSign.png", 
				GraffitiSpotIndicatorStyle.RainbowCan => "RainbowCan_GraffitiSign.png", 
				GraffitiSpotIndicatorStyle.RedCan => "RedCan_GraffitiSign.png", 
				GraffitiSpotIndicatorStyle.WhiteCan => "WhiteCan_GraffitiSign.png", 
				GraffitiSpotIndicatorStyle.PurpleCan => "PurpleCan_GraffitiSign.png", 
				GraffitiSpotIndicatorStyle.OrangeCan => "OrangeCan_GraffitiSign.png", 
				GraffitiSpotIndicatorStyle.GreenCan => "GreenCan_GraffitiSign.png", 
				GraffitiSpotIndicatorStyle.BlackCan => "BlackCan_GraffitiSign.png", 
				_ => "GraffitiSign.png", 
			};
		}

		public static Color GetOutlineColor(GraffitiOutlineColor color)
		{
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			return (Color)(color switch
			{
				GraffitiOutlineColor.Red => new Color(1f, 0.2f, 0.2f), 
				GraffitiOutlineColor.Yellow => new Color(1f, 0.9f, 0.2f), 
				GraffitiOutlineColor.Blue => new Color(0.2f, 0.5f, 1f), 
				GraffitiOutlineColor.Pink => new Color(1f, 0.4f, 0.6f), 
				GraffitiOutlineColor.Purple => new Color(0.6f, 0.2f, 1f), 
				GraffitiOutlineColor.Orange => new Color(1f, 0.5f, 0.1f), 
				GraffitiOutlineColor.White => new Color(1f, 1f, 1f), 
				GraffitiOutlineColor.Black => new Color(0.1f, 0.1f, 0.1f), 
				_ => new Color(0.2f, 1f, 0.3f), 
			});
		}

		public static Color GetEffectiveOutlineColor()
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			if (UseGraffitiOutlineColorHexCode != null && UseGraffitiOutlineColorHexCode.Value && GraffitiOutlineColorHexCode != null && !string.IsNullOrWhiteSpace(GraffitiOutlineColorHexCode.Value))
			{
				string text = GraffitiOutlineColorHexCode.Value.Trim();
				if (!text.StartsWith("#"))
				{
					text = "#" + text;
				}
				Color result = default(Color);
				if (ColorUtility.TryParseHtmlString(text, ref result))
				{
					return result;
				}
			}
			return GetOutlineColor((GraffitiOutlineColorPreset != null) ? GraffitiOutlineColorPreset.Value : GraffitiOutlineColor.Green);
		}

		public static bool IsOutlineColorDefault()
		{
			if (UseGraffitiOutlineColorHexCode != null && UseGraffitiOutlineColorHexCode.Value)
			{
				return false;
			}
			if (GraffitiOutlineColorPreset != null)
			{
				return GraffitiOutlineColorPreset.Value == GraffitiOutlineColor.Yellow;
			}
			return true;
		}

		public static Color GetUIColorFromHex(ConfigEntry<string> entry, Color defaultColor)
		{
			//IL_0010: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			if (entry == null || string.IsNullOrWhiteSpace(entry.Value))
			{
				return defaultColor;
			}
			string text = entry.Value.Trim();
			if (!text.StartsWith("#"))
			{
				text = "#" + text;
			}
			Color result = default(Color);
			if (ColorUtility.TryParseHtmlString(text, ref result))
			{
				return result;
			}
			return defaultColor;
		}

		private void Awake()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Expected O, but got Unknown
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_037c: Expected O, but got Unknown
			Instance = this;
			ModFolder = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			try
			{
				HasBombRushMP = AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => a.GetName().Name?.StartsWith("BombRushMP", StringComparison.OrdinalIgnoreCase) ?? false);
			}
			catch
			{
				HasBombRushMP = false;
			}
			DefaultSpotCount = ((BaseUnityPlugin)this).Config.Bind<int>("Race", "DefaultSpotCount", 10, new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 70), Array.Empty<object>()));
			CountdownSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("Race", "CountdownSeconds", 3, new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
			QuickGraffiti = ((BaseUnityPlugin)this).Config.Bind<bool>("Race", "QuickGraffiti", true, "");
			AutoGraffiti = ((BaseUnityPlugin)this).Config.Bind<bool>("Race", "AutoGraffiti", false, "");
			GraffitiSpotIndicator = ((BaseUnityPlugin)this).Config.Bind<GraffitiSpotIndicatorStyle>("Indicators", "GraffitiSpotIndicator", GraffitiSpotIndicatorStyle.Normal, "");
			UseCustomGraffitiIndicator = ((BaseUnityPlugin)this).Config.Bind<bool>("Indicators", "UseCustomGraffitiIndicator", false, "");
			GraffitiOutlineColorPreset = ((BaseUnityPlugin)this).Config.Bind<GraffitiOutlineColor>("Indicators", "GraffitiOutlineColor", GraffitiOutlineColor.Green, "");
			UseGraffitiOutlineColorHexCode = ((BaseUnityPlugin)this).Config.Bind<bool>("Indicators", "Use Graffiti Outline Color Hex Code", false, "");
			GraffitiOutlineColorHexCode = ((BaseUnityPlugin)this).Config.Bind<string>("Indicators", "Graffiti Outline Color Hex Code", "#00FF4D", "");
			AutoStart = ((BaseUnityPlugin)this).Config.Bind<bool>("Race", "AutoStart", false, "");
			AutoStartDelay = ((BaseUnityPlugin)this).Config.Bind<int>("Race", "AutoStartDelay", 3, new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
			SaveToStats = ((BaseUnityPlugin)this).Config.Bind<bool>("Race", "SaveToStats", true, "");
			ShowUICountdownTimer = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowUICountdownTimer", true, "");
			ShowUIRaceTimeTracker = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowUIRaceTimeTracker", true, "");
			ShowUITagCounter = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowUITagCounter", true, "");
			ShowUIPB = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowUIPB", true, "");
			ShowUIAVG = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowUIAVG", true, "");
			UICountdownTimerColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "UICountdownTimerColorHex", "#FFFF00", "");
			UIRaceTimeTrackerColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "UIRaceTimeTrackerColorHex", "#D9D9D9", "");
			UITagCounterColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "UITagCounterColorHex", "#FFFFFF", "");
			UIPBColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "UIPBColorHex", "#00FF00", "");
			UIAVGColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "UIAVGColorHex", "#FFFF00", "");
			GraceStats.Initialize();
			_harmony = new Harmony("com.localgrace");
			_harmony.PatchAll();
			AppLocalGRACE.Initialize();
		}

		private void Update()
		{
			if (Race != null && Race.InGame)
			{
				Race.Update();
			}
		}

		private IEnumerator AutoStartAfterDelay(float delay)
		{
			yield return (object)new WaitForSeconds(delay);
			_autoStartCoroutine = null;
			if ((Object)(object)Instance == (Object)null || Race == null || _autoStartAtTime == 0f)
			{
				yield break;
			}
			_autoStartAtTime = 0f;
			if (Race.InGame)
			{
				yield break;
			}
			try
			{
				int lastCountdownSecondsForAutoStart = AppLocalGRACE.LastCountdownSecondsForAutoStart;
				int lastSpotAmountForAutoStart = AppLocalGRACE.LastSpotAmountForAutoStart;
				Race.SetCountdown(lastCountdownSecondsForAutoStart);
				Race.StartRace(lastSpotAmountForAutoStart);
			}
			catch
			{
			}
		}

		internal static void OnRaceEnded(bool cancelled)
		{
			if ((Object)(object)Instance == (Object)null)
			{
				return;
			}
			if (cancelled)
			{
				if (Instance._autoStartCoroutine != null)
				{
					((MonoBehaviour)Instance).StopCoroutine(Instance._autoStartCoroutine);
					Instance._autoStartCoroutine = null;
				}
				Instance._autoStartAtTime = 0f;
				if (AutoStart != null)
				{
					AutoStart.Value = false;
				}
			}
			else if (AutoStart.Value)
			{
				Instance._autoStartAtTime = Time.time + (float)AutoStartDelay.Value;
				if (Instance._autoStartCoroutine != null)
				{
					((MonoBehaviour)Instance).StopCoroutine(Instance._autoStartCoroutine);
				}
				Instance._autoStartCoroutine = ((MonoBehaviour)Instance).StartCoroutine(Instance.AutoStartAfterDelay(AutoStartDelay.Value));
			}
		}

		internal static void CancelAutoStart()
		{
			if (!((Object)(object)Instance == (Object)null))
			{
				if (Instance._autoStartCoroutine != null)
				{
					((MonoBehaviour)Instance).StopCoroutine(Instance._autoStartCoroutine);
					Instance._autoStartCoroutine = null;
				}
				Instance._autoStartAtTime = 0f;
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "com.localgrace";

		public const string PLUGIN_NAME = "LocalGRACE";

		public const string PLUGIN_VERSION = "1.0.0";
	}
	public class AppLocalGRACE : CustomApp
	{
		private SimplePhoneButton _startOrEndButton;

		private SimplePhoneButton _spotAmountButton;

		private SimplePhoneButton _countdownButton;

		private SimplePhoneButton _autoStartButton;

		private SimplePhoneButton _autoStartDelayButton;

		private SimplePhoneButton _graffitiSignButton;

		private SimplePhoneButton _graffitiOutlineColorButton;

		private bool _showingExtraSettings;

		private bool _showingStats;

		private int _spotAmount = 10;

		private int _countdownSeconds = 3;

		private int _maxSpotsOnMap = 1;

		public static int LastSpotAmountForAutoStart { get; private set; }

		public static int LastCountdownSecondsForAutoStart { get; private set; }

		public static void Initialize()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			Sprite val = null;
			string appIconPath = LocalGRACEPlugin.GetAppIconPath();
			if (!string.IsNullOrEmpty(appIconPath) && File.Exists(appIconPath))
			{
				try
				{
					byte[] array = File.ReadAllBytes(appIconPath);
					Texture2D val2 = new Texture2D(2, 2);
					if (ImageConversion.LoadImage(val2, array))
					{
						((Texture)val2).wrapMode = (TextureWrapMode)1;
						((Texture)val2).filterMode = (FilterMode)1;
						val2.Apply();
						val = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f));
					}
				}
				catch
				{
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				PhoneAPI.RegisterApp<AppLocalGRACE>("GRACE", val);
			}
			else
			{
				PhoneAPI.RegisterApp<AppLocalGRACE>("GRACE", (Sprite)null);
			}
			RefreshAutoStartValuesFromConfig();
		}

		public static void RefreshAutoStartValuesFromConfig()
		{
			if (LocalGRACEPlugin.DefaultSpotCount != null)
			{
				LastSpotAmountForAutoStart = LocalGRACEPlugin.DefaultSpotCount.Value;
			}
			if (LocalGRACEPlugin.CountdownSeconds != null)
			{
				LastCountdownSecondsForAutoStart = LocalGRACEPlugin.CountdownSeconds.Value;
			}
		}

		public override void OnAppInit()
		{
			((CustomApp)this).OnAppInit();
			((CustomApp)this).CreateIconlessTitleBar("Local GRACE", 80f);
			base.ScrollView = PhoneScrollView.Create((CustomApp)(object)this, 275f, 1600f);
			PopulateButtons();
		}

		public override void OnAppEnable()
		{
			((App)this).OnAppEnable();
			_showingExtraSettings = false;
			_showingStats = false;
			RefreshMaxSpots();
			ClampSpotAmountToMax();
			PopulateButtons();
		}

		public override void OnAppUpdate()
		{
			((App)this).OnAppUpdate();
		}

		private void RefreshMaxSpots()
		{
			StandaloneGraffitiRace race = LocalGRACEPlugin.Race;
			_maxSpotsOnMap = ((race == null) ? 1 : Mathf.Max(1, race.GetValidSpotCount()));
		}

		private void ClampSpotAmountToMax()
		{
			if (_spotAmount < 1 || _spotAmount > _maxSpotsOnMap)
			{
				_spotAmount = Mathf.Clamp(_spotAmount, 1, _maxSpotsOnMap);
			}
		}

		private void SetDefaultSpotAmount()
		{
			RefreshMaxSpots();
			_spotAmount = ((_maxSpotsOnMap < 10) ? _maxSpotsOnMap : 10);
		}

		private void PopulateButtons()
		{
			base.ScrollView.RemoveAllButtons();
			_showingExtraSettings = false;
			_showingStats = false;
			StandaloneGraffitiRace race = LocalGRACEPlugin.Race;
			RefreshMaxSpots();
			if (_spotAmount < 1 || _spotAmount > _maxSpotsOnMap)
			{
				SetDefaultSpotAmount();
			}
			_startOrEndButton = PhoneUIUtility.CreateSimpleButton(StartOrEndButtonLabel());
			SimplePhoneButton startOrEndButton = _startOrEndButton;
			((PhoneButton)startOrEndButton).OnConfirm = (Action)Delegate.Combine(((PhoneButton)startOrEndButton).OnConfirm, (Action)delegate
			{
				if (race.InGame)
				{
					race.EndRace();
					if ((Object)(object)_startOrEndButton?.Label != (Object)null)
					{
						((TMP_Text)_startOrEndButton.Label).text = StartOrEndButtonLabel();
					}
					return;
				}
				try
				{
					LocalGRACEPlugin.CancelAutoStart();
					LastSpotAmountForAutoStart = _spotAmount;
					LastCountdownSecondsForAutoStart = _countdownSeconds;
					race.SetCountdown(_countdownSeconds);
					race.StartRace(_spotAmount);
					((App)this).MyPhone.TurnOff(true);
				}
				catch
				{
				}
			});
			base.ScrollView.AddButton((PhoneButton)(object)_startOrEndButton);
			SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Stats");
			((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate
			{
				ShowStatsView();
			});
			base.ScrollView.AddButton((PhoneButton)(object)val);
			SimplePhoneButton val2 = PhoneUIUtility.CreateSimpleButton("Extra Settings");
			((PhoneButton)val2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val2).OnConfirm, (Action)delegate
			{
				ShowExtraSettingsView();
			});
			base.ScrollView.AddButton((PhoneButton)(object)val2);
			_spotAmountButton = PhoneUIUtility.CreateSimpleButton(SpotAmountLabel());
			SimplePhoneButton spotAmountButton = _spotAmountButton;
			((PhoneButton)spotAmountButton).OnConfirm = (Action)Delegate.Combine(((PhoneButton)spotAmountButton).OnConfirm, (Action)delegate
			{
				RefreshMaxSpots();
				_spotAmount++;
				if (_spotAmount > _maxSpotsOnMap)
				{
					_spotAmount = 1;
				}
				LastSpotAmountForAutoStart = _spotAmount;
				if ((Object)(object)_spotAmountButton?.Label != (Object)null)
				{
					((TMP_Text)_spotAmountButton.Label).text = SpotAmountLabel();
				}
			});
			base.ScrollView.AddButton((PhoneButton)(object)_spotAmountButton);
			_countdownButton = PhoneUIUtility.CreateSimpleButton(CountdownLabel());
			SimplePhoneButton countdownButton = _countdownButton;
			((PhoneButton)countdownButton).OnConfirm = (Action)Delegate.Combine(((PhoneButton)countdownButton).OnConfirm, (Action)delegate
			{
				_countdownSeconds++;
				if (_countdownSeconds > 10)
				{
					_countdownSeconds = 1;
				}
				LastCountdownSecondsForAutoStart = _countdownSeconds;
				race.SetCountdown(_countdownSeconds);
				if ((Object)(object)_countdownButton?.Label != (Object)null)
				{
					((TMP_Text)_countdownButton.Label).text = CountdownLabel();
				}
			});
			base.ScrollView.AddButton((PhoneButton)(object)_countdownButton);
			_autoStartButton = PhoneUIUtility.CreateSimpleButton(AutoStartLabel());
			SimplePhoneButton autoStartButton = _autoStartButton;
			((PhoneButton)autoStartButton).OnConfirm = (Action)Delegate.Combine(((PhoneButton)autoStartButton).OnConfirm, (Action)delegate
			{
				LocalGRACEPlugin.AutoStart.Value = !LocalGRACEPlugin.AutoStart.Value;
				if ((Object)(object)_autoStartButton?.Label != (Object)null)
				{
					((TMP_Text)_autoStartButton.Label).text = AutoStartLabel();
				}
			});
			base.ScrollView.AddButton((PhoneButton)(object)_autoStartButton);
			_autoStartDelayButton = PhoneUIUtility.CreateSimpleButton(AutoStartDelayLabel());
			SimplePhoneButton autoStartDelayButton = _autoStartDelayButton;
			((PhoneButton)autoStartDelayButton).OnConfirm = (Action)Delegate.Combine(((PhoneButton)autoStartDelayButton).OnConfirm, (Action)delegate
			{
				int value = LocalGRACEPlugin.AutoStartDelay.Value;
				value++;
				if (value > 10)
				{
					value = 1;
				}
				LocalGRACEPlugin.AutoStartDelay.Value = value;
				if ((Object)(object)_autoStartDelayButton?.Label != (Object)null)
				{
					((TMP_Text)_autoStartDelayButton.Label).text = AutoStartDelayLabel();
				}
			});
			base.ScrollView.AddButton((PhoneButton)(object)_autoStartDelayButton);
			SimplePhoneButton saveToStatsButton = PhoneUIUtility.CreateSimpleButton(SaveToStatsLabel());
			SimplePhoneButton obj = saveToStatsButton;
			((PhoneButton)obj).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj).OnConfirm, (Action)delegate
			{
				if (LocalGRACEPlugin.SaveToStats != null)
				{
					LocalGRACEPlugin.SaveToStats.Value = !LocalGRACEPlugin.SaveToStats.Value;
					if ((Object)(object)saveToStatsButton?.Label != (Object)null)
					{
						((TMP_Text)saveToStatsButton.Label).text = SaveToStatsLabel();
					}
				}
			});
			base.ScrollView.AddButton((PhoneButton)(object)saveToStatsButton);
		}

		private void ShowExtraSettingsView()
		{
			_showingExtraSettings = true;
			base.ScrollView.RemoveAllButtons();
			_graffitiSignButton = PhoneUIUtility.CreateSimpleButton(GraffitiSignLabel());
			SimplePhoneButton graffitiSignButton = _graffitiSignButton;
			((PhoneButton)graffitiSignButton).OnConfirm = (Action)Delegate.Combine(((PhoneButton)graffitiSignButton).OnConfirm, (Action)delegate
			{
				CycleGraffitiSpotIndicator();
				if ((Object)(object)_graffitiSignButton?.Label != (Object)null)
				{
					((TMP_Text)_graffitiSignButton.Label).text = GraffitiSignLabel();
				}
			});
			base.ScrollView.AddButton((PhoneButton)(object)_graffitiSignButton);
			_graffitiOutlineColorButton = PhoneUIUtility.CreateSimpleButton(GraffitiOutlineColorLabel());
			SimplePhoneButton graffitiOutlineColorButton = _graffitiOutlineColorButton;
			((PhoneButton)graffitiOutlineColorButton).OnConfirm = (Action)Delegate.Combine(((PhoneButton)graffitiOutlineColorButton).OnConfirm, (Action)delegate
			{
				CycleGraffitiOutlineColor();
				if ((Object)(object)_graffitiOutlineColorButton?.Label != (Object)null)
				{
					((TMP_Text)_graffitiOutlineColorButton.Label).text = GraffitiOutlineColorLabel();
				}
			});
			base.ScrollView.AddButton((PhoneButton)(object)_graffitiOutlineColorButton);
			SimplePhoneButton countdownTimerBtn = PhoneUIUtility.CreateSimpleButton(ShowUICountdownTimerLabel());
			SimplePhoneButton obj = countdownTimerBtn;
			((PhoneButton)obj).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj).OnConfirm, (Action)delegate
			{
				ToggleConfigRefreshLabel(LocalGRACEPlugin.ShowUICountdownTimer, countdownTimerBtn, ShowUICountdownTimerLabel);
			});
			base.ScrollView.AddButton((PhoneButton)(object)countdownTimerBtn);
			SimplePhoneButton raceTimeTrackerBtn = PhoneUIUtility.CreateSimpleButton(ShowUIRaceTimeTrackerLabel());
			SimplePhoneButton obj2 = raceTimeTrackerBtn;
			((PhoneButton)obj2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj2).OnConfirm, (Action)delegate
			{
				ToggleConfigRefreshLabel(LocalGRACEPlugin.ShowUIRaceTimeTracker, raceTimeTrackerBtn, ShowUIRaceTimeTrackerLabel);
			});
			base.ScrollView.AddButton((PhoneButton)(object)raceTimeTrackerBtn);
			SimplePhoneButton tagCounterBtn = PhoneUIUtility.CreateSimpleButton(ShowUITagCounterLabel());
			SimplePhoneButton obj3 = tagCounterBtn;
			((PhoneButton)obj3).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj3).OnConfirm, (Action)delegate
			{
				ToggleConfigRefreshLabel(LocalGRACEPlugin.ShowUITagCounter, tagCounterBtn, ShowUITagCounterLabel);
			});
			base.ScrollView.AddButton((PhoneButton)(object)tagCounterBtn);
			SimplePhoneButton pbBtn = PhoneUIUtility.CreateSimpleButton(ShowUIPBLabel());
			SimplePhoneButton obj4 = pbBtn;
			((PhoneButton)obj4).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj4).OnConfirm, (Action)delegate
			{
				ToggleConfigRefreshLabel(LocalGRACEPlugin.ShowUIPB, pbBtn, ShowUIPBLabel);
			});
			base.ScrollView.AddButton((PhoneButton)(object)pbBtn);
			SimplePhoneButton avgBtn = PhoneUIUtility.CreateSimpleButton(ShowUIAVGLabel());
			SimplePhoneButton obj5 = avgBtn;
			((PhoneButton)obj5).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj5).OnConfirm, (Action)delegate
			{
				ToggleConfigRefreshLabel(LocalGRACEPlugin.ShowUIAVG, avgBtn, ShowUIAVGLabel);
			});
			base.ScrollView.AddButton((PhoneButton)(object)avgBtn);
			SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Back");
			((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate
			{
				PopulateButtons();
			});
			base.ScrollView.AddButton((PhoneButton)(object)val);
		}

		private void ShowStatsView()
		{
			_showingStats = true;
			base.ScrollView.RemoveAllButtons();
			int stageId = GameAPI.GetCurrentStageId();
			double? bestTime = GraceStats.GetBestTime(stageId);
			double? averageTime = GraceStats.GetAverageTime(stageId);
			int soloGamesCount = GraceStats.GetSoloGamesCount(stageId);
			SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton(StatsBestTimeLabel(bestTime));
			((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate
			{
			});
			base.ScrollView.AddButton((PhoneButton)(object)val);
			SimplePhoneButton val2 = PhoneUIUtility.CreateSimpleButton(StatsAvgTimeLabel(averageTime));
			((PhoneButton)val2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val2).OnConfirm, (Action)delegate
			{
			});
			base.ScrollView.AddButton((PhoneButton)(object)val2);
			SimplePhoneButton val3 = PhoneUIUtility.CreateSimpleButton(StatsSoloGamesLabel(soloGamesCount));
			((PhoneButton)val3).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val3).OnConfirm, (Action)delegate
			{
			});
			base.ScrollView.AddButton((PhoneButton)(object)val3);
			SimplePhoneButton val4 = PhoneUIUtility.CreateSimpleButton("Clear Best Time");
			((PhoneButton)val4).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val4).OnConfirm, (Action)delegate
			{
				GraceStats.ClearBestTime(stageId);
				ShowStatsView();
			});
			base.ScrollView.AddButton((PhoneButton)(object)val4);
			SimplePhoneButton val5 = PhoneUIUtility.CreateSimpleButton("Clear AVG Time");
			((PhoneButton)val5).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val5).OnConfirm, (Action)delegate
			{
				GraceStats.ClearMapStats(stageId);
				ShowStatsView();
			});
			base.ScrollView.AddButton((PhoneButton)(object)val5);
			SimplePhoneButton val6 = PhoneUIUtility.CreateSimpleButton("Back");
			((PhoneButton)val6).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val6).OnConfirm, (Action)delegate
			{
				PopulateButtons();
			});
			base.ScrollView.AddButton((PhoneButton)(object)val6);
		}

		private static string StatsBestTimeLabel(double? bestTime)
		{
			return "Best Time: " + (bestTime.HasValue ? $"{bestTime.Value:F1}s" : "—");
		}

		private static string StatsAvgTimeLabel(double? avgTime)
		{
			return "AVG Time: " + (avgTime.HasValue ? $"{avgTime.Value:F1}s" : "—");
		}

		private static string StatsSoloGamesLabel(int soloGames)
		{
			return "Solo Games: " + soloGames;
		}

		private void CycleGraffitiSpotIndicator()
		{
			if (LocalGRACEPlugin.GraffitiSpotIndicator != null)
			{
				GraffitiSpotIndicatorStyle value = LocalGRACEPlugin.GraffitiSpotIndicator.Value switch
				{
					GraffitiSpotIndicatorStyle.Normal => GraffitiSpotIndicatorStyle.PinkCan, 
					GraffitiSpotIndicatorStyle.PinkCan => GraffitiSpotIndicatorStyle.RainbowCan, 
					GraffitiSpotIndicatorStyle.RainbowCan => GraffitiSpotIndicatorStyle.RedCan, 
					GraffitiSpotIndicatorStyle.RedCan => GraffitiSpotIndicatorStyle.WhiteCan, 
					GraffitiSpotIndicatorStyle.WhiteCan => GraffitiSpotIndicatorStyle.PurpleCan, 
					GraffitiSpotIndicatorStyle.PurpleCan => GraffitiSpotIndicatorStyle.OrangeCan, 
					GraffitiSpotIndicatorStyle.OrangeCan => GraffitiSpotIndicatorStyle.GreenCan, 
					GraffitiSpotIndicatorStyle.GreenCan => GraffitiSpotIndicatorStyle.BlackCan, 
					GraffitiSpotIndicatorStyle.BlackCan => GraffitiSpotIndicatorStyle.Normal, 
					_ => GraffitiSpotIndicatorStyle.Normal, 
				};
				LocalGRACEPlugin.GraffitiSpotIndicator.Value = value;
			}
		}

		private static string GraffitiSignLabel()
		{
			GraffitiSpotIndicatorStyle style = ((LocalGRACEPlugin.GraffitiSpotIndicator != null) ? LocalGRACEPlugin.GraffitiSpotIndicator.Value : GraffitiSpotIndicatorStyle.Normal);
			return "Graffiti Sign: " + GetStyleDisplayName(style);
		}

		private static string GetStyleDisplayName(GraffitiSpotIndicatorStyle style)
		{
			return style switch
			{
				GraffitiSpotIndicatorStyle.PinkCan => "Pink Can", 
				GraffitiSpotIndicatorStyle.RainbowCan => "Rainbow Can", 
				GraffitiSpotIndicatorStyle.RedCan => "Red Can", 
				GraffitiSpotIndicatorStyle.WhiteCan => "White Can", 
				GraffitiSpotIndicatorStyle.PurpleCan => "Purple Can", 
				GraffitiSpotIndicatorStyle.OrangeCan => "Orange Can", 
				GraffitiSpotIndicatorStyle.GreenCan => "Green Can", 
				GraffitiSpotIndicatorStyle.BlackCan => "Black Can", 
				_ => "Normal", 
			};
		}

		private void CycleGraffitiOutlineColor()
		{
			if (LocalGRACEPlugin.GraffitiOutlineColorPreset != null)
			{
				GraffitiOutlineColor value = LocalGRACEPlugin.GraffitiOutlineColorPreset.Value switch
				{
					GraffitiOutlineColor.Green => GraffitiOutlineColor.Red, 
					GraffitiOutlineColor.Red => GraffitiOutlineColor.Yellow, 
					GraffitiOutlineColor.Yellow => GraffitiOutlineColor.Blue, 
					GraffitiOutlineColor.Blue => GraffitiOutlineColor.Pink, 
					GraffitiOutlineColor.Pink => GraffitiOutlineColor.Purple, 
					GraffitiOutlineColor.Purple => GraffitiOutlineColor.Orange, 
					GraffitiOutlineColor.Orange => GraffitiOutlineColor.White, 
					GraffitiOutlineColor.White => GraffitiOutlineColor.Black, 
					GraffitiOutlineColor.Black => GraffitiOutlineColor.Green, 
					_ => GraffitiOutlineColor.Green, 
				};
				LocalGRACEPlugin.GraffitiOutlineColorPreset.Value = value;
			}
		}

		private static string GraffitiOutlineColorLabel()
		{
			return "Graffiti Outline Color: " + ((LocalGRACEPlugin.GraffitiOutlineColorPreset != null) ? LocalGRACEPlugin.GraffitiOutlineColorPreset.Value : GraffitiOutlineColor.Green).ToString().ToLowerInvariant();
		}

		private static void ToggleConfigRefreshLabel(ConfigEntry<bool> entry, SimplePhoneButton button, Func<string> labelFunc)
		{
			if (entry != null && labelFunc != null)
			{
				entry.Value = !entry.Value;
				if ((Object)(object)button?.Label != (Object)null)
				{
					((TMP_Text)button.Label).text = labelFunc();
				}
			}
		}

		private static string ShowUICountdownTimerLabel()
		{
			bool flag = LocalGRACEPlugin.ShowUICountdownTimer != null && LocalGRACEPlugin.ShowUICountdownTimer.Value;
			return "Countdown Timer: " + (flag ? "On" : "Off");
		}

		private static string ShowUIRaceTimeTrackerLabel()
		{
			bool flag = LocalGRACEPlugin.ShowUIRaceTimeTracker != null && LocalGRACEPlugin.ShowUIRaceTimeTracker.Value;
			return "Race Time Tracker: " + (flag ? "On" : "Off");
		}

		private static string ShowUITagCounterLabel()
		{
			bool flag = LocalGRACEPlugin.ShowUITagCounter != null && LocalGRACEPlugin.ShowUITagCounter.Value;
			return "Tag Counter: " + (flag ? "On" : "Off");
		}

		private static string ShowUIPBLabel()
		{
			bool flag = LocalGRACEPlugin.ShowUIPB != null && LocalGRACEPlugin.ShowUIPB.Value;
			return "PB: " + (flag ? "On" : "Off");
		}

		private static string ShowUIAVGLabel()
		{
			bool flag = LocalGRACEPlugin.ShowUIAVG != null && LocalGRACEPlugin.ShowUIAVG.Value;
			return "AVG: " + (flag ? "On" : "Off");
		}

		private static string StartOrEndButtonLabel()
		{
			StandaloneGraffitiRace race = LocalGRACEPlugin.Race;
			if (race != null && race.InGame)
			{
				return "End Race";
			}
			return "Start Race";
		}

		private string SpotAmountLabel()
		{
			return $"Spot amount: {_spotAmount}";
		}

		private string CountdownLabel()
		{
			return $"Countdown Timer: {_countdownSeconds}s";
		}

		private static string AutoStartLabel()
		{
			return "Auto Start: " + ((LocalGRACEPlugin.AutoStart != null && LocalGRACEPlugin.AutoStart.Value) ? "On" : "Off");
		}

		private static string AutoStartDelayLabel()
		{
			return "Auto Start Delay: " + ((LocalGRACEPlugin.AutoStartDelay != null) ? (LocalGRACEPlugin.AutoStartDelay.Value + "s") : "—");
		}

		private static string SaveToStatsLabel()
		{
			bool flag = LocalGRACEPlugin.SaveToStats != null && LocalGRACEPlugin.SaveToStats.Value;
			return "Save To Stats: " + (flag ? "On" : "Off");
		}
	}
	public class StandaloneGraffitiRace
	{
		public enum State
		{
			None,
			Countdown,
			Main,
			Finished
		}

		private const float DistanceOffWall = 3f;

		private const int MinGraffiti = 5;

		private const int MaxGraffiti = 70;

		private static readonly Vector3 HideoutBlockedSpawnPosition = new Vector3(-5.05f, 9.1f, -36.35f);

		private const float BlockedSpawnPositionTolerance = 0.5f;

		private WorldHandler _worldHandler;

		private State _state;

		private float _countdownTimer;

		private int _score;

		private Dictionary<GraffitiSpot, object> _originalProgress = new Dictionary<GraffitiSpot, object>();

		private HashSet<GraffitiSpot> _otherSpots = new HashSet<GraffitiSpot>();

		private List<StageChunk> _offStageChunks = new List<StageChunk>();

		private List<GraffitiSpot> _raceSpots = new List<GraffitiSpot>();

		private Vector3 _spawnPos;

		private Quaternion _spawnRot;

		private int _countdownSeconds = 3;

		private float _raceElapsedTime;

		private TimerUI _timerUI;

		private UIScreenIndicators _indicators;

		private Dictionary<GraffitiSpot, MapPin> _mapPins = new Dictionary<GraffitiSpot, MapPin>();

		private Dictionary<GraffitiSpot, List<(Renderer, Material, int, Color, Material)>> _outlineColorBackups = new Dictionary<GraffitiSpot, List<(Renderer, Material, int, Color, Material)>>();

		private int _lastCountdownRemaining = -999;

		private int _lastDisplayedScore = -1;

		private string _lastFormattedRaceTime;

		private static readonly int ShaderPropColor = Shader.PropertyToID("_Color");

		private static readonly int ShaderPropBaseColor = Shader.PropertyToID("_BaseColor");

		private static readonly int ShaderPropMainTex = Shader.PropertyToID("_MainTex");

		public bool InGame
		{
			get
			{
				if (_state != State.Countdown)
				{
					return _state == State.Main;
				}
				return true;
			}
		}

		public int Score => _score;

		public int TotalSpots => _raceSpots?.Count ?? 0;

		public State CurrentState => _state;

		private static bool IsBlockedHideoutSpawnSpot(GraffitiSpot spot)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)spot == (Object)null || (Object)(object)((Component)spot).transform == (Object)null)
			{
				return false;
			}
			return Vector3.Distance(((Component)spot).transform.position, HideoutBlockedSpawnPosition) <= 0.5f;
		}

		public StandaloneGraffitiRace()
		{
			_worldHandler = WorldHandler.instance;
		}

		public void SetCountdown(int seconds)
		{
			_countdownSeconds = Mathf.Clamp(seconds, 1, 10);
		}

		public void StartRace(int spotCount = -1)
		{
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_036c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0362: Unknown result type (might be due to invalid IL or missing references)
			//IL_0368: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0371: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0475: Unknown result type (might be due to invalid IL or missing references)
			//IL_047b: Unknown result type (might be due to invalid IL or missing references)
			_worldHandler = WorldHandler.instance;
			if (InGame || (Object)(object)_worldHandler == (Object)null)
			{
				return;
			}
			List<GraffitiSpot> validSpots = GetValidSpots();
			if (validSpots == null || validSpots.Count == 0)
			{
				return;
			}
			int num = ((spotCount > 0) ? spotCount : LocalGRACEPlugin.DefaultSpotCount.Value);
			num = Mathf.Clamp(num, 1, Mathf.Min(70, validSpots.Count));
			if (validSpots.Count < num)
			{
				num = Mathf.Max(1, Mathf.CeilToInt((float)validSpots.Count * 0.5f));
			}
			_raceSpots = new List<GraffitiSpot>();
			List<GraffitiSpot> list = new List<GraffitiSpot>(validSpots);
			for (int i = 0; i < num; i++)
			{
				int index = Random.Range(0, list.Count);
				_raceSpots.Add(list[index]);
				list.RemoveAt(index);
			}
			int currentStageId = GameAPI.GetCurrentStageId();
			if (currentStageId == 5 && _raceSpots.Count > 1 && IsBlockedHideoutSpawnSpot(_raceSpots[0]))
			{
				GraffitiSpot item = _raceSpots[0];
				_raceSpots.RemoveAt(0);
				_raceSpots.Add(item);
			}
			GraffitiSpot val = _raceSpots[0];
			Vector3 val2 = -((Component)val).transform.forward;
			_spawnPos = ((Component)val).transform.position - val2 * 3f;
			val2.y = 0f;
			val2 = ((Vector3)(ref val2)).normalized;
			_spawnRot = Quaternion.LookRotation(val2, Vector3.up);
			TurnOnAllStageChunks();
			RaycastHit val5 = default(RaycastHit);
			foreach (GraffitiSpot raceSpot in _raceSpots)
			{
				if (currentStageId != 5 || !IsBlockedHideoutSpawnSpot(raceSpot))
				{
					Vector3 val3 = -((Component)raceSpot).transform.forward;
					Vector3 val4 = ((Component)raceSpot).transform.position - val3 * 3f;
					if (Physics.Raycast(new Ray(val4, Vector3.down), ref val5, 1000f, int.MaxValue, (QueryTriggerInteraction)2) && Vector3.Angle(((RaycastHit)(ref val5)).normal, Vector3.up) <= 20f && ((Component)((RaycastHit)(ref val5)).collider).gameObject.layer == 0)
					{
						_spawnPos = ((RaycastHit)(ref val5)).point;
						val3.y = 0f;
						_spawnRot = Quaternion.LookRotation(((Vector3)(ref val3)).normalized, Vector3.up);
						break;
					}
				}
			}
			RestoreStageChunks();
			_state = State.Countdown;
			_countdownTimer = 0f;
			_score = 0;
			_lastCountdownRemaining = -999;
			_lastDisplayedScore = -1;
			_lastFormattedRaceTime = null;
			_raceElapsedTime = 0f;
			_originalProgress.Clear();
			_otherSpots.Clear();
			List<GraffitiSpot> grafSpots = GameAPI.GetGrafSpots(_worldHandler);
			if (grafSpots != null)
			{
				foreach (GraffitiSpot item2 in grafSpots)
				{
					((Component)item2).gameObject.SetActive(false);
					_otherSpots.Add(item2);
				}
			}
			Mapcontroller instance = Mapcontroller.Instance;
			_indicators = UIScreenIndicators.Create();
			_timerUI = TimerUI.CreateAndGet();
			bool flag = !LocalGRACEPlugin.IsOutlineColorDefault();
			Color color = (Color)(flag ? LocalGRACEPlugin.GetEffectiveOutlineColor() : default(Color));
			foreach (GraffitiSpot raceSpot2 in _raceSpots)
			{
				((Component)raceSpot2).gameObject.SetActive(true);
				_otherSpots.Remove(raceSpot2);
				_originalProgress[raceSpot2] = GameAPI.GetProgressableData((AProgressable)(object)raceSpot2);
				GameAPI.GraffitiSpotClearPaint(raceSpot2);
				raceSpot2.bottomCrew = (Crew)1;
				if (flag)
				{
					ApplyOutlineColorToSpot(raceSpot2, color);
				}
				_indicators.AddIndicator(((Component)raceSpot2).transform);
				MapPin val6 = GameAPI.MapControllerCreatePin(instance, (PinType)2);
				if ((Object)(object)val6 != (Object)null)
				{
					val6.AssignGameplayEvent(((Component)raceSpot2).gameObject);
					val6.InitMapPin((PinType)2);
					val6.OnPinEnable();
					_mapPins[raceSpot2] = val6;
				}
			}
			Player currentPlayer = WorldHandler.instance.GetCurrentPlayer();
			if ((Object)(object)currentPlayer != (Object)null)
			{
				currentPlayer.boostCharge = GameAPI.GetPlayerMaxBoostCharge(currentPlayer);
				GameAPI.SetPlayerUserInputEnabled(currentPlayer, value: false);
				PlacePlayerAt(_spawnPos, _spawnRot);
			}
			_timerUI.Activate();
		}

		public void EndRace(bool cancelled = true)
		{
			if (!InGame)
			{
				return;
			}
			_state = State.Finished;
			int currentStageId = GameAPI.GetCurrentStageId();
			double timeInSeconds = _raceElapsedTime;
			bool flag = !cancelled;
			if (currentStageId >= 0 && LocalGRACEPlugin.SaveToStats != null && LocalGRACEPlugin.SaveToStats.Value)
			{
				if (flag)
				{
					GraceStats.CheckAndUpdateBestTime(currentStageId, timeInSeconds, out var _);
				}
				GraceStats.SaveMapStats(currentStageId, timeInSeconds, flag);
			}
			Mapcontroller instance = Mapcontroller.Instance;
			ICollection<MapPin> collection = (((Object)(object)instance != (Object)null) ? GameAPI.GetMapControllerPins(instance) : null);
			foreach (MapPin value in _mapPins.Values)
			{
				if (!((Object)(object)value == (Object)null))
				{
					collection?.Remove(value);
					GameAPI.SetMapPinValid(value, value: false);
					GameAPI.MapPinDisable(value);
					Object.Destroy((Object)(object)((Component)value).gameObject);
				}
			}
			_mapPins.Clear();
			if ((Object)(object)_indicators != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_indicators).gameObject);
				_indicators = null;
			}
			RestoreOutlineColors();
			_outlineColorBackups.Clear();
			foreach (GraffitiSpot otherSpot in _otherSpots)
			{
				if ((Object)(object)otherSpot != (Object)null)
				{
					((Component)otherSpot).gameObject.SetActive(true);
				}
			}
			foreach (KeyValuePair<GraffitiSpot, object> item in _originalProgress)
			{
				if ((Object)(object)item.Key != (Object)null && item.Value != null)
				{
					GameAPI.SetProgressableData((AProgressable)(object)item.Key, item.Value);
					((AProgressable)item.Key).ReadFromData();
				}
			}
			_originalProgress.Clear();
			_otherSpots.Clear();
			Player currentPlayer = WorldHandler.instance.GetCurrentPlayer();
			if ((Object)(object)currentPlayer != (Object)null)
			{
				GameAPI.SetPlayerUserInputEnabled(currentPlayer, value: true);
			}
			if ((Object)(object)_timerUI != (Object)null)
			{
				_timerUI.DeactivateDelayed();
				_timerUI = null;
			}
			_state = State.None;
			_raceSpots = null;
			LocalGRACEPlugin.OnRaceEnded(cancelled);
		}

		public void Update()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			if (!InGame)
			{
				return;
			}
			if (_state == State.Countdown)
			{
				_timerUI?.SetMainLabelColor(LocalGRACEPlugin.GetUIColorFromHex(LocalGRACEPlugin.UICountdownTimerColorHex, Color.yellow));
				_countdownTimer += Time.deltaTime;
				int num = Mathf.Max(0, _countdownSeconds - Mathf.FloorToInt(_countdownTimer));
				if (num != _lastCountdownRemaining)
				{
					_lastCountdownRemaining = num;
					if (LocalGRACEPlugin.ShowUICountdownTimer != null && LocalGRACEPlugin.ShowUICountdownTimer.Value)
					{
						_timerUI?.SetText((num > 0) ? num.ToString() : "GO!");
					}
					else
					{
						_timerUI?.SetText("");
					}
					_timerUI?.SetTime("");
				}
				if (_countdownTimer >= (float)_countdownSeconds)
				{
					_state = State.Main;
					_raceElapsedTime = 0f;
					Player currentPlayer = WorldHandler.instance.GetCurrentPlayer();
					if ((Object)(object)currentPlayer != (Object)null)
					{
						GameAPI.SetPlayerUserInputEnabled(currentPlayer, value: true);
					}
				}
			}
			else
			{
				if (_state != State.Main)
				{
					return;
				}
				_timerUI?.SetMainLabelColor(LocalGRACEPlugin.GetUIColorFromHex(LocalGRACEPlugin.UITagCounterColorHex, Color.white));
				if (_score < TotalSpots)
				{
					_raceElapsedTime += Time.deltaTime;
				}
				if (_score != _lastDisplayedScore)
				{
					_lastDisplayedScore = _score;
					if (LocalGRACEPlugin.ShowUITagCounter != null && LocalGRACEPlugin.ShowUITagCounter.Value)
					{
						_timerUI?.SetText($"{_score}/{TotalSpots}");
					}
					else
					{
						_timerUI?.SetText("");
					}
				}
				string text = FormatRaceTime(_raceElapsedTime);
				if (text != _lastFormattedRaceTime)
				{
					_lastFormattedRaceTime = text;
					if (LocalGRACEPlugin.ShowUIRaceTimeTracker != null && LocalGRACEPlugin.ShowUIRaceTimeTracker.Value)
					{
						_timerUI?.SetTime(text);
					}
					else
					{
						_timerUI?.SetTime("");
					}
				}
			}
		}

		private static string FormatRaceTime(float seconds)
		{
			int num = Mathf.FloorToInt(seconds / 60f);
			float num2 = seconds - (float)num * 60f;
			if (num > 0)
			{
				return $"{num}:{num2:00.00}";
			}
			return $"{num2:0.00}";
		}

		public bool IsPartOfRace(GraffitiSpot spot)
		{
			if (_originalProgress != null)
			{
				return _originalProgress.ContainsKey(spot);
			}
			return false;
		}

		public bool IsRaceGraffitiSpot(GraffitiSpot spot)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			if (IsPartOfRace(spot))
			{
				return (int)spot.topCrew == 0;
			}
			return false;
		}

		public void AddScore(GraffitiSpot gSpot)
		{
			if (_state == State.Main)
			{
				_score++;
				_lastDisplayedScore = _score;
				_lastFormattedRaceTime = FormatRaceTime(_raceElapsedTime);
				MarkGraffitiSpotDone(gSpot);
				_timerUI?.SetText($"{_score}/{TotalSpots}");
				_timerUI?.SetTime(_lastFormattedRaceTime);
				if (_score >= TotalSpots)
				{
					EndRace(cancelled: false);
				}
			}
		}

		public void MarkGraffitiSpotDone(GraffitiSpot grafSpot)
		{
			_indicators?.RemoveIndicator(((Component)grafSpot).transform);
			if (_mapPins.TryGetValue(grafSpot, out var value))
			{
				GameAPI.GetMapControllerPins(Mapcontroller.Instance)?.Remove(value);
				GameAPI.SetMapPinValid(value, value: false);
				GameAPI.MapPinDisable(value);
				Object.Destroy((Object)(object)((Component)value).gameObject);
				_mapPins.Remove(grafSpot);
			}
		}

		public int GetValidSpotCount()
		{
			return GetValidSpots()?.Count ?? 0;
		}

		private List<GraffitiSpot> GetValidSpots()
		{
			_worldHandler = WorldHandler.instance;
			if ((Object)(object)_worldHandler == (Object)null)
			{
				return new List<GraffitiSpot>();
			}
			List<GraffitiSpot> grafSpots = GameAPI.GetGrafSpots(_worldHandler);
			if (grafSpots == null || grafSpots.Count == 0)
			{
				return grafSpots ?? new List<GraffitiSpot>();
			}
			return grafSpots.Where(GameAPI.IsValidGraffitiSpotForRace).ToList();
		}

		private void ApplyOutlineColorToSpot(GraffitiSpot spot, Color color)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)spot == (Object)null || (Object)(object)((Component)spot).gameObject == (Object)null)
			{
				return;
			}
			List<(Renderer, Material, int, Color, Material)> list = new List<(Renderer, Material, int, Color, Material)>();
			Renderer[] componentsInChildren = ((Component)spot).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Material material = val.material;
				if ((Object)(object)material == (Object)null)
				{
					continue;
				}
				int num = -1;
				if (material.HasProperty(ShaderPropColor))
				{
					num = ShaderPropColor;
				}
				else if (material.HasProperty(ShaderPropBaseColor))
				{
					num = ShaderPropBaseColor;
				}
				if (num >= 0)
				{
					Color color2 = material.GetColor(num);
					list.Add((val, material, num, color2, null));
					material.SetColor(num, color);
					continue;
				}
				Material val2 = CreateMaterialWithColor(material, color);
				if ((Object)(object)val2 != (Object)null)
				{
					list.Add((val, material, -1, default(Color), val2));
					val.material = val2;
				}
			}
			if (list.Count > 0)
			{
				_outlineColorBackups[spot] = list;
			}
		}

		private static Material CreateMaterialWithColor(Material source, Color color)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0090: 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)
			Shader val = Shader.Find("Unlit/Texture") ?? Shader.Find("Standard") ?? Shader.Find("Legacy Shaders/VertexLit");
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Material val2 = new Material(val);
			if ((Object)(object)source != (Object)null && source.HasProperty(ShaderPropMainTex))
			{
				Texture texture = source.GetTexture(ShaderPropMainTex);
				if ((Object)(object)texture != (Object)null && val2.HasProperty(ShaderPropMainTex))
				{
					val2.SetTexture(ShaderPropMainTex, texture);
				}
			}
			if (val2.HasProperty(ShaderPropColor))
			{
				val2.SetColor(ShaderPropColor, color);
			}
			else if (val2.HasProperty(ShaderPropBaseColor))
			{
				val2.SetColor(ShaderPropBaseColor, color);
			}
			return val2;
		}

		private void RestoreOutlineColors()
		{
			//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)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			foreach (List<(Renderer, Material, int, Color, Material)> value in _outlineColorBackups.Values)
			{
				foreach (var (val, val2, num, val3, val4) in value)
				{
					if ((Object)(object)val4 != (Object)null)
					{
						if ((Object)(object)val != (Object)null)
						{
							val.material = val2;
						}
						Object.Destroy((Object)(object)val4);
					}
					else if ((Object)(object)val2 != (Object)null && num >= 0)
					{
						val2.SetColor(num, val3);
					}
				}
			}
		}

		private void TurnOnAllStageChunks()
		{
			_offStageChunks.Clear();
			IList stageChunks = GameAPI.GetStageChunks(WorldHandler.instance);
			if (stageChunks == null)
			{
				return;
			}
			foreach (object item in stageChunks)
			{
				StageChunk val = (StageChunk)((item is StageChunk) ? item : null);
				if (val != null && !((Component)val).gameObject.activeSelf)
				{
					_offStageChunks.Add(val);
					((Component)val).gameObject.SetActive(true);
				}
			}
		}

		private void RestoreStageChunks()
		{
			foreach (StageChunk offStageChunk in _offStageChunks)
			{
				if ((Object)(object)offStageChunk != (Object)null)
				{
					((Component)offStageChunk).gameObject.SetActive(false);
				}
			}
			_offStageChunks.Clear();
		}

		private static void PlacePlayerAt(Vector3 position, Quaternion rotation)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			Player currentPlayer = WorldHandler.instance.GetCurrentPlayer();
			if (!((Object)(object)currentPlayer == (Object)null))
			{
				currentPlayer.CompletelyStop();
				WorldHandler.instance.PlacePlayerAt(currentPlayer, position, rotation, true);
				currentPlayer.CompletelyStop();
				currentPlayer.SetPosAndRotHard(position, rotation);
			}
		}
	}
	public class TimerUI : MonoBehaviour
	{
		private GameObject _canvasGo;

		private TextMeshProUGUI _label;

		private TextMeshProUGUI _timeLabel;

		private TextMeshProUGUI _pbLabel;

		private TextMeshProUGUI _avgLabel;

		public void Activate()
		{
			((MonoBehaviour)this).StopAllCoroutines();
			if ((Object)(object)_canvasGo != (Object)null)
			{
				_canvasGo.SetActive(true);
			}
			RefreshPbAvg();
		}

		private void RefreshPbAvg()
		{
			int currentStageId = GameAPI.GetCurrentStageId();
			double? bestTime = GraceStats.GetBestTime(currentStageId);
			double? averageTime = GraceStats.GetAverageTime(currentStageId);
			if ((Object)(object)_pbLabel != (Object)null)
			{
				((TMP_Text)_pbLabel).text = "PB: " + (bestTime.HasValue ? $"{bestTime.Value:F1}s" : "—");
			}
			if ((Object)(object)_avgLabel != (Object)null)
			{
				((TMP_Text)_avgLabel).text = "AVG: " + (averageTime.HasValue ? $"{averageTime.Value:F1}s" : "—");
			}
		}

		public void Deactivate()
		{
			((MonoBehaviour)this).StopAllCoroutines();
			if ((Object)(object)_canvasGo != (Object)null)
			{
				_canvasGo.SetActive(false);
			}
		}

		public void DeactivateDelayed()
		{
			((MonoBehaviour)this).StartCoroutine(DeactivateCoroutine());
		}

		private IEnumerator DeactivateCoroutine()
		{
			yield return (object)new WaitForSeconds(3f);
			Deactivate();
		}

		public void SetText(string text)
		{
			if ((Object)(object)_label != (Object)null)
			{
				((TMP_Text)_label).text = text;
			}
		}

		public void SetMainLabelColor(Color color)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_label != (Object)null)
			{
				((Graphic)_label).color = color;
			}
		}

		public void SetTime(string timeText)
		{
			if ((Object)(object)_timeLabel != (Object)null)
			{
				((TMP_Text)_timeLabel).text = timeText;
				((Component)_timeLabel).gameObject.SetActive(!string.IsNullOrEmpty(timeText));
			}
		}

		private void Awake()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			Core instance = Core.Instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				UIManager uIManager = instance.UIManager;
				obj = ((uIManager != null) ? ((Component)uIManager).transform : null);
			}
			Transform val = (Transform)obj;
			if (!((Object)(object)val == (Object)null))
			{
				_canvasGo = new GameObject("LocalGRACE_TimerCanvas");
				_canvasGo.transform.SetParent(val, false);
				Canvas obj2 = _canvasGo.AddComponent<Canvas>();
				obj2.renderMode = (RenderMode)0;
				obj2.sortingOrder = 100;
				_canvasGo.AddComponent<CanvasScaler>();
				_canvasGo.AddComponent<GraphicRaycaster>();
				CreateTMPLabels();
				Deactivate();
			}
		}

		private void CreateTMPLabels()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0075: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Expected O, but got Unknown
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: 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_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Expected O, but got Unknown
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: Expected O, but got Unknown
			//IL_03d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02de: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_030a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0320: Unknown result type (might be due to invalid IL or missing references)
			//IL_0336: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0415: Unknown result type (might be due to invalid IL or missing references)
			//IL_042b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0441: Unknown result type (might be due to invalid IL or missing references)
			//IL_0457: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Timer");
			val.transform.SetParent(_canvasGo.transform, false);
			_label = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)_label).text = "0";
			((TMP_Text)_label).fontSize = 72f;
			((TMP_Text)_label).fontStyle = (FontStyles)2;
			((TMP_Text)_label).alignment = (TextAlignmentOptions)513;
			((Graphic)_label).color = LocalGRACEPlugin.GetUIColorFromHex(LocalGRACEPlugin.UITagCounterColorHex, Color.white);
			RectTransform component = val.GetComponent<RectTransform>();
			if ((Object)(object)component != (Object)null)
			{
				component.anchorMin = new Vector2(0f, 1f);
				component.anchorMax = new Vector2(0f, 1f);
				component.pivot = new Vector2(0f, 1f);
				component.anchoredPosition = new Vector2(24f, -2f);
				component.sizeDelta = new Vector2(400f, 100f);
			}
			GameObject val2 = new GameObject("Time");
			val2.transform.SetParent(_canvasGo.transform, false);
			_timeLabel = val2.AddComponent<TextMeshProUGUI>();
			((TMP_Text)_timeLabel).text = "";
			((TMP_Text)_timeLabel).fontSize = 48f;
			((TMP_Text)_timeLabel).fontStyle = (FontStyles)2;
			((TMP_Text)_timeLabel).alignment = (TextAlignmentOptions)513;
			((Graphic)_timeLabel).color = LocalGRACEPlugin.GetUIColorFromHex(LocalGRACEPlugin.UIRaceTimeTrackerColorHex, new Color(0.85f, 0.85f, 0.85f, 1f));
			((Component)_timeLabel).gameObject.SetActive(false);
			RectTransform component2 = val2.GetComponent<RectTransform>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.anchorMin = new Vector2(0f, 1f);
				component2.anchorMax = new Vector2(0f, 1f);
				component2.pivot = new Vector2(0f, 1f);
				component2.anchoredPosition = new Vector2(24f, -102f);
				component2.sizeDelta = new Vector2(400f, 60f);
			}
			if (LocalGRACEPlugin.ShowUIPB != null && LocalGRACEPlugin.ShowUIPB.Value)
			{
				GameObject val3 = new GameObject("PB");
				val3.transform.SetParent(_canvasGo.transform, false);
				_pbLabel = val3.AddComponent<TextMeshProUGUI>();
				((TMP_Text)_pbLabel).text = "PB: —";
				((TMP_Text)_pbLabel).fontSize = 36f;
				((TMP_Text)_pbLabel).fontStyle = (FontStyles)2;
				((TMP_Text)_pbLabel).alignment = (TextAlignmentOptions)513;
				((Graphic)_pbLabel).color = LocalGRACEPlugin.GetUIColorFromHex(LocalGRACEPlugin.UIPBColorHex, Color.green);
				RectTransform component3 = val3.GetComponent<RectTransform>();
				if ((Object)(object)component3 != (Object)null)
				{
					component3.anchorMin = new Vector2(0f, 1f);
					component3.anchorMax = new Vector2(0f, 1f);
					component3.pivot = new Vector2(0f, 1f);
					component3.anchoredPosition = new Vector2(24f, -168f);
					component3.sizeDelta = new Vector2(350f, 44f);
				}
			}
			if (LocalGRACEPlugin.ShowUIAVG != null && LocalGRACEPlugin.ShowUIAVG.Value)
			{
				GameObject val4 = new GameObject("AVG");
				val4.transform.SetParent(_canvasGo.transform, false);
				_avgLabel = val4.AddComponent<TextMeshProUGUI>();
				((TMP_Text)_avgLabel).text = "AVG: —";
				((TMP_Text)_avgLabel).fontSize = 36f;
				((TMP_Text)_avgLabel).fontStyle = (FontStyles)2;
				((TMP_Text)_avgLabel).alignment = (TextAlignmentOptions)513;
				((Graphic)_avgLabel).color = LocalGRACEPlugin.GetUIColorFromHex(LocalGRACEPlugin.UIAVGColorHex, Color.yellow);
				RectTransform component4 = val4.GetComponent<RectTransform>();
				if ((Object)(object)component4 != (Object)null)
				{
					component4.anchorMin = new Vector2(0f, 1f);
					component4.anchorMax = new Vector2(0f, 1f);
					component4.pivot = new Vector2(0f, 1f);
					component4.anchoredPosition = new Vector2(24f, -218f);
					component4.sizeDelta = new Vector2(350f, 44f);
				}
			}
		}

		public static TimerUI CreateAndGet()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			Core instance = Core.Instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				UIManager uIManager = instance.UIManager;
				obj = ((uIManager != null) ? ((Component)uIManager).transform : null);
			}
			Transform val = (Transform)obj;
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			GameObject val2 = new GameObject("LocalGRACE_TimerUI");
			val2.transform.SetParent(val, false);
			return val2.AddComponent<TimerUI>();
		}
	}
	public class UIScreenIndicators : MonoBehaviour
	{
		private float _minDistance = 5f;

		private float _maxDistance = 20f;

		private float _distanceSizeMultiplier = 0.5f;

		private Vector2 _originalSize;

		private UIManager _uiManager;

		private GameObject _indicator;

		private Transform _canvasTf;

		private Dictionary<Transform, RectTransform> _indicators = new Dictionary<Transform, RectTransform>();

		private void Awake()
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			_uiManager = Core.Instance.UIManager;
			_canvasTf = ((Component)this).transform.Find("Canvas");
			_indicator = ((Component)_canvasTf.Find("Indicator")).gameObject;
			RectTransform component = _indicator.GetComponent<RectTransform>();
			_originalSize = (Vector2)(((Object)(object)component != (Object)null) ? component.sizeDelta : new Vector2(117.2f, 116.8f));
			_indicator.SetActive(false);
		}

		private void LateUpdate()
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: 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_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			if (!GameAPI.IsGameplayScreenActive(_uiManager))
			{
				((Component)_canvasTf).gameObject.SetActive(false);
				return;
			}
			((Component)_canvasTf).gameObject.SetActive(true);
			Camera val = GameAPI.GetGameplayCameraCam();
			if ((Object)(object)val == (Object)null)
			{
				val = Camera.main;
			}
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			foreach (KeyValuePair<Transform, RectTransform> indicator in _indicators)
			{
				Transform key = indicator.Key;
				RectTransform value = indicator.Value;
				if (!IsInFront(((Component)val).transform.position, ((Component)val).transform.forward, key.position))
				{
					((Component)value).gameObject.SetActive(false);
					continue;
				}
				((Component)value).gameObject.SetActive(true);
				Vector3 val2 = val.WorldToViewportPoint(key.position);
				value.anchorMax = Vector2.op_Implicit(val2);
				value.anchorMin = Vector2.op_Implicit(val2);
				float num = Vector3.Distance(((Component)val).transform.position, key.position);
				num -= _minDistance;
				num /= _maxDistance;
				num = Mathf.Clamp(num, 0f, 1f);
				value.sizeDelta = Vector2.Lerp(_originalSize, _originalSize * _distanceSizeMultiplier, num);
			}
		}

		private bool IsInFront(Vector3 cameraPos, Vector3 cameraForward, Vector3 targetPos)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//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)
			Vector3 val = targetPos - cameraPos;
			return Vector3.Dot(cameraForward, val) > 0f;
		}

		public void AddIndicator(Transform target)
		{
			GameObject val = Object.Instantiate<GameObject>(_indicator);
			val.transform.SetParent(_canvasTf, false);
			val.SetActive(true);
			_indicators[target] = val.GetComponent<RectTransform>();
		}

		public void RemoveIndicator(Transform target)
		{
			if (_indicators.TryGetValue(target, out var value))
			{
				_indic