Update (MOST) instances of EntityUid, Component in GunSystem to Entity<T> (#41966)

* Entity<T>, skipping Magazine and ChamberMagazine

* missed some

* AUGH!!

* ballistic examine

* dotnet hates me

* WHY ARE YOU CALLED THAT!!!!

* cheers aada
This commit is contained in:
mq
2026-01-02 06:00:49 +11:00
committed by GitHub
parent 445d1b673b
commit 4920c9e907
58 changed files with 719 additions and 736 deletions

View File

@@ -4,10 +4,8 @@ using Content.Shared.Weapons.Ranged.Components;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Serialization;
using Robust.Client.UserInterface;
using Robust.Shared.Enums;
using Robust.Shared.Graphics;
using Robust.Shared.Utility;
namespace Content.Client.CombatMode;

View File

@@ -11,17 +11,20 @@ public sealed partial class MagazineVisualsComponent : Component
/// <summary>
/// What RsiState we use.
/// </summary>
[DataField("magState")] public string? MagState;
[DataField]
public string? MagState;
/// <summary>
/// How many steps there are
/// </summary>
[DataField("steps")] public int MagSteps;
[DataField("steps")]
public int MagSteps;
/// <summary>
/// Should we hide when the count is 0
/// </summary>
[DataField("zeroVisible")] public bool ZeroVisible;
[DataField]
public bool ZeroVisible;
}
public enum GunVisualLayers : byte

View File

@@ -8,9 +8,10 @@ public sealed partial class SpentAmmoVisualsComponent : Component
/// <summary>
/// Should we do "{_state}-spent" or just "spent"
/// </summary>
[DataField("suffix")] public bool Suffix = true;
[DataField]
public bool Suffix = true;
[DataField("state")]
[DataField]
public string State = "base";
}

View File

@@ -48,7 +48,7 @@ public sealed class GunSpreadOverlay : Overlay
if (mapPos.MapId == MapId.Nullspace)
return;
if (!_guns.TryGetGun(player.Value, out var gunUid, out var gun))
if (!_guns.TryGetGun(player.Value, out var gun))
return;
var mouseScreenPos = _input.MouseScreenPosition;
@@ -58,12 +58,12 @@ public sealed class GunSpreadOverlay : Overlay
return;
// (☞゚ヮ゚)☞
var maxSpread = gun.MaxAngleModified;
var minSpread = gun.MinAngleModified;
var timeSinceLastFire = (_timing.CurTime - gun.NextFire).TotalSeconds;
var currentAngle = new Angle(MathHelper.Clamp(gun.CurrentAngle.Theta - gun.AngleDecayModified.Theta * timeSinceLastFire,
gun.MinAngleModified.Theta, gun.MaxAngleModified.Theta));
var direction = (mousePos.Position - mapPos.Position);
var maxSpread = gun.Comp.MaxAngleModified;
var minSpread = gun.Comp.MinAngleModified;
var timeSinceLastFire = (_timing.CurTime - gun.Comp.NextFire).TotalSeconds;
var currentAngle = new Angle(MathHelper.Clamp(gun.Comp.CurrentAngle.Theta - gun.Comp.AngleDecayModified.Theta * timeSinceLastFire,
gun.Comp.MinAngleModified.Theta, gun.Comp.MaxAngleModified.Theta));
var direction = mousePos.Position - mapPos.Position;
worldHandle.DrawLine(mapPos.Position, mousePos.Position + direction, Color.Orange);

View File

@@ -41,7 +41,7 @@ public abstract class BaseBulletRenderer : Control
{
var countPerRow = Math.Min(Capacity, CountPerRow(availableSize.X));
var rows = Math.Min((int) MathF.Ceiling(Capacity / (float) countPerRow), Rows);
var rows = Math.Min((int)MathF.Ceiling(Capacity / (float)countPerRow), Rows);
var height = _params.ItemHeight * rows + (_params.VerticalSeparation * rows - 1);
var width = RowWidth(countPerRow);
@@ -110,7 +110,7 @@ public abstract class BaseBulletRenderer : Control
private int CountPerRow(float width)
{
return (int) ((width - _params.ItemWidth + _params.ItemSeparation) / _params.ItemSeparation);
return (int)((width - _params.ItemWidth + _params.ItemSeparation) / _params.ItemSeparation);
}
private int RowWidth(int count)

View File

@@ -2,10 +2,8 @@ using Content.Shared.Projectiles;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Systems;
using Robust.Client.Player;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Physics.Events;
using Robust.Shared.Player;
using Robust.Shared.Random;
namespace Content.Client.Weapons.Ranged.Systems;
@@ -22,26 +20,26 @@ public sealed class FlyBySoundSystem : SharedFlyBySoundSystem
SubscribeLocalEvent<FlyBySoundComponent, StartCollideEvent>(OnCollide);
}
private void OnCollide(EntityUid uid, FlyBySoundComponent component, ref StartCollideEvent args)
private void OnCollide(Entity<FlyBySoundComponent> ent, ref StartCollideEvent args)
{
var attachedEnt = _player.LocalEntity;
// If it's not our ent or we shot it.
if (attachedEnt == null ||
args.OtherEntity != attachedEnt ||
TryComp<ProjectileComponent>(uid, out var projectile) &&
TryComp<ProjectileComponent>(ent, out var projectile) &&
projectile.Shooter == attachedEnt)
{
return;
}
if (args.OurFixtureId != FlyByFixture ||
!_random.Prob(component.Prob))
!_random.Prob(ent.Comp.Prob))
{
return;
}
// Play attached to our entity because the projectile may immediately delete or the likes.
_audio.PlayPredicted(component.Sound, attachedEnt.Value, attachedEnt.Value);
_audio.PlayPredicted(ent.Comp.Sound, attachedEnt.Value, attachedEnt.Value);
}
}

View File

@@ -14,12 +14,12 @@ namespace Content.Client.Weapons.Ranged.Systems;
public sealed partial class GunSystem
{
private void OnAmmoCounterCollect(EntityUid uid, AmmoCounterComponent component, ItemStatusCollectMessage args)
private void OnAmmoCounterCollect(Entity<AmmoCounterComponent> ent, ref ItemStatusCollectMessage args)
{
RefreshControl(uid, component);
RefreshControl(ent);
if (component.Control != null)
args.Controls.Add(component.Control);
if (ent.Comp.Control != null)
args.Controls.Add(ent.Comp.Control);
}
/// <summary>
@@ -27,35 +27,32 @@ public sealed partial class GunSystem
/// </summary>
/// <param name="uid"></param>
/// <param name="component"></param>
private void RefreshControl(EntityUid uid, AmmoCounterComponent? component = null)
private void RefreshControl(Entity<AmmoCounterComponent> ent)
{
if (!Resolve(uid, ref component, false))
return;
component.Control?.Dispose();
component.Control = null;
ent.Comp.Control?.Dispose();
ent.Comp.Control = null;
var ev = new AmmoCounterControlEvent();
RaiseLocalEvent(uid, ev, false);
RaiseLocalEvent(ent, ev, false);
// Fallback to default if none specified
ev.Control ??= new DefaultStatusControl();
component.Control = ev.Control;
UpdateAmmoCount(uid, component);
ent.Comp.Control = ev.Control;
UpdateAmmoCount(ent);
}
private void UpdateAmmoCount(EntityUid uid, AmmoCounterComponent component)
private void UpdateAmmoCount(Entity<AmmoCounterComponent> ent)
{
if (component.Control == null)
if (ent.Comp.Control == null)
return;
var ev = new UpdateAmmoCounterEvent()
{
Control = component.Control
Control = ent.Comp.Control
};
RaiseLocalEvent(uid, ev, false);
RaiseLocalEvent(ent, ev, false);
}
protected override void UpdateAmmoCount(EntityUid uid, bool prediction = true)
@@ -68,7 +65,7 @@ public sealed partial class GunSystem
return;
}
UpdateAmmoCount(uid, clientComp);
UpdateAmmoCount((uid, clientComp));
}
/// <summary>

View File

@@ -12,41 +12,41 @@ public sealed partial class GunSystem
SubscribeLocalEvent<BallisticAmmoProviderComponent, UpdateAmmoCounterEvent>(OnBallisticAmmoCount);
}
private void OnBallisticAmmoCount(EntityUid uid, BallisticAmmoProviderComponent component, UpdateAmmoCounterEvent args)
private void OnBallisticAmmoCount(Entity<BallisticAmmoProviderComponent> ent, ref UpdateAmmoCounterEvent args)
{
if (args.Control is DefaultStatusControl control)
{
control.Update(GetBallisticShots(component), component.Capacity);
control.Update(GetBallisticShots(ent.Comp), ent.Comp.Capacity);
}
}
protected override void Cycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates)
protected override void Cycle(Entity<BallisticAmmoProviderComponent> ent, MapCoordinates coordinates)
{
if (!Timing.IsFirstTimePredicted)
return;
EntityUid? ent = null;
EntityUid? ammoEnt = null;
// TODO: Combine with TakeAmmo
if (component.Entities.Count > 0)
if (ent.Comp.Entities.Count > 0)
{
var existing = component.Entities[^1];
component.Entities.RemoveAt(component.Entities.Count - 1);
var existing = ent.Comp.Entities[^1];
ent.Comp.Entities.RemoveAt(ent.Comp.Entities.Count - 1);
Containers.Remove(existing, component.Container);
Containers.Remove(existing, ent.Comp.Container);
EnsureShootable(existing);
}
else if (component.UnspawnedCount > 0)
else if (ent.Comp.UnspawnedCount > 0)
{
component.UnspawnedCount--;
ent = Spawn(component.Proto, coordinates);
EnsureShootable(ent.Value);
ent.Comp.UnspawnedCount--;
ammoEnt = Spawn(ent.Comp.Proto, coordinates);
EnsureShootable(ammoEnt.Value);
}
if (ent != null && IsClientSide(ent.Value))
Del(ent.Value);
if (ammoEnt != null && IsClientSide(ammoEnt.Value))
Del(ammoEnt.Value);
var cycledEvent = new GunCycledEvent();
RaiseLocalEvent(uid, ref cycledEvent);
RaiseLocalEvent(ent, ref cycledEvent);
}
}

View File

@@ -10,11 +10,11 @@ public partial class GunSystem
SubscribeLocalEvent<BasicEntityAmmoProviderComponent, UpdateAmmoCounterEvent>(OnBasicEntityAmmoCount);
}
private void OnBasicEntityAmmoCount(EntityUid uid, BasicEntityAmmoProviderComponent component, UpdateAmmoCounterEvent args)
private void OnBasicEntityAmmoCount(Entity<BasicEntityAmmoProviderComponent> ent, ref UpdateAmmoCounterEvent args)
{
if (args.Control is DefaultStatusControl control && component.Count != null && component.Capacity != null)
if (args.Control is DefaultStatusControl control && ent.Comp.Count != null && ent.Comp.Capacity != null)
{
control.Update(component.Count.Value, component.Capacity.Value);
control.Update(ent.Comp.Count.Value, ent.Comp.Capacity.Value);
}
}
}

View File

@@ -18,11 +18,11 @@ public sealed partial class GunSystem
SubscribeLocalEvent<ChamberMagazineAmmoProviderComponent, AppearanceChangeEvent>(OnChamberMagazineAppearance);
}
private void OnChamberMagazineAppearance(EntityUid uid, ChamberMagazineAmmoProviderComponent component, ref AppearanceChangeEvent args)
private void OnChamberMagazineAppearance(Entity<ChamberMagazineAmmoProviderComponent> ent, ref AppearanceChangeEvent args)
{
if (args.Sprite == null ||
!_sprite.LayerMapTryGet((uid, args.Sprite), GunVisualLayers.Base, out var boltLayer, false) ||
!Appearance.TryGetData(uid, AmmoVisuals.BoltClosed, out bool boltClosed))
!_sprite.LayerMapTryGet((ent, args.Sprite), GunVisualLayers.Base, out var boltLayer, false) ||
!Appearance.TryGetData(ent, AmmoVisuals.BoltClosed, out bool boltClosed))
{
return;
}
@@ -30,11 +30,11 @@ public sealed partial class GunSystem
// Maybe re-using base layer for this will bite me someday but screw you future sloth.
if (boltClosed)
{
_sprite.LayerSetRsiState((uid, args.Sprite), boltLayer, "base");
_sprite.LayerSetRsiState((ent, args.Sprite), boltLayer, "base");
}
else
{
_sprite.LayerSetRsiState((uid, args.Sprite), boltLayer, "bolt-open");
_sprite.LayerSetRsiState((ent, args.Sprite), boltLayer, "bolt-open");
}
}
@@ -55,17 +55,17 @@ public sealed partial class GunSystem
// to avoid 6-7 additional entity spawns.
}
private void OnChamberMagazineCounter(EntityUid uid, ChamberMagazineAmmoProviderComponent component, AmmoCounterControlEvent args)
private void OnChamberMagazineCounter(Entity<ChamberMagazineAmmoProviderComponent> ent, ref AmmoCounterControlEvent args)
{
args.Control = new ChamberMagazineStatusControl();
}
private void OnChamberMagazineAmmoUpdate(EntityUid uid, ChamberMagazineAmmoProviderComponent component, UpdateAmmoCounterEvent args)
private void OnChamberMagazineAmmoUpdate(Entity<ChamberMagazineAmmoProviderComponent> ent, ref UpdateAmmoCounterEvent args)
{
if (args.Control is not ChamberMagazineStatusControl control) return;
var chambered = GetChamberEntity(uid);
var magEntity = GetMagazineEntity(uid);
var chambered = GetChamberEntity(ent);
var magEntity = GetMagazineEntity(ent);
var ammoCountEv = new GetAmmoCountEvent();
if (magEntity != null)

View File

@@ -11,11 +11,11 @@ public sealed partial class GunSystem
SubscribeLocalEvent<MagazineAmmoProviderComponent, AmmoCounterControlEvent>(OnMagazineControl);
}
private void OnMagazineAmmoUpdate(EntityUid uid, MagazineAmmoProviderComponent component, UpdateAmmoCounterEvent args)
private void OnMagazineAmmoUpdate(Entity<MagazineAmmoProviderComponent> ent, ref UpdateAmmoCounterEvent args)
{
var ent = GetMagazineEntity(uid);
var magEnt = GetMagazineEntity(ent);
if (ent == null)
if (magEnt == null)
{
if (args.Control is DefaultStatusControl control)
{
@@ -25,14 +25,14 @@ public sealed partial class GunSystem
return;
}
RaiseLocalEvent(ent.Value, args, false);
RaiseLocalEvent(magEnt.Value, args, false);
}
private void OnMagazineControl(EntityUid uid, MagazineAmmoProviderComponent component, AmmoCounterControlEvent args)
private void OnMagazineControl(Entity<MagazineAmmoProviderComponent> ent, ref AmmoCounterControlEvent args)
{
var ent = GetMagazineEntity(uid);
if (ent == null)
var magEnt = GetMagazineEntity(ent);
if (magEnt == null)
return;
RaiseLocalEvent(ent.Value, args, false);
RaiseLocalEvent(magEnt.Value, args, false);
}
}

View File

