]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Fire MeleeHitEvent on misses. (#12867)
authorVordenburg <114301317+Vordenburg@users.noreply.github.com>
Mon, 13 Feb 2023 12:55:39 +0000 (07:55 -0500)
committerGitHub <noreply@github.com>
Mon, 13 Feb 2023 12:55:39 +0000 (12:55 +0000)
* Fire MeleeHitEvent when there are no targets.

* Prevent certain weapons from activating if they had no hit entities on hit.

* Prevent miss events from firing when target is yourself or was deleted.

* Use .Value as Target is already known not to be null.

* uid changes

---------

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
Content.Client/Weapons/Melee/MeleeWeaponSystem.cs
Content.Server/Flash/FlashSystem.cs
Content.Server/NPC/Systems/NPCCombatSystem.Melee.cs
Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs
Content.Server/Weapons/Melee/MeleeWeaponSystem.cs
Content.Shared/Damage/Systems/StaminaSystem.cs
Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs
Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs

index 7b9f06c2012c5db9e369d53855ed7c793a39e4e3..973d4974557808d443f3b84a16e332cc1d27bdf3 100644 (file)
@@ -220,9 +220,9 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
             RaiseLocalEvent(new DamageEffectEvent(Color.Red, targets));
     }
 
-    protected override bool DoDisarm(EntityUid user, DisarmAttackEvent ev, MeleeWeaponComponent component, ICommonSession? session)
+    protected override bool DoDisarm(EntityUid user, DisarmAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session)
     {
-        if (!base.DoDisarm(user, ev, component, session))
+        if (!base.DoDisarm(user, ev, meleeUid, component, session))
             return false;
 
         if (!TryComp<CombatModeComponent>(user, out var combatMode) ||
index 2ba8c884648f844f6e542f3ed746455c4204bf4f..666b2b257a82f167a90ba38e34e2d6836141b4ce 100644 (file)
@@ -1,3 +1,4 @@
+using System.Linq;
 using Content.Server.Flash.Components;
 using Content.Server.Light.EntitySystems;
 using Content.Server.Stunnable;
@@ -43,11 +44,12 @@ namespace Content.Server.Flash
 
         private void OnFlashMeleeHit(EntityUid uid, FlashComponent comp, MeleeHitEvent args)
         {
-            if (!args.IsHit)
-                return;
-
-            if (!UseFlash(comp, args.User))
+            if (!args.IsHit ||
+                !args.HitEntities.Any() ||
+                !UseFlash(comp, args.User))
+            {
                 return;
+            }
 
             args.Handled = true;
             foreach (var e in args.HitEntities)
index b29bd52b267519247b2356b5a4c7a5fc41013a26..afef3dea0e478d704585476b46c0fbecd76d8ed5 100644 (file)
@@ -95,17 +95,19 @@ public sealed partial class NPCCombatSystem
 
         foreach (var (comp, _) in EntityQuery<NPCMeleeCombatComponent, ActiveNPCComponent>())
         {
-            if (!combatQuery.TryGetComponent(comp.Owner, out var combat) || !combat.IsInCombatMode)
+            var uid = comp.Owner;
+
+            if (!combatQuery.TryGetComponent(uid, out var combat) || !combat.IsInCombatMode)
             {
-                RemComp<NPCMeleeCombatComponent>(comp.Owner);
+                RemComp<NPCMeleeCombatComponent>(uid);
                 continue;
             }
 
-            Attack(comp, curTime, physicsQuery, xformQuery);
+            Attack(uid, comp, curTime, physicsQuery, xformQuery);
         }
     }
 
-    private void Attack(NPCMeleeCombatComponent component, TimeSpan curTime, EntityQuery<PhysicsComponent> physicsQuery, EntityQuery<TransformComponent> xformQuery)
+    private void Attack(EntityUid uid, NPCMeleeCombatComponent component, TimeSpan curTime, EntityQuery<PhysicsComponent> physicsQuery, EntityQuery<TransformComponent> xformQuery)
     {
         component.Status = CombatStatus.Normal;
 
@@ -115,7 +117,7 @@ public sealed partial class NPCCombatSystem
             return;
         }
 
-        if (!xformQuery.TryGetComponent(component.Owner, out var xform) ||
+        if (!xformQuery.TryGetComponent(uid, out var xform) ||
             !xformQuery.TryGetComponent(component.Target, out var targetXform))
         {
             component.Status = CombatStatus.TargetUnreachable;
@@ -134,7 +136,7 @@ public sealed partial class NPCCombatSystem
             return;
         }
 
-        if (TryComp<NPCSteeringComponent>(component.Owner, out var steering) &&
+        if (TryComp<NPCSteeringComponent>(uid, out var steering) &&
             steering.Status == SteeringStatus.NoPath)
         {
             component.Status = CombatStatus.TargetUnreachable;
@@ -147,11 +149,11 @@ public sealed partial class NPCCombatSystem
             return;
         }
 
-        steering = EnsureComp<NPCSteeringComponent>(component.Owner);
+        steering = EnsureComp<NPCSteeringComponent>(uid);
         steering.Range = MathF.Max(0.2f, weapon.Range - 0.4f);
 
         // Gets unregistered on component shutdown.
-        _steering.TryRegister(component.Owner, new EntityCoordinates(component.Target, Vector2.Zero), steering);
+        _steering.TryRegister(uid, new EntityCoordinates(component.Target, Vector2.Zero), steering);
 
         if (weapon.NextAttack > curTime || !Enabled)
             return;
@@ -160,11 +162,11 @@ public sealed partial class NPCCombatSystem
             physicsQuery.TryGetComponent(component.Target, out var targetPhysics) &&
             targetPhysics.LinearVelocity.LengthSquared != 0f)
         {
-            _melee.AttemptLightAttackMiss(component.Owner, weapon, targetXform.Coordinates.Offset(_random.NextVector2(0.5f)));
+            _melee.AttemptLightAttackMiss(uid, component.Weapon, weapon, targetXform.Coordinates.Offset(_random.NextVector2(0.5f)));
         }
         else
         {
-            _melee.AttemptLightAttack(component.Owner, weapon, component.Target);
+            _melee.AttemptLightAttack(uid, component.Weapon, weapon, component.Target);
         }
     }
 }
index 03f1fb06f957cdd2bda5e28a5e6a4cf558e7125d..d8c32482ce894ec1d4d913b6e9155a1e9bb843b1 100644 (file)
@@ -127,7 +127,7 @@ public sealed partial class NPCSteeringSystem
                         // TODO: Validate we can damage it
                         if (destructibleQuery.HasComponent(ent))
                         {
-                            _melee.AttemptLightAttack(component.Owner, meleeWeapon, ent);
+                            _melee.AttemptLightAttack(component.Owner, component.Owner, meleeWeapon, ent);
                             break;
                         }
                     }
