Decompiled source of EasyPZ v3.0.3

plugins/EasyPZ/EasyPZ.dll

Decompiled 5 months ago
using System;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using Configgy;
using Configgy.Assets;
using Configgy.UI;
using EasyPZ.Components;
using EasyPZ.Ghosts;
using EasyPZ.Properties;
using EasyPZ.UIEdit;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Steamworks;
using Steamworks.Data;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.AddressableAssets;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Hydraxous")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Tiny QoL mod for P-Ranking in ULTRAKILL")]
[assembly: AssemblyFileVersion("3.0.3.0")]
[assembly: AssemblyInformationalVersion("3.0.3+037c0d77cba207114d0fa225c89b471185b21850")]
[assembly: AssemblyProduct("EasyPZ")]
[assembly: AssemblyTitle("EasyPZ")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("3.0.3.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
[Serializable]
public class EasyPZCache
{
	public bool AskedAboutRecording;
}
namespace EasyPZ
{
	internal static class Paths
	{
		[Configgable("Ghosts/Recording", "Recording Path", 0, null)]
		public static ConfigInputField<string> ghostRecordingPath = new ConfigInputField<string>(DataFolder, (Func<string, bool>)null, (Func<string, ValueTuple<bool, string>>)null);

		public static string ExecutionPath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

		public static string DataFolder => Path.Combine(ExecutionPath, "GhostRecordings");

		[Configgable("Ghosts/Recording", "Open Recordings Folder", 0, null)]
		public static void OpenDataFolder()
		{
			Application.OpenURL(((ConfigValueElement<string>)(object)ghostRecordingPath).Value);
		}
	}
	public static class ConstInfo
	{
		public const string NAME = "EasyPZ";

		public const string GUID = "Hydraxous.ULTRAKILL.EasyPZ";

		public const string VERSION = "3.0.3";

		public const string GITHUB_URL = "https://www.github.com/Hydraxous/EasyPZ-ULTRAKILL";

		public const string GITHUB_VERSION_URL = "https://api.github.com/repos/Hydraxous/EasyPZ-ULTRAKILL/tags";

		public const string KOFI_URL = "https://www.ko-fi.com/Hydraxous";

		public const string DISCORD_URL = "https://discord.gg/kCHnwMDPt4";
	}
	internal static class Data
	{
		private static EasyPZCache cache;

		private const string cacheFileName = "cache.json";

		internal static EasyPZCache Cache
		{
			get
			{
				if (cache == null)
				{
					LoadCache();
				}
				return cache;
			}
		}

		private static string cacheFilePath => Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "cache.json");

		private static void LoadCache()
		{
			if (!File.Exists(cacheFilePath))
			{
				cache = new EasyPZCache();
				return;
			}
			try
			{
				string text = File.ReadAllText(cacheFilePath);
				cache = JsonConvert.DeserializeObject<EasyPZCache>(text);
				if (cache == null)
				{
					throw new Exception("Cache data is null or corrupted.");
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)"Failed to load EasyPZ cache file");
				Debug.LogException(ex);
				cache = new EasyPZCache();
			}
		}

		public static void SaveCache()
		{
			if (cache != null)
			{
				string contents = JsonConvert.SerializeObject((object)cache, (Formatting)1);
				File.WriteAllText(cacheFilePath, contents);
			}
		}
	}
	public static class InGameCheck
	{
		public enum UKLevelType
		{
			Intro,
			MainMenu,
			Level,
			Endless,
			Sandbox,
			Credits,
			Custom,
			Intermission,
			Secret,
			PrimeSanctum,
			Unknown
		}

		public delegate void OnLevelChangedHandler(UKLevelType uKLevelType);

		private static bool initialized;

		public static UKLevelType CurrentLevelType = UKLevelType.Intro;

		public static string CurrentSceneName = "";

		public static OnLevelChangedHandler OnLevelTypeChanged;

		public static OnLevelChangedHandler OnLevelChanged;

		[Configgable("Extras/Advanced", "Force Tracker In All Scenes", 0, null)]
		private static ConfigToggle CFG_ForceInLevelCheckTrue = new ConfigToggle(false);

		public static void Init()
		{
			if (!initialized)
			{
				initialized = true;
				SceneManager.sceneLoaded += OnSceneLoad;
			}
		}

		private static void OnSceneLoad(Scene scene, LoadSceneMode loadSceneMode)
		{
			//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)
			if (!(scene != SceneManager.GetActiveScene()))
			{
				UKLevelType uKLevelType = GetUKLevelType(SceneHelper.CurrentScene);
				if (uKLevelType != CurrentLevelType)
				{
					CurrentLevelType = uKLevelType;
					CurrentSceneName = SceneHelper.CurrentScene;
					OnLevelTypeChanged?.Invoke(uKLevelType);
				}
				OnLevelChanged?.Invoke(CurrentLevelType);
			}
		}

		public static UKLevelType GetUKLevelType(string sceneName)
		{
			sceneName = (sceneName.Contains("P-") ? "Sanctum" : sceneName);
			sceneName = (sceneName.Contains("-S") ? "Secret" : sceneName);
			sceneName = (sceneName.Contains("Level") ? "Level" : sceneName);
			sceneName = (sceneName.Contains("Intermission") ? "Intermission" : sceneName);
			return sceneName switch
			{
				"Main Menu" => UKLevelType.MainMenu, 
				"Custom Content" => UKLevelType.Custom, 
				"Intro" => UKLevelType.Intro, 
				"Endless" => UKLevelType.Endless, 
				"uk_construct" => UKLevelType.Sandbox, 
				"Intermission" => UKLevelType.Intermission, 
				"Level" => UKLevelType.Level, 
				"Secret" => UKLevelType.Secret, 
				"Sanctum" => UKLevelType.PrimeSanctum, 
				"CreditsMuseum2" => UKLevelType.Credits, 
				_ => UKLevelType.Unknown, 
			};
		}

		public static bool InLevel()
		{
			if (((ConfigValueElement<bool>)(object)CFG_ForceInLevelCheckTrue).Value)
			{
				return true;
			}
			UKLevelType currentLevelType = CurrentLevelType;
			UKLevelType uKLevelType = currentLevelType;
			if ((uint)uKLevelType <= 1u || (uint)(uKLevelType - 7) <= 1u)
			{
				return false;
			}
			return true;
		}
	}
	[BepInPlugin("Hydraxous.ULTRAKILL.EasyPZ", "EasyPZ", "3.0.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class EasyPZ : BaseUnityPlugin
	{
		private Harmony harmony;

		public static AssetLoader AssetLoader { get; private set; }

		public static EasyPZ Instance { get; private set; }

		public static ConfigBuilder ConfigBuilder { get; private set; }

		public static string LatestVersion { get; private set; } = "3.0.3";


		public static bool UsingLatestVersion { get; private set; } = true;


		private void Awake()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			Instance = this;
			AssetLoader = new AssetLoader(EasyPZResources.EasyPZ);
			harmony = new Harmony("Hydraxous.ULTRAKILL.EasyPZ.harmony");
			harmony.PatchAll();
			ConfigBuilder = new ConfigBuilder("Hydraxous.ULTRAKILL.EasyPZ", "EasyPZ");
			ConfigBuilder.BuildAll();
			InGameCheck.Init();
			GhostManager.PreloadGhostPrefab();
			VersionCheck.CheckVersion("https://api.github.com/repos/Hydraxous/EasyPZ-ULTRAKILL/tags", "3.0.3", (Action<bool, string>)delegate(bool r, string version)
			{
				UsingLatestVersion = r;
				if (!r)
				{
					LatestVersion = version;
					Debug.LogWarning((object)("EasyPZ is out of date. New version available (" + version + ")"));
				}
			});
			((BaseUnityPlugin)this).Logger.LogInfo((object)"EasyPZ Loaded. Good luck on P-ing in all the levels! :D");
		}
	}
	public static class Prefabs
	{
		public static GameObject ClassicTrackerPrefab => EasyPZ.AssetLoader.LoadAsset<GameObject>("ClassicTracker");

		public static GameObject StandardTracker => EasyPZ.AssetLoader.LoadAsset<GameObject>("StandardTracker");

		public static GameObject CompactTracker => EasyPZ.AssetLoader.LoadAsset<GameObject>("CompactTracker");

		public static GameObject TrackerManager => EasyPZ.AssetLoader.LoadAsset<GameObject>("TrackerManager");

		public static GameObject RunManagerMenu => EasyPZ.AssetLoader.LoadAsset<GameObject>("RunManagerMenu");

		public static GameObject PlayerNameplate => EasyPZ.AssetLoader.LoadAsset<GameObject>("PlayerNameplate");

		public static GameObject GhostSavedNotifier => EasyPZ.AssetLoader.LoadAsset<GameObject>("GhostSavedNotifier");

		public static Material GhostMaterial => EasyPZ.AssetLoader.LoadAsset<Material>("GhostMaterial");

		public static AudioClip ShittyBoom => EasyPZ.AssetLoader.LoadAsset<AudioClip>("ShittyBoom");

		public static Font VCR_Font => EasyPZ.AssetLoader.LoadAsset<Font>("VCR_OSD_MONO");
	}
	public static class Restarter
	{
		[Configgable("Auto Restart", "Restart Type", 0, null)]
		private static ConfigDropdown<RestartType> restartType = new ConfigDropdown<RestartType>((RestartType[])Enum.GetValues(typeof(RestartType)), ((RestartType[])Enum.GetValues(typeof(RestartType))).Select((RestartType x) => x.ToString()).ToArray(), 0);

		[Configgable("Auto Restart", "Sound Effect On Auto Restart", 0, null)]
		private static ConfigToggle soundEffectOnAutoRestart = new ConfigToggle(true);

		private static AudioClip _customSound;

		private static readonly string[] fileFormats = new string[3] { ".wav", ".mp3", ".ogg" };

		private static AudioSource _audioSource;

		private static AudioClip _restartClip;

		private static AudioClip customSound
		{
			get
			{
				if ((Object)(object)_customSound == (Object)null)
				{
				}
				return _customSound;
			}
		}

		private static AudioSource audioSource
		{
			get
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_audioSource == (Object)null)
				{
					_audioSource = new GameObject("EZPZ Beeper").AddComponent<AudioSource>();
					_audioSource.playOnAwake = false;
					_audioSource.loop = false;
					_audioSource.volume = 1f;
					_audioSource.spatialBlend = 0f;
					_audioSource.clip = restartClip;
					Object.DontDestroyOnLoad((Object)(object)((Component)_audioSource).gameObject);
				}
				return _audioSource;
			}
		}

		private static AudioClip restartClip
		{
			get
			{
				if ((Object)(object)_restartClip == (Object)null)
				{
					_restartClip = Prefabs.ShittyBoom;
				}
				return _restartClip;
			}
		}

		private static bool ValidateSoundFileLocation(string filePath)
		{
			if (!File.Exists(filePath))
			{
				Debug.LogError((object)("File " + filePath + " not found!"));
				return false;
			}
			for (int i = 0; i < fileFormats.Length; i++)
			{
				if (filePath.EndsWith(fileFormats[i]))
				{
					return true;
				}
			}
			Debug.LogError((object)("File " + filePath + " filetype not supported!"));
			return false;
		}

		public static void Restart()
		{
			if (((ConfigValueElement<bool>)(object)soundEffectOnAutoRestart).Value)
			{
				audioSource.Play();
			}
			MonoSingleton<OptionsManager>.Instance.RestartMission();
		}

		private static IEnumerator RestartAfterTime(float time)
		{
			yield return (object)new WaitForSeconds(time);
			MonoSingleton<OptionsManager>.Instance.RestartMission();
		}
	}
	public enum RestartType
	{
		Standard
	}
	public static class ThemeHelper
	{
		public static readonly Color PColorOn = new Color(255f, 119f, 0f, 255f);

		public static readonly Color PColorOff = new Color(191f, 191f, 191f, 150f);
	}
	public class AssetLoader
	{
		private Dictionary<string, Object> loadedAssets = new Dictionary<string, Object>();

		public AssetBundle Bundle { get; }

		public AssetLoader(AssetBundle bundle)
		{
			Bundle = bundle;
		}

		public AssetLoader(byte[] bundleBytes)
		{
			Bundle = AssetBundle.LoadFromMemory(bundleBytes);
		}

		public AssetLoader(string filePath)
		{
			Bundle = AssetBundle.LoadFromFile(filePath);
		}

		public T LoadAsset<T>(string assetName) where T : Object
		{
			if (loadedAssets.ContainsKey(assetName))
			{
				return (T)(object)loadedAssets[assetName];
			}
			T val = Bundle.LoadAsset<T>(assetName);
			if ((Object)(object)val == (Object)null)
			{
				return default(T);
			}
			loadedAssets.Add(assetName, (Object)(object)val);
			return val;
		}

		public T[] LoadAllAssets<T>() where T : Object
		{
			T[] array = Bundle.LoadAllAssets<T>();
			T[] array2 = array;
			foreach (T val in array2)
			{
				if (!((Object)(object)val == (Object)null) && !loadedAssets.ContainsKey(((Object)val).name))
				{
					loadedAssets.Add(((Object)val).name, (Object)(object)val);
				}
			}
			return array;
		}

		public void Unload(bool unloadAllLoadedObjects = true)
		{
			Bundle.Unload(unloadAllLoadedObjects);
		}
	}
	public interface IEZPZTracker
	{
		void SetStatGoal(StatGoal goal);
	}
	public interface IUIEditable
	{
		void StartEditMode();

		void EndEditMode();
	}
	[Serializable]
	public struct StatGoal
	{
		public int Difficulty;

		public int Kills;

		public float Seconds;

		public int Deaths;

		public int Style;

		public bool NotEmpty()
		{
			return Kills > 0 || Seconds > 0f || Deaths > 0 || Style > 0;
		}

		public bool IsFailed()
		{
			if (MonoSingleton<NewMovement>.Instance.dead)
			{
				if (MonoSingleton<StatsManager>.Instance.restarts + 1 > Deaths)
				{
					return true;
				}
			}
			else if (MonoSingleton<StatsManager>.Instance.restarts > Deaths)
			{
				return true;
			}
			if (MonoSingleton<StatsManager>.Instance.seconds > Seconds)
			{
				return true;
			}
			if (MonoSingleton<StatsManager>.Instance.infoSent)
			{
				if (MonoSingleton<StatsManager>.Instance.kills < Kills)
				{
					return true;
				}
				if (MonoSingleton<StatsManager>.Instance.stylePoints < Style)
				{
					return true;
				}
			}
			return false;
		}

		public bool IsComplete()
		{
			if (MonoSingleton<StatsManager>.Instance.seconds > Seconds)
			{
				return false;
			}
			if (MonoSingleton<StatsManager>.Instance.restarts > Deaths)
			{
				return false;
			}
			if (MonoSingleton<StatsManager>.Instance.stylePoints < Style)
			{
				return false;
			}
			if (MonoSingleton<StatsManager>.Instance.kills < Kills)
			{
				return false;
			}
			return true;
		}
	}
}
namespace EasyPZ.Properties
{
	[GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
	[DebuggerNonUserCode]
	[CompilerGenerated]
	internal class EasyPZResources
	{
		private static ResourceManager resourceMan;

		private static CultureInfo resourceCulture;

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		internal static ResourceManager ResourceManager
		{
			get
			{
				if (resourceMan == null)
				{
					ResourceManager resourceManager = new ResourceManager("EasyPZ.Properties.Resources", typeof(EasyPZResources).Assembly);
					resourceMan = resourceManager;
				}
				return resourceMan;
			}
		}

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		internal static CultureInfo Culture
		{
			get
			{
				return resourceCulture;
			}
			set
			{
				resourceCulture = value;
			}
		}

		internal static byte[] EasyPZ
		{
			get
			{
				object @object = ResourceManager.GetObject("EasyPZ", resourceCulture);
				return (byte[])@object;
			}
		}

		internal EasyPZResources()
		{
		}
	}
}
namespace EasyPZ.Patches
{
	[HarmonyPatch(typeof(CanvasController))]
	public static class InstanceUI
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		public static void OnAwake(CanvasController __instance)
		{
			RectTransform component = ((Component)__instance).GetComponent<RectTransform>();
			if ((Object)(object)component == (Object)null)
			{
				Debug.LogError((object)"EZPZ: Canvas controller patch issue!, RectTransform could not be found!");
			}
			else
			{
				InstanceElements(component);
			}
		}

