mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-20 01:46:15 +00:00
Attribute based options API (wip)
This commit is contained in:
1 parent
4155b10ac6
commit
947a202c55
27 files changed
+1024
-118
No files matched your search
@@ -1,9 +0,0 @@
|
|||||||
using MiraAPI.GameOptions.Attributes;
|
|
||||||
|
|
||||||
namespace MiraAPI.Example
|
|
||||||
{
|
|
||||||
public class ExOptions
|
|
||||||
{
|
|
||||||
[RegisterModdedOption("Use Thing")] public bool useThing = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using MiraAPI.GameOptions;
|
||||||
|
using MiraAPI.GameOptions.Attributes;
|
||||||
|
|
||||||
|
namespace MiraAPI.Example
|
||||||
|
{
|
||||||
|
public class ExampleOptions : IModdedOptionGroup
|
||||||
|
{
|
||||||
|
public string GroupName => "General Group";
|
||||||
|
[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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using BepInEx;
|
using BepInEx;
|
||||||
using BepInEx.Unity.IL2CPP;
|
using BepInEx.Unity.IL2CPP;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
|
using MiraAPI.Utilities.Assets;
|
||||||
using Reactor;
|
using Reactor;
|
||||||
using Reactor.Networking;
|
using Reactor.Networking;
|
||||||
using Reactor.Networking.Attributes;
|
using Reactor.Networking.Attributes;
|
||||||
@@ -11,14 +12,19 @@ namespace MiraAPI.Example;
|
|||||||
[BepInProcess("Among Us.exe")]
|
[BepInProcess("Among Us.exe")]
|
||||||
[BepInDependency(ReactorPlugin.Id)]
|
[BepInDependency(ReactorPlugin.Id)]
|
||||||
[ReactorModFlags(ModFlags.RequireOnAllClients)]
|
[ReactorModFlags(ModFlags.RequireOnAllClients)]
|
||||||
public partial class ExamplePlugin : BasePlugin
|
public partial class ExamplePlugin : BasePlugin, IMiraConfig
|
||||||
{
|
{
|
||||||
public Harmony Harmony { get; } = new(Id);
|
public Harmony Harmony { get; } = new(Id);
|
||||||
public static ExamplePlugin Instance { get; private set; }
|
public static ExamplePlugin Instance { get; private set; }
|
||||||
|
public ModdedOptionTabSettings TabSettings => new ModdedOptionTabSettings()
|
||||||
|
{
|
||||||
|
Title = "Example Mod",
|
||||||
|
TabIcon = MiraAssets.Empty
|
||||||
|
};
|
||||||
|
|
||||||
public override void Load()
|
public override void Load()
|
||||||
{
|
{
|
||||||
Instance = this;
|
Instance = this;
|
||||||
Harmony.PatchAll();
|
Harmony.PatchAll();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,33 @@
|
|||||||
|
using MiraAPI.GameOptions.OptionTypes;
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Debug.LogError(value.ToString());
|
||||||
|
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,41 +0,0 @@
|
|||||||
using BepInEx.Unity.IL2CPP;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace MiraAPI.GameOptions.Attributes
|
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
|
||||||
public class RegisterModdedOptionAttribute : Attribute
|
|
||||||
{
|
|
||||||
private static readonly HashSet<Assembly> RegisteredAssemblies = [];
|
|
||||||
|
|
||||||
public string Title { get; private set; }
|
|
||||||
public RegisterModdedOptionAttribute(string title)
|
|
||||||
{
|
|
||||||
Title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
|
||||||
{
|
|
||||||
foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public))
|
|
||||||
{
|
|
||||||
var attribute = property.GetCustomAttribute<RegisterModdedOptionAttribute>();
|
|
||||||
if (attribute != null)
|
|
||||||
{
|
|
||||||
ModdedOptionsManager.Instance.CreateOption(type, attribute, property);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,19 @@
|
|||||||
namespace MiraAPI.GameOptions
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace MiraAPI.GameOptions
|
||||||
{
|
{
|
||||||
public interface IModdedOption
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using HarmonyLib;
|
using BepInEx.Unity.IL2CPP;
|
||||||
|
using HarmonyLib;
|
||||||
using MiraAPI.GameOptions.Attributes;
|
using MiraAPI.GameOptions.Attributes;
|
||||||
using MiraAPI.GameOptions.OptionTypes;
|
|
||||||
using Reactor.Utilities;
|
using Reactor.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -12,62 +12,101 @@ namespace MiraAPI.GameOptions
|
|||||||
{
|
{
|
||||||
public class ModdedOptionsManager
|
public class ModdedOptionsManager
|
||||||
{
|
{
|
||||||
public static ModdedOptionsManager Instance;
|
public static List<IModdedOption> Options = new();
|
||||||
public List<IModdedOption> Options = new List<IModdedOption>();
|
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();
|
||||||
|
|
||||||
private Dictionary<PropertyInfo, IModdedOption> PatchedOptions = new Dictionary<PropertyInfo, IModdedOption>();
|
public static void Initialize()
|
||||||
|
|
||||||
public void CreateOption(Type type, RegisterModdedOptionAttribute attribute, PropertyInfo property)
|
|
||||||
{
|
{
|
||||||
Debug.LogError(property.GetConstantValue().GetType().ToString());
|
IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) =>
|
||||||
|
|
||||||
switch (property.GetConstantValue())
|
|
||||||
{
|
{
|
||||||
case bool:
|
if (plugin.GetType().GetInterfaces().Contains(typeof(IMiraConfig)))
|
||||||
CreateToggleOption(type, attribute, property);
|
{
|
||||||
break;
|
IMiraConfig config = (IMiraConfig)Activator.CreateInstance(plugin.GetType());
|
||||||
default:
|
RegisteredMods.Add(assembly, config);
|
||||||
Debug.LogError($"Mira API does not support type {property.GetConstantValue().GetType()}");
|
}
|
||||||
break;
|
|
||||||
}
|
RegisterOptionGroups(assembly);
|
||||||
|
|
||||||
|
ModdedOptionAttribute.Register(assembly);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void PropertySetterPatch(MethodBase __originalMethod, object[] args)
|
private static void RegisterOptionGroups(Assembly assembly)
|
||||||
{
|
{
|
||||||
IModdedOption opt = Instance.PatchedOptions.First(pair => pair.Key.GetSetMethod().Equals(__originalMethod)).Value;
|
foreach (var type in assembly.GetTypes())
|
||||||
|
|
||||||
if (opt != null)
|
|
||||||
{
|
{
|
||||||
object value = args[0];
|
if (typeof(IModdedOptionGroup).IsAssignableFrom(type))
|
||||||
switch (value)
|
|
||||||
{
|
{
|
||||||
case bool:
|
IModdedOptionGroup group = (IModdedOptionGroup)Activator.CreateInstance(type);
|
||||||
ModdedToggleOption toggleOpt = opt as ModdedToggleOption;
|
ModdedOptionGroup newGroup = new ModdedOptionGroup()
|
||||||
toggleOpt.SetValue((bool)value);
|
{
|
||||||
break;
|
AdvancedRole = group.AdvancedRole,
|
||||||
|
GroupColor = group.GroupColor,
|
||||||
|
GroupName = group.GroupName,
|
||||||
|
GroupVisible = group.GroupVisible
|
||||||
|
};
|
||||||
|
|
||||||
|
Groups.Add(newGroup);
|
||||||
|
OriginalTypes.Add(type, newGroup);
|
||||||
|
|
||||||
|
newGroup.ParentMod = RegisteredMods[assembly];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModdedToggleOption CreateToggleOption(Type type, RegisterModdedOptionAttribute attribute, PropertyInfo property)
|
public static void RegisterOption(Assembly assembly, Type type, ModdedOptionAttribute attribute, PropertyInfo property)
|
||||||
{
|
{
|
||||||
var toggleOpt = new ModdedToggleOption(attribute.Title, (bool)property.GetValue(type));
|
if (OptionAttributes.ContainsKey(property)) return;
|
||||||
Options.Add(toggleOpt);
|
object newObj = Activator.CreateInstance(type);
|
||||||
|
IModdedOption result = attribute.CreateOption(property.GetValue(newObj), property);
|
||||||
|
|
||||||
var setterOriginal = property.GetSetMethod();
|
if (result != null)
|
||||||
var setterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertySetterPatch));
|
{
|
||||||
PluginSingleton<MiraAPIPlugin>.Instance.Harmony.Patch(setterOriginal, postfix: new HarmonyMethod(setterPatch));
|
var setterOriginal = property.GetSetMethod();
|
||||||
/*
|
var setterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertySetterPatch));
|
||||||
var getterOriginal = property.GetGetMethod();
|
PluginSingleton<MiraAPIPlugin>.Instance.Harmony.Patch(setterOriginal, postfix: new HarmonyMethod(setterPatch));
|
||||||
var getterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertyGetterPatch));
|
|
||||||
PluginSingleton<MiraAPIPlugin>.Instance.Harmony.Patch(getterOriginal, prefix: new HarmonyMethod(getterPatch));*/
|
|
||||||
|
|
||||||
toggleOpt.PropertyInfo = property;
|
var getterOriginal = property.GetGetMethod();
|
||||||
|
var getterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertyGetterPatch));
|
||||||
|
PluginSingleton<MiraAPIPlugin>.Instance.Harmony.Patch(getterOriginal, prefix: new HarmonyMethod(getterPatch));
|
||||||
|
|
||||||
if (!PatchedOptions.ContainsKey(property))
|
attribute.HolderOption = result;
|
||||||
PatchedOptions.Add(property, toggleOpt);
|
result.ParentMod = RegisteredMods[assembly];
|
||||||
|
|
||||||
return toggleOpt;
|
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,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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Reactor.Localization.Utilities;
|
using MiraAPI.Roles;
|
||||||
|
using Reactor.Localization.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace MiraAPI.GameOptions.OptionTypes
|
namespace MiraAPI.GameOptions.OptionTypes
|
||||||
{
|
{
|
||||||
@@ -9,20 +10,26 @@ namespace MiraAPI.GameOptions.OptionTypes
|
|||||||
public T Value { get; set; }
|
public T Value { get; set; }
|
||||||
public T DefaultValue { get; set; }
|
public T DefaultValue { get; set; }
|
||||||
public Action<T> ChangedEvent { get; private set; }
|
public Action<T> ChangedEvent { get; private set; }
|
||||||
|
|
||||||
public PropertyInfo PropertyInfo;
|
|
||||||
public string Title { get; }
|
public string Title { get; }
|
||||||
public StringNames StringName { get; }
|
public StringNames StringName { get; }
|
||||||
public Func<bool> Hidden { get; set; }
|
public Func<bool> Visible { get; set; }
|
||||||
public OptionBehaviour OptionBehaviour { get; protected 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)
|
public ModdedOption(string title, T defaultValue, Type roleType)
|
||||||
{
|
{
|
||||||
Title = title;
|
Title = title;
|
||||||
DefaultValue = defaultValue;
|
DefaultValue = defaultValue;
|
||||||
Value = defaultValue;
|
Value = defaultValue;
|
||||||
StringName = CustomStringName.CreateAndRegister(Title);
|
StringName = CustomStringName.CreateAndRegister(Title);
|
||||||
Hidden = () => false;
|
Visible = () => true;
|
||||||
|
|
||||||
|
if (roleType is not null && roleType.IsAssignableTo(typeof(ICustomRole)))
|
||||||
|
{
|
||||||
|
AdvancedRole = roleType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ValueChanged(OptionBehaviour optionBehaviour)
|
public void ValueChanged(OptionBehaviour optionBehaviour)
|
||||||
@@ -39,9 +46,15 @@ namespace MiraAPI.GameOptions.OptionTypes
|
|||||||
ChangedEvent.Invoke(Value);
|
ChangedEvent.Invoke(Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyInfo.SetValue(null, newValue);
|
OnValueChanged(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract void OnValueChanged(T newValue);
|
||||||
|
|
||||||
public abstract T GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,42 @@
|
|||||||
namespace MiraAPI.GameOptions.OptionTypes
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace MiraAPI.GameOptions.OptionTypes
|
||||||
{
|
{
|
||||||
public class ModdedToggleOption : ModdedOption<bool>
|
public class ModdedToggleOption : ModdedOption<bool>
|
||||||
{
|
{
|
||||||
public ModdedToggleOption(string title, bool defaultValue = false) : base(title, defaultValue)
|
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)
|
public override bool GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour)
|
||||||
{
|
{
|
||||||
return optionBehaviour.GetBool();
|
return optionBehaviour.GetBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void OnValueChanged(bool newValue)
|
||||||
|
{
|
||||||
|
if (OptionBehaviour is null) return;
|
||||||
|
|
||||||
|
ToggleOption toggleOpt = OptionBehaviour as ToggleOption;
|
||||||
|
toggleOpt.CheckMark.enabled = newValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
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 ModdedOptionsTab
|
||||||
|
{
|
||||||
|
public static List<GameObject> CustomTabs = new List<GameObject>();
|
||||||
|
public static List<GameObject> CustomScreens = new List<GameObject>();
|
||||||
|
|
||||||
|
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(config, __instance);
|
||||||
|
var tab = CreateCustomTab(config, __instance, screen, gameBtn, roleBtn);
|
||||||
|
|
||||||
|
CustomScreens.Add(screen);
|
||||||
|
CustomTabs.Add(tab);
|
||||||
|
|
||||||
|
SpriteRenderer rend = tab.transform.FindChild("Btn/Tab Background").GetComponent<SpriteRenderer>();
|
||||||
|
rend.enabled = false;
|
||||||
|
|
||||||
|
UpdateListeners(__instance, gameBtn.GetComponentInChildren<PassiveButton>(), roleBtn.GetComponentInChildren<PassiveButton>(), rend);
|
||||||
|
|
||||||
|
var container = screen.transform.FindChild("GameGroup/SliderInner");
|
||||||
|
container.DestroyChildren();
|
||||||
|
//CreateNewResetButton(__instance, container);
|
||||||
|
|
||||||
|
return container.gameObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
foreach (var screen in CustomScreens) screen.gameObject.SetActive(false);
|
||||||
|
|
||||||
|
rend.enabled = false;
|
||||||
|
|
||||||
|
__instance.GameSettingsHightlight.enabled = true;
|
||||||
|
__instance.RolesSettingsHightlight.enabled = false;
|
||||||
|
}));
|
||||||
|
|
||||||
|
roleB.OnClick.RemoveAllListeners();
|
||||||
|
roleB.OnClick.AddListener((UnityAction)(() =>
|
||||||
|
{
|
||||||
|
__instance.RegularGameSettings.SetActive(false);
|
||||||
|
__instance.RolesSettings.gameObject.SetActive(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)
|
||||||
|
{
|
||||||
|
var header = Object.Instantiate(toggleOpt, container);
|
||||||
|
|
||||||
|
header.Title = StringNames.None;
|
||||||
|
header.TitleText.text = title;
|
||||||
|
header.name = "Header";
|
||||||
|
|
||||||
|
var checkBox = header.transform.FindChild("CheckBox")?.gameObject;
|
||||||
|
if (checkBox)
|
||||||
|
{
|
||||||
|
checkBox.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
var background = header.transform.FindChild("Background")?.gameObject;
|
||||||
|
if (background)
|
||||||
|
{
|
||||||
|
background.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
header.GetComponent<OptionBehaviour>().Destroy();
|
||||||
|
return header.gameObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
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";
|
||||||
|
|
||||||
|
var btn = inside.GetComponentInChildren<PassiveButton>();
|
||||||
|
|
||||||
|
btn.OnClick.RemoveAllListeners();
|
||||||
|
|
||||||
|
btn.OnClick.AddListener((Action)TabAction);
|
||||||
|
|
||||||
|
var spriteRend = inside.GetComponentInChildren<SpriteRenderer>();
|
||||||
|
// if (options.TabSettings.TabIcon != null) spriteRend.sprite = options.TabSettings.TabIcon.LoadAsset();
|
||||||
|
|
||||||
|
return newTab;
|
||||||
|
|
||||||
|
void TabAction()
|
||||||
|
{
|
||||||
|
__instance.RegularGameSettings.SetActive(false);
|
||||||
|
__instance.RolesSettings.gameObject.SetActive(false);
|
||||||
|
|
||||||
|
foreach (var screen in CustomScreens)
|
||||||
|
{
|
||||||
|
screen.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
newSettings.gameObject.SetActive(true);
|
||||||
|
|
||||||
|
SpriteRenderer rend = newTab.transform.FindChild("Btn/Tab Background").GetComponent<SpriteRenderer>();
|
||||||
|
rend.enabled = true;
|
||||||
|
|
||||||
|
gameTab.transform.FindChild("ColorButton/Tab Background").gameObject.GetComponent<SpriteRenderer>().enabled = false;
|
||||||
|
roleTab.transform.FindChild("Hat Button/Tab Background").gameObject.GetComponent<SpriteRenderer>().enabled = false;
|
||||||
|
|
||||||
|
foreach (var tab in CustomTabs)
|
||||||
|
{
|
||||||
|
tab.transform.FindChild("Btn/Tab Background").gameObject.GetComponent<SpriteRenderer>().enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GameObject CreateNewMenu(IMiraConfig config, GameSettingMenu __instance)
|
||||||
|
{
|
||||||
|
var gameSettings = __instance.RegularGameSettings;
|
||||||
|
var newSettings = Object.Instantiate(gameSettings, gameSettings.transform.parent);
|
||||||
|
newSettings.name = "Settings";
|
||||||
|
newSettings.SetActive(false);
|
||||||
|
|
||||||
|
var launchpadGroup = newSettings.transform.FindChild("GameGroup").gameObject;
|
||||||
|
var text = launchpadGroup.transform.FindChild("Text").gameObject.GetComponent<TextMeshPro>();
|
||||||
|
text.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
|
||||||
|
text.text = $"{config.TabSettings.Title}";
|
||||||
|
|
||||||
|
return newSettings;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,10 +26,6 @@
|
|||||||
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="GameOptions\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<Target Name="Copy" AfterTargets="Build" Condition="'$(AmongUs)' != ''">
|
<Target Name="Copy" AfterTargets="Build" Condition="'$(AmongUs)' != ''">
|
||||||
<Copy SourceFiles="$(TargetPath)" DestinationFolder="$(AmongUs)/BepInEx/plugins/" />
|
<Copy SourceFiles="$(TargetPath)" DestinationFolder="$(AmongUs)/BepInEx/plugins/" />
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
using BepInEx.Unity.IL2CPP;
|
using BepInEx.Unity.IL2CPP;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using MiraAPI.GameModes;
|
using MiraAPI.GameModes;
|
||||||
using MiraAPI.GameOptions.Attributes;
|
using MiraAPI.GameOptions;
|
||||||
using MiraAPI.Hud;
|
using MiraAPI.Hud;
|
||||||
using MiraAPI.Roles;
|
using MiraAPI.Roles;
|
||||||
using Reactor;
|
using Reactor;
|
||||||
@@ -27,7 +27,7 @@ public partial class MiraAPIPlugin : BasePlugin
|
|||||||
Harmony.PatchAll();
|
Harmony.PatchAll();
|
||||||
|
|
||||||
RegisterCustomRoleAttribute.Initialize();
|
RegisterCustomRoleAttribute.Initialize();
|
||||||
RegisterModdedOptionAttribute.Initialize();
|
ModdedOptionsManager.Initialize();
|
||||||
|
|
||||||
RegisterGameModeAttribute.Register(Assembly.GetCallingAssembly());
|
RegisterGameModeAttribute.Register(Assembly.GetCallingAssembly());
|
||||||
RegisterButtonAttribute.Register(Assembly.GetExecutingAssembly());
|
RegisterButtonAttribute.Register(Assembly.GetExecutingAssembly());
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using InnerNet;
|
using InnerNet;
|
||||||
|
using MiraAPI.GameOptions;
|
||||||
using MiraAPI.Hud;
|
using MiraAPI.Hud;
|
||||||
|
using MiraAPI.Patches.Options;
|
||||||
using MiraAPI.Roles;
|
using MiraAPI.Roles;
|
||||||
using Reactor.Utilities.Extensions;
|
using Reactor.Utilities.Extensions;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -16,10 +18,67 @@ namespace MiraAPI.Patches
|
|||||||
/// Custom role tab
|
/// Custom role tab
|
||||||
private static TaskPanelBehaviour _roleTab;
|
private static TaskPanelBehaviour _roleTab;
|
||||||
|
|
||||||
|
/// Scrolling increment
|
||||||
|
private const float Increment = 0.3f;
|
||||||
|
|
||||||
|
/// Bounds for scrolling
|
||||||
|
private static FloatRange _bounds = new FloatRange(2.9f, 4.6f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add options scrolling (on the hud text)
|
||||||
|
/// </summary>
|
||||||
|
public static void OptionsScrollingLogic(HudManager __instance)
|
||||||
|
{
|
||||||
|
if (Input.GetKeyDown(KeyCode.Tab))
|
||||||
|
{
|
||||||
|
if (ToHudStringPatch.CurrentPage >= ModdedOptionsManager.RegisteredMods.Count)
|
||||||
|
{
|
||||||
|
ToHudStringPatch.CurrentPage = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ToHudStringPatch.CurrentPage += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var numPlayers = GameData.Instance ? GameData.Instance.PlayerCount : 10;
|
||||||
|
HudManager.Instance.GameSettings.text = GameOptionsManager.Instance.CurrentGameOptions.ToHudString(numPlayers);
|
||||||
|
var pos = __instance.GameSettings.transform.localPosition;
|
||||||
|
|
||||||
|
if (!PlayerControl.LocalPlayer.CanMove)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ToHudStringPatch.CurrentPage == 0)
|
||||||
|
{
|
||||||
|
__instance.GameSettings.transform.localPosition = new Vector3(pos.x, _bounds.min, pos.z);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_bounds.max = __instance.GameSettings.rectTransform.sizeDelta.y + _bounds.min;
|
||||||
|
|
||||||
|
var delta = Input.mouseScrollDelta.y switch
|
||||||
|
{
|
||||||
|
> 0f => -Increment,
|
||||||
|
< 0f => Increment,
|
||||||
|
_ => 0f
|
||||||
|
};
|
||||||
|
|
||||||
|
pos = new Vector3(pos.x, Mathf.Clamp(pos.y + delta, _bounds.min, _bounds.max), pos.z);
|
||||||
|
|
||||||
|
__instance.GameSettings.transform.localPosition = pos;
|
||||||
|
}
|
||||||
|
|
||||||
[HarmonyPostfix, HarmonyPatch("Update")]
|
[HarmonyPostfix, HarmonyPatch("Update")]
|
||||||
public static void UpdatePostfix(HudManager __instance)
|
public static void UpdatePostfix(HudManager __instance)
|
||||||
{
|
{
|
||||||
var local = PlayerControl.LocalPlayer;
|
var local = PlayerControl.LocalPlayer;
|
||||||
|
if (LobbyBehaviour.Instance)
|
||||||
|
{
|
||||||
|
OptionsScrollingLogic(__instance);
|
||||||
|
}
|
||||||
|
|
||||||
if (AmongUsClient.Instance.GameState != InnerNetClient.GameStates.Started && !ShipStatus.Instance)
|
if (AmongUsClient.Instance.GameState != InnerNetClient.GameStates.Started && !ShipStatus.Instance)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -122,4 +181,4 @@ namespace MiraAPI.Patches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using Il2CppSystem;
|
||||||
|
using MiraAPI.GameOptions;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace MiraAPI.Patches.Options;
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(GameOptionsMenu))]
|
||||||
|
public static class GameOptionsMenuPatch
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Change the OnValueChanged action for all custom game options
|
||||||
|
/// </summary>
|
||||||
|
[HarmonyPostfix, HarmonyPatch("Start")]
|
||||||
|
public static void StartPostfix(GameOptionsMenu __instance)
|
||||||
|
{
|
||||||
|
foreach (var customOption in ModdedOptionsManager.Options)
|
||||||
|
{
|
||||||
|
if (customOption.AdvancedRole is not null || !customOption.OptionBehaviour)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
customOption.OptionBehaviour.OnValueChanged = (Action<OptionBehaviour>)customOption.ValueChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GameSettingMenu menu;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the position and offset of all custom game options
|
||||||
|
/// </summary>
|
||||||
|
[HarmonyPostfix, HarmonyPatch("Update")]
|
||||||
|
public static void UpdatePostfix(GameOptionsMenu __instance)
|
||||||
|
{
|
||||||
|
if (!menu)
|
||||||
|
{
|
||||||
|
menu = Object.FindObjectsOfType<GameSettingMenu>().First();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menu.RegularGameSettings.active || menu.RolesSettings.gameObject.active || menu.HideNSeekSettings.active)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var startOffset = 2.5f;
|
||||||
|
__instance.GetComponentInParent<Scroller>().ContentYBounds.max = startOffset + __instance.Children.Count * 0.5f;
|
||||||
|
|
||||||
|
foreach (var option in ModdedOptionsManager.Options.Where(option => option.Group == null))
|
||||||
|
{
|
||||||
|
if (!option.OptionBehaviour) continue;
|
||||||
|
|
||||||
|
option.OptionBehaviour.enabled = option.Visible();
|
||||||
|
option.OptionBehaviour.gameObject.SetActive(option.Visible());
|
||||||
|
|
||||||
|
if (option.Visible())
|
||||||
|
{
|
||||||
|
startOffset -= 0.55f;
|
||||||
|
}
|
||||||
|
|
||||||
|
var transform = option.OptionBehaviour.transform;
|
||||||
|
var optionPosition = transform.localPosition;
|
||||||
|
transform.localPosition = new Vector3(optionPosition.x, startOffset, optionPosition.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var group in ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null))
|
||||||
|
{
|
||||||
|
if (group.Header == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
group.Header.SetActive(group.GroupVisible());
|
||||||
|
|
||||||
|
if (group.GroupVisible())
|
||||||
|
{
|
||||||
|
startOffset -= 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
var position = group.Header.transform.localPosition;
|
||||||
|
group.Header.transform.localPosition = new Vector3(position.x, startOffset, position.z);
|
||||||
|
|
||||||
|
foreach (var option in ModdedOptionsManager.Options.Where(x => x.Group == group))
|
||||||
|
{
|
||||||
|
if (!option.OptionBehaviour)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var enabled = group.GroupVisible() && option.Visible();
|
||||||
|
|
||||||
|
option.OptionBehaviour.enabled = enabled;
|
||||||
|
option.OptionBehaviour.gameObject.SetActive(enabled);
|
||||||
|
|
||||||
|
if (group.GroupVisible() && option.Visible())
|
||||||
|
{
|
||||||
|
startOffset -= 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
var transform = option.OptionBehaviour.transform;
|
||||||
|
var optionPosition = transform.localPosition;
|
||||||
|
transform.localPosition = new Vector3(optionPosition.x, startOffset, optionPosition.z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using MiraAPI.API.GameOptions;
|
||||||
|
using MiraAPI.GameOptions;
|
||||||
|
using Reactor.Utilities;
|
||||||
|
using Reactor.Utilities.Extensions;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace MiraAPI.Patches.Options;
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(GameSettingMenu))]
|
||||||
|
public static class GameSettingsMenuPatches
|
||||||
|
{
|
||||||
|
private static GameObject _gameBtn;
|
||||||
|
private static GameObject _roleBtn;
|
||||||
|
|
||||||
|
[HarmonyPrefix, HarmonyPatch("Start")]
|
||||||
|
public static void StartPrefix(GameSettingMenu __instance)
|
||||||
|
{
|
||||||
|
foreach (var tab in ModdedOptionsTab.CustomTabs) tab.gameObject.Destroy();
|
||||||
|
foreach (var screen in ModdedOptionsTab.CustomScreens) screen.gameObject.Destroy();
|
||||||
|
|
||||||
|
ModdedOptionsTab.CustomScreens.Clear();
|
||||||
|
ModdedOptionsTab.CustomTabs.Clear();
|
||||||
|
|
||||||
|
_gameBtn = __instance.transform.FindChild("Header/Tabs/GameTab").gameObject;
|
||||||
|
_roleBtn = __instance.transform.FindChild("Header/Tabs/RoleTab").gameObject;
|
||||||
|
|
||||||
|
var numberOpt = __instance.RegularGameSettings.GetComponentInChildren<NumberOption>();
|
||||||
|
var toggleOpt = Object.FindObjectOfType<ToggleOption>();
|
||||||
|
var stringOpt = __instance.RegularGameSettings.GetComponentInChildren<StringOption>();
|
||||||
|
|
||||||
|
foreach (var pair in ModdedOptionsManager.RegisteredMods)
|
||||||
|
{
|
||||||
|
var container = ModdedOptionsTab.InitializeForMod(pair.Value, __instance).transform;
|
||||||
|
|
||||||
|
foreach (var group in ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null && group.ParentMod == pair.Value))
|
||||||
|
{
|
||||||
|
group.Header = ModdedOptionsTab.CreateHeader(toggleOpt, container, group.GroupName);
|
||||||
|
CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateOptionsFor(__instance, toggleOpt, numberOpt, stringOpt, container, ModdedOptionsManager.Options.Where(x => x.Group is null).ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!numberOpt || !toggleOpt || !stringOpt)
|
||||||
|
{
|
||||||
|
Logger<MiraAPIPlugin>.Error("OPTION PREFABS MISSING");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CreateOptionsFor(GameSettingMenu __instance, ToggleOption togglePrefab, NumberOption numberPrefab, StringOption stringPrefab, Transform container,
|
||||||
|
List<IModdedOption> options)
|
||||||
|
{
|
||||||
|
foreach (IModdedOption option in options)
|
||||||
|
{
|
||||||
|
if (option.AdvancedRole is not null || option.OptionBehaviour)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionBehaviour newOption = option.CreateOption(togglePrefab, numberPrefab, stringPrefab, container);
|
||||||
|
__instance.AllItems = __instance.AllItems.AddItem(newOption.transform).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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}:</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.GroupName}</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.GroupName}</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>";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using UnityEngine;
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace MiraAPI.Utilities.Assets
|
namespace MiraAPI.Utilities.Assets
|
||||||
{
|
{
|
||||||
@@ -12,8 +11,15 @@ namespace MiraAPI.Utilities.Assets
|
|||||||
return _loadedAsset;
|
return _loadedAsset;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _loadedAsset = SpriteTools.LoadSpriteFromPath(resourcesFolder + name);
|
try
|
||||||
throw new Exception($"INVALID ASSET: {name}");
|
{
|
||||||
|
return _loadedAsset = SpriteTools.LoadSpriteFromPath(resourcesFolder + name);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Debug.LogError($"Not loading, invalid asset: {name}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user