@@ -13,24 +13,24 @@ public sealed partial class GunSystem
SubscribeLocalEvent<MagazineVisualsComponent, AppearanceChangeEvent>(OnMagazineVisualsChange);
}
private void OnMagazineVisualsInit(EntityUid uid, MagazineVisualsComponent component, ComponentInit args)
private void OnMagazineVisualsInit(Entity<MagazineVisualsComponent> ent, ref ComponentInit args)
{
if (!TryComp<SpriteComponent>(uid, out var sprite)) return;
if (!TryComp<SpriteComponent>(ent, out var sprite)) return;
if (_sprite.LayerMapTryGet((uid, sprite), GunVisualLayers.Mag, out _, false))
if (_sprite.LayerMapTryGet((ent, sprite), GunVisualLayers.Mag, out _, false))
{
_sprite.LayerSetRsiState((uid, sprite), GunVisualLayers.Mag, $"{component.MagState}-{component.MagSteps - 1}");
_sprite.LayerSetVisible((uid, sprite), GunVisualLayers.Mag, false);
_sprite.LayerSetRsiState((ent, sprite), GunVisualLayers.Mag, $"{ent.Comp.MagState}-{ent.Comp.MagSteps - 1}");
_sprite.LayerSetVisible((ent, sprite), GunVisualLayers.Mag, false);
}
if (_sprite.LayerMapTryGet((uid, sprite), GunVisualLayers.MagUnshaded, out _, false))
if (_sprite.LayerMapTryGet((ent, sprite), GunVisualLayers.MagUnshaded, out _, false))
{
_sprite.LayerSetRsiState((uid, sprite), GunVisualLayers.MagUnshaded, $"{component.MagState}-unshaded-{component.MagSteps - 1}");
_sprite.LayerSetVisible((uid, sprite), GunVisualLayers.MagUnshaded, false);
_sprite.LayerSetRsiState((ent, sprite), GunVisualLayers.MagUnshaded, $"{ent.Comp.MagState}-unshaded-{ent.Comp.MagSteps - 1}");
_sprite.LayerSetVisible((ent, sprite), GunVisualLayers.MagUnshaded, false);
}
}
private void OnMagazineVisualsChange(EntityUid uid, MagazineVisualsComponent component, ref AppearanceChangeEvent args)
private void OnMagazineVisualsChange(Entity<MagazineVisualsComponent> ent, ref AppearanceChangeEvent args)
{
// tl;dr
// 1.If no mag then hide it OR
@@ -45,53 +45,53 @@ public sealed partial class GunSystem
{
if (!args.AppearanceData.TryGetValue(AmmoVisuals.AmmoMax, out var capacity))
{
capacity = component.MagSteps;
capacity = ent.Comp.MagSteps;
}
if (!args.AppearanceData.TryGetValue(AmmoVisuals.AmmoCount, out var current))
{
current = component.MagSteps;
current = ent.Comp.MagSteps;
}
var step = ContentHelpers.RoundToLevels((int)current, (int)capacity, component.MagSteps);
var step = ContentHelpers.RoundToLevels((int)current, (int)capacity, ent.Comp.MagSteps);
if (step == 0 && !component.ZeroVisible)
if (step == 0 && !ent.Comp.ZeroVisible)
{
if (_sprite.LayerMapTryGet((uid, sprite), GunVisualLayers.Mag, out _, false))
if (_sprite.LayerMapTryGet((ent, sprite), GunVisualLayers.Mag, out _, false))
{
_sprite.LayerSetVisible((uid, sprite), GunVisualLayers.Mag, false);
_sprite.LayerSetVisible((ent, sprite), GunVisualLayers.Mag, false);
}
if (_sprite.LayerMapTryGet((uid, sprite), GunVisualLayers.MagUnshaded, out _, false))
if (_sprite.LayerMapTryGet((ent, sprite), GunVisualLayers.MagUnshaded, out _, false))
{
_sprite.LayerSetVisible((uid, sprite), GunVisualLayers.MagUnshaded, false);
_sprite.LayerSetVisible((ent, sprite), GunVisualLayers.MagUnshaded, false);
}
return;
}
if (_sprite.LayerMapTryGet((uid, sprite), GunVisualLayers.Mag, out _, false))
if (_sprite.LayerMapTryGet((ent, sprite), GunVisualLayers.Mag, out _, false))
{
_sprite.LayerSetVisible((uid, sprite), GunVisualLayers.Mag, true);
_sprite.LayerSetRsiState((uid, sprite), GunVisualLayers.Mag, $"{component.MagState}-{step}");
_sprite.LayerSetVisible((ent, sprite), GunVisualLayers.Mag, true);
_sprite.LayerSetRsiState((ent, sprite), GunVisualLayers.Mag, $"{ent.Comp.MagState}-{step}");
}
if (_sprite.LayerMapTryGet((uid, sprite), GunVisualLayers.MagUnshaded, out _, false))
if (_sprite.LayerMapTryGet((ent, sprite), GunVisualLayers.MagUnshaded, out _, false))
{
_sprite.LayerSetVisible((uid, sprite), GunVisualLayers.MagUnshaded, true);
_sprite.LayerSetRsiState((uid, sprite), GunVisualLayers.MagUnshaded, $"{component.MagState}-unshaded-{step}");
_sprite.LayerSetVisible((ent, sprite), GunVisualLayers.MagUnshaded, true);
_sprite.LayerSetRsiState((ent, sprite), GunVisualLayers.MagUnshaded, $"{ent.Comp.MagState}-unshaded-{step}");
}
}
else
{
if (_sprite.LayerMapTryGet((uid, sprite), GunVisualLayers.Mag, out _, false))
if (_sprite.LayerMapTryGet((ent, sprite), GunVisualLayers.Mag, out _, false))
{
_sprite.LayerSetVisible((uid, sprite), GunVisualLayers.Mag, false);
_sprite.LayerSetVisible((ent, sprite), GunVisualLayers.Mag, false);
}
if (_sprite.LayerMapTryGet((uid, sprite), GunVisualLayers.MagUnshaded, out _, false))
if (_sprite.LayerMapTryGet((ent, sprite), GunVisualLayers.MagUnshaded, out _, false))
{
_sprite.LayerSetVisible((uid, sprite), GunVisualLayers.MagUnshaded, false);
_sprite.LayerSetVisible((ent, sprite), GunVisualLayers.MagUnshaded, false);
}
}
}

View File

@@ -14,25 +14,25 @@ public sealed partial class GunSystem
SubscribeLocalEvent<RevolverAmmoProviderComponent, EntRemovedFromContainerMessage>(OnRevolverEntRemove);
}
private void OnRevolverEntRemove(EntityUid uid, RevolverAmmoProviderComponent component, EntRemovedFromContainerMessage args)
private void OnRevolverEntRemove(Entity<RevolverAmmoProviderComponent> ent, ref EntRemovedFromContainerMessage args)
{
if (args.Container.ID != RevolverContainer)
return;
// See ChamberMagazineAmmoProvider
// <See ChamberMagazineAmmoProvider>
if (!IsClientSide(args.Entity))
return;
QueueDel(args.Entity);
}
private void OnRevolverAmmoUpdate(EntityUid uid, RevolverAmmoProviderComponent component, UpdateAmmoCounterEvent args)
private void OnRevolverAmmoUpdate(Entity<RevolverAmmoProviderComponent> ent, ref UpdateAmmoCounterEvent args)
{
if (args.Control is not RevolverStatusControl control) return;
control.Update(component.CurrentIndex, component.Chambers);
control.Update(ent.Comp.CurrentIndex, ent.Comp.Chambers);
}
private void OnRevolverCounter(EntityUid uid, RevolverAmmoProviderComponent component, AmmoCounterControlEvent args)
private void OnRevolverCounter(Entity<RevolverAmmoProviderComponent> ent, ref AmmoCounterControlEvent args)
{
args.Control = new RevolverStatusControl();
}

View File

@@ -11,7 +11,7 @@ public sealed partial class GunSystem
SubscribeLocalEvent<SpentAmmoVisualsComponent, AppearanceChangeEvent>(OnSpentAmmoAppearance);
}
private void OnSpentAmmoAppearance(EntityUid uid, SpentAmmoVisualsComponent component, ref AppearanceChangeEvent args)
private void OnSpentAmmoAppearance(Entity<SpentAmmoVisualsComponent> ent, ref AppearanceChangeEvent args)
{
var sprite = args.Sprite;
if (sprite == null) return;
@@ -21,15 +21,15 @@ public sealed partial class GunSystem
return;
}
var spent = (bool) varSpent;
var spent = (bool)varSpent;
string state;
if (spent)
state = component.Suffix ? $"{component.State}-spent" : "spent";
state = ent.Comp.Suffix ? $"{ent.Comp.State}-spent" : "spent";
else
state = component.State;
state = ent.Comp.State;
_sprite.LayerSetRsiState((uid, sprite), AmmoVisualLayers.Base, state);
_sprite.RemoveLayer((uid, sprite), AmmoVisualLayers.Tip, false);
_sprite.LayerSetRsiState((ent, sprite), AmmoVisualLayers.Base, state);
_sprite.RemoveLayer((ent, sprite), AmmoVisualLayers.Tip, false);
}
}

View File

@@ -31,13 +31,13 @@ namespace Content.Client.Weapons.Ranged.Systems;
public sealed partial class GunSystem : SharedGunSystem
{
[Dependency] private readonly AnimationPlayerSystem _animPlayer = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly InputSystem _inputSystem = default!;
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IStateManager _state = default!;
[Dependency] private readonly AnimationPlayerSystem _animPlayer = default!;
[Dependency] private readonly InputSystem _inputSystem = default!;
[Dependency] private readonly SharedCameraRecoilSystem _recoil = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
[Dependency] private readonly SharedTransformSystem _xform = default!;
@@ -167,29 +167,29 @@ public sealed partial class GunSystem : SharedGunSystem
var entity = entityNull.Value;
if (!TryGetGun(entity, out var gunUid, out var gun))
if (!TryGetGun(entity, out var gun))
{
return;
}
var useKey = gun.UseKey ? EngineKeyFunctions.Use : EngineKeyFunctions.UseSecondary;
var useKey = gun.Comp.UseKey ? EngineKeyFunctions.Use : EngineKeyFunctions.UseSecondary;
if (_inputSystem.CmdStates.GetState(useKey) != BoundKeyState.Down && !gun.BurstActivated)
if (_inputSystem.CmdStates.GetState(useKey) != BoundKeyState.Down && !gun.Comp.BurstActivated)
{
if (gun.ShotCounter != 0)
RaisePredictiveEvent(new RequestStopShootEvent { Gun = GetNetEntity(gunUid) });
if (gun.Comp.ShotCounter != 0)
RaisePredictiveEvent(new RequestStopShootEvent { Gun = GetNetEntity(gun) });
return;
}
if (gun.NextFire > Timing.CurTime)
if (gun.Comp.NextFire > Timing.CurTime)
return;
var mousePos = _eyeManager.PixelToMap(_inputManager.MouseScreenPosition);
if (mousePos.MapId == MapId.Nullspace)
{
if (gun.ShotCounter != 0)
RaisePredictiveEvent(new RequestStopShootEvent { Gun = GetNetEntity(gunUid) });
if (gun.Comp.ShotCounter != 0)
RaisePredictiveEvent(new RequestStopShootEvent { Gun = GetNetEntity(gun) });
return;
}
@@ -207,11 +207,11 @@ public sealed partial class GunSystem : SharedGunSystem
{
Target = target,
Coordinates = GetNetCoordinates(coordinates),
Gun = GetNetEntity(gunUid),
Gun = GetNetEntity(gun),
});
}
public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? Entity, IShootable Shootable)> ammo,
public override void Shoot(Entity<GunComponent> gun, List<(EntityUid? Entity, IShootable Shootable)> ammo,
EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, out bool userImpulse, EntityUid? user = null, bool throwItems = false)
{
userImpulse = true;
@@ -226,7 +226,7 @@ public sealed partial class GunSystem : SharedGunSystem
{
if (throwItems)
{
Recoil(user, direction, gun.CameraRecoilScalarModified);
Recoil(user, direction, gun.Comp.CameraRecoilScalarModified);
if (IsClientSide(ent!.Value))
Del(ent.Value);
else
@@ -241,9 +241,9 @@ public sealed partial class GunSystem : SharedGunSystem
if (!cartridge.Spent)
{
SetCartridgeSpent(ent!.Value, cartridge, true);
MuzzleFlash(gunUid, cartridge, worldAngle, user);
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
Recoil(user, direction, gun.CameraRecoilScalarModified);
MuzzleFlash(gun, cartridge, worldAngle, user);
Audio.PlayPredicted(gun.Comp.SoundGunshotModified, gun, user);
Recoil(user, direction, gun.Comp.CameraRecoilScalarModified);
// TODO: Can't predict entity deletions.
//if (cartridge.DeleteOnSpawn)
// Del(cartridge.Owner);
@@ -251,7 +251,7 @@ public sealed partial class GunSystem : SharedGunSystem
else
{
userImpulse = false;
Audio.PlayPredicted(gun.SoundEmpty, gunUid, user);
Audio.PlayPredicted(gun.Comp.SoundEmpty, gun, user);
}
if (IsClientSide(ent!.Value))
@@ -259,17 +259,17 @@ public sealed partial class GunSystem : SharedGunSystem
break;
case AmmoComponent newAmmo:
MuzzleFlash(gunUid, newAmmo, worldAngle, user);
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
Recoil(user, direction, gun.CameraRecoilScalarModified);
MuzzleFlash(gun, newAmmo, worldAngle, user);
Audio.PlayPredicted(gun.Comp.SoundGunshotModified, gun, user);
Recoil(user, direction, gun.Comp.CameraRecoilScalarModified);
if (IsClientSide(ent!.Value))
Del(ent.Value);
else
RemoveShootable(ent.Value);
break;
case HitscanAmmoComponent:
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
Recoil(user, direction, gun.CameraRecoilScalarModified);
Audio.PlayPredicted(gun.Comp.SoundGunshotModified, gun, user);
Recoil(user, direction, gun.Comp.CameraRecoilScalarModified);
break;
}
}
@@ -407,5 +407,5 @@ public sealed partial class GunSystem : SharedGunSystem
}
// TODO: Move RangedDamageSoundComponent to shared so this can be predicted.
public override void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound) {}
public override void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound) { }
}

View File

@@ -478,11 +478,11 @@ public abstract partial class InteractionTest
var wasInCombatMode = IsInCombatMode();
await SetCombatMode(true);
Assert.That(SGun.TryGetGun(SPlayer, out var gunUid, out var gunComp), "Player was not holding a gun!");
Assert.That(SGun.TryGetGun(SPlayer, out var gun), "Player was not holding a gun!");
await Server.WaitAssertion(() =>
{
var success = SGun.AttemptShoot(SPlayer, gunUid, gunComp!, actualTarget);
var success = SGun.AttemptShoot(SPlayer, gun, actualTarget);
if (assert)
Assert.That(success, "Gun failed to shoot.");
});
@@ -517,11 +517,11 @@ public abstract partial class InteractionTest
var wasInCombatMode = IsInCombatMode();
await SetCombatMode(true);
Assert.That(SGun.TryGetGun(SPlayer, out var gunUid, out var gunComp), "Player was not holding a gun!");
Assert.That(SGun.TryGetGun(SPlayer, out var gun), "Player was not holding a gun!");
await Server.WaitAssertion(() =>
{
var success = SGun.AttemptShoot(SPlayer, gunUid, gunComp!, Position(actualTarget!.Value), ToServer(actualTarget));
var success = SGun.AttemptShoot(SPlayer, gun, Position(actualTarget!.Value), ToServer(actualTarget));
if (assert)
Assert.That(success, "Gun failed to shoot.");
});
@@ -839,7 +839,7 @@ public abstract partial class InteractionTest
/// <param name="uid">The entity at which the events were directed</param>
/// <param name="count">How many new events are expected</param>
/// <param name="predicate">A predicate that can be used to filter the recorded events</param>
protected void AssertEvent<TEvent>(EntityUid? uid = null, int count = 1, Func<TEvent,bool>? predicate = null)
protected void AssertEvent<TEvent>(EntityUid? uid = null, int count = 1, Func<TEvent, bool>? predicate = null)
where TEvent : notnull
{
Assert.That(GetEvents(uid, predicate).Count, Is.EqualTo(count));
@@ -872,7 +872,7 @@ public abstract partial class InteractionTest
where TEvent : notnull
{
if (_listenerCache.TryGetValue(typeof(TEvent), out var listener))
return (TestListenerSystem<TEvent>) listener;
return (TestListenerSystem<TEvent>)listener;
var type = Server.Resolve<IReflectionManager>().GetAllChildren<TestListenerSystem<TEvent>>().Single();
if (!SEntMan.EntitySysManager.TryGetEntitySystem(type, out var systemObj))

View File

@@ -722,7 +722,7 @@ public sealed partial class AdminVerbSystem
return;
_gun.SetBallisticUnspawned((args.Target, ballisticAmmo), result);
_gun.UpdateBallisticAppearance(args.Target, ballisticAmmo);
_gun.UpdateBallisticAppearance((args.Target, ballisticAmmo));
});
},
Impact = LogImpact.Medium,

View File

@@ -27,18 +27,18 @@ public sealed partial class GunSignalControlSystem : EntitySystem
return;
if (args.Port == gunControl.Comp.TriggerPort)
_gun.AttemptShoot(gunControl, gun);
_gun.AttemptShoot((gunControl, gun));
if (!TryComp<AutoShootGunComponent>(gunControl, out var autoShoot))
return;
if (args.Port == gunControl.Comp.TogglePort)
_gun.SetEnabled(gunControl, autoShoot, !autoShoot.Enabled);
_gun.SetEnabled((gunControl, autoShoot), !autoShoot.Enabled);
if (args.Port == gunControl.Comp.OnPort)
_gun.SetEnabled(gunControl, autoShoot, true);
_gun.SetEnabled((gunControl, autoShoot), true);
if (args.Port == gunControl.Comp.OffPort)
_gun.SetEnabled(gunControl, autoShoot, false);
_gun.SetEnabled((gunControl, autoShoot), false);
}
}

View File

