diff --git a/EXILED/Exiled.Events/EventArgs/Item/DisruptorFiringEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Item/DisruptorFiringEventArgs.cs
new file mode 100644
index 0000000000..ad3feef017
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Item/DisruptorFiringEventArgs.cs
@@ -0,0 +1,56 @@
+// ----------------DissolveMatPool-------------------------------------------------------
+//
+// 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.Pickups;
+ using Interfaces;
+ using InventorySystem.Items.Firearms.Modules;
+ using InventorySystem.Items.Pickups;
+
+ ///
+ /// Contains all information before a pickup shoot while on the ground.
+ ///
+ public class DisruptorFiringEventArgs : IDeniableEvent, IPickupEvent
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public DisruptorFiringEventArgs(Pickup disruptor, API.Features.Player attacker, DisruptorActionModule.FiringState state, bool isAllowed = true)
+ {
+ Pickup = disruptor;
+ Attacker = attacker;
+ State = state;
+ IsAllowed = isAllowed;
+ }
+
+ ///
+ /// Gets or Sets a value indicating whether the disruptor shoot and the ground.
+ /// The client will still see all effects, like sounds and shoot.
+ ///
+ public bool IsAllowed { get; set; }
+
+ ///
+ /// Gets or Sets whether is the attacker.
+ ///
+ public API.Features.Player Attacker { get; set; }
+
+ ///
+ /// Gets the state of the weapon.
+ ///
+ public DisruptorActionModule.FiringState State { get; }
+
+ ///
+ /// Gets the pickup who shot the bullet.
+ ///
+ public Pickup Pickup { get; }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Handlers/Item.cs b/EXILED/Exiled.Events/Handlers/Item.cs
index 1c3f8b15e0..d8153a586e 100644
--- a/EXILED/Exiled.Events/Handlers/Item.cs
+++ b/EXILED/Exiled.Events/Handlers/Item.cs
@@ -54,10 +54,23 @@ public static class Item
public static Event UsingRadioPickupBattery { get; set; } = new();
///
- /// Invoked before a state is changed.
+ /// Invoked before a state is changed.
///
public static Event ChangingMicroHIDPickupState { get; set; } = new();
+ ///
+ /// Invoked before a firing while on the ground.
+ /// The client will still see all effects, like sounds and shoot.
+ ///
+ public static Event DisruptorFiring { get; set; } = new();
+
+ ///
+ /// Called before a firing while on the ground.
+ /// WARNING: Client still receive the shoot sound AND the ammo is still removed. (even if = false).
+ ///
+ /// The instance.
+ public static void OnDisruptorFiring(DisruptorFiringEventArgs ev) => DisruptorFiring.InvokeSafely(ev);
+
///
/// Called before the ammo of an firearm is changed.
///
diff --git a/EXILED/Exiled.Events/Patches/Events/Item/DisruptorFiring.cs b/EXILED/Exiled.Events/Patches/Events/Item/DisruptorFiring.cs
new file mode 100644
index 0000000000..889cbada1c
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Events/Item/DisruptorFiring.cs
@@ -0,0 +1,89 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) ExMod Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Events.Item
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using Exiled.API.Features;
+ using Exiled.API.Features.Pickups;
+ using Exiled.API.Features.Pools;
+ using Exiled.Events.Attributes;
+ using Exiled.Events.EventArgs.Item;
+ using Footprinting;
+ using HarmonyLib;
+ using InventorySystem.Items;
+ using InventorySystem.Items.Firearms;
+ using InventorySystem.Items.Firearms.Extensions;
+ using InventorySystem.Items.Firearms.Modules;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches . Adds the event.
+ ///
+ [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.DisruptorFiring))]
+ [HarmonyPatch(typeof(DisruptorWorldmodelActionExtension), nameof(DisruptorWorldmodelActionExtension.ServerFire))]
+ public class DisruptorFiring
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ LocalBuilder ev = generator.DeclareLocal(typeof(DisruptorFiringEventArgs));
+ Label continueLabel = generator.DefineLabel();
+ newInstructions.InsertRange(0, new CodeInstruction[]
+ {
+ // Pickup.Get(this._worldmodel.Identifier.SerialNumber)
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Ldfld, Field(typeof(DisruptorWorldmodelActionExtension), nameof(DisruptorWorldmodelActionExtension._worldmodel))),
+ new(OpCodes.Callvirt, PropertyGetter(typeof(FirearmWorldmodel), nameof(FirearmWorldmodel.Identifier))),
+ new(OpCodes.Ldfld, Field(typeof(ItemIdentifier), nameof(ItemIdentifier.SerialNumber))),
+ new(OpCodes.Call, Method(typeof(Pickup), nameof(Pickup.Get), new[] { typeof(ushort) })),
+
+ // Player.Get(this._scheduledAttackerFootprint)
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Ldfld, Field(typeof(DisruptorWorldmodelActionExtension), nameof(DisruptorWorldmodelActionExtension._scheduledAttackerFootprint))),
+ new(OpCodes.Call, Method(typeof(API.Features.Player), nameof(API.Features.Player.Get), new[] { typeof(Footprint) })),
+
+ // this._scheduledFiringState
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Ldfld, Field(typeof(DisruptorWorldmodelActionExtension), nameof(DisruptorWorldmodelActionExtension._scheduledFiringState))),
+
+ // true
+ new(OpCodes.Ldc_I4_0),
+
+ // DisruptorFiringEventArgs ev = new DisruptorFiringEventArgs(Pickup.Get(this._worldmodel.Identifier.SerialNumber), Player.Get(this._scheduledAttackerFootprint), this._scheduledFiringState, true)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(DisruptorFiringEventArgs))[0]),
+ new(OpCodes.Stloc_S, ev.LocalIndex),
+
+ // Handlers.Item.OnDisruptorFiring(ev);
+ new(OpCodes.Ldloc_S, ev.LocalIndex),
+ new(OpCodes.Call, Method(typeof(Handlers.Item), nameof(Handlers.Item.OnDisruptorFiring))),
+
+ // if (!ev.IsAllowed) return;
+ new(OpCodes.Ldloc_S, ev.LocalIndex),
+ new (OpCodes.Callvirt, PropertyGetter(typeof(DisruptorFiringEventArgs), nameof(DisruptorFiringEventArgs.IsAllowed))),
+ new(OpCodes.Brtrue_S, continueLabel),
+ new(OpCodes.Ret),
+
+ // this._scheduledAttackerFootprint = Attacker.Footprint;
+ new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex).WithLabels(continueLabel),
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Callvirt, PropertyGetter(typeof(DisruptorFiringEventArgs), nameof(DisruptorFiringEventArgs.Attacker))),
+ new(OpCodes.Callvirt, PropertyGetter(typeof(API.Features.Player), nameof(API.Features.Player.Footprint))),
+ new(OpCodes.Stfld, Field(typeof(DisruptorWorldmodelActionExtension), nameof(DisruptorWorldmodelActionExtension._scheduledAttackerFootprint))),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
\ No newline at end of file