From c60537cbd139ea45832e1fb801879b056b914e52 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 17 Mar 2025 09:02:24 -0400 Subject: [PATCH 01/28] add comments to imoddedoption --- MiraAPI/GameOptions/IModdedOption.cs | 58 ++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/MiraAPI/GameOptions/IModdedOption.cs b/MiraAPI/GameOptions/IModdedOption.cs index f40f3cf..86baf9d 100644 --- a/MiraAPI/GameOptions/IModdedOption.cs +++ b/MiraAPI/GameOptions/IModdedOption.cs @@ -6,18 +6,76 @@ using UnityEngine; namespace MiraAPI.GameOptions; +/// +/// Interface for modded options. +/// public interface IModdedOption { + /// + /// Gets the unique identifier for the option. + /// uint Id { get; } + + /// + /// Gets the title of the option. + /// string Title { get; } + + /// + /// Gets the StringName for the option, used for localization. + /// StringNames StringName { get; } + + /// + /// Gets or sets the MiraPlugin that created this option. + /// IMiraPlugin? ParentMod { get; set; } + + /// + /// Gets the game setting data for the option. + /// BaseGameSetting Data { get; } + + /// + /// Gets the OptionBehaviour object of the option. + /// OptionBehaviour? OptionBehaviour { get; } + + /// + /// Gets or sets the visibility function for the option. + /// Func Visible { get; set; } + + /// + /// Gets or sets the ConfigDefinition for the option, used for BepInEx configuration. + /// ConfigDefinition? ConfigDefinition { get; set; } + + /// + /// Creates the option behaviour for the modded option. + /// + /// The ToggleOption template. + /// The NumberOption template. + /// The StringOption template. + /// >The Transform container for the option. + /// >The created OptionBehaviour object. OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container); + + /// + /// Gets the value as a float. + /// + /// >The value of the option as a float. float GetFloatData(); + + /// + /// Gets the NetData for the option, used for network synchronization. + /// + /// >Returns the NetData object for the option. NetData GetNetData(); + + /// + /// Handles incoming network data for the option. + /// + /// >The byte array representing the network data. void HandleNetData(byte[] data); } From 22487264500cb29a19d584dac97c0c790b002617 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Mon, 17 Mar 2025 09:12:26 -0400 Subject: [PATCH 02/28] IncludeInPreset property --- MiraAPI/GameOptions/IModdedOption.cs | 5 ++ .../OptionTypes/ModdedEnumOption.cs | 18 ++--- .../OptionTypes/ModdedNumberOption.cs | 4 +- .../GameOptions/OptionTypes/ModdedOption.cs | 65 ++++++------------- .../OptionTypes/ModdedToggleOption.cs | 3 +- 5 files changed, 39 insertions(+), 56 deletions(-) diff --git a/MiraAPI/GameOptions/IModdedOption.cs b/MiraAPI/GameOptions/IModdedOption.cs index 86baf9d..e002912 100644 --- a/MiraAPI/GameOptions/IModdedOption.cs +++ b/MiraAPI/GameOptions/IModdedOption.cs @@ -46,6 +46,11 @@ public interface IModdedOption /// Func Visible { get; set; } + /// + /// Gets or sets a value indicating whether the option should be included with presets. + /// + bool IncludeInPreset { get; set; } + /// /// Gets or sets the ConfigDefinition for the option, used for BepInEx configuration. /// diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs index ff422f5..18f13c6 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs @@ -18,7 +18,7 @@ public class ModdedEnumOption : ModdedOption /// /// Gets the string values of the enum. /// - public string[]? Values { get; } + public string[] Values { get; } /// /// Initializes a new instance of the class. @@ -27,7 +27,8 @@ public class ModdedEnumOption : ModdedOption /// The default value as an int. /// The Enum type. /// An option list of string values to use in place of the enum name. - public ModdedEnumOption(string title, int defaultValue, Type enumType, string[]? values = null) : base(title, defaultValue) + /// Whether to include this option in the preset or not. + public ModdedEnumOption(string title, int defaultValue, Type enumType, string[]? values = null, bool includeInPreset=true) : base(title, defaultValue, includeInPreset) { Values = values ?? Enum.GetNames(enumType); Data = ScriptableObject.CreateInstance(); @@ -37,7 +38,7 @@ public class ModdedEnumOption : ModdedOption data.Type = global::OptionTypes.String; data.Values = values is null ? Enum.GetNames(enumType).Select(CustomStringName.CreateAndRegister).ToArray() - : values.Select(CustomStringName.CreateAndRegister).ToArray(); + : [.. values.Select(CustomStringName.CreateAndRegister)]; data.Index = Value; } @@ -87,7 +88,7 @@ public class ModdedEnumOption : ModdedOption /// protected override void OnValueChanged(int newValue) { - HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data?.GetValueString(newValue), false); + HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data.GetValueString(newValue), false); if (!OptionBehaviour) { return; @@ -109,7 +110,7 @@ public class ModdedEnumOption : ModdedOption where T : Enum /// /// Gets the string values of the enum. /// - public string[]? Values { get; } + public string[] Values { get; } /// /// Initializes a new instance of the class. @@ -117,7 +118,8 @@ public class ModdedEnumOption : ModdedOption where T : Enum /// The title of the option. /// The default value as an int. /// An option list of string values to use in place of the enum name. - public ModdedEnumOption(string title, T defaultValue, string[]? values = null) : base(title, defaultValue) + /// Whether to include this option in the preset or not. + public ModdedEnumOption(string title, T defaultValue, string[]? values = null, bool includeInPreset=true) : base(title, defaultValue, includeInPreset) { Values = values ?? Enum.GetNames(typeof(T)); Data = ScriptableObject.CreateInstance(); @@ -127,9 +129,9 @@ public class ModdedEnumOption : ModdedOption where T : Enum data.Type = global::OptionTypes.String; data.Values = values is null ? Enum.GetNames(typeof(T)).Select(CustomStringName.CreateAndRegister).ToArray() - : values.Select(CustomStringName.CreateAndRegister).ToArray(); + : [.. values.Select(CustomStringName.CreateAndRegister)]; - data.Index = Convert.ToInt32(Value); + data.Index = Convert.ToInt32(Value, NumberFormatInfo.InvariantInfo); } /// diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs index 4d38c04..a01ffe8 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs @@ -48,6 +48,7 @@ public class ModdedNumberOption : ModdedOption /// The suffix type. /// Optional format string for the option screen. /// Whether zero is infinity or not. + /// Whether to include this option in the preset or not. public ModdedNumberOption( string title, float defaultValue, @@ -56,7 +57,8 @@ public class ModdedNumberOption : ModdedOption float increment, MiraNumberSuffixes suffixType, string? formatString = null, - bool zeroInfinity = false) : base(title, defaultValue) + bool zeroInfinity = false, + bool includeInPreset = true) : base(title, defaultValue, includeInPreset) { Min = min; Max = max; diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs index 670fb3c..2b61e2b 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs @@ -16,29 +16,19 @@ public abstract class ModdedOption : IModdedOption { private IMiraPlugin? _parentMod; - /// - /// Gets the unique identifier of the option. - /// + /// public uint Id { get; } - /// - /// Gets the title of the option. - /// + /// public string Title { get; } - /// - /// Gets the StringName object of the option. - /// + /// public StringNames StringName { get; } - /// - /// Gets the BaseGameSetting data of the option. - /// - public BaseGameSetting Data { get; protected init; } + /// + public BaseGameSetting Data { get; protected init; } = null!; - /// - /// Gets or sets the parent mod of the option. - /// + /// public IMiraPlugin? ParentMod { get => _parentMod; @@ -66,19 +56,16 @@ public abstract class ModdedOption : IModdedOption /// public Action? ChangedEvent { get; set; } - /// - /// Gets or sets the visibility of the option. - /// + /// public Func Visible { get; set; } - /// - /// Gets or sets the option behaviour of the option. - /// + /// + public bool IncludeInPreset { get; set; } + + /// public OptionBehaviour? OptionBehaviour { get; protected set; } - /// - /// Gets or sets the config definition of the option. - /// + /// public ConfigDefinition? ConfigDefinition { get => _configDefinition; @@ -96,7 +83,8 @@ public abstract class ModdedOption : IModdedOption /// /// The option title. /// The default value. - protected ModdedOption(string title, T defaultValue) + /// Whether to include the option in the preset. + protected ModdedOption(string title, T defaultValue, bool includeInPreset = true) { Id = ModdedOptionsManager.NextId; Title = title; @@ -104,6 +92,7 @@ public abstract class ModdedOption : IModdedOption Value = defaultValue; StringName = CustomStringName.CreateAndRegister(Title); Visible = () => true; + IncludeInPreset = includeInPreset; } internal void ValueChanged(OptionBehaviour optionBehaviour) @@ -138,22 +127,13 @@ public abstract class ModdedOption : IModdedOption OnValueChanged(newValue); } - /// - /// Gets the float data of the option. - /// - /// A float object representing the option's value. + /// public abstract float GetFloatData(); - /// - /// Gets the net data of the option. - /// - /// A NetData object representing this option's data. + /// public abstract NetData GetNetData(); - /// - /// Handles incoming net data. - /// - /// The NetData's byte array. + /// public abstract void HandleNetData(byte[] data); /// @@ -169,14 +149,7 @@ public abstract class ModdedOption : IModdedOption /// The value. public abstract T GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour); - /// - /// Creates the option behaviour. - /// - /// The ToggleOption prefab. - /// The NumberOption prefab. - /// The StringOption prefab. - /// The options container. - /// A new OptionBehaviour for this modded option. + /// public abstract OptionBehaviour CreateOption( ToggleOption toggleOpt, NumberOption numberOpt, diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs index 3495e86..26ecc74 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs @@ -15,7 +15,8 @@ public class ModdedToggleOption : ModdedOption /// /// The option title. /// The default value. - public ModdedToggleOption(string title, bool defaultValue) : base(title, defaultValue) + /// Whether to include this option in the preset or not. + public ModdedToggleOption(string title, bool defaultValue, bool includeInPreset=true) : base(title, defaultValue, includeInPreset) { Data = ScriptableObject.CreateInstance(); From 5b608f5b3a2c4b18b10b23895261bb94032d8b69 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:21:18 -0400 Subject: [PATCH 03/28] preset WIP --- .../Patches/Options/GamePresetsTabPatches.cs | 27 +++++++++ MiraAPI/Presets/OptionPreset.cs | 56 +++++++++++++++++++ MiraAPI/Presets/PresetManager.cs | 49 ++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 MiraAPI/Patches/Options/GamePresetsTabPatches.cs create mode 100644 MiraAPI/Presets/OptionPreset.cs create mode 100644 MiraAPI/Presets/PresetManager.cs diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs new file mode 100644 index 0000000..ca8f4ae --- /dev/null +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -0,0 +1,27 @@ +using System.Linq; +using HarmonyLib; +using MiraAPI.Presets; + +namespace MiraAPI.Patches.Options; + +internal static class GamePresetsTabPatches +{ + [HarmonyPatch(typeof(GamePresetsTab), nameof(GamePresetsTab.OpenMenu))] + public static class GamePresetsTabOnEnablePatch + { + public static void Postfix(GamePresetsTab __instance) + { + if (GameSettingMenuPatches.SelectedModIdx == 0) + { + return; + } + + __instance.StandardPresetButton.gameObject.SetActive(false); + __instance.SecondPresetButton.gameObject.SetActive(false); + + foreach (var preset in PresetManager.Presets.Where(x=>x.Plugin==GameSettingMenuPatches.SelectedMod)) + { + } + } + } +} diff --git a/MiraAPI/Presets/OptionPreset.cs b/MiraAPI/Presets/OptionPreset.cs new file mode 100644 index 0000000..d16f5f0 --- /dev/null +++ b/MiraAPI/Presets/OptionPreset.cs @@ -0,0 +1,56 @@ +using System.Linq; +using BepInEx.Configuration; +using MiraAPI.PluginLoading; + +namespace MiraAPI.Presets; + +/// +/// Represents a preset of game options that can be applied to the game. +/// +public class OptionPreset +{ + /// + /// Gets the name of the preset. + /// + public string Name { get; } + + /// + /// Gets the plugin associated with the preset. + /// + public MiraPluginInfo Plugin { get; } + + /// + /// Gets the configuration file of the plugin associated with the preset. + /// + public ConfigFile PluginConfig { get; } + + /// + /// Gets the configuration file for the preset. + /// + public ConfigFile PresetConfig { get; } + + /// + /// Initializes a new instance of the class with the specified name and configuration file. + /// + /// >The name of the preset. + /// The plugin associated with the preset. + /// >The configuration file for the preset. + public OptionPreset(string name, MiraPluginInfo plugin, ConfigFile presetConfig) + { + Name = name; + Plugin = plugin; + PluginConfig = plugin.PluginConfig; + PresetConfig = presetConfig; + } + + /// + /// Loads the preset by applying the values from the preset configuration to the plugin configuration. + /// + public void LoadPreset() + { + foreach (var option in PresetConfig.Where(x=>PluginConfig.ContainsKey(x.Key))) + { + PluginConfig[option.Key].BoxedValue = option.Value.BoxedValue; + } + } +} diff --git a/MiraAPI/Presets/PresetManager.cs b/MiraAPI/Presets/PresetManager.cs new file mode 100644 index 0000000..7a8dc2b --- /dev/null +++ b/MiraAPI/Presets/PresetManager.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.IO; +using BepInEx.Configuration; +using MiraAPI.PluginLoading; +using UnityEngine; + +namespace MiraAPI.Presets; + +/// +/// Provides functionality to manage and load game option presets for plugins. +/// +public static class PresetManager +{ + /// + /// Gets the directory where the presets are stored. + /// + 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; } = []; + + /// + /// Loads the presets for the specified plugin by reading the configuration files from the preset directory. + /// + /// >The plugin for which the presets should be loaded. + public static void LoadPresets(MiraPluginInfo plugin) + { + Presets.Clear(); + if (!Directory.Exists(PresetDirectory)) + { + Directory.CreateDirectory(PresetDirectory); + } + + var pluginPresetPath = Path.Join(PresetDirectory, plugin.PluginId); + if (!Directory.Exists(pluginPresetPath)) + { + Directory.CreateDirectory(pluginPresetPath); + } + + foreach (var file in Directory.GetFiles(pluginPresetPath, "*.cfg")) + { + var presetName = Path.GetFileNameWithoutExtension(file); + var presetConfig = new ConfigFile(file, true); + Presets.Add(new OptionPreset(presetName, plugin, presetConfig)); + } + } +} From 38f72c42a3ad9b9fc37b2e451ff158d3291372b3 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 23 Mar 2025 13:44:40 -0400 Subject: [PATCH 04/28] presets v1 --- MiraAPI/GameOptions/IModdedOption.cs | 7 ++ MiraAPI/GameOptions/ModdedOptionsManager.cs | 10 +- .../GameOptions/OptionTypes/ModdedOption.cs | 19 ++++ MiraAPI/Hud/CustomButtonManager.cs | 2 +- MiraAPI/Modifiers/BaseModifier.cs | 4 +- MiraAPI/Modifiers/ModifierManager.cs | 2 +- MiraAPI/Patches/Freeplay/TaskAdderPatches.cs | 14 +-- .../Patches/Options/GameOptionsMenuPatch.cs | 20 ++-- .../Patches/Options/GamePresetsTabPatches.cs | 89 +++++++++++++-- .../Patches/Options/GameSettingMenuPatches.cs | 103 +++++++++--------- .../Patches/Options/LobbyViewPanePatches.cs | 18 +-- .../Patches/Options/RoleSettingMenuPatches.cs | 8 +- MiraAPI/PluginLoading/MiraPluginInfo.cs | 61 +++++------ MiraAPI/PluginLoading/MiraPluginManager.cs | 16 ++- MiraAPI/Presets/OptionPreset.cs | 6 + MiraAPI/Presets/PresetManager.cs | 30 +++-- MiraAPI/Roles/CustomRoleManager.cs | 8 +- 17 files changed, 275 insertions(+), 142 deletions(-) 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]; From 9e8c05f21da0d8ec394cb09da0e37171724c170c Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 23 Mar 2025 15:01:31 -0400 Subject: [PATCH 05/28] text input WIP --- MiraAPI/Hud/Tools/TextInput.cs | 39 +++++++++++++++++++ .../Patches/Options/GamePresetsTabPatches.cs | 14 +++++++ 2 files changed, 53 insertions(+) create mode 100644 MiraAPI/Hud/Tools/TextInput.cs diff --git a/MiraAPI/Hud/Tools/TextInput.cs b/MiraAPI/Hud/Tools/TextInput.cs new file mode 100644 index 0000000..62a2051 --- /dev/null +++ b/MiraAPI/Hud/Tools/TextInput.cs @@ -0,0 +1,39 @@ +using System; +using Reactor.Utilities.Attributes; +using UnityEngine; +using UnityEngine.UI; + +namespace MiraAPI.Hud.Tools; + +[RegisterInIl2Cpp] +public class TextInput : MonoBehaviour +{ + public TextBoxTMP textBox; + + public Action? OnTextChanged; + + public delegate bool ValidateInput(string input); + public ValidateInput? Validate; + + public static void Create(Action? onTextChanged = null, ValidateInput? validate = null) + { + var source = FindObjectOfType(true); + var input = Instantiate(source); + var inputBox = input.gameObject.AddComponent(); + inputBox.textBox = source.nameSource; + inputBox.OnTextChanged = onTextChanged; + inputBox.Validate = validate; + + inputBox.textBox.OnChange = new Button.ButtonClickedEvent(); + inputBox.textBox.OnChange.AddListener((UnityEngine.Events.UnityAction)(() => + { + var text = inputBox.textBox.text; + if (inputBox.Validate != null && !inputBox.Validate(text)) + { + return; + } + + inputBox.OnTextChanged?.Invoke(text); + })); + } +} diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index c068a52..07aca24 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -6,6 +6,7 @@ using Reactor.Utilities.Extensions; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; +using Object = UnityEngine.Object; namespace MiraAPI.Patches.Options; @@ -24,6 +25,19 @@ internal static class GamePresetsTabPatches return; } + var saveButton = Object.Instantiate(prefab, __instance.transform); + saveButton.buttonText.text = "Save Settings"; + if (saveButton.buttonText.TryGetComponent(out var textTranslator)) + { + textTranslator.DestroyImmediate(); + } + saveButton.OnClick = new Button.ButtonClickedEvent(); + saveButton.OnClick.AddListener( + (UnityAction)(() => + { + + })); + var holder = new GameObject("PresetHolder"); holder.transform.SetParent(__instance.transform, false); holder.transform.localPosition = new Vector3(-1.7f, 1.7f, 0); From bf5dadc6a097b0a595b8cc8c9fa57bb1fb041cd5 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 23 Mar 2025 15:34:20 -0400 Subject: [PATCH 06/28] that def doesnt work --- MiraAPI/Hud/Tools/TextInput.cs | 39 ------------------- .../Patches/Options/GamePresetsTabPatches.cs | 1 - 2 files changed, 40 deletions(-) delete mode 100644 MiraAPI/Hud/Tools/TextInput.cs diff --git a/MiraAPI/Hud/Tools/TextInput.cs b/MiraAPI/Hud/Tools/TextInput.cs deleted file mode 100644 index 62a2051..0000000 --- a/MiraAPI/Hud/Tools/TextInput.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using Reactor.Utilities.Attributes; -using UnityEngine; -using UnityEngine.UI; - -namespace MiraAPI.Hud.Tools; - -[RegisterInIl2Cpp] -public class TextInput : MonoBehaviour -{ - public TextBoxTMP textBox; - - public Action? OnTextChanged; - - public delegate bool ValidateInput(string input); - public ValidateInput? Validate; - - public static void Create(Action? onTextChanged = null, ValidateInput? validate = null) - { - var source = FindObjectOfType(true); - var input = Instantiate(source); - var inputBox = input.gameObject.AddComponent(); - inputBox.textBox = source.nameSource; - inputBox.OnTextChanged = onTextChanged; - inputBox.Validate = validate; - - inputBox.textBox.OnChange = new Button.ButtonClickedEvent(); - inputBox.textBox.OnChange.AddListener((UnityEngine.Events.UnityAction)(() => - { - var text = inputBox.textBox.text; - if (inputBox.Validate != null && !inputBox.Validate(text)) - { - return; - } - - inputBox.OnTextChanged?.Invoke(text); - })); - } -} diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index 07aca24..bffc373 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -35,7 +35,6 @@ internal static class GamePresetsTabPatches saveButton.OnClick.AddListener( (UnityAction)(() => { - })); var holder = new GameObject("PresetHolder"); From 429b64fc033a9fc5e322e3dcb5ee442dfe637632 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 23 Mar 2025 17:28:22 -0400 Subject: [PATCH 07/28] preset v2 --- MiraAPI/Hud/InputBox.cs | 91 ++++++++++ .../Patches/Options/GamePresetsTabPatches.cs | 166 +++++++++++++++--- .../Patches/Options/GameSettingMenuPatches.cs | 1 + MiraAPI/Presets/PresetManager.cs | 8 + 4 files changed, 239 insertions(+), 27 deletions(-) create mode 100644 MiraAPI/Hud/InputBox.cs diff --git a/MiraAPI/Hud/InputBox.cs b/MiraAPI/Hud/InputBox.cs new file mode 100644 index 0000000..b1b1466 --- /dev/null +++ b/MiraAPI/Hud/InputBox.cs @@ -0,0 +1,91 @@ +using System; +using Reactor.Utilities.Attributes; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.UI; + +namespace MiraAPI.Hud; + +[RegisterInIl2Cpp] +public class InputBox : MonoBehaviour +{ + private GameObject dialog; + private InputField inputField; + + public void CreateDialog(string title, Action onSubmit) + { + // Create Canvas + var canvasObj = new GameObject("DialogCanvas"); + canvasObj.transform.SetParent(transform); + canvasObj.layer = LayerMask.NameToLayer("UI"); + var canvas = canvasObj.AddComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + canvasObj.AddComponent().uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; + canvasObj.AddComponent(); + + // Create Panel + var panelObj = new GameObject("DialogPanel"); + panelObj.transform.SetParent(canvasObj.transform); + var panelRect = panelObj.AddComponent(); + var img = panelObj.AddComponent(); + img.color = new Color(0, 0, 0, 0.6f); // Semi-transparent black + img.raycastTarget = true; + + panelRect.sizeDelta = new Vector2(200, 200); + panelRect.anchoredPosition = Vector2.zero; + + // Create Title Text + var titleText = CreateText(panelObj, title); + titleText.alignment = TextAnchor.MiddleCenter; + titleText.fontSize = 24; + titleText.color = Color.white; + var titleRect = titleText.GetComponent(); + titleRect.sizeDelta = new Vector2(200, 40); + titleRect.anchoredPosition = new Vector2(0, 70); + + // Create InputField + var inputObj = new GameObject("InputField"); + inputObj.transform.SetParent(panelObj.transform); + var inputRect = inputObj.AddComponent(); + inputRect.sizeDelta = new Vector2(150, 50); + inputRect.anchoredPosition = new Vector2(0, 30); + + inputField = inputObj.AddComponent(); + inputField.textComponent = CreateText(inputObj); + inputField.placeholder = CreateText(inputObj, "Enter text here..."); + + // Create Submit Button + var buttonObj = new GameObject("SubmitButton"); + buttonObj.transform.SetParent(panelObj.transform); + var button = buttonObj.AddComponent public void LoadPreset() { - foreach (var option in PresetConfig.Where(x=>PluginConfig.ContainsKey(x.Key))) + PresetConfig.Reload(); + foreach (var option in Plugin.InternalOptions) { - PluginConfig[option.Key].BoxedValue = option.Value.BoxedValue; + option.LoadFromPreset(PresetConfig); } } } diff --git a/MiraAPI/Presets/PresetManager.cs b/MiraAPI/Presets/PresetManager.cs index 033d8e7..732b602 100644 --- a/MiraAPI/Presets/PresetManager.cs +++ b/MiraAPI/Presets/PresetManager.cs @@ -2,6 +2,7 @@ using System.Linq; using BepInEx.Configuration; using MiraAPI.PluginLoading; +using Reactor.Utilities; using UnityEngine; namespace MiraAPI.Presets; @@ -14,17 +15,17 @@ public static class PresetManager /// /// Gets the directory where the presets are stored. /// - public static string PresetDirectory { get; } = Path.Join(Application.persistentDataPath, "mira_presets"); + public static string PresetDirectory { get; } = Path.GetFullPath("mira_presets", Application.persistentDataPath); internal static void CreateDefaultPreset(MiraPluginInfo plugin) { - var presetPath = Path.Join(PresetManager.PresetDirectory, plugin.PluginId); + var presetPath = Path.Combine(PresetDirectory, plugin.PluginId); if (!Directory.Exists(presetPath)) { Directory.CreateDirectory(presetPath); } - var presetConfig = new ConfigFile(Path.Join(presetPath, "Default.cfg"), true); + var presetConfig = new ConfigFile(Path.Combine(presetPath, "Default.cfg"), false); foreach (var option in plugin.InternalOptions.Where(x => x.IncludeInPreset)) { @@ -54,7 +55,7 @@ public static class PresetManager Directory.CreateDirectory(PresetDirectory); } - var pluginPresetPath = Path.Join(PresetDirectory, plugin.PluginId); + var pluginPresetPath = Path.Combine(PresetDirectory, plugin.PluginId); if (!Directory.Exists(pluginPresetPath)) { Directory.CreateDirectory(pluginPresetPath); @@ -62,8 +63,13 @@ public static class PresetManager foreach (var file in Directory.GetFiles(pluginPresetPath, "*.cfg")) { + Logger.Info($"Loading preset file {file}"); var presetName = Path.GetFileNameWithoutExtension(file); var presetConfig = new ConfigFile(file, true); + foreach (var option in plugin.InternalOptions.Where(x=>x.IncludeInPreset)) + { + option.Bind(presetConfig); + } plugin.InternalPresets.Add(new OptionPreset(presetName, plugin, presetConfig)); } plugin.Presets = [.. plugin.InternalPresets]; From 2c901bb9b9d4d68f6c91705548db6c21303dfdb3 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Fri, 2 May 2025 14:56:28 -0400 Subject: [PATCH 20/28] remove unused thingy --- MiraAPI/Patches/Options/GamePresetsTabPatches.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index 5d0ad56..376b4a0 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -25,8 +25,6 @@ internal static class GamePresetsTabPatches private static GameObject _presetHolder = null!; private static GridArrange _arrange = null!; - private static FileSystemWatcher? _watcher; - private static readonly object Lock = new(); public static void DivideSize(GameObject obj, float amount) From 6f1da2de9f03cc3f3896d1c97b9693b6599f5dcf Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Fri, 2 May 2025 19:25:03 -0400 Subject: [PATCH 21/28] add roles to presets --- .../GameOptions/OptionTypes/ModdedOption.cs | 10 +---- MiraAPI/Presets/OptionPreset.cs | 9 ++++- MiraAPI/Presets/PresetManager.cs | 11 ++++++ MiraAPI/Roles/ICustomRole.cs | 39 +++++++++++++++++++ 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs index cf5e900..94f6f3d 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs @@ -126,14 +126,8 @@ public abstract class ModdedOption : IModdedOption 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; + Bind(presetConfig); + presetConfig[ConfigDefinition].BoxedValue = saveDefault ? DefaultValue : Value; } /// diff --git a/MiraAPI/Presets/OptionPreset.cs b/MiraAPI/Presets/OptionPreset.cs index 3a551a4..7e07df5 100644 --- a/MiraAPI/Presets/OptionPreset.cs +++ b/MiraAPI/Presets/OptionPreset.cs @@ -1,5 +1,7 @@ -using BepInEx.Configuration; +using System.Linq; +using BepInEx.Configuration; using MiraAPI.PluginLoading; +using MiraAPI.Roles; using UnityEngine; namespace MiraAPI.Presets; @@ -58,5 +60,10 @@ public class OptionPreset { option.LoadFromPreset(PresetConfig); } + + foreach (var role in Plugin.InternalRoles.Values.OfType()) + { + role.LoadFromPreset(PresetConfig); + } } } diff --git a/MiraAPI/Presets/PresetManager.cs b/MiraAPI/Presets/PresetManager.cs index 732b602..e6c6460 100644 --- a/MiraAPI/Presets/PresetManager.cs +++ b/MiraAPI/Presets/PresetManager.cs @@ -2,6 +2,7 @@ using System.Linq; using BepInEx.Configuration; using MiraAPI.PluginLoading; +using MiraAPI.Roles; using Reactor.Utilities; using UnityEngine; @@ -32,6 +33,11 @@ public static class PresetManager option.SaveToPreset(presetConfig, true); } + foreach (var role in plugin.InternalRoles.Values.OfType()) + { + role.SaveToPreset(presetConfig); + } + presetConfig.Save(); } @@ -70,6 +76,11 @@ public static class PresetManager { option.Bind(presetConfig); } + + foreach (var role in plugin.InternalRoles.Values.OfType()) + { + role.BindConfig(presetConfig); + } plugin.InternalPresets.Add(new OptionPreset(presetName, plugin, presetConfig)); } plugin.Presets = [.. plugin.InternalPresets]; diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index abc0656..8f69e3d 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -84,6 +84,45 @@ public interface ICustomRole : IOptionable internal ConfigDefinition NumConfigDefinition => new("Roles", $"Num {GetType().FullName}"); internal ConfigDefinition ChanceConfigDefinition => new("Roles", $"Chance {GetType().FullName}"); + /// + /// Binds the configuration options for this role to the provided ConfigFile. + /// + /// The ConfigFile to bind the options to. + public virtual void BindConfig(ConfigFile config) + { + config.Bind(NumConfigDefinition, Configuration.DefaultRoleCount); + config.Bind(ChanceConfigDefinition, Configuration.DefaultChance); + } + + /// + /// Saves the role's configuration to a preset ConfigFile. + /// + /// The ConfigFile to save the preset configuration to. + /// Whether to use the default values for the configuration. + public virtual void SaveToPreset(ConfigFile presetConfig, bool useDefault=false) + { + BindConfig(presetConfig); + presetConfig[NumConfigDefinition].BoxedValue = useDefault ? Configuration.DefaultRoleCount : GetCount(); + presetConfig[ChanceConfigDefinition].BoxedValue = useDefault ? Configuration.DefaultChance : GetChance(); + } + + /// + /// Loads the role's configuration from a preset ConfigFile. + /// + /// The ConfigFile containing the preset configuration. + public virtual void LoadFromPreset(ConfigFile presetConfig) + { + if (presetConfig.TryGetEntry(NumConfigDefinition, out ConfigEntry numEntry)) + { + SetCount(numEntry.Value); + } + + if (presetConfig.TryGetEntry(ChanceConfigDefinition, out ConfigEntry chanceEntry)) + { + SetChance(chanceEntry.Value); + } + } + /// /// Gets the role chance option. /// From e0da3865677f820c3eefa981b4d1d1e66d506e3f Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Sun, 4 May 2025 18:10:34 -0400 Subject: [PATCH 22/28] fix lag and broken roles saving/loading --- .../Patches/Options/GamePresetsTabPatches.cs | 30 ++++++++++++------- MiraAPI/Presets/PresetManager.cs | 11 +++++-- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index 376b4a0..6a747bc 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -5,6 +5,7 @@ using BepInEx.Configuration; using HarmonyLib; using MiraAPI.PluginLoading; using MiraAPI.Presets; +using MiraAPI.Roles; using MiraAPI.Utilities.Assets; using Reactor.Utilities; using Reactor.Utilities.Extensions; @@ -235,13 +236,21 @@ internal static class GamePresetsTabPatches PresetManager.PresetDirectory, GameSettingMenuPatches.SelectedMod.PluginId, $"{name}.cfg"), - false); - foreach (var option in GameSettingMenuPatches.SelectedMod.InternalOptions.Where( - x => x.IncludeInPreset)) + false) + { + SaveOnConfigSet = false, + }; + + foreach (var option in GameSettingMenuPatches.SelectedMod.InternalOptions.Where(x => x.IncludeInPreset)) { option.SaveToPreset(presetFile); } + foreach (var role in GameSettingMenuPatches.SelectedMod.InternalRoles.Values.OfType().Where(x=>!x.Configuration.HideSettings)) + { + role.SaveToPreset(presetFile); + } + presetFile.Save(); Refresh(); }); @@ -324,15 +333,16 @@ internal static class GamePresetsTabPatches private static void Refresh() { + Logger.Error("Refreshing presets"); + if (GameSettingMenuPatches.SelectedMod == null) + { + Logger.Error("Selected mod is null, cannot refresh presets"); + return; + } lock (Lock) { - Logger.Error("Refreshing presets"); - foreach (var mod in MiraPluginManager.Instance.RegisteredPlugins) - { - PresetManager.LoadPresets(mod); - } - - GameSettingMenu.Instance.ChangeTab(0, false); // refresh the tab + PresetManager.LoadPresets(GameSettingMenuPatches.SelectedMod); } + GameSettingMenu.Instance.ChangeTab(0, false); // refresh the tab } } diff --git a/MiraAPI/Presets/PresetManager.cs b/MiraAPI/Presets/PresetManager.cs index e6c6460..2d4fb16 100644 --- a/MiraAPI/Presets/PresetManager.cs +++ b/MiraAPI/Presets/PresetManager.cs @@ -71,16 +71,23 @@ public static class PresetManager { Logger.Info($"Loading preset file {file}"); var presetName = Path.GetFileNameWithoutExtension(file); - var presetConfig = new ConfigFile(file, true); + var presetConfig = new ConfigFile(file, false) + { + SaveOnConfigSet = false, + }; + foreach (var option in plugin.InternalOptions.Where(x=>x.IncludeInPreset)) { option.Bind(presetConfig); } - foreach (var role in plugin.InternalRoles.Values.OfType()) + foreach (var role in plugin.InternalRoles.Values.OfType().Where(x=>!x.Configuration.HideSettings)) { role.BindConfig(presetConfig); } + + presetConfig.Save(); + plugin.InternalPresets.Add(new OptionPreset(presetName, plugin, presetConfig)); } plugin.Presets = [.. plugin.InternalPresets]; From 11ba3dae64be6de4f497e1b12bdcb2dd34c329fb Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Fri, 16 May 2025 19:24:47 -0400 Subject: [PATCH 23/28] save scroll position in roles menu --- .../Patches/Options/GameSettingMenuPatches.cs | 14 ++++++++--- .../Patches/Options/RoleSettingMenuPatches.cs | 24 ++++++++++++++----- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs index 623ae56..8993969 100644 --- a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs @@ -112,6 +112,8 @@ internal static class GameSettingMenuPatches passiveButton.OnClick.AddListener( (UnityAction)(() => { + RoleSettingMenuPatches.Scrolls[SelectedModIdx] = __instance.RoleSettingsTab.scrollBar.Inner.localPosition; + SelectedModIdx += 1; if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins.Length) { @@ -130,6 +132,8 @@ internal static class GameSettingMenuPatches backButton.gameObject.GetComponent().OnClick.AddListener( (UnityAction)(() => { + RoleSettingMenuPatches.Scrolls[SelectedModIdx] = __instance.RoleSettingsTab.scrollBar.Inner.localPosition; + SelectedModIdx -= 1; if (SelectedModIdx < 0) { @@ -392,12 +396,12 @@ internal static class GameSettingMenuPatches private static void CleanupTab(GameOptionsMenu settings, RolesSettingsMenu roles) { - if (roles.roleChances != null && SelectedMod?.InternalRoles.Count > 0) + if (roles.roleChances != null) { CleanupRoleSettings(roles); } - if (settings.Children != null && SelectedMod?.InternalOptionGroups.Count > 0) + if (settings.Children != null) { CleanupSettings(settings); } @@ -438,12 +442,16 @@ internal static class GameSettingMenuPatches { role.gameObject.DestroyImmediate(); } + rolesMenu.roleChances?.Clear(); rolesMenu.AdvancedRolesSettings.gameObject.SetActive(false); rolesMenu.RoleChancesSettings.gameObject.SetActive(true); rolesMenu.SetQuotaTab(); - rolesMenu.scrollBar.ScrollToTop(); + if (SelectedModIdx == 0) + { + rolesMenu.scrollBar.ScrollToTop(); + } } } } diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index c59d458..8984694 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -22,15 +22,16 @@ namespace MiraAPI.Patches.Options; [HarmonyPatch(typeof(RolesSettingsMenu))] public static class RoleSettingMenuPatches { - private static Dictionary RoleGroupHidden { get; set; } = new(); - private static List Headers { get; set; } = new(); - private static List RoleOptionSettings { get; set; } = new(); + public static Dictionary Scrolls { get; } = []; + private static Dictionary RoleGroupHidden { get; } = []; + private static List Headers { get; } = []; + private static List RoleOptionSettings { get; } = []; private static float ScrollerNum { get; set; } = 0.522f; [HarmonyPrefix] [HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))] - public static bool PatchStart(RolesSettingsMenu __instance) + public static bool SetQuotaTabPatch(RolesSettingsMenu __instance) { Headers.ForEach(Object.Destroy); RoleOptionSettings.ForEach(Object.Destroy); @@ -232,6 +233,15 @@ public static class RoleSettingMenuPatches private static void SetScrollBounds(this Scroller scroller) { scroller.CalculateAndSetYBounds(1 + 1.5f * Headers.Count + RoleOptionSettings.Count, 1f, 6f, 0.43f); + if (Scrolls.TryGetValue(GameSettingMenuPatches.SelectedModIdx, out var scroll)) + { + scroller.Inner.localPosition = scroll; + scroller.UpdateScrollBars(); + } + else + { + scroller.ScrollToTop(); + } } [HarmonyPrefix] @@ -245,13 +255,12 @@ public static class RoleSettingMenuPatches [HarmonyPatch(nameof(RolesSettingsMenu.OpenChancesTab))] public static void OpenChancesTabPostfix(RolesSettingsMenu __instance) { - if (GameSettingMenuPatches.SelectedModIdx == 0 || !__instance.scrollBar) + if (GameSettingMenuPatches.SelectedModIdx == 0) { return; } __instance.scrollBar.SetScrollBounds(); - __instance.scrollBar.ScrollToTop(); } private static void ValueChanged(OptionBehaviour obj) @@ -344,6 +353,7 @@ public static class RoleSettingMenuPatches newOpt.Initialize(); } + Scrolls[GameSettingMenuPatches.SelectedModIdx] = __instance.scrollBar.Inner.localPosition; __instance.scrollBar.CalculateAndSetYBounds(__instance.advancedSettingChildren.Count + 3, 1f, 6f, 0.45f); __instance.scrollBar.ScrollToTop(); } @@ -356,6 +366,8 @@ public static class RoleSettingMenuPatches return; } + Scrolls[GameSettingMenuPatches.SelectedModIdx] = __instance.scrollBar.Inner.localPosition; + __instance.roleDescriptionText.text = customRole.RoleLongDescription; __instance.roleTitleText.text = TranslationController.Instance.GetString( role.StringName, From b94472ee07c0870296d897adc851162e0c11991d Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Fri, 16 May 2025 19:42:59 -0400 Subject: [PATCH 24/28] save options scroll states --- .../Patches/Options/GameSettingMenuPatches.cs | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs index 8993969..c252c6e 100644 --- a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using HarmonyLib; using MiraAPI.Modifiers; @@ -30,6 +31,8 @@ internal static class GameSettingMenuPatches private static PassiveButton? _modifiersButton; private static PassiveButton? _smallRolesButton; + private static Dictionary OptionScrolls = []; + [HarmonyPostfix] [HarmonyPatch(nameof(GameSettingMenu.ChangeTab))] // ReSharper disable once InconsistentNaming @@ -112,7 +115,10 @@ internal static class GameSettingMenuPatches passiveButton.OnClick.AddListener( (UnityAction)(() => { - RoleSettingMenuPatches.Scrolls[SelectedModIdx] = __instance.RoleSettingsTab.scrollBar.Inner.localPosition; + RoleSettingMenuPatches.Scrolls[SelectedModIdx] = + __instance.RoleSettingsTab.scrollBar.Inner.localPosition; + OptionScrolls[SelectedModIdx] = + __instance.GameSettingsTab.scrollBar.Inner.localPosition; SelectedModIdx += 1; if (SelectedModIdx > MiraPluginManager.Instance.RegisteredPlugins.Length) @@ -132,7 +138,10 @@ internal static class GameSettingMenuPatches backButton.gameObject.GetComponent().OnClick.AddListener( (UnityAction)(() => { - RoleSettingMenuPatches.Scrolls[SelectedModIdx] = __instance.RoleSettingsTab.scrollBar.Inner.localPosition; + RoleSettingMenuPatches.Scrolls[SelectedModIdx] = + __instance.RoleSettingsTab.scrollBar.Inner.localPosition; + OptionScrolls[SelectedModIdx] = + __instance.GameSettingsTab.scrollBar.Inner.localPosition; SelectedModIdx -= 1; if (SelectedModIdx < 0) @@ -251,6 +260,7 @@ internal static class GameSettingMenuPatches })); } } + private static void UpdateText(GameSettingMenu menu, GameOptionsMenu settings, RolesSettingsMenu roles) { if (_text is not null && SelectedModIdx == 0) @@ -276,8 +286,10 @@ internal static class GameSettingMenuPatches if (SelectedModIdx != 0) { 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); + 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); @@ -317,6 +329,7 @@ internal static class GameSettingMenuPatches { menu.ChangeTab(0, false); } + // If the mod has roles, we can enable the bigger role button and disable the small one. if (modHasRoles) { @@ -389,8 +402,10 @@ internal static class GameSettingMenuPatches { continue; } + child.gameObject.DestroyImmediate(); } + options.Clear(); } @@ -410,6 +425,7 @@ internal static class GameSettingMenuPatches { CleanupSettings(_modifiersTab); } + void CleanupSettings(GameOptionsMenu gameOptMenu) { ClearOptions(gameOptMenu.Children); @@ -421,7 +437,15 @@ internal static class GameSettingMenuPatches .ForEach(header => header.gameObject.DestroyImmediate()); gameOptMenu.Initialize(); - gameOptMenu.scrollBar.ScrollToTop(); + if (OptionScrolls.TryGetValue(SelectedModIdx, out var pos)) + { + gameOptMenu.scrollBar.Inner.localPosition = pos; + gameOptMenu.scrollBar.UpdateScrollBars(); + } + else + { + gameOptMenu.scrollBar.ScrollToTop(); + } } void CleanupRoleSettings(RolesSettingsMenu rolesMenu) @@ -450,7 +474,15 @@ internal static class GameSettingMenuPatches rolesMenu.SetQuotaTab(); if (SelectedModIdx == 0) { - rolesMenu.scrollBar.ScrollToTop(); + if (RoleSettingMenuPatches.Scrolls.TryGetValue(0, out var pos)) + { + rolesMenu.scrollBar.Inner.localPosition = pos; + rolesMenu.scrollBar.UpdateScrollBars(); + } + else + { + rolesMenu.scrollBar.ScrollToTop(); + } } } } From 52de9a22debbd1db1f0babefc441f577f6b44559 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Thu, 19 Jun 2025 23:24:02 -0400 Subject: [PATCH 25/28] fix preset loading networking --- MiraAPI/GameOptions/OptionTypes/ModdedOption.cs | 9 +++++---- MiraAPI/Presets/OptionPreset.cs | 5 +++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs index 94f6f3d..3c98d24 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs @@ -27,7 +27,7 @@ public abstract class ModdedOption : IModdedOption public StringNames StringName { get; } /// - public BaseGameSetting Data { get; protected init; } = null!; + public BaseGameSetting Data { get; protected set; } = null!; /// public IMiraPlugin? ParentMod @@ -95,7 +95,8 @@ public abstract class ModdedOption : IModdedOption /// Sets the value of the option. /// /// The new value. - public void SetValue(T newValue) + /// Whether to send the value to other players. + public void SetValue(T newValue, bool sendRpc = true) { var oldVal = Value; Value = newValue; @@ -105,7 +106,7 @@ public abstract class ModdedOption : IModdedOption ChangedEvent?.Invoke(Value); } - if (AmongUsClient.Instance.AmHost) + if (sendRpc && AmongUsClient.Instance.AmHost) { if (ParentMod?.GetConfigFile().TryGetEntry(ConfigDefinition, out var entry) == true) { @@ -141,7 +142,7 @@ public abstract class ModdedOption : IModdedOption { if (presetConfig.TryGetEntry(ConfigDefinition, out ConfigEntry entry)) { - SetValue(entry.Value); + SetValue(entry.Value, false); } else { diff --git a/MiraAPI/Presets/OptionPreset.cs b/MiraAPI/Presets/OptionPreset.cs index 7e07df5..06e3f8a 100644 --- a/MiraAPI/Presets/OptionPreset.cs +++ b/MiraAPI/Presets/OptionPreset.cs @@ -1,5 +1,6 @@ using System.Linq; using BepInEx.Configuration; +using MiraAPI.GameOptions; using MiraAPI.PluginLoading; using MiraAPI.Roles; using UnityEngine; @@ -61,9 +62,13 @@ public class OptionPreset option.LoadFromPreset(PresetConfig); } + ModdedOptionsManager.SyncAllOptions(); + foreach (var role in Plugin.InternalRoles.Values.OfType()) { role.LoadFromPreset(PresetConfig); } + + CustomRoleManager.SyncAllRoleSettings(); } } From 3b7b247ac0a1f69281c37ad4878da3bf85bd7b8d Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Fri, 20 Jun 2025 08:32:24 -0400 Subject: [PATCH 26/28] refresh presets when opening preset menu --- MiraAPI/Patches/Options/GamePresetsTabPatches.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index 6a747bc..2451941 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -87,6 +87,8 @@ internal static class GamePresetsTabPatches _newDivider.SetActive(GameSettingMenuPatches.SelectedModIdx != 0); } + Refresh(); + var prefab = GameSettingMenu.Instance.GameSettingsButton; foreach (var preset in MiraPluginManager.Instance.RegisteredPlugins.SelectMany(x => x.Presets)) From 130236e8997dcd2463ce7b4b2f395fc738526a2d Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Fri, 20 Jun 2025 08:38:31 -0400 Subject: [PATCH 27/28] oops fix bad code --- MiraAPI/Patches/Options/GamePresetsTabPatches.cs | 4 +--- MiraAPI/Patches/Options/GameSettingMenuPatches.cs | 13 +++++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index 2451941..64138ca 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -87,8 +87,6 @@ internal static class GamePresetsTabPatches _newDivider.SetActive(GameSettingMenuPatches.SelectedModIdx != 0); } - Refresh(); - var prefab = GameSettingMenu.Instance.GameSettingsButton; foreach (var preset in MiraPluginManager.Instance.RegisteredPlugins.SelectMany(x => x.Presets)) @@ -333,7 +331,7 @@ internal static class GamePresetsTabPatches } } - private static void Refresh() + public static void Refresh() { Logger.Error("Refreshing presets"); if (GameSettingMenuPatches.SelectedMod == null) diff --git a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs index c252c6e..307dcc2 100644 --- a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs @@ -40,7 +40,11 @@ internal static class GameSettingMenuPatches { if ((previewOnly && Controller.currentTouchType == Controller.TouchType.Joystick) || !previewOnly) { - if (_modifiersTab) _modifiersTab!.gameObject.SetActive(tabNum == 3); + if (_modifiersTab) + { + _modifiersTab!.gameObject.SetActive(tabNum == 3); + } + _modifiersButton?.SelectButton(tabNum == 3); _smallRolesButton?.SelectButton(tabNum == 2); if (tabNum == 3) @@ -64,7 +68,10 @@ internal static class GameSettingMenuPatches _modifiersButton?.SelectButton(tabNum == 3); _smallRolesButton?.SelectButton(tabNum == 2); - if (tabNum != 3) return; + if (tabNum != 3) + { + return; + } if (__instance.RoleSettingsButton.gameObject.active) { @@ -223,6 +230,8 @@ internal static class GameSettingMenuPatches _modifierBtnOgPos = _modifiersButton.transform.localPosition; + GamePresetsTabPatches.Refresh(); + UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab); } From cf0bd6ec38b01933786524b7952885b074de07e1 Mon Sep 17 00:00:00 2001 From: XtraCube <72575280+XtraCube@users.noreply.github.com> Date: Fri, 20 Jun 2025 08:43:11 -0400 Subject: [PATCH 28/28] fixed presets --- MiraAPI/Patches/Options/GamePresetsTabPatches.cs | 2 +- MiraAPI/Patches/Options/GameSettingMenuPatches.cs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index 64138ca..6a747bc 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -331,7 +331,7 @@ internal static class GamePresetsTabPatches } } - public static void Refresh() + private static void Refresh() { Logger.Error("Refreshing presets"); if (GameSettingMenuPatches.SelectedMod == null) diff --git a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs index 307dcc2..5b4be8c 100644 --- a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs @@ -4,6 +4,7 @@ using System.Linq; using HarmonyLib; using MiraAPI.Modifiers; using MiraAPI.PluginLoading; +using MiraAPI.Presets; using MiraAPI.Utilities.Assets; using Reactor.Utilities.Extensions; using TMPro; @@ -230,7 +231,10 @@ internal static class GameSettingMenuPatches _modifierBtnOgPos = _modifiersButton.transform.localPosition; - GamePresetsTabPatches.Refresh(); + foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins) + { + PresetManager.LoadPresets(plugin); + } UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab); }