]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Make vending machine restocks predicted (and its sound not spammable) (#38609)
authorPerry Fraser <perryprog@users.noreply.github.com>
Thu, 11 Sep 2025 18:27:54 +0000 (14:27 -0400)
committerGitHub <noreply@github.com>
Thu, 11 Sep 2025 18:27:54 +0000 (20:27 +0200)
* feat: make vending machine restocks predicted

* refactor: VendingMachineRestockComponent cleanup

* refactor: minor simplification

* revert: refactor: minor simplification; load bearing IsFirstTimePredicted

lol second guessed myself

* chore: unneeded VendingMachineSystem dep

* Update Content.Shared/VendingMachines/VendingMachineComponent.cs

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
Content.Server/VendingMachines/VendingMachineSystem.cs
Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs
Content.Shared/VendingMachines/SharedVendingMachineSystem.cs
Content.Shared/VendingMachines/VendingMachineComponent.cs
Content.Shared/VendingMachines/VendingMachineRestockComponent.cs

index 23a744ddd68d92d131dc1d0fb047a75560e972f4..df204cf338a139d23b92b959c2750fdfbff83e96 100644 (file)
@@ -8,20 +8,14 @@ using Content.Server.Vocalization.Systems;
 using Content.Shared.Cargo;
 using Content.Shared.Damage;
 using Content.Shared.Destructible;
-using Content.Shared.DoAfter;
 using Content.Shared.Emp;
-using Content.Shared.IdentityManagement;
-using Content.Shared.Popups;
 using Content.Shared.Power;
 using Content.Shared.Throwing;
 using Content.Shared.UserInterface;
 using Content.Shared.VendingMachines;
 using Content.Shared.Wall;
-using Robust.Shared.Audio;
-using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
-using Robust.Shared.Timing;
 
 namespace Content.Server.VendingMachines
 {
@@ -30,7 +24,6 @@ namespace Content.Server.VendingMachines
         [Dependency] private readonly IRobustRandom _random = default!;
         [Dependency] private readonly PricingSystem _pricing = default!;
         [Dependency] private readonly ThrowingSystem _throwingSystem = default!;
-        [Dependency] private readonly IGameTiming _timing = default!;
 
         private const float WallVendEjectDistanceFromWall = 1f;
 
@@ -46,11 +39,8 @@ namespace Content.Server.VendingMachines
             SubscribeLocalEvent<VendingMachineComponent, TryVocalizeEvent>(OnTryVocalize);
 
             SubscribeLocalEvent<VendingMachineComponent, ActivatableUIOpenAttemptEvent>(OnActivatableUIOpenAttempt);
-
             SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense);
 
-            SubscribeLocalEvent<VendingMachineComponent, RestockDoAfterEvent>(OnDoAfter);
-
             SubscribeLocalEvent<VendingMachineRestockComponent, PriceCalculationEvent>(OnPriceCalculation);
         }
 
@@ -133,30 +123,6 @@ namespace Content.Server.VendingMachines
             EjectRandom(uid, throwItem: true, forceEject: false, component);
         }
 
-        private void OnDoAfter(EntityUid uid, VendingMachineComponent component, DoAfterEvent args)
-        {
-            if (args.Handled || args.Cancelled || args.Args.Used == null)
-                return;
-
-            if (!TryComp<VendingMachineRestockComponent>(args.Args.Used, out var restockComponent))
-            {
-                Log.Error($"{ToPrettyString(args.Args.User)} tried to restock {ToPrettyString(uid)} with {ToPrettyString(args.Args.Used.Value)} which did not have a VendingMachineRestockComponent.");
-                return;
-            }
-
-            TryRestockInventory(uid, component);
-
-            Popup.PopupEntity(Loc.GetString("vending-machine-restock-done-self", ("target", uid)), args.Args.User, args.Args.User, PopupType.Medium);
-            var othersFilter = Filter.PvsExcept(args.Args.User);
-            Popup.PopupEntity(Loc.GetString("vending-machine-restock-done-others", ("user", Identity.Entity(args.User, EntityManager)), ("target", uid)), args.Args.User, othersFilter, true, PopupType.Medium);
-
-            Audio.PlayPvs(restockComponent.SoundRestockDone, uid, AudioParams.Default.WithVolume(-2f).WithVariation(0.2f));
-
-            Del(args.Args.Used.Value);
-
-            args.Handled = true;
-        }
-
         /// <summary>
         /// Sets the <see cref="VendingMachineComponent.CanShoot"/> property of the vending machine.
         /// </summary>
