diff --git a/Content.Server/VendingMachines/VendingMachineSystem.cs b/Content.Server/VendingMachines/VendingMachineSystem.cs index 23a744ddd6..df204cf338 100644 --- a/Content.Server/VendingMachines/VendingMachineSystem.cs +++ b/Content.Server/VendingMachines/VendingMachineSystem.cs @@ -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(OnTryVocalize); SubscribeLocalEvent(OnActivatableUIOpenAttempt); - SubscribeLocalEvent(OnSelfDispense); - SubscribeLocalEvent(OnDoAfter); - SubscribeLocalEvent(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(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; - } - /// /// Sets the property of the vending machine. /// @@ -259,7 +225,7 @@ namespace Content.Server.VendingMachines var disabled = EntityQueryEnumerator(); 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 priceSets = new(); @@ -308,7 +263,7 @@ namespace Content.Server.VendingMachines { args.Affected = true; args.Disabled = true; - component.NextEmpEject = _timing.CurTime; + component.NextEmpEject = Timing.CurTime; } } diff --git a/Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs b/Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs index 00355dedff..8c62fadfd2 100644 --- a/Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs +++ b/Content.Shared/VendingMachines/SharedVendingMachineSystem.Restock.cs @@ -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); - Audio.PlayPredicted(component.SoundRestockStart, uid, args.User); + + if (!Timing.IsFirstTimePredicted) + return; + + Audio.Stop(machineComponent.RestockStream); + machineComponent.RestockStream = Audio.PlayPredicted(component.SoundRestockStart, target, args.User)?.Entity; + } + + private void OnRestockDoAfter(Entity 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(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); + + PredictedQueueDel(args.Used.Value); } } diff --git a/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs b/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs index 22b8d18674..141183873d 100644 --- a/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs +++ b/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs @@ -41,6 +41,7 @@ public abstract partial class SharedVendingMachineSystem : EntitySystem SubscribeLocalEvent(OnVendingGetState); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnEmagged); + SubscribeLocalEvent(OnRestockDoAfter); SubscribeLocalEvent(OnAfterInteract); diff --git a/Content.Shared/VendingMachines/VendingMachineComponent.cs b/Content.Shared/VendingMachines/VendingMachineComponent.cs index 32cd0ca382..6a9d650898 100644 --- a/Content.Shared/VendingMachines/VendingMachineComponent.cs +++ b/Content.Shared/VendingMachines/VendingMachineComponent.cs @@ -139,6 +139,12 @@ namespace Content.Shared.VendingMachines [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] public TimeSpan NextEmpEject = TimeSpan.Zero; + /// + /// Audio entity used during restock in case the doafter gets canceled. + /// + [DataField] + public EntityUid? RestockStream; + #region Client Visuals /// /// RSI state for when the vending machine is unpowered. diff --git a/Content.Shared/VendingMachines/VendingMachineRestockComponent.cs b/Content.Shared/VendingMachines/VendingMachineRestockComponent.cs index 1b5f07ae6a..02806b3adb 100644 --- a/Content.Shared/VendingMachines/VendingMachineRestockComponent.cs +++ b/Content.Shared/VendingMachines/VendingMachineRestockComponent.cs @@ -12,23 +12,20 @@ public sealed partial class VendingMachineRestockComponent : Component /// /// The time (in seconds) that it takes to restock a machine. /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("restockDelay")] + [DataField] 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(); + [DataField(customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet CanRestock = []; /// /// Sound that plays when starting to restock a machine. /// - [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 /// /// Sound that plays when finished restocking a machine. /// - [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;