mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-20 01:46:15 +00:00
custom game over
This commit is contained in:
1 parent
6f4eff7b38
commit
530cb79981
6 files changed
+242
-8
No files matched your search
@@ -0,0 +1,64 @@
|
||||
using System.Collections.Generic;
|
||||
using MiraAPI.Networking;
|
||||
using Reactor.Networking.Rpc;
|
||||
using Reactor.Utilities;
|
||||
|
||||
namespace MiraAPI.GameEnd;
|
||||
|
||||
/// <summary>
|
||||
/// Custom Game Over.
|
||||
/// </summary>
|
||||
public abstract class CustomGameOver
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current CustomGameOver.
|
||||
/// </summary>
|
||||
public static CustomGameOver? Instance { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Run before setting up the game over.
|
||||
/// </summary>
|
||||
/// <param name="endGameManager">The EndGameManager instance.</param>
|
||||
/// <returns>Return True to use the default implementation, return False to skip it.</returns>
|
||||
public virtual bool BeforeSetEverythingUp(EndGameManager endGameManager)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run after setting up the game over.
|
||||
/// </summary>
|
||||
/// <param name="endGameManager">The EndGameManager instance.</param>
|
||||
public virtual void AfterSetEverythingUp(EndGameManager endGameManager)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a custom game over.
|
||||
/// </summary>
|
||||
/// <param name="winners">A collection of winners.</param>
|
||||
/// <typeparam name="T">Type of the custom game over.</typeparam>
|
||||
public static void Send<T>(IEnumerable<NetworkedPlayerInfo> winners) where T : CustomGameOver
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error("Only the host can send a custom game over.");
|
||||
return;
|
||||
}
|
||||
|
||||
var reason = GameOverManager.GetGameOverId<T>();
|
||||
var data = new GameOverData(reason, [.. winners]);
|
||||
|
||||
Rpc<CustomGameOverRpc>.Instance.Send(PlayerControl.LocalPlayer, data, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly convert a CustomGameOver to a GameOverReason.
|
||||
/// </summary>
|
||||
/// <param name="gameOver">The CustomGameOver instance.</param>
|
||||
/// <returns>The GameOverReason associated with the CustomGameOver.</returns>
|
||||
public static implicit operator GameOverReason(CustomGameOver gameOver)
|
||||
{
|
||||
return (GameOverReason)GameOverManager.GetGameOverId(gameOver.GetType());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MiraAPI.GameEnd;
|
||||
|
||||
/// <summary>
|
||||
/// Data for a game over.
|
||||
/// </summary>
|
||||
/// <param name="Reason">Reason for the game over.</param>
|
||||
/// <param name="Winners">A collection of winners.</param>
|
||||
public record struct GameOverData(int Reason, List<NetworkedPlayerInfo> Winners);
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MiraAPI.GameEnd;
|
||||
|
||||
/// <summary>
|
||||
/// Manage custom game overs.
|
||||
/// </summary>
|
||||
public static class GameOverManager
|
||||
{
|
||||
private static readonly Dictionary<Type, int> GameOverIds = [];
|
||||
private static readonly Dictionary<int, Type> GameOverTypes = [];
|
||||
|
||||
private static int _nextId = Enum.GetNames<GameOverReason>().Length;
|
||||
|
||||
/// <summary>
|
||||
/// Register a custom game over.
|
||||
/// </summary>
|
||||
/// <param name="gameOverType">Type of the custom game over.</param>
|
||||
/// <exception cref="ArgumentException">Thrown when the type is not a subclass of CustomGameOver, is abstract, or does not have a parameterless constructor.</exception>
|
||||
public static void RegisterGameOver(Type gameOverType)
|
||||
{
|
||||
if (!typeof(CustomGameOver).IsAssignableFrom(gameOverType))
|
||||
throw new ArgumentException("The type must be a subclass of CustomGameOver.");
|
||||
|
||||
if (gameOverType.IsAbstract)
|
||||
throw new ArgumentException("The type must not be abstract.");
|
||||
|
||||
if (gameOverType.GetConstructor(Type.EmptyTypes) == null)
|
||||
throw new ArgumentException("The type must have a parameterless constructor.");
|
||||
|
||||
GameOverIds.Add(gameOverType, _nextId);
|
||||
GameOverTypes.Add(_nextId, gameOverType);
|
||||
_nextId++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of a custom game over.
|
||||
/// </summary>
|
||||
/// <param name="id">ID of the custom game over.</param>
|
||||
/// <param name="customGameOver">The created instance of the custom game over.</param>
|
||||
/// <returns>An instance of the custom game over.</returns>
|
||||
public static bool TryGetGameOver(int id, out CustomGameOver? customGameOver)
|
||||
{
|
||||
if (GameOverTypes.TryGetValue(id, out var gameOverType))
|
||||
{
|
||||
customGameOver = Activator.CreateInstance(gameOverType) as CustomGameOver;
|
||||
return customGameOver != null;
|
||||
}
|
||||
|
||||
customGameOver = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the ID of a custom game over.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the custom game over.</typeparam>
|
||||
/// <returns>The ID of the custom game over.</returns>
|
||||
public static int GetGameOverId<T>() where T : CustomGameOver
|
||||
{
|
||||
return GetGameOverId(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the ID of a custom game over.
|
||||
/// </summary>
|
||||
/// <param name="gameOverType">Type of the custom game over.</param>
|
||||
/// <returns>The ID of the custom game over.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown when the type is not registered.</exception>
|
||||
public static int GetGameOverId(Type gameOverType)
|
||||
{
|
||||
if (GameOverIds.TryGetValue(gameOverType, out var id))
|
||||
return id;
|
||||
|
||||
throw new ArgumentException($"{gameOverType.FullName} is not a registered custom game over!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.Collections.Generic;
|
||||
using Hazel;
|
||||
using InnerNet;
|
||||
using MiraAPI.GameEnd;
|
||||
using Reactor.Networking.Attributes;
|
||||
using Reactor.Networking.Rpc;
|
||||
using Reactor.Utilities;
|
||||
|
||||
namespace MiraAPI.Networking;
|
||||
|
||||
/// <summary>
|
||||
/// Custom Game Over RPC.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
[RegisterCustomRpc((uint)MiraRpc.CustomGameOver)]
|
||||
public class CustomGameOverRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, GameOverData>(plugin, id)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override RpcLocalHandling LocalHandling => RpcLocalHandling.After;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(MessageWriter writer, GameOverData data)
|
||||
{
|
||||
writer.WritePacked(data.Reason);
|
||||
writer.WritePacked(data.Winners.Count);
|
||||
foreach (var player in data.Winners)
|
||||
{
|
||||
writer.WriteNetObject(player);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override GameOverData Read(MessageReader reader)
|
||||
{
|
||||
var reason = reader.ReadPackedInt32();
|
||||
var count = reader.ReadInt32();
|
||||
var data = new List<NetworkedPlayerInfo>(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
data.Add(reader.ReadNetObject<NetworkedPlayerInfo>());
|
||||
}
|
||||
|
||||
return new GameOverData(reason, data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Handle(PlayerControl innerNetObject, GameOverData data)
|
||||
{
|
||||
if (GameOverManager.TryGetGameOver(data.Reason, out var gameOver))
|
||||
{
|
||||
CustomGameOver.Instance = gameOver;
|
||||
|
||||
if (AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
GameManager.Instance.RpcEndGame(gameOver!, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CustomGameOver.Instance = null;
|
||||
Logger<MiraApiPlugin>.Error($"Unknown game over reason: {data.Reason}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,4 +34,9 @@ public enum MiraRpc : uint
|
||||
/// Custom Murder RPC.
|
||||
/// </summary>
|
||||
CustomMurder,
|
||||
|
||||
/// <summary>
|
||||
/// Custom Game Over RPC.
|
||||
/// </summary>
|
||||
CustomGameOver,
|
||||
}
|
||||
@@ -1,19 +1,32 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Gameplay;
|
||||
using MiraAPI.GameEnd;
|
||||
|
||||
namespace MiraAPI.Patches.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Patch for trigging the GameEndEvent.
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
public static class EndGamePatches
|
||||
[HarmonyPatch(typeof(EndGameManager), nameof(EndGameManager.SetEverythingUp))]
|
||||
internal static class EndGamePatches
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(EndGameManager), nameof(EndGameManager.SetEverythingUp))]
|
||||
public static void SetEverythingUpPatch(EndGameManager __instance)
|
||||
public static bool Prefix(EndGameManager __instance)
|
||||
{
|
||||
if (CustomGameOver.Instance is not { } gameOver)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var result = gameOver.BeforeSetEverythingUp(__instance);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void Postfix(EndGameManager __instance)
|
||||
{
|
||||
if (CustomGameOver.Instance is { } gameOver)
|
||||
{
|
||||
gameOver.AfterSetEverythingUp(__instance);
|
||||
CustomGameOver.Instance = null;
|
||||
}
|
||||
|
||||
var @event = new GameEndEvent(__instance);
|
||||
MiraEventManager.InvokeEvent(@event);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user