		private static void InstanceElements(RectTransform root)
		{
			Object.Instantiate<GameObject>(Prefabs.TrackerManager, (Transform)(object)root);
			Object.Instantiate<GameObject>(Prefabs.RunManagerMenu, (Transform)(object)root);
			Object.Instantiate<GameObject>(Prefabs.GhostSavedNotifier, (Transform)(object)root);
		}
	}
}
namespace EasyPZ.UIEdit
{
	public class MovableWindow : MonoBehaviour, IBeginDragHandler, IEventSystemHandler, IDragHandler
	{
		[SerializeField]
		private RectTransform target;

		private Canvas canvas;

		public void OnBeginDrag(PointerEventData eventData)
		{
			if (((Behaviour)this).enabled)
			{
				canvas = ((Component)target).GetComponentInParent<Canvas>();
			}
		}

		public void OnDrag(PointerEventData eventData)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (((Behaviour)this).enabled)
			{
				RectTransform obj = target;
				obj.anchoredPosition += eventData.delta / canvas.scaleFactor;
			}
		}
	}
	public class WindowScalar : MonoBehaviour, IBeginDragHandler, IEventSystemHandler, IDragHandler
	{
		[SerializeField]
		private RectTransform target;

		[SerializeField]
		private Vector2 maxSize;

		[SerializeField]
		private Vector2 minSize;

		[SerializeField]
		private TextAnchor scaleDirection;

		private Canvas canvas;

		public void OnBeginDrag(PointerEventData eventData)
		{
			canvas = ((Component)target).GetComponentInParent<Canvas>();
		}

		public void OnDrag(PointerEventData eventData)
		{
			//IL_0003: 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_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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)
			Vector2 val = Vector2.Scale(GetMultiplier(scaleDirection), eventData.delta / canvas.scaleFactor);
			Vector2 val2 = target.sizeDelta + val;
			val2.x = Mathf.Clamp(val2.x, minSize.x, maxSize.x);
			val2.y = Mathf.Clamp(val2.y, minSize.y, maxSize.y);
			target.sizeDelta = val2;
		}

		private Vector2 GetMultiplier(TextAnchor alignment)
		{
			//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_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected I4, but got Unknown
			//IL_003e: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: 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_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			return (Vector2)((int)alignment switch
			{
				0 => new Vector2(-1f, 1f), 
				2 => Vector2.one, 
				6 => -Vector2.one, 
				8 => new Vector2(1f, -1f), 
				7 => new Vector2(0f, -1f), 
				1 => new Vector2(0f, 1f), 
				4 => Vector2.zero, 
				3 => new Vector2(-1f, 0f), 
				5 => new Vector2(1f, 0f), 
				_ => Vector2.zero, 
			});
		}
	}
}
namespace EasyPZ.Ghosts
{
	public static class GhostFileReaderFactory
	{
		private static readonly GhostFileReaderV0 readerV0 = new GhostFileReaderV0();

		private static readonly GhostFileReaderV1 readerV1 = new GhostFileReaderV1();

		private static readonly GhostFileReaderV2 readerV2 = new GhostFileReaderV2();

		public static IGhostFileReader GetReader(int fileType)
		{
			return fileType switch
			{
				0 => readerV0, 
				1 => readerV1, 
				2 => readerV2, 
				_ => throw new Exception($"Unknown ghost file type {fileType}. Not supported in this version."), 
			};
		}
	}
	public interface IGhostFileReader
	{
		GhostRecording Read(BinaryReader br);

		GhostRecordingMetadata ReadMetadata(BinaryReader br);

		GhostRecordingFrame ReadFrame(BinaryReader br);
	}
	public class GhostRecording
	{
		public const int FILE_TYPE_VERSION = 2;

		public int fileTypeVersion = 2;

		public GhostRecordingMetadata Metadata;

		public List<GhostRecordingFrame> frames;

		public float GetTotalTime()
		{
			return frames[frames.Count - 1].time;
		}

		public GhostRecording()
		{
			Metadata = new GhostRecordingMetadata();
			frames = new List<GhostRecordingFrame>();
		}

		public void SetStats(StatGoal stats)
		{
			Metadata.StatGoal = stats;
		}

		public void FixTimeOffset()
		{
			float time = frames[0].time;
			for (int i = 0; i < frames.Count; i++)
			{
				float time2 = frames[i].time;
				frames[i].time -= time;
			}
		}

		public GhostRecordingFrame[] GetNearestTwoFrames(float time)
		{
			GhostRecordingFrame[] array = new GhostRecordingFrame[2];
			if (frames.Count < 2)
			{
				return null;
			}
			float totalTime = GetTotalTime();
			if (time >= frames[frames.Count - 1].time)
			{
				return new GhostRecordingFrame[2]
				{
					frames[frames.Count - 2],
					frames[frames.Count - 1]
				};
			}
			if (time <= 0f)
			{
				return new GhostRecordingFrame[2]
				{
					frames[0],
					frames[1]
				};
			}
			for (int i = 1; i < frames.Count; i++)
			{
				GhostRecordingFrame ghostRecordingFrame = frames[i - 1];
				GhostRecordingFrame ghostRecordingFrame2 = frames[i];
				if (ghostRecordingFrame.time <= time && ghostRecordingFrame2.time >= time)
				{
					array[0] = ghostRecordingFrame;
					array[1] = ghostRecordingFrame2;
					return array;
				}
			}
			return null;
		}

		public static GhostRecording LoadFromBytes(byte[] data)
		{
			using MemoryStream input = new MemoryStream(data);
			using BinaryReader binaryReader = new BinaryReader(input);
			int fileType = binaryReader.ReadInt32();
			return GhostFileReaderFactory.GetReader(fileType).Read(binaryReader);
		}

		public static GhostRecordingMetadata LoadMetadataOnlyFromFilePath(string filePath)
		{
			if (!File.Exists(filePath))
			{
				throw new FileNotFoundException("File " + filePath + " does not exist.");
			}
			try
			{
				using FileStream input = new FileStream(filePath, FileMode.Open);
				using BinaryReader binaryReader = new BinaryReader(input);
				int fileType = binaryReader.ReadInt32();
				return GhostFileReaderFactory.GetReader(fileType).ReadMetadata(binaryReader);
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("Failed to read file " + filePath));
				Debug.LogException(ex);
			}
			return null;
		}

