diff --git a/MiraAPI.Example/Buttons/NeutralKillerButton.cs b/MiraAPI.Example/Buttons/NeutralKillerButton.cs new file mode 100644 index 0000000..d38253a --- /dev/null +++ b/MiraAPI.Example/Buttons/NeutralKillerButton.cs @@ -0,0 +1,24 @@ +using MiraAPI.Example.GameOver; +using MiraAPI.Example.Roles; +using MiraAPI.GameEnd; +using MiraAPI.Hud; +using MiraAPI.Utilities.Assets; +using UnityEngine; + +namespace MiraAPI.Example.Buttons; + +public class NeutralKillerButton : CustomActionButton +{ + public override string Name => "Win Game"; + public override float Cooldown => 0f; + public override LoadableAsset Sprite => ExampleAssets.ExampleButton; + protected override void OnClick() + { + CustomGameOver.Trigger([PlayerControl.LocalPlayer.Data]); + } + + public override bool Enabled(RoleBehaviour? role) + { + return role is NeutralKillerRole; + } +} diff --git a/MiraAPI.Example/GameOver/NeutralKillerGameOver.cs b/MiraAPI.Example/GameOver/NeutralKillerGameOver.cs new file mode 100644 index 0000000..8d48a64 --- /dev/null +++ b/MiraAPI.Example/GameOver/NeutralKillerGameOver.cs @@ -0,0 +1,15 @@ +using MiraAPI.GameEnd; +using MiraAPI.Utilities; +using UnityEngine; + +namespace MiraAPI.Example.GameOver; + +public class NeutralKillerGameOver : CustomGameOver +{ + public override void AfterEndGameSetup(EndGameManager endGameManager) + { + endGameManager.WinText.text = "Outcast Killer Wins!"; + endGameManager.WinText.color = Color.magenta; + endGameManager.BackgroundBar.material.SetColor(ShaderID.Color, Color.magenta); + } +} diff --git a/MiraAPI.Example/Roles/NeutralKillerRole.cs b/MiraAPI.Example/Roles/NeutralKillerRole.cs index e9b4344..55fdc51 100644 --- a/MiraAPI.Example/Roles/NeutralKillerRole.cs +++ b/MiraAPI.Example/Roles/NeutralKillerRole.cs @@ -1,4 +1,6 @@ -using MiraAPI.Roles; +using MiraAPI.Example.GameOver; +using MiraAPI.GameEnd; +using MiraAPI.Roles; using UnityEngine; namespace MiraAPI.Example.Roles; @@ -22,7 +24,7 @@ public class NeutralKillerRole : ImpostorRole, ICustomRole public TeamIntroConfiguration? IntroConfiguration { get; } = new( Color.gray, - "NEUTRAL", + "OUTCAST", "You are an Outcast. You do not have a team."); public override void SpawnTaskHeader(PlayerControl playerControl) @@ -32,6 +34,6 @@ public class NeutralKillerRole : ImpostorRole, ICustomRole public override bool DidWin(GameOverReason gameOverReason) { - return GameManager.Instance.DidHumansWin(gameOverReason); + return gameOverReason == CustomGameOver.GameOverReason(); } } diff --git a/MiraAPI/GameEnd/CustomGameOver.cs b/MiraAPI/GameEnd/CustomGameOver.cs index d65e300..e54c750 100644 --- a/MiraAPI/GameEnd/CustomGameOver.cs +++ b/MiraAPI/GameEnd/CustomGameOver.cs @@ -16,29 +16,39 @@ public abstract class CustomGameOver public static CustomGameOver? Instance { get; internal set; } /// - /// Run before setting up the game over. + /// Runs before the base game calls EndGameManager.SetEverythingUp. /// /// The EndGameManager instance. - /// Return True to use the default implementation, return False to skip it. - public virtual bool BeforeSetEverythingUp(EndGameManager endGameManager) + /// Return True to use the run the original method, return False to skip it. + public virtual bool BeforeEndGameSetup(EndGameManager endGameManager) { return true; } /// - /// Run after setting up the game over. + /// Runs after the base game calls EndGameManager.SetEverythingUp. /// /// The EndGameManager instance. - public virtual void AfterSetEverythingUp(EndGameManager endGameManager) + public virtual void AfterEndGameSetup(EndGameManager endGameManager) { } + /// + /// Get the GameOverReason associated with this CustomGameOver. + /// + /// Type of the custom game over. + /// The GameOverReason associated with the custom game over. + public static GameOverReason GameOverReason() where T : CustomGameOver + { + return (GameOverReason)GameOverManager.GetGameOverId(); + } + /// /// Send a custom game over. /// /// A collection of winners. /// Type of the custom game over. - public static void Send(IEnumerable winners) where T : CustomGameOver + public static void Trigger(IEnumerable winners) where T : CustomGameOver { if (!AmongUsClient.Instance.AmHost) { diff --git a/MiraAPI/GameEnd/GameOverManager.cs b/MiraAPI/GameEnd/GameOverManager.cs index 147536c..c25aa37 100644 --- a/MiraAPI/GameEnd/GameOverManager.cs +++ b/MiraAPI/GameEnd/GameOverManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Reactor.Utilities; namespace MiraAPI.GameEnd; @@ -18,20 +19,30 @@ public static class GameOverManager /// /// Type of the custom game over. /// Thrown when the type is not a subclass of CustomGameOver, is abstract, or does not have a parameterless constructor. - public static void RegisterGameOver(Type gameOverType) + /// >True if the game over was registered successfully, false otherwise. + public static bool RegisterGameOver(Type gameOverType) { if (!typeof(CustomGameOver).IsAssignableFrom(gameOverType)) - throw new ArgumentException("The type must be a subclass of CustomGameOver."); + { + return false; + } if (gameOverType.IsAbstract) - throw new ArgumentException("The type must not be abstract."); + { + Logger.Error("The type must not be abstract."); + return false; + } if (gameOverType.GetConstructor(Type.EmptyTypes) == null) - throw new ArgumentException("The type must have a parameterless constructor."); + { + Logger.Error("The type must have a parameterless constructor."); + return false; + } GameOverIds.Add(gameOverType, _nextId); GameOverTypes.Add(_nextId, gameOverType); _nextId++; + return true; } /// diff --git a/MiraAPI/Networking/CustomGameOverRpc.cs b/MiraAPI/Networking/CustomGameOverRpc.cs index 0c95d63..a2419f0 100644 --- a/MiraAPI/Networking/CustomGameOverRpc.cs +++ b/MiraAPI/Networking/CustomGameOverRpc.cs @@ -33,7 +33,7 @@ public class CustomGameOverRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc< public override GameOverData Read(MessageReader reader) { var reason = reader.ReadPackedInt32(); - var count = reader.ReadInt32(); + var count = reader.ReadPackedInt32(); var data = new List(count); for (var i = 0; i < count; i++) { diff --git a/MiraAPI/Patches/Events/EndGamePatches.cs b/MiraAPI/Patches/Events/EndGamePatches.cs index 3e9b0fa..2c04d6b 100644 --- a/MiraAPI/Patches/Events/EndGamePatches.cs +++ b/MiraAPI/Patches/Events/EndGamePatches.cs @@ -15,7 +15,7 @@ internal static class EndGamePatches return true; } - var result = gameOver.BeforeSetEverythingUp(__instance); + var result = gameOver.BeforeEndGameSetup(__instance); return result; } @@ -23,7 +23,7 @@ internal static class EndGamePatches { if (CustomGameOver.Instance is { } gameOver) { - gameOver.AfterSetEverythingUp(__instance); + gameOver.AfterEndGameSetup(__instance); CustomGameOver.Instance = null; } diff --git a/MiraAPI/PluginLoading/MiraPluginManager.cs b/MiraAPI/PluginLoading/MiraPluginManager.cs index a7ed480..42be8e0 100644 --- a/MiraAPI/PluginLoading/MiraPluginManager.cs +++ b/MiraAPI/PluginLoading/MiraPluginManager.cs @@ -1,6 +1,12 @@ -using BepInEx.Unity.IL2CPP; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using BepInEx.Unity.IL2CPP; using MiraAPI.Colors; using MiraAPI.Events; +using MiraAPI.GameEnd; using MiraAPI.GameOptions; using MiraAPI.GameOptions.Attributes; using MiraAPI.Hud; @@ -9,11 +15,6 @@ using MiraAPI.Roles; using MiraAPI.Utilities; using Reactor.Networking; using Reactor.Utilities; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; namespace MiraAPI.PluginLoading; @@ -69,6 +70,11 @@ public sealed class MiraPluginManager continue; } + if (RegisterGameOver(type)) + { + continue; + } + RegisterColorClasses(type); } @@ -96,6 +102,19 @@ public sealed class MiraPluginManager return Instance._registeredPlugins.Values.FirstOrDefault(plugin => plugin.PluginId == pluginId); } + private static bool RegisterGameOver(Type type) + { + try + { + return GameOverManager.RegisterGameOver(type); + } + catch (Exception e) + { + Logger.Error($"Failed to register game over {type.Name}: {e}"); + return false; + } + } + private static bool RegisterOptions(Type type, MiraPluginInfo pluginInfo) { try