@@ -10,10 +10,10 @@ public sealed partial class GunAmmoPrecondition : HTNPrecondition
{
[Dependency] private readonly IEntityManager _entManager = default!;
[DataField("minPercent")]
[DataField]
public float MinPercent = 0f;
[DataField("maxPercent")]
[DataField]
public float MaxPercent = 1f;
public override bool IsMet(NPCBlackboard blackboard)
@@ -21,19 +21,19 @@ public sealed partial class GunAmmoPrecondition : HTNPrecondition
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
var gunSystem = _entManager.System<GunSystem>();
if (!gunSystem.TryGetGun(owner, out var gunUid, out _))
if (!gunSystem.TryGetGun(owner, out var gun))
{
return false;
}
var ammoEv = new GetAmmoCountEvent();
_entManager.EventBus.RaiseLocalEvent(gunUid, ref ammoEv);
_entManager.EventBus.RaiseLocalEvent(gun, ref ammoEv);
float percent;
if (ammoEv.Capacity == 0)
percent = 0f;
else
percent = ammoEv.Count / (float) ammoEv.Capacity;
percent = ammoEv.Count / (float)ammoEv.Capacity;
percent = System.Math.Clamp(percent, 0f, 1f);

View File

@@ -96,7 +96,7 @@ public sealed partial class NPCCombatSystem
_combat.SetInCombatMode(uid, true, combatMode);
}
if (!_gun.TryGetGun(uid, out var gunUid, out var gun))
if (!_gun.TryGetGun(uid, out var gun))
{
comp.Status = CombatStatus.NoWeapon;
comp.ShootAccumulator = 0f;
@@ -104,12 +104,12 @@ public sealed partial class NPCCombatSystem
}
var ammoEv = new GetAmmoCountEvent();
RaiseLocalEvent(gunUid, ref ammoEv);
RaiseLocalEvent(gun, ref ammoEv);
if (ammoEv.Count == 0)
{
// Recharging then?
if (_rechargeQuery.HasComponent(gunUid))
if (_rechargeQuery.HasComponent(gun))
{
continue;
}
@@ -200,12 +200,12 @@ public sealed partial class NPCCombatSystem
comp.Status = CombatStatus.Normal;
if (gun.NextFire > _timing.CurTime)
if (gun.Comp.NextFire > _timing.CurTime)
{
return;
}
_gun.AttemptShoot(uid, gunUid, gun, targetCordinates, comp.Target);
_gun.AttemptShoot(uid, gun, targetCordinates, comp.Target);
}
}
}

View File

@@ -232,7 +232,7 @@ namespace Content.Server.Singularity.EntitySystems
var targetPos = new EntityCoordinates(uid, new Vector2(0, -1));
_gun.Shoot(uid, gunComponent, ent, xform.Coordinates, targetPos, out _);
_gun.Shoot((uid, gunComponent), ent, xform.Coordinates, targetPos, out _);
}
private void UpdateAppearance(EntityUid uid, EmitterComponent component)

View File

@@ -84,7 +84,7 @@ public sealed partial class DeployableTurretSystem : SharedDeployableTurretSyste
args.Data.TryGetValue(command, out int? armamentState))
{
if (TryComp<BatteryWeaponFireModesComponent>(ent, out var batteryWeaponFireModes))
_fireModes.TrySetFireMode(ent, batteryWeaponFireModes, armamentState.Value);
_fireModes.TrySetFireMode((ent, batteryWeaponFireModes), armamentState.Value);
TrySetState(ent, armamentState.Value >= 0);
return;

View File

@@ -2,7 +2,4 @@ using Content.Shared.Weapons.Marker;
namespace Content.Server.Weapons;
public sealed class DamageMarkerSystem : SharedDamageMarkerSystem
{
}
public sealed class DamageMarkerSystem : SharedDamageMarkerSystem { }

View File

@@ -3,4 +3,4 @@ using Content.Shared.Weapons.Ranged.Components;
namespace Content.Server.Weapons.Ranged.Components;
[RegisterComponent]
public sealed partial class AmmoCounterComponent : SharedAmmoCounterComponent {}
public sealed partial class AmmoCounterComponent : SharedAmmoCounterComponent { }

View File

@@ -1,11 +1,10 @@
namespace Content.Server.Weapons.Ranged.Components
{
[RegisterComponent]
public sealed partial class ChemicalAmmoComponent : Component
{
public const string DefaultSolutionName = "ammo";
namespace Content.Server.Weapons.Ranged.Components;
[DataField("solution")]
public string SolutionName { get; set; } = DefaultSolutionName;
}
[RegisterComponent]
public sealed partial class ChemicalAmmoComponent : Component
{
public const string DefaultSolutionName = "ammo";
[DataField("solution")]
public string SolutionName { get; set; } = DefaultSolutionName;
}

View File

@@ -16,15 +16,13 @@ public sealed partial class RangedDamageSoundComponent : Component
/// Specified sounds to apply when the entity takes damage with the specified group.
/// Will fallback to defaults if none specified.
/// </summary>
[DataField("soundGroups",
customTypeSerializer: typeof(PrototypeIdDictionarySerializer<SoundSpecifier, DamageGroupPrototype>))]
[DataField(customTypeSerializer: typeof(PrototypeIdDictionarySerializer<SoundSpecifier, DamageGroupPrototype>))]
public Dictionary<string, SoundSpecifier>? SoundGroups;
/// <summary>
/// Specified sounds to apply when the entity takes damage with the specified type.
/// Will fallback to defaults if none specified.
/// </summary>
[DataField("soundTypes",
customTypeSerializer: typeof(PrototypeIdDictionarySerializer<SoundSpecifier, DamageTypePrototype>))]
[DataField(customTypeSerializer: typeof(PrototypeIdDictionarySerializer<SoundSpecifier, DamageTypePrototype>))]
public Dictionary<string, SoundSpecifier>? SoundTypes;
}

View File

@@ -1,49 +1,48 @@
using Content.Server.Weapons.Ranged.Components;
using Content.Shared.Chemistry.Components;
using Content.Shared.Weapons.Ranged.Events;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Weapons.Ranged.Events;
using System.Linq;
namespace Content.Server.Weapons.Ranged.Systems
namespace Content.Server.Weapons.Ranged.Systems;
public sealed class ChemicalAmmoSystem : EntitySystem
{
public sealed class ChemicalAmmoSystem : EntitySystem
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
public override void Initialize()
{
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
SubscribeLocalEvent<ChemicalAmmoComponent, AmmoShotEvent>(OnFire);
}
public override void Initialize()
private void OnFire(Entity<ChemicalAmmoComponent> entity, ref AmmoShotEvent args)
{
if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var ammoSoln, out var ammoSolution))
return;
var projectiles = args.FiredProjectiles;
var projectileSolutionContainers = new List<(EntityUid, Entity<SolutionComponent>)>();
foreach (var projectile in projectiles)
{
SubscribeLocalEvent<ChemicalAmmoComponent, AmmoShotEvent>(OnFire);
if (_solutionContainerSystem
.TryGetSolution(projectile, entity.Comp.SolutionName, out var projectileSoln, out _))
{
projectileSolutionContainers.Add((projectile, projectileSoln.Value));
}
}
private void OnFire(Entity<ChemicalAmmoComponent> entity, ref AmmoShotEvent args)
if (!projectileSolutionContainers.Any())
return;
var solutionPerProjectile = ammoSolution.Volume * (1 / projectileSolutionContainers.Count);
foreach (var (_, projectileSolution) in projectileSolutionContainers)
{
if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var ammoSoln, out var ammoSolution))
return;
var projectiles = args.FiredProjectiles;
var projectileSolutionContainers = new List<(EntityUid, Entity<SolutionComponent>)>();
foreach (var projectile in projectiles)
{
if (_solutionContainerSystem
.TryGetSolution(projectile, entity.Comp.SolutionName, out var projectileSoln, out _))
{
projectileSolutionContainers.Add((projectile, projectileSoln.Value));
}
}
if (!projectileSolutionContainers.Any())
return;
var solutionPerProjectile = ammoSolution.Volume * (1 / projectileSolutionContainers.Count);
foreach (var (_, projectileSolution) in projectileSolutionContainers)
{
var solutionToTransfer = _solutionContainerSystem.SplitSolution(ammoSoln.Value, solutionPerProjectile);
_solutionContainerSystem.TryAddSolution(projectileSolution, solutionToTransfer);
}
_solutionContainerSystem.RemoveAllSolution(ammoSoln.Value);
var solutionToTransfer = _solutionContainerSystem.SplitSolution(ammoSoln.Value, solutionPerProjectile);
_solutionContainerSystem.TryAddSolution(projectileSolution, solutionToTransfer);
}
_solutionContainerSystem.RemoveAllSolution(ammoSoln.Value);
}
}

View File

@@ -2,4 +2,4 @@ using Content.Shared.Weapons.Ranged.Systems;
namespace Content.Server.Weapons.Ranged.Systems;
public sealed class FlyBySoundSystem : SharedFlyBySoundSystem {}
public sealed class FlyBySoundSystem : SharedFlyBySoundSystem { }

View File

@@ -27,15 +27,15 @@ public sealed partial class GunSystem
if (!autoShoot.Enabled)
continue;
AttemptShoot(uid, gun);
AttemptShoot((uid, gun));
}
else if (gun.BurstActivated)
{
var parent = TransformSystem.GetParentUid(uid);
if (HasComp<DamageableComponent>(parent))
AttemptShoot(parent, uid, gun, gun.ShootCoordinates ?? new EntityCoordinates(uid, gun.DefaultDirection));
AttemptShoot(parent, (uid, gun), gun.ShootCoordinates ?? new EntityCoordinates(uid, gun.DefaultDirection));
else
AttemptShoot(uid, gun);
AttemptShoot((uid, gun));
}
}
}

View File

@@ -6,32 +6,32 @@ namespace Content.Server.Weapons.Ranged.Systems;
public sealed partial class GunSystem
{
protected override void Cycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates)
protected override void Cycle(Entity<BallisticAmmoProviderComponent> ent, MapCoordinates coordinates)
{
EntityUid? ent = null;
EntityUid? ammoEnt = null;
// TODO: Combine with TakeAmmo
if (component.Entities.Count > 0)
if (ent.Comp.Entities.Count > 0)
{
var existing = component.Entities[^1];
component.Entities.RemoveAt(component.Entities.Count - 1);
DirtyField(uid, component, nameof(BallisticAmmoProviderComponent.Entities));
var existing = ent.Comp.Entities[^1];
ent.Comp.Entities.RemoveAt(ent.Comp.Entities.Count - 1);
DirtyField(ent.AsNullable(), nameof(BallisticAmmoProviderComponent.Entities));
Containers.Remove(existing, component.Container);
Containers.Remove(existing, ent.Comp.Container);
EnsureShootable(existing);
}
else if (component.UnspawnedCount > 0)
else if (ent.Comp.UnspawnedCount > 0)
{
component.UnspawnedCount--;
DirtyField(uid, component, nameof(BallisticAmmoProviderComponent.UnspawnedCount));
ent = Spawn(component.Proto, coordinates);
EnsureShootable(ent.Value);
ent.Comp.UnspawnedCount--;
DirtyField(ent.AsNullable(), nameof(BallisticAmmoProviderComponent.UnspawnedCount));
ammoEnt = Spawn(ent.Comp.Proto, coordinates);
EnsureShootable(ammoEnt.Value);
}
if (ent != null)
EjectCartridge(ent.Value);
if (ammoEnt != null)
EjectCartridge(ammoEnt.Value);
var cycledEvent = new GunCycledEvent();
RaiseLocalEvent(uid, ref cycledEvent);
RaiseLocalEvent(ent, ref cycledEvent);
}
}

View File

@@ -4,15 +4,15 @@ namespace Content.Server.Weapons.Ranged.Systems;
public sealed partial class GunSystem
{
protected override void SpinRevolver(EntityUid revolverUid, RevolverAmmoProviderComponent component, EntityUid? user = null)
protected override void SpinRevolver(Entity<RevolverAmmoProviderComponent> ent, EntityUid? user = null)
{
base.SpinRevolver(revolverUid, component, user);
var index = Random.Next(component.Capacity);
base.SpinRevolver(ent, user);
var index = Random.Next(ent.Comp.Capacity);
if (component.CurrentIndex == index)
if (ent.Comp.CurrentIndex == index)
return;
component.CurrentIndex = index;
Dirty(revolverUid, component);
ent.Comp.CurrentIndex = index;
Dirty(ent);
}
}

View File

@@ -23,63 +23,63 @@ public sealed partial class GunSystem
private void OnSolutionMapInit(Entity<SolutionAmmoProviderComponent> entity, ref MapInitEvent args)
{
UpdateSolutionShots(entity.Owner, entity.Comp);
UpdateSolutionShots(entity);
}
private void OnSolutionChanged(Entity<SolutionAmmoProviderComponent> entity, ref SolutionContainerChangedEvent args)
{
if (args.Solution.Name == entity.Comp.SolutionId)
UpdateSolutionShots(entity.Owner, entity.Comp, args.Solution);
UpdateSolutionShots(entity, args.Solution);
}
protected override void UpdateSolutionShots(EntityUid uid, SolutionAmmoProviderComponent component, Solution? solution = null)
protected override void UpdateSolutionShots(Entity<SolutionAmmoProviderComponent> ent, Solution? solution = null)
{
var shots = 0;
var maxShots = 0;
if (solution == null && !_solutionContainer.TryGetSolution(uid, component.SolutionId, out _, out solution))
if (solution == null && !_solutionContainer.TryGetSolution(ent.Owner, ent.Comp.SolutionId, out _, out solution))
{
component.Shots = shots;
DirtyField(uid, component, nameof(SolutionAmmoProviderComponent.Shots));
component.MaxShots = maxShots;
DirtyField(uid, component, nameof(SolutionAmmoProviderComponent.MaxShots));
ent.Comp.Shots = shots;
DirtyField(ent.AsNullable(), nameof(SolutionAmmoProviderComponent.Shots));
ent.Comp.MaxShots = maxShots;
DirtyField(ent.AsNullable(), nameof(SolutionAmmoProviderComponent.MaxShots));
return;
}
shots = (int) (solution.Volume / component.FireCost);
maxShots = (int) (solution.MaxVolume / component.FireCost);
shots = (int)(solution.Volume / ent.Comp.FireCost);
maxShots = (int)(solution.MaxVolume / ent.Comp.FireCost);
component.Shots = shots;
DirtyField(uid, component, nameof(SolutionAmmoProviderComponent.Shots));
ent.Comp.Shots = shots;
DirtyField(ent.AsNullable(), nameof(SolutionAmmoProviderComponent.Shots));
component.MaxShots = maxShots;
DirtyField(uid, component, nameof(SolutionAmmoProviderComponent.MaxShots));
ent.Comp.MaxShots = maxShots;
DirtyField(ent.AsNullable(), nameof(SolutionAmmoProviderComponent.MaxShots));
UpdateSolutionAppearance(uid, component);
UpdateSolutionAppearance(ent);
}
protected override (EntityUid Entity, IShootable) GetSolutionShot(EntityUid uid, SolutionAmmoProviderComponent component, EntityCoordinates position)
protected override (EntityUid Entity, IShootable) GetSolutionShot(Entity<SolutionAmmoProviderComponent> ent, EntityCoordinates position)
{
var (ent, shootable) = base.GetSolutionShot(uid, component, position);
var (shot, shootable) = base.GetSolutionShot(ent, position);
if (!_solutionContainer.TryGetSolution(uid, component.SolutionId, out var solution, out _))
return (ent, shootable);
if (!_solutionContainer.TryGetSolution(ent.Owner, ent.Comp.SolutionId, out var solution, out _))
return (shot, shootable);
var newSolution = _solutionContainer.SplitSolution(solution.Value, component.FireCost);
var newSolution = _solutionContainer.SplitSolution(solution.Value, ent.Comp.FireCost);
if (newSolution.Volume <= FixedPoint2.Zero)
return (ent, shootable);
return (shot, shootable);
if (TryComp<AppearanceComponent>(ent, out var appearance))
if (TryComp<AppearanceComponent>(shot, out var appearance))
{
Appearance.SetData(ent, VaporVisuals.Color, newSolution.GetColor(ProtoManager).WithAlpha(1f), appearance);
Appearance.SetData(ent, VaporVisuals.State, true, appearance);
Appearance.SetData(shot, VaporVisuals.Color, newSolution.GetColor(ProtoManager).WithAlpha(1f), appearance);
Appearance.SetData(shot, VaporVisuals.State, true, appearance);
}
// Add the solution to the vapor and actually send the thing
if (_solutionContainer.TryGetSolution(ent, VaporComponent.SolutionName, out var vaporSolution, out _))
if (_solutionContainer.TryGetSolution(shot, VaporComponent.SolutionName, out var vaporSolution, out _))
{
_solutionContainer.TryAddSolution(vaporSolution.Value, newSolution);
}
return (ent, shootable);
return (shot, shootable);
}
}

View File

