diff --git a/EXILED/Exiled.Events/EventArgs/Item/InspectedItemEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Item/InspectedItemEventArgs.cs new file mode 100644 index 0000000000..283fd4910f --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Item/InspectedItemEventArgs.cs @@ -0,0 +1,35 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Item +{ + using Exiled.API.Features; + using Exiled.API.Features.Items; + using Exiled.Events.EventArgs.Interfaces; + using InventorySystem.Items; + + /// + /// Contains all information before weapon is inspected. + /// + public class InspectedItemEventArgs : IItemEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + public InspectedItemEventArgs(ItemBase item) + { + Item = Item.Get(item); + } + + /// + public Player Player => Item.Owner; + + /// + public Item Item { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Item/InspectingItemEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Item/InspectingItemEventArgs.cs new file mode 100644 index 0000000000..5bb7cb4077 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Item/InspectingItemEventArgs.cs @@ -0,0 +1,41 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Item +{ + using Exiled.API.Features; + using Exiled.API.Features.Items; + using Exiled.Events.EventArgs.Interfaces; + using InventorySystem.Items; + + /// + /// Contains all information before weapon is inspected. + /// + public class InspectingItemEventArgs : IItemEvent, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public InspectingItemEventArgs(ItemBase item, bool isAllowed = true) + { + Item = Item.Get(item); + IsAllowed = isAllowed; + } + + /// + public Player Player => Item.Owner; + + /// + public Item Item { get; } + + /// + /// Setter will not work if inspected is a or a . + public bool IsAllowed { get; set; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/EventArgs/Scp939/PlacingMimicPointEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp939/PlacingMimicPointEventArgs.cs new file mode 100644 index 0000000000..a761942caf --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp939/PlacingMimicPointEventArgs.cs @@ -0,0 +1,48 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Scp939 +{ + using Exiled.API.Features; + using Exiled.API.Features.Roles; + using Exiled.Events.EventArgs.Interfaces; + using RelativePositioning; + + /// + /// Contains all information before mimicry point is placed. + /// + public class PlacingMimicPointEventArgs : IScp939Event, IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public PlacingMimicPointEventArgs(Player player, RelativePosition position, bool isAllowed = true) + { + Player = player; + Scp939 = player.Role.As(); + Position = position; + IsAllowed = isAllowed; + } + + /// + public Player Player { get; } + + /// + public Scp939Role Scp939 { get; } + + /// + public bool IsAllowed { get; set; } + + /// + /// Gets or sets a position of mimicry point. + /// + public RelativePosition Position { get; set; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Handlers/Item.cs b/EXILED/Exiled.Events/Handlers/Item.cs index d8153a586e..4cca796fef 100644 --- a/EXILED/Exiled.Events/Handlers/Item.cs +++ b/EXILED/Exiled.Events/Handlers/Item.cs @@ -58,6 +58,16 @@ public static class Item /// public static Event ChangingMicroHIDPickupState { get; set; } = new(); + /// + /// Invoked before item inspection is started. + /// + public static Event InspectingItem { get; set; } = new(); + + /// + /// Invoked after item inspection is started. + /// + public static Event InspectedItem { get; set; } = new(); + /// /// Invoked before a firing while on the ground. /// The client will still see all effects, like sounds and shoot. @@ -118,5 +128,17 @@ public static class Item /// /// The instance. public static void OnChangingMicroHIDPickupState(ChangingMicroHIDPickupStateEventArgs ev) => ChangingMicroHIDPickupState.InvokeSafely(ev); + + /// + /// Called before item inspection is started. + /// + /// The instance. + public static void OnInspectingItem(InspectingItemEventArgs ev) => InspectingItem.InvokeSafely(ev); + + /// + /// Called before item inspection is started. + /// + /// The instance. + public static void OnInspectedItem(InspectedItemEventArgs ev) => InspectedItem.InvokeSafely(ev); } } \ No newline at end of file diff --git a/EXILED/Exiled.Events/Handlers/Scp939.cs b/EXILED/Exiled.Events/Handlers/Scp939.cs index d2ca5e4533..d5d5da378e 100644 --- a/EXILED/Exiled.Events/Handlers/Scp939.cs +++ b/EXILED/Exiled.Events/Handlers/Scp939.cs @@ -74,6 +74,11 @@ public static class Scp939 /// public static Event ValidatingVisibility { get; set; } = new(); + /// + /// Invoked before mimicry point is placed. + /// + public static Event PlacingMimicPoint { get; set; } = new(); + /// /// Called before SCP-939 changes its target focus. /// @@ -139,5 +144,11 @@ public static class Scp939 /// /// The instance. public static void OnValidatingVisibility(ValidatingVisibilityEventArgs ev) => ValidatingVisibility.InvokeSafely(ev); + + /// + /// Called before mimicry point is placed. + /// + /// The instance. + public static void OnPlacingMimicPoint(PlacingMimicPointEventArgs ev) => PlacingMimicPoint.InvokeSafely(ev); } } \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Item/Inspect.cs b/EXILED/Exiled.Events/Patches/Events/Item/Inspect.cs new file mode 100644 index 0000000000..860d064ea1 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Item/Inspect.cs @@ -0,0 +1,319 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Item +{ +#pragma warning disable SA1402 +#pragma warning disable SA1649 + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Item; + using HarmonyLib; + using InventorySystem.Items.Firearms.Modules; + using InventorySystem.Items.Jailbird; + using InventorySystem.Items.Keycards; + using InventorySystem.Items.MicroHID.Modules; + using InventorySystem.Items.Usables.Scp1344; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add and event. + /// + [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.InspectingItem))] + [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.InspectedItem))] + [HarmonyPatch(typeof(SimpleInspectorModule), nameof(SimpleInspectorModule.ServerProcessCmd))] + internal class InspectWeapon + { + private static IEnumerable Transpiler(IEnumerable instructions) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int index = newInstructions.FindLastIndex(x => x.opcode == OpCodes.Ldarg_0); + + newInstructions.InsertRange(index, new[] + { + // this.Firearm + new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Callvirt, PropertyGetter(typeof(SimpleInspectorModule), nameof(SimpleInspectorModule.Firearm))), + + // true + new(OpCodes.Ldc_I4_1), + + // InspectingItemEventArgs ev = new(this.Firearm) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(InspectingItemEventArgs))[0]), + + // Handlers.Item.OnInspectingItem(ev) + new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnInspectingItem))), + }); + + newInstructions.InsertRange(newInstructions.Count - 1, new CodeInstruction[] + { + // this.Firearm + new(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(SimpleInspectorModule), nameof(SimpleInspectorModule.Firearm))), + + // InspectedItemEventArgs ev = new(this.Firearm) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(InspectedItemEventArgs))[0]), + + // Handlers.Item.OnInspectedItem(ev) + new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnInspectedItem))), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } + + /// + /// Patches + /// to add and event. + /// + [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.InspectingItem))] + [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.InspectedItem))] + [HarmonyPatch(typeof(Scp1344NetworkHandler), nameof(Scp1344NetworkHandler.TryInspect))] + internal class InspectScp1344 + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int offset = 1; + int index = newInstructions.FindIndex(x => x.Is(OpCodes.Callvirt, PropertyGetter(typeof(Scp1344Item), nameof(Scp1344Item.AllowInspect)))) + offset; + + LocalBuilder allowed = generator.DeclareLocal(typeof(bool)); + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // store isAllowed + new(OpCodes.Stloc_S, allowed.LocalIndex), + + // curInstance + new(OpCodes.Ldloc_0), + + // load isAllowed + new(OpCodes.Ldloc_S, allowed.LocalIndex), + + // InspectingItemEventArgs ev = new(curInstance, isAllowed); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(InspectingItemEventArgs))[0]), + new(OpCodes.Dup), + + // Handlers.Item.OnInspectingItem(ev); + new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnInspectingItem))), + + // load isAllowed + new(OpCodes.Callvirt, PropertyGetter(typeof(InspectingItemEventArgs), nameof(InspectingItemEventArgs.IsAllowed))), + }); + + newInstructions.InsertRange(newInstructions.Count - 1, new CodeInstruction[] + { + // curInstance + new(OpCodes.Ldloc_0), + + // InspectedItemEventArgs = new(curInstance); + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(InspectedItemEventArgs))[0]), + + // Handlers.Item.OnInspectedItem(ev); + new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnInspectedItem))), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } + + /// + /// Patches + /// to add and event. + /// + [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.InspectingItem))] + [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.InspectedItem))] + [HarmonyPatch(typeof(KeycardItem), nameof(KeycardItem.ServerProcessCmd))] + internal class InspectKeycard + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int offset = 1; + int index = newInstructions.FindLastIndex(x => x.opcode == OpCodes.Ldloc_1) + offset; + + LocalBuilder allowed = generator.DeclareLocal(typeof(bool)); + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // save flag value as isAllowed + new(OpCodes.Stloc_S, allowed.LocalIndex), + + // this + new(OpCodes.Ldarg_0), + + // load isAllowed + new(OpCodes.Ldloc_S, allowed.LocalIndex), + + // InspectingItemEventArgs ev = new(this, isAllowed) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(InspectingItemEventArgs))[0]), + new(OpCodes.Dup), + + // Handlers.Item.OnInspectingItem(ev) + new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnInspectingItem))), + + // load isAllowed + new(OpCodes.Callvirt, PropertyGetter(typeof(InspectingItemEventArgs), nameof(InspectingItemEventArgs.IsAllowed))), + }); + + newInstructions.InsertRange(newInstructions.Count - 1, new CodeInstruction[] + { + // this + new(OpCodes.Ldarg_0), + + // InspectedItemEventArgs ev = new(this) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(InspectedItemEventArgs))[0]), + + // Handlers.Item.OnInspectedItem(ev) + new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnInspectedItem))), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + + private static void Return(KeycardItem item) => item.ServerSendPrivateRpc(x => KeycardItem.WriteInspect(x, false)); + } + + /// + /// Patches + /// to add and event. + /// + [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.InspectingItem))] + [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.InspectedItem))] + [HarmonyPatch(typeof(JailbirdItem), nameof(JailbirdItem.ServerProcessCmd))] + internal class InspectJailbird + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int offset = 1; + int index = newInstructions.FindLastIndex(x => x.opcode == OpCodes.Ldloc_2) + offset; + + LocalBuilder allowed = generator.DeclareLocal(typeof(bool)); + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // save flag value as isAllowed + new(OpCodes.Stloc_S, allowed.LocalIndex), + + // this + new(OpCodes.Ldarg_0), + + // load isAllowed + new(OpCodes.Ldloc_S, allowed.LocalIndex), + + // InspectingItemEventArgs ev = new(this, isAllowed) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(InspectingItemEventArgs))[0]), + new(OpCodes.Dup), + + // Handlers.Item.OnInspectingItem(ev) + new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnInspectingItem))), + + // load isAllowed + new(OpCodes.Callvirt, PropertyGetter(typeof(InspectingItemEventArgs), nameof(InspectingItemEventArgs.IsAllowed))), + }); + + index = newInstructions.FindLastIndex(x => x.Calls(Method(typeof(JailbirdItem), nameof(JailbirdItem.SendRpc)))) + offset; + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // this + new(OpCodes.Ldarg_0), + + // InspectedItemEventArgs ev = new(this) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(InspectedItemEventArgs))[0]), + + // Handlers.Item.OnInspectedItem(ev) + new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnInspectedItem))), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } + + /// + /// Patches + /// to add and event. + /// + [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.InspectingItem))] + [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.InspectedItem))] + [HarmonyPatch(typeof(DrawAndInspectorModule), nameof(DrawAndInspectorModule.ServerProcessCmd))] + internal class InspectMicroHid + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int index = newInstructions.FindLastIndex(x => x.opcode == OpCodes.Ldarg_0); + + Label returnLabel = generator.DefineLabel(); + + newInstructions.InsertRange(index, new[] + { + // this.MicroHid + new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]), + new(OpCodes.Callvirt, PropertyGetter(typeof(DrawAndInspectorModule), nameof(DrawAndInspectorModule.MicroHid))), + + // true + new(OpCodes.Ldc_I4_1), + + // InspectingItemEventArgs ev = new(this.MicroHid, true) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(InspectingItemEventArgs))[0]), + new(OpCodes.Dup), + + // Handlers.Item.OnInspectingItem(ev) + new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnInspectingItem))), + + // if (!ev.IsAllowed) + // return + new(OpCodes.Callvirt, PropertyGetter(typeof(InspectingItemEventArgs), nameof(InspectingItemEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, returnLabel), + }); + + newInstructions.InsertRange(newInstructions.Count - 1, new CodeInstruction[] + { + // this.MicroHid + new(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(DrawAndInspectorModule), nameof(DrawAndInspectorModule.MicroHid))), + + // InspectedItemEventArgs = new(this.MicroHid) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(InspectedItemEventArgs))[0]), + + // Handlers.Item.OnInspectedItem(ev) + new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnInspectedItem))), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(returnLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp939/PlacingMimicPoint.cs b/EXILED/Exiled.Events/Patches/Events/Scp939/PlacingMimicPoint.cs new file mode 100644 index 0000000000..753291df01 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp939/PlacingMimicPoint.cs @@ -0,0 +1,84 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp939 +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Scp939; + using HarmonyLib; + using PlayerRoles.PlayableScps.Scp939.Mimicry; + + using static HarmonyLib.AccessTools; + + /// + /// Patches + /// to add event. + /// + [EventPatch(typeof(Handlers.Scp939), nameof(Handlers.Scp939.PlacingMimicPoint))] + [HarmonyPatch(typeof(MimicPointController), nameof(MimicPointController.ServerProcessCmd))] + internal class PlacingMimicPoint + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int offset = 1; + int index = newInstructions.FindLastIndex(x => x.opcode == OpCodes.Stfld) + offset; + + Label returnLabel = generator.DefineLabel(); + + LocalBuilder ev = generator.DeclareLocal(typeof(PlacingMimicPointEventArgs)); + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // Player.Get(this.Owner) + new(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(MimicPointController), nameof(MimicPointController.Owner))), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + + // this._syncPos + new(OpCodes.Ldarg_0), + new(OpCodes.Ldfld, Field(typeof(MimicPointController), nameof(MimicPointController._syncPos))), + + // true + new(OpCodes.Ldc_I4_1), + + // PlacingMimicPointEventArgs = new(Player.Get(this.Owner), this._syncPos, true) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(PlacingMimicPointEventArgs))[0]), + new(OpCodes.Dup), + new(OpCodes.Dup), + new(OpCodes.Stloc_S, ev.LocalIndex), + + // Handlers.Scp939.OnPlacingMimicPoint(ev) + new(OpCodes.Call, Method(typeof(Handlers.Scp939), nameof(Handlers.Scp939.OnPlacingMimicPoint))), + + // if (!ev.IsAllowed) + // return + new(OpCodes.Callvirt, PropertyGetter(typeof(PlacingMimicPointEventArgs), nameof(PlacingMimicPointEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, returnLabel), + + // this._syncPos = ev.Position + new(OpCodes.Ldarg_0), + new(OpCodes.Ldloc_S, ev.LocalIndex), + new(OpCodes.Callvirt, PropertyGetter(typeof(PlacingMimicPointEventArgs), nameof(PlacingMimicPointEventArgs.Position))), + new(OpCodes.Stfld, Field(typeof(MimicPointController), nameof(MimicPointController._syncPos))), + }); + + newInstructions[newInstructions.Count - 1].labels.Add(returnLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file