diff --git a/MiraAPI.Example/GameModes/ExampleMode.cs b/MiraAPI.Example/GameModes/ExampleMode.cs new file mode 100644 index 0000000..ff8292d --- /dev/null +++ b/MiraAPI.Example/GameModes/ExampleMode.cs @@ -0,0 +1,9 @@ +using MiraAPI.GameModes; + +namespace MiraAPI.Example.GameModes; + +public class ExampleMode : AbstractGameMode +{ + public override string Name => "Example Mode"; + public override string Description => "An example gamemode."; +} diff --git a/MiraAPI/GameModes/AbstractGameMode.cs b/MiraAPI/GameModes/AbstractGameMode.cs index 3bb80ff..2da08aa 100644 --- a/MiraAPI/GameModes/AbstractGameMode.cs +++ b/MiraAPI/GameModes/AbstractGameMode.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Collections.Generic; +using MiraAPI.GameOptions; using MiraAPI.PluginLoading; using UnityEngine; @@ -9,7 +10,7 @@ namespace MiraAPI.GameModes; /// Base class for custom gamemodes. /// [MiraIgnore] -public abstract class AbstractGameMode +public abstract class AbstractGameMode : IOptionable { /// /// Gets the game mode name. diff --git a/MiraAPI/GameModes/CustomGameModeManager.cs b/MiraAPI/GameModes/CustomGameModeManager.cs index 8e531bb..f179944 100644 --- a/MiraAPI/GameModes/CustomGameModeManager.cs +++ b/MiraAPI/GameModes/CustomGameModeManager.cs @@ -12,7 +12,7 @@ namespace MiraAPI.GameModes; /// public static class CustomGameModeManager { - private static readonly Dictionary IdToModeMap = []; + public static readonly Dictionary IdToModeMap = []; private static uint GetNextId() { diff --git a/MiraAPI/GameModes/GameModeOption.cs b/MiraAPI/GameModes/GameModeOption.cs index 9c02dc6..83de6ff 100644 --- a/MiraAPI/GameModes/GameModeOption.cs +++ b/MiraAPI/GameModes/GameModeOption.cs @@ -6,6 +6,7 @@ namespace MiraAPI.GameModes; /// /// The game mode option. /// +// TODO: Replace this with a proper gamemode selection. public class GameModeOption : AbstractOptionGroup { /// diff --git a/MiraAPI/GameOptions/ModdedOptionsManager.cs b/MiraAPI/GameOptions/ModdedOptionsManager.cs index 7aaa154..8a89500 100644 --- a/MiraAPI/GameOptions/ModdedOptionsManager.cs +++ b/MiraAPI/GameOptions/ModdedOptionsManager.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using BepInEx.Configuration; using HarmonyLib; +using Il2CppInterop.Runtime.Injection; using MiraAPI.GameOptions.Attributes; using MiraAPI.Networking; using MiraAPI.PluginLoading; @@ -27,6 +28,29 @@ public static class ModdedOptionsManager internal static uint NextId => _nextId++; private static uint _nextId = 1; + internal static bool RegisterGroup(Type type) + { + if (Activator.CreateInstance(type) is not AbstractOptionGroup group) + { + return false; + } + + if (TypeToGroup.ContainsKey(type)) + { + Logger.Error($"Group {type.Name} already exists."); + return false; + } + + Groups.Add(group); + TypeToGroup.Add(type, group); + + typeof(OptionGroupSingleton<>).MakeGenericType(type) + .GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)! + .SetValue(null, group); + + return true; + } + internal static bool RegisterGroup(Type type, MiraPluginInfo pluginInfo) { if (Activator.CreateInstance(type) is not AbstractOptionGroup group) diff --git a/MiraAPI/Patches/GameModes/CreateOptionsPatch.cs b/MiraAPI/Patches/GameModes/CreateOptionsPatch.cs new file mode 100644 index 0000000..7e8144e --- /dev/null +++ b/MiraAPI/Patches/GameModes/CreateOptionsPatch.cs @@ -0,0 +1,18 @@ +using HarmonyLib; + +namespace MiraAPI.Patches.GameModes; + +[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.SetGameMode))] +internal static class CreateOptionsPatch +{ + public static bool Prefix(CreateOptionsPicker __instance, AmongUs.GameOptions.GameModes mode) + { + if (mode <= AmongUs.GameOptions.GameModes.SeekFools) + { + return true; + } + + // TODO: finish this + return false; + } +} \ No newline at end of file diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index c2e2e61..f636d61 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -6,8 +6,10 @@ using System.Linq; using System.Reflection; using BepInEx; using BepInEx.Unity.IL2CPP; +using Il2CppInterop.Runtime.Injection; using MiraAPI.Colors; using MiraAPI.Events; +using MiraAPI.GameEnd; using MiraAPI.GameModes; using MiraAPI.GameOptions; using MiraAPI.GameOptions.Attributes; @@ -15,6 +17,7 @@ using MiraAPI.Hud; using MiraAPI.Modifiers; using MiraAPI.Roles; using MiraAPI.Utilities; +using Reactor.Localization.Utilities; using Reactor.Networking; using Reactor.Utilities; @@ -34,6 +37,7 @@ public sealed class MiraPluginManager internal void Initialize() { Instance = this; + ModdedOptionsManager.RegisterGroup(typeof(GameModeOption)); // TODO: dont do this CustomGameModeManager.RegisterDefaultMode(); CustomGameModeManager.GetAndSetGameMode(); IL2CPPChainloader.Instance.PluginLoad += RegisterPlugin; @@ -44,6 +48,20 @@ public sealed class MiraPluginManager { CustomButtonManager.Buttons = new ReadOnlyCollection(CustomButtonManager.CustomButtons); }; + IL2CPPChainloader.Instance.Finished += () => + { + var dict = new Dictionary(); + foreach (var (key, value) in CustomGameModeManager.IdToModeMap) + { + if (value is DefaultMode) + { + continue; + } + dict.Add(value.Name, key); + GameModesHelpers.ModeToName.Add((AmongUs.GameOptions.GameModes)key, CustomStringName.CreateAndRegister(value.Name)); + } + EnumInjector.InjectEnumValues(dict); + }; } private void RegisterPlugin(PluginInfo pluginInfo, Assembly assembly, BasePlugin plugin) @@ -63,57 +81,57 @@ public sealed class MiraPluginManager continue; } - foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public)) + foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public)) + { + var eventAttribute = method.GetCustomAttribute(); + if (eventAttribute != null) { - var eventAttribute = method.GetCustomAttribute(); - if (eventAttribute != null) + var parameters = method.GetParameters(); + if (parameters.Length != 1 || !parameters[0].ParameterType.IsSubclassOf(typeof(MiraEvent))) { - var parameters = method.GetParameters(); - if (parameters.Length != 1 || !parameters[0].ParameterType.IsSubclassOf(typeof(MiraEvent))) - { - Logger.Error($"Invalid event registration method {method.Name} in {type.Name}"); - continue; - } - - var paramType = parameters[0].ParameterType; - MiraEventManager.RegisterEventHandler(paramType, method, eventAttribute.Priority); + Logger.Error($"Invalid event registration method {method.Name} in {type.Name}"); + continue; } - } - if (RegisterModifier(type, info)) - { - continue; + var paramType = parameters[0].ParameterType; + MiraEventManager.RegisterEventHandler(paramType, method, eventAttribute.Priority); } - - if (RegisterOptions(type, info)) - { - continue; - } - - if (RegisterRoleAttribute(type, info, out var role)) - { - roles.Add(role); - continue; - } - - if (RegisterButtonAttribute(type, info)) - { - continue; - } - - if (RegisterGameOver(type)) - { - continue; - } - - if (RegisterGameModeAttribute(type, info)) - { - continue; - } - - RegisterColorClasses(type); } + if (RegisterModifier(type, info)) + { + continue; + } + + if (RegisterOptions(type, info)) + { + continue; + } + + if (RegisterRoleAttribute(type, info, out var role)) + { + roles.Add(role); + continue; + } + + if (RegisterButtonAttribute(type, info)) + { + continue; + } + + if (RegisterGameOver(type)) + { + continue; + } + + if (RegisterGameModeAttribute(type, info)) + { + continue; + } + + RegisterColorClasses(type); + } + info.OptionGroups.Sort((x, y) => x.GroupPriority.CompareTo(y.GroupPriority)); CustomRoleManager.RegisterRoleTypes(roles, info); @@ -181,6 +199,7 @@ public sealed class MiraPluginManager { Logger.Error($"Failed to register options for {type.Name}: {e}"); } + return false; } @@ -207,6 +226,7 @@ public sealed class MiraPluginManager { Logger.Error($"Failed to register role for {type.Name}: {e}"); } + return false; }