Remove gun .Owners (#14585)

This commit is contained in:
metalgearsloth
2023-03-11 20:08:22 +11:00
committed by GitHub
parent 19f5c403b5
commit 330bb7bb14
18 changed files with 333 additions and 280 deletions

View File

@@ -36,20 +36,24 @@ public sealed class GunSpreadOverlay : Overlay
var player = _player.LocalPlayer?.ControlledEntity; var player = _player.LocalPlayer?.ControlledEntity;
if (player == null || if (player == null ||
!_entManager.TryGetComponent<TransformComponent>(player, out var xform)) return; !_entManager.TryGetComponent<TransformComponent>(player, out var xform))
{
return;
}
var mapPos = xform.MapPosition; var mapPos = xform.MapPosition;
if (mapPos.MapId == MapId.Nullspace) return; if (mapPos.MapId == MapId.Nullspace)
return;
var gun = _guns.GetGun(player.Value); if (!_guns.TryGetGun(player.Value, out var gunUid, out var gun))
return;
if (gun == null) return;
var mouseScreenPos = _input.MouseScreenPosition; var mouseScreenPos = _input.MouseScreenPosition;
var mousePos = _eye.ScreenToMap(mouseScreenPos); var mousePos = _eye.ScreenToMap(mouseScreenPos);
if (mapPos.MapId != mousePos.MapId) return; if (mapPos.MapId != mousePos.MapId)
return;
// (☞゚ヮ゚)☞ // (☞゚ヮ゚)☞
var maxSpread = gun.MaxAngle; var maxSpread = gun.MaxAngle;

View File

@@ -27,7 +27,8 @@ public sealed partial class GunSystem
/// <param name="component"></param> /// <param name="component"></param>
private void RefreshControl(EntityUid uid, AmmoCounterComponent? component = null) 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?.Dispose();
component.Control = null; component.Control = null;
@@ -44,7 +45,8 @@ public sealed partial class GunSystem
private void UpdateAmmoCount(EntityUid uid, AmmoCounterComponent component) private void UpdateAmmoCount(EntityUid uid, AmmoCounterComponent component)
{ {
if (component.Control == null) return; if (component.Control == null)
return;
var ev = new UpdateAmmoCounterEvent() 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 // 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 // share as much code as possible
if (!Timing.IsFirstTimePredicted || if (!Timing.IsFirstTimePredicted ||
!TryComp<AmmoCounterComponent>(uid, out var clientComp)) return; !TryComp<AmmoCounterComponent>(uid, out var clientComp))
return;
UpdateAmmoCount(uid, clientComp); UpdateAmmoCount(uid, clientComp);
} }

View File

@@ -12,10 +12,8 @@ using Robust.Client.Graphics;
using Robust.Client.Input; using Robust.Client.Input;
using Robust.Client.Player; using Robust.Client.Player;
using Robust.Shared.Animations; using Robust.Shared.Animations;
using Robust.Shared.Audio;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using SharedGunSystem = Content.Shared.Weapons.Ranged.Systems.SharedGunSystem; using SharedGunSystem = Content.Shared.Weapons.Ranged.Systems.SharedGunSystem;
@@ -35,7 +33,9 @@ public sealed partial class GunSystem : SharedGunSystem
get => _spreadOverlay; get => _spreadOverlay;
set set
{ {
if (_spreadOverlay == value) return; if (_spreadOverlay == value)
return;
_spreadOverlay = value; _spreadOverlay = value;
var overlayManager = IoCManager.Resolve<IOverlayManager>(); var overlayManager = IoCManager.Resolve<IOverlayManager>();
@@ -131,9 +131,8 @@ public sealed partial class GunSystem : SharedGunSystem
} }
var entity = entityNull.Value; var entity = entityNull.Value;
var gun = GetGun(entity);
if (gun == null) if (!TryGetGun(entity, out var gunUid, out var gun))
{ {
return; return;
} }
@@ -141,7 +140,7 @@ public sealed partial class GunSystem : SharedGunSystem
if (_inputSystem.CmdStates.GetState(EngineKeyFunctions.Use) != BoundKeyState.Down) if (_inputSystem.CmdStates.GetState(EngineKeyFunctions.Use) != BoundKeyState.Down)
{ {
if (gun.ShotCounter != 0) if (gun.ShotCounter != 0)
EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = gun.Owner }); EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = gunUid });
return; return;
} }
@@ -153,40 +152,40 @@ public sealed partial class GunSystem : SharedGunSystem
if (mousePos.MapId == MapId.Nullspace) if (mousePos.MapId == MapId.Nullspace)
{ {
if (gun.ShotCounter != 0) if (gun.ShotCounter != 0)
EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = gun.Owner }); EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = gunUid });
return; return;
} }
// Define target coordinates relative to gun entity, so that network latency on moving grids doesn't fuck up the target location. // 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}"); Sawmill.Debug($"Sending shoot request tick {Timing.CurTick} / {Timing.CurTime}");
EntityManager.RaisePredictiveEvent(new RequestShootEvent EntityManager.RaisePredictiveEvent(new RequestShootEvent
{ {
Coordinates = coordinates, Coordinates = coordinates,
Gun = gun.Owner, Gun = gunUid,
}); });
} }
public override void Shoot(GunComponent gun, List<IShootable> 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 // 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. // 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. // 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: case CartridgeAmmoComponent cartridge:
if (!cartridge.Spent) if (!cartridge.Spent)
{ {
SetCartridgeSpent(cartridge, true); SetCartridgeSpent(ent!.Value, cartridge, true);
MuzzleFlash(gun.Owner, cartridge, user); MuzzleFlash(gunUid, cartridge, user);
Audio.PlayPredicted(gun.SoundGunshot, gun.Owner, user); Audio.PlayPredicted(gun.SoundGunshot, gunUid, user);
Recoil(user, direction); Recoil(user, direction);
// TODO: Can't predict entity deletions. // TODO: Can't predict entity deletions.
//if (cartridge.DeleteOnSpawn) //if (cartridge.DeleteOnSpawn)
@@ -194,24 +193,24 @@ public sealed partial class GunSystem : SharedGunSystem
} }
else else
{ {
Audio.PlayPredicted(gun.SoundEmpty, gun.Owner, user); Audio.PlayPredicted(gun.SoundEmpty, gunUid, user);
} }
if (cartridge.Owner.IsClientSide()) if (ent!.Value.IsClientSide())
Del(cartridge.Owner); Del(ent.Value);
break; break;
case AmmoComponent newAmmo: case AmmoComponent newAmmo:
MuzzleFlash(gun.Owner, newAmmo, user); MuzzleFlash(gunUid, newAmmo, user);
Audio.PlayPredicted(gun.SoundGunshot, gun.Owner, user); Audio.PlayPredicted(gun.SoundGunshot, gunUid, user);
Recoil(user, direction); Recoil(user, direction);
if (newAmmo.Owner.IsClientSide()) if (ent!.Value.IsClientSide())
Del(newAmmo.Owner); Del(ent.Value);
else else
RemComp<AmmoComponent>(newAmmo.Owner); RemComp<AmmoComponent>(ent.Value);
break; break;
case HitscanPrototype: case HitscanPrototype:
Audio.PlayPredicted(gun.SoundGunshot, gun.Owner, user); Audio.PlayPredicted(gun.SoundGunshot, gunUid, user);
Recoil(user, direction); Recoil(user, direction);
break; break;
} }
@@ -220,13 +219,17 @@ public sealed partial class GunSystem : SharedGunSystem
private void Recoil(EntityUid? user, Vector2 recoil) 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); _recoil.KickCamera(user.Value, recoil.Normalized * 0.5f);
} }
protected override void Popup(string message, EntityUid? uid, EntityUid? user) 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); PopupSystem.PopupEntity(message, uid.Value, user.Value);
} }

View File