index 4f7017c77d46dfb453af94bc371808c90cd5a697..d57583fd35fee30c3263d99c5c11c222ec926522 100644 (file)
@@ -91,14 +91,14 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
             return;
 
         if (user == null)
-            PopupSystem.PopupEntity(message, uid.Value); 
+            PopupSystem.PopupEntity(message, uid.Value);
         else
             PopupSystem.PopupEntity(message, uid.Value, Filter.PvsExcept(user.Value, entityManager: EntityManager), true);
     }
 
-    protected override bool DoDisarm(EntityUid user, DisarmAttackEvent ev, MeleeWeaponComponent component, ICommonSession? session)
+    protected override bool DoDisarm(EntityUid user, DisarmAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session)
     {
-        if (!base.DoDisarm(user, ev, component, session))
+        if (!base.DoDisarm(user, ev, meleeUid, component, session))
             return false;
 
         if (!TryComp<CombatModeComponent>(user, out var combatMode) ||
@@ -228,11 +228,12 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
 
     private void OnChemicalInjectorHit(EntityUid owner, MeleeChemicalInjectorComponent comp, MeleeHitEvent args)
     {
-        if (!args.IsHit)
-            return;
-
-        if (!_solutions.TryGetSolution(owner, comp.Solution, out var solutionContainer))
+        if (!args.IsHit ||
+            !args.HitEntities.Any() ||
+            !_solutions.TryGetSolution(owner, comp.Solution, out var solutionContainer))
+        {
             return;
+        }
 
         var hitBloodstreams = new List<BloodstreamComponent>();
         var bloodQuery = GetEntityQuery<BloodstreamComponent>();
index f2ecf2c090afe4117da571dd33990a2ead9ee1a7..e4a89aac931c66f0005425ee1ec799ef51c2e95b 100644 (file)
@@ -1,3 +1,4 @@
+using System.Linq;
 using Content.Shared.Administration.Logs;
 using Content.Shared.Alert;
 using Content.Shared.CombatMode;
@@ -139,10 +140,12 @@ public sealed class StaminaSystem : EntitySystem
 
     private void OnHit(EntityUid uid, StaminaDamageOnHitComponent component, MeleeHitEvent args)
     {
-        if (!args.IsHit)
+        if (!args.IsHit ||
+            !args.HitEntities.Any() ||
+            component.Damage <= 0f)
+        {
             return;
-
-        if (component.Damage <= 0f) return;
+        }
 
         var ev = new StaminaDamageOnHitAttemptEvent();
         RaiseLocalEvent(uid, ref ev);
index 2b80cff7a1190c39f7bb9f40bfd288dcc21d8702..6810cb6dca3431cc900959868b457971f8bd7d2e 100644 (file)
@@ -12,7 +12,7 @@ public sealed class MeleeHitEvent : HandledEntityEventArgs
     /// <summary>
     ///     The base amount of damage dealt by the melee hit.
     /// </summary>
-    public readonly DamageSpecifier BaseDamage = new();
+    public readonly DamageSpecifier BaseDamage;
 
     /// <summary>
     ///     Modifier sets to apply to the hit event when it's all said and done.
@@ -31,18 +31,18 @@ public sealed class MeleeHitEvent : HandledEntityEventArgs
     /// <summary>
     ///     A list containing every hit entity. Can be zero.
     /// </summary>
-    public IEnumerable<EntityUid> HitEntities { get; }
+    public IReadOnlyList<EntityUid> HitEntities;
 
     /// <summary>
     ///     Used to define a new hit sound in case you want to override the default GenericHit.
     ///     Also gets a pitch modifier added to it.
     /// </summary>
-    public SoundSpecifier? HitSoundOverride {get; set;}
+    public SoundSpecifier? HitSoundOverride;
 
     /// <summary>
     /// The user who attacked with the melee weapon.
     /// </summary>
-    public EntityUid User { get; }
+    public readonly EntityUid User;
 
     /// <summary>
     /// Check if this is true before attempting to do something during a melee attack other than changing/adding bonus damage. <br/>
@@ -59,4 +59,4 @@ public sealed class MeleeHitEvent : HandledEntityEventArgs
         User = user;
         BaseDamage = baseDamage;
     }
-}
\ No newline at end of file
+}
index 9c609394a64b73c51ec5f46e645c6276b458410c..362756a52c59b1b98f7e34dfd594fc429e3f9065 100644 (file)
@@ -40,6 +40,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
     [Dependency] protected readonly SharedInteractionSystem Interaction = default!;
     [Dependency] private   readonly SharedPhysicsSystem _physics = default!;
     [Dependency] protected readonly SharedPopupSystem PopupSystem = default!;
+    [Dependency] private   readonly SharedTransformSystem _transform = default!;
     [Dependency] private   readonly StaminaSystem _stamina = default!;
 
     protected ISawmill Sawmill = default!;
@@ -148,7 +149,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
         if (weapon?.Owner != msg.Weapon)
             return;
 
-        AttemptAttack(args.SenderSession.AttachedEntity!.Value, weapon, msg, args.SenderSession);
+        AttemptAttack(args.SenderSession.AttachedEntity!.Value, msg.Weapon, weapon, msg, args.SenderSession);
     }
 
     private void OnStopHeavyAttack(StopHeavyAttackEvent msg, EntitySessionEventArgs args)