		public byte[] ToBytes()
		{
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream))
			{
				binaryWriter.Write(2);
				Metadata.TotalLength = GetTotalTime();
				Metadata.Write(binaryWriter);
				binaryWriter.Write(frames.Count);
				for (int i = 0; i < frames.Count; i++)
				{
					frames[i].Write(binaryWriter);
				}
			}
			return memoryStream.ToArray();
		}
	}
	public class GhostRecordingFrame
	{
		public Vector3 position;

		public Vector2 rotation;

		public int animation;

		public float time;

		public void Write(BinaryWriter w)
		{
			w.Write(time);
			w.Write(animation);
			w.Write(position.x);
			w.Write(position.y);
			w.Write(position.z);
			w.Write(rotation.x);
			w.Write(rotation.y);
		}
	}
	public class GhostRecordingMetadata
	{
		public string LevelName;

		public int Difficulty;

		public ulong SteamID;

		public string RunnerName;

		public Color RunnerColor;

		public string Title;

		public string Description;

		public DateTime DateCreated;

		public string ModVersion;

		public string GameVersion;

		public float TotalLength;

		public bool CheatsUsed;

		public bool MajorAssistsUsed;

		public bool NoDamage;

		public string LocatedFilePath;

		public StatGoal StatGoal;

		public string GetFileName()
		{
			if (string.IsNullOrEmpty(LocatedFilePath))
			{
				return "FILE_PATH_INVALID";
			}
			return Path.GetFileNameWithoutExtension(LocatedFilePath);
		}

		public string GetTimeString(bool letters = false)
		{
			TimeSpan timeSpan = TimeSpan.FromSeconds(StatGoal.Seconds);
			if (letters)
			{
				return $"{timeSpan.Minutes:D2}m:{timeSpan.Seconds:D2}s:{timeSpan.Milliseconds:D3}ms";
			}
			return $"{timeSpan.Minutes:D2}:{timeSpan.Seconds:D2}:{timeSpan.Milliseconds:D3}";
		}

		public void Write(BinaryWriter bw)
		{
			bw.Write(LevelName);
			bw.Write(Difficulty);
			bw.Write(SteamID);
			bw.Write(RunnerName);
			bw.Write(RunnerColor.r);
			bw.Write(RunnerColor.g);
			bw.Write(RunnerColor.b);
			bw.Write(Title);
			bw.Write(Description);
			bw.Write(DateCreated.Ticks);
			bw.Write(ModVersion);
			bw.Write(GameVersion);
			bw.Write(TotalLength);
			bw.Write(CheatsUsed);
			bw.Write(MajorAssistsUsed);
			bw.Write(NoDamage);
			bw.Write(StatGoal.Kills);
			bw.Write(StatGoal.Deaths);
			bw.Write(StatGoal.Style);
			bw.Write(StatGoal.Seconds);
		}

		public byte[] ToBytes()
		{
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter bw = new BinaryWriter(memoryStream))
			{
				Write(bw);
			}
			return memoryStream.ToArray();
		}
	}
	public class GhostFileReaderV0 : IGhostFileReader
	{
		public GhostRecording Read(BinaryReader br)
		{
			GhostRecording ghostRecording = new GhostRecording();
			ghostRecording.fileTypeVersion = 0;
			ghostRecording.Metadata = ReadMetadata(br);
			int num = br.ReadInt32();
			ghostRecording.frames = new List<GhostRecordingFrame>();
			for (int i = 0; i < num; i++)
			{
				ghostRecording.frames.Add(ReadFrame(br));
			}
			return ghostRecording;
		}

		public GhostRecordingMetadata ReadMetadata(BinaryReader br)
		{
			//IL_002c: 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)
			GhostRecordingMetadata ghostRecordingMetadata = new GhostRecordingMetadata();
			ghostRecordingMetadata.LevelName = br.ReadString();
			ghostRecordingMetadata.Difficulty = br.ReadInt32();
			ghostRecordingMetadata.SteamID = br.ReadUInt64();
			ghostRecordingMetadata.RunnerColor = Color.red;
			ghostRecordingMetadata.Title = br.ReadString();
			ghostRecordingMetadata.Description = br.ReadString();
			ghostRecordingMetadata.DateCreated = new DateTime(br.ReadInt64());
			ghostRecordingMetadata.ModVersion = br.ReadString();
			ghostRecordingMetadata.GameVersion = br.ReadString();
			ghostRecordingMetadata.TotalLength = br.ReadSingle();
			ghostRecordingMetadata.StatGoal = new StatGoal
			{
				Kills = br.ReadInt32(),
				Deaths = br.ReadInt32(),
				Style = br.ReadInt32(),
				Seconds = br.ReadSingle()
			};
			return ghostRecordingMetadata;
		}

		public GhostRecordingFrame ReadFrame(BinaryReader br)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			GhostRecordingFrame ghostRecordingFrame = new GhostRecordingFrame();
			ghostRecordingFrame.time = br.ReadSingle();
			ghostRecordingFrame.animation = br.ReadInt32();
			ghostRecordingFrame.position = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
			ghostRecordingFrame.rotation = new Vector2(br.ReadSingle(), br.ReadSingle());
			return ghostRecordingFrame;
		}
	}
	public class GhostFileReaderV1 : IGhostFileReader
	{
		public GhostRecording Read(BinaryReader br)
		{
			GhostRecording ghostRecording = new GhostRecording();
			ghostRecording.fileTypeVersion = 1;
			ghostRecording.Metadata = ReadMetadata(br);
			int num = br.ReadInt32();
			ghostRecording.frames = new List<GhostRecordingFrame>();
			for (int i = 0; i < num; i++)
			{
				ghostRecording.frames.Add(ReadFrame(br));
			}
			return ghostRecording;
		}

		public GhostRecordingMetadata ReadMetadata(BinaryReader br)
		{
			//IL_002c: 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)
			GhostRecordingMetadata ghostRecordingMetadata = new GhostRecordingMetadata();
			ghostRecordingMetadata.LevelName = br.ReadString();
			ghostRecordingMetadata.Difficulty = br.ReadInt32();
			ghostRecordingMetadata.SteamID = br.ReadUInt64();
			ghostRecordingMetadata.RunnerColor = Color.red;
			ghostRecordingMetadata.Title = br.ReadString();
			ghostRecordingMetadata.Description = br.ReadString();
			ghostRecordingMetadata.DateCreated = new DateTime(br.ReadInt64());
			ghostRecordingMetadata.ModVersion = br.ReadString();
			ghostRecordingMetadata.GameVersion = br.ReadString();
			ghostRecordingMetadata.TotalLength = br.ReadSingle();
			ghostRecordingMetadata.CheatsUsed = br.ReadBoolean();
			ghostRecordingMetadata.MajorAssistsUsed = br.ReadBoolean();
			ghostRecordingMetadata.NoDamage = br.ReadBoolean();
			ghostRecordingMetadata.StatGoal = new StatGoal
			{
				Kills = br.ReadInt32(),
				Deaths = br.ReadInt32(),
				Style = br.ReadInt32(),
				Seconds = br.ReadSingle()
			};
			return ghostRecordingMetadata;
		}

		public GhostRecordingFrame ReadFrame(BinaryReader br)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			GhostRecordingFrame ghostRecordingFrame = new GhostRecordingFrame();
			ghostRecordingFrame.time = br.ReadSingle();
			ghostRecordingFrame.animation = br.ReadInt32();
			ghostRecordingFrame.position = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
			ghostRecordingFrame.rotation = new Vector2(br.ReadSingle(), br.ReadSingle());
			return ghostRecordingFrame;
		}
	}
	public class GhostFileReaderV2 : IGhostFileReader
	{
		public GhostRecording Read(BinaryReader br)
		{
			GhostRecording ghostRecording = new GhostRecording();
			ghostRecording.fileTypeVersion = 2;
			ghostRecording.Metadata = ReadMetadata(br);
			int num = br.ReadInt32();
			ghostRecording.frames = new List<GhostRecordingFrame>();
			for (int i = 0; i < num; i++)
			{
				ghostRecording.frames.Add(ReadFrame(br));
			}
			return ghostRecording;
		}

		public GhostRecordingMetadata ReadMetadata(BinaryReader br)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			GhostRecordingMetadata ghostRecordingMetadata = new GhostRecordingMetadata();
			ghostRecordingMetadata.LevelName = br.ReadString();
			ghostRecordingMetadata.Difficulty = br.ReadInt32();
			ghostRecordingMetadata.SteamID = br.ReadUInt64();
			ghostRecordingMetadata.RunnerName = br.ReadString();
			ghostRecordingMetadata.RunnerColor = new Color
			{
				r = br.ReadSingle(),
				g = br.ReadSingle(),
				b = br.ReadSingle(),
				a = 1f
			};
			ghostRecordingMetadata.Title = br.ReadString();
			ghostRecordingMetadata.Description = br.ReadString();
			ghostRecordingMetadata.DateCreated = new DateTime(br.ReadInt64());
			ghostRecordingMetadata.ModVersion = br.ReadString();
			ghostRecordingMetadata.GameVersion = br.ReadString();
			ghostRecordingMetadata.TotalLength = br.ReadSingle();
			ghostRecordingMetadata.CheatsUsed = br.ReadBoolean();
			ghostRecordingMetadata.MajorAssistsUsed = br.ReadBoolean();
			ghostRecordingMetadata.NoDamage = br.ReadBoolean();
			ghostRecordingMetadata.StatGoal = new StatGoal
			{
				Kills = br.ReadInt32(),
				Deaths = br.ReadInt32(),
				Style = br.ReadInt32(),
				Seconds = br.ReadSingle()
			};
			return ghostRecordingMetadata;
		}

		public GhostRecordingFrame ReadFrame(BinaryReader br)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			GhostRecordingFrame ghostRecordingFrame = new GhostRecordingFrame();
			ghostRecordingFrame.time = br.ReadSingle();
			ghostRecordingFrame.animation = br.ReadInt32();
			ghostRecordingFrame.position = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
			ghostRecordingFrame.rotation = new Vector2(br.ReadSingle(), br.ReadSingle());
			return ghostRecordingFrame;
		}
	}
	public static class GhostFileManager
	{
		public static List<GhostRecordingMetadata> FetchMetadata()
		{
			string value = ((ConfigValueElement<string>)(object)Paths.ghostRecordingPath).Value;
			if (!Directory.Exists(value))
			{
				return new List<GhostRecordingMetadata>();
			}
			DirectoryInfo directoryInfo = new DirectoryInfo(value);
			List<GhostRecordingMetadata> list = new List<GhostRecordingMetadata>();
			FileInfo[] files = directoryInfo.GetFiles("*.ukrun", SearchOption.AllDirectories);
			foreach (FileInfo fileInfo in files)
			{
				try
				{
					GhostRecordingMetadata ghostRecordingMetadata = GhostRecording.LoadMetadataOnlyFromFilePath(fileInfo.FullName);
					ghostRecordingMetadata.LocatedFilePath = fileInfo.FullName;
					list.Add(ghostRecordingMetadata);
				}
				catch (Exception ex)
				{
					Debug.LogError((object)("EASYPZ: Error loading metadata for file " + fileInfo.FullName));
					Debug.LogException(ex);
				}
			}
			return list;
		}

		internal static void UpdateMetadata(GhostRecordingMetadata metadata)
		{
			if (string.IsNullOrEmpty(metadata.LocatedFilePath))
			{
				throw new Exception("Cannot update metadata without a file path");
			}
			if (!File.Exists(metadata.LocatedFilePath))
			{
				throw new Exception("File does not exist or was moved.");
			}
			try
			{
				GhostRecording ghostRecording = GhostRecording.LoadFromBytes(File.ReadAllBytes(metadata.LocatedFilePath));
				ghostRecording.Metadata = metadata;
				File.WriteAllBytes(metadata.LocatedFilePath, ghostRecording.ToBytes());
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("EASYPZ: Error updating metadata for file " + metadata.LocatedFilePath));
				Debug.LogException(ex);
			}
		}

		internal static void RenameRun(GhostRecordingMetadata metadata, string newFileName)
		{
			if (string.IsNullOrEmpty(metadata.LocatedFilePath))
			{
				throw new Exception("Cannot update metadata without a file path");
			}
			if (!File.Exists(metadata.LocatedFilePath))
			{
				throw new Exception("File does not exist or was moved.");
			}
			string locatedFilePath = metadata.LocatedFilePath;
			string text = Path.Combine(Path.GetDirectoryName(locatedFilePath), newFileName + ".ukrun");
			try
			{
				byte[] bytes = File.ReadAllBytes(locatedFilePath);
				File.WriteAllBytes(text, bytes);
				if (!File.Exists(text))
				{
					throw new Exception("Failed to write new file. Unknown issue? File not found?");
				}
				File.Delete(locatedFilePath);
				metadata.LocatedFilePath = text;
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("EASYPZ: Error renaming run for file " + locatedFilePath));
				Debug.LogException(ex);
			}
			Debug.Log((object)("EasyPZ: Renamed run " + Path.GetFileNameWithoutExtension(locatedFilePath) + " to " + newFileName));
		}

		internal static void DeleteRun(GhostRecordingMetadata metadata)
		{
			if (string.IsNullOrEmpty(metadata.LocatedFilePath))
			{
				throw new Exception("Cannot update metadata without a file path");
			}
			if (File.Exists(metadata.LocatedFilePath))
			{
				File.Delete(metadata.LocatedFilePath);
			}
		}

		[Configgable("Extras/Advanced", "Print Metadata", 0, null)]
		public static void PrintFileMetaData()
		{
			string value = ((ConfigValueElement<string>)(object)Paths.ghostRecordingPath).Value;
			DirectoryInfo directoryInfo = new DirectoryInfo(value);
			StringBuilder stringBuilder = new StringBuilder();
			FileInfo[] files = directoryInfo.GetFiles("*.ukrun", SearchOption.AllDirectories);
			foreach (FileInfo fileInfo in files)
			{
				try
				{
					stringBuilder.Clear();
					GhostRecordingMetadata ghostRecordingMetadata = GhostRecording.LoadMetadataOnlyFromFilePath(fileInfo.FullName);
					stringBuilder.AppendLine("FILE: " + fileInfo.FullName);
					stringBuilder.AppendLine("LEVEL: " + ghostRecordingMetadata.LevelName);
					stringBuilder.AppendLine($"STEAMID: {ghostRecordingMetadata.SteamID}");
					stringBuilder.AppendLine($"LENGTH: {ghostRecordingMetadata.TotalLength}");
					stringBuilder.AppendLine("GAMEVERSION: " + ghostRecordingMetadata.GameVersion);
					stringBuilder.AppendLine("MODVERSION: " + ghostRecordingMetadata.ModVersion);
					stringBuilder.AppendLine("TITLE: " + ghostRecordingMetadata.Title);
					stringBuilder.AppendLine("DESC: " + ghostRecordingMetadata.Description);
					stringBuilder.AppendLine($"DIFF: {ghostRecordingMetadata.Difficulty}");
					stringBuilder.AppendLine();
					Debug.Log((object)stringBuilder.ToString());
				}
				catch (Exception ex)
				{
					Debug.LogError((object)ex);
				}
			}
		}
	}
}
namespace EasyPZ.Components
{
	public class SpeedLimiter : LevelSessionBehaviour
	{
		[Configgable("Extras/Speed Limiter", "Speed Limit Enabled", 0, null)]
		private static ConfigToggle enableSpeedLimit = new ConfigToggle(false);

