wip options api

This commit is contained in:
ang-xd committed 2024-06-23 17:06:40 -04:00
1 parent 2affea8c96
commit e2e811254a
30 files changed
+544 -632

No files matched your search

+1 -1
View File
@@ -3,7 +3,7 @@ using UnityEngine;
namespace MiraAPI.Example
{
[RegisterCustomRole(ExamplePlugin.Id)]
[RegisterCustomRole]
public class CustomRole : CrewmateRole, ICustomRole
{
public string RoleName => "Test Role";
+4 -4
View File
@@ -9,11 +9,11 @@ namespace MiraAPI.Example
public string GroupName => "General Group";
public Color GroupColor => Color.red;
[ModdedToggleOption("Use Thing")] public bool useThing { get; set; } = true;
[ModdedToggleOption("Use another thing")] public bool useAnotherThing { get; set; } = false;
[ModdedToggleOption("Use Thing")] public bool useThing { get; set; } = false;
[ModdedToggleOption("Use another thing")] public bool useAnotherThing { get; set; } = true;
[ModdedNumberOption("Sussy level", min: 0, max: 10)] public float sussyLevel { get; set; } = 4f;
[ModdedStringOption("Sus choices", ["hello", "hello 2", "hello 3"])] public int susChoices { get; set; } = 0;
[ModdedEnumOption("Enum test", typeof(TestEnum))] public TestEnum enumTest { get; set; } = TestEnum.Hello;
[ModdedStringOption("Sus choices", ["hello", "hello 2", "hello 3"])] public int susChoices { get; set; } = 2;
[ModdedEnumOption("Enum test", typeof(TestEnum))] public TestEnum enumTest { get; set; } = TestEnum.Bye;
}
public enum TestEnum
+2 -10
View File
@@ -1,7 +1,7 @@
using BepInEx;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.Utilities.Assets;
using MiraAPI.PluginLoading;
using Reactor;
using Reactor.Networking;
using Reactor.Networking.Attributes;
@@ -12,19 +12,11 @@ namespace MiraAPI.Example;
[BepInProcess("Among Us.exe")]
[BepInDependency(ReactorPlugin.Id)]
[ReactorModFlags(ModFlags.RequireOnAllClients)]
public partial class ExamplePlugin : BasePlugin, IMiraConfig
public partial class ExamplePlugin : BasePlugin, IMiraPlugin
{
public Harmony Harmony { get; } = new(Id);
public static ExamplePlugin Instance { get; private set; }
public ModdedOptionTabSettings TabSettings => new ModdedOptionTabSettings()
{
Title = "MiraAPI Example Mod",
TabIcon = MiraAssets.Empty
};
public override void Load()
{
Instance = this;
Harmony.PatchAll();
}
}
+2 -1
View File
@@ -4,6 +4,7 @@ namespace MiraAPI.Example
{
public class SomeRandomNonGroupClass
{
[ModdedStringOption("Yeah, idk", ["Idk 1", "idk 2", "idk 3", "idk 4"])] public static int yeaIdk { get; set; } = 2;
[ModdedStringOption("Yeah, idk", ["Idk 1", "idk 2", "idk 3", "idk 4"])] public static int yeaIdk { get; set; } = 3;
[ModdedNumberOption("Aw man", min: 45, max: 95)] public float sussyLevel { get; set; } = 63;
}
}
@@ -1,7 +1,6 @@
using MiraAPI.GameOptions.OptionTypes;
using System;
using System.Reflection;
using UnityEngine;
namespace MiraAPI.GameOptions.Attributes
{
@@ -24,7 +23,6 @@ namespace MiraAPI.GameOptions.Attributes
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;
}
@@ -15,23 +15,6 @@ namespace MiraAPI.GameOptions.Attributes
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);
+4 -4
View File
@@ -1,4 +1,5 @@
using System;
using MiraAPI.PluginLoading;
using System;
using UnityEngine;
namespace MiraAPI.GameOptions
@@ -11,9 +12,8 @@ namespace MiraAPI.GameOptions
public StringNames StringName { get; }
public Func<bool> Visible { get; set; }
public ModdedOptionGroup Group { get; set; }
public IMiraConfig ParentMod { get; set; }
public IMiraPlugin ParentMod { get; set; }
public void ValueChanged(OptionBehaviour optionBehaviour);
public string GetHudStringText();
public OptionBehaviour CreateOption(OptionBehaviour optionBehaviour, Transform container);
public OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container);
}
}
+3 -2
View File
@@ -1,11 +1,12 @@
using System;
using MiraAPI.PluginLoading;
using System;
using UnityEngine;
namespace MiraAPI.GameOptions
{
public class ModdedOptionGroup
{
public IMiraConfig ParentMod;
public IMiraPlugin ParentMod;
public GameObject Header;
public string GroupName;
public Func<bool> GroupVisible;
+7 -48
View File
@@ -1,12 +1,10 @@
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using HarmonyLib;
using MiraAPI.GameOptions.Attributes;
using Reactor.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace MiraAPI.GameOptions
{
@@ -15,51 +13,11 @@ namespace MiraAPI.GameOptions
public static List<IModdedOption> Options = [];
public static List<ModdedOptionGroup> Groups = [];
private static Dictionary<PropertyInfo, ModdedOptionAttribute> OptionAttributes = new();
private static Dictionary<Type, ModdedOptionGroup> OriginalTypes = new();
public static Dictionary<Assembly, IMiraConfig> RegisteredMods = new();
public static Dictionary<Type, ModdedOptionGroup> OriginalTypes = new();
public static void Initialize()
public static IModdedOption RegisterOption(Type type, ModdedOptionAttribute attribute, PropertyInfo property)
{
IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) =>
{
if (plugin.GetType().GetInterfaces().Contains(typeof(IMiraConfig)))
{
IMiraConfig config = (IMiraConfig)Activator.CreateInstance(plugin.GetType());
RegisteredMods.Add(assembly, config);
}
RegisterOptionGroups(assembly);
ModdedOptionAttribute.Register(assembly);
};
}
private static void RegisterOptionGroups(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
if (typeof(IModdedOptionGroup).IsAssignableFrom(type))
{
IModdedOptionGroup group = (IModdedOptionGroup)Activator.CreateInstance(type);
ModdedOptionGroup newGroup = new ModdedOptionGroup()
{
AdvancedRole = group.AdvancedRole,
GroupColor = group.GroupColor,
GroupName = group.GroupName,
GroupVisible = group.GroupVisible
};
Groups.Add(newGroup);
OriginalTypes.Add(type, newGroup);
newGroup.ParentMod = RegisteredMods[assembly];
}
}
}
public static void RegisterOption(Assembly assembly, Type type, ModdedOptionAttribute attribute, PropertyInfo property)
{
if (OptionAttributes.ContainsKey(property)) return;
if (OptionAttributes.ContainsKey(property)) return null;
object newObj = Activator.CreateInstance(type);
IModdedOption result = attribute.CreateOption(property.GetValue(newObj), property);
@@ -74,17 +32,18 @@ namespace MiraAPI.GameOptions
PluginSingleton<MiraAPIPlugin>.Instance.Harmony.Patch(getterOriginal, prefix: new HarmonyMethod(getterPatch));
attribute.HolderOption = result;
result.ParentMod = RegisteredMods[assembly];
Options.Add(result);
OptionAttributes.Add(property, attribute);
if (OriginalTypes.ContainsKey(type))
{
Debug.LogError($"grouping {attribute.Title} with {OriginalTypes[type].GroupName}");
Logger<MiraAPIPlugin>.Error($"Grouping {attribute.Title} with {OriginalTypes[type].GroupName}");
result.Group = OriginalTypes[type];
}
}
return result;
}
public static void PropertySetterPatch(MethodBase __originalMethod, object value)
@@ -2,6 +2,7 @@
using System;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.GameOptions.OptionTypes
{
@@ -14,19 +15,30 @@ namespace MiraAPI.GameOptions.OptionTypes
Values = Enum.GetNames(enumType);
}
public override OptionBehaviour CreateOption(OptionBehaviour optionBehaviour, Transform container)
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{
var stringOption = (StringOption)UnityEngine.Object.Instantiate(optionBehaviour, container);
var stringOption = Object.Instantiate(stringOpt, container);
var vals = Values.Select(CustomStringName.CreateAndRegister).ToArray();
var data = ScriptableObject.CreateInstance<StringGameSetting>();
stringOption.name = Title;
data.Title = StringName;
data.Type = global::OptionTypes.String;
data.Values = vals;
data.Index = Value;
stringOption.data = data;
stringOption.Title = StringName;
stringOption.Value = Value;
stringOption.Values = Values.Select(CustomStringName.CreateAndRegister).ToArray();
stringOption.TitleText.text = Title;
stringOption.Values = vals;
stringOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
stringOption.Initialize();
OptionBehaviour = stringOption;
stringOption.Initialize();
stringOption.Value = Value;
stringOption.ValueText.text = Values[Value];
stringOption.SetUpFromData(stringOption.data, 20);
return stringOption;
}
@@ -46,6 +58,8 @@ namespace MiraAPI.GameOptions.OptionTypes
var opt = OptionBehaviour as StringOption;
opt.Value = newValue;
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, OptionBehaviour.GetValueString(Value), false);
}
}
}
@@ -21,23 +21,26 @@ namespace MiraAPI.GameOptions.OptionTypes
Value = Mathf.Clamp(defaultValue, min, max);
}
public override OptionBehaviour CreateOption(OptionBehaviour optionBehaviour, Transform container)
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{
var numberOption = (NumberOption)Object.Instantiate(optionBehaviour, container);
var numberOption = Object.Instantiate(numberOpt, Vector3.zero, Quaternion.identity, container);
var data = ScriptableObject.CreateInstance<FloatGameSetting>();
data.Title = StringName;
data.Type = global::OptionTypes.Float;
data.Increment = Increment;
data.SuffixType = SuffixType;
data.FormatString = "0";
data.ValidRange = new FloatRange(Min, Max);
data.ZeroIsInfinity = ZeroInfinity;
data.Value = Value;
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.data = data;
numberOption.SetUpFromData(numberOption.data, 20);
numberOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
numberOption.Initialize();
OptionBehaviour = numberOption;
numberOption.Value = Value;
return numberOption;
}
@@ -59,6 +62,8 @@ namespace MiraAPI.GameOptions.OptionTypes
var opt = OptionBehaviour as NumberOption;
opt.Value = newValue;
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, OptionBehaviour.GetValueString(Value), false);
}
}
}
@@ -1,4 +1,5 @@
using MiraAPI.Roles;
using MiraAPI.PluginLoading;
using MiraAPI.Roles;
using Reactor.Localization.Utilities;
using System;
using UnityEngine;
@@ -16,7 +17,7 @@ namespace MiraAPI.GameOptions.OptionTypes
public Type AdvancedRole { get; }
public OptionBehaviour OptionBehaviour { get; set; }
public ModdedOptionGroup Group { get; set; } = null;
public IMiraConfig ParentMod { get; set; } = null;
public IMiraPlugin ParentMod { get; set; } = null;
public ModdedOption(string title, T defaultValue, Type roleType)
{
@@ -53,7 +54,7 @@ namespace MiraAPI.GameOptions.OptionTypes
public abstract T GetValueFromOptionBehaviour(OptionBehaviour optionBehaviour);
public abstract OptionBehaviour CreateOption(OptionBehaviour optionBehaviour, Transform container);
public abstract OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container);
public abstract string GetHudStringText();
}
@@ -1,6 +1,7 @@
using Reactor.Localization.Utilities;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
namespace MiraAPI.GameOptions.OptionTypes
{
@@ -12,19 +13,26 @@ namespace MiraAPI.GameOptions.OptionTypes
Values = values;
}
public override OptionBehaviour CreateOption(OptionBehaviour optionBehaviour, Transform container)
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{
var stringOption = (StringOption)Object.Instantiate(optionBehaviour, container);
var stringOption = Object.Instantiate(stringOpt, container);
var vals = Values.Select(CustomStringName.CreateAndRegister).ToArray();
var data = ScriptableObject.CreateInstance<StringGameSetting>();
stringOption.name = Title;
stringOption.Title = StringName;
stringOption.Value = Value;
stringOption.Values = Values.Select(CustomStringName.CreateAndRegister).ToArray();
data.Title = StringName;
data.Type = global::OptionTypes.String;
data.Values = vals;
data.Index = Value;
stringOption.data = data;
stringOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
stringOption.Initialize();
OptionBehaviour = stringOption;
stringOption.SetUpFromData(stringOption.data, 20);
stringOption.Value = Value;
return stringOption;
}
@@ -44,6 +52,8 @@ namespace MiraAPI.GameOptions.OptionTypes
var opt = OptionBehaviour as StringOption;
opt.Value = newValue;
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, OptionBehaviour.GetValueString(Value), false);
}
}
}
@@ -8,16 +8,23 @@ namespace MiraAPI.GameOptions.OptionTypes
{
}
public override OptionBehaviour CreateOption(OptionBehaviour optionBehaviour, Transform container)
public override OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container)
{
var toggleOption = (ToggleOption)Object.Instantiate(optionBehaviour, container);
var toggleOption = Object.Instantiate(toggleOpt, Vector3.zero, Quaternion.identity, container);
var data = ScriptableObject.CreateInstance<CheckboxGameSetting>();
data.Title = StringName;
data.Type = global::OptionTypes.Checkbox;
toggleOption.name = Title;
toggleOption.Title = StringName;
toggleOption.data = data;
toggleOption.TitleText.text = Title;
toggleOption.CheckMark.enabled = Value;
toggleOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
toggleOption.Initialize();
OptionBehaviour = toggleOption;
toggleOption.SetUpFromData(toggleOption.data, 20);
return toggleOption;
}
@@ -37,6 +44,8 @@ namespace MiraAPI.GameOptions.OptionTypes
var toggleOpt = OptionBehaviour as ToggleOption;
toggleOpt.CheckMark.enabled = newValue;
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, Value ? "On" : "Off", false);
}
}
}
-156
View File
@@ -1,156 +0,0 @@
using System;
using System.Collections.Generic;
using Reactor.Utilities.Extensions;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using Object = UnityEngine.Object;
namespace MiraAPI.GameOptions.UI;
/*
public static class ModdedOptionsTab
{
public static readonly List<GameObject> CustomTabs = [];
public static readonly List<GameObject> CustomScreens = [];
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, Color color)
{
var header = Object.Instantiate(toggleOpt, container);
header.Title = StringNames.None;
header.TitleText.text = title;
header.TitleText.color = color;
header.name = "Header";
var checkBox = header.transform.FindChild("CheckBox")?.gameObject;
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} Options";
return newSettings;
}
}*/
-17
View File
@@ -1,17 +0,0 @@
using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets;
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;
}
}
+24 -9
View File
@@ -1,14 +1,14 @@
using BepInEx;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.GameModes;
using MiraAPI.GameOptions;
using MiraAPI.Hud;
using MiraAPI.Roles;
using MiraAPI.PluginLoading;
using Reactor;
using Reactor.Networking;
using Reactor.Networking.Attributes;
using System.Reflection;
using Reactor.Patches;
using System;
using System.Text;
using UnityEngine;
namespace MiraAPI;
@@ -20,16 +20,31 @@ public partial class MiraAPIPlugin : BasePlugin
{
public Harmony Harmony { get; } = new(Id);
public static MiraAPIPlugin Instance { get; private set; }
public static MiraPluginManager PluginManager { get; private set; }
public static Color MiraColor = new Color32(238, 154, 112, 255);
public override void Load()
{
Instance = this;
Harmony.PatchAll();
RegisterCustomRoleAttribute.Initialize();
ModdedOptionsManager.Initialize();
PluginManager = new MiraPluginManager();
PluginManager.Initialize();
RegisterGameModeAttribute.Register(Assembly.GetCallingAssembly());
RegisterButtonAttribute.Register(Assembly.GetExecutingAssembly());
ReactorVersionShower.TextUpdated += (text) =>
{
text.text = new StringBuilder($"{MiraColor.ToTextColor()}Mira API</color> ")
.Append(GetShortHashVersion(Version))
.Append("\n")
.Append(text.text)
.ToString();
};
}
private static string GetShortHashVersion(string version)
{
var index = version.IndexOf("+", StringComparison.Ordinal);
return index < 0 ? version : version[..(index + 3)];
}
}
+1 -3
View File
@@ -1,8 +1,6 @@
using HarmonyLib;
using InnerNet;
using MiraAPI.GameOptions;
using MiraAPI.Hud;
using MiraAPI.Patches.Options;
using MiraAPI.Roles;
using Reactor.Utilities.Extensions;
using UnityEngine;
@@ -30,7 +28,7 @@ namespace MiraAPI.Patches
}
//CustomGameModeManager.ActiveMode.HudUpdate(__instance);
if (local.Data.Role is null) return;
if (local is null || local.Data is null || local.Data.Role is null) return;
if (local.Data.Role is ICustomRole customRole)
{
+101 -88
View File
@@ -1,109 +1,122 @@
using HarmonyLib;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using MiraAPI.GameOptions;
using MiraAPI.Utilities;
using Reactor.Localization.Utilities;
using System.Linq;
using TMPro;
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(nameof(GameOptionsMenu.Start))]
public static void StartPostfix(GameOptionsMenu __instance)
[HarmonyPrefix, HarmonyPatch(nameof(GameOptionsMenu.CreateSettings))]
public static bool SettingsPatch(GameOptionsMenu __instance)
{
foreach (var customOption in ModdedOptionsManager.Options)
if (GameSettingMenuPatches.currentSelectedMod == 0) return true;
__instance.MapPicker.gameObject.SetActive(false);
float num = 2.1f;
foreach (IModdedOptionGroup group in GameSettingMenuPatches.selectedMod.OptionGroups)
{
if (customOption.AdvancedRole is not null || !customOption.OptionBehaviour)
CategoryHeaderMasked categoryHeaderMasked = UnityEngine.Object.Instantiate(__instance.categoryHeaderOrigin, Vector3.zero, Quaternion.identity, __instance.settingsContainer);
categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName), 20);
categoryHeaderMasked.transform.localScale = Vector3.one * 0.63f;
categoryHeaderMasked.transform.localPosition = new Vector3(-0.903f, num, -2f);
num -= 0.63f;
foreach (var opt in ModdedOptionsManager.Options.Where(opt => opt.Group is not null && opt.Group.GroupName == group.GroupName))
{
continue;
}
OptionBehaviour newOpt = opt.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.settingsContainer);
newOpt.transform.localPosition = new Vector3(0.952f, num, -2f);
newOpt.SetClickMask(__instance.ButtonClickMask);
customOption.OptionBehaviour.OnValueChanged = (Action<OptionBehaviour>)customOption.ValueChanged;
}
}
/// <summary>
/// Set the position and offset of all custom game options
/// </summary>
[HarmonyPostfix, HarmonyPatch(nameof(GameOptionsMenu.Update))]
public static void UpdatePostfix(GameOptionsMenu __instance)
{
if (!GameSettingMenu.Instance)
{
return;
}
if (GameSettingMenu.Instance.GameSettingsTab.gameObject.active || GameSettingMenu.Instance.RoleSettingsTab.gameObject.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)
SpriteRenderer[] componentsInChildren = newOpt.GetComponentsInChildren<SpriteRenderer>(true);
for (int i = 0; i < componentsInChildren.Length; i++)
{
continue;
componentsInChildren[i].material.SetInt(PlayerMaterial.MaskLayer, 20);
}
foreach (TextMeshPro textMeshPro in newOpt.GetComponentsInChildren<TextMeshPro>(true))
{
textMeshPro.fontMaterial.SetFloat("_StencilComp", 3f);
textMeshPro.fontMaterial.SetFloat("_Stencil", 20);
}
var enabled = group.GroupVisible() && option.Visible();
__instance.Children.Add(newOpt);
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);
num -= 0.45f;
newOpt.Initialize();
}
}
CategoryHeaderMasked ungroupedHeader = UnityEngine.Object.Instantiate(__instance.categoryHeaderOrigin, Vector3.zero, Quaternion.identity, __instance.settingsContainer);
ungroupedHeader.SetHeader(CustomStringName.CreateAndRegister("Ungrouped Options"), 20);
ungroupedHeader.transform.localScale = Vector3.one * 0.63f;
ungroupedHeader.transform.localPosition = new Vector3(-0.903f, num, -2f);
num -= 0.63f;
foreach (var opt in ModdedOptionsManager.Options.Where(opt => opt.Group is null))
{
OptionBehaviour newOpt = opt.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.settingsContainer);
newOpt.transform.localPosition = new Vector3(0.952f, num, -2f);
newOpt.SetClickMask(__instance.ButtonClickMask);
newOpt.SetUpFromData(newOpt.data, 20);
SpriteRenderer[] componentsInChildren = newOpt.GetComponentsInChildren<SpriteRenderer>(true);
for (int i = 0; i < componentsInChildren.Length; i++)
{
componentsInChildren[i].material.SetInt(PlayerMaterial.MaskLayer, 20);
}
foreach (TextMeshPro textMeshPro in newOpt.GetComponentsInChildren<TextMeshPro>(true))
{
textMeshPro.fontMaterial.SetFloat("_StencilComp", 3f);
textMeshPro.fontMaterial.SetFloat("_Stencil", 20);
}
__instance.Children.Add(newOpt);
num -= 0.45f;
newOpt.Initialize();
}
__instance.scrollBar.SetYBoundsMax(-num - 1.65f);
return false;
}
}*/
[HarmonyPrefix, HarmonyPatch(nameof(GameOptionsMenu.Initialize))]
public static bool InitPatch(GameOptionsMenu __instance)
{
if (__instance.Children == null || __instance.Children.Count == 0)
{
__instance.MapPicker.gameObject.SetActive(true);
__instance.MapPicker.Initialize(20);
BaseGameSetting mapNameSetting = GameManager.Instance.GameSettingsList.MapNameSetting;
__instance.MapPicker.SetUpFromData(mapNameSetting, 20);
__instance.Children = new List<OptionBehaviour>();
__instance.Children.Add(__instance.MapPicker);
__instance.CreateSettings();
__instance.cachedData = GameOptionsManager.Instance.CurrentGameOptions;
for (int i = 0; i < __instance.Children.Count; i++)
{
OptionBehaviour optionBehaviour = __instance.Children[i];
if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost)
{
optionBehaviour.SetAsPlayer();
}
if (optionBehaviour.IsCustom()) continue;
optionBehaviour.OnValueChanged = new System.Action<OptionBehaviour>(__instance.ValueChanged);
}
}
return false;
}
}
@@ -1,6 +1,9 @@
using HarmonyLib;
using MiraAPI.PluginLoading;
using MiraAPI.Utilities.Assets;
using Reactor.Utilities.Extensions;
using System;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
@@ -10,8 +13,9 @@ namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(GameSettingMenu))]
public static class GameSettingMenuPatches
{
public static int currentSelectedMod = 0;
public static MiraPluginInfo selectedMod = null;
public static TextMeshPro text;
[HarmonyPrefix]
[HarmonyPatch(nameof(GameSettingMenu.Start))]
@@ -26,11 +30,11 @@ public static class GameSettingMenuPatches
tmpText.name = "SelectedMod";
tmpText.transform.localPosition = new Vector3(-3.3382f, 1.5399f, -2);
TextMeshPro tmp = tmpText.GetComponent<TextMeshPro>();
tmp.text = "<b>Default</b>";
tmp.fontSizeMax = 3.2f;
text = tmpText.GetComponent<TextMeshPro>();
text.fontSizeMax = 3.2f;
UpdateText(__instance.GameSettingsTab);
tmp.alignment = TextAlignmentOptions.Center;
text.alignment = TextAlignmentOptions.Center;
GameObject nextButton = GameObject.Instantiate(__instance.BackButton, __instance.BackButton.transform.parent).gameObject;
nextButton.transform.localPosition = new Vector3(-2.2663f, 1.5272f, -25f);
@@ -48,7 +52,11 @@ public static class GameSettingMenuPatches
passiveButton.inactiveSprites = nextButton.transform.FindChild("Inactive").gameObject;
passiveButton.OnClick.AddListener((UnityAction)(() =>
{
currentSelectedMod += 1;
if (currentSelectedMod != MiraPluginManager.Instance.RegisteredPlugins.Count)
{
currentSelectedMod += 1;
}
UpdateText(__instance.GameSettingsTab);
}));
GameObject backButton = GameObject.Instantiate(nextButton, __instance.BackButton.transform.parent).gameObject;
@@ -58,7 +66,53 @@ public static class GameSettingMenuPatches
backButton.transform.FindChild("Active").gameObject.GetComponent<SpriteRenderer>().flipX = backButton.transform.FindChild("Inactive").gameObject.GetComponent<SpriteRenderer>().flipX = true;
backButton.gameObject.GetComponent<PassiveButton>().OnClick.AddListener((UnityAction)(() =>
{
currentSelectedMod -= 1;
if (currentSelectedMod != 0)
{
currentSelectedMod -= 1;
}
UpdateText(__instance.GameSettingsTab);
}));
}
public static void UpdateText(GameOptionsMenu settings)
{
if (currentSelectedMod == 0)
{
text.text = "Default";
text.fontSizeMax = 3.2f;
}
else
{
text.fontSizeMax = 2f;
selectedMod = MiraPluginManager.Instance.RegisteredPlugins.ElementAt(currentSelectedMod - 1).Value;
if (selectedMod == null)
{
currentSelectedMod = 0;
UpdateText(settings);
}
string name = selectedMod.PluginInfo.Metadata.Name;
text.text = name.Substring(0, Math.Min(name.Length, 15));
}
if (settings.Children is null || settings is null) return;
foreach (var child in settings.Children)
{
if (child.TryCast<GameOptionsMapPicker>()) continue;
if (child.gameObject is null) continue;
child.gameObject.DestroyImmediate();
}
foreach (var header in settings.settingsContainer.GetComponentsInChildren<CategoryHeaderMasked>())
{
header.gameObject.DestroyImmediate();
}
settings.Children.Clear();
settings.Children = null;
settings.Initialize();
}
}
-118
View File
@@ -1,118 +0,0 @@
using AmongUs.GameOptions;
using HarmonyLib;
using MiraAPI.GameOptions;
using MiraAPI.Roles;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MiraAPI.Patches.Options;
[HarmonyPatch(typeof(IGameOptionsExtensions), "ToHudString")]
public static class ToHudStringPatch
{
public static int CurrentPage = 0;
/// <summary>
/// Generic method to add options
/// </summary>
public static void AddOptions(StringBuilder sb,
List<IModdedOption> options)
{
foreach (var opt in options)
{
sb.AppendLine(opt.GetHudStringText());
}
}
public static void Prefix()
{
if (GameManager.Instance is null || GameManager.Instance.IsHideAndSeek())
{
return;
}
foreach (var role in CustomRoleManager.CustomRoles.Values)
{
var customRole = role as ICustomRole;
if (customRole.IsGhostRole)
{
role.Role = RoleTypes.CrewmateGhost;
}
}
}
/// <summary>
/// Update the HudOptions on the left of the screen if player is using Launchpad options
/// </summary>
public static void Postfix(IGameOptions __instance, ref string __result)
{
if (GameManager.Instance is null)
{
return;
}
foreach (var pair in CustomRoleManager.CustomRoles)
{
var customRole = pair.Value as ICustomRole;
if (customRole.IsGhostRole)
{
pair.Value.Role = (RoleTypes)pair.Key;
}
}
if (CurrentPage != 0)
{
IMiraConfig currentPlugin = ModdedOptionsManager.RegisteredMods.Values.ToArray()[CurrentPage - 1];
if (currentPlugin == null)
{
CurrentPage = 0;
return;
}
var sb = new StringBuilder($"<size=180%><b>{currentPlugin.TabSettings.Title} Options:</b></size>\n<size=130%>");
var groupsWithRoles = ModdedOptionsManager.Groups.Where(group => group.AdvancedRole != null && group.ParentMod == currentPlugin);
var groupsWithoutRoles = ModdedOptionsManager.Groups.Where(group => group.AdvancedRole == null && group.ParentMod == currentPlugin);
AddOptions(sb,
ModdedOptionsManager.Options.Where(option => option.Group == null && option.ParentMod == currentPlugin && option.Visible()).ToList()
);
foreach (var group in groupsWithoutRoles)
{
if (!group.GroupVisible())
{
continue;
}
sb.AppendLine($"\n<size=160%><b>{group.GroupColor.ToTextColor()}{group.GroupName}</color></b></size>");
AddOptions(sb, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList());
}
var customOptionGroups = groupsWithRoles as ModdedOptionGroup[] ?? groupsWithRoles.ToArray();
if (customOptionGroups.Any())
{
sb.AppendLine("\n<size=160%><b>Roles</b></size>");
foreach (var group in customOptionGroups)
{
if (!group.GroupVisible())
{
continue;
}
sb.AppendLine($"<size=140%><b>{group.GroupColor.ToTextColor()}{group.GroupName}</color></b></size><size=120%>");
AddOptions(sb, ModdedOptionsManager.Options.Where(x => x.Group == group).ToList());
sb.Append("</size>\n");
}
}
__result = sb.ToString();
}
if (CurrentPage == 0)
{
__result = "<size=160%><b>Normal Options:</b></size>\n<size=130%>" + __result;
}
__result += $"\nUse <b>TAB</b> to switch pages (Page {CurrentPage}/{ModdedOptionsManager.RegisteredMods.Count})</size>";
}
}
+56
View File
@@ -0,0 +1,56 @@
using HarmonyLib;
using Il2CppSystem;
using MiraAPI.Utilities;
namespace MiraAPI.Patches.Options
{
[HarmonyPatch]
public static class OptionsPatches
{
[HarmonyPrefix, HarmonyPatch(typeof(ToggleOption), nameof(ToggleOption.Initialize))]
public static bool ToggleInit(ToggleOption __instance)
{
if (!__instance.IsCustom()) return true;
__instance.TitleText.text = DestroyableSingleton<TranslationController>.Instance.GetString(__instance.Title, Array.Empty<Object>());
return false;
}
[HarmonyPrefix, HarmonyPatch(typeof(ToggleOption), nameof(ToggleOption.UpdateValue))]
public static bool ToggleUpdate(ToggleOption __instance)
{
return !__instance.IsCustom();
}
[HarmonyPrefix, HarmonyPatch(typeof(NumberOption), nameof(NumberOption.Initialize))]
public static bool NumberInit(NumberOption __instance)
{
if (!__instance.IsCustom()) return true;
__instance.TitleText.text = DestroyableSingleton<TranslationController>.Instance.GetString(__instance.Title, Array.Empty<Object>());
return false;
}
[HarmonyPrefix, HarmonyPatch(typeof(NumberOption), nameof(NumberOption.UpdateValue))]
public static bool NumberUpdate(NumberOption __instance)
{
return !__instance.IsCustom();
}
[HarmonyPrefix, HarmonyPatch(typeof(StringOption), nameof(StringOption.Initialize))]
public static bool StringInit(StringOption __instance)
{
if (!__instance.IsCustom()) return true;
__instance.TitleText.text = DestroyableSingleton<TranslationController>.Instance.GetString(__instance.Title, Array.Empty<Object>());
__instance.ValueText.text = DestroyableSingleton<TranslationController>.Instance.GetString(__instance.Values[__instance.Value], Array.Empty<Object>());
return false;
}
[HarmonyPrefix, HarmonyPatch(typeof(StringOption), nameof(StringOption.UpdateValue))]
public static bool StringUpdate(StringOption __instance)
{
return !__instance.IsCustom();
}
}
}
+38 -7
View File
@@ -3,7 +3,9 @@ using HarmonyLib;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.Text;
using MiraAPI.PluginLoading;
using MiraAPI.Roles;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
@@ -22,17 +24,16 @@ public static class TaskAdderPatch
RolesFolder.gameObject.SetActive(false);
RolesFolder.FolderName = "Roles";
RolesFolder.name = "RolesFolder";
foreach (var pair in CustomRoleManager.ModRoles)
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins.Values)
{
var newFolder = Object.Instantiate(__instance.RootFolderPrefab, __instance.transform);
newFolder.FolderName = pair.Key.Metadata.Name;
newFolder.name = pair.Key.Metadata.Name;
newFolder.FolderName = newFolder.name = plugin.PluginInfo.Metadata.Name;
newFolder.gameObject.SetActive(false);
RolesFolder.SubFolders.Add(newFolder);
if (!ModsFolders.ContainsKey(pair.Key.Metadata.Name))
if (!ModsFolders.ContainsKey(plugin.PluginId))
{
ModsFolders.Add(pair.Key.Metadata.Name, pair.Key.Metadata.GUID);
ModsFolders.Add(plugin.PluginInfo.Metadata.Name, plugin.PluginId);
}
}
@@ -150,12 +151,42 @@ public static class TaskAdderPatch
}
}
}
if (taskFolder.FolderName == "Roles")
{
for (int m = 0; m < DestroyableSingleton<RoleManager>.Instance.AllRoles.Length; m++)
{
RoleBehaviour roleBehaviour = DestroyableSingleton<RoleManager>.Instance.AllRoles[m];
if (roleBehaviour.Role != RoleTypes.ImpostorGhost && roleBehaviour.Role != RoleTypes.CrewmateGhost && !CustomRoleManager.CustomRoles.ContainsKey((ushort)roleBehaviour.Role))
{
TaskAddButton taskAddButton2 = Object.Instantiate<TaskAddButton>(__instance.RoleButton);
taskAddButton2.SafePositionWorld = __instance.SafePositionWorld;
taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe";
__instance.AddFileAsChild(RolesFolder, taskAddButton2, ref num, ref num2, ref num3);
taskAddButton2.Role = roleBehaviour;
if (taskAddButton2 != null && taskAddButton2.Button != null)
{
ControllerManager.Instance.AddSelectableUiElement(taskAddButton2.Button, false);
if (m == 0 && __instance.restorePreviousSelectionFound != null)
{
ControllerManager.Instance.SetDefaultSelection(__instance.restorePreviousSelectionFound, null);
__instance.restorePreviousSelectionByFolderName = string.Empty;
__instance.restorePreviousSelectionFound = null;
}
else if (m == 0)
{
ControllerManager.Instance.SetDefaultSelection(taskAddButton2.Button, null);
}
}
}
}
}
if (ModsFolders.TryGetValue(taskFolder.FolderName, out string guid))
{
for (var m = 0; m < CustomRoleManager.GetRolesRegistered(guid).Count; m++)
MiraPluginInfo plugin = MiraPluginManager.Instance.GetPluginByGUID(guid);
for (var m = 0; m < plugin.CustomRoles.Count; m++)
{
var roleBehaviour = CustomRoleManager.GetRolesRegistered(guid)[m];
var roleBehaviour = plugin.CustomRoles.ElementAt(m).Value;
if (roleBehaviour.Role != RoleTypes.ImpostorGhost && roleBehaviour.Role != RoleTypes.CrewmateGhost && !roleBehaviour.IsDead)
{
var taskAddButton2 = Object.Instantiate(__instance.RoleButton);
+6
View File
@@ -0,0 +1,6 @@
namespace MiraAPI.PluginLoading
{
public interface IMiraPlugin
{
}
}
+27
View File
@@ -0,0 +1,27 @@
using BepInEx;
using MiraAPI.GameModes;
using MiraAPI.GameOptions;
using System.Collections.Generic;
namespace MiraAPI.PluginLoading;
public class MiraPluginInfo
{
public string PluginId { get; set; }
public IMiraPlugin Config { get; set; }
public PluginInfo PluginInfo { get; set; }
public readonly List<IModdedOptionGroup> OptionGroups = [];
public readonly List<IModdedOption> Options = [];
public readonly Dictionary<ushort, RoleBehaviour> CustomRoles = [];
public readonly Dictionary<int, CustomGameMode> GameModes = [];
public MiraPluginInfo(string id, IMiraPlugin config, PluginInfo info)
{
PluginId = id;
Config = config;
PluginInfo = info;
}
}
@@ -0,0 +1,99 @@
using BepInEx;
using BepInEx.Unity.IL2CPP;
using Il2CppInterop.Runtime.Injection;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.Attributes;
using MiraAPI.Roles;
using Reactor.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace MiraAPI.PluginLoading;
public class MiraPluginManager
{
public Dictionary<Assembly, MiraPluginInfo> RegisteredPlugins = [];
public static MiraPluginManager Instance;
public void Initialize()
{
Instance = this;
IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) =>
{
if (!plugin.GetType().GetInterfaces().Contains(typeof(IMiraPlugin))) return;
var id = MetadataHelper.GetMetadata(plugin.GetType()).GUID;
var info = new MiraPluginInfo(id, plugin as IMiraPlugin, IL2CPPChainloader.Instance.Plugins[id]);
RegisterRoleAttribute(assembly, info);
RegisterOptionsGroups(assembly, info);
RegisterOptionsAttributes(assembly, info);
RegisteredPlugins.Add(assembly, info);
Logger<MiraAPIPlugin>.Info($"Registering mod {id} with Mira API.");
};
}
public MiraPluginInfo GetPluginByGUID(string guid)
{
return RegisteredPlugins.Values.Where(plugin => plugin.PluginId == guid).First();
}
private static void RegisterOptionsAttributes(Assembly assembly, MiraPluginInfo pluginInfo)
{
foreach (var type in assembly.GetTypes())
{
foreach (PropertyInfo property in type.GetProperties())
{
foreach (var attribute in property.GetCustomAttributes())
{
if (attribute.GetType().BaseType == typeof(ModdedOptionAttribute))
{
pluginInfo.Options.Add(ModdedOptionsManager.RegisterOption(type, (ModdedOptionAttribute)attribute, property));
}
}
}
}
}
private static void RegisterOptionsGroups(Assembly assembly, MiraPluginInfo pluginInfo)
{
foreach (var type in assembly.GetTypes())
{
if (typeof(IModdedOptionGroup).IsAssignableFrom(type))
{
IModdedOptionGroup group = (IModdedOptionGroup)Activator.CreateInstance(type);
ModdedOptionGroup newGroup = new ModdedOptionGroup()
{
AdvancedRole = group.AdvancedRole,
GroupColor = group.GroupColor,
GroupName = group.GroupName,
GroupVisible = group.GroupVisible
};
ModdedOptionsManager.Groups.Add(newGroup);
ModdedOptionsManager.OriginalTypes.Add(type, newGroup);
pluginInfo.OptionGroups.Add(group);
}
}
}
private static void RegisterRoleAttribute(Assembly assembly, MiraPluginInfo pluginInfo)
{
foreach (var type in assembly.GetTypes())
{
var attribute = type.GetCustomAttribute<RegisterCustomRoleAttribute>();
if (attribute == null) continue;
ClassInjector.RegisterTypeInIl2Cpp(type);
var role = CustomRoleManager.RegisterRole(type, attribute.RoleId);
pluginInfo.CustomRoles.Add((ushort)role.Role, role);
}
}
}
+23 -51
View File
@@ -1,7 +1,6 @@
using AmongUs.GameOptions;
using BepInEx;
using BepInEx.Unity.IL2CPP;
using Il2CppInterop.Runtime;
using MiraAPI.Networking;
using Reactor.Localization.Utilities;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
@@ -9,7 +8,6 @@ using Reactor.Utilities.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using MiraAPI.Networking;
using TMPro;
using UnityEngine;
using Object = UnityEngine.Object;
@@ -18,46 +16,40 @@ namespace MiraAPI.Roles;
public static class CustomRoleManager
{
public static readonly Dictionary<ushort, RoleBehaviour> CustomRoles = [];
public static readonly Dictionary<PluginInfo, List<RoleBehaviour>> ModRoles = [];
public static Dictionary<ushort, RoleBehaviour> CustomRoles = new();
public static int GetNextRoleId(ushort requestedId)
{
while (CustomRoles.ContainsKey(requestedId))
{
requestedId++;
}
return requestedId;
}
public static void RegisterInRoleManager()
{
RoleManager.Instance.AllRoles = RoleManager.Instance.AllRoles.Concat(CustomRoles.Values).ToArray();
}
internal static void RegisterRole(Type roleType, ushort roleId, string modId)
internal static RoleBehaviour RegisterRole(Type roleType, ushort roleId)
{
if (!IL2CPPChainloader.Instance.Plugins.TryGetValue(modId, out PluginInfo info))
{
Debug.LogError($"{modId} is not a valid mod!");
return;
}
if (!(typeof(RoleBehaviour).IsAssignableFrom(roleType) && typeof(ICustomRole).IsAssignableFrom(roleType)))
{
return;
}
if (CustomRoles.Any(behaviour => behaviour.GetType() == roleType))
{
return;
Logger<MiraAPIPlugin>.Error($"{roleType?.Name} does not inherit from RoleBehaviour or ICustomRole.");
return null;
}
var roleBehaviour = (RoleBehaviour)new GameObject(roleType.Name).DontDestroy().AddComponent(Il2CppType.From(roleType));
if (roleBehaviour is not ICustomRole customRole)
{
return;
roleBehaviour.gameObject.Destroy();
return null;
}
if (CustomRoles.ContainsKey(roleId))
{
Debug.LogError($"Role ID conflict for {customRole.RoleName}. Use a different ID.");
return;
}
roleBehaviour.Role = (RoleTypes)roleId;
roleBehaviour.Role = (RoleTypes)GetNextRoleId(roleId);
roleBehaviour.TeamType = customRole.Team == ModdedRoleTeams.Neutral ? RoleTeamTypes.Crewmate : (RoleTeamTypes)customRole.Team;
roleBehaviour.NameColor = customRole.RoleColor;
roleBehaviour.StringName = CustomStringName.CreateAndRegister(customRole.RoleName);
@@ -76,47 +68,27 @@ public static class CustomRoleManager
RoleManager.GhostRoles.Add(roleBehaviour.Role);
}
if (ModRoles.TryGetValue(info, out List<RoleBehaviour> roles))
{
roles.Add(roleBehaviour);
}
else
{
ModRoles.TryAdd(info, [roleBehaviour]);
}
CustomRoles.Add(roleId, roleBehaviour);
if (customRole.HideSettings)
{
return;
return roleBehaviour;
}
var config = PluginSingleton<MiraAPIPlugin>.Instance.Config;
config.Bind(customRole.NumConfigDefinition, 1);
config.Bind(customRole.ChanceConfigDefinition, 100);
return roleBehaviour;
}
public static List<RoleBehaviour> GetRolesRegistered(string modId)
{
PluginInfo info = ModRoles.Keys.First(key => key.Metadata.GUID == modId);
if (ModRoles.TryGetValue(info, out List<RoleBehaviour> roles))
{
return roles;
}
else
{
Debug.LogError($"{modId} has no roles registered.");
return null;
}
}
public static bool GetCustomRoleBehaviour(RoleTypes roleType, out ICustomRole result)
{
CustomRoles.TryGetValue((ushort)roleType, out var temp);
if (temp is ICustomRole role)
if (temp != null)
{
result = role;
result = temp as ICustomRole;
return true;
}
+3 -31
View File
@@ -1,46 +1,18 @@
using BepInEx.Unity.IL2CPP;
using MiraAPI.Utilities;
using System;
using System.Collections.Generic;
using System.Reflection;
using System;
namespace MiraAPI.Roles;
[AttributeUsage(AttributeTargets.Class)]
public class RegisterCustomRoleAttribute : Attribute
{
private static readonly HashSet<Assembly> RegisteredAssemblies = [];
public ushort RoleId { get; }
public string ModId { get; }
public RegisterCustomRoleAttribute(string modId)
public RegisterCustomRoleAttribute()
{
ModId = modId;
RoleId = (ushort)(20 + CustomRoleManager.CustomRoles.Count);
}
public RegisterCustomRoleAttribute(string modId, ushort roleId)
public RegisterCustomRoleAttribute(ushort roleId)
{
ModId = modId;
RoleId = roleId;
}
public static void Initialize()
{
IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) => Register(assembly);
}
public static void Register(Assembly assembly)
{
if (!RegisteredAssemblies.Add(assembly)) return;
foreach (var type in assembly.GetTypes())
{
var attribute = type.GetCustomAttribute<RegisterCustomRoleAttribute>();
if (attribute != null)
{
Helpers.RegisterType(type);
CustomRoleManager.RegisterRole(type, attribute.RoleId, attribute.ModId);
}
}
}
}
+9 -1
View File
@@ -1,4 +1,5 @@
using MiraAPI.Roles;
using MiraAPI.GameOptions;
using MiraAPI.Roles;
using Reactor.Utilities.Extensions;
using System.Linq;
using UnityEngine;
@@ -7,6 +8,13 @@ namespace MiraAPI.Utilities
{
public static class Extensions
{
public static bool IsCustom(this OptionBehaviour optionBehaviour)
{
if (ModdedOptionsManager.Options.Any(opt => opt.OptionBehaviour == optionBehaviour)) return true;
return false;
}
public static void UpdateBodies(this PlayerControl playerControl, Color outlineColor, ref DeadBody target)
{
foreach (var body in Object.FindObjectsOfType<DeadBody>())
+1 -22
View File
@@ -1,7 +1,4 @@
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using MiraAPI.Roles;
using System;
using MiraAPI.Roles;
using System.Linq;
using System.Text;
using UnityEngine;
@@ -17,24 +14,6 @@ namespace MiraAPI.Utilities
return ShipStatus.Instance.AllRooms.ToList().Find(room => room.roomArea.OverlapPoint(pos));
}
public static void RegisterType(Type type)
{
if (ClassInjector.IsTypeRegisteredInIl2Cpp(type))
{
return;
}
try
{
ClassInjector.RegisterTypeInIl2Cpp(type);
}
catch (Exception e)
{
Debug.LogError($"Failed to register {type.FullDescription()}: {e}");
}
}
public static string GetSuffix(NumberSuffixes suffix)
{
return suffix switch