@@ -33,30 +33,30 @@ public sealed partial class GunSystem : SharedGunSystem
SubscribeLocalEvent<BallisticAmmoProviderComponent, PriceCalculationEvent>(OnBallisticPrice);
}
private void OnBallisticPrice(EntityUid uid, BallisticAmmoProviderComponent component, ref PriceCalculationEvent args)
private void OnBallisticPrice(Entity<BallisticAmmoProviderComponent> ent, ref PriceCalculationEvent args)
{
if (string.IsNullOrEmpty(component.Proto) || component.UnspawnedCount == 0)
if (string.IsNullOrEmpty(ent.Comp.Proto) || ent.Comp.UnspawnedCount == 0)
return;
if (!ProtoManager.TryIndex<EntityPrototype>(component.Proto, out var proto))
if (!ProtoManager.TryIndex<EntityPrototype>(ent.Comp.Proto, out var proto))
{
Log.Error($"Unable to find fill prototype for price on {component.Proto} on {ToPrettyString(uid)}");
Log.Error($"Unable to find fill prototype for price on {ent.Comp.Proto} on {ToPrettyString(ent)}");
return;
}
// Probably good enough for most.
var price = _pricing.GetEstimatedPrice(proto);
args.Price += price * component.UnspawnedCount;
args.Price += price * ent.Comp.UnspawnedCount;
}
public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? Entity, IShootable Shootable)> ammo,
public override void Shoot(Entity<GunComponent> gun, List<(EntityUid? Entity, IShootable Shootable)> ammo,
EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, out bool userImpulse, EntityUid? user = null, bool throwItems = false)
{
userImpulse = true;
if (user != null)
{
var selfEvent = new SelfBeforeGunShotEvent(user.Value, (gunUid, gun), ammo);
var selfEvent = new SelfBeforeGunShotEvent(user.Value, gun, ammo);
RaiseLocalEvent(user.Value, selfEvent);
if (selfEvent.Cancelled)
{
@@ -90,7 +90,7 @@ public sealed partial class GunSystem : SharedGunSystem
// pneumatic cannon doesn't shoot bullets it just throws them, ignore ammo handling
if (throwItems && ent != null)
{
ShootOrThrow(ent.Value, mapDirection, gunVelocity, gun, gunUid, user);
ShootOrThrow(ent.Value, mapDirection, gunVelocity, gun, user);
continue;
}
@@ -117,7 +117,7 @@ public sealed partial class GunSystem : SharedGunSystem
else
{
userImpulse = false;
Audio.PlayPredicted(gun.SoundEmpty, gunUid, user);
Audio.PlayPredicted(gun.Comp.SoundEmpty, gun, user);
}
// Something like ballistic might want to leave it in the container still
@@ -141,22 +141,22 @@ public sealed partial class GunSystem : SharedGunSystem
{
FromCoordinates = fromCoordinates,
ShotDirection = mapDirection.Normalized(),
Gun = gunUid,
Gun = gun,
Shooter = user,
Target = gun.Target,
Target = gun.Comp.Target,
};
RaiseLocalEvent(ent.Value, ref hitscanEv);
Del(ent);
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
Audio.PlayPredicted(gun.Comp.SoundGunshotModified, gun, user);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
RaiseLocalEvent(gunUid, new AmmoShotEvent()
RaiseLocalEvent(gun, new AmmoShotEvent()
{
FiredProjectiles = shotProjectiles,
});
@@ -166,35 +166,35 @@ public sealed partial class GunSystem : SharedGunSystem
if (TryComp<ProjectileSpreadComponent>(ammoEnt, out var ammoSpreadComp))
{
var spreadEvent = new GunGetAmmoSpreadEvent(ammoSpreadComp.Spread);
RaiseLocalEvent(gunUid, ref spreadEvent);
RaiseLocalEvent(gun, ref spreadEvent);
var angles = LinearSpread(mapAngle - spreadEvent.Spread / 2,
mapAngle + spreadEvent.Spread / 2, ammoSpreadComp.Count);
ShootOrThrow(ammoEnt, angles[0].ToVec(), gunVelocity, gun, gunUid, user);
ShootOrThrow(ammoEnt, angles[0].ToVec(), gunVelocity, gun, user);
shotProjectiles.Add(ammoEnt);
for (var i = 1; i < ammoSpreadComp.Count; i++)
{
var newuid = Spawn(ammoSpreadComp.Proto, fromEnt);
ShootOrThrow(newuid, angles[i].ToVec(), gunVelocity, gun, gunUid, user);
ShootOrThrow(newuid, angles[i].ToVec(), gunVelocity, gun, user);
shotProjectiles.Add(newuid);
}
}
else
{
ShootOrThrow(ammoEnt, mapDirection, gunVelocity, gun, gunUid, user);
ShootOrThrow(ammoEnt, mapDirection, gunVelocity, gun, user);
shotProjectiles.Add(ammoEnt);
}
MuzzleFlash(gunUid, ammoComp, mapDirection.ToAngle(), user);
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
MuzzleFlash(gun, ammoComp, mapDirection.ToAngle(), user);
Audio.PlayPredicted(gun.Comp.SoundGunshotModified, gun, user);
}
}
private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVelocity, GunComponent gun, EntityUid gunUid, EntityUid? user)
private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVelocity, Entity<GunComponent> gun, EntityUid? user)
{
if (gun.Target is { } target && !TerminatingOrDeleted(target))
if (gun.Comp.Target is { } target && !TerminatingOrDeleted(target))
{
var targeted = EnsureComp<TargetedProjectileComponent>(uid);
targeted.Target = target;
@@ -206,11 +206,11 @@ public sealed partial class GunSystem : SharedGunSystem
{
RemoveShootable(uid);
// TODO: Someone can probably yeet this a billion miles so need to pre-validate input somewhere up the call stack.
ThrowingSystem.TryThrow(uid, mapDirection, gun.ProjectileSpeedModified, user);
ThrowingSystem.TryThrow(uid, mapDirection, gun.Comp.ProjectileSpeedModified, user);
return;
}
ShootProjectile(uid, mapDirection, gunVelocity, gunUid, user, gun.ProjectileSpeedModified);
ShootProjectile(uid, mapDirection, gunVelocity, gun, user, gun.Comp.ProjectileSpeedModified);
}
/// <summary>

View File

@@ -461,7 +461,7 @@ public abstract class SharedMagicSystem : EntitySystem
return;
if (TryComp<BasicEntityAmmoProviderComponent>(wand, out var basicAmmoComp) && basicAmmoComp.Count != null)
_gunSystem.UpdateBasicEntityAmmoCount(wand.Value, basicAmmoComp.Count.Value + ev.Charge, basicAmmoComp);
_gunSystem.UpdateBasicEntityAmmoCount((wand.Value, basicAmmoComp), basicAmmoComp.Count.Value + ev.Charge);
else if (TryComp<LimitedChargesComponent>(wand, out var charges))
_charges.AddCharges((wand.Value, charges), ev.Charge);
}

View File

@@ -13,27 +13,27 @@ public sealed partial class ChamberMagazineAmmoProviderComponent : MagazineAmmoP
/// <summary>
/// If the gun has a bolt and whether that bolt is closed. Firing is impossible
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("boltClosed"), AutoNetworkedField]
[DataField, AutoNetworkedField]
public bool? BoltClosed = false;
/// <summary>
/// Does the gun automatically open and close the bolt upon shooting.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("autoCycle"), AutoNetworkedField]
[DataField, AutoNetworkedField]
public bool AutoCycle = true;
/// <summary>
/// Can the gun be racked, which opens and then instantly closes the bolt to cycle a round.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("canRack"), AutoNetworkedField]
[DataField, AutoNetworkedField]
public bool CanRack = true;
[ViewVariables(VVAccess.ReadWrite), DataField("soundBoltClosed"), AutoNetworkedField]
[DataField("soundBoltClosed"), AutoNetworkedField]
public SoundSpecifier? BoltClosedSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Bolt/rifle_bolt_closed.ogg");
[ViewVariables(VVAccess.ReadWrite), DataField("soundBoltOpened"), AutoNetworkedField]
[DataField("soundBoltOpened"), AutoNetworkedField]
public SoundSpecifier? BoltOpenedSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Bolt/rifle_bolt_open.ogg");
[ViewVariables(VVAccess.ReadWrite), DataField("soundRack"), AutoNetworkedField]
[DataField("soundRack"), AutoNetworkedField]
public SoundSpecifier? RackSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Cock/ltrifle_cock.ogg");
}

View File

@@ -225,7 +225,7 @@ public sealed partial class GunComponent : Component
/// When the gun is next available to be shot.
/// Can be set multiple times in a single tick due to guns firing faster than a single tick time.
/// </summary>
[DataField(customTypeSerializer:typeof(TimeOffsetSerializer))]
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoNetworkedField]
[AutoPausedField]
public TimeSpan NextFire = TimeSpan.Zero;

View File

@@ -6,7 +6,7 @@ namespace Content.Shared.Weapons.Ranged.Components;
/// This component modifies the spread of the gun it is attached to.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class GunSpreadModifierComponent: Component
public sealed partial class GunSpreadModifierComponent : Component
{
/// <summary>
/// A scalar value multiplied by the spread built into the ammo itself.

View File

@@ -11,12 +11,12 @@ namespace Content.Shared.Weapons.Ranged;
[Access(typeof(SharedGunSystem))]
public partial class MagazineAmmoProviderComponent : AmmoProviderComponent
{
[ViewVariables(VVAccess.ReadWrite), DataField("soundAutoEject")]
[DataField]
public SoundSpecifier? SoundAutoEject = new SoundPathSpecifier("/Audio/Weapons/Guns/EmptyAlarm/smg_empty_alarm.ogg");
/// <summary>
/// Should the magazine automatically eject when empty.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("autoEject")]
[DataField]
public bool AutoEject = false;
}

View File

@@ -3,7 +3,7 @@ using Robust.Shared.Serialization;
namespace Content.Shared.Weapons.Ranged.Events;
/// <summary>
/// Raised on the client to request it would like to stop hooting.
/// Raised on the client to request it would like to stop shooting.
/// </summary>
[Serializable, NetSerializable]
public sealed class RequestStopShootEvent : EntityEventArgs

View File

@@ -28,14 +28,14 @@ public sealed class ActionGunSystem : EntitySystem
private void OnShutdown(Entity<ActionGunComponent> ent, ref ComponentShutdown args)
{
if (ent.Comp.Gun is {} gun)
if (ent.Comp.Gun is { } gun)
QueueDel(gun);
}
private void OnShoot(Entity<ActionGunComponent> ent, ref ActionGunShootEvent args)
{
if (TryComp<GunComponent>(ent.Comp.Gun, out var gun))
_gun.AttemptShoot(ent, ent.Comp.Gun.Value, gun, args.Target);
_gun.AttemptShoot(ent, (ent.Comp.Gun.Value, gun), args.Target);
}
}

View File

@@ -1,4 +1,3 @@
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Database;
using Content.Shared.Examine;
@@ -6,18 +5,17 @@ using Content.Shared.Interaction.Events;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Events;
using Robust.Shared.Prototypes;
namespace Content.Shared.Weapons.Ranged.Systems;
public sealed class BatteryWeaponFireModesSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly SharedGunSystem _gun = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
public override void Initialize()
{
@@ -28,12 +26,12 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem
SubscribeLocalEvent<BatteryWeaponFireModesComponent, ExaminedEvent>(OnExamined);
}
private void OnExamined(EntityUid uid, BatteryWeaponFireModesComponent component, ExaminedEvent args)
private void OnExamined(Entity<BatteryWeaponFireModesComponent> ent, ref ExaminedEvent args)
{
if (component.FireModes.Count < 2)
if (ent.Comp.FireModes.Count < 2)
return;
var fireMode = GetMode(component);
var fireMode = GetMode(ent.Comp);
if (!_prototypeManager.TryIndex<EntityPrototype>(fireMode.Prototype, out var proto))
return;
@@ -73,7 +71,7 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem
DoContactInteraction = true,
Act = () =>
{
TrySetFireMode(uid, component, index, args.User);
TrySetFireMode((uid, component), index, args.User);
}
};
@@ -81,60 +79,60 @@ public sealed class BatteryWeaponFireModesSystem : EntitySystem
}
}
private void OnUseInHandEvent(EntityUid uid, BatteryWeaponFireModesComponent component, UseInHandEvent args)
private void OnUseInHandEvent(Entity<BatteryWeaponFireModesComponent> ent, ref UseInHandEvent args)
{
if(args.Handled)
if (args.Handled)
return;
args.Handled = true;
TryCycleFireMode(uid, component, args.User);
TryCycleFireMode(ent, args.User);
}
public void TryCycleFireMode(EntityUid uid, BatteryWeaponFireModesComponent component, EntityUid? user = null)
public void TryCycleFireMode(Entity<BatteryWeaponFireModesComponent> ent, EntityUid? user = null)
{
if (component.FireModes.Count < 2)
if (ent.Comp.FireModes.Count < 2)
return;
var index = (component.CurrentFireMode + 1) % component.FireModes.Count;
TrySetFireMode(uid, component, index, user);
var index = (ent.Comp.CurrentFireMode + 1) % ent.Comp.FireModes.Count;
TrySetFireMode(ent, index, user);
}
public bool TrySetFireMode(EntityUid uid, BatteryWeaponFireModesComponent component, int index, EntityUid? user = null)
public bool TrySetFireMode(Entity<BatteryWeaponFireModesComponent> ent, int index, EntityUid? user = null)
{
if (index < 0 || index >= component.FireModes.Count)
if (index < 0 || index >= ent.Comp.FireModes.Count)
return false;
if (user != null && !_accessReaderSystem.IsAllowed(user.Value, uid))
if (user != null && !_accessReaderSystem.IsAllowed(user.Value, ent))
return false;
SetFireMode(uid, component, index, user);
SetFireMode(ent, index, user);
return true;
}
private void SetFireMode(EntityUid uid, BatteryWeaponFireModesComponent component, int index, EntityUid? user = null)
private void SetFireMode(Entity<BatteryWeaponFireModesComponent> ent, int index, EntityUid? user = null)
{
var fireMode = component.FireModes[index];
component.CurrentFireMode = index;
Dirty(uid, component);
var fireMode = ent.Comp.FireModes[index];
ent.Comp.CurrentFireMode = index;
Dirty(ent);
if (_prototypeManager.TryIndex<EntityPrototype>(fireMode.Prototype, out var prototype))
{
if (TryComp<AppearanceComponent>(uid, out var appearance))
_appearanceSystem.SetData(uid, BatteryWeaponFireModeVisuals.State, prototype.ID, appearance);
if (TryComp<AppearanceComponent>(ent, out var appearance))
_appearanceSystem.SetData(ent, BatteryWeaponFireModeVisuals.State, prototype.ID, appearance);
if (user != null)
_popupSystem.PopupClient(Loc.GetString("gun-set-fire-mode-popup", ("mode", prototype.Name)), uid, user.Value);
_popupSystem.PopupClient(Loc.GetString("gun-set-fire-mode-popup", ("mode", prototype.Name)), ent, user.Value);
}
if (TryComp(uid, out BatteryAmmoProviderComponent? batteryAmmoProviderComponent))
if (TryComp(ent, out BatteryAmmoProviderComponent? batteryAmmoProviderComponent))
{
batteryAmmoProviderComponent.Prototype = fireMode.Prototype;
batteryAmmoProviderComponent.FireCost = fireMode.FireCost;
Dirty(uid, batteryAmmoProviderComponent);
Dirty(ent, batteryAmmoProviderComponent);
_gun.UpdateShots((uid, batteryAmmoProviderComponent));
_gun.UpdateShots((ent, batteryAmmoProviderComponent));
}
}
}

View File

