Fire MeleeHitEvent on misses. (#12867)

* 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>
This commit is contained in:
Vordenburg
2023-02-13 07:55:39 -05:00
committed by GitHub
parent 650ade0d4e
commit 4183b5f449
8 changed files with 95 additions and 82 deletions

View File

@@ -220,9 +220,9 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
RaiseLocalEvent(new DamageEffectEvent(Color.Red, targets)); 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; return false;
if (!TryComp<CombatModeComponent>(user, out var combatMode) || if (!TryComp<CombatModeComponent>(user, out var combatMode) ||

View File

@@ -1,3 +1,4 @@
using System.Linq;
using Content.Server.Flash.Components; using Content.Server.Flash.Components;
using Content.Server.Light.EntitySystems; using Content.Server.Light.EntitySystems;
using Content.Server.Stunnable; using Content.Server.Stunnable;
@@ -43,11 +44,12 @@ namespace Content.Server.Flash
private void OnFlashMeleeHit(EntityUid uid, FlashComponent comp, MeleeHitEvent args) private void OnFlashMeleeHit(EntityUid uid, FlashComponent comp, MeleeHitEvent args)
{ {
if (!args.IsHit) if (!args.IsHit ||
return; !args.HitEntities.Any() ||
!UseFlash(comp, args.User))
if (!UseFlash(comp, args.User)) {
return; return;
}
args.Handled = true; args.Handled = true;
foreach (var e in args.HitEntities) foreach (var e in args.HitEntities)

View File

@@ -95,17 +95,19 @@ public sealed partial class NPCCombatSystem
foreach (var (comp, _) in EntityQuery<NPCMeleeCombatComponent, ActiveNPCComponent>()) 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; 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; component.Status = CombatStatus.Normal;
@@ -115,7 +117,7 @@ public sealed partial class NPCCombatSystem
return; return;
} }
if (!xformQuery.TryGetComponent(component.Owner, out var xform) || if (!xformQuery.TryGetComponent(uid, out var xform) ||
!xformQuery.TryGetComponent(component.Target, out var targetXform)) !xformQuery.TryGetComponent(component.Target, out var targetXform))
{ {
component.Status = CombatStatus.TargetUnreachable; component.Status = CombatStatus.TargetUnreachable;
@@ -134,7 +136,7 @@ public sealed partial class NPCCombatSystem
return; return;
} }
if (TryComp<NPCSteeringComponent>(component.Owner, out var steering) && if (TryComp<NPCSteeringComponent>(uid, out var steering) &&
steering.Status == SteeringStatus.NoPath) steering.Status == SteeringStatus.NoPath)
{ {
component.Status = CombatStatus.TargetUnreachable; component.Status = CombatStatus.TargetUnreachable;
@@ -147,11 +149,11 @@ public sealed partial class NPCCombatSystem
return; return;
} }
steering = EnsureComp<NPCSteeringComponent>(component.Owner); steering = EnsureComp<NPCSteeringComponent>(uid);
steering.Range = MathF.Max(0.2f, weapon.Range - 0.4f); steering.Range = MathF.Max(0.2f, weapon.Range - 0.4f);
// Gets unregistered on component shutdown. // 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) if (weapon.NextAttack > curTime || !Enabled)
return; return;
@@ -160,11 +162,11 @@ public sealed partial class NPCCombatSystem
physicsQuery.TryGetComponent(component.Target, out var targetPhysics) && physicsQuery.TryGetComponent(component.Target, out var targetPhysics) &&
targetPhysics.LinearVelocity.LengthSquared != 0f) 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 else
{ {
_melee.AttemptLightAttack(component.Owner, weapon, component.Target); _melee.AttemptLightAttack(uid, component.Weapon, weapon, component.Target);
} }
} }
} }

View File

@@ -127,7 +127,7 @@ public sealed partial class NPCSteeringSystem
// TODO: Validate we can damage it // TODO: Validate we can damage it
if (destructibleQuery.HasComponent(ent)) if (destructibleQuery.HasComponent(ent))
{ {
_melee.AttemptLightAttack(component.Owner, meleeWeapon, ent); _melee.AttemptLightAttack(component.Owner, component.Owner, meleeWeapon, ent);
break; break;
} }
} }

View File

