diff --git a/MiraAPI/GameOptions/IModdedOption.cs b/MiraAPI/GameOptions/IModdedOption.cs index 232cf5a..a041cc7 100644 --- a/MiraAPI/GameOptions/IModdedOption.cs +++ b/MiraAPI/GameOptions/IModdedOption.cs @@ -9,7 +9,7 @@ public interface IModdedOption { public uint Id { get; } public BaseGameSetting Data { get; } - public IModdedOptionGroup Group { get; set; } + public bool HasGroup { get; set; } public IMiraPlugin ParentMod { get; set; } public Type AdvancedRole { get; set; } public OptionBehaviour OptionBehaviour { get; protected set; } diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs index 0d8e15f..88228a7 100644 --- a/MiraAPI/GameOptions/ModdedOptionsManager.cs +++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs @@ -12,12 +12,16 @@ namespace MiraAPI.GameOptions; public class ModdedOptionsManager { - public static readonly Dictionary ModdedOptions = new(); - public static readonly List Options = []; - public static readonly List Groups = []; private static readonly Dictionary OptionAttributes = new(); - public static readonly Dictionary OriginalTypes = new(); - public static uint NextId = 1; + + public static readonly Dictionary ModdedOptions = new(); + public static readonly Dictionary> GroupedOptions = new(); + + public static readonly List Groups = []; + public static readonly Dictionary TypeToGroup = new(); + + public static uint NextId => _nextId++; + private static uint _nextId = 1; public static IModdedOption RegisterOption(Type type, ModdedOptionAttribute attribute, PropertyInfo property) { @@ -50,11 +54,12 @@ public class ModdedOptionsManager OptionAttributes.Add(property, attribute); - if (OriginalTypes.ContainsKey(type)) + if (TypeToGroup.ContainsKey(type)) { - Logger.Error($"Grouping {attribute.Title} with {OriginalTypes[type].GroupName}"); - result.Group = OriginalTypes[type]; - result.AdvancedRole = OriginalTypes[type].AdvancedRole; + Logger.Error($"Grouping {attribute.Title} with {TypeToGroup[type].GroupName}"); + GroupedOptions[TypeToGroup[type]].Add(result); + result.HasGroup = true; + result.AdvancedRole = TypeToGroup[type].AdvancedRole; } } diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs index 386cc7c..3021cd1 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs @@ -4,7 +4,6 @@ using Reactor.Localization.Utilities; using System; using MiraAPI.Networking; using Reactor.Networking.Rpc; -using Reactor.Utilities; using UnityEngine; namespace MiraAPI.GameOptions.OptionTypes; @@ -13,6 +12,8 @@ public abstract class ModdedOption : IModdedOption { public uint Id { get; } public BaseGameSetting Data { get; protected init; } + public bool HasGroup { get; set; } = false; + public IMiraPlugin ParentMod { get; set; } = null; public T Value { get; protected set; } public T DefaultValue { get; init; } public Action ChangedEvent { get; set; } @@ -21,8 +22,6 @@ public abstract class ModdedOption : IModdedOption public Func Visible { get; set; } public Type AdvancedRole { get; set; } public OptionBehaviour OptionBehaviour { get; set; } - public IModdedOptionGroup Group { get; set; } = null; - public IMiraPlugin ParentMod { get; set; } = null; protected ModdedOption(string title, T defaultValue, Type roleType) : this() { @@ -40,8 +39,7 @@ public abstract class ModdedOption : IModdedOption private ModdedOption() { - Id = ModdedOptionsManager.NextId++; - ModdedOptionsManager.Options.Add(this); + Id = ModdedOptionsManager.NextId; ModdedOptionsManager.ModdedOptions.Add(Id, this); } diff --git a/MiraAPI/MiraAPI.csproj b/MiraAPI/MiraAPI.csproj index aad0751..0de367f 100644 --- a/MiraAPI/MiraAPI.csproj +++ b/MiraAPI/MiraAPI.csproj @@ -22,6 +22,7 @@ + diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs index 20e1d96..580add3 100644 --- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs +++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs @@ -22,7 +22,10 @@ public static class GameOptionsMenuPatch __instance.MapPicker.gameObject.SetActive(false); float num = 2.1f; - foreach (IModdedOptionGroup group in GameSettingMenuPatches.SelectedMod.OptionGroups) + + var filteredGroups = GameSettingMenuPatches.SelectedMod.OptionGroups.Where(x => x.AdvancedRole is null); + + foreach (IModdedOptionGroup group in filteredGroups) { CategoryHeaderMasked categoryHeaderMasked = Object.Instantiate(__instance.categoryHeaderOrigin, Vector3.zero, Quaternion.identity, __instance.settingsContainer); categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName), 20); @@ -35,7 +38,7 @@ public static class GameOptionsMenuPatch categoryHeaderMasked.transform.localPosition = new Vector3(-0.903f, num, -2f); num -= 0.63f; - foreach (var opt in ModdedOptionsManager.Options.Where(opt => opt.Group is not null && opt.AdvancedRole is null && opt.Group.GroupName == group.GroupName)) + foreach (var opt in ModdedOptionsManager.GroupedOptions[group]) { OptionBehaviour newOpt = opt.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.settingsContainer); newOpt.transform.localPosition = new Vector3(0.952f, num, -2f); @@ -62,36 +65,46 @@ public static class GameOptionsMenuPatch } } - CategoryHeaderMasked ungroupedHeader = Object.Instantiate(__instance.categoryHeaderOrigin, Vector3.zero, Quaternion.identity, __instance.settingsContainer); - ungroupedHeader.SetHeader(CustomStringName.CreateAndRegister("Ungrouped Options"), 20); - ungroupedHeader.transform.localScale = Vector3.one * 0.63f; - ungroupedHeader.transform.localPosition = new Vector3(-0.903f, num, -2f); + var filteredOptions = GameSettingMenuPatches.SelectedMod.Options.Where(x => x.AdvancedRole is null && !x.HasGroup).ToList(); - num -= 0.63f; - - foreach (var opt in ModdedOptionsManager.Options.Where(opt => opt.Group is null && opt.AdvancedRole is null)) + if (filteredOptions.Count > 0) { - OptionBehaviour newOpt = opt.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.settingsContainer); - newOpt.transform.localPosition = new Vector3(0.952f, num, -2f); - newOpt.SetClickMask(__instance.ButtonClickMask); - newOpt.SetUpFromData(newOpt.data, 20); - SpriteRenderer[] componentsInChildren = newOpt.GetComponentsInChildren(true); - for (int i = 0; i < componentsInChildren.Length; i++) + CategoryHeaderMasked ungroupedHeader = Object.Instantiate(__instance.categoryHeaderOrigin, Vector3.zero, + Quaternion.identity, __instance.settingsContainer); + ungroupedHeader.SetHeader(CustomStringName.CreateAndRegister("Ungrouped Options"), 20); + ungroupedHeader.transform.localScale = Vector3.one * 0.63f; + ungroupedHeader.transform.localPosition = new Vector3(-0.903f, num, -2f); + + num -= 0.63f; + + + foreach (var opt in filteredOptions) { - componentsInChildren[i].material.SetInt(PlayerMaterial.MaskLayer, 20); + OptionBehaviour newOpt = opt.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, + __instance.stringOptionOrigin, __instance.settingsContainer); + newOpt.transform.localPosition = new Vector3(0.952f, num, -2f); + newOpt.SetClickMask(__instance.ButtonClickMask); + newOpt.SetUpFromData(newOpt.data, 20); + + SpriteRenderer[] componentsInChildren = newOpt.GetComponentsInChildren(true); + for (int i = 0; i < componentsInChildren.Length; i++) + { + componentsInChildren[i].material.SetInt(PlayerMaterial.MaskLayer, 20); + } + + foreach (TextMeshPro textMeshPro in newOpt.GetComponentsInChildren(true)) + { + textMeshPro.fontMaterial.SetFloat(ShaderID.StencilComp, 3f); + textMeshPro.fontMaterial.SetFloat(ShaderID.Stencil, 20); + } + + __instance.Children.Add(newOpt); + + num -= 0.45f; + + newOpt.Initialize(); } - foreach (TextMeshPro textMeshPro in newOpt.GetComponentsInChildren(true)) - { - textMeshPro.fontMaterial.SetFloat(ShaderID.StencilComp, 3f); - textMeshPro.fontMaterial.SetFloat(ShaderID.Stencil, 20); - } - - __instance.Children.Add(newOpt); - - num -= 0.45f; - - newOpt.Initialize(); } __instance.scrollBar.SetYBoundsMax(-num - 1.65f); diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs index ff62645..4bd63e7 100644 --- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -139,8 +139,10 @@ public static class RoleSettingMenuPatches __instance.advancedSettingChildren.Clear(); float num = -0.872f; - foreach (var option in GameSettingMenuPatches.SelectedMod.Options.Where(option => option.AdvancedRole != null && option.AdvancedRole == role.GetType())) + foreach (var option in GameSettingMenuPatches.SelectedMod.Options) { + if (option.AdvancedRole is not null && option.AdvancedRole != role.GetType()) continue; + OptionBehaviour newOpt = option.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.AdvancedRolesSettings.transform); newOpt.transform.localPosition = new Vector3(2.17f, num, -2f); newOpt.SetClickMask(__instance.ButtonClickMask); diff --git a/MiraAPI/Patches/Roles/NameTagPatch.cs b/MiraAPI/Patches/Roles/NameTagPatch.cs index 3566416..929ccaf 100644 --- a/MiraAPI/Patches/Roles/NameTagPatch.cs +++ b/MiraAPI/Patches/Roles/NameTagPatch.cs @@ -1,6 +1,4 @@ using HarmonyLib; -using MiraAPI.GameModes; -using MiraAPI.GameOptions; using UnityEngine; namespace MiraAPI.Patches.Roles; diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index 6ac174e..17235bd 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -74,9 +74,11 @@ public class MiraPluginManager if (typeof(IModdedOptionGroup).IsAssignableFrom(type)) { IModdedOptionGroup group = (IModdedOptionGroup)Activator.CreateInstance(type); + if (group == null) continue; ModdedOptionsManager.Groups.Add(group); - ModdedOptionsManager.OriginalTypes.Add(type, group); + ModdedOptionsManager.TypeToGroup.Add(type, group); + ModdedOptionsManager.GroupedOptions.Add(group, []); pluginInfo.OptionGroups.Add(group); foreach (var property in type.GetProperties()) @@ -84,9 +86,13 @@ public class MiraPluginManager if (typeof(IModdedOption).IsAssignableFrom(property.PropertyType)) { IModdedOption option = (IModdedOption)property.GetValue(group); - group.Options.Add(option); - option.Group = group; + if (option == null) continue; + + option.HasGroup = true; option.AdvancedRole = group.AdvancedRole; + + ModdedOptionsManager.GroupedOptions[group].Add(option); + pluginInfo.Options.Add(option); } } } diff --git a/MiraAPI/Resources/Empty.png b/MiraAPI/Resources/Empty.png new file mode 100644 index 0000000..478b531 Binary files /dev/null and b/MiraAPI/Resources/Empty.png differ diff --git a/MiraAPI/Utilities/Assets/MiraAssets.cs b/MiraAPI/Utilities/Assets/MiraAssets.cs index f904dac..12b0d4b 100644 --- a/MiraAPI/Utilities/Assets/MiraAssets.cs +++ b/MiraAPI/Utilities/Assets/MiraAssets.cs @@ -2,7 +2,7 @@ public static class MiraAssets { - public static readonly LoadableResourceAsset Empty = new(""); + public static readonly LoadableResourceAsset Empty = new("MiraAPI.Resources.Empty.png"); public static readonly LoadableResourceAsset NextButton = new("MiraAPI.Resources.NextButton.png"); public static readonly LoadableResourceAsset NextButtonActive = new("MiraAPI.Resources.NextButtonActive.png"); diff --git a/MiraAPI/Utilities/Extensions.cs b/MiraAPI/Utilities/Extensions.cs index d810b8f..e80e0b2 100644 --- a/MiraAPI/Utilities/Extensions.cs +++ b/MiraAPI/Utilities/Extensions.cs @@ -10,7 +10,7 @@ public static class Extensions { public static bool IsCustom(this OptionBehaviour optionBehaviour) { - return ModdedOptionsManager.Options.Any(opt => opt.OptionBehaviour && opt.OptionBehaviour.Equals(optionBehaviour)); + return ModdedOptionsManager.ModdedOptions.Values.Any(opt => opt.OptionBehaviour && opt.OptionBehaviour.Equals(optionBehaviour)); } public static Color DarkenColor(this Color color)