Make vending machine restocks predicted (and its sound not spammable) (#38609)
* 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>
This commit is contained in:
@@ -8,20 +8,14 @@ using Content.Server.Vocalization.Systems;
|
|||||||
using Content.Shared.Cargo;
|
using Content.Shared.Cargo;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Destructible;
|
using Content.Shared.Destructible;
|
||||||
using Content.Shared.DoAfter;
|
|
||||||
using Content.Shared.Emp;
|
using Content.Shared.Emp;
|
||||||
using Content.Shared.IdentityManagement;
|
|
||||||
using Content.Shared.Popups;
|
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Throwing;
|
using Content.Shared.Throwing;
|
||||||
using Content.Shared.UserInterface;
|
using Content.Shared.UserInterface;
|
||||||
using Content.Shared.VendingMachines;
|
using Content.Shared.VendingMachines;
|
||||||
using Content.Shared.Wall;
|
using Content.Shared.Wall;
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Timing;
|
|
||||||
|
|
||||||
namespace Content.Server.VendingMachines
|
namespace Content.Server.VendingMachines
|
||||||
{
|
{
|
||||||
@@ -30,7 +24,6 @@ namespace Content.Server.VendingMachines
|
|||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly PricingSystem _pricing = default!;
|
[Dependency] private readonly PricingSystem _pricing = default!;
|
||||||
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
|
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
|
||||||
|
|
||||||
private const float WallVendEjectDistanceFromWall = 1f;
|
private const float WallVendEjectDistanceFromWall = 1f;
|
||||||
|
|
||||||
@@ -46,11 +39,8 @@ namespace Content.Server.VendingMachines
|
|||||||
SubscribeLocalEvent<VendingMachineComponent, TryVocalizeEvent>(OnTryVocalize);
|
SubscribeLocalEvent<VendingMachineComponent, TryVocalizeEvent>(OnTryVocalize);
|
||||||
|
|
||||||
SubscribeLocalEvent<VendingMachineComponent, ActivatableUIOpenAttemptEvent>(OnActivatableUIOpenAttempt);
|
SubscribeLocalEvent<VendingMachineComponent, ActivatableUIOpenAttemptEvent>(OnActivatableUIOpenAttempt);
|
||||||
|
|
||||||
SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense);
|
SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense);
|
||||||
|
|
||||||
SubscribeLocalEvent<VendingMachineComponent, RestockDoAfterEvent>(OnDoAfter);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<VendingMachineRestockComponent, PriceCalculationEvent>(OnPriceCalculation);
|
SubscribeLocalEvent<VendingMachineRestockComponent, PriceCalculationEvent>(OnPriceCalculation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,30 +123,6 @@ namespace Content.Server.VendingMachines
|
|||||||
EjectRandom(uid, throwItem: true, forceEject: false, component);
|
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>
|
/// <summary>
|
||||||
/// Sets the <see cref="VendingMachineComponent.CanShoot"/> property of the vending machine.
|
/// Sets the <see cref="VendingMachineComponent.CanShoot"/> property of the vending machine.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -259,7 +225,7 @@ namespace Content.Server.VendingMachines
|
|||||||
var disabled = EntityQueryEnumerator<EmpDisabledComponent, VendingMachineComponent>();
|
var disabled = EntityQueryEnumerator<EmpDisabledComponent, VendingMachineComponent>();
|
||||||
while (disabled.MoveNext(out var uid, out _, out var comp))
|
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);
|
EjectRandom(uid, true, false, comp);
|
||||||
comp.NextEmpEject += (5 * comp.EjectDelay);
|
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)
|
private void OnPriceCalculation(EntityUid uid, VendingMachineRestockComponent component, ref PriceCalculationEvent args)
|
||||||
{
|
{
|
||||||
List<double> priceSets = new();
|
List<double> priceSets = new();
|
||||||
@@ -308,7 +263,7 @@ namespace Content.Server.VendingMachines
|
|||||||
{
|
{
|
||||||
args.Affected = true;
|
args.Affected = true;
|
||||||
args.Disabled = true;
|
args.Disabled = true;
|
||||||
component.NextEmpEject = _timing.CurTime;
|
component.NextEmpEject = Timing.CurTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using Content.Shared.IdentityManagement;
|
|||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Wires;
|
using Content.Shared.Wires;
|
||||||
using Robust.Shared.Audio;
|
|
||||||
|
|
||||||
namespace Content.Shared.VendingMachines;
|
namespace Content.Shared.VendingMachines;
|
||||||
|
|
||||||
@@ -46,6 +45,17 @@ public abstract partial class SharedVendingMachineSystem
|
|||||||
return true;
|
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)
|
private void OnAfterInteract(EntityUid uid, VendingMachineRestockComponent component, AfterInteractEvent args)
|
||||||
{
|
{
|
||||||
if (args.Target is not { } target || !args.CanReach || args.Handled)
|
if (args.Target is not { } target || !args.CanReach || args.Handled)
|
||||||
@@ -62,8 +72,13 @@ public abstract partial class SharedVendingMachineSystem
|
|||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
|
|
||||||
var doAfterArgs = new DoAfterArgs(EntityManager, args.User, (float)component.RestockDelay.TotalSeconds, new RestockDoAfterEvent(), target,
|
var doAfterArgs = new DoAfterArgs(EntityManager,
|
||||||
target: target, used: uid)
|
args.User,
|
||||||
|
component.RestockDelay,
|
||||||
|
new RestockDoAfterEvent(),
|
||||||
|
target,
|
||||||
|
target: target,
|
||||||
|
used: uid)
|
||||||
{
|
{
|
||||||
BreakOnMove = true,
|
BreakOnMove = true,
|
||||||
BreakOnDamage = true,
|
BreakOnDamage = true,
|
||||||
@@ -74,13 +89,48 @@ public abstract partial class SharedVendingMachineSystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var selfMessage = Loc.GetString("vending-machine-restock-start-self", ("target", target));
|
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));
|
var othersMessage = Loc.GetString("vending-machine-restock-start-others",
|
||||||
Popup.PopupPredicted(selfMessage,
|
("user", Identity.Entity(args.User, EntityManager)),
|
||||||
othersMessage,
|
("target", target));
|
||||||
uid,
|
Popup.PopupPredicted(selfMessage, othersMessage, target, args.User, PopupType.Medium);
|
||||||
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<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);
|
||||||
|
|
||||||
|
PredictedQueueDel(args.Used.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public abstract partial class SharedVendingMachineSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<VendingMachineComponent, ComponentGetState>(OnVendingGetState);
|
SubscribeLocalEvent<VendingMachineComponent, ComponentGetState>(OnVendingGetState);
|
||||||
SubscribeLocalEvent<VendingMachineComponent, MapInitEvent>(OnMapInit);
|
SubscribeLocalEvent<VendingMachineComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<VendingMachineComponent, GotEmaggedEvent>(OnEmagged);
|
SubscribeLocalEvent<VendingMachineComponent, GotEmaggedEvent>(OnEmagged);
|
||||||
|
SubscribeLocalEvent<VendingMachineComponent, RestockDoAfterEvent>(OnRestockDoAfter);
|
||||||
|
|
||||||
SubscribeLocalEvent<VendingMachineRestockComponent, AfterInteractEvent>(OnAfterInteract);
|
SubscribeLocalEvent<VendingMachineRestockComponent, AfterInteractEvent>(OnAfterInteract);
|
||||||
|
|
||||||
|
|||||||
@@ -139,6 +139,12 @@ namespace Content.Shared.VendingMachines
|
|||||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
public TimeSpan NextEmpEject = TimeSpan.Zero;
|
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
|
#region Client Visuals
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// RSI state for when the vending machine is unpowered.
|
/// RSI state for when the vending machine is unpowered.
|
||||||
|
|||||||
@@ -12,23 +12,20 @@ public sealed partial class VendingMachineRestockComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time (in seconds) that it takes to restock a machine.
|
/// The time (in seconds) that it takes to restock a machine.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
[DataField("restockDelay")]
|
|
||||||
public TimeSpan RestockDelay = TimeSpan.FromSeconds(5.0f);
|
public TimeSpan RestockDelay = TimeSpan.FromSeconds(5.0f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// What sort of machine inventory does this restock?
|
/// What sort of machine inventory does this restock?
|
||||||
/// This is checked against the VendingMachineComponent's pack value.
|
/// This is checked against the VendingMachineComponent's pack value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField(customTypeSerializer: typeof(PrototypeIdHashSetSerializer<VendingMachineInventoryPrototype>))]
|
||||||
[DataField("canRestock", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<VendingMachineInventoryPrototype>))]
|
public HashSet<string> CanRestock = [];
|
||||||
public HashSet<string> CanRestock = new();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sound that plays when starting to restock a machine.
|
/// Sound that plays when starting to restock a machine.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
[DataField("soundRestockStart")]
|
|
||||||
public SoundSpecifier SoundRestockStart = new SoundPathSpecifier("/Audio/Machines/vending_restock_start.ogg")
|
public SoundSpecifier SoundRestockStart = new SoundPathSpecifier("/Audio/Machines/vending_restock_start.ogg")
|
||||||
{
|
{
|
||||||
Params = new AudioParams
|
Params = new AudioParams
|
||||||
@@ -41,12 +38,10 @@ public sealed partial class VendingMachineRestockComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sound that plays when finished restocking a machine.
|
/// Sound that plays when finished restocking a machine.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
[DataField("soundRestockDone")]
|
public SoundSpecifier SoundRestockDone = new SoundPathSpecifier("/Audio/Machines/vending_restock_done.ogg",
|
||||||
public SoundSpecifier SoundRestockDone = new SoundPathSpecifier("/Audio/Machines/vending_restock_done.ogg");
|
AudioParams.Default.WithVolume(-2f).WithVariation(0.2f));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed partial class RestockDoAfterEvent : SimpleDoAfterEvent
|
public sealed partial class RestockDoAfterEvent : SimpleDoAfterEvent;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user