Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// -----------------------------------------------------------------------
// <copyright file="ConsumableActivatingEffectsEventArgs.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.EventArgs.Player
{
using Exiled.API.Features.Items;
using Exiled.Events.EventArgs.Interfaces;

using BaseConsumable = InventorySystem.Items.Usables.Consumable;

/// <summary>
/// Interesting.
/// </summary>
public class ConsumableActivatingEffectsEventArgs : IPlayerEvent, IDeniableEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="ConsumableActivatingEffectsEventArgs"/> class.
/// </summary>
/// <param name="referenceHub"><inheritdoc cref="ReferenceHub"/></param>
/// <param name="consumable"><inheritdoc cref="BaseConsumable" /></param>
/// <param name="isAllowed"><inheritdoc cref="bool" /></param>
public ConsumableActivatingEffectsEventArgs(ReferenceHub referenceHub, BaseConsumable consumable, bool isAllowed = true)
{
Player = API.Features.Player.Get(referenceHub);
IsAllowed = isAllowed;
Consumable = (Consumable)Item.Get(consumable);
}

/// <summary>
/// Gets the player that consumed the <see cref="Consumable"/>.
/// </summary>
public API.Features.Player Player { get; }

/// <inheritdoc />
public bool IsAllowed { get; set; }

/// <summary>
/// Gets the <see cref="API.Features.Items.Consumable"/> that was consumed.
/// </summary>
public Consumable Consumable { get; }
}
}
11 changes: 11 additions & 0 deletions EXILED/Exiled.Events/Handlers/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,11 @@ public class Player
/// </summary>
public static Event<Scp1576TransmissionEndedEventArgs> Scp1576TransmissionEnded { get; set; } = new();

/// <summary>
/// Invoked before <see cref="API.Features.Player"/>'s consumable activates effects.
/// </summary>
public static Event<ConsumableActivatingEffectsEventArgs> ConsumableActivatingEffects { get; set; } = new();

/// <summary>
/// Called before a player's emotion changed.
/// </summary>
Expand Down Expand Up @@ -1434,5 +1439,11 @@ public static void OnItemRemoved(ReferenceHub referenceHub, InventorySystem.Item
/// </summary>
/// <param name="ev">The <see cref="Scp1576TransmissionEndedEventArgs"/> instance.</param>
public static void OnScp1576TransmissionEnded(Scp1576TransmissionEndedEventArgs ev) => Scp1576TransmissionEnded.InvokeSafely(ev);

/// <summary>
/// Called before <see cref="API.Features.Player"/>'s consumable activates its effects.
/// </summary>
/// <param name="ev">The <see cref="ConsumableActivatingEffectsEventArgs"/> instance.</param>
public static void OnConsumableActivatingEffects(ConsumableActivatingEffectsEventArgs ev) => ConsumableActivatingEffects.InvokeSafely(ev);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// -----------------------------------------------------------------------
// <copyright file="ConsumableActivatingEffects.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.Patches.Events.Player
{
using System.Collections.Generic;
using System.Reflection.Emit;

using Exiled.API.Features.Pools;
using Exiled.Events.Attributes;
using Exiled.Events.EventArgs.Player;
using HarmonyLib;
using InventorySystem.Items.Usables;

using static HarmonyLib.AccessTools;

/// <summary>
/// Patches <see cref="Consumable.ActivateEffects"/> and adds <see cref="Handlers.Player.ConsumableActivatingEffects"/> event.
/// </summary>
[HarmonyPatch(typeof(Consumable), nameof(Consumable.ActivateEffects))]
[EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.ConsumableActivatingEffects))]
internal static class ConsumableActivatingEffects
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.Pool.Get(instructions);

Label continueLabel = generator.DefineLabel();

int secondOffset = 1;
int secondIndex = newInstructions.FindIndex(i => i.opcode == OpCodes.Ldarg_0) + secondOffset;

int firstOffset = -1;
int firstIndex = newInstructions.FindIndex(i => i.opcode == OpCodes.Callvirt) + firstOffset;
newInstructions[firstIndex].WithLabels(continueLabel);
newInstructions.InsertRange(firstIndex, new List<CodeInstruction>()
{
new(OpCodes.Ldarg_0),
});

newInstructions.InsertRange(secondIndex, new List<CodeInstruction>()
{
// this.Owner
new(OpCodes.Callvirt, PropertyGetter(typeof(Consumable), nameof(Consumable.Owner))),

// this
new(OpCodes.Ldarg_0),

// true
new(OpCodes.Ldc_I4_1),

// ConsumableActivatingEffectsEventArgs ev = new(this.Owner, this, true)
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ConsumableActivatingEffectsEventArgs))[0]),
new(OpCodes.Dup),

// OnConsumableActivatingEffects(ev)
new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnConsumableActivatingEffects))),

// if (!ev.IsAllowed)
// return;
new(OpCodes.Callvirt, PropertyGetter(typeof(ConsumableActivatingEffectsEventArgs), nameof(ConsumableActivatingEffectsEventArgs.IsAllowed))),
new(OpCodes.Brtrue_S, continueLabel),
new(OpCodes.Ret),
});

for (int z = 0; z < newInstructions.Count; z++)
yield return newInstructions[z];

ListPool<CodeInstruction>.Pool.Return(newInstructions);
}
}
}
Loading