diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs index acd4cd1a74..f91d0a6104 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs @@ -159,10 +159,10 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range, overlapCheck: false); } - protected override void DoDamageEffect(List targets, EntityUid? user, TransformComponent targetXform) + protected override void DoDamageEffect(HashSet targets, EntityUid? user, TransformComponent targetXform) { // Server never sends the event to us for predictiveeevent. - _color.RaiseEffect(Color.Red, targets, Filter.Local()); + _color.RaiseEffect(Color.Red, targets.ToList(), Filter.Local()); } /// @@ -189,8 +189,9 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem // This should really be improved. GetEntitiesInArc uses pos instead of bounding boxes. // Server will validate it with InRangeUnobstructed. - var entities = GetNetEntityList(ArcRayCast(userPos, direction.ToWorldAngle(), component.Angle, distance, userXform.MapID, user).ToList()); - RaisePredictiveEvent(new HeavyAttackEvent(GetNetEntity(meleeUid), entities.GetRange(0, Math.Min(MaxTargets, entities.Count)), GetNetCoordinates(coordinates))); + var entities = GetNetEntitySet(ArcRayCast(userPos, direction.ToWorldAngle(), component.Angle, distance, userXform.MapID, user)); + entities = entities.Take(Math.Min(MaxTargets, entities.Count)).ToHashSet(); + RaisePredictiveEvent(new HeavyAttackEvent(GetNetEntity(meleeUid), entities, GetNetCoordinates(coordinates))); } private void ClientDisarm(EntityUid attacker, MapCoordinates mousePos, EntityCoordinates coordinates) diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs index a98c272eed..84727915c3 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs @@ -63,7 +63,7 @@ public sealed partial class SolutionInjectOnCollideSystem : EntitySystem private void DoInjection(Entity injectorEntity, EntityUid target, EntityUid? source = null) { - TryInjectTargets(injectorEntity, [target], source); + TryInjectTargets(injectorEntity, new HashSet() { target }, source); } /// @@ -79,7 +79,7 @@ public sealed partial class SolutionInjectOnCollideSystem : EntitySystem /// /// /// true if at least one target was successfully injected, otherwise false - private bool TryInjectTargets(Entity injector, IReadOnlyList targets, EntityUid? user = null) + private bool TryInjectTargets(Entity injector, IReadOnlySet targets, EntityUid? user = null) { // Make sure we have at least one target if (targets.Count == 0) diff --git a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs index e7f4d9bf44..26b2f8e48a 100644 --- a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs @@ -68,10 +68,10 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem return Interaction.InRangeUnobstructed(user, target, range); } - protected override void DoDamageEffect(List targets, EntityUid? user, TransformComponent targetXform) + protected override void DoDamageEffect(HashSet targets, EntityUid? user, TransformComponent targetXform) { var filter = Filter.Pvs(targetXform.Coordinates, entityMan: EntityManager).RemoveWhereAttachedEntity(o => o == user); - _color.RaiseEffect(Color.Red, targets, filter); + _color.RaiseEffect(Color.Red, targets.ToList(), filter); } public override void DoLunge(EntityUid user, EntityUid weapon, Angle angle, Vector2 localPos, string? animation, bool predicted = true) diff --git a/Content.Shared/Chemistry/EntitySystems/InjectorSystem.cs b/Content.Shared/Chemistry/EntitySystems/InjectorSystem.cs index ad209ce5d8..88eedd929f 100644 --- a/Content.Shared/Chemistry/EntitySystems/InjectorSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/InjectorSystem.cs @@ -100,10 +100,10 @@ public sealed partial class InjectorSystem : EntitySystem private void OnAttack(Entity injector, ref MeleeHitEvent args) { - if (args.HitEntities is []) + if (args.HitEntities.Count == 0) return; - TryMobsDoAfter(injector, args.User, args.HitEntities[0]); + TryMobsDoAfter(injector, args.User, args.HitEntities.First()); } /// diff --git a/Content.Shared/Flash/SharedFlashSystem.cs b/Content.Shared/Flash/SharedFlashSystem.cs index f61a78b3ce..6baeead4f7 100644 --- a/Content.Shared/Flash/SharedFlashSystem.cs +++ b/Content.Shared/Flash/SharedFlashSystem.cs @@ -92,7 +92,7 @@ public abstract partial class SharedFlashSystem : EntitySystem Flash(target, args.User, ent.Owner, ent.Comp.MeleeDuration, ent.Comp.SlowTo, melee: true, stunDuration: ent.Comp.MeleeStunDuration); } - EntityUid? firstTarget = args.HitEntities.Count > 0 ? args.HitEntities[0] : null; // Just pick the first hit entity. + EntityUid? firstTarget = args.HitEntities.Count > 0 ? args.HitEntities.First() : null; // Just pick the first hit entity. var ev = new AfterFlashActivatedEvent(firstTarget, args.User); RaiseLocalEvent(ent, ref ev); } diff --git a/Content.Shared/Trigger/Systems/TriggerOnMeleeSystem.cs b/Content.Shared/Trigger/Systems/TriggerOnMeleeSystem.cs index 7f90d6f790..f3b80e96a5 100644 --- a/Content.Shared/Trigger/Systems/TriggerOnMeleeSystem.cs +++ b/Content.Shared/Trigger/Systems/TriggerOnMeleeSystem.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Shared.Trigger.Components.Triggers; using Content.Shared.Weapons.Melee.Events; @@ -30,7 +31,7 @@ public sealed partial class TriggerOnMeleeTriggerSystem : TriggerOnXSystem if (args.HitEntities.Count == 0) target = ent.Comp.TargetIsUser ? null : args.User; else - target = ent.Comp.TargetIsUser ? args.HitEntities[0] : args.User; + target = ent.Comp.TargetIsUser ? args.HitEntities.First() : args.User; Trigger.Trigger(ent.Owner, target, ent.Comp.KeyOut); } @@ -42,7 +43,7 @@ public sealed partial class TriggerOnMeleeTriggerSystem : TriggerOnXSystem if (!ent.Comp.TriggerEveryHit) { - var target = ent.Comp.TargetIsUser ? args.HitEntities[0] : args.User; + var target = ent.Comp.TargetIsUser ? args.HitEntities.First() : args.User; Trigger.Trigger(ent.Owner, target, ent.Comp.KeyOut); return; } diff --git a/Content.Shared/Weapons/Melee/Events/HeavyAttackEvent.cs b/Content.Shared/Weapons/Melee/Events/HeavyAttackEvent.cs index 6ea6caadc9..9cc8983039 100644 --- a/Content.Shared/Weapons/Melee/Events/HeavyAttackEvent.cs +++ b/Content.Shared/Weapons/Melee/Events/HeavyAttackEvent.cs @@ -14,9 +14,9 @@ public sealed class HeavyAttackEvent : AttackEvent /// /// As what the client swung at will not match server we'll have them tell us what they hit so we can verify. /// - public List Entities; + public HashSet Entities; - public HeavyAttackEvent(NetEntity weapon, List entities, NetCoordinates coordinates) : base(coordinates) + public HeavyAttackEvent(NetEntity weapon, HashSet entities, NetCoordinates coordinates) : base(coordinates) { Weapon = weapon; Entities = entities; diff --git a/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs b/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs index 75c85790de..d470c2799d 100644 --- a/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs +++ b/Content.Shared/Weapons/Melee/Events/MeleeHitEvent.cs @@ -31,9 +31,9 @@ public sealed class MeleeHitEvent : HandledEntityEventArgs public DamageSpecifier BonusDamage = new(); /// - /// A list containing every hit entity. Can be zero. + /// A set containing every hit entity. Can be zero. /// - public IReadOnlyList HitEntities; + public IReadOnlySet HitEntities; /// /// Used to define a new hit sound in case you want to override the default GenericHit. @@ -66,7 +66,7 @@ public sealed class MeleeHitEvent : HandledEntityEventArgs /// public bool IsHit = true; - public MeleeHitEvent(List hitEntities, EntityUid user, EntityUid weapon, DamageSpecifier baseDamage, Vector2? direction) + public MeleeHitEvent(HashSet hitEntities, EntityUid user, EntityUid weapon, DamageSpecifier baseDamage, Vector2? direction) { HitEntities = hitEntities; User = user; diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index a4b11168f5..95e6eaa626 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -527,7 +527,7 @@ public abstract partial class SharedMeleeWeaponSystem : EntitySystem LogImpact.Low, $"{ToPrettyString(user):actor} melee attacked (light) using {ToPrettyString(meleeUid):tool} and missed"); } - var missEvent = new MeleeHitEvent(new List(), user, meleeUid, damage, null); + var missEvent = new MeleeHitEvent(new HashSet(), user, meleeUid, damage, null); RaiseLocalEvent(meleeUid, missEvent); _meleeSound.PlaySwingSound(user, meleeUid, component); return; @@ -536,15 +536,15 @@ public abstract partial class SharedMeleeWeaponSystem : EntitySystem // 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 { target.Value }, user, meleeUid, damage, null); + var hitEvent = new MeleeHitEvent(new HashSet { target.Value }, user, meleeUid, damage, null); RaiseLocalEvent(meleeUid, hitEvent); if (hitEvent.Handled) return; - var targets = new List(1) + var targets = new HashSet(1) { - target.Value + target.Value, }; var weapon = GetEntity(ev.Weapon); @@ -601,7 +601,7 @@ public abstract partial class SharedMeleeWeaponSystem : EntitySystem } } - protected abstract void DoDamageEffect(List targets, EntityUid? user, TransformComponent targetXform); + protected abstract void DoDamageEffect(HashSet targets, EntityUid? user, TransformComponent targetXform); private bool DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session) { @@ -620,7 +620,7 @@ public abstract partial class SharedMeleeWeaponSystem : EntitySystem var damage = GetDamage(meleeUid, user, component); var resistanceBypass = GetResistanceBypass(meleeUid, user, component); - var entities = GetEntityList(ev.Entities); + var entities = GetEntitySet(ev.Entities); if (entities.Count == 0) { @@ -636,7 +636,7 @@ public abstract partial class SharedMeleeWeaponSystem : EntitySystem LogImpact.Low, $"{ToPrettyString(user):actor} melee attacked (heavy) using {ToPrettyString(meleeUid):tool} and missed"); } - var missEvent = new MeleeHitEvent(new List(), user, meleeUid, damage, direction); + var missEvent = new MeleeHitEvent(new HashSet(), user, meleeUid, damage, direction); RaiseLocalEvent(meleeUid, missEvent); // immediate audio feedback @@ -648,35 +648,21 @@ public abstract partial class SharedMeleeWeaponSystem : EntitySystem // Naughty input if (entities.Count > MaxTargets) { - entities.RemoveRange(MaxTargets, entities.Count - MaxTargets); + entities = entities.Take(MaxTargets).ToHashSet(); } // Validate client - for (var i = entities.Count - 1; i >= 0; i--) - { - var entity = entities[i]; + entities.RemoveWhere(entity => TerminatingOrDeleted(entity) || + !ArcRaySuccessful(entity, + userPos, + direction.ToWorldAngle(), + component.Angle, + distance, + userXform.MapID, + user, + session)); - if (TerminatingOrDeleted(entity)) - { - entities.RemoveAt(i); - continue; - } - - if (!ArcRaySuccessful(entity, - userPos, - direction.ToWorldAngle(), - component.Angle, - distance, - userXform.MapID, - user, - session)) - { - // Bad input - entities.RemoveAt(i); - } - } - - var targets = new List(); + var targets = new HashSet(); foreach (var entity in entities) { if (entity == user || @@ -710,17 +696,17 @@ public abstract partial class SharedMeleeWeaponSystem : EntitySystem } var appliedDamage = new DamageSpecifier(); + var filteredTargets = new HashSet(); - for (var i = targets.Count - 1; i >= 0; i--) + foreach (var entity in targets) { - var entity = targets[i]; // We raise an attack attempt here as well, // primarily because this was an untargeted wideswing: if a subscriber to that event cared about // the potential target (such as for pacifism), they need to be made aware of the target here. // In that case, just continue. if (!Blocker.CanAttack(user, entity, (weapon, component))) { - targets.RemoveAt(i); + filteredTargets.Add(entity); continue; } @@ -755,9 +741,11 @@ public abstract partial class SharedMeleeWeaponSystem : EntitySystem } if (TerminatingOrDeleted(entity)) - targets.RemoveAt(i); + filteredTargets.Add(entity); } + targets.ExceptWith(filteredTargets); + if (entities.Count != 0) { var target = entities.First(); @@ -768,12 +756,12 @@ public abstract partial class SharedMeleeWeaponSystem : EntitySystem { if (appliedDamage.GetTotal() > FixedPoint2.Zero) { - DoDamageEffect(targets, user, Transform(targets[0])); + DoDamageEffect(targets, user, Transform(targets.First())); ResetUndamagedSwingsCount((meleeUid, component)); } else { - UndamagedAttack((meleeUid, component), targets[0], user); + UndamagedAttack((meleeUid, component), targets.First(), user); } }