@@ -96,9 +96,9 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
PopupSystem.PopupEntity(message, uid.Value, Filter.PvsExcept(user.Value, entityManager: EntityManager), true); 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; return false;
if (!TryComp<CombatModeComponent>(user, out var combatMode) || 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) private void OnChemicalInjectorHit(EntityUid owner, MeleeChemicalInjectorComponent comp, MeleeHitEvent args)
{ {
if (!args.IsHit) if (!args.IsHit ||
return; !args.HitEntities.Any() ||
!_solutions.TryGetSolution(owner, comp.Solution, out var solutionContainer))
if (!_solutions.TryGetSolution(owner, comp.Solution, out var solutionContainer)) {
return; return;
}
var hitBloodstreams = new List<BloodstreamComponent>(); var hitBloodstreams = new List<BloodstreamComponent>();
var bloodQuery = GetEntityQuery<BloodstreamComponent>(); var bloodQuery = GetEntityQuery<BloodstreamComponent>();

View File

@@ -1,3 +1,4 @@
using System.Linq;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.CombatMode; using Content.Shared.CombatMode;
@@ -139,10 +140,12 @@ public sealed class StaminaSystem : EntitySystem
private void OnHit(EntityUid uid, StaminaDamageOnHitComponent component, MeleeHitEvent args) private void OnHit(EntityUid uid, StaminaDamageOnHitComponent component, MeleeHitEvent args)
{ {
if (!args.IsHit) if (!args.IsHit ||
!args.HitEntities.Any() ||
component.Damage <= 0f)
{
return; return;
}
if (component.Damage <= 0f) return;
var ev = new StaminaDamageOnHitAttemptEvent(); var ev = new StaminaDamageOnHitAttemptEvent();
RaiseLocalEvent(uid, ref ev); RaiseLocalEvent(uid, ref ev);

View File

@@ -12,7 +12,7 @@ public sealed class MeleeHitEvent : HandledEntityEventArgs
/// <summary> /// <summary>
/// The base amount of damage dealt by the melee hit. /// The base amount of damage dealt by the melee hit.
/// </summary> /// </summary>
public readonly DamageSpecifier BaseDamage = new(); public readonly DamageSpecifier BaseDamage;
/// <summary> /// <summary>
/// Modifier sets to apply to the hit event when it's all said and done. /// 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> /// <summary>
/// A list containing every hit entity. Can be zero. /// A list containing every hit entity. Can be zero.
/// </summary> /// </summary>
public IEnumerable<EntityUid> HitEntities { get; } public IReadOnlyList<EntityUid> HitEntities;
/// <summary> /// <summary>
/// Used to define a new hit sound in case you want to override the default GenericHit. /// Used to define a new hit sound in case you want to override the default GenericHit.
/// Also gets a pitch modifier added to it. /// Also gets a pitch modifier added to it.
/// </summary> /// </summary>
public SoundSpecifier? HitSoundOverride {get; set;} public SoundSpecifier? HitSoundOverride;
/// <summary> /// <summary>
/// The user who attacked with the melee weapon. /// The user who attacked with the melee weapon.
/// </summary> /// </summary>
public EntityUid User { get; } public readonly EntityUid User;
/// <summary> /// <summary>
/// Check if this is true before attempting to do something during a melee attack other than changing/adding bonus damage. <br/> /// Check if this is true before attempting to do something during a melee attack other than changing/adding bonus damage. <br/>

View File

@@ -40,6 +40,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
[Dependency] protected readonly SharedInteractionSystem Interaction = default!; [Dependency] protected readonly SharedInteractionSystem Interaction = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] protected readonly SharedPopupSystem PopupSystem = default!; [Dependency] protected readonly SharedPopupSystem PopupSystem = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly StaminaSystem _stamina = default!; [Dependency] private readonly StaminaSystem _stamina = default!;
protected ISawmill Sawmill = default!; protected ISawmill Sawmill = default!;
@@ -148,7 +149,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
if (weapon?.Owner != msg.Weapon) if (weapon?.Owner != msg.Weapon)
return; 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) private void OnStopHeavyAttack(StopHeavyAttackEvent msg, EntitySessionEventArgs args)
@@ -186,7 +187,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
if (userWeapon != weapon) if (userWeapon != weapon)
return; 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) private void OnDisarmAttack(DisarmAttackEvent msg, EntitySessionEventArgs args)
@@ -201,7 +202,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
if (userWeapon == null) if (userWeapon == null)
return; 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) private void OnGetState(EntityUid uid, MeleeWeaponComponent component, ref ComponentGetState args)
@@ -264,31 +265,31 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
return null; 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)) if (!TryComp<TransformComponent>(target, out var targetXform))
return; 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)) if (!TryComp<TransformComponent>(target, out var targetXform))
return; return;
AttemptAttack(user, weapon, new DisarmAttackEvent(target, targetXform.Coordinates), null); AttemptAttack(user, weaponUid, weapon, new DisarmAttackEvent(target, targetXform.Coordinates), null);
} }
/// <summary> /// <summary>
/// Called when a windup is finished and an attack is tried. /// Called when a windup is finished and an attack is tried.
/// </summary> /// </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; var curTime = Timing.CurTime;
@@ -327,17 +328,17 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
switch (attack) switch (attack)
{ {
case LightAttackEvent light: case LightAttackEvent light:
DoLightAttack(user, light, weapon, session); DoLightAttack(user, light, weaponUid, weapon, session);
animation = weapon.ClickAnimation; animation = weapon.ClickAnimation;
break; break;
case DisarmAttackEvent disarm: case DisarmAttackEvent disarm:
if (!DoDisarm(user, disarm, weapon, session)) if (!DoDisarm(user, disarm, weaponUid, weapon, session))
return; return;
animation = weapon.ClickAnimation; animation = weapon.ClickAnimation;
break; break;
case HeavyAttackEvent heavy: case HeavyAttackEvent heavy:
DoHeavyAttack(user, heavy, weapon, session); DoHeavyAttack(user, heavy, weaponUid, weapon, session);
animation = weapon.WideAnimation; animation = weapon.WideAnimation;
break; break;
default: default:
@@ -385,34 +386,34 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
protected abstract bool InRange(EntityUid user, EntityUid target, float range, ICommonSession? session); 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)
{ {
// Can't attack yourself
// Not in LOS.
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))
{
Audio.PlayPredicted(component.SwingSound, component.Owner, user);
return;
}
var damage = component.Damage * GetModifier(component, true); var damage = component.Damage * GetModifier(component, true);
// Can't attack yourself
// For consistency with wide attacks stuff needs damageable.
if (user == ev.Target ||
Deleted(ev.Target) ||
!HasComp<DamageableComponent>(ev.Target) ||
!TryComp<TransformComponent>(ev.Target, out var targetXform) ||
// Not in LOS.
!InRange(user, ev.Target.Value, component.Range, session))
{
// 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;
}
// Sawmill.Debug($"Melee damage is {damage.Total} out of {component.Damage.Total}"); // 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 // 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); var hitEvent = new MeleeHitEvent(new List<EntityUid> { ev.Target.Value }, user, damage);
RaiseLocalEvent(component.Owner, hitEvent); RaiseLocalEvent(meleeUid, hitEvent);
if (hitEvent.Handled) if (hitEvent.Handled)
return; return;
@@ -430,7 +431,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
Interaction.DoContactInteraction(user, ev.Target); Interaction.DoContactInteraction(user, ev.Target);
// For stuff that cares about it being attacked. // 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 modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage, hitEvent.ModifiersList);
var damageResult = Damageable.TryChangeDamage(ev.Target, modifiedDamage, origin:user); 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)); _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, AdminLogger.Add(LogType.MeleeHit,
$"{ToPrettyString(user):user} melee attacked {ToPrettyString(ev.Target.Value):target} using their hands and dealt {damageResult.Total:damage} damage"); $"{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) if (hitEvent.HitSoundOverride != null)
{ {
Audio.PlayPredicted(hitEvent.HitSoundOverride, component.Owner, user); Audio.PlayPredicted(hitEvent.HitSoundOverride, meleeUid, user);
} }
else 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 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 // TODO: This is copy-paste as fuck with DoPreciseAttack
if (!TryComp<TransformComponent>(user, out var userXform)) if (!TryComp<TransformComponent>(user, out var userXform))
@@ -491,16 +492,21 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
return; return;
} }
var userPos = userXform.WorldPosition; var userPos = _transform.GetWorldPosition(userXform);
var direction = targetMap.Position - userPos; var direction = targetMap.Position - userPos;
var distance = Math.Min(component.Range, direction.Length); 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. // This should really be improved. GetEntitiesInArc uses pos instead of bounding boxes.
var entities = ArcRayCast(userPos, direction.ToWorldAngle(), component.Angle, distance, userXform.MapID, user); var entities = ArcRayCast(userPos, direction.ToWorldAngle(), component.Angle, distance, userXform.MapID, user);
if (entities.Count == 0) 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; return;
} }
@@ -516,12 +522,11 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
targets.Add(entity); targets.Add(entity);
} }
var damage = component.Damage * GetModifier(component, false);
// Sawmill.Debug($"Melee damage is {damage.Total} out of {component.Damage.Total}"); // 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 // Raise event before doing damage so we can cancel damage if the event is handled
var hitEvent = new MeleeHitEvent(targets, user, damage); var hitEvent = new MeleeHitEvent(targets, user, damage);
RaiseLocalEvent(component.Owner, hitEvent); RaiseLocalEvent(meleeUid, hitEvent);
if (hitEvent.Handled) if (hitEvent.Handled)
return; return;
@@ -537,7 +542,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
// somewhat messy scuffle. See also, light attacks. // somewhat messy scuffle. See also, light attacks.
Interaction.DoContactInteraction(user, target); 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); var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage, hitEvent.ModifiersList);
@@ -545,7 +550,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
foreach (var entity in targets) 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); var damageResult = Damageable.TryChangeDamage(entity, modifiedDamage, origin:user);
@@ -553,7 +558,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
{ {
appliedDamage += damageResult; appliedDamage += damageResult;
if (component.Owner == user) if (meleeUid == user)
{ {
AdminLogger.Add(LogType.MeleeHit, AdminLogger.Add(LogType.MeleeHit,
$"{ToPrettyString(user):user} melee attacked {ToPrettyString(entity):target} using their hands and dealt {damageResult.Total:damage} damage"); $"{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) if (hitEvent.HitSoundOverride != null)
{ {
Audio.PlayPredicted(hitEvent.HitSoundOverride, component.Owner, user); Audio.PlayPredicted(hitEvent.HitSoundOverride, meleeUid, user);
} }
else 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; 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) || if (Deleted(ev.Target) ||
user == ev.Target) user == ev.Target)
return false; return false;
// Play a sound to give instant feedback; same with playing the animations // 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; return true;
} }