diff --git a/EXILED/Exiled.Events/EventArgs/Player/ConsumableActivatingEffectsEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ConsumableActivatingEffectsEventArgs.cs new file mode 100644 index 000000000..7210db91c --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Player/ConsumableActivatingEffectsEventArgs.cs @@ -0,0 +1,46 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Player +{ + using Exiled.API.Features.Items; + using Exiled.Events.EventArgs.Interfaces; + + using BaseConsumable = InventorySystem.Items.Usables.Consumable; + + /// + /// Interesting. + /// + public class ConsumableActivatingEffectsEventArgs : IPlayerEvent, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public ConsumableActivatingEffectsEventArgs(ReferenceHub referenceHub, BaseConsumable consumable, bool isAllowed = true) + { + Player = API.Features.Player.Get(referenceHub); + IsAllowed = isAllowed; + Consumable = (Consumable)Item.Get(consumable); + } + + /// + /// Gets the player that consumed the . + /// + public API.Features.Player Player { get; } + + /// + public bool IsAllowed { get; set; } + + /// + /// Gets the that was consumed. + /// + public Consumable Consumable { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs index c03bcebeb..10cb36799 100644 --- a/EXILED/Exiled.Events/Handlers/Player.cs +++ b/EXILED/Exiled.Events/Handlers/Player.cs @@ -651,6 +651,11 @@ public class Player /// public static Event Scp1576TransmissionEnded { get; set; } = new(); + /// + /// Invoked before 's consumable activates effects. + /// + public static Event ConsumableActivatingEffects { get; set; } = new(); + /// /// Called before a player's emotion changed. /// @@ -1434,5 +1439,11 @@ public static void OnItemRemoved(ReferenceHub referenceHub, InventorySystem.Item /// /// The instance. public static void OnScp1576TransmissionEnded(Scp1576TransmissionEndedEventArgs ev) => Scp1576TransmissionEnded.InvokeSafely(ev); + + /// + /// Called before 's consumable activates its effects. + /// + /// The instance. + public static void OnConsumableActivatingEffects(ConsumableActivatingEffectsEventArgs ev) => ConsumableActivatingEffects.InvokeSafely(ev); } } diff --git a/EXILED/Exiled.Events/Patches/Events/Player/ConsumableActivatingEffects.cs b/EXILED/Exiled.Events/Patches/Events/Player/ConsumableActivatingEffects.cs new file mode 100644 index 000000000..c4e944b8d --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Player/ConsumableActivatingEffects.cs @@ -0,0 +1,76 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +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; + + /// + /// Patches and adds event. + /// + [HarmonyPatch(typeof(Consumable), nameof(Consumable.ActivateEffects))] + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.ConsumableActivatingEffects))] + internal static class ConsumableActivatingEffects + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.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() + { + new(OpCodes.Ldarg_0), + }); + + newInstructions.InsertRange(secondIndex, new List() + { + // 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.Pool.Return(newInstructions); + } + } +} \ No newline at end of file