@@ -186,7 +187,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
         if (userWeapon != weapon)
             return;
 
-        AttemptAttack(args.SenderSession.AttachedEntity.Value, weapon, msg, args.SenderSession);
+        AttemptAttack(args.SenderSession.AttachedEntity.Value, msg.Weapon, weapon, msg, args.SenderSession);
     }
 
     private void OnDisarmAttack(DisarmAttackEvent msg, EntitySessionEventArgs args)
@@ -201,7 +202,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
         if (userWeapon == null)
             return;
 
-        AttemptAttack(args.SenderSession.AttachedEntity.Value, userWeapon, msg, args.SenderSession);
+        AttemptAttack(args.SenderSession.AttachedEntity.Value, userWeapon.Owner, userWeapon, msg, args.SenderSession);
     }
 
     private void OnGetState(EntityUid uid, MeleeWeaponComponent component, ref ComponentGetState args)
@@ -264,31 +265,31 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
         return null;
     }
 
-    public void AttemptLightAttackMiss(EntityUid user, MeleeWeaponComponent weapon, EntityCoordinates coordinates)
+    public void AttemptLightAttackMiss(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, EntityCoordinates coordinates)
     {
-        AttemptAttack(user, weapon, new LightAttackEvent(null, weapon.Owner, coordinates), null);
+        AttemptAttack(user, weaponUid, weapon, new LightAttackEvent(null, weaponUid, coordinates), null);
     }
 
