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..9404938e42 100644
--- a/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs
@@ -7,145 +7,53 @@
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 System;
- using API.Features.Pools;
+ 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 UnityEngine;
-
- 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);
-
- int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Ldloc_2);
-
- Label continueLabel = generator.DefineLabel();
+ ShotEventArgs args = new(result, __instance);
+ Handlers.Player.OnShot(args);
- LocalBuilder ev = generator.DeclareLocal(typeof(ShotEventArgs));
-
- newInstructions.InsertRange(
- index,
- new[]
+ try
+ {
+ if (args.CanDamageDestructibles)
{
- // 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),
+ foreach (DestructibleHitPair destructible in result.Destructibles)
+ __instance.ServerApplyDestructibleDamage(destructible, result);
+ }
- // 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 (args.CanDamageObstacles)
{
- // 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];
+ foreach (HitRayPair obstacle in result.Obstacles)
+ __instance.ServerApplyObstacleDamage(obstacle, result);
+ }
- 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 =
+ __instance.ServerSendAllIndicators(result);
+ }
+ catch (Exception e)
{
- // hitInfo
- new(OpCodes.Ldloc_1),
-
- // 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];
+ Log.Error($"Error in {nameof(Shot)} event - invalid modification of ev.{nameof(ShotEventArgs.ShotResult)} has caused an exception: {e}");
+ }
- ListPool.Pool.Return(newInstructions);
+ return false;
}
}
}