@@ -4,8 +4,7 @@ using Content.Shared.Weapons.Ranged.Events;
namespace Content.Shared.Weapons.Ranged.Systems;
public sealed class GunSpreadModifierSystem: EntitySystem
public sealed class GunSpreadModifierSystem : EntitySystem
{
public override void Initialize()
{
@@ -14,14 +13,14 @@ public sealed class GunSpreadModifierSystem: EntitySystem
SubscribeLocalEvent<GunSpreadModifierComponent, ExaminedEvent>(OnExamine);
}
private void OnGunGetAmmoSpread(EntityUid uid, GunSpreadModifierComponent comp, ref GunGetAmmoSpreadEvent args)
private void OnGunGetAmmoSpread(Entity<GunSpreadModifierComponent> ent, ref GunGetAmmoSpreadEvent args)
{
args.Spread *= comp.Spread;
args.Spread *= ent.Comp.Spread;
}
private void OnExamine(EntityUid uid, GunSpreadModifierComponent comp, ExaminedEvent args)
private void OnExamine(Entity<GunSpreadModifierComponent> ent, ref ExaminedEvent args)
{
var percentage = Math.Round(comp.Spread * 100);
var percentage = Math.Round(ent.Comp.Spread * 100);
var loc = percentage < 100 ? "examine-gun-spread-modifier-reduction" : "examine-gun-spread-modifier-increase";
percentage = percentage < 100 ? 100 - percentage : percentage - 100;
var msg = Loc.GetString(loc, ("percentage", percentage));

View File

@@ -1,11 +1,8 @@
using Content.Shared.Examine;
using Content.Shared.Weapons.Ranged.Components;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Shared.Weapons.Ranged.Systems;
@@ -13,9 +10,9 @@ public sealed class RechargeBasicEntityAmmoSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly INetManager _netManager = default!;
[Dependency] private readonly MetaDataSystem _metadata = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedGunSystem _gun = default!;
[Dependency] private readonly MetaDataSystem _metadata = default!;
public override void Initialize()
{
@@ -38,7 +35,7 @@ public sealed class RechargeBasicEntityAmmoSystem : EntitySystem
if (recharge.NextCharge > _timing.CurTime)
continue;
if (_gun.UpdateBasicEntityAmmoCount(uid, ammo.Count.Value + 1, ammo))
if (_gun.UpdateBasicEntityAmmoCount((uid, ammo), ammo.Count.Value + 1))
{
// We don't predict this because occasionally on client it may not play.
// PlayPredicted will still be predicted on the client.
@@ -58,38 +55,38 @@ public sealed class RechargeBasicEntityAmmoSystem : EntitySystem
}
}
private void OnInit(EntityUid uid, RechargeBasicEntityAmmoComponent component, MapInitEvent args)
private void OnInit(Entity<RechargeBasicEntityAmmoComponent> ent, ref MapInitEvent args)
{
component.NextCharge = _timing.CurTime;
Dirty(uid, component);
ent.Comp.NextCharge = _timing.CurTime;
Dirty(ent);
}
private void OnExamined(EntityUid uid, RechargeBasicEntityAmmoComponent component, ExaminedEvent args)
private void OnExamined(Entity<RechargeBasicEntityAmmoComponent> ent, ref ExaminedEvent args)
{
if (!component.ShowExamineText)
if (!ent.Comp.ShowExamineText)
return;
if (!TryComp<BasicEntityAmmoProviderComponent>(uid, out var ammo)
if (!TryComp<BasicEntityAmmoProviderComponent>(ent, out var ammo)
|| ammo.Count == ammo.Capacity ||
component.NextCharge == null)
ent.Comp.NextCharge == null)
{
args.PushMarkup(Loc.GetString("recharge-basic-entity-ammo-full"));
return;
}
var timeLeft = component.NextCharge + _metadata.GetPauseTime(uid) - _timing.CurTime;
var timeLeft = ent.Comp.NextCharge + _metadata.GetPauseTime(ent) - _timing.CurTime;
args.PushMarkup(Loc.GetString("recharge-basic-entity-ammo-can-recharge", ("seconds", Math.Round(timeLeft.Value.TotalSeconds, 1))));
}
public void Reset(EntityUid uid, RechargeBasicEntityAmmoComponent? recharge = null)
public void Reset(Entity<RechargeBasicEntityAmmoComponent?> ent)
{
if (!Resolve(uid, ref recharge, false))
if (!Resolve(ent, ref ent.Comp, false))
return;
if (recharge.NextCharge == null || recharge.NextCharge < _timing.CurTime)
if (ent.Comp.NextCharge == null || ent.Comp.NextCharge < _timing.CurTime)
{
recharge.NextCharge = _timing.CurTime + TimeSpan.FromSeconds(recharge.RechargeCooldown);
Dirty(uid, recharge);
ent.Comp.NextCharge = _timing.CurTime + TimeSpan.FromSeconds(ent.Comp.RechargeCooldown);
Dirty(ent);
}
}
}

View File

@@ -16,19 +16,19 @@ public sealed class RechargeCycleAmmoSystem : EntitySystem
SubscribeLocalEvent<RechargeCycleAmmoComponent, ActivateInWorldEvent>(OnRechargeCycled);
}
private void OnRechargeCycled(EntityUid uid, RechargeCycleAmmoComponent component, ActivateInWorldEvent args)
private void OnRechargeCycled(Entity<RechargeCycleAmmoComponent> ent, ref ActivateInWorldEvent args)
{
if (!args.Complex)
return;
if (!TryComp<BasicEntityAmmoProviderComponent>(uid, out var basic) || args.Handled)
if (!TryComp<BasicEntityAmmoProviderComponent>(ent, out var basic) || args.Handled)
return;
if (basic.Count >= basic.Capacity || basic.Count == null)
return;
_gun.UpdateBasicEntityAmmoCount(uid, basic.Count.Value + 1, basic);
Dirty(uid, basic);
_gun.UpdateBasicEntityAmmoCount((ent, basic), basic.Count.Value + 1);
Dirty(ent, basic);
args.Handled = true;
}
}

View File

@@ -24,24 +24,24 @@ public abstract class SharedFlyBySoundSystem : EntitySystem
SubscribeLocalEvent<FlyBySoundComponent, ComponentShutdown>(OnShutdown);
}
private void OnStartup(EntityUid uid, FlyBySoundComponent component, ComponentStartup args)
private void OnStartup(Entity<FlyBySoundComponent> ent, ref ComponentStartup args)
{
if (!TryComp<PhysicsComponent>(uid, out var body))
if (!TryComp<PhysicsComponent>(ent, out var body))
return;
var shape = new PhysShapeCircle(component.Range);
var shape = new PhysShapeCircle(ent.Comp.Range);
_fixtures.TryCreateFixture(uid, shape, FlyByFixture, collisionLayer: (int) CollisionGroup.MobMask, hard: false, body: body);
_fixtures.TryCreateFixture(ent, shape, FlyByFixture, collisionLayer: (int)CollisionGroup.MobMask, hard: false, body: body);
}
private void OnShutdown(EntityUid uid, FlyBySoundComponent component, ComponentShutdown args)
private void OnShutdown(Entity<FlyBySoundComponent> ent, ref ComponentShutdown args)
{
if (!TryComp<PhysicsComponent>(uid, out var body) ||
MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating)
if (!TryComp<PhysicsComponent>(ent, out var body) ||
MetaData(ent).EntityLifeStage >= EntityLifeStage.Terminating)
{
return;
}
_fixtures.DestroyFixture(uid, FlyByFixture, body: body);
_fixtures.DestroyFixture(ent, FlyByFixture, body: body);
}
}

View File

@@ -4,8 +4,8 @@ namespace Content.Shared.Weapons.Ranged.Systems;
public partial class SharedGunSystem
{
public void SetEnabled(EntityUid uid, AutoShootGunComponent component, bool status)
public void SetEnabled(Entity<AutoShootGunComponent> ent, bool status)
{
component.Enabled = status;
ent.Comp.Enabled = status;
}
}

View File

@@ -36,26 +36,26 @@ public abstract partial class SharedGunSystem
SubscribeLocalEvent<BallisticAmmoSelfRefillerComponent, EmpPulseEvent>(OnRefillerEmpPulsed);
}
private void OnBallisticRefillerMapInit(Entity<BallisticAmmoSelfRefillerComponent> entity, ref MapInitEvent args)
private void OnBallisticRefillerMapInit(Entity<BallisticAmmoSelfRefillerComponent> entity, ref MapInitEvent _)
{
entity.Comp.NextAutoRefill = Timing.CurTime + entity.Comp.AutoRefillRate;
}
private void OnBallisticUse(EntityUid uid, BallisticAmmoProviderComponent component, UseInHandEvent args)
private void OnBallisticUse(Entity<BallisticAmmoProviderComponent> ent, ref UseInHandEvent args)
{
if (args.Handled)
return;
ManualCycle(uid, component, TransformSystem.GetMapCoordinates(uid), args.User);
ManualCycle(ent, TransformSystem.GetMapCoordinates(ent), args.User);
args.Handled = true;
}
private void OnBallisticInteractUsing(EntityUid uid, BallisticAmmoProviderComponent component, InteractUsingEvent args)
private void OnBallisticInteractUsing(Entity<BallisticAmmoProviderComponent> ent, ref InteractUsingEvent args)
{
if (args.Handled)
return;
if (TryBallisticInsert((uid, component), args.Used, args.User))
if (TryBallisticInsert(ent, args.Used, args.User))
args.Handled = true;
}
@@ -168,65 +168,65 @@ public abstract partial class SharedGunSystem
{
Text = Loc.GetString("gun-ballistic-cycle"),
Disabled = GetBallisticShots(component) == 0,
Act = () => ManualCycle(uid, component, TransformSystem.GetMapCoordinates(uid), args.User),
Act = () => ManualCycle((uid, component), TransformSystem.GetMapCoordinates(uid), args.User),
});
}
}
private void OnBallisticExamine(EntityUid uid, BallisticAmmoProviderComponent component, ExaminedEvent args)
private void OnBallisticExamine(Entity<BallisticAmmoProviderComponent> ent, ref ExaminedEvent args)
{
if (!args.IsInDetailsRange)
return;
args.PushMarkup(Loc.GetString("gun-magazine-examine", ("color", AmmoExamineColor), ("count", GetBallisticShots(component))));
args.PushMarkup(Loc.GetString("gun-magazine-examine", ("color", AmmoExamineColor), ("count", GetBallisticShots(ent.Comp))));
}
private void ManualCycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates, EntityUid? user = null, GunComponent? gunComp = null)
private void ManualCycle(Entity<BallisticAmmoProviderComponent> ent, MapCoordinates coordinates, EntityUid? user = null, GunComponent? gunComp = null)
{
if (!component.Cycleable)
if (!ent.Comp.Cycleable)
return;
// Reset shotting for cycling
if (Resolve(uid, ref gunComp, false) &&
if (Resolve(ent, ref gunComp, false) &&
gunComp is { FireRateModified: > 0f } &&
!Paused(uid))
!Paused(ent))
{
gunComp.NextFire = Timing.CurTime + TimeSpan.FromSeconds(1 / gunComp.FireRateModified);
DirtyField(uid, gunComp, nameof(GunComponent.NextFire));
DirtyField(ent, gunComp, nameof(GunComponent.NextFire));
}
Audio.PlayPredicted(component.SoundRack, uid, user);
Audio.PlayPredicted(ent.Comp.SoundRack, ent, user);
var shots = GetBallisticShots(component);
Cycle(uid, component, coordinates);
var shots = GetBallisticShots(ent.Comp);
Cycle(ent, coordinates);
var text = Loc.GetString(shots == 0 ? "gun-ballistic-cycled-empty" : "gun-ballistic-cycled");
Popup(text, uid, user);
UpdateBallisticAppearance(uid, component);
UpdateAmmoCount(uid);
Popup(text, ent, user);
UpdateBallisticAppearance(ent);
UpdateAmmoCount(ent);
}
protected abstract void Cycle(EntityUid uid, BallisticAmmoProviderComponent component, MapCoordinates coordinates);
protected abstract void Cycle(Entity<BallisticAmmoProviderComponent> ent, MapCoordinates coordinates);
private void OnBallisticInit(EntityUid uid, BallisticAmmoProviderComponent component, ComponentInit args)
private void OnBallisticInit(Entity<BallisticAmmoProviderComponent> ent, ref ComponentInit args)
{
component.Container = Containers.EnsureContainer<Container>(uid, "ballistic-ammo");
ent.Comp.Container = Containers.EnsureContainer<Container>(ent, "ballistic-ammo");
// TODO: This is called twice though we need to support loading appearance data (and we need to call it on MapInit
// to ensure it's correct).
UpdateBallisticAppearance(uid, component);
UpdateBallisticAppearance(ent);
}
private void OnBallisticMapInit(EntityUid uid, BallisticAmmoProviderComponent component, MapInitEvent args)
private void OnBallisticMapInit(Entity<BallisticAmmoProviderComponent> ent, ref MapInitEvent args)
{
// TODO this should be part of the prototype, not set on map init.
// Alternatively, just track spawned count, instead of unspawned count.
if (component.Proto != null)
if (ent.Comp.Proto != null)
{
component.UnspawnedCount = Math.Max(0, component.Capacity - component.Container.ContainedEntities.Count);
UpdateBallisticAppearance(uid, component);
DirtyField(uid, component, nameof(BallisticAmmoProviderComponent.UnspawnedCount));
ent.Comp.UnspawnedCount = Math.Max(0, ent.Comp.Capacity - ent.Comp.Container.ContainedEntities.Count);
UpdateBallisticAppearance(ent);
DirtyField(ent.AsNullable(), nameof(BallisticAmmoProviderComponent.UnspawnedCount));
}
}
@@ -235,43 +235,43 @@ public abstract partial class SharedGunSystem
return component.Entities.Count + component.UnspawnedCount;
}
private void OnBallisticTakeAmmo(EntityUid uid, BallisticAmmoProviderComponent component, TakeAmmoEvent args)
private void OnBallisticTakeAmmo(Entity<BallisticAmmoProviderComponent> ent, ref TakeAmmoEvent args)
{
for (var i = 0; i < args.Shots; i++)
{
EntityUid? ammoEntity = null;
if (component.Entities.Count > 0)
if (ent.Comp.Entities.Count > 0)
{
var existingEnt = component.Entities[^1];
component.Entities.RemoveAt(component.Entities.Count - 1);
DirtyField(uid, component, nameof(BallisticAmmoProviderComponent.Entities));
Containers.Remove(existingEnt, component.Container);
var existingEnt = ent.Comp.Entities[^1];
ent.Comp.Entities.RemoveAt(ent.Comp.Entities.Count - 1);
DirtyField(ent.AsNullable(), nameof(BallisticAmmoProviderComponent.Entities));
Containers.Remove(existingEnt, ent.Comp.Container);
ammoEntity = existingEnt;
}
else if (component.UnspawnedCount > 0)
else if (ent.Comp.UnspawnedCount > 0)
{
component.UnspawnedCount--;
DirtyField(uid, component, nameof(BallisticAmmoProviderComponent.UnspawnedCount));
ammoEntity = Spawn(component.Proto, args.Coordinates);
ent.Comp.UnspawnedCount--;
DirtyField(ent.AsNullable(), nameof(BallisticAmmoProviderComponent.UnspawnedCount));
ammoEntity = Spawn(ent.Comp.Proto, args.Coordinates);
}
if (ammoEntity is { } ent)
if (ammoEntity is not { } ammoEnt)
continue;
args.Ammo.Add((ammoEnt, EnsureShootable(ammoEnt)));
if (TryComp<BallisticAmmoSelfRefillerComponent>(ent, out var refiller))
{
args.Ammo.Add((ent, EnsureShootable(ent)));
if (TryComp<BallisticAmmoSelfRefillerComponent>(uid, out var refiller))
{
PauseSelfRefill((uid, refiller));
}
PauseSelfRefill((ent, refiller));
}
}
UpdateBallisticAppearance(uid, component);
UpdateBallisticAppearance(ent);
}
private void OnBallisticAmmoCount(EntityUid uid, BallisticAmmoProviderComponent component, ref GetAmmoCountEvent args)
private void OnBallisticAmmoCount(Entity<BallisticAmmoProviderComponent> ent, ref GetAmmoCountEvent args)
{
args.Count = GetBallisticShots(component);
args.Capacity = component.Capacity;
args.Count = GetBallisticShots(ent.Comp);
args.Capacity = ent.Comp.Capacity;
}
/// <summary>
@@ -334,20 +334,20 @@ public abstract partial class SharedGunSystem
Audio.PlayPredicted(entity.Comp.SoundInsert, entity, user);
}
UpdateBallisticAppearance(entity, entity.Comp);
UpdateBallisticAppearance(entity);
UpdateAmmoCount(entity);
DirtyField(entity.AsNullable(), nameof(BallisticAmmoProviderComponent.Entities));
return true;
}
public void UpdateBallisticAppearance(EntityUid uid, BallisticAmmoProviderComponent component)
public void UpdateBallisticAppearance(Entity<BallisticAmmoProviderComponent> ent)
{
if (!Timing.IsFirstTimePredicted || !TryComp<AppearanceComponent>(uid, out var appearance))
if (!Timing.IsFirstTimePredicted || !TryComp<AppearanceComponent>(ent, out var appearance))
return;
Appearance.SetData(uid, AmmoVisuals.AmmoCount, GetBallisticShots(component), appearance);
Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.Capacity, appearance);
Appearance.SetData(ent, AmmoVisuals.AmmoCount, GetBallisticShots(ent.Comp), appearance);
Appearance.SetData(ent, AmmoVisuals.AmmoMax, ent.Comp.Capacity, appearance);
}
public void SetBallisticUnspawned(Entity<BallisticAmmoProviderComponent> entity, int count)
@@ -356,7 +356,7 @@ public abstract partial class SharedGunSystem
return;
entity.Comp.UnspawnedCount = count;
UpdateBallisticAppearance(entity.Owner, entity.Comp);
UpdateBallisticAppearance(entity);
UpdateAmmoCount(entity.Owner);
Dirty(entity);
}

View File