@@ -49,8 +49,9 @@ public sealed partial class NPCCombatSystem
var bodyQuery = GetEntityQuery<PhysicsComponent>(); var bodyQuery = GetEntityQuery<PhysicsComponent>();
var xformQuery = GetEntityQuery<TransformComponent>(); var xformQuery = GetEntityQuery<TransformComponent>();
var combatQuery = GetEntityQuery<SharedCombatModeComponent>(); var combatQuery = GetEntityQuery<SharedCombatModeComponent>();
var query = EntityQueryEnumerator<NPCRangedCombatComponent, TransformComponent>();
foreach (var (comp, xform) in EntityQuery<NPCRangedCombatComponent, TransformComponent>()) while (query.MoveNext(out var uid, out var comp, out var xform))
{ {
if (comp.Status == CombatStatus.Unspecified) if (comp.Status == CombatStatus.Unspecified)
continue; continue;
@@ -70,14 +71,12 @@ public sealed partial class NPCCombatSystem
continue; continue;
} }
if (combatQuery.TryGetComponent(comp.Owner, out var combatMode)) if (combatQuery.TryGetComponent(uid, out var combatMode))
{ {
combatMode.IsInCombatMode = true; combatMode.IsInCombatMode = true;
} }
var gun = _gun.GetGun(comp.Owner); if (!_gun.TryGetGun(uid, out var gunUid, out var gun))
if (gun == null)
{ {
comp.Status = CombatStatus.NoWeapon; comp.Status = CombatStatus.NoWeapon;
comp.ShootAccumulator = 0f; comp.ShootAccumulator = 0f;
@@ -98,7 +97,7 @@ public sealed partial class NPCCombatSystem
if (comp.LOSAccumulator < 0f) if (comp.LOSAccumulator < 0f)
{ {
comp.LOSAccumulator += UnoccludedCooldown; 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) if (!comp.TargetInLOS)
@@ -110,7 +109,7 @@ public sealed partial class NPCCombatSystem
if (!oldInLos && comp.SoundTargetInLOS != null) if (!oldInLos && comp.SoundTargetInLOS != null)
{ {
_audio.PlayPvs(comp.SoundTargetInLOS, comp.Owner); _audio.PlayPvs(comp.SoundTargetInLOS, uid);
} }
comp.ShootAccumulator += frameTime; comp.ShootAccumulator += frameTime;
@@ -127,7 +126,7 @@ public sealed partial class NPCCombatSystem
var goalRotation = (targetSpot - worldPos).ToWorldAngle(); var goalRotation = (targetSpot - worldPos).ToWorldAngle();
var rotationSpeed = comp.RotationSpeed; 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; continue;
} }
@@ -154,7 +153,7 @@ public sealed partial class NPCCombatSystem
targetCordinates = new EntityCoordinates(xform.MapUid!.Value, targetSpot); targetCordinates = new EntityCoordinates(xform.MapUid!.Value, targetSpot);
} }
_gun.AttemptShoot(comp.Owner, gun, targetCordinates); _gun.AttemptShoot(uid, gunUid, gun, targetCordinates);
} }
} }
} }

View File

@@ -298,7 +298,7 @@ namespace Content.Server.Singularity.EntitySystems
_projectile.SetShooter(proj, uid); _projectile.SetShooter(proj, uid);
var targetPos = new EntityCoordinates(uid, (0, -1)); 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) private void UpdateAppearance(EmitterComponent component)

View File

@@ -4,12 +4,13 @@ namespace Content.Server.Weapons.Ranged.Systems;
public sealed partial class GunSystem 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); var index = Random.Next(component.Capacity);
if (component.CurrentIndex == index) return; if (component.CurrentIndex == index)
return;
component.CurrentIndex = index; component.CurrentIndex = index;
Dirty(component); Dirty(component);

View File

