diff --git a/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs b/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs index b817bffee4..d179f3f63f 100644 --- a/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs +++ b/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs @@ -36,20 +36,24 @@ public sealed class GunSpreadOverlay : Overlay var player = _player.LocalPlayer?.ControlledEntity; if (player == null || - !_entManager.TryGetComponent(player, out var xform)) return; + !_entManager.TryGetComponent(player, out var xform)) + { + return; + } var mapPos = xform.MapPosition; - if (mapPos.MapId == MapId.Nullspace) return; + if (mapPos.MapId == MapId.Nullspace) + return; - var gun = _guns.GetGun(player.Value); - - if (gun == null) return; + if (!_guns.TryGetGun(player.Value, out var gunUid, out var gun)) + return; var mouseScreenPos = _input.MouseScreenPosition; var mousePos = _eye.ScreenToMap(mouseScreenPos); - if (mapPos.MapId != mousePos.MapId) return; + if (mapPos.MapId != mousePos.MapId) + return; // (☞゚ヮ゚)☞ var maxSpread = gun.MaxAngle; diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.AmmoCounter.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.AmmoCounter.cs index afb4229bb7..10f789b42a 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.AmmoCounter.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.AmmoCounter.cs @@ -27,7 +27,8 @@ public sealed partial class GunSystem /// private void RefreshControl(EntityUid uid, AmmoCounterComponent? component = null) { - if (!Resolve(uid, ref component, false)) return; + if (!Resolve(uid, ref component, false)) + return; component.Control?.Dispose(); component.Control = null; @@ -44,7 +45,8 @@ public sealed partial class GunSystem private void UpdateAmmoCount(EntityUid uid, AmmoCounterComponent component) { - if (component.Control == null) return; + if (component.Control == null) + return; var ev = new UpdateAmmoCounterEvent() { @@ -59,7 +61,8 @@ public sealed partial class GunSystem // Don't use resolves because the method is shared and there's no compref and I'm trying to // share as much code as possible if (!Timing.IsFirstTimePredicted || - !TryComp(uid, out var clientComp)) return; + !TryComp(uid, out var clientComp)) + return; UpdateAmmoCount(uid, clientComp); } diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs index 2c52865cd6..f41c50fc90 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs @@ -12,10 +12,8 @@ using Robust.Client.Graphics; using Robust.Client.Input; using Robust.Client.Player; using Robust.Shared.Animations; -using Robust.Shared.Audio; using Robust.Shared.Input; using Robust.Shared.Map; -using Robust.Shared.Player; using Robust.Shared.Utility; using SharedGunSystem = Content.Shared.Weapons.Ranged.Systems.SharedGunSystem; @@ -35,7 +33,9 @@ public sealed partial class GunSystem : SharedGunSystem get => _spreadOverlay; set { - if (_spreadOverlay == value) return; + if (_spreadOverlay == value) + return; + _spreadOverlay = value; var overlayManager = IoCManager.Resolve(); @@ -131,9 +131,8 @@ public sealed partial class GunSystem : SharedGunSystem } var entity = entityNull.Value; - var gun = GetGun(entity); - if (gun == null) + if (!TryGetGun(entity, out var gunUid, out var gun)) { return; } @@ -141,7 +140,7 @@ public sealed partial class GunSystem : SharedGunSystem if (_inputSystem.CmdStates.GetState(EngineKeyFunctions.Use) != BoundKeyState.Down) { if (gun.ShotCounter != 0) - EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = gun.Owner }); + EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = gunUid }); return; } @@ -153,40 +152,40 @@ public sealed partial class GunSystem : SharedGunSystem if (mousePos.MapId == MapId.Nullspace) { if (gun.ShotCounter != 0) - EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = gun.Owner }); + EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = gunUid }); return; } // Define target coordinates relative to gun entity, so that network latency on moving grids doesn't fuck up the target location. - var coordinates = EntityCoordinates.FromMap(entity, mousePos, EntityManager); + var coordinates = EntityCoordinates.FromMap(entity, mousePos, Transform, EntityManager); Sawmill.Debug($"Sending shoot request tick {Timing.CurTick} / {Timing.CurTime}"); EntityManager.RaisePredictiveEvent(new RequestShootEvent { Coordinates = coordinates, - Gun = gun.Owner, + Gun = gunUid, }); } - public override void Shoot(GunComponent gun, List ammo, EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, EntityUid? user = null) + public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? Entity, IShootable Shootable)> ammo, EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, EntityUid? user = null) { // Rather than splitting client / server for every ammo provider it's easier // to just delete the spawned entities. This is for programmer sanity despite the wasted perf. // This also means any ammo specific stuff can be grabbed as necessary. - var direction = fromCoordinates.ToMapPos(EntityManager) - toCoordinates.ToMapPos(EntityManager); + var direction = fromCoordinates.ToMapPos(EntityManager, Transform) - toCoordinates.ToMapPos(EntityManager, Transform); - foreach (var ent in ammo) + foreach (var (ent, shootable) in ammo) { - switch (ent) + switch (shootable) { case CartridgeAmmoComponent cartridge: if (!cartridge.Spent) { - SetCartridgeSpent(cartridge, true); - MuzzleFlash(gun.Owner, cartridge, user); - Audio.PlayPredicted(gun.SoundGunshot, gun.Owner, user); + SetCartridgeSpent(ent!.Value, cartridge, true); + MuzzleFlash(gunUid, cartridge, user); + Audio.PlayPredicted(gun.SoundGunshot, gunUid, user); Recoil(user, direction); // TODO: Can't predict entity deletions. //if (cartridge.DeleteOnSpawn) @@ -194,24 +193,24 @@ public sealed partial class GunSystem : SharedGunSystem } else { - Audio.PlayPredicted(gun.SoundEmpty, gun.Owner, user); + Audio.PlayPredicted(gun.SoundEmpty, gunUid, user); } - if (cartridge.Owner.IsClientSide()) - Del(cartridge.Owner); + if (ent!.Value.IsClientSide()) + Del(ent.Value); break; case AmmoComponent newAmmo: - MuzzleFlash(gun.Owner, newAmmo, user); - Audio.PlayPredicted(gun.SoundGunshot, gun.Owner, user); + MuzzleFlash(gunUid, newAmmo, user); + Audio.PlayPredicted(gun.SoundGunshot, gunUid, user); Recoil(user, direction); - if (newAmmo.Owner.IsClientSide()) - Del(newAmmo.Owner); + if (ent!.Value.IsClientSide()) + Del(ent.Value); else - RemComp(newAmmo.Owner); + RemComp(ent.Value); break; case HitscanPrototype: - Audio.PlayPredicted(gun.SoundGunshot, gun.Owner, user); + Audio.PlayPredicted(gun.SoundGunshot, gunUid, user); Recoil(user, direction); break; } @@ -220,13 +219,17 @@ public sealed partial class GunSystem : SharedGunSystem private void Recoil(EntityUid? user, Vector2 recoil) { - if (!Timing.IsFirstTimePredicted || user == null || recoil == Vector2.Zero) return; + if (!Timing.IsFirstTimePredicted || user == null || recoil == Vector2.Zero) + return; + _recoil.KickCamera(user.Value, recoil.Normalized * 0.5f); } protected override void Popup(string message, EntityUid? uid, EntityUid? user) { - if (uid == null || user == null || !Timing.IsFirstTimePredicted) return; + if (uid == null || user == null || !Timing.IsFirstTimePredicted) + return; + PopupSystem.PopupEntity(message, uid.Value, user.Value); } diff --git a/Content.Server/NPC/Systems/NPCCombatSystem.Ranged.cs b/Content.Server/NPC/Systems/NPCCombatSystem.Ranged.cs index 3149f2a5f1..19367703b9 100644 --- a/Content.Server/NPC/Systems/NPCCombatSystem.Ranged.cs +++ b/Content.Server/NPC/Systems/NPCCombatSystem.Ranged.cs @@ -49,8 +49,9 @@ public sealed partial class NPCCombatSystem var bodyQuery = GetEntityQuery(); var xformQuery = GetEntityQuery(); var combatQuery = GetEntityQuery(); + var query = EntityQueryEnumerator(); - foreach (var (comp, xform) in EntityQuery()) + while (query.MoveNext(out var uid, out var comp, out var xform)) { if (comp.Status == CombatStatus.Unspecified) continue; @@ -70,14 +71,12 @@ public sealed partial class NPCCombatSystem continue; } - if (combatQuery.TryGetComponent(comp.Owner, out var combatMode)) + if (combatQuery.TryGetComponent(uid, out var combatMode)) { combatMode.IsInCombatMode = true; } - var gun = _gun.GetGun(comp.Owner); - - if (gun == null) + if (!_gun.TryGetGun(uid, out var gunUid, out var gun)) { comp.Status = CombatStatus.NoWeapon; comp.ShootAccumulator = 0f; @@ -98,7 +97,7 @@ public sealed partial class NPCCombatSystem if (comp.LOSAccumulator < 0f) { comp.LOSAccumulator += UnoccludedCooldown; - comp.TargetInLOS = _interaction.InRangeUnobstructed(comp.Owner, comp.Target, distance + 0.1f); + comp.TargetInLOS = _interaction.InRangeUnobstructed(uid, comp.Target, distance + 0.1f); } if (!comp.TargetInLOS) @@ -110,7 +109,7 @@ public sealed partial class NPCCombatSystem if (!oldInLos && comp.SoundTargetInLOS != null) { - _audio.PlayPvs(comp.SoundTargetInLOS, comp.Owner); + _audio.PlayPvs(comp.SoundTargetInLOS, uid); } comp.ShootAccumulator += frameTime; @@ -127,7 +126,7 @@ public sealed partial class NPCCombatSystem var goalRotation = (targetSpot - worldPos).ToWorldAngle(); var rotationSpeed = comp.RotationSpeed; - if (!_rotate.TryRotateTo(comp.Owner, goalRotation, frameTime, comp.AccuracyThreshold, rotationSpeed?.Theta ?? double.MaxValue, xform)) + if (!_rotate.TryRotateTo(uid, goalRotation, frameTime, comp.AccuracyThreshold, rotationSpeed?.Theta ?? double.MaxValue, xform)) { continue; } @@ -154,7 +153,7 @@ public sealed partial class NPCCombatSystem targetCordinates = new EntityCoordinates(xform.MapUid!.Value, targetSpot); } - _gun.AttemptShoot(comp.Owner, gun, targetCordinates); + _gun.AttemptShoot(uid, gunUid, gun, targetCordinates); } } } diff --git a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs index 10dd6f74ce..869a98a615 100644 --- a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs @@ -298,7 +298,7 @@ namespace Content.Server.Singularity.EntitySystems _projectile.SetShooter(proj, uid); var targetPos = new EntityCoordinates(uid, (0, -1)); - _gun.Shoot(guncomp, ent, xform.Coordinates, targetPos); + _gun.Shoot(uid, guncomp, ent, xform.Coordinates, targetPos); } private void UpdateAppearance(EmitterComponent component) diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.Revolver.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.Revolver.cs index 73e815f2c4..6ff4750729 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.Revolver.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.Revolver.cs @@ -4,12 +4,13 @@ namespace Content.Server.Weapons.Ranged.Systems; public sealed partial class GunSystem { - protected override void SpinRevolver(RevolverAmmoProviderComponent component, EntityUid? user = null) + protected override void SpinRevolver(EntityUid revolverUid, RevolverAmmoProviderComponent component, EntityUid? user = null) { - base.SpinRevolver(component, user); + base.SpinRevolver(revolverUid, component, user); var index = Random.Next(component.Capacity); - if (component.CurrentIndex == index) return; + if (component.CurrentIndex == index) + return; component.CurrentIndex = index; Dirty(component); diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index f5c9b31ccc..cb67880a00 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -4,7 +4,6 @@ using Content.Server.Cargo.Systems; using Content.Server.Examine; using Content.Server.Interaction; using Content.Server.Stunnable; -using Content.Server.Weapons.Melee; using Content.Server.Weapons.Ranged.Components; using Content.Shared.Damage; using Content.Shared.Damage.Systems; @@ -30,13 +29,13 @@ namespace Content.Server.Weapons.Ranged.Systems; public sealed partial class GunSystem : SharedGunSystem { + [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IComponentFactory _factory = default!; [Dependency] private readonly ExamineSystem _examine = default!; [Dependency] private readonly InteractionSystem _interaction = default!; [Dependency] private readonly PricingSystem _pricing = default!; [Dependency] private readonly StaminaSystem _stamina = default!; [Dependency] private readonly StunSystem _stun = default!; - [Dependency] private readonly IAdminLogManager _adminLogger = default!; public const float DamagePitchVariation = SharedMeleeWeaponSystem.DamagePitchVariation; public const float GunClumsyChance = 0.5f; @@ -63,9 +62,10 @@ public sealed partial class GunSystem : SharedGunSystem args.Price += price * component.UnspawnedCount; } - public override void Shoot(GunComponent gun, List ammo, EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, EntityUid? user = null) + public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? Entity, IShootable Shootable)> ammo, EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, EntityUid? user = null) { // Try a clumsy roll + // TODO: Who put this here if (TryComp(user, out var clumsy)) { for (var i = 0; i < ammo.Count; i++) @@ -77,38 +77,38 @@ public sealed partial class GunSystem : SharedGunSystem _stun.TryParalyze(user.Value, TimeSpan.FromSeconds(3f), true); // Apply salt to the wound ("Honk!") - Audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg"), gun.Owner); - Audio.PlayPvs(new SoundPathSpecifier("/Audio/Items/bikehorn.ogg"), gun.Owner); + Audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg"), gunUid); + Audio.PlayPvs(new SoundPathSpecifier("/Audio/Items/bikehorn.ogg"), gunUid); PopupSystem.PopupEntity(Loc.GetString("gun-clumsy"), user.Value); - _adminLogger.Add(LogType.EntityDelete, LogImpact.Medium, $"Clumsy fire by {ToPrettyString(user.Value)} deleted {ToPrettyString(gun.Owner)}"); - Del(gun.Owner); + _adminLogger.Add(LogType.EntityDelete, LogImpact.Medium, $"Clumsy fire by {ToPrettyString(user.Value)} deleted {ToPrettyString(gunUid)}"); + Del(gunUid); return; } } } - var fromMap = fromCoordinates.ToMap(EntityManager); - var toMap = toCoordinates.ToMapPos(EntityManager); + var fromMap = fromCoordinates.ToMap(EntityManager, Transform); + var toMap = toCoordinates.ToMapPos(EntityManager, Transform); var mapDirection = toMap - fromMap.Position; var mapAngle = mapDirection.ToAngle(); var angle = GetRecoilAngle(Timing.CurTime, gun, mapDirection.ToAngle()); // If applicable, this ensures the projectile is parented to grid on spawn, instead of the map. - EntityCoordinates fromEnt = MapManager.TryFindGridAt(fromMap, out var grid) + var fromEnt = MapManager.TryFindGridAt(fromMap, out var grid) ? fromCoordinates.WithEntityId(grid.Owner, EntityManager) - : new(MapManager.GetMapEntityId(fromMap.MapId), fromMap.Position); + : new EntityCoordinates(MapManager.GetMapEntityId(fromMap.MapId), fromMap.Position); // Update shot based on the recoil toMap = fromMap.Position + angle.ToVec() * mapDirection.Length; mapDirection = toMap - fromMap.Position; - var gunVelocity = Physics.GetMapLinearVelocity(gun.Owner); + var gunVelocity = Physics.GetMapLinearVelocity(gunUid); // I must be high because this was getting tripped even when true. // DebugTools.Assert(direction != Vector2.Zero); var shotProjectiles = new List(ammo.Count); - foreach (var shootable in ammo) + foreach (var (ent, shootable) in ammo) { switch (shootable) { @@ -135,46 +135,45 @@ public sealed partial class GunSystem : SharedGunSystem shotProjectiles.Add(uid); } - //signal ChemicalAmmo to transfer solution - RaiseLocalEvent(cartridge.Owner, new AmmoShotEvent() + RaiseLocalEvent(ent!.Value, new AmmoShotEvent() { FiredProjectiles = shotProjectiles, }); - SetCartridgeSpent(cartridge, true); - MuzzleFlash(gun.Owner, cartridge, user); - Audio.PlayPredicted(gun.SoundGunshot, gun.Owner, user); + SetCartridgeSpent(ent.Value, cartridge, true); + MuzzleFlash(gunUid, cartridge, user); + Audio.PlayPredicted(gun.SoundGunshot, gunUid, user); if (cartridge.DeleteOnSpawn) - Del(cartridge.Owner); + Del(ent.Value); } else { - Audio.PlayPredicted(gun.SoundEmpty, gun.Owner, user); + Audio.PlayPredicted(gun.SoundEmpty, gunUid, user); } // Something like ballistic might want to leave it in the container still - if (!cartridge.DeleteOnSpawn && !Containers.IsEntityInContainer(cartridge.Owner)) - EjectCartridge(cartridge.Owner); + if (!cartridge.DeleteOnSpawn && !Containers.IsEntityInContainer(ent!.Value)) + EjectCartridge(ent.Value); Dirty(cartridge); break; // Ammo shoots itself case AmmoComponent newAmmo: - shotProjectiles.Add(newAmmo.Owner); - MuzzleFlash(gun.Owner, newAmmo, user); - Audio.PlayPredicted(gun.SoundGunshot, gun.Owner, user); + shotProjectiles.Add(ent!.Value); + MuzzleFlash(gunUid, newAmmo, user); + Audio.PlayPredicted(gun.SoundGunshot, gunUid, user); // Do a throw - if (!HasComp(newAmmo.Owner)) + if (!HasComp(ent.Value)) { - RemComp(newAmmo.Owner); + RemComp(ent.Value); // TODO: Someone can probably yeet this a billion miles so need to pre-validate input somewhere up the call stack. - ThrowingSystem.TryThrow(newAmmo.Owner, mapDirection, gun.ProjectileSpeed, user); + ThrowingSystem.TryThrow(ent.Value, mapDirection, gun.ProjectileSpeed, user); break; } - ShootProjectile(newAmmo.Owner, mapDirection, gunVelocity, user, gun.ProjectileSpeed); + ShootProjectile(ent.Value, mapDirection, gunVelocity, user, gun.ProjectileSpeed); break; case HitscanPrototype hitscan: var ray = new CollisionRay(fromMap.Position, mapDirection.Normalized, hitscan.CollisionMask); @@ -194,7 +193,6 @@ public sealed partial class GunSystem : SharedGunSystem var dmg = hitscan.Damage; - bool deleted = false; string hitName = ToPrettyString(hitEntity); if (dmg != null) dmg = Damageable.TryChangeDamage(hitEntity, dmg, origin: user); @@ -202,9 +200,7 @@ public sealed partial class GunSystem : SharedGunSystem // check null again, as TryChangeDamage returns modified damage values if (dmg != null) { - deleted = Deleted(hitEntity); - - if (!deleted) + if (!Deleted(hitEntity)) { if (dmg.Total > FixedPoint2.Zero) RaiseNetworkEvent(new DamageEffectEvent(Color.Red, new List {result.HitEntity}), Filter.Pvs(hitEntity, entityManager: EntityManager)); @@ -230,17 +226,17 @@ public sealed partial class GunSystem : SharedGunSystem FireEffects(fromCoordinates, hitscan.MaxLength, mapDirection.ToAngle(), hitscan); } - Audio.PlayPredicted(gun.SoundGunshot, gun.Owner, user); + Audio.PlayPredicted(gun.SoundGunshot, gunUid, user); break; default: throw new ArgumentOutOfRangeException(); } } - RaiseLocalEvent(gun.Owner, new AmmoShotEvent() + RaiseLocalEvent(gunUid, new AmmoShotEvent() { FiredProjectiles = shotProjectiles, - }, false); + }); } public void ShootProjectile(EntityUid uid, Vector2 direction, Vector2 gunVelocity, EntityUid? user = null, float speed = 20f) @@ -259,7 +255,7 @@ public sealed partial class GunSystem : SharedGunSystem Projectiles.SetShooter(projectile, user.Value); } - Transform(uid).WorldRotation = direction.ToWorldAngle(); + Transform.SetWorldRotation(uid, direction.ToWorldAngle()); } /// @@ -320,7 +316,7 @@ public sealed partial class GunSystem : SharedGunSystem if (!forceWeaponSound && modifiedDamage != null && modifiedDamage.Total > 0 && TryComp(otherEntity, out var rangedSound)) { - var type = MeleeWeaponSystem.GetHighestDamageSound(modifiedDamage, ProtoManager); + var type = SharedMeleeWeaponSystem.GetHighestDamageSound(modifiedDamage, ProtoManager); if (type != null && rangedSound.SoundTypes?.TryGetValue(type, out var damageSoundType) == true) { @@ -354,12 +350,14 @@ public sealed partial class GunSystem : SharedGunSystem // We'll get the effects relative to the grid / map of the firer // Look you could probably optimise this a bit with redundant transforms at this point. - if (TryComp(gridUid, out var gridXform)) + var xformQuery = GetEntityQuery(); + + if (xformQuery.TryGetComponent(gridUid, out var gridXform)) { - var (_, gridRot, gridInvMatrix) = gridXform.GetWorldPositionRotationInvMatrix(); + var (_, gridRot, gridInvMatrix) = Transform.GetWorldPositionRotationInvMatrix(gridUid.Value, xformQuery); fromCoordinates = new EntityCoordinates(gridUid.Value, - gridInvMatrix.Transform(fromCoordinates.ToMapPos(EntityManager))); + gridInvMatrix.Transform(fromCoordinates.ToMapPos(EntityManager, Transform))); // Use the fallback angle I guess? angle -= gridRot; diff --git a/Content.Shared/Weapons/Ranged/Events/TakeAmmoEvent.cs b/Content.Shared/Weapons/Ranged/Events/TakeAmmoEvent.cs index 5ad53fd970..a28816d8e2 100644 --- a/Content.Shared/Weapons/Ranged/Events/TakeAmmoEvent.cs +++ b/Content.Shared/Weapons/Ranged/Events/TakeAmmoEvent.cs @@ -7,16 +7,16 @@ namespace Content.Shared.Weapons.Ranged.Events; /// public sealed class TakeAmmoEvent : EntityEventArgs { - public EntityUid? User; + public readonly EntityUid? User; public readonly int Shots; - public List Ammo; + public List<(EntityUid? Entity, IShootable Shootable)> Ammo; /// /// Coordinates to spawn the ammo at. /// public EntityCoordinates Coordinates; - public TakeAmmoEvent(int shots, List ammo, EntityCoordinates coordinates, EntityUid? user) + public TakeAmmoEvent(int shots, List<(EntityUid? Entity, IShootable Shootable)> ammo, EntityCoordinates coordinates, EntityUid? user) { Shots = shots; Ammo = ammo; diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index dcaaec58ab..3bd3e9d789 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -8,7 +8,6 @@ using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Map; using Robust.Shared.Serialization; -using Robust.Shared.Utility; namespace Content.Shared.Weapons.Ranged.Systems; @@ -32,22 +31,24 @@ public abstract partial class SharedGunSystem private void OnBallisticUse(EntityUid uid, BallisticAmmoProviderComponent component, UseInHandEvent args) { - ManualCycle(component, Transform(uid).MapPosition, args.User); + ManualCycle(uid, component, Transform(uid).MapPosition, args.User); args.Handled = true; } private void OnBallisticInteractUsing(EntityUid uid, BallisticAmmoProviderComponent component, InteractUsingEvent args) { - if (args.Handled || component.Whitelist?.IsValid(args.Used, EntityManager) != true) return; + if (args.Handled || component.Whitelist?.IsValid(args.Used, EntityManager) != true) + return; - if (GetBallisticShots(component) >= component.Capacity) return; + if (GetBallisticShots(component) >= component.Capacity) + return; component.Entities.Add(args.Used); component.Container.Insert(args.Used); // Not predicted so Audio.PlayPredicted(component.SoundInsert, uid, args.User); args.Handled = true; - UpdateBallisticAppearance(component); + UpdateBallisticAppearance(uid, component); Dirty(component); } @@ -93,20 +94,20 @@ public abstract partial class SharedGunSystem RaiseLocalEvent(ammoProvider, evInsert); } - List ammo = new(); + List<(EntityUid? Entity, IShootable Shootable)> ammo = new(); var evTakeAmmo = new TakeAmmoEvent(1, ammo, Transform(args.Used).Coordinates, args.User); RaiseLocalEvent(args.Used, evTakeAmmo); - foreach (var shot in ammo) + foreach (var (ent, _) in ammo) { - if (shot is not AmmoComponent cast) + if (ent == null) continue; - if (!targetComponent.Whitelist.IsValid(cast.Owner)) + if (!targetComponent.Whitelist.IsValid(ent.Value)) { Popup( Loc.GetString("gun-ballistic-transfer-invalid", - ("ammoEntity", cast.Owner), + ("ammoEntity", ent.Value), ("targetEntity", args.Target.Value)), args.Used, args.User); @@ -114,27 +115,28 @@ public abstract partial class SharedGunSystem // TODO: For better or worse, this will play a sound, but it's the // more future-proof thing to do than copying the same code // that OnBallisticInteractUsing has, sans sound. - SimulateInsertAmmo(cast.Owner, args.Used, Transform(args.Used).Coordinates); + SimulateInsertAmmo(ent.Value, args.Used, Transform(args.Used).Coordinates); } else { - SimulateInsertAmmo(cast.Owner, args.Target.Value, Transform(args.Target.Value).Coordinates); + SimulateInsertAmmo(ent.Value, args.Target.Value, Transform(args.Target.Value).Coordinates); } - if (cast.Owner.IsClientSide()) - Del(cast.Owner); + if (ent.Value.IsClientSide()) + Del(ent.Value); } } private void OnBallisticVerb(EntityUid uid, BallisticAmmoProviderComponent component, GetVerbsEvent args) { - if (!args.CanAccess || !args.CanInteract || args.Hands == null) return; + if (!args.CanAccess || !args.CanInteract || args.Hands == null) + return; args.Verbs.Add(new Verb() { Text = Loc.GetString("gun-ballistic-cycle"), Disabled = GetBallisticShots(component) == 0, - Act = () => ManualCycle(component, Transform(uid).MapPosition, args.User), + Act = () => ManualCycle(uid, component, Transform(uid).MapPosition, args.User), }); } @@ -146,17 +148,17 @@ public abstract partial class SharedGunSystem args.PushMarkup(Loc.GetString("gun-magazine-examine", ("color", AmmoExamineColor), ("count", GetBallisticShots(component)))); } - private void ManualCycle(BallisticAmmoProviderComponent component, MapCoordinates coordinates, EntityUid? user = null) + private void ManualCycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates, EntityUid? user = null, GunComponent? gunComp = null) { // Reset shotting for cycling - if (TryComp(component.Owner, out var gunComp) && + if (Resolve(uid, ref gunComp, false) && gunComp is { FireRate: > 0f }) { gunComp.NextFire = Timing.CurTime + TimeSpan.FromSeconds(1 / gunComp.FireRate); } Dirty(component); - Audio.PlayPredicted(component.SoundRack, component.Owner, user); + Audio.PlayPredicted(component.SoundRack, uid, user); var shots = GetBallisticShots(component); component.Cycled = true; @@ -165,9 +167,9 @@ public abstract partial class SharedGunSystem var text = Loc.GetString(shots == 0 ? "gun-ballistic-cycled-empty" : "gun-ballistic-cycled"); - Popup(text, component.Owner, user); - UpdateBallisticAppearance(component); - UpdateAmmoCount(component.Owner); + Popup(text, uid, user); + UpdateBallisticAppearance(uid, component); + UpdateAmmoCount(uid); } protected abstract void Cycle(BallisticAmmoProviderComponent component, MapCoordinates coordinates); @@ -184,7 +186,8 @@ public abstract partial class SharedGunSystem private void OnBallisticHandleState(EntityUid uid, BallisticAmmoProviderComponent component, ref ComponentHandleState args) { - if (args.Current is not BallisticAmmoProviderComponentState state) return; + if (args.Current is not BallisticAmmoProviderComponentState state) + return; component.Cycled = state.Cycled; component.UnspawnedCount = state.UnspawnedCount; @@ -222,7 +225,8 @@ public abstract partial class SharedGunSystem { for (var i = 0; i < args.Shots; i++) { - if (!component.Cycled) break; + if (!component.Cycled) + break; EntityUid entity; @@ -230,7 +234,7 @@ public abstract partial class SharedGunSystem { entity = component.Entities[^1]; - args.Ammo.Add(EnsureComp(entity)); + args.Ammo.Add((entity, EnsureComp(entity))); // Leave the entity as is if it doesn't auto cycle // TODO: Suss this out with NewAmmoComponent as I don't think it gets removed from container properly @@ -246,7 +250,7 @@ public abstract partial class SharedGunSystem { component.UnspawnedCount--; entity = Spawn(component.FillProto, args.Coordinates); - args.Ammo.Add(EnsureComp(entity)); + args.Ammo.Add((entity, EnsureComp(entity))); // Put it back in if it doesn't auto-cycle if (HasComp(entity) && !component.AutoCycle) @@ -269,7 +273,7 @@ public abstract partial class SharedGunSystem } } - UpdateBallisticAppearance(component); + UpdateBallisticAppearance(uid, component); Dirty(component); } @@ -279,13 +283,13 @@ public abstract partial class SharedGunSystem args.Capacity = component.Capacity; } - private void UpdateBallisticAppearance(BallisticAmmoProviderComponent component) + private void UpdateBallisticAppearance(EntityUid uid, BallisticAmmoProviderComponent component) { - if (!Timing.IsFirstTimePredicted || !TryComp(component.Owner, out var appearance)) + if (!Timing.IsFirstTimePredicted || !TryComp(uid, out var appearance)) return; - Appearance.SetData(appearance.Owner, AmmoVisuals.AmmoCount, GetBallisticShots(component), appearance); - Appearance.SetData(appearance.Owner, AmmoVisuals.AmmoMax, component.Capacity, appearance); + Appearance.SetData(uid, AmmoVisuals.AmmoCount, GetBallisticShots(component), appearance); + Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.Capacity, appearance); } [Serializable, NetSerializable] diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.BasicEntity.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.BasicEntity.cs index 73d8effe73..58c3f01d16 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.BasicEntity.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.BasicEntity.cs @@ -38,7 +38,7 @@ public abstract partial class SharedGunSystem Dirty(component); } - UpdateBasicEntityAppearance(component); + UpdateBasicEntityAppearance(uid, component); } private void OnBasicEntityTakeAmmo(EntityUid uid, BasicEntityAmmoProviderComponent component, TakeAmmoEvent args) @@ -54,10 +54,10 @@ public abstract partial class SharedGunSystem } var ent = Spawn(component.Proto, args.Coordinates); - args.Ammo.Add(EnsureComp(ent)); + args.Ammo.Add((ent, EnsureComp(ent))); } - UpdateBasicEntityAppearance(component); + UpdateBasicEntityAppearance(uid, component); Dirty(component); } @@ -67,13 +67,14 @@ public abstract partial class SharedGunSystem args.Count = component.Count ?? int.MaxValue; } - private void UpdateBasicEntityAppearance(BasicEntityAmmoProviderComponent component) + private void UpdateBasicEntityAppearance(EntityUid uid, BasicEntityAmmoProviderComponent component) { - if (!Timing.IsFirstTimePredicted || !TryComp(component.Owner, out var appearance)) return; + if (!Timing.IsFirstTimePredicted || !TryComp(uid, out var appearance)) + return; - Appearance.SetData(appearance.Owner, AmmoVisuals.HasAmmo, component.Count != 0, appearance); - Appearance.SetData(appearance.Owner, AmmoVisuals.AmmoCount, component.Count ?? int.MaxValue, appearance); - Appearance.SetData(appearance.Owner, AmmoVisuals.AmmoMax, component.Capacity ?? int.MaxValue, appearance); + Appearance.SetData(uid, AmmoVisuals.HasAmmo, component.Count != 0, appearance); + Appearance.SetData(uid, AmmoVisuals.AmmoCount, component.Count ?? int.MaxValue, appearance); + Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.Capacity ?? int.MaxValue, appearance); } #region Public API @@ -88,7 +89,7 @@ public abstract partial class SharedGunSystem component.Count = count; Dirty(component); - UpdateBasicEntityAppearance(component); + UpdateBasicEntityAppearance(uid, component); return true; } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs index 2074297039..1d6c85f6f1 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs @@ -29,7 +29,8 @@ public abstract partial class SharedGunSystem private void OnBatteryHandleState(EntityUid uid, BatteryAmmoProviderComponent component, ref ComponentHandleState args) { - if (args.Current is not BatteryAmmoProviderComponentState state) return; + if (args.Current is not BatteryAmmoProviderComponentState state) + return; component.Shots = state.Shots; component.Capacity = state.MaxShots; @@ -56,7 +57,8 @@ public abstract partial class SharedGunSystem var shots = Math.Min(args.Shots, component.Shots); // Don't dirty if it's an empty fire. - if (shots == 0) return; + if (shots == 0) + return; for (var i = 0; i < shots; i++) { @@ -90,15 +92,15 @@ public abstract partial class SharedGunSystem Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.Capacity, appearance); } - private IShootable GetShootable(BatteryAmmoProviderComponent component, EntityCoordinates coordinates) + private (EntityUid? Entity, IShootable) GetShootable(BatteryAmmoProviderComponent component, EntityCoordinates coordinates) { switch (component) { case ProjectileBatteryAmmoProviderComponent proj: var ent = Spawn(proj.Prototype, coordinates); - return EnsureComp(ent); + return (ent, EnsureComp(ent)); case HitscanBatteryAmmoProviderComponent hitscan: - return ProtoManager.Index(hitscan.Prototype); + return (null, ProtoManager.Index(hitscan.Prototype)); default: throw new ArgumentOutOfRangeException(); } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Cartridges.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Cartridges.cs index 6d2c6163e3..14ed2df950 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Cartridges.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Cartridges.cs @@ -14,7 +14,9 @@ public abstract partial class SharedGunSystem private void OnCartridgeHandleState(EntityUid uid, CartridgeAmmoComponent component, ref ComponentHandleState args) { - if (args.Current is not CartridgeAmmoComponentState state) return; + if (args.Current is not CartridgeAmmoComponentState state) + return; + component.Spent = state.Spent; } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs index db7350e35f..39e3a83a36 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs @@ -27,7 +27,7 @@ public abstract partial class SharedGunSystem if (!args.IsInDetailsRange) return; - var (count, _) = GetChamberMagazineCountCapacity(component); + var (count, _) = GetChamberMagazineCountCapacity(uid, component); args.PushMarkup(Loc.GetString("gun-magazine-examine", ("color", AmmoExamineColor), ("count", count))); } @@ -41,7 +41,9 @@ public abstract partial class SharedGunSystem } entity = slot.ContainedEntity; - if (entity == null) return false; + if (entity == null) + return false; + container.Remove(entity.Value); return true; } @@ -57,10 +59,10 @@ public abstract partial class SharedGunSystem return slot.ContainedEntity; } - protected (int, int) GetChamberMagazineCountCapacity(ChamberMagazineAmmoProviderComponent component) + protected (int, int) GetChamberMagazineCountCapacity(EntityUid uid, ChamberMagazineAmmoProviderComponent component) { - var count = GetChamberEntity(component.Owner) != null ? 1 : 0; - var (magCount, magCapacity) = GetMagazineCountCapacity(component); + var count = GetChamberEntity(uid) != null ? 1 : 0; + var (magCount, magCapacity) = GetMagazineCountCapacity(uid, component); return (count + magCount, magCapacity); } @@ -81,7 +83,7 @@ public abstract partial class SharedGunSystem if (TryTakeChamberEntity(uid, out var chamberEnt)) { - args.Ammo.Add(EnsureComp(chamberEnt.Value)); + args.Ammo.Add((chamberEnt.Value, EnsureComp(chamberEnt.Value))); } var magEnt = GetMagazineEntity(uid); @@ -90,15 +92,15 @@ public abstract partial class SharedGunSystem if (magEnt != null) { // We pass in Shots not Shots - 1 as we'll take the last entity and move it into the chamber. - var relayedArgs = new TakeAmmoEvent(args.Shots, new List(), args.Coordinates, args.User); - RaiseLocalEvent(magEnt.Value, relayedArgs, false); + var relayedArgs = new TakeAmmoEvent(args.Shots, new List<(EntityUid? Entity, IShootable Shootable)>(), args.Coordinates, args.User); + RaiseLocalEvent(magEnt.Value, relayedArgs); // Put in the nth slot back into the chamber // Rest of the ammo gets shot if (relayedArgs.Ammo.Count > 0) { - var newChamberEnt = ((AmmoComponent) relayedArgs.Ammo[^1]).Owner; - TryInsertChamber(uid, newChamberEnt); + var newChamberEnt = relayedArgs.Ammo[^1].Entity; + TryInsertChamber(uid, newChamberEnt!.Value); } // Anything above the chamber-refill amount gets fired. @@ -117,7 +119,7 @@ public abstract partial class SharedGunSystem const int capacity = 1; var ammoEv = new GetAmmoCountEvent(); - RaiseLocalEvent(magEnt.Value, ref ammoEv, false); + RaiseLocalEvent(magEnt.Value, ref ammoEv); FinaliseMagazineTakeAmmo(uid, component, args, count + ammoEv.Count, capacity + ammoEv.Capacity, appearance); } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Container.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Container.cs index 453daded30..204faecdb6 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Container.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Container.cs @@ -32,7 +32,7 @@ public partial class SharedGunSystem if (_netMan.IsServer) container.Remove(ent); - args.Ammo.Add(EnsureComp(ent)); + args.Ammo.Add((ent, EnsureComp(ent))); } } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Interactions.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Interactions.cs index 2b04c41e33..953881d7d6 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Interactions.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Interactions.cs @@ -33,7 +33,7 @@ public abstract partial class SharedGunSystem AlternativeVerb verb = new() { - Act = () => SelectFire(component, nextMode, args.User), + Act = () => SelectFire(uid, component, nextMode, args.User), Text = Loc.GetString("gun-selector-verb", ("mode", GetLocSelector(nextMode))), Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/fold.svg.192dpi.png")), }; @@ -47,7 +47,9 @@ public abstract partial class SharedGunSystem foreach (var mode in Enum.GetValues()) { - if ((mode & component.AvailableModes) == 0x0) continue; + if ((mode & component.AvailableModes) == 0x0) + continue; + modes.Add(mode); } @@ -55,9 +57,10 @@ public abstract partial class SharedGunSystem return modes[(index + 1) % modes.Count]; } - private void SelectFire(GunComponent component, SelectiveFire fire, EntityUid? user = null) + private void SelectFire(EntityUid uid, GunComponent component, SelectiveFire fire, EntityUid? user = null) { - if (component.SelectedMode == fire) return; + if (component.SelectedMode == fire) + return; DebugTools.Assert((component.AvailableModes & fire) != 0x0); component.SelectedMode = fire; @@ -69,22 +72,23 @@ public abstract partial class SharedGunSystem else component.NextFire += cooldown; - Audio.PlayPredicted(component.SoundModeToggle, component.Owner, user); - Popup(Loc.GetString("gun-selected-mode", ("mode", GetLocSelector(fire))), component.Owner, user); + Audio.PlayPredicted(component.SoundModeToggle, uid, user); + Popup(Loc.GetString("gun-selected-mode", ("mode", GetLocSelector(fire))), uid, user); Dirty(component); } /// /// Cycles the gun's to the next available one. /// - public void CycleFire(GunComponent component, EntityUid? user = null) + public void CycleFire(EntityUid uid, GunComponent component, EntityUid? user = null) { // Noop - if (component.SelectedMode == component.AvailableModes) return; + if (component.SelectedMode == component.AvailableModes) + return; DebugTools.Assert((component.AvailableModes & component.SelectedMode) == component.SelectedMode); var nextMode = GetNextMode(component); - SelectFire(component, nextMode, user); + SelectFire(uid, component, nextMode, user); } // TODO: Actions need doing for guns anyway. @@ -95,6 +99,6 @@ public abstract partial class SharedGunSystem private void OnCycleMode(EntityUid uid, GunComponent component, CycleModeEvent args) { - SelectFire(component, args.Mode, args.Performer); + SelectFire(uid, component, args.Mode, args.Performer); } } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Magazine.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Magazine.cs index cabd0d0683..2faa6da960 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Magazine.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Magazine.cs @@ -26,7 +26,7 @@ public abstract partial class SharedGunSystem if (!args.IsInDetailsRange) return; - var (count, _) = GetMagazineCountCapacity(component); + var (count, _) = GetMagazineCountCapacity(uid, component); args.PushMarkup(Loc.GetString("gun-magazine-examine", ("color", AmmoExamineColor), ("count", count))); } @@ -34,23 +34,25 @@ public abstract partial class SharedGunSystem { var magEnt = GetMagazineEntity(uid); - if (magEnt == null) return; + if (magEnt == null) + return; RaiseLocalEvent(magEnt.Value, args); UpdateAmmoCount(uid); - UpdateMagazineAppearance(component, magEnt.Value); + UpdateMagazineAppearance(uid, component, magEnt.Value); } private void OnMagazineVerb(EntityUid uid, MagazineAmmoProviderComponent component, GetVerbsEvent args) { - if (!args.CanInteract || !args.CanAccess) return; + if (!args.CanInteract || !args.CanAccess) + return; var magEnt = GetMagazineEntity(uid); if (magEnt != null) { RaiseLocalEvent(magEnt.Value, args); - UpdateMagazineAppearance(component, magEnt.Value); + UpdateMagazineAppearance(magEnt.Value, component, magEnt.Value); } } @@ -66,11 +68,11 @@ public abstract partial class SharedGunSystem Appearance.SetData(uid, AmmoVisuals.MagLoaded, GetMagazineEntity(uid) != null, appearance); } - protected (int, int) GetMagazineCountCapacity(MagazineAmmoProviderComponent component) + protected (int, int) GetMagazineCountCapacity(EntityUid uid, MagazineAmmoProviderComponent component) { var count = 0; var capacity = 1; - var magEnt = GetMagazineEntity(component.Owner); + var magEnt = GetMagazineEntity(uid); if (magEnt != null) { @@ -86,7 +88,11 @@ public abstract partial class SharedGunSystem protected EntityUid? GetMagazineEntity(EntityUid uid) { if (!Containers.TryGetContainer(uid, MagazineSlot, out var container) || - container is not ContainerSlot slot) return null; + container is not ContainerSlot slot) + { + return null; + } + return slot.ContainedEntity; } @@ -102,11 +108,11 @@ public abstract partial class SharedGunSystem } // Pass the event onwards. - RaiseLocalEvent(magEntity.Value, args, false); + RaiseLocalEvent(magEntity.Value, args); // Should be Dirtied by what other ammoprovider is handling it. var ammoEv = new GetAmmoCountEvent(); - RaiseLocalEvent(magEntity.Value, ref ammoEv, false); + RaiseLocalEvent(magEntity.Value, ref ammoEv); FinaliseMagazineTakeAmmo(uid, component, args, ammoEv.Count, ammoEv.Capacity, appearance); } @@ -115,23 +121,23 @@ public abstract partial class SharedGunSystem // If no ammo then check for autoeject if (component.AutoEject && args.Ammo.Count == 0) { - EjectMagazine(component); + EjectMagazine(uid, component); Audio.PlayPredicted(component.SoundAutoEject, uid, args.User); } - UpdateMagazineAppearance(appearance, true, count, capacity); + UpdateMagazineAppearance(uid, appearance, true, count, capacity); } - private void UpdateMagazineAppearance(MagazineAmmoProviderComponent component, EntityUid magEnt) + private void UpdateMagazineAppearance(EntityUid uid, MagazineAmmoProviderComponent component, EntityUid magEnt) { - TryComp(component.Owner, out var appearance); + TryComp(uid, out var appearance); var count = 0; var capacity = 0; if (component is ChamberMagazineAmmoProviderComponent chamber) { - count = GetChamberEntity(chamber.Owner) != null ? 1 : 0; + count = GetChamberEntity(uid) != null ? 1 : 0; capacity = 1; } @@ -143,27 +149,28 @@ public abstract partial class SharedGunSystem capacity += addCapacity; } - UpdateMagazineAppearance(appearance, true, count, capacity); + UpdateMagazineAppearance(uid, appearance, true, count, capacity); } - private void UpdateMagazineAppearance(AppearanceComponent? appearance, bool magLoaded, int count, int capacity) + private void UpdateMagazineAppearance(EntityUid uid, AppearanceComponent? appearance, bool magLoaded, int count, int capacity) { if (appearance == null) return; // Copy the magazine's appearance data - Appearance.SetData(appearance.Owner, AmmoVisuals.MagLoaded, magLoaded, appearance); - Appearance.SetData(appearance.Owner, AmmoVisuals.HasAmmo, count != 0, appearance); - Appearance.SetData(appearance.Owner, AmmoVisuals.AmmoCount, count, appearance); - Appearance.SetData(appearance.Owner, AmmoVisuals.AmmoMax, capacity, appearance); + Appearance.SetData(uid, AmmoVisuals.MagLoaded, magLoaded, appearance); + Appearance.SetData(uid, AmmoVisuals.HasAmmo, count != 0, appearance); + Appearance.SetData(uid, AmmoVisuals.AmmoCount, count, appearance); + Appearance.SetData(uid, AmmoVisuals.AmmoMax, capacity, appearance); } - private void EjectMagazine(MagazineAmmoProviderComponent component) + private void EjectMagazine(EntityUid uid, MagazineAmmoProviderComponent component) { - var ent = GetMagazineEntity(component.Owner); + var ent = GetMagazineEntity(uid); - if (ent == null) return; + if (ent == null) + return; - _slots.TryEject(component.Owner, MagazineSlot, null, out var a, excludeUserAudio: true); + _slots.TryEject(uid, MagazineSlot, null, out var a, excludeUserAudio: true); } } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs index 8f9a2475bb..040d3f5713 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs @@ -34,9 +34,10 @@ public partial class SharedGunSystem private void OnRevolverInteractUsing(EntityUid uid, RevolverAmmoProviderComponent component, InteractUsingEvent args) { - if (args.Handled) return; + if (args.Handled) + return; - if (TryRevolverInsert(component, args.Used, args.User)) + if (TryRevolverInsert(uid, component, args.Used, args.User)) args.Handled = true; } @@ -52,7 +53,8 @@ public partial class SharedGunSystem private void OnRevolverHandleState(EntityUid uid, RevolverAmmoProviderComponent component, ref ComponentHandleState args) { - if (args.Current is not RevolverAmmoProviderComponentState state) return; + if (args.Current is not RevolverAmmoProviderComponentState state) + return; var oldIndex = component.CurrentIndex; component.CurrentIndex = state.CurrentIndex; @@ -73,7 +75,7 @@ public partial class SharedGunSystem } } - public bool TryRevolverInsert(RevolverAmmoProviderComponent component, EntityUid uid, EntityUid? user) + public bool TryRevolverInsert(EntityUid revolverUid, RevolverAmmoProviderComponent component, EntityUid uid, EntityUid? user) { if (component.Whitelist?.IsValid(uid, EntityManager) == false) return false; @@ -93,19 +95,19 @@ public partial class SharedGunSystem if (freeSlots == 0) { - Popup(Loc.GetString("gun-revolver-full"), component.Owner, user); + Popup(Loc.GetString("gun-revolver-full"), revolverUid, user); return false; } var xformQuery = GetEntityQuery(); var xform = xformQuery.GetComponent(uid); - var ammo = new List(freeSlots); + var ammo = new List<(EntityUid? Entity, IShootable Shootable)>(freeSlots); var ev = new TakeAmmoEvent(freeSlots, ammo, xform.Coordinates, user); RaiseLocalEvent(uid, ev); if (ev.Ammo.Count == 0) { - Popup(Loc.GetString("gun-speedloader-empty"), component.Owner, user); + Popup(Loc.GetString("gun-speedloader-empty"), revolverUid, user); return false; } @@ -119,29 +121,29 @@ public partial class SharedGunSystem continue; } - var ent = ev.Ammo.Last(); + var ent = ev.Ammo.Last().Entity; ev.Ammo.RemoveAt(ev.Ammo.Count - 1); - if (ent is not AmmoComponent ammoComp) + if (ent == null) { Sawmill.Error($"Tried to load hitscan into a revolver which is unsupported"); continue; } - component.AmmoSlots[index] = ammoComp.Owner; - component.AmmoContainer.Insert(ammoComp.Owner, EntityManager); + component.AmmoSlots[index] = ent.Value; + component.AmmoContainer.Insert(ent.Value, EntityManager); if (ev.Ammo.Count == 0) break; } DebugTools.Assert(ammo.Count == 0); - UpdateRevolverAppearance(component); + UpdateRevolverAppearance(revolverUid, component); UpdateAmmoCount(uid); Dirty(component); - Audio.PlayPredicted(component.SoundInsert, component.Owner, user); - Popup(Loc.GetString("gun-revolver-insert"), component.Owner, user); + Audio.PlayPredicted(component.SoundInsert, revolverUid, user); + Popup(Loc.GetString("gun-revolver-insert"), revolverUid, user); return true; } @@ -151,30 +153,35 @@ public partial class SharedGunSystem var index = (component.CurrentIndex + i) % component.Capacity; if (component.AmmoSlots[index] != null || - component.Chambers[index] != null) continue; + component.Chambers[index] != null) + { + continue; + } component.AmmoSlots[index] = uid; component.AmmoContainer.Insert(uid); - Audio.PlayPredicted(component.SoundInsert, component.Owner, user); - Popup(Loc.GetString("gun-revolver-insert"), component.Owner, user); - UpdateRevolverAppearance(component); + Audio.PlayPredicted(component.SoundInsert, revolverUid, user); + Popup(Loc.GetString("gun-revolver-insert"), revolverUid, user); + UpdateRevolverAppearance(revolverUid, component); UpdateAmmoCount(uid); Dirty(component); return true; } - Popup(Loc.GetString("gun-revolver-full"), component.Owner, user); + + Popup(Loc.GetString("gun-revolver-full"), revolverUid, user); return false; } private void OnRevolverVerbs(EntityUid uid, RevolverAmmoProviderComponent component, GetVerbsEvent args) { - if (!args.CanAccess || !args.CanInteract || args.Hands == null) return; + if (!args.CanAccess || !args.CanInteract || args.Hands == null) + return; args.Verbs.Add(new AlternativeVerb() { Text = Loc.GetString("gun-revolver-empty"), Disabled = !AnyRevolverCartridges(component), - Act = () => EmptyRevolver(component, args.User), + Act = () => EmptyRevolver(uid, component, args.User), Priority = 1 }); @@ -182,7 +189,7 @@ public partial class SharedGunSystem { Text = Loc.GetString("gun-revolver-spin"), // Category = VerbCategory.G, - Act = () => SpinRevolver(component, args.User) + Act = () => SpinRevolver(uid, component, args.User) }); } @@ -191,7 +198,10 @@ public partial class SharedGunSystem for (var i = 0; i < component.Capacity; i++) { if (component.Chambers[i] != null || - component.AmmoSlots[i] != null) return true; + component.AmmoSlots[i] != null) + { + return true; + } } return false; @@ -238,9 +248,9 @@ public partial class SharedGunSystem return count; } - public void EmptyRevolver(RevolverAmmoProviderComponent component, EntityUid? user = null) + public void EmptyRevolver(EntityUid revolverUid, RevolverAmmoProviderComponent component, EntityUid? user = null) { - var xform = Transform(component.Owner); + var xform = Transform(revolverUid); var mapCoordinates = xform.MapPosition; var anyEmpty = false; @@ -259,7 +269,7 @@ public partial class SharedGunSystem var uid = Spawn(component.FillPrototype, mapCoordinates); if (TryComp(uid, out var cartridge)) - SetCartridgeSpent(cartridge, !(bool) chamber); + SetCartridgeSpent(uid, cartridge, !(bool) chamber); EjectCartridge(uid); } @@ -281,28 +291,28 @@ public partial class SharedGunSystem if (anyEmpty) { - Audio.PlayPredicted(component.SoundEject, component.Owner, user); - UpdateAmmoCount(component.Owner); - UpdateRevolverAppearance(component); + Audio.PlayPredicted(component.SoundEject, revolverUid, user); + UpdateAmmoCount(revolverUid); + UpdateRevolverAppearance(revolverUid, component); Dirty(component); } } - private void UpdateRevolverAppearance(RevolverAmmoProviderComponent component) + private void UpdateRevolverAppearance(EntityUid uid, RevolverAmmoProviderComponent component) { - if (!TryComp(component.Owner, out var appearance)) + if (!TryComp(uid, out var appearance)) return; var count = GetRevolverCount(component); - Appearance.SetData(component.Owner, AmmoVisuals.HasAmmo, count != 0, appearance); - Appearance.SetData(component.Owner, AmmoVisuals.AmmoCount, count, appearance); - Appearance.SetData(component.Owner, AmmoVisuals.AmmoMax, component.Capacity, appearance); + Appearance.SetData(uid, AmmoVisuals.HasAmmo, count != 0, appearance); + Appearance.SetData(uid, AmmoVisuals.AmmoCount, count, appearance); + Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.Capacity, appearance); } - protected virtual void SpinRevolver(RevolverAmmoProviderComponent component, EntityUid? user = null) + protected virtual void SpinRevolver(EntityUid revolverUid, RevolverAmmoProviderComponent component, EntityUid? user = null) { - Audio.PlayPredicted(component.SoundSpin, component.Owner, user); - Popup(Loc.GetString("gun-revolver-spun"), component.Owner, user); + Audio.PlayPredicted(component.SoundSpin, revolverUid, user); + Popup(Loc.GetString("gun-revolver-spun"), revolverUid, user); } private void OnRevolverTakeAmmo(EntityUid uid, RevolverAmmoProviderComponent component, TakeAmmoEvent args) @@ -327,14 +337,15 @@ public partial class SharedGunSystem if (TryComp(ent, out var cartridge)) { component.Chambers[index] = false; - SetCartridgeSpent(cartridge, true); - args.Ammo.Add(EnsureComp(Spawn(cartridge.Prototype, args.Coordinates))); + SetCartridgeSpent(ent, cartridge, true); + var spawned = Spawn(cartridge.Prototype, args.Coordinates); + args.Ammo.Add((spawned, EnsureComp(spawned))); Del(ent); continue; } component.Chambers[i] = null; - args.Ammo.Add(EnsureComp(ent)); + args.Ammo.Add((ent, EnsureComp(ent))); } } else if (component.AmmoSlots[index] != null) @@ -343,21 +354,23 @@ public partial class SharedGunSystem if (TryComp(ent, out var cartridge)) { - if (cartridge.Spent) continue; + if (cartridge.Spent) + continue; - SetCartridgeSpent(cartridge, true); - args.Ammo.Add(EnsureComp(Spawn(cartridge.Prototype, args.Coordinates))); + SetCartridgeSpent(ent.Value, cartridge, true); + var spawned = Spawn(cartridge.Prototype, args.Coordinates); + args.Ammo.Add((spawned, EnsureComp(spawned))); continue; } component.AmmoContainer.Remove(ent.Value); component.AmmoSlots[index] = null; - args.Ammo.Add(EnsureComp(ent.Value)); + args.Ammo.Add((ent.Value, EnsureComp(ent.Value))); Transform(ent.Value).Coordinates = args.Coordinates; } } - UpdateRevolverAppearance(component); + UpdateRevolverAppearance(uid, component); Dirty(component); } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index 55e2dbfd9c..84f5922d69 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -1,6 +1,6 @@ +using System.Diagnostics.CodeAnalysis; using Content.Shared.Actions; using Content.Shared.Administration.Logs; -using Content.Shared.Audio; using Content.Shared.CombatMode; using Content.Shared.Containers.ItemSlots; using Content.Shared.Damage; @@ -22,7 +22,6 @@ using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Physics.Systems; using Robust.Shared.Physics.Components; -using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization; @@ -53,6 +52,7 @@ public abstract partial class SharedGunSystem : EntitySystem [Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] private readonly SharedGravitySystem _gravity = default!; [Dependency] protected readonly SharedProjectileSystem Projectiles = default!; + [Dependency] protected readonly SharedTransformSystem Transform = default!; protected ISawmill Sawmill = default!; @@ -107,27 +107,31 @@ public abstract partial class SharedGunSystem : EntitySystem { var user = args.SenderSession.AttachedEntity; - if (user == null) return; + if (user == null || + !TryGetGun(user.Value, out var ent, out var gun)) + return; - var gun = GetGun(user.Value); - - if (gun?.Owner != msg.Gun) return; + if (ent != msg.Gun) + return; gun.ShootCoordinates = msg.Coordinates; Sawmill.Debug($"Set shoot coordinates to {gun.ShootCoordinates}"); - AttemptShoot(user.Value, gun); + AttemptShoot(user.Value, ent, gun); } private void OnStopShootRequest(RequestStopShootEvent ev, EntitySessionEventArgs args) { if (args.SenderSession.AttachedEntity == null || - !TryComp(ev.Gun, out var gun)) return; + !TryComp(ev.Gun, out var gun) || + !TryGetGun(args.SenderSession.AttachedEntity.Value, out _, out var userGun)) + { + return; + } - var userGun = GetGun(args.SenderSession.AttachedEntity.Value); + if (userGun != gun) + return; - if (userGun != gun) return; - - StopShooting(gun); + StopShooting(ev.Gun, gun); } private void OnGetState(EntityUid uid, GunComponent component, ref ComponentGetState args) @@ -147,7 +151,8 @@ public abstract partial class SharedGunSystem : EntitySystem private void OnHandleState(EntityUid uid, GunComponent component, ref ComponentHandleState args) { - if (args.Current is not GunComponentState state) return; + if (args.Current is not GunComponentState state) + return; Sawmill.Debug($"Handle state: setting shot count from {component.ShotCounter} to {state.ShotCounter}"); component.FireRate = state.FireRate; @@ -168,27 +173,40 @@ public abstract partial class SharedGunSystem : EntitySystem return true; } - public GunComponent? GetGun(EntityUid entity) + public bool TryGetGun(EntityUid entity, out EntityUid gunEntity, [NotNullWhen(true)] out GunComponent? gunComp) { + gunEntity = default; + gunComp = null; + if (!_combatMode.IsInCombatMode(entity)) - return null; + return false; if (EntityManager.TryGetComponent(entity, out SharedHandsComponent? hands) && hands.ActiveHandEntity is { } held && TryComp(held, out GunComponent? gun)) { - return gun; + gunEntity = held; + gunComp = gun; + return true; } // Last resort is check if the entity itself is a gun. - return !TryComp(entity, out gun) ? null : gun; + if (TryComp(entity, out gun)) + { + gunEntity = entity; + gunComp = gun; + return true; + } + + return false; } - private void StopShooting(GunComponent gun) + private void StopShooting(EntityUid uid, GunComponent gun) { - if (gun.ShotCounter == 0) return; + if (gun.ShotCounter == 0) + return; - Sawmill.Debug($"Stopped shooting {ToPrettyString(gun.Owner)}"); + Sawmill.Debug($"Stopped shooting {ToPrettyString(uid)}"); gun.ShotCounter = 0; gun.ShootCoordinates = null; Dirty(gun); @@ -197,14 +215,14 @@ public abstract partial class SharedGunSystem : EntitySystem /// /// Attempts to shoot at the target coordinates. Resets the shot counter after every shot. /// - public void AttemptShoot(EntityUid user, GunComponent gun, EntityCoordinates toCoordinates) + public void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun, EntityCoordinates toCoordinates) { gun.ShootCoordinates = toCoordinates; - AttemptShoot(user, gun); + AttemptShoot(user, gunUid, gun); gun.ShotCounter = 0; } - private void AttemptShoot(EntityUid user, GunComponent gun) + private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) { if (gun.FireRate <= 0f) return; @@ -260,22 +278,22 @@ public abstract partial class SharedGunSystem : EntitySystem var fromCoordinates = Transform(user).Coordinates; // Remove ammo - var ev = new TakeAmmoEvent(shots, new List(), fromCoordinates, user); + var ev = new TakeAmmoEvent(shots, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoordinates, user); // Listen it just makes the other code around it easier if shots == 0 to do this. if (shots > 0) - RaiseLocalEvent(gun.Owner, ev, false); + RaiseLocalEvent(gunUid, ev); DebugTools.Assert(ev.Ammo.Count <= shots); DebugTools.Assert(shots >= 0); - UpdateAmmoCount(gun.Owner); + UpdateAmmoCount(gunUid); // Even if we don't actually shoot update the ShotCounter. This is to avoid spamming empty sounds // where the gun may be SemiAuto or Burst. gun.ShotCounter += shots; var attemptEv = new AttemptShootEvent(user); - RaiseLocalEvent(gun.Owner, ref attemptEv); + RaiseLocalEvent(gunUid, ref attemptEv); if (ev.Ammo.Count <= 0 || attemptEv.Cancelled) { @@ -286,7 +304,7 @@ public abstract partial class SharedGunSystem : EntitySystem // Don't spam safety sounds at gun fire rate, play it at a reduced rate. // May cause prediction issues? Needs more tweaking gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds)); - Audio.PlayPredicted(gun.SoundEmpty, gun.Owner, user); + Audio.PlayPredicted(gun.SoundEmpty, gunUid, user); Dirty(gun); return; } @@ -295,19 +313,20 @@ public abstract partial class SharedGunSystem : EntitySystem } // Shoot confirmed - sounds also played here in case it's invalid (e.g. cartridge already spent). - Shoot(gun, ev.Ammo, fromCoordinates, toCoordinates.Value, user); + Shoot(gunUid, gun, ev.Ammo, fromCoordinates, toCoordinates.Value, user); var shotEv = new GunShotEvent(user); - RaiseLocalEvent(gun.Owner, ref shotEv); + RaiseLocalEvent(gunUid, ref shotEv); // Projectiles cause impulses especially important in non gravity environments if (TryComp(user, out var userPhysics)) { if (_gravity.IsWeightless(user, userPhysics)) - CauseImpulse(fromCoordinates, toCoordinates.Value, userPhysics); + CauseImpulse(fromCoordinates, toCoordinates.Value, user, userPhysics); } Dirty(gun); } public void Shoot( + EntityUid gunUid, GunComponent gun, EntityUid ammo, EntityCoordinates fromCoordinates, @@ -315,26 +334,17 @@ public abstract partial class SharedGunSystem : EntitySystem EntityUid? user = null) { var shootable = EnsureComp(ammo); - Shoot(gun, new List(1) { shootable }, fromCoordinates, toCoordinates, user); + Shoot(gunUid, gun, new List<(EntityUid? Entity, IShootable Shootable)>(1) { (ammo, shootable) }, fromCoordinates, toCoordinates, user); } public abstract void Shoot( + EntityUid gunUid, GunComponent gun, - List ammo, + List<(EntityUid? Entity, IShootable Shootable)> ammo, EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, EntityUid? user = null); - public void Shoot( - GunComponent gun, - IShootable ammo, - EntityCoordinates fromCoordinates, - EntityCoordinates toCoordinates, - EntityUid? user = null) - { - Shoot(gun, new List(1) { ammo }, fromCoordinates, toCoordinates, user); - } - protected abstract void Popup(string message, EntityUid? uid, EntityUid? user); /// @@ -342,13 +352,13 @@ public abstract partial class SharedGunSystem : EntitySystem /// protected virtual void UpdateAmmoCount(EntityUid uid) {} - protected void SetCartridgeSpent(CartridgeAmmoComponent cartridge, bool spent) + protected void SetCartridgeSpent(EntityUid uid, CartridgeAmmoComponent cartridge, bool spent) { if (cartridge.Spent != spent) Dirty(cartridge); cartridge.Spent = spent; - Appearance.SetData(cartridge.Owner, AmmoVisuals.Spent, spent); + Appearance.SetData(uid, AmmoVisuals.Spent, spent); } /// @@ -385,15 +395,15 @@ public abstract partial class SharedGunSystem : EntitySystem CreateEffect(gun, ev, user); } - public void CauseImpulse(EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, PhysicsComponent userPhysics) + public void CauseImpulse(EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, EntityUid user, PhysicsComponent userPhysics) { - var fromMap = fromCoordinates.ToMapPos(EntityManager); - var toMap = toCoordinates.ToMapPos(EntityManager); + var fromMap = fromCoordinates.ToMapPos(EntityManager, Transform); + var toMap = toCoordinates.ToMapPos(EntityManager, Transform); var shotDirection = (toMap - fromMap).Normalized; const float impulseStrength = 5.0f; var impulseVector = shotDirection * impulseStrength; - Physics.ApplyLinearImpulse(userPhysics.Owner, -impulseVector, body: userPhysics); + Physics.ApplyLinearImpulse(user, -impulseVector, body: userPhysics); } protected abstract void CreateEffect(EntityUid uid, MuzzleFlashEvent message, EntityUid? user = null);