]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Reduce vendor restocking time + some code cleanup (#16025)
authorNemanja <98561806+EmoGarbage404@users.noreply.github.com>
Wed, 3 May 2023 05:38:03 +0000 (01:38 -0400)
committerGitHub <noreply@github.com>
Wed, 3 May 2023 05:38:03 +0000 (15:38 +1000)
Content.IntegrationTests/Tests/VendingMachineRestockTest.cs
Content.Server/Destructible/Thresholds/Behaviors/DumpRestockInventory.cs
Content.Server/VendingMachines/Restock/VendingMachineRestockComponent.cs [deleted file]
Content.Server/VendingMachines/Restock/VendingMachineRestockSystem.cs [deleted file]
Content.Server/VendingMachines/VendingMachineSystem.cs
Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs [new file with mode: 0644]
Content.Shared/VendingMachines/SharedVendingMachineSystem.cs
Content.Shared/VendingMachines/VendingMachineComponent.cs
Content.Shared/VendingMachines/VendingMachineRestockComponent.cs [new file with mode: 0644]

index 6ff6b23dc76fe6d29b779b9fdb7d9539caf3d070..5760e00f0f39cd9191912d596b4b6d2242291c9e 100644 (file)
@@ -3,12 +3,10 @@ using System.Collections.Generic;
 using System.Threading.Tasks;
 using NUnit.Framework;
 using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
 using Robust.Shared.Map;
 using Robust.Shared.Prototypes;
 using Content.Server.Storage.Components;
 using Content.Server.VendingMachines;
-using Content.Server.VendingMachines.Restock;
 using Content.Shared.Cargo.Prototypes;
 using Content.Shared.Damage;
 using Content.Shared.Damage.Prototypes;
@@ -20,7 +18,7 @@ namespace Content.IntegrationTests.Tests
 {
     [TestFixture]
     [TestOf(typeof(VendingMachineRestockComponent))]
-    [TestOf(typeof(VendingMachineRestockSystem))]
+    [TestOf(typeof(VendingMachineSystem))]
     public sealed class VendingMachineRestockTest : EntitySystem
     {
         private const string Prototypes = @"
@@ -209,25 +207,24 @@ namespace Content.IntegrationTests.Tests
                 Assert.True(entityManager.TryGetComponent(packageWrong, out restockWrongComponent!), $"Wrong package has no {nameof(VendingMachineRestockComponent)}");
                 Assert.True(entityManager.TryGetComponent(machine, out machineWiresPanel!), $"Machine has no {nameof(WiresPanelComponent)}");
 
-                var systemRestock = entitySystemManager.GetEntitySystem<VendingMachineRestockSystem>();
                 var systemMachine = entitySystemManager.GetEntitySystem<VendingMachineSystem>();
 
                 // Test that the panel needs to be opened first.
-                Assert.That(systemRestock.TryAccessMachine(packageRight, restockRightComponent, machineComponent, user, machine), Is.False, "Right package is able to restock without opened access panel");
-                Assert.That(systemRestock.TryAccessMachine(packageWrong, restockWrongComponent, machineComponent, user, machine), Is.False, "Wrong package is able to restock without opened access panel");
+                Assert.That(systemMachine.TryAccessMachine(packageRight, restockRightComponent, machineComponent, user, machine), Is.False, "Right package is able to restock without opened access panel");
+                Assert.That(systemMachine.TryAccessMachine(packageWrong, restockWrongComponent, machineComponent, user, machine), Is.False, "Wrong package is able to restock without opened access panel");
 
                 var systemWires = entitySystemManager.GetEntitySystem<WiresSystem>();
                 // Open the panel.
                 systemWires.TogglePanel(machine, machineWiresPanel, true);
 
                 // Test that the right package works for the right machine.
-                Assert.That(systemRestock.TryAccessMachine(packageRight, restockRightComponent, machineComponent, user, machine), Is.True, "Correct package is unable to restock with access panel opened");
+                Assert.That(systemMachine.TryAccessMachine(packageRight, restockRightComponent, machineComponent, user, machine), Is.True, "Correct package is unable to restock with access panel opened");
 
                 // Test that the wrong package does not work.
-                Assert.That(systemRestock.TryMatchPackageToMachine(packageWrong, restockWrongComponent, machineComponent, user, machine), Is.False, "Package with invalid canRestock is able to restock machine");
+                Assert.That(systemMachine.TryMatchPackageToMachine(packageWrong, restockWrongComponent, machineComponent, user, machine), Is.False, "Package with invalid canRestock is able to restock machine");
 
                 // Test that the right package does work.
-                Assert.That(systemRestock.TryMatchPackageToMachine(packageRight, restockRightComponent, machineComponent, user, machine), Is.True, "Package with valid canRestock is unable to restock machine");
+                Assert.That(systemMachine.TryMatchPackageToMachine(packageRight, restockRightComponent, machineComponent, user, machine), Is.True, "Package with valid canRestock is unable to restock machine");
 
                 // Make sure there's something in there to begin with.
                 Assert.That(systemMachine.GetAvailableInventory(machine, machineComponent).Count, Is.GreaterThan(0),
index eae6a04109b83b15afe138eb43c88e9532fc16f9..56c4200fcb799fc9bd796527e12aafbddaf158f1 100644 (file)
@@ -1,6 +1,5 @@
 using Robust.Shared.Random;
 using Content.Shared.Stacks;
-using Content.Server.VendingMachines.Restock;
 using Content.Shared.Prototypes;
 using Content.Shared.VendingMachines;
 
diff --git a/Content.Server/VendingMachines/Restock/VendingMachineRestockComponent.cs b/Content.Server/VendingMachines/Restock/VendingMachineRestockComponent.cs
deleted file mode 100644 (file)
index 944ce5b..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-using System.Threading;
-using Robust.Shared.Audio;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
-using Content.Shared.VendingMachines;
-
-namespace Content.Server.VendingMachines.Restock
-{
-    [RegisterComponent]
-    public sealed class VendingMachineRestockComponent : Component
-    {
-        /// <summary>
-        /// The time (in seconds) that it takes to restock a machine.
-        /// </summary>
-        [ViewVariables(VVAccess.ReadWrite)]
-        [DataField("restockDelay")]
-        public TimeSpan RestockDelay = TimeSpan.FromSeconds(8.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();
-
-        /// <summary>
-        ///     Sound that plays when starting to restock a machine.
-        /// </summary>
-        [ViewVariables(VVAccess.ReadWrite)]
-        [DataField("soundRestockStart")]
-        public SoundSpecifier SoundRestockStart = new SoundPathSpecifier("/Audio/Machines/vending_restock_start.ogg");
-
-        /// <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");
-    }
-}
diff --git a/Content.Server/VendingMachines/Restock/VendingMachineRestockSystem.cs b/Content.Server/VendingMachines/Restock/VendingMachineRestockSystem.cs
deleted file mode 100644 (file)
index a3fa904..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-using System.Linq;
-using Content.Server.Cargo.Systems;
-using Content.Shared.DoAfter;
-using Content.Shared.Interaction;
-using Content.Shared.Popups;
-using Content.Shared.VendingMachines;
-using Content.Shared.Wires;
-using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.VendingMachines.Restock
-{
-    public sealed class VendingMachineRestockSystem : EntitySystem
-    {
-        [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
-
-        [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
-        [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
-        [Dependency] private readonly AudioSystem _audioSystem = default!;
-        [Dependency] private readonly PricingSystem _pricingSystem = default!;
-
-        public override void Initialize()
-        {
-            base.Initialize();
-
-            SubscribeLocalEvent<VendingMachineRestockComponent, AfterInteractEvent>(OnAfterInteract);
-            SubscribeLocalEvent<VendingMachineRestockComponent, PriceCalculationEvent>(OnPriceCalculation);
-        }
-
-        public bool TryAccessMachine(EntityUid uid,
-            VendingMachineRestockComponent restock,
-            VendingMachineComponent machineComponent,
-            EntityUid user,
-            EntityUid target)
-        {
-            if (!TryComp<WiresPanelComponent>(target, out var panel) || !panel.Open)
-            {
-                _popupSystem.PopupCursor(Loc.GetString("vending-machine-restock-needs-panel-open",
-                        ("this", uid),
-                        ("user", user),
-                        ("target", target)),
-                    user);
-                return false;
-            }
-
-            return true;
-        }
-
-        public bool TryMatchPackageToMachine(EntityUid uid,
-            VendingMachineRestockComponent component,
-            VendingMachineComponent machineComponent,
-            EntityUid user,
-            EntityUid target)
-        {
-            if (!component.CanRestock.Contains(machineComponent.PackPrototypeId))
-            {
-                _popupSystem.PopupCursor(Loc.GetString("vending-machine-restock-invalid-inventory", ("this", uid), ("user", user), ("target", target)), user);
-                return false;
-            }
-
-            return true;
-        }
-
-        private void OnAfterInteract(EntityUid uid, VendingMachineRestockComponent component, AfterInteractEvent args)
-        {
-            if (args.Target == null || !args.CanReach || args.Handled)
-                return;
-
-            if (!TryComp<VendingMachineComponent>(args.Target, out var machineComponent))
-                return;
-
-            if (!TryMatchPackageToMachine(uid, component, machineComponent, args.User, args.Target.Value))
-                return;
-
-            if (!TryAccessMachine(uid, component, machineComponent, args.User, args.Target.Value))
-                return;
-
-            args.Handled = true;
-
-            var doAfterArgs = new DoAfterArgs(args.User, (float) component.RestockDelay.TotalSeconds, new RestockDoAfterEvent(), args.Target,
-                target: args.Target, used: uid)
-            {
-                BreakOnTargetMove = true,
-                BreakOnUserMove = true,
-                BreakOnDamage = true,
-                NeedHand = true
-            };
-
-            if (!_doAfterSystem.TryStartDoAfter(doAfterArgs))
-                return;
-
-            _popupSystem.PopupEntity(Loc.GetString("vending-machine-restock-start", ("this", uid), ("user", args.User), ("target", args.Target)),
-                args.User,
-                PopupType.Medium);
-
-            _audioSystem.PlayPvs(component.SoundRestockStart, component.Owner, AudioParams.Default.WithVolume(-2f).WithVariation(0.2f));
-        }
-
-        private void OnPriceCalculation(EntityUid uid, VendingMachineRestockComponent component, ref PriceCalculationEvent args)
-        {
-            List<double> priceSets = new();
-
-            // Find the most expensive inventory and use that as the highest price.
-            foreach (var vendingInventory in component.CanRestock)
-            {
-                double total = 0;
-
-                if (_prototypeManager.TryIndex(vendingInventory, out VendingMachineInventoryPrototype? inventoryPrototype))
-                {
-                    foreach (var (item, amount) in inventoryPrototype.StartingInventory)
-                    {
-                        if (_prototypeManager.TryIndex(item, out EntityPrototype? entity))
-                            total += _pricingSystem.GetEstimatedPrice(entity) * amount;
-                    }
-                }
-
-                priceSets.Add(total);
-            }
-
-            args.Price += priceSets.Max();
-        }
-    }
-}
index 0fb12fd55b9d0ffe4ba82c32a22a760810e598a3..2f31da2cd450df67ac3808751d74fd7c241aa0c2 100644 (file)
@@ -1,9 +1,8 @@
+using System.Linq;
 using Content.Server.Cargo.Systems;
-using Content.Server.Popups;
 using Content.Server.Power.Components;
 using Content.Server.Power.EntitySystems;
 using Content.Server.UserInterface;
-using Content.Server.VendingMachines.Restock;
 using Content.Shared.Access.Components;
 using Content.Shared.Access.Systems;
 using Content.Shared.Actions;
@@ -26,11 +25,8 @@ namespace Content.Server.VendingMachines
     public sealed class VendingMachineSystem : SharedVendingMachineSystem
     {
         [Dependency] private readonly IRobustRandom _random = default!;
-        [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
         [Dependency] private readonly AccessReaderSystem _accessReader = default!;
         [Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
-        [Dependency] private readonly AudioSystem _audioSystem = default!;
-        [Dependency] private readonly PopupSystem _popupSystem = default!;
         [Dependency] private readonly SharedActionsSystem _action = default!;
         [Dependency] private readonly PricingSystem _pricing = default!;
         [Dependency] private readonly ThrowingSystem _throwingSystem = default!;
@@ -56,15 +52,17 @@ namespace Content.Server.VendingMachines
             SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense);
 
             SubscribeLocalEvent<VendingMachineComponent, RestockDoAfterEvent>(OnDoAfter);
+
+            SubscribeLocalEvent<VendingMachineRestockComponent, PriceCalculationEvent>(OnPriceCalculation);
         }
 
         private void OnVendingPrice(EntityUid uid, VendingMachineComponent component, ref PriceCalculationEvent args)
         {
             var price = 0.0;
 
-            foreach (var (id, entry) in component.Inventory)
+            foreach (var entry in component.Inventory.Values)
             {
-                if (!_prototypeManager.TryIndex<EntityPrototype>(entry.ID, out var proto))
+                if (!PrototypeManager.TryIndex<EntityPrototype>(entry.ID, out var proto))
                 {
                     _sawmill.Error($"Unable to find entity prototype {entry.ID} on {ToPrettyString(uid)} vending.");
                     continue;
@@ -80,14 +78,14 @@ namespace Content.Server.VendingMachines
         {
             base.OnComponentInit(uid, component, args);
 
-            if (HasComp<ApcPowerReceiverComponent>(component.Owner))
+            if (HasComp<ApcPowerReceiverComponent>(uid))
             {
                 TryUpdateVisualState(uid, component);
             }
 
             if (component.Action != null)
             {
-                var action = new InstantAction(_prototypeManager.Index<InstantActionPrototype>(component.Action));
+                var action = new InstantAction(PrototypeManager.Index<InstantActionPrototype>(component.Action));
                 _action.AddAction(uid, action, uid);
             }
         }
@@ -100,14 +98,14 @@ namespace Content.Server.VendingMachines
 
         private void OnBoundUIOpened(EntityUid uid, VendingMachineComponent component, BoundUIOpenedEvent args)
         {
-            UpdateVendingMachineInterfaceState(component);
+            UpdateVendingMachineInterfaceState(uid, component);
         }
 
-        private void UpdateVendingMachineInterfaceState(VendingMachineComponent component)
+        private void UpdateVendingMachineInterfaceState(EntityUid uid, VendingMachineComponent component)
         {
-            var state = new VendingMachineInterfaceState(GetAllInventory(component.Owner, component));
+            var state = new VendingMachineInterfaceState(GetAllInventory(uid, component));
 
-            _userInterfaceSystem.TrySetUiState(component.Owner, VendingMachineUiKey.Key, state);
+            _userInterfaceSystem.TrySetUiState(uid, VendingMachineUiKey.Key, state);
         }
 
         private void OnInventoryEjectMessage(EntityUid uid, VendingMachineComponent component, VendingMachineEjectMessage args)
@@ -175,9 +173,9 @@ namespace Content.Server.VendingMachines
 
             TryRestockInventory(uid, component);
 
-            _popupSystem.PopupEntity(Loc.GetString("vending-machine-restock-done", ("this", args.Args.Used), ("user", args.Args.User), ("target", uid)), args.Args.User, PopupType.Medium);
+            Popup.PopupEntity(Loc.GetString("vending-machine-restock-done", ("this", args.Args.Used), ("user", args.Args.User), ("target", uid)), args.Args.User, PopupType.Medium);
 
-            _audioSystem.PlayPvs(restockComponent.SoundRestockDone, uid, AudioParams.Default.WithVolume(-2f).WithVariation(0.2f));
+            Audio.PlayPvs(restockComponent.SoundRestockDone, uid, AudioParams.Default.WithVolume(-2f).WithVariation(0.2f));
 
             Del(args.Args.Used.Value);
 
@@ -204,38 +202,41 @@ namespace Content.Server.VendingMachines
                 return;
 
             vendComponent.Denying = true;
-            _audioSystem.PlayPvs(vendComponent.SoundDeny, vendComponent.Owner, AudioParams.Default.WithVolume(-2f));
+            Audio.PlayPvs(vendComponent.SoundDeny, uid, AudioParams.Default.WithVolume(-2f));
             TryUpdateVisualState(uid, vendComponent);
         }
 
         /// <summary>
         /// Checks if the user is authorized to use this vending machine
         /// </summary>
+        /// <param name="uid"></param>
         /// <param name="sender">Entity trying to use the vending machine</param>
-        public bool IsAuthorized(EntityUid uid, EntityUid? sender, VendingMachineComponent? vendComponent = null)
+        /// <param name="vendComponent"></param>
+        public bool IsAuthorized(EntityUid uid, EntityUid sender, VendingMachineComponent? vendComponent = null)
         {
-            if (!Resolve(uid, ref vendComponent) || sender == null)
+            if (!Resolve(uid, ref vendComponent))
                 return false;
 
-            if (TryComp<AccessReaderComponent?>(vendComponent.Owner, out var accessReader))
-            {
-                if (!_accessReader.IsAllowed(sender.Value, accessReader) && !HasComp<EmaggedComponent>(uid))
-                {
-                    _popupSystem.PopupEntity(Loc.GetString("vending-machine-component-try-eject-access-denied"), uid);
-                    Deny(uid, vendComponent);
-                    return false;
-                }
-            }
-            return true;
+            if (!TryComp<AccessReaderComponent?>(uid, out var accessReader))
+                return true;
+
+            if (_accessReader.IsAllowed(sender, accessReader) || HasComp<EmaggedComponent>(uid))
+                return true;
+
+            Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-access-denied"), uid);
+            Deny(uid, vendComponent);
+            return false;
         }
 
         /// <summary>
         /// Tries to eject the provided item. Will do nothing if the vending machine is incapable of ejecting, already ejecting
         /// or the item doesn't exist in its inventory.
         /// </summary>
+        /// <param name="uid"></param>
         /// <param name="type">The type of inventory the item is from</param>
         /// <param name="itemId">The prototype ID of the item</param>
         /// <param name="throwItem">Whether the item should be thrown in a random direction after ejection</param>
+        /// <param name="vendComponent"></param>
         public void TryEjectVendorItem(EntityUid uid, InventoryType type, string itemId, bool throwItem, VendingMachineComponent? vendComponent = null)
         {
             if (!Resolve(uid, ref vendComponent))
@@ -246,18 +247,18 @@ namespace Content.Server.VendingMachines
                 return;
             }
 
-            var entry = GetEntry(itemId, type, vendComponent);
+            var entry = GetEntry(uid, itemId, type, vendComponent);
 
             if (entry == null)
             {
-                _popupSystem.PopupEntity(Loc.GetString("vending-machine-component-try-eject-invalid-item"), uid);
+                Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-invalid-item"), uid);
                 Deny(uid, vendComponent);
                 return;
             }
 
             if (entry.Amount <= 0)
             {
-                _popupSystem.PopupEntity(Loc.GetString("vending-machine-component-try-eject-out-of-stock"), uid);
+                Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-out-of-stock"), uid);
                 Deny(uid, vendComponent);
                 return;
             }
@@ -265,25 +266,25 @@ namespace Content.Server.VendingMachines
             if (string.IsNullOrEmpty(entry.ID))
                 return;
 
-            if (!TryComp<TransformComponent>(vendComponent.Owner, out var transformComp))
-                return;
 
             // Start Ejecting, and prevent users from ordering while anim playing
             vendComponent.Ejecting = true;
             vendComponent.NextItemToEject = entry.ID;
             vendComponent.ThrowNextItem = throwItem;
             entry.Amount--;
-            UpdateVendingMachineInterfaceState(vendComponent);
+            UpdateVendingMachineInterfaceState(uid, vendComponent);
             TryUpdateVisualState(uid, vendComponent);
-            _audioSystem.PlayPvs(vendComponent.SoundVend, vendComponent.Owner, AudioParams.Default.WithVolume(-2f));
+            Audio.PlayPvs(vendComponent.SoundVend, uid);
         }
 
         /// <summary>
         /// Checks whether the user is authorized to use the vending machine, then ejects the provided item if true
         /// </summary>
+        /// <param name="uid"></param>
         /// <param name="sender">Entity that is trying to use the vending machine</param>
         /// <param name="type">The type of inventory the item is from</param>
         /// <param name="itemId">The prototype ID of the item</param>
+        /// <param name="component"></param>
         public void AuthorizedVend(EntityUid uid, EntityUid sender, InventoryType type, string itemId, VendingMachineComponent component)
         {
             if (IsAuthorized(uid, sender, component))
@@ -318,17 +319,16 @@ namespace Content.Server.VendingMachines
                 finalState = VendingMachineVisualState.Off;
             }
 
-            if (TryComp<AppearanceComponent>(vendComponent.Owner, out var appearance))
-            {
-                _appearanceSystem.SetData(uid, VendingMachineVisuals.VisualState, finalState, appearance);
-            }
+            _appearanceSystem.SetData(uid, VendingMachineVisuals.VisualState, finalState);
         }
 
         /// <summary>
         /// Ejects a random item from the available stock. Will do nothing if the vending machine is empty.
         /// </summary>
+        /// <param name="uid"></param>
         /// <param name="throwItem">Whether to throw the item in a random direction after dispensing it.</param>
         /// <param name="forceEject">Whether to skip the regular ejection checks and immediately dispense the item without animation.</param>
+        /// <param name="vendComponent"></param>
         public void EjectRandom(EntityUid uid, bool throwItem, bool forceEject = false, VendingMachineComponent? vendComponent = null)
         {
             if (!Resolve(uid, ref vendComponent))
@@ -336,9 +336,7 @@ namespace Content.Server.VendingMachines
 
             var availableItems = GetAvailableInventory(uid, vendComponent);
             if (availableItems.Count <= 0)
-            {
                 return;
-            }
 
             var item = _random.Pick(availableItems);
 
@@ -346,20 +344,25 @@ namespace Content.Server.VendingMachines
             {
                 vendComponent.NextItemToEject = item.ID;
                 vendComponent.ThrowNextItem = throwItem;
-                var entry = GetEntry(item.ID, item.Type, vendComponent);
+                var entry = GetEntry(uid, item.ID, item.Type, vendComponent);
                 if (entry != null)
                     entry.Amount--;
-                EjectItem(vendComponent, forceEject);
+                EjectItem(uid, vendComponent, forceEject);
             }
             else
+            {
                 TryEjectVendorItem(uid, item.Type, item.ID, throwItem, vendComponent);
+            }
         }
 
-        private void EjectItem(VendingMachineComponent vendComponent, bool forceEject = false)
+        private void EjectItem(EntityUid uid, VendingMachineComponent? vendComponent = null, bool forceEject = false)
         {
+            if (!Resolve(uid, ref vendComponent))
+                return;
+
             // No need to update the visual state because we never changed it during a forced eject
             if (!forceEject)
-                TryUpdateVisualState(vendComponent.Owner, vendComponent);
+                TryUpdateVisualState(uid, vendComponent);
 
             if (string.IsNullOrEmpty(vendComponent.NextItemToEject))
             {
@@ -367,7 +370,7 @@ namespace Content.Server.VendingMachines
                 return;
             }
 
-            var ent = EntityManager.SpawnEntity(vendComponent.NextItemToEject, Transform(vendComponent.Owner).Coordinates);
+            var ent = Spawn(vendComponent.NextItemToEject, Transform(uid).Coordinates);
             if (vendComponent.ThrowNextItem)
             {
                 var range = vendComponent.NonLimitedEjectRange;
@@ -379,14 +382,12 @@ namespace Content.Server.VendingMachines
             vendComponent.ThrowNextItem = false;
         }
 
-        private void DenyItem(VendingMachineComponent vendComponent)
+        private VendingMachineInventoryEntry? GetEntry(EntityUid uid, string entryId, InventoryType type, VendingMachineComponent? component = null)
         {
-            TryUpdateVisualState(vendComponent.Owner, vendComponent);
-        }
+            if (!Resolve(uid, ref component))
+                return null;
 
-        private VendingMachineInventoryEntry? GetEntry(string entryId, InventoryType type, VendingMachineComponent component)
-        {
-            if (type == InventoryType.Emagged && HasComp<EmaggedComponent>(component.Owner))
+            if (type == InventoryType.Emagged && HasComp<EmaggedComponent>(uid))
                 return component.EmaggedInventory.GetValueOrDefault(entryId);
 
             if (type == InventoryType.Contraband && component.Contraband)
@@ -399,7 +400,8 @@ namespace Content.Server.VendingMachines
         {
             base.Update(frameTime);
 
-            foreach (var comp in EntityQuery<VendingMachineComponent>())
+            var query = EntityQueryEnumerator<VendingMachineComponent>();
+            while (query.MoveNext(out var uid, out var comp))
             {
                 if (comp.Ejecting)
                 {
@@ -409,7 +411,7 @@ namespace Content.Server.VendingMachines
                         comp.EjectAccumulator = 0f;
                         comp.Ejecting = false;
 
-                        EjectItem(comp);
+                        EjectItem(uid, comp);
                     }
                 }
 
@@ -421,7 +423,7 @@ namespace Content.Server.VendingMachines
                         comp.DenyAccumulator = 0f;
                         comp.Denying = false;
 
-                        DenyItem(comp);
+                        TryUpdateVisualState(uid, comp);
                     }
                 }
 
@@ -444,8 +446,32 @@ namespace Content.Server.VendingMachines
 
             RestockInventoryFromPrototype(uid, vendComponent);
 
-            UpdateVendingMachineInterfaceState(vendComponent);
+            UpdateVendingMachineInterfaceState(uid, vendComponent);
             TryUpdateVisualState(uid, vendComponent);
         }
+
+        private void OnPriceCalculation(EntityUid uid, VendingMachineRestockComponent component, ref PriceCalculationEvent args)
+        {
+            List<double> priceSets = new();
+
+            // Find the most expensive inventory and use that as the highest price.
+            foreach (var vendingInventory in component.CanRestock)
+            {
+                double total = 0;
+
+                if (PrototypeManager.TryIndex(vendingInventory, out VendingMachineInventoryPrototype? inventoryPrototype))
+                {
+                    foreach (var (item, amount) in inventoryPrototype.StartingInventory)
+                    {
+                        if (PrototypeManager.TryIndex(item, out EntityPrototype? entity))
+                            total += _pricing.GetEstimatedPrice(entity) * amount;
+                    }
+                }
+
+                priceSets.Add(total);
+            }
+
+            args.Price += priceSets.Max();
+        }
     }
 }
diff --git a/Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs b/Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs
new file mode 100644 (file)
index 0000000..2385982
--- /dev/null
@@ -0,0 +1,92 @@
+using Content.Shared.DoAfter;
+using Content.Shared.Interaction;
+using Content.Shared.Popups;
+using Content.Shared.Wires;
+using Robust.Shared.Audio;
+
+namespace Content.Shared.VendingMachines;
+
+public abstract partial class SharedVendingMachineSystem
+{
+    public bool TryAccessMachine(EntityUid uid,
+        VendingMachineRestockComponent restock,
+        VendingMachineComponent machineComponent,
+        EntityUid user,
+        EntityUid target)
+    {
+        if (!TryComp<WiresPanelComponent>(target, out var panel) || !panel.Open)
+        {
+            if (_net.IsServer)
+            {
+                Popup.PopupCursor(Loc.GetString("vending-machine-restock-needs-panel-open",
+                        ("this", uid),
+                        ("user", user),
+                        ("target", target)),
+                    user);
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    public bool TryMatchPackageToMachine(EntityUid uid,
+        VendingMachineRestockComponent component,
+        VendingMachineComponent machineComponent,
+        EntityUid user,
+        EntityUid target)
+    {
+        if (!component.CanRestock.Contains(machineComponent.PackPrototypeId))
+        {
+            if (_net.IsServer)
+            {
+                Popup.PopupCursor(Loc.GetString("vending-machine-restock-invalid-inventory", ("this", uid), ("user", user),
+                        ("target", target)), user);
+            }
+
+            return false;
+        }
+
+        return true;
+    }
+
+    private void OnAfterInteract(EntityUid uid, VendingMachineRestockComponent component, AfterInteractEvent args)
+    {
+        if (args.Target is not { } target || !args.CanReach || args.Handled)
+            return;
+
+        if (!TryComp<VendingMachineComponent>(args.Target, out var machineComponent))
+            return;
+
+        if (!TryMatchPackageToMachine(uid, component, machineComponent, args.User, target))
+            return;
+
+        if (!TryAccessMachine(uid, component, machineComponent, args.User, target))
+            return;
+
+        args.Handled = true;
+
+        var doAfterArgs = new DoAfterArgs(args.User, (float) component.RestockDelay.TotalSeconds, new RestockDoAfterEvent(), target,
+            target: target, used: uid)
+        {
+            BreakOnTargetMove = true,
+            BreakOnUserMove = true,
+            BreakOnDamage = true,
+            NeedHand = true
+        };
+
+        if (!_doAfter.TryStartDoAfter(doAfterArgs))
+            return;
+
+        if (_net.IsServer)
+        {
+            Popup.PopupEntity(Loc.GetString("vending-machine-restock-start", ("this", uid), ("user", args.User),
+                    ("target", target)),
+                args.User,
+                PopupType.Medium);
+        }
+
+        Audio.PlayPredicted(component.SoundRestockStart, uid, args.User);
+    }
+}
index 78c774c5b357650d389907221de7c147c3b68d1c..522138eb8b51b22e0b992e6147558eeea2bd0ff7 100644 (file)
@@ -2,18 +2,25 @@ using Content.Shared.Emag.Components;
 using Robust.Shared.Prototypes;
 using System.Linq;
 using Content.Shared.DoAfter;
-using Robust.Shared.Serialization;
+using Content.Shared.Interaction;
+using Content.Shared.Popups;
+using Robust.Shared.Network;
 
 namespace Content.Shared.VendingMachines;
 
-public abstract class SharedVendingMachineSystem : EntitySystem
+public abstract partial class SharedVendingMachineSystem : EntitySystem
 {
-    [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+    [Dependency] private readonly INetManager _net = default!;
+    [Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
+    [Dependency] protected readonly SharedAudioSystem Audio = default!;
+    [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
+    [Dependency] protected readonly SharedPopupSystem Popup = default!;
 
     public override void Initialize()
     {
         base.Initialize();
         SubscribeLocalEvent<VendingMachineComponent, ComponentInit>(OnComponentInit);
+        SubscribeLocalEvent<VendingMachineRestockComponent, AfterInteractEvent>(OnAfterInteract);
     }
 
     protected virtual void OnComponentInit(EntityUid uid, VendingMachineComponent component, ComponentInit args)
@@ -29,7 +36,7 @@ public abstract class SharedVendingMachineSystem : EntitySystem
             return;
         }
 
-        if (!_prototypeManager.TryIndex(component.PackPrototypeId, out VendingMachineInventoryPrototype? packPrototype))
+        if (!PrototypeManager.TryIndex(component.PackPrototypeId, out VendingMachineInventoryPrototype? packPrototype))
             return;
 
         AddInventoryFromPrototype(uid, packPrototype.StartingInventory, InventoryType.Regular, component);
@@ -96,9 +103,9 @@ public abstract class SharedVendingMachineSystem : EntitySystem
 
         foreach (var (id, amount) in entries)
         {
-            if (_prototypeManager.HasIndex<EntityPrototype>(id))
+            if (PrototypeManager.HasIndex<EntityPrototype>(id))
             {
-                if (inventory.TryGetValue(id, out VendingMachineInventoryEntry? entry))
+                if (inventory.TryGetValue(id, out var entry))
                     // Prevent a machine's stock from going over three times
                     // the prototype's normal amount. This is an arbitrary
                     // number and meant to be a convenience for someone
@@ -112,8 +119,3 @@ public abstract class SharedVendingMachineSystem : EntitySystem
         }
     }
 }
-
-[Serializable, NetSerializable]
-public sealed class RestockDoAfterEvent : SimpleDoAfterEvent
-{
-}
index 52264aa4f3f0dcb06b76ca894f7c5ef3358ca1cb..85e1178d46872c41101ed6ada4499bc64fcd791c 100644 (file)
@@ -86,7 +86,13 @@ namespace Content.Shared.VendingMachines
         /// </summary>
         [DataField("soundVend")]
         // Grabbed from: https://github.com/discordia-space/CEV-Eris/blob/f702afa271136d093ddeb415423240a2ceb212f0/sound/machines/vending_drop.ogg
-        public SoundSpecifier SoundVend = new SoundPathSpecifier("/Audio/Machines/machine_vend.ogg");
+        public SoundSpecifier SoundVend = new SoundPathSpecifier("/Audio/Machines/machine_vend.ogg")
+        {
+            Params = new AudioParams
+            {
+                Volume = -2f
+            }
+        };
 
         /// <summary>
         ///     Sound that plays when an item can't be ejected
diff --git a/Content.Shared/VendingMachines/VendingMachineRestockComponent.cs b/Content.Shared/VendingMachines/VendingMachineRestockComponent.cs
new file mode 100644 (file)
index 0000000..e73d7eb
--- /dev/null
@@ -0,0 +1,52 @@
+using Content.Shared.DoAfter;
+using Robust.Shared.Audio;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.VendingMachines;
+
+[RegisterComponent, NetworkedComponent, Access(typeof(SharedVendingMachineSystem))]
+public sealed class VendingMachineRestockComponent : Component
+{
+    /// <summary>
+    /// The time (in seconds) that it takes to restock a machine.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite)]
+    [DataField("restockDelay")]
+    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();
+
+    /// <summary>
+    ///     Sound that plays when starting to restock a machine.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite)]
+    [DataField("soundRestockStart")]
+    public SoundSpecifier SoundRestockStart = new SoundPathSpecifier("/Audio/Machines/vending_restock_start.ogg")
+    {
+        Params = new AudioParams
+        {
+            Volume = -2f,
+            Variation = 0.2f
+        }
+    };
+
+    /// <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");
+}
+
+[Serializable, NetSerializable]
+public sealed class RestockDoAfterEvent : SimpleDoAfterEvent
+{
+}