diff --git a/EXILED/Exiled.Events/EventArgs/Scp173/BeingObservedEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp173/BeingObservedEventArgs.cs new file mode 100644 index 000000000..15f248e80 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp173/BeingObservedEventArgs.cs @@ -0,0 +1,57 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp173 +{ + using Exiled.API.Features; + using Exiled.API.Features.Roles; + using Exiled.Events.EventArgs.Interfaces; + + /// + /// Contains all the information before SCP-173 is observed. + /// + public class BeingObservedEventArgs : IScp173Event, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The instance of the target. + /// + /// + /// The instance of the SCP-173. + /// + /// + /// Whether or not the target will be counted as observing the SCP-173. + /// + public BeingObservedEventArgs(API.Features.Player target, API.Features.Player scp173, bool isAllowed = true) + { + Target = target; + Player = scp173; + Scp173 = scp173.Role.As(); + IsAllowed = isAllowed; + } + + /// + /// Gets the player who's observing the SCP-173. + /// + public Player Target { get; } + + /// + /// Gets the player who's being observed. + /// + public Player Player { get; } + + /// + public Scp173Role Scp173 { get; } + + /// + /// Gets or sets a value indicating whether or not the player can be counted as observing. + /// + public bool IsAllowed { get; set; } + } +} diff --git a/EXILED/Exiled.Events/Handlers/Scp173.cs b/EXILED/Exiled.Events/Handlers/Scp173.cs index bf844714b..93e11329b 100644 --- a/EXILED/Exiled.Events/Handlers/Scp173.cs +++ b/EXILED/Exiled.Events/Handlers/Scp173.cs @@ -37,6 +37,11 @@ public static class Scp173 /// public static Event UsingBreakneckSpeeds { get; set; } = new(); + /// + /// Invoked before SCP-173 is observed. + /// + public static Event BeingObserved { get; set; } = new(); + /// /// Called before players near SCP-173 blink. /// @@ -60,5 +65,11 @@ public static class Scp173 /// /// The instance. public static void OnUsingBreakneckSpeeds(UsingBreakneckSpeedsEventArgs ev) => UsingBreakneckSpeeds.InvokeSafely(ev); + + /// + /// Called before Scp 173 is observed. + /// + /// The instance. + public static void OnBeingObserved(BeingObservedEventArgs ev) => BeingObserved.InvokeSafely(ev); } -} \ No newline at end of file +} diff --git a/EXILED/Exiled.Events/Patches/Events/Scp173/BeingObserved.cs b/EXILED/Exiled.Events/Patches/Events/Scp173/BeingObserved.cs new file mode 100644 index 000000000..d26c97205 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp173/BeingObserved.cs @@ -0,0 +1,83 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp173 +{ + using System; + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp173; + using HarmonyLib; + using PlayerRoles.PlayableScps.Scp173; + using PlayerRoles.Subroutines; + using PluginAPI.Events; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . + /// Adds the event. + /// + [EventPatch(typeof(Handlers.Scp173), nameof(Handlers.Scp173.BeingObserved))] + [HarmonyPatch(typeof(Scp173ObserversTracker), nameof(Scp173ObserversTracker.IsObservedBy))] + internal static class BeingObserved + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label continueLabel = generator.DefineLabel(); + + const int offset = -4; + int index = newInstructions.FindIndex(i => i.Is(OpCodes.Call, Method(typeof(EventManager), nameof(EventManager.ExecuteEvent), new Type[] { typeof(IEventArguments) }))) + offset; + + newInstructions.InsertRange( + index, + new CodeInstruction[] + { + // Player.Get(target) + new(OpCodes.Ldarg_1), + new(OpCodes.Call, Method(typeof(API.Features.Player), nameof(API.Features.Player.Get), new[] { typeof(ReferenceHub) })), + + // Player.Get(base.Owner) + new(OpCodes.Ldarg_0), + new(OpCodes.Call, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))), + new(OpCodes.Call, Method(typeof(API.Features.Player), nameof(API.Features.Player.Get), new[] { typeof(ReferenceHub) })), + + // true + new(OpCodes.Ldc_I4_1), + + // BeingObservedEventArgs ev = new(Player, Player, bool) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(BeingObservedEventArgs))[0]), + new(OpCodes.Dup), + + // Handlers.Scp173.OnBeingObserved(ev) + new(OpCodes.Call, Method(typeof(Handlers.Scp173), nameof(Handlers.Scp173.OnBeingObserved))), + + // if (ev.IsAllowed) + // goto continueLabel + new(OpCodes.Callvirt, PropertyGetter(typeof(BeingObservedEventArgs), nameof(BeingObservedEventArgs.IsAllowed))), + new(OpCodes.Brtrue, continueLabel), + + // return false + new(OpCodes.Ldc_I4_0), + new(OpCodes.Ret), + + // continueLabel: + new CodeInstruction(OpCodes.Nop).WithLabels(continueLabel), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +}