From 04647d93c481cb7142ccf129a473309fd0a9631f Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Thu, 25 Dec 2025 21:46:19 +0100 Subject: [PATCH 1/9] fix: FixEffectOrder --- .../Patches/Fixes/FixEffectOrder.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs diff --git a/EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs b/EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs new file mode 100644 index 000000000..e3917aa1a --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs @@ -0,0 +1,52 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Fixes +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using API.Features.Pools; + using CustomPlayerEffects; + using Exiled.API.Features; + using HarmonyLib; + + using static HarmonyLib.AccessTools; + + /// + /// Patches delegate. + /// Fix than NW do not updated the EffectDuration before Intensity https://github.com/northwood-studios/LabAPI/issues/248. + /// + [HarmonyPatch(typeof(StatusEffectBase), nameof(StatusEffectBase.ServerSetState))] + internal class FixEffectOrder + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + // Find the setter call index + int intensityCallIndex = newInstructions.FindIndex(ci => ci.Calls(PropertySetter(typeof(StatusEffectBase), nameof(StatusEffectBase.Intensity)))); + + // Extract: ldarg.0, ldarg.1, call set_Intensity + List intensityBlock = newInstructions.GetRange(intensityCallIndex - 2, 3); + + // Remove it from original location + newInstructions.RemoveRange(intensityCallIndex - 2, 3); + + // Find ServerChangeDuration call + int serverChangeIndex = newInstructions.FindIndex(ci => ci.Calls(Method(typeof(StatusEffectBase), nameof(StatusEffectBase.ServerChangeDuration)))); + + // Insert AFTER ServerChangeDuration + newInstructions.InsertRange(serverChangeIndex + 1, intensityBlock); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} From 4eb54a584f383ba4ece082ee700c307f37f89166 Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Sat, 27 Dec 2025 15:31:53 +0100 Subject: [PATCH 2/9] more fix --- EXILED/Exiled.API/Features/Player.cs | 6 +-- .../Exiled.API/Features/Roles/Scp049Role.cs | 3 +- .../Patches/Fixes/FixEffectOrder.cs | 51 ++++++++++++++++++- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs index 3a7c05c20..8e4f9e165 100644 --- a/EXILED/Exiled.API/Features/Player.cs +++ b/EXILED/Exiled.API/Features/Player.cs @@ -3564,8 +3564,7 @@ public void ChangeEffectIntensity(byte intensity, float duration = 0) { if (ReferenceHub.playerEffectsController.TryGetEffect(out T statusEffect)) { - statusEffect.Intensity = intensity; - statusEffect.ServerChangeDuration(duration, true); + statusEffect.ServerSetState(intensity, duration, false); } } @@ -3579,8 +3578,7 @@ public void ChangeEffectIntensity(EffectType type, byte intensity, float duratio { if (TryGetEffect(type, out StatusEffectBase statusEffect)) { - statusEffect.Intensity = intensity; - statusEffect.ServerChangeDuration(duration, false); + statusEffect.ServerSetState(intensity, duration, false); } } diff --git a/EXILED/Exiled.API/Features/Roles/Scp049Role.cs b/EXILED/Exiled.API/Features/Roles/Scp049Role.cs index 1c015e2ff..6283226eb 100644 --- a/EXILED/Exiled.API/Features/Roles/Scp049Role.cs +++ b/EXILED/Exiled.API/Features/Roles/Scp049Role.cs @@ -271,8 +271,7 @@ public void Attack(Player player) else { cardiacArrest.SetAttacker(AttackAbility.Owner); - cardiacArrest.Intensity = 1; - cardiacArrest.ServerChangeDuration(AttackAbility._statusEffectDuration, false); + cardiacArrest.ServerSetState(1, AttackAbility._statusEffectDuration, false); } SenseAbility.OnServerHit(AttackAbility._target); diff --git a/EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs b/EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs index e3917aa1a..91e850366 100644 --- a/EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs +++ b/EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs @@ -8,17 +8,22 @@ namespace Exiled.Events.Patches.Fixes { using System.Collections.Generic; + using System.Reflection; using System.Reflection.Emit; using API.Features.Pools; using CustomPlayerEffects; using Exiled.API.Features; using HarmonyLib; + using InventorySystem.Items.Usables.Scp330; + using PlayerRoles.PlayableScps.Scp049; using static HarmonyLib.AccessTools; +#pragma warning disable SA1402 // File may only contain a single type + /// - /// Patches delegate. + /// Patches . /// Fix than NW do not updated the EffectDuration before Intensity https://github.com/northwood-studios/LabAPI/issues/248. /// [HarmonyPatch(typeof(StatusEffectBase), nameof(StatusEffectBase.ServerSetState))] @@ -49,4 +54,46 @@ private static IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions); } } -} + + /// + /// Patches Method that should use . + /// Fix than NW do not updated the EffectDuration before Intensity https://github.com/northwood-studios/LabAPI/issues/248. + /// + [HarmonyPatch] + internal class FixEffectOrder2 + { + private static IEnumerable TargetMethods() + { + yield return Method(typeof(CandyYellow), nameof(CandyYellow.ServerApplyEffects)); + yield return Method(typeof(Scp049AttackAbility), nameof(Scp049AttackAbility.ServerProcessCmd)); + yield return Method(typeof(SugarCrave), nameof(SugarCrave.Disabled)); + } + + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int intensityCallIndex = newInstructions.FindIndex(ci => ci.Calls(PropertySetter(typeof(StatusEffectBase), nameof(StatusEffectBase.Intensity)))); + + int offset = 6; + if (newInstructions[intensityCallIndex - offset].opcode == OpCodes.Dup) + { + // specific to Yellow candy that get it's Instance from a OpCodes.Dup + newInstructions.RemoveAt(intensityCallIndex); + newInstructions.RemoveAt(intensityCallIndex - offset); + } + else + { + newInstructions.RemoveRange(intensityCallIndex, 2); + } + + int serverChangeIndex = newInstructions.FindIndex(ci => ci.Calls(Method(typeof(StatusEffectBase), nameof(StatusEffectBase.ServerChangeDuration)))); + + newInstructions[serverChangeIndex] = new CodeInstruction(OpCodes.Callvirt, Method(typeof(StatusEffectBase), nameof(StatusEffectBase.ServerSetState))); + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file From 5165f58324d96d17957b148ebb1c0bfc991caefd Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Sat, 27 Dec 2025 16:10:17 +0100 Subject: [PATCH 3/9] ReceivingEffectEventArgs::Intensity --- .../EventArgs/Player/ReceivingEffectEventArgs.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EXILED/Exiled.Events/EventArgs/Player/ReceivingEffectEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ReceivingEffectEventArgs.cs index 168a0f578..fe4574f28 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ReceivingEffectEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ReceivingEffectEventArgs.cs @@ -34,7 +34,7 @@ public ReceivingEffectEventArgs(Player player, StatusEffectBase effect, byte int Effect = effect; this.intensity = intensity; CurrentIntensity = currentIntensity; - Duration = duration; + Duration = intensity is 0 ? 0 : duration; } /// @@ -63,7 +63,7 @@ public byte Intensity { intensity = value; - if (intensity == 0) + if (value == 0 && intensity != 0) IsAllowed = false; } } From 4e023680d21d33d3612456a4bc4e7a7a6cea8181 Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Sat, 27 Dec 2025 16:10:53 +0100 Subject: [PATCH 4/9] change Duration to TimeLeft the real Duration of the Effect --- .../Patches/Events/Player/ReceivingStatusEffect.cs | 12 ++++++------ EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs | 11 ++++++++--- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/EXILED/Exiled.Events/Patches/Events/Player/ReceivingStatusEffect.cs b/EXILED/Exiled.Events/Patches/Events/Player/ReceivingStatusEffect.cs index 9d22a80b6..707743a24 100644 --- a/EXILED/Exiled.Events/Patches/Events/Player/ReceivingStatusEffect.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/ReceivingStatusEffect.cs @@ -23,9 +23,9 @@ namespace Exiled.Events.Patches.Events.Player /// /// Patches the method. - /// Adds the event. + /// Adds the event and fix NW Duration not being correctly set to 0 when effect is Reset. /// - [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.ReceivingEffect))] + // [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.ReceivingEffect))] [HarmonyPatch(typeof(StatusEffectBase), nameof(StatusEffectBase.ForceIntensity))] internal static class ReceivingStatusEffect { @@ -74,9 +74,9 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions); // Find the setter call index - int intensityCallIndex = newInstructions.FindIndex(ci => ci.Calls(PropertySetter(typeof(StatusEffectBase), nameof(StatusEffectBase.Intensity)))); + int offset = -2; + int intensityCallIndex = newInstructions.FindIndex(ci => ci.Calls(PropertySetter(typeof(StatusEffectBase), nameof(StatusEffectBase.Intensity)))) + offset; // Extract: ldarg.0, ldarg.1, call set_Intensity - List intensityBlock = newInstructions.GetRange(intensityCallIndex - 2, 3); + List intensityBlock = newInstructions.GetRange(intensityCallIndex, 3); // Remove it from original location - newInstructions.RemoveRange(intensityCallIndex - 2, 3); + newInstructions.RemoveRange(intensityCallIndex, 3); + newInstructions[intensityCallIndex].WithLabels(intensityBlock[0].ExtractLabels()); // Find ServerChangeDuration call int serverChangeIndex = newInstructions.FindIndex(ci => ci.Calls(Method(typeof(StatusEffectBase), nameof(StatusEffectBase.ServerChangeDuration)))); @@ -49,7 +51,10 @@ private static IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions); } From 0b393e6af364af24102eac9485e2c56a69579cd4 Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Sat, 27 Dec 2025 16:34:14 +0100 Subject: [PATCH 5/9] Remove Debug line --- EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs b/EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs index 9a596e82a..6ea4a5ac0 100644 --- a/EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs +++ b/EXILED/Exiled.Events/Patches/Fixes/FixEffectOrder.cs @@ -51,10 +51,7 @@ private static IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions); } From e201aea1e2cb2b7e4f230dc3c0d313ca7032d5e2 Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Sat, 27 Dec 2025 16:44:26 +0100 Subject: [PATCH 6/9] fix: use TimeLeft for Duration --- EXILED/Exiled.API/Features/Effect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXILED/Exiled.API/Features/Effect.cs b/EXILED/Exiled.API/Features/Effect.cs index f90196691..426b68dcd 100644 --- a/EXILED/Exiled.API/Features/Effect.cs +++ b/EXILED/Exiled.API/Features/Effect.cs @@ -35,7 +35,7 @@ public Effect(StatusEffectBase statusEffectBase) if (!statusEffectBase.TryGetEffectType(out EffectType effect)) Log.Error($"EffectType not found please report to Exiled BugReport : {statusEffectBase}"); Type = effect; - Duration = statusEffectBase.Duration; + Duration = statusEffectBase.TimeLeft; Intensity = statusEffectBase.Intensity; IsEnabled = statusEffectBase.IsEnabled; } From 53be605e1eeef6dad178c20556db1a54e9fe1837 Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Sat, 27 Dec 2025 16:44:48 +0100 Subject: [PATCH 7/9] better method for it --- EXILED/Exiled.API/Features/Player.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs index 8e4f9e165..6874edb59 100644 --- a/EXILED/Exiled.API/Features/Player.cs +++ b/EXILED/Exiled.API/Features/Player.cs @@ -3433,10 +3433,7 @@ public void SyncEffect(Effect effect) { if (effect.IsEnabled) { - EnableEffect(effect.Type, effect.Duration, effect.AddDurationIfActive); - - if (effect.Intensity > 0) - ChangeEffectIntensity(effect.Type, effect.Intensity, effect.Duration); + EnableEffect(effect.Type, effect.Intensity, effect.Duration, effect.AddDurationIfActive); } } From 6e4eb8a3d475a3b8dbf0aa05c806f9def48f8ed9 Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Sat, 27 Dec 2025 17:26:08 +0100 Subject: [PATCH 8/9] Trying this option --- .../EventArgs/Player/ReceivingEffectEventArgs.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/EXILED/Exiled.Events/EventArgs/Player/ReceivingEffectEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ReceivingEffectEventArgs.cs index fe4574f28..ba8e4fa37 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ReceivingEffectEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ReceivingEffectEventArgs.cs @@ -18,8 +18,6 @@ namespace Exiled.Events.EventArgs.Player /// public class ReceivingEffectEventArgs : IPlayerEvent, IDeniableEvent { - private byte intensity; - /// /// Initializes a new instance of the class. /// @@ -32,7 +30,7 @@ public ReceivingEffectEventArgs(Player player, StatusEffectBase effect, byte int { Player = player; Effect = effect; - this.intensity = intensity; + Intensity = intensity; CurrentIntensity = currentIntensity; Duration = intensity is 0 ? 0 : duration; } @@ -56,17 +54,7 @@ public ReceivingEffectEventArgs(Player player, StatusEffectBase effect, byte int /// Gets or sets the value of the new intensity of the effect. Setting this to 0 is the same as setting IsAllowed to /// . /// - public byte Intensity - { - get => intensity; - set - { - intensity = value; - - if (value == 0 && intensity != 0) - IsAllowed = false; - } - } + public byte Intensity { get; set; } /// /// Gets the value of the intensity of this effect on the player. From 18d1ec577a6c3f324128afadb87adc5bc9a3f8e4 Mon Sep 17 00:00:00 2001 From: Yamato <66829532+louis1706@users.noreply.github.com> Date: Sat, 27 Dec 2025 17:59:26 +0100 Subject: [PATCH 9/9] doc: updated --- .../Exiled.Events/EventArgs/Player/ReceivingEffectEventArgs.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/EXILED/Exiled.Events/EventArgs/Player/ReceivingEffectEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ReceivingEffectEventArgs.cs index ba8e4fa37..038a3e16c 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ReceivingEffectEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ReceivingEffectEventArgs.cs @@ -51,8 +51,7 @@ public ReceivingEffectEventArgs(Player player, StatusEffectBase effect, byte int public float Duration { get; set; } = 0; /// - /// Gets or sets the value of the new intensity of the effect. Setting this to 0 is the same as setting IsAllowed to - /// . + /// Gets or sets the value of the new intensity of the effect. /// public byte Intensity { get; set; }