Add supplies to restock vending machines. (#11506)

This commit is contained in:
Vordenburg
2023-01-01 18:42:56 -05:00
committed by GitHub
parent d9be09b034
commit 5553976d70
43 changed files with 1625 additions and 15 deletions

View File

@@ -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();
}
}
}
}
}
}

View File

@@ -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");
}
}

View File

@@ -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 { }
}
}

View File

@@ -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;
}
}
}