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
56 changes: 56 additions & 0 deletions EXILED/Exiled.Events/EventArgs/Scp173/BeingObservedEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// -----------------------------------------------------------------------
// <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.Roles;
using Exiled.Events.EventArgs.Interfaces;

/// <summary>
/// Contains all the information before being observed.
6hundred9 marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public class BeingObservedEventArgs : IScp173Event, IDeniableEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="BeingObservedEventArgs" /> class.
/// </summary>
/// <param name="target">
/// <inheritdoc cref="Target" />
/// </param>
/// <param name="scp173">
/// <inheritdoc cref="Player"/>
/// </param>
/// <param name="isAllowed">
/// <inheritdoc cref="IsAllowed"/>
/// </param>
6hundred9 marked this conversation as resolved.
Show resolved Hide resolved
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 API.Features.Player Target { get; }

/// <summary>
/// Gets the player who's being observed.
/// </summary>
public API.Features.Player Player { get; }
6hundred9 marked this conversation as resolved.
Show resolved Hide resolved

/// <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.
6hundred9 marked this conversation as resolved.
Show resolved Hide resolved
/// </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);
}
}
}
82 changes: 82 additions & 0 deletions EXILED/Exiled.Events/Patches/Events/Scp173/BeingObserved.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// -----------------------------------------------------------------------
// <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.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)))) + 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