diff --git a/Content.Shared/Weapons/Ranged/Components/BallisticAmmoProviderComponent.cs b/Content.Shared/Weapons/Ranged/Components/BallisticAmmoProviderComponent.cs
index 679b0df782..d5fb2e7dc8 100644
--- a/Content.Shared/Weapons/Ranged/Components/BallisticAmmoProviderComponent.cs
+++ b/Content.Shared/Weapons/Ranged/Components/BallisticAmmoProviderComponent.cs
@@ -53,4 +53,10 @@ public sealed partial class BallisticAmmoProviderComponent : Component
///
[ViewVariables(VVAccess.ReadWrite), DataField("mayTransfer")]
public bool MayTransfer = false;
+
+ ///
+ /// DoAfter delay for filling a bullet into another ballistic ammo provider.
+ ///
+ [DataField("fillDelay")]
+ public TimeSpan FillDelay = TimeSpan.FromSeconds(0.5);
}
diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs
index a6a31bc992..aea7fb4e8b 100644
--- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs
+++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs
@@ -1,3 +1,4 @@
+using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
@@ -13,6 +14,8 @@ namespace Content.Shared.Weapons.Ranged.Systems;
public abstract partial class SharedGunSystem
{
+ [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
+
protected virtual void InitializeBallistic()
{
SubscribeLocalEvent(OnBallisticInit);
@@ -24,6 +27,7 @@ public abstract partial class SharedGunSystem
SubscribeLocalEvent>(OnBallisticVerb);
SubscribeLocalEvent(OnBallisticInteractUsing);
SubscribeLocalEvent(OnBallisticAfterInteract);
+ SubscribeLocalEvent(OnBallisticAmmoFillDoAfter);
SubscribeLocalEvent(OnBallisticUse);
}
@@ -61,7 +65,7 @@ public abstract partial class SharedGunSystem
args.Target == null ||
args.Used == args.Target ||
Deleted(args.Target) ||
- !TryComp(args.Target, out BallisticAmmoProviderComponent? targetComponent) ||
+ !TryComp(args.Target, out var targetComponent) ||
targetComponent.Whitelist == null)
{
return;
@@ -69,7 +73,23 @@ public abstract partial class SharedGunSystem
args.Handled = true;
- if (targetComponent.Entities.Count + targetComponent.UnspawnedCount == targetComponent.Capacity)
+ _doAfter.TryStartDoAfter(new DoAfterArgs(args.User, component.FillDelay, new AmmoFillDoAfterEvent(), used: uid, target: args.Target, eventTarget: uid)
+ {
+ BreakOnTargetMove = true,
+ BreakOnUserMove = true,
+ BreakOnDamage = false,
+ NeedHand = true
+ });
+ }
+
+ private void OnBallisticAmmoFillDoAfter(EntityUid uid, BallisticAmmoProviderComponent component, AmmoFillDoAfterEvent args)
+ {
+ if (Deleted(args.Target) ||
+ !TryComp(args.Target, out var target) ||
+ target.Whitelist == null)
+ return;
+
+ if (target.Entities.Count + target.UnspawnedCount == target.Capacity)
{
Popup(
Loc.GetString("gun-ballistic-transfer-target-full",
@@ -83,8 +103,8 @@ public abstract partial class SharedGunSystem
{
Popup(
Loc.GetString("gun-ballistic-transfer-empty",
- ("entity", args.Used)),
- args.Used,
+ ("entity", uid)),
+ uid,
args.User);
return;
}
@@ -96,27 +116,25 @@ public abstract partial class SharedGunSystem
}
List<(EntityUid? Entity, IShootable Shootable)> ammo = new();
- var evTakeAmmo = new TakeAmmoEvent(1, ammo, Transform(args.Used).Coordinates, args.User);
- RaiseLocalEvent(args.Used, evTakeAmmo);
+ var evTakeAmmo = new TakeAmmoEvent(1, ammo, Transform(uid).Coordinates, args.User);
+ RaiseLocalEvent(uid, evTakeAmmo);
foreach (var (ent, _) in ammo)
{
if (ent == null)
continue;
- if (!targetComponent.Whitelist.IsValid(ent.Value))
+ if (!target.Whitelist.IsValid(ent.Value))
{
Popup(
Loc.GetString("gun-ballistic-transfer-invalid",
("ammoEntity", ent.Value),
("targetEntity", args.Target.Value)),
- args.Used,
+ uid,
args.User);
- // TODO: For better or worse, this will play a sound, but it's the
- // more future-proof thing to do than copying the same code
- // that OnBallisticInteractUsing has, sans sound.
- SimulateInsertAmmo(ent.Value, args.Used, Transform(args.Used).Coordinates);
+ // play sound to be cool
+ SimulateInsertAmmo(ent.Value, uid, Transform(uid).Coordinates);
}
else
{
@@ -126,6 +144,11 @@ public abstract partial class SharedGunSystem
if (ent.Value.IsClientSide())
Del(ent.Value);
}
+
+ // repeat if there is more space in the target and more ammo to fill it
+ var moreSpace = target.Entities.Count + target.UnspawnedCount < target.Capacity;
+ var moreAmmo = component.Entities.Count + component.UnspawnedCount > 0;
+ args.Repeat = moreSpace && moreAmmo;
}
private void OnBallisticVerb(EntityUid uid, BallisticAmmoProviderComponent component, GetVerbsEvent args)
@@ -247,3 +270,11 @@ public abstract partial class SharedGunSystem
Appearance.SetData(uid, AmmoVisuals.AmmoMax, component.Capacity, appearance);
}
}
+
+///
+/// DoAfter event for filling one ballistic ammo provider from another.
+///
+[Serializable, NetSerializable]
+public sealed partial class AmmoFillDoAfterEvent : SimpleDoAfterEvent
+{
+}