mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-19 15:51:48 +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.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 = "Example Mod",
|
||||
TabIcon = MiraAssets.Empty
|
||||
};
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
Instance = this;
|
||||
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 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.OptionTypes;
|
||||
using Reactor.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -12,62 +12,101 @@ namespace MiraAPI.GameOptions
|
||||
{
|
||||
public class ModdedOptionsManager
|
||||
{
|
||||
public static ModdedOptionsManager Instance;
|
||||
public List<IModdedOption> Options = new List<IModdedOption>();
|
||||
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();
|
||||
|
||||
private Dictionary<PropertyInfo, IModdedOption> PatchedOptions = new Dictionary<PropertyInfo, IModdedOption>();
|
||||
|
||||
public void CreateOption(Type type, RegisterModdedOptionAttribute attribute, PropertyInfo property)
|
||||
public static void Initialize()
|
||||
{
|
||||
Debug.LogError(property.GetConstantValue().GetType().ToString());
|
||||
|
||||
switch (property.GetConstantValue())
|
||||
IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) =>
|
||||
{
|
||||
case bool:
|
||||
CreateToggleOption(type, attribute, property);
|
||||
break;
|
||||
default:
|
||||
Debug.LogError($"Mira API does not support type {property.GetConstantValue().GetType()}");
|
||||
break;
|
||||
}
|
||||
if (plugin.GetType().GetInterfaces().Contains(typeof(IMiraConfig)))
|
||||
{
|
||||
IMiraConfig config = (IMiraConfig)Activator.CreateInstance(plugin.GetType());
|
||||
RegisteredMods.Add(assembly, config);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (opt != null)
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
object value = args[0];
|
||||
switch (value)
|
||||
if (typeof(IModdedOptionGroup).IsAssignableFrom(type))
|
||||
{
|
||||
case bool:
|
||||
ModdedToggleOption toggleOpt = opt as ModdedToggleOption;
|
||||
toggleOpt.SetValue((bool)value);
|
||||
break;
|
||||
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 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));
|
||||
Options.Add(toggleOpt);
|
||||
if (OptionAttributes.ContainsKey(property)) return;
|
||||
object newObj = Activator.CreateInstance(type);
|
||||
IModdedOption result = attribute.CreateOption(property.GetValue(newObj), property);
|
||||
|
||||
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));*/
|
||||
if (result != null)
|
||||
{
|
||||
var setterOriginal = property.GetSetMethod();
|
||||
var setterPatch = typeof(ModdedOptionsManager).GetMethod(nameof(PropertySetterPatch));
|
||||
PluginSingleton<MiraAPIPlugin>.Instance.Harmony.Patch(setterOriginal, postfix: new HarmonyMethod(setterPatch));
|
||||
|
||||
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))
|
||||
PatchedOptions.Add(property, toggleOpt);
|
||||
attribute.HolderOption = result;
|
||||
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.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.GameOptions.OptionTypes
|
||||
{
|
||||
@@ -9,20 +10,26 @@ namespace MiraAPI.GameOptions.OptionTypes
|
||||
public T Value { get; set; }
|
||||
public T DefaultValue { get; set; }
|
||||
public Action<T> ChangedEvent { get; private set; }
|
||||
|
||||
public PropertyInfo PropertyInfo;
|
||||
public string Title { get; }
|
||||
public StringNames StringName { get; }
|
||||
public Func<bool> Hidden { get; set; }
|
||||
public OptionBehaviour OptionBehaviour { get; protected set; }
|
||||
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)
|
||||
public ModdedOption(string title, T defaultValue, Type roleType)
|
||||
{
|
||||
Title = title;
|
||||
DefaultValue = defaultValue;
|
||||
Value = defaultValue;
|
||||
StringName = CustomStringName.CreateAndRegister(Title);
|
||||
Hidden = () => false;
|
||||
Visible = () => true;
|
||||
|
||||
if (roleType is not null && roleType.IsAssignableTo(typeof(ICustomRole)))
|
||||
{
|
||||
AdvancedRole = roleType;
|
||||
}
|
||||
}
|
||||
|
||||
public void ValueChanged(OptionBehaviour optionBehaviour)
|
||||
@@ -39,9 +46,15 @@ namespace MiraAPI.GameOptions.OptionTypes
|
||||
ChangedEvent.Invoke(Value);
|
||||
}
|
||||
|
||||
PropertyInfo.SetValue(null, newValue);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,42 @@
|
||||
namespace MiraAPI.GameOptions.OptionTypes
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.GameOptions.OptionTypes
|
||||
{
|
||||
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)
|
||||
{
|
||||
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>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="GameOptions\" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="Copy" AfterTargets="Build" Condition="'$(AmongUs)' != ''">
|
||||
<Copy SourceFiles="$(TargetPath)" DestinationFolder="$(AmongUs)/BepInEx/plugins/" />
|
||||
|
||||
@@ -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();
|
||||
RegisterModdedOptionAttribute.Initialize();
|
||||
ModdedOptionsManager.Initialize();
|
||||
|
||||
RegisterGameModeAttribute.Register(Assembly.GetCallingAssembly());
|
||||
RegisterButtonAttribute.Register(Assembly.GetExecutingAssembly());
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using HarmonyLib;
|
||||
using InnerNet;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Patches.Options;
|
||||
using MiraAPI.Roles;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using UnityEngine;
|
||||
@@ -16,10 +18,67 @@ namespace MiraAPI.Patches
|
||||
/// Custom role tab
|
||||
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")]
|
||||
public static void UpdatePostfix(HudManager __instance)
|
||||
{
|
||||
var local = PlayerControl.LocalPlayer;
|
||||
if (LobbyBehaviour.Instance)
|
||||
{
|
||||
OptionsScrollingLogic(__instance);
|
||||
}
|
||||
|
||||
if (AmongUsClient.Instance.GameState != InnerNetClient.GameStates.Started && !ShipStatus.Instance)
|
||||
{
|
||||
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
|
||||
{
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user