@@ -1,6 +1,5 @@
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Events;
using Robust.Shared.GameStates;
namespace Content.Shared.Weapons.Ranged.Systems;
@@ -13,75 +12,70 @@ public abstract partial class SharedGunSystem
SubscribeLocalEvent<BasicEntityAmmoProviderComponent, GetAmmoCountEvent>(OnBasicEntityAmmoCount);
}
private void OnBasicEntityMapInit(EntityUid uid, BasicEntityAmmoProviderComponent component, MapInitEvent args)
private void OnBasicEntityMapInit(Entity<BasicEntityAmmoProviderComponent> ent, ref MapInitEvent args)
{
if (component.Count is null)
if (ent.Comp.Count is null)
{
component.Count = component.Capacity;
Dirty(uid, component);
ent.Comp.Count = ent.Comp.Capacity;
Dirty(ent);
}
UpdateBasicEntityAppearance(uid, component);
UpdateBasicEntityAppearance(ent);
}
private void OnBasicEntityTakeAmmo(EntityUid uid, BasicEntityAmmoProviderComponent component, TakeAmmoEvent args)
private void OnBasicEntityTakeAmmo(Entity<BasicEntityAmmoProviderComponent> ent, ref TakeAmmoEvent args)
{
for (var i = 0; i < args.Shots; i++)
{
if (component.Count <= 0)
if (ent.Comp.Count <= 0)
return;
if (component.Count != null)
{
component.Count--;
}
if (ent.Comp.Count != null)
ent.Comp.Count--;
var ent = Spawn(component.Proto, args.Coordinates);
args.Ammo.Add((ent, EnsureShootable(ent)));
var ammoEnt = Spawn(ent.Comp.Proto, args.Coordinates);
args.Ammo.Add((ammoEnt, EnsureShootable(ammoEnt)));
}
_recharge.Reset(uid);
UpdateBasicEntityAppearance(uid, component);
Dirty(uid, component);
_recharge.Reset(ent.Owner);
UpdateBasicEntityAppearance(ent);
Dirty(ent);
}
private void OnBasicEntityAmmoCount(EntityUid uid, BasicEntityAmmoProviderComponent component, ref GetAmmoCountEvent args)
private void OnBasicEntityAmmoCount(Entity<BasicEntityAmmoProviderComponent> ent, ref GetAmmoCountEvent args)
{
args.Capacity = component.Capacity ?? int.MaxValue;
args.Count = component.Count ?? int.MaxValue;
args.Capacity = ent.Comp.Capacity ?? int.MaxValue;
args.Count = ent.Comp.Count ?? int.MaxValue;
}
private void UpdateBasicEntityAppearance(EntityUid uid, BasicEntityAmmoProviderComponent component)
private void UpdateBasicEntityAppearance(Entity<BasicEntityAmmoProviderComponent> ent)
{
if (!Timing.IsFirstTimePredicted || !TryComp<AppearanceComponent>(uid, out var appearance))
if (!Timing.IsFirstTimePredicted || !TryComp<AppearanceComponent>(ent, out var appearance))
return;
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);
Appearance.SetData(ent, AmmoVisuals.HasAmmo, ent.Comp.Count != 0, appearance);
Appearance.SetData(ent, AmmoVisuals.AmmoCount, ent.Comp.Count ?? int.MaxValue, appearance);
Appearance.SetData(ent, AmmoVisuals.AmmoMax, ent.Comp.Capacity ?? int.MaxValue, appearance);
}
#region Public API
public bool ChangeBasicEntityAmmoCount(EntityUid uid, int delta, BasicEntityAmmoProviderComponent? component = null)
public bool ChangeBasicEntityAmmoCount(Entity<BasicEntityAmmoProviderComponent?> ent, int delta)
{
if (!Resolve(uid, ref component, false) || component.Count == null)
if (!Resolve(ent, ref ent.Comp, false) || ent.Comp.Count == null)
return false;
return UpdateBasicEntityAmmoCount(uid, component.Count.Value + delta, component);
return UpdateBasicEntityAmmoCount((ent.Owner, ent.Comp), ent.Comp.Count.Value + delta);
}
public bool UpdateBasicEntityAmmoCount(EntityUid uid, int count, BasicEntityAmmoProviderComponent? component = null)
public bool UpdateBasicEntityAmmoCount(Entity<BasicEntityAmmoProviderComponent?> ent, int count)
{
if (!Resolve(uid, ref component, false))
if (!Resolve(ent, ref ent.Comp, false) || count > ent.Comp.Capacity)
return false;
if (count > component.Capacity)
return false;
component.Count = count;
UpdateBasicEntityAppearance(uid, component);
UpdateAmmoCount(uid);
Dirty(uid, component);
ent.Comp.Count = count;
UpdateBasicEntityAppearance((ent.Owner, ent.Comp));
UpdateAmmoCount(ent);
Dirty(ent);
return true;
}

View File

@@ -26,9 +26,9 @@ public abstract partial class SharedGunSystem
: Loc.GetString("gun-cartridge-unspent"));
}
private void OnCartridgeDamageExamine(EntityUid uid, CartridgeAmmoComponent component, ref DamageExamineEvent args)
private void OnCartridgeDamageExamine(Entity<CartridgeAmmoComponent> ent, ref DamageExamineEvent args)
{
var damageSpec = GetProjectileDamage(component.Prototype);
var damageSpec = GetProjectileDamage(ent.Comp.Prototype);
if (damageSpec == null)
return;

View File

@@ -12,8 +12,6 @@ namespace Content.Shared.Weapons.Ranged.Systems;
public abstract partial class SharedGunSystem
{
protected const string ChamberSlot = "gun_chamber";
protected virtual void InitializeChamberMagazine()
{
SubscribeLocalEvent<ChamberMagazineAmmoProviderComponent, ComponentStartup>(OnChamberStartup);
@@ -42,7 +40,7 @@ public abstract partial class SharedGunSystem
// Appearance data doesn't get serialized and want to make sure this is correct on spawn (regardless of MapInit) so.
if (component.BoltClosed != null)
{
Appearance.SetData(uid, AmmoVisuals.BoltClosed, component.BoltClosed.Value);
Appearance.SetData(uid, AmmoVisuals.BoltClosed, component.BoltClosed.Value);
}
}

View File

@@ -16,7 +16,7 @@ public partial class SharedGunSystem
{
var getConnectedContainerEvent = new GetConnectedContainerEvent();
RaiseLocalEvent(uid, ref getConnectedContainerEvent);
if(!getConnectedContainerEvent.ContainerEntity.HasValue)
if (!getConnectedContainerEvent.ContainerEntity.HasValue)
return;
RaiseLocalEvent(getConnectedContainerEvent.ContainerEntity.Value, args);

View File

@@ -14,10 +14,10 @@ public partial class SharedGunSystem
SubscribeLocalEvent<ContainerAmmoProviderComponent, GetAmmoCountEvent>(OnContainerAmmoCount);
}
private void OnContainerTakeAmmo(EntityUid uid, ContainerAmmoProviderComponent component, TakeAmmoEvent args)
private void OnContainerTakeAmmo(Entity<ContainerAmmoProviderComponent> ent, ref TakeAmmoEvent args)
{
component.ProviderUid ??= uid;
if (!Containers.TryGetContainer(component.ProviderUid.Value, component.Container, out var container))
ent.Comp.ProviderUid ??= ent;
if (!Containers.TryGetContainer(ent.Comp.ProviderUid.Value, ent.Comp.Container, out var container))
return;
for (var i = 0; i < args.Shots; i++)
@@ -25,19 +25,19 @@ public partial class SharedGunSystem
if (!container.ContainedEntities.Any())
break;
var ent = container.ContainedEntities[0];
var ammoEnt = container.ContainedEntities[0];
if (_netManager.IsServer)
Containers.Remove(ent, container);
Containers.Remove(ammoEnt, container);
args.Ammo.Add((ent, EnsureShootable(ent)));
args.Ammo.Add((ammoEnt, EnsureShootable(ammoEnt)));
}
}
private void OnContainerAmmoCount(EntityUid uid, ContainerAmmoProviderComponent component, ref GetAmmoCountEvent args)
private void OnContainerAmmoCount(Entity<ContainerAmmoProviderComponent> ent, ref GetAmmoCountEvent args)
{
component.ProviderUid ??= uid;
if (!Containers.TryGetContainer(component.ProviderUid.Value, component.Container, out var container))
ent.Comp.ProviderUid ??= ent;
if (!Containers.TryGetContainer(ent.Comp.ProviderUid.Value, ent.Comp.Container, out var container))
{
args.Capacity = 0;
args.Count = 0;

View File

@@ -66,7 +66,7 @@ public abstract partial class SharedGunSystem
if (component.SelectedMode == fire)
return;
DebugTools.Assert((component.AvailableModes & fire) != 0x0);
DebugTools.Assert((component.AvailableModes & fire) != 0x0);
component.SelectedMode = fire;
if (!Paused(uid))
@@ -113,7 +113,7 @@ public abstract partial class SharedGunSystem
private void OnGunSelected(EntityUid uid, GunComponent component, HandSelectedEvent args)
{
if (Timing.ApplyingState)
return;
return;
if (component.FireRateModified <= 0)
return;

View File

@@ -8,8 +8,6 @@ namespace Content.Shared.Weapons.Ranged.Systems;
public abstract partial class SharedGunSystem
{
protected const string MagazineSlot = "gun_magazine";
protected virtual void InitializeMagazine()
{
SubscribeLocalEvent<MagazineAmmoProviderComponent, MapInitEvent>(OnMagazineMapInit);

View File

@@ -31,82 +31,82 @@ public partial class SharedGunSystem
SubscribeLocalEvent<RevolverAmmoProviderComponent, UseInHandEvent>(OnRevolverUse);
}
private void OnRevolverUse(EntityUid uid, RevolverAmmoProviderComponent component, UseInHandEvent args)
private void OnRevolverUse(Entity<RevolverAmmoProviderComponent> ent, ref UseInHandEvent args)
{
if (args.Handled)
return;
if (!_useDelay.TryResetDelay(uid))
if (!_useDelay.TryResetDelay(ent))
return;
args.Handled = true;
Cycle(component);
UpdateAmmoCount(uid, prediction: false);
Dirty(uid, component);
Cycle(ent.Comp);
UpdateAmmoCount(ent, prediction: false);
Dirty(ent);
}
private void OnRevolverGetAmmoCount(EntityUid uid, RevolverAmmoProviderComponent component, ref GetAmmoCountEvent args)
private void OnRevolverGetAmmoCount(Entity<RevolverAmmoProviderComponent> ent, ref GetAmmoCountEvent args)
{
args.Count += GetRevolverCount(component);
args.Capacity += component.Capacity;
args.Count += GetRevolverCount(ent.Comp);
args.Capacity += ent.Comp.Capacity;
}
private void OnRevolverInteractUsing(EntityUid uid, RevolverAmmoProviderComponent component, InteractUsingEvent args)
private void OnRevolverInteractUsing(Entity<RevolverAmmoProviderComponent> ent, ref InteractUsingEvent args)
{
if (args.Handled)
return;
if (TryRevolverInsert(uid, component, args.Used, args.User))
if (TryRevolverInsert(ent, args.Used, args.User))
args.Handled = true;
}
private void OnRevolverGetState(EntityUid uid, RevolverAmmoProviderComponent component, ref ComponentGetState args)
private void OnRevolverGetState(Entity<RevolverAmmoProviderComponent> ent, ref ComponentGetState args)
{
args.State = new RevolverAmmoProviderComponentState
{
CurrentIndex = component.CurrentIndex,
AmmoSlots = GetNetEntityList(component.AmmoSlots),
Chambers = component.Chambers,
CurrentIndex = ent.Comp.CurrentIndex,
AmmoSlots = GetNetEntityList(ent.Comp.AmmoSlots),
Chambers = ent.Comp.Chambers,
};
}
private void OnRevolverHandleState(EntityUid uid, RevolverAmmoProviderComponent component, ref ComponentHandleState args)
private void OnRevolverHandleState(Entity<RevolverAmmoProviderComponent> ent, ref ComponentHandleState args)
{
if (args.Current is not RevolverAmmoProviderComponentState state)
return;
var oldIndex = component.CurrentIndex;
component.CurrentIndex = state.CurrentIndex;
component.Chambers = new bool?[state.Chambers.Length];
var oldIndex = ent.Comp.CurrentIndex;
ent.Comp.CurrentIndex = state.CurrentIndex;
ent.Comp.Chambers = new bool?[state.Chambers.Length];
// Need to copy across the state rather than the ref.
for (var i = 0; i < component.AmmoSlots.Count; i++)
for (var i = 0; i < ent.Comp.AmmoSlots.Count; i++)
{
component.AmmoSlots[i] = EnsureEntity<RevolverAmmoProviderComponent>(state.AmmoSlots[i], uid);
component.Chambers[i] = state.Chambers[i];
ent.Comp.AmmoSlots[i] = EnsureEntity<RevolverAmmoProviderComponent>(state.AmmoSlots[i], ent);
ent.Comp.Chambers[i] = state.Chambers[i];
}
// Handle spins
if (oldIndex != state.CurrentIndex)
{
UpdateAmmoCount(uid, prediction: false);
UpdateAmmoCount(ent, prediction: false);
}
}
public bool TryRevolverInsert(EntityUid revolverUid, RevolverAmmoProviderComponent component, EntityUid uid, EntityUid? user)
public bool TryRevolverInsert(Entity<RevolverAmmoProviderComponent> ent, EntityUid insertEnt, EntityUid? user)
{
if (_whitelistSystem.IsWhitelistFail(component.Whitelist, uid))
if (_whitelistSystem.IsWhitelistFail(ent.Comp.Whitelist, insertEnt))
return false;
// If it's a speedloader try to get ammo from it.
if (HasComp<SpeedLoaderComponent>(uid))
if (HasComp<SpeedLoaderComponent>(insertEnt))
{
var freeSlots = 0;
for (var i = 0; i < component.Capacity; i++)
for (var i = 0; i < ent.Comp.Capacity; i++)
{
if (component.AmmoSlots[i] != null || component.Chambers[i] != null)
if (ent.Comp.AmmoSlots[i] != null || ent.Comp.Chambers[i] != null)
continue;
freeSlots++;
@@ -114,94 +114,94 @@ public partial class SharedGunSystem
if (freeSlots == 0)
{
Popup(Loc.GetString("gun-revolver-full"), revolverUid, user);
Popup(Loc.GetString("gun-revolver-full"), ent, user);
return false;
}
var xformQuery = GetEntityQuery<TransformComponent>();
var xform = xformQuery.GetComponent(uid);
var xform = xformQuery.GetComponent(insertEnt);
var ammo = new List<(EntityUid? Entity, IShootable Shootable)>(freeSlots);
var ev = new TakeAmmoEvent(freeSlots, ammo, xform.Coordinates, user);
RaiseLocalEvent(uid, ev);
RaiseLocalEvent(insertEnt, ev);
if (ev.Ammo.Count == 0)
{
Popup(Loc.GetString("gun-speedloader-empty"), revolverUid, user);
Popup(Loc.GetString("gun-speedloader-empty"), ent, user);
return false;
}
for (var i = 0; i < component.Capacity; i++)
for (var i = 0; i < ent.Comp.Capacity; i++)
{
var index = (component.CurrentIndex + i) % component.Capacity;
var index = (ent.Comp.CurrentIndex + i) % ent.Comp.Capacity;
if (component.AmmoSlots[index] != null ||
component.Chambers[index] != null)
if (ent.Comp.AmmoSlots[index] != null ||
ent.Comp.Chambers[index] != null)
{
continue;
}
var ent = ev.Ammo.Last().Entity;
var ammoEnt = ev.Ammo.Last().Entity;
ev.Ammo.RemoveAt(ev.Ammo.Count - 1);
if (ent == null)
if (ammoEnt == null)
{
Log.Error($"Tried to load hitscan into a revolver which is unsupported");
continue;
}
component.AmmoSlots[index] = ent.Value;
Containers.Insert(ent.Value, component.AmmoContainer);
SetChamber(index, component, uid);
ent.Comp.AmmoSlots[index] = ammoEnt.Value;
Containers.Insert(ammoEnt.Value, ent.Comp.AmmoContainer);
SetChamber(ent, insertEnt, index);
if (ev.Ammo.Count == 0)
break;
}
DebugTools.Assert(ammo.Count == 0);
UpdateRevolverAppearance(revolverUid, component);
UpdateAmmoCount(revolverUid);
Dirty(revolverUid, component);
UpdateRevolverAppearance(ent);
UpdateAmmoCount(ent);
Dirty(ent);
Audio.PlayPredicted(component.SoundInsert, revolverUid, user);
Popup(Loc.GetString("gun-revolver-insert"), revolverUid, user);
Audio.PlayPredicted(ent.Comp.SoundInsert, ent, user);
Popup(Loc.GetString("gun-revolver-insert"), ent, user);
return true;
}
// Try to insert the entity directly.
for (var i = 0; i < component.Capacity; i++)
for (var i = 0; i < ent.Comp.Capacity; i++)
{
var index = (component.CurrentIndex + i) % component.Capacity;
var index = (ent.Comp.CurrentIndex + i) % ent.Comp.Capacity;
if (component.AmmoSlots[index] != null ||
component.Chambers[index] != null)
if (ent.Comp.AmmoSlots[index] != null ||
ent.Comp.Chambers[index] != null)
{
continue;
}
component.AmmoSlots[index] = uid;
Containers.Insert(uid, component.AmmoContainer);
SetChamber(index, component, uid);
Audio.PlayPredicted(component.SoundInsert, revolverUid, user);
Popup(Loc.GetString("gun-revolver-insert"), revolverUid, user);
UpdateRevolverAppearance(revolverUid, component);
UpdateAmmoCount(revolverUid);
Dirty(revolverUid, component);
ent.Comp.AmmoSlots[index] = insertEnt;
Containers.Insert(insertEnt, ent.Comp.AmmoContainer);
SetChamber(ent, insertEnt, index);
Audio.PlayPredicted(ent.Comp.SoundInsert, ent, user);
Popup(Loc.GetString("gun-revolver-insert"), ent, user);
UpdateRevolverAppearance(ent);
UpdateAmmoCount(ent);
Dirty(ent);
return true;
}
Popup(Loc.GetString("gun-revolver-full"), revolverUid, user);
Popup(Loc.GetString("gun-revolver-full"), ent, user);
return false;
}
private void SetChamber(int index, RevolverAmmoProviderComponent component, EntityUid uid)
private void SetChamber(Entity<RevolverAmmoProviderComponent> ent, Entity<CartridgeAmmoComponent?> ammo, int index)
{
if (TryComp<CartridgeAmmoComponent>(uid, out var cartridge) && cartridge.Spent)
if (!Resolve(ammo, ref ammo.Comp, false) || ammo.Comp.Spent)
{
component.Chambers[index] = false;
ent.Comp.Chambers[index] = false;
return;
}
component.Chambers[index] = true;
ent.Comp.Chambers[index] = true;
}
private void OnRevolverVerbs(EntityUid uid, RevolverAmmoProviderComponent component, GetVerbsEvent<AlternativeVerb> args)
@@ -213,7 +213,7 @@ public partial class SharedGunSystem
{
Text = Loc.GetString("gun-revolver-empty"),
Disabled = !AnyRevolverCartridges(component),
Act = () => EmptyRevolver(uid, component, args.User),
Act = () => EmptyRevolver((uid, component), args.User),
Priority = 1
});
@@ -221,7 +221,7 @@ public partial class SharedGunSystem
{
Text = Loc.GetString("gun-revolver-spin"),
// Category = VerbCategory.G,
Act = () => SpinRevolver(uid, component, args.User)
Act = () => SpinRevolver((uid, component), args.User)
});
}
@@ -281,15 +281,15 @@ public partial class SharedGunSystem
return count;
}
public void EmptyRevolver(EntityUid revolverUid, RevolverAmmoProviderComponent component, EntityUid? user = null)
public void EmptyRevolver(Entity<RevolverAmmoProviderComponent> ent, EntityUid? user = null)
{
var mapCoordinates = TransformSystem.GetMapCoordinates(revolverUid);
var mapCoordinates = TransformSystem.GetMapCoordinates(ent);
var anyEmpty = false;
for (var i = 0; i < component.Capacity; i++)
for (var i = 0; i < ent.Comp.Capacity; i++)
{
var chamber = component.Chambers[i];
var slot = component.AmmoSlots[i];
var chamber = ent.Comp.Chambers[i];
var slot = ent.Comp.AmmoSlots[i];
if (slot == null)
{
@@ -299,22 +299,22 @@ public partial class SharedGunSystem
// Too lazy to make a new method don't sue me.
if (!_netManager.IsClient)
{
var uid = Spawn(component.FillPrototype, mapCoordinates);
var uid = Spawn(ent.Comp.FillPrototype, mapCoordinates);
if (TryComp<CartridgeAmmoComponent>(uid, out var cartridge))
SetCartridgeSpent(uid, cartridge, !(bool) chamber);
SetCartridgeSpent(uid, cartridge, !(bool)chamber);
EjectCartridge(uid);
}
component.Chambers[i] = null;
ent.Comp.Chambers[i] = null;
anyEmpty = true;
}
else
{
component.AmmoSlots[i] = null;
Containers.Remove(slot.Value, component.AmmoContainer);
component.Chambers[i] = null;
ent.Comp.AmmoSlots[i] = null;
Containers.Remove(slot.Value, ent.Comp.AmmoContainer);
ent.Comp.Chambers[i] = null;
if (!_netManager.IsClient)
EjectCartridge(slot.Value);
@@ -325,47 +325,47 @@ public partial class SharedGunSystem
if (anyEmpty)
{
Audio.PlayPredicted(component.SoundEject, revolverUid, user);
UpdateAmmoCount(revolverUid, prediction: false);
UpdateRevolverAppearance(revolverUid, component);
Dirty(revolverUid, component);
Audio.PlayPredicted(ent.Comp.SoundEject, ent, user);
UpdateAmmoCount(ent, prediction: false);
UpdateRevolverAppearance(ent);
Dirty(ent);
}
}
private void UpdateRevolverAppearance(EntityUid uid, RevolverAmmoProviderComponent component)
private void UpdateRevolverAppearance(Entity<RevolverAmmoProviderComponent> ent)
{
if (!TryComp<AppearanceComponent>(uid, out var appearance))
if (!TryComp<AppearanceComponent>(ent, out var appearance))
return;
var count = GetRevolverCount(component);
Appearance.SetData(uid, AmmoVisuals.HasAmmo, count != 0, appearance);
Appearance.SetData(uid, AmmoVisuals.AmmoCount, count, appearance);
Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.Capacity, appearance);
var count = GetRevolverCount(ent.Comp);
Appearance.SetData(ent, AmmoVisuals.HasAmmo, count != 0, appearance);
Appearance.SetData(ent, AmmoVisuals.AmmoCount, count, appearance);
Appearance.SetData(ent, AmmoVisuals.AmmoMax, ent.Comp.Capacity, appearance);
}
protected virtual void SpinRevolver(EntityUid revolverUid, RevolverAmmoProviderComponent component, EntityUid? user = null)
protected virtual void SpinRevolver(Entity<RevolverAmmoProviderComponent> ent, EntityUid? user = null)
{
Audio.PlayPredicted(component.SoundSpin, revolverUid, user);
Popup(Loc.GetString("gun-revolver-spun"), revolverUid, user);
Audio.PlayPredicted(ent.Comp.SoundSpin, ent, user);
Popup(Loc.GetString("gun-revolver-spun"), ent, user);
}
private void OnRevolverTakeAmmo(EntityUid uid, RevolverAmmoProviderComponent component, TakeAmmoEvent args)
private void OnRevolverTakeAmmo(Entity<RevolverAmmoProviderComponent> ent, ref TakeAmmoEvent args)
{
var currentIndex = component.CurrentIndex;
Cycle(component, args.Shots);
var currentIndex = ent.Comp.CurrentIndex;
Cycle(ent.Comp, args.Shots);
// Revolvers provide the bullets themselves rather than the cartridges so they stay in the revolver.
for (var i = 0; i < args.Shots; i++)
{
var index = (currentIndex + i) % component.Capacity;
var chamber = component.Chambers[index];
EntityUid? ent = null;
var index = (currentIndex + i) % ent.Comp.Capacity;
var chamber = ent.Comp.Chambers[index];
EntityUid? ammoEnt = null;
// Get contained entity if it exists.
if (component.AmmoSlots[index] != null)
if (ent.Comp.AmmoSlots[index] != null)
{
ent = component.AmmoSlots[index]!;
component.Chambers[index] = false;
ammoEnt = ent.Comp.AmmoSlots[index]!;
ent.Comp.Chambers[index] = false;
}
// Try to spawn a round if it's available.
else if (chamber != null)
@@ -373,55 +373,55 @@ public partial class SharedGunSystem
if (chamber == true)
{
// Pretend it's always been there.
ent = Spawn(component.FillPrototype, args.Coordinates);
ammoEnt = Spawn(ent.Comp.FillPrototype, args.Coordinates);
if (!_netManager.IsClient)
{
component.AmmoSlots[index] = ent;
Containers.Insert(ent.Value, component.AmmoContainer);
ent.Comp.AmmoSlots[index] = ammoEnt;
Containers.Insert(ammoEnt.Value, ent.Comp.AmmoContainer);
}
component.Chambers[index] = false;
ent.Comp.Chambers[index] = false;
}
}
// Chamber empty or spent
if (ent == null)
if (ammoEnt == null)
continue;
if (TryComp<CartridgeAmmoComponent>(ent, out var cartridge))
if (TryComp<CartridgeAmmoComponent>(ammoEnt, out var cartridge))
{
if (cartridge.Spent)
continue;
// Mark cartridge as spent and if it's caseless delete from the chamber slot.
SetCartridgeSpent(ent.Value, cartridge, true);
SetCartridgeSpent(ammoEnt.Value, cartridge, true);
var spawned = Spawn(cartridge.Prototype, args.Coordinates);
args.Ammo.Add((spawned, EnsureComp<AmmoComponent>(spawned)));
if (cartridge.DeleteOnSpawn)
{
component.AmmoSlots[index] = null;
component.Chambers[index] = null;
ent.Comp.AmmoSlots[index] = null;
ent.Comp.Chambers[index] = null;
}
}
else
{
component.AmmoSlots[index] = null;
component.Chambers[index] = null;
args.Ammo.Add((ent.Value, EnsureComp<AmmoComponent>(ent.Value)));
ent.Comp.AmmoSlots[index] = null;
ent.Comp.Chambers[index] = null;
args.Ammo.Add((ammoEnt.Value, EnsureComp<AmmoComponent>(ammoEnt.Value)));
}
// Delete the cartridge entity on client
if (_netManager.IsClient)
{
QueueDel(ent);
QueueDel(ammoEnt);
}
}
UpdateAmmoCount(uid, prediction: false);
UpdateRevolverAppearance(uid, component);
Dirty(uid, component);
UpdateAmmoCount(ent, prediction: false);
UpdateRevolverAppearance(ent);
Dirty(ent);
}
private void Cycle(RevolverAmmoProviderComponent component, int count = 1)
@@ -429,34 +429,34 @@ public partial class SharedGunSystem
component.CurrentIndex = (component.CurrentIndex + count) % component.Capacity;
}
private void OnRevolverInit(EntityUid uid, RevolverAmmoProviderComponent component, ComponentInit args)
private void OnRevolverInit(Entity<RevolverAmmoProviderComponent> ent, ref ComponentInit args)
{
component.AmmoContainer = Containers.EnsureContainer<Container>(uid, RevolverContainer);
component.AmmoSlots.EnsureCapacity(component.Capacity);
var remainder = component.Capacity - component.AmmoSlots.Count;
ent.Comp.AmmoContainer = Containers.EnsureContainer<Container>(ent, RevolverContainer);
ent.Comp.AmmoSlots.EnsureCapacity(ent.Comp.Capacity);
var remainder = ent.Comp.Capacity - ent.Comp.AmmoSlots.Count;
for (var i = 0; i < remainder; i++)
{
component.AmmoSlots.Add(null);
ent.Comp.AmmoSlots.Add(null);
}
component.Chambers = new bool?[component.Capacity];
ent.Comp.Chambers = new bool?[ent.Comp.Capacity];
if (component.FillPrototype != null)
if (ent.Comp.FillPrototype != null)
{
for (var i = 0; i < component.Capacity; i++)
for (var i = 0; i < ent.Comp.Capacity; i++)
{
if (component.AmmoSlots[i] != null)
if (ent.Comp.AmmoSlots[i] != null)
{
component.Chambers[i] = null;
ent.Comp.Chambers[i] = null;
continue;
}
component.Chambers[i] = true;
ent.Comp.Chambers[i] = true;
}
}
DebugTools.Assert(component.AmmoSlots.Count == component.Capacity);
DebugTools.Assert(ent.Comp.AmmoSlots.Count == ent.Comp.Capacity);
}
[Serializable, NetSerializable]

View File

@@ -13,9 +13,9 @@ public partial class SharedGunSystem
SubscribeLocalEvent<SolutionAmmoProviderComponent, GetAmmoCountEvent>(OnSolutionAmmoCount);
}
private void OnSolutionTakeAmmo(EntityUid uid, SolutionAmmoProviderComponent component, TakeAmmoEvent args)
private void OnSolutionTakeAmmo(Entity<SolutionAmmoProviderComponent> ent, ref TakeAmmoEvent args)
{
var shots = Math.Min(args.Shots, component.Shots);
var shots = Math.Min(args.Shots, ent.Comp.Shots);
// Don't dirty if it's an empty fire.
if (shots == 0)
@@ -23,38 +23,35 @@ public partial class SharedGunSystem
for (var i = 0; i < shots; i++)
{
args.Ammo.Add(GetSolutionShot(uid, component, args.Coordinates));
component.Shots--;
args.Ammo.Add(GetSolutionShot(ent, args.Coordinates));
ent.Comp.Shots--;
}
UpdateSolutionShots(uid, component);
UpdateSolutionAppearance(uid, component);
UpdateSolutionShots(ent);
UpdateSolutionAppearance(ent);
}
private void OnSolutionAmmoCount(EntityUid uid, SolutionAmmoProviderComponent component, ref GetAmmoCountEvent args)
private void OnSolutionAmmoCount(Entity<SolutionAmmoProviderComponent> ent, ref GetAmmoCountEvent args)
{
args.Count = component.Shots;
args.Capacity = component.MaxShots;
args.Count = ent.Comp.Shots;
args.Capacity = ent.Comp.MaxShots;
}
protected virtual void UpdateSolutionShots(EntityUid uid, SolutionAmmoProviderComponent component, Solution? solution = null)
{
protected virtual void UpdateSolutionShots(Entity<SolutionAmmoProviderComponent> ent, Solution? solution = null) { }
protected virtual (EntityUid Entity, IShootable) GetSolutionShot(Entity<SolutionAmmoProviderComponent> ent, EntityCoordinates position)
{
var shot = Spawn(ent.Comp.Prototype, position);
return (shot, EnsureShootable(shot));
}
protected virtual (EntityUid Entity, IShootable) GetSolutionShot(EntityUid uid, SolutionAmmoProviderComponent component, EntityCoordinates position)
protected void UpdateSolutionAppearance(Entity<SolutionAmmoProviderComponent> ent)
{
var ent = Spawn(component.Prototype, position);
return (ent, EnsureShootable(ent));
}
protected void UpdateSolutionAppearance(EntityUid uid, SolutionAmmoProviderComponent component)
{
if (!TryComp<AppearanceComponent>(uid, out var appearance))
if (!TryComp<AppearanceComponent>(ent, out var appearance))
return;
Appearance.SetData(uid, AmmoVisuals.HasAmmo, component.Shots != 0, appearance);
Appearance.SetData(uid, AmmoVisuals.AmmoCount, component.Shots, appearance);
Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.MaxShots, appearance);
Appearance.SetData(ent, AmmoVisuals.HasAmmo, ent.Comp.Shots != 0, appearance);
Appearance.SetData(ent, AmmoVisuals.AmmoCount, ent.Comp.Shots, appearance);
Appearance.SetData(ent, AmmoVisuals.AmmoMax, ent.Comp.MaxShots, appearance);
}
}

