Merge pull request #15 from All-Of-Us-Mods/events-system

initial event draft
This commit is contained in:
XtraCube authored and GitHub committed 2024-12-21 15:11:50 -05:00
commit d3a815faa9
31 files changed
+820 -33

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
using MiraAPI.Events;
using MiraAPI.Events.Mira;
using MiraAPI.Events.Vanilla;
using MiraAPI.Example.Buttons.Freezer;
using Reactor.Utilities;
namespace MiraAPI.Example;
public static class ExampleEventHandlers
{
public static void Initialize()
{
// Register event handlers here
MiraEventManager.RegisterEventHandler<MiraButtonClickEvent<FreezeButton>>(FreezeButtonClickHandler, 1);
MiraEventManager.RegisterEventHandler<MiraButtonCancelledEvent<FreezeButton>>(FreezeButtonCancelledHandler);
MiraEventManager.RegisterEventHandler<UpdateSystemEvent>(UpdateSystemEventHandler);
}
public static void UpdateSystemEventHandler(UpdateSystemEvent @event)
{
Logger<ExamplePlugin>.Error(@event.SystemType.ToString());
}
// Example event handler
public static void FreezeButtonClickHandler(MiraButtonClickEvent<FreezeButton> @event)
{
Logger<ExamplePlugin>.Warning("Freeze button clicked!");
if (PlayerControl.LocalPlayer.Data.PlayerName == "stupid")
{
@event.Cancel();
@event.Button.SetTimer(15f);
}
}
// Example event handler
public static void FreezeButtonCancelledHandler(MiraButtonCancelledEvent<FreezeButton> @event)
{
Logger<ExamplePlugin>.Warning("Freeze button cancelled!");
@event.Button.OverrideName("Freeze Canceled");
}
}
+5
View File
@@ -2,10 +2,14 @@
using BepInEx.Configuration;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Mira;
using MiraAPI.Example.Buttons.Freezer;
using MiraAPI.PluginLoading;
using Reactor;
using Reactor.Networking;
using Reactor.Networking.Attributes;
using Reactor.Utilities;
namespace MiraAPI.Example;
@@ -21,6 +25,7 @@ public partial class ExamplePlugin : BasePlugin, IMiraPlugin
public ConfigFile GetConfigFile() => Config;
public override void Load()
{
ExampleEventHandlers.Initialize();
Harmony.PatchAll();
}
}
@@ -0,0 +1,44 @@
using MiraAPI.Hud;
namespace MiraAPI.Events.Mira;
/// <summary>
/// Invoked when a Mira Button click is cancelled. Do not use for vanilla buttons.
/// </summary>
/// <typeparam name="T">The Mira Button type.</typeparam>
public sealed class MiraButtonCancelledEvent<T> : MiraEvent where T : CustomActionButton
{
/// <summary>
/// Gets Mira Button whose click was cancelled.
/// </summary>
public T Button { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MiraButtonCancelledEvent{T}"/> class.
/// </summary>
/// <param name="button">The Mira Button whose click was cancelled.</param>
public MiraButtonCancelledEvent(T button)
{
Button = button;
}
}
/// <summary>
/// Invoked when a Mira Button click is cancelled. Do not use for vanilla buttons.
/// </summary>
public sealed class MiraButtonCancelledEvent : MiraEvent
{
/// <summary>
/// Gets Mira Button whose click was cancelled.
/// </summary>
public CustomActionButton Button { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MiraButtonCancelledEvent"/> class.
/// </summary>
/// <param name="button">The Mira Button whose click was cancelled.</param>
public MiraButtonCancelledEvent(CustomActionButton button)
{
Button = button;
}
}
@@ -0,0 +1,51 @@
using MiraAPI.Hud;
namespace MiraAPI.Events.Mira;
/// <summary>
/// Button click event for Mira Buttons only. Do not use for vanilla buttons.
/// </summary>
/// <typeparam name="T">The Mira Button type.</typeparam>
public sealed class MiraButtonClickEvent<T> : MiraCancelableEvent where T : CustomActionButton
{
/// <summary>
/// Gets Mira Button that was clicked.
/// </summary>
public T Button { get; }
/// <summary>
/// Gets the generic click event that is invoked before this button-specific event.
/// </summary>
public MiraButtonClickEvent GenericClickEvent { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MiraButtonClickEvent{T}"/> class.
/// </summary>
/// <param name="button">The Mira Button that was clicked.</param>
/// <param name="genericClickEvent">The generic click event invoked before button-specific events.</param>
public MiraButtonClickEvent(T button, MiraButtonClickEvent genericClickEvent)
{
GenericClickEvent = genericClickEvent;
Button = button;
}
}
/// <summary>
/// Button click event for Mira Buttons only. Do not use for vanilla buttons.
/// </summary>
public sealed class MiraButtonClickEvent : MiraCancelableEvent
{
/// <summary>
/// Gets Mira Button that was clicked.
/// </summary>
public CustomActionButton Button { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MiraButtonClickEvent"/> class.
/// </summary>
/// <param name="button">The Mira Button that was clicked.</param>
public MiraButtonClickEvent(CustomActionButton button)
{
Button = button;
}
}
+28
View File
@@ -0,0 +1,28 @@
namespace MiraAPI.Events;
/// <summary>
/// Abstract class for Mira Events that can be cancelled.
/// </summary>
public abstract class MiraCancelableEvent : MiraEvent
{
/// <summary>
/// Gets a value indicating whether the event is cancelled.
/// </summary>
public bool IsCancelled { get; private set; }
/// <summary>
/// Cancels the event.
/// </summary>
public void Cancel()
{
IsCancelled = true;
}
/// <summary>
/// Uncancels the event.
/// </summary>
public void UnCancel()
{
IsCancelled = false;
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace MiraAPI.Events;
/// <summary>
/// Abstract class for Mira Events.
/// </summary>
public abstract class MiraEvent;
+87
View File
@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using BepInEx.Unity.IL2CPP;
using Reactor.Utilities;
namespace MiraAPI.Events;
/// <summary>
/// Mira Event manager.
/// </summary>
public static class MiraEventManager
{
private static readonly Dictionary<Type, List<MiraEventWrapper>> EventWrappers = [];
/// <summary>
/// Invoke an event.
/// </summary>
/// <param name="eventInstance">The event instance.</param>
/// <typeparam name="T">Type of Event.</typeparam>
/// <returns>If there was an event handler invoked for this event, return true. Otherwise, return false.</returns>
public static bool InvokeEvent<T>(T eventInstance) where T : MiraEvent
{
EventWrappers.TryGetValue(typeof(T), out var handlers);
if (handlers == null)
{
Logger<MiraApiPlugin>.Warning("No handlers for event " + typeof(T).Name);
return false;
}
foreach (var handler in handlers)
{
((Action<T>)handler.EventHandler).Invoke(eventInstance);
}
return true;
}
/// <summary>
/// Invoke an event and use a specific type to find the handlers.
/// </summary>
/// <param name="eventInstance">The event instance.</param>
/// <param name="type">The type to use for handler lookup.</param>
/// <returns>If there was an event handler invoked for this event, return true. Otherwise, return false.</returns>
public static bool InvokeEvent(MiraEvent eventInstance, Type type)
{
EventWrappers.TryGetValue(type, out var handlers);
if (handlers == null)
{
Logger<MiraApiPlugin>.Warning("No handlers for event " + type.Name);
return false;
}
foreach (var handler in handlers)
{
handler.EventHandler.DynamicInvoke(eventInstance);
}
return true;
}
/// <summary>
/// Register an event.
/// </summary>
/// <param name="handler">The callback method/handler for the event.</param>
/// <param name="priority">The priority of the event handler. Higher values are called first.</param>
/// <typeparam name="T">Type of event.</typeparam>
public static void RegisterEventHandler<T>(Action<T> handler, int priority = 0) where T : MiraEvent
{
if (!EventWrappers.ContainsKey(typeof(T)))
{
EventWrappers.Add(typeof(T), []);
}
var handlers = EventWrappers[typeof(T)];
handlers.Add(new MiraEventWrapper(handler, priority));
Logger<MiraApiPlugin>.Info("Registered event handler for " + typeof(T).Name);
}
internal static void SortAllHandlers()
{
foreach (var handlers in EventWrappers.Values)
{
handlers.Sort((a, b) => a.Priority.CompareTo(b.Priority));
}
}
}
+30
View File
@@ -0,0 +1,30 @@
using System;
namespace MiraAPI.Events;
/// <summary>
/// Wrapper for event handlers.
/// </summary>
public class MiraEventWrapper
{
/// <summary>
/// Gets the event handler delegate.
/// </summary>
public Delegate EventHandler { get; }
/// <summary>
/// Gets the priority of the handler. Higher values are called first.
/// </summary>
public int Priority { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MiraEventWrapper"/> class.
/// </summary>
/// <param name="eventHandler">The action for the event handler.</param>
/// <param name="priority">The priority of the handler.</param>
public MiraEventWrapper(Delegate eventHandler, int priority)
{
EventHandler = eventHandler;
Priority = priority;
}
}
@@ -0,0 +1,21 @@
namespace MiraAPI.Events.Vanilla;
/// <summary>
/// Button click event for <see cref="AdminButton"/> from Vanilla Among Us.
/// </summary>
public class AdminButtonClickEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the instance of <see cref="AdminButton"/> that was clicked.
/// </summary>
public AdminButton Button { get; }
/// <summary>
/// Initializes a new instance of the <see cref="AdminButtonClickEvent"/> class.
/// </summary>
/// <param name="button">The AdminButton.</param>
public AdminButtonClickEvent(AdminButton button)
{
Button = button;
}
}
@@ -0,0 +1,28 @@
namespace MiraAPI.Events.Vanilla;
/// <summary>
/// Event that is invoked after a player is murdered. Only called after a successful murder. This event is not cancelable.
/// </summary>
public class AfterMurderEvent : MiraEvent
{
/// <summary>
/// Gets the player that killed the target.
/// </summary>
public PlayerControl Source { get; }
/// <summary>
/// Gets the player that was killed.
/// </summary>
public PlayerControl Target { get; }
/// <summary>
/// Initializes a new instance of the <see cref="AfterMurderEvent"/> class.
/// </summary>
/// <param name="source">The killer.</param>
/// <param name="target">The killed player.</param>
public AfterMurderEvent(PlayerControl source, PlayerControl target)
{
Source = source;
Target = target;
}
}
@@ -0,0 +1,28 @@
namespace MiraAPI.Events.Vanilla;
/// <summary>
/// Event that is invoked before a player is murdered. This event is cancelable.
/// </summary>
public sealed class BeforeMurderEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the player that is killing the target.
/// </summary>
public PlayerControl Source { get; }
/// <summary>
/// Gets the player that is being killed.
/// </summary>
public PlayerControl Target { get; }
/// <summary>
/// Initializes a new instance of the <see cref="BeforeMurderEvent"/> class.
/// </summary>
/// <param name="source">The player that is killing the target.</param>
/// <param name="target">The player that is being killed.</param>
public BeforeMurderEvent(PlayerControl source, PlayerControl target)
{
Source = source;
Target = target;
}
}
+21
View File
@@ -0,0 +1,21 @@
namespace MiraAPI.Events.Vanilla;
/// <summary>
/// Event fired when a player closes the doors in a room.
/// </summary>
public class CloseDoorsEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the room that the doors were closed in.
/// </summary>
public SystemTypes Room { get; }
/// <summary>
/// Initializes a new instance of the <see cref="CloseDoorsEvent"/> class.
/// </summary>
/// <param name="room">The room that was closed.</param>
public CloseDoorsEvent(SystemTypes room)
{
Room = room;
}
}
+28
View File
@@ -0,0 +1,28 @@
namespace MiraAPI.Events.Vanilla;
/// <summary>
/// Event that is invoked when a player enters a vent. This event is cancelable.
/// </summary>
public class EnterVentEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the player that is entering the vent.
/// </summary>
public PlayerControl Player { get; }
/// <summary>
/// Gets the vent that the player is entering.
/// </summary>
public Vent? Vent { get; }
/// <summary>
/// Initializes a new instance of the <see cref="EnterVentEvent"/> class.
/// </summary>
/// <param name="player">The player that is entering the vent.</param>
/// <param name="vent">The vent being entered.</param>
public EnterVentEvent(PlayerControl player, Vent? vent)
{
Player = player;
Vent = vent;
}
}
+28
View File
@@ -0,0 +1,28 @@
namespace MiraAPI.Events.Vanilla;
/// <summary>
/// Event that is invoked when a player exits a vent. This event is cancelable.
/// </summary>
public class ExitVentEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the player that is exiting the vent.
/// </summary>
public PlayerControl Player { get; }
/// <summary>
/// Gets the vent that the player is exiting.
/// </summary>
public Vent? Vent { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ExitVentEvent"/> class.
/// </summary>
/// <param name="player">The player who is exiting the vent.</param>
/// <param name="vent">The vent being exited from.</param>
public ExitVentEvent(PlayerControl player, Vent? vent)
{
Player = player;
Vent = vent;
}
}
@@ -0,0 +1,21 @@
namespace MiraAPI.Events.Vanilla;
/// <summary>
/// Button click event for <see cref="SabotageButton"/> from Vanilla Among Us.
/// </summary>
public class SabotageButtonClickEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the instance of <see cref="SabotageButton"/> that was clicked.
/// </summary>
public SabotageButton Button { get; }
/// <summary>
/// Initializes a new instance of the <see cref="SabotageButtonClickEvent"/> class.
/// </summary>
/// <param name="button">The SabotageButton.</param>
public SabotageButtonClickEvent(SabotageButton button)
{
Button = button;
}
}
@@ -0,0 +1,35 @@
namespace MiraAPI.Events.Vanilla;
/// <summary>
/// The event that is invoked when a meeting is called
/// </summary>
public class StartMeetingEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the player that is calling the meeting.
/// </summary>
public PlayerControl Reporter { get; }
/// <summary>
/// Gets the player that is being reported. Will be null for emergency meeting.
/// </summary>
public NetworkedPlayerInfo? Target { get; }
/// <summary>
/// Gets the body that is being reported. Will be null for emergency meeting.
/// </summary>
public DeadBody? Body { get; }
/// <summary>
/// Initializes a new instance of the <see cref="StartMeetingEvent"/> class.
/// </summary>
/// <param name="reporter">The player who reported the body.</param>
/// <param name="target">The player being reported.</param>
/// <param name="body">The body being reported.</param>
public StartMeetingEvent(PlayerControl reporter, NetworkedPlayerInfo? target, DeadBody? body)
{
Reporter = reporter;
Target = target;
Body = body;
}
}
@@ -0,0 +1,34 @@
namespace MiraAPI.Events.Vanilla;
/// <summary>
/// Cancelable event that is invoked before a system is updated. Usually used for sabotage events.
/// </summary>
public class UpdateSystemEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the type of system that will be updated.
/// </summary>
public SystemTypes SystemType { get; }
/// <summary>
/// Gets the player that is updating the system.
/// </summary>
public PlayerControl Player { get; }
/// <summary>
/// Gets the byte amount the system is being updated by.
/// </summary>
public byte Amount { get; }
/// <summary>
/// Initializes a new instance of the <see cref="UpdateSystemEvent"/> class.
/// </summary>
/// <param name="systemType">The SystemType being updated.</param>
/// <param name="amount">Amount to update System to.</param>
public UpdateSystemEvent(SystemTypes systemType, PlayerControl player, byte amount)
{
SystemType = systemType;
Player = player;
Amount = amount;
}
}
@@ -0,0 +1,21 @@
namespace MiraAPI.Events.Vanilla;
/// <summary>
/// Button click event for <see cref="UseButton"/> from Vanilla Among Us.
/// </summary>
public class UseButtonClickEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the instance of <see cref="UseButton"/> that was clicked.
/// </summary>
public UseButton Button { get; }
/// <summary>
/// Initializes a new instance of the <see cref="UseButtonClickEvent"/> class.
/// </summary>
/// <param name="button">The UseButton.</param>
public UseButtonClickEvent(UseButton button)
{
Button = button;
}
}
@@ -0,0 +1,21 @@
namespace MiraAPI.Events.Vanilla;
/// <summary>
/// Button click event for <see cref="VentButton"/> from Vanilla Among Us.
/// </summary>
public class VentButtonClickEvent : MiraCancelableEvent
{
/// <summary>
/// Gets the instance of <see cref="VentButton"/> that was clicked.
/// </summary>
public VentButton Button { get; }
/// <summary>
/// Initializes a new instance of the <see cref="VentButtonClickEvent"/> class.
/// </summary>
/// <param name="button">The VentButton.</param>
public VentButtonClickEvent(VentButton button)
{
Button = button;
}
}
+41 -2
View File
@@ -1,7 +1,11 @@
using MiraAPI.Utilities.Assets;
using System;
using MiraAPI.Events;
using MiraAPI.Events.Mira;
using MiraAPI.Utilities.Assets;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace MiraAPI.Hud;
@@ -95,7 +99,42 @@ public abstract class CustomActionButton
var pb = Button.GetComponent<PassiveButton>();
pb.OnClick = new Button.ButtonClickedEvent();
pb.OnClick.AddListener((UnityAction)ClickHandler);
pb.OnClick.AddListener((UnityAction)(() =>
{
// Invoke the generic button click event.
var genericEvent = new MiraButtonClickEvent(this);
MiraEventManager.InvokeEvent(genericEvent);
if (genericEvent.IsCancelled)
{
MiraEventManager.InvokeEvent(new MiraButtonCancelledEvent(this));
}
// Invoke the button click event for specific button.
var eventType = CustomButtonManager.ButtonEventTypes[GetType()];
var @event = (MiraCancelableEvent)Activator.CreateInstance(eventType, this)!;
var specificInvoked = MiraEventManager.InvokeEvent(@event, eventType);
if (@event.IsCancelled)
{
var cancelEventType = CustomButtonManager.ButtonCancelledEventTypes[GetType()];
var cancelEvent = (MiraEvent)Activator.CreateInstance(cancelEventType, this)!;
MiraEventManager.InvokeEvent(cancelEvent, cancelEventType);
}
if (specificInvoked)
{
if (!@event.IsCancelled)
{
ClickHandler();
}
}
else
{
if (!genericEvent.IsCancelled)
{
ClickHandler();
}
}
}));
}
/// <summary>
+6
View File
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using MiraAPI.Events.Mira;
using MiraAPI.PluginLoading;
using Reactor.Utilities;
@@ -9,6 +10,8 @@ namespace MiraAPI.Hud;
internal static class CustomButtonManager
{
internal static readonly List<CustomActionButton> CustomButtons = [];
internal static readonly Dictionary<Type, Type> ButtonEventTypes = [];
internal static readonly Dictionary<Type, Type> ButtonCancelledEventTypes = [];
internal static void RegisterButton(Type buttonType, MiraPluginInfo pluginInfo)
{
@@ -29,5 +32,8 @@ internal static class CustomButtonManager
typeof(CustomButtonSingleton<>).MakeGenericType(buttonType)
.GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)!
.SetValue(null, button);
ButtonEventTypes.Add(buttonType, typeof(MiraButtonClickEvent<>).MakeGenericType(buttonType));
ButtonCancelledEventTypes.Add(buttonType, typeof(MiraButtonCancelledEvent<>).MakeGenericType(buttonType));
}
}
+15
View File
@@ -3,6 +3,9 @@ using System.Linq;
using AmongUs.GameOptions;
using Assets.CoreScripts;
using BepInEx.Unity.IL2CPP.Utils;
using Discord;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla;
using Reactor.Networking.Attributes;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
@@ -39,6 +42,15 @@ public static class CustomMurderRpc
bool playKillSound = true)
{
var murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError;
var beforeMurderEvent = new BeforeMurderEvent(source, target);
MiraEventManager.InvokeEvent(beforeMurderEvent);
if (beforeMurderEvent.IsCancelled)
{
murderResultFlags = MurderResultFlags.FailedError;
}
var murderResultFlags2 = MurderResultFlags.DecisionByHost | murderResultFlags;
source.CustomMurder(
@@ -268,5 +280,8 @@ public static class CustomMurderRpc
PlayerControl.LocalPlayer.isKilling = false;
source.isKilling = false;
var afterMurderEvent = new AfterMurderEvent(source, target);
MiraEventManager.InvokeEvent(afterMurderEvent);
}
}
@@ -0,0 +1,49 @@
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla;
using Reactor.Utilities;
namespace MiraAPI.Patches.Events;
/// <summary>
/// Patch for button related MiraEvents.
/// </summary>
[HarmonyPatch]
public static class HudButtonEventPatches
{
[HarmonyPrefix]
[HarmonyPatch(typeof(AdminButton), nameof(AdminButton.DoClick))]
public static bool AdminButtonDoClickPrefix(AdminButton __instance)
{
var @event = new AdminButtonClickEvent(__instance);
MiraEventManager.InvokeEvent(@event);
return !@event.IsCancelled;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(SabotageButton), nameof(SabotageButton.DoClick))]
public static bool SabotageButtonDoClickPrefix(SabotageButton __instance)
{
var @event = new SabotageButtonClickEvent(__instance);
MiraEventManager.InvokeEvent(@event);
return !@event.IsCancelled;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(UseButton), nameof(UseButton.DoClick))]
public static bool UseButtonDoClickPrefix(UseButton __instance)
{
var @event = new UseButtonClickEvent(__instance);
MiraEventManager.InvokeEvent(@event);
return !@event.IsCancelled;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(VentButton), nameof(VentButton.DoClick))]
public static bool VentButtonDoClickPrefix(VentButton __instance)
{
var @event = new VentButtonClickEvent(__instance);
MiraEventManager.InvokeEvent(@event);
return !@event.IsCancelled;
}
}
@@ -0,0 +1,30 @@
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla;
namespace MiraAPI.Patches.Events;
/// <summary>
/// Used for patching sabotage/system related MiraEvents.
/// </summary>
[HarmonyPatch]
public static class SabotageEventPatches
{
[HarmonyPrefix]
[HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.RpcUpdateSystem), typeof(SystemTypes), typeof(byte))]
public static bool ShipStatusUpdateSystemPrefix(ShipStatus __instance, SystemTypes systemType, byte amount)
{
var @event = new UpdateSystemEvent(systemType, PlayerControl.LocalPlayer, amount);
MiraEventManager.InvokeEvent(@event);
return !@event.IsCancelled;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.RpcCloseDoorsOfType))]
public static bool ShipStatusCloseDoorsOfTypePrefix(ShipStatus __instance, SystemTypes type)
{
var @event = new CloseDoorsEvent(type);
MiraEventManager.InvokeEvent(@event);
return !@event.IsCancelled;
}
}
@@ -0,0 +1,21 @@
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla;
using MiraAPI.Utilities;
namespace MiraAPI.Patches.Events;
[HarmonyPatch]
public static class StartMeetingEventPatch
{
[HarmonyPrefix]
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.CmdReportDeadBody))]
public static bool PlayerControlCmdReportDeadBodyPrefix(PlayerControl __instance, NetworkedPlayerInfo? target)
{
var body = target != null ? Helpers.GetBodyById(target.PlayerId) : null;
var @event = new StartMeetingEvent(__instance, target, body);
MiraEventManager.InvokeEvent(@event);
return !@event.IsCancelled;
}
}
@@ -0,0 +1,60 @@
using System.Linq;
using BepInEx.Unity.IL2CPP.Utils.Collections;
using HarmonyLib;
using MiraAPI.Events;
using MiraAPI.Events.Vanilla;
using MiraAPI.Utilities;
using Reactor.Utilities;
namespace MiraAPI.Patches.Events;
/// <summary>
/// Patches to invoke vent related MiraEvents.
/// </summary>
[HarmonyPatch]
public static class VentEventPatches
{
// necessary because Vent.Use is inlined in il2cpp.
private static bool _showButtons;
[HarmonyPostfix]
[HarmonyPatch(typeof(VentButton), nameof(VentButton.DoClick))]
public static void VentUsePostfix(VentButton __instance)
{
__instance.currentTarget?.SetButtons(_showButtons);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(PlayerPhysics), nameof(PlayerPhysics.RpcEnterVent))]
public static bool PlayerPhysicsRpcEnterVentPrefix(PlayerPhysics __instance, int id)
{
var pc = __instance.myPlayer;
var vent = ShipStatus.Instance.AllVents.FirstOrDefault(v => v.Id == id);
var @event = new EnterVentEvent(pc, vent);
MiraEventManager.InvokeEvent(@event);
if (pc.AmOwner)
{
_showButtons = !@event.IsCancelled;
}
return !@event.IsCancelled;
}
[HarmonyPrefix]
[HarmonyPatch(typeof(PlayerPhysics), nameof(PlayerPhysics.RpcExitVent))]
public static bool PlayerPhysicsRpcExitVentPrefix(PlayerPhysics __instance, int id)
{
var pc = __instance.myPlayer;
var vent = ShipStatus.Instance.AllVents.First(v => v.Id == id);
var @event = new ExitVentEvent(pc, vent);
MiraEventManager.InvokeEvent(@event);
if (pc.AmOwner)
{
_showButtons = @event.IsCancelled;
}
return !@event.IsCancelled;
}
}
@@ -4,6 +4,7 @@ using System.Linq;
using System.Reflection;
using BepInEx.Unity.IL2CPP;
using MiraAPI.Colors;
using MiraAPI.Events;
using MiraAPI.GameOptions;
using MiraAPI.GameOptions.Attributes;
using MiraAPI.Hud;
@@ -51,6 +52,7 @@ public sealed class MiraPluginManager
Logger<MiraApiPlugin>.Info($"Registering mod {pluginInfo.Metadata.GUID} with Mira API.");
};
IL2CPPChainloader.Instance.Finished += PaletteManager.RegisterAllColors;
IL2CPPChainloader.Instance.Finished += MiraEventManager.SortAllHandlers;
}
/// <summary>
+4 -8
View File
@@ -15,6 +15,8 @@ public struct CustomRoleConfiguration
/// <param name="role">The role in which you are configuring.</param>
public CustomRoleConfiguration(ICustomRole role)
{
var roleBehaviour = role as RoleBehaviour;
MaxRoleCount = 15;
DefaultRoleCount = 0;
DefaultChance = 0;
@@ -29,13 +31,12 @@ public struct CustomRoleConfiguration
};
RoleHintType = RoleHintType.RoleTab;
GhostRole = role.Team is ModdedRoleTeams.Impostor ? RoleTypes.ImpostorGhost : RoleTypes.CrewmateGhost;
CanGetKilled = !IsGhostRole && role.Team is not ModdedRoleTeams.Impostor;
CanGetKilled = roleBehaviour?.IsDead == false && role.Team is not ModdedRoleTeams.Impostor;
UseVanillaKillButton = role.Team is ModdedRoleTeams.Impostor;
CanUseVent = role.Team is ModdedRoleTeams.Impostor;
CanUseSabotage = role.Team is ModdedRoleTeams.Impostor;
TasksCountForProgress = role.Team is ModdedRoleTeams.Crewmate;
IsGhostRole = false;
HideSettings = IsGhostRole;
HideSettings = roleBehaviour?.IsDead == true;
CanModifyChance = true;
}
@@ -99,11 +100,6 @@ public struct CustomRoleConfiguration
/// </summary>
public bool TasksCountForProgress;
/// <summary>
/// Gets a value indicating whether the role is a Ghost.
/// </summary>
public bool IsGhostRole;
/// <summary>
/// Gets a value indicating whether the role should show up in the Role Options menu.
/// </summary>
+1 -1
View File
@@ -93,7 +93,7 @@ public static class CustomRoleManager
roleBehaviour.MaxCount = customRole.Configuration.MaxRoleCount;
roleBehaviour.RoleScreenshot = customRole.Configuration.OptionsScreenshot.LoadAsset();
if (customRole.Configuration.IsGhostRole)
if (roleBehaviour.IsDead)
{
RoleManager.GhostRoles.Add(roleBehaviour.Role);
}
+1 -21
View File
@@ -13,26 +13,6 @@ namespace MiraAPI.Utilities.Assets;
/// </summary>
public static class SpriteTools
{
private delegate bool DLoadImage(IntPtr tex, IntPtr data, bool markNonReadable);
private static DLoadImage? _iCallLoadImage;
/// <summary>
/// Load an image into a texture.
/// </summary>
/// <param name="tex">The texture.</param>
/// <param name="data">Byte data of image.</param>
/// <param name="markNonReadable">Mark nonreadable.</param>
/// <returns>True if succeeded.</returns>
public static bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable)
{
_iCallLoadImage ??= IL2CPP.ResolveICall<DLoadImage>("UnityEngine.ImageConversion::LoadImage");
var il2CPPArray = (Il2CppStructArray<byte>)data;
return _iCallLoadImage.Invoke(tex.Pointer, il2CPPArray.Pointer, markNonReadable);
}
/// <summary>
/// Load a sprite from a resource path.
/// </summary>
@@ -46,7 +26,7 @@ public static class SpriteTools
if (myStream != null)
{
var buttonTexture = myStream.ReadFully();
LoadImage(tex, buttonTexture, false);
tex.LoadImage(buttonTexture, false);
}
else
{
+11 -1
View File
@@ -1,4 +1,5 @@
using MiraAPI.Roles;
using System.Collections;
using MiraAPI.Roles;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -14,6 +15,15 @@ namespace MiraAPI.Utilities;
/// </summary>
public static class Helpers
{
/// <summary>
/// Returns an empty coroutine.
/// </summary>
/// <returns>Empty coroutine.</returns>
public static IEnumerator EmptyCoroutine()
{
yield break;
}
/// <summary>
/// Creates a ContactFilter2D from a layer mask.
/// </summary>