-    public void AttemptLightAttack(EntityUid user, MeleeWeaponComponent weapon, EntityUid target)
+    public void AttemptLightAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, EntityUid target)
     {
         if (!TryComp<TransformComponent>(target, out var targetXform))
             return;
 
-        AttemptAttack(user, weapon, new LightAttackEvent(target, weapon.Owner, targetXform.Coordinates), null);
+        AttemptAttack(user, weaponUid, weapon, new LightAttackEvent(target, weaponUid, targetXform.Coordinates), null);
     }
 
-    public void AttemptDisarmAttack(EntityUid user, MeleeWeaponComponent weapon, EntityUid target)
+    public void AttemptDisarmAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, EntityUid target)
     {
         if (!TryComp<TransformComponent>(target, out var targetXform))
             return;
 
-        AttemptAttack(user, weapon, new DisarmAttackEvent(target, targetXform.Coordinates), null);
+        AttemptAttack(user, weaponUid, weapon, new DisarmAttackEvent(target, targetXform.Coordinates), null);
     }
 
     /// <summary>
     /// Called when a windup is finished and an attack is tried.
     /// </summary>
-    private void AttemptAttack(EntityUid user, MeleeWeaponComponent weapon, AttackEvent attack, ICommonSession? session)
+    private void AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, AttackEvent attack, ICommonSession? session)
     {
         var curTime = Timing.CurTime;
 
@@ -327,17 +328,17 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
         switch (attack)
         {
             case LightAttackEvent light:
-                DoLightAttack(user, light, weapon, session);
+                DoLightAttack(user, light, weaponUid, weapon, session);
                 animation = weapon.ClickAnimation;
                 break;
             case DisarmAttackEvent disarm:
-                if (!DoDisarm(user, disarm, weapon, session))
+                if (!DoDisarm(user, disarm, weaponUid, weapon, session))
                     return;
 
                 animation = weapon.ClickAnimation;
                 break;
             case HeavyAttackEvent heavy:
-                DoHeavyAttack(user, heavy, weapon, session);
+                DoHeavyAttack(user, heavy, weaponUid, weapon, session);
                 animation = weapon.WideAnimation;
                 break;
             default:
@@ -385,34 +386,34 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
 
     protected abstract bool InRange(EntityUid user, EntityUid target, float range, ICommonSession? session);
 
-    protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, MeleeWeaponComponent component, ICommonSession? session)
+    protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session)
     {
+        var damage = component.Damage * GetModifier(component, true);
+
         // Can't attack yourself
-        // Not in LOS.
+        // For consistency with wide attacks stuff needs damageable.
         if (user == ev.Target ||
-            ev.Target == null ||
             Deleted(ev.Target) ||
-            // For consistency with wide attacks stuff needs damageable.
             !HasComp<DamageableComponent>(ev.Target) ||
-            !TryComp<TransformComponent>(ev.Target, out var targetXform))
-        {
-            Audio.PlayPredicted(component.SwingSound, component.Owner, user);
-            return;
-        }
-
-        if (!InRange(user, ev.Target.Value, component.Range, session))
+            !TryComp<TransformComponent>(ev.Target, out var targetXform) ||
+            // Not in LOS.
+            !InRange(user, ev.Target.Value, component.Range, session))
         {
-            Audio.PlayPredicted(component.SwingSound, component.Owner, user);
+            // Leave IsHit set to true, because the only time it's set to false
+            // is when a melee weapon is examined. Misses are inferred from an
+            // empty HitEntities.
+            // TODO: This needs fixing
+            var missEvent = new MeleeHitEvent(new List<EntityUid>(), user, damage);
+            RaiseLocalEvent(meleeUid, missEvent);
+            Audio.PlayPredicted(component.SwingSound, meleeUid, user);
             return;
         }
 