@@ -4,7 +4,6 @@ using Content.Server.Cargo.Systems;
using Content.Server.Examine; using Content.Server.Examine;
using Content.Server.Interaction; using Content.Server.Interaction;
using Content.Server.Stunnable; using Content.Server.Stunnable;
using Content.Server.Weapons.Melee;
using Content.Server.Weapons.Ranged.Components; using Content.Server.Weapons.Ranged.Components;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Systems; using Content.Shared.Damage.Systems;
@@ -30,13 +29,13 @@ namespace Content.Server.Weapons.Ranged.Systems;
public sealed partial class GunSystem : SharedGunSystem public sealed partial class GunSystem : SharedGunSystem
{ {
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly IComponentFactory _factory = default!; [Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly ExamineSystem _examine = default!; [Dependency] private readonly ExamineSystem _examine = default!;
[Dependency] private readonly InteractionSystem _interaction = default!; [Dependency] private readonly InteractionSystem _interaction = default!;
[Dependency] private readonly PricingSystem _pricing = default!; [Dependency] private readonly PricingSystem _pricing = default!;
[Dependency] private readonly StaminaSystem _stamina = default!; [Dependency] private readonly StaminaSystem _stamina = default!;
[Dependency] private readonly StunSystem _stun = default!; [Dependency] private readonly StunSystem _stun = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
public const float DamagePitchVariation = SharedMeleeWeaponSystem.DamagePitchVariation; public const float DamagePitchVariation = SharedMeleeWeaponSystem.DamagePitchVariation;
public const float GunClumsyChance = 0.5f; public const float GunClumsyChance = 0.5f;
@@ -63,9 +62,10 @@ public sealed partial class GunSystem : SharedGunSystem
args.Price += price * component.UnspawnedCount; args.Price += price * component.UnspawnedCount;
} }
public override void Shoot(GunComponent gun, List<IShootable> 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 // Try a clumsy roll
// TODO: Who put this here
if (TryComp<ClumsyComponent>(user, out var clumsy)) if (TryComp<ClumsyComponent>(user, out var clumsy))
{ {
for (var i = 0; i < ammo.Count; i++) 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); _stun.TryParalyze(user.Value, TimeSpan.FromSeconds(3f), true);
// Apply salt to the wound ("Honk!") // Apply salt to the wound ("Honk!")
Audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg"), gun.Owner); Audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg"), gunUid);
Audio.PlayPvs(new SoundPathSpecifier("/Audio/Items/bikehorn.ogg"), gun.Owner); Audio.PlayPvs(new SoundPathSpecifier("/Audio/Items/bikehorn.ogg"), gunUid);
PopupSystem.PopupEntity(Loc.GetString("gun-clumsy"), user.Value); PopupSystem.PopupEntity(Loc.GetString("gun-clumsy"), user.Value);
_adminLogger.Add(LogType.EntityDelete, LogImpact.Medium, $"Clumsy fire by {ToPrettyString(user.Value)} deleted {ToPrettyString(gun.Owner)}"); _adminLogger.Add(LogType.EntityDelete, LogImpact.Medium, $"Clumsy fire by {ToPrettyString(user.Value)} deleted {ToPrettyString(gunUid)}");
Del(gun.Owner); Del(gunUid);
return; return;
} }
} }
} }
var fromMap = fromCoordinates.ToMap(EntityManager); var fromMap = fromCoordinates.ToMap(EntityManager, Transform);
var toMap = toCoordinates.ToMapPos(EntityManager); var toMap = toCoordinates.ToMapPos(EntityManager, Transform);
var mapDirection = toMap - fromMap.Position; var mapDirection = toMap - fromMap.Position;
var mapAngle = mapDirection.ToAngle(); var mapAngle = mapDirection.ToAngle();
var angle = GetRecoilAngle(Timing.CurTime, gun, 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. // 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) ? 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 // Update shot based on the recoil
toMap = fromMap.Position + angle.ToVec() * mapDirection.Length; toMap = fromMap.Position + angle.ToVec() * mapDirection.Length;
mapDirection = toMap - fromMap.Position; 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. // I must be high because this was getting tripped even when true.
// DebugTools.Assert(direction != Vector2.Zero); // DebugTools.Assert(direction != Vector2.Zero);
var shotProjectiles = new List<EntityUid>(ammo.Count); var shotProjectiles = new List<EntityUid>(ammo.Count);
foreach (var shootable in ammo) foreach (var (ent, shootable) in ammo)
{ {
switch (shootable) switch (shootable)
{ {
@@ -135,46 +135,45 @@ public sealed partial class GunSystem : SharedGunSystem
shotProjectiles.Add(uid); shotProjectiles.Add(uid);
} }
//signal ChemicalAmmo to transfer solution RaiseLocalEvent(ent!.Value, new AmmoShotEvent()
RaiseLocalEvent(cartridge.Owner, new AmmoShotEvent()
{ {
FiredProjectiles = shotProjectiles, FiredProjectiles = shotProjectiles,
}); });
SetCartridgeSpent(cartridge, true); SetCartridgeSpent(ent.Value, cartridge, true);
MuzzleFlash(gun.Owner, cartridge, user); MuzzleFlash(gunUid, cartridge, user);
Audio.PlayPredicted(gun.SoundGunshot, gun.Owner, user); Audio.PlayPredicted(gun.SoundGunshot, gunUid, user);
if (cartridge.DeleteOnSpawn) if (cartridge.DeleteOnSpawn)
Del(cartridge.Owner); Del(ent.Value);
} }
else 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 // Something like ballistic might want to leave it in the container still
if (!cartridge.DeleteOnSpawn && !Containers.IsEntityInContainer(cartridge.Owner)) if (!cartridge.DeleteOnSpawn && !Containers.IsEntityInContainer(ent!.Value))
EjectCartridge(cartridge.Owner); EjectCartridge(ent.Value);
Dirty(cartridge); Dirty(cartridge);
break; break;
// Ammo shoots itself // Ammo shoots itself
case AmmoComponent newAmmo: case AmmoComponent newAmmo:
shotProjectiles.Add(newAmmo.Owner); shotProjectiles.Add(ent!.Value);
MuzzleFlash(gun.Owner, newAmmo, user); MuzzleFlash(gunUid, newAmmo, user);
Audio.PlayPredicted(gun.SoundGunshot, gun.Owner, user); Audio.PlayPredicted(gun.SoundGunshot, gunUid, user);
// Do a throw // Do a throw
if (!HasComp<ProjectileComponent>(newAmmo.Owner)) if (!HasComp<ProjectileComponent>(ent.Value))
{ {
RemComp<AmmoComponent>(newAmmo.Owner); RemComp<AmmoComponent>(ent.Value);
// TODO: Someone can probably yeet this a billion miles so need to pre-validate input somewhere up the call stack. // 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; break;
} }
ShootProjectile(newAmmo.Owner, mapDirection, gunVelocity, user, gun.ProjectileSpeed); ShootProjectile(ent.Value, mapDirection, gunVelocity, user, gun.ProjectileSpeed);
break; break;
case HitscanPrototype hitscan: case HitscanPrototype hitscan:
var ray = new CollisionRay(fromMap.Position, mapDirection.Normalized, hitscan.CollisionMask); var ray = new CollisionRay(fromMap.Position, mapDirection.Normalized, hitscan.CollisionMask);
@@ -194,7 +193,6 @@ public sealed partial class GunSystem : SharedGunSystem
var dmg = hitscan.Damage; var dmg = hitscan.Damage;
bool deleted = false;
string hitName = ToPrettyString(hitEntity); string hitName = ToPrettyString(hitEntity);
if (dmg != null) if (dmg != null)
dmg = Damageable.TryChangeDamage(hitEntity, dmg, origin: user); 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 // check null again, as TryChangeDamage returns modified damage values
if (dmg != null) if (dmg != null)
{ {
deleted = Deleted(hitEntity); if (!Deleted(hitEntity))
if (!deleted)
{ {
if (dmg.Total > FixedPoint2.Zero) if (dmg.Total > FixedPoint2.Zero)
RaiseNetworkEvent(new DamageEffectEvent(Color.Red, new List<EntityUid> {result.HitEntity}), Filter.Pvs(hitEntity, entityManager: EntityManager)); RaiseNetworkEvent(new DamageEffectEvent(Color.Red, new List<EntityUid> {result.HitEntity}), Filter.Pvs(hitEntity, entityManager: EntityManager));
@@ -230,17 +226,17 @@ public sealed partial class GunSystem : SharedGunSystem
FireEffects(fromCoordinates, hitscan.MaxLength, mapDirection.ToAngle(), hitscan); FireEffects(fromCoordinates, hitscan.MaxLength, mapDirection.ToAngle(), hitscan);
} }
Audio.PlayPredicted(gun.SoundGunshot, gun.Owner, user); Audio.PlayPredicted(gun.SoundGunshot, gunUid, user);
break; break;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
} }
RaiseLocalEvent(gun.Owner, new AmmoShotEvent() RaiseLocalEvent(gunUid, new AmmoShotEvent()
{ {
FiredProjectiles = shotProjectiles, FiredProjectiles = shotProjectiles,
}, false); });
} }
public void ShootProjectile(EntityUid uid, Vector2 direction, Vector2 gunVelocity, EntityUid? user = null, float speed = 20f) 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); Projectiles.SetShooter(projectile, user.Value);
} }
Transform(uid).WorldRotation = direction.ToWorldAngle(); Transform.SetWorldRotation(uid, direction.ToWorldAngle());
} }
/// <summary> /// <summary>
@@ -320,7 +316,7 @@ public sealed partial class GunSystem : SharedGunSystem
if (!forceWeaponSound && modifiedDamage != null && modifiedDamage.Total > 0 && TryComp<RangedDamageSoundComponent>(otherEntity, out var rangedSound)) if (!forceWeaponSound && modifiedDamage != null && modifiedDamage.Total > 0 && TryComp<RangedDamageSoundComponent>(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) 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 // 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. // Look you could probably optimise this a bit with redundant transforms at this point.
if (TryComp<TransformComponent>(gridUid, out var gridXform)) var xformQuery = GetEntityQuery<TransformComponent>();
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, fromCoordinates = new EntityCoordinates(gridUid.Value,
gridInvMatrix.Transform(fromCoordinates.ToMapPos(EntityManager))); gridInvMatrix.Transform(fromCoordinates.ToMapPos(EntityManager, Transform)));
// Use the fallback angle I guess? // Use the fallback angle I guess?
angle -= gridRot; angle -= gridRot;

View File

