diff --git a/MiraAPI.Example/Options/Roles/FreezerRoleSettings.cs b/MiraAPI.Example/Options/Roles/FreezerRoleSettings.cs index a99c9e0..6113408 100644 --- a/MiraAPI.Example/Options/Roles/FreezerRoleSettings.cs +++ b/MiraAPI.Example/Options/Roles/FreezerRoleSettings.cs @@ -6,12 +6,10 @@ using MiraAPI.Utilities; namespace MiraAPI.Example.Options.Roles; -public class FreezerRoleSettings : AbstractOptionGroup +public class FreezerRoleSettings : AbstractOptionGroup { public override string GroupName => "Custom Role"; - public override Type AdvancedRole => typeof(FreezerRole); - [ModdedNumberOption("Freeze Duration", 1, 15, 1, MiraNumberSuffixes.Seconds)] public float FreezeDuration { get; set; } = 5; diff --git a/MiraAPI.Example/Options/Roles/TeleporterOptions.cs b/MiraAPI.Example/Options/Roles/TeleporterOptions.cs index 34ccb94..1b0c1da 100644 --- a/MiraAPI.Example/Options/Roles/TeleporterOptions.cs +++ b/MiraAPI.Example/Options/Roles/TeleporterOptions.cs @@ -7,12 +7,10 @@ using MiraAPI.Utilities; namespace MiraAPI.Example.Options.Roles; -public class TeleporterOptions : AbstractOptionGroup +public class TeleporterOptions : AbstractOptionGroup { public override string GroupName => "Teleporter"; - public override Type AdvancedRole => typeof(TeleporterRole); - public ModdedNumberOption TeleportCooldown { get; set; } = new("Teleport Cooldown", 10, 5, 60, 2.5f, MiraNumberSuffixes.Seconds); [ModdedNumberOption("Teleport Duration", 5, 25, 1, MiraNumberSuffixes.Seconds)] diff --git a/MiraAPI/GameOptions/AbstractOptionGroup.cs b/MiraAPI/GameOptions/AbstractOptionGroup.cs index 418f25b..96dcdd7 100644 --- a/MiraAPI/GameOptions/AbstractOptionGroup.cs +++ b/MiraAPI/GameOptions/AbstractOptionGroup.cs @@ -32,12 +32,19 @@ public abstract class AbstractOptionGroup /// public virtual uint GroupPriority => uint.MaxValue; - /// - /// Gets the role the group is associated with. This is used for the advanced role options menu. - /// - public virtual Type? AdvancedRole => null; - internal bool AllOptionsHidden { get; set; } internal CategoryHeaderMasked? Header { get; set; } } + +/// +/// Base class for option groups. An option group is a collection of options that are displayed together in the options menu. +/// +/// The type of the optionable that this group contains. +public abstract class AbstractOptionGroup : AbstractOptionGroup, IOptionableGroup where T : IOptionable +{ + /// + /// Gets the Optionable type of the group. + /// + public Type OptionableType => typeof(T); +} diff --git a/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs index da6094b..54680f2 100644 --- a/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/ModdedEnumOptionAttribute.cs @@ -8,12 +8,12 @@ namespace MiraAPI.GameOptions.Attributes; /// Attribute for creating an enum option. /// [AttributeUsage(AttributeTargets.Property)] -public class ModdedEnumOptionAttribute(string title, Type enumType, string[]? values = null, Type? roleType = null) - : ModdedOptionAttribute(title, roleType) +public class ModdedEnumOptionAttribute(string title, Type enumType, string[]? values = null) + : ModdedOptionAttribute(title) { internal override IModdedOption CreateOption(object? value, PropertyInfo property) { - var opt = new ModdedEnumOption(Title, (int)(value ?? 0), enumType, values, RoleType); + var opt = new ModdedEnumOption(Title, (int)(value ?? 0), enumType, values); return opt; } diff --git a/MiraAPI/GameOptions/Attributes/ModdedNumberOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedNumberOptionAttribute.cs index d679051..07b5cfc 100644 --- a/MiraAPI/GameOptions/Attributes/ModdedNumberOptionAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/ModdedNumberOptionAttribute.cs @@ -16,13 +16,12 @@ public class ModdedNumberOptionAttribute( float increment = 1, MiraNumberSuffixes suffixType = MiraNumberSuffixes.None, string? formatString = null, - bool zeroInfinity = false, - Type? roleType = null) - : ModdedOptionAttribute(title, roleType) + bool zeroInfinity = false) + : ModdedOptionAttribute(title) { internal override IModdedOption CreateOption(object? value, PropertyInfo property) { - return new ModdedNumberOption(Title, (float)(value ?? min+increment), min, max, increment, suffixType, formatString, zeroInfinity, RoleType); + return new ModdedNumberOption(Title, (float)(value ?? min+increment), min, max, increment, suffixType, formatString, zeroInfinity); } /// diff --git a/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs b/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs index d44a7c0..0ad64da 100644 --- a/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs +++ b/MiraAPI/GameOptions/Attributes/ModdedToggleOptionAttribute.cs @@ -8,13 +8,12 @@ namespace MiraAPI.GameOptions.Attributes; /// Attribute for a toggle option. /// /// The option title. -/// An optional role type. [AttributeUsage(AttributeTargets.Property)] -public class ModdedToggleOptionAttribute(string title, Type? roleType = null) : ModdedOptionAttribute(title, roleType) +public class ModdedToggleOptionAttribute(string title) : ModdedOptionAttribute(title) { internal override IModdedOption CreateOption(object? value, PropertyInfo property) { - var toggleOpt = new ModdedToggleOption(Title, (bool)(value ?? false), RoleType); + var toggleOpt = new ModdedToggleOption(Title, (bool)(value ?? false)); return toggleOpt; } diff --git a/MiraAPI/GameOptions/IModdedOption.cs b/MiraAPI/GameOptions/IModdedOption.cs index 1aee3c4..f40f3cf 100644 --- a/MiraAPI/GameOptions/IModdedOption.cs +++ b/MiraAPI/GameOptions/IModdedOption.cs @@ -13,7 +13,6 @@ public interface IModdedOption StringNames StringName { get; } IMiraPlugin? ParentMod { get; set; } BaseGameSetting Data { get; } - Type? AdvancedRole { get; set; } OptionBehaviour? OptionBehaviour { get; } Func Visible { get; set; } ConfigDefinition? ConfigDefinition { get; set; } diff --git a/MiraAPI/GameOptions/IOptionable.cs b/MiraAPI/GameOptions/IOptionable.cs new file mode 100644 index 0000000..adc28f6 --- /dev/null +++ b/MiraAPI/GameOptions/IOptionable.cs @@ -0,0 +1,6 @@ +namespace MiraAPI.GameOptions; + +/// +/// Interface for classes that can be used for option groups. +/// +public interface IOptionable; diff --git a/MiraAPI/GameOptions/IOptionableGroup.cs b/MiraAPI/GameOptions/IOptionableGroup.cs new file mode 100644 index 0000000..e476548 --- /dev/null +++ b/MiraAPI/GameOptions/IOptionableGroup.cs @@ -0,0 +1,14 @@ +using System; + +namespace MiraAPI.GameOptions; + +/// +/// Interface for groups that have some type of "Optionable" class. +/// +public interface IOptionableGroup +{ + /// + /// Gets the type of the Optionable class. + /// + Type OptionableType { get; } +} diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs index a6dd1c9..cbb6459 100644 --- a/MiraAPI/GameOptions/ModdedOptionsManager.cs +++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs @@ -119,10 +119,7 @@ public static class ModdedOptionsManager option.ConfigDefinition = new ConfigDefinition(groupName, propertyName); option.ParentMod = pluginInfo.MiraPlugin; - option.AdvancedRole = group.AdvancedRole; - pluginInfo.Options.Add(option); - ModdedOptions.Add(option.Id, option); group.Options.Add(option); } diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs index f25fda6..ff422f5 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs @@ -27,8 +27,7 @@ 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. - /// An optional role type to specify for a specific role. - public ModdedEnumOption(string title, int defaultValue, Type enumType, string[]? values = null, Type? roleType = null) : base(title, defaultValue, roleType) + public ModdedEnumOption(string title, int defaultValue, Type enumType, string[]? values = null) : base(title, defaultValue) { Values = values ?? Enum.GetNames(enumType); Data = ScriptableObject.CreateInstance(); @@ -101,7 +100,6 @@ public class ModdedEnumOption : ModdedOption } } - /// /// An option for selecting an enum value. /// @@ -118,10 +116,8 @@ public class ModdedEnumOption : ModdedOption where T : Enum /// /// The title of the option. /// The default value as an int. - /// The Enum type. /// An option list of string values to use in place of the enum name. - /// An optional role type to specify for a specific role. - public ModdedEnumOption(string title, T defaultValue, string[]? values = null, Type? roleType = null) : base(title, defaultValue, roleType) + public ModdedEnumOption(string title, T defaultValue, string[]? values = null) : base(title, defaultValue) { Values = values ?? Enum.GetNames(typeof(T)); Data = ScriptableObject.CreateInstance(); diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs index f075662..0174326 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs @@ -48,7 +48,6 @@ public class ModdedNumberOption : ModdedOption /// The suffix type. /// Optional format string for the option screen. /// Whether zero is infinity or not. - /// An optional role type. public ModdedNumberOption( string title, float defaultValue, @@ -57,8 +56,7 @@ public class ModdedNumberOption : ModdedOption float increment, MiraNumberSuffixes suffixType, string? formatString = null, - bool zeroInfinity = false, - Type? roleType = null) : base(title, defaultValue, roleType) + bool zeroInfinity = false) : base(title, defaultValue) { Min = min; Max = max; @@ -144,7 +142,7 @@ public class ModdedNumberOption : ModdedOption Value = Mathf.Clamp(newValue, Min, Max); HudManager.Instance.Notifier.AddSettingsChangeMessage( StringName, - Data?.GetValueString(Value), + Data.GetValueString(Value), false); if (OptionBehaviour is NumberOption opt) diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs index 9d42be0..0cfefa8 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs @@ -72,11 +72,6 @@ public abstract class ModdedOption : IModdedOption /// public Func Visible { get; set; } - /// - /// Gets or sets the advanced role of the option. - /// - public Type? AdvancedRole { get; set; } - /// /// Gets or sets the option behaviour of the option. /// @@ -102,8 +97,7 @@ public abstract class ModdedOption : IModdedOption /// /// The option title. /// The default value. - /// The Role Type or null if it doesn't belong to a role. - protected ModdedOption(string title, T defaultValue, Type? roleType) + protected ModdedOption(string title, T defaultValue) { Id = ModdedOptionsManager.NextId; Title = title; @@ -111,11 +105,6 @@ public abstract class ModdedOption : IModdedOption Value = defaultValue; StringName = CustomStringName.CreateAndRegister(Title); Visible = () => true; - - if (roleType is not null && roleType.IsAssignableTo(typeof(ICustomRole))) - { - AdvancedRole = roleType; - } } internal void ValueChanged(OptionBehaviour optionBehaviour) diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs index 1773512..3495e86 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs @@ -15,8 +15,7 @@ public class ModdedToggleOption : ModdedOption /// /// The option title. /// The default value. - /// An optional role type. - public ModdedToggleOption(string title, bool defaultValue, Type? roleType = null) : base(title, defaultValue, roleType) + public ModdedToggleOption(string title, bool defaultValue) : base(title, defaultValue) { Data = ScriptableObject.CreateInstance(); diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs index c641e88..c0aef14 100644 --- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs +++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs @@ -1,5 +1,7 @@ using System.Linq; using HarmonyLib; +using MiraAPI.GameOptions; +using MiraAPI.Roles; using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; using Reactor.Localization.Utilities; @@ -31,8 +33,8 @@ public static class GameOptionsMenuPatch var num = 2.1f; var filteredGroups = - GameSettingMenuPatches.SelectedMod?.OptionGroups.Where( - x => x.AdvancedRole is null) ?? []; + GameSettingMenuPatches.SelectedMod?.OptionGroups + .Where(x=> x is not IOptionableGroup) ?? []; foreach (var group in filteredGroups) { @@ -99,7 +101,8 @@ public static class GameOptionsMenuPatch __instance.MapPicker.gameObject.SetActive(false); - var filteredGroups = GameSettingMenuPatches.SelectedMod?.OptionGroups.Where(x => x.AdvancedRole is null) ?? []; + var filteredGroups = GameSettingMenuPatches.SelectedMod?.OptionGroups + .Where(x => x is not IOptionableGroup) ?? []; foreach (var group in filteredGroups) { diff --git a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs index 3c65092..4f87780 100644 --- a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs @@ -113,6 +113,8 @@ public static class GameSettingMenuPatches menu.GameSettingsButton.gameObject.SetActive(true); menu.RoleSettingsButton.gameObject.SetActive(true); + // TODO: reimplement this + /* if (SelectedModIdx != 0) { if (SelectedMod?.Options.Where(x=>x.AdvancedRole==null).ToList().Count == 0) @@ -129,7 +131,7 @@ public static class GameSettingMenuPatches { menu.RoleSettingsButton.transform.localPosition = menu.GameSettingsButton.transform.localPosition; } - } + }*/ if (roles.roleChances != null && SelectedMod != null && SelectedMod.CustomRoles.Count != 0) { diff --git a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs index 4f8c060..bda1c2b 100644 --- a/MiraAPI/Patches/Options/LobbyViewPanePatches.cs +++ b/MiraAPI/Patches/Options/LobbyViewPanePatches.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using HarmonyLib; +using MiraAPI.GameOptions; using MiraAPI.PluginLoading; using MiraAPI.Roles; using MiraAPI.Utilities; @@ -125,7 +126,8 @@ public static class LobbyViewPanePatches var num = 1.44f; - var filteredGroups = SelectedMod.OptionGroups.Where(x => x.GroupVisible.Invoke() && x.AdvancedRole is null); + var filteredGroups = SelectedMod.OptionGroups + .Where(x => x.GroupVisible.Invoke() && x is not IOptionableGroup); foreach (var group in filteredGroups) { @@ -277,8 +279,9 @@ public static class LobbyViewPanePatches viewSettingsInfoPanelRoleVariant.transform.localScale = Vector3.one; viewSettingsInfoPanelRoleVariant.transform.localPosition = new Vector3(num2, num, -2f); - var advancedRoleOptions = SelectedMod.Options - .Where(x => x.AdvancedRole == customRole.GetType()) + var advancedRoleOptions = SelectedMod.OptionGroups + .Where(x => x is IOptionableGroup optionGroup && optionGroup.OptionableType == customRole.GetType()) + .SelectMany(x => x.Options) .ToList(); if (numPerGame > 0 && advancedRoleOptions.Count > 0) @@ -386,8 +389,9 @@ public static class LobbyViewPanePatches var num = viewPanel.yPosStart; var num2 = 1.08f; - var filteredOptions = SelectedMod.Options - .Where(x => x.AdvancedRole == roleType) + var filteredOptions = SelectedMod.OptionGroups + .Where(x => x is IOptionableGroup optionGroup && optionGroup.OptionableType == roleType) + .SelectMany(x=>x.Options) .ToList(); for (var i = 0; i < filteredOptions.Count; i++) diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index 33061d4..20ffa18 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; +using MiraAPI.GameOptions; using MiraAPI.Networking; using MiraAPI.Roles; using MiraAPI.Utilities; @@ -304,15 +305,14 @@ public static class RoleSettingMenuPatches var hasImage = role.RoleScreenshot != null; var num = hasImage ? -0.872f : -1; - var filteredOptions = GameSettingMenuPatches.SelectedMod?.Options.Where(x => x.AdvancedRole == role.GetType()) ?? []; + // TODO: create sub groups under the role settings. + var filteredOptions = GameSettingMenuPatches.SelectedMod?.OptionGroups + .Where(x=> x is IOptionableGroup optionableGroup && optionableGroup.OptionableType == role.GetType()) + .SelectMany(x=>x.Options) + .ToList() ?? []; foreach (var option in filteredOptions) { - if (option.AdvancedRole is not null && option.AdvancedRole != role.GetType()) - { - continue; - } - var newOpt = option.CreateOption( __instance.checkboxOrigin, __instance.numberOptionOrigin, @@ -470,8 +470,8 @@ public static class RoleSettingMenuPatches roleOptionSetting.titleText.horizontalAlignment = HorizontalAlignmentOptions.Left; if (GameSettingMenuPatches.SelectedMod is null || - GameSettingMenuPatches.SelectedMod.Options.Exists( - x => x.AdvancedRole != null && x.AdvancedRole.IsInstanceOfType(role))) + GameSettingMenuPatches.SelectedMod.OptionGroups + .Exists(x => x is IOptionableGroup optionGroup && optionGroup.OptionableType == role.GetType())) { var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform); newButton.name = "ConfigButton"; diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index 3c52fb8..ecb4326 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -1,5 +1,6 @@ using System.Text; using BepInEx.Configuration; +using MiraAPI.GameOptions; using MiraAPI.Modifiers; using MiraAPI.PluginLoading; using MiraAPI.Utilities; @@ -11,7 +12,7 @@ namespace MiraAPI.Roles; /// /// Interface for custom roles. /// -public interface ICustomRole +public interface ICustomRole : IOptionable { /// /// Gets the name of the role.