diff --git a/Main.cs b/Main.cs index a7ce4015b..05ca90864 100644 --- a/Main.cs +++ b/Main.cs @@ -207,6 +207,7 @@ public class Main : BasePlugin public static ConfigEntry ButtonCooldownInDecimalUnder10s { get; private set; } public static ConfigEntry CancelPetAnimation { get; private set; } public static ConfigEntry TryFixStuttering { get; private set; } + public static ConfigEntry ShowClientControlGUI { get; private set; } public static ConfigEntry UIScaleFactor { get; private set; } // Preset Name Options @@ -300,6 +301,7 @@ public class Main : BasePlugin ButtonCooldownInDecimalUnder10s = Config.Bind("Client Options", "ButtonCooldownInDecimalUnder10s", false); CancelPetAnimation = Config.Bind("Client Options", "CancelPetAnimation", true); TryFixStuttering = Config.Bind("Client Options", "TryFixStuttering", true); + ShowClientControlGUI = Config.Bind("Client Options", "ShowClientControlGUI", true); UIScaleFactor = Config.Bind("Client Options", "UIScaleFactor", 1f); AddComponent(); diff --git a/Modules/ClientOptionItem.cs b/Modules/ClientOptionItem.cs index 428510c71..a04751880 100644 --- a/Modules/ClientOptionItem.cs +++ b/Modules/ClientOptionItem.cs @@ -25,7 +25,7 @@ public class ClientOptionItem ToggleButtonBehaviour mouseMoveToggle = optionsMenuBehaviour.DisableMouseMovement; - if (CustomBackground == null) + if (!CustomBackground) { CustomBackground = Object.Instantiate(optionsMenuBehaviour.Background, optionsMenuBehaviour.transform); CustomBackground.name = "CustomBackground"; diff --git a/Modules/Extensions/CountdownTimer.cs b/Modules/Extensions/CountdownTimer.cs index 1db46f9b7..bfb776262 100644 --- a/Modules/Extensions/CountdownTimer.cs +++ b/Modules/Extensions/CountdownTimer.cs @@ -8,7 +8,6 @@ namespace EHR.Modules.Extensions; public sealed class CountdownTimer : IDisposable { private readonly float _durationSeconds; - private readonly Stopwatch _stopwatch; private Coroutine _coroutine; private bool _completed; @@ -27,7 +26,7 @@ public sealed class CountdownTimer : IDisposable throw new ArgumentOutOfRangeException(nameof(durationSeconds)); _durationSeconds = durationSeconds; - _stopwatch = new Stopwatch(); + Stopwatch = new Stopwatch(); _cancelOnMeeting = cancelOnMeeting; _cancelOnGameEnd = cancelOnGameEnd; @@ -57,12 +56,12 @@ public sealed class CountdownTimer : IDisposable if (_completed) return TimeSpan.Zero; - TimeSpan remaining = TimeSpan.FromSeconds(_durationSeconds) - _stopwatch.Elapsed; + TimeSpan remaining = TimeSpan.FromSeconds(_durationSeconds) - Stopwatch.Elapsed; return remaining >= TimeSpan.Zero ? remaining : TimeSpan.Zero; } } - public Stopwatch Stopwatch => _stopwatch; + public Stopwatch Stopwatch { get; } public void Start() { @@ -70,7 +69,7 @@ public sealed class CountdownTimer : IDisposable return; _completed = false; - _stopwatch.Restart(); + Stopwatch.Restart(); _coroutine = Main.Instance.StartCoroutine(_hasTickEvent ? Run() : RunWithoutTicks()); } @@ -84,7 +83,7 @@ public sealed class CountdownTimer : IDisposable { int lastRemaining = (int)Math.Ceiling(Remaining.TotalSeconds); - while (_stopwatch.Elapsed.TotalSeconds < _durationSeconds) + while (Stopwatch.Elapsed.TotalSeconds < _durationSeconds) { int remaining = (int)Math.Ceiling(Remaining.TotalSeconds); @@ -101,7 +100,7 @@ public sealed class CountdownTimer : IDisposable if (remaining > 1) yield return new WaitForSecondsRealtime(1f); else - yield return new WaitForSecondsRealtime((float)(_durationSeconds - _stopwatch.Elapsed.TotalSeconds)); + yield return new WaitForSecondsRealtime((float)(_durationSeconds - Stopwatch.Elapsed.TotalSeconds)); } Complete(); @@ -119,7 +118,7 @@ public sealed class CountdownTimer : IDisposable return; _completed = true; - _stopwatch.Stop(); + Stopwatch.Stop(); try { @@ -146,7 +145,7 @@ public sealed class CountdownTimer : IDisposable _coroutine = null; } - _stopwatch.Stop(); + Stopwatch.Stop(); OnElapsed = null; OnTick = null; OnCanceled = null; diff --git a/Modules/GuessManager.cs b/Modules/GuessManager.cs index 481510dfd..75ec1165b 100644 --- a/Modules/GuessManager.cs +++ b/Modules/GuessManager.cs @@ -1200,6 +1200,34 @@ public static class GuessManager public static void Postfix() { Object.Destroy(TextTemplate.gameObject); + if (GuesserUI) Object.Destroy(GuesserUI); + + foreach (List roleButtonsValue in RoleButtons.Values) + { + foreach (Transform transform in roleButtonsValue) + { + if (transform.gameObject) + Object.Destroy(transform.gameObject); + } + } + + foreach (SpriteRenderer spriteRenderer in RoleSelectButtons.Values) + { + if (spriteRenderer.gameObject) + Object.Destroy(spriteRenderer.gameObject); + } + + foreach (SpriteRenderer spriteRenderer in PageButtons) + { + if (spriteRenderer.gameObject) + Object.Destroy(spriteRenderer.gameObject); + } + + TextTemplate = null; + GuesserUI = null; + RoleButtons.Clear(); + RoleSelectButtons.Clear(); + PageButtons.Clear(); } } diff --git a/Modules/ModUpdater.cs b/Modules/ModUpdater.cs index daf7105dc..21ee6311f 100644 --- a/Modules/ModUpdater.cs +++ b/Modules/ModUpdater.cs @@ -30,6 +30,7 @@ public static class ModUpdater public static string DownloadUrl; private static GenericPopup InfoPopup; private static GenericPopup InfoPopupV2; + private static readonly HttpClient HttpClient = new(); [HarmonyPatch(typeof(MainMenuManager), nameof(MainMenuManager.Start))] [HarmonyPrefix] @@ -75,8 +76,7 @@ public static class ModUpdater public static string Get(string url) { string result; - HttpClient req = new(); - HttpResponseMessage res = req.GetAsync(url).Result; + HttpResponseMessage res = HttpClient.GetAsync(url).Result; Stream stream = res.Content.ReadAsStreamAsync().Result; try @@ -227,9 +227,7 @@ public static class ModUpdater // Delete the temporary file if it exists if (File.Exists(savePath)) File.Delete(savePath); - HttpResponseMessage response; - - using (HttpClient client = new()) response = await client.GetAsync(url); + HttpResponseMessage response = await HttpClient.GetAsync(url); if (response is not { IsSuccessStatusCode: true }) throw new($"File retrieval failed with status code: {response.StatusCode}"); @@ -266,9 +264,7 @@ public static class ModUpdater // Delete the temporary file if it exists if (File.Exists(savePath)) File.Delete(savePath); - HttpResponseMessage response; - - using (HttpClient client = new()) response = await client.GetAsync(url); + HttpResponseMessage response = await HttpClient.GetAsync(url); if (response is not { IsSuccessStatusCode: true }) throw new($"File retrieval failed with status code: {response.StatusCode}"); @@ -302,41 +298,44 @@ public static class ModUpdater public static void ShowPopup(string message, StringNames buttonText, bool showButton = false, bool buttonIsExit = true) { - if (InfoPopup == null) return; + if (!InfoPopup) return; InfoPopup.Show(message); Transform button = InfoPopup.transform.FindChild("ExitGame"); - if (button != null) + if (button) { button.gameObject.SetActive(showButton); - button.GetChild(0).GetComponent().TargetText = buttonText; - button.GetChild(0).GetComponent().ResetText(); - button.GetComponent().OnClick = new(); + var textTranslatorTMP = button.GetChild(0).GetComponent(); + textTranslatorTMP.TargetText = buttonText; + textTranslatorTMP.ResetText(); + var passiveButton = button.GetComponent(); + passiveButton.OnClick = new(); if (buttonIsExit) - button.GetComponent().OnClick.AddListener((Action)Application.Quit); + passiveButton.OnClick.AddListener((Action)Application.Quit); else - button.GetComponent().OnClick.AddListener((Action)(() => InfoPopup.Close())); + passiveButton.OnClick.AddListener((Action)(() => InfoPopup.Close())); } } private static void ShowPopupWithTwoButtons(string message, string firstButtonText, string secondButtonText = "", Action onClickOnFirstButton = null, Action onClickOnSecondButton = null) { - if (InfoPopupV2 != null) + if (InfoPopupV2) { var templateExitGame = InfoPopupV2.transform.FindChild("ExitGame"); - if (templateExitGame == null) return; + if (!templateExitGame) return; var background = InfoPopupV2.transform.FindChild("Background"); - if (background == null) return; + if (!background) return; background.localScale *= 2f; InfoPopupV2.Show(message); templateExitGame.gameObject.SetActive(false); var firstButton = Object.Instantiate(templateExitGame, InfoPopupV2.transform); var secondButton = Object.Instantiate(templateExitGame, InfoPopupV2.transform); - if (firstButton != null) + + if (firstButton) { firstButton.gameObject.SetActive(true); firstButton.name = "FirstButton"; @@ -344,17 +343,20 @@ public static class ModUpdater firstButton.transform.localPosition = new Vector3(firstButtonTransform.localPosition.x - 1f, firstButtonTransform.localPosition.y - 0.7f, firstButtonTransform.localPosition.z); firstButton.transform.localScale *= 1.2f; var firstButtonGetChild = firstButton.GetChild(0); - firstButtonGetChild.GetComponent().TargetText = StringNames.Cancel; - firstButtonGetChild.GetComponent().ResetText(); - firstButtonGetChild.GetComponent().DestroyTranslator(); + var textTranslatorTMP = firstButtonGetChild.GetComponent(); + textTranslatorTMP.TargetText = StringNames.Cancel; + textTranslatorTMP.ResetText(); + textTranslatorTMP.DestroyTranslator(); firstButtonGetChild.GetComponent().text = firstButtonText; firstButtonGetChild.GetComponent().text = firstButtonText; - firstButton.GetComponent().OnClick = new(); + var passiveButton = firstButton.GetComponent(); + passiveButton.OnClick = new(); if (onClickOnFirstButton != null) - firstButton.GetComponent().OnClick.AddListener((UnityAction)(() => { onClickOnFirstButton(); InfoPopupV2.Close();})); - else firstButton.GetComponent().OnClick.AddListener((UnityAction)(() => InfoPopupV2.Close())); + passiveButton.OnClick.AddListener((UnityAction)(() => { onClickOnFirstButton(); InfoPopupV2.Close();})); + else passiveButton.OnClick.AddListener((UnityAction)(() => InfoPopupV2.Close())); } - if (secondButton != null) + + if (secondButton) { secondButton.gameObject.SetActive(true); secondButton.name = "SecondButton"; @@ -362,18 +364,21 @@ public static class ModUpdater secondButton.transform.localPosition = new Vector3(secondButtonTransform.localPosition.x + 1f, secondButtonTransform.localPosition.y - 0.7f, secondButtonTransform.localPosition.z); secondButton.transform.localScale *= 1.2f; var secondButtonGetChild = secondButton.GetChild(0); - secondButtonGetChild.GetComponent().TargetText = StringNames.Cancel; - secondButtonGetChild.GetComponent().ResetText(); + var textTranslatorTMP = secondButtonGetChild.GetComponent(); + textTranslatorTMP.TargetText = StringNames.Cancel; + textTranslatorTMP.ResetText(); if (!string.IsNullOrWhiteSpace(secondButtonText)) { - secondButtonGetChild.GetComponent().DestroyTranslator(); + textTranslatorTMP.DestroyTranslator(); secondButtonGetChild.GetComponent().text = secondButtonText; secondButtonGetChild.GetComponent().text = secondButtonText; } - secondButton.GetComponent().OnClick = new(); + + var passiveButton = secondButton.GetComponent(); + passiveButton.OnClick = new(); if (onClickOnSecondButton != null) - secondButton.GetComponent().OnClick.AddListener((UnityAction)(() => { onClickOnSecondButton(); InfoPopupV2.Close(); })); - else secondButton.GetComponent().OnClick.AddListener((UnityAction)(() => InfoPopupV2.Close())); + passiveButton.OnClick.AddListener((UnityAction)(() => { onClickOnSecondButton(); InfoPopupV2.Close(); })); + else passiveButton.OnClick.AddListener((UnityAction)(() => InfoPopupV2.Close())); } } } diff --git a/Modules/OnlinePresetsManager.cs b/Modules/OnlinePresetsManager.cs index a83fa9e52..a8e2bf38f 100644 --- a/Modules/OnlinePresetsManager.cs +++ b/Modules/OnlinePresetsManager.cs @@ -98,7 +98,6 @@ public static class OnlinePresetsManager TextMeshPro plusText = row.PlusBtn.GetComponentInChildren(); plusText.DestroyTranslator(); plusText.text = "ⓘ"; - row.PlusBtn.transform.localPosition += new Vector3(1.8f, 0f, 0f); row.PlusBtn.OnClick = new(); row.PlusBtn.OnClick.AddListener((UnityAction)(() => { diff --git a/Modules/OptionItem/OptionItem.cs b/Modules/OptionItem/OptionItem.cs index 4cbe174ee..8480810eb 100644 --- a/Modules/OptionItem/OptionItem.cs +++ b/Modules/OptionItem/OptionItem.cs @@ -65,7 +65,8 @@ public abstract class OptionItem private bool IsHidden { get; set; } public bool IsText { get; protected set; } - public TextOptionItem Header { get; set; } = null; + public TextOptionItem Header { get; set; } + public BaseGameSetting CachedSetting { get; set; } public Dictionary ReplacementDictionary { diff --git a/Patches/ClientOptionsPatch.cs b/Patches/ClientOptionsPatch.cs index 661b175db..d7ea36649 100644 --- a/Patches/ClientOptionsPatch.cs +++ b/Patches/ClientOptionsPatch.cs @@ -33,6 +33,7 @@ public static class OptionsMenuBehaviourStartPatch private static ClientOptionItem ButtonCooldownInDecimalUnder10s; private static ClientOptionItem CancelPetAnimation; private static ClientOptionItem TryFixStuttering; + private static ClientOptionItem ShowClientControlGUI; #if DEBUG private static ClientOptionItem GodMode; #endif @@ -256,6 +257,24 @@ public static class OptionsMenuBehaviourStartPatch } } } + + if (ShowClientControlGUI == null || !ShowClientControlGUI.ToggleButton) + { + ShowClientControlGUI = ClientOptionItem.Create("ShowClientControlGUI", Main.ShowClientControlGUI, __instance, ShowClientControlGUIButtonToggle); + + static void ShowClientControlGUIButtonToggle() + { + switch (Main.ShowClientControlGUI.Value) + { + case true when !ClientControlGUI.Instance: + Main.Instance.AddComponent(); + break; + case false when ClientControlGUI.Instance: + Object.Destroy(ClientControlGUI.Instance); + break; + } + } + } #if DEBUG if ((GodMode == null || GodMode.ToggleButton == null) && DebugModeManager.AmDebugger) diff --git a/Patches/GameOptionsMenuPatch.cs b/Patches/GameOptionsMenuPatch.cs index 3877025ec..615935d4f 100644 --- a/Patches/GameOptionsMenuPatch.cs +++ b/Patches/GameOptionsMenuPatch.cs @@ -20,7 +20,7 @@ namespace EHR; public static class ModGameOptionsMenu { public static int TabIndex; - public static readonly List BaseGameSettingCache = new(); + public static readonly List RunningMainCoroutines = new(); public static readonly Dictionary OptionList = new(); public static readonly Dictionary BehaviourList = new(); public static readonly Dictionary CategoryHeaderList = new(); @@ -46,7 +46,7 @@ public static class ModGameOptionsMenu foreach (var btn in ui.GetComponentsInChildren(true)) btn.OnClick.RemoveAllListeners(); - Object.Destroy(ui); + Object.DestroyImmediate(ui); } SpawnedUI.Clear(); @@ -64,7 +64,7 @@ public static class ModGameOptionsMenu kv.Key.SetClickMask(null); if (kv.Key.TryGetComponent(out var btn)) btn.OnClick.RemoveAllListeners(); - Object.Destroy(kv.Key.gameObject); + Object.DestroyImmediate(kv.Key.gameObject); } } @@ -76,7 +76,7 @@ public static class ModGameOptionsMenu kv.Value.SetClickMask(null); if (kv.Value.TryGetComponent(out var btn)) btn.OnClick.RemoveAllListeners(); - Object.Destroy(kv.Value.gameObject); + Object.DestroyImmediate(kv.Value.gameObject); } } @@ -86,18 +86,20 @@ public static class ModGameOptionsMenu { if (kv.Value.TryGetComponent(out var btn)) btn.OnClick.RemoveAllListeners(); - Object.Destroy(kv.Value.gameObject); + Object.DestroyImmediate(kv.Value.gameObject); } } - foreach (var setting in BaseGameSettingCache) + + foreach (var co in RunningMainCoroutines) { - if (setting) Object.Destroy(setting); + if (co != null) + Main.Instance.StopCoroutine(co); } OptionList.Clear(); BehaviourList.Clear(); CategoryHeaderList.Clear(); - BaseGameSettingCache.Clear(); + RunningMainCoroutines.Clear(); } } @@ -289,8 +291,6 @@ public static class GameOptionsMenuPatch foreach (UiElement x in __instance.scrollBar.GetComponentsInChildren()) __instance.ControllerSelectable.Add(x); - - yield break; } float CalculateScrollBarYBoundsMax() @@ -468,6 +468,8 @@ public static class GameOptionsMenuPatch } public static BaseGameSetting GetSetting(OptionItem item) { + if (item.CachedSetting) return item.CachedSetting; + BaseGameSetting baseGameSetting; switch (item) @@ -525,7 +527,7 @@ public static class GameOptionsMenuPatch if (baseGameSetting) { baseGameSetting.Title = StringNames.Accept; - ModGameOptionsMenu.BaseGameSettingCache.Add(baseGameSetting); + item.CachedSetting = baseGameSetting; } return baseGameSetting; } @@ -878,7 +880,11 @@ public static class StringOptionPatch long now = Utils.TimeStamp; bool startCoRoutine = now > HelpShowEndTS; HelpShowEndTS = now + 15; - if (startCoRoutine) Main.Instance.StartCoroutine(CoRoutine()); + if (startCoRoutine) + { + var co = Main.Instance.StartCoroutine(CoRoutine()); + ModGameOptionsMenu.RunningMainCoroutines.Add(co); + } IEnumerator CoRoutine() { @@ -1077,14 +1083,14 @@ public static class GameSettingMenuPatch private static float VanillaKillCooldownOnOpen = 1; private static float ModdedKillCooldownOnOpen = 1; - public static void DestroySetting() + private static void DestroySetting() { foreach (var button in ModSettingsButtons.Values) { if (button) { button.OnClick.RemoveAllListeners(); - Object.Destroy(button.gameObject); + Object.DestroyImmediate(button.gameObject); } } @@ -1101,7 +1107,7 @@ public static class GameSettingMenuPatch if (child.TryGetComponent(out var btn)) btn.OnClick.RemoveAllListeners(); - Object.Destroy(child.gameObject); + Object.DestroyImmediate(child.gameObject); } tab.Children.Clear(); } @@ -1109,7 +1115,7 @@ public static class GameSettingMenuPatch } foreach (var gmButton in GMButtons) - if (gmButton) Object.Destroy(gmButton); + if (gmButton) Object.DestroyImmediate(gmButton); ModSettingsButtons.Clear(); ModSettingsTabs.Clear(); @@ -1499,7 +1505,7 @@ public static class GameSettingMenuPatch if (!OnlinePresetsManager.PresetsLoaded && tabGroup == TabGroup.PresetExplorer) { - Main.Instance.StartCoroutine( + var co = Main.Instance.StartCoroutine( OnlinePresetsManager.FetchPresetList(list => { OnlinePresetsManager.CachedPresets = list; @@ -1507,6 +1513,7 @@ public static class GameSettingMenuPatch GameOptionsMenuPatch.ReloadUI(); }) ); + ModGameOptionsMenu.RunningMainCoroutines.Add(co); } if ((previewOnly && Controller.currentTouchType == Controller.TouchType.Joystick) || !previewOnly) @@ -1678,5 +1685,4 @@ public static class RpcSyncSettingsPatch { OptionItem.SyncAllOptions(); } - } \ No newline at end of file diff --git a/Patches/LobbyViewSettingsPanePatch.cs b/Patches/LobbyViewSettingsPanePatch.cs index d57459f0a..12d798ffc 100644 --- a/Patches/LobbyViewSettingsPanePatch.cs +++ b/Patches/LobbyViewSettingsPanePatch.cs @@ -28,6 +28,20 @@ public static class LobbyViewSettingsPanePatch private static readonly HashSet RoleEnabledList = []; private static StringNames VanillaSettingsTabName => StringNames.OverviewCategory; private static StringNames VanillaRolesTabName => StringNames.RolesCategory; + + // Storing Unity objects in static variables prevents GC from collecting them + // (and everything inside them, like event listeners) + // even if the object was destroyed + // This method doesn't check object lifetime because it's called when a game starts, + // and these are guaranteed to be destroyed by then + public static void ClearReferences() + { + RoleListCoroutine = null; + OptionsCoroutine = null; + ShowOnlyEnabledRolesButton = null; + TabButtons.Clear(); + AllTabButtons.Clear(); + } [HarmonyPatch(nameof(LobbyViewSettingsPane.Awake))] [HarmonyPostfix] @@ -293,7 +307,7 @@ public static class LobbyViewSettingsPanePatch rightPassiveButton.OnClick.AddListener((UnityAction)(() => { LastGameModeSelected++; - CustomGameMode[] enumGameModes = Enum.GetValues().Without(CustomGameMode.All).ToArray(); + CustomGameMode[] enumGameModes = Enum.GetValues()[..^1]; if ((int)LastGameModeSelected > enumGameModes.Length) LastGameModeSelected = CustomGameMode.Standard; __instance.ChangeTab(VanillaSettingsTabName); @@ -313,7 +327,7 @@ public static class LobbyViewSettingsPanePatch leftButtonPassiveButton.OnClick.AddListener((UnityAction)(() => { LastGameModeSelected--; - CustomGameMode[] enumGameModes = Enum.GetValues().Without(CustomGameMode.All).ToArray(); + CustomGameMode[] enumGameModes = Enum.GetValues()[..^1]; if ((int)LastGameModeSelected < 0x01) LastGameModeSelected = (CustomGameMode)enumGameModes.Length; __instance.ChangeTab(VanillaSettingsTabName); @@ -722,7 +736,7 @@ public static class LobbyViewSettingsPanePatch private static void DrawRoles(LobbyViewSettingsPane viewSettings, TabGroup tabName) { var yPos = 1.3f; - var xPos = -6.53f; + const float xPos = -6.53f; float xPosOpt; var index = 0; RoleEnabledList.Clear(); @@ -828,7 +842,7 @@ public static class LobbyViewSettingsPanePatch // Roles if (allCustomRoles.FindFirst(x => x.ToString() == realName, out CustomRoles role)) { - if (role == default(CustomRoles) || role is CustomRoles.CovenLeader) continue; + if (role is default(CustomRoles) or CustomRoles.CovenLeader) continue; try { @@ -911,9 +925,7 @@ public static class LobbyViewSettingsPanePatch viewSettings.settingsInfo.Add(viewSettingsInfoPanelRoleVariant.gameObject); yPos -= 0.65f; } - else if (LastGameModeSelected == CustomGameMode.HideAndSeek && role is CustomRoles.Seeker or CustomRoles.Hider) - RoleEnabledList.Add(role); - else if (!enabledOrNotCollapsed && enabled && !roleDisabled) + else if (LastGameModeSelected == CustomGameMode.HideAndSeek && role is CustomRoles.Seeker or CustomRoles.Hider || !enabledOrNotCollapsed && enabled && !roleDisabled) RoleEnabledList.Add(role); } catch (Exception e) { Utils.ThrowException(e); } @@ -922,8 +934,8 @@ public static class LobbyViewSettingsPanePatch { // Skip all role settings if (option.Parent != null && allCustomRoles.Any(x => x.ToString() == option.Parent?.Name)) continue; - else if (option.Parent?.Parent != null && allCustomRoles.Any(x => x.ToString() == option.Parent?.Parent?.Name)) continue; - else if (option.Parent?.Parent?.Parent != null && allCustomRoles.Any(x => x.ToString() == option.Parent?.Parent?.Parent.Name)) continue; + if (option.Parent?.Parent != null && allCustomRoles.Any(x => x.ToString() == option.Parent?.Parent?.Name)) continue; + if (option.Parent?.Parent?.Parent != null && allCustomRoles.Any(x => x.ToString() == option.Parent?.Parent?.Parent.Name)) continue; ViewSettingsInfoPanel viewSettingsInfoPanel = Object.Instantiate(viewSettings.infoPanelOrigin, Vector3.zero, Quaternion.identity, viewSettings.settingsContainer); viewSettingsInfoPanel.name = option.Name; @@ -986,6 +998,7 @@ public static class LobbyViewSettingsPanePatch if (RoleEnabledList.Count <= 0) yield break; yield return CoShowRoleSettings().WrapToIl2Cpp(); + yield break; IEnumerator CoShowRoleSettings() { @@ -1001,8 +1014,8 @@ public static class LobbyViewSettingsPanePatch float leftY = startY; float rightY = startY; - float leftX = -5.8f; - var rightX = 0.15f; + const float leftX = -5.8f; + const float rightX = 0.15f; foreach (CustomRoles role in RoleEnabledList) { diff --git a/Patches/MainMenuManagerPatch.cs b/Patches/MainMenuManagerPatch.cs index 1033fe9cc..0c163ce34 100644 --- a/Patches/MainMenuManagerPatch.cs +++ b/Patches/MainMenuManagerPatch.cs @@ -185,7 +185,7 @@ public static class MainMenuManagerPatch { GameOptionsManager.Instance.Initialize(); - if (GameOptionsManager.Instance.normalGameHostOptions.MapId is 3 or > 5) + if (GameOptionsManager.Instance.normalGameHostOptions.MapId == 3 || (GameOptionsManager.Instance.normalGameHostOptions.MapId > 5 && !SubmergedCompatibility.Loaded)) { GameOptionsManager.Instance.normalGameHostOptions.MapId = 0; GameOptionsManager.Instance.SaveNormalHostOptions(); diff --git a/Patches/OnGameStartedPatch.cs b/Patches/OnGameStartedPatch.cs index b4ddec005..ac72aca63 100644 --- a/Patches/OnGameStartedPatch.cs +++ b/Patches/OnGameStartedPatch.cs @@ -280,6 +280,8 @@ internal static class ChangeRoleSettings ChatCommands.LastSpectators.UnionWith(ChatCommands.Spectators); RPCHandlerPatch.RemoveExpiredWhiteList(); + + LobbyViewSettingsPanePatch.ClearReferences(); try { diff --git a/Resources/Lang/en_US.jsonc b/Resources/Lang/en_US.jsonc index 99d010bb9..94135cbfe 100644 --- a/Resources/Lang/en_US.jsonc +++ b/Resources/Lang/en_US.jsonc @@ -10589,6 +10589,7 @@ "ButtonCooldownInDecimalUnder10s": "Button Cooldown in Decimal Under 10s", "CancelPetAnimation": "Fully Cancel Self Pet Animation As Host", "TryFixStuttering": "Try to Fix Stuttering", + "ShowClientControlGUI": "Show Client Control GUI", "GM": "Game Master", "GMInfo": "Spectate the chaos!", "GMInfoLong": "(Other):\\nThe Game Master is an observer role. Their presence does not affect the game; all players know who the Game Master is. The Game Master role will be assigned to the Host/Spectators, who will automatically become a ghost at the start of the game.", diff --git a/Roles/Standard/Crewmate/Investigate/Imitator.cs b/Roles/Standard/Crewmate/Investigate/Imitator.cs index 3bf57221b..c1578a03f 100644 --- a/Roles/Standard/Crewmate/Investigate/Imitator.cs +++ b/Roles/Standard/Crewmate/Investigate/Imitator.cs @@ -81,7 +81,7 @@ public class Imitator : RoleBase foreach (PlayerVoteArea pva in __instance.playerStates.ToArray()) { PlayerControl pc = Utils.GetPlayerById(pva.TargetPlayerId); - if (pc == null || pc.IsAlive()) continue; + if (!pc || pc.IsAlive()) continue; GameObject template = pva.Buttons.transform.Find("CancelButton").gameObject; GameObject targetBox = Object.Instantiate(template, pva.transform); diff --git a/Roles/Standard/Crewmate/Investigate/Inspector.cs b/Roles/Standard/Crewmate/Investigate/Inspector.cs index d7acccf52..b86777be7 100644 --- a/Roles/Standard/Crewmate/Investigate/Inspector.cs +++ b/Roles/Standard/Crewmate/Investigate/Inspector.cs @@ -418,7 +418,7 @@ public class Inspector : RoleBase foreach (PlayerVoteArea pva in __instance.playerStates) { PlayerControl pc = Utils.GetPlayerById(pva.TargetPlayerId); - if (pc == null || !pc.IsAlive()) continue; + if (!pc || !pc.IsAlive()) continue; GameObject template = pva.Buttons.transform.Find("CancelButton").gameObject; GameObject targetBox = Object.Instantiate(template, pva.transform); diff --git a/Roles/Standard/Crewmate/Killing/Judge.cs b/Roles/Standard/Crewmate/Killing/Judge.cs index f2d79bd80..c5f548e07 100644 --- a/Roles/Standard/Crewmate/Killing/Judge.cs +++ b/Roles/Standard/Crewmate/Killing/Judge.cs @@ -267,7 +267,7 @@ public class Judge : RoleBase foreach (PlayerVoteArea pva in __instance.playerStates) { PlayerControl pc = Utils.GetPlayerById(pva.TargetPlayerId); - if (pc == null || !pc.IsAlive()) continue; + if (!pc || !pc.IsAlive()) continue; GameObject template = pva.Buttons.transform.Find("CancelButton").gameObject; GameObject targetBox = Object.Instantiate(template, pva.transform); diff --git a/Roles/Standard/Crewmate/Killing/Retributionist.cs b/Roles/Standard/Crewmate/Killing/Retributionist.cs index c627e8150..b0dbefdc8 100644 --- a/Roles/Standard/Crewmate/Killing/Retributionist.cs +++ b/Roles/Standard/Crewmate/Killing/Retributionist.cs @@ -135,7 +135,7 @@ public class Retributionist : RoleBase foreach (PlayerVoteArea pva in __instance.playerStates.ToArray()) { PlayerControl pc = Utils.GetPlayerById(pva.TargetPlayerId); - if (pc == null || !pc.IsAlive()) continue; + if (!pc || !pc.IsAlive()) continue; GameObject template = pva.Buttons.transform.Find("CancelButton").gameObject; GameObject targetBox = Object.Instantiate(template, pva.transform); diff --git a/Roles/Standard/Crewmate/Power/Swapper.cs b/Roles/Standard/Crewmate/Power/Swapper.cs index a46e07fc6..34cda314f 100644 --- a/Roles/Standard/Crewmate/Power/Swapper.cs +++ b/Roles/Standard/Crewmate/Power/Swapper.cs @@ -281,7 +281,7 @@ public class Swapper : RoleBase foreach (PlayerVoteArea pva in __instance.playerStates) { PlayerControl pc = Utils.GetPlayerById(pva.TargetPlayerId); - if (pc == null || !pc.IsAlive()) continue; + if (!pc || !pc.IsAlive()) continue; GameObject template = pva.Buttons.transform.Find("CancelButton").gameObject; GameObject targetBox = Object.Instantiate(template, pva.transform); diff --git a/Roles/Standard/Impostor/Killing/Councillor.cs b/Roles/Standard/Impostor/Killing/Councillor.cs index 8487206ed..5324c3213 100644 --- a/Roles/Standard/Impostor/Killing/Councillor.cs +++ b/Roles/Standard/Impostor/Killing/Councillor.cs @@ -295,7 +295,7 @@ public class Councillor : RoleBase foreach (PlayerVoteArea pva in __instance.playerStates.ToArray()) { PlayerControl pc = Utils.GetPlayerById(pva.TargetPlayerId); - if (pc == null || !pc.IsAlive()) continue; + if (!pc || !pc.IsAlive()) continue; GameObject template = pva.Buttons.transform.Find("CancelButton").gameObject; GameObject targetBox = Object.Instantiate(template, pva.transform); diff --git a/Roles/Standard/Impostor/Killing/Nemesis.cs b/Roles/Standard/Impostor/Killing/Nemesis.cs index b3d8c16de..ec6f27d55 100644 --- a/Roles/Standard/Impostor/Killing/Nemesis.cs +++ b/Roles/Standard/Impostor/Killing/Nemesis.cs @@ -204,7 +204,7 @@ internal class Nemesis : RoleBase foreach (PlayerVoteArea pva in __instance.playerStates.ToArray()) { PlayerControl pc = Utils.GetPlayerById(pva.TargetPlayerId); - if (pc == null || !pc.IsAlive()) continue; + if (!pc || !pc.IsAlive()) continue; GameObject template = pva.Buttons.transform.Find("CancelButton").gameObject; GameObject targetBox = Object.Instantiate(template, pva.transform); diff --git a/Roles/Standard/Impostor/Support/Ventriloquist.cs b/Roles/Standard/Impostor/Support/Ventriloquist.cs index dca961f1c..edfdfbda1 100644 --- a/Roles/Standard/Impostor/Support/Ventriloquist.cs +++ b/Roles/Standard/Impostor/Support/Ventriloquist.cs @@ -64,7 +64,7 @@ public class Ventriloquist : RoleBase foreach (PlayerVoteArea pva in __instance.playerStates.ToArray()) { PlayerControl pc = Utils.GetPlayerById(pva.TargetPlayerId); - if (pc == null || !pc.IsAlive()) continue; + if (!pc || !pc.IsAlive()) continue; GameObject template = pva.Buttons.transform.Find("CancelButton").gameObject; GameObject targetBox = Object.Instantiate(template, pva.transform); diff --git a/Roles/Standard/Neutral/Pariah/Starspawn.cs b/Roles/Standard/Neutral/Pariah/Starspawn.cs index e03bd02c3..5f88de089 100644 --- a/Roles/Standard/Neutral/Pariah/Starspawn.cs +++ b/Roles/Standard/Neutral/Pariah/Starspawn.cs @@ -105,8 +105,9 @@ public class Starspawn : RoleBase private static void CreateStarspawnButton(MeetingHud __instance) { - if (GameObject.Find("StarspawnButton") != null) Object.Destroy(GameObject.Find("StarspawnButton").gameObject); - if (PlayerControl.LocalPlayer == null || !PlayerControl.LocalPlayer.IsAlive()) return; + GameObject existingButton = GameObject.Find("StarspawnButton"); + if (existingButton) Object.Destroy(existingButton.gameObject); + if (!PlayerControl.LocalPlayer || !PlayerControl.LocalPlayer.IsAlive()) return; GameObject parent = GameObject.Find("Main Camera").transform.Find("Hud").Find("ChatUi").Find("ChatScreenRoot").Find("ChatScreenContainer").gameObject; GameObject template = __instance.transform.Find("MeetingContents").Find("ButtonStuff").Find("button_skipVoting").gameObject;