@@ -259,7 +225,7 @@ namespace Content.Server.VendingMachines
             var disabled = EntityQueryEnumerator<EmpDisabledComponent, VendingMachineComponent>();
             while (disabled.MoveNext(out var uid, out _, out var comp))
             {
-                if (comp.NextEmpEject < _timing.CurTime)
+                if (comp.NextEmpEject < Timing.CurTime)
                 {
                     EjectRandom(uid, true, false, comp);
                     comp.NextEmpEject += (5 * comp.EjectDelay);
@@ -267,17 +233,6 @@ namespace Content.Server.VendingMachines
             }
         }
 
-        public void TryRestockInventory(EntityUid uid, VendingMachineComponent? vendComponent = null)
-        {
-            if (!Resolve(uid, ref vendComponent))
-                return;
-
-            RestockInventoryFromPrototype(uid, vendComponent);
-
-            Dirty(uid, vendComponent);
-            TryUpdateVisualState((uid, vendComponent));
-        }
-
         private void OnPriceCalculation(EntityUid uid, VendingMachineRestockComponent component, ref PriceCalculationEvent args)
         {
             List<double> priceSets = new();
@@ -308,7 +263,7 @@ namespace Content.Server.VendingMachines
             {
                 args.Affected = true;
                 args.Disabled = true;
-                component.NextEmpEject = _timing.CurTime;
+                component.NextEmpEject = Timing.CurTime;
             }
         }
 
index 00355dedff83131656cc65869fa50299c4154905..8c62fadfd295d9d7025d51ed1a5d9975ab44b820 100644 (file)
@@ -3,7 +3,6 @@ using Content.Shared.IdentityManagement;
 using Content.Shared.Interaction;
 using Content.Shared.Popups;
 using Content.Shared.Wires;
-using Robust.Shared.Audio;
 
 namespace Content.Shared.VendingMachines;
 
@@ -46,6 +45,17 @@ public abstract partial class SharedVendingMachineSystem
         return true;
     }
 
+    public void TryRestockInventory(EntityUid uid, VendingMachineComponent? vendComponent = null)
+    {
+        if (!Resolve(uid, ref vendComponent))
+            return;
+
+        RestockInventoryFromPrototype(uid, vendComponent);
+
+        Dirty(uid, vendComponent);
+        TryUpdateVisualState((uid, vendComponent));
+    }
+
     private void OnAfterInteract(EntityUid uid, VendingMachineRestockComponent component, AfterInteractEvent args)
     {
         if (args.Target is not { } target || !args.CanReach || args.Handled)
@@ -62,8 +72,13 @@ public abstract partial class SharedVendingMachineSystem
 
         args.Handled = true;
 
-        var doAfterArgs = new DoAfterArgs(EntityManager, args.User, (float)component.RestockDelay.TotalSeconds, new RestockDoAfterEvent(), target,
-            target: target, used: uid)
+        var doAfterArgs = new DoAfterArgs(EntityManager,
+            args.User,
+            component.RestockDelay,
+            new RestockDoAfterEvent(),
+            target,
+            target: target,
+            used: uid)
         {
             BreakOnMove = true,
             BreakOnDamage = true,
@@ -74,13 +89,48 @@ public abstract partial class SharedVendingMachineSystem
             return;
 
         var selfMessage = Loc.GetString("vending-machine-restock-start-self", ("target", target));
-        var othersMessage = Loc.GetString("vending-machine-restock-start-others", ("user", Identity.Entity(args.User, EntityManager)), ("target", target));
-        Popup.PopupPredicted(selfMessage,
-            othersMessage,
-            uid,
-            args.User,
-            PopupType.Medium);
+        var othersMessage = Loc.GetString("vending-machine-restock-start-others",
+            ("user", Identity.Entity(args.User, EntityManager)),
+            ("target", target));
+        Popup.PopupPredicted(selfMessage, othersMessage, target, args.User, PopupType.Medium);
+
+
+        if (!Timing.IsFirstTimePredicted)
+            return;
+
+        Audio.Stop(machineComponent.RestockStream);
+        machineComponent.RestockStream = Audio.PlayPredicted(component.SoundRestockStart, target, args.User)?.Entity;
+    }
+
+    private void OnRestockDoAfter(Entity<VendingMachineComponent> ent, ref RestockDoAfterEvent args)
+    {
+        if (args.Cancelled)
+        {
+            // Future predicted ticks can clobber the RestockStream with null while not stopping anything
+            if (Timing.IsFirstTimePredicted)
+                ent.Comp.RestockStream = Audio.Stop(ent.Comp.RestockStream);
+            return;
+        }
+
+        if (args.Handled || args.Used == null)
+            return;
+
+        if (!TryComp<VendingMachineRestockComponent>(args.Used, out var restockComponent))
+        {
+            Log.Error($"{ToPrettyString(args.User)} tried to restock {ToPrettyString(ent)} with {ToPrettyString(args.Used.Value)} which did not have a VendingMachineRestockComponent.");
+            return;
+        }
+
+        TryRestockInventory(ent, ent.Comp);
+
+        var userMessage = Loc.GetString("vending-machine-restock-done-self", ("target", ent));
+        var othersMessage = Loc.GetString("vending-machine-restock-done-others",
+            ("user", Identity.Entity(args.User, EntityManager)),
+            ("target", ent));
+        Popup.PopupPredicted(userMessage, othersMessage, ent, args.User, PopupType.Medium);
+
+        Audio.PlayPredicted(restockComponent.SoundRestockDone, ent, args.User);
 
-        Audio.PlayPredicted(component.SoundRestockStart, uid, args.User);
+        PredictedQueueDel(args.Used.Value);
     }
 }
