Decompiled source of GRACEPLUS v1.4.6
GRACEPlus.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BombRushMP.Common; using BombRushMP.Common.Packets; using BombRushMP.Plugin; using BombRushMP.Plugin.Gamemodes; using CommonAPI; using CommonAPI.Phone; using GRACEPlus.Patches; using GRACEPlus.Phone; using HarmonyLib; using Reptile; using Reptile.Phone; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("GRACEPlus")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("GRACEPlus")] [assembly: AssemblyTitle("GRACEPlus")] [assembly: AssemblyVersion("1.0.0.0")] namespace GRACEPlus { public enum TimerUILocation { Left, Center, Right } [BepInPlugin("com.snail.graceplus", "GRACEPlus", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class GRACEPlusPlugin : BaseUnityPlugin { private static Harmony _harmony; private static string _recordsFilePath; private static string _teamRecordsFilePath; private static string _customStatsFilePath; private static string _mapStatsFolder; private static Dictionary<int, double> _bestTimes = new Dictionary<int, double>(); private static Dictionary<int, double> _bestTeamTimes = new Dictionary<int, double>(); private static int _customWins = 0; private static int _customLosses = 0; private static int _customGamesPlayed = 0; private static int _customSoloWins = 0; private static int _customTeamWins = 0; private static int _customTeamGamesPlayed = 0; private static bool _gameCancelled = false; private static int _maxPossibleScore = 0; public static ConfigEntry<string> FirstWinMessage; public static ConfigEntry<string> NewRecordMessage; public static ConfigEntry<string> WinMessage; public static ConfigEntry<string> LoseMessage; public static ConfigEntry<string> FirstWinColor; public static ConfigEntry<string> NewRecordColor; public static ConfigEntry<string> WinColor; public static ConfigEntry<string> LoseColor; public static ConfigEntry<bool> SaveAllMatchResults; public static ConfigEntry<bool> TrackMapStats; public static ConfigEntry<bool> HideChatDuringRace; public static ConfigEntry<bool> HideLobbyUIDuringRace; public static ConfigEntry<bool> HideMinimapDuringRace; public static ConfigEntry<bool> HideTagCounterDuringRace; public static ConfigEntry<bool> HideRaceTimer; public static ConfigEntry<string> TagCounterColor; public static ConfigEntry<bool> UseCustomTagCounterColor; public static ConfigEntry<string> CountdownColor; public static ConfigEntry<bool> UseCustomCountdownColor; public static ConfigEntry<string> TimerColor; public static ConfigEntry<bool> UseCustomTimerColor; public static ConfigEntry<string> ScoreTextColor; public static ConfigEntry<bool> UseCustomScoreTextColor; public static ConfigEntry<string> WinCounterColor; public static ConfigEntry<bool> UseCustomWinCounterColor; public static ConfigEntry<bool> ShowLobbyWinCounter; public static ConfigEntry<TimerUILocation> TagCounterLocation; public static ConfigEntry<TimerUILocation> TimerLocation; public static ConfigEntry<string> LobbyNameText; public static ConfigEntry<string> LobbyNameColor; public static ConfigEntry<bool> UseCustomLobbyName; public static ConfigEntry<bool> UseCustomGraffitiIndicator; public static ConfigEntry<bool> CustomGraffitiOutline; public static ConfigEntry<string> CustomGraffitiOutlineHexColor; public static ConfigEntry<bool> UseConfigHexForGraffitiOutline; public static ConfigEntry<string> GraffitiOutlinePreset; public static ConfigEntry<bool> HideAllAutomatedMessages; public static ConfigEntry<bool> LogEvents; public static ConfigEntry<bool> AutoStartGRaceRounds; public static ConfigEntry<int> AutoStartDelaySeconds; internal static GRACEPlusPlugin Instance; private static Coroutine _autoStartCoroutine; private static Coroutine _quickStartCoroutine; private static string _debugLogFilePath; public static string GracePlusFolderPath { get; private set; } public static string ModFolder { get; private set; } public static string GetAppIconPath() { if (string.IsNullOrEmpty(ModFolder)) { return null; } try { string path = Path.Combine(ModFolder, "backendTextures", "AppIcon"); if (!Directory.Exists(path)) { return null; } string[] files = Directory.GetFiles(path, "*.png", SearchOption.TopDirectoryOnly); return (files.Length != 0) ? files[0] : null; } catch { return null; } } private void Awake() { //IL_0633: Unknown result type (might be due to invalid IL or missing references) //IL_063d: Expected O, but got Unknown //IL_0647: Unknown result type (might be due to invalid IL or missing references) //IL_0651: Expected O, but got Unknown Instance = this; ModFolder = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); GracePlusFolderPath = Path.Combine(Paths.BepInExRootPath, "GRACEPlus"); Directory.CreateDirectory(GracePlusFolderPath); AppGRACEPlus.Initialize(); FirstWinMessage = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", "FirstWinMessage", "FIRST {gamemode}WIN! GRACE Ended IN {time} Seconds on Stage {stage}!", "Message for first win on a stage. Variables: {gamemode}, {time}, {stage}"); NewRecordMessage = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", "NewRecordMessage", "NEW {gamemode}RECORD! GRACE Ended IN {time} Seconds on Stage {stage}! (Previous: {previous}s)", "Message for new record. Variables: {gamemode}, {time}, {stage}, {previous}"); WinMessage = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", "WinMessage", "{gamemode}GRACE Ended IN {time} Seconds! You WON! (Best: {best}s)", "Message for winning without a new record. Variables: {gamemode}, {time}, {best}"); LoseMessage = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", "LoseMessage", "{gamemode}GRACE Ended IN {time} Seconds! You LOST!", "Message for losing a race. Variables: {gamemode}, {time}"); FirstWinColor = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", "FirstWinColor", "yellow", "Color for first win messages (red, green, blue, yellow, white)"); NewRecordColor = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", "NewRecordColor", "yellow", "Color for new record messages (red, green, blue, yellow, white)"); WinColor = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", "WinColor", "green", "Color for win messages (red, green, blue, yellow, white)"); LoseColor = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", "LoseColor", "yellow", "Color for lose messages (red, green, blue, yellow, white)"); HideAllAutomatedMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("Messages", "HideAllAutomatedMessages", false, "When enabled, the mod will not send any automated messages to chat (win/lose/record messages, auto-start notifications). Responses to chat commands like /mapstats and /avgtime will still be shown."); SaveAllMatchResults = ((BaseUnityPlugin)this).Config.Bind<bool>("Features", "SaveAllMatchResults", false, "Save all match results to daily text files in the mod folder"); TrackMapStats = ((BaseUnityPlugin)this).Config.Bind<bool>("Features", "TrackMapStats", false, "Track detailed statistics for each map including wins, losses, and multiplayer status"); HideChatDuringRace = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "HideChatDuringRace", false, "Hide BombRushMP's chat UI during GRACE races for a cleaner screen"); HideLobbyUIDuringRace = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "HideLobbyUIDuringRace", false, "Hide BombRushMP's lobby UI during GRACE races for a cleaner screen"); HideMinimapDuringRace = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "HideMinimapDuringRace", false, "Hide BombRushMP's minimap during GRACE races for a cleaner screen"); HideTagCounterDuringRace = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "HideTagCounterDuringRace", false, "Hide the graffiti spots counter during GRACE races for a cleaner screen"); HideRaceTimer = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "HideRaceTimer", true, "When enabled (default), the BombRushMP race timer is shown. When disabled, the race timer is hidden during GRACE races."); TagCounterColor = ((BaseUnityPlugin)this).Config.Bind<string>("UI Options", "TagCounterColor", "red", "Color for the graffiti spots counter (red, green, blue, yellow, white, cyan, magenta, black, gray)"); UseCustomTagCounterColor = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "UseCustomTagCounterColor", false, "Enable custom color for the graffiti spots counter during GRACE races"); UseCustomCountdownColor = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "UseCustomCountdownColor", false, "Enable custom color for the countdown timer during GRACE races"); CountdownColor = ((BaseUnityPlugin)this).Config.Bind<string>("UI Options", "CountdownColor", "red", "Color for the countdown timer (red, green, blue, yellow, white, cyan, magenta, black, gray)"); UseCustomTimerColor = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "UseCustomTimerColor", false, "Enable custom color for the race timer text (elapsed time) during GRACE races. Uses TimerColor in RGB."); TimerColor = ((BaseUnityPlugin)this).Config.Bind<string>("UI Options", "TimerColor", "255,255,255", "Race timer text color in RGB. Format: R,G,B (e.g. 255,128,0 for orange) or R,G,B,A for opacity. Values 0-255. Only used when UseCustomTimerColor is enabled."); UseCustomScoreTextColor = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "UseCustomScoreTextColor", false, "Enable custom color for score text in lobby UI"); ScoreTextColor = ((BaseUnityPlugin)this).Config.Bind<string>("UI Options", "ScoreTextColor", "red", "Color for score text in lobby UI (red, green, blue, yellow, white, cyan, magenta, black, gray)"); UseCustomWinCounterColor = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "UseCustomWinCounterColor", false, "Enable custom color for the win counter (the number in parentheses) in lobby UI. Uses WinCounterColor in RGB."); WinCounterColor = ((BaseUnityPlugin)this).Config.Bind<string>("UI Options", "WinCounterColor", "255,200,100", "Win counter color in RGB (the number shown next to score, e.g. (3)). Format: R,G,B (e.g. 255,200,100 for gold). Values 0-255. Only used when UseCustomWinCounterColor is enabled."); ShowLobbyWinCounter = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "ShowLobbyWinCounter", true, "Show win counter (e.g. (3)) next to score in lobby UI. When false, only the score is shown."); TagCounterLocation = ((BaseUnityPlugin)this).Config.Bind<TimerUILocation>("UI Options", "TagCounterLocation", TimerUILocation.Center, "Screen position for the tag counter (e.g. 3/10). Options: Left, Center, Right."); TimerLocation = ((BaseUnityPlugin)this).Config.Bind<TimerUILocation>("UI Options", "TimerLocation", TimerUILocation.Center, "Screen position for the race timer (elapsed time). Options: Left, Center, Right."); UseCustomLobbyName = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "UseCustomLobbyName", false, "Enable custom lobby name and color"); LobbyNameText = ((BaseUnityPlugin)this).Config.Bind<string>("UI Options", "LobbyNameText", "GRace Plus Lobby", "Custom text for lobby name"); LobbyNameColor = ((BaseUnityPlugin)this).Config.Bind<string>("UI Options", "LobbyNameColor", "yellow", "Color for lobby name text (red, green, blue, yellow, white, cyan, magenta, black, gray)"); CustomGraffitiOutline = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "CustomGraffitiOutline", false, "When enabled, race graffiti spots use the custom outline color from Custom Graffiti Outline Hex Color during GRACE races."); CustomGraffitiOutlineHexColor = ((BaseUnityPlugin)this).Config.Bind<string>("UI Options", "Custom Graffiti Outline Hex Color", "#FF0000", "Hex color for the graffiti spot outline when CustomGraffitiOutline is enabled and Use Config Hex is on (e.g. #FF0000 or FF0000). Default is red."); UseConfigHexForGraffitiOutline = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "UseConfigHexForGraffitiOutline", false, "When enabled, use Custom Graffiti Outline Hex Color from this config file and ignore the in-app Graffiti Outline Color preset."); GraffitiOutlinePreset = ((BaseUnityPlugin)this).Config.Bind<string>("UI Options", "GraffitiOutlinePreset", "normal", "In-app preset: normal, orange, red, blue, pink, purple, green, white, black. Only used when UseConfigHexForGraffitiOutline is false."); UseCustomGraffitiIndicator = ((BaseUnityPlugin)this).Config.Bind<bool>("UI Options", "UseCustomGraffitiIndicator", false, "When enabled, use the first PNG in GRACEPlus/Textures/GraffitiSign as the on-screen graffiti race indicator instead of BombRushMP's default."); string gracePlusFolderPath = GracePlusFolderPath; Directory.CreateDirectory(gracePlusFolderPath); Directory.CreateDirectory(Path.Combine(gracePlusFolderPath, "Textures")); Directory.CreateDirectory(Path.Combine(gracePlusFolderPath, "Textures", "GraffitiSign")); _recordsFilePath = Path.Combine(gracePlusFolderPath, "grace_records.txt"); _teamRecordsFilePath = Path.Combine(gracePlusFolderPath, "grace_team_records.txt"); _debugLogFilePath = Path.Combine(gracePlusFolderPath, "debug_log.txt"); string text = Path.Combine(gracePlusFolderPath, "savefile"); Directory.CreateDirectory(text); _customStatsFilePath = Path.Combine(text, "custom_grace_stats.txt"); _mapStatsFolder = Path.Combine(gracePlusFolderPath, "Map Stats"); Directory.CreateDirectory(_mapStatsFolder); LoadRecords(); LoadTeamRecords(); LoadCustomStats(); LogEvents = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogEvents", false, "Enable detailed event logging to debug_log.txt for troubleshooting"); AutoStartGRaceRounds = ((BaseUnityPlugin)this).Config.Bind<bool>("Lobby", "AutoStartGRaceRounds", false, "Automatically start the next GRACE round after a round ends (host only)"); AutoStartDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<int>("Lobby", "AutoStartDelaySeconds", 5, new ConfigDescription("Seconds to wait before auto-starting the next round (when AutoStartGRaceRounds is enabled)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 60), Array.Empty<object>())); _harmony = new Harmony("com.snail.graceplus"); _harmony.PatchAll(); BlockStageSwitchDuringRacePatch.Apply(_harmony); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } public static void ShowNotification(string message) { ConfigEntry<bool> hideAllAutomatedMessages = HideAllAutomatedMessages; if (hideAllAutomatedMessages == null || !hideAllAutomatedMessages.Value) { ChatUI instance = ChatUI.Instance; if (instance != null) { instance.AddMessage(message); } } } public static void ShowCommandResponse(string message) { ChatUI instance = ChatUI.Instance; if (instance != null) { instance.AddMessage(message); } } public static void RunMapStatsCommand() { ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } Lobby val = (Lobby)obj; if (val != null) { int stage = val.LobbyState.Stage; if (!TrackMapStats.Value && !HasMapStatsFile(stage)) { ShowCommandResponse("<color=yellow>Enable Map Stat Tracking in config, run some games on this map, and then you will have stats to see here</color>"); return; } double? averageTime = GetAverageTime(stage); double? bestTime = GetBestTime(stage); double? averageTime2 = GetAverageTime(stage, isTeamMode: true); double? bestTime2 = GetBestTime(stage, isTeamMode: true); var (num, num2, num3, num4) = GetMapStats(stage); if (averageTime.HasValue || bestTime.HasValue || averageTime2.HasValue || bestTime2.HasValue || num > 0 || num2 > 0 || num3 > 0) { ShowCommandResponse($"<color=green>=== Stage {stage} Statistics ===</color>"); if (averageTime.HasValue) { ShowCommandResponse($"<color=green>Avg Win Time: {averageTime.Value:F1}s</color>"); } else { ShowCommandResponse("<color=yellow>Avg Win Time: No wins recorded</color>"); } if (bestTime.HasValue) { ShowCommandResponse($"<color=green>Best Time: {bestTime.Value:F1}s</color>"); } else { ShowCommandResponse("<color=yellow>Best Time: No record set</color>"); } if (num > 0 || num2 > 0) { ShowCommandResponse($"<color=green>Multiplayer Win Ratio: {num4:F1}%</color>"); ShowCommandResponse($"<color=green>Multiplayer Wins: {num}</color>"); ShowCommandResponse($"<color=green>Multiplayer Losses: {num2}</color>"); } else { ShowCommandResponse("<color=yellow>Multiplayer Win Ratio: No multiplayer games</color>"); ShowCommandResponse("<color=yellow>Multiplayer Wins: 0</color>"); ShowCommandResponse("<color=yellow>Multiplayer Losses: 0</color>"); } ShowCommandResponse($"<color=green>Solo Games: {num3}</color>"); if (averageTime2.HasValue || bestTime2.HasValue) { ShowCommandResponse("<color=yellow>=== Team Mode ===</color>"); if (averageTime2.HasValue) { ShowCommandResponse($"<color=yellow>Avg Team Time: {averageTime2.Value:F1}s</color>"); } else { ShowCommandResponse("<color=yellow>Avg Team Time: No wins recorded</color>"); } if (bestTime2.HasValue) { ShowCommandResponse($"<color=yellow>Best Team Time: {bestTime2.Value:F1}s</color>"); } else { ShowCommandResponse("<color=yellow>Best Team Time: No record set</color>"); } } } else { ShowCommandResponse("<color=yellow>Enable Map Stat Tracking in config, run some games on this map, and then you will have stats to see here</color>"); } } else { ShowCommandResponse("<color=red>You must be in a lobby to use /mapstats!</color>"); } } public static void RunGraceStatsCommand() { MPSaveData instance = MPSaveData.Instance; if (instance?.Stats != null) { MPStats stats = instance.Stats; uint value = 0u; uint value2 = 0u; uint value3 = 0u; stats.GamemodesWon.TryGetValue((GamemodeIDs)1, out value); stats.GamemodesPlayed.TryGetValue((GamemodeIDs)1, out value2); stats.GamemodesPlayedAlone.TryGetValue((GamemodeIDs)1, out value3); uint elitesPlayedAgainst = stats.ElitesPlayedAgainst; uint elitesBeaten = stats.ElitesBeaten; ShowCommandResponse("<color=yellow>=== ACN Stats: ===</color>"); ShowCommandResponse($"<color=yellow>GRACE Wins: {value}</color>"); ShowCommandResponse($"<color=yellow>GRACE Games Played: {value2}</color>"); ShowCommandResponse($"<color=yellow>GRACE Solo Games: {value3}</color>"); ShowCommandResponse($"<color=yellow>Elites Played Against: {elitesPlayedAgainst}</color>"); ShowCommandResponse($"<color=yellow>Elites Beaten: {elitesBeaten}</color>"); (int wins, int losses, int gamesPlayed, int soloWins, int teamWins, int teamGamesPlayed) customStats = GetCustomStats(); int item = customStats.wins; int item2 = customStats.losses; int item3 = customStats.soloWins; int item4 = customStats.teamWins; int item5 = customStats.teamGamesPlayed; int num = item + item2; double num2 = ((num > 0) ? ((double)item / (double)num * 100.0) : 0.0); double num3 = ((item5 > 0) ? ((double)item4 / (double)item5 * 100.0) : 0.0); ShowCommandResponse("<color=green>=== GRACEPlus Custom Stats: ===</color>"); ShowCommandResponse($"<color=green>Solo Wins: {item3}</color>"); ShowCommandResponse($"<color=green>Multiplayer Wins: {item}</color>"); ShowCommandResponse($"<color=green>Multiplayer Losses: {item2}</color>"); ShowCommandResponse($"<color=green>Multiplayer Games: {num}</color>"); ShowCommandResponse($"<color=green>Multiplayer Win Ratio: {num2:F1}%</color>"); ShowCommandResponse($"<color=green>Team Wins: {item4}</color>"); ShowCommandResponse($"<color=green>Team Games: {item5}</color>"); ShowCommandResponse($"<color=green>Team Win Ratio: {num3:F1}%</color>"); } } public static void RunGraceHelpCommand() { ShowCommandResponse("<color=green>=== GRACEPlus Commands ===</color>"); ShowCommandResponse("<color=white>/showbest - Show your best time for current stage</color>"); ShowCommandResponse("<color=white>/timeclear - Clear best time for current stage</color>"); ShowCommandResponse("<color=white>/timeclearall - Clear all saved best times</color>"); ShowCommandResponse("<color=white>/teamtimeclear - Clear team best time for current stage</color>"); ShowCommandResponse("<color=white>/teamtimeclearall - Clear all saved team best times</color>"); ShowCommandResponse("<color=white>/gracestats - Show lifetime GRACE statistics</color>"); ShowCommandResponse("<color=white>/gracestatsclear - Clear custom statistics</color>"); ShowCommandResponse("<color=white>/avgtime - Show average win time for current stage</color>"); ShowCommandResponse("<color=white>/mapstats - Show detailed stats for current stage</color>"); ShowCommandResponse("<color=white>/clearmapstats - Clear map stats for current stage</color>"); ShowCommandResponse("<color=white>/clearmapstatsall - Clear all map statistics</color>"); ShowCommandResponse("<color=white>/start - Start the round (host only)</color>"); ShowCommandResponse("<color=white>/end - End the round (host only)</color>"); ShowCommandResponse("<color=white>/leave - Leave the lobby</color>"); ShowCommandResponse("<color=white>/gracehelp - Show this help message</color>"); } public static Color? GetEffectiveOutlineColor() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) if (CustomGraffitiOutline == null || !CustomGraffitiOutline.Value) { return null; } if (UseConfigHexForGraffitiOutline != null && UseConfigHexForGraffitiOutline.Value && CustomGraffitiOutlineHexColor != null && !string.IsNullOrWhiteSpace(CustomGraffitiOutlineHexColor.Value)) { string text = CustomGraffitiOutlineHexColor.Value.Trim(); if (!text.StartsWith("#")) { text = "#" + text; } Color value = default(Color); if (ColorUtility.TryParseHtmlString(text, ref value)) { return value; } } string text2 = ((GraffitiOutlinePreset != null) ? GraffitiOutlinePreset.Value : "normal"); if (string.IsNullOrEmpty(text2) || string.Equals(text2, "normal", StringComparison.OrdinalIgnoreCase)) { return null; } string presetOutlineHex = GetPresetOutlineHex(text2); Color value2 = default(Color); if (presetOutlineHex != null && ColorUtility.TryParseHtmlString(presetOutlineHex, ref value2)) { return value2; } return null; } public static string GetCustomGraffitiSignPath() { if (UseCustomGraffitiIndicator == null || !UseCustomGraffitiIndicator.Value) { return null; } try { string path = Path.Combine(Path.Combine(Paths.BepInExRootPath, "GRACEPlus"), "Textures", "GraffitiSign"); if (!Directory.Exists(path)) { return null; } string[] files = Directory.GetFiles(path, "*.png", SearchOption.TopDirectoryOnly); return (files.Length != 0) ? files[0] : null; } catch { return null; } } public static string GetPresetOutlineHex(string preset) { if (string.IsNullOrEmpty(preset)) { return null; } return preset.ToLowerInvariant() switch { "orange" => "#FFA500", "red" => "#FF0000", "blue" => "#2080F0", "pink" => "#FF69B4", "purple" => "#9932CC", "green" => "#00FF00", "white" => "#FFFFFF", "black" => "#111111", _ => null, }; } public static void ScheduleAutoStartIfEnabled() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Invalid comparison between Unknown and I4 //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Invalid comparison between Unknown and I4 if (!AutoStartGRaceRounds.Value || (Object)(object)Instance == (Object)null) { return; } ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } Lobby val = (Lobby)obj; if (val != null && val.LobbyState.HostId == instance.LocalID && ((int)val.LobbyState.Gamemode == 1 || (int)val.LobbyState.Gamemode == 2)) { int delaySeconds = Math.Max(1, Math.Min(AutoStartDelaySeconds.Value, 60)); if (_autoStartCoroutine != null) { ((MonoBehaviour)Instance).StopCoroutine(_autoStartCoroutine); } _autoStartCoroutine = ((MonoBehaviour)Instance).StartCoroutine(AutoStartCoroutine(delaySeconds)); } } private static IEnumerator AutoStartCoroutine(int delaySeconds) { ShowNotification($"<color=yellow>Auto-starting next round in {delaySeconds} seconds...</color>"); yield return (object)new WaitForSecondsRealtime((float)delaySeconds); _autoStartCoroutine = null; ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } Lobby val = (Lobby)obj; if (val != null && val.LobbyState.HostId == instance.LocalID) { if (val.InGame) { ShowNotification("<color=yellow>Round already started, skipping auto-start.</color>"); yield break; } instance.ClientLobbyManager.StartGame(); ShowNotification("<color=green>Auto-started next round!</color>"); } } public static void ScheduleQuickStartGame() { if (!((Object)(object)Instance == (Object)null)) { if (_quickStartCoroutine != null) { ((MonoBehaviour)Instance).StopCoroutine(_quickStartCoroutine); _quickStartCoroutine = null; } _quickStartCoroutine = ((MonoBehaviour)Instance).StartCoroutine(QuickStartBufferCoroutine()); } } private static IEnumerator QuickStartBufferCoroutine() { yield return (object)new WaitForSecondsRealtime(1f); _quickStartCoroutine = null; ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } Lobby val = (Lobby)obj; if (val != null && val.LobbyState.HostId == instance.LocalID && ((int)val.LobbyState.Gamemode == 1 || (int)val.LobbyState.Gamemode == 2) && !val.InGame) { instance.ClientLobbyManager.StartGame(); } } public static void ShowConfigurableMessage(ConfigEntry<string> messageTemplate, ConfigEntry<string> color, params (string key, string value)[] variables) { ConfigEntry<bool> hideAllAutomatedMessages = HideAllAutomatedMessages; if ((hideAllAutomatedMessages == null || !hideAllAutomatedMessages.Value) && messageTemplate != null && color != null && !string.IsNullOrWhiteSpace(messageTemplate.Value)) { string text = messageTemplate.Value; for (int i = 0; i < variables.Length; i++) { (string key, string value) tuple = variables[i]; string item = tuple.key; string item2 = tuple.value; string oldValue = "{" + item + "}"; text = text.Replace(oldValue, item2); } string text2 = "<color=" + color.Value + ">" + text + "</color>"; ChatUI instance = ChatUI.Instance; if (instance != null) { instance.AddMessage(text2); } } } public static Color ParseColor(string colorName) { //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0165: 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) switch (colorName.ToLower()) { case "red": return Color.red; case "green": return Color.green; case "blue": return Color.blue; case "yellow": return Color.yellow; case "white": return Color.white; case "cyan": return Color.cyan; case "magenta": return Color.magenta; case "black": return Color.black; case "gray": case "grey": return Color.gray; default: return Color.white; } } public static Color ParseColorRgb(string rgb) { //IL_0008: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(rgb)) { return Color.white; } string[] array = rgb.Split(new char[1] { ',' }); if (array.Length < 3) { return Color.white; } if (!byte.TryParse(array[0].Trim(), out var result)) { return Color.white; } if (!byte.TryParse(array[1].Trim(), out var result2)) { return Color.white; } if (!byte.TryParse(array[2].Trim(), out var result3)) { return Color.white; } float num = 1f; if (array.Length >= 4 && byte.TryParse(array[3].Trim(), out var result4)) { num = (float)(int)result4 / 255f; } return new Color((float)(int)result / 255f, (float)(int)result2 / 255f, (float)(int)result3 / 255f, num); } private static void LoadRecords() { if (!File.Exists(_recordsFilePath)) { return; } string[] array = File.ReadAllLines(_recordsFilePath); foreach (string text in array) { if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("#")) { string[] array2 = text.Split(new char[1] { '=' }); if (array2.Length == 2 && int.TryParse(array2[0].Trim(), out var result) && double.TryParse(array2[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { _bestTimes[result] = result2; } } } } private static void SaveRecords() { List<string> list = new List<string> { "# GRACEPlus Best Times (Solo Mode)", "# Format: StageID=TimeInSeconds", "# Generated automatically - do not edit manually", "" }; foreach (KeyValuePair<int, double> bestTime in _bestTimes) { list.Add(string.Format("{0}={1}", bestTime.Key, bestTime.Value.ToString("F1", CultureInfo.InvariantCulture))); } File.WriteAllLines(_recordsFilePath, list); } private static void LoadTeamRecords() { if (!File.Exists(_teamRecordsFilePath)) { return; } string[] array = File.ReadAllLines(_teamRecordsFilePath); foreach (string text in array) { if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("#")) { string[] array2 = text.Split(new char[1] { '=' }); if (array2.Length == 2 && int.TryParse(array2[0].Trim(), out var result) && double.TryParse(array2[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { _bestTeamTimes[result] = result2; } } } } private static void SaveTeamRecords() { List<string> list = new List<string> { "# GRACEPlus Best Times (Team Mode)", "# Format: StageID=TimeInSeconds", "# Generated automatically - do not edit manually", "" }; foreach (KeyValuePair<int, double> bestTeamTime in _bestTeamTimes) { list.Add(string.Format("{0}={1}", bestTeamTime.Key, bestTeamTime.Value.ToString("F1", CultureInfo.InvariantCulture))); } File.WriteAllLines(_teamRecordsFilePath, list); } public static bool CheckAndUpdateBestTime(int stageId, double timeInSeconds, bool isTeamMode, out double? previousBest) { bool flag = false; previousBest = null; Dictionary<int, double> dictionary = (isTeamMode ? _bestTeamTimes : _bestTimes); if (!dictionary.ContainsKey(stageId)) { dictionary[stageId] = timeInSeconds; flag = true; } else if (timeInSeconds < dictionary[stageId]) { previousBest = dictionary[stageId]; dictionary[stageId] = timeInSeconds; flag = true; } if (flag) { if (isTeamMode) { SaveTeamRecords(); } else { SaveRecords(); } } return flag; } public static double? GetBestTime(int stageId, bool isTeamMode = false) { Dictionary<int, double> dictionary = (isTeamMode ? _bestTeamTimes : _bestTimes); if (!dictionary.ContainsKey(stageId)) { return null; } return dictionary[stageId]; } public static bool ClearBestTime(int stageId, bool isTeamMode = false) { Dictionary<int, double> dictionary = (isTeamMode ? _bestTeamTimes : _bestTimes); if (dictionary.ContainsKey(stageId)) { dictionary.Remove(stageId); if (isTeamMode) { SaveTeamRecords(); } else { SaveRecords(); } return true; } return false; } public static int ClearAllBestTimes(bool isTeamMode = false) { Dictionary<int, double> dictionary = (isTeamMode ? _bestTeamTimes : _bestTimes); int count = dictionary.Count; if (count > 0) { dictionary.Clear(); if (isTeamMode) { SaveTeamRecords(); return count; } SaveRecords(); } return count; } private static string GetDailyMatchResultsFilePath() { string path = Path.Combine(Paths.BepInExRootPath, "GRACEPlus"); string text = DateTime.Now.ToString("yyyy-MM-dd"); return Path.Combine(path, "Graces_" + text + ".txt"); } public static void SaveMatchResult(int stageId, double timeInSeconds, bool won, bool isTeamMode, bool isNewRecord, double? previousBest) { if (!SaveAllMatchResults.Value) { return; } string dailyMatchResultsFilePath = GetDailyMatchResultsFilePath(); string text = DateTime.Now.ToString("HH:mm:ss"); string text2 = (isTeamMode ? "Team GRACE" : "Solo GRACE"); string text3 = (won ? "WIN" : "LOSS"); string text4 = ""; if (won && isNewRecord) { text4 = ((!previousBest.HasValue) ? " [FIRST WIN!]" : $" [NEW RECORD! Previous: {previousBest.Value:F1}s]"); } else if (won) { double? bestTime = GetBestTime(stageId); if (bestTime.HasValue) { text4 = $" [Best: {bestTime.Value:F1}s]"; } } string text5 = $"[{text}] Stage {stageId} | {text2} | {text3} | {timeInSeconds:F1}s{text4}"; if (!File.Exists(dailyMatchResultsFilePath)) { List<string> contents = new List<string> { "# GRACEPlus Daily Match Results", $"# Date: {DateTime.Now:yyyy-MM-dd}", "# Format: [Time] Stage ID | Gamemode | Result | Duration | Additional Info", "" }; File.WriteAllLines(dailyMatchResultsFilePath, contents); } File.AppendAllText(dailyMatchResultsFilePath, text5 + Environment.NewLine); } private static void LoadCustomStats() { if (!File.Exists(_customStatsFilePath)) { return; } string[] array = File.ReadAllLines(_customStatsFilePath); foreach (string text in array) { if (string.IsNullOrWhiteSpace(text) || text.StartsWith("#")) { continue; } string[] array2 = text.Split(new char[1] { '=' }); if (array2.Length != 2) { continue; } string text2 = array2[0].Trim(); if (int.TryParse(array2[1].Trim(), out var result)) { switch (text2) { case "Wins": _customWins = result; break; case "Losses": _customLosses = result; break; case "GamesPlayed": _customGamesPlayed = result; break; case "SoloWins": _customSoloWins = result; break; case "TeamWins": _customTeamWins = result; break; case "TeamGamesPlayed": _customTeamGamesPlayed = result; break; } } } } private static void SaveCustomStats() { List<string> contents = new List<string> { "# GRACEPlus Custom Statistics", "# Format: StatName=Value", "# Generated automatically - do not edit manually", "", $"Wins={_customWins}", $"Losses={_customLosses}", $"GamesPlayed={_customGamesPlayed}", $"SoloWins={_customSoloWins}", $"TeamWins={_customTeamWins}", $"TeamGamesPlayed={_customTeamGamesPlayed}" }; File.WriteAllLines(_customStatsFilePath, contents); } public static void IncrementCustomWins(bool isSolo, bool isTeamMode) { if (isTeamMode) { _customTeamWins++; _customTeamGamesPlayed++; } else if (isSolo) { _customSoloWins++; _customGamesPlayed++; } else { _customWins++; _customGamesPlayed++; } SaveCustomStats(); } public static void IncrementCustomLosses(bool isTeamMode) { if (isTeamMode) { _customTeamGamesPlayed++; } else { _customLosses++; _customGamesPlayed++; } SaveCustomStats(); } public static (int wins, int losses, int gamesPlayed, int soloWins, int teamWins, int teamGamesPlayed) GetCustomStats() { return (_customWins, _customLosses, _customGamesPlayed, _customSoloWins, _customTeamWins, _customTeamGamesPlayed); } public static void SetGameCancelled(bool cancelled) { _gameCancelled = cancelled; } public static bool IsGameCancelled() { return _gameCancelled; } public static void SetMaxPossibleScore(int maxScore) { _maxPossibleScore = maxScore; } public static int GetMaxPossibleScore() { return _maxPossibleScore; } public static void ResetMaxPossibleScore() { _maxPossibleScore = 0; } public static void SaveWinTime(int stageId, double timeInSeconds) { if (TrackMapStats.Value) { string path = Path.Combine(_mapStatsFolder, $"Stage_{stageId}.txt"); string arg = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); string text = $"{arg},{timeInSeconds:F1}"; File.AppendAllText(path, text + Environment.NewLine); } } public static void SaveMapStats(int stageId, double timeInSeconds, bool won, bool isMultiplayer, bool isTeamMode = false) { if (TrackMapStats.Value) { string path = Path.Combine(_mapStatsFolder, $"Stage_{stageId}.txt"); string text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); string text2 = (won ? "WIN" : "LOSS"); string text3 = (isMultiplayer ? "MULTIPLAYER" : "SOLO"); string text4 = (isTeamMode ? "TEAM" : "NORMAL"); string text5 = $"{text},{timeInSeconds:F1},{text2},{text3},{text4}"; if (!File.Exists(path)) { List<string> contents = new List<string> { "# GRACEPlus Map Statistics", $"# Stage: {stageId}", "# Format: Timestamp,Time,Result,GameType,ModeType", "# Result: WIN or LOSS", "# GameType: MULTIPLAYER or SOLO", "# ModeType: TEAM or NORMAL", "" }; File.WriteAllLines(path, contents); } File.AppendAllText(path, text5 + Environment.NewLine); } } public static double? GetAverageTime(int stageId, bool isTeamMode = false) { if (!TrackMapStats.Value) { return null; } string path = Path.Combine(_mapStatsFolder, $"Stage_{stageId}.txt"); if (!File.Exists(path)) { return null; } string[] array = File.ReadAllLines(path); List<double> list = new List<double>(); string text = (isTeamMode ? "TEAM" : "NORMAL"); string[] array2 = array; foreach (string text2 in array2) { if (string.IsNullOrWhiteSpace(text2) || text2.StartsWith("#")) { continue; } string[] array3 = text2.Split(new char[1] { ',' }); if (array3.Length >= 3 && double.TryParse(array3[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && array3[2] == "WIN") { if (array3.Length >= 5 && array3[4] == text) { list.Add(result); } else if (array3.Length < 5 && !isTeamMode) { list.Add(result); } } } if (list.Count <= 0) { return null; } return list.Average(); } public static int GetWinCount(int stageId) { if (!TrackMapStats.Value) { return 0; } string path = Path.Combine(_mapStatsFolder, $"Stage_{stageId}.txt"); if (!File.Exists(path)) { return 0; } string[] array = File.ReadAllLines(path); int num = 0; string[] array2 = array; foreach (string text in array2) { if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("#")) { string[] array3 = text.Split(new char[1] { ',' }); if (array3.Length >= 3 && array3[2] == "WIN") { num++; } } } return num; } public static (int multiplayerWins, int multiplayerLosses, int soloGames, double multiplayerWinRatio) GetMapStats(int stageId) { if (!TrackMapStats.Value) { return (0, 0, 0, 0.0); } string path = Path.Combine(_mapStatsFolder, $"Stage_{stageId}.txt"); if (!File.Exists(path)) { return (0, 0, 0, 0.0); } string[] array = File.ReadAllLines(path); int num = 0; int num2 = 0; int num3 = 0; string[] array2 = array; foreach (string text in array2) { if (string.IsNullOrWhiteSpace(text) || text.StartsWith("#")) { continue; } string[] array3 = text.Split(new char[1] { ',' }); if (array3.Length < 4) { continue; } string text2 = array3[2]; string text3 = array3[3]; if (text3 == "MULTIPLAYER") { if (text2 == "WIN") { num++; } else if (text2 == "LOSS") { num2++; } } else if (text3 == "SOLO") { num3++; } } double item = 0.0; int num4 = num + num2; if (num4 > 0) { item = (double)num / (double)num4 * 100.0; } return (num, num2, num3, item); } public static bool HasMapStatsFile(int stageId) { return File.Exists(Path.Combine(_mapStatsFolder, $"Stage_{stageId}.txt")); } public static bool ClearMapStats(int stageId) { string path = Path.Combine(_mapStatsFolder, $"Stage_{stageId}.txt"); if (File.Exists(path)) { File.Delete(path); return true; } return false; } public static int ClearAllMapStats() { if (!Directory.Exists(_mapStatsFolder)) { return 0; } string[] files = Directory.GetFiles(_mapStatsFolder, "Stage_*.txt"); int num = 0; string[] array = files; foreach (string path in array) { try { File.Delete(path); num++; } catch { } } return num; } public static bool ClearCustomStats() { if (File.Exists(_customStatsFilePath)) { File.Delete(_customStatsFilePath); _customWins = 0; _customLosses = 0; _customGamesPlayed = 0; _customSoloWins = 0; _customTeamWins = 0; _customTeamGamesPlayed = 0; return true; } return false; } public static void DebugLog(string message) { if (!LogEvents.Value) { return; } try { string text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); string text2 = "[" + text + "] " + message; File.AppendAllText(_debugLogFilePath, text2 + Environment.NewLine); } catch { } } public static void ClearDebugLog() { try { if (File.Exists(_debugLogFilePath)) { File.Delete(_debugLogFilePath); } DebugLog("=== Debug Log Started ==="); } catch { } } } public static class PluginInfo { public const string PLUGIN_GUID = "com.snail.graceplus"; public const string PLUGIN_NAME = "GRACEPlus"; public const string PLUGIN_VERSION = "1.0.0"; } public static class MyPluginInfo { public const string PLUGIN_GUID = "GRACEPlus"; public const string PLUGIN_NAME = "GRACEPlus"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace GRACEPlus.Phone { public class AppGRACEPlus : CustomApp { private SimplePhoneButton _trackMapStatsButton; private SimplePhoneButton _autoRestartButton; private SimplePhoneButton _autoRestartDelayButton; private SimplePhoneButton _graffitiOutlineColorButton; private SimplePhoneButton _customGraffitiSignButton; private static bool _quickStartWaitingForLobby; private static readonly string[] GraffitiOutlinePresetOrder = new string[9] { "normal", "orange", "red", "blue", "pink", "purple", "green", "white", "black" }; public static void Initialize() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown try { Sprite val = null; string appIconPath = GRACEPlusPlugin.GetAppIconPath(); if (!string.IsNullOrEmpty(appIconPath) && File.Exists(appIconPath)) { try { byte[] array = File.ReadAllBytes(appIconPath); Texture2D val2 = new Texture2D(1, 1); if (ImageConversion.LoadImage(val2, array)) { ((Texture)val2).wrapMode = (TextureWrapMode)1; val = TextureUtility.CreateSpriteFromTexture(val2); } } catch { } } if ((Object)(object)val != (Object)null) { PhoneAPI.RegisterApp<AppGRACEPlus>("GRACE Plus", val); } else { PhoneAPI.RegisterApp<AppGRACEPlus>("GRACE Plus", (Sprite)null); } } catch { } } public override void OnAppInit() { ((CustomApp)this).OnAppInit(); ((CustomApp)this).CreateIconlessTitleBar("GRACE Plus", 80f); base.ScrollView = PhoneScrollView.Create((CustomApp)(object)this, 275f, 1600f); PopulateMainScreen(); } public override void OnAppEnable() { ((App)this).OnAppEnable(); if ((Object)(object)ClientController.Instance == (Object)null || !ClientController.Instance.Connected) { _quickStartWaitingForLobby = false; ClientController.PacketReceived = (Action<Packets, Packet>)Delegate.Remove(ClientController.PacketReceived, new Action<Packets, Packet>(OnPacketReceived_QuickStartLobbyCreation)); } PopulateMainScreen(); } private void PopulateMainScreen() { if (!((Object)(object)base.ScrollView == (Object)null)) { base.ScrollView.RemoveAllButtons(); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Quick Start"); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate { OnQuickStartPressed(); }); base.ScrollView.AddButton((PhoneButton)(object)val); SimplePhoneButton val2 = PhoneUIUtility.CreateSimpleButton("Quick Lobby"); ((PhoneButton)val2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val2).OnConfirm, (Action)delegate { OnQuickLobbyPressed(); }); base.ScrollView.AddButton((PhoneButton)(object)val2); SimplePhoneButton val3 = PhoneUIUtility.CreateSimpleButton("Game Settings"); ((PhoneButton)val3).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val3).OnConfirm, (Action)delegate { ShowGameSettingsView(); }); base.ScrollView.AddButton((PhoneButton)(object)val3); SimplePhoneButton val4 = PhoneUIUtility.CreateSimpleButton("Stats"); ((PhoneButton)val4).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val4).OnConfirm, (Action)delegate { ShowStatsView(); }); base.ScrollView.AddButton((PhoneButton)(object)val4); SimplePhoneButton val5 = PhoneUIUtility.CreateSimpleButton("Customize"); ((PhoneButton)val5).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val5).OnConfirm, (Action)delegate { ShowCustomizeView(); }); base.ScrollView.AddButton((PhoneButton)(object)val5); SimplePhoneButton val6 = PhoneUIUtility.CreateSimpleButton("UI Options"); ((PhoneButton)val6).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val6).OnConfirm, (Action)delegate { ShowUIOptionsView(); }); base.ScrollView.AddButton((PhoneButton)(object)val6); SimplePhoneButton val7 = PhoneUIUtility.CreateSimpleButton("Commands"); ((PhoneButton)val7).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val7).OnConfirm, (Action)delegate { ShowCommandsView(); }); base.ScrollView.AddButton((PhoneButton)(object)val7); } } private void ShowCommandsView() { base.ScrollView.RemoveAllButtons(); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Map Stats"); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate { GRACEPlusPlugin.RunMapStatsCommand(); }); base.ScrollView.AddButton((PhoneButton)(object)val); SimplePhoneButton val2 = PhoneUIUtility.CreateSimpleButton("Grace Stats"); ((PhoneButton)val2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val2).OnConfirm, (Action)delegate { GRACEPlusPlugin.RunGraceStatsCommand(); }); base.ScrollView.AddButton((PhoneButton)(object)val2); SimplePhoneButton val3 = PhoneUIUtility.CreateSimpleButton("Grace Help"); ((PhoneButton)val3).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val3).OnConfirm, (Action)delegate { GRACEPlusPlugin.RunGraceHelpCommand(); }); base.ScrollView.AddButton((PhoneButton)(object)val3); SimplePhoneButton val4 = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val4).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val4).OnConfirm, (Action)delegate { PopulateMainScreen(); }); base.ScrollView.AddButton((PhoneButton)(object)val4); } private static void OnQuickLobbyPressed() { ClientController instance = ClientController.Instance; if ((Object)(object)instance == (Object)null || !instance.Connected) { return; } ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; if (clientLobbyManager != null && clientLobbyManager.CurrentLobby == null && clientLobbyManager.CanJoinLobby()) { GamemodeSettings gamemodeSettings = GamemodeFactory.GetGamemodeSettings((GamemodeIDs)1); MPSaveData instance2 = MPSaveData.Instance; SavedGamemodeSettings val = ((instance2 != null) ? instance2.GetSavedSettings((GamemodeIDs)1) : null); if (val != null) { gamemodeSettings.ApplySaved(val); } clientLobbyManager.CreateLobby((GamemodeIDs)1, gamemodeSettings); } } private static void OnQuickStartPressed() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Invalid comparison between Unknown and I4 //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Invalid comparison between Unknown and I4 ClientController instance = ClientController.Instance; if ((Object)(object)instance == (Object)null || !instance.Connected) { return; } ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; Lobby val = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); if (val != null) { bool num = val.LobbyState.HostId == instance.LocalID; bool flag = (int)val.LobbyState.Gamemode == 1 || (int)val.LobbyState.Gamemode == 2; if (num && flag && !val.InGame) { GRACEPlusPlugin.ScheduleQuickStartGame(); } } else if (clientLobbyManager != null && clientLobbyManager.CanJoinLobby()) { GamemodeSettings gamemodeSettings = GamemodeFactory.GetGamemodeSettings((GamemodeIDs)1); MPSaveData instance2 = MPSaveData.Instance; SavedGamemodeSettings val2 = ((instance2 != null) ? instance2.GetSavedSettings((GamemodeIDs)1) : null); if (val2 != null) { gamemodeSettings.ApplySaved(val2); } _quickStartWaitingForLobby = true; ClientController.PacketReceived = (Action<Packets, Packet>)Delegate.Combine(ClientController.PacketReceived, new Action<Packets, Packet>(OnPacketReceived_QuickStartLobbyCreation)); clientLobbyManager.CreateLobby((GamemodeIDs)1, gamemodeSettings); } } private static void OnPacketReceived_QuickStartLobbyCreation(Packets packetId, Packet packet) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 if ((int)packetId == 14 && _quickStartWaitingForLobby) { _quickStartWaitingForLobby = false; ClientController.PacketReceived = (Action<Packets, Packet>)Delegate.Remove(ClientController.PacketReceived, new Action<Packets, Packet>(OnPacketReceived_QuickStartLobbyCreation)); GRACEPlusPlugin.ScheduleQuickStartGame(); } } private void ShowCustomizeView() { base.ScrollView.RemoveAllButtons(); _graffitiOutlineColorButton = PhoneUIUtility.CreateSimpleButton(GraffitiOutlineColorLabel()); SimplePhoneButton graffitiOutlineColorButton = _graffitiOutlineColorButton; ((PhoneButton)graffitiOutlineColorButton).OnConfirm = (Action)Delegate.Combine(((PhoneButton)graffitiOutlineColorButton).OnConfirm, (Action)delegate { CycleGraffitiOutlinePreset(); if ((Object)(object)_graffitiOutlineColorButton?.Label != (Object)null) { ((TMP_Text)_graffitiOutlineColorButton.Label).text = GraffitiOutlineColorLabel(); } }); base.ScrollView.AddButton((PhoneButton)(object)_graffitiOutlineColorButton); _customGraffitiSignButton = PhoneUIUtility.CreateSimpleButton(CustomGraffitiSignLabel()); SimplePhoneButton customGraffitiSignButton = _customGraffitiSignButton; ((PhoneButton)customGraffitiSignButton).OnConfirm = (Action)Delegate.Combine(((PhoneButton)customGraffitiSignButton).OnConfirm, (Action)delegate { if (GRACEPlusPlugin.UseCustomGraffitiIndicator != null) { GRACEPlusPlugin.UseCustomGraffitiIndicator.Value = !GRACEPlusPlugin.UseCustomGraffitiIndicator.Value; if ((Object)(object)_customGraffitiSignButton?.Label != (Object)null) { ((TMP_Text)_customGraffitiSignButton.Label).text = CustomGraffitiSignLabel(); } } }); base.ScrollView.AddButton((PhoneButton)(object)_customGraffitiSignButton); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate { PopulateMainScreen(); }); base.ScrollView.AddButton((PhoneButton)(object)val); } private void ShowUIOptionsView() { base.ScrollView.RemoveAllButtons(); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Visible"); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate { ShowVisibleView(); }); base.ScrollView.AddButton((PhoneButton)(object)val); SimplePhoneButton val2 = PhoneUIUtility.CreateSimpleButton("Locations"); ((PhoneButton)val2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val2).OnConfirm, (Action)delegate { ShowLocationsView(); }); base.ScrollView.AddButton((PhoneButton)(object)val2); SimplePhoneButton val3 = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val3).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val3).OnConfirm, (Action)delegate { PopulateMainScreen(); }); base.ScrollView.AddButton((PhoneButton)(object)val3); } private void ShowLocationsView() { base.ScrollView.RemoveAllButtons(); SimplePhoneButton timerLocationButton = PhoneUIUtility.CreateSimpleButton(TimerLocationLabel()); SimplePhoneButton obj = timerLocationButton; ((PhoneButton)obj).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj).OnConfirm, (Action)delegate { CycleLocation(GRACEPlusPlugin.TimerLocation); if ((Object)(object)timerLocationButton?.Label != (Object)null) { ((TMP_Text)timerLocationButton.Label).text = TimerLocationLabel(); } }); base.ScrollView.AddButton((PhoneButton)(object)timerLocationButton); SimplePhoneButton tagCounterLocationButton = PhoneUIUtility.CreateSimpleButton(TagCounterLocationLabel()); SimplePhoneButton obj2 = tagCounterLocationButton; ((PhoneButton)obj2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj2).OnConfirm, (Action)delegate { CycleLocation(GRACEPlusPlugin.TagCounterLocation); if ((Object)(object)tagCounterLocationButton?.Label != (Object)null) { ((TMP_Text)tagCounterLocationButton.Label).text = TagCounterLocationLabel(); } }); base.ScrollView.AddButton((PhoneButton)(object)tagCounterLocationButton); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate { ShowUIOptionsView(); }); base.ScrollView.AddButton((PhoneButton)(object)val); } private static void CycleLocation(ConfigEntry<TimerUILocation> entry) { if (entry != null) { entry.Value = entry.Value switch { TimerUILocation.Left => TimerUILocation.Center, TimerUILocation.Center => TimerUILocation.Right, _ => TimerUILocation.Left, }; } } private static string TimerLocationLabel() { return "Timer Location: " + ((GRACEPlusPlugin.TimerLocation == null) ? TimerUILocation.Center : GRACEPlusPlugin.TimerLocation.Value); } private static string TagCounterLocationLabel() { return "Tag Counter Location: " + ((GRACEPlusPlugin.TagCounterLocation == null) ? TimerUILocation.Center : GRACEPlusPlugin.TagCounterLocation.Value); } private void ShowVisibleView() { base.ScrollView.RemoveAllButtons(); SimplePhoneButton showTimerButton = PhoneUIUtility.CreateSimpleButton(ShowTimerLabel()); SimplePhoneButton obj = showTimerButton; ((PhoneButton)obj).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj).OnConfirm, (Action)delegate { if (GRACEPlusPlugin.HideRaceTimer != null) { GRACEPlusPlugin.HideRaceTimer.Value = !GRACEPlusPlugin.HideRaceTimer.Value; if ((Object)(object)showTimerButton?.Label != (Object)null) { ((TMP_Text)showTimerButton.Label).text = ShowTimerLabel(); } } }); base.ScrollView.AddButton((PhoneButton)(object)showTimerButton); SimplePhoneButton showLobbyWinCounterButton = PhoneUIUtility.CreateSimpleButton(ShowLobbyWinCounterLabel()); SimplePhoneButton obj2 = showLobbyWinCounterButton; ((PhoneButton)obj2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)obj2).OnConfirm, (Action)delegate { if (GRACEPlusPlugin.ShowLobbyWinCounter != null) { GRACEPlusPlugin.ShowLobbyWinCounter.Value = !GRACEPlusPlugin.ShowLobbyWinCounter.Value; if ((Object)(object)showLobbyWinCounterButton?.Label != (Object)null) { ((TMP_Text)showLobbyWinCounterButton.Label).text = ShowLobbyWinCounterLabel(); } } }); base.ScrollView.AddButton((PhoneButton)(object)showLobbyWinCounterButton); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate { ShowUIOptionsView(); }); base.ScrollView.AddButton((PhoneButton)(object)val); } private static void CycleGraffitiOutlinePreset() { if (GRACEPlusPlugin.GraffitiOutlinePreset != null) { string current = GRACEPlusPlugin.GraffitiOutlinePreset.Value ?? "normal"; int num = Array.FindIndex(GraffitiOutlinePresetOrder, (string x) => string.Equals(x, current, StringComparison.OrdinalIgnoreCase)); if (num < 0) { num = 0; } num = (num + 1) % GraffitiOutlinePresetOrder.Length; string text = GraffitiOutlinePresetOrder[num]; GRACEPlusPlugin.GraffitiOutlinePreset.Value = text; if (GRACEPlusPlugin.CustomGraffitiOutline != null) { GRACEPlusPlugin.CustomGraffitiOutline.Value = !string.Equals(text, "normal", StringComparison.OrdinalIgnoreCase); } } } private static string GraffitiOutlineColorLabel() { if (GRACEPlusPlugin.UseConfigHexForGraffitiOutline != null && GRACEPlusPlugin.UseConfigHexForGraffitiOutline.Value) { return "Graffiti Outline Color: (config hex)"; } string text = ((GRACEPlusPlugin.GraffitiOutlinePreset != null) ? GRACEPlusPlugin.GraffitiOutlinePreset.Value : "normal"); return "Graffiti Outline Color: " + (string.IsNullOrEmpty(text) ? "normal" : text.ToLowerInvariant()); } private static string CustomGraffitiSignLabel() { bool flag = GRACEPlusPlugin.UseCustomGraffitiIndicator != null && GRACEPlusPlugin.UseCustomGraffitiIndicator.Value; return "Custom Graffiti Sign: " + (flag ? "On" : "Off"); } private static string ShowTimerLabel() { bool flag = GRACEPlusPlugin.HideRaceTimer != null && !GRACEPlusPlugin.HideRaceTimer.Value; return "Show Timer: " + (flag ? "On" : "Off"); } private static string ShowLobbyWinCounterLabel() { bool flag = GRACEPlusPlugin.ShowLobbyWinCounter != null && GRACEPlusPlugin.ShowLobbyWinCounter.Value; return "Show Lobby Win Counter: " + (flag ? "On" : "Off"); } private static int GetCurrentStageId() { //IL_0038: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected I4, but got Unknown try { ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } if (obj != null) { return instance.ClientLobbyManager.CurrentLobby.LobbyState.Stage; } Scene activeScene = SceneManager.GetActiveScene(); if (!((Scene)(ref activeScene)).IsValid()) { return -1; } string text = ((Scene)(ref activeScene)).name ?? ""; if (string.IsNullOrEmpty(text)) { return -1; } return (int)Utility.SceneNameToStage(text); } catch { return -1; } } private void ShowStatsView() { base.ScrollView.RemoveAllButtons(); int stageId = GetCurrentStageId(); double? bestTime = ((stageId >= 0) ? GRACEPlusPlugin.GetBestTime(stageId) : null); double? avgTime = ((stageId >= 0) ? GRACEPlusPlugin.GetAverageTime(stageId) : null); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton(StatsBestTimeLabel(bestTime)); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate { }); base.ScrollView.AddButton((PhoneButton)(object)val); SimplePhoneButton val2 = PhoneUIUtility.CreateSimpleButton(StatsAvgTimeLabel(avgTime)); ((PhoneButton)val2).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val2).OnConfirm, (Action)delegate { }); base.ScrollView.AddButton((PhoneButton)(object)val2); SimplePhoneButton val3 = PhoneUIUtility.CreateSimpleButton("Clear Best Time"); ((PhoneButton)val3).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val3).OnConfirm, (Action)delegate { if (stageId >= 0) { GRACEPlusPlugin.ClearBestTime(stageId); ShowStatsView(); } }); base.ScrollView.AddButton((PhoneButton)(object)val3); SimplePhoneButton val4 = PhoneUIUtility.CreateSimpleButton("Clear AVG Time"); ((PhoneButton)val4).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val4).OnConfirm, (Action)delegate { if (stageId >= 0) { GRACEPlusPlugin.ClearMapStats(stageId); ShowStatsView(); } }); base.ScrollView.AddButton((PhoneButton)(object)val4); SimplePhoneButton val5 = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val5).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val5).OnConfirm, (Action)delegate { PopulateMainScreen(); }); base.ScrollView.AddButton((PhoneButton)(object)val5); } private static string StatsBestTimeLabel(double? bestTime) { return "Best Time: " + (bestTime.HasValue ? $"{bestTime.Value:F1}s" : "—"); } private static string StatsAvgTimeLabel(double? avgTime) { return "AVG Time: " + (avgTime.HasValue ? $"{avgTime.Value:F1}s" : "—"); } private void ShowGameSettingsView() { base.ScrollView.RemoveAllButtons(); _trackMapStatsButton = PhoneUIUtility.CreateSimpleButton(TrackMapStatsLabel()); SimplePhoneButton trackMapStatsButton = _trackMapStatsButton; ((PhoneButton)trackMapStatsButton).OnConfirm = (Action)Delegate.Combine(((PhoneButton)trackMapStatsButton).OnConfirm, (Action)delegate { if (GRACEPlusPlugin.TrackMapStats != null) { GRACEPlusPlugin.TrackMapStats.Value = !GRACEPlusPlugin.TrackMapStats.Value; if ((Object)(object)_trackMapStatsButton?.Label != (Object)null) { ((TMP_Text)_trackMapStatsButton.Label).text = TrackMapStatsLabel(); } } }); base.ScrollView.AddButton((PhoneButton)(object)_trackMapStatsButton); _autoRestartButton = PhoneUIUtility.CreateSimpleButton(AutoRestartLabel()); SimplePhoneButton autoRestartButton = _autoRestartButton; ((PhoneButton)autoRestartButton).OnConfirm = (Action)Delegate.Combine(((PhoneButton)autoRestartButton).OnConfirm, (Action)delegate { if (GRACEPlusPlugin.AutoStartGRaceRounds != null) { GRACEPlusPlugin.AutoStartGRaceRounds.Value = !GRACEPlusPlugin.AutoStartGRaceRounds.Value; if ((Object)(object)_autoRestartButton?.Label != (Object)null) { ((TMP_Text)_autoRestartButton.Label).text = AutoRestartLabel(); } } }); base.ScrollView.AddButton((PhoneButton)(object)_autoRestartButton); _autoRestartDelayButton = PhoneUIUtility.CreateSimpleButton(AutoRestartDelayLabel()); SimplePhoneButton autoRestartDelayButton = _autoRestartDelayButton; ((PhoneButton)autoRestartDelayButton).OnConfirm = (Action)Delegate.Combine(((PhoneButton)autoRestartDelayButton).OnConfirm, (Action)delegate { if (GRACEPlusPlugin.AutoStartDelaySeconds != null) { int autoRestartDelayForCycle = GetAutoRestartDelayForCycle(); autoRestartDelayForCycle++; if (autoRestartDelayForCycle > 10) { autoRestartDelayForCycle = 1; } GRACEPlusPlugin.AutoStartDelaySeconds.Value = autoRestartDelayForCycle; if ((Object)(object)_autoRestartDelayButton?.Label != (Object)null) { ((TMP_Text)_autoRestartDelayButton.Label).text = AutoRestartDelayLabel(); } } }); base.ScrollView.AddButton((PhoneButton)(object)_autoRestartDelayButton); SimplePhoneButton val = PhoneUIUtility.CreateSimpleButton("Back"); ((PhoneButton)val).OnConfirm = (Action)Delegate.Combine(((PhoneButton)val).OnConfirm, (Action)delegate { PopulateMainScreen(); }); base.ScrollView.AddButton((PhoneButton)(object)val); } private static string TrackMapStatsLabel() { bool flag = GRACEPlusPlugin.TrackMapStats != null && GRACEPlusPlugin.TrackMapStats.Value; return "Track Map Stats: " + (flag ? "On" : "Off"); } private static string AutoRestartLabel() { bool flag = GRACEPlusPlugin.AutoStartGRaceRounds != null && GRACEPlusPlugin.AutoStartGRaceRounds.Value; return "Auto Restart: " + (flag ? "On" : "Off"); } private static int GetAutoRestartDelayForCycle() { if (GRACEPlusPlugin.AutoStartDelaySeconds == null) { return 1; } int value = GRACEPlusPlugin.AutoStartDelaySeconds.Value; if (value < 1) { return 1; } if (value > 10) { return 10; } return value; } private static string AutoRestartDelayLabel() { return "Auto Restart Delay: " + ((GRACEPlusPlugin.AutoStartDelaySeconds == null) ? 1 : GetAutoRestartDelayForCycle()) + "s"; } } } namespace GRACEPlus.Patches { internal static class BlockStageSwitchDuringRacePatch { public const string BlockMessage = "<color=yellow>Can't change stage during a GRACE race. Finish or end the round first.</color>"; public static bool ShouldBlock() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Invalid comparison between Unknown and I4 //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Invalid comparison between Unknown and I4 ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } Lobby val = (Lobby)obj; if (val == null || !val.InGame) { return false; } if ((int)val.LobbyState.Gamemode != 1) { return (int)val.LobbyState.Gamemode == 2; } return true; } public static bool Prefix_SwitchStage1(Stage newStage) { if (!ShouldBlock()) { return true; } GRACEPlusPlugin.ShowNotification("<color=yellow>Can't change stage during a GRACE race. Finish or end the round first.</color>"); return false; } public static bool Prefix_SwitchStage2(Stage newStage, Stage fromStage) { if (!ShouldBlock()) { return true; } GRACEPlusPlugin.ShowNotification("<color=yellow>Can't change stage during a GRACE race. Finish or end the round first.</color>"); return false; } public static bool Prefix_ExitCurrentStage(Stage nextStage, Stage prevStageSpecifiedForSpawner) { if (!ShouldBlock()) { return true; } GRACEPlusPlugin.ShowNotification("<color=yellow>Can't change stage during a GRACE race. Finish or end the round first.</color>"); return false; } public static void Apply(Harmony harmony) { //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Expected O, but got Unknown //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown if (harmony == null) { return; } Assembly assembly = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { if (assembly2.GetType("Reptile.BaseModule") != null) { assembly = assembly2; break; } } if (assembly == null) { return; } Type type = assembly.GetType("Reptile.BaseModule"); Type type2 = assembly.GetType("Reptile.StageManager"); Type type3 = assembly.GetType("Reptile.Stage"); if (type3 == null) { type3 = typeof(Stage); } if (type != null && type3 != null) { MethodInfo method = type.GetMethod("SwitchStage", new Type[1] { type3 }); MethodInfo method2 = type.GetMethod("SwitchStage", new Type[2] { type3, type3 }); if (method != null) { harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(BlockStageSwitchDuringRacePatch), "Prefix_SwitchStage1", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } if (method2 != null) { harmony.Patch((MethodBase)method2, new HarmonyMethod(typeof(BlockStageSwitchDuringRacePatch), "Prefix_SwitchStage2", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } if (type2 != null && type3 != null) { MethodInfo method3 = type2.GetMethod("ExitCurrentStage", new Type[2] { type3, type3 }); if (method3 != null) { harmony.Patch((MethodBase)method3, new HarmonyMethod(typeof(BlockStageSwitchDuringRacePatch), "Prefix_ExitCurrentStage", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } } } [HarmonyPatch(typeof(ChatUI), "ProcessLocalCommand")] public static class ChatCommandPatch { private static bool Prefix(ChatUI __instance, string message) { if (string.IsNullOrEmpty(message)) { return true; } switch (message.ToLower()) { case "/timeclear": HandleTimeClearCommand(); return false; case "/teamtimeclear": HandleTeamTimeClearCommand(); return false; case "/timeclearall": HandleTimeClearAllCommand(); return false; case "/teamtimeclearall": HandleTeamTimeClearAllCommand(); return false; case "/showbest": HandleShowBestCommand(); return false; case "/gracestats": GRACEPlusPlugin.RunGraceStatsCommand(); return false; case "/avgtime": HandleAvgTimeCommand(); return false; case "/mapstats": GRACEPlusPlugin.RunMapStatsCommand(); return false; case "/clearmapstats": HandleClearMapStatsCommand(); return false; case "/clearmapstatsall": HandleClearMapStatsAllCommand(); return false; case "/gracestatsclear": HandleGraceStatsClearCommand(); return false; case "/gracehelp": GRACEPlusPlugin.RunGraceHelpCommand(); return false; case "/end": HandleEndCommand(); return false; case "/leave": HandleLeaveCommand(); return false; case "/start": HandleStartCommand(); return false; default: return true; } } private static void HandleTimeClearCommand() { //IL_0036: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Invalid comparison between Unknown and I4 ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } Lobby val = (Lobby)obj; if (val != null) { int stage = val.LobbyState.Stage; GamemodeIDs gamemode = val.LobbyState.Gamemode; if ((int)gamemode == 1 || (int)gamemode == 2) { double? bestTime = GRACEPlusPlugin.GetBestTime(stage); if (bestTime.HasValue) { if (GRACEPlusPlugin.ClearBestTime(stage)) { string arg = (((int)gamemode == 2) ? "Team GRACE" : "GRACE"); GRACEPlusPlugin.ShowCommandResponse($"<color=red>Cleared {arg} record for Stage {stage}! (Was: {bestTime.Value:F1}s)</color>"); } else { GRACEPlusPlugin.ShowCommandResponse("<color=red>Failed to clear record!</color>"); } } else { GRACEPlusPlugin.ShowCommandResponse($"<color=yellow>No record found for Stage {stage} to clear!</color>"); } } else { GRACEPlusPlugin.ShowCommandResponse("<color=red>/timeclear can only be used in GRACE lobbies!</color>"); } } else { GRACEPlusPlugin.ShowCommandResponse("<color=red>You must be in a lobby to use /timeclear!</color>"); } } private static void HandleTimeClearAllCommand() { int num = GRACEPlusPlugin.ClearAllBestTimes(); if (num > 0) { GRACEPlusPlugin.ShowCommandResponse($"<color=red>Cleared ALL GRACE records! ({num} records deleted)</color>"); } else { GRACEPlusPlugin.ShowCommandResponse("<color=yellow>No records found to clear!</color>"); } } private static void HandleTeamTimeClearCommand() { //IL_0036: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } Lobby val = (Lobby)obj; if (val != null) { int stage = val.LobbyState.Stage; GamemodeIDs gamemode = val.LobbyState.Gamemode; if ((int)gamemode == 1 || (int)gamemode == 2) { double? bestTime = GRACEPlusPlugin.GetBestTime(stage, isTeamMode: true); if (bestTime.HasValue) { if (GRACEPlusPlugin.ClearBestTime(stage, isTeamMode: true)) { GRACEPlusPlugin.ShowCommandResponse($"<color=red>Cleared Team GRACE record for Stage {stage}! (Was: {bestTime.Value:F1}s)</color>"); } else { GRACEPlusPlugin.ShowCommandResponse("<color=red>Failed to clear team record!</color>"); } } else { GRACEPlusPlugin.ShowCommandResponse($"<color=yellow>No team record found for Stage {stage} to clear!</color>"); } } else { GRACEPlusPlugin.ShowCommandResponse("<color=red>/teamtimeclear can only be used in GRACE lobbies!</color>"); } } else { GRACEPlusPlugin.ShowCommandResponse("<color=red>You must be in a lobby to use /teamtimeclear!</color>"); } } private static void HandleTeamTimeClearAllCommand() { int num = GRACEPlusPlugin.ClearAllBestTimes(isTeamMode: true); if (num > 0) { GRACEPlusPlugin.ShowCommandResponse($"<color=red>Cleared ALL Team GRACE records! ({num} records deleted)</color>"); } else { GRACEPlusPlugin.ShowCommandResponse("<color=yellow>No team records found to clear!</color>"); } } private static void HandleShowBestCommand() { ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } Lobby val = (Lobby)obj; if (val != null) { int stage = val.LobbyState.Stage; double? bestTime = GRACEPlusPlugin.GetBestTime(stage); double? bestTime2 = GRACEPlusPlugin.GetBestTime(stage, isTeamMode: true); if (bestTime.HasValue || bestTime2.HasValue) { GRACEPlusPlugin.ShowCommandResponse($"<color=green>=== Stage {stage} Best Times ===</color>"); if (bestTime.HasValue) { GRACEPlusPlugin.ShowCommandResponse($"<color=green>Solo: {bestTime.Value:F1}s</color>"); } else { GRACEPlusPlugin.ShowCommandResponse("<color=yellow>Solo: No record set</color>"); } if (bestTime2.HasValue) { GRACEPlusPlugin.ShowCommandResponse($"<color=green>Team: {bestTime2.Value:F1}s</color>"); } else { GRACEPlusPlugin.ShowCommandResponse("<color=yellow>Team: No record set</color>"); } } else { GRACEPlusPlugin.ShowCommandResponse("<color=yellow>No best time saved yet for this map</color>"); } } else { GRACEPlusPlugin.ShowCommandResponse("<color=red>You must be in a lobby to use /showbest!</color>"); } } private static void HandleAvgTimeCommand() { if (!GRACEPlusPlugin.TrackMapStats.Value) { GRACEPlusPlugin.ShowCommandResponse("<color=red>Map statistics tracking is disabled! Enable 'Track Map Stats' in config.</color>"); return; } ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } Lobby val = (Lobby)obj; if (val != null) { int stage = val.LobbyState.Stage; double? averageTime = GRACEPlusPlugin.GetAverageTime(stage); int winCount = GRACEPlusPlugin.GetWinCount(stage); if (averageTime.HasValue && winCount > 0) { GRACEPlusPlugin.ShowCommandResponse($"<color=green>Stage {stage} Average: {averageTime.Value:F1}s ({winCount} wins)</color>"); } else { GRACEPlusPlugin.ShowCommandResponse($"<color=yellow>No win times recorded yet for Stage {stage}</color>"); } } else { GRACEPlusPlugin.ShowCommandResponse("<color=red>You must be in a lobby to use /avgtime!</color>"); } } private static void HandleClearMapStatsCommand() { ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } Lobby val = (Lobby)obj; if (val != null) { int stage = val.LobbyState.Stage; if (GRACEPlusPlugin.ClearMapStats(stage)) { GRACEPlusPlugin.ShowCommandResponse($"<color=red>Cleared map statistics for Stage {stage}!</color>"); } else { GRACEPlusPlugin.ShowCommandResponse("<color=yellow>There is no map stats to clear</color>"); } } else { GRACEPlusPlugin.ShowCommandResponse("<color=red>You must be in a lobby to use /clearmapstats!</color>"); } } private static void HandleClearMapStatsAllCommand() { int num = GRACEPlusPlugin.ClearAllMapStats(); if (num > 0) { GRACEPlusPlugin.ShowCommandResponse($"<color=red>Cleared ALL map statistics! ({num} files deleted)</color>"); } else { GRACEPlusPlugin.ShowCommandResponse("<color=yellow>There are no map stats to clear</color>"); } } private static void HandleGraceStatsClearCommand() { if (GRACEPlusPlugin.ClearCustomStats()) { GRACEPlusPlugin.ShowCommandResponse("<color=red>Cleared all custom GRACE statistics!</color>"); } else { GRACEPlusPlugin.ShowCommandResponse("<color=yellow>No custom GRACE statistics to clear</color>"); } } private static void HandleEndCommand() { ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } Lobby val = (Lobby)obj; if (val == null) { GRACEPlusPlugin.ShowCommandResponse("<color=red>You must be in a lobby to use /end!</color>"); return; } if (val.LobbyState.HostId != instance.LocalID) { GRACEPlusPlugin.ShowCommandResponse("<color=red>Only the host can end the round!</color>"); return; } if (!val.InGame) { GRACEPlusPlugin.ShowCommandResponse("<color=yellow>No round is currently in progress!</color>"); return; } instance.ClientLobbyManager.EndGame(); GRACEPlusPlugin.ShowCommandResponse("<color=green>Ending the round...</color>"); } private static void HandleLeaveCommand() { ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } if (obj == null) { GRACEPlusPlugin.ShowCommandResponse("<color=yellow>You are not in a lobby!</color>"); return; } instance.ClientLobbyManager.LeaveLobby(); GRACEPlusPlugin.ShowCommandResponse("<color=green>Leaving lobby...</color>"); } private static void HandleStartCommand() { ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } Lobby val = (Lobby)obj; if (val == null) { GRACEPlusPlugin.ShowCommandResponse("<color=red>You must be in a lobby to use /start!</color>"); return; } if (val.LobbyState.HostId != instance.LocalID) { GRACEPlusPlugin.ShowCommandResponse("<color=red>Only the host can start the round!</color>"); return; } if (val.InGame) { GRACEPlusPlugin.ShowCommandResponse("<color=yellow>A round is already in progress! Use /end to end it first.</color>"); return; } instance.ClientLobbyManager.StartGame(); GRACEPlusPlugin.ShowCommandResponse("<color=green>Starting the round...</color>"); } } [HarmonyPatch(typeof(UIScreenIndicators), "Awake")] internal static class CustomGraffitiIndicatorPatch { private static readonly FieldInfo IndicatorField = typeof(UIScreenIndicators).GetField("_indicator", BindingFlags.Instance | BindingFlags.NonPublic); [HarmonyPostfix] private static void Postfix(UIScreenIndicators __instance) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown if ((Object)(object)__instance == (Object)null || IndicatorField == null) { return; } string customGraffitiSignPath = GRACEPlusPlugin.GetCustomGraffitiSignPath(); if (string.IsNullOrEmpty(customGraffitiSignPath) || !File.Exists(customGraffitiSignPath)) { return; } object? value = IndicatorField.GetValue(__instance); GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val == (Object)null) { return; } RawImage component = val.GetComponent<RawImage>(); if ((Object)(object)component == (Object)null) { return; } try { byte[] array = File.ReadAllBytes(customGraffitiSignPath); Texture2D val2 = new Texture2D(2, 2); if (ImageConversion.LoadImage(val2, array)) { ((Texture)val2).filterMode = (FilterMode)1; val2.Apply(); component.texture = (Texture)(object)val2; } } catch { } } } [HarmonyPatch(typeof(GraffitiRace), "OnEnd")] internal class GraffitiRaceCancellationPatch { private static void Prefix(GraffitiRace __instance, bool cancelled) { GRACEPlusPlugin.SetGameCancelled(cancelled); GRACEPlusPlugin.DebugLog($"EVENT: Race ending - Cancelled: {cancelled}"); } } [HarmonyPatch(typeof(GraffitiRace), "OnPacketReceived_InGame")] internal class GraffitiRaceTimerPatch { private static bool isRaceActive; private static void Postfix(GraffitiRace __instance, Packets packetId, Packet packet) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 if ((int)packetId == 19 && !isRaceActive) { isRaceActive = true; GRACEPlusPlugin.SetGameCancelled(cancelled: false); GRACEPlusPlugin.DebugLog("EVENT: Race started (ServerGamemodeBegin received)"); } } [HarmonyPostfix] [HarmonyPatch(typeof(GraffitiRace), "OnEnd")] private static void OnEnd_Postfix(GraffitiRace __instance) { if (!isRaceActive) { return; } double num = Math.Round(Traverse.Create((object)__instance).Field("_countdownTimer").GetValue<float>(), 1); isRaceActive = false; GRACEPlusPlugin.DebugLog($"EVENT: Race ended - Duration: {num}s (from BombRushMP timer)"); ClientController instance = ClientController.Instance; object obj; if (instance == null) { obj = null; } else { ClientLobbyManager clientLobbyManager = instance.ClientLobbyManager; obj = ((clientLobbyManager != null) ? clientLobbyManager.CurrentLobby : null); } if (obj != null) { Lobby currentLobby = instance.ClientLobbyManager.CurrentLobby; ushort localID = instance.LocalID; int stage = currentLobby.LobbyState.Stage; GRACEPlusPlugin.DebugLog($"INFO: Local Player ID: {localID}, Stage ID: {stage}"); GRACEPlusPlugin.DebugLog($"INFO: Team Based: {((Gamemode)__instance).TeamBased}"); bool flag = false; int maxPossibleScore = GRACEPlusPlugin.GetMaxPossibleScore(); GRACEPlusPlugin.DebugLog($"INFO: Max Possible Score: {maxPossibleScore}"); if (maxPossibleScore > 0) { if (((Gamemode)__instance).TeamBased) { if (currentLobby.LobbyState.Players.ContainsKey(localID)) { byte team = currentLobby.LobbyState.Players[localID].Team; float scoreForTeam = currentLobby.LobbyState.GetScoreForTeam(team); GRACEPlusPlugin.DebugLog($"INFO: User Team: {team}, Team Score: {scoreForTeam}"); flag = scoreForTeam >= (float)maxPossibleScore && scoreForTeam > 0f; GRACEPlusPlugin.DebugLog($"CALC: userTeamScore ({scoreForTeam}) >= maxPossibleScore ({maxPossibleScore}) && userTeamScore > 0 = {flag}"); } else { GRACEPlusPlugin.DebugLog($"ERROR: Local player ID {localID} not found in lobby players!"); } } else if (currentLobby.LobbyState.Players.ContainsKey(localID)) { float score = currentLobby.LobbyState.Players[localID].Score; GRACEPlusPlugin.DebugLog($"INFO: User Score: {score}"); flag = score >= (float)maxPossibleScore && score > 0f; GRACEPlusPlugin.DebugLog($"CALC: userScore ({score}) >= maxPossibleScore ({maxPossibleScore}) && userScore > 0 = {flag}"); } else { GRACEPlusPlugin.DebugLog($"ERROR: Local player ID {localID} not found in lobby players!"); } } else { flag = false; GRACEPlusPlugin.DebugLog("CALC: maxPossibleScore is 0, setting userWon = false"); } GRACEPlusPlugin.DebugLog($"RESULT: User Won = {flag}"); string text = (((Gamemode)__instance).TeamBased ? "Team " : ""); if (flag) { double? previousBest; bool flag2 = GRACEPlusPlugin.CheckAndUpdateBestTime(stage, num, ((Gamemode)__instance).TeamBased, out previousBest); GRACEPlusPlugin.SaveMatchResult(stage, num, won: true, ((Gamemode)__instance).TeamBased, flag2, previousBest); if (GRACEPlusPlugin.TrackMapStats.Value) { bool isMultiplayer = currentLobby.LobbyState.Players.Count > 1; GRACEPlusPlugin.SaveMapStats(stage, num, won: true, isMultiplayer, ((Gamemode)__instance).TeamBased); } if (!GRACEPlusPlugin.IsGameCancelled()) { GRACEPlusPlugin.IncrementCustomWins(currentLobby.LobbyState.Players.Count == 1, ((Gamemode)__instance).TeamBased); } if (flag2) { if (previousBest.HasValue) { GRACEPlusPlugin.ShowConfigurableMessage(GRACEPlusPlugin.NewRecordMessage, GRACEPlusPlugin.NewRecordColor, ("gamemode", text.ToUpper()), ("time", num.ToString()), ("stage", stage.ToString()), ("previous", previousBest.Value.ToString("F1"))); } else { GRACEPlusPlugin.ShowConfigurableMessage(GRACEPlusPlugin.FirstWinMessage, GRACEPlusPlugin.FirstWinColor, ("gamemode", text.ToUpper()), ("time", num.ToString()), ("stage", stage.ToString())); } } else { double? bestTime = GRACEPlusPlugin.GetBestTime(stage, ((Gamemode)__instance).TeamBased); GRACEPlusPlugin.ShowConfigurableMessage(GRACEPlusPlugin.WinMessage, GRACEPlusPlugin.WinColor, ("gamemode", text), ("time", num.ToString()), ("best", bestTime?.ToString("F1") ?? "N/A")); } } else { GRACEPlusPlugin.SaveMatchResult(stage, num, won: false, ((Gamemode)__instance).TeamBased, isNewRecord: false, null); if (GRACEPlusPlugin.TrackMapStats.Value) { bool isMultiplayer2 = currentLobby.LobbyState.Players.Count > 1; GRACEPlusPlugin.SaveMapStats(stage, num, won: false, isMultiplayer2, ((Gamemode)__instance).TeamBased); } if (!GRACEPlusPlugin.IsGameCancelled()) { GRACEPlusPlugin.IncrementCustomLosses(((Gamemode)__instance).TeamBased); } GRACEPlusPlugin.ShowConfigurableMessage(GRACEPlusPlugin.LoseMessage, GRACEPlusPlugin.LoseColor, ("gamemode", text), ("time", num.ToString())); } } else { string arg = (((Gamemode)__instance).TeamBased ? "Team " : ""); GRACEPlusPlugin.ShowNotification($"<color=green>{arg}GRACE Ended IN {num} Seconds!</color>"); GRACEPlusPlugin.DebugLog("INFO: No lobby data available, showing basic message"); } GRACEPlusPlugin.ResetMaxPossibleScore(); GRACEPlusPlugin.DebugLog("EVENT: Max possible score reset to 0"); GRACEPlusPlugin.ScheduleAutoStartIfEnabled(); } } [HarmonyPatch(typeof(GraffitiRace), "OnReceive_GraffitiRaceData")] internal class GraffitiRaceDataReceivePatch { private static void Postfix(GraffitiRace __instance, ClientGraffitiRaceGSpots packet) { int num = GRACEPlusPlugin.GetMaxPossibleScore() + packet.GraffitiSpots.Count; GRACEPlusPlugin.SetMaxPossibleScore(num); GRACEPlusPlugin.DebugLog($"EVENT: OnReceive_GraffitiRaceData - Added {packet.GraffitiSpots.Count} spots, Total max score: {num}"); } } [HarmonyPatch(typeof(GraffitiRace))] internal static class GraffitiOutlinePatch { private static readonly int ShaderPropColor = Shader.PropertyToID("_Color"); private static readonly int ShaderPropBaseColor = Shader.PropertyToID("_BaseColor"); internal static readonly FieldInfo OriginalProgressField = typeof(GraffitiRace).GetField("_originalProgress", BindingFlags.Instance | BindingFlags.NonPublic); [HarmonyPostfix] [HarmonyPatch("OnReceive_GraffitiRaceData", new Type[] { typeof(ClientGraffitiRaceGSpots) })] private static void OnReceive_GraffitiRaceData_Postfix(GraffitiRace __instance) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) Color? effectiveOutlineColor = GRACEPlusPlugin.GetEffectiveOutlineColor(); if (!effectiveOutlineColor.HasValue) { return; } object obj = OriginalProgressField?.GetValue(__instance); if (obj == null || !(obj is IDictionary dictionary)) { return; } Color value = effectiveOutlineColor.Value; foreach (DictionaryEntry item in dictionary) { object key = item.Key; GraffitiSpot val = (GraffitiSpot)((key is GraffitiSpot) ? key : null); if (val != null && !((Object)(object)val == (Object)null)) { ApplyOutlineColorToSpot(val, value); } } EnsureOnEnableReapplyOnRaceSpots(__instance, dictionary); } private static void EnsureOnEnableReapplyOnRaceSpots(GraffitiRace grafRace, IDictionary idict) { foreach (DictionaryEntry item in idict) { object key = item.Key; GraffitiSpot val = (GraffitiSpot)((key is GraffitiSpot) ? key : null); if (val != null && !((Object)(object)((val != null) ? ((Component)val).gameObject : null) == (Object)null) && !((Object)(object)((Component)val).gameObject.GetComponent<GraffitiOutlineOnEnableReapply>() != (Object)null)) { ((Component)val).gameObject.AddComponent<GraffitiOutlineOnEnableReapply>().SetRace(grafRace); } } } internal static void ApplyOutlineColorToSpot(GraffitiSpot spot, Color color) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)((spot != null) ? ((Component)spot).gameObject : null) == (Object)null) { return; } Renderer[] componentsInChildren = ((Component)spot).GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.material == (Object)null)) { Material material = val.material; int num = -1; if (material.HasProperty(ShaderPropColor)) { num = ShaderPropColor; } else if (material.HasProperty(ShaderPropBaseColor)) { num = ShaderPropBaseColor; } if (num >= 0) { material.SetColor(num, color); } } } } } internal sealed class GraffitiOutlineOnEnableReapply : MonoBehaviour { private GraffitiRace _race; public void SetRace(GraffitiRace grafRace) { _race = grafRace; } private void OnEnable() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) Color? effectiveOutlineColor = GRACEPlusPlugin.GetEffectiveOutlineColor(); if (!effectiveOutlineColor.HasValue || _race == null) { return; } GraffitiSpot component = ((Component)this).GetComponent<GraffitiSpot>(); if (!((Object)(object)component == (Object)null)) { object obj = GraffitiOutlinePatch.OriginalProgressField?.GetValue(_race); if (obj != null && obj is IDictionary dictionary && dictionary.Contains(component)) { GraffitiOutlinePatch.ApplyOutlineColorToSpot(component, effectiveOutlineColor.Value); } } } } [HarmonyPatch(typeof(LobbyUI), "UpdateUI")] internal class LobbyUIUpdatePatch { private static void Postfix(LobbyUI __instance) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) try { FieldInfo field = typeof(LobbyUI).GetField("_canvas", BindingFlags.Instance | BindingFlags.NonPublic); if (field == null || (Object)/*isinst with value type is only supported in some contexts*/ == (Object)null || !GRACEPlusPlugin.UseCustomLobbyName.Value) { return; } FieldInfo field2 = typeof(LobbyUI).GetField("_lobbyName", BindingFlags.Instance | BindingFlags.NonPublic); if (field2 != null) { object? value = field2.GetValue(__instance); TextMeshProUGUI val = (TextMeshProUGUI)((value is TextMeshProUGUI) ? value : null); if ((Object)(object)val != (Object)null) { ((TMP_Text)val).text = GRACEPlusPlugin.LobbyNameText.Value; ((Graphic)val).color = GRACEPlusPlugin.ParseColor(GRACEPlusPlugin.LobbyNameColor.Value); } } } catch { } } } [HarmonyPatch(typeof(LobbyPlayerUI), "SetPlayer")] internal class LobbyPlayerUISetPlayerPatch { private static void Postfix(LobbyPlayerUI __instance) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) try { object? obj = typeof(LobbyPlayerUI).GetField("_score", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance); TextMeshProUGUI val = (TextMeshProUGUI)((obj is TextMeshProUGUI) ? obj : null); if ((Object)(object)val == (Object)null) { return; } if (GRACEPlusPlugin.UseCustomScoreTextColor.Value) { ((Graphic)val).color = GRACEPlusPlugin.ParseColor(GRACEPlusPlugin.ScoreTextColor.Value); } object obj2 = typeof(LobbyPlayerUI).GetField("_lobbyPlayer", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance); if (obj2 == null) { return; } FieldInfo field = obj2.GetType().GetField("Wins", BindingFlags.Instance | BindingFlags.Public); int num = ((field != null) ? ((int)(field.GetValue(obj2) ?? ((object)0))) : 0); string text = ((TMP_Text)val).text; string text2 = $" ({num})"; if (text == null || !text.EndsWith(")")) { return; } int num2 = text.LastIndexOf(" (", StringComparison.Ordinal); if (num2 >= 0 && text.Substring(num2) == text2) { if (!GRACEPlusPlugin.ShowLobbyWinCounter.Value) { ((TMP_Text)val).text = text.Substring(0, num2); } else if (GRACEPlusPlugin.UseCustomWinCounterColor.Value) { string text3 = ColorUtility.ToHtmlStringRGB(GRACEPlusPlugin.ParseColorRgb(GRACEPlusPlugin.WinCounterColor?.Value ?? "255,200,100")); ((TMP_Text)val).text = text.Substring(0, num2) + " <color=#" + text3 + ">" + text2 + "</color>"; } } } catch { } } } [HarmonyPatch(typeof(LobbyPlayerUI), "SetTeam")] internal class LobbyPlayerUISetTeamPatch { private static void Postfix(LobbyPlayerUI __instance) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) try { FieldInfo field = typeof(LobbyPlayerUI).GetField("_score", BindingFlags.Instance | BindingFlags.NonPublic); if (GRACEPlusPlugin.UseCustomScoreTextColor.Value && field != null) { object? value = field.GetValue(__instance); TextMeshProUGUI val = (TextMeshProUGUI)((value is TextMeshProUGUI) ? value : null); if ((Object)(object)val != (Object)null) { ((Graphic)val).color = GRACEPlusPlugin.ParseColor(GRACEPlusPlugin.ScoreTextColor.Value); } } } catch { } } } [HarmonyPatch(typeof(GraffitiRace), "OnPacketReceived_InGame")] internal