From b4e30b2b68814e798ccc4c6e6a095efd8176eb9d Mon Sep 17 00:00:00 2001 From: xChipseq Date: Fri, 29 Aug 2025 13:04:18 +0200 Subject: [PATCH 01/12] wip --- MiraAPI.Example/ExampleLocalSetings.cs | 19 ++ MiraAPI.Example/ExamplePlugin.cs | 1 + .../Attributes/LocalSettingAttribute.cs | 11 ++ .../Attributes/LocalSettingToggleAttribute.cs | 11 ++ MiraAPI/LocalSettings/ILocalSetting.cs | 16 ++ MiraAPI/LocalSettings/LocalSettingsTab.cs | 164 ++++++++++++++++++ .../SettingTypes/LocalSetingToggle.cs | 117 +++++++++++++ .../SettingTypes/LocalSettingBase.cs | 54 ++++++ 8 files changed, 393 insertions(+) create mode 100644 MiraAPI.Example/ExampleLocalSetings.cs create mode 100644 MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs create mode 100644 MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs create mode 100644 MiraAPI/LocalSettings/ILocalSetting.cs create mode 100644 MiraAPI/LocalSettings/LocalSettingsTab.cs create mode 100644 MiraAPI/LocalSettings/SettingTypes/LocalSetingToggle.cs create mode 100644 MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs diff --git a/MiraAPI.Example/ExampleLocalSetings.cs b/MiraAPI.Example/ExampleLocalSetings.cs new file mode 100644 index 0000000..0feb0f0 --- /dev/null +++ b/MiraAPI.Example/ExampleLocalSetings.cs @@ -0,0 +1,19 @@ +using BepInEx.Configuration; +using MiraAPI.LocalSettings; +using MiraAPI.LocalSettings.Attributes; +using Reactor.Utilities; + +namespace MiraAPI.Example; + +public static class ExampleLocalSetings +{ + public static LocalSettingsTab ModTab { get; } = new LocalSettingsTab("Example Tab"); + + [LocalSettingToggle] + public static ConfigEntry ExampleToggleSetting { get; set; } + + internal static void BindSettings(ConfigFile config) + { + ExampleToggleSetting = config.Bind("General", "Example Toggle", false); + } +} \ No newline at end of file diff --git a/MiraAPI.Example/ExamplePlugin.cs b/MiraAPI.Example/ExamplePlugin.cs index b20ec41..19d23e7 100644 --- a/MiraAPI.Example/ExamplePlugin.cs +++ b/MiraAPI.Example/ExamplePlugin.cs @@ -21,6 +21,7 @@ public partial class ExamplePlugin : BasePlugin, IMiraPlugin public ConfigFile GetConfigFile() => Config; public override void Load() { + ExampleLocalSetings.BindSettings(Config); ExampleEventHandlers.Initialize(); Harmony.PatchAll(); } diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs new file mode 100644 index 0000000..3d72d3c --- /dev/null +++ b/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace MiraAPI.LocalSettings.Attributes; + +/// +/// Base class for all local settings attributes. +/// +[AttributeUsage(AttributeTargets.Property)] +public abstract class LocalSettingAttribute : Attribute +{ +} diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs new file mode 100644 index 0000000..428acf1 --- /dev/null +++ b/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace MiraAPI.LocalSettings.Attributes; + +/// +/// Creates a toggle and binds it with the config entry. +/// +[AttributeUsage(AttributeTargets.Property)] +public class LocalSettingToggleAttribute : LocalSettingAttribute +{ +} diff --git a/MiraAPI/LocalSettings/ILocalSetting.cs b/MiraAPI/LocalSettings/ILocalSetting.cs new file mode 100644 index 0000000..78d3647 --- /dev/null +++ b/MiraAPI/LocalSettings/ILocalSetting.cs @@ -0,0 +1,16 @@ +using BepInEx.Configuration; +using UnityEngine; + +namespace MiraAPI.LocalSettings; + +/// +/// Interface for all local settings +/// +public interface ILocalSetting +{ + string Name { get; } + string Description { get; } + GameObject? Setting { get; } + ConfigEntryBase ConfigEntry { get; } + GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last); +} diff --git a/MiraAPI/LocalSettings/LocalSettingsTab.cs b/MiraAPI/LocalSettings/LocalSettingsTab.cs new file mode 100644 index 0000000..8a6b2dc --- /dev/null +++ b/MiraAPI/LocalSettings/LocalSettingsTab.cs @@ -0,0 +1,164 @@ +using System.Collections.Generic; +using System.Linq; +using HarmonyLib; +using MiraAPI.Utilities.Assets; +using Reactor.Utilities.Extensions; +using TMPro; +using UnityEngine; +using UnityEngine.Events; + +namespace MiraAPI.LocalSettings; + +/// +/// Represents a local settings tab. Gets a button by default +/// +public class LocalSettingsTab +{ + public string TabName { get; } + public Color TabColor { get; } + public Color TabHoverColor { get; } + public Color TabActiveColor { get; } + public LoadableAsset? TabIcon { get; } + + public TabGroup? TabButton { get; protected set; } + private bool ShouldCreateButton { get; set; } + + public List Settings { get; } + + /// + public LocalSettingsTab(string tabName) : this( + tabName, + Color.white, + Palette.AcceptedGreen, + Palette.AcceptedGreen, + createButton: true) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name of the tab. + /// The color of the tab. + /// The hover color of the tab. + /// The active color of the tab. + /// The tab icon. Defaults to no icon. + /// Should the tab button be created. Without it the tab needs to be opened manually. + public LocalSettingsTab(string tabName, Color tabColor, Color tabHoverColor, Color tabActiveColor, LoadableAsset? tabIcon = null, bool createButton = false) + { + TabName = tabName; + TabColor = tabColor; + TabHoverColor = tabHoverColor; + TabActiveColor = tabActiveColor; + TabIcon = tabIcon; + ShouldCreateButton = createButton; + Settings = new(); + } + + public GameObject? CreateTab(OptionsMenuBehaviour instance, ref int tabIdx, ref float offset) + { + var tab = Object.Instantiate(instance.transform.FindChild("GeneralTab").gameObject, instance.transform); + tab.name = $"{TabName}Tab"; + tab.transform.DestroyChildren(); + tab.gameObject.SetActive(false); + + if (TabButton != null) + { + TabButton.Content = tab.gameObject; + } + + var toggle = instance.transform.FindChild("GeneralTab").FindChild("ChatGroup").FindChild("CensorChatButton").GetComponent(); + var slider = instance.transform.FindChild("GeneralTab").FindChild("SoundGroup").FindChild("SFXSlider").GetComponent(); + + float contentOffset = 0; + var contentOrder = 1; + var contentIndex = 1; + foreach (var setting in Settings) + { + setting.CreateOption( + toggle, + slider, + tab.transform, + ref contentOffset, + ref contentOrder, + contentIndex == Settings.Count); + contentIndex++; + } + + if (ShouldCreateButton) + { + var tabButtonObject = Object.Instantiate(instance.Tabs[0], instance.transform); + tabButtonObject.name = $"{TabName} Button"; + tabButtonObject.transform.localPosition = new Vector3(2.4f, 2.1f - offset, 5.5f); + tabButtonObject.transform.localScale = new Vector3(1.25f, 1.25f, 1); + tabButtonObject.Button.color = TabColor; + TabButton = tabButtonObject; + + var tabButtonText = tabButtonObject.transform.FindChild("Text_TMP").GetComponent(); + tabButtonText.GetComponent().Destroy(); // i hate text translators + tabButtonText.transform.localPosition = new Vector3(0.078f, 0, 0); + tabButtonText.transform.localScale = new Vector3(0.9f, 0.9f); + tabButtonText.alignment = TextAlignmentOptions.Right; + tabButtonText.text = $"{GetShortName(TabName)}"; + + var tabButton = tabButtonObject.GetComponent(); + var rollover = tabButtonObject.Rollover; + rollover.OverColor = TabColor; + rollover.OutColor = TabColor; + + SpriteRenderer tabButtonRend = null; + if (TabIcon != null) + { + tabButtonRend = new GameObject("sprite").AddComponent(); + tabButtonRend.gameObject.layer = 5; // ui layer + tabButtonRend.transform.SetParent(tabButtonObject.transform); + tabButtonRend.transform.localPosition = new Vector3(0.5f, 0, -2); + tabButtonRend.transform.localScale = new Vector3(0.5f, 0.5f, 1); + tabButtonRend.sprite = TabIcon.LoadAsset(); + tabButtonText.gameObject.SetActive(false); + } + + int tabIndex = tabIdx; // Local copies of the tab variables so we can use them in the lambdas + float tabOffset = offset; + tabButton.OnClick.AddListener((UnityAction)(() => + { + instance.OpenTabGroup(tabIndex + 10); + })); + tabButton.OnMouseOver.AddListener((UnityAction)(() => + { + tabButton.transform.localPosition = new Vector3(3.55f, 2.1f - tabOffset, 5.5f); + + if (tabButtonRend != null) + { + tabButtonRend.gameObject.SetActive(false); + } + tabButtonText.gameObject.SetActive(true); + tabButtonText.transform.localPosition = new Vector3(0.045f, 0, 0); + tabButtonText.maxVisibleCharacters = int.MaxValue; + tabButtonText.text = TabName; + })); + tabButton.OnMouseOut.AddListener((UnityAction)(() => + { + tabButton.transform.localPosition = new Vector3(2.4f, 2.1f - tabOffset, 5.5f); + + if (tabButtonRend != null) + { + tabButtonRend.gameObject.SetActive(true); + tabButtonText.gameObject.SetActive(false); + } + tabButtonText.transform.localPosition = new Vector3(0.1f, 0, 0); + tabButtonText.maxVisibleCharacters = 4; + tabButtonText.text = $"{GetShortName(TabName)}"; + })); + } + + return tab; + } + + private static string GetShortName(string name) + { + string shortName = string.Empty; + name.Split(' ').Do(x => shortName += x[0]); + return shortName; + } +} diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSetingToggle.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSetingToggle.cs new file mode 100644 index 0000000..a2443dd --- /dev/null +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSetingToggle.cs @@ -0,0 +1,117 @@ +using BepInEx.Configuration; +using Reactor.Localization.Utilities; +using TMPro; +using UnityEngine; +using UnityEngine.Events; + +namespace MiraAPI.LocalSettings.SettingTypes; + +public class LocalSetingToggle : LocalSettingBase +{ + /// + /// Gets the color of the toggle. + /// + public Color ToggleColor { get; } + + /// + /// Gets the color of the toggle when hovered. + /// + public Color ToggleHoverColor { get; } + + /// + /// Gets the color of the toggle when enabled. + /// + public Color ToggleActiveColor { get; } + + /// + public LocalSetingToggle(LocalSettingsTab tab, ConfigEntryBase configEntry, string name = null, string description = null) : this( + tab, + configEntry, + name, + description, + Color.white, + Palette.AcceptedGreen, + Palette.AcceptedGreen) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The binded config entry. + /// The name of the setting. Defaults to ConfigEntry key. + /// The desription of the setting. No description by default. + /// The color of the toggle. + /// The hover color of the toggle. + /// The active color of the toggle. + public LocalSetingToggle( + LocalSettingsTab tab, + ConfigEntryBase configEntry, + string name, + string description, + Color toggleColor, + Color toggleHoverColor, + Color toggleActiveColor) + : base(tab, configEntry, name, description) + { + ToggleColor = toggleColor; + ToggleHoverColor = toggleHoverColor; + ToggleActiveColor = toggleActiveColor; + } + + public override GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last) + { + var toggleObject = Object.Instantiate(toggle, parent).GetComponent(); + var tmp = toggleObject.transform.FindChild("Text_TMP").GetComponent(); + var passiveButton = toggleObject.GetComponent(); + var rollover = toggleObject.GetComponent(); + toggleObject.gameObject.SetActive(true); + + if (last && order == 1) + { + // Toggle in the middle + toggleObject.transform.localPosition = new Vector3(0, 1.85f - offset); + } + else + { + toggleObject.transform.localPosition = new Vector3(order == 1 ? -1.35f : 1.28f, 1.85f - offset); + } + + toggleObject.BaseText = CustomStringName.CreateAndRegister(Name); + toggleObject.UpdateText(GetValue()); + toggleObject.name = Name; + toggleObject.Background.color = GetValue() ? ToggleActiveColor : ToggleColor; + passiveButton.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); + rollover.OverColor = ToggleHoverColor; + + passiveButton.OnClick.AddListener((UnityAction)(() => + { + SetValue(!GetValue()); + toggleObject.UpdateText(GetValue()); + toggleObject.Background.color = GetValue() ? ToggleActiveColor : ToggleColor; + })); + passiveButton.OnMouseOver.AddListener((UnityAction)(() => + { + if (!Description.IsNullOrWhiteSpace()) + { + tmp.text = Description; + } + })); + passiveButton.OnMouseOut.AddListener((UnityAction)(() => + { + toggleObject.UpdateText(GetValue()); + toggleObject.Background.color = GetValue() ? ToggleActiveColor : ToggleColor; + })); + + order++; + if (order > 2 && !last) + { + offset += 0.5f; + order = 1; + } + if (last) + offset += 0.6f; + + return toggleObject.gameObject; + } +} diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs new file mode 100644 index 0000000..5634ecf --- /dev/null +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs @@ -0,0 +1,54 @@ +using BepInEx.Configuration; +using TMPro; +using UnityEngine; + +namespace MiraAPI.LocalSettings.SettingTypes; + +public abstract class LocalSettingBase : ILocalSetting +{ + public string Name { get; } + public string Description { get; } + + public GameObject? Setting { get; } + public ConfigEntryBase ConfigEntry { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The config entry. + /// The name of the setting. + /// The description of the setting. + protected LocalSettingBase(LocalSettingsTab tab, ConfigEntryBase configEntry, string name = null, string description = null) + { + ConfigEntry = configEntry; + Name = name ?? ConfigEntry.Definition.Key; + Description = description; + + tab.Settings.Add(this); + } + + /// + /// Creates an instance of the setting. + /// + /// The transform parent of the setting. + /// The created setting. + public abstract GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last); + + /// + /// Gets the value of the config entry, casted to T. + /// + /// The value. + public T GetValue() + { + return (T)ConfigEntry.BoxedValue; + } + + /// + /// Sets the value of the config entry + /// + /// The value to set to. + public void SetValue(T value) + { + ConfigEntry.BoxedValue = value; + } +} From 5fb59408fa4ec388cd3a1ca93505a2846628b457 Mon Sep 17 00:00:00 2001 From: xChipseq Date: Sun, 31 Aug 2025 17:51:02 +0200 Subject: [PATCH 02/12] wip --- MiraAPI.Example/ExampleLocalSetings.cs | 13 ++---- MiraAPI.Example/ExamplePlugin.cs | 6 ++- .../Attributes/LocalSettingAttribute.cs | 15 ++++++- .../Attributes/LocalSettingToggleAttribute.cs | 17 ++++++- MiraAPI/LocalSettings/LocalSettingsTab.cs | 20 +++------ .../SettingTypes/LocalSettingBase.cs | 8 ++-- ...lSetingToggle.cs => LocalSettingToggle.cs} | 36 ++++++--------- MiraAPI/PluginLoading/MiraPluginManager.cs | 45 +++++++++++++++++++ 8 files changed, 105 insertions(+), 55 deletions(-) rename MiraAPI/LocalSettings/SettingTypes/{LocalSetingToggle.cs => LocalSettingToggle.cs} (83%) diff --git a/MiraAPI.Example/ExampleLocalSetings.cs b/MiraAPI.Example/ExampleLocalSetings.cs index 0feb0f0..c682617 100644 --- a/MiraAPI.Example/ExampleLocalSetings.cs +++ b/MiraAPI.Example/ExampleLocalSetings.cs @@ -7,13 +7,8 @@ namespace MiraAPI.Example; public static class ExampleLocalSetings { - public static LocalSettingsTab ModTab { get; } = new LocalSettingsTab("Example Tab"); + public static LocalSettingsTab ModTab { get; } = new("Example Tab"); - [LocalSettingToggle] - public static ConfigEntry ExampleToggleSetting { get; set; } - - internal static void BindSettings(ConfigFile config) - { - ExampleToggleSetting = config.Bind("General", "Example Toggle", false); - } -} \ No newline at end of file + [LocalSetting] + public static ConfigEntry ExampleToggleSetting { get; private set; } +} diff --git a/MiraAPI.Example/ExamplePlugin.cs b/MiraAPI.Example/ExamplePlugin.cs index 19d23e7..3819347 100644 --- a/MiraAPI.Example/ExamplePlugin.cs +++ b/MiraAPI.Example/ExamplePlugin.cs @@ -1,4 +1,5 @@ -using BepInEx; +using System.Linq; +using BepInEx; using BepInEx.Configuration; using BepInEx.Unity.IL2CPP; using HarmonyLib; @@ -21,8 +22,9 @@ public partial class ExamplePlugin : BasePlugin, IMiraPlugin public ConfigFile GetConfigFile() => Config; public override void Load() { - ExampleLocalSetings.BindSettings(Config); ExampleEventHandlers.Initialize(); Harmony.PatchAll(); + + Config.Keys. } } diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs index 3d72d3c..6735d4e 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs @@ -1,11 +1,22 @@ using System; +using BepInEx.Configuration; +using UnityEngine; namespace MiraAPI.LocalSettings.Attributes; /// /// Base class for all local settings attributes. /// +/// The local setting type the attribute handles. [AttributeUsage(AttributeTargets.Property)] -public abstract class LocalSettingAttribute : Attribute +public abstract class LocalSettingAttribute( + LocalSettingsTab tab, + string? name = null, + string? description = null) : Attribute { -} + /// + /// Returns the created setting object. + /// + /// The created class. + protected abstract GameObject CreateSetting(); +} \ No newline at end of file diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs index 428acf1..7a8cd34 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs @@ -1,4 +1,7 @@ using System; +using BepInEx.Configuration; +using MiraAPI.LocalSettings.SettingTypes; +using UnityEngine; namespace MiraAPI.LocalSettings.Attributes; @@ -6,6 +9,18 @@ namespace MiraAPI.LocalSettings.Attributes; /// Creates a toggle and binds it with the config entry. /// [AttributeUsage(AttributeTargets.Property)] -public class LocalSettingToggleAttribute : LocalSettingAttribute +public class LocalSettingToggleAttribute( + LocalSettingsTab tab, + string? name = null, + string? description = null, + Color? toggleColor = null, + Color? toggleHoverColor = null, + Color? toggleActiveColor = null + ) : LocalSettingAttribute(tab, name, description) { + /// + protected override LocalSettingToggle CreateSetting(ConfigEntryBase configEntry) + { + return new LocalSettingToggle(tab, configEntry, name, description, toggleColor, toggleHoverColor, toggleActiveColor); + } } diff --git a/MiraAPI/LocalSettings/LocalSettingsTab.cs b/MiraAPI/LocalSettings/LocalSettingsTab.cs index 8a6b2dc..e956589 100644 --- a/MiraAPI/LocalSettings/LocalSettingsTab.cs +++ b/MiraAPI/LocalSettings/LocalSettingsTab.cs @@ -25,16 +25,6 @@ public class LocalSettingsTab public List Settings { get; } - /// - public LocalSettingsTab(string tabName) : this( - tabName, - Color.white, - Palette.AcceptedGreen, - Palette.AcceptedGreen, - createButton: true) - { - } - /// /// Initializes a new instance of the class. /// @@ -44,18 +34,18 @@ public class LocalSettingsTab /// The active color of the tab. /// The tab icon. Defaults to no icon. /// Should the tab button be created. Without it the tab needs to be opened manually. - public LocalSettingsTab(string tabName, Color tabColor, Color tabHoverColor, Color tabActiveColor, LoadableAsset? tabIcon = null, bool createButton = false) + public LocalSettingsTab(string tabName, Color? tabColor = null, Color? tabHoverColor = null, Color? tabActiveColor = null, LoadableAsset? tabIcon = null, bool createButton = true) { TabName = tabName; - TabColor = tabColor; - TabHoverColor = tabHoverColor; - TabActiveColor = tabActiveColor; + TabColor = tabColor ?? Color.white; + TabHoverColor = tabHoverColor ?? Palette.AcceptedGreen; + TabActiveColor = tabActiveColor ?? Palette.AcceptedGreen; TabIcon = tabIcon; ShouldCreateButton = createButton; Settings = new(); } - public GameObject? CreateTab(OptionsMenuBehaviour instance, ref int tabIdx, ref float offset) + internal GameObject? CreateTab(OptionsMenuBehaviour instance, ref int tabIdx, ref float offset) { var tab = Object.Instantiate(instance.transform.FindChild("GeneralTab").gameObject, instance.transform); tab.name = $"{TabName}Tab"; diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs index 5634ecf..e93131a 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs @@ -15,14 +15,15 @@ public abstract class LocalSettingBase : ILocalSetting /// /// Initializes a new instance of the class. /// + /// The tab to create the setting in. /// The config entry. /// The name of the setting. /// The description of the setting. - protected LocalSettingBase(LocalSettingsTab tab, ConfigEntryBase configEntry, string name = null, string description = null) + protected LocalSettingBase(LocalSettingsTab tab, ConfigEntryBase configEntry, string? name = null, string? description = null) { ConfigEntry = configEntry; Name = name ?? ConfigEntry.Definition.Key; - Description = description; + Description = description ?? ConfigEntry.Description.Description; tab.Settings.Add(this); } @@ -30,12 +31,11 @@ public abstract class LocalSettingBase : ILocalSetting /// /// Creates an instance of the setting. /// - /// The transform parent of the setting. /// The created setting. public abstract GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last); /// - /// Gets the value of the config entry, casted to T. + /// Gets the value of the config entry, casted to the setting type. /// /// The value. public T GetValue() diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSetingToggle.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs similarity index 83% rename from MiraAPI/LocalSettings/SettingTypes/LocalSetingToggle.cs rename to MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs index a2443dd..00babc1 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalSetingToggle.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs @@ -6,7 +6,10 @@ using UnityEngine.Events; namespace MiraAPI.LocalSettings.SettingTypes; -public class LocalSetingToggle : LocalSettingBase +/// +/// Local setting class for toggles. +/// +public class LocalSettingToggle : LocalSettingBase { /// /// Gets the color of the toggle. @@ -23,40 +26,29 @@ public class LocalSetingToggle : LocalSettingBase /// public Color ToggleActiveColor { get; } - /// - public LocalSetingToggle(LocalSettingsTab tab, ConfigEntryBase configEntry, string name = null, string description = null) : this( - tab, - configEntry, - name, - description, - Color.white, - Palette.AcceptedGreen, - Palette.AcceptedGreen) - { - } - /// /// Initializes a new instance of the class. /// + /// /// Tab that the toggle belongs to. /// The binded config entry. /// The name of the setting. Defaults to ConfigEntry key. /// The desription of the setting. No description by default. /// The color of the toggle. /// The hover color of the toggle. /// The active color of the toggle. - public LocalSetingToggle( + public LocalSettingToggle( LocalSettingsTab tab, ConfigEntryBase configEntry, - string name, - string description, - Color toggleColor, - Color toggleHoverColor, - Color toggleActiveColor) + string? name = null, + string? description = null, + Color? toggleColor = null, + Color? toggleHoverColor = null, + Color? toggleActiveColor = null) : base(tab, configEntry, name, description) { - ToggleColor = toggleColor; - ToggleHoverColor = toggleHoverColor; - ToggleActiveColor = toggleActiveColor; + ToggleColor = toggleColor ?? Color.white; + ToggleHoverColor = toggleHoverColor ?? Palette.AcceptedGreen; + ToggleActiveColor = toggleActiveColor ?? Palette.AcceptedGreen; } public override GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last) diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index 759a0fd..aa6dfcd 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -4,6 +4,7 @@ using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using BepInEx.Configuration; using BepInEx.Unity.IL2CPP; using MiraAPI.Colors; using MiraAPI.Events; @@ -12,6 +13,7 @@ using MiraAPI.GameOptions; using MiraAPI.GameOptions.Attributes; using MiraAPI.Hud; using MiraAPI.Keybinds; +using MiraAPI.LocalSettings.Attributes; using MiraAPI.Modifiers; using MiraAPI.Presets; using MiraAPI.Roles; @@ -109,6 +111,7 @@ public sealed class MiraPluginManager continue; } + RegisterLocalSettings(type); RegisterColorClasses(type); } @@ -317,4 +320,46 @@ public sealed class MiraPluginManager return false; } + + private static void RegisterLocalSettings(Type type) + { + try + { + if (!type.IsStatic()) + { + Logger.Error($"The type {type.Name} has to be static for local settings."); + return; + } + + foreach (var property in type.GetProperties()) + { + if (property.PropertyType.IsAssignableTo(typeof(ConfigEntryBase))) + { + continue; + } + + if (property.GetValue(null) is not ConfigEntryBase configEntry) + { + continue; + } + + var attribute = property.GetCustomAttribute(); + if (attribute == null) + { + continue; + } + + attribute.Create(configEntry); + } + + foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(ConfigEntryBase)) && f.GetCustomAttribute() != null)) + { + Logger.Error($"{field.Name} is a field, not a property. Use properties for local settings."); + } + } + catch (Exception e) + { + Logger.Error($"Failed to register local settings from {type.Name}: {e}"); + } + } } From b4e6e448f2b024f3505838698d285a1d8a31edec Mon Sep 17 00:00:00 2001 From: ang-xd Date: Sun, 31 Aug 2025 18:04:52 -0400 Subject: [PATCH 03/12] Major work/rewrite on local settings --- MiraAPI.Example/ExampleLocalSetings.cs | 35 +- MiraAPI.Example/ExamplePlugin.cs | 2 - .../Attributes/LocalSettingAttribute.cs | 9 +- .../Attributes/LocalSettingToggleAttribute.cs | 12 +- MiraAPI/LocalSettings/ILocalSetting.cs | 26 ++ MiraAPI/LocalSettings/LocalSettingsManager.cs | 42 +++ MiraAPI/LocalSettings/LocalSettingsTab.cs | 309 +++++++++++------- .../LocalSettingsTabSingleton.cs | 20 ++ .../SettingTypes/LocalSettingBase.cs | 16 +- .../SettingTypes/LocalSettingToggle.cs | 11 +- MiraAPI/MiraApiPlugin.cs | 5 - .../{MiraApiConfig.cs => MiraApiSettings.cs} | 12 +- MiraAPI/Modifiers/ModifierComponent.cs | 3 +- .../ModifierDisplayComponent.cs | 3 +- .../LocalSettings/OptionsMenuPatches.cs | 116 +++++++ .../LocalSettings/ToggleBehaviourPatch.cs | 37 +++ .../Patches/Options/GamePresetsTabPatches.cs | 21 +- MiraAPI/PluginLoading/MiraPluginInfo.cs | 1 + MiraAPI/PluginLoading/MiraPluginManager.cs | 47 ++- MiraAPI/Resources/Settings.png | Bin 0 -> 9598 bytes MiraAPI/Utilities/Assets/MiraAssets.cs | 5 + MiraAPI/Utilities/Helpers.cs | 51 +++ 22 files changed, 604 insertions(+), 179 deletions(-) create mode 100644 MiraAPI/LocalSettings/LocalSettingsManager.cs create mode 100644 MiraAPI/LocalSettings/LocalSettingsTabSingleton.cs rename MiraAPI/{MiraApiConfig.cs => MiraApiSettings.cs} (62%) create mode 100644 MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs create mode 100644 MiraAPI/Patches/LocalSettings/ToggleBehaviourPatch.cs create mode 100644 MiraAPI/Resources/Settings.png diff --git a/MiraAPI.Example/ExampleLocalSetings.cs b/MiraAPI.Example/ExampleLocalSetings.cs index c682617..fb07869 100644 --- a/MiraAPI.Example/ExampleLocalSetings.cs +++ b/MiraAPI.Example/ExampleLocalSetings.cs @@ -5,10 +5,37 @@ using Reactor.Utilities; namespace MiraAPI.Example; -public static class ExampleLocalSetings +public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config) { - public static LocalSettingsTab ModTab { get; } = new("Example Tab"); + public override string TabName => "Mira Example"; - [LocalSetting] - public static ConfigEntry ExampleToggleSetting { get; private set; } + [LocalSettingToggle] + public ConfigEntry PlayStartupSound { get; private set; } = config.Bind("Audio", "Play Startup Sound", true); + + [LocalSettingToggle] + public ConfigEntry MuteInBackground { get; private set; } = config.Bind("Audio", "Mute In Background", false); + + [LocalSettingToggle] + public ConfigEntry AutoSaveEnabled { get; private set; } = config.Bind("General", "Auto Save Enabled", true); + + [LocalSettingToggle] + public ConfigEntry EnableCloudSync { get; private set; } = config.Bind("General", "Enable Cloud Sync", false); + + [LocalSettingToggle] + public ConfigEntry ShowAdvancedTooltips { get; private set; } = config.Bind("UI", "Show Advanced Tooltips", true); + + [LocalSettingToggle] + public ConfigEntry CompactUI { get; private set; } = config.Bind("UI", "Compact UI Layout", false); + + [LocalSettingToggle] + public ConfigEntry EnableControllerSupport { get; private set; } = config.Bind("Input", "Enable Controller Support", true); + + [LocalSettingToggle] + public ConfigEntry EnableVibration { get; private set; } = config.Bind("Input", "Enable Controller Vibration", true); + + [LocalSettingToggle] + public ConfigEntry ShowPlayerNames { get; private set; } = config.Bind("Gameplay", "Show Player Names", true); + + [LocalSettingToggle] + public ConfigEntry EnableHardMode { get; private set; } = config.Bind("Gameplay", "Enable Hard Mode", false); } diff --git a/MiraAPI.Example/ExamplePlugin.cs b/MiraAPI.Example/ExamplePlugin.cs index 3819347..0aed9c4 100644 --- a/MiraAPI.Example/ExamplePlugin.cs +++ b/MiraAPI.Example/ExamplePlugin.cs @@ -24,7 +24,5 @@ public partial class ExamplePlugin : BasePlugin, IMiraPlugin { ExampleEventHandlers.Initialize(); Harmony.PatchAll(); - - Config.Keys. } } diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs index 6735d4e..1b101c5 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs @@ -1,22 +1,21 @@ using System; using BepInEx.Configuration; -using UnityEngine; namespace MiraAPI.LocalSettings.Attributes; /// /// Base class for all local settings attributes. /// -/// The local setting type the attribute handles. [AttributeUsage(AttributeTargets.Property)] public abstract class LocalSettingAttribute( - LocalSettingsTab tab, string? name = null, string? description = null) : Attribute { /// /// Returns the created setting object. /// + /// Gets the tab where the setting is located. + /// Gets the config entry the setting is attached to. /// The created class. - protected abstract GameObject CreateSetting(); -} \ No newline at end of file + internal abstract ILocalSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase); +} diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs index 7a8cd34..7b44f7f 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs @@ -10,17 +10,13 @@ namespace MiraAPI.LocalSettings.Attributes; /// [AttributeUsage(AttributeTargets.Property)] public class LocalSettingToggleAttribute( - LocalSettingsTab tab, string? name = null, - string? description = null, - Color? toggleColor = null, - Color? toggleHoverColor = null, - Color? toggleActiveColor = null - ) : LocalSettingAttribute(tab, name, description) + string? description = null + ) : LocalSettingAttribute(name, description) { /// - protected override LocalSettingToggle CreateSetting(ConfigEntryBase configEntry) + internal override LocalSettingToggle CreateSetting(Type tab, ConfigEntryBase configEntryBase) { - return new LocalSettingToggle(tab, configEntry, name, description, toggleColor, toggleHoverColor, toggleActiveColor); + return new LocalSettingToggle(tab, configEntryBase, name, description); } } diff --git a/MiraAPI/LocalSettings/ILocalSetting.cs b/MiraAPI/LocalSettings/ILocalSetting.cs index 78d3647..e6649bb 100644 --- a/MiraAPI/LocalSettings/ILocalSetting.cs +++ b/MiraAPI/LocalSettings/ILocalSetting.cs @@ -8,9 +8,35 @@ namespace MiraAPI.LocalSettings; /// public interface ILocalSetting { + /// + /// Gets the preferred name of a setting. + /// string Name { get; } + + /// + /// Gets the description of a setting. + /// string Description { get; } + + /// + /// Gets the setting's gameobject. + /// GameObject? Setting { get; } + + /// + /// Gets the setting's config entry. + /// ConfigEntryBase ConfigEntry { get; } + + /// + /// Used to create the setting + /// + /// Toggle template. + /// Slider template. + /// The parent of the setting. + /// The offset. + /// The order. + /// Whether it's the last on the row. + /// The created setting. GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last); } diff --git a/MiraAPI/LocalSettings/LocalSettingsManager.cs b/MiraAPI/LocalSettings/LocalSettingsManager.cs new file mode 100644 index 0000000..3b75d05 --- /dev/null +++ b/MiraAPI/LocalSettings/LocalSettingsManager.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using BepInEx.Unity.IL2CPP; +using MiraAPI.GameOptions; +using Reactor.Utilities; + +namespace MiraAPI.LocalSettings; + +/// +/// Manages local settings. +/// +public static class LocalSettingsManager +{ + internal static readonly Dictionary TypeToTab = []; + internal static readonly List Tabs = []; + + internal static bool RegisterTab(Type type, BasePlugin pluginInfo) + { + if (Activator.CreateInstance(type, pluginInfo.Config) is not LocalSettingsTab tab) + { + return false; + } + + if (TypeToTab.ContainsKey(type)) + { + Logger.Error($"Local settings tab {type.Name} already exists."); + return false; + } + + Tabs.Add(tab); + TypeToTab.Add(type, tab); + + typeof(LocalSettingsTabSingleton<>).MakeGenericType(type) +#pragma warning disable S3011 + .GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)! +#pragma warning restore S3011 + .SetValue(null, tab); + + return true; + } +} diff --git a/MiraAPI/LocalSettings/LocalSettingsTab.cs b/MiraAPI/LocalSettings/LocalSettingsTab.cs index e956589..a7e2ea7 100644 --- a/MiraAPI/LocalSettings/LocalSettingsTab.cs +++ b/MiraAPI/LocalSettings/LocalSettingsTab.cs @@ -1,154 +1,231 @@ -using System.Collections.Generic; -using System.Linq; +using System; +using System.Collections.Generic; +using BepInEx.Configuration; using HarmonyLib; +using MiraAPI.Patches.LocalSettings; +using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; using UnityEngine.Events; +using Object = UnityEngine.Object; namespace MiraAPI.LocalSettings; /// /// Represents a local settings tab. Gets a button by default /// -public class LocalSettingsTab +public abstract class LocalSettingsTab(ConfigFile config) { - public string TabName { get; } - public Color TabColor { get; } - public Color TabHoverColor { get; } - public Color TabActiveColor { get; } - public LoadableAsset? TabIcon { get; } - - public TabGroup? TabButton { get; protected set; } - private bool ShouldCreateButton { get; set; } - - public List Settings { get; } + /// + /// Gets the name of the tab + /// + public abstract string TabName { get; } /// - /// Initializes a new instance of the class. + /// Gets the tab button icon. /// - /// The name of the tab. - /// The color of the tab. - /// The hover color of the tab. - /// The active color of the tab. - /// The tab icon. Defaults to no icon. - /// Should the tab button be created. Without it the tab needs to be opened manually. - public LocalSettingsTab(string tabName, Color? tabColor = null, Color? tabHoverColor = null, Color? tabActiveColor = null, LoadableAsset? tabIcon = null, bool createButton = true) - { - TabName = tabName; - TabColor = tabColor ?? Color.white; - TabHoverColor = tabHoverColor ?? Palette.AcceptedGreen; - TabActiveColor = tabActiveColor ?? Palette.AcceptedGreen; - TabIcon = tabIcon; - ShouldCreateButton = createButton; - Settings = new(); - } + public virtual LoadableAsset? TabIcon => null; + + /// + /// Gets the color of the tab button. + /// + public virtual Color TabColor => Color.white; + + /// + /// Gets the color of the tab button when hovered. + /// + public Color TabHoverColor => Palette.AcceptedGreen; + + /// + /// Gets the color of the tab button when active. + /// + public virtual Color TabActiveColor => Palette.AcceptedGreen; + + /// + /// Gets a value indicating whether a button should be created for the tab. + /// + protected virtual bool ShouldCreateButton => true; + + /// + /// Gets the mod's config file. Automatically found in the plugin loader. + /// + public ConfigFile Config { get; } = config ?? throw new ArgumentNullException(nameof(config)); + + internal TabGroup? TabButton { get; set; } + internal List Settings { get; } = []; + private Scroller? Scroller { get; set; } internal GameObject? CreateTab(OptionsMenuBehaviour instance, ref int tabIdx, ref float offset) { - var tab = Object.Instantiate(instance.transform.FindChild("GeneralTab").gameObject, instance.transform); - tab.name = $"{TabName}Tab"; - tab.transform.DestroyChildren(); - tab.gameObject.SetActive(false); + if (ShouldCreateButton) + { + CreateTabButton(instance, ref tabIdx, ref offset); + } - if (TabButton != null) + var tab = Object.Instantiate(instance.transform.FindChild("GeneralTab").gameObject, instance.transform); + tab.name = $"{TabName}Tab"; + tab.transform.DestroyChildren(); + tab.gameObject.SetActive(false); + + if (Scroller == null) + { + Scroller = Helpers.CreateScroller(tab.transform, OptionsMenuPatches.MaskCollider); + } + + if (TabButton != null) + { + TabButton.Content = tab.gameObject; + } + + var generalLabel = instance.transform.FindChild("GeneralTab").FindChild("ControlGroup") + .FindChild("ControlText_TMP").gameObject; + var toggle = instance.transform.FindChild("GeneralTab").FindChild("ChatGroup").FindChild("CensorChatButton").GetComponent(); + var slider = instance.transform.FindChild("GeneralTab").FindChild("SoundGroup").FindChild("SFXSlider").GetComponent(); + + Dictionary> entriesByGroup = new(); + foreach (var entry in Settings) + { + var group = entry.ConfigEntry.Definition.Section; + if (!entriesByGroup.ContainsKey(group)) + entriesByGroup.Add(group, []); + + entriesByGroup[group].Add(entry); + } + + float contentOffset = 0; + var contentOrder = 1; + var contentIndex = 1; + + foreach (var pair in entriesByGroup) + { + CreateLabel(generalLabel, Scroller.Inner, pair.Key, ref contentOffset); + + foreach (var setting in pair.Value) + { + var obj = setting.CreateOption( + toggle, + slider, + Scroller.Inner, + ref contentOffset, + ref contentOrder, + contentIndex == pair.Value.Count); + + obj!.GetComponentsInChildren().Do(x => + { + x.maskInteraction = SpriteMaskInteraction.VisibleInsideMask; + x.sortingOrder = 150; + x.sortingLayerName = "Default"; + }); + obj.GetComponentsInChildren().Do(x => { x.ClickMask = OptionsMenuPatches.MaskCollider; }); + + contentIndex++; + } + + contentOrder = 1; + } + + Scroller.SetBounds(new FloatRange(0, Settings.Count * 0.5f - 5f), new FloatRange(0, 0)); + return tab; + } + + private void CreateTabButton(OptionsMenuBehaviour instance, ref int tabIdx, ref float offset) + { + var tabButtonObject = Object.Instantiate(instance.Tabs[0], instance.transform); + tabButtonObject.name = $"{TabName} Button"; + tabButtonObject.transform.localPosition = new Vector3(2.4f, 2.1f - offset, 5.5f); + tabButtonObject.transform.localScale = new Vector3(1.25f, 1.25f, 1); + tabButtonObject.Button.color = TabColor; + TabButton = tabButtonObject; + + var tabButtonText = tabButtonObject.transform.FindChild("Text_TMP").GetComponent(); + tabButtonText.GetComponent().Destroy(); // i hate text translators + tabButtonText.transform.localPosition = new Vector3(0.078f, 0, 0); + tabButtonText.transform.localScale = new Vector3(0.9f, 0.9f); + tabButtonText.alignment = TextAlignmentOptions.Right; + tabButtonText.text = $"{GetShortName(TabName)}"; + + var tabButton = tabButtonObject.GetComponent(); + var rollover = tabButtonObject.Rollover; + rollover.OverColor = TabColor; + rollover.OutColor = TabColor; + + SpriteRenderer? tabButtonRend = null; + if (TabIcon != null) { - TabButton.Content = tab.gameObject; + tabButtonRend = new GameObject("sprite").AddComponent(); + tabButtonRend.gameObject.layer = 5; // ui layer + tabButtonRend.transform.SetParent(tabButtonObject.transform); + tabButtonRend.transform.localPosition = new Vector3(0.5f, 0, -2); + tabButtonRend.transform.localScale = new Vector3(0.2f, 0.2f, 1); + tabButtonRend.sprite = TabIcon.LoadAsset(); + tabButtonText.gameObject.SetActive(false); } - var toggle = instance.transform.FindChild("GeneralTab").FindChild("ChatGroup").FindChild("CensorChatButton").GetComponent(); - var slider = instance.transform.FindChild("GeneralTab").FindChild("SoundGroup").FindChild("SFXSlider").GetComponent(); - - float contentOffset = 0; - var contentOrder = 1; - var contentIndex = 1; - foreach (var setting in Settings) + var tabIndex = tabIdx; // Local copies of the tab variables so we can use them in the lambdas + var tabOffset = offset; + tabButton.OnClick.AddListener((UnityAction)(() => { instance.OpenTabGroup(tabIndex + 10); })); + tabButton.OnMouseOver.AddListener((UnityAction)(() => { - setting.CreateOption( - toggle, - slider, - tab.transform, - ref contentOffset, - ref contentOrder, - contentIndex == Settings.Count); - contentIndex++; - } - - if (ShouldCreateButton) + tabButton.transform.localPosition = new Vector3(3.55f, 2.1f - tabOffset, 5.5f); + tabButtonObject.Button.color = TabHoverColor; + tabButtonObject.Rollover.OutColor = TabHoverColor; + tabButtonText.gameObject.SetActive(true); + tabButtonText.transform.localPosition = new Vector3(-0.024f, 0, 0); + tabButtonText.maxVisibleCharacters = int.MaxValue; + tabButtonText.text = TabName; + tabButtonText.alignment = TextAlignmentOptions.Left; + })); + tabButton.OnMouseOut.AddListener((UnityAction)(() => { - var tabButtonObject = Object.Instantiate(instance.Tabs[0], instance.transform); - tabButtonObject.name = $"{TabName} Button"; - tabButtonObject.transform.localPosition = new Vector3(2.4f, 2.1f - offset, 5.5f); - tabButtonObject.transform.localScale = new Vector3(1.25f, 1.25f, 1); - tabButtonObject.Button.color = TabColor; - TabButton = tabButtonObject; - - var tabButtonText = tabButtonObject.transform.FindChild("Text_TMP").GetComponent(); - tabButtonText.GetComponent().Destroy(); // i hate text translators - tabButtonText.transform.localPosition = new Vector3(0.078f, 0, 0); - tabButtonText.transform.localScale = new Vector3(0.9f, 0.9f); - tabButtonText.alignment = TextAlignmentOptions.Right; - tabButtonText.text = $"{GetShortName(TabName)}"; - - var tabButton = tabButtonObject.GetComponent(); - var rollover = tabButtonObject.Rollover; - rollover.OverColor = TabColor; - rollover.OutColor = TabColor; - - SpriteRenderer tabButtonRend = null; - if (TabIcon != null) + if (!tabButtonObject.Content.gameObject.activeSelf) { - tabButtonRend = new GameObject("sprite").AddComponent(); - tabButtonRend.gameObject.layer = 5; // ui layer - tabButtonRend.transform.SetParent(tabButtonObject.transform); - tabButtonRend.transform.localPosition = new Vector3(0.5f, 0, -2); - tabButtonRend.transform.localScale = new Vector3(0.5f, 0.5f, 1); - tabButtonRend.sprite = TabIcon.LoadAsset(); + tabButtonObject.Button.color = TabColor; + tabButtonObject.Rollover.OutColor = TabColor; + } + + tabButton.transform.localPosition = new Vector3(2.4f, 2.1f - tabOffset, 5.5f); + tabButtonText.alignment = TextAlignmentOptions.Right; + if (tabButtonRend != null) + { + tabButtonRend.gameObject.SetActive(true); tabButtonText.gameObject.SetActive(false); } - int tabIndex = tabIdx; // Local copies of the tab variables so we can use them in the lambdas - float tabOffset = offset; - tabButton.OnClick.AddListener((UnityAction)(() => - { - instance.OpenTabGroup(tabIndex + 10); - })); - tabButton.OnMouseOver.AddListener((UnityAction)(() => - { - tabButton.transform.localPosition = new Vector3(3.55f, 2.1f - tabOffset, 5.5f); + tabButtonText.transform.localPosition = new Vector3(0.1f, 0, 0); + tabButtonText.maxVisibleCharacters = 4; + tabButtonText.text = $"{GetShortName(TabName)}"; + })); - if (tabButtonRend != null) - { - tabButtonRend.gameObject.SetActive(false); - } - tabButtonText.gameObject.SetActive(true); - tabButtonText.transform.localPosition = new Vector3(0.045f, 0, 0); - tabButtonText.maxVisibleCharacters = int.MaxValue; - tabButtonText.text = TabName; - })); - tabButton.OnMouseOut.AddListener((UnityAction)(() => - { - tabButton.transform.localPosition = new Vector3(2.4f, 2.1f - tabOffset, 5.5f); - - if (tabButtonRend != null) - { - tabButtonRend.gameObject.SetActive(true); - tabButtonText.gameObject.SetActive(false); - } - tabButtonText.transform.localPosition = new Vector3(0.1f, 0, 0); - tabButtonText.maxVisibleCharacters = 4; - tabButtonText.text = $"{GetShortName(TabName)}"; - })); - } - - return tab; + offset += 0.6f; + tabIdx++; } + private static void CreateLabel(GameObject template, Transform parent, string text, ref float offset) + { + var label = Object.Instantiate(template, parent); + label.transform.localPosition = new Vector3(0, 1.85f - offset); + label.GetComponent().Destroy(); + label.name = text; + + var tmp = label.GetComponent(); + tmp.text = $"{text}"; + tmp.alignment = TextAlignmentOptions.Center; + + var meshRend = label.GetComponent(); + meshRend.sortingLayerName = "Default"; + meshRend.sortingOrder = 151; + + offset += 0.5f; + } + + private static string GetShortName(string name) { - string shortName = string.Empty; + var shortName = string.Empty; name.Split(' ').Do(x => shortName += x[0]); return shortName; } } + diff --git a/MiraAPI/LocalSettings/LocalSettingsTabSingleton.cs b/MiraAPI/LocalSettings/LocalSettingsTabSingleton.cs new file mode 100644 index 0000000..1994235 --- /dev/null +++ b/MiraAPI/LocalSettings/LocalSettingsTabSingleton.cs @@ -0,0 +1,20 @@ +using System.Linq; +using MiraAPI.LocalSettings; + +namespace MiraAPI.GameOptions; + +/// +/// Singleton for local settings tabs. +/// +/// The settings tab type. +public static class LocalSettingsTabSingleton where T : LocalSettingsTab +{ + private static T? _instance; + + /// + /// Gets the instance of the setting tab. + /// +#pragma warning disable CA1000 + public static T Instance => _instance ??= LocalSettingsManager.Tabs.OfType().Single(); +#pragma warning restore CA1000 +} diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs index e93131a..e1ebe10 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs @@ -1,15 +1,23 @@ -using BepInEx.Configuration; +using System; +using BepInEx.Configuration; using TMPro; using UnityEngine; namespace MiraAPI.LocalSettings.SettingTypes; +/// public abstract class LocalSettingBase : ILocalSetting { + /// public string Name { get; } + + /// public string Description { get; } - public GameObject? Setting { get; } + /// + public GameObject? Setting { get; } = null!; + + /// public ConfigEntryBase ConfigEntry { get; } /// @@ -19,13 +27,13 @@ public abstract class LocalSettingBase : ILocalSetting /// The config entry. /// The name of the setting. /// The description of the setting. - protected LocalSettingBase(LocalSettingsTab tab, ConfigEntryBase configEntry, string? name = null, string? description = null) + protected LocalSettingBase(Type tab, ConfigEntryBase configEntry, string? name = null, string? description = null) { ConfigEntry = configEntry; Name = name ?? ConfigEntry.Definition.Key; Description = description ?? ConfigEntry.Description.Description; - tab.Settings.Add(this); + LocalSettingsManager.TypeToTab[tab].Settings.Add(this); } /// diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs index 00babc1..fa36333 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs @@ -1,8 +1,11 @@ -using BepInEx.Configuration; +using System; +using BepInEx.Configuration; +using MiraAPI.Utilities; using Reactor.Localization.Utilities; using TMPro; using UnityEngine; using UnityEngine.Events; +using Object = UnityEngine.Object; namespace MiraAPI.LocalSettings.SettingTypes; @@ -37,7 +40,7 @@ public class LocalSettingToggle : LocalSettingBase /// The hover color of the toggle. /// The active color of the toggle. public LocalSettingToggle( - LocalSettingsTab tab, + Type tab, ConfigEntryBase configEntry, string? name = null, string? description = null, @@ -66,7 +69,7 @@ public class LocalSettingToggle : LocalSettingBase } else { - toggleObject.transform.localPosition = new Vector3(order == 1 ? -1.35f : 1.28f, 1.85f - offset); + toggleObject.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset); } toggleObject.BaseText = CustomStringName.CreateAndRegister(Name); @@ -95,6 +98,8 @@ public class LocalSettingToggle : LocalSettingBase toggleObject.Background.color = GetValue() ? ToggleActiveColor : ToggleColor; })); + Helpers.DivideSize(toggleObject.gameObject, 1.1f); + order++; if (order > 2 && !last) { diff --git a/MiraAPI/MiraApiPlugin.cs b/MiraAPI/MiraApiPlugin.cs index aa7f3c6..8654cf6 100644 --- a/MiraAPI/MiraApiPlugin.cs +++ b/MiraAPI/MiraApiPlugin.cs @@ -30,10 +30,6 @@ public partial class MiraApiPlugin : BasePlugin /// public static Color DefaultHeaderColor { get; } = new Color32(77, 77, 77, 255); - /// - /// Gets the Mira Api Config Handler. - /// - public MiraApiConfig? MiraConfig { get; private set; } private static MiraPluginManager? PluginManager { get; set; } internal Harmony Harmony { get; } = new(Id); @@ -44,7 +40,6 @@ public partial class MiraApiPlugin : BasePlugin ReactorCredits.Register("Mira API", Version, true, ReactorCredits.AlwaysShow); - MiraConfig = new MiraApiConfig(Config); PluginManager = new MiraPluginManager(); PluginManager.Initialize(); } diff --git a/MiraAPI/MiraApiConfig.cs b/MiraAPI/MiraApiSettings.cs similarity index 62% rename from MiraAPI/MiraApiConfig.cs rename to MiraAPI/MiraApiSettings.cs index 10c9776..e5fe303 100644 --- a/MiraAPI/MiraApiConfig.cs +++ b/MiraAPI/MiraApiSettings.cs @@ -2,7 +2,10 @@ using BepInEx.Configuration; using BepInEx.Unity.IL2CPP; using HarmonyLib; +using MiraAPI.LocalSettings; +using MiraAPI.LocalSettings.Attributes; using MiraAPI.PluginLoading; +using MiraAPI.Utilities.Assets; using Reactor; using Reactor.Networking; using Reactor.Networking.Attributes; @@ -14,10 +17,17 @@ namespace MiraAPI; /// /// Mira API Config File Handler /// -public class MiraApiConfig(ConfigFile config) +public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config) { + /// + public override string TabName => "Mira API"; + + /// + public override LoadableAsset? TabIcon => MiraAssets.SettingsIcon; + /// /// Gets whether the modifiers hud should be on the left side of the screen (under roles/task tab). Recommended for streamers. /// + [LocalSettingToggle] public ConfigEntry ModifiersHudLeftSide { get; private set; } = config.Bind("Displays", "Show Modifiers HUD on Left Side", false); } diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs index 4f556f5..f4003c9 100644 --- a/MiraAPI/Modifiers/ModifierComponent.cs +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; using Il2CppInterop.Runtime.Attributes; +using MiraAPI.GameOptions; using MiraAPI.Modifiers.ModifierDisplay; using MiraAPI.Modifiers.Types; using MiraAPI.Patches.Roles; @@ -129,7 +130,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr) { var taskPanelOpen = HudManager.Instance.TaskPanel.open; var roleTabOpen = HudManagerPatches.RoleTab != null && HudManagerPatches.RoleTab.open; - var leftSideHud = PluginSingleton.Instance.MiraConfig!.ModifiersHudLeftSide.Value; + var leftSideHud = LocalSettingsTabSingleton.Instance.ModifiersHudLeftSide.Value; if (leftSideHud && (taskPanelOpen || roleTabOpen)) { diff --git a/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs b/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs index 0836935..0cbb8c3 100644 --- a/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs +++ b/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs @@ -4,6 +4,7 @@ using System.Collections.ObjectModel; using System.Linq; using HarmonyLib; using Il2CppInterop.Runtime.Attributes; +using MiraAPI.GameOptions; using MiraAPI.Patches.Roles; using MiraAPI.Utilities.Assets; using Reactor.Utilities; @@ -98,7 +99,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr) IsOpen = false; _children.gameObject.SetActive(false); - if (PluginSingleton.Instance.MiraConfig!.ModifiersHudLeftSide.Value) + if (LocalSettingsTabSingleton.Instance.ModifiersHudLeftSide.Value) { var aspect = GetComponent(); _toggleButton.transform.localPosition = new Vector3(-1f, 2.7f, 0f); diff --git a/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs b/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs new file mode 100644 index 0000000..0b4ad9c --- /dev/null +++ b/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs @@ -0,0 +1,116 @@ +using System.Collections.Generic; +using System.Linq; +using HarmonyLib; +using MiraAPI.LocalSettings; +using Reactor.Localization.Utilities; +using Reactor.Utilities.Extensions; +using TMPro; +using UnityEngine; +using UnityEngine.Events; + +namespace MiraAPI.Patches.LocalSettings; + +[HarmonyPatch(typeof(OptionsMenuBehaviour))] +public static class OptionsMenuPatches +{ + internal static BoxCollider2D MaskCollider; + /// + /// Creates the tabs and their content + /// + [HarmonyPostfix] + [HarmonyPatch(nameof(OptionsMenuBehaviour.Start))] + public static void StartPostfix(OptionsMenuBehaviour __instance) + { + // Fix for tabs not being clickable in the main menu + if (!AmongUsClient.Instance.IsInGame) + { + __instance.Background.GetComponent().enabled = false; + __instance.transform.FindChild("Tint").SetLocalZ(5.6f); + } + + var maskObj = new GameObject + { + layer = 5, + name = "SpriteMask", + }; + maskObj.transform.SetParent(__instance.transform); + maskObj.transform.localPosition = new Vector3(0, -0.3f, 0); + maskObj.transform.localScale = new Vector3(500, 120, 1); + + var blank = Sprite.Create( + Texture2D.whiteTexture, + new Rect(0, 0, Texture2D.whiteTexture.width, Texture2D.whiteTexture.height), + new Vector2(0.5f, 0.5f)); + + var mask = maskObj.AddComponent(); + mask.sprite = blank; + mask.isCustomRangeActive = true; + mask.sortingLayerName = "Default"; + mask.backSortingOrder = 100; + mask.frontSortingOrder = 500; + + MaskCollider = maskObj.AddComponent(); + MaskCollider.size = new Vector2(0.01f, 0.1f); + MaskCollider.isTrigger = true; + MaskCollider.enabled = true; + + float yOffset = 0; + var i = 0; + foreach (var settings in LocalSettingsManager.Tabs) + { + settings.CreateTab(__instance, ref i, ref yOffset); + } + } + + /// + /// Modifies how opening tabs is handled for the custom ones to work + /// + [HarmonyPrefix] + [HarmonyPatch(nameof(OptionsMenuBehaviour.OpenTabGroup))] + public static bool OpenTabGroupPrefix(OptionsMenuBehaviour __instance, ref int index) + { + __instance.Tabs.ToList().ForEach(x => x.Close()); // Close all vanilla tabs + LocalSettingsManager.Tabs.ForEach(CustomClose); // Close all mod tabs + + if (index >= 10) // Tabs with index 10 and above are the mod settings tabs + { + CustomOpen(LocalSettingsManager.Tabs[index - 10]); + return false; + } + + return true; + } + + /// + /// Makes the custom tabs close when opening the options menu + /// + [HarmonyPostfix] + [HarmonyPatch(nameof(OptionsMenuBehaviour.Open))] + public static void OpenPostfix(OptionsMenuBehaviour __instance) + { + LocalSettingsManager.Tabs.ForEach(CustomClose); + } + + private static void CustomOpen(LocalSettingsTab tab) + { + if (tab.TabButton?.Button) + tab.TabButton!.Button.color = tab.TabHoverColor; + + if (tab.TabButton?.Rollover) + tab.TabButton!.Rollover.OutColor = tab.TabHoverColor; + + if (tab.TabButton?.Content) + tab.TabButton!.Content.SetActive(true); + } + private static void CustomClose(LocalSettingsTab tab) + { + if (tab.TabButton?.Button) + tab.TabButton!.Button.color = tab.TabColor; + + if (tab.TabButton?.Rollover) + tab.TabButton!.Rollover.OutColor = tab.TabColor; + + if (tab.TabButton?.Content) + tab.TabButton!.Content.SetActive(false); + } +} diff --git a/MiraAPI/Patches/LocalSettings/ToggleBehaviourPatch.cs b/MiraAPI/Patches/LocalSettings/ToggleBehaviourPatch.cs new file mode 100644 index 0000000..5022d1c --- /dev/null +++ b/MiraAPI/Patches/LocalSettings/ToggleBehaviourPatch.cs @@ -0,0 +1,37 @@ +using HarmonyLib; +using UnityEngine; + +namespace MiraAPI.Patches.LocalSettings; + +[HarmonyPatch(typeof(ToggleButtonBehaviour))] +public static class ToggleBehaviourPatch +{ + /// + /// ResetText was inlined ofc + /// + [HarmonyPrefix] + [HarmonyPatch(nameof(ToggleButtonBehaviour.UpdateText))] + public static bool ResetTextPrefix(ToggleButtonBehaviour __instance, bool on) + { + __instance.onState = on; + var color = on ? new Color(0f, 1f, 0.16470589f, 1f) : Color.white; + __instance.Background.color = color; + + // Used to mask the text + __instance.Text.richText = true; + __instance.Text.text = $"" + + $"{DestroyableSingleton.Instance.GetString(__instance.BaseText)}: " + + $"{DestroyableSingleton.Instance.GetString(__instance.onState ? StringNames.SettingsOn : StringNames.SettingsOff)}"; + + __instance.Text.sortingOrder = 151; + __instance.Text.renderer.sortingLayerName = "Default"; + __instance.Text.renderer.sortingOrder = 151; + + if (__instance.Rollover) + { + __instance.Rollover.ChangeOutColor(color); + } + + return false; + } +} diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index 2b4f77d..ed80bb1 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -6,6 +6,7 @@ using HarmonyLib; using MiraAPI.PluginLoading; using MiraAPI.Presets; using MiraAPI.Roles; +using MiraAPI.Utilities; using MiraAPI.Utilities.Assets; using Reactor.Utilities; using Reactor.Utilities.Extensions; @@ -28,22 +29,6 @@ internal static class GamePresetsTabPatches private static readonly object Lock = new(); - public static void DivideSize(GameObject obj, float amount) - { - foreach (var collider in obj.GetComponentsInChildren(true)) - { - if (collider.TryCast() is { } col) - { - col.size = new Vector2(col.size.x / amount, col.size.y); - } - } - - foreach (var rend in obj.GetComponentsInChildren(true)) - { - rend.size = new Vector2(rend.size.x / amount, rend.size.y); - } - } - [HarmonyPatch(typeof(GamePresetsTab), nameof(GamePresetsTab.OnEnable))] public static class GamePresetsOnEnablePatch { @@ -207,7 +192,7 @@ internal static class GamePresetsTabPatches saveText.transform.parent.localPosition.y, saveText.transform.parent.localPosition.z); - DivideSize(saveButton.gameObject, 2f); + Helpers.DivideSize(saveButton.gameObject, 2f); saveButton.OnClick = new Button.ButtonClickedEvent(); saveButton.OnClick.AddListener( @@ -276,7 +261,7 @@ internal static class GamePresetsTabPatches var iconSpriteRend = icon.AddComponent(); iconSpriteRend.sprite = MiraAssets.RefreshIcon.LoadAsset(); - DivideSize(refreshButton.gameObject, 2f); + Helpers.DivideSize(refreshButton.gameObject, 2f); refreshButton.buttonText.gameObject.Destroy(); refreshButton.OnClick = new Button.ButtonClickedEvent(); diff --git a/MiraAPI/PluginLoading/MiraPluginInfo.cs b/MiraAPI/PluginLoading/MiraPluginInfo.cs index 4a454a6..891c3ba 100644 --- a/MiraAPI/PluginLoading/MiraPluginInfo.cs +++ b/MiraAPI/PluginLoading/MiraPluginInfo.cs @@ -5,6 +5,7 @@ using BepInEx.Configuration; using MiraAPI.GameModes; using MiraAPI.GameOptions; using MiraAPI.Hud; +using MiraAPI.LocalSettings; using MiraAPI.Modifiers; using MiraAPI.Presets; diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index aa6dfcd..98b8db5 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -13,6 +13,7 @@ using MiraAPI.GameOptions; using MiraAPI.GameOptions.Attributes; using MiraAPI.Hud; using MiraAPI.Keybinds; +using MiraAPI.LocalSettings; using MiraAPI.LocalSettings.Attributes; using MiraAPI.Modifiers; using MiraAPI.Presets; @@ -95,6 +96,11 @@ public sealed class MiraPluginManager continue; } + if (RegisterLocalTabs(type, plugin)) + { + continue; + } + if (RegisterRole(type, info, out var role)) { roles.Add(role); @@ -111,7 +117,6 @@ public sealed class MiraPluginManager continue; } - RegisterLocalSettings(type); RegisterColorClasses(type); } @@ -150,6 +155,8 @@ public sealed class MiraPluginManager Logger.Info($"Registered keybind for button '{button.GetType().Name}' with default key {(KeyboardKeyCode)button.DefaultKeybind}."); } }; + + RegisterLocalTabs(typeof(MiraApiSettings), PluginSingleton.Instance); } /// @@ -321,45 +328,63 @@ public sealed class MiraPluginManager return false; } - private static void RegisterLocalSettings(Type type) + private static bool RegisterLocalTabs(Type type, BasePlugin pluginInfo) { try { - if (!type.IsStatic()) + if (!type.IsAssignableTo(typeof(LocalSettingsTab))) { - Logger.Error($"The type {type.Name} has to be static for local settings."); - return; + return false; + } + + if (!LocalSettingsManager.RegisterTab(type, pluginInfo)) + { + return false; } foreach (var property in type.GetProperties()) { - if (property.PropertyType.IsAssignableTo(typeof(ConfigEntryBase))) + if (!typeof(ConfigEntryBase).IsAssignableFrom(property.PropertyType)) { continue; } - if (property.GetValue(null) is not ConfigEntryBase configEntry) + if (property.GetMethod?.IsStatic == true) + { + Logger.Error($"Option property {property.Name} in {type.Name} must not be static."); + continue; + } + + if (LocalSettingsManager.TypeToTab[type] is not { } tabInstance) { continue; } - var attribute = property.GetCustomAttribute(); + if (property.GetValue(tabInstance) is not ConfigEntryBase configEntry) + { + continue; + } + + var attribute = property.GetCustomAttribute(); if (attribute == null) { continue; } - attribute.Create(configEntry); + attribute.CreateSetting(type, configEntry); } - foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(ConfigEntryBase)) && f.GetCustomAttribute() != null)) + foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(ConfigEntryBase)) && f.GetCustomAttribute() != null)) { Logger.Error($"{field.Name} is a field, not a property. Use properties for local settings."); } + + return true; } catch (Exception e) { - Logger.Error($"Failed to register local settings from {type.Name}: {e}"); + Logger.Error($"Failed to register options for {type.Name}: {e.ToString()}"); } + return false; } } diff --git a/MiraAPI/Resources/Settings.png b/MiraAPI/Resources/Settings.png new file mode 100644 index 0000000000000000000000000000000000000000..6f0da713c35a5771e03c581ac0520a5f91629211 GIT binary patch literal 9598 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 literal 0 HcmV?d00001 diff --git a/MiraAPI/Utilities/Assets/MiraAssets.cs b/MiraAPI/Utilities/Assets/MiraAssets.cs index 148c65a..3f50a79 100644 --- a/MiraAPI/Utilities/Assets/MiraAssets.cs +++ b/MiraAPI/Utilities/Assets/MiraAssets.cs @@ -69,6 +69,11 @@ public static class MiraAssets /// Gets the Next Button sprite. /// public static LoadableResourceAsset NextButton { get; } = new("MiraAPI.Resources.NextButton.png"); + + /// + /// Gets the Mira settings icon.. + /// + public static LoadableResourceAsset SettingsIcon { get; } = new("MiraAPI.Resources.Settings.png"); /// /// Gets the highlighted Next Button sprite. diff --git a/MiraAPI/Utilities/Helpers.cs b/MiraAPI/Utilities/Helpers.cs index 6c0bf4c..06ddc76 100644 --- a/MiraAPI/Utilities/Helpers.cs +++ b/MiraAPI/Utilities/Helpers.cs @@ -3,8 +3,10 @@ using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; +using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem.IO; using MiraAPI.Keybinds; +using MiraAPI.Patches.LocalSettings; using MiraAPI.Utilities.Assets; using Rewired; using TMPro; @@ -26,6 +28,7 @@ public static class Helpers { return [.. GameData.Instance.AllPlayers.ToArray().Where(x => !x.IsDead && !x.Disconnected && x.Object).Select(x => x.Object)]; } + internal static GameObject CreateKeybindIcon(GameObject button, KeyboardKeyCode keyCode, Vector3 localPos) { var keybindIcon = Object.Instantiate(HudManager.Instance.AbilityButton.usesRemainingSprite.gameObject, button.transform); @@ -36,6 +39,54 @@ public static class Helpers return keybindIcon; } + /// + /// Creates a draggable scroller. Add items into the scroller.Inner transform. This does not automatically position children. + /// + /// Where the scroller should be parented to. + /// The collider of the scroller. Used to drag. + /// The created scroller object. + public static Scroller CreateScroller(Transform parent, BoxCollider2D hitBoxCollider) + { + var scrollObj = new GameObject("Scroller"); + scrollObj.transform.SetParent(parent); + scrollObj.transform.localScale = new Vector3(1, 1, 1); + + var inner = new GameObject("Inner"); + inner.transform.SetParent(scrollObj.transform); + inner.transform.localScale = new Vector3(1, 1, 1); + inner.transform.localPosition = new Vector3(0, 0, 2); + + var scroller = scrollObj.AddComponent(); + scroller.allowX = false; + scroller.allowY = true; + scroller.DragScrollSpeed = 1f; + scroller.Colliders = new Il2CppReferenceArray([hitBoxCollider]); + scroller.Inner = inner.transform; + + return scroller; + } + + /// + /// Divides a button/etc by a certain amount by resizing colliders and renderer sizes. + /// + /// The object you want to divide. + /// How much you want to divide by. + public static void DivideSize(GameObject obj, float amount) + { + foreach (var collider in obj.GetComponentsInChildren(true)) + { + if (collider.TryCast() is { } col) + { + col.size = new Vector2(col.size.x / amount, col.size.y); + } + } + + foreach (var rend in obj.GetComponentsInChildren(true)) + { + rend.size = new Vector2(rend.size.x / amount, rend.size.y); + } + } + /// /// Gets the keybind for an action with ReInput. /// From 04d5e72caa3d13d33df02dcb299ca6cb7aac64d8 Mon Sep 17 00:00:00 2001 From: ang-xd Date: Sun, 31 Aug 2025 18:45:22 -0400 Subject: [PATCH 04/12] Add slider float value --- MiraAPI.Example/ExampleLocalSetings.cs | 12 ++ .../Attributes/LocalSettingFloatAttribute.cs | 28 ++++ MiraAPI/LocalSettings/LocalSettingsTab.cs | 9 +- .../SettingTypes/LocalSettingFloat.cs | 127 ++++++++++++++++++ .../SettingTypes/LocalSettingToggle.cs | 5 +- 5 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 MiraAPI/LocalSettings/Attributes/LocalSettingFloatAttribute.cs create mode 100644 MiraAPI/LocalSettings/SettingTypes/LocalSettingFloat.cs diff --git a/MiraAPI.Example/ExampleLocalSetings.cs b/MiraAPI.Example/ExampleLocalSetings.cs index fb07869..fcbc455 100644 --- a/MiraAPI.Example/ExampleLocalSetings.cs +++ b/MiraAPI.Example/ExampleLocalSetings.cs @@ -1,6 +1,7 @@ using BepInEx.Configuration; using MiraAPI.LocalSettings; using MiraAPI.LocalSettings.Attributes; +using MiraAPI.Utilities; using Reactor.Utilities; namespace MiraAPI.Example; @@ -9,6 +10,9 @@ public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Mira Example"; + [LocalSettingFloat(min: 0, max: 15, suffixType: MiraNumberSuffixes.Multiplier, roundValue: false)] + public ConfigEntry ExampleFloatEntry { get; private set; } = config.Bind("General", "Example Float Setting", 50f); + [LocalSettingToggle] public ConfigEntry PlayStartupSound { get; private set; } = config.Bind("Audio", "Play Startup Sound", true); @@ -38,4 +42,12 @@ public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config) [LocalSettingToggle] public ConfigEntry EnableHardMode { get; private set; } = config.Bind("Gameplay", "Enable Hard Mode", false); + [LocalSettingToggle] + public ConfigEntry EnableHardMode2 { get; private set; } = config.Bind("Gameplay", "Enable Hard Mode", false); + [LocalSettingToggle] + public ConfigEntry EnableHardMode3 { get; private set; } = config.Bind("Gameplay", "Enable Hard Mode", false); + [LocalSettingToggle] + public ConfigEntry EnableHardMode4 { get; private set; } = config.Bind("Gameplay", "Enable Hard Mode", false); + [LocalSettingToggle] + public ConfigEntry EnableHardMode5 { get; private set; } = config.Bind("Gameplay", "Enable Hard Mode", false); } diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingFloatAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSettingFloatAttribute.cs new file mode 100644 index 0000000..1414ad8 --- /dev/null +++ b/MiraAPI/LocalSettings/Attributes/LocalSettingFloatAttribute.cs @@ -0,0 +1,28 @@ +using System; +using BepInEx.Configuration; +using MiraAPI.LocalSettings.SettingTypes; +using MiraAPI.Utilities; +using UnityEngine; + +namespace MiraAPI.LocalSettings.Attributes; + +/// +/// Creates a float and binds it with the config entry. +/// +[AttributeUsage(AttributeTargets.Property)] +public class LocalSettingFloatAttribute( + string? name = null, + float min = 0, + float max = 100, + string? description = null, + string? formatString = null, + bool roundValue = true, + MiraNumberSuffixes suffixType = MiraNumberSuffixes.None + ) : LocalSettingAttribute(name, description) +{ + /// + internal override LocalSettingFloat CreateSetting(Type tab, ConfigEntryBase configEntryBase) + { + return new LocalSettingFloat(tab, configEntryBase, name, description, new FloatRange(min, max), suffixType, formatString, roundValue); + } +} diff --git a/MiraAPI/LocalSettings/LocalSettingsTab.cs b/MiraAPI/LocalSettings/LocalSettingsTab.cs index a7e2ea7..f6bb88c 100644 --- a/MiraAPI/LocalSettings/LocalSettingsTab.cs +++ b/MiraAPI/LocalSettings/LocalSettingsTab.cs @@ -112,12 +112,19 @@ public abstract class LocalSettingsTab(ConfigFile config) ref contentOrder, contentIndex == pair.Value.Count); - obj!.GetComponentsInChildren().Do(x => + obj!.GetComponentsInChildren(true).Do(x => { x.maskInteraction = SpriteMaskInteraction.VisibleInsideMask; x.sortingOrder = 150; x.sortingLayerName = "Default"; }); + + obj!.GetComponentsInChildren(true).Do(x => + { + x.sortingOrder = 151; + x.sortingLayerName = "Default"; + }); + obj.GetComponentsInChildren().Do(x => { x.ClickMask = OptionsMenuPatches.MaskCollider; }); contentIndex++; diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSettingFloat.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSettingFloat.cs new file mode 100644 index 0000000..ca5781a --- /dev/null +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSettingFloat.cs @@ -0,0 +1,127 @@ +using System; +using System.Globalization; +using BepInEx.Configuration; +using MiraAPI.Utilities; +using Reactor.Localization.Utilities; +using Reactor.Utilities.Extensions; +using TMPro; +using UnityEngine; +using UnityEngine.Events; +using Object = UnityEngine.Object; + +namespace MiraAPI.LocalSettings.SettingTypes; + +/// +/// Local setting class for toggles. +/// +public class LocalSettingFloat : LocalSettingBase +{ + /// + /// Gets the color of the slider. + /// + public Color SliderColor { get; } + + /// + /// Gets the range of the slider. + /// + public FloatRange SliderRange { get; } + + /// + /// Gets a format for the text to use to format the number. + /// + public string FormatString { get; } + + /// + /// Gets a value indicating whether the value should be rounded. + /// + public bool RoundValue { get; } + + /// + /// Gets the suffix for the number value. + /// + public MiraNumberSuffixes SuffixType { get; } + + /// + /// Initializes a new instance of the class. + /// + /// /// Tab that the toggle belongs to. + /// The binded config entry. + /// The name of the setting. Defaults to ConfigEntry key. + /// The desription of the setting. No description by default. + /// Gets the suffix for the number value. + /// Gets a format for the text to use to format the number. + /// Gets the color of the slider. + /// Gets the range of the slider. + /// Gets whether the value should be rounded. + public LocalSettingFloat( + Type tab, + ConfigEntryBase configEntry, + string? name = null, + string? description = null, + FloatRange? sliderRange = null, + MiraNumberSuffixes? suffixType = null, + string? formatString = null, + bool roundValue = true) + : base(tab, configEntry, name, description) + { + SliderColor = Palette.AcceptedGreen; + SuffixType = suffixType ?? MiraNumberSuffixes.None; + SliderRange = sliderRange ?? new FloatRange(0, 100); + RoundValue = roundValue; + FormatString = formatString ?? "0.0"; + } + + /// + public override GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last) + { + var newSlider = Object.Instantiate(slider, parent).GetComponent(); + var rollover = newSlider.GetComponent(); + newSlider.Title = newSlider.transform.FindChild("Text_TMP").GetComponent(); // Why the hell slider has a title property that is not even assigned??? + newSlider.Title.GetComponent().Destroy(); + newSlider.gameObject.SetActive(true); + + if (order == 2) + offset += 0.5f; + + newSlider.Bar.transform.localPosition = new Vector3(2.85f, 0, 0); + newSlider.transform.localPosition = new Vector3(-2.12f, 1.85f - offset, -7); + newSlider.name = Name; + newSlider.Range = new FloatRange(-1.5f, 1.5f); + newSlider.SetValue(Mathf.InverseLerp(SliderRange.min, SliderRange.max, GetValue())); + rollover.OverColor = SliderColor; + newSlider.Title.transform.localPosition = new Vector3(0.5f, 0, -1f); + newSlider.Title.horizontalAlignment = HorizontalAlignmentOptions.Right; + newSlider.Title.text = GetValueText(); + + newSlider.OnValueChange.AddListener((UnityAction)(() => + { + SetValue(RoundValue + ? Mathf.Round(Mathf.Lerp(SliderRange.min, SliderRange.max, newSlider.Value)) + : Mathf.Lerp(SliderRange.min, SliderRange.max, newSlider.Value)); + + newSlider.Title.text = GetValueText(); + })); + + order = 1; + offset += 0.5f; + return newSlider.gameObject; + } + + private string FormatValue(float value) + { + return SuffixType switch + { + MiraNumberSuffixes.None => value.ToString(FormatString, NumberFormatInfo.InvariantInfo), + MiraNumberSuffixes.Multiplier => value.ToString(FormatString, NumberFormatInfo.InvariantInfo) + "x", + MiraNumberSuffixes.Percent => value.ToString(FormatString, NumberFormatInfo.InvariantInfo) + "%", + _ => TranslationController.Instance.GetString( + StringNames.GameSecondsAbbrev, + (Il2CppSystem.Object[])[value.ToString(FormatString, CultureInfo.InvariantCulture)]), + }; + } + private string GetValueText() + { + var value = GetValue(); + return $"{Name}: {FormatValue(value)} / {FormatValue(SliderRange.max)}"; + } +} diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs index fa36333..0624345 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs @@ -54,6 +54,7 @@ public class LocalSettingToggle : LocalSettingBase ToggleActiveColor = toggleActiveColor ?? Palette.AcceptedGreen; } + /// public override GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last) { var toggleObject = Object.Instantiate(toggle, parent).GetComponent(); @@ -65,11 +66,11 @@ public class LocalSettingToggle : LocalSettingBase if (last && order == 1) { // Toggle in the middle - toggleObject.transform.localPosition = new Vector3(0, 1.85f - offset); + toggleObject.transform.localPosition = new Vector3(0, 1.85f - offset, -7); } else { - toggleObject.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset); + toggleObject.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset, -7); } toggleObject.BaseText = CustomStringName.CreateAndRegister(Name); From 57bab258e5b7c9a6c68913a47d8b30ada8883b51 Mon Sep 17 00:00:00 2001 From: xChipseq Date: Mon, 1 Sep 2025 21:54:13 +0200 Subject: [PATCH 05/12] enum and int options and lots of other stuff i forgot --- MiraAPI.Example/ExampleLocalSetings.cs | 36 ++++- .../Attributes/LocalEnumSettingAttribute.cs | 24 +++ .../Attributes/LocalNumberSettingAttribute.cs | 33 ++++ .../Attributes/LocalSettingAttribute.cs | 5 +- .../Attributes/LocalSettingFloatAttribute.cs | 28 ---- .../Attributes/LocalSettingToggleAttribute.cs | 8 +- .../Attributes/LocalSliderSettingAttribute.cs | 32 ++++ MiraAPI/LocalSettings/ILocalSetting.cs | 4 +- MiraAPI/LocalSettings/LocalSettingsTab.cs | 68 +++------ .../LocalSettingsTabAppearance.cs | 81 ++++++++++ .../LocalSettingsTabSingleton.cs | 3 +- .../SettingTypes/LocalEnumSetting.cs | 126 +++++++++++++++ .../SettingTypes/LocalNumberSetting.cs | 144 ++++++++++++++++++ .../SettingTypes/LocalSettingBase.cs | 18 ++- ...lSettingFloat.cs => LocalSliderSetting.cs} | 57 +++---- ...SettingToggle.cs => LocalToggleSetting.cs} | 49 ++---- MiraAPI/MiraApiSettings.cs | 6 +- MiraAPI/Modifiers/ModifierComponent.cs | 2 +- .../ModifierDisplayComponent.cs | 7 +- .../LocalSettings/OptionsMenuPatches.cs | 132 ++++++++++++++-- .../LocalSettings/ToggleBehaviourPatch.cs | 8 +- MiraAPI/Utilities/Helpers.cs | 20 +++ 22 files changed, 708 insertions(+), 183 deletions(-) create mode 100644 MiraAPI/LocalSettings/Attributes/LocalEnumSettingAttribute.cs create mode 100644 MiraAPI/LocalSettings/Attributes/LocalNumberSettingAttribute.cs delete mode 100644 MiraAPI/LocalSettings/Attributes/LocalSettingFloatAttribute.cs create mode 100644 MiraAPI/LocalSettings/Attributes/LocalSliderSettingAttribute.cs create mode 100644 MiraAPI/LocalSettings/LocalSettingsTabAppearance.cs create mode 100644 MiraAPI/LocalSettings/SettingTypes/LocalEnumSetting.cs create mode 100644 MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs rename MiraAPI/LocalSettings/SettingTypes/{LocalSettingFloat.cs => LocalSliderSetting.cs} (57%) rename MiraAPI/LocalSettings/SettingTypes/{LocalSettingToggle.cs => LocalToggleSetting.cs} (53%) diff --git a/MiraAPI.Example/ExampleLocalSetings.cs b/MiraAPI.Example/ExampleLocalSetings.cs index fcbc455..da7e4f5 100644 --- a/MiraAPI.Example/ExampleLocalSetings.cs +++ b/MiraAPI.Example/ExampleLocalSetings.cs @@ -3,14 +3,22 @@ using MiraAPI.LocalSettings; using MiraAPI.LocalSettings.Attributes; using MiraAPI.Utilities; using Reactor.Utilities; +using UnityEngine; namespace MiraAPI.Example; public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Mira Example"; + public override LocalSettingTabAppearance TabAppearance => new() + { + TabColor = Color.cyan, + ToggleActiveColor = Color.blue, + ToggleInactiveColor = Color.red, + SliderHoverColor = Color.magenta, + }; - [LocalSettingFloat(min: 0, max: 15, suffixType: MiraNumberSuffixes.Multiplier, roundValue: false)] + [LocalSliderSetting(min: 0, max: 15, suffixType: MiraNumberSuffixes.Multiplier)] public ConfigEntry ExampleFloatEntry { get; private set; } = config.Bind("General", "Example Float Setting", 50f); [LocalSettingToggle] @@ -50,4 +58,30 @@ public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config) public ConfigEntry EnableHardMode4 { get; private set; } = config.Bind("Gameplay", "Enable Hard Mode", false); [LocalSettingToggle] public ConfigEntry EnableHardMode5 { get; private set; } = config.Bind("Gameplay", "Enable Hard Mode", false); + + [LocalNumberSetting] + public ConfigEntry NumberExample { get; private set; } = config.Bind("Gameplay", "Number of cheeseburgers", 3); + + [LocalEnumSetting] + public ConfigEntry EnumExample { get; private set; } = config.Bind("Gameplay", "Anything else", ExampleSettingEnum.Fries); } + +public enum ExampleSettingEnum +{ + Fries, + OnionRings, + ChickenNuggets, + Gun, +} + +public class Tab1(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } +public class Tab2(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } +public class Tab4(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } +public class Tab3(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } +public class Tab5(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } +public class Tab6(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } +public class Tab7(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } +public class Tab8(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } +public class Tab9(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } +public class Tab10(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } +public class Tab11(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } diff --git a/MiraAPI/LocalSettings/Attributes/LocalEnumSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalEnumSettingAttribute.cs new file mode 100644 index 0000000..a4a22e2 --- /dev/null +++ b/MiraAPI/LocalSettings/Attributes/LocalEnumSettingAttribute.cs @@ -0,0 +1,24 @@ +using System; +using BepInEx.Configuration; +using MiraAPI.LocalSettings.SettingTypes; + +namespace MiraAPI.LocalSettings.Attributes; + +/// +/// Creates an enum setting for the . +/// +/// Optional custom enum names. +/// +[AttributeUsage(AttributeTargets.Property)] +public class LocalEnumSettingAttribute( + string? name = null, + string? description = null, + string[]? names = null + ) : LocalSettingAttribute(name, description) +{ + /// + internal override LocalEnumSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase) + { + return new LocalEnumSetting(tab, configEntryBase, configEntryBase.SettingType, name, description, names); + } +} diff --git a/MiraAPI/LocalSettings/Attributes/LocalNumberSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalNumberSettingAttribute.cs new file mode 100644 index 0000000..20a55f3 --- /dev/null +++ b/MiraAPI/LocalSettings/Attributes/LocalNumberSettingAttribute.cs @@ -0,0 +1,33 @@ +using System; +using BepInEx.Configuration; +using MiraAPI.LocalSettings.SettingTypes; +using MiraAPI.Utilities; + +namespace MiraAPI.LocalSettings.Attributes; + +/// +/// Creates a number setting for the . +/// +/// Minimum range. +/// Maximum range. +/// Increment per use. +/// Suffix for the value. +/// Format string used when formating. +/// +[AttributeUsage(AttributeTargets.Property)] +public class LocalNumberSettingAttribute( + string? name = null, + string? description = null, + int min = 1, + int max = 5, + int increment = 1, + MiraNumberSuffixes suffixType = MiraNumberSuffixes.None, + string? formatString = null + ) : LocalSettingAttribute(name, description) +{ + /// + internal override LocalNumberSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase) + { + return new LocalNumberSetting(tab, configEntryBase, name, description, new IntRange(min, max), increment, suffixType, formatString); + } +} diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs index 1b101c5..86dfbc9 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalSettingAttribute.cs @@ -6,10 +6,13 @@ namespace MiraAPI.LocalSettings.Attributes; /// /// Base class for all local settings attributes. /// +/// The name of the setting. Defaults to entry key. +/// The description of the setting. Defalts to entry description. [AttributeUsage(AttributeTargets.Property)] public abstract class LocalSettingAttribute( string? name = null, - string? description = null) : Attribute + string? description = null + ) : Attribute { /// /// Returns the created setting object. diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingFloatAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSettingFloatAttribute.cs deleted file mode 100644 index 1414ad8..0000000 --- a/MiraAPI/LocalSettings/Attributes/LocalSettingFloatAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using BepInEx.Configuration; -using MiraAPI.LocalSettings.SettingTypes; -using MiraAPI.Utilities; -using UnityEngine; - -namespace MiraAPI.LocalSettings.Attributes; - -/// -/// Creates a float and binds it with the config entry. -/// -[AttributeUsage(AttributeTargets.Property)] -public class LocalSettingFloatAttribute( - string? name = null, - float min = 0, - float max = 100, - string? description = null, - string? formatString = null, - bool roundValue = true, - MiraNumberSuffixes suffixType = MiraNumberSuffixes.None - ) : LocalSettingAttribute(name, description) -{ - /// - internal override LocalSettingFloat CreateSetting(Type tab, ConfigEntryBase configEntryBase) - { - return new LocalSettingFloat(tab, configEntryBase, name, description, new FloatRange(min, max), suffixType, formatString, roundValue); - } -} diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs index 7b44f7f..2add91d 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs @@ -1,13 +1,13 @@ using System; using BepInEx.Configuration; using MiraAPI.LocalSettings.SettingTypes; -using UnityEngine; namespace MiraAPI.LocalSettings.Attributes; /// -/// Creates a toggle and binds it with the config entry. +/// Creates a toggle setting for the . /// +/// [AttributeUsage(AttributeTargets.Property)] public class LocalSettingToggleAttribute( string? name = null, @@ -15,8 +15,8 @@ public class LocalSettingToggleAttribute( ) : LocalSettingAttribute(name, description) { /// - internal override LocalSettingToggle CreateSetting(Type tab, ConfigEntryBase configEntryBase) + internal override LocalToggleSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase) { - return new LocalSettingToggle(tab, configEntryBase, name, description); + return new LocalToggleSetting(tab, configEntryBase, name, description); } } diff --git a/MiraAPI/LocalSettings/Attributes/LocalSliderSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSliderSettingAttribute.cs new file mode 100644 index 0000000..53555fa --- /dev/null +++ b/MiraAPI/LocalSettings/Attributes/LocalSliderSettingAttribute.cs @@ -0,0 +1,32 @@ +using System; +using BepInEx.Configuration; +using MiraAPI.LocalSettings.SettingTypes; +using MiraAPI.Utilities; + +namespace MiraAPI.LocalSettings.Attributes; + +/// +/// Creates a slider setting for the . +/// +/// +/// Minimum range. +/// Maximum range. +/// Should the value be rounded. +/// Suffix for the value. +[AttributeUsage(AttributeTargets.Property)] +public class LocalSliderSettingAttribute( + string? name = null, + float min = 0, + float max = 100, + string? description = null, + string? formatString = null, + bool roundValue = false, + MiraNumberSuffixes suffixType = MiraNumberSuffixes.None + ) : LocalSettingAttribute(name, description) +{ + /// + internal override LocalSliderSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase) + { + return new LocalSliderSetting(tab, configEntryBase, name, description, new FloatRange(min, max), suffixType, formatString, roundValue); + } +} diff --git a/MiraAPI/LocalSettings/ILocalSetting.cs b/MiraAPI/LocalSettings/ILocalSetting.cs index e6649bb..a94e102 100644 --- a/MiraAPI/LocalSettings/ILocalSetting.cs +++ b/MiraAPI/LocalSettings/ILocalSetting.cs @@ -4,7 +4,7 @@ using UnityEngine; namespace MiraAPI.LocalSettings; /// -/// Interface for all local settings +/// Interface for all local settings. /// public interface ILocalSetting { @@ -29,7 +29,7 @@ public interface ILocalSetting ConfigEntryBase ConfigEntry { get; } /// - /// Used to create the setting + /// Used to create the setting. /// /// Toggle template. /// Slider template. diff --git a/MiraAPI/LocalSettings/LocalSettingsTab.cs b/MiraAPI/LocalSettings/LocalSettingsTab.cs index f6bb88c..5516d05 100644 --- a/MiraAPI/LocalSettings/LocalSettingsTab.cs +++ b/MiraAPI/LocalSettings/LocalSettingsTab.cs @@ -4,7 +4,6 @@ using BepInEx.Configuration; using HarmonyLib; using MiraAPI.Patches.LocalSettings; using MiraAPI.Utilities; -using MiraAPI.Utilities.Assets; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; @@ -14,39 +13,29 @@ using Object = UnityEngine.Object; namespace MiraAPI.LocalSettings; /// -/// Represents a local settings tab. Gets a button by default +/// Represents a local settings tab. Gets a button by default. /// public abstract class LocalSettingsTab(ConfigFile config) { /// - /// Gets the name of the tab + /// Gets the name of the tab. /// public abstract string TabName { get; } /// - /// Gets the tab button icon. + /// Gets the tab appearance. /// - public virtual LoadableAsset? TabIcon => null; - - /// - /// Gets the color of the tab button. - /// - public virtual Color TabColor => Color.white; - - /// - /// Gets the color of the tab button when hovered. - /// - public Color TabHoverColor => Palette.AcceptedGreen; - - /// - /// Gets the color of the tab button when active. - /// - public virtual Color TabActiveColor => Palette.AcceptedGreen; + public virtual LocalSettingTabAppearance TabAppearance { get; } = new(); /// /// Gets a value indicating whether a button should be created for the tab. /// - protected virtual bool ShouldCreateButton => true; + public virtual bool ShouldCreateButton => true; + + /// + /// Gets a value indicating whether the tab should have category labels. + /// + protected virtual bool ShouldCreateLabels => true; /// /// Gets the mod's config file. Automatically found in the plugin loader. @@ -57,13 +46,8 @@ public abstract class LocalSettingsTab(ConfigFile config) internal List Settings { get; } = []; private Scroller? Scroller { get; set; } - internal GameObject? CreateTab(OptionsMenuBehaviour instance, ref int tabIdx, ref float offset) + internal GameObject CreateTab(OptionsMenuBehaviour instance) { - if (ShouldCreateButton) - { - CreateTabButton(instance, ref tabIdx, ref offset); - } - var tab = Object.Instantiate(instance.transform.FindChild("GeneralTab").gameObject, instance.transform); tab.name = $"{TabName}Tab"; tab.transform.DestroyChildren(); @@ -100,7 +84,10 @@ public abstract class LocalSettingsTab(ConfigFile config) foreach (var pair in entriesByGroup) { - CreateLabel(generalLabel, Scroller.Inner, pair.Key, ref contentOffset); + if (ShouldCreateLabels) + { + CreateLabel(generalLabel, Scroller.Inner, pair.Key, ref contentOffset); + } foreach (var setting in pair.Value) { @@ -119,7 +106,7 @@ public abstract class LocalSettingsTab(ConfigFile config) x.sortingLayerName = "Default"; }); - obj!.GetComponentsInChildren(true).Do(x => + obj.GetComponentsInChildren(true).Do(x => { x.sortingOrder = 151; x.sortingLayerName = "Default"; @@ -137,13 +124,13 @@ public abstract class LocalSettingsTab(ConfigFile config) return tab; } - private void CreateTabButton(OptionsMenuBehaviour instance, ref int tabIdx, ref float offset) + internal GameObject CreateTabButton(OptionsMenuBehaviour instance, ref int tabIdx, ref float offset) { var tabButtonObject = Object.Instantiate(instance.Tabs[0], instance.transform); tabButtonObject.name = $"{TabName} Button"; tabButtonObject.transform.localPosition = new Vector3(2.4f, 2.1f - offset, 5.5f); tabButtonObject.transform.localScale = new Vector3(1.25f, 1.25f, 1); - tabButtonObject.Button.color = TabColor; + tabButtonObject.Button.color = TabAppearance.TabButtonColor; TabButton = tabButtonObject; var tabButtonText = tabButtonObject.transform.FindChild("Text_TMP").GetComponent(); @@ -155,18 +142,18 @@ public abstract class LocalSettingsTab(ConfigFile config) var tabButton = tabButtonObject.GetComponent(); var rollover = tabButtonObject.Rollover; - rollover.OverColor = TabColor; - rollover.OutColor = TabColor; + rollover.OverColor = TabAppearance.TabButtonHoverColor; + rollover.OutColor = TabAppearance.TabButtonColor; SpriteRenderer? tabButtonRend = null; - if (TabIcon != null) + if (TabAppearance.TabIcon != null) { tabButtonRend = new GameObject("sprite").AddComponent(); tabButtonRend.gameObject.layer = 5; // ui layer tabButtonRend.transform.SetParent(tabButtonObject.transform); tabButtonRend.transform.localPosition = new Vector3(0.5f, 0, -2); tabButtonRend.transform.localScale = new Vector3(0.2f, 0.2f, 1); - tabButtonRend.sprite = TabIcon.LoadAsset(); + tabButtonRend.sprite = TabAppearance.TabIcon.LoadAsset(); tabButtonText.gameObject.SetActive(false); } @@ -176,8 +163,6 @@ public abstract class LocalSettingsTab(ConfigFile config) tabButton.OnMouseOver.AddListener((UnityAction)(() => { tabButton.transform.localPosition = new Vector3(3.55f, 2.1f - tabOffset, 5.5f); - tabButtonObject.Button.color = TabHoverColor; - tabButtonObject.Rollover.OutColor = TabHoverColor; tabButtonText.gameObject.SetActive(true); tabButtonText.transform.localPosition = new Vector3(-0.024f, 0, 0); tabButtonText.maxVisibleCharacters = int.MaxValue; @@ -188,8 +173,8 @@ public abstract class LocalSettingsTab(ConfigFile config) { if (!tabButtonObject.Content.gameObject.activeSelf) { - tabButtonObject.Button.color = TabColor; - tabButtonObject.Rollover.OutColor = TabColor; + //tabButtonObject.Button.color = TabColor; + //tabButtonObject.Rollover.OutColor = TabColor; } tabButton.transform.localPosition = new Vector3(2.4f, 2.1f - tabOffset, 5.5f); @@ -205,8 +190,7 @@ public abstract class LocalSettingsTab(ConfigFile config) tabButtonText.text = $"{GetShortName(TabName)}"; })); - offset += 0.6f; - tabIdx++; + return tabButtonObject.gameObject; } private static void CreateLabel(GameObject template, Transform parent, string text, ref float offset) @@ -227,7 +211,6 @@ public abstract class LocalSettingsTab(ConfigFile config) offset += 0.5f; } - private static string GetShortName(string name) { var shortName = string.Empty; @@ -235,4 +218,3 @@ public abstract class LocalSettingsTab(ConfigFile config) return shortName; } } - diff --git a/MiraAPI/LocalSettings/LocalSettingsTabAppearance.cs b/MiraAPI/LocalSettings/LocalSettingsTabAppearance.cs new file mode 100644 index 0000000..2cad514 --- /dev/null +++ b/MiraAPI/LocalSettings/LocalSettingsTabAppearance.cs @@ -0,0 +1,81 @@ +using MiraAPI.Utilities.Assets; +using UnityEngine; + +namespace MiraAPI.LocalSettings; + +/// +/// Struct for modyfing tab apparance. +/// Defines how the settings look. +/// +public record struct LocalSettingTabAppearance() +{ + /// + /// Gets or sets main color of the tab. + /// + public Color TabColor { get; set; } = Color.white; + + /// + /// Gets or sets tab button icon. No icon by default. + /// + public LoadableAsset? TabIcon { get; set; } + + /// + /// Gets or sets color of the tab button. + /// + public Color TabButtonColor { get; set; } = Color.white; + + /// + /// Gets or sets hover color of the tab button. + /// + public Color TabButtonHoverColor { get; set; } = Palette.AcceptedGreen; + + /// + /// Gets or sets active color of the tab button. + /// + public Color TabButtonActiveColor { get; set; } = Palette.AcceptedGreen; + + /// + /// Gets or sets hover color of toggles. + /// + public Color ToggleHoverColor { get; set; } = Palette.AcceptedGreen; + + /// + /// Gets or sets active color of toggles. + /// + public Color ToggleActiveColor { get; set; } = Palette.AcceptedGreen; + + /// + /// Gets or sets inactive color of toggles. + /// + public Color ToggleInactiveColor { get; set; } = Color.red; + + /// + /// Gets or sets color of number buttons. + /// + public Color NumberColor { get; set; } = Color.white; + + /// + /// Gets or sets hover color of number buttons. + /// + public Color NumberHoverColor { get; set; } = Palette.AcceptedGreen; + + /// + /// Gets or sets color of sliders. + /// + public Color SliderColor { get; set; } = Color.white; + + /// + /// Gets or sets hover color of sliders. + /// + public Color SliderHoverColor { get; set; } = Palette.AcceptedGreen; + + /// + /// Gets or sets color of enum buttons. + /// + public Color EnumColor { get; set; } = Color.white; + + /// + /// Gets or sets hover color of enum buttons. + /// + public Color EnumHoverColor { get; set; } = Palette.AcceptedGreen; +} diff --git a/MiraAPI/LocalSettings/LocalSettingsTabSingleton.cs b/MiraAPI/LocalSettings/LocalSettingsTabSingleton.cs index 1994235..eec81a2 100644 --- a/MiraAPI/LocalSettings/LocalSettingsTabSingleton.cs +++ b/MiraAPI/LocalSettings/LocalSettingsTabSingleton.cs @@ -1,7 +1,6 @@ using System.Linq; -using MiraAPI.LocalSettings; -namespace MiraAPI.GameOptions; +namespace MiraAPI.LocalSettings; /// /// Singleton for local settings tabs. diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalEnumSetting.cs b/MiraAPI/LocalSettings/SettingTypes/LocalEnumSetting.cs new file mode 100644 index 0000000..c435a58 --- /dev/null +++ b/MiraAPI/LocalSettings/SettingTypes/LocalEnumSetting.cs @@ -0,0 +1,126 @@ +using System; +using BepInEx.Configuration; +using MiraAPI.Utilities; +using Reactor.Utilities.Extensions; +using TMPro; +using UnityEngine; +using UnityEngine.Events; +using Object = UnityEngine.Object; + +namespace MiraAPI.LocalSettings.SettingTypes; + +/// +/// Local setting class for enums. +/// +public class LocalEnumSetting : LocalSettingBase +{ + /// + /// Gets the enum type of the setting. + /// + public Type EnumType { get; } + + /// + /// Gets the enum values. + /// + public string[] Values { get; } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The enum type. + /// The optional values array to replace the enum names. + public LocalEnumSetting( + Type tab, + ConfigEntryBase configEntry, + Type enumType, + string? name = null, + string? description = null, + string[]? values = null) + : base(tab, configEntry, name, description) + { + EnumType = enumType; + Values = values ?? Enum.GetNames(EnumType); + } + + /// + public override GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last) + { + var button = Object.Instantiate(toggle, parent).GetComponent(); + var tmp = button.transform.FindChild("Text_TMP").GetComponent(); + var rollover = button.GetComponent(); + tmp.GetComponent().Destroy(); + button.gameObject.SetActive(true); + + var toggleComp = button.GetComponent(); + var background = toggleComp.Background; + var highlight = button.transform.FindChild("ButtonHighlight")?.GetComponent(); + if (highlight != null) + { + highlight.color = Tab!.TabAppearance.EnumHoverColor; + } + toggleComp.Destroy(); + + if (last && order == 1) + { + // Button in the middle + button.transform.localPosition = new Vector3(0, 1.85f - offset, -7); + } + else + { + button.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset, -7); + } + + tmp.text = GetValueText(); + button.name = Name; + button.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); + rollover.OutColor = Tab!.TabAppearance.EnumColor; + rollover.OverColor = Tab!.TabAppearance.EnumHoverColor; + background.color = Tab!.TabAppearance.EnumColor; + + button.OnClick.AddListener((UnityAction)(() => + { + int value = GetValue(); + value++; + if (value >= Values.Length) + { + value = 0; + } + + SetValue(value); + tmp.text = GetValueText(); + })); + button.OnMouseOver.AddListener((UnityAction)(() => + { + if (!Description.IsNullOrWhiteSpace()) + { + tmp.text = Description; + } + highlight?.gameObject.SetActive(true); + })); + button.OnMouseOut.AddListener((UnityAction)(() => + { + tmp.text = GetValueText(); + highlight?.gameObject.SetActive(false); + })); + + Helpers.DivideSize(button.gameObject, 1.1f); + + order++; + if (order > 2 && !last) + { + offset += 0.5f; + order = 1; + } + if (last) + offset += 0.6f; + + return button.gameObject; + } + + /// + protected override string GetValueText() + { + return $"{Name}: {Values[GetValue()]}"; + } +} diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs b/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs new file mode 100644 index 0000000..1473889 --- /dev/null +++ b/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs @@ -0,0 +1,144 @@ +using System; +using BepInEx.Configuration; +using MiraAPI.Utilities; +using Reactor.Utilities.Extensions; +using TMPro; +using UnityEngine; +using UnityEngine.Events; +using Object = UnityEngine.Object; + +namespace MiraAPI.LocalSettings.SettingTypes; + +/// +/// Local setting class for numbers. +/// +public class LocalNumberSetting : LocalSettingBase +{ + /// + /// Gets the range of the button. + /// + public IntRange NumberRange { get; } + + /// + /// Gets the increment of the value when button is pressed. + /// + public int Increment { get; } + + /// + /// Gets a format for the text to use to format the number. + /// + public string FormatString { get; } + + /// + /// Gets the suffix for the number value. + /// + public MiraNumberSuffixes SuffixType { get; } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The value range. + /// The increment per click. + /// The suffix used for formating. + /// The format string used for formating. + public LocalNumberSetting( + Type tab, + ConfigEntryBase configEntry, + string? name = null, + string? description = null, + IntRange? numberRange = null, + int? increment = null, + MiraNumberSuffixes? suffixType = null, + string? formatString = null) + : base(tab, configEntry, name, description) + { + SuffixType = suffixType ?? MiraNumberSuffixes.None; + NumberRange = numberRange ?? new IntRange(1, 5); + Increment = increment ?? 1; + FormatString = formatString ?? "0"; + } + + /// + public override GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last) + { + var button = Object.Instantiate(toggle, parent).GetComponent(); + var tmp = button.transform.FindChild("Text_TMP").GetComponent(); + var rollover = button.GetComponent(); + tmp.GetComponent().Destroy(); + button.gameObject.SetActive(true); + + var toggleComp = button.GetComponent(); + var background = toggleComp.Background; + var highlight = button.transform.FindChild("ButtonHighlight")?.GetComponent(); + if (highlight != null) + { + highlight.color = Tab!.TabAppearance.NumberHoverColor; + } + toggleComp.Destroy(); + + if (last && order == 1) + { + // Button in the middle + button.transform.localPosition = new Vector3(0, 1.85f - offset, -7); + } + else + { + button.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset, -7); + } + + tmp.text = GetValueText(); + button.name = Name; + button.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); + rollover.OutColor = Tab!.TabAppearance.NumberColor; + rollover.OverColor = Tab!.TabAppearance.NumberHoverColor; + background.color = Tab!.TabAppearance.NumberColor; + + button.OnClick.AddListener((UnityAction)(() => + { + int value = GetValue(); + value += Increment; + if (value > NumberRange.max) + { + value = NumberRange.min; + } + + SetValue(value); + tmp.text = GetValueText(); + })); + button.OnMouseOver.AddListener((UnityAction)(() => + { + if (!Description.IsNullOrWhiteSpace()) + { + tmp.text = Description; + } + highlight?.gameObject.SetActive(true); + })); + button.OnMouseOut.AddListener((UnityAction)(() => + { + tmp.text = GetValueText(); + highlight?.gameObject.SetActive(false); + })); + + Helpers.DivideSize(button.gameObject, 1.1f); + + order++; + if (order > 2 && !last) + { + offset += 0.5f; + order = 1; + } + if (last) + offset += 0.6f; + + return button.gameObject; + } + + /// + protected override string GetValueText() + { + var value = GetValue(); + var formated = Helpers.FormatValue(value, SuffixType, FormatString); + return $"{Name}: {formated}"; + } +} diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs index e1ebe10..934b33e 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs @@ -1,6 +1,6 @@ using System; +using System.Linq; using BepInEx.Configuration; -using TMPro; using UnityEngine; namespace MiraAPI.LocalSettings.SettingTypes; @@ -20,6 +20,8 @@ public abstract class LocalSettingBase : ILocalSetting /// public ConfigEntryBase ConfigEntry { get; } + internal LocalSettingsTab? Tab => LocalSettingsManager.Tabs.FirstOrDefault(x => x.Settings.Contains(this)); + /// /// Initializes a new instance of the class. /// @@ -32,7 +34,6 @@ public abstract class LocalSettingBase : ILocalSetting ConfigEntry = configEntry; Name = name ?? ConfigEntry.Definition.Key; Description = description ?? ConfigEntry.Description.Description; - LocalSettingsManager.TypeToTab[tab].Settings.Add(this); } @@ -40,7 +41,16 @@ public abstract class LocalSettingBase : ILocalSetting /// Creates an instance of the setting. /// /// The created setting. - public abstract GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last); + public abstract GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last); + + /// + /// Returns the formated string to use in the text of the setting. + /// + /// The value text. + protected virtual string GetValueText() + { + return GetValue()?.ToString() ?? string.Empty; + } /// /// Gets the value of the config entry, casted to the setting type. @@ -52,7 +62,7 @@ public abstract class LocalSettingBase : ILocalSetting } /// - /// Sets the value of the config entry + /// Sets the value of the config entry. /// /// The value to set to. public void SetValue(T value) diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSettingFloat.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSliderSetting.cs similarity index 57% rename from MiraAPI/LocalSettings/SettingTypes/LocalSettingFloat.cs rename to MiraAPI/LocalSettings/SettingTypes/LocalSliderSetting.cs index ca5781a..8535baa 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalSettingFloat.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSliderSetting.cs @@ -1,8 +1,6 @@ using System; -using System.Globalization; using BepInEx.Configuration; using MiraAPI.Utilities; -using Reactor.Localization.Utilities; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; @@ -12,15 +10,10 @@ using Object = UnityEngine.Object; namespace MiraAPI.LocalSettings.SettingTypes; /// -/// Local setting class for toggles. +/// Local setting class for sliders. /// -public class LocalSettingFloat : LocalSettingBase +public class LocalSliderSetting : LocalSettingBase { - /// - /// Gets the color of the slider. - /// - public Color SliderColor { get; } - /// /// Gets the range of the slider. /// @@ -42,18 +35,14 @@ public class LocalSettingFloat : LocalSettingBase public MiraNumberSuffixes SuffixType { get; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// /// Tab that the toggle belongs to. - /// The binded config entry. - /// The name of the setting. Defaults to ConfigEntry key. - /// The desription of the setting. No description by default. - /// Gets the suffix for the number value. - /// Gets a format for the text to use to format the number. - /// Gets the color of the slider. - /// Gets the range of the slider. - /// Gets whether the value should be rounded. - public LocalSettingFloat( + /// + /// The value range. + /// The suffix used for formating. + /// The format string used for formating. + /// Should the value be rounded. + public LocalSliderSetting( Type tab, ConfigEntryBase configEntry, string? name = null, @@ -61,10 +50,9 @@ public class LocalSettingFloat : LocalSettingBase FloatRange? sliderRange = null, MiraNumberSuffixes? suffixType = null, string? formatString = null, - bool roundValue = true) + bool roundValue = false) : base(tab, configEntry, name, description) { - SliderColor = Palette.AcceptedGreen; SuffixType = suffixType ?? MiraNumberSuffixes.None; SliderRange = sliderRange ?? new FloatRange(0, 100); RoundValue = roundValue; @@ -72,13 +60,14 @@ public class LocalSettingFloat : LocalSettingBase } /// - public override GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last) + public override GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last) { var newSlider = Object.Instantiate(slider, parent).GetComponent(); var rollover = newSlider.GetComponent(); newSlider.Title = newSlider.transform.FindChild("Text_TMP").GetComponent(); // Why the hell slider has a title property that is not even assigned??? newSlider.Title.GetComponent().Destroy(); newSlider.gameObject.SetActive(true); + newSlider.Bar.color = Tab!.TabAppearance.SliderColor; if (order == 2) offset += 0.5f; @@ -88,7 +77,8 @@ public class LocalSettingFloat : LocalSettingBase newSlider.name = Name; newSlider.Range = new FloatRange(-1.5f, 1.5f); newSlider.SetValue(Mathf.InverseLerp(SliderRange.min, SliderRange.max, GetValue())); - rollover.OverColor = SliderColor; + rollover.OutColor = Tab!.TabAppearance.SliderColor; + rollover.OverColor = Tab!.TabAppearance.SliderHoverColor; newSlider.Title.transform.localPosition = new Vector3(0.5f, 0, -1f); newSlider.Title.horizontalAlignment = HorizontalAlignmentOptions.Right; newSlider.Title.text = GetValueText(); @@ -107,21 +97,12 @@ public class LocalSettingFloat : LocalSettingBase return newSlider.gameObject; } - private string FormatValue(float value) - { - return SuffixType switch - { - MiraNumberSuffixes.None => value.ToString(FormatString, NumberFormatInfo.InvariantInfo), - MiraNumberSuffixes.Multiplier => value.ToString(FormatString, NumberFormatInfo.InvariantInfo) + "x", - MiraNumberSuffixes.Percent => value.ToString(FormatString, NumberFormatInfo.InvariantInfo) + "%", - _ => TranslationController.Instance.GetString( - StringNames.GameSecondsAbbrev, - (Il2CppSystem.Object[])[value.ToString(FormatString, CultureInfo.InvariantCulture)]), - }; - } - private string GetValueText() + /// + protected override string GetValueText() { var value = GetValue(); - return $"{Name}: {FormatValue(value)} / {FormatValue(SliderRange.max)}"; + var formated = Helpers.FormatValue(value, SuffixType, FormatString); + var maxFormated = Helpers.FormatValue(SliderRange.max, SuffixType, FormatString); + return $"{Name}: {formated} / {maxFormated}"; } } diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs b/MiraAPI/LocalSettings/SettingTypes/LocalToggleSetting.cs similarity index 53% rename from MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs rename to MiraAPI/LocalSettings/SettingTypes/LocalToggleSetting.cs index 0624345..54cd8ee 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalSettingToggle.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalToggleSetting.cs @@ -12,50 +12,23 @@ namespace MiraAPI.LocalSettings.SettingTypes; /// /// Local setting class for toggles. /// -public class LocalSettingToggle : LocalSettingBase +public class LocalToggleSetting : LocalSettingBase { /// - /// Gets the color of the toggle. + /// Initializes a new instance of the class. /// - public Color ToggleColor { get; } - - /// - /// Gets the color of the toggle when hovered. - /// - public Color ToggleHoverColor { get; } - - /// - /// Gets the color of the toggle when enabled. - /// - public Color ToggleActiveColor { get; } - - /// - /// Initializes a new instance of the class. - /// - /// /// Tab that the toggle belongs to. - /// The binded config entry. - /// The name of the setting. Defaults to ConfigEntry key. - /// The desription of the setting. No description by default. - /// The color of the toggle. - /// The hover color of the toggle. - /// The active color of the toggle. - public LocalSettingToggle( + /// + public LocalToggleSetting( Type tab, ConfigEntryBase configEntry, string? name = null, - string? description = null, - Color? toggleColor = null, - Color? toggleHoverColor = null, - Color? toggleActiveColor = null) - : base(tab, configEntry, name, description) + string? description = null + ) : base(tab, configEntry, name, description) { - ToggleColor = toggleColor ?? Color.white; - ToggleHoverColor = toggleHoverColor ?? Palette.AcceptedGreen; - ToggleActiveColor = toggleActiveColor ?? Palette.AcceptedGreen; } /// - public override GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last) + public override GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last) { var toggleObject = Object.Instantiate(toggle, parent).GetComponent(); var tmp = toggleObject.transform.FindChild("Text_TMP").GetComponent(); @@ -76,15 +49,15 @@ public class LocalSettingToggle : LocalSettingBase toggleObject.BaseText = CustomStringName.CreateAndRegister(Name); toggleObject.UpdateText(GetValue()); toggleObject.name = Name; - toggleObject.Background.color = GetValue() ? ToggleActiveColor : ToggleColor; + toggleObject.Background.color = GetValue() ? Tab!.TabAppearance.ToggleActiveColor : Tab!.TabAppearance.ToggleInactiveColor; passiveButton.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); - rollover.OverColor = ToggleHoverColor; + rollover.OverColor = Tab!.TabAppearance.ToggleHoverColor; passiveButton.OnClick.AddListener((UnityAction)(() => { SetValue(!GetValue()); toggleObject.UpdateText(GetValue()); - toggleObject.Background.color = GetValue() ? ToggleActiveColor : ToggleColor; + toggleObject.Background.color = GetValue() ? Tab!.TabAppearance.ToggleActiveColor : Tab!.TabAppearance.ToggleInactiveColor; })); passiveButton.OnMouseOver.AddListener((UnityAction)(() => { @@ -96,7 +69,7 @@ public class LocalSettingToggle : LocalSettingBase passiveButton.OnMouseOut.AddListener((UnityAction)(() => { toggleObject.UpdateText(GetValue()); - toggleObject.Background.color = GetValue() ? ToggleActiveColor : ToggleColor; + toggleObject.Background.color = GetValue() ? Tab!.TabAppearance.ToggleActiveColor : Tab!.TabAppearance.ToggleInactiveColor; })); Helpers.DivideSize(toggleObject.gameObject, 1.1f); diff --git a/MiraAPI/MiraApiSettings.cs b/MiraAPI/MiraApiSettings.cs index e5fe303..4ff4d04 100644 --- a/MiraAPI/MiraApiSettings.cs +++ b/MiraAPI/MiraApiSettings.cs @@ -23,7 +23,11 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config) public override string TabName => "Mira API"; /// - public override LoadableAsset? TabIcon => MiraAssets.SettingsIcon; + public override LocalSettingTabAppearance TabAppearance => new() + { + TabButtonHoverColor = MiraApiPlugin.MiraColor, + TabIcon = MiraAssets.SettingsIcon, + }; /// /// Gets whether the modifiers hud should be on the left side of the screen (under roles/task tab). Recommended for streamers. diff --git a/MiraAPI/Modifiers/ModifierComponent.cs b/MiraAPI/Modifiers/ModifierComponent.cs index f4003c9..ef812a5 100644 --- a/MiraAPI/Modifiers/ModifierComponent.cs +++ b/MiraAPI/Modifiers/ModifierComponent.cs @@ -4,7 +4,7 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; using Il2CppInterop.Runtime.Attributes; -using MiraAPI.GameOptions; +using MiraAPI.LocalSettings; using MiraAPI.Modifiers.ModifierDisplay; using MiraAPI.Modifiers.Types; using MiraAPI.Patches.Roles; diff --git a/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs b/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs index 0cbb8c3..2982f67 100644 --- a/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs +++ b/MiraAPI/Modifiers/ModifierDisplay/ModifierDisplayComponent.cs @@ -1,13 +1,10 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using HarmonyLib; using Il2CppInterop.Runtime.Attributes; -using MiraAPI.GameOptions; -using MiraAPI.Patches.Roles; +using MiraAPI.LocalSettings; using MiraAPI.Utilities.Assets; -using Reactor.Utilities; using Reactor.Utilities.Attributes; using Reactor.Utilities.Extensions; using TMPro; diff --git a/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs b/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs index 0b4ad9c..3c73e1a 100644 --- a/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs +++ b/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs @@ -2,11 +2,12 @@ using System.Linq; using HarmonyLib; using MiraAPI.LocalSettings; -using Reactor.Localization.Utilities; +using MiraAPI.Utilities.Assets; +using Reactor.Utilities; using Reactor.Utilities.Extensions; -using TMPro; using UnityEngine; using UnityEngine.Events; +using UnityEngine.UI; namespace MiraAPI.Patches.LocalSettings; @@ -14,6 +15,11 @@ namespace MiraAPI.Patches.LocalSettings; public static class OptionsMenuPatches { internal static BoxCollider2D MaskCollider; + private static SpriteRenderer? background; + + private static int currentPage = 1; + private static Dictionary> tabButtons = new(); + /// /// Creates the tabs and their content /// @@ -28,6 +34,8 @@ public static class OptionsMenuPatches __instance.transform.FindChild("Tint").SetLocalZ(5.6f); } + background = __instance.Background; + var maskObj = new GameObject { layer = 5, @@ -54,16 +62,101 @@ public static class OptionsMenuPatches MaskCollider.isTrigger = true; MaskCollider.enabled = true; + currentPage = 1; + tabButtons.Clear(); float yOffset = 0; - var i = 0; + int i = 0; + int tabIdx = 0; + int page = 1; foreach (var settings in LocalSettingsManager.Tabs) { - settings.CreateTab(__instance, ref i, ref yOffset); + var tab = settings.CreateTab(__instance); + if (settings.ShouldCreateButton) + { + var button = settings.CreateTabButton(__instance, ref tabIdx, ref yOffset); + button.GetComponent().Content = tab; + button.SetActive(page == currentPage); + + if (!tabButtons.TryGetValue(page, out var list)) + { + tabButtons.Add(page, new()); + list = tabButtons[page]; + } + list.Add(button); + yOffset += 0.6f; + tabIdx++; + i++; + } + if (i > 7) + { + i = 0; + yOffset = 0; + page++; + } } + + void ChangePage(int increase) + { + currentPage += increase; + if (currentPage > tabButtons.Count) + { + currentPage = 1; + } + if (currentPage < 1) + { + currentPage = tabButtons.Count; + } + } + + // Create page buttons + // TODO: In-game menu has no close button. I tried making one from scratch but failed. Add one later plz + if (tabButtons.Count <= 1) + { + return; + } + + var nextButton = Object.Instantiate(__instance.BackButton, __instance.transform).GetComponent(); + nextButton.transform.localPosition = new Vector3(3.2f, 2.6f, -10); + nextButton.transform.localScale = new Vector2(0.25f, 0.25f); + var nextRend = nextButton.GetComponent(); + nextRend.sprite = MiraAssets.NextButton.LoadAsset(); + nextButton.gameObject.GetComponent().DestroyImmediate(); + + var nextRollover = nextButton.gameObject.AddComponent(); + nextRollover.Target = nextRend; + nextRollover.OverColor = Palette.AcceptedGreen; + + nextButton.OnClick = new Button.ButtonClickedEvent(); + nextButton.OnClick.AddListener( + (UnityAction)(() => + { + ChangePage(1); + UpdatePages(); + })); + + var backButton = Object.Instantiate(__instance.BackButton, __instance.transform).GetComponent(); + backButton.transform.localPosition = new Vector3(2.9f, 2.6f, -10); + backButton.transform.localScale = new Vector2(0.25f, 0.25f); + var backRend = backButton.GetComponent(); + backRend.sprite = MiraAssets.NextButton.LoadAsset(); + backRend.flipX = true; + backButton.gameObject.GetComponent().DestroyImmediate(); + + var backRollover = backButton.gameObject.AddComponent(); + backRollover.Target = backRend; + backRollover.OverColor = Palette.AcceptedGreen; + + backButton.OnClick = new Button.ButtonClickedEvent(); + backButton.OnClick.AddListener( + (UnityAction)(() => + { + ChangePage(-1); + UpdatePages(); + })); } /// - /// Modifies how opening tabs is handled for the custom ones to work + /// Modifies how opening tabs is handled for the custom ones to work. /// [HarmonyPrefix] [HarmonyPatch(nameof(OptionsMenuBehaviour.OpenTabGroup))] @@ -72,7 +165,8 @@ public static class OptionsMenuPatches __instance.Tabs.ToList().ForEach(x => x.Close()); // Close all vanilla tabs LocalSettingsManager.Tabs.ForEach(CustomClose); // Close all mod tabs - if (index >= 10) // Tabs with index 10 and above are the mod settings tabs + // Tabs with index 10 and above are the mod settings tabs + if (index >= 10) { CustomOpen(LocalSettingsManager.Tabs[index - 10]); return false; @@ -82,35 +176,51 @@ public static class OptionsMenuPatches } /// - /// Makes the custom tabs close when opening the options menu + /// Makes the custom tabs close when opening the options menu and reset pages. /// [HarmonyPostfix] [HarmonyPatch(nameof(OptionsMenuBehaviour.Open))] public static void OpenPostfix(OptionsMenuBehaviour __instance) { LocalSettingsManager.Tabs.ForEach(CustomClose); + currentPage = 1; + UpdatePages(); + } + + private static void UpdatePages() + { + foreach (var pages in tabButtons) + { + pages.Value.Do(x => x.SetActive(pages.Key == currentPage)); + } } private static void CustomOpen(LocalSettingsTab tab) { if (tab.TabButton?.Button) - tab.TabButton!.Button.color = tab.TabHoverColor; + tab.TabButton!.Button.color = tab.TabAppearance.TabButtonHoverColor; if (tab.TabButton?.Rollover) - tab.TabButton!.Rollover.OutColor = tab.TabHoverColor; + tab.TabButton!.Rollover.OutColor = tab.TabAppearance.TabButtonHoverColor; if (tab.TabButton?.Content) tab.TabButton!.Content.SetActive(true); + + if (background != null) + background.color = tab.TabAppearance.TabColor; } private static void CustomClose(LocalSettingsTab tab) { if (tab.TabButton?.Button) - tab.TabButton!.Button.color = tab.TabColor; + tab.TabButton!.Button.color = tab.TabAppearance.TabButtonColor; if (tab.TabButton?.Rollover) - tab.TabButton!.Rollover.OutColor = tab.TabColor; + tab.TabButton!.Rollover.OutColor = tab.TabAppearance.TabButtonColor; if (tab.TabButton?.Content) tab.TabButton!.Content.SetActive(false); + + if (background != null) + background.color = Color.white; } } diff --git a/MiraAPI/Patches/LocalSettings/ToggleBehaviourPatch.cs b/MiraAPI/Patches/LocalSettings/ToggleBehaviourPatch.cs index 5022d1c..9a8321d 100644 --- a/MiraAPI/Patches/LocalSettings/ToggleBehaviourPatch.cs +++ b/MiraAPI/Patches/LocalSettings/ToggleBehaviourPatch.cs @@ -7,14 +7,15 @@ namespace MiraAPI.Patches.LocalSettings; public static class ToggleBehaviourPatch { /// - /// ResetText was inlined ofc + /// ResetText was inlined ofc. + /// Skill issue /// [HarmonyPrefix] [HarmonyPatch(nameof(ToggleButtonBehaviour.UpdateText))] - public static bool ResetTextPrefix(ToggleButtonBehaviour __instance, bool on) + public static bool UpdateTextPrefix(ToggleButtonBehaviour __instance, bool on) { - __instance.onState = on; var color = on ? new Color(0f, 1f, 0.16470589f, 1f) : Color.white; + __instance.onState = on; __instance.Background.color = color; // Used to mask the text @@ -22,7 +23,6 @@ public static class ToggleBehaviourPatch __instance.Text.text = $"" + $"{DestroyableSingleton.Instance.GetString(__instance.BaseText)}: " + $"{DestroyableSingleton.Instance.GetString(__instance.onState ? StringNames.SettingsOn : StringNames.SettingsOff)}"; - __instance.Text.sortingOrder = 151; __instance.Text.renderer.sortingLayerName = "Default"; __instance.Text.renderer.sortingOrder = 151; diff --git a/MiraAPI/Utilities/Helpers.cs b/MiraAPI/Utilities/Helpers.cs index 06ddc76..4717189 100644 --- a/MiraAPI/Utilities/Helpers.cs +++ b/MiraAPI/Utilities/Helpers.cs @@ -496,4 +496,24 @@ public static class Helpers } return KeyboardKeyCode.None; } + + /// + /// Returns the formated value using the specified suffix and format string. + /// + /// The value to format. + /// The suffix to add. + /// The format string to use to format. + /// The formated value. + public static string FormatValue(float value, MiraNumberSuffixes suffix = MiraNumberSuffixes.None, string formatString = "0.0") + { + return suffix switch + { + MiraNumberSuffixes.None => value.ToString(formatString, NumberFormatInfo.InvariantInfo), + MiraNumberSuffixes.Multiplier => value.ToString(formatString, NumberFormatInfo.InvariantInfo) + "x", + MiraNumberSuffixes.Percent => value.ToString(formatString, NumberFormatInfo.InvariantInfo) + "%", + _ => TranslationController.Instance.GetString( + StringNames.GameSecondsAbbrev, + (Il2CppSystem.Object[])[value.ToString(formatString, CultureInfo.InvariantCulture)]), + }; + } } From 8b2d24ccc103da68f2d2e3cf22ee33ce0bedd9d7 Mon Sep 17 00:00:00 2001 From: xChipseq Date: Mon, 1 Sep 2025 22:22:32 +0200 Subject: [PATCH 06/12] more stuff --- MiraAPI.Example/ExampleLocalSetings.cs | 59 +++++-------------- .../Attributes/LocalSliderSettingAttribute.cs | 3 +- ...bute.cs => LocalToggleSettingAttribute.cs} | 2 +- .../SettingTypes/LocalEnumSetting.cs | 8 ++- .../SettingTypes/LocalNumberSetting.cs | 1 + .../SettingTypes/LocalSettingBase.cs | 2 +- .../SettingTypes/LocalSliderSetting.cs | 26 +++++--- MiraAPI/MiraApiSettings.cs | 2 +- 8 files changed, 46 insertions(+), 57 deletions(-) rename MiraAPI/LocalSettings/Attributes/{LocalSettingToggleAttribute.cs => LocalToggleSettingAttribute.cs} (93%) diff --git a/MiraAPI.Example/ExampleLocalSetings.cs b/MiraAPI.Example/ExampleLocalSetings.cs index da7e4f5..258a970 100644 --- a/MiraAPI.Example/ExampleLocalSetings.cs +++ b/MiraAPI.Example/ExampleLocalSetings.cs @@ -10,68 +10,37 @@ namespace MiraAPI.Example; public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Mira Example"; + protected override bool ShouldCreateLabels => false; + public override LocalSettingTabAppearance TabAppearance => new() { TabColor = Color.cyan, ToggleActiveColor = Color.blue, ToggleInactiveColor = Color.red, - SliderHoverColor = Color.magenta, }; - [LocalSliderSetting(min: 0, max: 15, suffixType: MiraNumberSuffixes.Multiplier)] - public ConfigEntry ExampleFloatEntry { get; private set; } = config.Bind("General", "Example Float Setting", 50f); + [LocalToggleSetting] + public ConfigEntry ExampleToggle { get; private set; } = config.Bind("General", "Example Bool", true); - [LocalSettingToggle] - public ConfigEntry PlayStartupSound { get; private set; } = config.Bind("Audio", "Play Startup Sound", true); + [LocalToggleSetting] + public ConfigEntry ExampleToggle2 { get; private set; } = config.Bind("General", "Example Bool 2", false); - [LocalSettingToggle] - public ConfigEntry MuteInBackground { get; private set; } = config.Bind("Audio", "Mute In Background", false); + [LocalSliderSetting(suffixType: MiraNumberSuffixes.Percent, formatString: "0", roundValue: true)] + public ConfigEntry ExampleSlider { get; private set; } = config.Bind("General", "Slider", 50f); - [LocalSettingToggle] - public ConfigEntry AutoSaveEnabled { get; private set; } = config.Bind("General", "Auto Save Enabled", true); - - [LocalSettingToggle] - public ConfigEntry EnableCloudSync { get; private set; } = config.Bind("General", "Enable Cloud Sync", false); - - [LocalSettingToggle] - public ConfigEntry ShowAdvancedTooltips { get; private set; } = config.Bind("UI", "Show Advanced Tooltips", true); - - [LocalSettingToggle] - public ConfigEntry CompactUI { get; private set; } = config.Bind("UI", "Compact UI Layout", false); - - [LocalSettingToggle] - public ConfigEntry EnableControllerSupport { get; private set; } = config.Bind("Input", "Enable Controller Support", true); - - [LocalSettingToggle] - public ConfigEntry EnableVibration { get; private set; } = config.Bind("Input", "Enable Controller Vibration", true); - - [LocalSettingToggle] - public ConfigEntry ShowPlayerNames { get; private set; } = config.Bind("Gameplay", "Show Player Names", true); - - [LocalSettingToggle] - public ConfigEntry EnableHardMode { get; private set; } = config.Bind("Gameplay", "Enable Hard Mode", false); - [LocalSettingToggle] - public ConfigEntry EnableHardMode2 { get; private set; } = config.Bind("Gameplay", "Enable Hard Mode", false); - [LocalSettingToggle] - public ConfigEntry EnableHardMode3 { get; private set; } = config.Bind("Gameplay", "Enable Hard Mode", false); - [LocalSettingToggle] - public ConfigEntry EnableHardMode4 { get; private set; } = config.Bind("Gameplay", "Enable Hard Mode", false); - [LocalSettingToggle] - public ConfigEntry EnableHardMode5 { get; private set; } = config.Bind("Gameplay", "Enable Hard Mode", false); - - [LocalNumberSetting] - public ConfigEntry NumberExample { get; private set; } = config.Bind("Gameplay", "Number of cheeseburgers", 3); + [LocalNumberSetting(suffixType: MiraNumberSuffixes.Seconds)] + public ConfigEntry ExampleNumber { get; private set; } = config.Bind("General", "Example Number", 4); [LocalEnumSetting] - public ConfigEntry EnumExample { get; private set; } = config.Bind("Gameplay", "Anything else", ExampleSettingEnum.Fries); + public ConfigEntry ExampleEnum { get; private set; } = config.Bind("General", "Example Enum", ExampleEnumSetting.Fries); } -public enum ExampleSettingEnum +public enum ExampleEnumSetting { + Cheeseburger, + Sandwich, Fries, - OnionRings, ChickenNuggets, - Gun, } public class Tab1(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } diff --git a/MiraAPI/LocalSettings/Attributes/LocalSliderSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSliderSettingAttribute.cs index 53555fa..4a211d5 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalSliderSettingAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalSliderSettingAttribute.cs @@ -19,6 +19,7 @@ public class LocalSliderSettingAttribute( float min = 0, float max = 100, string? description = null, + bool displayValue = false, string? formatString = null, bool roundValue = false, MiraNumberSuffixes suffixType = MiraNumberSuffixes.None @@ -27,6 +28,6 @@ public class LocalSliderSettingAttribute( /// internal override LocalSliderSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase) { - return new LocalSliderSetting(tab, configEntryBase, name, description, new FloatRange(min, max), suffixType, formatString, roundValue); + return new LocalSliderSetting(tab, configEntryBase, name, description, new FloatRange(min, max), displayValue, suffixType, formatString, roundValue); } } diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalToggleSettingAttribute.cs similarity index 93% rename from MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs rename to MiraAPI/LocalSettings/Attributes/LocalToggleSettingAttribute.cs index 2add91d..31c9b09 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalSettingToggleAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalToggleSettingAttribute.cs @@ -9,7 +9,7 @@ namespace MiraAPI.LocalSettings.Attributes; /// /// [AttributeUsage(AttributeTargets.Property)] -public class LocalSettingToggleAttribute( +public class LocalToggleSettingAttribute( string? name = null, string? description = null ) : LocalSettingAttribute(name, description) diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalEnumSetting.cs b/MiraAPI/LocalSettings/SettingTypes/LocalEnumSetting.cs index c435a58..437eed1 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalEnumSetting.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalEnumSetting.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using BepInEx.Configuration; using MiraAPI.Utilities; using Reactor.Utilities.Extensions; @@ -40,7 +41,11 @@ public class LocalEnumSetting : LocalSettingBase : base(tab, configEntry, name, description) { EnumType = enumType; - Values = values ?? Enum.GetNames(EnumType); + Values = values ?? Enum + .GetValues(configEntry.SettingType) + .Cast() + .Select(x => x.ToDisplayString()) + .ToArray(); } /// @@ -58,6 +63,7 @@ public class LocalEnumSetting : LocalSettingBase if (highlight != null) { highlight.color = Tab!.TabAppearance.EnumHoverColor; + highlight.gameObject.SetActive(false); } toggleComp.Destroy(); diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs b/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs index 1473889..70be2aa 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs @@ -74,6 +74,7 @@ public class LocalNumberSetting : LocalSettingBase if (highlight != null) { highlight.color = Tab!.TabAppearance.NumberHoverColor; + highlight.gameObject.SetActive(false); } toggleComp.Destroy(); diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs index 934b33e..7d2bc5b 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs @@ -49,7 +49,7 @@ public abstract class LocalSettingBase : ILocalSetting /// The value text. protected virtual string GetValueText() { - return GetValue()?.ToString() ?? string.Empty; + return string.Empty; } /// diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSliderSetting.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSliderSetting.cs index 8535baa..ae6f500 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalSliderSetting.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSliderSetting.cs @@ -19,6 +19,11 @@ public class LocalSliderSetting : LocalSettingBase /// public FloatRange SliderRange { get; } + /// + /// Gets a value indicating whether the value should be displayed next to name. + /// + public bool DisplayValue { get; } + /// /// Gets a format for the text to use to format the number. /// @@ -48,15 +53,17 @@ public class LocalSliderSetting : LocalSettingBase string? name = null, string? description = null, FloatRange? sliderRange = null, + bool displayValue = false, MiraNumberSuffixes? suffixType = null, string? formatString = null, bool roundValue = false) : base(tab, configEntry, name, description) { - SuffixType = suffixType ?? MiraNumberSuffixes.None; SliderRange = sliderRange ?? new FloatRange(0, 100); - RoundValue = roundValue; + DisplayValue = displayValue; + SuffixType = suffixType ?? MiraNumberSuffixes.None; FormatString = formatString ?? "0.0"; + RoundValue = roundValue; } /// @@ -80,7 +87,7 @@ public class LocalSliderSetting : LocalSettingBase rollover.OutColor = Tab!.TabAppearance.SliderColor; rollover.OverColor = Tab!.TabAppearance.SliderHoverColor; newSlider.Title.transform.localPosition = new Vector3(0.5f, 0, -1f); - newSlider.Title.horizontalAlignment = HorizontalAlignmentOptions.Right; + newSlider.Title.horizontalAlignment = DisplayValue ? HorizontalAlignmentOptions.Left : HorizontalAlignmentOptions.Center; newSlider.Title.text = GetValueText(); newSlider.OnValueChange.AddListener((UnityAction)(() => @@ -100,9 +107,14 @@ public class LocalSliderSetting : LocalSettingBase /// protected override string GetValueText() { - var value = GetValue(); - var formated = Helpers.FormatValue(value, SuffixType, FormatString); - var maxFormated = Helpers.FormatValue(SliderRange.max, SuffixType, FormatString); - return $"{Name}: {formated} / {maxFormated}"; + if (DisplayValue) + { + var value = GetValue(); + var formated = Helpers.FormatValue(value, SuffixType, FormatString); + var maxFormated = Helpers.FormatValue(SliderRange.max, SuffixType, FormatString); + return $"{Name}: {formated} / {maxFormated}"; + } + + return $"{Name}"; } } diff --git a/MiraAPI/MiraApiSettings.cs b/MiraAPI/MiraApiSettings.cs index 4ff4d04..6a659f1 100644 --- a/MiraAPI/MiraApiSettings.cs +++ b/MiraAPI/MiraApiSettings.cs @@ -32,6 +32,6 @@ public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config) /// /// Gets whether the modifiers hud should be on the left side of the screen (under roles/task tab). Recommended for streamers. /// - [LocalSettingToggle] + [LocalToggleSetting] public ConfigEntry ModifiersHudLeftSide { get; private set; } = config.Bind("Displays", "Show Modifiers HUD on Left Side", false); } From f515516937b8bc3c363767c38d472f8f2acbd4d6 Mon Sep 17 00:00:00 2001 From: xChipseq Date: Mon, 1 Sep 2025 22:24:58 +0200 Subject: [PATCH 07/12] remove the two million test tabs --- MiraAPI.Example/ExampleLocalSetings.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/MiraAPI.Example/ExampleLocalSetings.cs b/MiraAPI.Example/ExampleLocalSetings.cs index 258a970..a258967 100644 --- a/MiraAPI.Example/ExampleLocalSetings.cs +++ b/MiraAPI.Example/ExampleLocalSetings.cs @@ -2,7 +2,6 @@ using MiraAPI.LocalSettings; using MiraAPI.LocalSettings.Attributes; using MiraAPI.Utilities; -using Reactor.Utilities; using UnityEngine; namespace MiraAPI.Example; @@ -41,16 +40,4 @@ public enum ExampleEnumSetting Sandwich, Fries, ChickenNuggets, -} - -public class Tab1(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } -public class Tab2(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } -public class Tab4(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } -public class Tab3(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } -public class Tab5(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } -public class Tab6(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } -public class Tab7(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } -public class Tab8(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } -public class Tab9(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } -public class Tab10(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } -public class Tab11(ConfigFile config) : LocalSettingsTab(config) { public override string TabName => "Tab"; public override LocalSettingTabAppearance TabAppearance => new(); } +} \ No newline at end of file From 6b37b9c09c72646970bd4f6cf6bee765ddb1075b Mon Sep 17 00:00:00 2001 From: xChipseq Date: Mon, 1 Sep 2025 22:28:30 +0200 Subject: [PATCH 08/12] add an error --- MiraAPI/PluginLoading/MiraPluginManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index 98b8db5..27d8e92 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -362,6 +362,7 @@ public sealed class MiraPluginManager if (property.GetValue(tabInstance) is not ConfigEntryBase configEntry) { + Logger.Error($"Option property {property.Name} in {type.Name} has to be a config entry."); continue; } From 1f6962efe9fa63e9e2ac2b45140050954377489d Mon Sep 17 00:00:00 2001 From: xChipseq Date: Mon, 1 Sep 2025 23:34:17 +0200 Subject: [PATCH 09/12] add local setting buttons --- MiraAPI.Example/ExampleLocalSetings.cs | 12 ++- .../LocalSettingsButtonAttribute.cs | 12 +++ MiraAPI/LocalSettings/LocalSettingsButton.cs | 80 +++++++++++++++++++ MiraAPI/LocalSettings/LocalSettingsTab.cs | 60 ++++++++++++-- .../LocalSettingsTabAppearance.cs | 10 +++ .../SettingTypes/LocalSettingBase.cs | 1 + MiraAPI/PluginLoading/MiraPluginManager.cs | 19 +++-- 7 files changed, 182 insertions(+), 12 deletions(-) create mode 100644 MiraAPI/LocalSettings/Attributes/LocalSettingsButtonAttribute.cs create mode 100644 MiraAPI/LocalSettings/LocalSettingsButton.cs diff --git a/MiraAPI.Example/ExampleLocalSetings.cs b/MiraAPI.Example/ExampleLocalSetings.cs index a258967..21b68f0 100644 --- a/MiraAPI.Example/ExampleLocalSetings.cs +++ b/MiraAPI.Example/ExampleLocalSetings.cs @@ -2,6 +2,7 @@ using MiraAPI.LocalSettings; using MiraAPI.LocalSettings.Attributes; using MiraAPI.Utilities; +using Reactor.Utilities; using UnityEngine; namespace MiraAPI.Example; @@ -13,11 +14,13 @@ public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config) public override LocalSettingTabAppearance TabAppearance => new() { - TabColor = Color.cyan, ToggleActiveColor = Color.blue, ToggleInactiveColor = Color.red, }; + [LocalSettingsButton] + public LocalSettingsButton ExampleButton { get; private set; } = new("Example Button", OnExampleButtonClick); + [LocalToggleSetting] public ConfigEntry ExampleToggle { get; private set; } = config.Bind("General", "Example Bool", true); @@ -32,6 +35,11 @@ public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config) [LocalEnumSetting] public ConfigEntry ExampleEnum { get; private set; } = config.Bind("General", "Example Enum", ExampleEnumSetting.Fries); + + private static void OnExampleButtonClick(LocalSettingsTab instance) + { + Logger.Warning("Button clicked"); + } } public enum ExampleEnumSetting @@ -40,4 +48,4 @@ public enum ExampleEnumSetting Sandwich, Fries, ChickenNuggets, -} \ No newline at end of file +} diff --git a/MiraAPI/LocalSettings/Attributes/LocalSettingsButtonAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalSettingsButtonAttribute.cs new file mode 100644 index 0000000..043a81d --- /dev/null +++ b/MiraAPI/LocalSettings/Attributes/LocalSettingsButtonAttribute.cs @@ -0,0 +1,12 @@ +using System; +using BepInEx.Configuration; + +namespace MiraAPI.LocalSettings.Attributes; + +/// +/// Attribute for local setting buttons. +/// +[AttributeUsage(AttributeTargets.Property)] +public class LocalSettingsButtonAttribute : Attribute +{ +} diff --git a/MiraAPI/LocalSettings/LocalSettingsButton.cs b/MiraAPI/LocalSettings/LocalSettingsButton.cs new file mode 100644 index 0000000..2876dac --- /dev/null +++ b/MiraAPI/LocalSettings/LocalSettingsButton.cs @@ -0,0 +1,80 @@ +using System; +using MiraAPI.Utilities; +using Reactor.Localization.Utilities; +using Reactor.Utilities.Extensions; +using TMPro; +using UnityEngine; +using UnityEngine.Events; +using Object = UnityEngine.Object; + +namespace MiraAPI.LocalSettings; + +/// +/// Represents a local settings button. +/// +public class LocalSettingsButton(string text, Action onClick) +{ + /// + /// Gets or sets the button text. + /// + public string Text { get; set; } = text; + + /// + /// Gets or sets the button on-click action. + /// + public Action OnClick { get; set; } = onClick; + + /// + /// Gets the tab instance this button belongs to. + /// + public LocalSettingsTab Tab { get; internal set; } + + internal GameObject CreateButton(ToggleButtonBehaviour toggle, Transform parent, ref float offset, ref int order, bool last) + { + var button = Object.Instantiate(toggle, parent).GetComponent(); + var tmp = button.transform.FindChild("Text_TMP").GetComponent(); + var rollover = button.GetComponent(); + tmp.GetComponent().Destroy(); + button.gameObject.SetActive(true); + + var toggleComp = button.GetComponent(); + var background = toggleComp.Background; + button.transform.FindChild("ButtonHighlight")?.gameObject.DestroyImmediate(); + toggleComp.Destroy(); + + if (last && order == 1) + { + // Button in the middle + button.transform.localPosition = new Vector3(0, 1.85f - offset, -7); + } + else + { + button.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset, -7); + } + + tmp.text = Text; + button.name = Text; + button.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); + rollover.OutColor = Tab!.TabAppearance.ButtonColor; + rollover.OverColor = Tab!.TabAppearance.ButtonHoverColor; + rollover.Target = background; + background.color = Tab!.TabAppearance.ButtonColor; + + var tab = Tab; + var click = OnClick; + button.OnClick.AddListener((UnityAction)(() => click(tab))); + + Helpers.DivideSize(button.gameObject, 1.1f); + + order++; + if (order > 2 && !last) + { + offset += 0.5f; + order = 1; + } + if (last) + offset += 0.6f; + + return button.gameObject; + } +} diff --git a/MiraAPI/LocalSettings/LocalSettingsTab.cs b/MiraAPI/LocalSettings/LocalSettingsTab.cs index 5516d05..7f30306 100644 --- a/MiraAPI/LocalSettings/LocalSettingsTab.cs +++ b/MiraAPI/LocalSettings/LocalSettingsTab.cs @@ -42,11 +42,32 @@ public abstract class LocalSettingsTab(ConfigFile config) /// public ConfigFile Config { get; } = config ?? throw new ArgumentNullException(nameof(config)); - internal TabGroup? TabButton { get; set; } - internal List Settings { get; } = []; + /// + /// Gets the tab button instance. + /// + public TabGroup? TabButton { get; internal set; } + + /// + /// Gets the list of settings. + /// + public List Settings { get; } = []; + + /// + /// Gets the list of buttons. + /// + public List Buttons { get; } = []; + private Scroller? Scroller { get; set; } - internal GameObject CreateTab(OptionsMenuBehaviour instance) + /// + /// Invoked when an option value is changed. + /// + /// The config entry which was changed. + public virtual void OnOptionChanged(ConfigEntryBase configEntry) + { + } + + public virtual GameObject CreateTab(OptionsMenuBehaviour instance) { var tab = Object.Instantiate(instance.transform.FindChild("GeneralTab").gameObject, instance.transform); tab.name = $"{TabName}Tab"; @@ -120,11 +141,40 @@ public abstract class LocalSettingsTab(ConfigFile config) contentOrder = 1; } - Scroller.SetBounds(new FloatRange(0, Settings.Count * 0.5f - 5f), new FloatRange(0, 0)); + contentIndex = 0; + contentOrder = 1; + foreach (var button in Buttons) + { + var obj = button.CreateButton( + toggle, + Scroller.Inner, + ref contentOffset, + ref contentOrder, + contentIndex == Buttons.Count); + + obj!.GetComponentsInChildren(true).Do(x => + { + x.maskInteraction = SpriteMaskInteraction.VisibleInsideMask; + x.sortingOrder = 150; + x.sortingLayerName = "Default"; + }); + + obj.GetComponentsInChildren(true).Do(x => + { + x.sortingOrder = 151; + x.sortingLayerName = "Default"; + }); + + obj.GetComponentsInChildren().Do(x => { x.ClickMask = OptionsMenuPatches.MaskCollider; }); + + contentIndex++; + } + + Scroller.SetBounds(new FloatRange(0, (Settings.Count + Buttons.Count) * 0.5f - 5f), new FloatRange(0, 0)); return tab; } - internal GameObject CreateTabButton(OptionsMenuBehaviour instance, ref int tabIdx, ref float offset) + public virtual GameObject CreateTabButton(OptionsMenuBehaviour instance, ref int tabIdx, ref float offset) { var tabButtonObject = Object.Instantiate(instance.Tabs[0], instance.transform); tabButtonObject.name = $"{TabName} Button"; diff --git a/MiraAPI/LocalSettings/LocalSettingsTabAppearance.cs b/MiraAPI/LocalSettings/LocalSettingsTabAppearance.cs index 2cad514..26fd726 100644 --- a/MiraAPI/LocalSettings/LocalSettingsTabAppearance.cs +++ b/MiraAPI/LocalSettings/LocalSettingsTabAppearance.cs @@ -78,4 +78,14 @@ public record struct LocalSettingTabAppearance() /// Gets or sets hover color of enum buttons. /// public Color EnumHoverColor { get; set; } = Palette.AcceptedGreen; + + /// + /// Gets or sets color of buttons. + /// + public Color ButtonColor { get; set; } = Color.white; + + /// + /// Gets or sets hover color of buttons. + /// + public Color ButtonHoverColor { get; set; } = Palette.AcceptedGreen; } diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs index 7d2bc5b..f991376 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs @@ -68,5 +68,6 @@ public abstract class LocalSettingBase : ILocalSetting public void SetValue(T value) { ConfigEntry.BoxedValue = value; + Tab?.OnOptionChanged(ConfigEntry); } } diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index 27d8e92..0c987cd 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -344,6 +344,20 @@ public sealed class MiraPluginManager foreach (var property in type.GetProperties()) { + if (LocalSettingsManager.TypeToTab[type] is not { } tabInstance) + { + continue; + } + + if (property.GetCustomAttribute() != null && + property.PropertyType.IsAssignableTo(typeof(LocalSettingsButton))) + { + var button = property.GetValue(tabInstance) as LocalSettingsButton; + button!.Tab = tabInstance; + tabInstance.Buttons.Add(button); + continue; + } + if (!typeof(ConfigEntryBase).IsAssignableFrom(property.PropertyType)) { continue; @@ -355,11 +369,6 @@ public sealed class MiraPluginManager continue; } - if (LocalSettingsManager.TypeToTab[type] is not { } tabInstance) - { - continue; - } - if (property.GetValue(tabInstance) is not ConfigEntryBase configEntry) { Logger.Error($"Option property {property.Name} in {type.Name} has to be a config entry."); From ac113b6a7f13e148f9bb7388fcbf7e9e1d348159 Mon Sep 17 00:00:00 2001 From: xChipseq Date: Tue, 2 Sep 2025 18:55:27 +0200 Subject: [PATCH 10/12] add tab open method and fixes --- MiraAPI.Example/ExampleLocalSetings.cs | 4 +-- MiraAPI/LocalSettings/LocalSettingsButton.cs | 12 ++++---- MiraAPI/LocalSettings/LocalSettingsTab.cs | 28 ++++++++++++++++++- .../LocalSettings/OptionsMenuPatches.cs | 10 +++++-- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/MiraAPI.Example/ExampleLocalSetings.cs b/MiraAPI.Example/ExampleLocalSetings.cs index 21b68f0..e7c292f 100644 --- a/MiraAPI.Example/ExampleLocalSetings.cs +++ b/MiraAPI.Example/ExampleLocalSetings.cs @@ -36,9 +36,9 @@ public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config) [LocalEnumSetting] public ConfigEntry ExampleEnum { get; private set; } = config.Bind("General", "Example Enum", ExampleEnumSetting.Fries); - private static void OnExampleButtonClick(LocalSettingsTab instance) + private static void OnExampleButtonClick() { - Logger.Warning("Button clicked"); + LocalSettingsTabSingleton.Instance.Open(); } } diff --git a/MiraAPI/LocalSettings/LocalSettingsButton.cs b/MiraAPI/LocalSettings/LocalSettingsButton.cs index 2876dac..fa0c0bf 100644 --- a/MiraAPI/LocalSettings/LocalSettingsButton.cs +++ b/MiraAPI/LocalSettings/LocalSettingsButton.cs @@ -12,7 +12,7 @@ namespace MiraAPI.LocalSettings; /// /// Represents a local settings button. /// -public class LocalSettingsButton(string text, Action onClick) +public class LocalSettingsButton(string text, Action onClick) { /// /// Gets or sets the button text. @@ -22,12 +22,12 @@ public class LocalSettingsButton(string text, Action onClick) /// /// Gets or sets the button on-click action. /// - public Action OnClick { get; set; } = onClick; + public Action OnClick { get; set; } = onClick; /// /// Gets the tab instance this button belongs to. /// - public LocalSettingsTab Tab { get; internal set; } + public LocalSettingsTab? Tab { get; internal set; } internal GameObject CreateButton(ToggleButtonBehaviour toggle, Transform parent, ref float offset, ref int order, bool last) { @@ -59,11 +59,9 @@ public class LocalSettingsButton(string text, Action onClick) rollover.OverColor = Tab!.TabAppearance.ButtonHoverColor; rollover.Target = background; background.color = Tab!.TabAppearance.ButtonColor; + button.OnClick.AddListener((UnityAction)OnClick); - var tab = Tab; - var click = OnClick; - button.OnClick.AddListener((UnityAction)(() => click(tab))); - + background.transform.localScale = new Vector3(1, 1.25f, 1); Helpers.DivideSize(button.gameObject, 1.1f); order++; diff --git a/MiraAPI/LocalSettings/LocalSettingsTab.cs b/MiraAPI/LocalSettings/LocalSettingsTab.cs index 7f30306..a04830d 100644 --- a/MiraAPI/LocalSettings/LocalSettingsTab.cs +++ b/MiraAPI/LocalSettings/LocalSettingsTab.cs @@ -57,6 +57,11 @@ public abstract class LocalSettingsTab(ConfigFile config) /// public List Buttons { get; } = []; + /// + /// Gets the index of this tab. + /// + protected int TabIndex => LocalSettingsManager.Tabs.IndexOf(this) + 10; + private Scroller? Scroller { get; set; } /// @@ -67,6 +72,20 @@ public abstract class LocalSettingsTab(ConfigFile config) { } + /// + /// Attempts to open the tab. + /// Override for custom behavior. + /// + public virtual void Open() + { + OptionsMenuPatches.Instance?.OpenTabGroup(TabIndex); + } + + /// + /// Creates the tab and it's content. + /// + /// The instace. + /// The created tab . public virtual GameObject CreateTab(OptionsMenuBehaviour instance) { var tab = Object.Instantiate(instance.transform.FindChild("GeneralTab").gameObject, instance.transform); @@ -141,7 +160,7 @@ public abstract class LocalSettingsTab(ConfigFile config) contentOrder = 1; } - contentIndex = 0; + contentIndex = 1; contentOrder = 1; foreach (var button in Buttons) { @@ -174,6 +193,13 @@ public abstract class LocalSettingsTab(ConfigFile config) return tab; } + /// + /// Creates the tab button. + /// + /// The instance. + /// The tab index. + /// The current button offset. + /// The created button . public virtual GameObject CreateTabButton(OptionsMenuBehaviour instance, ref int tabIdx, ref float offset) { var tabButtonObject = Object.Instantiate(instance.Tabs[0], instance.transform); diff --git a/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs b/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs index 3c73e1a..574070f 100644 --- a/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs +++ b/MiraAPI/Patches/LocalSettings/OptionsMenuPatches.cs @@ -14,6 +14,7 @@ namespace MiraAPI.Patches.LocalSettings; [HarmonyPatch(typeof(OptionsMenuBehaviour))] public static class OptionsMenuPatches { + internal static OptionsMenuBehaviour? Instance { get; private set; } internal static BoxCollider2D MaskCollider; private static SpriteRenderer? background; @@ -27,6 +28,7 @@ public static class OptionsMenuPatches [HarmonyPatch(nameof(OptionsMenuBehaviour.Start))] public static void StartPostfix(OptionsMenuBehaviour __instance) { + Instance = __instance; // Fix for tabs not being clickable in the main menu if (!AmongUsClient.Instance.IsInGame) { @@ -176,7 +178,7 @@ public static class OptionsMenuPatches } /// - /// Makes the custom tabs close when opening the options menu and reset pages. + /// Makes the custom tabs close when opening the options menu and resets pages. /// [HarmonyPostfix] [HarmonyPatch(nameof(OptionsMenuBehaviour.Open))] @@ -191,7 +193,11 @@ public static class OptionsMenuPatches { foreach (var pages in tabButtons) { - pages.Value.Do(x => x.SetActive(pages.Key == currentPage)); + pages.Value? + .Where(x => x != null) + .Do( + x => x?.SetActive( + pages.Key == currentPage)); } } From ebdb4a09cab35e1e32046f60181da28556003cde Mon Sep 17 00:00:00 2001 From: xChipseq Date: Tue, 2 Sep 2025 19:24:34 +0200 Subject: [PATCH 11/12] make number options accept floats --- MiraAPI.Example/ExampleLocalSetings.cs | 8 +++++--- .../Attributes/LocalNumberSettingAttribute.cs | 8 ++++---- .../SettingTypes/LocalNumberSetting.cs | 14 +++++++------- .../LocalSettings/SettingTypes/LocalSettingBase.cs | 4 ++-- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/MiraAPI.Example/ExampleLocalSetings.cs b/MiraAPI.Example/ExampleLocalSetings.cs index e7c292f..bac0507 100644 --- a/MiraAPI.Example/ExampleLocalSetings.cs +++ b/MiraAPI.Example/ExampleLocalSetings.cs @@ -2,7 +2,6 @@ using MiraAPI.LocalSettings; using MiraAPI.LocalSettings.Attributes; using MiraAPI.Utilities; -using Reactor.Utilities; using UnityEngine; namespace MiraAPI.Example; @@ -30,8 +29,11 @@ public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config) [LocalSliderSetting(suffixType: MiraNumberSuffixes.Percent, formatString: "0", roundValue: true)] public ConfigEntry ExampleSlider { get; private set; } = config.Bind("General", "Slider", 50f); - [LocalNumberSetting(suffixType: MiraNumberSuffixes.Seconds)] - public ConfigEntry ExampleNumber { get; private set; } = config.Bind("General", "Example Number", 4); + [LocalNumberSetting] + public ConfigEntry ExampleNumber { get; private set; } = config.Bind("General", "Example Number", 4f); + + [LocalNumberSetting(min: 0.1f, max: 2.5f, increment: 0.1f, formatString: "0.0", suffixType: MiraNumberSuffixes.Multiplier)] + public ConfigEntry ExampleFloatNumber { get; private set; } = config.Bind("General", "Example Multplier", 1f); [LocalEnumSetting] public ConfigEntry ExampleEnum { get; private set; } = config.Bind("General", "Example Enum", ExampleEnumSetting.Fries); diff --git a/MiraAPI/LocalSettings/Attributes/LocalNumberSettingAttribute.cs b/MiraAPI/LocalSettings/Attributes/LocalNumberSettingAttribute.cs index 20a55f3..febd711 100644 --- a/MiraAPI/LocalSettings/Attributes/LocalNumberSettingAttribute.cs +++ b/MiraAPI/LocalSettings/Attributes/LocalNumberSettingAttribute.cs @@ -18,9 +18,9 @@ namespace MiraAPI.LocalSettings.Attributes; public class LocalNumberSettingAttribute( string? name = null, string? description = null, - int min = 1, - int max = 5, - int increment = 1, + float min = 1, + float max = 5, + float increment = 1, MiraNumberSuffixes suffixType = MiraNumberSuffixes.None, string? formatString = null ) : LocalSettingAttribute(name, description) @@ -28,6 +28,6 @@ public class LocalNumberSettingAttribute( /// internal override LocalNumberSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase) { - return new LocalNumberSetting(tab, configEntryBase, name, description, new IntRange(min, max), increment, suffixType, formatString); + return new LocalNumberSetting(tab, configEntryBase, name, description, new FloatRange(min, max), increment, suffixType, formatString); } } diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs b/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs index 70be2aa..fb86fdc 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalNumberSetting.cs @@ -12,17 +12,17 @@ namespace MiraAPI.LocalSettings.SettingTypes; /// /// Local setting class for numbers. /// -public class LocalNumberSetting : LocalSettingBase +public class LocalNumberSetting : LocalSettingBase { /// /// Gets the range of the button. /// - public IntRange NumberRange { get; } + public FloatRange NumberRange { get; } /// /// Gets the increment of the value when button is pressed. /// - public int Increment { get; } + public float Increment { get; } /// /// Gets a format for the text to use to format the number. @@ -47,14 +47,14 @@ public class LocalNumberSetting : LocalSettingBase ConfigEntryBase configEntry, string? name = null, string? description = null, - IntRange? numberRange = null, - int? increment = null, + FloatRange? numberRange = null, + float? increment = null, MiraNumberSuffixes? suffixType = null, string? formatString = null) : base(tab, configEntry, name, description) { SuffixType = suffixType ?? MiraNumberSuffixes.None; - NumberRange = numberRange ?? new IntRange(1, 5); + NumberRange = numberRange ?? new FloatRange(1, 5); Increment = increment ?? 1; FormatString = formatString ?? "0"; } @@ -97,7 +97,7 @@ public class LocalNumberSetting : LocalSettingBase button.OnClick.AddListener((UnityAction)(() => { - int value = GetValue(); + float value = GetValue(); value += Increment; if (value > NumberRange.max) { diff --git a/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs index f991376..d144ce4 100644 --- a/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs +++ b/MiraAPI/LocalSettings/SettingTypes/LocalSettingBase.cs @@ -56,7 +56,7 @@ public abstract class LocalSettingBase : ILocalSetting /// Gets the value of the config entry, casted to the setting type. /// /// The value. - public T GetValue() + public virtual T GetValue() { return (T)ConfigEntry.BoxedValue; } @@ -65,7 +65,7 @@ public abstract class LocalSettingBase : ILocalSetting /// Sets the value of the config entry. /// /// The value to set to. - public void SetValue(T value) + public virtual void SetValue(T value) { ConfigEntry.BoxedValue = value; Tab?.OnOptionChanged(ConfigEntry); From 94197e38ddfffdba1e83c560b6f490a7ec6ead5a Mon Sep 17 00:00:00 2001 From: xChipseq Date: Sun, 7 Sep 2025 20:47:59 +0200 Subject: [PATCH 12/12] cleanup --- MiraAPI/MiraApiSettings.cs | 11 +---------- MiraAPI/PluginLoading/MiraPluginManager.cs | 4 ++-- MiraAPI/Utilities/Helpers.cs | 2 -- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/MiraAPI/MiraApiSettings.cs b/MiraAPI/MiraApiSettings.cs index 6a659f1..3014411 100644 --- a/MiraAPI/MiraApiSettings.cs +++ b/MiraAPI/MiraApiSettings.cs @@ -1,16 +1,7 @@ -using BepInEx; -using BepInEx.Configuration; -using BepInEx.Unity.IL2CPP; -using HarmonyLib; +using BepInEx.Configuration; using MiraAPI.LocalSettings; using MiraAPI.LocalSettings.Attributes; -using MiraAPI.PluginLoading; using MiraAPI.Utilities.Assets; -using Reactor; -using Reactor.Networking; -using Reactor.Networking.Attributes; -using Reactor.Utilities; -using UnityEngine; namespace MiraAPI; diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index 0c987cd..4fbb203 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -22,7 +22,6 @@ using MiraAPI.Utilities; using Reactor.Networking; using Reactor.Utilities; using Rewired; -using UnityEngine; namespace MiraAPI.PluginLoading; @@ -155,7 +154,7 @@ public sealed class MiraPluginManager Logger.Info($"Registered keybind for button '{button.GetType().Name}' with default key {(KeyboardKeyCode)button.DefaultKeybind}."); } }; - + RegisterLocalTabs(typeof(MiraApiSettings), PluginSingleton.Instance); } @@ -395,6 +394,7 @@ public sealed class MiraPluginManager { Logger.Error($"Failed to register options for {type.Name}: {e.ToString()}"); } + return false; } } diff --git a/MiraAPI/Utilities/Helpers.cs b/MiraAPI/Utilities/Helpers.cs index 4717189..7b4bac6 100644 --- a/MiraAPI/Utilities/Helpers.cs +++ b/MiraAPI/Utilities/Helpers.cs @@ -4,9 +4,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using Il2CppInterop.Runtime.InteropTypes.Arrays; -using Il2CppSystem.IO; using MiraAPI.Keybinds; -using MiraAPI.Patches.LocalSettings; using MiraAPI.Utilities.Assets; using Rewired; using TMPro;