-        var damage = component.Damage * GetModifier(component, true);
-
         // Sawmill.Debug($"Melee damage is {damage.Total} out of {component.Damage.Total}");
 
         // Raise event before doing damage so we can cancel damage if the event is handled
         var hitEvent = new MeleeHitEvent(new List<EntityUid> { ev.Target.Value }, user, damage);
-        RaiseLocalEvent(component.Owner, hitEvent);
+        RaiseLocalEvent(meleeUid, hitEvent);
 
         if (hitEvent.Handled)
             return;
@@ -430,7 +431,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
         Interaction.DoContactInteraction(user, ev.Target);
 
         // For stuff that cares about it being attacked.
-        RaiseLocalEvent(ev.Target.Value, new AttackedEvent(component.Owner, user, targetXform.Coordinates));
+        RaiseLocalEvent(ev.Target.Value, new AttackedEvent(meleeUid, user, targetXform.Coordinates));
 
         var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage, hitEvent.ModifiersList);
         var damageResult = Damageable.TryChangeDamage(ev.Target, modifiedDamage, origin:user);
@@ -443,7 +444,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
                 _stamina.TakeStaminaDamage(ev.Target.Value, (bluntDamage * component.BluntStaminaDamageFactor).Float(), source:user, with:(component.Owner == user ? null : component.Owner));
             }
 
-            if (component.Owner == user)
+            if (meleeUid == user)
             {
                 AdminLogger.Add(LogType.MeleeHit,
                     $"{ToPrettyString(user):user} melee attacked {ToPrettyString(ev.Target.Value):target} using their hands and dealt {damageResult.Total:damage} damage");
@@ -460,11 +461,11 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
         {
             if (hitEvent.HitSoundOverride != null)
             {
-                Audio.PlayPredicted(hitEvent.HitSoundOverride, component.Owner, user);
+                Audio.PlayPredicted(hitEvent.HitSoundOverride, meleeUid, user);
             }
             else
             {
-                Audio.PlayPredicted(component.NoDamageSound, component.Owner, user);
+                Audio.PlayPredicted(component.NoDamageSound, meleeUid, user);
             }
         }
 
@@ -476,7 +477,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
 
     protected abstract void DoDamageEffect(List<EntityUid> targets, EntityUid? user,  TransformComponent targetXform);
 
-    protected virtual void DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, MeleeWeaponComponent component, ICommonSession? session)
+    protected virtual void DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session)
     {
         // TODO: This is copy-paste as fuck with DoPreciseAttack
         if (!TryComp<TransformComponent>(user, out var userXform))
@@ -491,16 +492,21 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
             return;
         }
 