		[Configgable("Extras/Speed Limiter", "Min Speed", 0, null)]
		private static ConfigInputField<float> minSpeed = new ConfigInputField<float>(10f, (Func<float, bool>)null, (Func<string, ValueTuple<bool, float>>)null);

		[Configgable("Extras/Speed Limiter", "Max Speed", 0, null)]
		private static ConfigInputField<float> maxSpeed = new ConfigInputField<float>(500f, (Func<float, bool>)null, (Func<string, ValueTuple<bool, float>>)null);

		[Configgable("Extras/Speed Limiter", "Forgiveness Time", 0, null)]
		private static ConfigInputField<float> forgivenessTime = new ConfigInputField<float>(1.5f, (Func<float, bool>)null, (Func<string, ValueTuple<bool, float>>)null);

		private float timeSpeedLimitBroken;

		protected override void OnSessionUpdate()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (!((ConfigValueElement<bool>)(object)enableSpeedLimit).Value)
			{
				return;
			}
			Vector3 velocity = MonoSingleton<NewMovement>.Instance.rb.velocity;
			float magnitude = ((Vector3)(ref velocity)).magnitude;
			if (magnitude > ((ConfigValueElement<float>)(object)maxSpeed).Value || magnitude < ((ConfigValueElement<float>)(object)minSpeed).Value)
			{
				timeSpeedLimitBroken += Time.deltaTime;
				if (timeSpeedLimitBroken > ((ConfigValueElement<float>)(object)forgivenessTime).Value)
				{
					MonoSingleton<OptionsManager>.Instance.RestartMission();
				}
			}
			else
			{
				timeSpeedLimitBroken = 0f;
			}
		}

		protected override void OnStartSession()
		{
		}