@@ -7,16 +7,16 @@ namespace Content.Shared.Weapons.Ranged.Events;
/// </summary> /// </summary>
public sealed class TakeAmmoEvent : EntityEventArgs public sealed class TakeAmmoEvent : EntityEventArgs
{ {
public EntityUid? User; public readonly EntityUid? User;
public readonly int Shots; public readonly int Shots;
public List<IShootable> Ammo; public List<(EntityUid? Entity, IShootable Shootable)> Ammo;
/// <summary> /// <summary>
/// Coordinates to spawn the ammo at. /// Coordinates to spawn the ammo at.
/// </summary> /// </summary>
public EntityCoordinates Coordinates; public EntityCoordinates Coordinates;
public TakeAmmoEvent(int shots, List<IShootable> ammo, EntityCoordinates coordinates, EntityUid? user) public TakeAmmoEvent(int shots, List<(EntityUid? Entity, IShootable Shootable)> ammo, EntityCoordinates coordinates, EntityUid? user)
{ {
Shots = shots; Shots = shots;
Ammo = ammo; Ammo = ammo;

View File

@@ -8,7 +8,6 @@ using Robust.Shared.Containers;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Shared.Weapons.Ranged.Systems; namespace Content.Shared.Weapons.Ranged.Systems;
@@ -32,22 +31,24 @@ public abstract partial class SharedGunSystem
private void OnBallisticUse(EntityUid uid, BallisticAmmoProviderComponent component, UseInHandEvent args) 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; args.Handled = true;
} }
private void OnBallisticInteractUsing(EntityUid uid, BallisticAmmoProviderComponent component, InteractUsingEvent args) 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.Entities.Add(args.Used);
component.Container.Insert(args.Used); component.Container.Insert(args.Used);
// Not predicted so // Not predicted so
Audio.PlayPredicted(component.SoundInsert, uid, args.User); Audio.PlayPredicted(component.SoundInsert, uid, args.User);
args.Handled = true; args.Handled = true;
UpdateBallisticAppearance(component); UpdateBallisticAppearance(uid, component);
Dirty(component); Dirty(component);
} }
@@ -93,20 +94,20 @@ public abstract partial class SharedGunSystem
RaiseLocalEvent(ammoProvider, evInsert); RaiseLocalEvent(ammoProvider, evInsert);
} }
List<IShootable> ammo = new(); List<(EntityUid? Entity, IShootable Shootable)> ammo = new();
var evTakeAmmo = new TakeAmmoEvent(1, ammo, Transform(args.Used).Coordinates, args.User); var evTakeAmmo = new TakeAmmoEvent(1, ammo, Transform(args.Used).Coordinates, args.User);
RaiseLocalEvent(args.Used, evTakeAmmo); RaiseLocalEvent(args.Used, evTakeAmmo);
foreach (var shot in ammo) foreach (var (ent, _) in ammo)
{ {
if (shot is not AmmoComponent cast) if (ent == null)
continue; continue;
if (!targetComponent.Whitelist.IsValid(cast.Owner)) if (!targetComponent.Whitelist.IsValid(ent.Value))
{ {
Popup( Popup(
Loc.GetString("gun-ballistic-transfer-invalid", Loc.GetString("gun-ballistic-transfer-invalid",
("ammoEntity", cast.Owner), ("ammoEntity", ent.Value),
("targetEntity", args.Target.Value)), ("targetEntity", args.Target.Value)),
args.Used, args.Used,
args.User); 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 // 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 // more future-proof thing to do than copying the same code
// that OnBallisticInteractUsing has, sans sound. // that OnBallisticInteractUsing has, sans sound.
SimulateInsertAmmo(cast.Owner, args.Used, Transform(args.Used).Coordinates); SimulateInsertAmmo(ent.Value, args.Used, Transform(args.Used).Coordinates);
} }
else 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()) if (ent.Value.IsClientSide())
Del(cast.Owner); Del(ent.Value);
} }
} }
private void OnBallisticVerb(EntityUid uid, BallisticAmmoProviderComponent component, GetVerbsEvent<Verb> args) private void OnBallisticVerb(EntityUid uid, BallisticAmmoProviderComponent component, GetVerbsEvent<Verb> args)
{ {
if (!args.CanAccess || !args.CanInteract || args.Hands == null) return; if (!args.CanAccess || !args.CanInteract || args.Hands == null)
return;
args.Verbs.Add(new Verb() args.Verbs.Add(new Verb()
{ {
Text = Loc.GetString("gun-ballistic-cycle"), Text = Loc.GetString("gun-ballistic-cycle"),
Disabled = GetBallisticShots(component) == 0, 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)))); 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 // Reset shotting for cycling
if (TryComp<GunComponent>(component.Owner, out var gunComp) && if (Resolve(uid, ref gunComp, false) &&
gunComp is { FireRate: > 0f }) gunComp is { FireRate: > 0f })
{ {
gunComp.NextFire = Timing.CurTime + TimeSpan.FromSeconds(1 / gunComp.FireRate); gunComp.NextFire = Timing.CurTime + TimeSpan.FromSeconds(1 / gunComp.FireRate);
} }
Dirty(component); Dirty(component);
Audio.PlayPredicted(component.SoundRack, component.Owner, user); Audio.PlayPredicted(component.SoundRack, uid, user);
var shots = GetBallisticShots(component); var shots = GetBallisticShots(component);
component.Cycled = true; 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"); var text = Loc.GetString(shots == 0 ? "gun-ballistic-cycled-empty" : "gun-ballistic-cycled");
Popup(text, component.Owner, user); Popup(text, uid, user);
UpdateBallisticAppearance(component); UpdateBallisticAppearance(uid, component);
UpdateAmmoCount(component.Owner); UpdateAmmoCount(uid);
} }
protected abstract void Cycle(BallisticAmmoProviderComponent component, MapCoordinates coordinates); 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) 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.Cycled = state.Cycled;
component.UnspawnedCount = state.UnspawnedCount; component.UnspawnedCount = state.UnspawnedCount;
@@ -222,7 +225,8 @@ public abstract partial class SharedGunSystem
{ {
for (var i = 0; i < args.Shots; i++) for (var i = 0; i < args.Shots; i++)
{ {
if (!component.Cycled) break; if (!component.Cycled)
break;
EntityUid entity; EntityUid entity;
@@ -230,7 +234,7 @@ public abstract partial class SharedGunSystem
{ {
entity = component.Entities[^1]; entity = component.Entities[^1];
args.Ammo.Add(EnsureComp<AmmoComponent>(entity)); args.Ammo.Add((entity, EnsureComp<AmmoComponent>(entity)));
// Leave the entity as is if it doesn't auto cycle // 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 // 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--; component.UnspawnedCount--;
entity = Spawn(component.FillProto, args.Coordinates); entity = Spawn(component.FillProto, args.Coordinates);
args.Ammo.Add(EnsureComp<AmmoComponent>(entity)); args.Ammo.Add((entity, EnsureComp<AmmoComponent>(entity)));
// Put it back in if it doesn't auto-cycle // Put it back in if it doesn't auto-cycle
if (HasComp<CartridgeAmmoComponent>(entity) && !component.AutoCycle) if (HasComp<CartridgeAmmoComponent>(entity) && !component.AutoCycle)
@@ -269,7 +273,7 @@ public abstract partial class SharedGunSystem
} }
} }
UpdateBallisticAppearance(component); UpdateBallisticAppearance(uid, component);
Dirty(component); Dirty(component);
} }
@@ -279,13 +283,13 @@ public abstract partial class SharedGunSystem
args.Capacity = component.Capacity; args.Capacity = component.Capacity;
} }
private void UpdateBallisticAppearance(BallisticAmmoProviderComponent component) private void UpdateBallisticAppearance(EntityUid uid, BallisticAmmoProviderComponent component)
{ {
if (!Timing.IsFirstTimePredicted || !TryComp<AppearanceComponent>(component.Owner, out var appearance)) if (!Timing.IsFirstTimePredicted || !TryComp<AppearanceComponent>(uid, out var appearance))
return; return;
Appearance.SetData(appearance.Owner, AmmoVisuals.AmmoCount, GetBallisticShots(component), appearance); Appearance.SetData(uid, AmmoVisuals.AmmoCount, GetBallisticShots(component), appearance);
Appearance.SetData(appearance.Owner, AmmoVisuals.AmmoMax, component.Capacity, appearance); Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.Capacity, appearance);
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]

View File

@@ -38,7 +38,7 @@ public abstract partial class SharedGunSystem
Dirty(component); Dirty(component);
} }
UpdateBasicEntityAppearance(component); UpdateBasicEntityAppearance(uid, component);
} }
private void OnBasicEntityTakeAmmo(EntityUid uid, BasicEntityAmmoProviderComponent component, TakeAmmoEvent args) 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); var ent = Spawn(component.Proto, args.Coordinates);
args.Ammo.Add(EnsureComp<AmmoComponent>(ent)); args.Ammo.Add((ent, EnsureComp<AmmoComponent>(ent)));
} }
UpdateBasicEntityAppearance(component); UpdateBasicEntityAppearance(uid, component);
Dirty(component); Dirty(component);
} }
@@ -67,13 +67,14 @@ public abstract partial class SharedGunSystem
args.Count = component.Count ?? int.MaxValue; args.Count = component.Count ?? int.MaxValue;
} }
private void UpdateBasicEntityAppearance(BasicEntityAmmoProviderComponent component) private void UpdateBasicEntityAppearance(EntityUid uid, BasicEntityAmmoProviderComponent component)
{ {
if (!Timing.IsFirstTimePredicted || !TryComp<AppearanceComponent>(component.Owner, out var appearance)) return; if (!Timing.IsFirstTimePredicted || !TryComp<AppearanceComponent>(uid, out var appearance))
return;
Appearance.SetData(appearance.Owner, AmmoVisuals.HasAmmo, component.Count != 0, appearance); Appearance.SetData(uid, AmmoVisuals.HasAmmo, component.Count != 0, appearance);
Appearance.SetData(appearance.Owner, AmmoVisuals.AmmoCount, component.Count ?? int.MaxValue, appearance); Appearance.SetData(uid, AmmoVisuals.AmmoCount, component.Count ?? int.MaxValue, appearance);
Appearance.SetData(appearance.Owner, AmmoVisuals.AmmoMax, component.Capacity ?? int.MaxValue, appearance); Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.Capacity ?? int.MaxValue, appearance);
} }
#region Public API #region Public API
@@ -88,7 +89,7 @@ public abstract partial class SharedGunSystem
component.Count = count; component.Count = count;
Dirty(component); Dirty(component);
UpdateBasicEntityAppearance(component); UpdateBasicEntityAppearance(uid, component);
return true; return true;
} }

View File

