Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EXILED::Events] BeingObserved event #72

Closed
wants to merge 14 commits into from
57 changes: 57 additions & 0 deletions EXILED/Exiled.Events/EventArgs/Scp173/BeingObservedEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// -----------------------------------------------------------------------
// <copyright file="BeingObservedEventArgs.cs" company="Exiled Team">
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.EventArgs.Scp173
{
using Exiled.API.Features;
using Exiled.API.Features.Roles;
using Exiled.Events.EventArgs.Interfaces;

/// <summary>
/// Contains all the information before SCP-173 is observed.
/// </summary>
public class BeingObservedEventArgs : IScp173Event, IDeniableEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="BeingObservedEventArgs" /> class.
/// </summary>
/// <param name="target">
/// The <see cref="Exiled.API.Features.Player"/> instance of the target.
/// </param>
/// <param name="scp173">
/// The <see cref="Exiled.API.Features.Player"/> instance of the SCP-173.
/// </param>
/// <param name="isAllowed">
/// Whether or not the target will be counted as observing the SCP-173.
/// </param>
public BeingObservedEventArgs(API.Features.Player target, API.Features.Player scp173, bool isAllowed = true)
{
Target = target;
Player = scp173;
Scp173 = scp173.Role.As<Scp173Role>();
IsAllowed = isAllowed;
}

/// <summary>
/// Gets the player who's observing the SCP-173.
/// </summary>
public Player Target { get; }

/// <summary>
/// Gets the player who's being observed.
/// </summary>
public Player Player { get; }

/// <inheritdoc/>
public Scp173Role Scp173 { get; }

/// <summary>
/// Gets or sets a value indicating whether or not the player can be counted as observing.
/// </summary>
public bool IsAllowed { get; set; }
}
}
13 changes: 12 additions & 1 deletion EXILED/Exiled.Events/Handlers/Scp173.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ public static class Scp173
/// </summary>
public static Event<UsingBreakneckSpeedsEventArgs> UsingBreakneckSpeeds { get; set; } = new();

/// <summary>
/// Invoked before SCP-173 is observed.
/// </summary>
public static Event<BeingObservedEventArgs> BeingObserved { get; set; } = new();

/// <summary>
/// Called before players near SCP-173 blink.
/// </summary>
Expand All @@ -60,5 +65,11 @@ public static class Scp173
/// </summary>
/// <param name="ev">The <see cref="UsingBreakneckSpeedsEventArgs" /> instance.</param>
public static void OnUsingBreakneckSpeeds(UsingBreakneckSpeedsEventArgs ev) => UsingBreakneckSpeeds.InvokeSafely(ev);

/// <summary>
/// Called before Scp 173 is observed.
6hundred9 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <param name="ev">The <see cref="BeingObservedEventArgs" /> instance.</param>
public static void OnBeingObserved(BeingObservedEventArgs ev) => BeingObserved.InvokeSafely(ev);
}
}
}
83 changes: 83 additions & 0 deletions EXILED/Exiled.Events/Patches/Events/Scp173/BeingObserved.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// -----------------------------------------------------------------------
// <copyright file="BeingObserved.cs" company="Exiled Team">
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

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;

/// <summary>
/// Patches <see cref="Scp173ObserversTracker.IsObservedBy" />.
/// Adds the <see cref="Handlers.Scp173.BeingObserved" /> event.
/// </summary>
[EventPatch(typeof(Handlers.Scp173), nameof(Handlers.Scp173.BeingObserved))]
[HarmonyPatch(typeof(Scp173ObserversTracker), nameof(Scp173ObserversTracker.IsObservedBy))]
internal static class BeingObserved
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.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<Scp173Role>), nameof(StandardSubroutine<Scp173Role>.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<CodeInstruction>.Pool.Return(newInstructions);
}
}
}
Loading