Decompiled source of SpeedrunUtilsV2 v1.1.0
plugins/SpeedrunUtilsV2/SpeedrunUtilsV2.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Reptile; using Reptile.Phone; using SpeedrunUtilsV2.Patches; using SpeedrunUtilsV2.ProgressTracker; using SpeedrunUtilsV2.UI; using UnityEngine; using UnityEngine.Playables; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("SpeedrunUtilsV2")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("SpeedrunUtilsV2")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("95a5680f-233b-41e1-9085-fb74f62592fa")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace SpeedrunUtilsV2 { internal static class ConnectionManager { private enum Commands { GetCurrentGameTime, SetGameTime, PauseGameTime, UnpauseGameTime, Reset, StartTimer, Split } private const int BUFFER = 1024; private const string TimeFormat = "hh\\:mm\\:ss\\.fffffff"; private static NetworkStream Stream; internal static bool StartingNewGame; internal static bool IsConnected { get { if (Stream != null) { return Plugin.liveSplitManager.ConnectionStatus == LiveSplitManager.Status.Connected; } return false; } } internal static void SetStream(NetworkStream stream) { Stream = stream; } private static string GetCommand(Commands command, string parameter) { if (parameter == null) { return command.ToString().ToLower(); } return command.ToString().ToLower() + " " + parameter; } private static async Task<(int, byte[])> SendAndReceiveResponse(Commands command, string parameter = null) { return await SendAndReceiveResponse(GetCommand(command, parameter)); } private static async Task<bool> SendWithNoResponse(Commands command, string parameter = null) { return await SendWithNoResponse(GetCommand(command, parameter)); } private static async Task<(int, byte[])> SendAndReceiveResponse(string data) { if (!(await WriteToStream(Encoding.UTF8.GetBytes(data + "\r\n")))) { return default((int, byte[])); } return await ReadCurrentStream(); } private static async Task<bool> SendWithNoResponse(string data) { return await WriteToStream(Encoding.UTF8.GetBytes(data + "\r\n")); } private static async Task<bool> WriteToStream(byte[] bytes, bool force = false) { if (IsConnected) { try { await Stream.WriteAsync(bytes, 0, bytes.Length); await Stream.FlushAsync(); return true; } catch { return false; } } return false; } private static async Task<(int, byte[])> ReadCurrentStream() { try { if (IsConnected && Stream.DataAvailable) { byte[] responseBuffer = new byte[1024]; return (await Stream.ReadAsync(responseBuffer, 0, responseBuffer.Length), responseBuffer); } } catch { return default((int, byte[])); } return default((int, byte[])); } internal static async void StartAddingCutsceneTime(TimeSpan time) { if (IsConnected) { await AddCutsceneTime(time); } } private static async Task AddCutsceneTime(TimeSpan time) { Stopwatch responseTimer = Stopwatch.StartNew(); (int, byte[]) tuple = await SendAndReceiveResponse(Commands.GetCurrentGameTime); responseTimer.Stop(); var (num, array) = tuple; if ((num != 0 || array != null) && TimeSpan.TryParse(Encoding.ASCII.GetString(tuple.Item2, 0, tuple.Item1).Trim(), out var result)) { await SendWithNoResponse(Commands.SetGameTime, (result + time - responseTimer.Elapsed).ToString("hh\\:mm\\:ss\\.fffffff")); } } internal static async void StartPausingTimer() { if (IsConnected) { await PauseTimer(); } } private static async Task PauseTimer() { await SendWithNoResponse(Commands.PauseGameTime); } internal static async void StartUnpausingTimer() { bool newGame = StartingNewGame; StartingNewGame = false; if (IsConnected) { await UnpauseTimer(); if (newGame) { await StartNewGame(); } } } private static async Task UnpauseTimer() { await SendWithNoResponse(Commands.UnpauseGameTime); } private static async Task StartNewGame() { if (IsConnected) { await NewGame(); } } private static async Task NewGame() { await SendWithNoResponse(Commands.Reset); await SendWithNoResponse(Commands.StartTimer); } internal static async void StartSplit(LiveSplitConfig.Splits split) { if (IsConnected) { await Split(); } } private static async Task Split() { await SendWithNoResponse(Commands.Split); } internal static async Task<TimeSpan> StartGettingGameTime() { if (IsConnected) { return await GetGameTimeFull(); } return default(TimeSpan); } private static async Task<TimeSpan> GetGameTimeFull() { (int, byte[]) tuple = await SendAndReceiveResponse(Commands.GetCurrentGameTime); var (num, array) = tuple; if (num == 0 && array == null) { return default(TimeSpan); } if (TimeSpan.TryParse(Encoding.ASCII.GetString(tuple.Item2, 0, tuple.Item1).Trim(), out var result)) { return result; } return default(TimeSpan); } } internal class CutsceneTimer { private class TimerInfo { internal TimeSpan StartingTime; internal TimeSpan EndingTime; internal TimeSpan TotalDelay; internal string name; internal int playable; internal TimerInfo(TimeSpan startingTime, string name, int playable) { StartingTime = startingTime; this.name = name; this.playable = playable; } } private TimerInfo ActiveTimer; internal async void StartTimer(string name, int playable) { Debug.Log((object)"Starting Cutscene Timer..."); Debug.Log((object)$"-- {name} | {playable} --"); Stopwatch responseTimer = Stopwatch.StartNew(); ActiveTimer = new TimerInfo(await ConnectionManager.StartGettingGameTime(), name, playable); responseTimer.Stop(); ActiveTimer.TotalDelay = responseTimer.Elapsed; } internal async void StopTimer() { if (ActiveTimer != null) { Stopwatch responseTimer = Stopwatch.StartNew(); TimerInfo activeTimer = ActiveTimer; activeTimer.EndingTime = await ConnectionManager.StartGettingGameTime(); responseTimer.Stop(); ActiveTimer.TotalDelay += responseTimer.Elapsed; Debug.Log((object)$"{ActiveTimer.name} | {ActiveTimer.playable}"); Debug.Log((object)"----------------------------"); Debug.Log((object)$"Final Time: {ActiveTimer.EndingTime - ActiveTimer.StartingTime - ActiveTimer.TotalDelay}"); Debug.Log((object)$"Total Delay: {ActiveTimer.TotalDelay} (Removed from Final Time)"); Debug.Log((object)"----------------------------"); } } } internal static class GameStatus { private static ObjectiveID _currentStoryObjective = (ObjectiveID)(-1); private static ObjectiveID PreviousStoryObjective = (ObjectiveID)(-1); private static Stage _currentStage = (Stage)(-1); private static Stage PreviousStage = (Stage)(-1); internal static ObjectiveID CurrentStoryObjective { get { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return _currentStoryObjective; } set { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_000d: Unknown result type (might be due to invalid IL or missing references) if (value != _currentStoryObjective) { PreviousStoryObjective = _currentStoryObjective; } _currentStoryObjective = value; } } internal static Stage CurrentStage { get { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return _currentStage; } set { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_000d: Unknown result type (might be due to invalid IL or missing references) if (value != _currentStage) { PreviousStage = _currentStage; } _currentStage = value; } } internal static void SetupNewGame(SaveSlotData saveSlotData) { LiveSplitConfig.RefreshSplitsFile(); LiveSplitConfig.SetDefaultSplitStates(); Tracking.CreateProgressData(saveSlotData.saveSlotId); if (ConnectionManager.IsConnected) { ConnectionManager.StartingNewGame = true; } } internal static void UpdateStage() { //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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) Core instance = Core.Instance; object obj; if (instance == null) { obj = null; } else { SaveManager saveManager = instance.SaveManager; obj = ((saveManager != null) ? saveManager.CurrentSaveSlot : null); } SaveSlotData val = (SaveSlotData)obj; Stage currentStage = Utility.GetCurrentStage(); if (val != null && (int)currentStage != -1) { CurrentStoryObjective = val.CurrentStoryObjective; CurrentStage = currentStage; CheckSplitsForStageChange(CurrentStage, PreviousStage); } } internal static void UpdateObjective(ObjectiveID objectiveID) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if ((int)objectiveID != -1) { CurrentStoryObjective = objectiveID; CheckSplitsForObjectiveChange(CurrentStoryObjective, PreviousStoryObjective); } } internal static void FinalBossDead() { ShouldSplit(LiveSplitConfig.Splits.FinalBossDefeated); } private static void CheckSplitsForStageChange(Stage currentStage, Stage previousStage) { //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_0029: 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_002e: Invalid comparison between Unknown and I4 //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Invalid comparison between Unknown and I4 //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Invalid comparison between Unknown and I4 //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Invalid comparison between Unknown and I4 //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Invalid comparison between Unknown and I4 //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Invalid comparison between Unknown and I4 //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Invalid comparison between Unknown and I4 //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Invalid comparison between Unknown and I4 //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Invalid comparison between Unknown and I4 //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Invalid comparison between Unknown and I4 //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Invalid comparison between Unknown and I4 //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Invalid comparison between Unknown and I4 //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Invalid comparison between Unknown and I4 //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Invalid comparison between Unknown and I4 //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Invalid comparison between Unknown and I4 //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Invalid comparison between Unknown and I4 //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Invalid comparison between Unknown and I4 //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Invalid comparison between Unknown and I4 //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Invalid comparison between Unknown and I4 //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Invalid comparison between Unknown and I4 //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Invalid comparison between Unknown and I4 //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Invalid comparison between Unknown and I4 //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Invalid comparison between Unknown and I4 //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Invalid comparison between Unknown and I4 //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Invalid comparison between Unknown and I4 //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Invalid comparison between Unknown and I4 //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Invalid comparison between Unknown and I4 //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Invalid comparison between Unknown and I4 //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Invalid comparison between Unknown and I4 //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Invalid comparison between Unknown and I4 //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Invalid comparison between Unknown and I4 //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Invalid comparison between Unknown and I4 Core instance = Core.Instance; object obj; if (instance == null) { obj = null; } else { SaveManager saveManager = instance.SaveManager; obj = ((saveManager != null) ? saveManager.CurrentSaveSlot : null); } SaveSlotData val = (SaveSlotData)obj; if (val != null) { ObjectiveID currentStoryObjective = val.CurrentStoryObjective; bool flag = (int)currentStoryObjective == 0 || (int)currentStoryObjective == 1; bool flag2 = (int)currentStoryObjective == 15 || (int)currentStoryObjective == 16 || (int)currentStoryObjective == 17 || (int)currentStoryObjective == 18 || (int)currentStoryObjective == 9; if ((int)previousStage == 8 && (int)currentStage == 5 && flag) { ShouldSplit(LiveSplitConfig.Splits.PrologueEnd); } else if ((int)previousStage == 7 && (int)currentStage == 11 && flag) { ShouldSplit(LiveSplitConfig.Splits.EarlySquare); } else if ((((int)previousStage == 11 && (int)currentStage == 4) || ((int)previousStage == 5 && (int)currentStage == 4)) && (flag || (int)currentStoryObjective == 2)) { ShouldSplit(LiveSplitConfig.Splits.VersumStart); } else if ((int)previousStage == 4 && (int)currentStage == 5 && (int)currentStoryObjective == 4) { ShouldSplit(LiveSplitConfig.Splits.Chapter1End); } else if ((int)previousStage == 11 && (int)currentStage == 12 && (int)currentStoryObjective == 5) { ShouldSplit(LiveSplitConfig.Splits.BrinkStart); } else if ((int)previousStage == 12 && (int)currentStage == 5 && (int)currentStoryObjective == 7) { ShouldSplit(LiveSplitConfig.Splits.Chapter2End); } else if ((int)previousStage == 11 && (int)currentStage == 6 && (int)currentStoryObjective == 7) { ShouldSplit(LiveSplitConfig.Splits.MallStart); } else if ((int)previousStage == 6 && (int)currentStage == 5 && (int)currentStoryObjective == 15) { ShouldSplit(LiveSplitConfig.Splits.Chapter3End); } else if ((int)previousStage == 11 && (int)currentStage == 9 && flag2) { ShouldSplit(LiveSplitConfig.Splits.PyramidStart); } else if ((int)previousStage == 9 && (int)currentStage == 5 && (int)currentStoryObjective == 11) { ShouldSplit(LiveSplitConfig.Splits.Chapter4End); } } } private static void CheckSplitsForObjectiveChange(ObjectiveID currentObjective, ObjectiveID previousObjective) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected I4, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Invalid comparison between Unknown and I4 //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Invalid comparison between Unknown and I4 //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Invalid comparison between Unknown and I4 if ((int)currentObjective != 3) { switch (currentObjective - 6) { case 0: ShouldSplit(LiveSplitConfig.Splits.Dream2Start); break; case 2: ShouldSplit(LiveSplitConfig.Splits.Dream3Start); break; case 4: ShouldSplit(LiveSplitConfig.Splits.Dream4Start); break; case 6: ShouldSplit(LiveSplitConfig.Splits.Dream5Start); break; case 10: if ((int)previousObjective == 15) { ShouldSplit(LiveSplitConfig.Splits.PrinceVersumEnd); } break; case 11: if ((int)previousObjective == 16) { ShouldSplit(LiveSplitConfig.Splits.PrinceSquareEnd); } break; case 12: if ((int)previousObjective == 17) { ShouldSplit(LiveSplitConfig.Splits.PrinceBrinkEnd); } break; case 1: case 3: case 5: case 7: case 8: case 9: break; } } else { ShouldSplit(LiveSplitConfig.Splits.Dream1Start); } } internal static void ShouldSplit(LiveSplitConfig.Splits split, bool ignoreHasBeenSplit = false) { if (LiveSplitConfig.SETTINGS_DebugMode.Item2) { Debug.LogWarning((object)$"Trying to split: {split}"); } if (ConnectionManager.IsConnected) { LiveSplitConfig.RefreshSplitsFile(); if (LiveSplitConfig.CurrentSplits.TryGetValue(split, out var value) && value.Item1 && (ignoreHasBeenSplit || (LiveSplitConfig.CurrentSplitStates.TryGetValue(split, out var value2) && !value2))) { LiveSplitConfig.CurrentSplitStates[split] = true; ConnectionManager.StartSplit(split); } } } } internal static class LiveSplitConfig { internal enum Splits { PrologueEnd, EarlySquare, VersumStart, Dream1Start, Chapter1End, BrinkStart, Dream2Start, Chapter2End, MallStart, Dream3Start, Chapter3End, PrinceVersumEnd, PrinceSquareEnd, PrinceBrinkEnd, PyramidStart, Dream4Start, Chapter4End, Dream5Start, UnlockOldHead, CharacterUnlock, FinalBossDefeated } internal enum Actions { OpenGUI, LimitFPS, UncapFPS, OpenTracker } internal static readonly Dictionary<Splits, (bool, bool, string)> CurrentSplits = new Dictionary<Splits, (bool, bool, string)> { { Splits.PrologueEnd, (false, true, Localization.Splits.s_PrologueEnd ?? "") }, { Splits.EarlySquare, (false, true, Localization.Splits.s_EarlySquare ?? "") }, { Splits.VersumStart, (false, true, Localization.Splits.s_VersumStart ?? "") }, { Splits.Dream1Start, (false, true, Localization.Splits.s_Dream1Start ?? "") }, { Splits.Chapter1End, (false, true, Localization.Splits.s_Chapter1End ?? "") }, { Splits.BrinkStart, (false, true, Localization.Splits.s_BrinkStart ?? "") }, { Splits.Dream2Start, (false, true, Localization.Splits.s_Dream2Start ?? "") }, { Splits.Chapter2End, (false, true, Localization.Splits.s_Chapter2End ?? "") }, { Splits.MallStart, (false, true, Localization.Splits.s_MallStart ?? "") }, { Splits.Dream3Start, (false, true, Localization.Splits.s_Dream3Start ?? "") }, { Splits.Chapter3End, (false, true, Localization.Splits.s_Chapter3End ?? "") }, { Splits.PrinceVersumEnd, (false, false, Localization.Splits.s_PrinceVersumEnd ?? "") }, { Splits.PrinceSquareEnd, (false, false, Localization.Splits.s_PrinceSquareEnd ?? "") }, { Splits.PrinceBrinkEnd, (false, false, Localization.Splits.s_PrinceBrinkEnd ?? "") }, { Splits.PyramidStart, (false, true, Localization.Splits.s_PyramidStart ?? "") }, { Splits.Dream4Start, (false, true, Localization.Splits.s_Dream4Start ?? "") }, { Splits.Chapter4End, (false, true, Localization.Splits.s_Chapter4End ?? "") }, { Splits.Dream5Start, (false, false, Localization.Splits.s_Dream5Start ?? "") }, { Splits.UnlockOldHead, (false, false, Localization.Splits.s_UnlockOldHead ?? "") }, { Splits.CharacterUnlock, (false, false, Localization.Splits.s_CharacterUnlock ?? "") }, { Splits.FinalBossDefeated, (false, true, Localization.Splits.s_FinalBossDefeated ?? "") } }; internal static readonly Dictionary<Splits, bool> CurrentSplitStates = new Dictionary<Splits, bool>(); internal static readonly Dictionary<Actions, KeyCode> ActionKeys = new Dictionary<Actions, KeyCode> { { Actions.OpenGUI, (KeyCode)283 }, { Actions.LimitFPS, (KeyCode)108 }, { Actions.UncapFPS, (KeyCode)111 }, { Actions.OpenTracker, (KeyCode)284 } }; internal static readonly Dictionary<Actions, KeyCode> ActionKeysDefault = new Dictionary<Actions, KeyCode> { { Actions.OpenGUI, (KeyCode)283 }, { Actions.LimitFPS, (KeyCode)108 }, { Actions.UncapFPS, (KeyCode)111 }, { Actions.OpenTracker, (KeyCode)284 } }; internal static (string, int, int) SETTINGS_DefaultFPS = ("Default FPS", 200, 200); internal static (string, int, int) SETTINGS_LimitValue = ("Limit FPS Value", 30, 30); internal static (string, bool, bool) SETTINGS_Uncap = ("Start Uncapped", false, false); internal static (string, bool, bool) SETTINGS_Tracker = ("Start Tracker Open", false, false); internal static (string, bool, bool) SETTINGS_Tracking = ("Tracking Game Progress", false, false); internal static (string, bool, bool) SETTINGS_Skip = ("Skip Unskippable Cutscenes", true, true); internal static (string, bool, bool) SETTINGS_MashEnabled = ("AutoMash Unskippable Cutscenes", true, true); internal static (string, bool, bool) SETTINGS_ShowFPS = ("Show FPS", true, true); internal static (string, int, int) SETTINGS_FPSSize = ("FPS Size", 33, 33); internal static (string, Vector2, Vector2) SETTINGS_FPSPos = ("FPS Screen Position", new Vector2(8f, 4f), new Vector2(8f, 4f)); internal static (string, bool, bool) SETTINGS_UncapLoading = ("Uncap FPS During Loading", true, true); internal static (string, bool, bool) SETTINGS_MouseFix = ("Enable Fix to Menu Mouse", true, true); internal static (string, bool, bool) SETTINGS_DebugMode = ("Debug Mode", false, false); private const string EXTENSION = ".cfg"; internal const string EXTENSION_SaveData = ".sru"; private const string FOLDER_UtilsFolder = "SpeedrunUtilsV2"; private const string FOLDER_SaveData = "SaveData"; private static readonly string FILE_Splits = "Splits.cfg"; private static readonly string FILE_Keys = "Keys.cfg"; private static readonly string FILE_Settings = "Settings.cfg"; private static readonly string PATH_Config = Paths.ConfigPath; private static readonly string PATH_UtilsConfig = Path.Combine(PATH_Config, "SpeedrunUtilsV2"); private static readonly string PATH_Splits = Path.Combine(PATH_UtilsConfig, FILE_Splits); private static readonly string PATH_Keys = Path.Combine(PATH_UtilsConfig, FILE_Keys); private static readonly string PATH_Settings = Path.Combine(PATH_UtilsConfig, FILE_Settings); internal static readonly string PATH_SaveData = Path.Combine(PATH_UtilsConfig, "SaveData"); private const string SECTION_Splits = "Splits"; private const string SECTION_Keys = "Keys"; private const string SECTION_Settings = "Settings"; private static readonly ConfigFile CONFIG_Splits = new ConfigFile(PATH_Splits, true); private static readonly ConfigFile CONFIG_Keys = new ConfigFile(PATH_Keys, true); private static readonly ConfigFile CONFIG_Settings = new ConfigFile(PATH_Settings, true); internal static void ManageConfigFiles() { CreateSplitsFile(); CreateKeysFile(); CreateSettingsFile(); SetDefaultSplitStates(); } internal static void SetDefaultSplitStates() { CurrentSplitStates.Clear(); foreach (object value in Enum.GetValues(typeof(Splits))) { CurrentSplitStates.Add((Splits)value, value: false); } } internal static void RefreshSplitsFile() { CreateSplitsFile(); } internal static void UpdateSplitsFile(Splits split, bool value) { if (CurrentSplits.ContainsKey(split)) { CurrentSplits[split] = (value, CurrentSplits[split].Item2, CurrentSplits[split].Item3); ConfigEntry<bool> val = default(ConfigEntry<bool>); if (CONFIG_Splits.TryGetEntry<bool>("Splits", CurrentSplits[split].Item3, ref val)) { val.Value = value; } } } internal static void UpdateProgressTrackerState(bool value) { ConfigEntry<bool> val = default(ConfigEntry<bool>); if (CONFIG_Settings.TryGetEntry<bool>("Settings", SETTINGS_Tracker.Item1, ref val)) { val.Value = value; } } internal static void UpdateProgressTrackingState(bool value) { ConfigEntry<bool> val = default(ConfigEntry<bool>); if (CONFIG_Settings.TryGetEntry<bool>("Settings", SETTINGS_Tracking.Item1, ref val)) { val.Value = value; } } private static void CreateSplitsFile() { ConfigEntry<bool> val = default(ConfigEntry<bool>); for (int i = 0; i < CurrentSplits.Count; i++) { Splits key = CurrentSplits.ElementAt(i).Key; if (CurrentSplits.TryGetValue(key, out var value)) { CONFIG_Splits.Bind<bool>("Splits", value.Item3, value.Item2, (ConfigDescription)null); if (CONFIG_Splits.TryGetEntry<bool>("Splits", value.Item3, ref val)) { CurrentSplits[key] = (val.Value, value.Item2, value.Item3); } } } } private static void CreateKeysFile() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<KeyCode> val = default(ConfigEntry<KeyCode>); foreach (Actions value2 in Enum.GetValues(typeof(Actions))) { if (ActionKeysDefault.TryGetValue(value2, out var value)) { string text = Regex.Replace(value2.ToString(), "([a-z])([A-Z])", "$1 $2"); CONFIG_Keys.Bind<KeyCode>("Keys", text, value, (ConfigDescription)null); if (CONFIG_Keys.TryGetEntry<KeyCode>("Keys", text, ref val)) { ActionKeys[value2] = val.Value; } } } } private static void CreateSettingsFile() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) Plugin.SetMaxFPS(BindSetting(ref SETTINGS_DefaultFPS)); BindSetting(ref SETTINGS_LimitValue); BindSetting(ref SETTINGS_Uncap); BindSetting(ref SETTINGS_Tracker); BindSetting(ref SETTINGS_Tracking); BindSetting(ref SETTINGS_Skip); BindSetting(ref SETTINGS_MashEnabled); BindSetting(ref SETTINGS_ShowFPS); BindSetting(ref SETTINGS_FPSSize); BindSetting(ref SETTINGS_FPSPos); BindSetting(ref SETTINGS_UncapLoading); BindSetting(ref SETTINGS_MouseFix, Localization.Descriptions.s_mouseFix ?? ""); BindSetting(ref SETTINGS_DebugMode); } private static T BindSetting<T>(ref (string, T, T) setting, string description = null) { if (description != null) { CONFIG_Settings.Bind<T>("Settings", setting.Item1, setting.Item3, description); } else { CONFIG_Settings.Bind<T>("Settings", setting.Item1, setting.Item3, (ConfigDescription)null); } ConfigEntry<T> val = default(ConfigEntry<T>); if (CONFIG_Settings.TryGetEntry<T>("Settings", setting.Item1, ref val)) { return setting.Item2 = val.Value; } return default(T); } internal static void UpdateFPSSetting(int fps) { SETTINGS_DefaultFPS.Item2 = fps; ConfigEntry<int> val = default(ConfigEntry<int>); if (CONFIG_Settings.TryGetEntry<int>("Settings", SETTINGS_DefaultFPS.Item1, ref val)) { val.Value = fps; } } } internal class LiveSplitManager { internal enum Status { Disconnected, Disconnecting, Connected, Connecting } private const string IP = "127.0.0.1"; private const int PORT = 16834; private const float RefreshRate = 2f; private NetworkStream Stream; private CancellationTokenSource cancelRefresh; private Status _connectionStatus; internal Status ConnectionStatus { get { return _connectionStatus; } private set { //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown if (value != _connectionStatus) { switch (value) { case Status.Connected: Patch_CutsceneSkip.OnSkippedCutscene = (Patch_CutsceneSkip.SkippedCutscene)Delegate.Combine(Patch_CutsceneSkip.OnSkippedCutscene, new Patch_CutsceneSkip.SkippedCutscene(ConnectionManager.StartAddingCutsceneTime)); Patch_Loading.OnEnteredLoading = (Patch_Loading.Loading)Delegate.Combine(Patch_Loading.OnEnteredLoading, new Patch_Loading.Loading(ConnectionManager.StartPausingTimer)); Patch_Loading.OnExitedLoading = (Patch_Loading.Loading)Delegate.Combine(Patch_Loading.OnExitedLoading, new Patch_Loading.Loading(ConnectionManager.StartUnpausingTimer)); StageManager.OnStageInitialized += new OnStageInitializedDelegate(GameStatus.UpdateStage); Patch_Objective.OnObjectiveChanged = (Patch_Objective.ObjectiveChanged)Delegate.Combine(Patch_Objective.OnObjectiveChanged, new Patch_Objective.ObjectiveChanged(GameStatus.UpdateObjective)); break; case Status.Disconnected: Patch_CutsceneSkip.OnSkippedCutscene = (Patch_CutsceneSkip.SkippedCutscene)Delegate.Remove(Patch_CutsceneSkip.OnSkippedCutscene, new Patch_CutsceneSkip.SkippedCutscene(ConnectionManager.StartAddingCutsceneTime)); Patch_Loading.OnEnteredLoading = (Patch_Loading.Loading)Delegate.Remove(Patch_Loading.OnEnteredLoading, new Patch_Loading.Loading(ConnectionManager.StartPausingTimer)); Patch_Loading.OnExitedLoading = (Patch_Loading.Loading)Delegate.Remove(Patch_Loading.OnExitedLoading, new Patch_Loading.Loading(ConnectionManager.StartUnpausingTimer)); StageManager.OnStageInitialized -= new OnStageInitializedDelegate(GameStatus.UpdateStage); Patch_Objective.OnObjectiveChanged = (Patch_Objective.ObjectiveChanged)Delegate.Remove(Patch_Objective.OnObjectiveChanged, new Patch_Objective.ObjectiveChanged(GameStatus.UpdateObjective)); break; } } if (value == Status.Disconnected) { ConnectionManager.SetStream(null); } _connectionStatus = value; } } internal LiveSplitManager() { LiveSplitConfig.ManageConfigFiles(); ConnectToLiveSplit(); } internal async void ConnectToLiveSplit() { if (ConnectionStatus != 0) { return; } ConnectionStatus = Status.Connecting; using (TcpClient client = new TcpClient()) { if (TryConnectToLiveSplit(client)) { using (Stream = client.GetStream()) { ConnectionManager.SetStream(Stream); cancelRefresh = new CancellationTokenSource(); ConnectionStatus = Status.Connected; Debug.Log((object)"Connection to LiveSplit was open!"); while (ConnectionStatus == Status.Connected) { try { await Task.Delay(TimeSpan.FromSeconds(2.0), cancelRefresh.Token); } catch { break; } bool flag = ConnectionStatus != Status.Connected; if (!flag) { flag = !(await TryPingClientAsync(client)); } if (flag) { break; } } } } } ConnectionStatus = Status.Disconnected; Debug.LogWarning((object)"Connection to LiveSplit was closed."); } internal void DisconnectFromLiveSplit() { if (ConnectionStatus == Status.Connected) { ConnectionStatus = Status.Disconnecting; cancelRefresh?.Cancel(); } } private bool TryConnectToLiveSplit(TcpClient client) { try { client?.Connect("127.0.0.1", 16834); return TryPingClient(client); } catch (Exception arg) { Debug.LogError((object)$"Failed to connect to LiveSplit: {arg}"); return false; } } private async Task<bool> TryPingClientAsync(TcpClient client) { return await TryPingClient(client, isAsync: true); } private bool TryPingClient(TcpClient client) { return TryPingClient(client, isAsync: false).GetAwaiter().GetResult(); } private async Task<bool> TryPingClient(TcpClient client, bool isAsync) { if (client == null || client.Client == null) { return false; } try { if (client.Client.Poll(0, SelectMode.SelectRead) && client.Client.Available == 0) { byte[] array = new byte[1]; if (isAsync) { return await SocketTaskExtensions.ReceiveAsync(client.Client, new ArraySegment<byte>(array), SocketFlags.Peek) != 0; } return client.Client.Receive(array, SocketFlags.Peek) != 0; } return true; } catch { return false; } } } internal static class EasyReflection { private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static readonly Dictionary<int, (MemberInfo, object[])> Members = new Dictionary<int, (MemberInfo, object[])>(); private const string EXCEPTION_BadMember = "Type \"{0}\" does not contain matching member \"{1}\" of the given parameters."; private const string EXCEPTION_BadType = "The return type \"{0}\" is not assignable from \"{1}\" in \"{2}\"."; private const string EXCEPTION_BadMemberType = "The member \"{0}\" in \"{1}\" is not a Field type or assignable Property."; private const string EXCEPTION_BadInvokeType = "The member \"{0}\" in \"{1}\" is not a Method type."; internal static T GetValue<T>(this object obj, string member, params object[] memberParams) { MemberInfo member2 = obj.GetMember(member, ref memberParams); switch (member2.MemberType) { case MemberTypes.Method: if (typeof(T).IsAssignableFrom((member2 as MethodInfo).ReturnType)) { return (T)(member2 as MethodInfo).Invoke(obj, memberParams); } break; case MemberTypes.Field: if (typeof(T).IsAssignableFrom((member2 as FieldInfo).FieldType)) { return (T)(member2 as FieldInfo).GetValue(obj); } break; case MemberTypes.Property: if (typeof(T).IsAssignableFrom((member2 as PropertyInfo).PropertyType)) { return (T)(member2 as PropertyInfo).GetValue(obj, null); } break; case MemberTypes.NestedType: if (typeof(T).IsAssignableFrom(typeof(TypeInfo))) { return (T)(object)(member2 as TypeInfo); } break; } throw new Exception($"The return type \"{typeof(T)}\" is not assignable from \"{member}\" in \"{obj.GetType()}\"."); } internal static TypeInfo GetNestedType(this object obj, string member) { return obj.GetValue<TypeInfo>(member, Array.Empty<object>()); } internal static void SetValue(this object obj, string member, object value) { object[] memberParams = Array.Empty<object>(); MemberInfo member2 = obj.GetMember(member, ref memberParams); if (member2.MemberType == MemberTypes.Field) { (member2 as FieldInfo).SetValue(obj, value); return; } if (member2.MemberType == MemberTypes.Property && (member2 as PropertyInfo).SetMethod != null) { (member2 as PropertyInfo).SetValue(obj, value); return; } throw new Exception($"The member \"{member}\" in \"{obj.GetType()}\" is not a Field type or assignable Property."); } internal static void InvokeMethod(this object obj, string member, params object[] memberParams) { MemberInfo member2 = obj.GetMember(member, ref memberParams); if (member2.MemberType == MemberTypes.Method) { (member2 as MethodInfo).Invoke(obj, memberParams); return; } throw new Exception($"The member \"{member}\" in \"{obj.GetType()}\" is not a Method type."); } internal static MemberInfo GetMember(this object obj, string member, params object[] memberParams) { return obj.GetMember(member, ref memberParams); } private static MemberInfo GetMember(this object obj, string member, ref object[] memberParams) { int hashCode = (obj.GetType(), member, HashedObjects(memberParams)).GetHashCode(); if (Members.TryGetValue(hashCode, out var value)) { memberParams = value.Item2; return value.Item1; } MemberInfo memberFinal = obj.GetMemberFinal(member, ref memberParams); if (memberFinal == null) { throw new Exception($"Type \"{obj.GetType()}\" does not contain matching member \"{member}\" of the given parameters."); } Members.Add(hashCode, (memberFinal, memberParams)); return memberFinal; } private static int HashedObjects(object[] memberParams) { int num = 17; for (int i = 0; i < memberParams.Length; i++) { num = num * 31 + (memberParams[i]?.GetHashCode() ?? 0); } return num; } private static MemberInfo GetMemberFinal(this object obj, string member, ref object[] memberParams) { MemberInfo[] member2 = obj.GetType().GetMember(member, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (member2.Length == 0) { return null; } if (member2.Length == 1) { return member2.First(); } int paramLength = memberParams.Length; MethodInfo[] array = member2.Where((MemberInfo x) => x.MemberType == MemberTypes.Method && (x as MethodInfo).GetParameters().Length >= paramLength).Cast<MethodInfo>().ToArray(); if (array.Length == 1) { return array.First(); } List<object> list = new List<object>(); list.AddRange(memberParams); MethodInfo[] array2 = array; foreach (MethodInfo methodInfo in array2) { ParameterInfo[] parameters = methodInfo.GetParameters(); bool flag = true; for (int j = 0; j < parameters.Length; j++) { bool flag2 = memberParams.Length - 1 >= j; if (!flag2 && parameters[j].HasDefaultValue) { list.Add(parameters[j].DefaultValue); } else if (!flag2 || !(memberParams[j].GetType() == parameters[j].ParameterType)) { flag = false; break; } } if (flag) { memberParams = list.ToArray(); return methodInfo; } } return null; } } [BepInPlugin("SpeedrunUtilsV2", "SpeedrunUtilsV2", "1.1.0")] internal class Plugin : BaseUnityPlugin { internal enum FPSStatus { Uncapped, Capped, Limited } private class ScreenInfo { private const float ReferenceResolution = 1080f; internal Vector2Int Resolution { get; private set; } internal Matrix4x4 Matrix { get; private set; } internal ScreenInfo(int width, int height) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) float num = 1080f * ((float)width / (float)height); float num2 = Mathf.Min((float)width / num, (float)height / 1080f); float num3 = ((float)width - num * num2) * 0.5f; float num4 = ((float)height - 1080f * num2) * 0.5f; Resolution = new Vector2Int((int)num, 1080); Matrix = Matrix4x4.TRS(new Vector3(num3, num4, 0f), Quaternion.identity, new Vector3(num2, num2, 1f)); } } private const string pluginGuid = "SpeedrunUtilsV2"; private const string pluginName = "SpeedrunUtilsV2"; private const string pluginVersion = "1.1.0"; internal static readonly LiveSplitManager liveSplitManager = new LiveSplitManager(); private bool _GUI_ENABLED; private static bool GUI_TRACKER_ENABLED = false; private static bool GUI_SPLITS_ENABLED = false; private static bool GUI_CREDITS_ENABLED = false; private const float FPSUpdateRate = 0.1f; private float FPSUpdate = 0.1f; private static int FPS = 0; private static bool FPS_Limited = false; private static UtilsUI.WindowProperties windowPropertiesMain; private static UtilsUI.WindowProperties windowPropertiesSplits; private static UtilsUI.WindowProperties windowPropertiesCredits; internal static UtilsUI.WindowProperties windowPropertiesTracker; private static Rect fpsRect; private static Rect fpsRectShadow; private ScreenInfo CurrentScreenInfo; private static string _fps = string.Empty; private bool GUI_ENABLED { get { return _GUI_ENABLED; } set { _GUI_ENABLED = value; if (!GUI_ENABLED) { GUI_SPLITS_ENABLED = false; GUI_CREDITS_ENABLED = false; } } } private static int FPS_LimitValue => Mathf.Clamp(LiveSplitConfig.SETTINGS_LimitValue.Item2, 30, 9999); internal static FPSStatus CurrentFPSStatus { get { if (LiveSplitConfig.SETTINGS_UncapLoading.Item2) { Core instance = Core.Instance; if ((Object)(object)((instance != null) ? instance.BaseModule : null) != (Object)null && Core.Instance.BaseModule.IsLoading) { return FPSStatus.Uncapped; } } if (FPS_Limited) { return FPSStatus.Limited; } if (LiveSplitConfig.SETTINGS_Uncap.Item2) { return FPSStatus.Uncapped; } return FPSStatus.Capped; } } private static int _fpsInt { get { if (int.TryParse(_fps, out var result)) { return result; } return Application.targetFrameRate; } } internal static string FPSfield { get { return _fps; } set { if (!(_fps == value)) { string text = Regex.Replace(value, "\\D", ""); if (text.Length > 4) { text = _fps; } _fps = text; } } } public void Awake() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) SetupProperties(Screen.width, Screen.height); Core.OnScreenSizeChanged += new ScreenSizeChangedDelegate(SetupProperties); new Harmony("SpeedrunUtilsV2").PatchAll(); } internal static void GUIFront(int windowID) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) string arg = ((CurrentFPSStatus == FPSStatus.Uncapped) ? ("<color=#C0392B>" + Localization.UI.s_uncapped + "</color>") : string.Format("<color={0}>{1}</color>", "#BDC3C7", Application.targetFrameRate)); UtilsUI.GUILabel($"({arg}) FPS: {FPS}", windowPropertiesMain); if (LiveSplitConfig.ActionKeys.TryGetValue(LiveSplitConfig.Actions.UncapFPS, out var value) && LiveSplitConfig.ActionKeys.TryGetValue(LiveSplitConfig.Actions.LimitFPS, out var value2)) { UtilsUI.GUILabel(string.Format("{0}: <color={1}>{2}</color> | {3} {4}: <color={5}>{6}</color>", Localization.UI.s_uncappedFramerate, "#BDC3C7", value, Localization.UI.s_limitFramerateTo, LiveSplitConfig.SETTINGS_LimitValue.Item2, "#BDC3C7", value2), windowPropertiesMain, small: true); } UtilsUI.GUILabel(ConnectionManager.IsConnected ? ("<color=#2ECC71>" + Localization.UI.s_connected + "</color>") : ("<color=#E74C3C>" + Localization.UI.s_disconnected + "</color>"), windowPropertiesMain); if (UtilsUI.GUIButton(ConnectionManager.IsConnected ? (Localization.UI.s_disconnect ?? "") : (Localization.UI.s_connect ?? ""), (!ConnectionManager.IsConnected) ? UtilsUI.Setup.ButtonType.Off : UtilsUI.Setup.ButtonType.On, windowPropertiesMain)) { ToggleLiveSplit(); } if (UtilsUI.GUIButton(Localization.UI.s_splits ?? "", (!GUI_SPLITS_ENABLED) ? UtilsUI.Setup.ButtonType.Off : UtilsUI.Setup.ButtonType.On, windowPropertiesMain)) { GUI_SPLITS_ENABLED = !GUI_SPLITS_ENABLED; } if (UtilsUI.GUIButton(Localization.UI.s_tracker ?? "", (!GUI_TRACKER_ENABLED) ? UtilsUI.Setup.ButtonType.Off : UtilsUI.Setup.ButtonType.On, windowPropertiesMain)) { ToggleProgressTracker(); } if (UtilsUI.GUIButton(Localization.UI.s_tracking ?? "", (!LiveSplitConfig.SETTINGS_Tracking.Item2) ? UtilsUI.Setup.ButtonType.Off : UtilsUI.Setup.ButtonType.On, windowPropertiesMain)) { ToggleProgressTracking(); } if (UtilsUI.GUIButton(Localization.UI.s_credits ?? "", (!GUI_CREDITS_ENABLED) ? UtilsUI.Setup.ButtonType.Off : UtilsUI.Setup.ButtonType.On, windowPropertiesMain)) { GUI_CREDITS_ENABLED = !GUI_CREDITS_ENABLED; } UtilsUI.GUIEmptySpace(8f, windowPropertiesMain); if (UtilsUI.GUIButton(Localization.UI.s_setMax ?? "", UtilsUI.Setup.ButtonType.Normal, windowPropertiesMain)) { SetMaxFPS(_fpsInt); } FPSfield = UtilsUI.GUIField(FPSfield, windowPropertiesMain); UtilsUI.Cleanup(windowPropertiesMain); } internal static void GUISplits(int windowID) { foreach (LiveSplitConfig.Splits value in Enum.GetValues(typeof(LiveSplitConfig.Splits))) { UpdateSplitGUI(value); } UtilsUI.Cleanup(windowPropertiesSplits); } internal static void GUICredits(int windowID) { UtilsUI.GUILabel("- " + Localization.UI.s_development + " -", windowPropertiesCredits); UtilsUI.GUILabel("<color=#FA7DE5>Ninja Cookie</color>", windowPropertiesCredits); UtilsUI.GUILabel("<color=#800080>Loomeh</color>", windowPropertiesCredits); UtilsUI.GUIEmptySpace(5f, windowPropertiesCredits); UtilsUI.GUILabel("- " + Localization.UI.s_research + " -", windowPropertiesCredits); UtilsUI.GUILabel("<color=#9FA1A4>Jomoko</color>", windowPropertiesCredits); UtilsUI.GUIEmptySpace(5f, windowPropertiesCredits); UtilsUI.GUILabel("- " + Localization.UI.s_originalTools + " -", windowPropertiesCredits); UtilsUI.GUILabel("<color=#45BF60>Judah Caruso</color>", windowPropertiesCredits); UtilsUI.Cleanup(windowPropertiesCredits); } internal static void GUITracker(int windowID) { SaveData.Data data = Tracking.CurrentSaveData?.StageData; if (LiveSplitConfig.SETTINGS_Tracking.Item2 && data != null && data.StageValid) { UtilsUI.GUILabelTracker(windowPropertiesTracker, Localization.Tracking.s_total + ": \t" + data.CurrentTotal, Localization.Tracking.s_graffiti + ": \t" + data.CurrentStageGraffiti, Localization.Tracking.s_taxi + ": \t" + data.CurrentStageTaxi); (string, bool)[] currentStageCollectableInfo = data.CurrentStageCollectableInfo; for (int i = 0; i < currentStageCollectableInfo.Length; i++) { (string, bool) tuple = currentStageCollectableInfo[i]; UtilsUI.GUILabelLeft("<color=" + GetColorCollected(tuple.Item2) + ">" + tuple.Item1 + "</color>", windowPropertiesTracker); } currentStageCollectableInfo = data.CurrentStageCharacterInfo; for (int i = 0; i < currentStageCollectableInfo.Length; i++) { (string, bool) tuple2 = currentStageCollectableInfo[i]; UtilsUI.GUILabelLeft("<color=" + GetColorCollected(tuple2.Item2) + ">" + tuple2.Item1 + "</color>", windowPropertiesTracker); } } else if (LiveSplitConfig.SETTINGS_Tracking.Item2 && data != null) { UtilsUI.GUILabelLeft("Waiting for stage...", windowPropertiesTracker); } else if (!LiveSplitConfig.SETTINGS_Tracking.Item2) { UtilsUI.GUILabelLeft("Tracking Disabled...", windowPropertiesTracker); } UtilsUI.Cleanup(windowPropertiesTracker); } private static string GetColorCollected(bool collected = false) { if (!collected) { return "#E74C3C"; } return "#2ECC71"; } public void Start() { GUI_TRACKER_ENABLED = LiveSplitConfig.SETTINGS_Tracker.Item2; } public void Update() { if (GetActionKeyDown(LiveSplitConfig.Actions.UncapFPS)) { LiveSplitConfig.SETTINGS_Uncap.Item2 = !LiveSplitConfig.SETTINGS_Uncap.Item2; } if (GetActionKeyDown(LiveSplitConfig.Actions.LimitFPS)) { FPS_Limited = !FPS_Limited; } UpdateFPS(); if (GetActionKeyDown(LiveSplitConfig.Actions.OpenGUI) && windowPropertiesMain != null) { GUI_ENABLED = !GUI_ENABLED; } if (GetActionKeyDown(LiveSplitConfig.Actions.OpenTracker) && windowPropertiesMain != null) { ToggleProgressTracker(); } } private bool GetActionKeyDown(LiveSplitConfig.Actions action) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (LiveSplitConfig.ActionKeys.TryGetValue(action, out var value)) { return Input.GetKeyDown(value); } return false; } private void UpdateFPS() { switch (CurrentFPSStatus) { case FPSStatus.Uncapped: if (Application.targetFrameRate != -1) { SetFPS(-1); } break; case FPSStatus.Capped: if (Application.targetFrameRate != LiveSplitConfig.SETTINGS_DefaultFPS.Item2) { SetMaxFPS(LiveSplitConfig.SETTINGS_DefaultFPS.Item2); } break; case FPSStatus.Limited: if (Application.targetFrameRate != FPS_LimitValue) { SetFPS(LiveSplitConfig.SETTINGS_LimitValue.Item2 = FPS_LimitValue); } break; } if (FPSUpdate <= 0f) { FPS = Mathf.RoundToInt(1f / Time.smoothDeltaTime); FPSUpdate = 0.1f; } FPSUpdate -= Time.deltaTime; } public void OnGUI() { //IL_000d: 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: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown ScreenInfo currentScreenInfo = CurrentScreenInfo; if (currentScreenInfo != null) { _ = currentScreenInfo.Matrix; if (true) { GUI.matrix = CurrentScreenInfo.Matrix; } } UtilsUI.Init(); _ = fpsRect; _ = fpsRectShadow; if (LiveSplitConfig.SETTINGS_ShowFPS.Item2) { UtilsUI.GUILabelFPS(FPS.ToString(), fpsRect, fpsRectShadow); } if (GUI_ENABLED) { UtilsUI.GUIWindow(0, windowPropertiesMain, new WindowFunction(GUIFront)); if (GUI_SPLITS_ENABLED) { UtilsUI.GUIWindow(1, windowPropertiesSplits, new WindowFunction(GUISplits)); } if (GUI_CREDITS_ENABLED) { UtilsUI.GUIWindow(2, windowPropertiesCredits, new WindowFunction(GUICredits)); } } if (GUI_TRACKER_ENABLED) { UtilsUI.GUIWindow(3, windowPropertiesTracker, new WindowFunction(GUITracker)); } } private void SetupProperties(int width, int height) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_002d: 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_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) CurrentScreenInfo = new ScreenInfo(width, height); Vector2Int resolution = CurrentScreenInfo.Resolution; width = ((Vector2Int)(ref resolution)).x; resolution = CurrentScreenInfo.Resolution; height = ((Vector2Int)(ref resolution)).y; windowPropertiesMain = new UtilsUI.WindowProperties((float)width * 0.5f - 175f); windowPropertiesSplits = new UtilsUI.WindowProperties(windowPropertiesMain.WindowX + 350f + 3f, 300f, 18f); windowPropertiesCredits = new UtilsUI.WindowProperties(windowPropertiesMain.WindowX - 200f - 3f, 200f, 18f); windowPropertiesTracker = new UtilsUI.WindowProperties((float)width - 300f - 18f, 300f, 18f); fpsRect = new Rect(LiveSplitConfig.SETTINGS_FPSPos.Item2.x, LiveSplitConfig.SETTINGS_FPSPos.Item2.y, (float)width, (float)height); fpsRectShadow = new Rect(LiveSplitConfig.SETTINGS_FPSPos.Item2.x - 2f, LiveSplitConfig.SETTINGS_FPSPos.Item2.y + 2f, (float)width, (float)height); } internal static void SetMaxFPS(int fps) { fps = Mathf.Clamp(fps, 30, 9999); if (CurrentFPSStatus != FPSStatus.Capped) { FPSfield = fps.ToString(); LiveSplitConfig.UpdateFPSSetting(fps); } else { SetFPS(fps); LiveSplitConfig.UpdateFPSSetting(Application.targetFrameRate); FPSfield = Application.targetFrameRate.ToString(); } } private static void SetFPS(int fps) { if (QualitySettings.vSyncCount > 0) { QualitySettings.vSyncCount = 0; } Application.targetFrameRate = fps; } internal static void ToggleLiveSplit() { if (!ConnectionManager.IsConnected && liveSplitManager.ConnectionStatus == LiveSplitManager.Status.Disconnected) { liveSplitManager.ConnectToLiveSplit(); } else if (ConnectionManager.IsConnected && liveSplitManager.ConnectionStatus == LiveSplitManager.Status.Connected) { liveSplitManager.DisconnectFromLiveSplit(); } } internal static void UpdateSplitGUI(LiveSplitConfig.Splits split) { bool flag = UtilsUI.GUIToggle(LiveSplitConfig.CurrentSplits[split].Item1, LiveSplitConfig.CurrentSplits[split].Item3, windowPropertiesSplits); if (LiveSplitConfig.CurrentSplits.TryGetValue(split, out var value) && flag != value.Item1) { LiveSplitConfig.UpdateSplitsFile(split, flag); } } internal static void ToggleProgressTracker() { GUI_TRACKER_ENABLED = !GUI_TRACKER_ENABLED; LiveSplitConfig.UpdateProgressTrackerState(LiveSplitConfig.SETTINGS_Tracker.Item2 = GUI_TRACKER_ENABLED); } internal static void ToggleProgressTracking() { LiveSplitConfig.UpdateProgressTrackingState(LiveSplitConfig.SETTINGS_Tracking.Item2 = !LiveSplitConfig.SETTINGS_Tracking.Item2); if (LiveSplitConfig.SETTINGS_Tracking.Item2) { Tracking.CurrentSaveData?.UpdateAll(); } } } internal static class Localization { internal static class UI { internal static string s_uncapped = "Uncapped"; internal static string s_uncappedFramerate = "Uncap Framerate"; internal static string s_limitFramerateTo = "Limit Framerate to"; internal static string s_connected = "Connected to LiveSplit!"; internal static string s_disconnected = "Disconnected from LiveSplit"; internal static string s_connect = "Connect to LiveSplit"; internal static string s_disconnect = "Disconnect from LiveSplit"; internal static string s_splits = "Splits"; internal static string s_setMax = "Set Max FPS"; internal static string s_credits = "Credits"; internal static string s_development = "Development"; internal static string s_research = "Previous Research"; internal static string s_originalTools = "Original Tools"; internal static string s_tracker = "Progress Tracker"; internal static string s_tracking = "Tracking Game Progress"; } internal static class Splits { internal static string s_PrologueEnd = "Prologue Ended"; internal static string s_EarlySquare = "Entered Square Early"; internal static string s_VersumStart = "Entered Versum"; internal static string s_Dream1Start = "Dream 1 Started"; internal static string s_Chapter1End = "Chapter 1 Ended"; internal static string s_BrinkStart = "Entered Brink"; internal static string s_Dream2Start = "Dream 2 Started"; internal static string s_Chapter2End = "Chapter 2 Ended"; internal static string s_MallStart = "Entered Mall"; internal static string s_Dream3Start = "Dream 3 Started"; internal static string s_Chapter3End = "Chapter 3 Ended"; internal static string s_PrinceVersumEnd = "Talked To Frank in Versum"; internal static string s_PrinceSquareEnd = "Beat the Frank Challenge in Square"; internal static string s_PrinceBrinkEnd = "Talked To Frank in Brink"; internal static string s_PyramidStart = "Entered Pyramid Island"; internal static string s_Dream4Start = "Dream 4 Started"; internal static string s_Chapter4End = "Chapter 4 Ended"; internal static string s_Dream5Start = "Dream 5 Started"; internal static string s_UnlockOldHead = "Unlocked Old Head"; internal static string s_CharacterUnlock = "Any Character Unlocked Manually"; internal static string s_FinalBossDefeated = "Defeated Final Boss"; } internal static class Tracking { internal static string s_total = "Game Total"; internal static string s_graffiti = "Stage Graffiti"; internal static string s_taxi = "Stage Taxi"; } internal static class Descriptions { internal static string s_mouseFix = "Makes it so the mouse does not take priority of selected items if currently using a controller"; } } } namespace SpeedrunUtilsV2.UI { internal static class UtilsUI { internal class WindowProperties { internal float WindowMarginW = 20f; internal float WindowMarginH = 20f; internal float WindowMarginIW = 5f; internal float WindowMarginIH = 3f; internal float WindowX; internal float WindowY; internal float WindowW = 350f; internal float ElementH = 20f; internal float Seperation = 3f; internal float IndexLine; internal float FinalIndex; internal Rect ElementRect; internal float WindowH => FinalIndex + WindowMarginIH * 2f; private float Spacing { get { if (IndexLine == 0f) { return WindowMarginIH; } return Seperation; } } internal float ElementY => IndexLine + Spacing; internal float ElementW => WindowW - WindowMarginIW * 2f; internal Rect ElementRectFinal { get { //IL_0042: Unknown result type (might be due to invalid IL or missing references) ((Rect)(ref ElementRect)).y = ElementY; ((Rect)(ref ElementRect)).width = ElementW; IndexLine += ((Rect)(ref ElementRect)).height + Spacing; return ElementRect; } } internal WindowProperties(float WindowX = 0f, float WindowW = 350f, float ElementH = 20f, float WindowY = -1f, Rect? customRect = null) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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) this.WindowX = WindowX; this.WindowW = WindowW; this.WindowY = ((WindowY != -1f) ? WindowY : WindowMarginH); ElementRect = (Rect)(((??)customRect) ?? new Rect(WindowMarginIW, 0f, ElementW, this.ElementH = ElementH)); } } internal static class Setup { internal enum ButtonType { Normal, Off, On } private static readonly Color TextColor = UtilsUIColor.color_text; private static readonly Color TextColorShadow = new Color(0f, 0f, 0f, 0.5f); private static readonly Color TextColorFps = new Color(1f, 1f, 1f, 0.8f); private static int FontSize = 16; private static readonly Texture2D Normal_BT = new Texture2D(1, 1); private static readonly Texture2D Hover_BT = new Texture2D(1, 1); private static readonly Texture2D Active_BT = new Texture2D(1, 1); private static readonly Texture2D Normal_BToff = new Texture2D(1, 1); private static readonly Texture2D Hover_BToff = new Texture2D(1, 1); private static readonly Texture2D Active_BToff = new Texture2D(1, 1); private static readonly Texture2D Normal_BTon = new Texture2D(1, 1); private static readonly Texture2D Hover_BTon = new Texture2D(1, 1); private static readonly Texture2D Active_BTon = new Texture2D(1, 1); private static readonly Texture2D WindowTexture = new Texture2D(1, 1); internal static GUIStyle Text() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_0025: 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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_0056: Expected O, but got Unknown GUIStyle val = new GUIStyle { alignment = (TextAnchor)4, wordWrap = false, fontSize = FontSize, fontStyle = (FontStyle)1 }; val.normal.textColor = TextColor; val.hover.textColor = TextColor; val.active.textColor = TextColor; return val; } internal static GUIStyle TextLeft() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_004d: 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_005e: Expected O, but got Unknown GUIStyle val = new GUIStyle { alignment = (TextAnchor)3, wordWrap = false, fontSize = (int)((float)FontSize * 0.9f), fontStyle = (FontStyle)1 }; val.normal.textColor = TextColor; val.hover.textColor = TextColor; val.active.textColor = TextColor; return val; } internal static GUIStyle TextSmall() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown GUIStyle val = new GUIStyle { alignment = (TextAnchor)4, wordWrap = false, fontSize = Mathf.RoundToInt((float)FontSize * 0.7f), fontStyle = (FontStyle)1 }; val.normal.textColor = TextColor; val.hover.textColor = TextColor; val.active.textColor = TextColor; return val; } internal static GUIStyle TextFPS() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown GUIStyle val = new GUIStyle { alignment = (TextAnchor)0, wordWrap = false, fontSize = Mathf.Max(LiveSplitConfig.SETTINGS_FPSSize.Item2, 1), fontStyle = (FontStyle)1 }; val.normal.textColor = TextColorFps; val.hover.textColor = TextColorFps; val.active.textColor = TextColorFps; return val; } internal static GUIStyle TextFPSShadow() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown GUIStyle val = new GUIStyle { alignment = (TextAnchor)0, wordWrap = false, fontSize = Mathf.Max(LiveSplitConfig.SETTINGS_FPSSize.Item2, 1), fontStyle = (FontStyle)1 }; val.normal.textColor = TextColorShadow; val.hover.textColor = TextColorShadow; val.active.textColor = TextColorShadow; return val; } internal static GUIStyle TextField() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown return new GUIStyle(GUI.skin.textField) { alignment = (TextAnchor)4, wordWrap = false, fontSize = Mathf.RoundToInt((float)FontSize * 0.8f), fontStyle = (FontStyle)1 }; } internal static GUIStyle Window() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown GUIStyle val = new GUIStyle(GUI.skin.box); WindowTexture.SetPixel(0, 0, new Color(0.073f, 0.123f, 0.155f, 0.75f)); WindowTexture.Apply(); val.normal.background = WindowTexture; return val; } internal static GUIStyle Button(ButtonType buttonType) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_003c: 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_0056: 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_0084: 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_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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) GUIStyle val = new GUIStyle(GUI.skin.button); Texture2D val2 = Normal_BT; Texture2D val3 = Hover_BT; Texture2D val4 = Active_BT; switch (buttonType) { case ButtonType.Normal: val2.SetPixel(0, 0, UtilsUIColor.color_button); val3.SetPixel(0, 0, UtilsUIColor.color_button_hover); val4.SetPixel(0, 0, UtilsUIColor.color_button_active); break; case ButtonType.Off: val2 = Normal_BToff; val3 = Hover_BToff; val4 = Active_BToff; val2.SetPixel(0, 0, UtilsUIColor.color_button_off); val3.SetPixel(0, 0, UtilsUIColor.color_button_off_hover); val4.SetPixel(0, 0, UtilsUIColor.color_button_off_active); break; case ButtonType.On: val2 = Normal_BTon; val3 = Hover_BTon; val4 = Active_BTon; val2.SetPixel(0, 0, UtilsUIColor.color_button_on); val3.SetPixel(0, 0, UtilsUIColor.color_button_on_hover); val4.SetPixel(0, 0, UtilsUIColor.color_button_on_active); break; } val2.Apply(); val3.Apply(); val4.Apply(); val.normal.background = val2; val.normal.textColor = TextColor; val.hover.background = val3; val.hover.textColor = TextColor; val.active.background = val4; val.active.textColor = TextColor; val.alignment = (TextAnchor)4; val.wordWrap = false; val.fontSize = Mathf.RoundToInt((float)FontSize * 0.8f); val.fontStyle = (FontStyle)1; return val; } internal static GUIStyle Toggle() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown return new GUIStyle(GUI.skin.toggle) { alignment = (TextAnchor)4, wordWrap = false, fontSize = Mathf.RoundToInt((float)FontSize * 0.8f), fontStyle = (FontStyle)1 }; } } internal static bool setup; private const string WINDOWNAME = ""; private static GUIStyle TEXT; private static GUIStyle TEXT_LEFT; private static GUIStyle TEXT_SMALL; private static GUIStyle TEXT_FPS; private static GUIStyle TEXT_FPS_SHADOW; private static GUIStyle WINDOW; private static GUIStyle BUTTON; private static GUIStyle BUTTON_OFF; private static GUIStyle BUTTON_ON; private static GUIStyle FIELD; private static GUIStyle TOGGLE; internal static void Init() { if (!setup) { TEXT = Setup.Text(); TEXT_LEFT = Setup.TextLeft(); TEXT_SMALL = Setup.TextSmall(); TEXT_FPS = Setup.TextFPS(); TEXT_FPS_SHADOW = Setup.TextFPSShadow(); WINDOW = Setup.Window(); BUTTON = Setup.Button(Setup.ButtonType.Normal); BUTTON_OFF = Setup.Button(Setup.ButtonType.Off); BUTTON_ON = Setup.Button(Setup.ButtonType.On); FIELD = Setup.TextField(); TOGGLE = Setup.Toggle(); setup = true; } } internal static void GUIWindow(int ID, WindowProperties windowProperties, WindowFunction func) { //IL_0019: 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) GUI.Window(ID, new Rect(windowProperties.WindowX, windowProperties.WindowY, windowProperties.WindowW, windowProperties.WindowH), func, "", WINDOW); } internal static void GUILabel(string text, WindowProperties windowProperties, bool small = false, bool fps = false) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) GUI.Label(windowProperties.ElementRectFinal, text, fps ? TEXT_FPS : (small ? TEXT_SMALL : TEXT)); } internal static void GUILabelLeft(string text, WindowProperties windowProperties) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) GUI.Label(windowProperties.ElementRectFinal, text, TEXT_LEFT); } internal static void GUILabelTracker(WindowProperties windowProperties, params string[] texts) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) foreach (string text in texts) { GUI.Label(windowProperties.ElementRectFinal, text, TEXT_LEFT); } } internal static void GUILabelFPS(string text, Rect rect, Rect shadow) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) GUI.Label(shadow, text, TEXT_FPS_SHADOW); GUI.Label(rect, text, TEXT_FPS); } internal static void GUIEmptySpace(float space, WindowProperties windowProperties) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) ((Rect)(ref windowProperties.ElementRect)).height = space; GUI.Label(windowProperties.ElementRectFinal, "", TEXT); ((Rect)(ref windowProperties.ElementRect)).height = windowProperties.ElementH; } internal static bool GUIButton(string text, Setup.ButtonType buttonType, WindowProperties windowProperties) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) GUIStyle val = BUTTON; switch (buttonType) { case Setup.ButtonType.Normal: val = BUTTON; break; case Setup.ButtonType.Off: val = BUTTON_OFF; break; case Setup.ButtonType.On: val = BUTTON_ON; break; } return GUI.Button(windowProperties.ElementRectFinal, text, val); } internal static string GUIField(string text, WindowProperties windowProperties) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return GUI.TextField(windowProperties.ElementRectFinal, text, FIELD); } internal static bool GUIToggle(bool value, string text, WindowProperties windowProperties) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return GUI.Toggle(windowProperties.ElementRectFinal, value, text, TOGGLE); } internal static void Cleanup(WindowProperties windowProperties) { if (windowProperties.FinalIndex == 0f) { windowProperties.FinalIndex = windowProperties.IndexLine; } windowProperties.IndexLine = 0f; } } internal static class UtilsUIColor { internal const string color_text_disconnect = "#E74C3C"; internal const string color_text_connect = "#2ECC71"; internal const string color_text_keys = "#BDC3C7"; internal const string color_text_red = "#C0392B"; internal const string color_text_fps = "#BDC3C7"; internal const string color_credit_ninja = "#FA7DE5"; internal const string color_credit_loomeh = "#800080"; internal const string color_credit_jomoko = "#9FA1A4"; internal const string color_credit_judah = "#45BF60"; internal static readonly Color color_text = new Color(0.9254902f, 0.9411765f, 0.94509804f, 1f); internal static readonly Color color_button_off = new Color(0.8980392f, 19f / 85f, 0.20784314f, 0.7f); internal static readonly Color color_button_off_hover = new Color(0.827451f, 0.18431373f, 0.18431373f, 0.7f); internal static readonly Color color_button_off_active = new Color(61f / 85f, 0.10980392f, 0.10980392f, 0.7f); internal static readonly Color color_button_on = new Color(0.2627451f, 32f / 51f, 0.2784314f, 1f); internal static readonly Color color_button_on_hover = new Color(0.21960784f, 0.5568628f, 0.23529412f, 1f); internal static readonly Color color_button_on_active = new Color(0.18039216f, 25f / 51f, 10f / 51f, 1f); internal static readonly Color color_button = new Color(47f / 85f, 54f / 85f, 0.7490196f, 0.7f); internal static readonly Color color_button_hover = new Color(0.47843137f, 0.58431375f, 0.6901961f, 0.7f); internal static readonly Color color_button_active = new Color(36f / 85f, 0.52156866f, 0.6313726f, 0.7f); } } namespace SpeedrunUtilsV2.ProgressTracker { internal static class Tracking { internal enum PickupType { Collectable, Pickup } internal static SaveData CurrentSaveData = new SaveData(); internal static void CreateProgressData(int id) { CurrentSaveData = new SaveData(); SaveProgressData(id); } internal static void SaveProgressData(int id) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) if (TryGetDirectory(out var directory)) { using BinaryWriter writer = new BinaryWriter(new FileStream(Path.Combine(directory, string.Format("data_{0}{1}", id, ".sru")), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)); Write(writer); } SaveData.Data data = CurrentSaveData?.StageData; if (data != null) { data.StageValid = SaveData.FromReptileStage(Utility.GetCurrentStage()) != SaveData.Stage.MAX; CurrentSaveData.GetPercentageTotal(); if (data.StageValid) { CurrentSaveData.GetPercentageStageTotal(); CurrentSaveData.StageData.CurrentStageGraffiti = CurrentSaveData.GetPercentage(data.Graffiti, Utility.GetCurrentStage()); CurrentSaveData.StageData.CurrentStageTaxi = CurrentSaveData.GetPercentage(data.Taxis, Utility.GetCurrentStage()); } } } internal static SaveData LoadProgressData(int id) { return CurrentSaveData = new SaveData(GetProgressData(id)); } private static SaveData.Data GetProgressData(int id) { SaveData.Data result = new SaveData.Data(); string path = Path.Combine(LiveSplitConfig.PATH_SaveData, string.Format("data_{0}{1}", id, ".sru")); if (!File.Exists(path)) { return result; } try { using BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None)); return Read(reader); } catch { return result = new SaveData.Data(); } } private static void Write(BinaryWriter writer) { float[] graffiti = CurrentSaveData.StageData.Graffiti; foreach (float value in graffiti) { writer.Write(value); } graffiti = CurrentSaveData.StageData.Collectables; foreach (float value2 in graffiti) { writer.Write(value2); } graffiti = CurrentSaveData.StageData.Characters; foreach (float value3 in graffiti) { writer.Write(value3); } graffiti = CurrentSaveData.StageData.Taxis; foreach (float value4 in graffiti) { writer.Write(value4); } } private static SaveData.Data Read(BinaryReader reader) { SaveData.Data data = new SaveData.Data(); for (int i = 0; i < data.Graffiti.Length; i++) { data.Graffiti[i] = reader.ReadSingle(); } for (int j = 0; j < data.Collectables.Length; j++) { data.Collectables[j] = reader.ReadSingle(); } for (int k = 0; k < data.Characters.Length; k++) { data.Characters[k] = reader.ReadSingle(); } for (int l = 0; l < data.Taxis.Length; l++) { data.Taxis[l] = reader.ReadSingle(); } return data; } private static bool TryGetDirectory(out string directory) { if (Directory.Exists(directory = LiveSplitConfig.PATH_SaveData)) { return true; } try { if (Directory.CreateDirectory(LiveSplitConfig.PATH_SaveData).Exists) { return true; } } catch { } return false; } internal static (Object, PickupType)[] GetCurrentStagePickups() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_0039: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 //IL_0056: Unknown result type (might be due to invalid IL or missing references) List<Pickup> list = new List<Pickup>(); Collectable[] array = Array.Empty<Collectable>(); List<(Object, PickupType)> list2 = new List<(Object, PickupType)>(); Stage currentStage = Utility.GetCurrentStage(); Core instance = Core.Instance; object obj; if (instance == null) { obj = null; } else { SaveManager saveManager = instance.SaveManager; obj = ((saveManager != null) ? saveManager.CurrentSaveSlot : null); } if (obj == null || (int)currentStage == -1 || (int)currentStage == 14) { return list2.ToArray(); } if (Core.Instance.SaveManager.CurrentSaveSlot.GetStageProgress(currentStage) == null) { return list2.ToArray(); } WorldHandler instance2 = WorldHandler.instance; array = ((instance2 == null) ? null : instance2.SceneObjectsRegister?.gameplayEvents?.Where((GameplayEvent x) => ((object)x).GetType() == typeof(Collectable))?.Cast<Collectable>()?.ToArray()); if (array == null) { return list2.ToArray(); } VendingMachine[] array2 = Object.FindObjectsOfType<VendingMachine>()?.Where((VendingMachine x) => x.rewards != null && x.rewards.Contains((Reward)4))?.ToArray(); if (array2 != null) { VendingMachine[] array3 = array2; Pickup item = default(Pickup); foreach (VendingMachine val in array3) { if ((Object)(object)val.unlockableDrop != (Object)null && val.unlockableDrop.TryGetComponent<Pickup>(ref item)) { list.Add(item); } } } Pickup val2 = WorldHandler.instance.SceneObjectsRegister.pickups?.FirstOrDefault((Func<Pickup, bool>)((Pickup x) => (int)x.pickupType == 10)); if ((Object)(object)val2 != (Object)null) { list.Add(val2); } foreach (Pickup item3 in list) { list2.Add(((Object)(object)item3, PickupType.Pickup)); } Collectable[] array4 = array; foreach (Collectable item2 in array4) { list2.Add(((Object)(object)item2, PickupType.Collectable)); } return list2.ToArray(); } } internal class SaveData { [Serializable] internal class Data { internal float[] Graffiti = new float[7]; internal float[] Collectables = new float[7]; internal float[] Characters = new float[7]; internal float[] Taxis = new float[7]; internal string CurrentTotal = string.Empty; internal string CurrentStageTotal = string.Empty; internal string CurrentStageGraffiti = string.Empty; internal string CurrentStageTaxi = string.Empty; internal bool StageValid; internal (string, bool)[] CurrentStageCollectableInfo = Array.Empty<(string, bool)>(); internal (string, bool)[] CurrentStageCharacterInfo = Array.Empty<(string, bool)>(); } internal enum Stage { NONE = -1, Hideout, Versum, Square, Brink, Mall, Pyramid, Mataan, MAX } internal static readonly Dictionary<Characters, Stage> CharacterList = new Dictionary<Characters, Stage> { { (Characters)8, (Stage)5 }, { (Characters)0, (Stage)5 }, { (Characters)6, (Stage)4 }, { (Characters)1, (Stage)4 }, { (Characters)14, (Stage)4 }, { (Characters)9, (Stage)11 }, { (Characters)19, (Stage)12 }, { (Characters)10, (Stage)12 }, { (Characters)7, (Stage)6 }, { (Characters)22, (Stage)6 }, { (Characters)21, (Stage)9 }, { (Characters)11, (Stage)9 }, { (Characters)2, (Stage)7 }, { (Characters)20, (Stage)7 }, { (Characters)13, (Stage)7 } }; internal Data StageData { get; private set; } internal SaveData(Data data = null) { StageData = data ?? new Data(); } internal string GetPercentageTotal() { if (StageData == null) { return 0f.ToString("##0.00") + "%"; } float[] data = new float[4] { GetProgress(StageData.Graffiti), GetProgress(StageData.Collectables), GetProgress(StageData.Characters), GetProgress(StageData.Taxis) }; return StageData.CurrentTotal = GetPercentage(data); } internal string GetPercentageStageTotal() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) int num = (int)FromReptileStage(Utility.GetCurrentStage()); if (StageData == null || StageData.Graffiti?.Length < num + 1 || StageData.Collectables?.Length < num + 1 || StageData.Characters?.Length < num + 1 || StageData.Taxis?.Length < num + 1) { return 0f.ToString("##0.00") + "%"; } float[] data = new float[4] { StageData.Graffiti[num], StageData.Collectables[num], StageData.Characters[num], StageData.Taxis[num] }; return StageData.CurrentStageTotal = GetPercentage(data); } internal string GetPercentage(float[] data, Stage? stage = null) { return GetProgress(data, stage).ToString("##0.00") + "%"; } internal float GetProgress(float[] data, Stage? stage = null) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (stage.HasValue) { int num = (int)FromReptileStage(stage.Value); if (num == -1 || data == null || data.Length < num + 1) { return 0f; } return data[num]; } if (data == null) { return 0f; } float num2 = 0f; foreach (float num3 in data) { num2 += num3; } return num2 / ((float)data.Length * 100f) * 100f; } internal void UpdateAll() { if (LiveSplitConfig.SETTINGS_Tracking.Item2) { UpdateTaxis(save: false); UpdateCurrentStageGraffiti(save: false); UpdateCurrentStageCollectables(save: false); UpdateCurrentCharacters(save: false); Core instance = Core.Instance; object obj; if (instance == null) { obj = null; } else { SaveManager saveManager = instance.SaveManager; obj = ((saveManager != null) ? saveManager.CurrentSaveSlot : null); } SaveSlotData val = (SaveSlotData)obj; if (val != null) { Tracking.SaveProgressData(val.saveSlotId); } if (Plugin.windowPropertiesTracker != null) { Plugin.windowPropertiesTracker.FinalIndex = 0f; } } } internal void UpdateTaxis(bool save = true) { //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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Invalid comparison between Unknown and I4 //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Invalid comparison between Unknown and I4 //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) Core instance = Core.Instance; object obj; if (instance == null) { obj = null; } else { SaveManager saveManager = instance.SaveManager; obj = ((saveManager != null) ? saveManager.CurrentSaveSlot : null); } SaveSlotData val = (SaveSlotData)obj; if (val == null || StageData?.Taxis == null) { return; } for (int i = 0; i < StageData.Taxis.Length; i++) { Stage val2 = ToReptileStage((Stage)i); bool flag = StageData.Taxis.Length >= i + 1; if (flag && (int)val2 != 11) { StageData.Taxis[i] = (val.GetStageProgress(val2).taxiFound ? 100f : 0f); } else if (flag && (int)val2 == 11) { float num = (val.GetStageProgress(val2).taxiFound ? 50f : 0f); if (!val.taxiLocked) { num += 50f; } StageData.Taxis[i] = num; } } if (save) { Tracking.SaveProgressData(val.saveSlotId); } } internal void UpdateCurrentStageGraffiti(bool save = true) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)WorldHandler.instance == (Object)null) { return; } Core instance = Core.Instance; object obj; if (instance == null) { obj = null; } else { SaveManager saveManager = instance.SaveManager; obj = ((saveManager != null) ? saveManager.CurrentSaveSlot : null); } if (obj == null) { return; } GraffitiSpot[] array = (from x in WorldHandler.instance.GetGrafSpots(false) where !(x is GraffitiSpotFinisher) select x).ToArray(); float num = (float)array.Where((GraffitiSpot x) => (int)x.topCrew == 1 || (int)x.topCrew == 7).Count() / (float)array.Length * 100f; int num2 = (int)FromReptileStage(Utility.GetCurrentStage()); if (num2 != -1 && StageData.Graffiti != null && StageData.Graffiti.Length >= num2 + 1) { StageData.Graffiti[num2] = num; if (save) { Tracking.SaveProgressData(Core.Instance.SaveManager.CurrentSaveSlot.saveSlotId); } } } private string GetLocalizedCollectableName(AUnlockable unlockable, PickUpType type) { //IL_0021: 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_004d: Expected I4, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) string text = string.Empty; Core instance = Core.Instance; IGameTextLocalizer val = ((instance != null) ? instance.Localizer : null); if (val == null) { return string.Empty; } switch (type - 3) { case 0: text = ((MusicTrack)(((unlockable is MusicTrack) ? unlockable : null)?)).Title; break; case 1: text = ((GraffitiAppEntry)(((unlockable is GraffitiAppEntry) ? unlockable : null)?)).Title; break; case 8: text = val.GetSkinText(((MoveStyleSkin)(((unlockable is MoveStyleSkin) ? unlockable : null)?)).Title, Array.Empty<string>()); break; case 2: text = val.GetSkinText(((OutfitUnlockable)((unlockable is OutfitUnlockable) ? unlockable : null)).outfitName, Array.Empty<string>()) + " (" + val.GetCharacterName(((OutfitUnlockable)((unlockable is OutfitUnlockable) ? unlockable : null)).character) + ")"; break; case 7: text = "Map"; break; } return text ?? string.Empty; } internal void UpdateCurrentStageCollectables(bool save = true) { //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Invalid comparison between Unknown and I4 //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Invalid comparison between Unknown and I4 //IL_010a: Unknown result type (might be due to invalid IL or missing references) Core instance = Core.Instance; object obj; if (instance == null) { obj = null; } else { APlatform platform = instance.Platform; obj = ((platform != null) ? platform.User : null); } AUser val = (AUser)obj; if ((Object)(object)WorldHandler.instance == (Object)null) { return; } Core instance2 = Core.Instance; object obj2; if (instance2 == null) { obj2 = null; } else { SaveManager saveManager = instance2.SaveManager; obj2 = ((saveManager != null) ? saveManager.CurrentSaveSlot : null); } if (obj2 == null || val == null) { return; } (Object, Tracking.PickupType)[] currentStagePickups = Tracking.GetCurrentStagePickups(); float num = 0f; List<(string, bool)> list = new List<(string, bool)>(); (Object, Tracking.PickupType)[] array = currentStagePickups; for (int i = 0; i < array.Length; i++) { (Object, Tracking.PickupType) tuple = array[i]; switch (tuple.Item2) { case Tracking.PickupType.Pickup: { Object item3 = tuple.Item1; Pickup val4 = (Pickup)(object)((item3 is Pickup) ? item3 : null); AUnlockable val5 = val4?.unlock; bool item4 = false; if (((int)val4.pickupType == 10 && Core.Instance.SaveManager.CurrentSaveSlot.GetCurrentStageProgress().mapFound) || ((int)val4.pickupType != 10 && (Object)(object)val5 != (Object)null && val.GetUnlockableSaveDataFor(val5).IsUnlocked)) { num += 1f; item4 = true; } list.Add((GetLocalizedCollectableName(val5, val4.pickupType), item4)); break; } case Tracking.PickupType.Collectable: { Object item = tuple.Item1; Collectable val2 = (Collectable)(object)((item is Collectable) ? item : null); AUnlockable val3 = val2?.unlock; bool item2 = false; if ((Object)(object)val3 != (Object)null && val.GetUnlockableSaveDataFor(val3).IsUnlocked) { num += 1f; item2 = true; } list.Add((GetLocalizedCollectableName(val3, val2.GetValue<PickUpType>("pickUpType", Array.Empty<object>())), item2)); break; } } } float num2 = num / (float)currentStagePickups.Length * 100f; int num3 = (int)FromReptileStage(Utility.GetCurrentStage()); if (num3 != -1 && StageData?.Collectables != null && StageData.Collectables.Length >= num3 + 1) { StageData.CurrentStageCollectableInfo = list.ToArray(); StageData.Collectables[num3] = num2; if (save) { Tracking.SaveProgressData(Core.Instance.SaveManager.CurrentSaveSlot.saveSlotId); } } } internal void UpdateCurrentCharacters(bool save = true) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) Core instance = Core.Instance; object obj; if (instance == null) { obj = null; } else { SaveManager saveManager = instance.SaveManager; obj = ((saveManager != null) ? saveManager.CurrentSaveSlot : null); } if (obj == null || StageData?.Characters == null) { return; } Core instance2 = Core.Instance; IGameTextLocalizer val = ((instance2 != null) ? instance2.Localizer : null); List<(string, bool)> list = new List<(string, bool)>(); for (int i = 0; i < StageData.Characters.Length; i++) { Stage stage = ToReptileStage((Stage)i); Characters[] array = CharacterList.Where((KeyValuePair<Characters, Stage> x) => x.Value == stage)?.Select((KeyValuePair<Characters, Stage> x) => x.Key)?.ToArray(); if (array == null || array.Length == 0) { continue; } List<Characters> list2 = new List<Characters>(); Characters[] array2 = array; foreach (Characters val2 in array2) { CharacterProgress characterProgress = Core.Instance.SaveManager.CurrentSaveSlot.GetCharacterProgress(val2); if (characterProgress != null) { bool item = false; if (characterProgress.unlocked) { list2.Add(val2); item = true; } if (stage == Utility.GetCurrentStage()) { list.Add((((val != null) ? val.GetCharacterName(val2) : null) ?? string.Empty, item)); } } } float num = (float)list2.Count / (float)array.Length * 100f; if (StageData.Characters.Length >= i + 1) { StageData.Characters[i] = num; } } StageData.CurrentStageCharacterInfo = list.ToArray(); if (save) { Tracking.SaveProgressData(Core.Instance.SaveManager.CurrentSaveSlot.saveSlotId); } } internal static Stage FromReptileStage(Stage stage) { //IL_0000: 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_002c: Expected I4, but got Unknown return (stage - 4) switch { 1 => Stage.Hideout, 0 => Stage.Versum, 7 => Stage.Square, 8 => Stage.Brink, 2 => Stage.Mall, 5 => Stage.Pyramid, 3 => Stage.Mataan, _ => Stage.MAX, }; } internal static St