@@ -29,7 +29,8 @@ public abstract partial class SharedGunSystem
private void OnBatteryHandleState(EntityUid uid, BatteryAmmoProviderComponent component, ref ComponentHandleState args) 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.Shots = state.Shots;
component.Capacity = state.MaxShots; component.Capacity = state.MaxShots;
@@ -56,7 +57,8 @@ public abstract partial class SharedGunSystem
var shots = Math.Min(args.Shots, component.Shots); var shots = Math.Min(args.Shots, component.Shots);
// Don't dirty if it's an empty fire. // Don't dirty if it's an empty fire.
if (shots == 0) return; if (shots == 0)
return;
for (var i = 0; i < shots; i++) for (var i = 0; i < shots; i++)
{ {
@@ -90,15 +92,15 @@ public abstract partial class SharedGunSystem
Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.Capacity, appearance); 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) switch (component)
{ {
case ProjectileBatteryAmmoProviderComponent proj: case ProjectileBatteryAmmoProviderComponent proj:
var ent = Spawn(proj.Prototype, coordinates); var ent = Spawn(proj.Prototype, coordinates);
return EnsureComp<AmmoComponent>(ent); return (ent, EnsureComp<AmmoComponent>(ent));
case HitscanBatteryAmmoProviderComponent hitscan: case HitscanBatteryAmmoProviderComponent hitscan:
return ProtoManager.Index<HitscanPrototype>(hitscan.Prototype); return (null, ProtoManager.Index<HitscanPrototype>(hitscan.Prototype));
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }

View File

@@ -14,7 +14,9 @@ public abstract partial class SharedGunSystem
private void OnCartridgeHandleState(EntityUid uid, CartridgeAmmoComponent component, ref ComponentHandleState args) 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; component.Spent = state.Spent;
} }

View File

@@ -27,7 +27,7 @@ public abstract partial class SharedGunSystem
if (!args.IsInDetailsRange) if (!args.IsInDetailsRange)
return; return;
var (count, _) = GetChamberMagazineCountCapacity(component); var (count, _) = GetChamberMagazineCountCapacity(uid, component);
args.PushMarkup(Loc.GetString("gun-magazine-examine", ("color", AmmoExamineColor), ("count", count))); args.PushMarkup(Loc.GetString("gun-magazine-examine", ("color", AmmoExamineColor), ("count", count)));
} }
@@ -41,7 +41,9 @@ public abstract partial class SharedGunSystem
} }
entity = slot.ContainedEntity; entity = slot.ContainedEntity;
if (entity == null) return false; if (entity == null)
return false;
container.Remove(entity.Value); container.Remove(entity.Value);
return true; return true;
} }
@@ -57,10 +59,10 @@ public abstract partial class SharedGunSystem
return slot.ContainedEntity; 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 count = GetChamberEntity(uid) != null ? 1 : 0;
var (magCount, magCapacity) = GetMagazineCountCapacity(component); var (magCount, magCapacity) = GetMagazineCountCapacity(uid, component);
return (count + magCount, magCapacity); return (count + magCount, magCapacity);
} }
@@ -81,7 +83,7 @@ public abstract partial class SharedGunSystem
if (TryTakeChamberEntity(uid, out var chamberEnt)) if (TryTakeChamberEntity(uid, out var chamberEnt))
{ {
args.Ammo.Add(EnsureComp<AmmoComponent>(chamberEnt.Value)); args.Ammo.Add((chamberEnt.Value, EnsureComp<AmmoComponent>(chamberEnt.Value)));
} }
var magEnt = GetMagazineEntity(uid); var magEnt = GetMagazineEntity(uid);
@@ -90,15 +92,15 @@ public abstract partial class SharedGunSystem
if (magEnt != null) if (magEnt != null)
{ {
// We pass in Shots not Shots - 1 as we'll take the last entity and move it into the chamber. // 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<IShootable>(), args.Coordinates, args.User); var relayedArgs = new TakeAmmoEvent(args.Shots, new List<(EntityUid? Entity, IShootable Shootable)>(), args.Coordinates, args.User);
RaiseLocalEvent(magEnt.Value, relayedArgs, false); RaiseLocalEvent(magEnt.Value, relayedArgs);
// Put in the nth slot back into the chamber // Put in the nth slot back into the chamber
// Rest of the ammo gets shot // Rest of the ammo gets shot
if (relayedArgs.Ammo.Count > 0) if (relayedArgs.Ammo.Count > 0)
{ {
var newChamberEnt = ((AmmoComponent) relayedArgs.Ammo[^1]).Owner; var newChamberEnt = relayedArgs.Ammo[^1].Entity;
TryInsertChamber(uid, newChamberEnt); TryInsertChamber(uid, newChamberEnt!.Value);
} }
// Anything above the chamber-refill amount gets fired. // Anything above the chamber-refill amount gets fired.
@@ -117,7 +119,7 @@ public abstract partial class SharedGunSystem
const int capacity = 1; const int capacity = 1;
var ammoEv = new GetAmmoCountEvent(); 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); FinaliseMagazineTakeAmmo(uid, component, args, count + ammoEv.Count, capacity + ammoEv.Capacity, appearance);
} }

View File

@@ -32,7 +32,7 @@ public partial class SharedGunSystem
if (_netMan.IsServer) if (_netMan.IsServer)
container.Remove(ent); container.Remove(ent);
args.Ammo.Add(EnsureComp<AmmoComponent>(ent)); args.Ammo.Add((ent, EnsureComp<AmmoComponent>(ent)));
} }
} }

View File

@@ -33,7 +33,7 @@ public abstract partial class SharedGunSystem
AlternativeVerb verb = new() 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))), Text = Loc.GetString("gun-selector-verb", ("mode", GetLocSelector(nextMode))),
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/fold.svg.192dpi.png")), 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<SelectiveFire>()) foreach (var mode in Enum.GetValues<SelectiveFire>())
{ {
if ((mode & component.AvailableModes) == 0x0) continue; if ((mode & component.AvailableModes) == 0x0)
continue;
modes.Add(mode); modes.Add(mode);
} }
@@ -55,9 +57,10 @@ public abstract partial class SharedGunSystem
return modes[(index + 1) % modes.Count]; 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); DebugTools.Assert((component.AvailableModes & fire) != 0x0);
component.SelectedMode = fire; component.SelectedMode = fire;
@@ -69,22 +72,23 @@ public abstract partial class SharedGunSystem
else else
component.NextFire += cooldown; component.NextFire += cooldown;
Audio.PlayPredicted(component.SoundModeToggle, component.Owner, user); Audio.PlayPredicted(component.SoundModeToggle, uid, user);
Popup(Loc.GetString("gun-selected-mode", ("mode", GetLocSelector(fire))), component.Owner, user); Popup(Loc.GetString("gun-selected-mode", ("mode", GetLocSelector(fire))), uid, user);
Dirty(component); Dirty(component);
} }
/// <summary> /// <summary>
/// Cycles the gun's <see cref="SelectiveFire"/> to the next available one. /// Cycles the gun's <see cref="SelectiveFire"/> to the next available one.
/// </summary> /// </summary>
public void CycleFire(GunComponent component, EntityUid? user = null) public void CycleFire(EntityUid uid, GunComponent component, EntityUid? user = null)
{ {
// Noop // Noop
if (component.SelectedMode == component.AvailableModes) return; if (component.SelectedMode == component.AvailableModes)
return;
DebugTools.Assert((component.AvailableModes & component.SelectedMode) == component.SelectedMode); DebugTools.Assert((component.AvailableModes & component.SelectedMode) == component.SelectedMode);
var nextMode = GetNextMode(component); var nextMode = GetNextMode(component);
SelectFire(component, nextMode, user); SelectFire(uid, component, nextMode, user);
} }
// TODO: Actions need doing for guns anyway. // 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) private void OnCycleMode(EntityUid uid, GunComponent component, CycleModeEvent args)
{ {
SelectFire(component, args.Mode, args.Performer); SelectFire(uid, component, args.Mode, args.Performer);
} }
} }

View File

