diff --git a/EXILED/Exiled.Events/Patches/Fixes/FixExplosionArmor.cs b/EXILED/Exiled.Events/Patches/Fixes/FixExplosionArmor.cs new file mode 100644 index 000000000..3561619ba --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Fixes/FixExplosionArmor.cs @@ -0,0 +1,114 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- +#pragma warning disable SA1402 // File may only contain a single type + +namespace Exiled.Events.Patches.Fixes +{ + using System; + using System.Collections.Generic; + using System.Reflection.Emit; + using System.Runtime.CompilerServices; + + using Exiled.API.Features.Pools; + + using Footprinting; + + using HarmonyLib; + + using InventorySystem.Items.Armor; + + using PlayerStatsSystem; + + using UnityEngine; + + using static HarmonyLib.AccessTools; + + /// + /// Patches cttor. + /// Fix Explosion damage calculation using Attacker BodyArmor instead of the one from Victim. + /// Bug was already reported to NW (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/work_items/3243). + /// + [HarmonyPatch(typeof(ExplosionDamageHandler), MethodType.Constructor, new Type[] { typeof(Footprint), typeof(Vector3), typeof(float), typeof(int), typeof(ExplosionType) })] + + internal class FixExplosionArmor + { +#pragma warning disable SA1600 // Elements should be documented + internal static readonly ConditionalWeakTable> Memory = new(); +#pragma warning restore SA1600 // Elements should be documented + + private static IEnumerable Transpiler(IEnumerable instructions) + { + List newInstructions = ListPool.Pool.Get(instructions); + + // remove the "if (armorPenetration == 0) return;" + newInstructions.RemoveRange(0, 5); + + int offset = 1; + int index = newInstructions.FindLastIndex(x => x.StoresField(Field(typeof(ExplosionDamageHandler), nameof(ExplosionDamageHandler._serverLogsText)))) + offset; + + newInstructions.InsertRange(index, new List() + { + // this.Damage = damage; + new(OpCodes.Ldarg_0), + new(OpCodes.Ldarg_3), + new(OpCodes.Callvirt, PropertySetter(typeof(StandardDamageHandler), nameof(StandardDamageHandler.Damage))), + + // FixExplosionArmor.Memory.Add(this, new StrongBox(value)) + new(OpCodes.Ldsfld, Field(typeof(FixExplosionArmor), nameof(FixExplosionArmor.Memory))), + new(OpCodes.Ldarg_0), + new(OpCodes.Ldarg_S, 4), + new(OpCodes.Newobj, DeclaredConstructor(typeof(StrongBox), new Type[] { typeof(int), }, false)), + new(OpCodes.Callvirt, Method(typeof(ConditionalWeakTable>), nameof(ConditionalWeakTable>.Add))), + }); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } + + /// + /// Patches . + /// Fix Explosion damage calculation using Attacker BodyArmor instead of the one from Victim. + /// Bug was already reported to NW (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/work_items/3243). + /// + [HarmonyPatch(typeof(ExplosionDamageHandler), nameof(ExplosionDamageHandler.ApplyDamage))] + internal class FixExplosionArmor2 + { +#pragma warning disable SA1600 // Elements should be documented + internal static float HelperMethod(ExplosionDamageHandler @this, ReferenceHub ply) +#pragma warning restore SA1600 // Elements should be documented + { + if (ply.inventory.TryGetBodyArmor(out BodyArmor bodyArmor) && FixExplosionArmor.Memory.TryGetValue(@this, out StrongBox armorPenetration)) + { + return BodyArmorUtils.ProcessDamage(bodyArmor.VestEfficacy, @this.Damage, armorPenetration.Value); + } + + return @this.Damage; + } + + private static IEnumerable Transpiler(IEnumerable instructions) + { + List newInstructions = ListPool.Pool.Get(instructions); + + newInstructions.InsertRange(0, new List() + { + // base.Damage = FixExplosionArmor2.HelperMethod(this, ply); + new(OpCodes.Ldarg_0), + new(OpCodes.Ldarg_0), + new(OpCodes.Ldarg_1), + new(OpCodes.Call, Method(typeof(FixExplosionArmor2), nameof(FixExplosionArmor2.HelperMethod))), + new(OpCodes.Callvirt, PropertySetter(typeof(StandardDamageHandler), nameof(StandardDamageHandler.Damage))), + }); + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file