View File

@@ -40,38 +40,48 @@ namespace Content.Shared.Weapons.Ranged.Systems;
public abstract partial class SharedGunSystem : EntitySystem
{
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
[Dependency] private readonly INetManager _netManager = default!;
[Dependency] private readonly ItemSlotsSystem _slots = default!;
[Dependency] private readonly RechargeBasicEntityAmmoSystem _recharge = default!;
[Dependency] private readonly SharedCombatModeSystem _combatMode = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly UseDelaySystem _useDelay = default!;
[Dependency] protected readonly DamageableSystem Damageable = default!;
[Dependency] protected readonly ExamineSystemShared Examine = default!;
[Dependency] protected readonly IGameTiming Timing = default!;
[Dependency] protected readonly IMapManager MapManager = default!;
[Dependency] private readonly INetManager _netManager = default!;
[Dependency] protected readonly IPrototypeManager ProtoManager = default!;
[Dependency] protected readonly IRobustRandom Random = default!;
[Dependency] protected readonly ISharedAdminLogManager Logs = default!;
[Dependency] protected readonly DamageableSystem Damageable = default!;
[Dependency] protected readonly ExamineSystemShared Examine = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly ItemSlotsSystem _slots = default!;
[Dependency] private readonly RechargeBasicEntityAmmoSystem _recharge = default!;
[Dependency] protected readonly SharedActionsSystem Actions = default!;
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
[Dependency] protected readonly SharedAudioSystem Audio = default!;
[Dependency] private readonly SharedCombatModeSystem _combatMode = default!;
[Dependency] protected readonly SharedContainerSystem Containers = default!;
[Dependency] protected readonly SharedPhysicsSystem Physics = default!;
[Dependency] protected readonly SharedPointLightSystem Lights = default!;
[Dependency] protected readonly SharedPopupSystem PopupSystem = default!;
[Dependency] protected readonly SharedPhysicsSystem Physics = default!;
[Dependency] protected readonly SharedProjectileSystem Projectiles = default!;
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
[Dependency] protected readonly TagSystem TagSystem = default!;
[Dependency] protected readonly ThrowingSystem ThrowingSystem = default!;
[Dependency] private readonly UseDelaySystem _useDelay = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
/// <summary>
/// Default projectile speed
/// </summary>
public const float ProjectileSpeed = 40f;
/// <summary>
/// Name of the container slot used as the gun's chamber
/// </summary>
public const string ChamberSlot = "gun_chamber";
/// <summary>
/// Name of the container slot used as the gun's magazine
/// </summary>
public const string MagazineSlot = "gun_magazine";
private static readonly ProtoId<TagPrototype> TrashTag = "Trash";
private const float InteractNextFire = 0.3f;
@@ -119,15 +129,15 @@ public abstract partial class SharedGunSystem : EntitySystem
RefreshModifiers((gun, gun));
}
private void OnGunMelee(EntityUid uid, GunComponent component, MeleeHitEvent args)
private void OnGunMelee(Entity<GunComponent> ent, ref MeleeHitEvent args)
{
if (!TryComp<MeleeWeaponComponent>(uid, out var melee))
if (!TryComp<MeleeWeaponComponent>(ent, out var melee))
return;
if (melee.NextAttack > component.NextFire)
if (melee.NextAttack > ent.Comp.NextFire)
{
component.NextFire = melee.NextAttack;
DirtyField(uid, component, nameof(GunComponent.NextFire));
ent.Comp.NextFire = melee.NextAttack;
DirtyField(ent.AsNullable(), nameof(GunComponent.NextFire));
}
}
@@ -137,17 +147,17 @@ public abstract partial class SharedGunSystem : EntitySystem
if (user == null ||
!_combatMode.IsInCombatMode(user) ||
!TryGetGun(user.Value, out var ent, out var gun))
!TryGetGun(user.Value, out var gun))
{
return;
}
if (ent != GetEntity(msg.Gun))
if (gun.Owner != GetEntity(msg.Gun))
return;
gun.ShootCoordinates = GetCoordinates(msg.Coordinates);
gun.Target = GetEntity(msg.Target);
AttemptShoot(user.Value, ent, gun);
gun.Comp.ShootCoordinates = GetCoordinates(msg.Coordinates);
gun.Comp.Target = GetEntity(msg.Target);
AttemptShoot(user.Value, gun);
}
private void OnStopShootRequest(RequestStopShootEvent ev, EntitySessionEventArgs args)
@@ -156,15 +166,15 @@ public abstract partial class SharedGunSystem : EntitySystem
if (args.SenderSession.AttachedEntity == null ||
!TryComp<GunComponent>(gunUid, out var gun) ||
!TryGetGun(args.SenderSession.AttachedEntity.Value, out _, out var userGun))
!TryGetGun(args.SenderSession.AttachedEntity.Value, out var userGun))
{
return;
}
if (userGun != gun)
if (userGun != (gunUid, gun))
return;
StopShooting(gunUid, gun);
StopShooting(userGun);
}
public bool CanShoot(GunComponent component)
@@ -175,75 +185,78 @@ public abstract partial class SharedGunSystem : EntitySystem
return true;
}
public bool TryGetGun(EntityUid entity, out EntityUid gunEntity, [NotNullWhen(true)] out GunComponent? gunComp)
/// <summary>
/// Tries to get an entity with <see cref="GunComponent"/> from the specified entity's hands, or from the entity itself.
/// </summary>
/// <param name="entity">Entity that is holding the gun, or is the gun</param>
/// <param name="gun">Gun entity to return</param>
/// <returns>True if gun was found</returns>
public bool TryGetGun(EntityUid entity, out Entity<GunComponent> gun)
{
gunEntity = default;
gunComp = null;
gun = default;
if (_hands.GetActiveItem(entity) is { } held &&
TryComp(held, out GunComponent? gun))
TryComp(held, out GunComponent? gunComp))
{
gunEntity = held;
gunComp = gun;
gun = (held, gunComp);
return true;
}
// Last resort is check if the entity itself is a gun.
if (TryComp(entity, out gun))
if (TryComp(entity, out gunComp))
{
gunEntity = entity;
gunComp = gun;
gun = (entity, gunComp);
return true;
}
return false;
}
private void StopShooting(EntityUid uid, GunComponent gun)
private void StopShooting(Entity<GunComponent> ent)
{
if (gun.ShotCounter == 0)
if (ent.Comp.ShotCounter == 0)
return;
gun.ShotCounter = 0;
gun.ShootCoordinates = null;
gun.Target = null;
DirtyField(uid, gun, nameof(GunComponent.ShotCounter));
ent.Comp.ShotCounter = 0;
ent.Comp.ShootCoordinates = null;
ent.Comp.Target = null;
DirtyField(ent.AsNullable(), nameof(GunComponent.ShotCounter));
}
/// <summary>
/// Attempts to shoot at the target coordinates. Resets the shot counter after every shot.
/// </summary>
public bool AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun, EntityCoordinates toCoordinates, EntityUid? target = null)
public bool AttemptShoot(EntityUid user, Entity<GunComponent> gun, EntityCoordinates toCoordinates, EntityUid? target = null)
{
gun.ShootCoordinates = toCoordinates;
gun.Target = target;
var result = AttemptShoot(user, gunUid, gun);
gun.ShotCounter = 0;
DirtyField(gunUid, gun, nameof(GunComponent.ShotCounter));
gun.Comp.ShootCoordinates = toCoordinates;
gun.Comp.Target = target;
var result = AttemptShoot(user, gun);
gun.Comp.ShotCounter = 0;
DirtyField(gun.AsNullable(), nameof(GunComponent.ShotCounter));
return result;
}
/// <summary>
/// Shoots by assuming the gun is the user at default coordinates.
/// </summary>
public bool AttemptShoot(EntityUid gunUid, GunComponent gun)
public bool AttemptShoot(Entity<GunComponent> gun)
{
var coordinates = new EntityCoordinates(gunUid, gun.DefaultDirection);
gun.ShootCoordinates = coordinates;
var result = AttemptShoot(gunUid, gunUid, gun);
gun.ShotCounter = 0;
var coordinates = new EntityCoordinates(gun, gun.Comp.DefaultDirection);
gun.Comp.ShootCoordinates = coordinates;
var result = AttemptShoot(gun, gun);
gun.Comp.ShotCounter = 0;
return result;
}
private bool AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun)
private bool AttemptShoot(EntityUid user, Entity<GunComponent> gun)
{
if (gun.FireRateModified <= 0f ||
if (gun.Comp.FireRateModified <= 0f ||
!_actionBlockerSystem.CanAttack(user))
{
return false;
}
var toCoordinates = gun.ShootCoordinates;
var toCoordinates = gun.Comp.ShootCoordinates;
if (toCoordinates == null)
return false;
@@ -254,9 +267,9 @@ public abstract partial class SharedGunSystem : EntitySystem
var prevention = new ShotAttemptedEvent
{
User = user,
Used = (gunUid, gun)
Used = gun
};
RaiseLocalEvent(gunUid, ref prevention);
RaiseLocalEvent(gun, ref prevention);
if (prevention.Cancelled)
return false;
@@ -266,95 +279,96 @@ public abstract partial class SharedGunSystem : EntitySystem
// Need to do this to play the clicking sound for empty automatic weapons
// but not play anything for burst fire.
if (gun.NextFire > curTime)
if (gun.Comp.NextFire > curTime)
return false;
var fireRate = TimeSpan.FromSeconds(1f / gun.FireRateModified);
var fireRate = TimeSpan.FromSeconds(1f / gun.Comp.FireRateModified);
if (gun.SelectedMode == SelectiveFire.Burst || gun.BurstActivated)
fireRate = TimeSpan.FromSeconds(1f / gun.BurstFireRate);
if (gun.Comp.SelectedMode == SelectiveFire.Burst || gun.Comp.BurstActivated)
fireRate = TimeSpan.FromSeconds(1f / gun.Comp.BurstFireRate);
// First shot
// Previously we checked shotcounter but in some cases all the bullets got dumped at once
// curTime - fireRate is insufficient because if you time it just right you can get a 3rd shot out slightly quicker.
if (gun.NextFire < curTime - fireRate || gun.ShotCounter == 0 && gun.NextFire < curTime)
gun.NextFire = curTime;
if (gun.Comp.NextFire < curTime - fireRate || gun.Comp.ShotCounter == 0 && gun.Comp.NextFire < curTime)
gun.Comp.NextFire = curTime;
var shots = 0;
var lastFire = gun.NextFire;
var lastFire = gun.Comp.NextFire;
while (gun.NextFire <= curTime)
while (gun.Comp.NextFire <= curTime)
{
gun.NextFire += fireRate;
gun.Comp.NextFire += fireRate;
shots++;
}
// NextFire has been touched regardless so need to dirty the gun.
DirtyField(gunUid, gun, nameof(GunComponent.NextFire));
DirtyField(gun.AsNullable(), nameof(GunComponent.NextFire));
// Get how many shots we're actually allowed to make, due to clip size or otherwise.
// Don't do this in the loop so we still reset NextFire.
if (!gun.BurstActivated)
if (!gun.Comp.BurstActivated)
{
switch (gun.SelectedMode)
switch (gun.Comp.SelectedMode)
{
case SelectiveFire.SemiAuto:
shots = Math.Min(shots, 1 - gun.ShotCounter);
shots = Math.Min(shots, 1 - gun.Comp.ShotCounter);
break;
case SelectiveFire.Burst:
shots = Math.Min(shots, gun.ShotsPerBurstModified - gun.ShotCounter);
shots = Math.Min(shots, gun.Comp.ShotsPerBurstModified - gun.Comp.ShotCounter);
break;
case SelectiveFire.FullAuto:
break;
default:
throw new ArgumentOutOfRangeException($"No implemented shooting behavior for {gun.SelectedMode}!");
throw new ArgumentOutOfRangeException($"No implemented shooting behavior for {gun.Comp.SelectedMode}!");
}
} else
}
else
{
shots = Math.Min(shots, gun.ShotsPerBurstModified - gun.ShotCounter);
shots = Math.Min(shots, gun.Comp.ShotsPerBurstModified - gun.Comp.ShotCounter);
}
var attemptEv = new AttemptShootEvent(user, null);
RaiseLocalEvent(gunUid, ref attemptEv);
RaiseLocalEvent(gun, ref attemptEv);
if (attemptEv.Cancelled)
{
if (attemptEv.Message != null)
{
PopupSystem.PopupClient(attemptEv.Message, gunUid, user);
PopupSystem.PopupClient(attemptEv.Message, gun, user);
}
gun.BurstActivated = false;
gun.BurstShotsCount = 0;
gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds));
gun.Comp.BurstActivated = false;
gun.Comp.BurstShotsCount = 0;
gun.Comp.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.Comp.NextFire.TotalSeconds));
return false;
}
var fromCoordinates = Transform(user).Coordinates;
// Remove ammo
var ev = new TakeAmmoEvent(shots, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoordinates, user);
var ev = new TakeAmmoEvent(shots, [], fromCoordinates, user);
// Listen it just makes the other code around it easier if shots == 0 to do this.
if (shots > 0)
RaiseLocalEvent(gunUid, ev);
RaiseLocalEvent(gun, ev);
DebugTools.Assert(ev.Ammo.Count <= shots);
DebugTools.Assert(shots >= 0);
UpdateAmmoCount(gunUid);
UpdateAmmoCount(gun);
// 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;
DirtyField(gunUid, gun, nameof(GunComponent.ShotCounter));
gun.Comp.ShotCounter += shots;
DirtyField(gun.AsNullable(), nameof(GunComponent.ShotCounter));
if (ev.Ammo.Count <= 0)
{
// triggers effects on the gun if it's empty
var emptyGunShotEvent = new OnEmptyGunShotEvent(user);
RaiseLocalEvent(gunUid, ref emptyGunShotEvent);
RaiseLocalEvent(gun, ref emptyGunShotEvent);
gun.BurstActivated = false;
gun.BurstShotsCount = 0;
gun.NextFire += TimeSpan.FromSeconds(gun.BurstCooldown);
gun.Comp.BurstActivated = false;
gun.Comp.BurstShotsCount = 0;
gun.Comp.NextFire += TimeSpan.FromSeconds(gun.Comp.BurstCooldown);
// Play empty gun sounds if relevant
// If they're firing an existing clip then don't play anything.
@@ -364,8 +378,8 @@ 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, gunUid, user);
gun.Comp.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.Comp.NextFire.TotalSeconds));
Audio.PlayPredicted(gun.Comp.SoundEmpty, gun, user);
return false;
}
@@ -373,25 +387,25 @@ public abstract partial class SharedGunSystem : EntitySystem
}
// Handle burstfire
if (gun.SelectedMode == SelectiveFire.Burst)
if (gun.Comp.SelectedMode == SelectiveFire.Burst)
{
gun.BurstActivated = true;
gun.Comp.BurstActivated = true;
}
if (gun.BurstActivated)
if (gun.Comp.BurstActivated)
{
gun.BurstShotsCount += shots;
if (gun.BurstShotsCount >= gun.ShotsPerBurstModified)
gun.Comp.BurstShotsCount += shots;
if (gun.Comp.BurstShotsCount >= gun.Comp.ShotsPerBurstModified)
{
gun.NextFire += TimeSpan.FromSeconds(gun.BurstCooldown);
gun.BurstActivated = false;
gun.BurstShotsCount = 0;
gun.Comp.NextFire += TimeSpan.FromSeconds(gun.Comp.BurstCooldown);
gun.Comp.BurstActivated = false;
gun.Comp.BurstShotsCount = 0;
}
}
// Shoot confirmed - sounds also played here in case it's invalid (e.g. cartridge already spent).
Shoot(gunUid, gun, ev.Ammo, fromCoordinates, toCoordinates.Value, out var userImpulse, user, throwItems: attemptEv.ThrowItems);
Shoot(gun, ev.Ammo, fromCoordinates, toCoordinates.Value, out var userImpulse, user, throwItems: attemptEv.ThrowItems);
var shotEv = new GunShotEvent(user, ev.Ammo);
RaiseLocalEvent(gunUid, ref shotEv);
RaiseLocalEvent(gun, ref shotEv);
if (!userImpulse || !TryComp<PhysicsComponent>(user, out var userPhysics))
return true;
@@ -400,13 +414,12 @@ public abstract partial class SharedGunSystem : EntitySystem
RaiseLocalEvent(user, ref shooterEv);
if (shooterEv.Push)
CauseImpulse(fromCoordinates, toCoordinates.Value, user, userPhysics);
CauseImpulse(fromCoordinates, toCoordinates.Value, (user, userPhysics));
return true;
}
public void Shoot(
EntityUid gunUid,
GunComponent gun,
Entity<GunComponent> gun,
EntityUid ammo,
EntityCoordinates fromCoordinates,
EntityCoordinates toCoordinates,
@@ -415,12 +428,11 @@ public abstract partial class SharedGunSystem : EntitySystem
bool throwItems = false)
{
var shootable = EnsureShootable(ammo);
Shoot(gunUid, gun, new List<(EntityUid? Entity, IShootable Shootable)>(1) { (ammo, shootable) }, fromCoordinates, toCoordinates, out userImpulse, user, throwItems);
Shoot(gun, new List<(EntityUid? Entity, IShootable Shootable)>(1) { (ammo, shootable) }, fromCoordinates, toCoordinates, out userImpulse, user, throwItems);
}
public abstract void Shoot(
EntityUid gunUid,
GunComponent gun,
Entity<GunComponent> gun,
List<(EntityUid? Entity, IShootable Shootable)> ammo,
EntityCoordinates fromCoordinates,
EntityCoordinates toCoordinates,
@@ -452,7 +464,7 @@ public abstract partial class SharedGunSystem : EntitySystem
/// <summary>
/// Call this whenever the ammo count for a gun changes.
/// </summary>
protected virtual void UpdateAmmoCount(EntityUid uid, bool prediction = true) {}
protected virtual void UpdateAmmoCount(EntityUid uid, bool prediction = true) { }
protected void SetCartridgeSpent(EntityUid uid, CartridgeAmmoComponent cartridge, bool spent)
{
@@ -492,7 +504,7 @@ public abstract partial class SharedGunSystem : EntitySystem
// decides direction the casing ejects and only when not cycling
if (angle != null)
{
Angle ejectAngle = angle.Value;
var ejectAngle = angle.Value;
ejectAngle += 3.7f; // 212 degrees; casings should eject slightly to the right and behind of a gun
ThrowingSystem.TryThrow(entity, ejectAngle.ToVec().Normalized() / 100, 5f);
}
@@ -535,15 +547,15 @@ public abstract partial class SharedGunSystem : EntitySystem
CreateEffect(gun, ev, user);
}
public void CauseImpulse(EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, EntityUid user, PhysicsComponent userPhysics)
public void CauseImpulse(EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, Entity<PhysicsComponent> user)
{
var fromMap = TransformSystem.ToMapCoordinates(fromCoordinates).Position;
var toMap = TransformSystem.ToMapCoordinates(toCoordinates).Position;
var shotDirection = (toMap - fromMap).Normalized();
const float impulseStrength = 25.0f;
var impulseVector = shotDirection * impulseStrength;
Physics.ApplyLinearImpulse(user, -impulseVector, body: userPhysics);
var impulseVector = shotDirection * impulseStrength;
Physics.ApplyLinearImpulse(user, -impulseVector, body: user.Comp);
}
public void RefreshModifiers(Entity<GunComponent?> gun)
@@ -632,7 +644,7 @@ public abstract partial class SharedGunSystem : EntitySystem
[Serializable, NetSerializable]
public sealed class HitscanEvent : EntityEventArgs
{
public List<(NetCoordinates coordinates, Angle angle, SpriteSpecifier Sprite, float Distance)> Sprites = new();
public List<(NetCoordinates coordinates, Angle angle, SpriteSpecifier Sprite, float Distance)> Sprites = [];
}
/// <summary>

View File

@@ -13,9 +13,9 @@ public sealed class UseDelayOnShootSystem : EntitySystem
SubscribeLocalEvent<UseDelayOnShootComponent, GunShotEvent>(OnUseShoot);
}
private void OnUseShoot(EntityUid uid, UseDelayOnShootComponent component, ref GunShotEvent args)
private void OnUseShoot(Entity<UseDelayOnShootComponent> ent, ref GunShotEvent args)
{
if (TryComp(uid, out UseDelayComponent? useDelay))
_delay.TryResetDelay((uid, useDelay));
if (TryComp(ent, out UseDelayComponent? useDelay))
_delay.TryResetDelay((ent, useDelay));
}
}