diff --git a/MiraAPI.Example/ExamplePlugin.cs b/MiraAPI.Example/ExamplePlugin.cs index 89ba051..45309eb 100644 --- a/MiraAPI.Example/ExamplePlugin.cs +++ b/MiraAPI.Example/ExamplePlugin.cs @@ -2,6 +2,8 @@ using BepInEx.Configuration; using BepInEx.Unity.IL2CPP; using HarmonyLib; +using MiraAPI.Events; +using MiraAPI.Events.Vanilla; using MiraAPI.PluginLoading; using Reactor; using Reactor.Networking; @@ -21,6 +23,14 @@ public partial class ExamplePlugin : BasePlugin, IMiraPlugin public ConfigFile GetConfigFile() => Config; public override void Load() { + MiraEventManager.RegisterEventHandler( + e => + { + if (e.Source.PlayerId == PlayerControl.LocalPlayer.PlayerId) + { + e.Cancel(); + } + }); Harmony.PatchAll(); } } diff --git a/MiraAPI/Events/Mira/MiraButtonCancelledEvent.cs b/MiraAPI/Events/Mira/MiraButtonCancelledEvent.cs new file mode 100644 index 0000000..cb0e0e1 --- /dev/null +++ b/MiraAPI/Events/Mira/MiraButtonCancelledEvent.cs @@ -0,0 +1,13 @@ +using MiraAPI.Hud; + +namespace MiraAPI.Events.Mira; + +public class MiraButtonCancelledEvent : MiraEvent where T : CustomActionButton +{ + public T Button { get; } + + public MiraButtonCancelledEvent(T button) + { + Button = button; + } +} diff --git a/MiraAPI/Events/Mira/MiraButtonClickEvent.cs b/MiraAPI/Events/Mira/MiraButtonClickEvent.cs new file mode 100644 index 0000000..8553760 --- /dev/null +++ b/MiraAPI/Events/Mira/MiraButtonClickEvent.cs @@ -0,0 +1,13 @@ +using MiraAPI.Hud; + +namespace MiraAPI.Events.Mira; + +public class MiraButtonClickEvent : MiraCancelableEvent where T : CustomActionButton +{ + public T Button { get; } + + public MiraButtonClickEvent(T button) + { + Button = button; + } +} diff --git a/MiraAPI/Events/MiraEvent.cs b/MiraAPI/Events/MiraEvent.cs new file mode 100644 index 0000000..dc5d484 --- /dev/null +++ b/MiraAPI/Events/MiraEvent.cs @@ -0,0 +1,18 @@ +namespace MiraAPI.Events; + +public abstract class MiraEvent; + +public abstract class MiraCancelableEvent : MiraEvent +{ + public bool IsCancelled { get; private set; } + + public void Cancel() + { + IsCancelled = true; + } + + public void UnCancel() + { + IsCancelled = false; + } +} diff --git a/MiraAPI/Events/MiraEventManager.cs b/MiraAPI/Events/MiraEventManager.cs new file mode 100644 index 0000000..7d6b044 --- /dev/null +++ b/MiraAPI/Events/MiraEventManager.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; + +namespace MiraAPI.Events; + +/// +/// Mira Event manager. +/// +public static class MiraEventManager +{ + private static readonly Dictionary> EventHandlers = []; + + /// + /// Invoke an event. + /// + /// The event instance. + /// Type of Event. + public static void InvokeEvent(T eventInstance) where T : MiraEvent + { + foreach (var handler in EventHandlers[typeof(T)]) + { + ((Action)handler).Invoke(eventInstance); + } + } + + /// + /// Register an event. + /// + /// The callback method/handler for the event. + /// Type of event. + public static void RegisterEventHandler(Action handler) where T : MiraEvent + { + if (!EventHandlers.ContainsKey(typeof(T))) + { + EventHandlers.Add(typeof(T), []); + } + EventHandlers[typeof(T)].Add(handler); + } +} diff --git a/MiraAPI/Events/Vanilla/BeforeMurderEvent.cs b/MiraAPI/Events/Vanilla/BeforeMurderEvent.cs new file mode 100644 index 0000000..a58aa80 --- /dev/null +++ b/MiraAPI/Events/Vanilla/BeforeMurderEvent.cs @@ -0,0 +1,7 @@ +namespace MiraAPI.Events.Vanilla; + +public class BeforeMurderEvent : MiraCancelableEvent +{ + public PlayerControl Source { get; } + public PlayerControl Target { get; } +} diff --git a/MiraAPI/Hud/CustomActionButton.cs b/MiraAPI/Hud/CustomActionButton.cs index c80adef..64d4eb0 100644 --- a/MiraAPI/Hud/CustomActionButton.cs +++ b/MiraAPI/Hud/CustomActionButton.cs @@ -1,4 +1,6 @@ -using MiraAPI.Utilities.Assets; +using MiraAPI.Events; +using MiraAPI.Events.Mira; +using MiraAPI.Utilities.Assets; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; @@ -95,7 +97,20 @@ public abstract class CustomActionButton var pb = Button.GetComponent(); pb.OnClick = new Button.ButtonClickedEvent(); - pb.OnClick.AddListener((UnityAction)ClickHandler); + pb.OnClick.AddListener((UnityAction)(() => + { + var @event = new MiraButtonClickEvent(this); + MiraEventManager.InvokeEvent(@event); + if (!@event.IsCancelled) + { + ClickHandler(); + } + else + { + var cancelledEvent = new MiraButtonCancelledEvent(this); + MiraEventManager.InvokeEvent(cancelledEvent); + } + })); } /// diff --git a/MiraAPI/Roles/CustomRoleConfiguration.cs b/MiraAPI/Roles/CustomRoleConfiguration.cs index 0df4297..5bd29bb 100644 --- a/MiraAPI/Roles/CustomRoleConfiguration.cs +++ b/MiraAPI/Roles/CustomRoleConfiguration.cs @@ -15,6 +15,8 @@ public struct CustomRoleConfiguration /// The role in which you are configuring. 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 /// public bool TasksCountForProgress; - /// - /// Gets a value indicating whether the role is a Ghost. - /// - public bool IsGhostRole; - /// /// Gets a value indicating whether the role should show up in the Role Options menu. /// diff --git a/MiraAPI/Roles/CustomRoleManager.cs b/MiraAPI/Roles/CustomRoleManager.cs index 8d7f48a..eb291d8 100644 --- a/MiraAPI/Roles/CustomRoleManager.cs +++ b/MiraAPI/Roles/CustomRoleManager.cs @@ -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); } diff --git a/MiraAPI/Utilities/Assets/SpriteTools.cs b/MiraAPI/Utilities/Assets/SpriteTools.cs index d9750ae..d8da0d0 100644 --- a/MiraAPI/Utilities/Assets/SpriteTools.cs +++ b/MiraAPI/Utilities/Assets/SpriteTools.cs @@ -13,26 +13,6 @@ namespace MiraAPI.Utilities.Assets; /// public static class SpriteTools { - private delegate bool DLoadImage(IntPtr tex, IntPtr data, bool markNonReadable); - - private static DLoadImage? _iCallLoadImage; - - /// - /// Load an image into a texture. - /// - /// The texture. - /// Byte data of image. - /// Mark nonreadable. - /// True if succeeded. - public static bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable) - { - _iCallLoadImage ??= IL2CPP.ResolveICall("UnityEngine.ImageConversion::LoadImage"); - - var il2CPPArray = (Il2CppStructArray)data; - - return _iCallLoadImage.Invoke(tex.Pointer, il2CPPArray.Pointer, markNonReadable); - } - /// /// Load a sprite from a resource path. /// @@ -46,7 +26,7 @@ public static class SpriteTools if (myStream != null) { var buttonTexture = myStream.ReadFully(); - LoadImage(tex, buttonTexture, false); + tex.LoadImage(buttonTexture, false); } else {