mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-19 15:51:48 +00:00
Merge branch 'fork/xChipseq/local-settings-rework' into dev
# Conflicts: # MiraAPI/MiraApiConfig.cs # MiraAPI/PluginLoading/MiraPluginManager.cs # MiraAPI/Utilities/Helpers.cs
This commit is contained in:
commit
1664aa1003
31 files changed
+1824
-32
No files matched your search
@@ -0,0 +1,53 @@
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.LocalSettings;
|
||||
using MiraAPI.LocalSettings.Attributes;
|
||||
using MiraAPI.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
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()
|
||||
{
|
||||
ToggleActiveColor = Color.blue,
|
||||
ToggleInactiveColor = Color.red,
|
||||
};
|
||||
|
||||
[LocalSettingsButton]
|
||||
public LocalSettingsButton ExampleButton { get; private set; } = new("Example Button", OnExampleButtonClick);
|
||||
|
||||
[LocalToggleSetting]
|
||||
public ConfigEntry<bool> ExampleToggle { get; private set; } = config.Bind("General", "Example Bool", true);
|
||||
|
||||
[LocalToggleSetting]
|
||||
public ConfigEntry<bool> ExampleToggle2 { get; private set; } = config.Bind("General", "Example Bool 2", false);
|
||||
|
||||
[LocalSliderSetting(suffixType: MiraNumberSuffixes.Percent, formatString: "0", roundValue: true)]
|
||||
public ConfigEntry<float> ExampleSlider { get; private set; } = config.Bind("General", "Slider", 50f);
|
||||
|
||||
[LocalNumberSetting]
|
||||
public ConfigEntry<float> 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<float> ExampleFloatNumber { get; private set; } = config.Bind("General", "Example Multplier", 1f);
|
||||
|
||||
[LocalEnumSetting]
|
||||
public ConfigEntry<ExampleEnumSetting> ExampleEnum { get; private set; } = config.Bind("General", "Example Enum", ExampleEnumSetting.Fries);
|
||||
|
||||
private static void OnExampleButtonClick()
|
||||
{
|
||||
LocalSettingsTabSingleton<MiraApiSettings>.Instance.Open();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ExampleEnumSetting
|
||||
{
|
||||
Cheeseburger,
|
||||
Sandwich,
|
||||
Fries,
|
||||
ChickenNuggets,
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using BepInEx;
|
||||
using System.Linq;
|
||||
using BepInEx;
|
||||
using BepInEx.Configuration;
|
||||
using BepInEx.Unity.IL2CPP;
|
||||
using HarmonyLib;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.LocalSettings.SettingTypes;
|
||||
|
||||
namespace MiraAPI.LocalSettings.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an enum setting for the <see cref="ConfigEntry{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="names">Optional custom enum names.</param>
|
||||
/// <inheritdoc/>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class LocalEnumSettingAttribute(
|
||||
string? name = null,
|
||||
string? description = null,
|
||||
string[]? names = null
|
||||
) : LocalSettingAttribute(name, description)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
internal override LocalEnumSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
|
||||
{
|
||||
return new LocalEnumSetting(tab, configEntryBase, configEntryBase.SettingType, name, description, names);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.LocalSettings.SettingTypes;
|
||||
using MiraAPI.Utilities;
|
||||
|
||||
namespace MiraAPI.LocalSettings.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a number setting for the <see cref="ConfigEntry{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="min">Minimum range.</param>
|
||||
/// <param name="max">Maximum range.</param>
|
||||
/// <param name="increment">Increment per use.</param>
|
||||
/// <param name="suffixType">Suffix for the value.</param>
|
||||
/// <param name="formatString">Format string used when formating.</param>
|
||||
/// <inheritdoc/>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class LocalNumberSettingAttribute(
|
||||
string? name = null,
|
||||
string? description = null,
|
||||
float min = 1,
|
||||
float max = 5,
|
||||
float increment = 1,
|
||||
MiraNumberSuffixes suffixType = MiraNumberSuffixes.None,
|
||||
string? formatString = null
|
||||
) : LocalSettingAttribute(name, description)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
internal override LocalNumberSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
|
||||
{
|
||||
return new LocalNumberSetting(tab, configEntryBase, name, description, new FloatRange(min, max), increment, suffixType, formatString);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using BepInEx.Configuration;
|
||||
|
||||
namespace MiraAPI.LocalSettings.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all local settings attributes.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the setting. Defaults to entry key.</param>
|
||||
/// <param name="description">The description of the setting. Defalts to entry description.</param>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public abstract class LocalSettingAttribute(
|
||||
string? name = null,
|
||||
string? description = null
|
||||
) : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the created setting object.
|
||||
/// </summary>
|
||||
/// <param name="tab">Gets the tab where the setting is located.</param>
|
||||
/// <param name="configEntryBase">Gets the config entry the setting is attached to.</param>
|
||||
/// <returns>The created class.</returns>
|
||||
internal abstract ILocalSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using BepInEx.Configuration;
|
||||
|
||||
namespace MiraAPI.LocalSettings.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Attribute for local setting buttons.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class LocalSettingsButtonAttribute : Attribute
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.LocalSettings.SettingTypes;
|
||||
using MiraAPI.Utilities;
|
||||
|
||||
namespace MiraAPI.LocalSettings.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a slider setting for the <see cref="ConfigEntry{T}"/>.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
/// <param name="min">Minimum range.</param>
|
||||
/// <param name="max">Maximum range.</param>
|
||||
/// <param name="roundValue">Should the value be rounded.</param>
|
||||
/// <param name="suffixType">Suffix for the value.</param>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class LocalSliderSettingAttribute(
|
||||
string? name = null,
|
||||
float min = 0,
|
||||
float max = 100,
|
||||
string? description = null,
|
||||
bool displayValue = false,
|
||||
string? formatString = null,
|
||||
bool roundValue = false,
|
||||
MiraNumberSuffixes suffixType = MiraNumberSuffixes.None
|
||||
) : LocalSettingAttribute(name, description)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
internal override LocalSliderSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
|
||||
{
|
||||
return new LocalSliderSetting(tab, configEntryBase, name, description, new FloatRange(min, max), displayValue, suffixType, formatString, roundValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.LocalSettings.SettingTypes;
|
||||
|
||||
namespace MiraAPI.LocalSettings.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a toggle setting for the <see cref="ConfigEntry{T}"/>.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class LocalToggleSettingAttribute(
|
||||
string? name = null,
|
||||
string? description = null
|
||||
) : LocalSettingAttribute(name, description)
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
internal override LocalToggleSetting CreateSetting(Type tab, ConfigEntryBase configEntryBase)
|
||||
{
|
||||
return new LocalToggleSetting(tab, configEntryBase, name, description);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using BepInEx.Configuration;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.LocalSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for all local settings.
|
||||
/// </summary>
|
||||
public interface ILocalSetting
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the preferred name of a setting.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of a setting.
|
||||
/// </summary>
|
||||
string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the setting's gameobject.
|
||||
/// </summary>
|
||||
GameObject? Setting { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the setting's config entry.
|
||||
/// </summary>
|
||||
ConfigEntryBase ConfigEntry { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to create the setting.
|
||||
/// </summary>
|
||||
/// <param name="toggle">Toggle template.</param>
|
||||
/// <param name="slider">Slider template.</param>
|
||||
/// <param name="parent">The parent of the setting.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <param name="order">The order.</param>
|
||||
/// <param name="last">Whether it's the last on the row.</param>
|
||||
/// <returns>The created setting.</returns>
|
||||
GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a local settings button.
|
||||
/// </summary>
|
||||
public class LocalSettingsButton(string text, Action onClick)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the button text.
|
||||
/// </summary>
|
||||
public string Text { get; set; } = text;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the button on-click action.
|
||||
/// </summary>
|
||||
public Action OnClick { get; set; } = onClick;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tab instance this button belongs to.
|
||||
/// </summary>
|
||||
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<PassiveButton>();
|
||||
var tmp = button.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>();
|
||||
var rollover = button.GetComponent<ButtonRolloverHandler>();
|
||||
tmp.GetComponent<TextTranslatorTMP>().Destroy();
|
||||
button.gameObject.SetActive(true);
|
||||
|
||||
var toggleComp = button.GetComponent<ToggleButtonBehaviour>();
|
||||
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;
|
||||
button.OnClick.AddListener((UnityAction)OnClick);
|
||||
|
||||
background.transform.localScale = new Vector3(1, 1.25f, 1);
|
||||
Helpers.DivideSize(button.gameObject, 1.1f);
|
||||
|
||||
order++;
|
||||
if (order > 2 && !last)
|
||||
{
|
||||
offset += 0.5f;
|
||||
order = 1;
|
||||
}
|
||||
if (last)
|
||||
offset += 0.6f;
|
||||
|
||||
return button.gameObject;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Manages local settings.
|
||||
/// </summary>
|
||||
public static class LocalSettingsManager
|
||||
{
|
||||
internal static readonly Dictionary<Type, LocalSettingsTab> TypeToTab = [];
|
||||
internal static readonly List<LocalSettingsTab> 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<MiraApiPlugin>.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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BepInEx.Configuration;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Patches.LocalSettings;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.LocalSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a local settings tab. Gets a button by default.
|
||||
/// </summary>
|
||||
public abstract class LocalSettingsTab(ConfigFile config)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the tab.
|
||||
/// </summary>
|
||||
public abstract string TabName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tab appearance.
|
||||
/// </summary>
|
||||
public virtual LocalSettingTabAppearance TabAppearance { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a button should be created for the tab.
|
||||
/// </summary>
|
||||
public virtual bool ShouldCreateButton => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the tab should have category labels.
|
||||
/// </summary>
|
||||
protected virtual bool ShouldCreateLabels => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mod's config file. Automatically found in the plugin loader.
|
||||
/// </summary>
|
||||
public ConfigFile Config { get; } = config ?? throw new ArgumentNullException(nameof(config));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tab button instance.
|
||||
/// </summary>
|
||||
public TabGroup? TabButton { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of settings.
|
||||
/// </summary>
|
||||
public List<ILocalSetting> Settings { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of buttons.
|
||||
/// </summary>
|
||||
public List<LocalSettingsButton> Buttons { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of this tab.
|
||||
/// </summary>
|
||||
protected int TabIndex => LocalSettingsManager.Tabs.IndexOf(this) + 10;
|
||||
|
||||
private Scroller? Scroller { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when an option value is changed.
|
||||
/// </summary>
|
||||
/// <param name="configEntry">The config entry which was changed.</param>
|
||||
public virtual void OnOptionChanged(ConfigEntryBase configEntry)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to open the tab.
|
||||
/// Override for custom behavior.
|
||||
/// </summary>
|
||||
public virtual void Open()
|
||||
{
|
||||
OptionsMenuPatches.Instance?.OpenTabGroup(TabIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the tab <see cref="GameObject"/> and it's content.
|
||||
/// </summary>
|
||||
/// <param name="instance">The <see cref="OptionsMenuBehaviour"/> instace.</param>
|
||||
/// <returns>The created tab <see cref="GameObject"/>.</returns>
|
||||
public virtual GameObject CreateTab(OptionsMenuBehaviour instance)
|
||||
{
|
||||
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<ToggleButtonBehaviour>();
|
||||
var slider = instance.transform.FindChild("GeneralTab").FindChild("SoundGroup").FindChild("SFXSlider").GetComponent<SlideBar>();
|
||||
|
||||
Dictionary<string, List<ILocalSetting>> 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)
|
||||
{
|
||||
if (ShouldCreateLabels)
|
||||
{
|
||||
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<SpriteRenderer>(true).Do(x =>
|
||||
{
|
||||
x.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
|
||||
x.sortingOrder = 150;
|
||||
x.sortingLayerName = "Default";
|
||||
});
|
||||
|
||||
obj.GetComponentsInChildren<MeshRenderer>(true).Do(x =>
|
||||
{
|
||||
x.sortingOrder = 151;
|
||||
x.sortingLayerName = "Default";
|
||||
});
|
||||
|
||||
obj.GetComponentsInChildren<PassiveButton>().Do(x => { x.ClickMask = OptionsMenuPatches.MaskCollider; });
|
||||
|
||||
contentIndex++;
|
||||
}
|
||||
|
||||
contentOrder = 1;
|
||||
}
|
||||
|
||||
contentIndex = 1;
|
||||
contentOrder = 1;
|
||||
foreach (var button in Buttons)
|
||||
{
|
||||
var obj = button.CreateButton(
|
||||
toggle,
|
||||
Scroller.Inner,
|
||||
ref contentOffset,
|
||||
ref contentOrder,
|
||||
contentIndex == Buttons.Count);
|
||||
|
||||
obj!.GetComponentsInChildren<SpriteRenderer>(true).Do(x =>
|
||||
{
|
||||
x.maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
|
||||
x.sortingOrder = 150;
|
||||
x.sortingLayerName = "Default";
|
||||
});
|
||||
|
||||
obj.GetComponentsInChildren<MeshRenderer>(true).Do(x =>
|
||||
{
|
||||
x.sortingOrder = 151;
|
||||
x.sortingLayerName = "Default";
|
||||
});
|
||||
|
||||
obj.GetComponentsInChildren<PassiveButton>().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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the tab button.
|
||||
/// </summary>
|
||||
/// <param name="instance">The <see cref="OptionsMenuBehaviour"/> instance.</param>
|
||||
/// <param name="tabIdx">The tab index.</param>
|
||||
/// <param name="offset">The current button offset.</param>
|
||||
/// <returns>The created button <see cref="GameObject"/>.</returns>
|
||||
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";
|
||||
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 = TabAppearance.TabButtonColor;
|
||||
TabButton = tabButtonObject;
|
||||
|
||||
var tabButtonText = tabButtonObject.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>();
|
||||
tabButtonText.GetComponent<TextTranslatorTMP>().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 = $"<b>{GetShortName(TabName)}</b>";
|
||||
|
||||
var tabButton = tabButtonObject.GetComponent<PassiveButton>();
|
||||
var rollover = tabButtonObject.Rollover;
|
||||
rollover.OverColor = TabAppearance.TabButtonHoverColor;
|
||||
rollover.OutColor = TabAppearance.TabButtonColor;
|
||||
|
||||
SpriteRenderer? tabButtonRend = null;
|
||||
if (TabAppearance.TabIcon != null)
|
||||
{
|
||||
tabButtonRend = new GameObject("sprite").AddComponent<SpriteRenderer>();
|
||||
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 = TabAppearance.TabIcon.LoadAsset();
|
||||
tabButtonText.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
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)(() =>
|
||||
{
|
||||
tabButton.transform.localPosition = new Vector3(3.55f, 2.1f - tabOffset, 5.5f);
|
||||
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)(() =>
|
||||
{
|
||||
if (!tabButtonObject.Content.gameObject.activeSelf)
|
||||
{
|
||||
//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);
|
||||
}
|
||||
|
||||
tabButtonText.transform.localPosition = new Vector3(0.1f, 0, 0);
|
||||
tabButtonText.maxVisibleCharacters = 4;
|
||||
tabButtonText.text = $"<b>{GetShortName(TabName)}</b>";
|
||||
}));
|
||||
|
||||
return tabButtonObject.gameObject;
|
||||
}
|
||||
|
||||
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<TextTranslatorTMP>().Destroy();
|
||||
label.name = text;
|
||||
|
||||
var tmp = label.GetComponent<TextMeshPro>();
|
||||
tmp.text = $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\"><b>{text}</b></font>";
|
||||
tmp.alignment = TextAlignmentOptions.Center;
|
||||
|
||||
var meshRend = label.GetComponent<MeshRenderer>();
|
||||
meshRend.sortingLayerName = "Default";
|
||||
meshRend.sortingOrder = 151;
|
||||
|
||||
offset += 0.5f;
|
||||
}
|
||||
|
||||
private static string GetShortName(string name)
|
||||
{
|
||||
var shortName = string.Empty;
|
||||
name.Split(' ').Do(x => shortName += x[0]);
|
||||
return shortName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.LocalSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Struct for modyfing tab apparance.
|
||||
/// Defines how the settings look.
|
||||
/// </summary>
|
||||
public record struct LocalSettingTabAppearance()
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets main color of the tab.
|
||||
/// </summary>
|
||||
public Color TabColor { get; set; } = Color.white;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets tab button icon. No icon by default.
|
||||
/// </summary>
|
||||
public LoadableAsset<Sprite>? TabIcon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets color of the tab button.
|
||||
/// </summary>
|
||||
public Color TabButtonColor { get; set; } = Color.white;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets hover color of the tab button.
|
||||
/// </summary>
|
||||
public Color TabButtonHoverColor { get; set; } = Palette.AcceptedGreen;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets active color of the tab button.
|
||||
/// </summary>
|
||||
public Color TabButtonActiveColor { get; set; } = Palette.AcceptedGreen;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets hover color of toggles.
|
||||
/// </summary>
|
||||
public Color ToggleHoverColor { get; set; } = Palette.AcceptedGreen;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets active color of toggles.
|
||||
/// </summary>
|
||||
public Color ToggleActiveColor { get; set; } = Palette.AcceptedGreen;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets inactive color of toggles.
|
||||
/// </summary>
|
||||
public Color ToggleInactiveColor { get; set; } = Color.red;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets color of number buttons.
|
||||
/// </summary>
|
||||
public Color NumberColor { get; set; } = Color.white;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets hover color of number buttons.
|
||||
/// </summary>
|
||||
public Color NumberHoverColor { get; set; } = Palette.AcceptedGreen;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets color of sliders.
|
||||
/// </summary>
|
||||
public Color SliderColor { get; set; } = Color.white;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets hover color of sliders.
|
||||
/// </summary>
|
||||
public Color SliderHoverColor { get; set; } = Palette.AcceptedGreen;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets color of enum buttons.
|
||||
/// </summary>
|
||||
public Color EnumColor { get; set; } = Color.white;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets hover color of enum buttons.
|
||||
/// </summary>
|
||||
public Color EnumHoverColor { get; set; } = Palette.AcceptedGreen;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets color of buttons.
|
||||
/// </summary>
|
||||
public Color ButtonColor { get; set; } = Color.white;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets hover color of buttons.
|
||||
/// </summary>
|
||||
public Color ButtonHoverColor { get; set; } = Palette.AcceptedGreen;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace MiraAPI.LocalSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton for local settings tabs.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The settings tab type.</typeparam>
|
||||
public static class LocalSettingsTabSingleton<T> where T : LocalSettingsTab
|
||||
{
|
||||
private static T? _instance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the setting tab.
|
||||
/// </summary>
|
||||
#pragma warning disable CA1000
|
||||
public static T Instance => _instance ??= LocalSettingsManager.Tabs.OfType<T>().Single();
|
||||
#pragma warning restore CA1000
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Local setting class for enums.
|
||||
/// </summary>
|
||||
public class LocalEnumSetting : LocalSettingBase<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the enum type of the setting.
|
||||
/// </summary>
|
||||
public Type EnumType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the enum values.
|
||||
/// </summary>
|
||||
public string[] Values { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalEnumSetting"/> class.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
/// <param name="enumType">The enum type.</param>
|
||||
/// <param name="values">The optional values array to replace the enum names.</param>
|
||||
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
|
||||
.GetValues(configEntry.SettingType)
|
||||
.Cast<Enum>()
|
||||
.Select(x => x.ToDisplayString())
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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<PassiveButton>();
|
||||
var tmp = button.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>();
|
||||
var rollover = button.GetComponent<ButtonRolloverHandler>();
|
||||
tmp.GetComponent<TextTranslatorTMP>().Destroy();
|
||||
button.gameObject.SetActive(true);
|
||||
|
||||
var toggleComp = button.GetComponent<ToggleButtonBehaviour>();
|
||||
var background = toggleComp.Background;
|
||||
var highlight = button.transform.FindChild("ButtonHighlight")?.GetComponent<SpriteRenderer>();
|
||||
if (highlight != null)
|
||||
{
|
||||
highlight.color = Tab!.TabAppearance.EnumHoverColor;
|
||||
highlight.gameObject.SetActive(false);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override string GetValueText()
|
||||
{
|
||||
return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name}: <b>{Values[GetValue()]}</font></b>";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Local setting class for numbers.
|
||||
/// </summary>
|
||||
public class LocalNumberSetting : LocalSettingBase<float>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the range of the button.
|
||||
/// </summary>
|
||||
public FloatRange NumberRange { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the increment of the value when button is pressed.
|
||||
/// </summary>
|
||||
public float Increment { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a format for the text to use to format the number.
|
||||
/// </summary>
|
||||
public string FormatString { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the suffix for the number value.
|
||||
/// </summary>
|
||||
public MiraNumberSuffixes SuffixType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalNumberSetting"/> class.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
/// <param name="numberRange">The value range.</param>
|
||||
/// <param name="increment">The increment per click.</param>
|
||||
/// <param name="suffixType">The suffix used for formating.</param>
|
||||
/// <param name="formatString">The format string used for formating.</param>
|
||||
public LocalNumberSetting(
|
||||
Type tab,
|
||||
ConfigEntryBase configEntry,
|
||||
string? name = null,
|
||||
string? description = 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 FloatRange(1, 5);
|
||||
Increment = increment ?? 1;
|
||||
FormatString = formatString ?? "0";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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<PassiveButton>();
|
||||
var tmp = button.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>();
|
||||
var rollover = button.GetComponent<ButtonRolloverHandler>();
|
||||
tmp.GetComponent<TextTranslatorTMP>().Destroy();
|
||||
button.gameObject.SetActive(true);
|
||||
|
||||
var toggleComp = button.GetComponent<ToggleButtonBehaviour>();
|
||||
var background = toggleComp.Background;
|
||||
var highlight = button.transform.FindChild("ButtonHighlight")?.GetComponent<SpriteRenderer>();
|
||||
if (highlight != null)
|
||||
{
|
||||
highlight.color = Tab!.TabAppearance.NumberHoverColor;
|
||||
highlight.gameObject.SetActive(false);
|
||||
}
|
||||
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)(() =>
|
||||
{
|
||||
float 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override string GetValueText()
|
||||
{
|
||||
var value = GetValue();
|
||||
var formated = Helpers.FormatValue(value, SuffixType, FormatString);
|
||||
return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name}: <b>{formated}</font></b>";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using BepInEx.Configuration;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.LocalSettings.SettingTypes;
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract class LocalSettingBase<T> : ILocalSetting
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Name { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public GameObject? Setting { get; } = null!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ConfigEntryBase ConfigEntry { get; }
|
||||
|
||||
internal LocalSettingsTab? Tab => LocalSettingsManager.Tabs.FirstOrDefault(x => x.Settings.Contains(this));
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalSettingBase{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="tab">The tab to create the setting in.</param>
|
||||
/// <param name="configEntry">The config entry.</param>
|
||||
/// <param name="name">The name of the setting.</param>
|
||||
/// <param name="description">The description of the setting.</param>
|
||||
protected LocalSettingBase(Type tab, ConfigEntryBase configEntry, string? name = null, string? description = null)
|
||||
{
|
||||
ConfigEntry = configEntry;
|
||||
Name = name ?? ConfigEntry.Definition.Key;
|
||||
Description = description ?? ConfigEntry.Description.Description;
|
||||
LocalSettingsManager.TypeToTab[tab].Settings.Add(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of the setting.
|
||||
/// </summary>
|
||||
/// <returns>The created setting.</returns>
|
||||
public abstract GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the formated string to use in the text of the setting.
|
||||
/// </summary>
|
||||
/// <returns>The value text.</returns>
|
||||
protected virtual string GetValueText()
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the config entry, casted to the setting type.
|
||||
/// </summary>
|
||||
/// <returns>The value.</returns>
|
||||
public virtual T GetValue()
|
||||
{
|
||||
return (T)ConfigEntry.BoxedValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of the config entry.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to set to.</param>
|
||||
public virtual void SetValue(T value)
|
||||
{
|
||||
ConfigEntry.BoxedValue = value;
|
||||
Tab?.OnOptionChanged(ConfigEntry);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Local setting class for sliders.
|
||||
/// </summary>
|
||||
public class LocalSliderSetting : LocalSettingBase<float>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the range of the slider.
|
||||
/// </summary>
|
||||
public FloatRange SliderRange { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the value should be displayed next to name.
|
||||
/// </summary>
|
||||
public bool DisplayValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a format for the text to use to format the number.
|
||||
/// </summary>
|
||||
public string FormatString { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the value should be rounded.
|
||||
/// </summary>
|
||||
public bool RoundValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the suffix for the number value.
|
||||
/// </summary>
|
||||
public MiraNumberSuffixes SuffixType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalSliderSetting"/> class.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
/// <param name="sliderRange">The value range.</param>
|
||||
/// <param name="suffixType">The suffix used for formating.</param>
|
||||
/// <param name="formatString">The format string used for formating.</param>
|
||||
/// <param name="roundValue">Should the value be rounded.</param>
|
||||
public LocalSliderSetting(
|
||||
Type tab,
|
||||
ConfigEntryBase configEntry,
|
||||
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)
|
||||
{
|
||||
SliderRange = sliderRange ?? new FloatRange(0, 100);
|
||||
DisplayValue = displayValue;
|
||||
SuffixType = suffixType ?? MiraNumberSuffixes.None;
|
||||
FormatString = formatString ?? "0.0";
|
||||
RoundValue = roundValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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<SlideBar>();
|
||||
var rollover = newSlider.GetComponent<ButtonRolloverHandler>();
|
||||
newSlider.Title = newSlider.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>(); // Why the hell slider has a title property that is not even assigned???
|
||||
newSlider.Title.GetComponent<TextTranslatorTMP>().Destroy();
|
||||
newSlider.gameObject.SetActive(true);
|
||||
newSlider.Bar.color = Tab!.TabAppearance.SliderColor;
|
||||
|
||||
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.OutColor = Tab!.TabAppearance.SliderColor;
|
||||
rollover.OverColor = Tab!.TabAppearance.SliderHoverColor;
|
||||
newSlider.Title.transform.localPosition = new Vector3(0.5f, 0, -1f);
|
||||
newSlider.Title.horizontalAlignment = DisplayValue ? HorizontalAlignmentOptions.Left : HorizontalAlignmentOptions.Center;
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override string GetValueText()
|
||||
{
|
||||
if (DisplayValue)
|
||||
{
|
||||
var value = GetValue();
|
||||
var formated = Helpers.FormatValue(value, SuffixType, FormatString);
|
||||
var maxFormated = Helpers.FormatValue(SliderRange.max, SuffixType, FormatString);
|
||||
return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name}: <b>{formated} / {maxFormated}</font></b>";
|
||||
}
|
||||
|
||||
return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name}</font></b>";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Local setting class for toggles.
|
||||
/// </summary>
|
||||
public class LocalToggleSetting : LocalSettingBase<bool>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalToggleSetting"/> class.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
public LocalToggleSetting(
|
||||
Type tab,
|
||||
ConfigEntryBase configEntry,
|
||||
string? name = null,
|
||||
string? description = null
|
||||
) : base(tab, configEntry, name, description)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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<ToggleButtonBehaviour>();
|
||||
var tmp = toggleObject.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>();
|
||||
var passiveButton = toggleObject.GetComponent<PassiveButton>();
|
||||
var rollover = toggleObject.GetComponent<ButtonRolloverHandler>();
|
||||
toggleObject.gameObject.SetActive(true);
|
||||
|
||||
if (last && order == 1)
|
||||
{
|
||||
// Toggle in the middle
|
||||
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, -7);
|
||||
}
|
||||
|
||||
toggleObject.BaseText = CustomStringName.CreateAndRegister(Name);
|
||||
toggleObject.UpdateText(GetValue());
|
||||
toggleObject.name = Name;
|
||||
toggleObject.Background.color = GetValue() ? Tab!.TabAppearance.ToggleActiveColor : Tab!.TabAppearance.ToggleInactiveColor;
|
||||
passiveButton.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent();
|
||||
rollover.OverColor = Tab!.TabAppearance.ToggleHoverColor;
|
||||
|
||||
passiveButton.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
SetValue(!GetValue());
|
||||
toggleObject.UpdateText(GetValue());
|
||||
toggleObject.Background.color = GetValue() ? Tab!.TabAppearance.ToggleActiveColor : Tab!.TabAppearance.ToggleInactiveColor;
|
||||
}));
|
||||
passiveButton.OnMouseOver.AddListener((UnityAction)(() =>
|
||||
{
|
||||
if (!Description.IsNullOrWhiteSpace())
|
||||
{
|
||||
tmp.text = Description;
|
||||
}
|
||||
}));
|
||||
passiveButton.OnMouseOut.AddListener((UnityAction)(() =>
|
||||
{
|
||||
toggleObject.UpdateText(GetValue());
|
||||
toggleObject.Background.color = GetValue() ? Tab!.TabAppearance.ToggleActiveColor : Tab!.TabAppearance.ToggleInactiveColor;
|
||||
}));
|
||||
|
||||
Helpers.DivideSize(toggleObject.gameObject, 1.1f);
|
||||
|
||||
order++;
|
||||
if (order > 2 && !last)
|
||||
{
|
||||
offset += 0.5f;
|
||||
order = 1;
|
||||
}
|
||||
if (last)
|
||||
offset += 0.6f;
|
||||
|
||||
return toggleObject.gameObject;
|
||||
}
|
||||
}
|
||||
@@ -30,10 +30,6 @@ public partial class MiraApiPlugin : BasePlugin
|
||||
/// </summary>
|
||||
public static Color DefaultHeaderColor { get; } = new Color32(77, 77, 77, 255);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Mira Api Config Handler.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.LocalSettings;
|
||||
using MiraAPI.LocalSettings.Attributes;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
|
||||
namespace MiraAPI;
|
||||
|
||||
/// <summary>
|
||||
/// Mira API Config File Handler
|
||||
/// </summary>
|
||||
public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string TabName => "Mira API";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override LocalSettingTabAppearance TabAppearance => new()
|
||||
{
|
||||
TabButtonHoverColor = MiraApiPlugin.MiraColor,
|
||||
TabIcon = MiraAssets.SettingsIcon,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the modifiers hud should be on the left side of the screen (under roles/task tab). Recommended for streamers.
|
||||
/// </summary>
|
||||
[LocalToggleSetting]
|
||||
public ConfigEntry<bool> ModifiersHudLeftSide { get; private set; } = config.Bind("Displays", "Show Modifiers HUD on Left Side", false);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using MiraAPI.LocalSettings;
|
||||
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<MiraApiPlugin>.Instance.MiraConfig!.ModifiersHudLeftSide.Value;
|
||||
var leftSideHud = LocalSettingsTabSingleton<MiraApiSettings>.Instance.ModifiersHudLeftSide.Value;
|
||||
|
||||
if (leftSideHud && (taskPanelOpen || roleTabOpen))
|
||||
{
|
||||
|
||||
@@ -1,12 +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.Patches.Roles;
|
||||
using MiraAPI.LocalSettings;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using TMPro;
|
||||
@@ -98,7 +96,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
|
||||
IsOpen = false;
|
||||
_children.gameObject.SetActive(false);
|
||||
|
||||
if (PluginSingleton<MiraApiPlugin>.Instance.MiraConfig!.ModifiersHudLeftSide.Value)
|
||||
if (LocalSettingsTabSingleton<MiraApiSettings>.Instance.ModifiersHudLeftSide.Value)
|
||||
{
|
||||
var aspect = GetComponent<AspectPosition>();
|
||||
_toggleButton.transform.localPosition = new Vector3(-1f, 2.7f, 0f);
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.LocalSettings;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
|
||||
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;
|
||||
|
||||
private static int currentPage = 1;
|
||||
private static Dictionary<int, List<GameObject>> tabButtons = new();
|
||||
|
||||
/// <summary>
|
||||
/// Creates the tabs and their content
|
||||
/// </summary>
|
||||
[HarmonyPostfix]
|
||||
[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)
|
||||
{
|
||||
__instance.Background.GetComponent<BoxCollider2D>().enabled = false;
|
||||
__instance.transform.FindChild("Tint").SetLocalZ(5.6f);
|
||||
}
|
||||
|
||||
background = __instance.Background;
|
||||
|
||||
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<SpriteMask>();
|
||||
mask.sprite = blank;
|
||||
mask.isCustomRangeActive = true;
|
||||
mask.sortingLayerName = "Default";
|
||||
mask.backSortingOrder = 100;
|
||||
mask.frontSortingOrder = 500;
|
||||
|
||||
MaskCollider = maskObj.AddComponent<BoxCollider2D>();
|
||||
MaskCollider.size = new Vector2(0.01f, 0.1f);
|
||||
MaskCollider.isTrigger = true;
|
||||
MaskCollider.enabled = true;
|
||||
|
||||
currentPage = 1;
|
||||
tabButtons.Clear();
|
||||
float yOffset = 0;
|
||||
int i = 0;
|
||||
int tabIdx = 0;
|
||||
int page = 1;
|
||||
foreach (var settings in LocalSettingsManager.Tabs)
|
||||
{
|
||||
var tab = settings.CreateTab(__instance);
|
||||
if (settings.ShouldCreateButton)
|
||||
{
|
||||
var button = settings.CreateTabButton(__instance, ref tabIdx, ref yOffset);
|
||||
button.GetComponent<TabGroup>().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<PassiveButton>();
|
||||
nextButton.transform.localPosition = new Vector3(3.2f, 2.6f, -10);
|
||||
nextButton.transform.localScale = new Vector2(0.25f, 0.25f);
|
||||
var nextRend = nextButton.GetComponent<SpriteRenderer>();
|
||||
nextRend.sprite = MiraAssets.NextButton.LoadAsset();
|
||||
nextButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().DestroyImmediate();
|
||||
|
||||
var nextRollover = nextButton.gameObject.AddComponent<ButtonRolloverHandler>();
|
||||
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<PassiveButton>();
|
||||
backButton.transform.localPosition = new Vector3(2.9f, 2.6f, -10);
|
||||
backButton.transform.localScale = new Vector2(0.25f, 0.25f);
|
||||
var backRend = backButton.GetComponent<SpriteRenderer>();
|
||||
backRend.sprite = MiraAssets.NextButton.LoadAsset();
|
||||
backRend.flipX = true;
|
||||
backButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().DestroyImmediate();
|
||||
|
||||
var backRollover = backButton.gameObject.AddComponent<ButtonRolloverHandler>();
|
||||
backRollover.Target = backRend;
|
||||
backRollover.OverColor = Palette.AcceptedGreen;
|
||||
|
||||
backButton.OnClick = new Button.ButtonClickedEvent();
|
||||
backButton.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
ChangePage(-1);
|
||||
UpdatePages();
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies how opening tabs is handled for the custom ones to work.
|
||||
/// </summary>
|
||||
[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
|
||||
|
||||
// Tabs with index 10 and above are the mod settings tabs
|
||||
if (index >= 10)
|
||||
{
|
||||
CustomOpen(LocalSettingsManager.Tabs[index - 10]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes the custom tabs close when opening the options menu and resets pages.
|
||||
/// </summary>
|
||||
[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?
|
||||
.Where(x => x != null)
|
||||
.Do(
|
||||
x => x?.SetActive(
|
||||
pages.Key == currentPage));
|
||||
}
|
||||
}
|
||||
|
||||
private static void CustomOpen(LocalSettingsTab tab)
|
||||
{
|
||||
if (tab.TabButton?.Button)
|
||||
tab.TabButton!.Button.color = tab.TabAppearance.TabButtonHoverColor;
|
||||
|
||||
if (tab.TabButton?.Rollover)
|
||||
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.TabAppearance.TabButtonColor;
|
||||
|
||||
if (tab.TabButton?.Rollover)
|
||||
tab.TabButton!.Rollover.OutColor = tab.TabAppearance.TabButtonColor;
|
||||
|
||||
if (tab.TabButton?.Content)
|
||||
tab.TabButton!.Content.SetActive(false);
|
||||
|
||||
if (background != null)
|
||||
background.color = Color.white;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Patches.LocalSettings;
|
||||
|
||||
[HarmonyPatch(typeof(ToggleButtonBehaviour))]
|
||||
public static class ToggleBehaviourPatch
|
||||
{
|
||||
/// <summary>
|
||||
/// ResetText was inlined ofc.
|
||||
/// Skill issue
|
||||
/// </summary>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(ToggleButtonBehaviour.UpdateText))]
|
||||
public static bool UpdateTextPrefix(ToggleButtonBehaviour __instance, bool 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
|
||||
__instance.Text.richText = true;
|
||||
__instance.Text.text = $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">" +
|
||||
$"{DestroyableSingleton<TranslationController>.Instance.GetString(__instance.BaseText)}: <b>" +
|
||||
$"{DestroyableSingleton<TranslationController>.Instance.GetString(__instance.onState ? StringNames.SettingsOn : StringNames.SettingsOff)}</b></font>";
|
||||
__instance.Text.sortingOrder = 151;
|
||||
__instance.Text.renderer.sortingLayerName = "Default";
|
||||
__instance.Text.renderer.sortingOrder = 151;
|
||||
|
||||
if (__instance.Rollover)
|
||||
{
|
||||
__instance.Rollover.ChangeOutColor(color);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -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<Collider2D>(true))
|
||||
{
|
||||
if (collider.TryCast<BoxCollider2D>() is { } col)
|
||||
{
|
||||
col.size = new Vector2(col.size.x / amount, col.size.y);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var rend in obj.GetComponentsInChildren<SpriteRenderer>(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<SpriteRenderer>();
|
||||
iconSpriteRend.sprite = MiraAssets.RefreshIcon.LoadAsset();
|
||||
|
||||
DivideSize(refreshButton.gameObject, 2f);
|
||||
Helpers.DivideSize(refreshButton.gameObject, 2f);
|
||||
refreshButton.buttonText.gameObject.Destroy();
|
||||
|
||||
refreshButton.OnClick = new Button.ButtonClickedEvent();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,12 +13,15 @@ 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;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Networking;
|
||||
using Reactor.Utilities;
|
||||
using Rewired;
|
||||
|
||||
namespace MiraAPI.PluginLoading;
|
||||
|
||||
@@ -91,6 +95,11 @@ public sealed class MiraPluginManager
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RegisterLocalTabs(type, plugin))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RegisterRole(type, info, out var role))
|
||||
{
|
||||
roles.Add(role);
|
||||
@@ -137,6 +146,7 @@ public sealed class MiraPluginManager
|
||||
};
|
||||
|
||||
RegisterKeybinds(typeof(MiraGlobalKeybinds), PluginSingleton<MiraApiPlugin>.Instance);
|
||||
RegisterLocalTabs(typeof(MiraApiSettings), PluginSingleton<MiraApiPlugin>.Instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -308,6 +318,77 @@ public sealed class MiraPluginManager
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool RegisterLocalTabs(Type type, BasePlugin pluginInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!type.IsAssignableTo(typeof(LocalSettingsTab)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LocalSettingsManager.RegisterTab(type, pluginInfo))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var property in type.GetProperties())
|
||||
{
|
||||
if (LocalSettingsManager.TypeToTab[type] is not { } tabInstance)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (property.GetCustomAttribute<LocalSettingsButtonAttribute>() != 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;
|
||||
}
|
||||
|
||||
if (property.GetMethod?.IsStatic == true)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Option property {property.Name} in {type.Name} must not be static.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (property.GetValue(tabInstance) is not ConfigEntryBase configEntry)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Option property {property.Name} in {type.Name} has to be a config entry.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var attribute = property.GetCustomAttribute<LocalSettingAttribute>();
|
||||
if (attribute == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
attribute.CreateSetting(type, configEntry);
|
||||
}
|
||||
|
||||
foreach (var field in type.GetFields().Where(f => f.FieldType.IsAssignableTo(typeof(ConfigEntryBase)) && f.GetCustomAttribute<LocalSettingAttribute>() != null))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"{field.Name} is a field, not a property. Use properties for local settings.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Failed to register options for {type.Name}: {e.ToString()}");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void RegisterKeybinds(Type type, BasePlugin source)
|
||||
{
|
||||
try
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
@@ -69,6 +69,11 @@ public static class MiraAssets
|
||||
/// Gets the Next Button sprite.
|
||||
/// </summary>
|
||||
public static LoadableResourceAsset NextButton { get; } = new("MiraAPI.Resources.NextButton.png");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Mira settings icon..
|
||||
/// </summary>
|
||||
public static LoadableResourceAsset SettingsIcon { get; } = new("MiraAPI.Resources.Settings.png");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the highlighted Next Button sprite.
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System.Collections;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||
using MiraAPI.Keybinds;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Rewired;
|
||||
using TMPro;
|
||||
@@ -34,6 +37,66 @@ public static class Helpers
|
||||
return keybindIcon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a draggable scroller. Add items into the scroller.Inner transform. This does not automatically position children.
|
||||
/// </summary>
|
||||
/// <param name="parent">Where the scroller should be parented to.</param>
|
||||
/// <param name="hitBoxCollider">The collider of the scroller. Used to drag.</param>
|
||||
/// <returns>The created scroller object.</returns>
|
||||
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>();
|
||||
scroller.allowX = false;
|
||||
scroller.allowY = true;
|
||||
scroller.DragScrollSpeed = 1f;
|
||||
scroller.Colliders = new Il2CppReferenceArray<Collider2D>([hitBoxCollider]);
|
||||
scroller.Inner = inner.transform;
|
||||
|
||||
return scroller;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides a button/etc by a certain amount by resizing colliders and renderer sizes.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object you want to divide.</param>
|
||||
/// <param name="amount">How much you want to divide by.</param>
|
||||
public static void DivideSize(GameObject obj, float amount)
|
||||
{
|
||||
foreach (var collider in obj.GetComponentsInChildren<Collider2D>(true))
|
||||
{
|
||||
if (collider.TryCast<BoxCollider2D>() is { } col)
|
||||
{
|
||||
col.size = new Vector2(col.size.x / amount, col.size.y);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var rend in obj.GetComponentsInChildren<SpriteRenderer>(true))
|
||||
{
|
||||
rend.size = new Vector2(rend.size.x / amount, rend.size.y);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the keybind for an action with ReInput.
|
||||
/// </summary>
|
||||
/// <param name="actionId">The action ID.</param>
|
||||
/// <returns>The keyboard key code.</returns>
|
||||
public static KeyboardKeyCode GetKeybindByActionId(int actionId)
|
||||
{
|
||||
var player = ReInput.players.GetPlayer(0);
|
||||
return player.controllers.maps.GetFirstElementMapWithAction(ControllerType.Keyboard, actionId, false)
|
||||
.keyboardKeyCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a given probability check succeeds.
|
||||
/// </summary>
|
||||
@@ -412,5 +475,43 @@ public static class Helpers
|
||||
return new string(Enumerable.Repeat(chars, length)
|
||||
.Select(s => s[UnityEngine.Random.RandomRangeInt(0, s.Length)]).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds and returns an unused KeyCode that is not equal to the excluded key.
|
||||
/// </summary>
|
||||
/// <param name="exclude">The KeyCode to skip during the search.</param>
|
||||
/// <returns>
|
||||
/// The first available KeyCode not currently used by any registered keybind,
|
||||
/// or KeyCode.None if none are available.
|
||||
/// </returns>
|
||||
public static KeyboardKeyCode FindAvailableKey(KeyboardKeyCode exclude)
|
||||
{
|
||||
foreach (KeyboardKeyCode key in Enum.GetValues(typeof(KeyboardKeyCode)))
|
||||
{
|
||||
if (key == exclude) continue;
|
||||
bool used = KeybindManager.GetEntries().Exists(e => e.Key == key);
|
||||
if (!used) return key;
|
||||
}
|
||||
return KeyboardKeyCode.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the formated value using the specified suffix and format string.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to format.</param>
|
||||
/// <param name="suffix">The suffix to add.</param>
|
||||
/// <param name="formatString">The format string to use to format.</param>
|
||||
/// <returns>The formated value.</returns>
|
||||
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)]),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user