diff --git a/MiraAPI/GameOptions/IModdedOption.cs b/MiraAPI/GameOptions/IModdedOption.cs
index 2679f46..0e84dc7 100644
--- a/MiraAPI/GameOptions/IModdedOption.cs
+++ b/MiraAPI/GameOptions/IModdedOption.cs
@@ -78,4 +78,14 @@ public interface IModdedOption
///
/// The NetData's byte array.
void HandleNetData(byte[] data);
+
+ ///
+ /// Changes the scales and positions of the option if it's attached to the game options menu.
+ ///
+ void ChangeGameSetting();
+
+ ///
+ /// Changes the scales and positions of the option if it's attached to the roles settings menu.
+ ///
+ void ChangeRoleSetting();
}
diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs
index 8b4321c..3ac0a3b 100644
--- a/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs
+++ b/MiraAPI/GameOptions/OptionTypes/ModdedEnumOption.cs
@@ -35,10 +35,7 @@ public class ModdedEnumOption : ModdedOption
data.Title = StringName;
data.Type = global::OptionTypes.String;
- data.Values = (values is null
- ? Enum.GetNames(enumType)
- : values)
- .Select(CustomStringName.CreateAndRegister).ToArray();
+ data.Values = Values.Select(CustomStringName.CreateAndRegister).ToArray();
data.Index = Value;
}
@@ -51,6 +48,10 @@ public class ModdedEnumOption : ModdedOption
stringOption.SetUpFromData(Data, 20);
stringOption.OnValueChanged = (Il2CppSystem.Action)ValueChanged;
+ // Changing the button text to make them more distinct from number options
+ stringOption.MinusBtn.ChangeButtonText("<");
+ stringOption.PlusBtn.ChangeButtonText(">");
+
// SetUpFromData method doesn't work correctly so we must set the values manually
stringOption.Title = StringName;
stringOption.Values = (Data as StringGameSetting)?.Values ?? new Il2CppStructArray(0);
@@ -61,6 +62,32 @@ public class ModdedEnumOption : ModdedOption
return stringOption;
}
+ ///
+ public override void ChangeGameSetting()
+ {
+ var stringOption = OptionBehaviour as StringOption;
+
+ stringOption!.MinusBtn.transform.localPosition += new Vector3(0.6f, 0f, 0f);
+ stringOption.PlusBtn.transform.localPosition += new Vector3(1.5f, 0f, 0f);
+
+ // The value box itself becomes the button
+ stringOption.PlusBtn.gameObject.SetActive(false);
+ stringOption.MinusBtn.gameObject.SetActive(false);
+
+ var background = stringOption.transform.GetChild(0);
+ background.localPosition += new Vector3(-0.8f, 0f, 0f);
+ background.localScale += new Vector3(1f, 0f, 0f);
+
+ var title = stringOption.TitleText;
+ title.transform.localPosition = new(-2.0466f, 0f, -2.9968f);
+ title.GetComponent().sizeDelta = new(5.8f, 0.458f);
+ title.fontSize = 2.9f; // Why is it different for string options??
+
+ var valueBox = stringOption.transform.GetChild(5);
+ valueBox.localPosition += new Vector3(1.05f, 0f, 0f);
+ valueBox.localScale += new Vector3(0.2f, 0f, 0f);
+ }
+
///
public override float GetFloatData()
{
@@ -126,10 +153,7 @@ public class ModdedEnumOption : ModdedOption where T : struct, Enum
data.Title = StringName;
data.Type = global::OptionTypes.String;
- data.Values = (values is null
- ? Enum.GetNames()
- : values)
- .Select(CustomStringName.CreateAndRegister).ToArray();
+ data.Values = Values.Select(CustomStringName.CreateAndRegister).ToArray();
data.Index = Convert.ToInt32(Value, NumberFormatInfo.InvariantInfo);
}
@@ -142,6 +166,10 @@ public class ModdedEnumOption : ModdedOption where T : struct, Enum
stringOption.SetUpFromData(Data, 20);
stringOption.OnValueChanged = (Action)ValueChanged;
+ // Changing the button text to make them more distinct from number options
+ stringOption.MinusBtn.ChangeButtonText("<");
+ stringOption.PlusBtn.ChangeButtonText(">");
+
// SetUpFromData method doesn't work correctly so we must set the values manually
stringOption.Title = StringName;
stringOption.Values = (Data as StringGameSetting)?.Values ?? new Il2CppStructArray(0);
@@ -152,6 +180,32 @@ public class ModdedEnumOption : ModdedOption where T : struct, Enum
return stringOption;
}
+ ///
+ public override void ChangeGameSetting()
+ {
+ var stringOption = OptionBehaviour as StringOption;
+
+ stringOption!.MinusBtn.transform.localPosition += new Vector3(0.6f, 0f, 0f);
+ stringOption.PlusBtn.transform.localPosition += new Vector3(1.5f, 0f, 0f);
+
+ // The value box itself becomes the button
+ stringOption.PlusBtn.gameObject.SetActive(false);
+ stringOption.MinusBtn.gameObject.SetActive(false);
+
+ var background = stringOption.transform.GetChild(0);
+ background.localPosition += new Vector3(-0.8f, 0f, 0f);
+ background.localScale += new Vector3(1f, 0f, 0f);
+
+ var title = stringOption.TitleText;
+ title.transform.localPosition = new(-2.0466f, 0f, -2.9968f);
+ title.GetComponent().sizeDelta = new(5.8f, 0.458f);
+ title.fontSize = 2.9f; // Why is it different for string options??
+
+ var valueBox = stringOption.transform.GetChild(5);
+ valueBox.localPosition += new Vector3(1.05f, 0f, 0f);
+ valueBox.localScale += new Vector3(0.2f, 0f, 0f);
+ }
+
///
public override float GetFloatData()
{
diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedMultiSelectOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedMultiSelectOption.cs
index ad5b67b..fd4de5f 100644
--- a/MiraAPI/GameOptions/OptionTypes/ModdedMultiSelectOption.cs
+++ b/MiraAPI/GameOptions/OptionTypes/ModdedMultiSelectOption.cs
@@ -1,11 +1,15 @@
+using AmongUs.GameOptions;
using System;
+using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Reflection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using MiraAPI.Modules;
using MiraAPI.Networking;
-using Reactor.Localization.Utilities;
-using Reactor.Utilities;
+using MiraAPI.Utilities;
+using MiraAPI.Utilities.Assets;
+using Reactor.Utilities.Extensions;
using UnityEngine;
using Object = UnityEngine.Object;
@@ -15,7 +19,7 @@ namespace MiraAPI.GameOptions.OptionTypes;
/// An option for selecting an enum value.
///
/// The enum type.
-public class ModdedMultiSelectOption : ModdedOption> where T : struct, Enum
+public class ModdedMultiSelectOption : ModdedOption where T : struct, Enum
{
///
/// Gets the string values of the enum.
@@ -32,6 +36,11 @@ public class ModdedMultiSelectOption : ModdedOption> wher
///
public T None { get; }
+ ///
+ /// Gets the list of buttons that relate to the current instance.
+ ///
+ public List Buttons { get; }
+
///
/// Initializes a new instance of the class.
///
@@ -40,35 +49,25 @@ public class ModdedMultiSelectOption : ModdedOption> wher
/// The all value.
/// The none value.
/// An option list of string values to use in place of the enum name.
+ /// The enum type doesn't have the flags attribute.
public ModdedMultiSelectOption(string title, T defaultValue, T all, T none, string[]? values = null) : base(title, defaultValue)
{
+ var tType = typeof(T);
+
+ if (tType.GetCustomAttribute() == null)
+ throw new ArgumentException($"{tType.Name} is not a flag enum type");
+
Values = values ?? Enum.GetNames();
All = all;
None = none;
Data = ScriptableObject.CreateInstance();
+ Buttons = [];
+
var data = (MultiSelectGameSetting)Data;
-
data.Title = StringName;
- data.Type = global::OptionTypes.String;
- data.Values = (values ?? Enum.GetNames()).Select(CustomStringName.CreateAndRegister).ToArray();
- }
-
- ///
- public override void BindConfig()
- {
- var entry = ParentMod?.GetConfigFile().Bind(ConfigDefinition, DefaultValue.ToString());
- if (entry != null)
- {
- try
- {
- Value = new MultiSelectValue([.. entry.Value.Split(",").Select(x => Enum.Parse(x.Trim()))]);
- }
- catch (Exception e)
- {
- Value = DefaultValue;
- Logger.Error($"Failed to bind config for {Title}: {e.Message}");
- }
- }
+ data.OptionName = Int32OptionNames.Invalid;
+ data.Type = global::OptionTypes.MultipleChoice; // How nice of the game to provide an enum value themselves
+ data.EnumNames = []; // We don't initialise the translation here, we just add one as and when needed
}
///
@@ -79,9 +78,23 @@ public class ModdedMultiSelectOption : ModdedOption> wher
stringOption.SetUpFromData(Data, 20);
stringOption.OnValueChanged = (Action)ValueChanged;
+ var toggle = Object.Instantiate(toggleOpt.GetComponentInChildren(), stringOption.transform);
+ toggle.name = "Button";
+ toggle.transform.DestroyChildren();
+ toggle.OverrideOnClickListeners(ToggleButtons);
+
+ var box = toggle.GetComponent();
+ var prevColliderSize = box.size;
+ prevColliderSize.x *= 4.97f;
+ box.size = prevColliderSize;
+
+ var rend = stringOption.transform.GetChild(5).GetComponent();
+ toggle.OverrideOnMouseOverListeners(() => rend.color = Buttons.Count > 0 ? Color.white : MiraAssets.AcceptedTeal);
+ toggle.OverrideOnMouseOutListeners(() => rend.color = Buttons.Count > 0 ? MiraAssets.AcceptedTeal : Color.white);
+
// SetUpFromData method doesn't work correctly so we must set the values manually
stringOption.Title = StringName;
- stringOption.Values = (Data as StringGameSetting)?.Values ?? new Il2CppStructArray(0);
+ stringOption.Values = (Data as MultiSelectGameSetting)?.EnumNames?.Values?.ToArray() ?? new Il2CppStructArray(0);
stringOption.Value = Convert.ToInt32(Value, NumberFormatInfo.InvariantInfo);
OptionBehaviour = stringOption;
@@ -89,6 +102,40 @@ public class ModdedMultiSelectOption : ModdedOption> wher
return stringOption;
}
+ ///
+ public override void ChangeGameSetting()
+ {
+ var stringOption = OptionBehaviour as StringOption;
+
+ // The value box itself becomes the button
+ stringOption!.PlusBtn.gameObject.SetActive(false);
+ stringOption.MinusBtn.gameObject.SetActive(false);
+
+ var background = stringOption.transform.GetChild(0);
+ background.localPosition += new Vector3(-0.8f, 0f, 0f);
+ background.localScale += new Vector3(1f, 0f, 0f);
+
+ var title = stringOption.TitleText;
+ title.transform.localPosition = new(-2.0466f, 0f, -2.9968f);
+ title.GetComponent().sizeDelta = new(5.8f, 0.458f);
+ title.fontSize = 2.9f; // Why is it different for string options??
+
+ var valueBox = stringOption.transform.GetChild(5);
+ valueBox.localPosition += new Vector3(1.05f, 0f, 0f);
+ valueBox.localScale += new Vector3(0.2f, 0f, 0f);
+ }
+
+ private void ToggleButtons()
+ {
+ if (Buttons.Count > 0)
+ {
+ Buttons.ForEach(x => x.gameObject.Destroy());
+ return;
+ }
+
+ // TODO: Implement this
+ }
+
///
public override float GetFloatData()
{
@@ -98,23 +145,23 @@ public class ModdedMultiSelectOption : ModdedOption> wher
///
public override NetData GetNetData()
{
- return new NetData(Id, Value.ToBytes());
+ return new NetData(Id, EnumSerializer.EnumToBytes(Value));
}
///
public override void HandleNetData(byte[] data)
{
- SetValue(MultiSelectValue.FromBytes(data));
+ SetValue(EnumSerializer.EnumFromBytes(data, 0));
}
///
- public override MultiSelectValue GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
+ public override T GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
{
- return []; // TODO: Add implementation
+ return Enum.Parse(optionBehaviour.GetInt().ToString(NumberFormatInfo.InvariantInfo));
}
///
- protected override void OnValueChanged(MultiSelectValue newValue)
+ protected override void OnValueChanged(T newValue)
{
HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data!.GetValueString(Convert.ToInt32(newValue, NumberFormatInfo.InvariantInfo)), false);
if (!OptionBehaviour)
diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs
index 4d38c04..a8f58af 100644
--- a/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs
+++ b/MiraAPI/GameOptions/OptionTypes/ModdedNumberOption.cs
@@ -112,6 +112,27 @@ public class ModdedNumberOption : ModdedOption
return numberOption;
}
+ ///
+ public override void ChangeGameSetting()
+ {
+ var numberOption = OptionBehaviour as NumberOption;
+
+ numberOption!.PlusBtn.transform.localPosition += new Vector3(0.6f, 0f, 0f);
+ numberOption.MinusBtn.transform.localPosition += new Vector3(1.5f, 0f, 0f);
+
+ var background = numberOption.transform.GetChild(0);
+ background.localPosition += new Vector3(-0.8f, 0f, 0f);
+ background.localScale += new Vector3(1f, 0f, 0f);
+
+ var title = numberOption.TitleText;
+ title.transform.localPosition = new(-2.0466f, 0f, -2.9968f);
+ title.GetComponent().sizeDelta = new(5.8f, 0.458f);
+
+ var valueBox = numberOption.transform.GetChild(5);
+ valueBox.localPosition += new Vector3(1.05f, 0f, 0f);
+ valueBox.localScale += new Vector3(0.2f, 0f, 0f);
+ }
+
///
public override float GetFloatData()
{
@@ -142,7 +163,7 @@ public class ModdedNumberOption : ModdedOption
Value = Mathf.Clamp(newValue, Min, Max);
HudManager.Instance.Notifier.AddSettingsChangeMessage(
StringName,
- Data.GetValueString(Value),
+ Data!.GetValueString(Value),
false);
if (OptionBehaviour is NumberOption opt)
diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs
index e5eb196..e24c314 100644
--- a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs
+++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs
@@ -139,6 +139,15 @@ public abstract class ModdedOption : IModdedOption where T : struct
StringOption stringOpt,
Transform container);
+ ///
+ public abstract void ChangeGameSetting();
+
+ ///
+ public virtual void ChangeRoleSetting()
+ {
+ // TODO: Implement this as an abstract
+ }
+
///
/// Binds the option to the configuration file of the parent mod, if available.
///
diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs
index 3495e86..e6f08f4 100644
--- a/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs
+++ b/MiraAPI/GameOptions/OptionTypes/ModdedToggleOption.cs
@@ -41,6 +41,21 @@ public class ModdedToggleOption : ModdedOption
return toggleOption;
}
+ ///
+ public override void ChangeGameSetting()
+ {
+ var toggleOption = OptionBehaviour as ToggleOption;
+ toggleOption!.transform.GetChild(1).localPosition += new Vector3(2.2f, 0f, 0f); // Checkbox
+
+ var title = toggleOption.TitleText;
+ title.transform.localPosition = new(-2.0466f, 0f, -2.9968f);
+ title.GetComponent().sizeDelta = new(5.8f, 0.458f);
+
+ var background = toggleOption.transform.GetChild(2);
+ background.localPosition += new Vector3(-0.8f, 0f, 0f);
+ background.localScale += new Vector3(1f, 0f, 0f);
+ }
+
///
public override float GetFloatData()
{
diff --git a/MiraAPI/Modules/MultiSelectGameSetting.cs b/MiraAPI/Modules/MultiSelectGameSetting.cs
index c8224d5..978c062 100644
--- a/MiraAPI/Modules/MultiSelectGameSetting.cs
+++ b/MiraAPI/Modules/MultiSelectGameSetting.cs
@@ -1,7 +1,9 @@
// TODO: Remove later before completing the pr
#pragma warning disable
using System;
+using System.Collections.Generic;
using AmongUs.GameOptions;
+using Il2CppInterop.Runtime.Attributes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Reactor.Utilities.Attributes;
@@ -10,14 +12,8 @@ namespace MiraAPI.Modules;
[RegisterInIl2Cpp, Serializable]
public class MultiSelectGameSetting : BaseGameSetting
{
- public MultiSelectGameSetting() => Type = OptionTypes.MultipleChoice; // How nice of the game to provide an enum value themselves
-
- public override string GetValueString(float value)
- {
- return TranslationController.Instance.GetString(default(StringNames), null, null);
- }
-
public Int32OptionNames OptionName;
+ public Dictionary EnumNames;
- public Il2CppStructArray Values;
+ public override string GetValueString(float value) => TranslationController.Instance.GetString(EnumNames[(int)value]);
}
diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs
index 8034eff..c72baeb 100644
--- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs
+++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs
@@ -226,6 +226,8 @@ public static class GameOptionsMenuPatch
menu.stringOptionOrigin,
menu.settingsContainer));
+ group.Options.ForEach(x => x.ChangeGameSetting());
+
foreach (var newOpt in options)
{
newOpt.SetClickMask(menu.ButtonClickMask);
diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs
index 9fe48d7..9f2d431 100644
--- a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs
+++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs
@@ -325,6 +325,7 @@ public static class RoleSettingMenuPatches
newOpt.transform.localPosition = new Vector3(hasImage ? 2.17f : 1.1f, num, -2f);
newOpt.SetClickMask(__instance.ButtonClickMask);
+ option.ChangeRoleSetting();
SpriteRenderer[] componentsInChildren = newOpt.GetComponentsInChildren(true);
foreach (var renderer in componentsInChildren)
diff --git a/MiraAPI/Utilities/Helpers.cs b/MiraAPI/Utilities/Helpers.cs
index e4c5281..09c6bf2 100644
--- a/MiraAPI/Utilities/Helpers.cs
+++ b/MiraAPI/Utilities/Helpers.cs
@@ -431,4 +431,50 @@ public static class Helpers
items.Do(x => result += $"{separator}{x}");
return result[separator.Length..];
}
+
+ ///
+ /// Shorthand helper for overriding the on click listeners for PassiveButton.
+ ///
+ /// The PassiveButton being modified.
+ /// The action that's replacing the original listeners.
+ public static void OverrideOnClickListeners(this PassiveButton passive, Action action)
+ {
+ if (!passive)
+ return;
+
+ passive.OnClick?.RemoveAllListeners();
+ passive.OnClick = new();
+ passive.OnClick.AddListener(action);
+ passive.enabled = true;
+ }
+
+ ///
+ /// Shorthand helper for overriding the on mouse over listeners for PassiveButton.
+ ///
+ ///
+ public static void OverrideOnMouseOverListeners(this PassiveButton passive, Action action)
+ {
+ if (!passive)
+ return;
+
+ passive.OnMouseOver?.RemoveAllListeners();
+ passive.OnMouseOver = new();
+ passive.OnMouseOver.AddListener(action);
+ passive.enabled = true;
+ }
+
+ ///
+ /// Shorthand helper for overriding the on mouse out listeners for PassiveButton.
+ ///
+ ///
+ public static void OverrideOnMouseOutListeners(this PassiveButton passive, Action action)
+ {
+ if (!passive)
+ return;
+
+ passive.OnMouseOut?.RemoveAllListeners();
+ passive.OnMouseOut = new();
+ passive.OnMouseOut.AddListener(action);
+ passive.enabled = true;
+ }
}