Decompiled source of BossRush v1.1.3

plugins/BossRush.dll

Decompiled 6 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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BossRush.Properties;
using BossRush.UI;
using Discord;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
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.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Hydraxous")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("BossRush gamemode for ULTRAKILL")]
[assembly: AssemblyFileVersion("1.1.3.0")]
[assembly: AssemblyInformationalVersion("1.1.3+f7ee2e98863a2ab5bbc2e6a5083023b78f8f0ffc")]
[assembly: AssemblyProduct("BossRush")]
[assembly: AssemblyTitle("BossRush")]
[assembly: AssemblyMetadata("RepositoryUrl", "https:")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.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;
		}
	}
}
namespace BossRush
{
	public static class Assets
	{
		public static AssetBundle assets;

		public static GameObject BossRushStatsPrefab { get; private set; }

		public static GameObject BossRushMenuPrefab { get; private set; }

		public static GameObject BossRushDeathScreen { get; private set; }

		public static void LoadAssets()
		{
			assets = AssetBundle.LoadFromMemory(Resources.BossRush);
			BossRushStatsPrefab = assets.LoadAsset<GameObject>("BossRushStatsDisplay");
			BossRushMenuPrefab = assets.LoadAsset<GameObject>("BossRushMenu");
			BossRushDeathScreen = assets.LoadAsset<GameObject>("BossRushDeathScreen");
		}
	}
	[BepInPlugin("Hydraxous.ULTRAKILL.BossRush", "BossRush", "1.1.3")]
	public class BossRush : BaseUnityPlugin
	{
		private Harmony harmony;

		private bool debug = false;

		public static BossRush Instance { get; private set; }

		public static ManualLogSource BepInExLogger => ((BaseUnityPlugin)Instance).Logger;

		public static bool LatestVersion { get; private set; }

		public static string VersionName { get; private set; }

		private void Awake()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			Instance = this;
			BossRushConfig.Bind();
			BossRushPaths.CheckFolders();
			LevelChainManager.LoadLevelChainTable();
			StatRecords.LoadRecords();
			harmony = new Harmony("Hydraxous.ULTRAKILL.BossRush.harmony");
			harmony.PatchAll();
			Assets.LoadAssets();
			BossRushStats.Spawn();
			VersionCheck.CheckVersion("https://api.github.com/repos/Hydraxous/bossrush/tags", "1.1.3", VersionCheckCallback);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Boss Rush 1.1.3 loaded!");
		}

		private void Update()
		{
			CheckDebugInputs();
		}

		private void VersionCheckCallback(bool onLatest, string versionName)
		{
			LatestVersion = onLatest;
			VersionName = versionName;
			if (versionName == "UNKNOWN")
			{
				LatestVersion = true;
				VersionName = "1.1.3";
			}
			if (LatestVersion)
			{
				Debug.Log((object)("BOSSRUSH: You are using the latest version of BossRush (" + VersionName + ")."));
			}
			else
			{
				Debug.LogWarning((object)("BOSSRUSH: You are using an outdated version of BossRush (1.1.3). Please consider updating to " + VersionName));
			}
		}

		private void CheckDebugInputs()
		{
			if (Input.GetKey((KeyCode)304) && debug)
			{
				if (Input.GetKeyDown((KeyCode)49) && !BossRushController.BossRushMode)
				{
					BossRushController.StartBossRushMode();
				}
				if (Input.GetKeyDown((KeyCode)50) && !BossRushController.BossRushMode)
				{
					BossRushController.StartBossRushMode(hardcore: true);
				}
				if (Input.GetKeyDown((KeyCode)51))
				{
					BossRushController.HardcoreMode = !BossRushController.HardcoreMode;
					Debug.Log((object)$"BR: Hardcore={BossRushController.HardcoreMode}");
				}
				if (Input.GetKeyDown((KeyCode)52))
				{
					BossRushController.BossRushMode = !BossRushController.BossRushMode;
					Debug.Log((object)$"BR: Enabled={BossRushController.BossRushMode}");
				}
				if (Input.GetKeyDown((KeyCode)53))
				{
					BossRushController.Laps--;
					Debug.Log((object)$"BR: Laps={BossRushController.Laps}");
				}
				if (Input.GetKeyDown((KeyCode)54))
				{
					BossRushController.Laps++;
					Debug.Log((object)$"BR: Laps={BossRushController.Laps}");
				}
				if (Input.GetKeyDown((KeyCode)55))
				{
					BossRushController.Laps = 0;
					Debug.Log((object)$"BR: Laps={BossRushController.Laps}");
				}
				if (Input.GetKeyDown((KeyCode)56))
				{
					StatRecords.SubmitRecord(BossRushController.GetCurrentStat());
					Debug.Log((object)$"BR: Laps={BossRushController.Laps}");
				}
			}
		}

