From 9c44ae99112fcd742189e3b047b3b3588ea95116 Mon Sep 17 00:00:00 2001 From: Banalny-Banan Date: Thu, 22 May 2025 17:04:14 +0300 Subject: [PATCH 1/4] Fix & update Shot event --- .../EventArgs/Player/ShotEventArgs.cs | 114 +++++++++++---- .../Patches/Events/Player/Shot.cs | 133 +++--------------- 2 files changed, 106 insertions(+), 141 deletions(-) diff --git a/EXILED/Exiled.Events/EventArgs/Player/ShotEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ShotEventArgs.cs index 18d2303353..de9e7fc478 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ShotEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ShotEventArgs.cs @@ -7,42 +7,61 @@ namespace Exiled.Events.EventArgs.Player { + using System; + using API.Features; using Exiled.API.Features.Items; using Interfaces; using InventorySystem.Items.Firearms.Modules; + using InventorySystem.Items.Firearms.Modules.Misc; using UnityEngine; /// /// Contains all information after a player has fired a weapon. /// - public class ShotEventArgs : IPlayerEvent, IFirearmEvent + public class ShotEventArgs : IFirearmEvent { /// /// Initializes a new instance of the class. /// + /// Hitscan result that contains all hit information. /// Hitreg module that calculated the shot. - /// Raycast hit info. - /// The firearm used. - /// The IDestructible that was hit. Can be null. - /// - public ShotEventArgs(HitscanHitregModuleBase hitregModule, RaycastHit hitInfo, InventorySystem.Items.Firearms.Firearm firearm, IDestructible destructible, float damage) + public ShotEventArgs(HitscanResult hitscanResult, HitscanHitregModuleBase hitregModule) { HitregModule = hitregModule; - RaycastHit = hitInfo; - Destructible = destructible; - Firearm = Item.Get(firearm); - Damage = damage; - + ShotResult = hitscanResult; + Firearm = Item.Get(HitregModule.Firearm); Player = Firearm.Owner; - if (Destructible is HitboxIdentity hitboxIdentity) + if (ShotResult.Destructibles.Count != 0) { - Hitbox = hitboxIdentity; - Target = Player.Get(Hitbox.TargetHub); + DestructibleHitPair firstPair = ShotResult.Destructibles[0]; + RaycastHit = firstPair.Hit; + Destructible = firstPair.Destructible; + Hitbox = Destructible as HitboxIdentity; + Target = Player.Get(Hitbox?.TargetHub); + } + else + { + if (ShotResult.Obstacles.Count > 0) + RaycastHit = ShotResult.Obstacles[0].Hit; + else + RaycastHit = ApproximateHit(); + } + + foreach (DestructibleDamageRecord damageRecord in ShotResult.DamagedDestructibles) + { + Damage += damageRecord.AppliedDamage; } } + /// + /// Gets the HitscanResult object which represents the result of weapon shot raycasts and calculations. + /// Each weapon controls how it is generated to determine the specific behavior. You can change that behaviour by modifying that object. + /// + /// + public HitscanResult ShotResult { get; } + /// /// Gets the player who fired the shot. /// @@ -62,48 +81,87 @@ public ShotEventArgs(HitscanHitregModuleBase hitregModule, RaycastHit hitInfo, I public HitscanHitregModuleBase HitregModule { get; } /// - /// Gets the raycast info. + /// Gets the hit info of the first hit. /// public RaycastHit RaycastHit { get; } /// - /// Gets the bullet travel distance. + /// Gets the bullet travel distance of the first bullet. /// public float Distance => RaycastHit.distance; /// - /// Gets the position of the hit. + /// Gets the position of the first hit. /// public Vector3 Position => RaycastHit.point; /// - /// Gets the firearm base damage at the hit distance. Actual inflicted damage may vary. + /// Gets the direction of the first bullet. + /// + public Vector3 Direction => Position - Player.CameraTransform.position; + + /// + /// Gets or sets a value indicating whether the shot can deal damage to objects such as players or glass windows. + /// Damage can be controlled on per instance basis using . + /// + public bool CanDamageDestructibles { get; set; } = true; + + /// + /// Gets or sets a value indicating whether the shot can deal damage to obstacles such as walls - spawning impact effects, or doors - breaking them in case of . + /// Damage can be controlled on per instance basis using . + /// + public bool CanDamageObstacles { get; set; } = true; + + /// + /// Gets the sum of the damage that is going to be dealt by the shot if remains unchanged. /// public float Damage { get; } /// - /// Gets the target player. Can be null. + /// Gets or sets a value indicating whether the shot can produce impact effects (e.g. bullet holes). /// - public Player Target { get; } + [Obsolete("Use CanDamageObstacles instead.")] + public bool CanSpawnImpactEffects { get => CanDamageObstacles; set => CanDamageObstacles = value; } /// - /// Gets the component of the target player that was hit. Can be null. + /// Gets or sets a value indicating whether the shot can deal damage. /// - public HitboxIdentity Hitbox { get; } + [Obsolete("Use CanDamageDestructibles instead.")] + public bool CanHurt { get => CanDamageDestructibles; set => CanDamageDestructibles = value; } /// - /// Gets the component of the hit collider. Can be null. + /// Gets the component of the first hit collider. Can be null. /// public IDestructible Destructible { get; } /// - /// Gets or sets a value indicating whether the shot can deal damage. + /// Gets the component of the first target player that was hit. Can be null. /// - public bool CanHurt { get; set; } = true; + public HitboxIdentity Hitbox { get; } /// - /// Gets or sets a value indicating whether the shot can produce impact effects (e.g. bullet holes). + /// Gets the first target player. Can be null. + /// + public Player Target { get; } + + /// + /// Reset the shot result generated by the firearm, allowing you to generate your own result. /// - public bool CanSpawnImpactEffects { get; set; } = true; + public void ResetShotResult() + { + ShotResult.Clear(); + } + + private RaycastHit ApproximateHit() + { + Ray ray = new(Player.CameraTransform.position, Player.CameraTransform.forward); + float maxDistance = HitregModule.DamageFalloffDistance + HitregModule.FullDamageDistance; + return new RaycastHit + { + distance = maxDistance, + point = ray.GetPoint(maxDistance), + normal = -ray.direction, + }; + } } -} +} \ No newline at end of file diff --git a/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs b/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs index afe7a08bfc..c60f58ef8f 100644 --- a/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs @@ -24,128 +24,35 @@ namespace Exiled.Events.Patches.Events.Player using static HarmonyLib.AccessTools; /// - /// Patches . + /// Patches . /// Adds the event. /// [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.Shot))] - [HarmonyPatch(typeof(HitscanHitregModuleBase), nameof(HitscanHitregModuleBase.ServerApplyDestructibleDamage))] - internal static class ShotTarget + [HarmonyPatch(typeof(HitscanHitregModuleBase), nameof(HitscanHitregModuleBase.ServerApplyDamage))] + internal static class Shot { - private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + // public virtual void ServerApplyDamage(HitscanResult result) +#pragma warning disable SA1313 + private static bool Prefix(HitscanResult result, HitscanHitregModuleBase __instance) +#pragma warning restore SA1313 { - List newInstructions = ListPool.Pool.Get(instructions); + ShotEventArgs args = new(result, __instance); + Handlers.Player.OnShot(args); - int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Ldloc_2); - - Label continueLabel = generator.DefineLabel(); - - LocalBuilder ev = generator.DeclareLocal(typeof(ShotEventArgs)); - - newInstructions.InsertRange( - index, - new[] - { - // this - new(OpCodes.Ldarg_0), - - // target.Raycast.Hit - new(OpCodes.Ldarg_1), - new(OpCodes.Ldfld, Field(typeof(DestructibleHitPair), nameof(DestructibleHitPair.Raycast))), - new(OpCodes.Ldfld, Field(typeof(HitRayPair), nameof(HitRayPair.Hit))), - - // this.Firearm - new(OpCodes.Ldarg_0), - new(OpCodes.Callvirt, PropertyGetter(typeof(HitscanHitregModuleBase), nameof(HitscanHitregModuleBase.Firearm))), - - // destructible - new(OpCodes.Ldloc_2), - - // damage - new(OpCodes.Ldloc_0), - - // ShotEventArgs ev = new ShotEventArgs(this, hitInfo, firearm, component, damage); - new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ShotEventArgs))[0]), - new(OpCodes.Dup), - new(OpCodes.Dup), - new(OpCodes.Stloc_S, ev.LocalIndex), - - new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnShot))), - - // if (!ev.CanHurt) num = 0; - new(OpCodes.Callvirt, PropertyGetter(typeof(ShotEventArgs), nameof(ShotEventArgs.CanHurt))), - new(OpCodes.Brtrue, continueLabel), - - new(OpCodes.Ldc_I4_0), - new(OpCodes.Stloc_0), - - new CodeInstruction(OpCodes.Nop).WithLabels(continueLabel), - }); - - index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Ldarg_0); - Label returnLabel = generator.DefineLabel(); - - newInstructions.InsertRange( - index, - new CodeInstruction[] - { - // if (!ev.CanSpawnImpactEffects) return; - new(OpCodes.Ldloc_S, ev.LocalIndex), - new(OpCodes.Callvirt, PropertyGetter(typeof(ShotEventArgs), nameof(ShotEventArgs.CanSpawnImpactEffects))), - new(OpCodes.Brfalse_S, returnLabel), - }); - - newInstructions[newInstructions.Count - 1].labels.Add(returnLabel); - - for (int z = 0; z < newInstructions.Count; z++) - yield return newInstructions[z]; - - ListPool.Pool.Return(newInstructions); - } - } - - /// - /// Patches . - /// Adds the event. - /// - [HarmonyPatch(typeof(HitscanHitregModuleBase), nameof(HitscanHitregModuleBase.ServerAppendPrescan))] - internal static class ShotMiss - { - private static IEnumerable Transpiler(IEnumerable instructions) - { - List newInstructions = ListPool.Pool.Get(instructions); - - CodeInstruction[] eventInstructions = + if (args.CanDamageDestructibles) { - // hitInfo - new(OpCodes.Ldloc_1), + foreach (DestructibleHitPair destructible in result.Destructibles) + __instance.ServerApplyDestructibleDamage(destructible, result); + } - // this.Firearm - new(OpCodes.Ldarg_0), - new(OpCodes.Callvirt, PropertyGetter(typeof(HitscanHitregModuleBase), nameof(HitscanHitregModuleBase.Firearm))), - - // (IDestructible)null - new(OpCodes.Ldnull), - - // 0f - new(OpCodes.Ldc_R4, 0f), - - // ShotEventArgs = new(this, hitInfo, this.Firearm, (IDestructible)null, 0f) - new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ShotEventArgs))[0]), - - // Handlers.Player.OnShot(ev); - new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnShot))), - }; - - int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Brtrue_S) + 1; - newInstructions.InsertRange(index, new CodeInstruction[] { new(OpCodes.Ldarg_0), }.AddRangeToArray(eventInstructions)); - - index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Ldarg_2); - newInstructions.InsertRange(index, new[] { new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]) }.AddRangeToArray(eventInstructions)); - - for (int z = 0; z < newInstructions.Count; z++) - yield return newInstructions[z]; + if (args.CanDamageObstacles) + { + foreach (HitRayPair obstacle in result.Obstacles) + __instance.ServerApplyObstacleDamage(obstacle, result); + } - ListPool.Pool.Return(newInstructions); + __instance.ServerSendAllIndicators(result); + return false; } } } From 20214e9d21d3ea80d8b1efd3f0c1da4bac1d0117 Mon Sep 17 00:00:00 2001 From: Banalny-Banan Date: Thu, 22 May 2025 18:45:01 +0300 Subject: [PATCH 2/4] Turned out I don't have better stuff to do --- .../Patches/Events/Player/Shot.cs | 68 +++++++++++++++---- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs b/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs index c60f58ef8f..55f3ae2598 100644 --- a/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs @@ -7,19 +7,14 @@ namespace Exiled.Events.Patches.Events.Player { -#pragma warning disable SA1402 -#pragma warning disable SA1649 using System.Collections.Generic; - using System.Reflection; using System.Reflection.Emit; - using API.Features.Pools; - using Exiled.Events.Attributes; + using Attributes; using Exiled.Events.EventArgs.Player; using HarmonyLib; using InventorySystem.Items.Firearms.Modules; using InventorySystem.Items.Firearms.Modules.Misc; - using UnityEngine; using static HarmonyLib.AccessTools; @@ -32,12 +27,30 @@ namespace Exiled.Events.Patches.Events.Player internal static class Shot { // public virtual void ServerApplyDamage(HitscanResult result) -#pragma warning disable SA1313 - private static bool Prefix(HitscanResult result, HitscanHitregModuleBase __instance) -#pragma warning restore SA1313 + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) { - ShotEventArgs args = new(result, __instance); - Handlers.Player.OnShot(args); + CodeMatcher matcher = new(instructions); + matcher.Start(); + + LocalBuilder eventArgs = il.DeclareLocal(typeof(ShotEventArgs)); + + /* Insert event call + ShotEventArgs args = new ShotEventArgs(result, this); + Handlers.Player.OnShot(args);*/ + matcher.Insert(new List + { + // ShotEventArgs args = new ShotEventArgs(result, this); + new(OpCodes.Ldarg_1), // result + new(OpCodes.Ldarg_0), // this + new(OpCodes.Newobj, Constructor(typeof(ShotEventArgs), new[] { typeof(HitscanResult), typeof(HitscanHitregModuleBase) })), + new(OpCodes.Stloc, eventArgs), + + // Handlers.Player.OnShot(args); + new(OpCodes.Ldloc, eventArgs), + new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnShot))), + }).Advance(6); + + /* Insert the if's: if (args.CanDamageDestructibles) { @@ -49,10 +62,37 @@ private static bool Prefix(HitscanResult result, HitscanHitregModuleBase __insta { foreach (HitRayPair obstacle in result.Obstacles) __instance.ServerApplyObstacleDamage(obstacle, result); - } + }*/ + + bool LoadsShotResult(CodeInstruction i) => i.IsLdarg(1); + + // Labels for skipping loops + Label skipDestructiblesLabel = il.DefineLabel(); + Label skipObstaclesLabel = il.DefineLabel(); + + matcher + .SearchForward(LoadsShotResult) + .Insert(new List + { + new(OpCodes.Ldloc, eventArgs), + new(OpCodes.Callvirt, PropertyGetter(typeof(ShotEventArgs), nameof(ShotEventArgs.CanDamageDestructibles))), + new(OpCodes.Brfalse, skipDestructiblesLabel), + }) + .SearchForward(LoadsShotResult).Advance(1) // Skip result inside of foreach + + .SearchForward(LoadsShotResult) + .Insert(new List + { + new(OpCodes.Ldloc, eventArgs), + new(OpCodes.Callvirt, PropertyGetter(typeof(ShotEventArgs), nameof(ShotEventArgs.CanDamageObstacles))), + new(OpCodes.Brfalse, skipObstaclesLabel), + }).AddLabels(new[] { skipDestructiblesLabel }) + .SearchForward(LoadsShotResult).Advance(1) // Skip result inside of foreach + + .SearchForward(i => i.IsLdarg(0)) // Go before the last call + .AddLabels(new[] { skipObstaclesLabel }); - __instance.ServerSendAllIndicators(result); - return false; + return matcher.Instructions(); } } } From 8799715a35455ddfdd9cd3b3b6fa064a1cf1a9fb Mon Sep 17 00:00:00 2001 From: Banalny-Banan Date: Fri, 23 May 2025 01:43:47 +0300 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=98=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Patches/Events/Player/Shot.cs | 87 +++++-------------- 1 file changed, 24 insertions(+), 63 deletions(-) diff --git a/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs b/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs index 55f3ae2598..f9b16ae26b 100644 --- a/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs @@ -7,17 +7,15 @@ namespace Exiled.Events.Patches.Events.Player { - using System.Collections.Generic; - using System.Reflection.Emit; + using System; - using Attributes; + using Exiled.API.Features; + using Exiled.Events.Attributes; using Exiled.Events.EventArgs.Player; using HarmonyLib; using InventorySystem.Items.Firearms.Modules; using InventorySystem.Items.Firearms.Modules.Misc; - using static HarmonyLib.AccessTools; - /// /// Patches . /// Adds the event. @@ -27,72 +25,35 @@ namespace Exiled.Events.Patches.Events.Player internal static class Shot { // public virtual void ServerApplyDamage(HitscanResult result) - private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator il) +#pragma warning disable SA1313 + private static bool Prefix(HitscanResult result, HitscanHitregModuleBase __instance) +#pragma warning restore SA1313 { - CodeMatcher matcher = new(instructions); - matcher.Start(); - - LocalBuilder eventArgs = il.DeclareLocal(typeof(ShotEventArgs)); - - /* Insert event call - ShotEventArgs args = new ShotEventArgs(result, this); - Handlers.Player.OnShot(args);*/ - matcher.Insert(new List - { - // ShotEventArgs args = new ShotEventArgs(result, this); - new(OpCodes.Ldarg_1), // result - new(OpCodes.Ldarg_0), // this - new(OpCodes.Newobj, Constructor(typeof(ShotEventArgs), new[] { typeof(HitscanResult), typeof(HitscanHitregModuleBase) })), - new(OpCodes.Stloc, eventArgs), + ShotEventArgs args = new(result, __instance); + Handlers.Player.OnShot(args); - // Handlers.Player.OnShot(args); - new(OpCodes.Ldloc, eventArgs), - new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnShot))), - }).Advance(6); - - /* Insert the if's: - - if (args.CanDamageDestructibles) + try { - foreach (DestructibleHitPair destructible in result.Destructibles) - __instance.ServerApplyDestructibleDamage(destructible, result); - } - - if (args.CanDamageObstacles) - { - foreach (HitRayPair obstacle in result.Obstacles) - __instance.ServerApplyObstacleDamage(obstacle, result); - }*/ - - bool LoadsShotResult(CodeInstruction i) => i.IsLdarg(1); - - // Labels for skipping loops - Label skipDestructiblesLabel = il.DefineLabel(); - Label skipObstaclesLabel = il.DefineLabel(); - - matcher - .SearchForward(LoadsShotResult) - .Insert(new List + if (args.CanDamageDestructibles) { - new(OpCodes.Ldloc, eventArgs), - new(OpCodes.Callvirt, PropertyGetter(typeof(ShotEventArgs), nameof(ShotEventArgs.CanDamageDestructibles))), - new(OpCodes.Brfalse, skipDestructiblesLabel), - }) - .SearchForward(LoadsShotResult).Advance(1) // Skip result inside of foreach + foreach (DestructibleHitPair destructible in result.Destructibles) + __instance.ServerApplyDestructibleDamage(destructible, result); + } - .SearchForward(LoadsShotResult) - .Insert(new List + if (args.CanDamageObstacles) { - new(OpCodes.Ldloc, eventArgs), - new(OpCodes.Callvirt, PropertyGetter(typeof(ShotEventArgs), nameof(ShotEventArgs.CanDamageObstacles))), - new(OpCodes.Brfalse, skipObstaclesLabel), - }).AddLabels(new[] { skipDestructiblesLabel }) - .SearchForward(LoadsShotResult).Advance(1) // Skip result inside of foreach + foreach (HitRayPair obstacle in result.Obstacles) + __instance.ServerApplyObstacleDamage(obstacle, result); + } - .SearchForward(i => i.IsLdarg(0)) // Go before the last call - .AddLabels(new[] { skipObstaclesLabel }); + __instance.ServerSendAllIndicators(result); + } + catch (Exception e) + { + Log.Error($"Error in {nameof(Shot)} event - invalud modification of ev.{nameof(ShotEventArgs.ShotResult)} has caused an exception: {e}"); + } - return matcher.Instructions(); + return false; } } } From 5175d4c8276011ad0107287fb33bf389d2220ef3 Mon Sep 17 00:00:00 2001 From: Banalny-Banan Date: Fri, 23 May 2025 01:45:26 +0300 Subject: [PATCH 4/4] typo --- EXILED/Exiled.Events/Patches/Events/Player/Shot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs b/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs index f9b16ae26b..9404938e42 100644 --- a/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs @@ -50,7 +50,7 @@ private static bool Prefix(HitscanResult result, HitscanHitregModuleBase __insta } catch (Exception e) { - Log.Error($"Error in {nameof(Shot)} event - invalud modification of ev.{nameof(ShotEventArgs.ShotResult)} has caused an exception: {e}"); + Log.Error($"Error in {nameof(Shot)} event - invalid modification of ev.{nameof(ShotEventArgs.ShotResult)} has caused an exception: {e}"); } return false;