diff --git a/MiraAPI.Example/Options/ExampleOptions2.cs b/MiraAPI.Example/Options/ExampleOptions2.cs index bc3c910..0f17078 100644 --- a/MiraAPI.Example/Options/ExampleOptions2.cs +++ b/MiraAPI.Example/Options/ExampleOptions2.cs @@ -10,6 +10,8 @@ public class ExampleOptions2 : AbstractOptionGroup public override uint GroupPriority => 0; // This group will be displayed first. The default value is uint.MaxValue. + public override MenuCategory ParentMenu => MenuCategory.CustomTwo; + public ModdedPlayerOption PlayerOption2 { get; } = new("YOU MUST CHOSE A PLAYER", false); public ModdedPlayerOption PlayerOption { get; } = new("Eh whatever"); public ModdedToggleOption ToggleOpt1 { get; } = new("Toggle Option 1", false); diff --git a/MiraAPI/GameOptions/AbstractOptionGroup.cs b/MiraAPI/GameOptions/AbstractOptionGroup.cs index f8f0877..01c9e09 100644 --- a/MiraAPI/GameOptions/AbstractOptionGroup.cs +++ b/MiraAPI/GameOptions/AbstractOptionGroup.cs @@ -69,7 +69,7 @@ public abstract class AbstractOptionGroup internal CategoryHeaderMasked? Header { get; set; } - internal Coroutine? CreationCoroutine { get; set; } + internal bool Ready { get; set; } } /// @@ -132,6 +132,11 @@ public abstract class AbstractRoleOptionGroup() : AbstractOptionGroup wher /// public enum MenuCategory { + /// + /// Placeholder for indexing purposes. Options don't exist in the presset tab. + /// + Preset, + /// /// Determines the option group is in the game settings tab. /// diff --git a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs index fa91e7b..ad0cabc 100644 --- a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs +++ b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs @@ -254,12 +254,6 @@ public static class TaskAdderPatches .ToArray() .Any(x => x.FolderName == child.FolderName); - [HarmonyPostfix] - [HarmonyPatch(nameof(TaskAdderGame.OnDisable))] - public static void ClosePatch() - { - Utilities.Extensions.ClearGarbageCollector(); - } // yes it might be crazy patching the entire method, but i tried so many other methods and only this works :cry: // true -chip [HarmonyPrefix] diff --git a/MiraAPI/Patches/HudManagerPatches.cs b/MiraAPI/Patches/HudManagerPatches.cs index a508ccf..76adc2c 100644 --- a/MiraAPI/Patches/HudManagerPatches.cs +++ b/MiraAPI/Patches/HudManagerPatches.cs @@ -150,7 +150,6 @@ public static class HudManagerPatches { t.gameObject.Destroy(); } - Utilities.Extensions.ClearGarbageCollector(); var gridArrange = BottomLeft.GetComponent(); var aspectPosition = BottomLeft.GetComponent(); diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs index b50b430..2bc615d 100644 --- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs +++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs @@ -21,6 +21,54 @@ namespace MiraAPI.Patches.Options; [HarmonyPatch(typeof(GameOptionsMenu))] internal static class GameOptionsMenuPatch { + [HarmonyPrefix] + [HarmonyPatch(nameof(GameOptionsMenu.Initialize))] + // ReSharper disable once InconsistentNaming + public static bool InitPatch(GameOptionsMenu __instance) + { + __instance.Children ??= new Il2CppSystem.Collections.Generic.List(); + __instance.Children.Clear(); + + if (MenuState.Instance.CurrentModIdx == 0) + { + __instance.MapPicker.gameObject.SetActive(true); + __instance.cachedData = GameOptionsManager.Instance.CurrentGameOptions; + var container = MenuState.Instance.CurrentContainer.transform; + + if (container.childCount == 1) + { + __instance.MapPicker.Initialize(20); + var mapNameSetting = GameManager.Instance.GameSettingsList.MapNameSetting; + __instance.MapPicker.SetUpFromData(mapNameSetting, 20); + __instance.Children.Add(__instance.MapPicker); + __instance.CreateSettings(); + foreach (var optionBehaviour in __instance.Children) + { + if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost) + { + optionBehaviour.SetAsPlayer(); + } + + optionBehaviour.OnValueChanged = new Action(__instance.ValueChanged); + } + } + else + { + foreach (var child in container.GetComponentsInChildren(true)) + { + __instance.Children.Add(child); + } + } + } + else + { + __instance.MapPicker.gameObject.SetActive(false); + CustomCreateSettings(__instance); + } + + return false; + } + [HarmonyPostfix] [HarmonyPatch(nameof(GameOptionsMenu.OnEnable))] public static void OpenPatch(GameOptionsMenu __instance) @@ -35,47 +83,41 @@ internal static class GameOptionsMenuPatch HudManager.Instance.PlayerCam.OverrideScreenShakeEnabled = true; } - [HarmonyPostfix] - [HarmonyPatch(nameof(GameOptionsMenu.CloseMenu))] - public static void FullMenuClosePatch(GameOptionsMenu __instance) - { - Utilities.Extensions.ClearGarbageCollector(); - } - [HarmonyPostfix] [HarmonyPatch(nameof(GameOptionsMenu.Update))] // ReSharper disable once InconsistentNaming public static void UpdatePatch(GameOptionsMenu __instance) { - if (GameSettingMenuPatches.SelectedModIdx == 0) + if (MenuState.Instance.CurrentModIdx == 0) { return; } var num = 2.1f; - if (__instance.name == "MODIFIERS TAB") - { - ModifiersUpdate(ref num); - } - else if (__instance.name == "CUSTOM TAB 1") - { - CustomMenuOneUpdate(ref num); - } - else if (__instance.name == "CUSTOM TAB 2") - { - CustomMenuTwoUpdate(ref num); - } - else - { - var filteredGroups = - GameSettingMenuPatches.SelectedMod?.InternalOptionGroups - .Where(x => x.OptionableType == null && - x.ParentMenu == MenuCategory.Game) ?? []; - foreach (var group in filteredGroups) - { - UpdateGroup(group, ref num); - } + switch (MenuState.Instance.CurrentMenu) + { + case MenuCategory.Modifiers: + ModifiersUpdate(ref num); + break; + case MenuCategory.CustomOne: + CustomMenuOneUpdate(ref num); + break; + case MenuCategory.CustomTwo: + CustomMenuTwoUpdate(ref num); + break; + default: + var filteredGroups = + MenuState.Instance.CurrentMod.InternalOptionGroups + .Where(x => x.OptionableType == null && + x.ParentMenu == MenuCategory.Game) ?? []; + + foreach (var group in filteredGroups) + { + UpdateGroup(group, ref num); + } + + break; } __instance.scrollBar.SetYBoundsMax(-num - 1.65f); @@ -83,7 +125,7 @@ internal static class GameOptionsMenuPatch private static void UpdateGroup(AbstractOptionGroup? group, ref float num) { - if (group is null || group.Options.Count == 0 || group.Header is null || group.CreationCoroutine is not null) + if (group is null || !group.Ready || group.Options.Count == 0 || group.Header is null) { return; } @@ -135,8 +177,8 @@ internal static class GameOptionsMenuPatch private static void CustomMenuOneUpdate(ref float num) { - var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups - .Where(x => x.ParentMenu == MenuCategory.CustomOne) ?? []; + var groups = MenuState.Instance.CurrentMod.InternalOptionGroups + .Where(x => x.ParentMenu == MenuCategory.CustomOne); foreach (var modGroup in groups) { @@ -144,20 +186,10 @@ internal static class GameOptionsMenuPatch } } - private static void CustomMenuOneCreate(GameOptionsMenu menu) - { - var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups - .Where(x => x.ParentMenu == MenuCategory.CustomOne) ?? []; - foreach (var group in groups) - { - CreateGroup(menu, group); - } - } - private static void CustomMenuTwoUpdate(ref float num) { - var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups - .Where(x => x.ParentMenu == MenuCategory.CustomTwo) ?? []; + var groups = MenuState.Instance.CurrentMod.InternalOptionGroups + .Where(x => x.ParentMenu == MenuCategory.CustomTwo); foreach (var modGroup in groups) { @@ -165,21 +197,11 @@ internal static class GameOptionsMenuPatch } } - private static void CustomMenuTwoCreate(GameOptionsMenu menu) - { - var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups - .Where(x => x.ParentMenu == MenuCategory.CustomTwo) ?? []; - foreach (var group in groups) - { - CreateGroup(menu, group); - } - } - private static void ModifiersUpdate(ref float num) { - var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups + var groups = MenuState.Instance.CurrentMod.InternalOptionGroups .Where(x => x.ParentMenu is MenuCategory.Modifiers || - x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) ?? []; + x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true); foreach (var modGroup in groups) { @@ -187,132 +209,109 @@ internal static class GameOptionsMenuPatch } } - private static void ModifiersCreate(GameOptionsMenu menu) + private static Coroutine? _creationCoroutine; + + public static void CustomCreateSettings(GameOptionsMenu menu) { - var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups - .Where(x => x.ParentMenu is MenuCategory.Modifiers || - x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true) ?? []; - foreach (var group in groups) - { - CreateGroup(menu, group); - } - } + menu.MapPicker.gameObject.SetActive(false); + var mod = MenuState.Instance.CurrentMod; - [HarmonyPrefix] - [HarmonyPatch(nameof(GameOptionsMenu.CreateSettings))] - // ReSharper disable once InconsistentNaming - public static bool SettingsPatch(GameOptionsMenu __instance) - { - if (GameSettingMenuPatches.SelectedModIdx == 0) + var filteredGroups = MenuState.Instance.CurrentMenu switch { - return true; + MenuCategory.Game => mod.InternalOptionGroups.Where(x => + x.OptionableType == null && x.ParentMenu == MenuCategory.Game), + MenuCategory.Modifiers => mod.InternalOptionGroups.Where(x => + x.ParentMenu is MenuCategory.Modifiers || + x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true), + MenuCategory.CustomOne => mod.InternalOptionGroups.Where(x => x.ParentMenu == MenuCategory.CustomOne), + MenuCategory.CustomTwo => mod.InternalOptionGroups.Where(x => x.ParentMenu == MenuCategory.CustomTwo), + _ => [], + }; + + if (_creationCoroutine != null) + { + GameSettingMenu.Instance.StopCoroutine(_creationCoroutine); } - __instance.MapPicker.gameObject.SetActive(false); + _creationCoroutine = GameSettingMenu.Instance.StartCoroutine(CreateGroups().WrapToIl2Cpp()); - switch (__instance.name) + IEnumerator CreateGroups() { - case "MODIFIERS TAB": - ModifiersCreate(__instance); - return false; - case "CUSTOM TAB 1": - CustomMenuOneCreate(__instance); - return false; - case "CUSTOM TAB 2": - CustomMenuTwoCreate(__instance); - return false; - } - - var filteredGroups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups - .Where(x => x.OptionableType == null && x.ParentMenu == MenuCategory.Game) ?? []; - - foreach (var group in filteredGroups) - { - CreateGroup(__instance, group); - } - - return false; - } - - [HarmonyPrefix] - [HarmonyPatch(nameof(GameOptionsMenu.Initialize))] - // ReSharper disable once InconsistentNaming - public static bool InitPatch(GameOptionsMenu __instance) - { - if (__instance.Children != null && __instance.Children.Count != 0) - { - return false; - } - - __instance.MapPicker.gameObject.SetActive(true); - __instance.MapPicker.Initialize(20); - var mapNameSetting = GameManager.Instance.GameSettingsList.MapNameSetting; - __instance.MapPicker.SetUpFromData(mapNameSetting, 20); - __instance.Children = new Il2CppSystem.Collections.Generic.List(); - __instance.Children.Add(__instance.MapPicker); - __instance.CreateSettings(); - __instance.cachedData = GameOptionsManager.Instance.CurrentGameOptions; - foreach (var optionBehaviour in __instance.Children) - { - if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost) + foreach (var group in filteredGroups) { - optionBehaviour.SetAsPlayer(); + yield return CoCreateGroup(menu, group); } - - if (optionBehaviour.IsCustom()) - { - continue; - } - - optionBehaviour.OnValueChanged = new Action(__instance.ValueChanged); } - - return false; } private static IEnumerator CoCreateGroup(GameOptionsMenu menu, AbstractOptionGroup group) { - var categoryHeaderMasked = Object.Instantiate( - menu.categoryHeaderOrigin, - Vector3.zero, - Quaternion.identity, - menu.settingsContainer); + var container = MenuState.Instance.CurrentContainer.transform; - categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName), 20); - categoryHeaderMasked.Background.color = group.GroupColor; - categoryHeaderMasked.Divider.color = group.GroupColor; - categoryHeaderMasked.Title.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor) - ? Color.white - : group.GroupColor.FindAlternateColor(); + group.Ready = false; + if (group.Header == null || !group.Header) + { + var categoryHeaderMasked = Object.Instantiate( + menu.categoryHeaderOrigin, + Vector3.zero, + Quaternion.identity, + container); - categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset(); - categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear; - categoryHeaderMasked.Background.sprite.texture.wrapMode = TextureWrapMode.Clamp; + categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName), 20); + categoryHeaderMasked.Background.color = group.GroupColor; + categoryHeaderMasked.Divider.color = group.GroupColor; + categoryHeaderMasked.Title.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor) + ? Color.white + : group.GroupColor.FindAlternateColor(); - categoryHeaderMasked.Background.transform.localPosition = new Vector3(0.5f, -0.1833f, 0); - categoryHeaderMasked.Background.size = new Vector2( - categoryHeaderMasked.Background.size.x + 1.5f, - categoryHeaderMasked.Background.size.y); + categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset(); + categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear; + categoryHeaderMasked.Background.sprite.texture.wrapMode = TextureWrapMode.Clamp; - categoryHeaderMasked.gameObject.SetActive(false); - group.Header = categoryHeaderMasked; + categoryHeaderMasked.Background.transform.localPosition = new Vector3(0.5f, -0.1833f, 0); + categoryHeaderMasked.Background.size = new Vector2( + categoryHeaderMasked.Background.size.x + 1.5f, + categoryHeaderMasked.Background.size.y); - var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform); - newText.text = "(Click to close)"; - newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f); - newText.gameObject.GetComponent().Destroy(); + categoryHeaderMasked.gameObject.SetActive(false); - var options = group.Options.Select(opt => opt.CreateOption( + var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform); + newText.text = "(Click to close)"; + newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f); + newText.gameObject.GetComponent().Destroy(); + + var boxCol = categoryHeaderMasked.gameObject.AddComponent(); + boxCol.size = new Vector2(7, 0.7f); + boxCol.offset = new Vector2(1.5f, -0.3f); + + var headerBtn = categoryHeaderMasked.gameObject.AddComponent(); + headerBtn.ClickSound = menu.BackButton.GetComponent().ClickSound; + headerBtn.OnMouseOver = new UnityEvent(); + headerBtn.OnMouseOut = new UnityEvent(); + headerBtn.OnClick.AddListener( + (UnityAction)(() => + { + group.AllOptionsHidden = !group.AllOptionsHidden; + newText.text = group.AllOptionsHidden + ? "(Click to open)" + : "(Click to close)"; + })); + headerBtn.SetButtonEnableState(true); + + group.Header = categoryHeaderMasked; + } + + var options = group.Options.Where(opt => opt.OptionBehaviour == null || !opt.OptionBehaviour + ).Select(opt => opt.CreateOption( menu.checkboxOrigin, menu.numberOptionOrigin, menu.stringOptionOrigin, menu.playerOptionOrigin, - menu.settingsContainer)); + container) + ); OptionPreset? defaultPreset = null; - if (GameSettingMenuPatches.SelectedMod != null && PresetManager.DefaultPresets.TryGetValue( - GameSettingMenuPatches.SelectedMod, - out var preset)) + if (PresetManager.DefaultPresets.TryGetValue(MenuState.Instance.CurrentMod, out var preset)) { defaultPreset = preset; } @@ -428,36 +427,12 @@ internal static class GameOptionsMenuPatch newOpt.Initialize(); newOpt.gameObject.SetActive(false); - }).WrapToIl2Cpp(); - var boxCol = categoryHeaderMasked.gameObject.AddComponent(); - boxCol.size = new Vector2(7, 0.7f); - boxCol.offset = new Vector2(1.5f, -0.3f); - - var headerBtn = categoryHeaderMasked.gameObject.AddComponent(); - headerBtn.ClickSound = menu.BackButton.GetComponent().ClickSound; - headerBtn.OnMouseOver = new UnityEvent(); - headerBtn.OnMouseOut = new UnityEvent(); - headerBtn.OnClick.AddListener( - (UnityAction)(() => + if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost) { - group.AllOptionsHidden = !group.AllOptionsHidden; - newText.text = group.AllOptionsHidden - ? "(Click to open)" - : "(Click to close)"; - })); - headerBtn.SetButtonEnableState(true); - - group.CreationCoroutine = null; - } - - private static void CreateGroup(GameOptionsMenu menu, AbstractOptionGroup group) - { - if (group.CreationCoroutine != null) - { - GameSettingMenu.Instance.StopCoroutine(group.CreationCoroutine); - } - - group.CreationCoroutine = GameSettingMenu.Instance.StartCoroutine(CoCreateGroup(menu, group).WrapToIl2Cpp()); + newOpt.SetAsPlayer(); + } + }).WrapToIl2Cpp(); + group.Ready = true; } } diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index ff75606..bc6dc3a 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using BepInEx.Configuration; using HarmonyLib; +using MiraAPI.GameOptions; using MiraAPI.PluginLoading; using MiraAPI.Presets; using MiraAPI.Roles; @@ -23,8 +24,6 @@ internal static class GamePresetsTabPatches private static GameObject _refreshButton = null!; private static GameObject _folderButton = null!; private static GameObject _newDivider = null!; - private static GameObject _presetHolder = null!; - private static GridArrange _arrange = null!; private static readonly object Lock = new(); @@ -41,10 +40,10 @@ internal static class GamePresetsTabPatches CreatePresetMenu(__instance); - __instance.StandardPresetButton.gameObject.SetActive(GameSettingMenuPatches.SelectedModIdx == 0); - __instance.SecondPresetButton.gameObject.SetActive(GameSettingMenuPatches.SelectedModIdx == 0); + __instance.StandardPresetButton.gameObject.SetActive(MenuState.Instance.CurrentModIdx == 0); + __instance.SecondPresetButton.gameObject.SetActive(MenuState.Instance.CurrentModIdx == 0); - if (GameSettingMenuPatches.SelectedModIdx != 0) + if (MenuState.Instance.CurrentModIdx != 0) { __instance.PresetDescriptionText.text = "Select or create an options preset for this mod"; } @@ -56,26 +55,41 @@ internal static class GamePresetsTabPatches // Show the save and refresh buttons if (_saveButton) { - _saveButton.SetActive(GameSettingMenuPatches.SelectedModIdx != 0); + _saveButton.SetActive(MenuState.Instance.CurrentModIdx != 0); } if (_refreshButton) { - _refreshButton.SetActive(GameSettingMenuPatches.SelectedModIdx != 0); + _refreshButton.SetActive(MenuState.Instance.CurrentModIdx != 0); } if (_folderButton) { - _folderButton.SetActive(GameSettingMenuPatches.SelectedModIdx != 0); + _folderButton.SetActive(MenuState.Instance.CurrentModIdx != 0); } if (_newDivider) { - _newDivider.SetActive(GameSettingMenuPatches.SelectedModIdx != 0); + _newDivider.SetActive(MenuState.Instance.CurrentModIdx != 0); + } + + if (MenuState.Instance.CurrentModIdx == 0) + { + return; } var prefab = GameSettingMenu.Instance.GameSettingsButton; - foreach (var preset in MiraPluginManager.Instance.RegisteredPlugins.SelectMany(x => x.InternalPresets)) + var presetHolder = MenuState.Instance.CurrentContainer; + var arrange = presetHolder.GetComponent(); + presetHolder.SetActive(true); + + foreach (var preset in MenuState.Instance.CurrentMod.InternalPresets) { - var button = Object.Instantiate(prefab, _presetHolder.transform); + if (preset.PresetButton != null && preset.PresetButton) + { + preset.PresetButton.SetActive(true); + continue; + } + + var button = Object.Instantiate(prefab, presetHolder.transform); button.buttonText.text = preset.Name; if (button.buttonText.TryGetComponent(out var tmp)) { @@ -90,23 +104,12 @@ internal static class GamePresetsTabPatches { preset.LoadPreset(); })); - button.gameObject.SetActive(false); + button.gameObject.SetActive(true); preset.PresetButton = button.gameObject; } - foreach (var mod in MiraPluginManager.Instance.RegisteredPlugins) - { - foreach (var button in mod.InternalPresets.Select(x => x.PresetButton)) - { - if (button != null) - { - button.SetActive(mod == GameSettingMenuPatches.SelectedMod); - } - } - } - - _arrange.Start(); - _arrange.ArrangeChilds(); + arrange.Start(); + arrange.ArrangeChilds(); } } @@ -116,8 +119,6 @@ internal static class GamePresetsTabPatches // ReSharper disable once InconsistentNaming public static void Postfix() { - Error("OnDisable called"); - if (_saveButton) { _saveButton.SetActive(false); @@ -197,7 +198,7 @@ internal static class GamePresetsTabPatches saveButton.OnClick.AddListener( (UnityAction)(() => { - if (GameSettingMenuPatches.SelectedModIdx == 0) + if (MenuState.Instance.CurrentModIdx == 0) { return; } @@ -205,7 +206,7 @@ internal static class GamePresetsTabPatches SavePresetPopup.CreatePopup( name => { - if (GameSettingMenuPatches.SelectedMod == null) + if (MenuState.Instance.CurrentModIdx == 0) { return; } @@ -218,19 +219,19 @@ internal static class GamePresetsTabPatches var presetFile = new ConfigFile( Path.Combine( PresetManager.PresetDirectory, - GameSettingMenuPatches.SelectedMod.PluginId, + MenuState.Instance.CurrentMod.PluginId, $"{name}.cfg"), false) { SaveOnConfigSet = false, }; - foreach (var option in GameSettingMenuPatches.SelectedMod.InternalOptions.Where(x => x.IncludeInPreset)) + foreach (var option in MenuState.Instance.CurrentMod.InternalOptions.Where(x => x.IncludeInPreset)) { option.SaveToPreset(presetFile); } - foreach (var role in GameSettingMenuPatches.SelectedMod.InternalRoles.Values.OfType().Where(x=>!x.Configuration.HideSettings)) + foreach (var role in MenuState.Instance.CurrentMod.InternalRoles.Values.OfType().Where(x=>!x.Configuration.HideSettings)) { role.SaveToPreset(presetFile); } @@ -285,7 +286,7 @@ internal static class GamePresetsTabPatches { var directory = Path.Combine( PresetManager.PresetDirectory, - GameSettingMenuPatches.SelectedMod?.PluginId ?? string.Empty); + MenuState.Instance.CurrentMod.PluginId); Directory.CreateDirectory(directory); Process.Start( new ProcessStartInfo @@ -301,31 +302,20 @@ internal static class GamePresetsTabPatches _refreshButton.SetActive(false); _folderButton.SetActive(false); _newDivider.SetActive(false); - - if (!_presetHolder) - { - _presetHolder = new GameObject("PresetHolder"); - _presetHolder.transform.SetParent(presetTab.transform, false); - _presetHolder.transform.localScale = new Vector3(0.9f, 0.9f, 1); - _presetHolder.transform.localPosition = new Vector3(-1.9f, 1.7f, 0); - _arrange = _presetHolder.AddComponent(); - _arrange.Alignment = GridArrange.StartAlign.Right; - _arrange.CellSize = new Vector2(2.15f, -.55f); - _arrange.MaxColumns = 2; - } } private static void Refresh() { - Error("Refreshing presets"); - if (GameSettingMenuPatches.SelectedMod == null) + Info("Refreshing presets"); + if (MenuState.Instance.CurrentModIdx == 0) { - Error("Selected mod is null, cannot refresh presets"); + Error("Current page is vanilla, cannot refresh presets"); return; } + lock (Lock) { - PresetManager.LoadPresets(GameSettingMenuPatches.SelectedMod); + PresetManager.LoadPresets(MenuState.Instance.CurrentMod); } GameSettingMenu.Instance.ChangeTab(0, false); // refresh the tab } diff --git a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs index d9cc5fb..c31b519 100644 --- a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using HarmonyLib; using MiraAPI.GameOptions; using MiraAPI.Modifiers; @@ -19,663 +18,38 @@ namespace MiraAPI.Patches.Options; [HarmonyPatch(typeof(GameSettingMenu))] internal static class GameSettingMenuPatches { - public static int SelectedModIdx { get; private set; } - - public static MiraPluginInfo? SelectedMod { get; private set; } - - private static TextMeshPro? _text; - - private static Vector3 _roleBtnOgPos; - private static Vector3 _smallRoleBtnOgPos; - private static Vector3 _modifierBtnOgPos; - private static Vector3 _customOneBtnOgPos; - private static Vector3 _customTwoBtnOgPos; - - private static GameOptionsMenu? _modifiersTab; - private static PassiveButton? _modifiersButton; - private static PassiveButton? _smallRolesButton; - private static GameOptionsMenu? _customOneTab; - private static PassiveButton? _customOneButton; - private static GameOptionsMenu? _customTwoTab; - private static PassiveButton? _customTwoButton; - private static GameObject? _nextModButton; - private static GameObject? _previousModButton; - - private static Dictionary OptionsPositions { get; } = []; - private static Dictionary ModifiersPositions { get; } = []; - private static Dictionary CustomOnePositions { get; } = []; - private static Dictionary CustomTwoPositions { get; } = []; - - private static void SaveScrollPositions(GameSettingMenu gameSettingMenu) - { - if (_modifiersTab) - { - ModifiersPositions[SelectedModIdx] = _modifiersTab!.scrollBar.Inner.localPosition; - } - else if (_customOneTab) - { - CustomOnePositions[SelectedModIdx] = _customOneTab!.scrollBar.Inner.localPosition; - } - else if (_customTwoTab) - { - CustomTwoPositions[SelectedModIdx] = _customTwoTab!.scrollBar.Inner.localPosition; - } - - RoleSettingMenuPatches.RolePositions[SelectedModIdx] = gameSettingMenu.RoleSettingsTab.scrollBar.Inner.localPosition; - OptionsPositions[SelectedModIdx] = gameSettingMenu.GameSettingsTab.scrollBar.Inner.localPosition; - } - - [HarmonyPrefix] - [HarmonyPatch(nameof(GameSettingMenu.ChangeTab))] - - public static void ChangeTabPrefix(GameSettingMenu __instance) - { - SaveScrollPositions(__instance); - } - - [HarmonyPostfix] - [HarmonyPatch(nameof(GameSettingMenu.ChangeTab))] - public static void ChangeTabPostfix(GameSettingMenu __instance, int tabNum, bool previewOnly) - { - if ((previewOnly && Controller.currentTouchType == Controller.TouchType.Joystick) || !previewOnly) - { - if (_modifiersTab) - { - _modifiersTab!.gameObject.SetActive(tabNum == 3); - } - - if (_customOneTab) - { - _customOneTab!.gameObject.SetActive(tabNum == 4); - } - - if (_customTwoTab) - { - _customTwoTab!.gameObject.SetActive(tabNum == 5); - } - - _modifiersButton?.SelectButton(tabNum == 3); - _smallRolesButton?.SelectButton(tabNum == 2); - _customOneButton?.SelectButton(tabNum == 4); - _customTwoButton?.SelectButton(tabNum == 5); - if (tabNum >= 3 && tabNum <= 5) - { - if (__instance.RoleSettingsButton.gameObject.active) - { - __instance.RoleSettingsButton.SelectButton(true); - } - else - { - switch (tabNum) - { - case 3: - _modifiersButton?.SelectButton(true); - __instance.MenuDescriptionText.text = SelectedMod != null ? SelectedMod.MiraPlugin.ModifierMenuDescription : "Configure modifiers and their settings here!"; - break; - case 4: - _customOneButton?.SelectButton(true); - __instance.MenuDescriptionText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuOneDescription : "Apply game settings for this mod!"; - break; - case 5: - _customTwoButton?.SelectButton(true); - __instance.MenuDescriptionText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuTwoDescription : "Apply game settings for this mod!"; - break; - } - } - } - } - - if (previewOnly) - { - return; - } - - _modifiersButton?.SelectButton(tabNum == 3); - _smallRolesButton?.SelectButton(tabNum == 2); - _customOneButton?.SelectButton(tabNum == 4); - _customTwoButton?.SelectButton(tabNum == 5); - - if (tabNum >= 3 && tabNum <= 5) - { - if (__instance.RoleSettingsButton.gameObject.active) - { - __instance.RoleSettingsButton.SelectButton(true); - } - else - { - switch (tabNum) - { - case 3: - _modifiersButton?.SelectButton(true); - break; - case 4: - _customOneButton?.SelectButton(true); - break; - case 5: - _customTwoButton?.SelectButton(true); - break; - } - } - } - } - /// - /// Prefix for the method. Sets up the custom options. + /// Prefix for the method. Sets up the custom options. /// /// The instance. [HarmonyPrefix] - [HarmonyPatch(nameof(GameSettingMenu.Start))] - public static void StartPrefix(GameSettingMenu __instance) + [HarmonyPatch(nameof(GameSettingMenu.OnEnable))] + public static void OnEnablePrefix(GameSettingMenu __instance) { - _roleBtnOgPos = __instance.RoleSettingsButton.transform.localPosition; - __instance.transform.FindChild("GameSettingsLabel").gameObject.SetActive(false); - - var helpThing = __instance.transform.FindChild("What Is This?"); - var tmpText = Object.Instantiate(helpThing.transform.FindChild("InfoText"), helpThing.parent).gameObject; - - tmpText.GetComponent().Destroy(); - tmpText.name = "SelectedMod"; - tmpText.transform.localPosition = new Vector3(-3.3382f, 1.5399f, -2); - - _text = tmpText.GetComponent(); - _text.fontSizeMax = 3.2f; - _text.overflowMode = TextOverflowModes.Overflow; - - _text.alignment = TextAlignmentOptions.Center; - - _nextModButton = Object.Instantiate(__instance.BackButton, __instance.BackButton.transform.parent).gameObject; - _nextModButton.transform.localPosition = new Vector3(-2.2663f, 1.5272f, -25f); - _nextModButton.name = "RightArrowButton"; - _nextModButton.transform.FindChild("Inactive").gameObject.GetComponent().sprite = - MiraAssets.NextButton.LoadAsset(); - _nextModButton.transform.FindChild("Active").gameObject.GetComponent().sprite = - MiraAssets.NextButtonActive.LoadAsset(); - _nextModButton.gameObject.GetComponent().DestroyImmediate(); - - var passiveButton = _nextModButton.gameObject.GetComponent(); - passiveButton.OnClick = new ButtonClickedEvent(); - passiveButton.OnClick.AddListener( - (UnityAction)(() => - { - SaveScrollPositions(__instance); - SelectedModIdx += 1; - if (SelectedModIdx > MiraPluginManager.Instance.RegisteredOptionablePlugins.Length) - { - SelectedModIdx = 0; - } - - UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab); - })); - - _previousModButton = Object.Instantiate(_nextModButton, __instance.BackButton.transform.parent).gameObject; - _previousModButton.transform.localPosition = new Vector3(-4.4209f, 1.5272f, -25f); - _previousModButton.name = "LeftArrowButton"; - _previousModButton.gameObject.GetComponent().Destroy(); - _previousModButton.transform.FindChild("Active").gameObject.GetComponent().flipX = - _previousModButton.transform.FindChild("Inactive").gameObject.GetComponent().flipX = true; - _previousModButton.gameObject.GetComponent().OnClick.AddListener( - (UnityAction)(() => - { - SaveScrollPositions(__instance); - SelectedModIdx -= 1; - if (SelectedModIdx < 0) - { - SelectedModIdx = MiraPluginManager.Instance.RegisteredOptionablePlugins.Length; - } - - UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab); - })); - - // clone game settings tab for modifiers - _modifiersTab = Object.Instantiate(__instance.GameSettingsTab, __instance.GameSettingsTab.transform.parent); - _modifiersTab.name = "MODIFIERS TAB"; - - // clone game settings tab for both custom tabs - _customOneTab = Object.Instantiate(__instance.GameSettingsTab, __instance.GameSettingsTab.transform.parent); - _customOneTab.name = "CUSTOM TAB 1"; - _customTwoTab = Object.Instantiate(__instance.GameSettingsTab, __instance.GameSettingsTab.transform.parent); - _customTwoTab.name = "CUSTOM TAB 2"; - - // create button for the first custom category - var customPos = new Vector3( - __instance.RoleSettingsButton.transform.localPosition.x, - __instance.RoleSettingsButton.transform.localPosition.y, - __instance.RoleSettingsButton.transform.localPosition.z); - _customOneButton = Object.Instantiate(__instance.RoleSettingsButton, __instance.RoleSettingsButton.transform.parent); - _customOneButton.buttonText.gameObject.GetComponent().Destroy(); - _customOneButton.OnClick = new ButtonClickedEvent(); - _customOneButton.OnClick.AddListener( - (UnityAction)(() => - { - __instance.ChangeTab(4, false); - })); - _customOneButton.OnMouseOver = new UnityEvent(); - _customOneButton.OnMouseOver.AddListener( - (UnityAction)(() => - { - __instance.ChangeTab(4, true); - })); - - _customOneButton.buttonText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuNameOne : "Custom Category 1"; - customPos.y -= 0.637f; - _customOneButton.transform.localPosition = customPos; - _customOneButton.name = "CustomOneButton"; - - _customOneBtnOgPos = _customOneButton.transform.localPosition; - - // create button for the first custom category - _customTwoButton = Object.Instantiate(_customOneButton, _customOneButton.transform.parent); - _customTwoButton.OnClick = new ButtonClickedEvent(); - _customTwoButton.OnClick.AddListener( - (UnityAction)(() => - { - __instance.ChangeTab(5, false); - })); - _customTwoButton.OnMouseOver = new UnityEvent(); - _customTwoButton.OnMouseOver.AddListener( - (UnityAction)(() => - { - __instance.ChangeTab(5, true); - })); - - _customTwoButton.buttonText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuNameTwo : "Custom Category 2"; - customPos.y -= 0.637f; - _customTwoButton.transform.localPosition = customPos; - _customTwoButton.name = "CustomTwoButton"; - - _customTwoBtnOgPos = _customTwoButton.transform.localPosition; - - // create button for modifiers - __instance.RoleSettingsButton.buttonText.gameObject.GetComponent().Destroy(); - __instance.RoleSettingsButton.gameObject.SetActive(false); - _smallRolesButton = Object.Instantiate( - __instance.RoleSettingsButton, - __instance.RoleSettingsButton.transform.parent); - var pos = new Vector3( - -3.65f, - _smallRolesButton.transform.localPosition.y, - _smallRolesButton.transform.localPosition.z); - _smallRolesButton.transform.localPosition = pos; - _smallRolesButton.OnClick.AddListener( - (UnityAction)(() => - { - __instance.ChangeTab(2, false); - })); - _smallRolesButton.OnMouseOver.AddListener( - (UnityAction)(() => - { - __instance.ChangeTab(2, true); - })); - - _smallRoleBtnOgPos = _smallRolesButton.transform.localPosition; - - var roleText = _smallRolesButton.buttonText; - roleText.text = "Roles"; - roleText.GetComponent().Destroy(); - roleText.alignment = TextAlignmentOptions.Center; - roleText.transform.parent.localPosition = new Vector3( - -.525f, - roleText.transform.parent.localPosition.y, - roleText.transform.parent.localPosition.z); - - foreach (var collider in _smallRolesButton.Colliders) + // Add Menu State object + if (!__instance.gameObject.GetComponent()) { - if (collider.TryCast() is { } col) - { - col.size = new Vector2(col.size.x / 2, col.size.y); - } - } - - foreach (var rend in _smallRolesButton.GetComponentsInChildren(true)) - { - rend.size = new Vector2(rend.size.x / 2, rend.size.y); - } - - _modifiersButton = Object.Instantiate(_smallRolesButton, _smallRolesButton.transform.parent); - _modifiersButton.OnClick = new ButtonClickedEvent(); - _modifiersButton.OnClick.AddListener( - (UnityAction)(() => - { - __instance.ChangeTab(3, false); - })); - _modifiersButton.OnMouseOver = new UnityEvent(); - _modifiersButton.OnMouseOver.AddListener( - (UnityAction)(() => - { - __instance.ChangeTab(3, true); - })); - - _modifiersButton.buttonText.text = "Modifiers"; - pos.x = -2.27f; - _modifiersButton.transform.localPosition = pos; - _modifiersButton.name = "ModifiersButton"; - - _modifierBtnOgPos = _modifiersButton.transform.localPosition; - - foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins) - { - PresetManager.LoadPresets(plugin); - } - - UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab); - } - - private static void ChangeRoleSettingButton(bool replace, GameSettingMenu menu) - { - menu.RoleSettingsButton.OnClick = new ButtonClickedEvent(); - menu.RoleSettingsButton.OnMouseOver = new UnityEvent(); - - if (replace) - { - menu.RoleSettingsButton.buttonText.text = "Modifiers Settings"; - menu.RoleSettingsButton.OnClick.AddListener( - (UnityAction)(() => - { - menu.ChangeTab(3, false); - })); - menu.RoleSettingsButton.OnMouseOver.AddListener( - (UnityAction)(() => - { - menu.ChangeTab(3, true); - })); + __instance.gameObject.AddComponent(); + Info("MenuState component added to GameSettingMenu."); } else { - menu.RoleSettingsButton.buttonText.text = "Roles Settings"; - menu.RoleSettingsButton.OnClick.AddListener( - (UnityAction)(() => - { - menu.ChangeTab(2, false); - })); - menu.RoleSettingsButton.OnMouseOver.AddListener( - (UnityAction)(() => - { - menu.ChangeTab(2, true); - })); + Info("MenuState component already added to GameSettingMenu."); } } - private static void UpdateText(GameSettingMenu menu, GameOptionsMenu settings, RolesSettingsMenu roles) + [HarmonyPrefix] + [HarmonyPatch(nameof(GameSettingMenu.ChangeTab))] + public static bool ChangeTabPrefix(GameSettingMenu __instance, int tabNum, bool previewOnly) { - if (_text is not null && SelectedModIdx == 0) + if (MenuState.Instance) { - _text.text = $"(Page 0/{MiraPluginManager.Instance.RegisteredOptionablePlugins.Length})\nMain"; - _text.fontSizeMax = 3.2f; - SelectedMod = null; - } - else if (_text is not null) - { - _text.fontSizeMax = 2.3f; - SelectedMod = MiraPluginManager.Instance.RegisteredOptionablePlugins[SelectedModIdx - 1]; - - var name = SelectedMod.MiraPlugin.OptionsTitleText; - _text.text = $"(Page {SelectedModIdx}/{MiraPluginManager.Instance.RegisteredOptionablePlugins.Length})\n" + name[..Math.Min(name.Length, 25)]; - } - - bool replaceWithModifiers = true; - _smallRolesButton!.transform.localPosition = _smallRoleBtnOgPos; - _modifiersButton!.transform.localPosition = _modifierBtnOgPos; - _customOneButton!.transform.localPosition = _customOneBtnOgPos; - _customTwoButton!.transform.localPosition = _customTwoBtnOgPos; - menu.RoleSettingsButton.transform.localPosition = _roleBtnOgPos; - - CleanupTab(settings, roles); - - if (SelectedModIdx != 0) - { - _customOneButton.buttonText.text = SelectedMod!.MiraPlugin.CustomOptionMenuNameOne; - _customTwoButton.buttonText.text = SelectedMod!.MiraPlugin.CustomOptionMenuNameTwo; - var modHasRoles = SelectedMod!.InternalRoles.Count != 0; - var modHasCustomOne = SelectedMod.InternalOptionGroups.Exists( - x => x.ParentMenu == MenuCategory.CustomOne); - var modHasCustomTwo = SelectedMod.InternalOptionGroups.Exists( - x => x.ParentMenu == MenuCategory.CustomTwo); - var modHasModifiers = SelectedMod.InternalOptionGroups.Exists( - x => x.ParentMenu == MenuCategory.Modifiers || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true); - var modHasOptions = - SelectedMod.InternalOptionGroups.Exists(x => x.OptionableType == null && - x.ParentMenu == MenuCategory.Game); - - _modifiersButton.gameObject.SetActive(true); - _smallRolesButton.gameObject.SetActive(true); - _customOneButton.gameObject.SetActive(true); - _customTwoButton.gameObject.SetActive(true); - menu.GameSettingsButton.gameObject.SetActive(true); - menu.RoleSettingsButton.gameObject.SetActive(false); - var defaultButton = menu.RoleSettingsButton; - - // If there are no registered custom roles in the selected mod, hide the button. - if (!modHasRoles) - { - _smallRolesButton.gameObject.SetActive(false); - - if (roles.gameObject.active) - { - menu.ChangeTab(0, false); - } - - // If the mod has modifiers, we can disable the smaller modifier button and enable the bigger one. - if (modHasModifiers) - { - _modifiersButton.gameObject.SetActive(false); - menu.RoleSettingsButton.gameObject.SetActive(true); - defaultButton = _modifiersButton; - - if (_modifiersTab!.gameObject.active) - { - menu.RoleSettingsButton.SelectButton(true); - } - } - } - - // If there are no modifiers in the selected mod, hide the modifiers button. - if (!modHasModifiers) - { - replaceWithModifiers = false; - _modifiersButton.gameObject.SetActive(false); - - if (_modifiersTab!.gameObject.active) - { - menu.ChangeTab(0, false); - } - - // If the mod has roles, we can enable the bigger role button and disable the small one. - if (modHasRoles) - { - defaultButton = menu.RoleSettingsButton; - menu.RoleSettingsButton.gameObject.SetActive(true); - _smallRolesButton.gameObject.SetActive(false); - } - } - - // If there are no custom game options registered (that aren't modifier options), hide game settings button. - if (!modHasOptions) - { - menu.GameSettingsButton.gameObject.SetActive(false); - - if (settings.gameObject.active) - { - menu.ChangeTab(0, false); - } - defaultButton = menu.RoleSettingsButton; - - // If the mod has roles and modifiers, we can move their buttons to the game settings button position, since nothing is there. - if (menu.RoleSettingsButton.gameObject.active) - { - menu.RoleSettingsButton.transform.localPosition = menu.GameSettingsButton.transform.localPosition; - } - else if (_modifiersButton.gameObject.active && _smallRolesButton.gameObject.active) - { - _modifiersButton.transform.localPosition = new Vector3( - _modifiersButton.transform.localPosition.x, - menu.GameSettingsButton.transform.localPosition.y, - _modifiersButton.transform.localPosition.z); - - _smallRolesButton.transform.localPosition = new Vector3( - _smallRolesButton.transform.localPosition.x, - menu.GameSettingsButton.transform.localPosition.y, - _smallRolesButton.transform.localPosition.z); - } - } - - // If the mod has no primary custom tab, we can go ahead and hide the button. - if (!modHasCustomOne) - { - _customOneButton.gameObject.SetActive(false); - if (_customOneTab!.gameObject.active) - { - defaultButton.SelectButton(true); - } - } - // If the mod has no secondary custom tab, we can go ahead and hide the button. - if (!modHasCustomTwo) - { - _customTwoButton.gameObject.SetActive(false); - if (_customTwoTab!.gameObject.active) - { - defaultButton.SelectButton(true); - } - } + MenuState.Instance.ChangeTabPatch(__instance, tabNum, previewOnly); } else { - _modifiersButton.gameObject.SetActive(false); - _smallRolesButton.gameObject.SetActive(false); - _customOneButton.gameObject.SetActive(false); - _customTwoButton.gameObject.SetActive(false); - menu.RoleSettingsButton.gameObject.SetActive(true); - menu.GameSettingsButton.gameObject.SetActive(true); - replaceWithModifiers = false; - - if (_modifiersTab!.gameObject.active || _customOneTab!.gameObject.active || _customTwoTab!.gameObject.active) - { - menu.ChangeTab(0, false); - } - } - - if (menu.RoleSettingsButton.gameObject.active) - { - ChangeRoleSettingButton(replaceWithModifiers, menu); - } - - if (menu.PresetsTab.gameObject.active) - { - menu.ChangeTab(0, false); - } - } - - private static void ClearOptions(Il2CppSystem.Collections.Generic.List options) - { - foreach (var child in options) - { - if (child.TryCast() || !child.gameObject) - { - continue; - } - - child.gameObject.DestroyImmediate(); - } - - options.Clear(); - } - - private static void CleanupTab(GameOptionsMenu settings, RolesSettingsMenu roles) - { - if (roles.roleChances != null) - { - CleanupRoleSettings(roles); - } - - if (settings.Children != null) - { - CleanupSettings(settings); - } - - if (_modifiersTab?.Children?.Count > 0) - { - CleanupSettings(_modifiersTab, MenuCategory.Modifiers); - } - - if (_customOneTab?.Children?.Count > 0) - { - CleanupSettings(_customOneTab, MenuCategory.CustomOne); - } - - if (_customTwoTab?.Children?.Count > 0) - { - CleanupSettings(_customTwoTab, MenuCategory.CustomTwo); - } - - Utilities.Extensions.ClearGarbageCollector(); - - void CleanupRoleSettings(RolesSettingsMenu rolesMenu) - { - if (rolesMenu.advancedSettingChildren != null) - { - ClearOptions(rolesMenu.advancedSettingChildren); - rolesMenu.advancedSettingChildren = null; - } - - rolesMenu.RoleChancesSettings - .transform - .GetComponentsInChildren() - .ToList() - .ForEach(header => header.gameObject.DestroyImmediate()); - - foreach (var role in rolesMenu.roleChances) - { - role.gameObject.DestroyImmediate(); - } - - rolesMenu.roleChances?.Clear(); - - rolesMenu.AdvancedRolesSettings.gameObject.SetActive(false); - rolesMenu.RoleChancesSettings.gameObject.SetActive(true); - rolesMenu.SetQuotaTab(); - if (RoleSettingMenuPatches.RolePositions.TryGetValue(SelectedModIdx, out var pos)) - { - rolesMenu.scrollBar.Inner.localPosition = pos; - rolesMenu.scrollBar.UpdateScrollBars(); - } - else - { - rolesMenu.scrollBar.ScrollToTop(); - } - } - - void CleanupSettings(GameOptionsMenu gameOptMenu, MenuCategory menuCategory = MenuCategory.Roles) - { - ClearOptions(gameOptMenu.Children); - gameOptMenu.Children = null; - - gameOptMenu.settingsContainer - .GetComponentsInChildren() - .ToList() - .ForEach(header => header.gameObject.DestroyImmediate()); - - gameOptMenu.Initialize(); - var positions = OptionsPositions; - switch (menuCategory) - { - case MenuCategory.Modifiers: - positions = ModifiersPositions; - break; - case MenuCategory.CustomOne: - positions = CustomOnePositions; - break; - case MenuCategory.CustomTwo: - positions = CustomTwoPositions; - break; - } - if (positions.TryGetValue(SelectedModIdx, out var pos)) - { - gameOptMenu.scrollBar.Inner.localPosition = pos; - gameOptMenu.scrollBar.UpdateScrollBars(); - } - else - { - gameOptMenu.scrollBar.ScrollToTop(); - } + Error("MenuState instance is null. Cannot change tab."); } + return false; } } diff --git a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs index dd80dc4..51e2e92 100644 --- a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs +++ b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs @@ -25,7 +25,7 @@ public static class LobbyViewPanePatches private static MiraPluginInfo? SelectedMod => SelectedModIdx == 0 ? null - : MiraPluginManager.Instance.RegisteredOptionablePlugins[SelectedModIdx - 1]; + : MiraPluginManager.Instance.RegisteredPluginsWithOptions[SelectedModIdx - 1]; private static PassiveButton? ModifiersTabButton { get; set; } @@ -75,7 +75,7 @@ public static class LobbyViewPanePatches (UnityAction)(() => { SelectedModIdx += 1; - if (SelectedModIdx > MiraPluginManager.Instance.RegisteredOptionablePlugins.Length) + if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPluginsWithOptions.Length) { SelectedModIdx = 0; } @@ -99,7 +99,7 @@ public static class LobbyViewPanePatches SelectedModIdx -= 1; if (SelectedModIdx < 0) { - SelectedModIdx = MiraPluginManager.Instance.RegisteredOptionablePlugins.Length; + SelectedModIdx = MiraPluginManager.Instance.RegisteredPluginsWithOptions.Length; } Refresh(__instance); diff --git a/MiraAPI/Patches/Options/MenuState.cs b/MiraAPI/Patches/Options/MenuState.cs new file mode 100644 index 0000000..39b3857 --- /dev/null +++ b/MiraAPI/Patches/Options/MenuState.cs @@ -0,0 +1,682 @@ +using System; +using System.Collections.Generic; +using Il2CppInterop.Runtime.Attributes; +using MiraAPI.GameOptions; +using MiraAPI.Modifiers; +using MiraAPI.PluginLoading; +using MiraAPI.Presets; +using MiraAPI.Utilities.Assets; +using Reactor.Utilities.Attributes; +using Reactor.Utilities.Extensions; +using TMPro; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.UI; + +namespace MiraAPI.Patches.Options; + +[RegisterInIl2Cpp] +public class MenuState(IntPtr cppPtr) : MonoBehaviour(cppPtr) +{ + public static MenuState Instance { get; private set; } = null!; + + public static int ModCount => MiraPluginManager.Instance.RegisteredPluginsWithOptions.Length; + + private GameSettingMenu Gsm { get; set; } + + public MenuCategory CurrentMenu { get; private set; } + + public int CurrentModIdx { get; private set; } + + public GameObject CurrentContainer => Containers[CurrentMenu][CurrentModIdx]; + + [HideFromIl2Cpp] + public MiraPluginInfo CurrentMod => MiraPluginManager.Instance.RegisteredPluginsWithOptions[CurrentModIdx - 1]; + + // UI elements + private TextMeshPro _text = null!; + + private enum MenuButton + { + Roles, + Modifiers, + CustomOne, + CustomTwo, + } + + private readonly Dictionary _smallButtons = []; + private readonly Dictionary _largeButtons = []; + + [HideFromIl2Cpp] + public Dictionary> Containers { get; } = new() + { + { MenuCategory.Preset, [] }, + { MenuCategory.Game, [] }, + { MenuCategory.Roles, [] }, + { MenuCategory.Modifiers, [] }, + { MenuCategory.CustomOne, [] }, + { MenuCategory.CustomTwo, [] }, + }; + + internal Dictionary FinishedRoleMenus = []; + + public void Awake() + { + if (Instance) + { + Error($"MenuState already exists! Destroying duplicate {name}."); + Destroy(this); + return; + } + + Instance = this; + Gsm = GetComponent(); + + if (!Gsm) + { + Error($"MenuState could not find GameSettingMenu component! Destroying {name}."); + Destroy(this); + return; + } + + // Reload presets + foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins) + { + PresetManager.LoadPresets(plugin); + } + + // Initialize and setup UI + InitializeContainers(); + InitializeUi(); + UpdateUi(); + } + + private void InitializeUi() + { + // Disable game settings label + Gsm.transform.FindChild("GameSettingsLabel").gameObject.SetActive(false); + + // Create mod name text + var helpThing = Gsm.transform.FindChild("What Is This?"); + var tmpText = Instantiate(helpThing.transform.FindChild("InfoText"), helpThing.parent).gameObject; + + Destroy(tmpText.GetComponent()); + tmpText.name = "SelectedMod"; + tmpText.transform.localPosition = new Vector3(-3.3382f, 1.5399f, -2); + + _text = tmpText.GetComponent(); + _text.fontSizeMax = 3.2f; + _text.overflowMode = TextOverflowModes.Overflow; + _text.alignment = TextAlignmentOptions.Center; + _text.rectTransform.sizeDelta = new Vector2(1.5f, 0.8f); + + // Create next and previous buttons + var nextButton = Instantiate(Gsm.BackButton, Gsm.BackButton.transform.parent).gameObject; + nextButton.transform.localPosition = new Vector3(-2.2663f, 1.5272f, -25f); + nextButton.name = "RightArrowButton"; + nextButton.transform.FindChild("Inactive").gameObject.GetComponent().sprite = + MiraAssets.NextButton.LoadAsset(); + nextButton.transform.FindChild("Active").gameObject.GetComponent().sprite = + MiraAssets.NextButtonActive.LoadAsset(); + nextButton.gameObject.GetComponent().DestroyImmediate(); + + var nextPassiveButton = nextButton.gameObject.GetComponent(); + nextPassiveButton.OnClick = new Button.ButtonClickedEvent(); + nextPassiveButton.OnClick.AddListener((UnityAction)NextMod); + + var previousButton = Instantiate(nextButton, Gsm.BackButton.transform.parent).gameObject; + previousButton.transform.localPosition = new Vector3(-4.4209f, 1.5272f, -25f); + previousButton.name = "LeftArrowButton"; + previousButton.gameObject.GetComponent().Destroy(); + previousButton.transform.FindChild("Active").gameObject.GetComponent().flipX = + previousButton.transform.FindChild("Inactive").gameObject.GetComponent().flipX = true; + + var prevPassiveButton = previousButton.gameObject.GetComponent(); + prevPassiveButton.OnClick = new Button.ButtonClickedEvent(); + prevPassiveButton.OnClick.AddListener((UnityAction)PreviousMod); + + // Create menu buttons + _largeButtons[(int)MenuButton.Roles] = Gsm.RoleSettingsButton; + + // modifiers button + var mBtn = Instantiate(Gsm.RoleSettingsButton, Gsm.RoleSettingsButton.transform.parent); + _largeButtons[MenuButton.Modifiers] = mBtn; + mBtn.name = "ModifiersButton"; + mBtn.buttonText.gameObject.GetComponent().Destroy(); + mBtn.buttonText.text = "Modifiers"; + mBtn.OnClick = new Button.ButtonClickedEvent(); + mBtn.OnClick.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.Modifiers, false); + })); + mBtn.OnMouseOver = new UnityEvent(); + mBtn.OnMouseOver.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.Modifiers, true); + })); + + // first custom menu + var c1Btn = Instantiate(mBtn, mBtn.transform.parent); + _largeButtons[MenuButton.CustomOne] = c1Btn; + c1Btn.name = "CustomOneButton"; + c1Btn.buttonText.gameObject.GetComponent().Destroy(); + c1Btn.OnClick = new Button.ButtonClickedEvent(); + c1Btn.OnClick.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.CustomOne, false); + })); + c1Btn.OnMouseOver = new UnityEvent(); + c1Btn.OnMouseOver.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.CustomOne, true); + })); + + // second custom menu + var c2Btn = Instantiate(c1Btn, c1Btn.transform.parent); + _largeButtons[MenuButton.CustomTwo] = c2Btn; + c2Btn.name = "CustomTwoButton"; + c2Btn.OnClick = new Button.ButtonClickedEvent(); + c2Btn.OnClick.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.CustomTwo, false); + })); + c2Btn.OnMouseOver = new UnityEvent(); + c2Btn.OnMouseOver.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.CustomTwo, true); + })); + + // small roles button + var smRolesBtn = Instantiate( + Gsm.RoleSettingsButton, + Gsm.RoleSettingsButton.transform.parent); + _smallButtons[MenuButton.Roles] = smRolesBtn; + smRolesBtn.name = "SmallRolesButton"; + smRolesBtn.OnClick = new Button.ButtonClickedEvent(); + smRolesBtn.OnClick.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.Roles, false); + })); + smRolesBtn.OnMouseOver = new UnityEvent(); + smRolesBtn.OnMouseOver.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.Roles, true); + })); + + var roleText = smRolesBtn.buttonText; + roleText.text = "Roles"; + roleText.GetComponent().Destroy(); + roleText.alignment = TextAlignmentOptions.Center; + roleText.transform.parent.localPosition = new Vector3( + -.525f, + roleText.transform.parent.localPosition.y, + roleText.transform.parent.localPosition.z); + + foreach (var collider in smRolesBtn.Colliders) + { + if (collider.TryCast() is { } col) + { + col.size = new Vector2(col.size.x / 2, col.size.y); + } + } + + foreach (var rend in smRolesBtn.GetComponentsInChildren(true)) + { + rend.size = new Vector2(rend.size.x / 2, rend.size.y); + } + + // small modifiers button + var smModBtn = Instantiate(smRolesBtn, smRolesBtn.transform.parent); + _smallButtons[MenuButton.Modifiers] = smModBtn; + smModBtn.name = "SmallModifiersButton"; + smModBtn.buttonText.text = "Modifiers"; + smModBtn.OnClick = new Button.ButtonClickedEvent(); + smModBtn.OnClick.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.Modifiers, false); + })); + smModBtn.OnMouseOver = new UnityEvent(); + smModBtn.OnMouseOver.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.Modifiers, true); + })); + + // small c1 button + var smC1Btn = Instantiate(smModBtn, smModBtn.transform.parent); + _smallButtons[MenuButton.CustomOne] = smC1Btn; + smC1Btn.name = "SmallCustomOneButton"; + smC1Btn.buttonText.text = "Custom 1"; + smC1Btn.OnClick = new Button.ButtonClickedEvent(); + smC1Btn.OnClick.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.CustomOne, false); + })); + smC1Btn.OnMouseOver = new UnityEvent(); + smC1Btn.OnMouseOver.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.CustomOne, true); + })); + + // small c2 button + var smC2Btn = Instantiate(smModBtn, smModBtn.transform.parent); + _smallButtons[MenuButton.CustomTwo] = smC2Btn; + smC2Btn.name = "SmallCustomTwoButton"; + smC2Btn.buttonText.text = "Custom 2"; + smC2Btn.OnClick = new Button.ButtonClickedEvent(); + smC2Btn.OnClick.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.CustomTwo, false); + })); + smC2Btn.OnMouseOver = new UnityEvent(); + smC2Btn.OnMouseOver.AddListener( + (UnityAction)(() => + { + Gsm.ChangeTab((int)MenuCategory.CustomTwo, true); + })); + } + + private void InitializeContainers() + { + // Set vanilla containers + Containers[MenuCategory.Game][0] = Gsm.GameSettingsTab.settingsContainer.gameObject; + Containers[MenuCategory.Roles][0] = Gsm.RoleSettingsTab.RoleChancesSettings; + + for (var idx = 0; idx < ModCount; idx++) + { + var mod = MiraPluginManager.Instance.RegisteredPluginsWithOptions[idx]; + + // Create preset container for each mod + var presetContainer = new GameObject($"PresetHolder_{mod.PluginInfo.Metadata.Name}"); + presetContainer.transform.SetParent(Gsm.PresetsTab.transform, false); + presetContainer.transform.localScale = new Vector3(0.9f, 0.9f, 1); + presetContainer.transform.localPosition = new Vector3(-1.9f, 1.7f, 0); + presetContainer.gameObject.SetActive(false); + + var arrange = presetContainer.AddComponent(); + arrange.Alignment = GridArrange.StartAlign.Right; + arrange.CellSize = new Vector2(2.15f, -.55f); + arrange.MaxColumns = 2; + + Containers[MenuCategory.Preset][idx + 1] = presetContainer; + + // Create game options container + var gameContainer = Containers[MenuCategory.Game][idx + 1] = Instantiate( + Gsm.GameSettingsTab.settingsContainer.gameObject, + Gsm.GameSettingsTab.settingsContainer.gameObject.transform.parent); + gameContainer.name = $"GameSettings_{mod.PluginInfo.Metadata.Name}"; + gameContainer.transform.DestroyChildren(); + gameContainer.SetActive(false); + + // Roles container + var rolesContainer = Containers[MenuCategory.Roles][idx + 1] = Instantiate( + Gsm.RoleSettingsTab.RoleChancesSettings, + Gsm.RoleSettingsTab.RoleChancesSettings.transform.parent); + rolesContainer.name = $"RoleSettings_{mod.PluginInfo.Metadata.Name}"; + rolesContainer.transform.DestroyChildren(); + rolesContainer.SetActive(false); + + // Modifiers + Custom containers + for (var i = MenuCategory.Modifiers; i <= MenuCategory.CustomTwo; i++) + { + var cont = Containers[i][idx + 1] = Instantiate( + Gsm.GameSettingsTab.settingsContainer.gameObject, + Gsm.GameSettingsTab.settingsContainer.gameObject.transform.parent); + cont.name = $"{i}_{mod.PluginInfo.Metadata.Name}"; + cont.transform.DestroyChildren(); + cont.SetActive(false); + } + } + } + + private void UpdateContainers() + { + foreach (var (_, dict) in Containers) + { + foreach (var (_, container) in dict) + { + container.SetActive(false); + } + } + + Info($"Enabled {CurrentMenu} for {CurrentModIdx}"); + + // Preset containers + if (CurrentModIdx != 0) + { + Containers[MenuCategory.Preset][CurrentModIdx].SetActive(true); + } + + // Game / Modifier / Custom Settings tab + // GST.settingsContainer is same as GST.scrollBar.Inner + if (CurrentMenu != MenuCategory.Preset && CurrentMenu != MenuCategory.Roles) + { + Gsm.GameSettingsTab.settingsContainer = Gsm.GameSettingsTab.scrollBar.Inner = CurrentContainer.transform; + CurrentContainer.SetActive(true); + } + + // Role Settings Tab + Gsm.RoleSettingsTab.RoleChancesSettings = Containers[MenuCategory.Roles][CurrentModIdx]; + Gsm.RoleSettingsTab.scrollBar.Inner = Containers[MenuCategory.Roles][CurrentModIdx].transform; + Containers[MenuCategory.Roles][CurrentModIdx].SetActive(true); + } + + private void UpdateUi() + { + // Update mod text + if (CurrentModIdx == 0) + { + _text.text = $"(Page 0/{ModCount})\nMain"; + } + else + { + var modName = CurrentMod.PluginInfo.Metadata.Name; + _text.text = $"(Page {CurrentModIdx}/{ModCount})\n" + + modName[..Math.Min(modName.Length, 25)]; + } + + UpdateContainers(); + + // Update buttons + foreach (var (_, btn) in _smallButtons) + { + btn.gameObject.SetActive(false); + } + + foreach (var (_, btn) in _largeButtons) + { + btn.gameObject.SetActive(false); + } + + if (CurrentModIdx == 0) + { + Gsm.GameSettingsButton.gameObject.SetActive(true); + Gsm.RoleSettingsButton.gameObject.SetActive(true); + } + else + { + var hasOptions = + CurrentMod.InternalOptionGroups.Exists(g => + g.OptionableType == null && g.ParentMenu == MenuCategory.Game); + var hasRoles = CurrentMod.InternalRoles.Count > 0; + var hasModifiers = CurrentMod.InternalOptionGroups.Exists(g => + g.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true + || g.ParentMenu == MenuCategory.Modifiers); + var hasC1 = CurrentMod.InternalOptionGroups.Exists(g => g.ParentMenu == MenuCategory.CustomOne); + var hasC2 = CurrentMod.InternalOptionGroups.Exists(g => g.ParentMenu == MenuCategory.CustomTwo); + const float leftPos = -3.65f; + const float rightPos = -2.27f; + + var smallRolesModifiers = hasRoles && hasModifiers && (hasC1 || hasC2); + var smallC1C2 = hasC1 && hasC2; + + Info("hasOptions: " + hasOptions); + Info("hasRoles: " + hasRoles); + Info("hasModifiers: " + hasModifiers); + Info("hasC1: " + hasC1); + Info("hasC2: " + hasC2); + Info("smallC1C2: " + smallC1C2); + Info("smallRolesModifiers: " + smallRolesModifiers); + + var position = Gsm.GameSettingsButton.transform.localPosition; + + // If this mod has game options, enable the game settings button + if (hasOptions) + { + Gsm.GameSettingsButton.gameObject.SetActive(true); + position.y -= 0.637f; + } + else + { + Gsm.GameSettingsButton.gameObject.SetActive(false); + } + + // If we should use small roles/modifiers buttons, enable them and position them accordingly + if (smallRolesModifiers) + { + _smallButtons[MenuButton.Roles].gameObject.SetActive(true); + _smallButtons[MenuButton.Roles].transform.localPosition = new Vector3(leftPos, position.y, position.z); + _smallButtons[MenuButton.Modifiers].gameObject.SetActive(true); + _smallButtons[MenuButton.Modifiers].transform.localPosition = + new Vector3(rightPos, position.y, position.z); + position.y -= 0.637f; + } + + // If we should use large roles button, enable and position, otherwise disable + if (hasRoles && !smallRolesModifiers) + { + _largeButtons[MenuButton.Roles].gameObject.SetActive(true); + _largeButtons[MenuButton.Roles].transform.localPosition = position; + position.y -= 0.637f; + } + else + { + _largeButtons[MenuButton.Roles].gameObject.SetActive(false); + } + + // If we should use large modifiers button, enable and position, otherwise disable + if (hasModifiers && !smallRolesModifiers) + { + _largeButtons[MenuButton.Modifiers].gameObject.SetActive(true); + _largeButtons[MenuButton.Modifiers].transform.localPosition = position; + position.y -= 0.637f; + } + else + { + _largeButtons[MenuButton.Modifiers].gameObject.SetActive(false); + } + + // if we should use small custom buttons, enable and position accordingly + if (smallC1C2) + { + _smallButtons[MenuButton.CustomOne].gameObject.SetActive(true); + _smallButtons[MenuButton.CustomOne].transform.localPosition = + new Vector3(leftPos, position.y, position.z); + _smallButtons[MenuButton.CustomOne].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameOne; + + _smallButtons[MenuButton.CustomTwo].gameObject.SetActive(true); + _smallButtons[MenuButton.CustomTwo].transform.localPosition = + new Vector3(rightPos, position.y, position.z); + _smallButtons[MenuButton.CustomTwo].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameTwo; + position.y -= 0.637f; + } + + // C1 only + if (hasC1 && !smallC1C2) + { + _largeButtons[MenuButton.CustomOne].gameObject.SetActive(true); + _largeButtons[MenuButton.CustomOne].transform.localPosition = position; + _largeButtons[MenuButton.CustomOne].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameOne; + position.y -= 0.637f; + } + else + { + _largeButtons[MenuButton.CustomOne].gameObject.SetActive(false); + } + + // C2 only + if (hasC2 && !smallC1C2) + { + _largeButtons[MenuButton.CustomTwo].gameObject.SetActive(true); + _largeButtons[MenuButton.CustomTwo].transform.localPosition = position; + _largeButtons[MenuButton.CustomTwo].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameTwo; + position.y -= 0.637f; + } + else + { + _largeButtons[MenuButton.CustomTwo].gameObject.SetActive(false); + } + } + } + + internal void ChangeTabPatch(GameSettingMenu menu, int tabNum, bool previewOnly) + { + Info($"Changed tab to {(MenuCategory)tabNum} (previewOnly: {previewOnly}) for mod {CurrentModIdx}"); + if ((previewOnly && Controller.currentTouchType == Controller.TouchType.Joystick) || !previewOnly) + { + CurrentMenu = (MenuCategory)tabNum; + UpdateContainers(); + + menu.PresetsTab.gameObject.SetActive(false); + menu.GameSettingsTab.gameObject.SetActive(false); + menu.RoleSettingsTab.gameObject.SetActive(false); + + menu.GamePresetsButton.SelectButton(false); + menu.GameSettingsButton.SelectButton(false); + menu.RoleSettingsButton.SelectButton(false); + + foreach (var (_, btn) in _largeButtons) + { + btn.SelectButton(false); + } + + foreach (var (_, btn) in _smallButtons) + { + btn.SelectButton(false); + } + + switch (CurrentMenu) + { + case MenuCategory.Preset: + menu.PresetsTab.gameObject.SetActive(true); + menu.MenuDescriptionText.text = TranslationController.Instance.GetString(StringNames.GamePresetsDescription); + break; + case MenuCategory.Game: + menu.GameSettingsTab.gameObject.SetActive(true); + menu.MenuDescriptionText.text = TranslationController.Instance.GetString(StringNames.GameSettingsDescription); + break; + case MenuCategory.Roles: + menu.RoleSettingsTab.gameObject.SetActive(true); + menu.RoleSettingsTab.OpenMenu(false); + menu.MenuDescriptionText.text = TranslationController.Instance.GetString(StringNames.RoleSettingsDescription); + break; + + case MenuCategory.Modifiers: + menu.GameSettingsTab.gameObject.SetActive(true); + menu.MenuDescriptionText.text = CurrentMod.MiraPlugin.ModifierMenuDescription; + break; + case MenuCategory.CustomOne: + menu.GameSettingsTab.gameObject.SetActive(true); + menu.MenuDescriptionText.text = CurrentMod.MiraPlugin.CustomOptionMenuOneDescription; + break; + case MenuCategory.CustomTwo: + menu.GameSettingsTab.gameObject.SetActive(true); + menu.MenuDescriptionText.text = CurrentMod.MiraPlugin.CustomOptionMenuTwoDescription; + break; + } + } + + if (previewOnly) + { + menu.ToggleLeftSideDarkener(false); + menu.ToggleRightSideDarkener(true); + return; + } + + menu.ToggleLeftSideDarkener(true); + menu.ToggleRightSideDarkener(false); + + CurrentMenu = (MenuCategory)tabNum; + UpdateContainers(); + switch (CurrentMenu) + { + case MenuCategory.Preset: + menu.PresetsTab.OpenMenu(); + menu.GamePresetsButton.SelectButton(true); + break; + case MenuCategory.Roles: + menu.RoleSettingsTab.OpenMenu(); + SelectButton(MenuButton.Roles, MenuCategory.Roles); + break; + case MenuCategory.Game: + case MenuCategory.Modifiers: + case MenuCategory.CustomOne: + case MenuCategory.CustomTwo: + menu.GameSettingsButton.SelectButton(CurrentMenu == MenuCategory.Game); + SelectButton(MenuButton.Modifiers, MenuCategory.Modifiers); + SelectButton(MenuButton.CustomOne, MenuCategory.CustomOne); + SelectButton(MenuButton.CustomTwo, MenuCategory.CustomTwo); + break; + } + } + + private void SelectButton(MenuButton button, MenuCategory category) + { + if (_largeButtons[button].gameObject.activeInHierarchy) + { + _largeButtons[button].SelectButton(CurrentMenu == category); + } + + if (_smallButtons[button].gameObject.activeInHierarchy) + { + _smallButtons[button].SelectButton(CurrentMenu == category); + } + } + + private MenuCategory GetNewPageIfNeeded() + { + if (CurrentModIdx == 0) + { + if (CurrentMenu >= MenuCategory.Modifiers) + { + return MenuCategory.Game; + } + } + else + { + var hasOptions = + CurrentMod.InternalOptionGroups.Exists(g => + g.OptionableType == null && g.ParentMenu == MenuCategory.Game); + var hasRoles = CurrentMod.InternalRoles.Count > 0; + var hasModifiers = CurrentMod.InternalOptionGroups.Exists(g => + g.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true + || g.ParentMenu == MenuCategory.Modifiers); + var hasC1 = CurrentMod.InternalOptionGroups.Exists(g => g.ParentMenu == MenuCategory.CustomOne); + var hasC2 = CurrentMod.InternalOptionGroups.Exists(g => g.ParentMenu == MenuCategory.CustomTwo); + + switch (CurrentMenu) + { + case MenuCategory.Game when !hasOptions: + case MenuCategory.Roles when !hasRoles: + case MenuCategory.Modifiers when !hasModifiers: + case MenuCategory.CustomOne when !hasC1: + case MenuCategory.CustomTwo when !hasC2: + return MenuCategory.Preset; + } + } + + return CurrentMenu; + } + + public void NextMod() + { + CurrentModIdx++; + // The existence of vanilla settings offsets the off-by-one errors. + if (CurrentModIdx > ModCount) CurrentModIdx = 0; + CurrentMenu = GetNewPageIfNeeded(); + UpdateUi(); + ChangeTabPatch(Gsm, (int)CurrentMenu, false); + } + + public void PreviousMod() + { + CurrentModIdx--; + // The existence of vanilla settings offsets the off-by-one errors. + if (CurrentModIdx < 0) CurrentModIdx = ModCount; + CurrentMenu = GetNewPageIfNeeded(); + UpdateUi(); + ChangeTabPatch(Gsm, (int)CurrentMenu, false); + } +} diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index cb1283b..723735c 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -24,11 +24,7 @@ namespace MiraAPI.Patches.Options; [HarmonyPatch(typeof(RolesSettingsMenu))] public static class RoleSettingMenuPatches { - public static Dictionary RolePositions { get; set; } = []; - private static Dictionary RoleGroupHidden { get; set; } = []; - private static List Headers { get; set; } = []; - private static List RoleOptionSettings { get; set; } = []; private static float ScrollerNum { get; set; } = 0.522f; @@ -42,48 +38,20 @@ public static class RoleSettingMenuPatches HudManager.Instance.PlayerCam.OverrideScreenShakeEnabled = false; } - [HarmonyPostfix] - [HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))] - public static void SetQuotaTabPostfix(RolesSettingsMenu __instance) - { - __instance.scrollBar.SetScrollBounds(__instance); - } - private static IEnumerator CoQuotaTabPatch(RolesSettingsMenu roleMenu) { - foreach (var obj in Headers) - { - if (!obj) - { - continue; - } - - obj.Destroy(); - } - - Headers.Clear(); - foreach (var obj in RoleOptionSettings) - { - if (!obj) - { - continue; - } - - obj.gameObject.Destroy(); - } - - RoleOptionSettings.Clear(); CurrentRole = null; CurrentRoleOptions = null; - roleMenu.roleChances = new Il2CppSystem.Collections.Generic.List(); - roleMenu.advancedSettingChildren = new Il2CppSystem.Collections.Generic.List(); + roleMenu.QuotaTabSelectables.Clear(); + roleMenu.roleChances.Clear(); + roleMenu.advancedSettingChildren.Clear(); var maskBg = roleMenu.scrollBar.transform.FindChild("MaskBg"); var hitbox = roleMenu.scrollBar.transform.FindChild("Hitbox"); var dividerImage = roleMenu.transform.FindChild("HeaderButtons/DividerImage"); - if (GameSettingMenuPatches.SelectedModIdx == 0) + if (MenuState.Instance.CurrentModIdx == 0) { roleMenu.AllButton.transform.parent.gameObject.SetActive(true); roleMenu.AllButton.gameObject.SetActive(true); @@ -93,7 +61,34 @@ public static class RoleSettingMenuPatches hitbox.localPosition = new Vector3(0.3297f, -.2333f, 4f); hitbox.localScale = new Vector3(1, 1, 1); dividerImage.gameObject.SetActive(true); + } + else + { + roleMenu.AllButton.transform.parent.gameObject.SetActive(false); + roleMenu.AllButton.gameObject.SetActive(false); + roleMenu.scrollBar.transform.localPosition = new Vector3(-1.4957f, 1.5261f, -4); + maskBg.localPosition = new Vector3(1.5353f, -1.0607f, -.1f); + maskBg.localScale = new Vector3(6.6811f, 4.1563f, 0.5598f); + hitbox.localPosition = new Vector3(0.3297f, -.6333f, 4f); + hitbox.localScale = new Vector3(1, 1.2f, 1); + dividerImage.gameObject.SetActive(false); + } + if (MenuState.Instance.FinishedRoleMenus.TryGetValue(MenuState.Instance.CurrentModIdx, out var finished) && finished) + { + var roleOptionSettings = roleMenu.scrollBar.Inner.GetComponentsInChildren(true); + foreach (var r in roleOptionSettings) + { + roleMenu.roleChances.Add(r); + roleMenu.QuotaTabSelectables.AddRange(new Il2CppSystem.Collections.Generic.IEnumerable(r.ControllerSelectable.Pointer)); + } + + Info($"Already created role options for {MenuState.Instance.CurrentModIdx}"); + yield break; + } + + if (MenuState.Instance.CurrentModIdx == 0) + { var num = 0.662f; var list = CustomRoleManager.AllRoles.Where(r => @@ -152,208 +147,186 @@ public static class RoleSettingMenuPatches } Info($"Created {num3} role options for the default game."); + _quotaTabCoroutine = null; + MenuState.Instance.FinishedRoleMenus[MenuState.Instance.CurrentModIdx] = true; yield break; } ScrollerNum = 0.522f; - roleMenu.AllButton.transform.parent.gameObject.SetActive(false); - roleMenu.AllButton.gameObject.SetActive(false); - roleMenu.scrollBar.transform.localPosition = new Vector3(-1.4957f, 1.5261f, -4); - maskBg.localPosition = new Vector3(1.5353f, -1.0607f, -.1f); - maskBg.localScale = new Vector3(6.6811f, 4.1563f, 0.5598f); - hitbox.localPosition = new Vector3(0.3297f, -.6333f, 4f); - hitbox.localScale = new Vector3(1, 1.2f, 1); - dividerImage.gameObject.SetActive(false); - var num4 = 0; - var roleGroups = GameSettingMenuPatches.SelectedMod?.InternalRoles.Values.OfType() + var roleGroups = MenuState.Instance.CurrentMod.InternalRoles.Values.OfType() .ToLookup(x => x.RoleOptionsGroup); - if (roleGroups is null) + if (roleGroups.Count == 0) { Error("No role groups found for the selected mod."); - yield break; } - - // sort the groups by priority - var sortedRoleGroups = roleGroups - .OrderBy(x => x.Key.Priority) - .ThenBy(x => x.Key.Name); - - var quotaThing = roleMenu.categoryHeaderEditRoleOrigin.transform.FindChild("QuotaHeader"); - var usingNewQuota = false; - var template = roleMenu.transform.parent.parent.GetComponent().GameSettingsTab - .categoryHeaderOrigin; - - foreach (var grouping in sortedRoleGroups) + else { - if (!grouping.Any() || grouping.All(x => x.Configuration.HideSettings || !x.VisibleInSettings())) + // sort the groups by priority + var sortedRoleGroups = roleGroups + .OrderBy(x => x.Key.Priority) + .ThenBy(x => x.Key.Name); + + var quotaThing = roleMenu.categoryHeaderEditRoleOrigin.transform.FindChild("QuotaHeader"); + var usingNewQuota = false; + var template = roleMenu.transform.parent.parent.GetComponent().GameSettingsTab + .categoryHeaderOrigin; + + foreach (var grouping in sortedRoleGroups) { - continue; - } - - var group = grouping.Key; - - RoleGroupHidden.TryAdd(group, false); - - var name = group.Name switch - { - "Crewmate" => StringNames.CrewmateRolesHeader, - "Impostor" => StringNames.ImpostorRolesHeader, - _ => CustomStringName.CreateAndRegister(group.Name), - }; - - var categoryHeaderMasked = Object.Instantiate( - template, - Vector3.zero, - Quaternion.identity, - roleMenu.RoleChancesSettings.transform); - - categoryHeaderMasked.SetHeader(name, 20); - - var quotaInst = Object.Instantiate(quotaThing, categoryHeaderMasked.transform); - quotaInst.transform.localScale = new Vector3(1.3f, 1.3f, 1.3f); - quotaInst.transform.localPosition = new Vector3(0.7f, -0.82f, 0); - - var chanceText = quotaInst.transform.FindChild("Chance Text"); - chanceText.transform.localPosition = new Vector3(4.3f, 0.0993f, 0); - - var countText = quotaInst.transform.FindChild("# Text"); - countText.transform.localPosition = new Vector3(1.9f, 0.0993f, 0f); - - if (!usingNewQuota) - { - var blankLabel = quotaInst.transform.FindChild("BlankLabel").gameObject; - var chanceLabel = quotaInst.transform.FindChild("Chance Label").gameObject; - var countLabel = quotaInst.transform.FindChild("# Label").gameObject; - blankLabel.Destroy(); - chanceLabel.Destroy(); - countLabel.Destroy(); - usingNewQuota = true; - quotaThing = quotaInst; - } - - categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset(); - categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear; - categoryHeaderMasked.Background.sprite.texture.wrapMode = TextureWrapMode.Clamp; - - categoryHeaderMasked.Background.transform.localPosition = new Vector3(0.5f, -0.1833f, 0); - - switch (name) - { - case StringNames.CrewmateRolesHeader: - categoryHeaderMasked.Title.color = Palette.CrewmateRoleHeaderTextBlue; - categoryHeaderMasked.Background.color = Palette.CrewmateRoleHeaderBlue; - break; - case StringNames.ImpostorRolesHeader: - categoryHeaderMasked.Title.color = Palette.ImpostorRoleHeaderTextRed; - categoryHeaderMasked.Background.color = Palette.ImpostorRoleHeaderRed; - break; - default: - categoryHeaderMasked.Title.color = group.Color.Equals(MiraApiPlugin.DefaultHeaderColor) - ? Color.white - : group.Color.FindAlternateColor(); - categoryHeaderMasked.Divider.color = group.Color; - categoryHeaderMasked.Background.color = group.Color; - break; - } - - categoryHeaderMasked.Title.fontStyle = roleMenu.categoryHeaderEditRoleOrigin.Title.fontStyle; - categoryHeaderMasked.Title.font = roleMenu.categoryHeaderEditRoleOrigin.Title.font; - categoryHeaderMasked.Title.fontMaterial = roleMenu.categoryHeaderEditRoleOrigin.Title.fontMaterial; - - categoryHeaderMasked.Divider.color = categoryHeaderMasked.Background.color; - categoryHeaderMasked.Background.transform.localPosition = new Vector3(0.55f, -0.1833f, 0); - categoryHeaderMasked.Background.size = new Vector2( - categoryHeaderMasked.Background.size.x + 1.5f, - categoryHeaderMasked.Background.size.y); - - categoryHeaderMasked.transform.localScale = Vector3.one * 0.63f; - categoryHeaderMasked.transform.localPosition = new Vector3(-0.44f, ScrollerNum, -2f); - categoryHeaderMasked.gameObject.SetActive(true); - quotaInst.gameObject.SetActive(!RoleGroupHidden[group]); - - var label = RoleGroupHidden[group] - ? "(Click to open)" - : "(Click to close)"; - var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform); - newText.text = $"{label}"; - newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f); - newText.gameObject.GetComponent().Destroy(); - - Headers.Add(categoryHeaderMasked.gameObject); - ScrollerNum -= 0.422f; - - if (!RoleGroupHidden[group]) - { - yield return - grouping.CoLoopWithBudget(role => - { - if (role is not RoleBehaviour roleBehaviour) - { - return; - } - - var option = CreateQuotaOption(roleMenu, roleBehaviour, num4); - if (option is not null) - { - RoleOptionSettings.Add(option); - num4++; - } - }); - } - - var boxCol = categoryHeaderMasked.gameObject.AddComponent(); - boxCol.size = new Vector2(7, 0.7f); - boxCol.offset = new Vector2(1.5f, -0.3f); - - var headerBtn = categoryHeaderMasked.gameObject.AddComponent(); - headerBtn.ClickSound = roleMenu.BackButton.GetComponent().ClickSound; - headerBtn.OnMouseOver = new UnityEvent(); - headerBtn.OnMouseOut = new UnityEvent(); - headerBtn.OnClick.AddListener( - (UnityAction)(() => + if (!grouping.Any() || grouping.All(x => x.Configuration.HideSettings || !x.VisibleInSettings())) { - RolePositions[GameSettingMenuPatches.SelectedModIdx] = roleMenu.scrollBar.Inner.localPosition; - if (RoleGroupHidden.TryGetValue(group, out var groupHidden)) - { - RoleGroupHidden[group] = !groupHidden; - } + continue; + } - foreach (var obj in Headers) - { - if (!obj) + var group = grouping.Key; + + RoleGroupHidden.TryAdd(group, false); + + var name = group.Name switch + { + "Crewmate" => StringNames.CrewmateRolesHeader, + "Impostor" => StringNames.ImpostorRolesHeader, + _ => CustomStringName.CreateAndRegister(group.Name), + }; + + var categoryHeaderMasked = Object.Instantiate( + template, + Vector3.zero, + Quaternion.identity, + roleMenu.RoleChancesSettings.transform); + + categoryHeaderMasked.SetHeader(name, 20); + + var quotaInst = Object.Instantiate(quotaThing, categoryHeaderMasked.transform); + quotaInst.transform.localScale = new Vector3(1.3f, 1.3f, 1.3f); + quotaInst.transform.localPosition = new Vector3(0.7f, -0.82f, 0); + + var chanceText = quotaInst.transform.FindChild("Chance Text"); + chanceText.transform.localPosition = new Vector3(4.3f, 0.0993f, 0); + + var countText = quotaInst.transform.FindChild("# Text"); + countText.transform.localPosition = new Vector3(1.9f, 0.0993f, 0f); + + if (!usingNewQuota) + { + var blankLabel = quotaInst.transform.FindChild("BlankLabel").gameObject; + var chanceLabel = quotaInst.transform.FindChild("Chance Label").gameObject; + var countLabel = quotaInst.transform.FindChild("# Label").gameObject; + blankLabel.Destroy(); + chanceLabel.Destroy(); + countLabel.Destroy(); + usingNewQuota = true; + quotaThing = quotaInst; + } + + categoryHeaderMasked.Background.sprite = MiraAssets.CategoryHeader.LoadAsset(); + categoryHeaderMasked.Background.sprite.texture.filterMode = FilterMode.Bilinear; + categoryHeaderMasked.Background.sprite.texture.wrapMode = TextureWrapMode.Clamp; + + categoryHeaderMasked.Background.transform.localPosition = new Vector3(0.5f, -0.1833f, 0); + + switch (name) + { + case StringNames.CrewmateRolesHeader: + categoryHeaderMasked.Title.color = Palette.CrewmateRoleHeaderTextBlue; + categoryHeaderMasked.Background.color = Palette.CrewmateRoleHeaderBlue; + break; + case StringNames.ImpostorRolesHeader: + categoryHeaderMasked.Title.color = Palette.ImpostorRoleHeaderTextRed; + categoryHeaderMasked.Background.color = Palette.ImpostorRoleHeaderRed; + break; + default: + categoryHeaderMasked.Title.color = group.Color.Equals(MiraApiPlugin.DefaultHeaderColor) + ? Color.white + : group.Color.FindAlternateColor(); + categoryHeaderMasked.Divider.color = group.Color; + categoryHeaderMasked.Background.color = group.Color; + break; + } + + categoryHeaderMasked.Title.fontStyle = roleMenu.categoryHeaderEditRoleOrigin.Title.fontStyle; + categoryHeaderMasked.Title.font = roleMenu.categoryHeaderEditRoleOrigin.Title.font; + categoryHeaderMasked.Title.fontMaterial = roleMenu.categoryHeaderEditRoleOrigin.Title.fontMaterial; + + categoryHeaderMasked.Divider.color = categoryHeaderMasked.Background.color; + categoryHeaderMasked.Background.transform.localPosition = new Vector3(0.55f, -0.1833f, 0); + categoryHeaderMasked.Background.size = new Vector2( + categoryHeaderMasked.Background.size.x + 1.5f, + categoryHeaderMasked.Background.size.y); + + categoryHeaderMasked.transform.localScale = Vector3.one * 0.63f; + categoryHeaderMasked.transform.localPosition = new Vector3(-0.44f, ScrollerNum, -2f); + categoryHeaderMasked.gameObject.SetActive(true); + quotaInst.gameObject.SetActive(!RoleGroupHidden[group]); + + var label = RoleGroupHidden[group] + ? "(Click to open)" + : "(Click to close)"; + var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform); + newText.text = $"{label}"; + newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f); + newText.gameObject.GetComponent().Destroy(); + + ScrollerNum -= 0.422f; + + if (!RoleGroupHidden[group]) + { + yield return + grouping.CoLoopWithBudget(role => { - continue; + if (role is not RoleBehaviour roleBehaviour) + { + return; + } + + var option = CreateQuotaOption(roleMenu, roleBehaviour, num4); + if (option is not null) + { + num4++; + } + }); + } + + var boxCol = categoryHeaderMasked.gameObject.AddComponent(); + boxCol.size = new Vector2(7, 0.7f); + boxCol.offset = new Vector2(1.5f, -0.3f); + + var headerBtn = categoryHeaderMasked.gameObject.AddComponent(); + headerBtn.ClickSound = roleMenu.BackButton.GetComponent().ClickSound; + headerBtn.OnMouseOver = new UnityEvent(); + headerBtn.OnMouseOut = new UnityEvent(); + headerBtn.OnClick.AddListener( + (UnityAction)(() => + { + if (RoleGroupHidden.TryGetValue(group, out var groupHidden)) + { + RoleGroupHidden[group] = !groupHidden; } - obj.Destroy(); - } + MenuState.Instance.FinishedRoleMenus[MenuState.Instance.CurrentModIdx] = false; - Headers.Clear(); - foreach (var obj in RoleOptionSettings) - { - if (!obj) + foreach (var child in roleMenu.RoleChancesSettings.transform) { - continue; + var transform = child.Cast(); + Object.Destroy(transform.gameObject); } - obj.gameObject.Destroy(); - } + roleMenu.OpenChancesTab(); + })); + headerBtn.SetButtonEnableState(true); - RoleOptionSettings.Clear(); - roleMenu.SetQuotaTab(); - })); - headerBtn.SetButtonEnableState(true); - - if (RoleGroupHidden.TryGetValue(group, out var value) && !value) - { - ScrollerNum -= 0.4f; + if (RoleGroupHidden.TryGetValue(group, out var value) && !value) + { + ScrollerNum -= 0.4f; + } } } + MenuState.Instance.FinishedRoleMenus[MenuState.Instance.CurrentModIdx] = true; roleMenu.scrollBar.SetScrollBounds(roleMenu); _quotaTabCoroutine = null; } @@ -361,36 +334,62 @@ public static class RoleSettingMenuPatches private static Coroutine? _quotaTabCoroutine; [HarmonyPrefix] - [HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))] - public static bool SetQuotaTabPatch(RolesSettingsMenu __instance) + [HarmonyPatch(nameof(RolesSettingsMenu.OpenChancesTab))] + public static bool OpenChancesTabPatch(RolesSettingsMenu __instance, bool controllerSelected) { + __instance.selectedRoleTab = 0; + __instance.RoleChancesSettings.SetActive(true); + __instance.AdvancedRolesSettings.SetActive(false); + __instance.ControllerSelectable.Clear(); if (_quotaTabCoroutine != null) { GameSettingMenu.Instance.StopCoroutine(_quotaTabCoroutine); } - _quotaTabCoroutine = GameSettingMenu.Instance.StartCoroutine(CoQuotaTabPatch(__instance).WrapToIl2Cpp()); + __instance.QuotaTabSelectables ??= new(); + __instance.roleChances ??= new(); + __instance.advancedSettingChildren ??= new(); + + _quotaTabCoroutine = GameSettingMenu.Instance.StartCoroutine(CoOpenChancesTab().WrapToIl2Cpp()); return false; + + IEnumerator CoOpenChancesTab() + { + yield return CoQuotaTabPatch(__instance); + if (controllerSelected) + { + __instance.ControllerSelectable.AddRange( + new Il2CppSystem.Collections.Generic.IEnumerable( + __instance.QuotaTabSelectables.Pointer)); + } + + __instance.scrollBar.CalculateAndSetYBounds(__instance.roleChances.Count + 3f, 1f, 6f, 0.43f); + __instance.scrollBar.SetScrollBounds(__instance); + + if (controllerSelected) + { + ControllerManager.Instance.CurrentUiState.SelectableUiElements = __instance.ControllerSelectable; + ControllerManager.Instance.SetDefaultSelection(__instance.roleChances[0].ControllerSelectable[0]); + } + var passiveButton = __instance.currentTabButton; + if (passiveButton) + { + passiveButton.SelectButton(false); + } + __instance.AllButton.SelectButton(true); + __instance.currentTabButton = __instance.AllButton; + } } private static void SetScrollBounds(this Scroller scroller, RolesSettingsMenu roleMenu) { - if (GameSettingMenuPatches.SelectedModIdx == 0) + if (MenuState.Instance.CurrentModIdx == 0) { scroller.CalculateAndSetYBounds(roleMenu.roleChances.Count + 3, 1f, 6f, 0.43f); return; } scroller.SetYBoundsMax(-ScrollerNum - 2); - if (RolePositions.TryGetValue(GameSettingMenuPatches.SelectedModIdx, out var scroll)) - { - scroller.Inner.localPosition = scroll; - scroller.UpdateScrollBars(); - } - else - { - scroller.ScrollToTop(); - } } [HarmonyPrefix] @@ -398,7 +397,7 @@ public static class RoleSettingMenuPatches [HarmonyPatch(nameof(RolesSettingsMenu.Update))] public static bool UpdatePatch(RolesSettingsMenu __instance) { - if (GameSettingMenuPatches.SelectedModIdx == 0) return true; + if (MenuState.Instance.CurrentModIdx == 0) return true; if (CurrentRole == null || CurrentRoleOptions == null) return false; var hasImage = CurrentRole.RoleScreenshot != null; @@ -489,10 +488,10 @@ public static class RoleSettingMenuPatches __instance.advancedSettingChildren.Clear(); // TODO: create sub groups under the role settings. - var filteredOptions = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups + var filteredOptions = MenuState.Instance.CurrentMod.InternalOptionGroups .Where(x => x.GroupVisible() && x.OptionableType == role.GetType()) .SelectMany(x => x.Options) - .ToList() ?? []; + .ToList(); foreach (var option in filteredOptions) { @@ -536,8 +535,6 @@ public static class RoleSettingMenuPatches return; } - RolePositions[GameSettingMenuPatches.SelectedModIdx] = __instance.scrollBar.Inner.localPosition; - __instance.roleDescriptionText.text = customRole.RoleLongDescription; __instance.roleTitleText.text = role.GetRoleName(); @@ -670,8 +667,7 @@ public static class RoleSettingMenuPatches rend.maskInteraction = SpriteMaskInteraction.VisibleInsideMask; } - if (GameSettingMenuPatches.SelectedMod is null || - GameSettingMenuPatches.SelectedMod.InternalOptionGroups + if (MenuState.Instance.CurrentMod.InternalOptionGroups .Exists(x => x.GroupVisible() && x.OptionableType == role.GetType() && x.Options.Any(y => y.Visible()))) { var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform); @@ -703,7 +699,7 @@ public static class RoleSettingMenuPatches roleOptionSetting.ChancePlusBtn.gameObject.SetActive(false); } - if (index < GameSettingMenuPatches.SelectedMod?.InternalRoles.Count - 1) + if (index < MenuState.Instance.CurrentMod.InternalRoles.Count - 1) { ScrollerNum -= 0.43f; } diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index 171ab90..e662859 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -17,6 +17,7 @@ using MiraAPI.Keybinds; using MiraAPI.LocalSettings; using MiraAPI.LocalSettings.Attributes; using MiraAPI.Modifiers; +using MiraAPI.Patches.Options; using MiraAPI.Presets; using MiraAPI.Roles; using MiraAPI.Utilities; @@ -33,10 +34,7 @@ public sealed class MiraPluginManager private readonly Dictionary _registeredPlugins = []; internal MiraPluginInfo[] RegisteredPlugins { get; private set; } = null!; - - private readonly Dictionary _registeredOptionablePlugins = []; - - internal MiraPluginInfo[] RegisteredOptionablePlugins { get; private set; } = null!; + internal MiraPluginInfo[] RegisteredPluginsWithOptions { get; private set; } = null!; internal Dictionary> QueuedRoleRegistrations { get; } = []; internal static MiraPluginManager Instance { get; private set; } = new(); @@ -131,10 +129,6 @@ public sealed class MiraPluginManager QueuedRoleRegistrations.Add(info, roles); _registeredPlugins.Add(assembly, info); - if (miraPlugin.DisplayOnOptionsMenu) - { - _registeredOptionablePlugins.Add(assembly, info); - } info.SavePublicCollections(); PresetManager.CreateDefaultPreset(info); @@ -149,7 +143,7 @@ public sealed class MiraPluginManager // Cache all the registered plugins into an array for easy access RegisteredPlugins = [.. _registeredPlugins.Values]; - RegisteredOptionablePlugins = [.. _registeredOptionablePlugins.Values]; + RegisteredPluginsWithOptions = [.. RegisteredPlugins.Where(m => m.MiraPlugin.DisplayOnOptionsMenu)]; ModifierManager.Modifiers = new ReadOnlyCollection(ModifierManager.InternalModifiers); }; diff --git a/MiraAPI/Roles/CustomRoleManager.cs b/MiraAPI/Roles/CustomRoleManager.cs index d85ed19..6c6760b 100644 --- a/MiraAPI/Roles/CustomRoleManager.cs +++ b/MiraAPI/Roles/CustomRoleManager.cs @@ -141,7 +141,11 @@ public static class CustomRoleManager roleBehaviour.CanVent = customRole.Configuration.CanUseVent || customRole.Configuration.GetsVentData; roleBehaviour.DefaultGhostRole = customRole.Configuration.GhostRole; roleBehaviour.MaxCount = customRole.Configuration.MaxRoleCount; - roleBehaviour.RoleScreenshot = customRole.Configuration.OptionsScreenshot?.LoadAsset(); + roleBehaviour.RoleScreenshot = Sprite.Create( + customRole.Configuration.OptionsScreenshot?.LoadAsset().texture, + new Rect(0, 0, 370, 230), + Vector2.one / 2, + 100); _emptySettings ??= new(0); _emptyKillAnimations ??= new(0); diff --git a/MiraAPI/Utilities/Assets/LoadableAsset.cs b/MiraAPI/Utilities/Assets/LoadableAsset.cs index 798dfc9..7b0c032 100644 --- a/MiraAPI/Utilities/Assets/LoadableAsset.cs +++ b/MiraAPI/Utilities/Assets/LoadableAsset.cs @@ -1,4 +1,6 @@ -namespace MiraAPI.Utilities.Assets; +using Reactor.Utilities.Extensions; + +namespace MiraAPI.Utilities.Assets; /// /// An abstract class that provides a simple pattern for loading assets. @@ -18,4 +20,20 @@ public abstract class LoadableAsset where T : UnityEngine.Object /// /// The loaded asset. public abstract T LoadAsset(); + + /// + /// Unloads an asset. + /// + /// True if the asset was unloaded, false otherwise. + public virtual bool UnloadAsset() + { + if (LoadedAsset == null || !LoadedAsset) + { + return false; + } + + LoadedAsset.DestroyImmediate(); + LoadedAsset = null; + return true; + } } diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index 9dd91e9..53e395e 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -354,10 +354,6 @@ public static class Extensions UnityEngine.Object.Destroy(go); yield return null; - if (clearGc) - { - ClearGarbageCollector(); - } } /// @@ -366,8 +362,8 @@ public static class Extensions public static void ClearGarbageCollector() { Resources.UnloadUnusedAssets(); - Il2CppSystem.GC.Collect(0, Il2CppSystem.GCCollectionMode.Forced); - GC.Collect(0, GCCollectionMode.Forced); + Il2CppSystem.GC.Collect(); + GC.Collect(); } ///