		private void OnApplicationQuit()
		{
			StatRecords.Save();
		}
	}
	public static class BossRushConfig
	{
		public static ConfigEntry<float> LapBuffMultiplier { get; private set; }

		public static ConfigEntry<bool> EnemySpeedBuff { get; private set; }

		public static ConfigEntry<bool> EnemyDamageBuff { get; private set; }

		public static ConfigEntry<bool> EnemyHealthBuff { get; private set; }

		public static ConfigEntry<bool> AlwaysShowStats { get; private set; }

		public static void Bind()
		{
			LapBuffMultiplier = ((BaseUnityPlugin)BossRush.Instance).Config.Bind<float>("EnemyBuff", "LapBuffMultiplier", 2f, "Applies X radiance tiers to enemies for every lap completed.");
			EnemySpeedBuff = ((BaseUnityPlugin)BossRush.Instance).Config.Bind<bool>("EnemyBuff", "EnemySpeedBuff", false, "Buffs enemy speed on laps.");
			EnemyDamageBuff = ((BaseUnityPlugin)BossRush.Instance).Config.Bind<bool>("EnemyBuff", "EnemyDamageBuff", true, "Buffs enemy damage on laps.");
			EnemyHealthBuff = ((BaseUnityPlugin)BossRush.Instance).Config.Bind<bool>("EnemyBuff", "EnemyHealthBuff", true, "Buffs enemy health on laps.");
			AlwaysShowStats = ((BaseUnityPlugin)BossRush.Instance).Config.Bind<bool>("General", "AlwaysShowStats", true, "Show always or only when pressing tab.");
		}
	}
	public static class BossRushController
	{
		private static float startTime;

		public static bool BossRushMode { get; internal set; }

		public static bool HardcoreMode { get; internal set; }

		public static int Laps { get; internal set; }

		public static int Deaths { get; internal set; }

		public static long StartTimeEpoc { get; internal set; }

		public static float TimeElapsed
		{
			get
			{
				float time = Time.time;
				if (!BossRushMode)
				{
					startTime = time;
				}
				return time - startTime;
			}
		}

		public static void StartBossRushMode(bool hardcore = false)
		{
			Reset();
			BossRushMode = true;
			HardcoreMode = hardcore;
			SceneHelper.LoadScene(LevelChainManager.GetFirstLevelName(), false);
		}

		public static void Reset()
		{
			BossRushMode = false;
			HardcoreMode = false;
			StatRecords.HighScoreRun = false;
			startTime = Time.time;
			StartTimeEpoc = DateTimeOffset.Now.ToUnixTimeSeconds();
			Deaths = 0;
			Laps = 0;
		}

		public static BossRushRecord GetCurrentStat()
		{
			return new BossRushRecord(Laps, TimeElapsed, Deaths, HardcoreMode);
		}
	}
	public static class BossRushPaths
	{
		public const string BossRushSaveFileName = "records";

		public static string GameFolder => Path.GetDirectoryName(Application.dataPath);

		public static string BepInExFolder => Path.Combine(GameFolder, "BepInEx");

		public static string BepInExConfigFolder => Path.Combine(BepInExFolder, "config");

		public static string BossRushConfigFolder => Path.Combine(BepInExConfigFolder, "BossRush");

		public static string BossRushSaveFile => Path.Combine(BossRushConfigFolder, "records.json");

		public static string BossRushLevelChain => Path.Combine(BossRushConfigFolder, "LevelChain.json");

		public static string GetModLocation()
		{
			return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
		}

		public static void CheckFolders()
		{
			CreateFolder(BossRushConfigFolder);
		}

		private static void CreateFolder(string path)
		{
			if (!Directory.Exists(path))
			{
				Directory.CreateDirectory(path);
			}
		}
	}
	public static class ConstInfo
	{
		public const string GUID = "Hydraxous.ULTRAKILL.BossRush";

		public const string NAME = "BossRush";

		public const string VERSION = "1.1.3";

		public const string GITHUB_URL = "https://api.github.com/repos/Hydraxous/bossrush/tags";
	}
	[HarmonyPatch(typeof(DiscordController))]
	public static class DiscordRPCPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch("SendActivity")]
		public static bool Prefix(ref Activity ___cachedActivity)
		{
			if (!BossRushController.BossRushMode)
			{
				return true;
			}
			___cachedActivity.Details = "BOSS RUSH: " + ParseDifficultyName(MonoSingleton<PrefsManager>.Instance.GetInt("difficulty", 0));
			string text = (BossRushController.HardcoreMode ? "HARDCORE" : ("DEATHS: " + BossRushController.Deaths.ToString("000")));
			___cachedActivity.State = "LAPS: " + BossRushController.Laps.ToString("000") + " | " + text;
			___cachedActivity.Timestamps.Start = BossRushController.StartTimeEpoc;
			return true;
		}

		private static string ParseDifficultyName(int diff)
		{
			return diff switch
			{
				0 => "HARMLESS", 
				1 => "LENIENT", 
				2 => "STANDARD", 
				3 => "VIOLENT", 
				4 => "BRUTAL", 
				5 => "UKMD", 
				_ => "Game Journalist", 
			};
		}
	}
	[HarmonyPatch(typeof(FinalRank), "LevelChange")]
	public static class FinalRankPatch
	{
		public static bool Prefix(FinalRank __instance)
		{
			if (BossRushController.BossRushMode && !SceneHelper.IsPlayingCustom)
			{
				LevelChain chainOfLevel = LevelChainManager.GetChainOfLevel(SceneHelper.CurrentScene);
				if (chainOfLevel == null)
				{
					BossRush.BepInExLogger.LogWarning((object)("Scene (" + SceneHelper.CurrentScene + ") has no valid chain."));
					return true;
				}
				if (!string.IsNullOrEmpty(chainOfLevel.PitTargetFilter) && __instance.targetLevelName != chainOfLevel.PitTargetFilter)
				{
					BossRush.BepInExLogger.LogInfo((object)("Player entering level " + __instance.targetLevelName + " when the level chain expected " + chainOfLevel.PitTargetFilter + ". Boss Rush will now be disabled."));
					MonoSingleton<HudMessageReceiver>.Instance.SendHudMessage("Sequence broken, boss rush mode disabled.", "", "", 0, false);
					BossRushController.Reset();
				}
				else if ((__instance.targetLevelName = chainOfLevel.LevelTo) == LevelChainManager.GetFirstLevelName())
				{
					BossRushController.Laps++;
					StatRecords.SubmitRecord(BossRushController.GetCurrentStat());
				}
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(OptionsManager))]
	public static class OptionsManagerFixes
	{
		[HarmonyPostfix]
		[HarmonyPatch("QuitMission")]
		public static void Postfix()
		{
			BossRushController.Reset();
		}

		[HarmonyPrefix]
		[HarmonyPatch("RestartMission")]
		public static bool Prefix()
		{
			if (!BossRushController.BossRushMode)
			{
				return true;
			}
			if (BossRushController.HardcoreMode)
			{
				BossRushController.StartBossRushMode(hardcore: true);
				return false;
			}
			BossRushController.Deaths++;
			return true;
		}
	}
	[HarmonyPatch(typeof(StatsManager))]
	public static class StasManagerPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("Restart")]
		public static void Postfix()
		{
			if (BossRushController.BossRushMode)
			{
				if (BossRushController.HardcoreMode)
				{
					BossRushDeathScreen.Instance.Open();
				}
				else
				{
					BossRushController.Deaths++;
				}
			}
		}
	}
	[HarmonyPatch(typeof(NewMovement))]
	public static class HardcoreResetPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch("GetHurt")]
		public static bool Prefix(NewMovement __instance, int damage, bool invincible)
		{
			if (!BossRushController.HardcoreMode || !BossRushController.BossRushMode)
			{
				return true;
			}
			if (((Component)__instance).gameObject.layer == 15)
			{
				return true;
			}
			if (__instance.hp - damage <= 0)
			{
				try
				{
					BossRushDeathScreen.Instance.Open();
				}
				catch (Exception ex)
				{
					Debug.LogException(ex);
					return true;
				}
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(EnemyIdentifier), "Start")]
	public static class EnemyDifficultyUpPatch
	{
		public static void Postfix(EnemyIdentifier __instance)
		{
			if (BossRushController.Laps > 0 && BossRushController.BossRushMode)
			{
				__instance.healthBuff = BossRushConfig.EnemyHealthBuff.Value;
				__instance.speedBuff = BossRushConfig.EnemySpeedBuff.Value;
				__instance.damageBuff = BossRushConfig.EnemyDamageBuff.Value;
				__instance.radianceTier = (float)BossRushController.Laps * BossRushConfig.LapBuffMultiplier.Value;
			}
		}
	}
	[HarmonyPatch(typeof(LevelStatsEnabler), "Start")]
	public static class StatsPatch
	{
		public static void Postfix(LevelStatsEnabler __instance)
		{
			BossRushStats.UK_LevelStatsObject = ((Component)((Component)__instance).transform.GetChild(0)).gameObject;
		}
	}
	[HarmonyPatch(typeof(CanvasController), "Awake")]
	public static class MainMenuUIPatch
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__Open;
		}

		public static void Postfix(CanvasController __instance)
		{
			SpawnDeathScreen(__instance);
			if (SceneHelper.CurrentScene == "Main Menu")
			{
				SpawnMenu(__instance);
				SpawnButton(__instance);
			}
		}

		private static void SpawnMenu(CanvasController __instance)
		{
			RectTransform component = ((Component)__instance).GetComponent<RectTransform>();
			if (!((Object)(object)component == (Object)null))
			{
				BossRush.BepInExLogger.LogInfo((object)"Menu Spawned");
				Object.Instantiate<GameObject>(Assets.BossRushMenuPrefab, (Transform)(object)component);
			}
		}

		private static void SpawnDeathScreen(CanvasController __instance)
		{
			RectTransform component = ((Component)__instance).GetComponent<RectTransform>();
			if (!((Object)(object)component == (Object)null))
			{
				BossRush.BepInExLogger.LogInfo((object)"DeathScreen Spawned");
				Object.Instantiate<GameObject>(Assets.BossRushDeathScreen, (Transform)(object)component);
			}
		}

		private static void SpawnButton(CanvasController __instance)
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: 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_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Expected O, but got Unknown
			BossRush.BepInExLogger.LogInfo((object)"Spawning Button");
			RectTransform component = ((Component)__instance).GetComponent<RectTransform>();
			GameObject gameObject = ((Component)((Transform)component).Find("Chapter Select")).gameObject;
			if ((Object)(object)gameObject == (Object)null)
			{
				Debug.LogError((object)"Chapter Select object is null");
				return;
			}
			BossRushMenu.SetLastPage(gameObject);
			RectTransform component2 = gameObject.GetComponent<RectTransform>();
			GameObject gameObject2 = ((Component)gameObject.transform.Find("Sandbox")).gameObject;
			if ((Object)(object)gameObject2 == (Object)null)
			{
				Debug.LogError((object)"Sandbox button is null");
				return;
			}
			GameObject val = Object.Instantiate<GameObject>(gameObject2, (Transform)(object)component2);
			((Object)val).name = "Boss Rush Button";
			Button component3 = val.GetComponent<Button>();
			ColorBlock colors = ((Selectable)component3).colors;
			Object.DestroyImmediate((Object)(object)component3);
			Button val2 = val.AddComponent<Button>();
			((Selectable)val2).colors = colors;
			RectTransform component4 = val.GetComponent<RectTransform>();
			Vector3 position = ((Transform)component4).position;
			position.y -= 55f;
			((Transform)component4).position = position;
			((TMP_Text)((Component)component4).GetComponentInChildren<TextMeshProUGUI>()).text = "BOSS RUSH";
			ButtonClickedEvent onClick = val2.onClick;
			object obj = <>O.<0>__Open;
			if (obj == null)
			{
				UnityAction val3 = BossRushMenu.Open;
				<>O.<0>__Open = val3;
				obj = (object)val3;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj);
		}
	}
	public static class LevelChainManager
	{
		private static LevelChainTable LevelChainTable { get; set; }

		public static Dictionary<string, string> LevelChainDictionary { get; private set; }

		public static void LoadLevelChainTable()
		{
			string bossRushLevelChain = BossRushPaths.BossRushLevelChain;
			if (!File.Exists(bossRushLevelChain))
			{
				BossRush.BepInExLogger.LogInfo((object)"No level chain file found. Writing default level chain.");
				WriteDefaultLevelChain();
				LevelChainTable = JsonConvert.DeserializeObject<LevelChainTable>(Resources.levelChainDefault);
			}
			else
			{
				string text = File.ReadAllText(bossRushLevelChain);
				try
				{
					LevelChainTable = JsonConvert.DeserializeObject<LevelChainTable>(text);
					if (LevelChainTable == null)
					{
						throw new Exception("Invalid level chain file. Malformed Json?");
					}
					if (LevelChainTable.ModVersion != "1.1.3")
					{
						throw new Exception("Level chain file version mismatch.");
					}
				}
				catch (Exception ex)
				{
					BossRush.BepInExLogger.LogFatal((object)"Error loading LevelChainTable. Details below.");
					BossRush.BepInExLogger.LogFatal((object)(ex.Message + "\n" + ex.StackTrace));
					BossRush.BepInExLogger.LogError((object)"Writing default level chain.");
					WriteDefaultLevelChain();
					LevelChainTable = JsonConvert.DeserializeObject<LevelChainTable>(Resources.levelChainDefault);
				}
			}
			LevelChainDictionary = LevelChainTable.ToDictionary();
		}

		private static void WriteDefaultLevelChain()
		{
			string bossRushLevelChain = BossRushPaths.BossRushLevelChain;
			string levelChainDefault = Resources.levelChainDefault;
			File.WriteAllText(bossRushLevelChain, levelChainDefault);
		}

		public static string GetFirstLevelName()
		{
			return LevelChainTable.LevelChains[0].LevelFrom;
		}

		public static LevelChain GetChainOfLevel(string currentLevel)
		{
			return LevelChainTable.LevelChains.FirstOrDefault((LevelChain x) => x.LevelFrom == currentLevel);
		}
	}
	[Serializable]
	public class LevelChainTable
	{
		public string ModVersion;

		public List<LevelChain> LevelChains;

		public Dictionary<string, string> ToDictionary()
		{
			return LevelChains.ToDictionary((LevelChain x) => x.LevelFrom, (LevelChain x) => x.LevelTo);
		}
	}
	[Serializable]
	public class LevelChain
	{
		public string LevelFrom;

		public string LevelTo;

		public string PitTargetFilter;
	}
	public static class StatRecords
	{
		public delegate void OnNewScoreHandler(BossRushRecord newScore);

		private static RecordLedger ledger;

		public static OnNewScoreHandler OnNewHighScore;

		public static OnNewScoreHandler OnNewScoreSubmitted;

		public static bool HighScoreRun;

		private static bool legderDirty;

		public static void MarkDirty()
		{
			legderDirty = true;
		}

		public static void LoadRecords()
		{
			string bossRushSaveFile = BossRushPaths.BossRushSaveFile;
			if (!File.Exists(bossRushSaveFile))
			{
				BossRush.BepInExLogger.LogInfo((object)"No BossRush Stats found. Creating new file.");
				ledger = new RecordLedger(new BossRushRecord[6]);
				SaveInternal();
				return;
			}
			BossRush.BepInExLogger.LogInfo((object)"Loading records.");
			try
			{
				string text = File.ReadAllText(bossRushSaveFile);
				ledger = JsonConvert.DeserializeObject<RecordLedger>(text);
				if (ledger == null || ledger.Records == null || ledger.Records.Length < 6)
				{
					throw new Exception("Invalid records file. Corrupt?");
				}
				legderDirty = false;
				BossRush.BepInExLogger.LogInfo((object)"Loaded BossRush Stats.");
			}
			catch (Exception ex)
			{
				BossRush.BepInExLogger.LogFatal((object)"Failed to load BossRush Stats.");
				BossRush.BepInExLogger.LogFatal((object)(ex.Message + "\n" + ex.StackTrace));
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(bossRushSaveFile);
				string text2 = ".json";
				int num = 0;
				while (File.Exists(Path.Combine(Path.GetDirectoryName(bossRushSaveFile), fileNameWithoutExtension + "_corrupted" + num + text2)))
				{
					num++;
				}
				string destFileName = Path.Combine(Path.GetDirectoryName(bossRushSaveFile), fileNameWithoutExtension + "_corrupted" + num + text2);
				File.Move(bossRushSaveFile, destFileName);
				BossRush.BepInExLogger.LogError((object)"File renamed to allow for saving records.");
				ledger = new RecordLedger(new BossRushRecord[6]);
				SaveInternal();
			}
		}

		public static BossRushRecord GetRecord(int difficulty = -1)
		{
			if (difficulty < 0)
			{
				difficulty = MonoSingleton<PrefsManager>.Instance.GetInt("difficulty", 0);
			}
			return ledger.Records[difficulty];
		}

		public static void SubmitRecord(BossRushRecord newRecord, int difficulty = -1)
		{
			if (difficulty < 0)
			{
				difficulty = MonoSingleton<PrefsManager>.Instance.GetInt("difficulty", 0);
			}
			BossRush.BepInExLogger.LogInfo((object)"Submitting new record.");
			BossRushRecord record = GetRecord(difficulty);
			ManualLogSource bepInExLogger = BossRush.BepInExLogger;
			BossRushRecord bossRushRecord = record;
			bepInExLogger.LogInfo((object)("Old Record " + bossRushRecord.ToString()));
			ManualLogSource bepInExLogger2 = BossRush.BepInExLogger;
			bossRushRecord = newRecord;
			bepInExLogger2.LogInfo((object)("New Record " + bossRushRecord.ToString()));
			if (!newRecord.IsBetterThan(record))
			{
				BossRush.BepInExLogger.LogInfo((object)"Record not sufficient for replacement.");
				return;
			}
			if (!newRecord.ValidRecord())
			{
				BossRush.BepInExLogger.LogInfo((object)"Record invalid, discarding.");
				return;
			}
			ledger.Records[difficulty] = newRecord;
			HighScoreRun = true;
			MarkDirty();
			Save();
			OnNewHighScore?.Invoke(newRecord);
		}

		private static void SaveInternal()
		{
			string bossRushSaveFile = BossRushPaths.BossRushSaveFile;
			if (!Directory.Exists(Path.GetDirectoryName(bossRushSaveFile)))
			{
				BossRush.BepInExLogger.LogInfo((object)"Creating BossRush Save Directory.");
				Directory.CreateDirectory(Path.GetDirectoryName(bossRushSaveFile));
			}
			try
			{
				string contents = JsonConvert.SerializeObject((object)ledger);
				File.WriteAllText(bossRushSaveFile, contents);
				BossRush.BepInExLogger.LogInfo((object)"Saved BossRush Stats.");
				legderDirty = false;
			}
			catch (Exception ex)
			{
				BossRush.BepInExLogger.LogError((object)"Failed to save BossRush Stats.");
				BossRush.BepInExLogger.LogFatal((object)(ex.Message + "\n" + ex.StackTrace));
			}
		}

		public static void Save()
		{
			if (legderDirty)
			{
				SaveInternal();
			}
		}
	}
	[Serializable]
	public class RecordLedger
	{
		public BossRushRecord[] Records;

		public RecordLedger()
		{
			Records = new BossRushRecord[6];
		}

		public RecordLedger(BossRushRecord[] records)
		{
			Records = records;
		}
	}
	[Serializable]
	public struct BossRushRecord
	{
		public int laps;

		public float time;

		public int deaths;

		public bool hardcore;

		public BossRushRecord(int laps, float time, int deaths, bool hardcore)
		{
			this.laps = 0;
			this.time = 0f;
			this.deaths = 0;
			this.laps = laps;
			this.time = time;
			this.deaths = deaths;
			this.hardcore = hardcore;
		}

		public BossRushRecord(BossRushRecord record)
		{
			laps = 0;
			time = 0f;
			deaths = 0;
			laps = record.laps;
			time = record.time;
			deaths = record.deaths;
			hardcore = record.hardcore;
		}

		public bool IsBetterThan(BossRushRecord record)
		{
			if (laps > record.laps)
			{
				return true;
			}
			if (hardcore && !record.hardcore)
			{
				return true;
			}
			if (time < record.time && record.ValidRecord())
			{
				return true;
			}
			if (time == record.time && deaths < record.deaths)
			{
				return true;
			}
			return false;
		}

		public bool ValidRecord()
		{
			if (laps <= 0)
			{
				return false;
			}
			if (time <= 0f)
			{
				return false;
			}
			if (deaths < 0)
			{
				return false;
			}
			if (deaths > 0 && hardcore)
			{
				return false;
			}
			return true;
		}

		public override string ToString()
		{
			return $"Laps: {laps} Time: {time} Deaths: {deaths} HC: {hardcore}";
		}
	}
	public static class VersionCheck
	{
		public class CoroutineDummy : MonoBehaviour
		{
		}

		private static MonoBehaviour _behaviour;

		private static MonoBehaviour behaviour
		{
			get
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_behaviour == (Object)null)
				{
					_behaviour = (MonoBehaviour)(object)new GameObject("VersionChecker").AddComponent<CoroutineDummy>();
				}
				return _behaviour;
			}
		}

		public static void CheckVersion(string githubURL, string runningVersionName, Action<bool, string> onCheckComplete = null)
		{
			behaviour.StartCoroutine(CheckLatestVersion(githubURL, runningVersionName, onCheckComplete));
		}

		private static IEnumerator CheckLatestVersion(string githubURL, string runningVersionName, Action<bool, string> onCheckComplete = null)
		{
			bool usingLatest = true;
			string latestVersionName = "UNKNOWN";
			UnityWebRequest webRequest = UnityWebRequest.Get(githubURL);
			try
			{
				yield return webRequest.SendWebRequest();
				if (!webRequest.isNetworkError)
				{
					string page = webRequest.downloadHandler.text;
					try
					{
						latestVersionName = JArray.Parse(page)[0].Value<string>((object)"name");
						usingLatest = latestVersionName == runningVersionName;
					}
					catch (Exception ex)
					{
						Exception e = ex;
						usingLatest = true;
						latestVersionName = runningVersionName;
						Debug.LogError((object)$"Error getting version info for {runningVersionName}. {e}");
					}
				}
			}
			finally
			{
				((IDisposable)webRequest)?.Dispose();
			}
			onCheckComplete(usingLatest, latestVersionName);
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "BossRush";

		public const string PLUGIN_NAME = "BossRush";

		public const string PLUGIN_VERSION = "1.1.3";
	}
}
namespace BossRush.UI
{
	public class BossRushDeathScreen : MonoBehaviour
	{
		[SerializeField]
		private GameObject screen;

