diff --git a/MiraAPI/GameOptions/IModdedOption.cs b/MiraAPI/GameOptions/IModdedOption.cs index e002912..ecffc1a 100644 --- a/MiraAPI/GameOptions/IModdedOption.cs +++ b/MiraAPI/GameOptions/IModdedOption.cs @@ -83,4 +83,11 @@ public interface IModdedOption /// /// >The byte array representing the network data. void HandleNetData(byte[] data); + + /// + /// Saves the option to a preset configuration file. + /// + /// The ConfigFile representing the preset configuration. + /// Indicates whether to save the default value instead of the current value. + void SaveToPreset(ConfigFile presetConfig, bool saveDefault = false); } diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs index 7aaa154..4f7c583 100644 --- a/MiraAPI/GameOptions/ModdedOptionsManager.cs +++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs @@ -42,7 +42,7 @@ public static class ModdedOptionsManager Groups.Add(group); TypeToGroup.Add(type, group); - pluginInfo.OptionGroups.Add(group); + pluginInfo.InternalOptionGroups.Add(group); typeof(OptionGroupSingleton<>).MakeGenericType(type) .GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)! @@ -119,7 +119,7 @@ public static class ModdedOptionsManager option.ConfigDefinition = new ConfigDefinition(groupName, propertyName); option.ParentMod = pluginInfo.MiraPlugin; - pluginInfo.Options.Add(option); + pluginInfo.InternalOptions.Add(option); ModdedOptions.Add(option.Id, option); group.Options.Add(option); } @@ -140,7 +140,7 @@ public static class ModdedOptionsManager // we dont know how other plugins handle their configs // this way, all the options are saved at once, instead of one by one var oldConfigSetting = new Dictionary(); - foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins()) + foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins) { oldConfigSetting.Add(plugin, plugin.PluginConfig.SaveOnConfigSet); plugin.PluginConfig.SaveOnConfigSet = false; @@ -156,7 +156,7 @@ public static class ModdedOptionsManager option.HandleNetData(netData.Data); } - foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins()) + foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins) { plugin.PluginConfig.Save(); plugin.PluginConfig.SaveOnConfigSet = oldConfigSetting[plugin]; @@ -173,6 +173,7 @@ public static class ModdedOptionsManager /// /// The original setter method. /// The new object value. + // ReSharper disable once InconsistentNaming public static void PropertySetterPatch(MethodBase __originalMethod, object value) { var attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod() == __originalMethod).Value; @@ -185,6 +186,7 @@ public static class ModdedOptionsManager /// The original getter method. /// The result of the property getter. /// False so the original getter gets skipped. + // ReSharper disable InconsistentNaming public static bool PropertyGetterPatch(MethodBase __originalMethod, ref object __result) { var attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod() == __originalMethod).Value; diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs index 2b61e2b..442f66b 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs @@ -4,6 +4,7 @@ using MiraAPI.Networking; using MiraAPI.PluginLoading; using Reactor.Localization.Utilities; using Reactor.Networking.Rpc; +using Reactor.Utilities; using UnityEngine; namespace MiraAPI.GameOptions.OptionTypes; @@ -127,6 +128,24 @@ public abstract class ModdedOption : IModdedOption OnValueChanged(newValue); } + /// + public void SaveToPreset(ConfigFile presetConfig, bool saveDefault=false) + { + if (ConfigDefinition is null) + { + Logger.Error($"Attempted to save {Title} to preset, but ConfigDefinition is null."); + return; + } + var value = saveDefault ? DefaultValue : Value; + + if (!presetConfig.ContainsKey(ConfigDefinition)) + { + presetConfig.Bind(ConfigDefinition, DefaultValue); + } + + presetConfig[ConfigDefinition].BoxedValue = value; + } + /// public abstract float GetFloatData(); diff --git a/MiraAPI/Hud/CustomButtonManager.cs b/MiraAPI/Hud/CustomButtonManager.cs index dd10f6c..9b176d2 100644 --- a/MiraAPI/Hud/CustomButtonManager.cs +++ b/MiraAPI/Hud/CustomButtonManager.cs @@ -29,7 +29,7 @@ public static class CustomButtonManager } CustomButtons.Add(button); - pluginInfo.Buttons.Add(button); + pluginInfo.InternalButtons.Add(button); typeof(CustomButtonSingleton<>).MakeGenericType(buttonType) .GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)! .SetValue(null, button); diff --git a/MiraAPI/Modifiers/BaseModifier.cs b/MiraAPI/Modifiers/BaseModifier.cs index 7b3dc3f..cd870eb 100644 --- a/MiraAPI/Modifiers/BaseModifier.cs +++ b/MiraAPI/Modifiers/BaseModifier.cs @@ -38,8 +38,8 @@ public abstract class BaseModifier : IOptionable /// Gets the parent mod of the modifier. /// public MiraPluginInfo ParentMod => Array.Find( - MiraPluginManager.Instance.RegisteredPlugins(), - x => x.Modifiers.Exists(y => y.TypeId == TypeId) + MiraPluginManager.Instance.RegisteredPlugins, + x => x.InternalModifiers.Exists(y => y.TypeId == TypeId) ) ?? throw new InvalidOperationException("Modifier is not registered."); /// diff --git a/MiraAPI/Modifiers/ModifierManager.cs b/MiraAPI/Modifiers/ModifierManager.cs index 5f6bbff..4aeee68 100644 --- a/MiraAPI/Modifiers/ModifierManager.cs +++ b/MiraAPI/Modifiers/ModifierManager.cs @@ -75,7 +75,7 @@ public static class ModifierManager modifier = (BaseModifier)FormatterServices.GetUninitializedObject(modifierType); } - info.Modifiers.Add(modifier); + info.InternalModifiers.Add(modifier); if (modifier is not GameModifier gameModifier) { diff --git a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs index 666a1ea..136934e 100644 --- a/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs +++ b/MiraAPI/Patches/Freeplay/TaskAdderPatches.cs @@ -67,9 +67,9 @@ internal static class TaskAdderPatches modifiersFolder.FolderName = "Modifiers"; modifiersFolder.name = "ModifiersFolder"; - foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins()) + foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins) { - if (plugin.CustomRoles.Any(x => x.Value is ICustomRole { Configuration.ShowInFreeplay: true })) + if (plugin.InternalRoles.Any(x => x.Value is ICustomRole { Configuration.ShowInFreeplay: true })) { var newFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner); newFolder.name = plugin.PluginId+":Roles"; @@ -78,7 +78,7 @@ internal static class TaskAdderPatches rolesFolder.SubFolders.Add(newFolder); } - if (plugin.Modifiers.Exists(x => x.ShowInFreeplay)) + if (plugin.InternalModifiers.Exists(x => x.ShowInFreeplay)) { var newFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner); newFolder.name = plugin.PluginId+":Modifiers"; @@ -317,9 +317,9 @@ internal static class TaskAdderPatches ref float num2, ref float num3) { - for (var m = 0; m < plugin.Modifiers.Count; m++) + for (var m = 0; m < plugin.InternalModifiers.Count; m++) { - var modifier = plugin.Modifiers[m]; + var modifier = plugin.InternalModifiers[m]; if (!modifier.ShowInFreeplay) { continue; @@ -368,9 +368,9 @@ internal static class TaskAdderPatches private static void PluginRoles(TaskAdderGame instance, MiraPluginInfo plugin, ref float num, ref float num2, ref float num3) { - for (var m = 0; m < plugin.CustomRoles.Count; m++) + for (var m = 0; m < plugin.InternalRoles.Count; m++) { - var roleBehaviour = plugin.CustomRoles.ElementAt(m).Value; + var roleBehaviour = plugin.InternalRoles.ElementAt(m).Value; if (roleBehaviour.Role == RoleTypes.ImpostorGhost || roleBehaviour.Role == RoleTypes.CrewmateGhost || roleBehaviour is ICustomRole { Configuration.ShowInFreeplay: false }) { diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs index 8034eff..0d88920 100644 --- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs +++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs @@ -14,18 +14,12 @@ using Object = UnityEngine.Object; namespace MiraAPI.Patches.Options; -/// -/// Patches the GameOptionsMenu to add custom options. -/// [HarmonyPatch(typeof(GameOptionsMenu))] -public static class GameOptionsMenuPatch +internal static class GameOptionsMenuPatch { - /// - /// Update patch for the GameOptionsMenu. - /// - /// The GameOptionsMenu instance. [HarmonyPostfix] [HarmonyPatch(nameof(GameOptionsMenu.Update))] + // ReSharper disable once InconsistentNaming public static void UpdatePatch(GameOptionsMenu __instance) { if (GameSettingMenuPatches.SelectedModIdx == 0) @@ -41,7 +35,7 @@ public static class GameOptionsMenuPatch else { var filteredGroups = - GameSettingMenuPatches.SelectedMod?.OptionGroups + GameSettingMenuPatches.SelectedMod?.InternalOptionGroups .Where(x => x.OptionableType == null && !x.ShowInModifiersMenu) ?? []; foreach (var group in filteredGroups) @@ -107,7 +101,7 @@ public static class GameOptionsMenuPatch private static void ModifiersUpdate(ref float num) { - var groups = GameSettingMenuPatches.SelectedMod?.OptionGroups + var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups .Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true) ?? []; foreach (var modGroup in groups) @@ -118,7 +112,7 @@ public static class GameOptionsMenuPatch private static void ModifiersCreate(GameOptionsMenu menu) { - var groups = GameSettingMenuPatches.SelectedMod?.OptionGroups + var groups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups .Where(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true) ?? []; foreach (var group in groups) { @@ -128,6 +122,7 @@ public static class GameOptionsMenuPatch [HarmonyPrefix] [HarmonyPatch(nameof(GameOptionsMenu.CreateSettings))] + // ReSharper disable once InconsistentNaming public static bool SettingsPatch(GameOptionsMenu __instance) { if (GameSettingMenuPatches.SelectedModIdx == 0) @@ -143,7 +138,7 @@ public static class GameOptionsMenuPatch return false; } - var filteredGroups = GameSettingMenuPatches.SelectedMod?.OptionGroups + var filteredGroups = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups .Where(x => x.OptionableType == null && !x.ShowInModifiersMenu) ?? []; foreach (var group in filteredGroups) @@ -156,6 +151,7 @@ public static class GameOptionsMenuPatch [HarmonyPrefix] [HarmonyPatch(nameof(GameOptionsMenu.Initialize))] + // ReSharper disable once InconsistentNaming public static bool InitPatch(GameOptionsMenu __instance) { if (__instance.Children != null && __instance.Children.Count != 0) diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index ca8f4ae..c068a52 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -1,26 +1,101 @@ using System.Linq; using HarmonyLib; -using MiraAPI.Presets; +using MiraAPI.PluginLoading; +using Reactor.Utilities; +using Reactor.Utilities.Extensions; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.UI; namespace MiraAPI.Patches.Options; internal static class GamePresetsTabPatches { - [HarmonyPatch(typeof(GamePresetsTab), nameof(GamePresetsTab.OpenMenu))] - public static class GamePresetsTabOnEnablePatch + [HarmonyPatch(typeof(GamePresetsTab), nameof(GamePresetsTab.Start))] + public static class GamePresetsTabStartPatch { + // ReSharper disable once InconsistentNaming public static void Postfix(GamePresetsTab __instance) { - if (GameSettingMenuPatches.SelectedModIdx == 0) + var prefab = GameSettingMenu.Instance.GameSettingsButton; + if (prefab == null) { + Logger.Error("GameSettingsButton prefab is null"); return; } - __instance.StandardPresetButton.gameObject.SetActive(false); - __instance.SecondPresetButton.gameObject.SetActive(false); + var holder = new GameObject("PresetHolder"); + holder.transform.SetParent(__instance.transform, false); + holder.transform.localPosition = new Vector3(-1.7f, 1.7f, 0); + var arrange = holder.AddComponent(); - foreach (var preset in PresetManager.Presets.Where(x=>x.Plugin==GameSettingMenuPatches.SelectedMod)) + foreach (var preset in MiraPluginManager.Instance.RegisteredPlugins.SelectMany(x=>x.Presets)) { + var button = Object.Instantiate(prefab, holder.transform); + button.buttonText.text = preset.Name; + if (button.buttonText.TryGetComponent(out var tmp)) + { + tmp.DestroyImmediate(); + } + + button.OnClick = new Button.ButtonClickedEvent(); + button.OnClick.AddListener( + (UnityAction)(() => + { + Logger.Error($"Loading preset {preset.Name}"); + preset.LoadPreset(); + })); + button.gameObject.SetActive(false); + preset.PresetButton = button.gameObject; + } + + arrange.Alignment = GridArrange.StartAlign.Right; + arrange.CellSize = new Vector2(2.5f, -.55f); + arrange.Start(); + arrange.ArrangeChilds(); + } + } + + [HarmonyPatch(typeof(GamePresetsTab), nameof(GamePresetsTab.OnEnable))] + public static class GamePresetsOnEnablePatch + { + // ReSharper disable once InconsistentNaming + public static void Postfix(GamePresetsTab __instance) + { + Logger.Error("OnEnable called"); + __instance.StandardPresetButton.gameObject.SetActive(GameSettingMenuPatches.SelectedModIdx == 0); + __instance.SecondPresetButton.gameObject.SetActive(GameSettingMenuPatches.SelectedModIdx == 0); + + foreach (var mod in MiraPluginManager.Instance.RegisteredPlugins) + { + foreach (var button in mod.Presets.Select(x=>x.PresetButton)) + { + if (button != null) + { + button.SetActive(mod == GameSettingMenuPatches.SelectedMod); + } + } + } + } + } + + [HarmonyPatch(typeof(GamePresetsTab), nameof(GamePresetsTab.OnDisable))] + public static class GamePresetsOnDisablePatch + { + // ReSharper disable once InconsistentNaming + public static void Postfix() + { + Logger.Error("OnDisable called"); + + foreach (var mod in MiraPluginManager.Instance.RegisteredPlugins) + { + foreach (var button in mod.Presets.Select(x=>x.PresetButton)) + { + if (button != null) + { + button.SetActive(false); + } + } } } } diff --git a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs index 9e05f58..a6d3798 100644 --- a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs @@ -4,7 +4,6 @@ using HarmonyLib; using MiraAPI.Modifiers; using MiraAPI.PluginLoading; using MiraAPI.Utilities.Assets; -using Reactor.Utilities; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; @@ -33,6 +32,7 @@ internal static class GameSettingMenuPatches [HarmonyPostfix] [HarmonyPatch(nameof(GameSettingMenu.ChangeTab))] + // ReSharper disable once InconsistentNaming public static void ChangeTabPostfix(GameSettingMenu __instance, int tabNum, bool previewOnly) { if ((previewOnly && Controller.currentTouchType == Controller.TouchType.Joystick) || !previewOnly) @@ -79,6 +79,7 @@ internal static class GameSettingMenuPatches /// The GameSettingMenu instance. [HarmonyPrefix] [HarmonyPatch(nameof(GameSettingMenu.Start))] + // ReSharper disable once InconsistentNaming public static void StartPrefix(GameSettingMenu __instance) { _roleBtnOgPos = __instance.RoleSettingsButton.transform.localPosition; @@ -112,7 +113,7 @@ internal static class GameSettingMenuPatches (UnityAction)(() => { SelectedModIdx += 1; - if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins().Length) + if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins.Length) { SelectedModIdx = 0; } @@ -132,7 +133,7 @@ internal static class GameSettingMenuPatches SelectedModIdx -= 1; if (SelectedModIdx < 0) { - SelectedModIdx = MiraPluginManager.Instance.RegisteredPlugins().Length; + SelectedModIdx = MiraPluginManager.Instance.RegisteredPlugins.Length; } UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab); @@ -212,37 +213,37 @@ internal static class GameSettingMenuPatches UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab); } - private static void ChangeRoleSettingButton(bool replace, GameSettingMenu __instance) + private static void ChangeRoleSettingButton(bool replace, GameSettingMenu menu) { - __instance.RoleSettingsButton.OnClick = new ButtonClickedEvent(); - __instance.RoleSettingsButton.OnMouseOver = new UnityEvent(); + menu.RoleSettingsButton.OnClick = new ButtonClickedEvent(); + menu.RoleSettingsButton.OnMouseOver = new UnityEvent(); if (replace) { - __instance.RoleSettingsButton.buttonText.text = "Modifiers Settings"; - __instance.RoleSettingsButton.OnClick.AddListener( + menu.RoleSettingsButton.buttonText.text = "Modifiers Settings"; + menu.RoleSettingsButton.OnClick.AddListener( (UnityAction)(() => { - __instance.ChangeTab(3, false); + menu.ChangeTab(3, false); })); - __instance.RoleSettingsButton.OnMouseOver.AddListener( + menu.RoleSettingsButton.OnMouseOver.AddListener( (UnityAction)(() => { - __instance.ChangeTab(3, true); + menu.ChangeTab(3, true); })); } else { - __instance.RoleSettingsButton.buttonText.text = "Roles Settings"; - __instance.RoleSettingsButton.OnClick.AddListener( + menu.RoleSettingsButton.buttonText.text = "Roles Settings"; + menu.RoleSettingsButton.OnClick.AddListener( (UnityAction)(() => { - __instance.ChangeTab(2, false); + menu.ChangeTab(2, false); })); - __instance.RoleSettingsButton.OnMouseOver.AddListener( + menu.RoleSettingsButton.OnMouseOver.AddListener( (UnityAction)(() => { - __instance.ChangeTab(2, true); + menu.ChangeTab(2, true); })); } } @@ -256,7 +257,7 @@ internal static class GameSettingMenuPatches else if (_text is not null) { _text.fontSizeMax = 2.3f; - SelectedMod = MiraPluginManager.Instance.RegisteredPlugins()[SelectedModIdx - 1]; + SelectedMod = MiraPluginManager.Instance.RegisteredPlugins[SelectedModIdx - 1]; var name = SelectedMod.MiraPlugin.OptionsTitleText; _text.text = name[..Math.Min(name.Length, 25)]; @@ -269,9 +270,9 @@ internal static class GameSettingMenuPatches if (SelectedModIdx != 0) { - var modHasRoles = SelectedMod!.CustomRoles.Count != 0; - var modHasModifiers = SelectedMod.OptionGroups.Exists(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true); - var modHasOptions = SelectedMod.OptionGroups.Exists(x => x.OptionableType == null && !x.ShowInModifiersMenu); + var modHasRoles = SelectedMod!.InternalRoles.Count != 0; + var modHasModifiers = SelectedMod.InternalOptionGroups.Exists(x => x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier)) == true); + var modHasOptions = SelectedMod.InternalOptionGroups.Exists(x => x.OptionableType == null && !x.ShowInModifiersMenu); _modifiersButton.gameObject.SetActive(true); _smallRolesButton.gameObject.SetActive(true); @@ -367,6 +368,11 @@ internal static class GameSettingMenuPatches ChangeRoleSettingButton(replaceWithModifiers, menu); } + if (menu.PresetsTab.gameObject.active) + { + menu.ChangeTab(0, false); + } + CleanupTab(settings, roles); } @@ -385,6 +391,34 @@ internal static class GameSettingMenuPatches private static void CleanupTab(GameOptionsMenu settings, RolesSettingsMenu roles) { + if (roles.roleChances != null && SelectedMod?.InternalRoles.Count > 0) + { + CleanupRoleSettings(roles); + } + + if (settings.Children != null && SelectedMod?.InternalOptionGroups.Count > 0) + { + CleanupSettings(settings); + } + + if (_modifiersTab?.Children?.Count > 0) + { + CleanupSettings(_modifiersTab); + } + void CleanupSettings(GameOptionsMenu gameOptMenu) + { + ClearOptions(gameOptMenu.Children); + gameOptMenu.Children = null; + + gameOptMenu.settingsContainer + .GetComponentsInChildren() + .ToList() + .ForEach(header => header.gameObject.DestroyImmediate()); + + gameOptMenu.Initialize(); + gameOptMenu.scrollBar.ScrollToTop(); + } + void CleanupRoleSettings(RolesSettingsMenu rolesMenu) { if (rolesMenu.advancedSettingChildren != null) @@ -410,34 +444,5 @@ internal static class GameSettingMenuPatches rolesMenu.SetQuotaTab(); rolesMenu.scrollBar.ScrollToTop(); } - - void CleanupSettings(GameOptionsMenu gameOptMenu) - { - ClearOptions(gameOptMenu.Children); - gameOptMenu.Children = null; - - gameOptMenu.settingsContainer - .GetComponentsInChildren() - .ToList() - .ForEach(header => header.gameObject.DestroyImmediate()); - - gameOptMenu.Initialize(); - gameOptMenu.scrollBar.ScrollToTop(); - } - - if (roles.roleChances != null && SelectedMod?.CustomRoles.Count > 0) - { - CleanupRoleSettings(roles); - } - - if (settings.Children != null && SelectedMod?.OptionGroups.Count > 0) - { - CleanupSettings(settings); - } - - if (_modifiersTab?.Children?.Count > 0) - { - CleanupSettings(_modifiersTab); - } } } diff --git a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs index 43c5719..7c7979d 100644 --- a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs +++ b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs @@ -24,7 +24,7 @@ public static class LobbyViewPanePatches private static MiraPluginInfo? SelectedMod => SelectedModIdx == 0 ? null - : MiraPluginManager.Instance.RegisteredPlugins()[SelectedModIdx - 1]; + : MiraPluginManager.Instance.RegisteredPlugins[SelectedModIdx - 1]; private static PassiveButton? ModifiersTabButton { get; set; } @@ -74,7 +74,7 @@ public static class LobbyViewPanePatches (UnityAction)(() => { SelectedModIdx += 1; - if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins().Length) + if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins.Length) { SelectedModIdx = 0; } @@ -98,7 +98,7 @@ public static class LobbyViewPanePatches SelectedModIdx -= 1; if (SelectedModIdx < 0) { - SelectedModIdx = MiraPluginManager.Instance.RegisteredPlugins().Length; + SelectedModIdx = MiraPluginManager.Instance.RegisteredPlugins.Length; } Refresh(__instance); @@ -127,7 +127,7 @@ public static class LobbyViewPanePatches __instance.rolesTabButton.SelectButton(false); ModifiersTabButton?.SelectButton(true); - var filteredGroups = SelectedMod.OptionGroups + var filteredGroups = SelectedMod.InternalOptionGroups .Where(x => x.GroupVisible() && (x.ShowInModifiersMenu || x.OptionableType?.IsAssignableTo(typeof(BaseModifier))==true)); DrawOptions(__instance, filteredGroups); return false; @@ -142,7 +142,7 @@ public static class LobbyViewPanePatches return true; } - var filteredGroups = SelectedMod.OptionGroups + var filteredGroups = SelectedMod.InternalOptionGroups .Where(x => x.OptionableType == null && x.GroupVisible.Invoke()); DrawOptions(__instance, filteredGroups); return false; @@ -250,7 +250,7 @@ public static class LobbyViewPanePatches var list = new List(); - var roleGroups = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values.OfType() + var roleGroups = GameSettingMenuPatches.SelectedMod?.InternalRoles.Values.OfType() .ToLookup(x => x.RoleOptionsGroup); if (roleGroups is null) @@ -315,7 +315,7 @@ public static class LobbyViewPanePatches viewSettingsInfoPanelRoleVariant.transform.localScale = Vector3.one; viewSettingsInfoPanelRoleVariant.transform.localPosition = new Vector3(num2, num, -2f); - var advancedRoleOptions = SelectedMod.OptionGroups + var advancedRoleOptions = SelectedMod.InternalOptionGroups .Where(x => x.OptionableType == customRole.GetType()) .SelectMany(x => x.Options) .ToList(); @@ -402,7 +402,7 @@ public static class LobbyViewPanePatches return 0; } - var role = SelectedMod.CustomRoles.Values.FirstOrDefault(x => x.GetType() == roleType); + var role = SelectedMod.InternalRoles.Values.FirstOrDefault(x => x.GetType() == roleType); if (role == null) { @@ -425,7 +425,7 @@ public static class LobbyViewPanePatches var num = viewPanel.yPosStart; var num2 = 1.08f; - var filteredOptions = SelectedMod.OptionGroups + var filteredOptions = SelectedMod.InternalOptionGroups .Where(x => x.OptionableType == roleType) .SelectMany(x=>x.Options) .ToList(); diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index 9fe48d7..791391d 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -67,7 +67,7 @@ public static class RoleSettingMenuPatches var num3 = 0; - var roleGroups = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values.OfType() + var roleGroups = GameSettingMenuPatches.SelectedMod?.InternalRoles.Values.OfType() .ToLookup(x => x.RoleOptionsGroup); if (roleGroups is null) @@ -310,7 +310,7 @@ public static class RoleSettingMenuPatches var num = hasImage ? -0.872f : -1; // TODO: create sub groups under the role settings. - var filteredOptions = GameSettingMenuPatches.SelectedMod?.OptionGroups + var filteredOptions = GameSettingMenuPatches.SelectedMod?.InternalOptionGroups .Where(x=> x.OptionableType == role.GetType()) .SelectMany(x=>x.Options) .ToList() ?? []; @@ -474,7 +474,7 @@ public static class RoleSettingMenuPatches roleOptionSetting.titleText.horizontalAlignment = HorizontalAlignmentOptions.Left; if (GameSettingMenuPatches.SelectedMod is null || - GameSettingMenuPatches.SelectedMod.OptionGroups + GameSettingMenuPatches.SelectedMod.InternalOptionGroups .Exists(x => x.OptionableType == role.GetType())) { var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform); @@ -506,7 +506,7 @@ public static class RoleSettingMenuPatches roleOptionSetting.ChancePlusBtn.gameObject.SetActive(false); } - if (index < GameSettingMenuPatches.SelectedMod?.CustomRoles.Count - 1) + if (index < GameSettingMenuPatches.SelectedMod?.InternalRoles.Count - 1) { ScrollerNum -= 0.43f; } diff --git a/MiraAPI/PluginLoading/MiraPluginInfo.cs b/MiraAPI/PluginLoading/MiraPluginInfo.cs index 68d3ff0..4a454a6 100644 --- a/MiraAPI/PluginLoading/MiraPluginInfo.cs +++ b/MiraAPI/PluginLoading/MiraPluginInfo.cs @@ -6,7 +6,9 @@ using MiraAPI.GameModes; using MiraAPI.GameOptions; using MiraAPI.Hud; using MiraAPI.Modifiers; +using MiraAPI.Presets; +// ReSharper disable UnusedAutoPropertyAccessor.Global namespace MiraAPI.PluginLoading; /// @@ -23,61 +25,58 @@ public class MiraPluginInfo } /// - /// Get a read only collection of this plugin's modifiers. This is probably not safe because they are uninitialized. + /// Gets a read only collection of this plugin's modifiers. This is not safe for modifiers with constructors. /// - /// Readonly collection of modifiers. - public IReadOnlyCollection GetModifiers() - { - return Modifiers.AsReadOnly(); - } + public IReadOnlyCollection Modifiers { get; private set; } = null!; /// - /// Get a read only collection of this plugin's Option Groups. + /// Gets a read only collection of this plugin's Option Groups. /// - /// Readonly collection of option groups. - public IReadOnlyCollection GetOptionGroups() - { - return OptionGroups.AsReadOnly(); - } + public IReadOnlyCollection OptionGroups { get; private set; } = null!; /// /// Gets a read only collection of this plugin's options. /// - /// Read only collection of options. - public IReadOnlyCollection GetOptions() - { - return Options.AsReadOnly(); - } + public IReadOnlyCollection Options { get; private set; } = null!; /// /// Gets a read only dictionary of Role IDs and the RoleBehaviour object they are associated with. /// - /// Read only dictionary of IDs and Roles. - public ReadOnlyDictionary GetRoles() - { - return new ReadOnlyDictionary(CustomRoles); - } + public ReadOnlyDictionary Roles { get; private set; } = null!; /// /// Gets a read only collection of this plugin's custom buttons. /// - /// Read only collection of buttons. - public IReadOnlyCollection GetButtons() + public IReadOnlyCollection Buttons { get; private set; } = null!; + + /// + /// Gets a read only dictionary of this plugin's custom game modes. + /// + public IReadOnlyCollection Presets { get; internal set; } = null!; + + internal void SavePublicCollections() { - return Buttons.AsReadOnly(); + Presets = [..InternalPresets]; + Modifiers = [..InternalModifiers]; + OptionGroups = [..InternalOptionGroups]; + Options = [..InternalOptions]; + Roles = new ReadOnlyDictionary(InternalRoles); + Buttons = [..InternalButtons]; } - internal List OptionGroups { get; } = []; + internal List InternalPresets { get; } = []; - internal List Options { get; } = []; + internal List InternalOptionGroups { get; } = []; - internal List Modifiers { get; } = []; + internal List InternalOptions { get; } = []; - internal Dictionary CustomRoles { get; } = []; + internal List InternalModifiers { get; } = []; - internal Dictionary GameModes { get; } = []; + internal Dictionary InternalRoles { get; } = []; - internal List Buttons { get; } = []; + internal Dictionary InternalGameModes { get; } = []; + + internal List InternalButtons { get; } = []; /// /// Gets the plugin's ID, as defined in the plugin's BepInEx metadata. diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index 885414d..e1f3f4b 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -12,6 +12,7 @@ using MiraAPI.GameOptions; using MiraAPI.GameOptions.Attributes; using MiraAPI.Hud; using MiraAPI.Modifiers; +using MiraAPI.Presets; using MiraAPI.Roles; using MiraAPI.Utilities; using Reactor.Networking; @@ -26,8 +27,9 @@ public sealed class MiraPluginManager { private readonly Dictionary _registeredPlugins = []; - internal MiraPluginInfo[] RegisteredPlugins() => [.. _registeredPlugins.Values]; - internal Dictionary> QueuedRoleRegistrations { get; } = new(); + internal MiraPluginInfo[] RegisteredPlugins { get; private set; } = null!; + + internal Dictionary> QueuedRoleRegistrations { get; } = []; internal static MiraPluginManager Instance { get; private set; } = new(); internal void Initialize() @@ -96,17 +98,25 @@ public sealed class MiraPluginManager RegisterColorClasses(type); } - info.OptionGroups.Sort((x, y) => x.GroupPriority.CompareTo(y.GroupPriority)); + info.InternalOptionGroups.Sort((x, y) => x.GroupPriority.CompareTo(y.GroupPriority)); QueuedRoleRegistrations.Add(info, roles); _registeredPlugins.Add(assembly, info); + + info.SavePublicCollections(); + PresetManager.CreateDefaultPreset(info); + PresetManager.LoadPresets(info); Logger.Info($"Registering mod {pluginInfo.Metadata.GUID} with Mira API."); }; IL2CPPChainloader.Instance.Finished += PaletteManager.RegisterAllColors; IL2CPPChainloader.Instance.Finished += MiraEventManager.SortAllHandlers; IL2CPPChainloader.Instance.Finished += () => { + // Save all buttons into a read-only collection for easy access CustomButtonManager.Buttons = new ReadOnlyCollection(CustomButtonManager.CustomButtons); + + // Cache all the registered plugins into an array for easy access + RegisteredPlugins = [.._registeredPlugins.Values]; }; } diff --git a/MiraAPI/Presets/OptionPreset.cs b/MiraAPI/Presets/OptionPreset.cs index d16f5f0..7ba935e 100644 --- a/MiraAPI/Presets/OptionPreset.cs +++ b/MiraAPI/Presets/OptionPreset.cs @@ -1,6 +1,7 @@ using System.Linq; using BepInEx.Configuration; using MiraAPI.PluginLoading; +using UnityEngine; namespace MiraAPI.Presets; @@ -29,6 +30,11 @@ public class OptionPreset /// public ConfigFile PresetConfig { get; } + /// + /// Gets or sets the button associated with the preset in the UI. + /// + public GameObject? PresetButton { get; set; } + /// /// Initializes a new instance of the class with the specified name and configuration file. /// diff --git a/MiraAPI/Presets/PresetManager.cs b/MiraAPI/Presets/PresetManager.cs index 7a8dc2b..a711127 100644 --- a/MiraAPI/Presets/PresetManager.cs +++ b/MiraAPI/Presets/PresetManager.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using System.IO; +using System.IO; +using System.Linq; using BepInEx.Configuration; using MiraAPI.PluginLoading; using UnityEngine; @@ -16,10 +16,23 @@ public static class PresetManager /// public static string PresetDirectory { get; } = Path.Join(Application.persistentDataPath, "mira_presets"); - /// - /// Gets the list of available presets for the game options. - /// - public static List Presets { get; } = []; + internal static void CreateDefaultPreset(MiraPluginInfo plugin) + { + var presetPath = Path.Join(PresetManager.PresetDirectory, plugin.PluginId); + if (!Directory.Exists(presetPath)) + { + Directory.CreateDirectory(presetPath); + } + + var presetConfig = new ConfigFile(Path.Join(presetPath, "Default.cfg"), true); + + foreach (var option in plugin.InternalOptions.Where(x => x.IncludeInPreset)) + { + option.SaveToPreset(presetConfig, true); + } + + presetConfig.Save(); + } /// /// Loads the presets for the specified plugin by reading the configuration files from the preset directory. @@ -27,7 +40,7 @@ public static class PresetManager /// >The plugin for which the presets should be loaded. public static void LoadPresets(MiraPluginInfo plugin) { - Presets.Clear(); + plugin.InternalPresets.Clear(); if (!Directory.Exists(PresetDirectory)) { Directory.CreateDirectory(PresetDirectory); @@ -43,7 +56,8 @@ public static class PresetManager { var presetName = Path.GetFileNameWithoutExtension(file); var presetConfig = new ConfigFile(file, true); - Presets.Add(new OptionPreset(presetName, plugin, presetConfig)); + plugin.InternalPresets.Add(new OptionPreset(presetName, plugin, presetConfig)); } + plugin.Presets = [.. plugin.InternalPresets]; } } diff --git a/MiraAPI/Roles/CustomRoleManager.cs b/MiraAPI/Roles/CustomRoleManager.cs index 6552641..01b3289 100644 --- a/MiraAPI/Roles/CustomRoleManager.cs +++ b/MiraAPI/Roles/CustomRoleManager.cs @@ -70,7 +70,7 @@ public static class CustomRoleManager continue; } - pluginInfo.CustomRoles.Add((ushort)role.Role, role); + pluginInfo.InternalRoles.Add((ushort)role.Role, role); } } @@ -156,7 +156,7 @@ public static class CustomRoleManager /// A MiraPluginInfo object representing the parent mod of the role. public static MiraPluginInfo FindParentMod(ICustomRole role) { - return MiraPluginManager.Instance.RegisteredPlugins().First(plugin => plugin.CustomRoles.ContainsValue(role as RoleBehaviour ?? throw new InvalidOperationException())); + return MiraPluginManager.Instance.RegisteredPlugins.First(plugin => plugin.InternalRoles.ContainsValue(role as RoleBehaviour ?? throw new InvalidOperationException())); } /// @@ -231,7 +231,7 @@ public static class CustomRoleManager // we dont know how other plugins handle their configs // this way, all the options are saved at once, instead of one by one var oldConfigSetting = new Dictionary(); - foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins()) + foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins) { oldConfigSetting.Add(plugin, plugin.PluginConfig.SaveOnConfigSet); plugin.PluginConfig.SaveOnConfigSet = false; @@ -266,7 +266,7 @@ public static class CustomRoleManager } } - foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins()) + foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins) { plugin.PluginConfig.Save(); plugin.PluginConfig.SaveOnConfigSet = oldConfigSetting[plugin];