Add supplies to restock vending machines. (#11506)
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
using Robust.Shared.Random;
|
||||
using Content.Shared.Stacks;
|
||||
using Content.Server.VendingMachines.Restock;
|
||||
using Content.Shared.Prototypes;
|
||||
using Content.Shared.VendingMachines;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Behaviors
|
||||
{
|
||||
/// <summary>
|
||||
/// Spawns a portion of the total items from one of the canRestock
|
||||
/// inventory entries on a VendingMachineRestock component.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataDefinition]
|
||||
public sealed class DumpRestockInventory: IThresholdBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// The percent of each inventory entry that will be salvaged
|
||||
/// upon destruction of the package.
|
||||
/// </summary>
|
||||
[DataField("percent", required: true)]
|
||||
public float Percent = 0.5f;
|
||||
|
||||
[DataField("offset")]
|
||||
public float Offset { get; set; } = 0.5f;
|
||||
|
||||
public void Execute(EntityUid owner, DestructibleSystem system)
|
||||
{
|
||||
if (!system.EntityManager.TryGetComponent<VendingMachineRestockComponent>(owner, out var packagecomp) ||
|
||||
!system.EntityManager.TryGetComponent<TransformComponent>(owner, out var xform))
|
||||
return;
|
||||
|
||||
var randomInventory = system.Random.Pick(packagecomp.CanRestock);
|
||||
|
||||
if (!system.PrototypeManager.TryIndex(randomInventory, out VendingMachineInventoryPrototype? packPrototype))
|
||||
return;
|
||||
|
||||
foreach (var (entityId, count) in packPrototype.StartingInventory)
|
||||
{
|
||||
var toSpawn = (int) Math.Round(count * Percent);
|
||||
|
||||
if (toSpawn == 0) continue;
|
||||
|
||||
if (EntityPrototypeHelpers.HasComponent<StackComponent>(entityId, system.PrototypeManager, system.ComponentFactory))
|
||||
{
|
||||
var spawned = system.EntityManager.SpawnEntity(entityId, xform.Coordinates.Offset(system.Random.NextVector2(-Offset, Offset)));
|
||||
system.StackSystem.SetCount(spawned, toSpawn);
|
||||
system.EntityManager.GetComponent<TransformComponent>(spawned).LocalRotation = system.Random.NextAngle();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < toSpawn; i++)
|
||||
{
|
||||
var spawned = system.EntityManager.SpawnEntity(entityId, xform.Coordinates.Offset(system.Random.NextVector2(-Offset, Offset)));
|
||||
system.EntityManager.GetComponent<TransformComponent>(spawned).LocalRotation = system.Random.NextAngle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
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
|
||||
{
|
||||
public CancellationTokenSource? CancelToken;
|
||||
|
||||
/// <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");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Server.Cargo.Systems;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.Wires;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.VendingMachines;
|
||||
|
||||
namespace Content.Server.VendingMachines.Restock
|
||||
{
|
||||
public sealed class VendingMachineRestockSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
[Dependency] private readonly DoAfterSystem _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);
|
||||
SubscribeLocalEvent<VendingMachineRestockComponent, RestockCancelledEvent>(OnRestockCancelled);
|
||||
}
|
||||
|
||||
public bool TryAccessMachine(EntityUid uid,
|
||||
VendingMachineRestockComponent component,
|
||||
VendingMachineComponent machineComponent,
|
||||
EntityUid user,
|
||||
EntityUid target)
|
||||
{
|
||||
if (!TryComp<WiresComponent>(target, out var wires) || !wires.IsPanelOpen) {
|
||||
_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 (component.CancelToken != null || args.Target == null || !args.CanReach)
|
||||
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;
|
||||
|
||||
component.CancelToken = new CancellationTokenSource();
|
||||
|
||||
_doAfterSystem.DoAfter(new DoAfterEventArgs(
|
||||
args.User,
|
||||
(float) component.RestockDelay.TotalSeconds,
|
||||
component.CancelToken.Token,
|
||||
args.Target,
|
||||
args.Used)
|
||||
{
|
||||
TargetFinishedEvent = new VendingMachineRestockEvent(args.User, uid),
|
||||
UsedCancelledEvent = new RestockCancelledEvent(),
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnStun = true,
|
||||
BreakOnDamage = true,
|
||||
NeedHand = true
|
||||
});
|
||||
|
||||
_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();
|
||||
}
|
||||
|
||||
private void OnRestockCancelled(EntityUid uid, VendingMachineRestockComponent component, RestockCancelledEvent args)
|
||||
{
|
||||
component.CancelToken?.Cancel();
|
||||
component.CancelToken = null;
|
||||
}
|
||||
|
||||
public readonly struct RestockCancelledEvent { }
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ 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;
|
||||
@@ -10,6 +11,7 @@ using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.Emag.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.VendingMachines;
|
||||
using Robust.Server.GameObjects;
|
||||
@@ -51,6 +53,8 @@ namespace Content.Server.VendingMachines
|
||||
SubscribeLocalEvent<VendingMachineComponent, VendingMachineEjectMessage>(OnInventoryEjectMessage);
|
||||
|
||||
SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense);
|
||||
|
||||
SubscribeLocalEvent<VendingMachineComponent, VendingMachineRestockEvent>(OnRestock);
|
||||
}
|
||||
|
||||
private void OnVendingPrice(EntityUid uid, VendingMachineComponent component, ref PriceCalculationEvent args)
|
||||
@@ -160,6 +164,31 @@ namespace Content.Server.VendingMachines
|
||||
EjectRandom(uid, throwItem: true, forceEject: false, component);
|
||||
}
|
||||
|
||||
private void OnRestock(EntityUid uid, VendingMachineComponent component, VendingMachineRestockEvent args)
|
||||
{
|
||||
if (!TryComp<VendingMachineRestockComponent>(args.RestockBox, out var restockComponent))
|
||||
{
|
||||
_sawmill.Error($"{ToPrettyString(args.User)} tried to restock {ToPrettyString(uid)} with {ToPrettyString(args.RestockBox)} which did not have a VendingMachineRestockComponent.");
|
||||
return;
|
||||
}
|
||||
|
||||
TryRestockInventory(uid, component);
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("vending-machine-restock-done",
|
||||
("this", args.RestockBox),
|
||||
("user", args.User),
|
||||
("target", uid)),
|
||||
args.User,
|
||||
PopupType.Medium);
|
||||
|
||||
_audioSystem.PlayPvs(restockComponent.SoundRestockDone, component.Owner,
|
||||
AudioParams.Default
|
||||
.WithVolume(-2f)
|
||||
.WithVariation(0.2f));
|
||||
|
||||
Del(args.RestockBox);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="VendingMachineComponent.CanShoot"/> property of the vending machine.
|
||||
/// </summary>
|
||||
@@ -412,5 +441,29 @@ namespace Content.Server.VendingMachines
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void TryRestockInventory(EntityUid uid, VendingMachineComponent? vendComponent = null)
|
||||
{
|
||||
if (!Resolve(uid, ref vendComponent))
|
||||
return;
|
||||
|
||||
RestockInventoryFromPrototype(uid, vendComponent);
|
||||
|
||||
UpdateVendingMachineInterfaceState(vendComponent);
|
||||
TryUpdateVisualState(uid, vendComponent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public sealed class VendingMachineRestockEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid User { get; }
|
||||
public EntityUid RestockBox { get; }
|
||||
|
||||
public VendingMachineRestockEvent(EntityUid user, EntityUid restockBox)
|
||||
{
|
||||
User = user;
|
||||
RestockBox = restockBox;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user