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