From 599f679da8f2512894f2edb46132630b0436a27a Mon Sep 17 00:00:00 2001 From: michcio Date: Mon, 22 Dec 2025 18:41:38 +0100 Subject: [PATCH 01/15] Adds a event that is called after player stops talking with 1576 --- .../Scp1576/TransmisionEndedEventArgs.cs | 41 +++++++++++++ EXILED/Exiled.Events/Handlers/Scp1576.cs | 29 +++++++++ .../Events/Scp1576/TransmissionEnded.cs | 60 +++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 EXILED/Exiled.Events/EventArgs/Scp1576/TransmisionEndedEventArgs.cs create mode 100644 EXILED/Exiled.Events/Handlers/Scp1576.cs create mode 100644 EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs diff --git a/EXILED/Exiled.Events/EventArgs/Scp1576/TransmisionEndedEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp1576/TransmisionEndedEventArgs.cs new file mode 100644 index 000000000..cdc762816 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Scp1576/TransmisionEndedEventArgs.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.Scp1576 +{ + using API.Features; + using API.Features.Items; + using Interfaces; + using InventorySystem.Items.Usables.Scp1576; + + /// + /// Contains all information after transmission has ended. + /// + public class TransmissionEndedEventArgs : IPlayerEvent + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + public TransmissionEndedEventArgs(Player player, Scp1576Item scp1576Item) + { + Player = player; + Scp1576 = Item.Get(scp1576Item); + } + + /// + /// Gets that transmission has ended for. + /// + public Player Player { get; } + + /// + /// Gets the instance. + /// + public Exiled.API.Features.Items.Scp1576 Scp1576 { get; } + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Handlers/Scp1576.cs b/EXILED/Exiled.Events/Handlers/Scp1576.cs new file mode 100644 index 000000000..85055ccd2 --- /dev/null +++ b/EXILED/Exiled.Events/Handlers/Scp1576.cs @@ -0,0 +1,29 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Handlers +{ + using Exiled.Events.EventArgs.Scp1576; + using Exiled.Events.Features; + + /// + /// Handles Scp1576 related events. + /// + public class Scp1576 + { + /// + /// Invoked after transmission has ended. + /// + public static Event TransmissionEnded { get; set; } = new Event(); + + /// + /// Called after the transmission has ended. + /// + /// The instance. + public static void OnTransmisionEnded(TransmissionEndedEventArgs ev) => TransmissionEnded.InvokeSafely(ev); + } +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs b/EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs new file mode 100644 index 000000000..f308ce054 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs @@ -0,0 +1,60 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Scp1576 +{ + 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.Scp1576; + using HarmonyLib; + using InventorySystem.Items.Usables.Scp1576; + + using static HarmonyLib.AccessTools; + + /// + /// Patches to add event + /// + [EventPatch(typeof(Handlers.Scp1576), nameof(Handlers.Scp1576.TransmissionEnded))] + [HarmonyPatch(typeof(InventorySystem.Items.Usables.Scp1576.Scp1576Item), nameof(Scp1576Item.ServerStopTransmitting))] + public class TransmissionEnded + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int index = newInstructions.FindLastIndex(x => x.opcode == OpCodes.Ret); + + newInstructions.InsertRange( + index, + [ + + // Player.Get(base.Owner) + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(Scp1576Item), nameof(Scp1576Item.Owner))), + new CodeInstruction(OpCodes.Call, Method(typeof(API.Features.Player), nameof(Player.Get), [typeof(ReferenceHub)])), + + // this + new CodeInstruction(OpCodes.Ldarg_0), + + // ServerStopTransmittingEventArgs ev = new(Player, this) + new CodeInstruction(OpCodes.Newobj, GetDeclaredConstructors(typeof(TransmissionEndedEventArgs))[0]), + + // OnTransmisionEnded(ev) + new CodeInstruction(OpCodes.Call, Method(typeof(Handlers.Scp1576), nameof(Handlers.Scp1576.OnTransmisionEnded))) + ]); + + foreach (CodeInstruction instruction in newInstructions) + yield return instruction; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file From ab779b554759e6db3bffa9be21e765bcd940491e Mon Sep 17 00:00:00 2001 From: michcio Date: Mon, 22 Dec 2025 18:49:15 +0100 Subject: [PATCH 02/15] Simplify initialization of TransmissionEnded event. --- EXILED/Exiled.Events/Handlers/Scp1576.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXILED/Exiled.Events/Handlers/Scp1576.cs b/EXILED/Exiled.Events/Handlers/Scp1576.cs index 85055ccd2..cdb863837 100644 --- a/EXILED/Exiled.Events/Handlers/Scp1576.cs +++ b/EXILED/Exiled.Events/Handlers/Scp1576.cs @@ -18,7 +18,7 @@ public class Scp1576 /// /// Invoked after transmission has ended. /// - public static Event TransmissionEnded { get; set; } = new Event(); + public static Event TransmissionEnded { get; set; } = new(); /// /// Called after the transmission has ended. From a02199edd162c653f1b23b717d1996ed8f947493 Mon Sep 17 00:00:00 2001 From: michcio Date: Mon, 22 Dec 2025 19:02:38 +0100 Subject: [PATCH 03/15] should be no more warnings when build --- ...dedEventArgs.cs => TransmissionEndedEventArgs.cs} | 0 EXILED/Exiled.Events/Handlers/Scp1576.cs | 3 ++- .../Patches/Events/Scp1576/TransmissionEnded.cs | 12 ++++++------ 3 files changed, 8 insertions(+), 7 deletions(-) rename EXILED/Exiled.Events/EventArgs/Scp1576/{TransmisionEndedEventArgs.cs => TransmissionEndedEventArgs.cs} (100%) diff --git a/EXILED/Exiled.Events/EventArgs/Scp1576/TransmisionEndedEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp1576/TransmissionEndedEventArgs.cs similarity index 100% rename from EXILED/Exiled.Events/EventArgs/Scp1576/TransmisionEndedEventArgs.cs rename to EXILED/Exiled.Events/EventArgs/Scp1576/TransmissionEndedEventArgs.cs diff --git a/EXILED/Exiled.Events/Handlers/Scp1576.cs b/EXILED/Exiled.Events/Handlers/Scp1576.cs index cdb863837..5df5941a7 100644 --- a/EXILED/Exiled.Events/Handlers/Scp1576.cs +++ b/EXILED/Exiled.Events/Handlers/Scp1576.cs @@ -9,11 +9,12 @@ namespace Exiled.Events.Handlers { using Exiled.Events.EventArgs.Scp1576; using Exiled.Events.Features; +#pragma warning disable SA1623 // Property summary documentation should match accessors /// /// Handles Scp1576 related events. /// - public class Scp1576 + public static class Scp1576 { /// /// Invoked after transmission has ended. diff --git a/EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs b/EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs index f308ce054..279a600c1 100644 --- a/EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs +++ b/EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs @@ -20,7 +20,7 @@ namespace Exiled.Events.Patches.Events.Scp1576 using static HarmonyLib.AccessTools; /// - /// Patches to add event + /// Patches to add event. /// [EventPatch(typeof(Handlers.Scp1576), nameof(Handlers.Scp1576.TransmissionEnded))] [HarmonyPatch(typeof(InventorySystem.Items.Usables.Scp1576.Scp1576Item), nameof(Scp1576Item.ServerStopTransmitting))] @@ -34,12 +34,12 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Date: Mon, 22 Dec 2025 19:28:01 +0100 Subject: [PATCH 04/15] Apply suggestions from code review Co-authored-by: @Someone <45270312+Someone-193@users.noreply.github.com> --- .../EventArgs/Scp1576/TransmissionEndedEventArgs.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EXILED/Exiled.Events/EventArgs/Scp1576/TransmissionEndedEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp1576/TransmissionEndedEventArgs.cs index cdc762816..3addf53b1 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp1576/TransmissionEndedEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Scp1576/TransmissionEndedEventArgs.cs @@ -13,7 +13,7 @@ namespace Exiled.Events.EventArgs.Scp1576 using InventorySystem.Items.Usables.Scp1576; /// - /// Contains all information after transmission has ended. + /// Contains all information after a SCP-1576 transmission has ended. /// public class TransmissionEndedEventArgs : IPlayerEvent { @@ -29,7 +29,7 @@ public TransmissionEndedEventArgs(Player player, Scp1576Item scp1576Item) } /// - /// Gets that transmission has ended for. + /// Gets the that the transmission ended for. /// public Player Player { get; } From d46ad62dbecbe92186234a81baf56b29d00bdfb7 Mon Sep 17 00:00:00 2001 From: michcio Date: Mon, 22 Dec 2025 19:47:35 +0100 Subject: [PATCH 05/15] added the suggestions to make it more exiled like --- .../Patches/Events/Scp1576/TransmissionEnded.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs b/EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs index 279a600c1..90615f0eb 100644 --- a/EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs +++ b/EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs @@ -30,7 +30,7 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions); - int index = newInstructions.FindLastIndex(x => x.opcode == OpCodes.Ret); + int index = newInstructions.Count - 1; newInstructions.InsertRange( index, @@ -51,8 +51,8 @@ private static IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions); } From 46af72aa7cd44c7399c8945796a4a55c22317d06 Mon Sep 17 00:00:00 2001 From: michcio Date: Mon, 22 Dec 2025 20:53:01 +0100 Subject: [PATCH 06/15] Moved to Player Handler --- .../Scp1576TransmissionEndedEventArgs.cs} | 10 +++---- EXILED/Exiled.Events/Handlers/Player.cs | 11 +++++++ EXILED/Exiled.Events/Handlers/Scp1576.cs | 30 ------------------- .../Scp1576TransmissionEnded.cs} | 19 ++++++------ 4 files changed, 25 insertions(+), 45 deletions(-) rename EXILED/Exiled.Events/EventArgs/{Scp1576/TransmissionEndedEventArgs.cs => Player/Scp1576TransmissionEndedEventArgs.cs} (75%) delete mode 100644 EXILED/Exiled.Events/Handlers/Scp1576.cs rename EXILED/Exiled.Events/Patches/Events/{Scp1576/TransmissionEnded.cs => Player/Scp1576TransmissionEnded.cs} (77%) diff --git a/EXILED/Exiled.Events/EventArgs/Scp1576/TransmissionEndedEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/Scp1576TransmissionEndedEventArgs.cs similarity index 75% rename from EXILED/Exiled.Events/EventArgs/Scp1576/TransmissionEndedEventArgs.cs rename to EXILED/Exiled.Events/EventArgs/Player/Scp1576TransmissionEndedEventArgs.cs index 3addf53b1..6dce01109 100644 --- a/EXILED/Exiled.Events/EventArgs/Scp1576/TransmissionEndedEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/Scp1576TransmissionEndedEventArgs.cs @@ -1,11 +1,11 @@ // ----------------------------------------------------------------------- -// +// // Copyright (c) ExMod Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. // // ----------------------------------------------------------------------- -namespace Exiled.Events.EventArgs.Scp1576 +namespace Exiled.Events.EventArgs.Player { using API.Features; using API.Features.Items; @@ -15,14 +15,14 @@ namespace Exiled.Events.EventArgs.Scp1576 /// /// Contains all information after a SCP-1576 transmission has ended. /// - public class TransmissionEndedEventArgs : IPlayerEvent + public class Scp1576TransmissionEndedEventArgs : IPlayerEvent { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// - public TransmissionEndedEventArgs(Player player, Scp1576Item scp1576Item) + public Scp1576TransmissionEndedEventArgs(Player player, Scp1576Item scp1576Item) { Player = player; Scp1576 = Item.Get(scp1576Item); diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs index 97e613700..998b54f81 100644 --- a/EXILED/Exiled.Events/Handlers/Player.cs +++ b/EXILED/Exiled.Events/Handlers/Player.cs @@ -621,6 +621,11 @@ public class Player /// public static Event InteractingEmergencyButton { get; set; } = new(); + /// + /// Invoked after transmission has ended. + /// + public static Event Scp1576TransmissionEnded { get; set; } = new(); + /// /// Called before a player's emotion changed. /// @@ -1356,5 +1361,11 @@ public static void OnItemRemoved(ReferenceHub referenceHub, InventorySystem.Item /// /// The instance. public static void OnInteractingEmergencyButton(InteractingEmergencyButtonEventArgs ev) => InteractingEmergencyButton.InvokeSafely(ev); + + /// + /// Called after a 1576 transmisiion has ended. + /// + /// The instance. + public static void OnScp1576TransmissionEnded(Scp1576TransmissionEndedEventArgs ev) => Scp1576TransmissionEnded.InvokeSafely(ev); } } diff --git a/EXILED/Exiled.Events/Handlers/Scp1576.cs b/EXILED/Exiled.Events/Handlers/Scp1576.cs deleted file mode 100644 index 5df5941a7..000000000 --- a/EXILED/Exiled.Events/Handlers/Scp1576.cs +++ /dev/null @@ -1,30 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) ExMod Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.Events.Handlers -{ - using Exiled.Events.EventArgs.Scp1576; - using Exiled.Events.Features; -#pragma warning disable SA1623 // Property summary documentation should match accessors - - /// - /// Handles Scp1576 related events. - /// - public static class Scp1576 - { - /// - /// Invoked after transmission has ended. - /// - public static Event TransmissionEnded { get; set; } = new(); - - /// - /// Called after the transmission has ended. - /// - /// The instance. - public static void OnTransmisionEnded(TransmissionEndedEventArgs ev) => TransmissionEnded.InvokeSafely(ev); - } -} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs b/EXILED/Exiled.Events/Patches/Events/Player/Scp1576TransmissionEnded.cs similarity index 77% rename from EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs rename to EXILED/Exiled.Events/Patches/Events/Player/Scp1576TransmissionEnded.cs index 90615f0eb..86cfb19f1 100644 --- a/EXILED/Exiled.Events/Patches/Events/Scp1576/TransmissionEnded.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/Scp1576TransmissionEnded.cs @@ -1,30 +1,29 @@ // ----------------------------------------------------------------------- -// +// // Copyright (c) ExMod Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. // // ----------------------------------------------------------------------- -namespace Exiled.Events.Patches.Events.Scp1576 +namespace Exiled.Events.Patches.Events.Player { 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.Scp1576; + using Exiled.Events.EventArgs.Player; using HarmonyLib; using InventorySystem.Items.Usables.Scp1576; using static HarmonyLib.AccessTools; /// - /// Patches to add event. + /// Patches to add event. /// - [EventPatch(typeof(Handlers.Scp1576), nameof(Handlers.Scp1576.TransmissionEnded))] + [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.Scp1576TransmissionEnded))] [HarmonyPatch(typeof(InventorySystem.Items.Usables.Scp1576.Scp1576Item), nameof(Scp1576Item.ServerStopTransmitting))] - public class TransmissionEnded + public class Scp1576TransmissionEnded { private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) { @@ -39,16 +38,16 @@ private static IEnumerable Transpiler(IEnumerable Date: Wed, 24 Dec 2025 19:59:59 +0100 Subject: [PATCH 07/15] Update Scp1576TransmissionEnded.cs Change comments --- .../Patches/Events/Player/Scp1576TransmissionEnded.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EXILED/Exiled.Events/Patches/Events/Player/Scp1576TransmissionEnded.cs b/EXILED/Exiled.Events/Patches/Events/Player/Scp1576TransmissionEnded.cs index 86cfb19f1..2f81bec7c 100644 --- a/EXILED/Exiled.Events/Patches/Events/Player/Scp1576TransmissionEnded.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/Scp1576TransmissionEnded.cs @@ -43,10 +43,10 @@ private static IEnumerable Transpiler(IEnumerable Date: Sat, 14 Feb 2026 17:48:40 +0100 Subject: [PATCH 08/15] Add `StartUsing` method to force item usage --- EXILED/Exiled.API/Features/Items/Usable.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/EXILED/Exiled.API/Features/Items/Usable.cs b/EXILED/Exiled.API/Features/Items/Usable.cs index b58f6b8dd..57db05707 100644 --- a/EXILED/Exiled.API/Features/Items/Usable.cs +++ b/EXILED/Exiled.API/Features/Items/Usable.cs @@ -146,6 +146,14 @@ public virtual void Use(Player owner = null) Base.Owner = oldOwner.ReferenceHub; } + /// + /// Forces the item to be used. + /// + public virtual void StartUsing() + { + UsableItemsController.ServerEmulateMessage(Serial, StatusMessage.StatusType.Start); + } + /// internal override void ReadPickupInfoBefore(Pickup pickup) { From e0b05f1eef0bb00e3678469b44436898b80f2db4 Mon Sep 17 00:00:00 2001 From: michcio Date: Sat, 14 Feb 2026 17:58:20 +0100 Subject: [PATCH 09/15] adds very professional comment --- EXILED/Exiled.API/Features/Items/Usable.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/EXILED/Exiled.API/Features/Items/Usable.cs b/EXILED/Exiled.API/Features/Items/Usable.cs index 57db05707..348ecd4df 100644 --- a/EXILED/Exiled.API/Features/Items/Usable.cs +++ b/EXILED/Exiled.API/Features/Items/Usable.cs @@ -149,6 +149,9 @@ public virtual void Use(Player owner = null) /// /// Forces the item to be used. /// + /// + /// The must be holding the item. + /// public virtual void StartUsing() { UsableItemsController.ServerEmulateMessage(Serial, StatusMessage.StatusType.Start); From 76370578ac88793d5f3f0c0bee82bcf236633465 Mon Sep 17 00:00:00 2001 From: michcio Date: Sat, 14 Feb 2026 21:20:29 +0100 Subject: [PATCH 10/15] Adds stop using --- EXILED/Exiled.API/Features/Items/Usable.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/EXILED/Exiled.API/Features/Items/Usable.cs b/EXILED/Exiled.API/Features/Items/Usable.cs index 348ecd4df..7648fee09 100644 --- a/EXILED/Exiled.API/Features/Items/Usable.cs +++ b/EXILED/Exiled.API/Features/Items/Usable.cs @@ -157,6 +157,17 @@ public virtual void StartUsing() UsableItemsController.ServerEmulateMessage(Serial, StatusMessage.StatusType.Start); } + /// + /// Forces the item to stop being used. + /// + /// + /// The must be holding the item. + /// + public virtual void StopUsing() + { + UsableItemsController.ServerEmulateMessage(Serial, StatusMessage.StatusType.Cancel); + } + /// internal override void ReadPickupInfoBefore(Pickup pickup) { From 6d80eeafcccfcf8bea7a48f9fc895c6f2f4c3786 Mon Sep 17 00:00:00 2001 From: michcio Date: Sun, 15 Feb 2026 13:21:05 +0100 Subject: [PATCH 11/15] Refactor `IsUsing` property to include setter and remove redundant usage methods --- EXILED/Exiled.API/Features/Items/Usable.cs | 30 +++++----------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/EXILED/Exiled.API/Features/Items/Usable.cs b/EXILED/Exiled.API/Features/Items/Usable.cs index 7648fee09..efdff0c2f 100644 --- a/EXILED/Exiled.API/Features/Items/Usable.cs +++ b/EXILED/Exiled.API/Features/Items/Usable.cs @@ -67,9 +67,13 @@ internal Usable(ItemType type) } /// - /// Gets a value indicating whether the item is currently being used. + /// Gets or sets a value indicating whether the item is currently being used. /// - public bool IsUsing => Base.IsUsing; + public bool IsUsing + { + get => Base.IsUsing; + set => UsableItemsController.ServerEmulateMessage(Serial, value ? StatusMessage.StatusType.Start : StatusMessage.StatusType.Cancel); + } /// /// Gets or sets how long it takes to use the item. @@ -146,28 +150,6 @@ public virtual void Use(Player owner = null) Base.Owner = oldOwner.ReferenceHub; } - /// - /// Forces the item to be used. - /// - /// - /// The must be holding the item. - /// - public virtual void StartUsing() - { - UsableItemsController.ServerEmulateMessage(Serial, StatusMessage.StatusType.Start); - } - - /// - /// Forces the item to stop being used. - /// - /// - /// The must be holding the item. - /// - public virtual void StopUsing() - { - UsableItemsController.ServerEmulateMessage(Serial, StatusMessage.StatusType.Cancel); - } - /// internal override void ReadPickupInfoBefore(Pickup pickup) { From 47a7b59e723f1049e96b4ab23eccc529bb6b0721 Mon Sep 17 00:00:00 2001 From: michcio Date: Sun, 15 Feb 2026 20:12:48 +0100 Subject: [PATCH 12/15] adds a new ConsumableActivatingEffects.cs event --- .../ConsumableActivatingEffectsEventArgs.cs | 39 +++++++++ EXILED/Exiled.Events/Handlers/Player.cs | 13 +++ .../Player/ConsumableActivatingEffects.cs | 79 +++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 EXILED/Exiled.Events/EventArgs/Player/ConsumableActivatingEffectsEventArgs.cs create mode 100644 EXILED/Exiled.Events/Patches/Events/Player/ConsumableActivatingEffects.cs diff --git a/EXILED/Exiled.Events/EventArgs/Player/ConsumableActivatingEffectsEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ConsumableActivatingEffectsEventArgs.cs new file mode 100644 index 000000000..030c1c458 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Player/ConsumableActivatingEffectsEventArgs.cs @@ -0,0 +1,39 @@ +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..722a4f6ce 100644 --- a/EXILED/Exiled.Events/Handlers/Player.cs +++ b/EXILED/Exiled.Events/Handlers/Player.cs @@ -5,6 +5,8 @@ // // ----------------------------------------------------------------------- +using Mirror; + namespace Exiled.Events.Handlers { using System; @@ -651,6 +653,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 +1441,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..d5a3c08d2 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Player/ConsumableActivatingEffects.cs @@ -0,0 +1,79 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +using Exiled.API.Features; + +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; + + /// + /// + /// + [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 From 7847a813c3ccd35ed15390fcbd373864099c7b36 Mon Sep 17 00:00:00 2001 From: michcio <89903081+michcio15@users.noreply.github.com> Date: Sun, 15 Feb 2026 20:15:20 +0100 Subject: [PATCH 13/15] Delete EXILED/Exiled.API/Features/Items/Usable.cs --- EXILED/Exiled.API/Features/Items/Usable.cs | 164 --------------------- 1 file changed, 164 deletions(-) delete mode 100644 EXILED/Exiled.API/Features/Items/Usable.cs diff --git a/EXILED/Exiled.API/Features/Items/Usable.cs b/EXILED/Exiled.API/Features/Items/Usable.cs deleted file mode 100644 index efdff0c2f..000000000 --- a/EXILED/Exiled.API/Features/Items/Usable.cs +++ /dev/null @@ -1,164 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (c) ExMod Team. All rights reserved. -// Licensed under the CC BY-SA 3.0 license. -// -// ----------------------------------------------------------------------- - -namespace Exiled.API.Features.Items -{ - using Exiled.API.Extensions; - using Exiled.API.Features.Pickups; - using Exiled.API.Interfaces; - - using InventorySystem; - using InventorySystem.Items; - using InventorySystem.Items.Pickups; - using InventorySystem.Items.Usables; - - using UnityEngine; - - /// - /// A wrapper class for . - /// - public class Usable : Item, IWrapper - { - /// - /// Initializes a new instance of the class. - /// - /// The base class. - public Usable(UsableItem itemBase) - : base(itemBase) - { - Base = itemBase; - } - - /// - /// Initializes a new instance of the class. - /// - /// The of the usable item. - internal Usable(ItemType type) - : this((UsableItem)Server.Host.Inventory.CreateItemInstance(new(type, 0), false)) - { - } - - /// - /// Gets the that this class is encapsulating. - /// - public new UsableItem Base { get; } - - /// - /// Gets a value indicating whether this item is equippable. - /// - public bool Equippable => Base.AllowEquip; - - /// - /// Gets a value indicating whether this item is holsterable. - /// - public bool Holsterable => Base.AllowHolster; - - /// - /// Gets or sets the weight of the item. - /// - public new float Weight - { - get => Base._weight; - set => Base._weight = value; - } - - /// - /// Gets or sets a value indicating whether the item is currently being used. - /// - public bool IsUsing - { - get => Base.IsUsing; - set => UsableItemsController.ServerEmulateMessage(Serial, value ? StatusMessage.StatusType.Start : StatusMessage.StatusType.Cancel); - } - - /// - /// Gets or sets how long it takes to use the item. - /// - public float UseTime - { - get => Base.UseTime; - set => Base.UseTime = value; - } - - /// - /// Gets or sets how long after using starts a player has to cancel using the item. - /// - public float MaxCancellableTime - { - get => Base.MaxCancellableTime; - set => Base.MaxCancellableTime = value; - } - - /// - /// Gets or sets the cooldown between repeated uses of this item. - /// - public float RemainingCooldown - { - get => UsableItemsController.GlobalItemCooldowns.TryGetValue(Serial, out float value) ? value : -1; - set => UsableItemsController.GlobalItemCooldowns[Serial] = Time.timeSinceLevelLoad + value; - } - - /// - /// Gets all the cooldown between uses of this item. - /// - public float PlayerGetCooldown => UsableItemsController.GetCooldown(Serial, Base, UsableItemsController.GetHandler(Base.Owner)); - - /// - /// Creates the that based on this . - /// - /// The location to spawn the item. - /// The rotation of the item. - /// Whether the should be initially spawned. - /// The created . - public override Pickup CreatePickup(Vector3 position, Quaternion? rotation = null, bool spawn = true) - { - PickupSyncInfo info = new(Type, Weight, Serial); - - ItemPickupBase ipb = InventoryExtensions.ServerCreatePickup(Base, info, position, rotation ?? Quaternion.identity); - - Pickup pickup = Pickup.Get(ipb); - - if (spawn) - pickup.Spawn(); - - return pickup; - } - - /// - /// Uses the item. - /// - public virtual void Use() => Use(Owner); - - /// - /// Uses the item. - /// - /// Target to use an . - public virtual void Use(Player owner = null) - { - Player oldOwner = Owner; - owner ??= Owner; - - Base.Owner = owner.ReferenceHub; - Base.ServerOnUsingCompleted(); - - typeof(UsableItemsController).InvokeStaticEvent(nameof(UsableItemsController.ServerOnUsingCompleted), new object[] { owner.ReferenceHub, Base }); - - Base.Owner = oldOwner.ReferenceHub; - } - - /// - internal override void ReadPickupInfoBefore(Pickup pickup) - { - base.ReadPickupInfoBefore(pickup); - if (pickup is UsablePickup usablePickup) - { - UseTime = usablePickup.UseTime; - MaxCancellableTime = usablePickup.MaxCancellableTime; - } - } - } -} \ No newline at end of file From b5ee70d405eb720aaf4841c58430d39fb504f31f Mon Sep 17 00:00:00 2001 From: michcio Date: Sun, 15 Feb 2026 20:17:46 +0100 Subject: [PATCH 14/15] revert --- EXILED/Exiled.API/Features/Items/Usable.cs | 160 +++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 EXILED/Exiled.API/Features/Items/Usable.cs diff --git a/EXILED/Exiled.API/Features/Items/Usable.cs b/EXILED/Exiled.API/Features/Items/Usable.cs new file mode 100644 index 000000000..b58f6b8dd --- /dev/null +++ b/EXILED/Exiled.API/Features/Items/Usable.cs @@ -0,0 +1,160 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.API.Features.Items +{ + using Exiled.API.Extensions; + using Exiled.API.Features.Pickups; + using Exiled.API.Interfaces; + + using InventorySystem; + using InventorySystem.Items; + using InventorySystem.Items.Pickups; + using InventorySystem.Items.Usables; + + using UnityEngine; + + /// + /// A wrapper class for . + /// + public class Usable : Item, IWrapper + { + /// + /// Initializes a new instance of the class. + /// + /// The base class. + public Usable(UsableItem itemBase) + : base(itemBase) + { + Base = itemBase; + } + + /// + /// Initializes a new instance of the class. + /// + /// The of the usable item. + internal Usable(ItemType type) + : this((UsableItem)Server.Host.Inventory.CreateItemInstance(new(type, 0), false)) + { + } + + /// + /// Gets the that this class is encapsulating. + /// + public new UsableItem Base { get; } + + /// + /// Gets a value indicating whether this item is equippable. + /// + public bool Equippable => Base.AllowEquip; + + /// + /// Gets a value indicating whether this item is holsterable. + /// + public bool Holsterable => Base.AllowHolster; + + /// + /// Gets or sets the weight of the item. + /// + public new float Weight + { + get => Base._weight; + set => Base._weight = value; + } + + /// + /// Gets a value indicating whether the item is currently being used. + /// + public bool IsUsing => Base.IsUsing; + + /// + /// Gets or sets how long it takes to use the item. + /// + public float UseTime + { + get => Base.UseTime; + set => Base.UseTime = value; + } + + /// + /// Gets or sets how long after using starts a player has to cancel using the item. + /// + public float MaxCancellableTime + { + get => Base.MaxCancellableTime; + set => Base.MaxCancellableTime = value; + } + + /// + /// Gets or sets the cooldown between repeated uses of this item. + /// + public float RemainingCooldown + { + get => UsableItemsController.GlobalItemCooldowns.TryGetValue(Serial, out float value) ? value : -1; + set => UsableItemsController.GlobalItemCooldowns[Serial] = Time.timeSinceLevelLoad + value; + } + + /// + /// Gets all the cooldown between uses of this item. + /// + public float PlayerGetCooldown => UsableItemsController.GetCooldown(Serial, Base, UsableItemsController.GetHandler(Base.Owner)); + + /// + /// Creates the that based on this . + /// + /// The location to spawn the item. + /// The rotation of the item. + /// Whether the should be initially spawned. + /// The created . + public override Pickup CreatePickup(Vector3 position, Quaternion? rotation = null, bool spawn = true) + { + PickupSyncInfo info = new(Type, Weight, Serial); + + ItemPickupBase ipb = InventoryExtensions.ServerCreatePickup(Base, info, position, rotation ?? Quaternion.identity); + + Pickup pickup = Pickup.Get(ipb); + + if (spawn) + pickup.Spawn(); + + return pickup; + } + + /// + /// Uses the item. + /// + public virtual void Use() => Use(Owner); + + /// + /// Uses the item. + /// + /// Target to use an . + public virtual void Use(Player owner = null) + { + Player oldOwner = Owner; + owner ??= Owner; + + Base.Owner = owner.ReferenceHub; + Base.ServerOnUsingCompleted(); + + typeof(UsableItemsController).InvokeStaticEvent(nameof(UsableItemsController.ServerOnUsingCompleted), new object[] { owner.ReferenceHub, Base }); + + Base.Owner = oldOwner.ReferenceHub; + } + + /// + internal override void ReadPickupInfoBefore(Pickup pickup) + { + base.ReadPickupInfoBefore(pickup); + if (pickup is UsablePickup usablePickup) + { + UseTime = usablePickup.UseTime; + MaxCancellableTime = usablePickup.MaxCancellableTime; + } + } + } +} \ No newline at end of file From 34f1c017141fda44fb148401803c69ffac38cf44 Mon Sep 17 00:00:00 2001 From: michcio Date: Sun, 15 Feb 2026 20:21:39 +0100 Subject: [PATCH 15/15] no more sad compiler errors --- .../Player/ConsumableActivatingEffectsEventArgs.cs | 11 +++++++++-- EXILED/Exiled.Events/Handlers/Player.cs | 2 -- .../Events/Player/ConsumableActivatingEffects.cs | 5 +---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/EXILED/Exiled.Events/EventArgs/Player/ConsumableActivatingEffectsEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ConsumableActivatingEffectsEventArgs.cs index 030c1c458..7210db91c 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ConsumableActivatingEffectsEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ConsumableActivatingEffectsEventArgs.cs @@ -1,4 +1,11 @@ -namespace Exiled.Events.EventArgs.Player +// ----------------------------------------------------------------------- +// +// 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; @@ -24,7 +31,7 @@ public ConsumableActivatingEffectsEventArgs(ReferenceHub referenceHub, BaseConsu } /// - /// Gets the player that consumed the + /// Gets the player that consumed the . /// public API.Features.Player Player { get; } diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs index 722a4f6ce..10cb36799 100644 --- a/EXILED/Exiled.Events/Handlers/Player.cs +++ b/EXILED/Exiled.Events/Handlers/Player.cs @@ -5,8 +5,6 @@ // // ----------------------------------------------------------------------- -using Mirror; - namespace Exiled.Events.Handlers { using System; diff --git a/EXILED/Exiled.Events/Patches/Events/Player/ConsumableActivatingEffects.cs b/EXILED/Exiled.Events/Patches/Events/Player/ConsumableActivatingEffects.cs index d5a3c08d2..c4e944b8d 100644 --- a/EXILED/Exiled.Events/Patches/Events/Player/ConsumableActivatingEffects.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/ConsumableActivatingEffects.cs @@ -5,8 +5,6 @@ // // ----------------------------------------------------------------------- -using Exiled.API.Features; - namespace Exiled.Events.Patches.Events.Player { using System.Collections.Generic; @@ -21,7 +19,7 @@ namespace Exiled.Events.Patches.Events.Player using static HarmonyLib.AccessTools; /// - /// + /// Patches and adds event. /// [HarmonyPatch(typeof(Consumable), nameof(Consumable.ActivateEffects))] [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.ConsumableActivatingEffects))] @@ -69,7 +67,6 @@ private static IEnumerable Transpiler(IEnumerable