		[SerializeField]
		private GameObject buttons;

		[SerializeField]
		private GameObject highScoreFlair;

		[SerializeField]
		private Text lapText;

		[SerializeField]
		private Text timeText;

		[SerializeField]
		private Text deathText;

		private bool open;

		private bool finished;

		private bool skip;

		private bool canSkip;

		public static BossRushDeathScreen Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
			Reset();
		}

		public void Update()
		{
			if (open)
			{
				if (Input.anyKeyDown && canSkip && !finished)
				{
					skip = true;
				}
				if (finished && Input.GetKeyDown((KeyCode)27))
				{
					Quit();
				}
			}
		}

		private IEnumerator DisplayStats()
		{
			BossRushRecord current = StatRecords.GetRecord();
			lapText.text = "";
			timeText.text = "";
			deathText.text = "";
			int currentLaps = 0;
			float lapTime = 1.5f / (float)((current.laps == 0) ? 1 : current.laps);
			for (; currentLaps < current.laps + 1; currentLaps++)
			{
				if (skip)
				{
					break;
				}
				lapText.text = currentLaps.ToString("000");
				yield return (object)new WaitForSecondsRealtime(lapTime);
			}
			canSkip = true;
			lapText.text = current.laps.ToString("000");
			float timer = 1.5f;
			while (timer > 0f && !skip)
			{
				float time = Mathf.Lerp(0f, current.time, 1f - timer / 1.5f);
				timer -= 0.016f;
				yield return (object)new WaitForSecondsRealtime(0.016f);
				timeText.text = StringHelper.GetTimeString(time);
			}
			timeText.text = StringHelper.GetTimeString(current.time);
			int currentDeaths = 0;
			float deathTime = 1.5f / (float)((current.deaths == 0) ? 1 : current.deaths);
			for (; currentDeaths < current.deaths; currentDeaths++)
			{
				if (skip)
				{
					break;
				}
				deathText.text = currentDeaths.ToString("000");
				yield return (object)new WaitForSecondsRealtime(deathTime);
			}
			deathText.text = current.deaths.ToString("000");
			if (!skip)
			{
				yield return (object)new WaitForSecondsRealtime(0.5f);
			}
			highScoreFlair.SetActive(StatRecords.HighScoreRun);
			if (!skip)
			{
				yield return (object)new WaitForSecondsRealtime(0.5f);
			}
			finished = true;
			buttons.SetActive(true);
		}

