From 7f3846b7c0aaa9bde25793725c66ace185494308 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Wed, 3 May 2023 01:38:03 -0400 Subject: [PATCH] Reduce vendor restocking time + some code cleanup (#16025) --- .../Tests/VendingMachineRestockTest.cs | 15 +- .../Behaviors/DumpRestockInventory.cs | 1 - .../Restock/VendingMachineRestockComponent.cs | 40 ----- .../Restock/VendingMachineRestockSystem.cs | 124 ---------------- .../VendingMachines/VendingMachineSystem.cs | 138 +++++++++++------- .../SharedVendingMachineSystem.Restock.cs | 92 ++++++++++++ .../SharedVendingMachineSystem.cs | 24 +-- .../VendingMachineComponent.cs | 8 +- .../VendingMachineRestockComponent.cs | 52 +++++++ 9 files changed, 252 insertions(+), 242 deletions(-) delete mode 100644 Content.Server/VendingMachines/Restock/VendingMachineRestockComponent.cs delete mode 100644 Content.Server/VendingMachines/Restock/VendingMachineRestockSystem.cs create mode 100644 Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs create mode 100644 Content.Shared/VendingMachines/VendingMachineRestockComponent.cs diff --git a/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs b/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs index 6ff6b23dc7..5760e00f0f 100644 --- a/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs +++ b/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs @@ -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(); var systemMachine = entitySystemManager.GetEntitySystem(); // 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(); // 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), diff --git a/Content.Server/Destructible/Thresholds/Behaviors/DumpRestockInventory.cs b/Content.Server/Destructible/Thresholds/Behaviors/DumpRestockInventory.cs index eae6a04109..56c4200fcb 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/DumpRestockInventory.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/DumpRestockInventory.cs @@ -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 index 944ce5bb0c..0000000000 --- a/Content.Server/VendingMachines/Restock/VendingMachineRestockComponent.cs +++ /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 - { - /// - /// The time (in seconds) that it takes to restock a machine. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("restockDelay")] - public TimeSpan RestockDelay = TimeSpan.FromSeconds(8.0f); - - /// - /// What sort of machine inventory does this restock? - /// This is checked against the VendingMachineComponent's pack value. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("canRestock", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet CanRestock = new(); - - /// - /// Sound that plays when starting to restock a machine. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("soundRestockStart")] - public SoundSpecifier SoundRestockStart = new SoundPathSpecifier("/Audio/Machines/vending_restock_start.ogg"); - - /// - /// Sound that plays when finished restocking a machine. - /// - [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 index a3fa90495c..0000000000 --- a/Content.Server/VendingMachines/Restock/VendingMachineRestockSystem.cs +++ /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(OnAfterInteract); - SubscribeLocalEvent(OnPriceCalculation); - } - - public bool TryAccessMachine(EntityUid uid, - VendingMachineRestockComponent restock, - VendingMachineComponent machineComponent, - EntityUid user, - EntityUid target) - { - if (!TryComp(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(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 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(); - } - } -} diff --git a/Content.Server/VendingMachines/VendingMachineSystem.cs b/Content.Server/VendingMachines/VendingMachineSystem.cs index 0fb12fd55b..2f31da2cd4 100644 --- a/Content.Server/VendingMachines/VendingMachineSystem.cs +++ b/Content.Server/VendingMachines/VendingMachineSystem.cs @@ -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(OnSelfDispense); SubscribeLocalEvent(OnDoAfter); + + SubscribeLocalEvent(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(entry.ID, out var proto)) + if (!PrototypeManager.TryIndex(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(component.Owner)) + if (HasComp(uid)) { TryUpdateVisualState(uid, component); } if (component.Action != null) { - var action = new InstantAction(_prototypeManager.Index(component.Action)); + var action = new InstantAction(PrototypeManager.Index(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); } /// /// Checks if the user is authorized to use this vending machine /// + /// /// Entity trying to use the vending machine - public bool IsAuthorized(EntityUid uid, EntityUid? sender, VendingMachineComponent? vendComponent = null) + /// + 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(vendComponent.Owner, out var accessReader)) - { - if (!_accessReader.IsAllowed(sender.Value, accessReader) && !HasComp(uid)) - { - _popupSystem.PopupEntity(Loc.GetString("vending-machine-component-try-eject-access-denied"), uid); - Deny(uid, vendComponent); - return false; - } - } - return true; + if (!TryComp(uid, out var accessReader)) + return true; + + if (_accessReader.IsAllowed(sender, accessReader) || HasComp(uid)) + return true; + + Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-access-denied"), uid); + Deny(uid, vendComponent); + return false; } /// /// 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. /// + /// /// The type of inventory the item is from /// The prototype ID of the item /// Whether the item should be thrown in a random direction after ejection + /// 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(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); } /// /// Checks whether the user is authorized to use the vending machine, then ejects the provided item if true /// + /// /// Entity that is trying to use the vending machine /// The type of inventory the item is from /// The prototype ID of the item + /// 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(vendComponent.Owner, out var appearance)) - { - _appearanceSystem.SetData(uid, VendingMachineVisuals.VisualState, finalState, appearance); - } + _appearanceSystem.SetData(uid, VendingMachineVisuals.VisualState, finalState); } /// /// Ejects a random item from the available stock. Will do nothing if the vending machine is empty. /// + /// /// Whether to throw the item in a random direction after dispensing it. /// Whether to skip the regular ejection checks and immediately dispense the item without animation. + /// 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(component.Owner)) + if (type == InventoryType.Emagged && HasComp(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()) + var query = EntityQueryEnumerator(); + 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 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 index 0000000000..2385982f8d --- /dev/null +++ b/Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs @@ -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(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(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); + } +} diff --git a/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs b/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs index 78c774c5b3..522138eb8b 100644 --- a/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs +++ b/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs @@ -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(OnComponentInit); + SubscribeLocalEvent(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(id)) + if (PrototypeManager.HasIndex(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 -{ -} diff --git a/Content.Shared/VendingMachines/VendingMachineComponent.cs b/Content.Shared/VendingMachines/VendingMachineComponent.cs index 52264aa4f3..85e1178d46 100644 --- a/Content.Shared/VendingMachines/VendingMachineComponent.cs +++ b/Content.Shared/VendingMachines/VendingMachineComponent.cs @@ -86,7 +86,13 @@ namespace Content.Shared.VendingMachines /// [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 + } + }; /// /// 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 index 0000000000..e73d7eb077 --- /dev/null +++ b/Content.Shared/VendingMachines/VendingMachineRestockComponent.cs @@ -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 +{ + /// + /// The time (in seconds) that it takes to restock a machine. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("restockDelay")] + public TimeSpan RestockDelay = TimeSpan.FromSeconds(5.0f); + + /// + /// What sort of machine inventory does this restock? + /// This is checked against the VendingMachineComponent's pack value. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("canRestock", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet CanRestock = new(); + + /// + /// Sound that plays when starting to restock a machine. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("soundRestockStart")] + public SoundSpecifier SoundRestockStart = new SoundPathSpecifier("/Audio/Machines/vending_restock_start.ogg") + { + Params = new AudioParams + { + Volume = -2f, + Variation = 0.2f + } + }; + + /// + /// Sound that plays when finished restocking a machine. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("soundRestockDone")] + public SoundSpecifier SoundRestockDone = new SoundPathSpecifier("/Audio/Machines/vending_restock_done.ogg"); +} + +[Serializable, NetSerializable] +public sealed class RestockDoAfterEvent : SimpleDoAfterEvent +{ +} -- 2.51.2