@@ -26,7 +26,7 @@ public abstract partial class SharedGunSystem
if (!args.IsInDetailsRange) if (!args.IsInDetailsRange)
return; return;
var (count, _) = GetMagazineCountCapacity(component); var (count, _) = GetMagazineCountCapacity(uid, component);
args.PushMarkup(Loc.GetString("gun-magazine-examine", ("color", AmmoExamineColor), ("count", count))); args.PushMarkup(Loc.GetString("gun-magazine-examine", ("color", AmmoExamineColor), ("count", count)));
} }
@@ -34,23 +34,25 @@ public abstract partial class SharedGunSystem
{ {
var magEnt = GetMagazineEntity(uid); var magEnt = GetMagazineEntity(uid);
if (magEnt == null) return; if (magEnt == null)
return;
RaiseLocalEvent(magEnt.Value, args); RaiseLocalEvent(magEnt.Value, args);
UpdateAmmoCount(uid); UpdateAmmoCount(uid);
UpdateMagazineAppearance(component, magEnt.Value); UpdateMagazineAppearance(uid, component, magEnt.Value);
} }
private void OnMagazineVerb(EntityUid uid, MagazineAmmoProviderComponent component, GetVerbsEvent<AlternativeVerb> args) private void OnMagazineVerb(EntityUid uid, MagazineAmmoProviderComponent component, GetVerbsEvent<AlternativeVerb> args)
{ {
if (!args.CanInteract || !args.CanAccess) return; if (!args.CanInteract || !args.CanAccess)
return;
var magEnt = GetMagazineEntity(uid); var magEnt = GetMagazineEntity(uid);
if (magEnt != null) if (magEnt != null)
{ {
RaiseLocalEvent(magEnt.Value, args); 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); 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 count = 0;
var capacity = 1; var capacity = 1;
var magEnt = GetMagazineEntity(component.Owner); var magEnt = GetMagazineEntity(uid);
if (magEnt != null) if (magEnt != null)
{ {
@@ -86,7 +88,11 @@ public abstract partial class SharedGunSystem
protected EntityUid? GetMagazineEntity(EntityUid uid) protected EntityUid? GetMagazineEntity(EntityUid uid)
{ {
if (!Containers.TryGetContainer(uid, MagazineSlot, out var container) || 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; return slot.ContainedEntity;
} }
@@ -102,11 +108,11 @@ public abstract partial class SharedGunSystem
} }
// Pass the event onwards. // Pass the event onwards.
RaiseLocalEvent(magEntity.Value, args, false); RaiseLocalEvent(magEntity.Value, args);
// Should be Dirtied by what other ammoprovider is handling it. // Should be Dirtied by what other ammoprovider is handling it.
var ammoEv = new GetAmmoCountEvent(); var ammoEv = new GetAmmoCountEvent();
RaiseLocalEvent(magEntity.Value, ref ammoEv, false); RaiseLocalEvent(magEntity.Value, ref ammoEv);
FinaliseMagazineTakeAmmo(uid, component, args, ammoEv.Count, ammoEv.Capacity, appearance); 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 no ammo then check for autoeject
if (component.AutoEject && args.Ammo.Count == 0) if (component.AutoEject && args.Ammo.Count == 0)
{ {
EjectMagazine(component); EjectMagazine(uid, component);
Audio.PlayPredicted(component.SoundAutoEject, uid, args.User); 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<AppearanceComponent>(component.Owner, out var appearance); TryComp<AppearanceComponent>(uid, out var appearance);
var count = 0; var count = 0;
var capacity = 0; var capacity = 0;
if (component is ChamberMagazineAmmoProviderComponent chamber) if (component is ChamberMagazineAmmoProviderComponent chamber)
{ {
count = GetChamberEntity(chamber.Owner) != null ? 1 : 0; count = GetChamberEntity(uid) != null ? 1 : 0;
capacity = 1; capacity = 1;
} }
@@ -143,27 +149,28 @@ public abstract partial class SharedGunSystem
capacity += addCapacity; 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) if (appearance == null)
return; return;
// Copy the magazine's appearance data // Copy the magazine's appearance data
Appearance.SetData(appearance.Owner, AmmoVisuals.MagLoaded, magLoaded, appearance); Appearance.SetData(uid, AmmoVisuals.MagLoaded, magLoaded, appearance);
Appearance.SetData(appearance.Owner, AmmoVisuals.HasAmmo, count != 0, appearance); Appearance.SetData(uid, AmmoVisuals.HasAmmo, count != 0, appearance);
Appearance.SetData(appearance.Owner, AmmoVisuals.AmmoCount, count, appearance); Appearance.SetData(uid, AmmoVisuals.AmmoCount, count, appearance);
Appearance.SetData(appearance.Owner, AmmoVisuals.AmmoMax, capacity, 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);
} }
} }

View File

@@ -34,9 +34,10 @@ public partial class SharedGunSystem
private void OnRevolverInteractUsing(EntityUid uid, RevolverAmmoProviderComponent component, InteractUsingEvent args) 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; args.Handled = true;
} }
@@ -52,7 +53,8 @@ public partial class SharedGunSystem
private void OnRevolverHandleState(EntityUid uid, RevolverAmmoProviderComponent component, ref ComponentHandleState args) 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; var oldIndex = component.CurrentIndex;
component.CurrentIndex = state.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) if (component.Whitelist?.IsValid(uid, EntityManager) == false)
return false; return false;
@@ -93,19 +95,19 @@ public partial class SharedGunSystem
if (freeSlots == 0) if (freeSlots == 0)
{ {
Popup(Loc.GetString("gun-revolver-full"), component.Owner, user); Popup(Loc.GetString("gun-revolver-full"), revolverUid, user);
return false; return false;
} }
var xformQuery = GetEntityQuery<TransformComponent>(); var xformQuery = GetEntityQuery<TransformComponent>();
var xform = xformQuery.GetComponent(uid); var xform = xformQuery.GetComponent(uid);
var ammo = new List<IShootable>(freeSlots); var ammo = new List<(EntityUid? Entity, IShootable Shootable)>(freeSlots);
var ev = new TakeAmmoEvent(freeSlots, ammo, xform.Coordinates, user); var ev = new TakeAmmoEvent(freeSlots, ammo, xform.Coordinates, user);
RaiseLocalEvent(uid, ev); RaiseLocalEvent(uid, ev);
if (ev.Ammo.Count == 0) if (ev.Ammo.Count == 0)
{ {
Popup(Loc.GetString("gun-speedloader-empty"), component.Owner, user); Popup(Loc.GetString("gun-speedloader-empty"), revolverUid, user);
return false; return false;
} }
@@ -119,29 +121,29 @@ public partial class SharedGunSystem
continue; continue;
} }
var ent = ev.Ammo.Last(); var ent = ev.Ammo.Last().Entity;
ev.Ammo.RemoveAt(ev.Ammo.Count - 1); 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"); Sawmill.Error($"Tried to load hitscan into a revolver which is unsupported");
continue; continue;
} }
component.AmmoSlots[index] = ammoComp.Owner; component.AmmoSlots[index] = ent.Value;
component.AmmoContainer.Insert(ammoComp.Owner, EntityManager); component.AmmoContainer.Insert(ent.Value, EntityManager);
if (ev.Ammo.Count == 0) if (ev.Ammo.Count == 0)
break; break;
} }
DebugTools.Assert(ammo.Count == 0); DebugTools.Assert(ammo.Count == 0);
UpdateRevolverAppearance(component); UpdateRevolverAppearance(revolverUid, component);
UpdateAmmoCount(uid); UpdateAmmoCount(uid);
Dirty(component); Dirty(component);
Audio.PlayPredicted(component.SoundInsert, component.Owner, user); Audio.PlayPredicted(component.SoundInsert, revolverUid, user);
Popup(Loc.GetString("gun-revolver-insert"), component.Owner, user); Popup(Loc.GetString("gun-revolver-insert"), revolverUid, user);
return true; return true;
} }
@@ -151,30 +153,35 @@ public partial class SharedGunSystem
var index = (component.CurrentIndex + i) % component.Capacity; var index = (component.CurrentIndex + i) % component.Capacity;
if (component.AmmoSlots[index] != null || if (component.AmmoSlots[index] != null ||
component.Chambers[index] != null) continue; component.Chambers[index] != null)
{
continue;
}
component.AmmoSlots[index] = uid; component.AmmoSlots[index] = uid;
component.AmmoContainer.Insert(uid); component.AmmoContainer.Insert(uid);
Audio.PlayPredicted(component.SoundInsert, component.Owner, user); Audio.PlayPredicted(component.SoundInsert, revolverUid, user);
Popup(Loc.GetString("gun-revolver-insert"), component.Owner, user); Popup(Loc.GetString("gun-revolver-insert"), revolverUid, user);
UpdateRevolverAppearance(component); UpdateRevolverAppearance(revolverUid, component);
UpdateAmmoCount(uid); UpdateAmmoCount(uid);
Dirty(component); Dirty(component);
return true; return true;
} }
Popup(Loc.GetString("gun-revolver-full"), component.Owner, user);
Popup(Loc.GetString("gun-revolver-full"), revolverUid, user);
return false; return false;
} }
private void OnRevolverVerbs(EntityUid uid, RevolverAmmoProviderComponent component, GetVerbsEvent<AlternativeVerb> args) private void OnRevolverVerbs(EntityUid uid, RevolverAmmoProviderComponent component, GetVerbsEvent<AlternativeVerb> args)
{ {
if (!args.CanAccess || !args.CanInteract || args.Hands == null) return; if (!args.CanAccess || !args.CanInteract || args.Hands == null)
return;
args.Verbs.Add(new AlternativeVerb() args.Verbs.Add(new AlternativeVerb()
{ {
Text = Loc.GetString("gun-revolver-empty"), Text = Loc.GetString("gun-revolver-empty"),
Disabled = !AnyRevolverCartridges(component), Disabled = !AnyRevolverCartridges(component),
Act = () => EmptyRevolver(component, args.User), Act = () => EmptyRevolver(uid, component, args.User),
Priority = 1 Priority = 1
}); });
@@ -182,7 +189,7 @@ public partial class SharedGunSystem
{ {
Text = Loc.GetString("gun-revolver-spin"), Text = Loc.GetString("gun-revolver-spin"),
// Category = VerbCategory.G, // 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++) for (var i = 0; i < component.Capacity; i++)
{ {
if (component.Chambers[i] != null || if (component.Chambers[i] != null ||
component.AmmoSlots[i] != null) return true; component.AmmoSlots[i] != null)
{
return true;
}
} }
return false; return false;
@@ -238,9 +248,9 @@ public partial class SharedGunSystem
return count; 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 mapCoordinates = xform.MapPosition;
var anyEmpty = false; var anyEmpty = false;
@@ -259,7 +269,7 @@ public partial class SharedGunSystem
var uid = Spawn(component.FillPrototype, mapCoordinates); var uid = Spawn(component.FillPrototype, mapCoordinates);
if (TryComp<CartridgeAmmoComponent>(uid, out var cartridge)) if (TryComp<CartridgeAmmoComponent>(uid, out var cartridge))
SetCartridgeSpent(cartridge, !(bool) chamber); SetCartridgeSpent(uid, cartridge, !(bool) chamber);
EjectCartridge(uid); EjectCartridge(uid);
} }
@@ -281,28 +291,28 @@ public partial class SharedGunSystem
if (anyEmpty) if (anyEmpty)
{ {
Audio.PlayPredicted(component.SoundEject, component.Owner, user); Audio.PlayPredicted(component.SoundEject, revolverUid, user);
UpdateAmmoCount(component.Owner); UpdateAmmoCount(revolverUid);
UpdateRevolverAppearance(component); UpdateRevolverAppearance(revolverUid, component);
Dirty(component); Dirty(component);
} }
} }
private void UpdateRevolverAppearance(RevolverAmmoProviderComponent component) private void UpdateRevolverAppearance(EntityUid uid, RevolverAmmoProviderComponent component)
{ {
if (!TryComp<AppearanceComponent>(component.Owner, out var appearance)) if (!TryComp<AppearanceComponent>(uid, out var appearance))
return; return;
var count = GetRevolverCount(component); var count = GetRevolverCount(component);
Appearance.SetData(component.Owner, AmmoVisuals.HasAmmo, count != 0, appearance); Appearance.SetData(uid, AmmoVisuals.HasAmmo, count != 0, appearance);
Appearance.SetData(component.Owner, AmmoVisuals.AmmoCount, count, appearance); Appearance.SetData(uid, AmmoVisuals.AmmoCount, count, appearance);
Appearance.SetData(component.Owner, AmmoVisuals.AmmoMax, component.Capacity, 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); Audio.PlayPredicted(component.SoundSpin, revolverUid, user);
Popup(Loc.GetString("gun-revolver-spun"), component.Owner, user); Popup(Loc.GetString("gun-revolver-spun"), revolverUid, user);
} }
private void OnRevolverTakeAmmo(EntityUid uid, RevolverAmmoProviderComponent component, TakeAmmoEvent args) private void OnRevolverTakeAmmo(EntityUid uid, RevolverAmmoProviderComponent component, TakeAmmoEvent args)
@@ -327,14 +337,15 @@ public partial class SharedGunSystem
if (TryComp<CartridgeAmmoComponent>(ent, out var cartridge)) if (TryComp<CartridgeAmmoComponent>(ent, out var cartridge))
{ {
component.Chambers[index] = false; component.Chambers[index] = false;
SetCartridgeSpent(cartridge, true); SetCartridgeSpent(ent, cartridge, true);
args.Ammo.Add(EnsureComp<AmmoComponent>(Spawn(cartridge.Prototype, args.Coordinates))); var spawned = Spawn(cartridge.Prototype, args.Coordinates);
args.Ammo.Add((spawned, EnsureComp<AmmoComponent>(spawned)));
Del(ent); Del(ent);
continue; continue;
} }
component.Chambers[i] = null; component.Chambers[i] = null;
args.Ammo.Add(EnsureComp<AmmoComponent>(ent)); args.Ammo.Add((ent, EnsureComp<AmmoComponent>(ent)));
} }
} }
else if (component.AmmoSlots[index] != null) else if (component.AmmoSlots[index] != null)
@@ -343,21 +354,23 @@ public partial class SharedGunSystem
if (TryComp<CartridgeAmmoComponent>(ent, out var cartridge)) if (TryComp<CartridgeAmmoComponent>(ent, out var cartridge))
{ {
if (cartridge.Spent) continue; if (cartridge.Spent)
continue;
SetCartridgeSpent(cartridge, true); SetCartridgeSpent(ent.Value, cartridge, true);
args.Ammo.Add(EnsureComp<AmmoComponent>(Spawn(cartridge.Prototype, args.Coordinates))); var spawned = Spawn(cartridge.Prototype, args.Coordinates);
args.Ammo.Add((spawned, EnsureComp<AmmoComponent>(spawned)));
continue; continue;
} }
component.AmmoContainer.Remove(ent.Value); component.AmmoContainer.Remove(ent.Value);
component.AmmoSlots[index] = null; component.AmmoSlots[index] = null;
args.Ammo.Add(EnsureComp<AmmoComponent>(ent.Value)); args.Ammo.Add((ent.Value, EnsureComp<AmmoComponent>(ent.Value)));
Transform(ent.Value).Coordinates = args.Coordinates; Transform(ent.Value).Coordinates = args.Coordinates;
} }
} }
UpdateRevolverAppearance(component); UpdateRevolverAppearance(uid, component);
Dirty(component); Dirty(component);
} }