		public void Retry()
		{
			Time.timeScale = 1f;
			Reset();
			BossRushController.StartBossRushMode(hardcore: true);
		}

		public void Quit()
		{
			Reset();
			MonoSingleton<OptionsManager>.Instance.QuitMission();
		}

		public void Open()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0020: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			if (!open)
			{
				GameState val = new GameState("br_deathscreen", screen);
				val.cursorLock = (LockMode)2;
				val.playerInputLock = (LockMode)1;
				val.cameraInputLock = (LockMode)1;
				val.priority = 10;
				GameStateManager.Instance.RegisterState(val);
				Time.timeScale = 0f;
				open = true;
				GameObject obj = screen;
				if (obj != null)
				{
					obj.SetActive(true);
				}
				((MonoBehaviour)this).StartCoroutine(DisplayStats());
			}
		}

		private void Reset()
		{
			if (open)
			{
				GameStateManager.Instance.PopState("br_deathscreen");
			}
			open = false;
			finished = false;
			skip = false;
			buttons.SetActive(false);
			screen.SetActive(false);
			highScoreFlair.SetActive(false);
		}
	}
	public class BossRushMenu : MonoBehaviour
	{
		public static BossRushMenu Instance;

		[SerializeField]
		private GameObject menuScreen;

		[SerializeField]
		private Text hardcoreModeText;

		private bool hardcoreMode;

		private static GameObject lastPage;

		private MenuEsc menuEsc;

		private bool onViolent;

		private void Awake()
		{
			Instance = this;
			GameObject obj = menuScreen;
			menuEsc = ((obj != null) ? obj.AddComponent<MenuEsc>() : null);
			hardcoreMode = BossRushController.HardcoreMode;
			GameObject obj2 = menuScreen;
			if (obj2 != null)
			{
				obj2.SetActive(false);
			}
		}

		public void StartBossRush()
		{
			BossRushController.StartBossRushMode(hardcoreMode);
		}

		public void ToggleHardcoreMode()
		{
			hardcoreMode = !hardcoreMode;
			UpdateHardcoreText();
		}

		public static void SetLastPage(GameObject chapter)
		{
			lastPage = chapter;
		}

		public void OpenMenu()
		{
			GameObject obj = lastPage;
			if (obj != null)
			{
				obj.SetActive(false);
			}
			Refresh();
			GameObject obj2 = menuScreen;
			if (obj2 != null)
			{
				obj2.SetActive(true);
			}
		}

		public static void Open()
		{
			if ((Object)(object)Instance == (Object)null)
			{
				Debug.LogError((object)"BR: Menu is null.");
			}
			else
			{
				Instance.OpenMenu();
			}
		}

		public void Refresh()
		{
			if ((Object)(object)lastPage != (Object)null)
			{
				menuEsc.previousPage = lastPage;
			}
			UpdateHardcoreText();
		}

		private void UpdateHardcoreText()
		{
			//IL_003c: 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)
			if (!((Object)(object)hardcoreModeText == (Object)null))
			{
				((Graphic)hardcoreModeText).color = (Color)(hardcoreMode ? Color.red : new Color(0.3f, 0f, 0f, 1f));
			}
		}
	}
	public class BossRushStats : MonoBehaviour
	{
		[SerializeField]
		private Text timeText;

		[SerializeField]
		private Text lapsText;

		[SerializeField]
		private Text modeText;

		[SerializeField]
		private Text deathText;

		[SerializeField]
		private Transform container;

		[SerializeField]
		private Transform deathCounter;

		[SerializeField]
		private Transform modeDisplay;

		[SerializeField]
		private GameObject newScoreFlash;

		internal static GameObject UK_LevelStatsObject;

		private bool flashing = false;

		private void Start()
		{
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			StatRecords.OnNewHighScore = (StatRecords.OnNewScoreHandler)Delegate.Combine(StatRecords.OnNewHighScore, (StatRecords.OnNewScoreHandler)delegate
			{
				FlashNewScore();
			});
			newScoreFlash.SetActive(false);
		}

		private void Update()
		{
			bool flag = CheckOpenState();
			Transform obj = container;
			if (obj != null)
			{
				GameObject gameObject = ((Component)obj).gameObject;
				if (gameObject != null)
				{
					gameObject.SetActive(flag);
				}
			}
			if (flag)
			{
				UpdateLapText();
				UpdateTimeText();
				UpdateThirdStat();
			}
		}

		private void FlashNewScore()
		{
			if (!flashing)
			{
				flashing = true;
				((MonoBehaviour)this).StartCoroutine(FlashNewHighScoreText());
			}
		}

		private IEnumerator FlashNewHighScoreText()
		{
			newScoreFlash.SetActive(true);
			int flashTimes = 15;
			while (flashTimes > 0)
			{
				newScoreFlash.SetActive(true);
				yield return (object)new WaitForSecondsRealtime(0.25f);
				newScoreFlash.SetActive(false);
				yield return (object)new WaitForSecondsRealtime(0.25f);
				int num = flashTimes - 1;
				flashTimes = num;
			}
			newScoreFlash.SetActive(false);
			flashing = false;
		}

		private bool CheckOpenState()
		{
			if ((Object)(object)UK_LevelStatsObject == (Object)null)
			{
				return false;
			}
			if (!BossRushController.BossRushMode)
			{
				return false;
			}
			if (BossRushConfig.AlwaysShowStats.Value)
			{
				return true;
			}
			return UK_LevelStatsObject.gameObject.activeInHierarchy;
		}

		private void UpdateTimeText()
		{
			if (!((Object)(object)timeText == (Object)null))
			{
				timeText.text = StringHelper.GetTimeString(BossRushController.TimeElapsed);
			}
		}

		private void UpdateLapText()
		{
			if (!((Object)(object)lapsText == (Object)null))
			{
				lapsText.text = BossRushController.Laps.ToString("000");
			}
		}

		private void UpdateThirdStat()
		{
			bool hardcoreMode = BossRushController.HardcoreMode;
			Transform obj = deathCounter;
			if (obj != null)
			{
				GameObject gameObject = ((Component)obj).gameObject;
				if (gameObject != null)
				{
					gameObject.SetActive(!hardcoreMode);
				}
			}
			Transform obj2 = modeDisplay;
			if (obj2 != null)
			{
				GameObject gameObject2 = ((Component)obj2).gameObject;
				if (gameObject2 != null)
				{
					gameObject2.SetActive(hardcoreMode);
				}
			}
			if (hardcoreMode)
			{
				if (!((Object)(object)modeText == (Object)null))
				{
					modeText.text = ((!hardcoreMode) ? "<color=orange>STANDARD</color>" : "<color=red>HARDCORE</color>");
				}
			}
			else if (!((Object)(object)deathText == (Object)null))
			{
				deathText.text = BossRushController.Deaths.ToString("000");
			}
		}

		public static void Spawn()
		{
			if (!((Object)(object)Assets.BossRushStatsPrefab == (Object)null))
			{
				Object.Instantiate<GameObject>(Assets.BossRushStatsPrefab);
			}
		}
	}
	public class BossRushVersionText : MonoBehaviour
	{
		private Text text;

		private void Awake()
		{
			text = ((Component)this).GetComponent<Text>();
		}

		private void OnEnable()
		{
			text.text = "BossRush - 1.1.3 - " + (BossRush.LatestVersion ? "(<color=green>Latest</color>)" : "(<color=red>Update Available</color>)");
		}
	}
	public class HighScoreBox : MonoBehaviour
	{
		[SerializeField]
		private Text lapText;

		[SerializeField]
		private Text timeText;

		[SerializeField]
		private Text deathsText;

		[SerializeField]
		private GameObject hardcoreLabel;

		[SerializeField]
		private GameObject subcontainer;

		private void Refresh()
		{
			int @int = MonoSingleton<PrefsManager>.Instance.GetInt("difficulty", 0);
			BossRushRecord record = StatRecords.GetRecord(@int);
			if (!record.ValidRecord())
			{
				lapText.text = "";
				timeText.text = "";
				deathsText.text = "";
				hardcoreLabel.SetActive(false);
				subcontainer.SetActive(false);
			}
			else
			{
				lapText.text = record.laps.ToString("000");
				timeText.text = StringHelper.GetTimeString(record.time);
				deathsText.text = record.deaths.ToString("000");
				((Component)deathsText).gameObject.SetActive(!record.hardcore);
				hardcoreLabel.SetActive(record.hardcore);
				subcontainer.SetActive(true);
			}
		}

		private void OnEnable()
		{
			Refresh();
		}
	}
	public static class StringHelper
	{
		public static string GetTimeString(float timeInSeconds)
		{
			int num = (int)Modulate(ref timeInSeconds, 3600f);
			int num2 = (int)Modulate(ref timeInSeconds, 60f);
			float num3 = timeInSeconds;
			return num.ToString("00") + ":" + num2.ToString("00") + ":" + num3.ToString("00.00");
		}

		public static float Modulate(ref float number, float modulationAmount)
		{
			float num = number % modulationAmount;
			float num2 = (number - num) / modulationAmount;
			number -= num2 * modulationAmount;
			return num2;
		}

		public static void DebugNull(string name, object obj)
		{
			Debug.Log((object)(name + " " + ((obj == null) ? "NULL" : "NOT NULL")));
		}
	}
}
namespace BossRush.Properties
{
	[GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
	[DebuggerNonUserCode]
	[CompilerGenerated]
	internal class Resources
	{
		private static ResourceManager resourceMan;

		private static CultureInfo resourceCulture;

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

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

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

		internal static string levelChainDefault => ResourceManager.GetString("levelChainDefault", resourceCulture);

		internal Resources()
		{
		}
	}
}