-        var userPos = userXform.WorldPosition;
+        var userPos = _transform.GetWorldPosition(userXform);
         var direction = targetMap.Position - userPos;
         var distance = Math.Min(component.Range, direction.Length);
 
+        var damage = component.Damage * GetModifier(component, false);
+
         // This should really be improved. GetEntitiesInArc uses pos instead of bounding boxes.
         var entities = ArcRayCast(userPos, direction.ToWorldAngle(), component.Angle, distance, userXform.MapID, user);
 
         if (entities.Count == 0)
         {
-            Audio.PlayPredicted(component.SwingSound, component.Owner, user);
+            var missEvent = new MeleeHitEvent(new List<EntityUid>(), user, damage);
+            RaiseLocalEvent(meleeUid, missEvent);
+
+            Audio.PlayPredicted(component.SwingSound, meleeUid, user);
             return;
         }
 
@@ -516,12 +522,11 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
             targets.Add(entity);
         }
 
-        var damage = component.Damage * GetModifier(component, false);
         // Sawmill.Debug($"Melee damage is {damage.Total} out of {component.Damage.Total}");
 
         // Raise event before doing damage so we can cancel damage if the event is handled
         var hitEvent = new MeleeHitEvent(targets, user, damage);
-        RaiseLocalEvent(component.Owner, hitEvent);
+        RaiseLocalEvent(meleeUid, hitEvent);
 
         if (hitEvent.Handled)
             return;
@@ -537,7 +542,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
             // somewhat messy scuffle. See also, light attacks.
             Interaction.DoContactInteraction(user, target);
 
-            RaiseLocalEvent(target, new AttackedEvent(component.Owner, user, Transform(target).Coordinates));
+            RaiseLocalEvent(target, new AttackedEvent(meleeUid, user, Transform(target).Coordinates));
         }
 
         var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage, hitEvent.ModifiersList);
@@ -545,7 +550,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
 
         foreach (var entity in targets)
         {
-            RaiseLocalEvent(entity, new AttackedEvent(component.Owner, user, ev.Coordinates));
+            RaiseLocalEvent(entity, new AttackedEvent(meleeUid, user, ev.Coordinates));
 
             var damageResult = Damageable.TryChangeDamage(entity, modifiedDamage, origin:user);
 
@@ -553,7 +558,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
             {
                 appliedDamage += damageResult;
 
-                if (component.Owner == user)
+                if (meleeUid == user)
                 {
                     AdminLogger.Add(LogType.MeleeHit,
                         $"{ToPrettyString(user):user} melee attacked {ToPrettyString(entity):target} using their hands and dealt {damageResult.Total:damage} damage");
@@ -577,11 +582,11 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
             {
                 if (hitEvent.HitSoundOverride != null)
                 {
-                    Audio.PlayPredicted(hitEvent.HitSoundOverride, component.Owner, user);
+                    Audio.PlayPredicted(hitEvent.HitSoundOverride, meleeUid, user);
                 }
                 else
                 {
-                    Audio.PlayPredicted(component.NoDamageSound, component.Owner, user);
+                    Audio.PlayPredicted(component.NoDamageSound, meleeUid, user);
                 }
             }
         }
@@ -703,14 +708,14 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
         return highestDamageType;
     }
 
-    protected virtual bool DoDisarm(EntityUid user, DisarmAttackEvent ev, MeleeWeaponComponent component, ICommonSession? session)
+    protected virtual bool DoDisarm(EntityUid user, DisarmAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session)
     {
         if (Deleted(ev.Target) ||
             user == ev.Target)
             return false;
 
         // Play a sound to give instant feedback; same with playing the animations
-        Audio.PlayPredicted(component.SwingSound, component.Owner, user);
+        Audio.PlayPredicted(component.SwingSound, meleeUid, user);
         return true;
     }