Merge pull request #1 from ang-xd/attribute-options

Attribute options
This commit is contained in:
Ang authored and GitHub committed 2024-06-16 18:55:20 -04:00
commit 0b84478184
45 files changed
+939 -995

No files matched your search

+2 -2
View File
@@ -6,10 +6,10 @@ namespace MiraAPI.Example
[RegisterCustomRole(ExamplePlugin.Id)]
public class CustomRole : CrewmateRole, ICustomRole
{
public string RoleName => "Sussy Baka";
public string RoleName => "Test Role";
public string RoleDescription => "Role decs?";
public string RoleLongDescription => "Extra role decs?";
public Color RoleColor => Palette.Orange;
public RoleTeamTypes Team => RoleTeamTypes.Crewmate;
public ModdedRoleTeams Team => ModdedRoleTeams.Neutral;
}
}
+17 -8
View File
@@ -1,16 +1,25 @@
using MiraAPI.API.GameOptions;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.Attributes;
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
using UnityEngine;
namespace MiraAPI.Example
{
[RegisterCustomOptions(ExamplePlugin.Id)]
public class ExampleOptions : IModOptions
public class ExampleOptions : IModdedOptionGroup
{
public LoadableAsset<Sprite> OptionsTabIcon => MiraAssets.Empty;
public static CustomToggleOption YeOrNa = new CustomToggleOption("Ye or Na", true);
public string GroupName => "General Group";
public Color GroupColor => Color.red;
[ModdedToggleOption("Use Thing")] public bool useThing { get; set; } = true;
[ModdedToggleOption("Use another thing")] public bool useAnotherThing { get; set; } = false;
[ModdedNumberOption("Sussy level", min: 0, max: 10)] public float sussyLevel { get; set; } = 4f;
[ModdedStringOption("Sus choices", ["hello", "hello 2", "hello 3"])] public int susChoices { get; set; } = 0;
[ModdedEnumOption("Enum test", typeof(TestEnum))] public TestEnum enumTest { get; set; } = TestEnum.Hello;
}
public enum TestEnum
{
Hello,
Hi,
Bye
}
}
+9 -3
View File
@@ -1,6 +1,7 @@
using BepInEx;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.Utilities.Assets;
using Reactor;
using Reactor.Networking;
using Reactor.Networking.Attributes;
@@ -11,14 +12,19 @@ namespace MiraAPI.Example;
[BepInProcess("Among Us.exe")]
[BepInDependency(ReactorPlugin.Id)]
[ReactorModFlags(ModFlags.RequireOnAllClients)]
public partial class ExamplePlugin : BasePlugin
public partial class ExamplePlugin : BasePlugin, IMiraConfig
{
public Harmony Harmony { get; } = new(Id);
public static ExamplePlugin Instance { get; private set; }
public ModdedOptionTabSettings TabSettings => new ModdedOptionTabSettings()
{
Title = "MiraAPI Example Mod",
TabIcon = MiraAssets.Empty
};
public override void Load()
{
Instance = this;
Harmony.PatchAll();
}
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace MiraAPI.Example
{
public class ModifierExample
{
//name, role specific, etc
// mono behaviour stuff here (ModdedPlayerModifier inherits monobehaviour)
}
}
@@ -0,0 +1,9 @@
using MiraAPI.GameOptions.Attributes;
namespace MiraAPI.Example
{
public class SomeRandomNonGroupClass
{
[ModdedStringOption("Yeah, idk", ["Idk 1", "idk 2", "idk 3", "idk 4"])] public static int yeaIdk { get; set; } = 2;
}
}
-39
View File
@@ -1,39 +0,0 @@
using BepInEx;
using MiraAPI.Roles;
using Reactor.Localization.Utilities;
using System;
namespace MiraAPI.API.GameOptions;
public abstract class AbstractGameOption
{
public string Title { get; }
public StringNames StringName { get; }
public Type AdvancedRole { get; }
public bool Save { get; }
public bool ShowInHideNSeek { get; init; }
public CustomOptionGroup Group { get; set; }
public Func<bool> Hidden { get; set; }
public OptionBehaviour OptionBehaviour { get; protected set; }
public PluginInfo ParentMod { get; set; }
public void ValueChanged(OptionBehaviour optionBehaviour)
{
OnValueChanged(optionBehaviour);
CustomOptionsManager.SyncOptions();
}
protected abstract void OnValueChanged(OptionBehaviour optionBehaviour);
protected AbstractGameOption(string title, Type roleType, bool save)
{
Title = title;
StringName = CustomStringName.CreateAndRegister(Title);
if (roleType is not null && roleType.IsAssignableTo(typeof(ICustomRole)))
{
AdvancedRole = roleType;
}
Save = save;
Hidden = () => false;
}
}
@@ -0,0 +1,33 @@
using MiraAPI.GameOptions.OptionTypes;
using System;
using System.Reflection;
namespace MiraAPI.GameOptions.Attributes
{
public class ModdedEnumOptionAttribute : ModdedOptionAttribute
{
public Type EnumType;
public ModdedEnumOptionAttribute(string title, Type enumType, Type roleType = null) : base(title, roleType)
{
EnumType = enumType;
}
public override IModdedOption CreateOption(object value, PropertyInfo property)
{
var opt = new ModdedEnumOption(Title, (int)value, EnumType, RoleType);
return opt;
}
public override void SetValue(object value)
{
ModdedEnumOption opt = HolderOption as ModdedEnumOption;
opt.SetValue((int)value);
}
public override object GetValue()
{
ModdedEnumOption opt = HolderOption as ModdedEnumOption;
return Enum.ToObject(EnumType.GetType(), opt.Value);
}
}
}
@@ -0,0 +1,44 @@
using MiraAPI.GameOptions.OptionTypes;
using System;
using System.Reflection;
using UnityEngine;
namespace MiraAPI.GameOptions.Attributes
{
public class ModdedNumberOptionAttribute : ModdedOptionAttribute
{
public float Min { get; private set; }
public float Max { get; private set; }
public float Increment { get; private set; }
public NumberSuffixes SuffixType { get; private set; }
public bool ZeroInfinity { get; private set; }
public ModdedNumberOptionAttribute(string title, float min, float max, float increment = 1, NumberSuffixes suffixType = NumberSuffixes.None, bool zeroInfinity = false, Type roleType = null) : base(title, roleType)
{
Min = min;
Max = max;
Increment = increment;
SuffixType = suffixType;
ZeroInfinity = zeroInfinity;
}
public override IModdedOption CreateOption(object value, PropertyInfo property)
{
Debug.LogError(value.ToString());
var toggleOpt = new ModdedNumberOption(Title, (float)value, Min, Max, Increment, SuffixType, ZeroInfinity, RoleType);
return toggleOpt;
}
public override void SetValue(object value)
{
ModdedNumberOption toggleOpt = HolderOption as ModdedNumberOption;
toggleOpt.SetValue((float)value);
}
public override object GetValue()
{
ModdedNumberOption toggleOpt = HolderOption as ModdedNumberOption;
return toggleOpt.Value;
}
}
}
@@ -0,0 +1,39 @@
using System;
using System.Reflection;
namespace MiraAPI.GameOptions.Attributes
{
[AttributeUsage(AttributeTargets.Property)]
public abstract class ModdedOptionAttribute : Attribute
{
public IModdedOption HolderOption { get; set; }
public string Title { get; private set; }
public Type RoleType { get; private set; }
public ModdedOptionAttribute(string title, Type roleType = null)
{
Title = title;
RoleType = roleType;
}
public static void Register(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
foreach (PropertyInfo property in type.GetProperties())
{
foreach (var attribute in property.GetCustomAttributes())
{
if (attribute.GetType().BaseType == typeof(ModdedOptionAttribute))
{
ModdedOptionsManager.RegisterOption(assembly, type, (ModdedOptionAttribute)attribute, property);
}
}
}
}
}
public abstract void SetValue(object val);
public abstract object GetValue();
public abstract IModdedOption CreateOption(object value, PropertyInfo property);
}
}
@@ -0,0 +1,33 @@
using MiraAPI.GameOptions.OptionTypes;
using System;
using System.Reflection;
namespace MiraAPI.GameOptions.Attributes
{
public class ModdedStringOptionAttribute : ModdedOptionAttribute
{
public string[] Values;
public ModdedStringOptionAttribute(string title, string[] strings, Type roleType = null) : base(title, roleType)
{
Values = strings;
}
public override IModdedOption CreateOption(object value, PropertyInfo property)
{
var opt = new ModdedStringOption(Title, (int)value, Values, RoleType);
return opt;
}
public override void SetValue(object value)
{
ModdedStringOption opt = HolderOption as ModdedStringOption;
opt.SetValue((int)value);
}
public override object GetValue()
{
ModdedStringOption opt = HolderOption as ModdedStringOption;
return opt.Value;
}
}
}
@@ -0,0 +1,31 @@
using MiraAPI.GameOptions.OptionTypes;
using System;
using System.Reflection;
namespace MiraAPI.GameOptions.Attributes
{
public class ModdedToggleOptionAttribute : ModdedOptionAttribute
{
public ModdedToggleOptionAttribute(string title, Type roleType = null) : base(title, roleType)
{
}
public override IModdedOption CreateOption(object value, PropertyInfo property)
{
var toggleOpt = new ModdedToggleOption(Title, (bool)value, RoleType);
return toggleOpt;
}
public override void SetValue(object value)
{
ModdedToggleOption toggleOpt = HolderOption as ModdedToggleOption;
toggleOpt.SetValue((bool)value);
}
public override object GetValue()
{
ModdedToggleOption toggleOpt = HolderOption as ModdedToggleOption;
return toggleOpt.Value;
}
}
}
@@ -1,18 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace MiraAPI.GameOptions.Attributes
{
[AttributeUsage(AttributeTargets.Field)]
public class RegisterCustomOptionAttribute : Attribute
{
private static readonly HashSet<Assembly> RegisteredAssemblies = [];
public string ModId { get; }
public RegisterCustomOptionAttribute(string modId)
{
ModId = modId;
}
}
}
@@ -1,43 +0,0 @@
using BepInEx.Unity.IL2CPP;
using MiraAPI.API.GameOptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace MiraAPI.GameOptions.Attributes
{
[AttributeUsage(AttributeTargets.Class)]
public class RegisterCustomOptionsAttribute : Attribute
{
private static readonly HashSet<Assembly> RegisteredAssemblies = [];
public string ModId { get; }
public List<FieldInfo> Options;
public RegisterCustomOptionsAttribute(string modId)
{
ModId = modId;
}
public static void Initialize()
{
IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) => Register(assembly);
}
public static void Register(Assembly assembly)
{
if (!RegisteredAssemblies.Add(assembly)) return;
foreach (var type in assembly.GetTypes())
{
var attribute = type.GetCustomAttribute<RegisterCustomOptionsAttribute>();
if (attribute != null)
{
attribute.Options = type.GetFields(BindingFlags.Static | BindingFlags.Public)
.Where(fi => fi.FieldType.BaseType == typeof(AbstractGameOption) || fi.FieldType.BaseType == typeof(CustomOptionGroup)).ToList();
CustomOptionsManager.RegisterOptionsForMod(type, attribute.Options, attribute.ModId);
}
}
}
}
}
-100
View File
@@ -1,100 +0,0 @@
using BepInEx.Configuration;
using Reactor.Utilities;
using System;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.API.GameOptions;
public class CustomNumberOption : AbstractGameOption
{
public float Value { get; private set; }
public float Min { get; }
public float Max { get; }
public float Increment { get; }
public NumberSuffixes SuffixType { get; }
public string NumberFormat { get; }
public float Default { get; }
public bool ZeroInfinity { get; }
public ConfigEntry<float> Config { get; }
public Action<float> ChangedEvent { get; set; }
public CustomNumberOption(string title, float defaultValue, float min, float max, float increment, NumberSuffixes suffixType, bool zeroInfinity = false, string numberFormat = "0", Type role = null, bool save = true) : base(title, role, save)
{
Value = defaultValue;
Default = defaultValue;
Min = min;
Max = max;
Increment = increment;
SuffixType = suffixType;
NumberFormat = numberFormat;
ZeroInfinity = zeroInfinity;
if (Save)
{
try
{
Config = MiraAPIPlugin.Instance.Config.Bind("Number Options", title, defaultValue);
SetValue(Save ? Config.Value : defaultValue);
}
catch (Exception e)
{
Logger<MiraAPIPlugin>.Warning(e.ToString());
}
}
}
public void SetValue(float newValue)
{
newValue = Mathf.Clamp(newValue, Min, Max);
if (Save && (!AmongUsClient.Instance || AmongUsClient.Instance.AmHost))
{
try
{
Config.Value = newValue;
}
catch (Exception e)
{
Logger<MiraAPIPlugin>.Warning(e.ToString());
}
}
Value = newValue;
var behaviour = (NumberOption)OptionBehaviour;
if (behaviour)
{
behaviour.Value = Value;
}
ChangedEvent?.Invoke(Value);
}
public NumberOption CreateNumberOption(NumberOption original, Transform container)
{
var numberOption = Object.Instantiate(original, container);
numberOption.name = Title;
numberOption.Title = StringName;
numberOption.Value = Value;
numberOption.Increment = Increment;
numberOption.SuffixType = SuffixType;
numberOption.FormatString = NumberFormat;
numberOption.ValidRange = new FloatRange(Min, Max);
numberOption.ZeroIsInfinity = ZeroInfinity;
numberOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
numberOption.OnEnable();
OptionBehaviour = numberOption;
return numberOption;
}
protected override void OnValueChanged(OptionBehaviour optionBehaviour)
{
SetValue(optionBehaviour.GetFloat());
}
}
-46
View File
@@ -1,46 +0,0 @@
using BepInEx;
using MiraAPI.Roles;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace MiraAPI.API.GameOptions;
public class CustomOptionGroup
{
public string Title { get; }
public Func<bool> Hidden { get; set; }
public GameObject Header;
public Type AdvancedRole { get; set; }
public PluginInfo ParentMod { get; set; }
public readonly List<AbstractGameOption> Options = [];
public readonly List<CustomNumberOption> CustomNumberOptions;
public readonly List<CustomToggleOption> CustomToggleOptions;
public readonly List<CustomStringOption> CustomStringOptions;
public CustomOptionGroup(string title, List<CustomNumberOption> numberOpt,
List<CustomToggleOption> toggleOpt, List<CustomStringOption> stringOpt, Type role = null)
{
Title = title;
if (role is not null && role.IsAssignableTo(typeof(ICustomRole)))
{
AdvancedRole = role;
}
Hidden = () => false;
CustomNumberOptions = numberOpt;
CustomToggleOptions = toggleOpt;
CustomStringOptions = stringOpt;
Options.AddRange(CustomNumberOptions);
Options.AddRange(CustomToggleOptions);
Options.AddRange(CustomStringOptions);
foreach (var option in Options)
{
option.Group = this;
}
}
}
-159
View File
@@ -1,159 +0,0 @@
using BepInEx;
using BepInEx.Unity.IL2CPP;
using MiraAPI.GameOptions;
using MiraAPI.Networking.Options;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace MiraAPI.API.GameOptions;
public static class CustomOptionsManager
{
public static readonly List<AbstractGameOption> CustomOptions = [];
public static readonly List<CustomNumberOption> CustomNumberOptions = [];
public static readonly List<CustomToggleOption> CustomToggleOptions = [];
public static readonly List<CustomStringOption> CustomStringOptions = [];
public static readonly List<CustomOptionGroup> CustomGroups = [];
public static readonly Dictionary<PluginInfo, IModOptions> RegisteredMods = [];
public static void RegisterOptionsForMod(IModOptions type, List<FieldInfo> fields, string modId)
{
PluginInfo info = IL2CPPChainloader.Instance.Plugins[modId];
if (info == null)
{
return;
}
Debug.Log($"Registering Options for {modId}");
RegisteredMods.Add(info, type);
foreach (FieldInfo field in fields)
{
AbstractGameOption option = (AbstractGameOption)field.GetValue(null);
if (option == null)
{
CustomOptionGroup group = (CustomOptionGroup)field.GetValue(null);
group.ParentMod = info;
CustomGroups.Add(group);
}
option.ParentMod = info;
CustomOptions.Add(option);
switch (field.FieldType.Name)
{
case nameof(CustomNumberOption):
CustomNumberOptions.Add((CustomNumberOption)option);
break;
case nameof(CustomToggleOption):
CustomToggleOptions.Add((CustomToggleOption)option);
break;
case nameof(CustomStringOption):
CustomStringOptions.Add((CustomStringOption)option);
break;
}
}
}
public static void SyncOptions()
{
if (!AmongUsClient.Instance.AmHost)
{
return;
}
var toggles = CustomToggleOptions.Select(x => x.Value).ToArray();
var numbers = CustomNumberOptions.Select(x => x.Value).ToArray();
var strings = CustomStringOptions.Select(x => x.IndexValue).ToArray();
Rpc<SyncOptionsRpc>.Instance.Send(new SyncOptionsRpc.Data(toggles, numbers, strings));
}
public static void SyncOptions(int targetId)
{
if (!AmongUsClient.Instance.AmHost)
{
return;
}
var toggles = CustomToggleOptions.Select(x => x.Value).ToArray();
var numbers = CustomNumberOptions.Select(x => x.Value).ToArray();
var strings = CustomStringOptions.Select(x => x.IndexValue).ToArray();
Rpc<SyncOptionsRpc>.Instance.SendTo(targetId, new SyncOptionsRpc.Data(toggles, numbers, strings));
}
public static void UpdateToConfig()
{
foreach (var numberOpt in CustomNumberOptions)
{
numberOpt.SetValue(numberOpt.Save ? numberOpt.Config.Value : numberOpt.Default);
}
foreach (var toggleOpt in CustomToggleOptions)
{
toggleOpt.SetValue(toggleOpt.Save ? toggleOpt.Config.Value : toggleOpt.Default);
}
foreach (var stringOpt in CustomStringOptions)
{
stringOpt.SetValue(stringOpt.Save ? stringOpt.Config.Value : stringOpt.Default);
}
}
public static void ResetToDefault()
{
foreach (var numberOpt in CustomNumberOptions)
{
numberOpt.SetValue(numberOpt.Default);
}
foreach (var stringOpt in CustomStringOptions)
{
stringOpt.SetValue(stringOpt.Default);
}
foreach (var toggleOpt in CustomToggleOptions)
{
toggleOpt.SetValue(toggleOpt.Default);
}
foreach (var option in CustomOptions)
{
if (!option.OptionBehaviour)
{
continue;
}
option.ValueChanged(option.OptionBehaviour);
}
}
public static void HandleOptionsSync(bool[] toggles, float[] numbers, int[] strings)
{
Logger<MiraAPIPlugin>.Warning("syncing options!");
for (var i = 0; i < toggles.Length; i++)
{
CustomToggleOptions[i].SetValue(toggles[i]);
}
for (var i = 0; i < numbers.Length; i++)
{
CustomNumberOptions[i].SetValue(numbers[i]);
}
for (var i = 0; i < strings.Length; i++)
{
CustomStringOptions[i].SetValue(strings[i]);
}
}
}
-92
View File
@@ -1,92 +0,0 @@
using BepInEx.Configuration;
using Reactor.Localization.Utilities;
using Reactor.Utilities;
using System;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.API.GameOptions;
public class CustomStringOption : AbstractGameOption
{
public int IndexValue { get; private set; }
public string Value => Options[IndexValue];
public int Default { get; }
public string[] Options { get; private set; }
public ConfigEntry<int> Config { get; }
public Action<int, string> ChangedEvent { get; init; }
public CustomStringOption(string title, int defaultValue, string[] options, Type role = null, bool save = true) : base(title, role, save)
{
IndexValue = defaultValue;
Options = options;
Default = defaultValue;
if (Save)
{
try
{
Config = MiraAPIPlugin.Instance.Config.Bind("String Options", title, defaultValue);
SetValue(Save ? Config.Value : defaultValue);
}
catch (Exception e)
{
Logger<MiraAPIPlugin>.Warning(e.ToString());
}
}
}
public void SetValue(int newValue)
{
if (Save && (!AmongUsClient.Instance || AmongUsClient.Instance.AmHost))
{
try
{
Config.Value = newValue;
}
catch (Exception e)
{
Logger<MiraAPIPlugin>.Warning(e.ToString());
}
}
var oldValue = IndexValue;
IndexValue = newValue;
var behaviour = (StringOption)OptionBehaviour;
if (behaviour)
{
behaviour.Value = newValue;
}
if (oldValue != newValue)
{
ChangedEvent?.Invoke(newValue, Value);
}
}
public void SetValue(string newValue) => SetValue(Options.ToList().IndexOf(newValue));
protected override void OnValueChanged(OptionBehaviour optionBehaviour)
{
SetValue(optionBehaviour.GetInt());
}
public StringOption CreateStringOption(StringOption original, Transform container)
{
var stringOption = Object.Instantiate(original, container);
stringOption.name = Title;
stringOption.Title = StringName;
stringOption.Value = Options.ToList().IndexOf(Value);
stringOption.Values = Options.Select(CustomStringName.CreateAndRegister).ToArray();
stringOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
stringOption.OnEnable();
OptionBehaviour = stringOption;
return stringOption;
}
}
-78
View File
@@ -1,78 +0,0 @@
using BepInEx.Configuration;
using Reactor.Utilities;
using System;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.API.GameOptions;
public class CustomToggleOption : AbstractGameOption
{
public bool Value { get; private set; }
public bool Default { get; }
public ConfigEntry<bool> Config { get; }
public Action<bool> ChangedEvent { get; init; }
public CustomToggleOption(string title, bool defaultValue, Type role = null, bool save = true) : base(title, role, save)
{
Default = defaultValue;
if (Save)
{
try
{
Config = MiraAPIPlugin.Instance.Config.Bind("Toggle Options", title, defaultValue);
SetValue(Save ? Config.Value : defaultValue);
}
catch (Exception e)
{
Logger<MiraAPIPlugin>.Warning(e.ToString());
}
}
}
public void SetValue(bool newValue)
{
if (Save && (!AmongUsClient.Instance || AmongUsClient.Instance.AmHost))
{
try
{
Config.Value = newValue;
}
catch (Exception e)
{
Logger<MiraAPIPlugin>.Warning(e.ToString());
}
}
var oldValue = Value;
Value = newValue;
var behaviour = (ToggleOption)OptionBehaviour;
if (behaviour)
{
behaviour.CheckMark.enabled = newValue;
}
if (newValue != oldValue)
{
ChangedEvent?.Invoke(newValue);
}
}
protected override void OnValueChanged(OptionBehaviour optionBehaviour)
{
SetValue(optionBehaviour.GetBool());
}
public ToggleOption CreateToggleOption(ToggleOption original, Transform container)
{
var toggleOption = Object.Instantiate(original, container);
toggleOption.name = Title;
toggleOption.Title = StringName;
toggleOption.CheckMark.enabled = Value;
toggleOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
toggleOption.OnEnable();
OptionBehaviour = toggleOption;
return toggleOption;
}
}
-10
View File
@@ -1,10 +0,0 @@
using MiraAPI.Utilities;
using UnityEngine;
namespace MiraAPI.GameOptions
{
public interface IModOptions
{
LoadableAsset<Sprite> OptionsTabIcon { get; }
}
}
+19
View File
@@ -0,0 +1,19 @@
using System;
using UnityEngine;
namespace MiraAPI.GameOptions
{
public interface IModdedOption
{
public Type AdvancedRole { get; }
public OptionBehaviour OptionBehaviour { get; protected set; }
public string Title { get; }
public StringNames StringName { get; }
public Func<bool> Visible { get; set; }
public ModdedOptionGroup Group { get; set; }
public IMiraConfig ParentMod { get; set; }
public void ValueChanged(OptionBehaviour optionBehaviour);
public string GetHudStringText();
public OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container);
}
}
+13
View File
@@ -0,0 +1,13 @@
using System;
using UnityEngine;
namespace MiraAPI.GameOptions
{
public interface IModdedOptionGroup
{
public string GroupName { get; }
public Func<bool> GroupVisible => () => true;
public Color GroupColor => Color.white;
public Type AdvancedRole => null;
}
}
@@ -0,0 +1,20 @@
using System;
using System.Linq;
namespace MiraAPI.GameOptions
{
public class ModdedGroupSingleton<T> where T : ModdedOptionGroup
{
private static T _instance;
public static T Instance
{
get => _instance ??= ModdedOptionsManager.Groups.OfType<T>().Single();
set
{
if (_instance != null) throw new InvalidOperationException($"Instance for {typeof(T)} is already set");
_instance = value;
}
}
}
}
+15
View File
@@ -0,0 +1,15 @@
using System;
using UnityEngine;
namespace MiraAPI.GameOptions
{
public class ModdedOptionGroup
{
public IMiraConfig ParentMod;
public GameObject Header;
public string GroupName;
public Func<bool> GroupVisible;
public Color GroupColor;
public Type AdvancedRole;
}
}
+112
View File
@@ -0,0 +1,112 @@
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.GameOptions.Attributes;
using Reactor.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace MiraAPI.GameOptions
{
public class ModdedOptionsManager
{
public static List<IModdedOption> Options = new();
public static List<ModdedOptionGroup> Groups = new();
private static Dictionary<PropertyInfo, ModdedOptionAttribute> OptionAttributes = new();
private static Dictionary<Type, ModdedOptionGroup> OriginalTypes = new();
public static Dictionary<Assembly, IMiraConfig> RegisteredMods = new();
public static void Initialize()
{
IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) =>
{
if (plugin.GetType().GetInterfaces().Contains(typeof(IMiraConfig)))
{
IMiraConfig config = (IMiraConfig)Activator.CreateInstance(plugin.GetType());
RegisteredMods.Add(assembly, config);
}
RegisterOptionGroups(assembly);
ModdedOptionAttribute.Register(assembly);
};
}
private static void RegisterOptionGroups(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
if (typeof(IModdedOptionGroup).IsAssignableFrom(type))
{
IModdedOptionGroup group = (IModdedOptionGroup)Activator.CreateInstance(type);
ModdedOptionGroup newGroup = new ModdedOptionGroup()
{
AdvancedRole = group.AdvancedRole,
GroupColor = group.GroupColor,
GroupName = group.GroupName,
GroupVisible = group.GroupVisible
};
Groups.Add(newGroup);
OriginalTypes.Add(type, newGroup);
newGroup.ParentMod = RegisteredMods[assembly];
}
}
}
public static void RegisterOption(Assembly assembly, Type type, ModdedOptionAttribute attribute, PropertyInfo property)
{
if (OptionAttributes.ContainsKey(property)) return;
object newObj = Activator.CreateInstance(type);
IModdedOption result = attribute.CreateOption(property.GetValue(newObj), property);
if (result != null)
{
var setterOriginal = property.GetSetMethod();
var setterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertySetterPatch));
PluginSingleton<MiraAPIPlugin>.Instance.Harmony.Patch(setterOriginal, postfix: new HarmonyMethod(setterPatch));
var getterOriginal = property.GetGetMethod();
var getterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertyGetterPatch));
PluginSingleton<MiraAPIPlugin>.Instance.Harmony.Patch(getterOriginal, prefix: new HarmonyMethod(getterPatch));
attribute.HolderOption = result;
result.ParentMod = RegisteredMods[assembly];
Options.Add(result);
OptionAttributes.Add(property, attribute);
if (OriginalTypes.ContainsKey(type))
{
Debug.LogError($"grouping {attribute.Title} with {OriginalTypes[type].GroupName}");
result.Group = OriginalTypes[type];
}
}
}
public static void PropertySetterPatch(MethodBase __originalMethod, object value)
{
ModdedOptionAttribute attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod().Equals(__originalMethod)).Value;
if (attribute != null)
{
attribute.SetValue(value);
}
}
public static bool PropertyGetterPatch(MethodBase __originalMethod, ref object __result)
{
ModdedOptionAttribute attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod().Equals(__originalMethod)).Value;
if (attribute != null)
{
__result = attribute.GetValue();
return false;
}
return true;
}
}
}
@@ -0,0 +1,51 @@
using Reactor.Localization.Utilities;
using System;
using System.Linq;
using UnityEngine;
namespace MiraAPI.GameOptions.OptionTypes
{
public class ModdedEnumOption : ModdedOption<int>
{
public string[] Values;
public ModdedEnumOption(string title, int defaultValue, Type enumType, System.Type roleType) : base(title, defaultValue, roleType)
{
Values = Enum.GetNames(enumType);
}
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{
var stringOption = UnityEngine.Object.Instantiate(stringOpt, container);
stringOption.name = Title;
stringOption.Title = StringName;
stringOption.Value = Value;
stringOption.Values = Values.Select(CustomStringName.CreateAndRegister).ToArray();
stringOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
stringOption.OnEnable();
OptionBehaviour = stringOption;
return stringOption;
}
public override string GetHudStringText()
{
return Title + ": " + Values[Value];
}
public override int GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
{
return optionBehaviour.GetInt();
}
public override void OnValueChanged(int newValue)
{
if (OptionBehaviour is null) return;
StringOption opt = OptionBehaviour as StringOption;
opt.Value = newValue;
}
}
}
@@ -0,0 +1,64 @@
using MiraAPI.Utilities;
using UnityEngine;
namespace MiraAPI.GameOptions.OptionTypes
{
public class ModdedNumberOption : ModdedOption<float>
{
public float Min { get; private set; }
public float Max { get; private set; }
public float Increment { get; private set; }
public NumberSuffixes SuffixType { get; private set; }
public bool ZeroInfinity { get; private set; }
public ModdedNumberOption(string title, float defaultValue, float min, float max, float increment, NumberSuffixes suffixType, bool zeroInfinity, System.Type roleType) : base(title, defaultValue, roleType)
{
Min = min;
Max = max;
Increment = increment;
SuffixType = suffixType;
ZeroInfinity = zeroInfinity;
Value = Mathf.Clamp(defaultValue, min, max);
}
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{
var numberOption = Object.Instantiate(numberOpt, container);
numberOption.name = Title;
numberOption.Title = StringName;
numberOption.Value = Value;
numberOption.Increment = Increment;
numberOption.SuffixType = SuffixType;
numberOption.FormatString = "0";
numberOption.ValidRange = new FloatRange(Min, Max);
numberOption.ZeroIsInfinity = ZeroInfinity;
numberOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
numberOption.OnEnable();
OptionBehaviour = numberOption;
return numberOption;
}
public override string GetHudStringText()
{
return Title + ": " + Value + Helpers.GetSuffix(SuffixType);
}
public override float GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
{
return Mathf.Clamp(optionBehaviour.GetFloat(), Min, Max);
}
public override void OnValueChanged(float newValue)
{
Value = Mathf.Clamp(newValue, Min, Max);
if (OptionBehaviour is null) return;
NumberOption opt = OptionBehaviour as NumberOption;
opt.Value = newValue;
}
}
}
@@ -0,0 +1,60 @@
using MiraAPI.Roles;
using Reactor.Localization.Utilities;
using System;
using UnityEngine;
namespace MiraAPI.GameOptions.OptionTypes
{
public abstract class ModdedOption<T> : IModdedOption
{
public T Value { get; set; }
public T DefaultValue { get; set; }
public Action<T> ChangedEvent { get; private set; }
public string Title { get; }
public StringNames StringName { get; }
public Func<bool> Visible { get; set; }
public Type AdvancedRole { get; }
public OptionBehaviour OptionBehaviour { get; set; }
public ModdedOptionGroup Group { get; set; } = null;
public IMiraConfig ParentMod { get; set; } = null;
public ModdedOption(string title, T defaultValue, Type roleType)
{
Title = title;
DefaultValue = defaultValue;
Value = defaultValue;
StringName = CustomStringName.CreateAndRegister(Title);
Visible = () => true;
if (roleType is not null && roleType.IsAssignableTo(typeof(ICustomRole)))
{
AdvancedRole = roleType;
}
}
public void ValueChanged(OptionBehaviour optionBehaviour)
{
SetValue(GetValueFromOptionBehaviour(optionBehaviour));
}
public void SetValue(T newValue)
{
T oldVal = Value;
Value = newValue;
if (!Value.Equals(oldVal) && ChangedEvent != null)
{
ChangedEvent.Invoke(Value);
}
OnValueChanged(newValue);
}
public abstract void OnValueChanged(T newValue);
public abstract T GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour);
public abstract OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container);
public abstract string GetHudStringText();
}
}
@@ -0,0 +1,49 @@
using Reactor.Localization.Utilities;
using System.Linq;
using UnityEngine;
namespace MiraAPI.GameOptions.OptionTypes
{
public class ModdedStringOption : ModdedOption<int>
{
public string[] Values { get; private set; }
public ModdedStringOption(string title, int defaultValue, string[] values, System.Type roleType) : base(title, defaultValue, roleType)
{
Values = values;
}
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{
var stringOption = Object.Instantiate(stringOpt, container);
stringOption.name = Title;
stringOption.Title = StringName;
stringOption.Value = Value;
stringOption.Values = Values.Select(CustomStringName.CreateAndRegister).ToArray();
stringOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
stringOption.OnEnable();
OptionBehaviour = stringOption;
return stringOption;
}
public override string GetHudStringText()
{
return Title + ": " + Values[Value];
}
public override int GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
{
return optionBehaviour.GetInt();
}
public override void OnValueChanged(int newValue)
{
if (OptionBehaviour is null) return;
StringOption opt = OptionBehaviour as StringOption;
opt.Value = newValue;
}
}
}
@@ -0,0 +1,42 @@
using UnityEngine;
namespace MiraAPI.GameOptions.OptionTypes
{
public class ModdedToggleOption : ModdedOption<bool>
{
public ModdedToggleOption(string title, bool defaultValue, System.Type roleType) : base(title, defaultValue, roleType)
{
}
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{
var toggleOption = UnityEngine.Object.Instantiate(toggleOpt, container);
toggleOption.name = Title;
toggleOption.Title = StringName;
toggleOption.CheckMark.enabled = Value;
toggleOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
toggleOption.OnEnable();
OptionBehaviour = toggleOption;
return toggleOption;
}
public override string GetHudStringText()
{
return Title + ": " + (Value ? "On" : "Off");
}
public override bool GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
{
return optionBehaviour.GetBool();
}
public override void OnValueChanged(bool newValue)
{
if (OptionBehaviour is null) return;
ToggleOption toggleOpt = OptionBehaviour as ToggleOption;
toggleOpt.CheckMark.enabled = newValue;
}
}
}
@@ -1,23 +1,23 @@
using MiraAPI.GameOptions;
using Reactor.Utilities.Extensions;
using Reactor.Utilities.Extensions;
using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;
namespace MiraAPI.API.GameOptions;
public static class CustomOptionsTab
public static class ModdedOptionsTab
{
public static List<GameObject> CustomTabs;
public static List<GameObject> CustomScreens;
public static List<GameObject> CustomTabs = new List<GameObject>();
public static List<GameObject> CustomScreens = new List<GameObject>();
public static GameObject InitializeForMod(IModOptions modOptions, GameSettingMenu __instance)
public static GameObject InitializeForMod(IMiraConfig config, GameSettingMenu __instance)
{
var gameBtn = __instance.transform.FindChild("Header/Tabs/GameTab").gameObject;
var roleBtn = __instance.transform.FindChild("Header/Tabs/RoleTab").gameObject;
var screen = CreateNewMenu(__instance);
var tab = CreateCustomTab(modOptions, __instance, screen, gameBtn, roleBtn);
var screen = CreateNewMenu(config, __instance);
var tab = CreateCustomTab(config, __instance, screen, gameBtn, roleBtn);
CustomScreens.Add(screen);
CustomTabs.Add(tab);
@@ -34,66 +34,44 @@ public static class CustomOptionsTab
return container.gameObject;
}
/*private static void CreateNewResetButton(GameSettingMenu __instance, Transform container)
{
var resetBtn = __instance.RegularGameSettings.transform.FindChild("GameGroup/SliderInner/ResetToDefault");
var newResetBtn = Object.Instantiate(resetBtn.gameObject, container);
newResetBtn.gameObject.name = "LaunchpadReset";
var tmp = newResetBtn.GetComponentInChildren<TextMeshPro>();
tmp.text = "Reset Options";
tmp.alignment = TextAlignmentOptions.Center;
tmp.gameObject.transform.localPosition = new Vector3(0, 0, 0);
newResetBtn.GetComponent<ToggleOption>().Destroy();
newResetBtn.transform.FindChild("CheckBox").gameObject.Destroy();
var toggle = newResetBtn.GetComponent<PassiveButton>();
toggle.OnClick.RemoveAllListeners();
toggle.OnMouseOver.RemoveAllListeners();
toggle.OnMouseOut.RemoveAllListeners();
toggle.OnClick.AddListener((UnityAction)(() => { CustomOptionsManager.ResetToDefault(); }));
toggle.OnMouseOver.AddListener((UnityAction)(() => { tmp.text = "<b>Reset Options</b>"; }));
toggle.OnMouseOut.AddListener((UnityAction)(() => { tmp.text = "Reset Options"; }));
}*/
private static void UpdateListeners(GameSettingMenu __instance, PassiveButton gameB, PassiveButton roleB, SpriteRenderer rend)
{
gameB.OnClick.RemoveAllListeners();
/* gameB.OnClick.AddListener((UnityAction)(() =>
{
__instance.RegularGameSettings.SetActive(true);
__instance.RolesSettings.gameObject.SetActive(false);
gameB.OnClick.AddListener((UnityAction)(() =>
{
__instance.RegularGameSettings.SetActive(true);
__instance.RolesSettings.gameObject.SetActive(false);
CustomScreen.gameObject.SetActive(false);
rend.enabled = false;
foreach (var screen in CustomScreens) screen.gameObject.SetActive(false);
__instance.GameSettingsHightlight.enabled = true;
__instance.RolesSettingsHightlight.enabled = false;
}));
rend.enabled = false;
roleB.OnClick.RemoveAllListeners();
roleB.OnClick.AddListener((UnityAction)(() =>
{
__instance.RegularGameSettings.SetActive(false);
__instance.RolesSettings.gameObject.SetActive(true);
__instance.GameSettingsHightlight.enabled = true;
__instance.RolesSettingsHightlight.enabled = false;
}));
CustomScreen.gameObject.SetActive(false);
rend.enabled = false;
roleB.OnClick.RemoveAllListeners();
roleB.OnClick.AddListener((UnityAction)(() =>
{
__instance.RegularGameSettings.SetActive(false);
__instance.RolesSettings.gameObject.SetActive(true);
__instance.GameSettingsHightlight.enabled = false;
__instance.RolesSettingsHightlight.enabled = true;
}));*/
foreach (var screen in CustomScreens) screen.gameObject.SetActive(false);
rend.enabled = false;
__instance.GameSettingsHightlight.enabled = false;
__instance.RolesSettingsHightlight.enabled = true;
}));
}
public static GameObject CreateHeader(ToggleOption toggleOpt, Transform container, string title)
public static GameObject CreateHeader(ToggleOption toggleOpt, Transform container, string title, Color color)
{
var header = Object.Instantiate(toggleOpt, container);
header.Title = StringNames.None;
header.TitleText.text = title;
header.TitleText.color = color;
header.name = "Header";
var checkBox = header.transform.FindChild("CheckBox")?.gameObject;
@@ -112,12 +90,14 @@ public static class CustomOptionsTab
return header.gameObject;
}
public static GameObject CreateCustomTab(IModOptions options, GameSettingMenu __instance, GameObject newSettings,
public static GameObject CreateCustomTab(IMiraConfig options, GameSettingMenu __instance, GameObject newSettings,
GameObject gameTab, GameObject roleTab)
{
var newTab = Object.Instantiate(gameTab, gameTab.transform.parent);
newTab.name = "Tab";
gameTab.transform.position += new Vector3(-1, 0, 0);
newTab.transform.position = roleTab.transform.position;
newTab.transform.position += new Vector3(CustomTabs.Count + 1, 0, 0);
__instance.Tabs.transform.position -= new Vector3(0.5f, 0, 0);
var inside = newTab.transform.FindChild("ColorButton");
inside.name = "Btn";
@@ -129,8 +109,7 @@ public static class CustomOptionsTab
btn.OnClick.AddListener((Action)TabAction);
var spriteRend = inside.GetComponentInChildren<SpriteRenderer>();
spriteRend.sprite = options.OptionsTabIcon.LoadAsset();
spriteRend.gameObject.transform.localScale = new Vector3(0.4f, 0.4f, 0.4f);
// if (options.TabSettings.TabIcon != null) spriteRend.sprite = options.TabSettings.TabIcon.LoadAsset();
return newTab;
@@ -143,10 +122,9 @@ public static class CustomOptionsTab
{
screen.gameObject.SetActive(false);
}
newSettings.gameObject.SetActive(true);
var rend = inside.transform.FindChild("Tab Background").GetComponent<SpriteRenderer>();
SpriteRenderer rend = newTab.transform.FindChild("Btn/Tab Background").GetComponent<SpriteRenderer>();
rend.enabled = true;
gameTab.transform.FindChild("ColorButton/Tab Background").gameObject.GetComponent<SpriteRenderer>().enabled = false;
@@ -159,7 +137,7 @@ public static class CustomOptionsTab
}
}
public static GameObject CreateNewMenu(GameSettingMenu __instance)
public static GameObject CreateNewMenu(IMiraConfig config, GameSettingMenu __instance)
{
var gameSettings = __instance.RegularGameSettings;
var newSettings = Object.Instantiate(gameSettings, gameSettings.transform.parent);
@@ -169,7 +147,7 @@ public static class CustomOptionsTab
var launchpadGroup = newSettings.transform.FindChild("GameGroup").gameObject;
var text = launchpadGroup.transform.FindChild("Text").gameObject.GetComponent<TextMeshPro>();
text.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
text.text = "Settings";
text.text = $"{config.TabSettings.Title} Options";
return newSettings;
}
+16
View File
@@ -0,0 +1,16 @@
using MiraAPI.Utilities;
using UnityEngine;
namespace MiraAPI
{
public interface IMiraConfig
{
public ModdedOptionTabSettings TabSettings { get; }
}
public struct ModdedOptionTabSettings(string title, LoadableAsset<Sprite> tabIcon)
{
public string Title { get; set; } = title;
public LoadableAsset<Sprite> TabIcon { get; set; } = tabIcon;
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion>
+2 -2
View File
@@ -2,7 +2,7 @@
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.GameModes;
using MiraAPI.GameOptions.Attributes;
using MiraAPI.GameOptions;
using MiraAPI.Hud;
using MiraAPI.Roles;
using Reactor;
@@ -27,7 +27,7 @@ public partial class MiraAPIPlugin : BasePlugin
Harmony.PatchAll();
RegisterCustomRoleAttribute.Initialize();
RegisterCustomOptionsAttribute.Initialize();
ModdedOptionsManager.Initialize();
RegisterGameModeAttribute.Register(Assembly.GetCallingAssembly());
RegisterButtonAttribute.Register(Assembly.GetExecutingAssembly());
+1 -2
View File
@@ -1,5 +1,4 @@
using Hazel;
using MiraAPI.API.GameOptions;
using Reactor.Networking.Attributes;
using Reactor.Networking.Rpc;
@@ -77,6 +76,6 @@ public class SyncOptionsRpc(MiraAPIPlugin plugin, uint id) : PlayerCustomRpc<Mir
return;
}
CustomOptionsManager.HandleOptionsSync(data.Toggles, data.Numbers, data.StringIDs);
//CustomOptionsManager.HandleOptionsSync(data.Toggles, data.Numbers, data.StringIDs);
}
}
+4 -3
View File
@@ -1,6 +1,6 @@
using HarmonyLib;
using InnerNet;
using MiraAPI.API.GameOptions;
using MiraAPI.GameOptions;
using MiraAPI.Hud;
using MiraAPI.Patches.Options;
using MiraAPI.Roles;
@@ -31,7 +31,7 @@ namespace MiraAPI.Patches
{
if (Input.GetKeyDown(KeyCode.Tab))
{
if (ToHudStringPatch.CurrentPage >= CustomOptionsManager.RegisteredMods.Count)
if (ToHudStringPatch.CurrentPage >= ModdedOptionsManager.RegisteredMods.Count)
{
ToHudStringPatch.CurrentPage = 0;
}
@@ -85,6 +85,7 @@ namespace MiraAPI.Patches
}
//CustomGameModeManager.ActiveMode.HudUpdate(__instance);
if (local.Data.Role is null) return;
if (local.Data.Role is ICustomRole customRole)
{
@@ -181,4 +182,4 @@ namespace MiraAPI.Patches
}
}
}
}
}
+13 -13
View File
@@ -1,6 +1,6 @@
using HarmonyLib;
using Il2CppSystem;
using MiraAPI.API.GameOptions;
using MiraAPI.GameOptions;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
@@ -16,7 +16,7 @@ public static class GameOptionsMenuPatch
[HarmonyPostfix, HarmonyPatch("Start")]
public static void StartPostfix(GameOptionsMenu __instance)
{
foreach (var customOption in CustomOptionsManager.CustomOptions)
foreach (var customOption in ModdedOptionsManager.Options)
{
if (customOption.AdvancedRole is not null || !customOption.OptionBehaviour)
{
@@ -46,17 +46,17 @@ public static class GameOptionsMenuPatch
return;
}
var startOffset = 2.15f;
var startOffset = 2.5f;
__instance.GetComponentInParent<Scroller>().ContentYBounds.max = startOffset + __instance.Children.Count * 0.5f;
foreach (var option in CustomOptionsManager.CustomOptions.Where(option => option.Group == null))
foreach (var option in ModdedOptionsManager.Options.Where(option => option.Group == null))
{
if (!option.OptionBehaviour) continue;
option.OptionBehaviour.enabled = !option.Hidden();
option.OptionBehaviour.gameObject.SetActive(!option.Hidden());
option.OptionBehaviour.enabled = option.Visible();
option.OptionBehaviour.gameObject.SetActive(option.Visible());
if (!option.Hidden())
if (option.Visible())
{
startOffset -= 0.55f;
}
@@ -66,16 +66,16 @@ public static class GameOptionsMenuPatch
transform.localPosition = new Vector3(optionPosition.x, startOffset, optionPosition.z);
}
foreach (var group in CustomOptionsManager.CustomGroups.Where(group => group.AdvancedRole == null))
foreach (var group in ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null))
{
if (group.Header == null)
{
continue;
}
group.Header.SetActive(!group.Hidden());
group.Header.SetActive(group.GroupVisible());
if (!group.Hidden())
if (group.GroupVisible())
{
startOffset -= 0.5f;
}
@@ -83,19 +83,19 @@ public static class GameOptionsMenuPatch
var position = group.Header.transform.localPosition;
group.Header.transform.localPosition = new Vector3(position.x, startOffset, position.z);
foreach (var option in group.Options)
foreach (var option in ModdedOptionsManager.Options.Where(x => x.Group == group))
{
if (!option.OptionBehaviour)
{
continue;
}
var enabled = !group.Hidden() && !option.Hidden();
var enabled = group.GroupVisible() && option.Visible();
option.OptionBehaviour.enabled = enabled;
option.OptionBehaviour.gameObject.SetActive(enabled);
if (!group.Hidden() && !option.Hidden())
if (group.GroupVisible() && option.Visible())
{
startOffset -= 0.5f;
}
@@ -1,7 +1,8 @@
using HarmonyLib;
using HarmonyLib;
using MiraAPI.API.GameOptions;
using MiraAPI.GameModes;
using MiraAPI.GameOptions;
using Reactor.Utilities;
using Reactor.Utilities.Extensions;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
@@ -18,12 +19,11 @@ public static class GameSettingsMenuPatches
[HarmonyPrefix, HarmonyPatch("Start")]
public static void StartPrefix(GameSettingMenu __instance)
{
if (CustomOptionsTab.CustomTabs != null)
{
return;
}
foreach (var tab in ModdedOptionsTab.CustomTabs) tab.gameObject.Destroy();
foreach (var screen in ModdedOptionsTab.CustomScreens) screen.gameObject.Destroy();
CustomOptionsTab.CustomTabs = new();
ModdedOptionsTab.CustomScreens.Clear();
ModdedOptionsTab.CustomTabs.Clear();
_gameBtn = __instance.transform.FindChild("Header/Tabs/GameTab").gameObject;
_roleBtn = __instance.transform.FindChild("Header/Tabs/RoleTab").gameObject;
@@ -32,36 +32,17 @@ public static class GameSettingsMenuPatches
var toggleOpt = Object.FindObjectOfType<ToggleOption>();
var stringOpt = __instance.RegularGameSettings.GetComponentInChildren<StringOption>();
foreach (var pair in CustomOptionsManager.RegisteredMods)
foreach (var pair in ModdedOptionsManager.RegisteredMods)
{
var container = CustomOptionsTab.InitializeForMod(pair.Value, __instance).transform;
var container = ModdedOptionsTab.InitializeForMod(pair.Value, __instance).transform;
foreach (var group in CustomOptionsManager.CustomGroups.Where(group => group.AdvancedRole == null && group.ParentMod == pair.Key))
foreach (var group in ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null && group.ParentMod == pair.Value))
{
if (GameManager.Instance.IsNormal())
{
group.Header = CustomOptionsTab.CreateHeader(toggleOpt, container, group.Title);
CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container,
group.CustomToggleOptions, group.CustomNumberOptions, group.CustomStringOptions);
continue;
}
if (!group.Options.Any(x => x.ShowInHideNSeek))
{
continue;
}
group.Header = CustomOptionsTab.CreateHeader(toggleOpt, container, group.Title);
__instance.AllHideAndSeekItems = __instance.AllHideAndSeekItems.Append(group.Header.transform).ToArray();
CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container,
group.CustomToggleOptions, group.CustomNumberOptions, group.CustomStringOptions);
group.Header = ModdedOptionsTab.CreateHeader(toggleOpt, container, group.GroupName, group.GroupColor);
CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList());
}
CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container,
CustomOptionsManager.CustomToggleOptions.Where(option => option.Group == null && option.ParentMod == pair.Key),
CustomOptionsManager.CustomNumberOptions.Where(option => option.Group == null && option.ParentMod == pair.Key),
CustomOptionsManager.CustomStringOptions.Where(option => option.Group == null && option.ParentMod == pair.Key));
CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container, ModdedOptionsManager.Options.Where(x => x.Group is null).ToList());
}
if (!numberOpt || !toggleOpt || !stringOpt)
@@ -70,106 +51,18 @@ public static class GameSettingsMenuPatches
}
}
[HarmonyPostfix, HarmonyPatch("Update")]
public static void UpdatePatch(GameSettingMenu __instance)
{
if (CustomGameModeManager.ActiveMode == null)
{
return;
}
_gameBtn.SetActive(CustomGameModeManager.ActiveMode.CanAccessSettingsTab());
_roleBtn.SetActive(CustomGameModeManager.ActiveMode.CanAccessRolesTab());
if (!CustomGameModeManager.ActiveMode.CanAccessSettingsTab())
{
__instance.RegularGameSettings.SetActive(false);
__instance.RolesSettings.gameObject.SetActive(false);
//CustomOptionsTab.CustomScreen.gameObject.SetActive(true);
__instance.GameSettingsHightlight.enabled = false;
__instance.RolesSettingsHightlight.enabled = false;
}
}
public static void CreateOptionsFor(GameSettingMenu __instance, ToggleOption togglePrefab, NumberOption numberPrefab, StringOption stringPrefab, Transform container,
IEnumerable<CustomToggleOption> toggles, IEnumerable<CustomNumberOption> numbers, IEnumerable<CustomStringOption> strings)
List<IModdedOption> options)
{
foreach (var customToggleOption in toggles)
foreach (IModdedOption option in options)
{
if (customToggleOption.AdvancedRole is not null || customToggleOption.OptionBehaviour)
if (option.AdvancedRole is not null || option.OptionBehaviour)
{
continue;
}
ToggleOption toggleOption;
if (GameManager.Instance.IsNormal())
{
toggleOption = customToggleOption.CreateToggleOption(togglePrefab, container);
__instance.AllItems = __instance.AllItems.AddItem(toggleOption.transform).ToArray();
continue;
}
if (!customToggleOption.ShowInHideNSeek)
{
continue;
}
toggleOption = customToggleOption.CreateToggleOption(togglePrefab, container);
__instance.AllHideAndSeekItems = __instance.AllHideAndSeekItems.AddItem(toggleOption.transform).ToArray();
}
foreach (var customNumberOption in numbers)
{
if (customNumberOption.AdvancedRole is not null || customNumberOption.OptionBehaviour)
{
continue;
}
NumberOption numberOption;
if (GameManager.Instance.IsNormal())
{
numberOption = customNumberOption.CreateNumberOption(numberPrefab, container);
__instance.AllItems = __instance.AllItems.AddItem(numberOption.transform).ToArray();
continue;
}
if (!customNumberOption.ShowInHideNSeek)
{
continue;
}
numberOption = customNumberOption.CreateNumberOption(numberPrefab, container);
__instance.AllHideAndSeekItems = __instance.AllHideAndSeekItems.AddItem(numberOption.transform).ToArray();
}
foreach (var customStringOption in strings)
{
if (customStringOption.AdvancedRole is not null || customStringOption.OptionBehaviour)
{
continue;
}
StringOption stringOption;
if (GameManager.Instance.IsNormal())
{
stringOption = customStringOption.CreateStringOption(stringPrefab, container);
__instance.AllItems = __instance.AllItems.AddItem(stringOption.transform).ToArray();
continue;
}
if (!customStringOption.ShowInHideNSeek)
{
continue;
}
stringOption = customStringOption.CreateStringOption(stringPrefab, container);
__instance.AllHideAndSeekItems = __instance.AllHideAndSeekItems.AddItem(stringOption.transform).ToArray();
OptionBehaviour newOption = option.CreateOption(togglePrefab, numberPrefab, stringPrefab, container);
__instance.AllItems = __instance.AllItems.AddItem(newOption.transform).ToArray();
}
}
}
}
+118
View File
@@ -0,0 +1,118 @@
using AmongUs.GameOptions;
using HarmonyLib;
using MiraAPI.GameOptions;
using MiraAPI.Roles;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(IGameOptionsExtensions), "ToHudString")]
public static class ToHudStringPatch
{
public static int CurrentPage = 0;
/// <summary>
/// Generic method to add options
/// </summary>
public static void AddOptions(StringBuilder sb,
List<IModdedOption> options)
{
foreach (var opt in options)
{
sb.AppendLine(opt.GetHudStringText());
}
}
public static void Prefix()
{
if (GameManager.Instance is null || GameManager.Instance.IsHideAndSeek())
{
return;
}
foreach (var role in CustomRoleManager.CustomRoles.Values)
{
var customRole = role as ICustomRole;
if (customRole.IsGhostRole)
{
role.Role = RoleTypes.CrewmateGhost;
}
}
}
/// <summary>
/// Update the HudOptions on the left of the screen if player is using Launchpad options
/// </summary>
public static void Postfix(IGameOptions __instance, ref string __result)
{
if (GameManager.Instance is null)
{
return;
}
foreach (var pair in CustomRoleManager.CustomRoles)
{
var customRole = pair.Value as ICustomRole;
if (customRole.IsGhostRole)
{
pair.Value.Role = (RoleTypes)pair.Key;
}
}
if (CurrentPage != 0)
{
IMiraConfig currentPlugin = ModdedOptionsManager.RegisteredMods.Values.ToArray()[CurrentPage - 1];
if (currentPlugin == null)
{
CurrentPage = 0;
return;
}
var sb = new StringBuilder($"<size=180%><b>{currentPlugin.TabSettings.Title} Options:</b></size>\n<size=130%>");
var groupsWithRoles = ModdedOptionsManager.Groups.Where(group => group.AdvancedRole != null && group.ParentMod == currentPlugin);
var groupsWithoutRoles = ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null && group.ParentMod == currentPlugin);
AddOptions(sb,
ModdedOptionsManager.Options.Where(option => option.Group == null && option.ParentMod == currentPlugin && option.Visible()).ToList()
);
foreach (var group in groupsWithoutRoles)
{
if (!group.GroupVisible())
{
continue;
}
sb.AppendLine($"\n<size=160%><b>{group.GroupColor.ToTextColor()}{group.GroupName}</color></b></size>");
AddOptions(sb, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList());
}
var customOptionGroups = groupsWithRoles as ModdedOptionGroup[] ?? groupsWithRoles.ToArray();
if (customOptionGroups.Any())
{
sb.AppendLine("\n<size=160%><b>Roles</b></size>");
foreach (var group in customOptionGroups)
{
if (!group.GroupVisible())
{
continue;
}
sb.AppendLine($"<size=140%><b>{group.GroupColor.ToTextColor()}{group.GroupName}</color></b></size><size=120%>");
AddOptions(sb, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList());
sb.Append("</size>\n");
}
}
__result = sb.ToString();
}
if (CurrentPage == 0)
{
__result = "<size=160%><b>Normal Options:</b></size>\n<size=130%>" + __result;
}
__result += $"\nUse <b>TAB</b> to switch pages (Page {CurrentPage}/{ModdedOptionsManager.RegisteredMods.Count})</size>";
}
}
-147
View File
@@ -1,147 +0,0 @@
using AmongUs.GameOptions;
using BepInEx;
using HarmonyLib;
using MiraAPI.API.GameOptions;
using MiraAPI.Roles;
using MiraAPI.Utilities;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(IGameOptionsExtensions), "ToHudString")]
public static class ToHudStringPatch
{
public static int CurrentPage = 0;
/// <summary>
/// Generic method to add options
/// </summary>
public static void AddOptions(StringBuilder sb,
IEnumerable<CustomNumberOption> numberOptions, IEnumerable<CustomStringOption> stringOptions, IEnumerable<CustomToggleOption> toggleOptions)
{
foreach (var numberOption in numberOptions.Where(x => !x.Hidden()))
{
if (GameManager.Instance.IsHideAndSeek() && !numberOption.ShowInHideNSeek)
{
continue;
}
sb.AppendLine(numberOption.Title + ": " + numberOption.Value + Helpers.GetSuffix(numberOption.SuffixType));
}
foreach (var toggleOption in toggleOptions.Where(x => !x.Hidden()))
{
if (GameManager.Instance.IsHideAndSeek() && !toggleOption.ShowInHideNSeek)
{
continue;
}
sb.AppendLine(toggleOption.Title + ": " + (toggleOption.Value ? "On" : "Off"));
}
foreach (var stringOption in stringOptions.Where(x => !x.Hidden()))
{
if (GameManager.Instance.IsHideAndSeek() && !stringOption.ShowInHideNSeek)
{
continue;
}
sb.AppendLine(stringOption.Title + ": " + stringOption.Value);
}
}
public static void Prefix()
{
if (GameManager.Instance is null || GameManager.Instance.IsHideAndSeek())
{
return;
}
foreach (var role in CustomRoleManager.CustomRoles.Values)
{
var customRole = role as ICustomRole;
if (customRole.IsGhostRole)
{
role.Role = RoleTypes.CrewmateGhost;
}
}
}
/// <summary>
/// Update the HudOptions on the left of the screen if player is using Launchpad options
/// </summary>
public static void Postfix(IGameOptions __instance, ref string __result)
{
if (GameManager.Instance is null)
{
return;
}
foreach (var pair in CustomRoleManager.CustomRoles)
{
var customRole = pair.Value as ICustomRole;
if (customRole.IsGhostRole)
{
pair.Value.Role = (RoleTypes)pair.Key;
}
}
if (CurrentPage != 0)
{
PluginInfo currentPlugin = CustomOptionsManager.RegisteredMods.Keys.ToArray()[CurrentPage - 1];
if (currentPlugin == null)
{
CurrentPage = 0;
return;
}
var sb = new StringBuilder($"<size=180%><b>{currentPlugin.Metadata.Name} Options:</b></size>\n<size=130%>");
var groupsWithRoles = CustomOptionsManager.CustomGroups.Where(group => group.AdvancedRole != null && group.ParentMod == currentPlugin);
var groupsWithoutRoles = CustomOptionsManager.CustomGroups.Where(group => group.AdvancedRole == null && group.ParentMod == currentPlugin);
AddOptions(sb,
CustomOptionsManager.CustomNumberOptions.Where(option => option.Group == null && option.ParentMod == currentPlugin && !option.Hidden()),
CustomOptionsManager.CustomStringOptions.Where(option => option.Group == null && option.ParentMod == currentPlugin && !option.Hidden()),
CustomOptionsManager.CustomToggleOptions.Where(option => option.Group == null && option.ParentMod == currentPlugin && !option.Hidden())
);
foreach (var group in groupsWithoutRoles)
{
if (group.Hidden() || (GameManager.Instance.IsHideAndSeek() && !group.Options.Any(x => x.ShowInHideNSeek)))
{
continue;
}
sb.AppendLine($"\n<size=160%><b>{group.Title}</b></size>");
AddOptions(sb, group.CustomNumberOptions, group.CustomStringOptions, group.CustomToggleOptions);
}
var customOptionGroups = groupsWithRoles as CustomOptionGroup[] ?? groupsWithRoles.ToArray();
if (customOptionGroups.Any())
{
sb.AppendLine("\n<size=160%><b>Roles</b></size>");
foreach (var group in customOptionGroups)
{
if (group.Hidden())
{
continue;
}
sb.AppendLine($"<size=140%><b>{group.Title}</b></size><size=120%>");
AddOptions(sb, group.CustomNumberOptions, group.CustomStringOptions, group.CustomToggleOptions);
sb.Append("</size>\n");
}
}
__result = sb.ToString();
}
if (CurrentPage == 0)
{
__result = "<size=160%><b>Normal Options:</b></size>\n<size=130%>" + __result;
}
__result += $"\nUse <b>TAB</b> to switch pages (Page {CurrentPage}/{CustomOptionsManager.RegisteredMods.Count})</size>";
}
}
@@ -1,8 +1,6 @@
using AmongUs.GameOptions;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppSystem;
using MiraAPI.API.GameOptions;
using MiraAPI.Networking.Options;
using MiraAPI.Roles;
using Reactor.Networking.Rpc;
@@ -54,26 +52,26 @@ public static class RolesSettingsMenuPatches
var startOffset = 0.5f;
foreach (var customOption in CustomOptionsManager.CustomOptions)
{
if (customOption.AdvancedRole is not null && customOption.AdvancedRole == role.GetType())
{
startOffset -= 0.5f;
/* foreach (var customOption in CustomOptionsManager.CustomOptions)
{
if (customOption.AdvancedRole is not null && customOption.AdvancedRole == role.GetType())
{
startOffset -= 0.5f;
switch (customOption)
{
case CustomNumberOption numberOption:
var numOpt = numberOption.CreateNumberOption(numberSet, newTab.transform);
numOpt.transform.localPosition = new Vector3(-1.25f, startOffset, 0);
break;
switch (customOption)
{
case CustomNumberOption numberOption:
var numOpt = numberOption.CreateNumberOption(numberSet, newTab.transform);
numOpt.transform.localPosition = new Vector3(-1.25f, startOffset, 0);
break;
case CustomToggleOption toggleOption:
var togOpt = toggleOption.CreateToggleOption(toggleSet, newTab.transform);
togOpt.transform.localPosition = new Vector3(-1.25f, startOffset, 0);
break;
}
}
}
case CustomToggleOption toggleOption:
var togOpt = toggleOption.CreateToggleOption(toggleSet, newTab.transform);
togOpt.transform.localPosition = new Vector3(-1.25f, startOffset, 0);
break;
}
}
}*/
var tmp = newTab.GetComponentInChildren<TextTranslatorTMP>();
tmp.defaultStr = role.NiceName;
@@ -98,13 +96,13 @@ public static class RolesSettingsMenuPatches
[HarmonyPostfix, HarmonyPatch("Start")]
public static void StartPostfix(RolesSettingsMenu __instance)
{
foreach (var customOption in CustomOptionsManager.CustomOptions)
{
if (customOption.AdvancedRole is not null)
{
customOption.OptionBehaviour.OnValueChanged = (Action<OptionBehaviour>)customOption.ValueChanged;
}
}
/* foreach (var customOption in CustomOptionsManager.CustomOptions)
{
if (customOption.AdvancedRole is not null)
{
customOption.OptionBehaviour.OnValueChanged = (Action<OptionBehaviour>)customOption.ValueChanged;
}
}*/
var scroll = __instance.GetComponentInChildren<Scroller>();
scroll.active = true;
+1 -1
View File
@@ -46,7 +46,7 @@ public static class TaskAdderPatch
{
if (__instance.role is ICustomRole lpRole)
{
if (lpRole.IsOutcast)
if (lpRole.IsNeutral)
{
__instance.FileImage.color = Color.gray;
}
+1 -1
View File
@@ -58,7 +58,7 @@ public static class CustomRoleManager
}
roleBehaviour.Role = (RoleTypes)roleId;
roleBehaviour.TeamType = customRole.Team;
roleBehaviour.TeamType = customRole.Team == ModdedRoleTeams.Neutral ? RoleTeamTypes.Crewmate : (RoleTeamTypes)customRole.Team;
roleBehaviour.NameColor = customRole.RoleColor;
roleBehaviour.StringName = CustomStringName.CreateAndRegister(customRole.RoleName);
roleBehaviour.BlurbName = CustomStringName.CreateAndRegister(customRole.RoleDescription);
+9 -9
View File
@@ -17,7 +17,7 @@ public interface ICustomRole
Color RoleColor { get; }
RoleTeamTypes Team { get; }
ModdedRoleTeams Team { get; }
LoadableAsset<Sprite> Icon => MiraAssets.Empty;
@@ -25,17 +25,17 @@ public interface ICustomRole
ConfigDefinition ChanceConfigDefinition => new("Roles", $"Chance{RoleName}");
bool AffectedByLight => Team == RoleTeamTypes.Crewmate;
bool AffectedByLight => Team == ModdedRoleTeams.Crewmate;
bool CanGetKilled => Team == RoleTeamTypes.Crewmate;
bool CanGetKilled => Team == ModdedRoleTeams.Crewmate;
bool IsOutcast => false;
bool IsNeutral => Team == ModdedRoleTeams.Neutral;
bool CanKill => Team == RoleTeamTypes.Impostor;
bool CanKill => Team == ModdedRoleTeams.Impostor;
bool CanUseVent => Team == RoleTeamTypes.Impostor;
bool CanUseVent => Team == ModdedRoleTeams.Impostor;
bool TasksCount => Team == RoleTeamTypes.Crewmate;
bool TasksCount => Team == ModdedRoleTeams.Crewmate;
bool IsGhostRole => false;
@@ -45,7 +45,7 @@ public interface ICustomRole
bool HideSettings => IsGhostRole;
RoleTypes GhostRole => Team == RoleTeamTypes.Crewmate ? RoleTypes.CrewmateGhost : RoleTypes.ImpostorGhost;
RoleTypes GhostRole => Team == ModdedRoleTeams.Crewmate ? RoleTypes.CrewmateGhost : RoleTypes.ImpostorGhost;
void CreateOptions() { }
@@ -55,7 +55,7 @@ public interface ICustomRole
string GetCustomEjectionMessage(GameData.PlayerInfo player)
{
return Team == RoleTeamTypes.Impostor ? $"{player.PlayerName} was The {RoleName}" : null;
return Team == ModdedRoleTeams.Impostor ? $"{player.PlayerName} was The {RoleName}" : null;
}
StringBuilder SetTabText()
+9
View File
@@ -0,0 +1,9 @@
namespace MiraAPI.Roles
{
public enum ModdedRoleTeams
{
Crewmate,
Impostor,
Neutral
}
}
@@ -1,5 +1,4 @@
using System;
using UnityEngine;
using UnityEngine;
namespace MiraAPI.Utilities.Assets
{
@@ -12,8 +11,15 @@ namespace MiraAPI.Utilities.Assets
return _loadedAsset;
}
return _loadedAsset = SpriteTools.LoadSpriteFromPath(resourcesFolder + name);
throw new Exception($"INVALID ASSET: {name}");
try
{
return _loadedAsset = SpriteTools.LoadSpriteFromPath(resourcesFolder + name);
}
catch
{
Debug.LogError($"Not loading, invalid asset: {name}");
return null;
}
}
}
}