		protected override void OnStopSession()
		{
		}
	}
	public class GhostManager : LevelSessionBehaviour
	{
		[Configgable("Ghosts/Playback", "Max Ghosts", 0, "Hard cap on ghosts to spawn regardless of selected amount.")]
		private static ConfigInputField<int> maxGhosts = new ConfigInputField<int>(3, (Func<int, bool>)null, (Func<string, ValueTuple<bool, int>>)null);

		[Configgable("Ghosts/Playback", "Ghosts Enabled", 0, null)]
		public static ConfigToggle GhostsEnabled = new ConfigToggle(false);

		[Configgable("Ghosts/Playback", "Ghost Opacity", 0, null)]
		private static FloatSlider ghostOpacity = new FloatSlider(1f, 0f, 1f);

		[Configgable("Ghosts/Playback", "Ghost Spawn Order", 0, "Changes how the selection of max ghosts are spawned.")]
		private static ConfigDropdown<GhostSpawnOrderType> ghostSpawningPattern = new ConfigDropdown<GhostSpawnOrderType>((GhostSpawnOrderType[])Enum.GetValues(typeof(GhostSpawnOrderType)), (string[])null, 0);

		private const string ghostSpawnPatternDescription = "Changes how the selection of max ghosts are spawned.";

		private static GameObject ghostPlayerPrefab;

		private List<GhostPlayer> ghostPlayers;

		private List<GhostRecording> loadedRecordings;

		private float timeStarted;

		private static GhostManager instance;

		private static Dictionary<string, bool> enabledGhosts = new Dictionary<string, bool>();

		private static GameObject GhostPlayerPrefab
		{
			get
			{
				if ((Object)(object)ghostPlayerPrefab == (Object)null)
				{
					ghostPlayerPrefab = BuildGhostPlayerPrefab();
				}
				return ghostPlayerPrefab;
			}
		}

		private bool spawnedGhosts => ghostPlayers != null;

		private void Awake()
		{
			instance = this;
		}

		public static bool IsGhostEnabled(string id)
		{
			if (!enabledGhosts.ContainsKey(id))
			{
				enabledGhosts.Add(id, value: true);
				return true;
			}
			return enabledGhosts[id];
		}

		public static void SetGhostEnabled(string id, bool enabled)
		{
			if (!enabledGhosts.ContainsKey(id))
			{
				enabledGhosts.Add(id, enabled);
				return;
			}
			enabledGhosts[id] = enabled;
			if ((Object)(object)instance != (Object)null)
			{
				instance.ResetGhosts();
			}
		}

		protected override void OnSessionUpdate()
		{
		}

		public static void PreloadGhostPrefab()
		{
			if (!((Object)(object)ghostPlayerPrefab != (Object)null))
			{
				ghostPlayerPrefab = BuildGhostPlayerPrefab();
			}
		}

		private void Start()
		{
			ConfigToggle ghostsEnabled = GhostsEnabled;
			((ConfigValueElement<bool>)(object)ghostsEnabled).OnValueChanged = (Action<bool>)Delegate.Combine(((ConfigValueElement<bool>)(object)ghostsEnabled).OnValueChanged, new Action<bool>(SetGhostsEnabled));
			ConfigInputField<int> obj = maxGhosts;
			((ConfigValueElement<int>)(object)obj).OnValueChanged = (Action<int>)Delegate.Combine(((ConfigValueElement<int>)(object)obj).OnValueChanged, new Action<int>(ResetGhostsFromEvent));
			ConfigDropdown<GhostSpawnOrderType> obj2 = ghostSpawningPattern;
			((ConfigValueElement<GhostSpawnOrderType>)(object)obj2).OnValueChanged = (Action<GhostSpawnOrderType>)Delegate.Combine(((ConfigValueElement<GhostSpawnOrderType>)(object)obj2).OnValueChanged, new Action<GhostSpawnOrderType>(ResetGhostsFromEvent));
			SetGhostsEnabled(((ConfigValueElement<bool>)(object)GhostsEnabled).Value);
		}

		private void LoadRecordings()
		{
			string currentScene = SceneHelper.CurrentScene;
			string path = Path.Combine(((ConfigValueElement<string>)(object)Paths.ghostRecordingPath).Value, currentScene);
			if (!Directory.Exists(path))
			{
				loadedRecordings = new List<GhostRecording>();
				return;
			}
			DirectoryInfo directoryInfo = new DirectoryInfo(path);
			List<GhostRecording> list = new List<GhostRecording>();
			FileInfo[] files = directoryInfo.GetFiles("*.ukrun", SearchOption.TopDirectoryOnly);
			foreach (FileInfo fileInfo in files)
			{
				try
				{
					GhostRecording ghostRecording = GhostRecording.LoadFromBytes(File.ReadAllBytes(fileInfo.FullName));
					ghostRecording.Metadata.LocatedFilePath = fileInfo.FullName;
					Debug.Log((object)$"Loaded recording {fileInfo.Name} with {ghostRecording.frames.Count} frames");
					list.Add(ghostRecording);
				}
				catch (Exception ex)
				{
					Debug.LogError((object)ex);
				}
			}
			loadedRecordings = list;
		}

		protected override void OnStartSession()
		{
			timeStarted = Time.time;
			if (((ConfigValueElement<bool>)(object)GhostsEnabled).Value)
			{
				PlayGhosts();
			}
		}

		protected override void OnStopSession()
		{
			DisposeGhosts();
		}

		private void SetGhostsEnabled(bool enabled)
		{
			if (!enabled)
			{
				if (spawnedGhosts)
				{
					DisposeGhosts();
				}
				return;
			}
			if (!spawnedGhosts)
			{
				SpawnGhosts();
			}
			if (started)
			{
				PlayGhosts();
			}
		}

		private void PlayGhosts()
		{
			foreach (GhostPlayer ghostPlayer in ghostPlayers)
			{
				ghostPlayer.Play(timeStarted);
			}
		}

		private void ResetGhostsFromEvent<T>(T _)
		{
			ResetGhosts();
		}

		private void ResetGhosts()
		{
			if (spawnedGhosts && ((ConfigValueElement<bool>)(object)GhostsEnabled).Value)
			{
				DisposeGhosts();
				SpawnGhosts();
				if (started)
				{
					PlayGhosts();
				}
			}
		}

		private void SpawnGhosts()
		{
			ghostPlayers = new List<GhostPlayer>();
			if (loadedRecordings == null)
			{
				LoadRecordings();
			}
			foreach (GhostRecording item in SelectRecordings())
			{
				SpawnGhost(item);
			}
		}

		private IEnumerable<GhostRecording> SelectRecordings()
		{
			if (loadedRecordings.Count == 0)
			{
				return Enumerable.Empty<GhostRecording>();
			}
			IEnumerable<GhostRecording> source = loadedRecordings.Where((GhostRecording x) => enabledGhosts.ContainsKey(x.Metadata.LocatedFilePath) && enabledGhosts[x.Metadata.LocatedFilePath]);
			if (source.Count() == 0)
			{
				return Enumerable.Empty<GhostRecording>();
			}
			switch (((ConfigValueElement<GhostSpawnOrderType>)(object)ghostSpawningPattern).Value)
			{
			case GhostSpawnOrderType.Fastest:
				source = source.OrderBy((GhostRecording x) => x.GetTotalTime());
				break;
			case GhostSpawnOrderType.Slowest:
				source = source.OrderByDescending((GhostRecording x) => x.GetTotalTime());
				break;
			case GhostSpawnOrderType.MostRecent:
				source = source.OrderByDescending((GhostRecording x) => x.Metadata.DateCreated.Ticks);
				break;
			}
			int count = Mathf.Min(((ConfigValueElement<int>)(object)maxGhosts).Value, source.Count());
			return source.Take(count);
		}

		private void SpawnGhost(GhostRecording recording)
		{
			GameObject val = Object.Instantiate<GameObject>(GhostPlayerPrefab);
			val.SetActive(true);
			GhostPlayer ghostPlayer = val.AddComponent<GhostPlayer>();
			ghostPlayer.SetRecording(recording);
			ghostPlayers.Add(ghostPlayer);
		}

		private void DisposeGhosts()
		{
			if (ghostPlayers == null)
			{
				return;
			}
			foreach (GhostPlayer ghostPlayer in ghostPlayers)
			{
				ghostPlayer?.Dispose();
			}
			ghostPlayers = null;
		}

		private void OnDestroy()
		{
			ConfigToggle ghostsEnabled = GhostsEnabled;
			((ConfigValueElement<bool>)(object)ghostsEnabled).OnValueChanged = (Action<bool>)Delegate.Remove(((ConfigValueElement<bool>)(object)ghostsEnabled).OnValueChanged, new Action<bool>(SetGhostsEnabled));
			ConfigInputField<int> obj = maxGhosts;
			((ConfigValueElement<int>)(object)obj).OnValueChanged = (Action<int>)Delegate.Remove(((ConfigValueElement<int>)(object)obj).OnValueChanged, new Action<int>(ResetGhostsFromEvent));
			ConfigDropdown<GhostSpawnOrderType> obj2 = ghostSpawningPattern;
			((ConfigValueElement<GhostSpawnOrderType>)(object)obj2).OnValueChanged = (Action<GhostSpawnOrderType>)Delegate.Remove(((ConfigValueElement<GhostSpawnOrderType>)(object)obj2).OnValueChanged, new Action<GhostSpawnOrderType>(ResetGhostsFromEvent));
		}

		private static GameObject BuildGhostPlayerPrefab()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Expected O, but got Unknown
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: 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)
			GameObject val = Addressables.LoadAssetAsync<GameObject>((object)"Assets/Prefabs/Enemies/V2.prefab").WaitForCompletion();
			if ((Object)(object)val == (Object)null)
			{
				Debug.LogError((object)"Could not find V2 prefab");
				return null;
			}
			try
			{
				GameObject val2 = Object.Instantiate<GameObject>(val);
				RemoveAllComponent<V2>(val2);
				RemoveAllComponent<Rigidbody>(val2);
				RemoveAllComponent<Collider>(val2);
				RemoveAllComponent<Machine>(val2);
				RemoveAllComponent<NavMeshAgent>(val2);
				RemoveAllComponent<EnemyIdentifier>(val2);
				RemoveAllComponent<EnemyIdentifierIdentifier>(val2);
				RemoveAllComponent<GroundCheckEnemy>(val2);
				RemoveAllComponent<BulletCheck>(val2);
				RemoveAllComponent<EnemySimplifier>(val2);
				RemoveAllComponent<EnemyShotgun>(val2);
				RemoveAllComponent<EnemyNailgun>(val2);
				RemoveAllComponent<EnemyRevolver>(val2);
				RemoveAllComponent<V2AnimationController>(val2);
				List<Renderer> list = new List<Renderer>();
				SkinnedMeshRenderer[] componentsInChildren = val2.GetComponentsInChildren<SkinnedMeshRenderer>(true);
				MeshRenderer[] componentsInChildren2 = val2.GetComponentsInChildren<MeshRenderer>(true);
				list.AddRange((IEnumerable<Renderer>)(object)componentsInChildren);
				list.AddRange((IEnumerable<Renderer>)(object)componentsInChildren2);
				Material ghostMaterial = Prefabs.GhostMaterial;
				List<Material> createdMaterials = new List<Material>();
				float value = ((ConfigValueElement<float>)(object)ghostOpacity).Value;
				for (int i = 0; i < list.Count; i++)
				{
					Material[] array = (Material[])(object)new Material[list[i].sharedMaterials.Length];
					for (int j = 0; j < list[i].sharedMaterials.Length; j++)
					{
						Material val3 = new Material(ghostMaterial);
						createdMaterials.Add(val3);
						Color color = val3.color;
						color.a = value;
						val3.color = color;
						array[j] = val3;
						Texture texture = list[i].sharedMaterials[j].GetTexture("_MainTex");
						array[j].SetTexture("_MainTex", texture);
					}
					list[i].sharedMaterials = array;
				}
				FloatSlider obj = ghostOpacity;
				((ConfigValueElement<float>)(object)obj).OnValueChanged = (Action<float>)Delegate.Combine(((ConfigValueElement<float>)(object)obj).OnValueChanged, (Action<float>)delegate(float v)
				{
					//IL_001a: Unknown result type (might be due to invalid IL or missing references)
					//IL_001f: Unknown result type (might be due to invalid IL or missing references)
					//IL_0029: Unknown result type (might be due to invalid IL or missing references)
					foreach (Material item in createdMaterials)
					{
						Color color2 = item.color;
						color2.a = v;
						item.color = color2;
					}
				});
				((Object)val2).name = "PlayerGhost";
				((Object)val2).hideFlags = (HideFlags)61;
				val2.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)val2);
				return val2;
			}
			catch (Exception ex)
			{
				Debug.LogError((object)"Error creating Player Ghost Prefab.");
				Debug.LogException(ex);
				return null;
			}
		}

		private static void RemoveAllComponent<T>(GameObject go) where T : Component
		{
			T[] componentsInChildren = go.GetComponentsInChildren<T>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				T val = componentsInChildren[i];
				componentsInChildren[i] = default(T);
				Object.Destroy((Object)(object)val);
			}
		}
	}
	public enum GhostSpawnOrderType
	{
		Fastest,
		Slowest,
		MostRecent
	}
	public class GhostNotifier : MonoBehaviour
	{
		private static GhostNotifier instance;

		private Animator animator;

		private void Awake()
		{
			instance = this;
			animator = ((Component)this).GetComponentInChildren<Animator>();
		}

		private void ShowNotif()
		{
			animator.Play("Show");
		}

		public static void Notify()
		{
			if (!((Object)(object)instance == (Object)null))
			{
				instance.ShowNotif();
			}
		}
	}
	public class GhostPlayer : MonoBehaviour, IDisposable
	{
		private GhostRecording recording;

		private Animator animator;

		private Transform[] rotationTransforms;

		private float timeStarted;

		private bool isPlaying;

		private int lastAnimationState = 0;

		public void SetRecording(GhostRecording recording)
		{
			this.recording = recording;
		}

		private void Awake()
		{
			animator = (from x in ((Component)this).GetComponentsInChildren<Animator>(true)
				where ((Object)x).name == "v2_combined"
				select x).FirstOrDefault();
			rotationTransforms = (Transform[])(object)new Transform[2];
			rotationTransforms[0] = (from x in ((Component)this).GetComponentsInChildren<Transform>(true)
				where ((Object)x).name == "spine.006"
				select x).FirstOrDefault();
			rotationTransforms[1] = (from x in ((Component)this).GetComponentsInChildren<Transform>(true)
				where ((Object)x).name == "upper_arm.R"
				select x).FirstOrDefault();
			TrailRenderer val = ((Component)this).GetComponentsInChildren<TrailRenderer>(true).FirstOrDefault();
			if ((Object)(object)val != (Object)null)
			{
				((Renderer)val).enabled = false;
			}
		}

		private void Start()
		{
			InstanceNamePlate();
		}

		private void InstanceNamePlate()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			string text = "";
			try
			{
				if (!SteamClient.IsValid)
				{
					throw new Exception("Steam not valid or not connected!");
				}
				Friend val = new Friend(SteamId.op_Implicit(recording.Metadata.SteamID));
				text = ((Friend)(ref val)).Name;
				Debug.Log((object)("Created Ghost " + text));
				GameObject gameObject = ((Component)this).gameObject;
				((Object)gameObject).name = ((Object)gameObject).name + "(" + text + ")";
			}
			catch (Exception ex)
			{
				Debug.LogException(ex);
				Debug.LogError((object)"Unable to name ghost :(");
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(Prefabs.PlayerNameplate, ((Component)this).transform);
			val2.transform.localPosition = new Vector3(0f, 3.854f, 0f);
			val2.transform.localEulerAngles = new Vector3(0f, 0f, 0f);
			Text componentInChildren = val2.GetComponentInChildren<Text>();
			componentInChildren.text = text;
		}

		private void Update()
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: 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_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			if (!isPlaying || recording == null)
			{
				return;
			}
			float num = Time.time - timeStarted;
			GhostRecordingFrame[] nearestTwoFrames = recording.GetNearestTwoFrames(num);
			if (nearestTwoFrames == null)
			{
				return;
			}
			if (nearestTwoFrames[1].time < num)
			{
				Dispose();
				return;
			}
			float num2 = Mathf.InverseLerp(nearestTwoFrames[0].time, nearestTwoFrames[1].time, num);
			int num3 = ((num2 > 0.5f) ? nearestTwoFrames[1].animation : nearestTwoFrames[0].animation);
			Vector3 val = Vector3.Lerp(nearestTwoFrames[0].position, nearestTwoFrames[1].position, num2);
			if (num3 == 1 || num3 == 0)
			{
				val += -Vector3.up * 1.15f;
			}
			float num4 = Mathf.LerpAngle(nearestTwoFrames[0].rotation.y, nearestTwoFrames[1].rotation.y, num2);
			float num5 = Mathf.LerpAngle(nearestTwoFrames[0].rotation.x, nearestTwoFrames[1].rotation.x, num2);
			((Component)this).transform.position = val;
			((Component)this).transform.eulerAngles = new Vector3(0f, num4, 0f);
			for (int i = 0; i < rotationTransforms.Length; i++)
			{
				if (!((Object)(object)rotationTransforms[i] == (Object)null))
				{
					rotationTransforms[i].eulerAngles = new Vector3(num5, 0f, 0f);
				}
			}
			SetAnimation(num3);
		}

		public void Play(float timeStarted)
		{
			this.timeStarted = timeStarted;
			isPlaying = true;
		}

		public void SetCurrentTime(float time)
		{
			timeStarted = Time.time - time;
		}

		public void Stop()
		{
			isPlaying = false;
		}

		private void SetAnimation(int animationState)
		{
			if (lastAnimationState != animationState && !((Object)(object)animator == (Object)null))
			{
				switch (animationState)
				{
				case 0:
					animator.SetLayerWeight(1, 0f);
					animator.SetBool("RunningForward", false);
					animator.SetBool("InAir", false);
					animator.SetBool("Sliding", false);
					break;
				case 1:
					animator.SetLayerWeight(1, 1f);
					animator.SetBool("RunningForward", true);
					animator.SetBool("InAir", false);
					animator.SetBool("Sliding", false);
					break;
				case 2:
					animator.SetLayerWeight(1, 0f);
					animator.Play("Slide", 0, 0f);
					animator.SetBool("RunningForward", false);
					animator.SetBool("InAir", false);
					animator.SetBool("Sliding", true);
					break;
				case 3:
					animator.SetLayerWeight(1, 0f);
					animator.SetBool("RunningForward", false);
					animator.SetBool("InAir", true);
					animator.SetBool("Sliding", false);
					break;
				}
			}
		}

		public void Dispose()
		{
			Object.Destroy((Object)(object)((Component)this).gameObject);
		}
	}
	public class SessionRecorder : LevelSessionBehaviour
	{
		private GhostRecording recording;

		private float timeStartedRecording;

		[Configgable("Ghosts/Recording", "Recording Frame Rate", 0, "The frame rate at which the ghost will be recorded. Higher frame rates will result in smoother playback, but larger file sizes. Frames can only be recorded as fast as your system can render them, so setting this higher than your max fps won't record additional frames.")]
		private static ConfigInputField<int> recorderFrameRate = new ConfigInputField<int>(16, (Func<int, bool>)null, (Func<string, ValueTuple<bool, int>>)null);

		private const string frameRateDescription = "The frame rate at which the ghost will be recorded. Higher frame rates will result in smoother playback, but larger file sizes. Frames can only be recorded as fast as your system can render them, so setting this higher than your max fps won't record additional frames.";

		[Configgable("Ghosts/Recording", "Recording Enabled", 0, null)]
		private static ConfigToggle recordingEnabled = new ConfigToggle(false);

		[Configgable("Ghosts/Recording", "My Ghost Color", 0, "The default color of the ghost in your recorded runs. Please note, this feature is still a work in progress and is not functional yet.")]
		private static ConfigColor defaultGhostColor = new ConfigColor(Color.red);

		private const string defaultGhostColorDescription = "The default color of the ghost in your recorded runs. Please note, this feature is still a work in progress and is not functional yet.";

		private bool recordCurrentSession = false;

		private float lastFrameTime;

		private bool cheatsUsedInSession;

		private bool assistsUsedInSession;

		private static float frameTime => 1f / (float)((ConfigValueElement<int>)(object)recorderFrameRate).Value;

		private void Awake()
		{
			if (!Data.Cache.AskedAboutRecording)
			{
				Data.Cache.AskedAboutRecording = true;
				Data.SaveCache();
				ModalDialogue.ShowSimple("Ghosts!?", "EasyPZ will record your movements as you play, save them when you complete a level, and play them back to you as rival ghosts to race against. Would you like to enable this feature? (This can be changed in the EasyPZ options later.)", (Action<bool>)delegate(bool r)
				{
					((ConfigValueElement<bool>)(object)recordingEnabled).SetValue(r);
					((ConfigValueElement<bool>)(object)GhostManager.GhostsEnabled).SetValue(r);
				}, "Enable Ghosts", "Disable Ghosts");
			}
		}

		protected override void OnSessionUpdate()
		{
			//IL_0083: 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)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if (recordCurrentSession && !(Time.time - lastFrameTime < frameTime))
			{
				lastFrameTime = Time.time;
				if (MonoSingleton<AssistController>.Instance.cheatsEnabled)
				{
					cheatsUsedInSession = true;
				}
				if (MonoSingleton<AssistController>.Instance.majorEnabled)
				{
					assistsUsedInSession = true;
				}
				recording.frames.Add(new GhostRecordingFrame
				{
					position = ((Component)MonoSingleton<NewMovement>.Instance).transform.position,
					rotation = new Vector2(((Component)MonoSingleton<CameraController>.Instance).transform.localEulerAngles.x, ((Component)MonoSingleton<CameraController>.Instance).transform.eulerAngles.y),
					animation = GetAnimationState(),
					time = Time.time - timeStartedRecording
				});
			}
		}

		private int GetAnimationState()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			MovementActions movement = MonoSingleton<InputManager>.Instance.InputSource.Actions.Movement;
			Vector2 val = ((MovementActions)(ref movement)).Move.ReadValue<Vector2>();
			movement = MonoSingleton<InputManager>.Instance.InputSource.Actions.Movement;
			bool flag = ((MovementActions)(ref movement)).Slide.IsPressed();
			bool onGround = MonoSingleton<NewMovement>.Instance.gc.onGround;
			if (flag)
			{
				return 2;
			}
			if (!onGround)
			{
				return 3;
			}
			if (val.y > 0f)
			{
				return 1;
			}
			return 0;
		}

		protected override void OnStartSession()
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			recordCurrentSession = ((ConfigValueElement<bool>)(object)recordingEnabled).Value;
			if (!recordCurrentSession)
			{
				((Behaviour)this).enabled = false;
				return;
			}
			timeStartedRecording = Time.time;
			recording = new GhostRecording();
			recording.Metadata.DateCreated = DateTime.Now;
			recording.Metadata.ModVersion = "3.0.3";
			recording.Metadata.GameVersion = Application.version;
			recording.Metadata.RunnerColor = ((ConfigValueElement<Color>)(object)defaultGhostColor).Value;
			if (SteamClient.IsValid)
			{
				recording.Metadata.SteamID = SteamClient.SteamId.Value;
				recording.Metadata.RunnerName = SteamClient.Name;
			}
			else
			{
				recording.Metadata.SteamID = 0uL;
				recording.Metadata.RunnerName = null;
			}
			recording.Metadata.Difficulty = MonoSingleton<PrefsManager>.Instance.GetInt("difficulty", 0);
			recording.Metadata.LevelName = SceneHelper.CurrentScene;
			recording.Metadata.Description = "My run of " + recording.Metadata.LevelName;
			recording.Metadata.Title = "My " + recording.Metadata.LevelName + " run";
			Debug.Log((object)"Ghost Recording Started");
		}

		protected override void OnStopSession()
		{
			if (recordCurrentSession)
			{
				recording.Metadata.NoDamage = !MonoSingleton<StatsManager>.Instance.tookDamage;
				recording.Metadata.CheatsUsed = cheatsUsedInSession;
				recording.Metadata.MajorAssistsUsed = assistsUsedInSession;
				Debug.Log((object)"Ghost Recording Stopped");
				recording.SetStats(new StatGoal
				{
					Deaths = MonoSingleton<StatsManager>.Instance.restarts,
					Kills = MonoSingleton<StatsManager>.Instance.kills,
					Style = MonoSingleton<StatsManager>.Instance.stylePoints,
					Seconds = MonoSingleton<StatsManager>.Instance.seconds
				});
				SaveRecording();
				GhostNotifier.Notify();
			}
		}

		private void SaveRecording()
		{
			recording.FixTimeOffset();
			string currentScene = SceneHelper.CurrentScene;
			string text = Path.Combine(((ConfigValueElement<string>)(object)Paths.ghostRecordingPath).Value, currentScene);
			if (!Directory.Exists(text))
			{
				Directory.CreateDirectory(text);
			}
			string text2 = DateTime.Now.ToString("yyyy-MM-dd-mm-ss");
			string text3 = ".ukrun";
			string text4 = Path.Combine(text, text2 + text3);
			Debug.Log((object)("Saved new ghost: " + text4));
			File.WriteAllBytes(text4, recording.ToBytes());
		}
	}
	public abstract class LevelSessionBehaviour : MonoBehaviour
	{
		protected bool started;

		protected bool stopped;

		private void Update()
		{
			if (stopped)
			{
				return;
			}
			if (!started)
			{
				if (!(MonoSingleton<StatsManager>.Instance.seconds > 0f))
				{
					return;
				}
				StartSession();
			}
			if (started && MonoSingleton<StatsManager>.Instance.infoSent)
			{
				StopSession();
			}
			OnSessionUpdate();
		}

		protected abstract void OnSessionUpdate();

		private void StartSession()
		{
			started = true;
			OnStartSession();
		}

		protected abstract void OnStartSession();

		protected abstract void OnStopSession();

		private void StopSession()
		{
			stopped = true;
			OnStopSession();
		}
	}
	public class RunEditor : MonoBehaviour
	{
		[SerializeField]
		private RunManagerMenu runManager;

		[SerializeField]
		private Text levelText;

		[SerializeField]
		private Text dateCreatedText;

		[SerializeField]
		private Text difficultyNameText;

		[SerializeField]
		private Text goalKillsText;

		[SerializeField]
		private Text goalStyleText;

		[SerializeField]
		private Text goalTimeText;

		[SerializeField]
		private Text goalDeathsText;

		[SerializeField]
		private GameObject flagsContainer;

		[SerializeField]
		private GameObject cheatsBubble;

		[SerializeField]
		private GameObject majorAssistsBubble;

		[SerializeField]
		private GameObject noDamageBubble;

		[SerializeField]
		private Button openInFolderButton;

		[SerializeField]
		private Button backButton;

		[SerializeField]
		private Button saveButton;

		[SerializeField]
		private Button deleteButton;

		[SerializeField]
		private Button setCustomGoalButton;

		[SerializeField]
		private InputField fileNameField;

		[SerializeField]
		private InputField runnerNameField;

		[SerializeField]
		private InputField titleField;

		[SerializeField]
		private InputField descriptionField;

		[SerializeField]
		private Text finalRankText;

		[SerializeField]
		private Image finalRankImage;

		public Button BackButton => backButton;

		public void SetRecording(GhostRecordingMetadata metadata)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fe: Expected O, but got Unknown
			//IL_0469: Unknown result type (might be due to invalid IL or missing references)
			//IL_0473: Expected O, but got Unknown
			//IL_043b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0445: Expected O, but got Unknown
			//IL_04b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bb: Expected O, but got Unknown
			bool isValid = SteamClient.IsValid;
			bool flag = false;
			if (isValid)
			{
				flag = metadata.SteamID == SteamId.op_Implicit(SteamClient.SteamId);
			}
			bool flag2 = metadata.LevelName == SceneHelper.CurrentScene;
			levelText.text = metadata.LevelName;
			dateCreatedText.text = metadata.DateCreated.ToString("MM/dd/yyyy hh:mm:ss");
			goalKillsText.text = metadata.StatGoal.Kills.ToString("000");
			goalStyleText.text = metadata.StatGoal.Style.ToString("000");
			goalTimeText.text = metadata.GetTimeString();
			goalDeathsText.text = metadata.StatGoal.Deaths.ToString("000");
			difficultyNameText.text = "-- " + ParseDifficultyName(metadata.Difficulty) + " --";
			string fileName = Path.GetFileNameWithoutExtension(metadata.LocatedFilePath);
			fileNameField.SetTextWithoutNotify(fileName);
			((UnityEventBase)fileNameField.onEndEdit).RemoveAllListeners();
			((Selectable)fileNameField).interactable = flag;
			titleField.SetTextWithoutNotify(metadata.Title);
			((UnityEventBase)titleField.onEndEdit).RemoveAllListeners();
			((Selectable)titleField).interactable = flag;
			string title = metadata.Title;
			descriptionField.SetTextWithoutNotify(metadata.Description);
			((UnityEventBase)descriptionField.onEndEdit).RemoveAllListeners();
			((Selectable)descriptionField).interactable = flag;
			string desc = metadata.Description;
			string runnerName = metadata.RunnerName;
			if (string.IsNullOrEmpty(runnerName))
			{
				if (SteamClient.IsValid)
				{
					Friend val = new Friend(SteamId.op_Implicit(metadata.SteamID));
					runnerName = ((Friend)(ref val)).Name;
				}
				else
				{
					runnerName = "Player (" + metadata.SteamID.ToString().Substring(0, 5) + "...)";
				}
			}
			runnerNameField.SetTextWithoutNotify(runnerName);
			((UnityEventBase)runnerNameField.onEndEdit).RemoveAllListeners();
			((Selectable)runnerNameField).interactable = flag;
			Func<bool> changesMade = () => desc != metadata.Description || title != metadata.Title || fileName != Path.GetFileNameWithoutExtension(metadata.LocatedFilePath) || runnerName != metadata.RunnerName;
			if (flag)
			{
				((UnityEvent<string>)(object)descriptionField.onEndEdit).AddListener((UnityAction<string>)delegate(string text)
				{
					desc = text;
					((Component)saveButton).gameObject.SetActive(changesMade());
				});
				((UnityEvent<string>)(object)titleField.onEndEdit).AddListener((UnityAction<string>)delegate(string text)
				{
					title = text;
					((Component)saveButton).gameObject.SetActive(changesMade());
				});
				((UnityEvent<string>)(object)fileNameField.onEndEdit).AddListener((UnityAction<string>)delegate(string text)
				{
					bool flag3 = true;
					flag3 &= !string.IsNullOrEmpty(text);
					if (!(flag3 & (text.IndexOfAny(Path.GetInvalidFileNameChars()) < 0)))
					{
						fileNameField.SetTextWithoutNotify(fileName);
					}
					else
					{
						fileName = text;
						((Component)saveButton).gameObject.SetActive(changesMade());
					}
				});
				((UnityEvent<string>)(object)runnerNameField.onEndEdit).AddListener((UnityAction<string>)delegate(string text)
				{
					runnerName = text;
					((Component)saveButton).gameObject.SetActive(changesMade());
				});
			}
			bool active = metadata.CheatsUsed || metadata.MajorAssistsUsed || metadata.NoDamage;
			flagsContainer.SetActive(active);
			cheatsBubble.SetActive(metadata.CheatsUsed);
			majorAssistsBubble.SetActive(metadata.MajorAssistsUsed);
			noDamageBubble.SetActive(metadata.NoDamage);
			((UnityEventBase)openInFolderButton.onClick).RemoveAllListeners();
			((UnityEvent)openInFolderButton.onClick).AddListener((UnityAction)delegate
			{
				Application.OpenURL(Path.GetDirectoryName(metadata.LocatedFilePath));
			});
			((UnityEventBase)saveButton.onClick).RemoveAllListeners();
			((Component)saveButton).gameObject.SetActive(false);
			if (flag)
			{
				((UnityEvent)saveButton.onClick).AddListener((UnityAction)delegate
				{
					metadata.Title = title;
					metadata.Description = desc;
					metadata.RunnerName = runnerName;
					GhostFileManager.UpdateMetadata(metadata);
					if (fileName != Path.GetFileNameWithoutExtension(metadata.LocatedFilePath))
					{
						GhostFileManager.RenameRun(metadata, fileName);
					}
					((Component)saveButton).gameObject.SetActive(false);
					runManager.RebuildMenu();
				});
			}
			((UnityEventBase)deleteButton.onClick).RemoveAllListeners();
			((UnityEvent)deleteButton.onClick).AddListener((UnityAction)delegate
			{
				ModalDialogue.ShowSimple("WARNING", "You are about to permanently delete this run. Confirm?", (Action<bool>)delegate(bool r)
				{
					if (r)
					{
						GhostFileManager.DeleteRun(metadata);
						runManager.RebuildMenu();
						((Component)this).gameObject.SetActive(false);
						runManager.Open();
					}
				}, "Delete", "Cancel");
			});
			((UnityEventBase)setCustomGoalButton.onClick).RemoveAllListeners();
			((Component)setCustomGoalButton).gameObject.SetActive(flag2);
			if (flag2)
			{
				((UnityEvent)setCustomGoalButton.onClick).AddListener((UnityAction)delegate
				{
					TrackerManager.SetCustomGoal(metadata.StatGoal);
					((UnityEventBase)setCustomGoalButton.onClick).RemoveAllListeners();
				});
			}
		}

		private string ParseDifficultyName(int difficulty)
		{
			return difficulty switch
			{
				0 => "HARMLESS", 
				1 => "LENIENT", 
				2 => "STANDARD", 
				3 => "VIOLENT", 
				4 => "BRUTAL", 
				5 => "ULTRAKILL MUST DIE", 
				_ => "UNKNOWN DIFFICULTY", 
			};
		}
	}
	public class RunList : MonoBehaviour
	{
		[SerializeField]
		private GameObject listElementPrefab;

		[SerializeField]
		private RectTransform contentBody;

		[SerializeField]
		private Button selectAllButton;

		[SerializeField]
		private Button deselectAllButton;

		[SerializeField]
		private Button backButton;

		[SerializeField]
		private InputField searchField;

		[SerializeField]
		private Text levelNameText;

		private Dictionary<GameObject, HashSet<string>> namedElements;

		private List<GameObject> instancedElements;

		private bool isInLevelOfFolder;

		private void Awake()
		{
			((UnityEvent<string>)(object)searchField.onEndEdit).AddListener((UnityAction<string>)delegate(string text)
			{
				UpdateSearch(text);
			});
		}

		public void SetName(string name)
		{
			isInLevelOfFolder = name == SceneHelper.CurrentScene;
			levelNameText.text = name;
		}

		private void ClearList()
		{
			if (instancedElements != null)
			{
				for (int i = 0; i < instancedElements.Count; i++)
				{
					if (!((Object)(object)instancedElements[i] == (Object)null))
					{
						GameObject val = instancedElements[i];
						instancedElements[i] = null;
						Object.Destroy((Object)(object)val);
					}
				}
				instancedElements.Clear();
				namedElements.Clear();
			}
			else
			{
				instancedElements = new List<GameObject>();
				namedElements = new Dictionary<GameObject, HashSet<string>>();
			}
		}

		public void SetList(List<GhostRecordingMetadata> metadatas, Action<GhostRecordingMetadata> onRunSelected)
		{
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Expected O, but got Unknown
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Expected O, but got Unknown
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Expected O, but got Unknown
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			ClearList();
			foreach (GhostRecordingMetadata metadata in metadatas.OrderByDescending((GhostRecordingMetadata x) => x.DateCreated.Ticks))
			{
				GameObject val = Object.Instantiate<GameObject>(listElementPrefab, (Transform)(object)contentBody);
				Button componentInChildren = val.GetComponentInChildren<Button>();
				Text componentInChildren2 = val.GetComponentInChildren<Text>();
				Toggle componentInChildren3 = val.GetComponentInChildren<Toggle>();
				instancedElements.Add(val);
				namedElements.Add(val, new HashSet<string>());
				namedElements[val].Add(metadata.GetFileName());
				if (!string.IsNullOrEmpty(metadata.Description))
				{
					namedElements[val].Add(metadata.Description);
				}
				if (!string.IsNullOrEmpty(metadata.Title))
				{
					namedElements[val].Add(metadata.Title);
				}
				if (!string.IsNullOrEmpty(metadata.RunnerName))
				{
					namedElements[val].Add(metadata.RunnerName);
				}
				string text = "Player (" + metadata.SteamID.ToString().Substring(0, 5) + "...)";
				if (SteamClient.IsValid)
				{
					Friend val2 = new Friend(SteamId.op_Implicit(metadata.SteamID));
					text = ((Friend)(ref val2)).Name;
				}
				componentInChildren2.text = text + " // " + metadata.Title + " // " + Path.GetFileNameWithoutExtension(metadata.LocatedFilePath);
				((UnityEvent)componentInChildren.onClick).AddListener((UnityAction)delegate
				{
					onRunSelected(metadata);
				});
				componentInChildren3.SetIsOnWithoutNotify(GhostManager.IsGhostEnabled(metadata.LocatedFilePath));
				((Component)componentInChildren3).gameObject.SetActive(isInLevelOfFolder);
				((UnityEvent<bool>)(object)componentInChildren3.onValueChanged).AddListener((UnityAction<bool>)delegate(bool value)
				{
					GhostManager.SetGhostEnabled(metadata.LocatedFilePath, value);
				});
			}
			((UnityEvent)selectAllButton.onClick).AddListener((UnityAction)delegate
			{
				foreach (GameObject instancedElement in instancedElements)
				{
					Toggle componentInChildren5 = instancedElement.GetComponentInChildren<Toggle>();
					componentInChildren5.isOn = true;
				}
			});
			((UnityEvent)deselectAllButton.onClick).AddListener((UnityAction)delegate
			{
				foreach (GameObject instancedElement2 in instancedElements)
				{
					Toggle componentInChildren4 = instancedElement2.GetComponentInChildren<Toggle>();
					componentInChildren4.isOn = false;
				}
			});
		}

		private void UpdateSearch(string text)
		{
			if (instancedElements == null || namedElements == null)
			{
				return;
			}
			if (string.IsNullOrEmpty(text))
			{
				foreach (GameObject instancedElement in instancedElements)
				{
					instancedElement.SetActive(true);
				}
				return;
			}
			foreach (KeyValuePair<GameObject, HashSet<string>> namedElement in namedElements)
			{
				bool active = false;
				foreach (string item in namedElement.Value)
				{
					if (item.ToLower().Contains(text.ToLower()))
					{
						active = true;
						break;
					}
				}
				namedElement.Key.SetActive(active);
			}
		}

		public void SetBackAction(Action onBackPressed)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			((UnityEventBase)backButton.onClick).RemoveAllListeners();
			((UnityEvent)backButton.onClick).AddListener((UnityAction)delegate
			{
				onBackPressed?.Invoke();
			});
		}
	}
	public class RunManagerMenu : MonoBehaviour
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction <>9__13_0;

			public static Func<GhostRecordingMetadata, string> <>9__15_0;

			public static Func<string, string> <>9__15_1;

			public static Func<string, string> <>9__15_2;

			public static Func<KeyValuePair<string, List<GhostRecordingMetadata>>, bool> <>9__15_4;

			public static Func<KeyValuePair<string, List<GhostRecordingMetadata>>, string> <>9__15_5;

			public static Func<KeyValuePair<string, List<GhostRecordingMetadata>>, List<GhostRecordingMetadata>> <>9__15_6;

			public static Action <>9__21_0;

			public static Action <>9__21_1;

			public static Action <>9__21_2;

			internal void <Awake>b__13_0()
			{
				Application.OpenURL("https://discord.gg/kCHnwMDPt4");
			}

			internal string <RebuildMenu>b__15_0(GhostRecordingMetadata x)
			{
				return x.LevelName;
			}

			internal string <RebuildMenu>b__15_1(string x)
			{
				return x;
			}

			internal string <RebuildMenu>b__15_2(string x)
			{
				return x;
			}

			internal bool <RebuildMenu>b__15_4(KeyValuePair<string, List<GhostRecordingMetadata>> x)
			{
				return x.Key == SceneHelper.CurrentScene;
			}

			internal string <RebuildMenu>b__15_5(KeyValuePair<string, List<GhostRecordingMetadata>> x)
			{
				return x.Key;
			}

			internal List<GhostRecordingMetadata> <RebuildMenu>b__15_6(KeyValuePair<string, List<GhostRecordingMetadata>> x)
			{
				return x.Value;
			}

			internal void <NotifyUpdateAvailable>b__21_0()
			{
				Application.OpenURL("https://www.github.com/Hydraxous/EasyPZ-ULTRAKILL/releases/latest");
			}

			internal void <NotifyUpdateAvailable>b__21_1()
			{
			}

			internal void <NotifyUpdateAvailable>b__21_2()
			{
				((ConfigValueElement<bool>)(object)notifyOnUpdateAvailable).SetValue(false);
			}
		}

		[SerializeField]
		private GameObject container;

		[SerializeField]
		private RunEditor runEditor;

		[SerializeField]
		private RectTransform listRoot;

		[SerializeField]
		private Button discordButton;

		[SerializeField]
		private Button backButton;

		[SerializeField]
		private RectTransform folderButtonContentBody;

		[SerializeField]
		private RectTransform foldersRoot;

		[SerializeField]
		private GameObject folderListPrefab;

		private List<GameObject> instancedMenus;

		private List<GameObject> instancedFolderButtons;

		private static RunManagerMenu instance;

		[Configgable("Extras/Advanced", "Notify on update available", 0, null)]
		private static ConfigToggle notifyOnUpdateAvailable = new ConfigToggle(true);

		private static bool openedOnce;

		private void Awake()
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//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_0031: Expected O, but got Unknown
			instance = this;
			ButtonClickedEvent onClick = discordButton.onClick;
			object obj = <>c.<>9__13_0;
			if (obj == null)
			{
				UnityAction val = delegate
				{
					Application.OpenURL("https://discord.gg/kCHnwMDPt4");
				};
				<>c.<>9__13_0 = val;
				obj = (object)val;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj);
			((UnityEvent)backButton.onClick).AddListener((UnityAction)delegate
			{
				Close();
			});
			RebuildMenu();
			container.SetActive(false);
			((Component)listRoot).gameObject.SetActive(false);
			((Component)foldersRoot).gameObject.SetActive(false);
			((Component)runEditor).gameObject.SetActive(false);
		}

		[Configgable("Ghosts", "Manage Runs", 0, null)]
		private static void OpenMenuStatic()
		{
			if ((Object)(object)instance == (Object)null)
			{
				Debug.LogError((object)"RunManagerMenu could not be found.");
				return;
			}
			ConfigurationMenu.Close();
			instance.Open();
		}

		public void RebuildMenu()
		{
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Expected O, but got Unknown
			ClearMenus();
			List<GhostRecordingMetadata> metadatas = GhostFileManager.FetchMetadata();
			List<string> source = (from x in metadatas.Select((GhostRecordingMetadata x) => x.LevelName).Distinct()
				orderby x
				select x).ToList();
			Dictionary<string, List<GhostRecordingMetadata>> source2 = source.ToDictionary((string x) => x, (string x) => metadatas.Where((GhostRecordingMetadata y) => y.LevelName == x).ToList());
			source2 = source2.OrderByDescending((KeyValuePair<string, List<GhostRecordingMetadata>> x) => x.Key == SceneHelper.CurrentScene).ToDictionary((KeyValuePair<string, List<GhostRecordingMetadata>> x) => x.Key, (KeyValuePair<string, List<GhostRecordingMetadata>> x) => x.Value);
			foreach (KeyValuePair<string, List<GhostRecordingMetadata>> item in source2)
			{
				GameObject folder = Object.Instantiate<GameObject>(folderListPrefab, (Transform)(object)foldersRoot);
				RunList list = folder.GetComponent<RunList>();
				bool flag = item.Key == SceneHelper.CurrentScene;
				list.SetName(item.Key);
				UnityAction val = default(UnityAction);
				list.SetList(item.Value, delegate(GhostRecordingMetadata d)
				{
					//IL_004d: Unknown result type (might be due to invalid IL or missing references)
					//IL_0052: Unknown result type (might be due to invalid IL or missing references)
					//IL_0054: Expected O, but got Unknown
					//IL_0059: Expected O, but got Unknown
					((UnityEventBase)runEditor.BackButton.onClick).RemoveAllListeners();
					ButtonClickedEvent onClick = runEditor.BackButton.onClick;
					UnityAction obj = val;
					if (obj == null)
					{
						UnityAction val3 = delegate
						{
							((Component)runEditor).gameObject.SetActive(false);
							((Component)foldersRoot).gameObject.SetActive(true);
							if ((Object)(object)folder != (Object)null)
							{
								folder.SetActive(true);
							}
							else
							{
								((Component)foldersRoot).gameObject.SetActive(false);
								((Component)listRoot).gameObject.SetActive(true);
							}
						};
						UnityAction val4 = val3;
						val = val3;
						obj = val4;
					}
					((UnityEvent)onClick).AddListener(obj);
					OpenRunInEditor(d);
				});
				folder.SetActive(false);
				GameObject val2 = Object.Instantiate<GameObject>(PluginAssets.ButtonPrefab, (Transform)(object)folderButtonContentBody);
				instancedFolderButtons.Add(val2);
				Button component = val2.GetComponent<Button>();
				Text componentInChildren = val2.GetComponentInChildren<Text>();
				RectTransform component2 = val2.GetComponent<RectTransform>();
				component2.sizeDelta = new Vector2(component2.sizeDelta.x, 35f);
				componentInChildren.text = $"{item.Key} ({item.Value.Count})";
				if (flag)
				{
					componentInChildren.text = "<color=orange>" + componentInChildren.text + "</color>";
				}
				instancedMenus.Add(folder);
				((UnityEvent)component.onClick).AddListener((UnityAction)delegate
				{
					OpenFolder(list);
				});
			}
		}

		private void Update()
		{
			if (container.activeInHierarchy && Input.GetKeyDown((KeyCode)27))
			{
				if (((Component)runEditor).gameObject.activeInHierarchy)
				{
					((Component)runEditor).gameObject.SetActive(false);
					((Component)foldersRoot).gameObject.SetActive(true);
				}
				else if (((Component)foldersRoot).gameObject.activeInHierarchy)
				{
					((Component)foldersRoot).gameObject.SetActive(false);
					((Component)listRoot).gameObject.SetActive(true);
				}
				else if (container.activeInHierarchy)
				{
					Close();
				}
			}
		}

		private void OpenRunInEditor(GhostRecordingMetadata metadata)
		{
			runEditor.SetRecording(metadata);
			((Component)runEditor).gameObject.SetActive(true);
			((Component)listRoot).gameObject.SetActive(false);
			((Component)foldersRoot).gameObject.SetActive(false);
		}

		private void OpenFolder(RunList list)
		{
			list.SetBackAction(delegate
			{
				((Component)listRoot).gameObject.SetActive(true);
				((Component)foldersRoot).gameObject.SetActive(false);
				((Component)list).gameObject.SetActive(false);
			});
			((Component)foldersRoot).gameObject.SetActive(true);
			((Component)listRoot).gameObject.SetActive(false);
			((Component)list).gameObject.SetActive(true);
		}

		private void ClearMenus()
		{
			if (instancedMenus != null)
			{
				for (int i = 0; i < instancedMenus.Count; i++)
				{
					if (!((Object)(object)instancedMenus[i] == (Object)null))
					{
						GameObject val = instancedMenus[i];
						instancedMenus[i] = null;
						Object.Destroy((Object)(object)val);
					}
				}
				instancedMenus.Clear();
			}
			else
			{
				instancedMenus = new List<GameObject>();
			}
			if (instancedFolderButtons != null)
			{
				for (int j = 0; j < instancedFolderButtons.Count; j++)
				{
					if (!((Object)(object)instancedFolderButtons[j] == (Object)null))
					{
						GameObject val2 = instancedFolderButtons[j];
						instancedFolderButtons[j] = null;
						Object.Destroy((Object)(object)val2);
					}
				}
				instancedFolderButtons.Clear();
			}
			else
			{
				instancedFolderButtons = new List<GameObject>();
			}
		}

		public void Open()
		{
			container.SetActive(true);
			Pauser.Pause(container);
			((Component)runEditor).gameObject.SetActive(false);
			((Component)foldersRoot).gameObject.SetActive(false);
			if (instancedMenus != null)
			{
				foreach (GameObject instancedMenu in instancedMenus)
				{
					instancedMenu.gameObject.SetActive(false);
				}
			}
			((Component)listRoot).gameObject.SetActive(true);
			if (!openedOnce)
			{
				openedOnce = true;
				if (!EasyPZ.UsingLatestVersion && ((ConfigValueElement<bool>)(object)notifyOnUpdateAvailable).Value)
				{
					NotifyUpdateAvailable();
				}
			}
		}

		private void NotifyUpdateAvailable()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			ModalDialogueEvent val = new ModalDialogueEvent();
			val.Title = "Outdated";
			val.Message = "You are using an outdated version of EasyPZ: (<color=red>3.0.3</color>). Please update to the latest version: (<color=green>" + EasyPZ.LatestVersion + "</color>)";
			val.Options = (DialogueBoxOption[])(object)new DialogueBoxOption[3]
			{
				new DialogueBoxOption
				{
					Name = "Open Browser",
					Color = Color.white,
					OnClick = delegate
					{
						Application.OpenURL("https://www.github.com/Hydraxous/EasyPZ-ULTRAKILL/releases/latest");
					}
				},
				new DialogueBoxOption
				{
					Name = "Later",
					Color = Color.white,
					OnClick = delegate
					{
					}
				},
				new DialogueBoxOption
				{
					Name = "Don't Ask Again.",
					Color = Color.red,
					OnClick = delegate
					{
						((ConfigValueElement<bool>)(object)notifyOnUpdateAvailable).SetValue(false);
					}
				}
			};
			ModalDialogue.ShowDialogue(val);
		}

		public void Close()
		{
			container.SetActive(false);
			ConfigurationMenu.Open();
		}
	}
	public class TrackerManager : MonoBehaviour
	{
		[Configgable("Binds", "Auto Restart Toggle", 0, null)]
		private static ConfigKeybind Key_PModeToggle = new ConfigKeybind((KeyCode)112);

		[Configgable("Binds", "Restart Mission", 0, null)]
		private static ConfigKeybind Key_RestartMission = new ConfigKeybind((KeyCode)307);

		[Configgable("Tracker", "Tracker Type", 0, null)]
		private static ConfigDropdown<TrackerType> CFG_trackerType = new ConfigDropdown<TrackerType>((TrackerType[])Enum.GetValues(typeof(TrackerType)), ((TrackerType[])Enum.GetValues(typeof(TrackerType))).Select((TrackerType x) => x.ToString()).ToArray(), 1);

		[Configgable("Tracker", "Always Show Tracker", 0, "When disabled the tracker will be tied to the tab stats overlay and will only show when it is on screen.")]
		private static ConfigToggle CFG_AlwaysShowTracker = new ConfigToggle(true);

		[Configgable("Auto Restart", "Only AutoRestart At Level End", 0, "This will only auto-restart if the goal is failed when you finish the level.")]
		private static ConfigToggle CFG_AutoRestartAtLevelEnd = new ConfigToggle(false);

		[Configgable("Auto Restart", "Enable Auto Restart", 0, null)]
		public static ConfigToggle AutoRestartEnabled = new ConfigToggle(false);

		[Configgable("Tracker/Goal", "Use P-Rank stats for Goal", 0, "This will use the P-Rank time instead of Any% time for leaderboard based goals.")]
		private static ConfigToggle CFG_UsePRankForLeaderboardGoals = new ConfigToggle(false);

		[Configgable("Tracker/Goal", "Goal Mode", 0, null)]
		private static ConfigDropdown<GoalMode> CFG_GoalMode = new ConfigDropdown<GoalMode>((GoalMode[])Enum.GetValues(typeof(GoalMode)), new string[5] { "P-Rank", "Custom", "Personal Best", "Top Friend", "Next Highest Friend" }, 0);

		[Configgable("Tracker/Goal/Custom Goal", "Kills", 0, null)]
		private static ConfigInputField<int> CFG_CustomGoalKills = new ConfigInputField<int>(0, (Func<int, bool>)null, (Func<string, ValueTuple<bool, int>>)null);

		[Configgable("Tracker/Goal/Custom Goal", "Seconds", 0, null)]
		private static ConfigInputField<float> CFG_CustomGoalSeconds = new ConfigInputField<float>(120f, (Func<float, bool>)null, (Func<string, ValueTuple<bool, float>>)null);

		[Configgable("Tracker/Goal/Custom Goal", "Deaths", 0, null)]
		private static ConfigInputField<int> CFG_CustomGoalDeaths = new ConfigInputField<int>(0, (Func<int, bool>)null, (Func<string, ValueTuple<bool, int>>)null);

		[Configgable("Tracker/Goal/Custom Goal", "Style", 0, null)]
		private static ConfigInputField<int> CFG_CustomGoalStyle = new ConfigInputField<int>(2000, (Func<int, bool>)null, (Func<string, ValueTuple<bool, int>>)null);

		[SerializeField]
		private Image blocker;

		[SerializeField]
		private GameObject container;

		private RectTransform content;

		private GameObject tracker;

		private LevelStats levelStats;

		private bool editMode;

		private StatGoal currentStatGoal;

		private static TrackerManager instance;

		private void Awake()
		{
			instance = this;
			LevelStatsEnabler obj = Object.FindObjectOfType<LevelStatsEnabler>();
			levelStats = ((obj != null) ? ((Component)obj).GetComponentInChildren<LevelStats>() : null);
			((Component)blocker).gameObject.SetActive(false);
			content = container.GetComponent<RectTransform>();
		}

		private void Start()
		{
			ConfigDropdown<TrackerType> cFG_trackerType = CFG_trackerType;
			((ConfigValueElement<TrackerType>)(object)cFG_trackerType).OnValueChanged = (Action<TrackerType>)Delegate.Combine(((ConfigValueElement<TrackerType>)(object)cFG_trackerType).OnValueChanged, new Action<TrackerType>(InstanceTracker));
			ConfigToggle cFG_UsePRankForLeaderboardGoals = CFG_UsePRankForLeaderboardGoals;
			((ConfigValueElement<bool>)(object)cFG_UsePRankForLeaderboardGoals).OnValueChanged = (Action<bool>)Delegate.Combine(((ConfigValueElement<bool>)(object)cFG_UsePRankForLeaderboardGoals).OnValueChanged, new Action<bool>(ReinitGoal));
			ConfigDropdown<GoalMode> cFG_GoalMode = CFG_GoalMode;
			((ConfigValueElement<GoalMode>)(object)cFG_GoalMode).OnValueChanged = (Action<GoalMode>)Delegate.Combine(((ConfigValueElement<GoalMode>)(object)cFG_GoalMode).OnValueChanged, new Action<GoalMode>(ReinitGoal));
			ConfigInputField<int> cFG_CustomGoalDeaths = CFG_CustomGoalDeaths;
			((ConfigValueElement<int>)(object)cFG_CustomGoalDeaths).OnValueChanged = (Action<int>)Delegate.Combine(((ConfigValueElement<int>)(object)cFG_CustomGoalDeaths).OnValueChanged, new Action<int>(ReinitGoal));
			ConfigInputField<int> cFG_CustomGoalKills = CFG_CustomGoalKills;
			((ConfigValueElement<int>)(object)cFG_CustomGoalKills).OnValueChanged = (Action<int>)Delegate.Combine(((ConfigValueElement<int>)(object)cFG_CustomGoalKills).OnValueChanged, new Action<int>(ReinitGoal));
			ConfigInputField<int> cFG_CustomGoalStyle = CFG_CustomGoalStyle;
			((ConfigValueElement<int>)(object)cFG_CustomGoalStyle).OnValueChanged = (Action<int>)Delegate.Combine(((ConfigValueElement<int>)(object)cFG_CustomGoalStyle).OnValueChanged, new Action<int>(ReinitGoal));
			ConfigInputField<float> cFG_CustomGoalSeconds = CFG_CustomGoalSeconds;
			((ConfigValueElement<float>)(object)cFG_CustomGoalSeconds).OnValueChanged = (Action<float>)Delegate.Combine(((ConfigValueElement<float>)(object)cFG_CustomGoalSeconds).OnValueChanged, new Action<float>(ReinitGoal));
			if (InGameCheck.InLevel())
			{
				InitializeGoal();
				InstanceTracker(((ConfigValueElement<TrackerType>)(object)CFG_trackerType).Value);
				((Component)this).gameObject.AddComponent<SessionRecorder>();
				((Component)this).gameObject.AddComponent<GhostManager>();
				((Component)this).gameObject.AddComponent<SpeedLimiter>();
			}
		}

		private void ReinitGoal<T>(T v)
		{
			InitializeGoal();
		}

		private void InitializeGoal()
		{
			SetGoal(GetCurrentGoal());
		}

		private void InstanceTracker(TrackerType trackerType)
		{
			if ((Object)(object)tracker != (Object)null)
			{
				GameObject val = tracker;
				Object.Destroy((Object)(object)val);
				tracker = null;
			}
			GameObject val2 = null;
			tracker = Object.Instantiate<GameObject>((GameObject)(trackerType switch
			{
				TrackerType.Classic => Prefabs.ClassicTrackerPrefab, 
				_ => Prefabs.CompactTracker, 
			}), (Transform)(object)content);
			IEZPZTracker iEZPZTracker = default(IEZPZTracker);
			if (tracker.TryGetComponent<IEZPZTracker>(ref iEZPZTracker))
			{
				iEZPZTracker.SetStatGoal(currentStatGoal);
			}
		}

		private void SetGoal(StatGoal goal)
		{
			currentStatGoal = goal;
			IEZPZTracker iEZPZTracker = default(IEZPZTracker);
			if ((Object)(object)tracker != (Object)null && tracker.TryGetComponent<IEZPZTracker>(ref iEZPZTracker))
			{
				iEZPZTracker.SetStatGoal(currentStatGoal);
			}
		}

		public static void SetCustomGoal(StatGoal goal)
		{
			CFG_GoalMode.SetIndex(1);