diff --git a/EXILED/Exiled.Events/EventArgs/Scp173/AddingObserverEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp173/AddingObserverEventArgs.cs new file mode 100644 index 0000000000..b47ee821c6 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp173/AddingObserverEventArgs.cs @@ -0,0 +1,51 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod 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 Interfaces; + + /// + /// Contains all information before a player sees SCP-173. + /// + public class AddingObserverEventArgs : IScp173Event, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public AddingObserverEventArgs(Player player, Player observer, bool isAllowed = true) + { + Scp173 = player.Role.As(); + Player = player; + Observer = observer; + IsAllowed = isAllowed; + } + + /// + public Scp173Role Scp173 { get; } + + /// + /// Gets the target who has looked at SCP-173. + /// + public Player Observer { get; } + + /// + /// Gets the player who's controlling SCP-173. + /// + public Player Player { get; } + + /// + /// Gets or sets a value indicating whether the player can be added as an observer. + /// + public bool IsAllowed { get; set; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp173/RemovedObserverEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp173/RemovedObserverEventArgs.cs new file mode 100644 index 0000000000..a941cfeea6 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp173/RemovedObserverEventArgs.cs @@ -0,0 +1,44 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod 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 Interfaces; + + /// + /// Contains all information after a player stops looking at SCP-173. + /// + public class RemovedObserverEventArgs : IScp173Event + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public RemovedObserverEventArgs(Player player, Player observer) + { + Scp173 = player.Role.As(); + Player = player; + Observer = observer; + } + + /// + public Scp173Role Scp173 { get; } + + /// + /// Gets the player who's controlling SCP-173. + /// + public Player Player { get; } + + /// + /// Gets the target who no longer sees SCP-173. + /// + public Player Observer { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Handlers/Scp173.cs b/EXILED/Exiled.Events/Handlers/Scp173.cs index ea6e0311ea..b5d577d5cd 100644 --- a/EXILED/Exiled.Events/Handlers/Scp173.cs +++ b/EXILED/Exiled.Events/Handlers/Scp173.cs @@ -42,6 +42,16 @@ public static class Scp173 /// public static Event BeingObserved { get; set; } = new(); + /// + /// Invoked before a player starts looking at SCP-173. + /// + public static Event AddingObserver { get; set; } = new(); + + /// + /// Invoked after a player stops looking at SCP-173. + /// + public static Event RemovedObserver { get; set; } = new(); + /// /// Called before players near SCP-173 blink. /// @@ -71,5 +81,17 @@ public static class Scp173 /// /// The instance. public static void OnBeingObserved(BeingObservedEventArgs ev) => BeingObserved.InvokeSafely(ev); + + /// + /// Called before player starts looking at SCP-173. + /// + /// The instance. + public static void OnAddingObserver(AddingObserverEventArgs ev) => AddingObserver.InvokeSafely(ev); + + /// + /// Called after a player stops looking at SCP-173. + /// + /// The instance. + public static void OnRemovedObserver(RemovedObserverEventArgs ev) => RemovedObserver.InvokeSafely(ev); } } diff --git a/EXILED/Exiled.Events/Patches/Events/Scp173/Observers.cs b/EXILED/Exiled.Events/Patches/Events/Scp173/Observers.cs new file mode 100644 index 0000000000..4f3cee3a94 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp173/Observers.cs @@ -0,0 +1,117 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp173 +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using API.Features; + using Attributes; + using Exiled.API.Features.Pools; + using Exiled.Events.EventArgs.Scp173; + using HarmonyLib; + using PlayerRoles.PlayableScps.Scp173; + using PlayerRoles.Subroutines; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . + /// Adds the event. + /// + [EventPatch(typeof(Handlers.Scp173), nameof(Handlers.Scp173.AddingObserver))] + [EventPatch(typeof(Handlers.Scp173), nameof(Handlers.Scp173.RemovedObserver))] + [HarmonyPatch(typeof(Scp173ObserversTracker), nameof(Scp173ObserversTracker.UpdateObserver))] + internal static class Observers + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + // AddingObserver patch + Label returnLabel = generator.DefineLabel(); + LocalBuilder ev = generator.DeclareLocal(typeof(AddingObserverEventArgs)); + + int index = newInstructions.FindIndex(x => x.Calls(Method(typeof(HashSet), nameof(HashSet.Add)))) + 2; + newInstructions[index].labels.Add(returnLabel); + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // Player.Get(Owner); + new(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // Player.Get(ply); + new(OpCodes.Ldarg_1), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // true + new(OpCodes.Ldc_I4_1), + + // AddingObserverEventArgs ev = new(Player.Get(Owner), Player.Get(ply), true); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(AddingObserverEventArgs))[0]), + new(OpCodes.Stloc, ev), + + // Scp173.OnAddingObserver(ev); + new(OpCodes.Ldloc, ev), + new(OpCodes.Call, Method(typeof(Handlers.Scp173), nameof(Handlers.Scp173.OnAddingObserver))), + + // if (!ev.IsAllowed) + new(OpCodes.Ldloc, ev), + new(OpCodes.Callvirt, + PropertyGetter(typeof(AddingObserverEventArgs), nameof(AddingObserverEventArgs.IsAllowed))), + new(OpCodes.Brtrue_S, returnLabel), + + // Observers.Remove(ply); + new(OpCodes.Ldarg_0), + new(OpCodes.Ldfld, Field(typeof(Scp173ObserversTracker), nameof(Scp173ObserversTracker.Observers))), + new(OpCodes.Ldarg_1), + new(OpCodes.Callvirt, Method(typeof(HashSet), nameof(HashSet.Remove))), + new(OpCodes.Pop), + + // return 0; + new(OpCodes.Ldc_I4_0), + new(OpCodes.Ret), + }); + + // RemoveObserver patch + int index2 = newInstructions.FindLastIndex(x => x.Calls(Method(typeof(HashSet), nameof(HashSet.Remove)))) + 2; + + LocalBuilder ev2 = generator.DeclareLocal(typeof(RemovedObserverEventArgs)); + + newInstructions.InsertRange(index2, new CodeInstruction[] + { + // Player.Get(Owner); + new(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, + PropertyGetter(typeof(StandardSubroutine), nameof(StandardSubroutine.Owner))), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // Player.Get(ply); + new(OpCodes.Ldarg_1), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // new RemovedObserverEventArgs(Player.Get(Owner), Player.Get(ply)); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovedObserverEventArgs))[0]), + new(OpCodes.Stloc, ev2), + + // Scp173.OnRemovingObserver(new RemovedObserverEventArgs(Player.Get(Owner), Player.Get(ply))); + new(OpCodes.Ldloc, ev2), + new(OpCodes.Call, Method(typeof(Handlers.Scp173), nameof(Handlers.Scp173.OnRemovedObserver))), + }); + + for (int z = 0; z < newInstructions.Count; z++) + { + yield return newInstructions[z]; + } + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file