index 22b8d1867497249673e5da773c1e187eef8a97d4..141183873d1d7f462a12d8c7b582b6a747807198 100644 (file)
@@ -41,6 +41,7 @@ public abstract partial class SharedVendingMachineSystem : EntitySystem
         SubscribeLocalEvent<VendingMachineComponent, ComponentGetState>(OnVendingGetState);
         SubscribeLocalEvent<VendingMachineComponent, MapInitEvent>(OnMapInit);
         SubscribeLocalEvent<VendingMachineComponent, GotEmaggedEvent>(OnEmagged);
+        SubscribeLocalEvent<VendingMachineComponent, RestockDoAfterEvent>(OnRestockDoAfter);
 
         SubscribeLocalEvent<VendingMachineRestockComponent, AfterInteractEvent>(OnAfterInteract);
 
index 32cd0ca382485d0aacc6f9d1938badd8ddd3952b..6a9d650898d98c8367dc97925e9803836b41af52 100644 (file)
@@ -139,6 +139,12 @@ namespace Content.Shared.VendingMachines
         [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
         public TimeSpan NextEmpEject = TimeSpan.Zero;
 
+        /// <summary>
+        /// Audio entity used during restock in case the doafter gets canceled.
+        /// </summary>
+        [DataField]
+        public EntityUid? RestockStream;
+
         #region Client Visuals
         /// <summary>
         /// RSI state for when the vending machine is unpowered.
index 1b5f07ae6a09b676ca6cbae69e30f77d367bdb7a..02806b3adb5d97e79f1bb16f0d384ab1b5f0edd2 100644 (file)
@@ -12,23 +12,20 @@ public sealed partial class VendingMachineRestockComponent : Component
     /// <summary>
     /// The time (in seconds) that it takes to restock a machine.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("restockDelay")]
+    [DataField]
     public TimeSpan RestockDelay = TimeSpan.FromSeconds(5.0f);
 
     /// <summary>
     /// What sort of machine inventory does this restock?
     /// This is checked against the VendingMachineComponent's pack value.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("canRestock", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<VendingMachineInventoryPrototype>))]
-    public HashSet<string> CanRestock = new();
+    [DataField(customTypeSerializer: typeof(PrototypeIdHashSetSerializer<VendingMachineInventoryPrototype>))]
+    public HashSet<string> CanRestock = [];
 
     /// <summary>
     ///     Sound that plays when starting to restock a machine.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("soundRestockStart")]
+    [DataField]
     public SoundSpecifier SoundRestockStart = new SoundPathSpecifier("/Audio/Machines/vending_restock_start.ogg")
     {
         Params = new AudioParams
@@ -41,12 +38,10 @@ public sealed partial class VendingMachineRestockComponent : Component
     /// <summary>
     ///     Sound that plays when finished restocking a machine.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
-    [DataField("soundRestockDone")]
-    public SoundSpecifier SoundRestockDone = new SoundPathSpecifier("/Audio/Machines/vending_restock_done.ogg");
+    [DataField]
+    public SoundSpecifier SoundRestockDone = new SoundPathSpecifier("/Audio/Machines/vending_restock_done.ogg",
+        AudioParams.Default.WithVolume(-2f).WithVariation(0.2f));
 }
 
 [Serializable, NetSerializable]
-public sealed partial class RestockDoAfterEvent : SimpleDoAfterEvent
-{
-}
+public sealed partial class RestockDoAfterEvent : SimpleDoAfterEvent;