View File

@@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Actions; using Content.Shared.Actions;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.Audio;
using Content.Shared.CombatMode; using Content.Shared.CombatMode;
using Content.Shared.Containers.ItemSlots; using Content.Shared.Containers.ItemSlots;
using Content.Shared.Damage; using Content.Shared.Damage;
@@ -22,7 +22,6 @@ using Robust.Shared.Map;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Physics.Systems; using Robust.Shared.Physics.Systems;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -53,6 +52,7 @@ public abstract partial class SharedGunSystem : EntitySystem
[Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!;
[Dependency] private readonly SharedGravitySystem _gravity = default!; [Dependency] private readonly SharedGravitySystem _gravity = default!;
[Dependency] protected readonly SharedProjectileSystem Projectiles = default!; [Dependency] protected readonly SharedProjectileSystem Projectiles = default!;
[Dependency] protected readonly SharedTransformSystem Transform = default!;
protected ISawmill Sawmill = default!; protected ISawmill Sawmill = default!;
@@ -107,27 +107,31 @@ public abstract partial class SharedGunSystem : EntitySystem
{ {
var user = args.SenderSession.AttachedEntity; 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 (ent != msg.Gun)
return;
if (gun?.Owner != msg.Gun) return;
gun.ShootCoordinates = msg.Coordinates; gun.ShootCoordinates = msg.Coordinates;
Sawmill.Debug($"Set shoot coordinates to {gun.ShootCoordinates}"); Sawmill.Debug($"Set shoot coordinates to {gun.ShootCoordinates}");
AttemptShoot(user.Value, gun); AttemptShoot(user.Value, ent, gun);
} }
private void OnStopShootRequest(RequestStopShootEvent ev, EntitySessionEventArgs args) private void OnStopShootRequest(RequestStopShootEvent ev, EntitySessionEventArgs args)
{ {
if (args.SenderSession.AttachedEntity == null || if (args.SenderSession.AttachedEntity == null ||
!TryComp<GunComponent>(ev.Gun, out var gun)) return; !TryComp<GunComponent>(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(ev.Gun, gun);
StopShooting(gun);
} }
private void OnGetState(EntityUid uid, GunComponent component, ref ComponentGetState args) 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) 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}"); Sawmill.Debug($"Handle state: setting shot count from {component.ShotCounter} to {state.ShotCounter}");
component.FireRate = state.FireRate; component.FireRate = state.FireRate;
@@ -168,27 +173,40 @@ public abstract partial class SharedGunSystem : EntitySystem
return true; 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)) if (!_combatMode.IsInCombatMode(entity))
return null; return false;
if (EntityManager.TryGetComponent(entity, out SharedHandsComponent? hands) && if (EntityManager.TryGetComponent(entity, out SharedHandsComponent? hands) &&
hands.ActiveHandEntity is { } held && hands.ActiveHandEntity is { } held &&
TryComp(held, out GunComponent? gun)) TryComp(held, out GunComponent? gun))
{ {
return gun; gunEntity = held;
gunComp = gun;
return true;
} }
// Last resort is check if the entity itself is a gun. // 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.ShotCounter = 0;
gun.ShootCoordinates = null; gun.ShootCoordinates = null;
Dirty(gun); Dirty(gun);
@@ -197,14 +215,14 @@ public abstract partial class SharedGunSystem : EntitySystem
/// <summary> /// <summary>
/// Attempts to shoot at the target coordinates. Resets the shot counter after every shot. /// Attempts to shoot at the target coordinates. Resets the shot counter after every shot.
/// </summary> /// </summary>
public void AttemptShoot(EntityUid user, GunComponent gun, EntityCoordinates toCoordinates) public void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun, EntityCoordinates toCoordinates)
{ {
gun.ShootCoordinates = toCoordinates; gun.ShootCoordinates = toCoordinates;
AttemptShoot(user, gun); AttemptShoot(user, gunUid, gun);
gun.ShotCounter = 0; gun.ShotCounter = 0;
} }
private void AttemptShoot(EntityUid user, GunComponent gun) private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun)
{ {
if (gun.FireRate <= 0f) if (gun.FireRate <= 0f)
return; return;
@@ -260,22 +278,22 @@ public abstract partial class SharedGunSystem : EntitySystem
var fromCoordinates = Transform(user).Coordinates; var fromCoordinates = Transform(user).Coordinates;
// Remove ammo // Remove ammo
var ev = new TakeAmmoEvent(shots, new List<IShootable>(), 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. // Listen it just makes the other code around it easier if shots == 0 to do this.
if (shots > 0) if (shots > 0)
RaiseLocalEvent(gun.Owner, ev, false); RaiseLocalEvent(gunUid, ev);
DebugTools.Assert(ev.Ammo.Count <= shots); DebugTools.Assert(ev.Ammo.Count <= shots);
DebugTools.Assert(shots >= 0); 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 // 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. // where the gun may be SemiAuto or Burst.
gun.ShotCounter += shots; gun.ShotCounter += shots;
var attemptEv = new AttemptShootEvent(user); var attemptEv = new AttemptShootEvent(user);
RaiseLocalEvent(gun.Owner, ref attemptEv); RaiseLocalEvent(gunUid, ref attemptEv);
if (ev.Ammo.Count <= 0 || attemptEv.Cancelled) 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. // Don't spam safety sounds at gun fire rate, play it at a reduced rate.
// May cause prediction issues? Needs more tweaking // May cause prediction issues? Needs more tweaking
gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds)); 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); Dirty(gun);
return; 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 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); var shotEv = new GunShotEvent(user);
RaiseLocalEvent(gun.Owner, ref shotEv); RaiseLocalEvent(gunUid, ref shotEv);
// Projectiles cause impulses especially important in non gravity environments // Projectiles cause impulses especially important in non gravity environments
if (TryComp<PhysicsComponent>(user, out var userPhysics)) if (TryComp<PhysicsComponent>(user, out var userPhysics))
{ {
if (_gravity.IsWeightless(user, userPhysics)) if (_gravity.IsWeightless(user, userPhysics))
CauseImpulse(fromCoordinates, toCoordinates.Value, userPhysics); CauseImpulse(fromCoordinates, toCoordinates.Value, user, userPhysics);
} }
Dirty(gun); Dirty(gun);
} }
public void Shoot( public void Shoot(
EntityUid gunUid,
GunComponent gun, GunComponent gun,
EntityUid ammo, EntityUid ammo,
EntityCoordinates fromCoordinates, EntityCoordinates fromCoordinates,
@@ -315,26 +334,17 @@ public abstract partial class SharedGunSystem : EntitySystem
EntityUid? user = null) EntityUid? user = null)
{ {
var shootable = EnsureComp<AmmoComponent>(ammo); var shootable = EnsureComp<AmmoComponent>(ammo);
Shoot(gun, new List<IShootable>(1) { shootable }, fromCoordinates, toCoordinates, user); Shoot(gunUid, gun, new List<(EntityUid? Entity, IShootable Shootable)>(1) { (ammo, shootable) }, fromCoordinates, toCoordinates, user);
} }
public abstract void Shoot( public abstract void Shoot(
EntityUid gunUid,
GunComponent gun, GunComponent gun,
List<IShootable> ammo, List<(EntityUid? Entity, IShootable Shootable)> ammo,
EntityCoordinates fromCoordinates, EntityCoordinates fromCoordinates,
EntityCoordinates toCoordinates, EntityCoordinates toCoordinates,
EntityUid? user = null); EntityUid? user = null);
public void Shoot(
GunComponent gun,
IShootable ammo,
EntityCoordinates fromCoordinates,
EntityCoordinates toCoordinates,
EntityUid? user = null)
{
Shoot(gun, new List<IShootable>(1) { ammo }, fromCoordinates, toCoordinates, user);
}
protected abstract void Popup(string message, EntityUid? uid, EntityUid? user); protected abstract void Popup(string message, EntityUid? uid, EntityUid? user);
/// <summary> /// <summary>
@@ -342,13 +352,13 @@ public abstract partial class SharedGunSystem : EntitySystem
/// </summary> /// </summary>
protected virtual void UpdateAmmoCount(EntityUid uid) {} 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) if (cartridge.Spent != spent)
Dirty(cartridge); Dirty(cartridge);
cartridge.Spent = spent; cartridge.Spent = spent;
Appearance.SetData(cartridge.Owner, AmmoVisuals.Spent, spent); Appearance.SetData(uid, AmmoVisuals.Spent, spent);
} }
/// <summary> /// <summary>
@@ -385,15 +395,15 @@ public abstract partial class SharedGunSystem : EntitySystem
CreateEffect(gun, ev, user); 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 fromMap = fromCoordinates.ToMapPos(EntityManager, Transform);
var toMap = toCoordinates.ToMapPos(EntityManager); var toMap = toCoordinates.ToMapPos(EntityManager, Transform);
var shotDirection = (toMap - fromMap).Normalized; var shotDirection = (toMap - fromMap).Normalized;
const float impulseStrength = 5.0f; const float impulseStrength = 5.0f;
var impulseVector = shotDirection * impulseStrength; 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); protected abstract void CreateEffect(EntityUid uid, MuzzleFlashEvent message, EntityUid? user = null);