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,360 @@
#nullable enable
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.Server.Wires;
using Content.Shared.Cargo.Prototypes;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.VendingMachines;
namespace Content.IntegrationTests.Tests
{
[TestFixture]
[TestOf(typeof(VendingMachineRestockComponent))]
[TestOf(typeof(VendingMachineRestockSystem))]
public sealed class VendingMachineRestockTest : EntitySystem
{
private const string Prototypes = @"
- type: entity
name: HumanDummy
id: HumanDummy
components:
- type: Hands
- type: Body
prototype: Human
- type: entity
parent: FoodSnackBase
id: TestRamen
name: TestRamen
- type: vendingMachineInventory
id: TestInventory
startingInventory:
TestRamen: 1
- type: vendingMachineInventory
id: OtherTestInventory
startingInventory:
TestRamen: 3
- type: vendingMachineInventory
id: BigTestInventory
startingInventory:
TestRamen: 4
- type: entity
parent: BaseVendingMachineRestock
id: TestRestockWrong
name: TestRestockWrong
components:
- type: VendingMachineRestock
canRestock:
- OtherTestInventory
- type: entity
parent: BaseVendingMachineRestock
id: TestRestockCorrect
name: TestRestockCorrect
components:
- type: VendingMachineRestock
canRestock:
- TestInventory
- type: entity
parent: BaseVendingMachineRestock
id: TestRestockExplode
name: TestRestockExplode
components:
- type: Damageable
damageContainer: Inorganic
damageModifierSet: Metallic
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 20
behaviors:
- !type:DumpRestockInventory
- !type:DoActsBehavior
acts: [ 'Destruction' ]
- type: VendingMachineRestock
canRestock:
- BigTestInventory
- type: entity
parent: VendingMachine
id: VendingMachineTest
name: Test Ramen
components:
- type: Wires
LayoutId: Vending
- type: VendingMachine
pack: TestInventory
- type: Sprite
sprite: error.rsi
";
[Test]
public async Task TestAllRestocksAreAvailableToBuy()
{
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
await server.WaitAssertion(() =>
{
HashSet<string> restocks = new();
Dictionary<string, List<string>> restockStores = new();
// Collect all the prototypes with restock components.
foreach (var proto in prototypeManager.EnumeratePrototypes<EntityPrototype>())
{
if (proto.Abstract ||
!proto.TryGetComponent<VendingMachineRestockComponent>(out var restock))
{
continue;
}
restocks.Add(proto.ID);
}
// Collect all the prototypes with StorageFills referencing those entities.
foreach (var proto in prototypeManager.EnumeratePrototypes<EntityPrototype>())
{
if (!proto.TryGetComponent<StorageFillComponent>(out var storage))
continue;
List<string> restockStore = new();
foreach (var spawnEntry in storage.Contents)
{
if (spawnEntry.PrototypeId != null && restocks.Contains(spawnEntry.PrototypeId))
restockStore.Add(spawnEntry.PrototypeId);
}
if (restockStore.Count > 0)
restockStores.Add(proto.ID, restockStore);
}
// Iterate through every CargoProduct and make sure each
// prototype with a restock component is referenced in a
// purchaseable entity with a StorageFill.
foreach (var proto in prototypeManager.EnumeratePrototypes<CargoProductPrototype>())
{
if (restockStores.ContainsKey(proto.Product))
{
foreach (var entry in restockStores[proto.Product])
restocks.Remove(entry);
restockStores.Remove(proto.Product);
}
}
Assert.That(restockStores.Count, Is.EqualTo(0),
$"Some entities containing entities with VendingMachineRestock components are unavailable for purchase: \n - {string.Join("\n - ", restockStores.Keys)}");
Assert.That(restocks.Count, Is.EqualTo(0),
$"Some entities with VendingMachineRestock components are unavailable for purchase: \n - {string.Join("\n - ", restocks)}");
});
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task TestCompleteRestockProcess()
{
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
var entitySystemManager = server.ResolveDependency<IEntitySystemManager>();
EntityUid packageRight;
EntityUid packageWrong;
EntityUid machine;
EntityUid user;
VendingMachineComponent machineComponent;
VendingMachineRestockComponent restockRightComponent;
VendingMachineRestockComponent restockWrongComponent;
WiresComponent machineWires;
var testMap = await PoolManager.CreateTestMap(pairTracker);
await server.WaitAssertion(() =>
{
var coordinates = testMap.GridCoords;
// Spawn the entities.
user = entityManager.SpawnEntity("HumanDummy", coordinates);
machine = entityManager.SpawnEntity("VendingMachineTest", coordinates);
packageRight = entityManager.SpawnEntity("TestRestockCorrect", coordinates);
packageWrong = entityManager.SpawnEntity("TestRestockWrong", coordinates);
// Sanity test for components existing.
Assert.True(entityManager.TryGetComponent(machine, out machineComponent!), $"Machine has no {nameof(VendingMachineComponent)}");
Assert.True(entityManager.TryGetComponent(packageRight, out restockRightComponent!), $"Correct package has no {nameof(VendingMachineRestockComponent)}");
Assert.True(entityManager.TryGetComponent(packageWrong, out restockWrongComponent!), $"Wrong package has no {nameof(VendingMachineRestockComponent)}");
Assert.True(entityManager.TryGetComponent(machine, out machineWires!), $"Machine has no {nameof(WiresComponent)}");
var systemRestock = entitySystemManager.GetEntitySystem<VendingMachineRestockSystem>();
var systemMachine = entitySystemManager.GetEntitySystem<VendingMachineSystem>();
// 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");
// Open the panel.
machineWires.IsPanelOpen = 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");
// 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");
// 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");
// Make sure there's something in there to begin with.
Assert.That(systemMachine.GetAvailableInventory(machine, machineComponent).Count, Is.GreaterThan(0),
"Machine inventory is empty before emptying.");
// Empty the inventory.
systemMachine.EjectRandom(machine, false, true, machineComponent);
Assert.That(systemMachine.GetAvailableInventory(machine, machineComponent).Count, Is.EqualTo(0),
"Machine inventory is not empty after ejecting.");
// Test that the inventory is actually restocked.
systemMachine.TryRestockInventory(machine, machineComponent);
Assert.That(systemMachine.GetAvailableInventory(machine, machineComponent).Count, Is.GreaterThan(0),
"Machine available inventory count is not greater than zero after restock.");
mapManager.DeleteMap(testMap.MapId);
});
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task TestRestockBreaksOpen()
{
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
var entitySystemManager = server.ResolveDependency<IEntitySystemManager>();
var damageableSystem = entitySystemManager.GetEntitySystem<DamageableSystem>();
var testMap = await PoolManager.CreateTestMap(pairTracker);
EntityUid restock = default;
await server.WaitAssertion(() =>
{
var coordinates = testMap.GridCoords;
var totalStartingRamen = 0;
foreach (var meta in entityManager.EntityQuery<MetaDataComponent>())
if (!meta.Deleted && meta.EntityPrototype?.ID == "TestRamen")
totalStartingRamen++;
Assert.That(totalStartingRamen, Is.EqualTo(0),
"Did not start with zero ramen.");
restock = entityManager.SpawnEntity("TestRestockExplode", coordinates);
var damageSpec = new DamageSpecifier(prototypeManager.Index<DamageTypePrototype>("Blunt"), 100);
var damageResult = damageableSystem.TryChangeDamage(restock, damageSpec);
Assert.IsNotNull(damageResult,
"Received null damageResult when attempting to damage restock box.");
Assert.That((int) damageResult!.Total, Is.GreaterThan(0),
"Box damage result was not greater than 0.");
});
await server.WaitRunTicks(15);
await server.WaitAssertion(() =>
{
Assert.That(entityManager.Deleted(restock),
"Restock box was not deleted after being damaged.");
var totalRamen = 0;
foreach (var meta in entityManager.EntityQuery<MetaDataComponent>())
if (!meta.Deleted && meta.EntityPrototype?.ID == "TestRamen")
totalRamen++;
Assert.That(totalRamen, Is.EqualTo(2),
"Did not find enough ramen after destroying restock box.");
mapManager.DeleteMap(testMap.MapId);
});
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task TestRestockInventoryBounds()
{
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
var server = pairTracker.Pair.Server;
await server.WaitIdleAsync();
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
var entitySystemManager = server.ResolveDependency<IEntitySystemManager>();
var vendingMachineSystem = entitySystemManager.GetEntitySystem<SharedVendingMachineSystem>();
var testMap = await PoolManager.CreateTestMap(pairTracker);
await server.WaitAssertion(() =>
{
var coordinates = testMap.GridCoords;
EntityUid machine = entityManager.SpawnEntity("VendingMachineTest", coordinates);
Assert.That(vendingMachineSystem.GetAvailableInventory(machine).Count, Is.EqualTo(1),
"Machine's available inventory did not contain one entry.");
Assert.That(vendingMachineSystem.GetAvailableInventory(machine)[0].Amount, Is.EqualTo(1),
"Machine's available inventory is not the expected amount.");
vendingMachineSystem.RestockInventoryFromPrototype(machine);
Assert.That(vendingMachineSystem.GetAvailableInventory(machine)[0].Amount, Is.EqualTo(2),
"Machine's available inventory is not double its starting amount after a restock.");
vendingMachineSystem.RestockInventoryFromPrototype(machine);
Assert.That(vendingMachineSystem.GetAvailableInventory(machine)[0].Amount, Is.EqualTo(3),
"Machine's available inventory is not triple its starting amount after two restocks.");
vendingMachineSystem.RestockInventoryFromPrototype(machine);
Assert.That(vendingMachineSystem.GetAvailableInventory(machine)[0].Amount, Is.EqualTo(3),
"Machine's available inventory did not stay the same after a third restock.");
});
await pairTracker.CleanReturnAsync();
}
}
}
#nullable disable

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.Components;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Server.UserInterface; using Content.Server.UserInterface;
using Content.Server.VendingMachines.Restock;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.Access.Systems; using Content.Shared.Access.Systems;
using Content.Shared.Actions; using Content.Shared.Actions;
@@ -10,6 +11,7 @@ using Content.Shared.Actions.ActionTypes;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Destructible; using Content.Shared.Destructible;
using Content.Shared.Emag.Systems; using Content.Shared.Emag.Systems;
using Content.Shared.Popups;
using Content.Shared.Throwing; using Content.Shared.Throwing;
using Content.Shared.VendingMachines; using Content.Shared.VendingMachines;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
@@ -51,6 +53,8 @@ namespace Content.Server.VendingMachines
SubscribeLocalEvent<VendingMachineComponent, VendingMachineEjectMessage>(OnInventoryEjectMessage); SubscribeLocalEvent<VendingMachineComponent, VendingMachineEjectMessage>(OnInventoryEjectMessage);
SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense); SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense);
SubscribeLocalEvent<VendingMachineComponent, VendingMachineRestockEvent>(OnRestock);
} }
private void OnVendingPrice(EntityUid uid, VendingMachineComponent component, ref PriceCalculationEvent args) 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); 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> /// <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>
@@ -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;
}
} }
} }

View File

@@ -15,6 +15,17 @@ public abstract class SharedVendingMachineSystem : EntitySystem
protected virtual void OnComponentInit(EntityUid uid, VendingMachineComponent component, ComponentInit args) protected virtual void OnComponentInit(EntityUid uid, VendingMachineComponent component, ComponentInit args)
{ {
RestockInventoryFromPrototype(uid, component);
}
public void RestockInventoryFromPrototype(EntityUid uid,
VendingMachineComponent? component = null)
{
if (!Resolve(uid, ref component))
{
return;
}
if (!_prototypeManager.TryIndex(component.PackPrototypeId, out VendingMachineInventoryPrototype? packPrototype)) if (!_prototypeManager.TryIndex(component.PackPrototypeId, out VendingMachineInventoryPrototype? packPrototype))
return; return;
@@ -64,28 +75,38 @@ public abstract class SharedVendingMachineSystem : EntitySystem
return; return;
} }
var inventory = new Dictionary<string, VendingMachineInventoryEntry>(); Dictionary<string, VendingMachineInventoryEntry> inventory;
switch (type)
{
case InventoryType.Regular:
inventory = component.Inventory;
break;
case InventoryType.Emagged:
inventory = component.EmaggedInventory;
break;
case InventoryType.Contraband:
inventory = component.ContrabandInventory;
break;
default:
return;
}
foreach (var (id, amount) in entries) foreach (var (id, amount) in entries)
{ {
if (_prototypeManager.HasIndex<EntityPrototype>(id)) if (_prototypeManager.HasIndex<EntityPrototype>(id))
{ {
inventory.Add(id, new VendingMachineInventoryEntry(type, id, amount)); if (inventory.TryGetValue(id, out VendingMachineInventoryEntry? 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
// restocking a machine who doesn't want to force vend out
// all the items just to restock one empty slot without
// losing the rest of the restock.
entry.Amount = Math.Min(entry.Amount + amount, 3 * amount);
else
inventory.Add(id, new VendingMachineInventoryEntry(type, id, amount));
} }
} }
switch (type)
{
case InventoryType.Regular:
component.Inventory = inventory;
break;
case InventoryType.Emagged:
component.EmaggedInventory = inventory;
break;
case InventoryType.Contraband:
component.ContrabandInventory = inventory;
break;
}
} }
} }

View File

@@ -1,3 +1,13 @@
- files: ["vending_restock_start.ogg"]
license: "CC0-1.0"
copyright: "https://freesound.org/people/Defaultv/"
source: "https://freesound.org/people/Defaultv/sounds/534362/"
- files: ["vending_restock_done.ogg"]
license: "CC-BY-3.0"
copyright: "https://freesound.org/people/felipelnv/"
source: "https://freesound.org/people/felipelnv/sounds/153298/"
- files: ["short_print_and_rip.ogg"] - files: ["short_print_and_rip.ogg"]
license: "CC0-1.0" license: "CC0-1.0"
copyright: "receipt printing.wav by 13F_Panska_Tlolkova_Matilda. This version is cleaned up, shortened, and converted to OGG." copyright: "receipt printing.wav by 13F_Panska_Tlolkova_Matilda. This version is cleaned up, shortened, and converted to OGG."

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,48 @@
ent-CrateVendingMachineRestockBooze = { ent-CrateVendingMachineRestockBoozeFilled }
.desc = { ent-CrateVendingMachineRestockBoozeFilled.desc }
ent-CrateVendingMachineRestockClothes = { ent-CrateVendingMachineRestockClothesFilled }
.desc = { ent-CrateVendingMachineRestockClothesFilled.desc }
ent-CrateVendingMachineRestockDinnerware = { ent-CrateVendingMachineRestockDinnerwareFilled }
.desc = { ent-CrateVendingMachineRestockDinnerwareFilled.desc }
ent-CrateVendingMachineRestockEngineering = { ent-CrateVendingMachineRestockEngineeringFilled }
.desc = { ent-CrateVendingMachineRestockEngineeringFilled.desc }
ent-CrateVendingMachineRestockGames = { ent-CrateVendingMachineRestockGamesFilled }
.desc = { ent-CrateVendingMachineRestockGamesFilled.desc }
ent-CrateVendingMachineRestockHotDrinks = { ent-CrateVendingMachineRestockHotDrinksFilled }
.desc = { ent-CrateVendingMachineRestockHotDrinksFilled.desc }
ent-CrateVendingMachineRestockMedical = { ent-CrateVendingMachineRestockMedicalFilled }
.desc = { ent-CrateVendingMachineRestockMedicalFilled.desc }
ent-CrateVendingMachineRestockNutriMax = { ent-CrateVendingMachineRestockNutriMaxFilled }
.desc = { ent-CrateVendingMachineRestockNutriMaxFilled.desc }
ent-CrateVendingMachineRestockPTech = { ent-CrateVendingMachineRestockPTechFilled }
.desc = { ent-CrateVendingMachineRestockPTechFilled.desc }
ent-CrateVendingMachineRestockRobustSoftdrinks = { ent-CrateVendingMachineRestockRobustSoftdrinksFilled }
.desc = { ent-CrateVendingMachineRestockRobustSoftdrinksFilled.desc }
ent-CrateVendingMachineRestockSalvageEquipment = { ent-CrateVendingMachineRestockSalvageEquipmentFilled }
.desc = { ent-CrateVendingMachineRestockSalvageEquipmentFilled.desc }
ent-CrateVendingMachineRestockSecTech = { ent-CrateVendingMachineRestockSecTechFilled }
.desc = { ent-CrateVendingMachineRestockSecTechFilled.desc }
ent-CrateVendingMachineRestockSeeds = { ent-CrateVendingMachineRestockSeedsFilled }
.desc = { ent-CrateVendingMachineRestockSeedsFilled.desc }
ent-CrateVendingMachineRestockSmokes = { ent-CrateVendingMachineRestockSmokesFilled }
.desc = { ent-CrateVendingMachineRestockSmokesFilled.desc }
ent-CrateVendingMachineRestockSnacks = { ent-CrateVendingMachineRestockSnacksFilled }
.desc = { ent-CrateVendingMachineRestockSnacksFilled.desc }
ent-CrateVendingMachineRestockTankDispenser = { ent-CrateVendingMachineRestockTankDispenserFilled }
.desc = { ent-CrateVendingMachineRestockTankDispenserFilled.desc }

View File

@@ -0,0 +1,47 @@
ent-CrateVendingMachineRestockBoozeFilled = Booze-O-Mat restock crate
.desc = Contains a restock box for the Booze-O-Mat.
ent-CrateVendingMachineRestockClothesFilled = Clothing restock crate
.desc = Contains a pair of restock boxes, one for the ClothesMate and one for the AutoDrobe.
ent-CrateVendingMachineRestockDinnerwareFilled = Plasteel Chef restock crate
.desc = Contains a restock box for the Plasteel Chef vending machine.
ent-CrateVendingMachineRestockEngineeringFilled = EngiVend restock crate
.desc = Contains a restock box for the EngiVend. Also supports the YouTool.
ent-CrateVendingMachineRestockGamesFilled = Good Clean Fun restock crate
.desc = Contains a restock box for the Good Clean Fun vending machine.
ent-CrateVendingMachineRestockHotDrinksFilled = Solar's Best restock crate
.desc = Contains two restock boxes for Solar's Best Hot Drinks vending machine.
ent-CrateVendingMachineRestockMedicalFilled = NanoMed restock crate
.desc = Contains a restock box, compatible with the NanoMed and NanoMedPlus.
ent-CrateVendingMachineRestockNutriMaxFilled = NutriMax restock crate
.desc = Contains a restock box for the NutriMax vending machine.
ent-CrateVendingMachineRestockPTechFilled = PTech restock crate
.desc = Contains a restock box for the PTech bureaucracy dispenser.
ent-CrateVendingMachineRestockRobustSoftdrinksFilled = Robust Softdrinks restock crate
.desc = Contains two restock boxes for the Robust Softdrinks LLC vending machine.
ent-CrateVendingMachineRestockSalvageEquipmentFilled = Salvage restock crate
.desc = Contains a restock box for the salvage vendor.
ent-CrateVendingMachineRestockSecTechFilled = SecTech restock crate
.desc = Contains a restock box for the SecTech vending machine.
ent-CrateVendingMachineRestockSeedsFilled = MegaSeed restock crate
.desc = Contains a restock box for the MegaSeed vending machine.
ent-CrateVendingMachineRestockSmokesFilled = ShadyCigs restock crate
.desc = Contains two restock boxes for the ShadyCigs vending machine.
ent-CrateVendingMachineRestockSnacksFilled = Snack restock crate
.desc = Contains four restock boxes, each covering a different snack vendor. Mr. Chang's, Discount Dans, Robust Donuts, and Getmore Chocolate are featured on the advertisement.
ent-CrateVendingMachineRestockTankDispenserFilled = Tank dispenser restock crate
.desc = Contains a restock box for an Engineering or Atmospherics tank dispenser.

View File

@@ -0,0 +1,4 @@
vending-machine-restock-invalid-inventory = { CAPITALIZE(THE($this)) } isn't the right package to restock { THE($target) }.
vending-machine-restock-needs-panel-open = { CAPITALIZE($target) } needs { POSS-ADJ($target) } maintenance panel opened first.
vending-machine-restock-start = { $user } starts restocking { $target }.
vending-machine-restock-done = { $user } finishes restocking { $target }.

View File

@@ -0,0 +1,167 @@
# If you've arrived here after a failed NoCargoOrderArbitrage test, it's likely
# because the product in question contains a restock box which links to a
# vending machine inventory that has had items added to it recently and caused
# its calculated price to exceed the cost below.
#
# The costs will need to be kept in line with the inventory of the respective
# vending machines. An extra hundred or two profit margin should be fine.
- type: cargoProduct
id: CrateVendingMachineRestockBooze
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockBoozeFilled
cost: 3200
category: Service
group: market
- type: cargoProduct
id: CrateVendingMachineRestockClothes
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockClothesFilled
cost: 4000
category: Service
group: market
- type: cargoProduct
id: CrateVendingMachineRestockDinnerware
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockDinnerwareFilled
cost: 2000
category: Service
group: market
- type: cargoProduct
id: CrateVendingMachineRestockEngineering
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockEngineeringFilled
cost: 3000
category: Engineering
group: market
- type: cargoProduct
id: CrateVendingMachineRestockGames
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockGamesFilled
cost: 650
category: Service
group: market
- type: cargoProduct
id: CrateVendingMachineRestockHotDrinks
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockHotDrinksFilled
cost: 1200
category: Service
group: market
- type: cargoProduct
id: CrateVendingMachineRestockMedical
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockMedicalFilled
cost: 1500
category: Medical
group: market
- type: cargoProduct
id: CrateVendingMachineRestockNutriMax
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockNutriMaxFilled
cost: 2400
category: Hydroponics
group: market
- type: cargoProduct
id: CrateVendingMachineRestockPTech
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockPTechFilled
cost: 800
category: Service
group: market
- type: cargoProduct
id: CrateVendingMachineRestockRobustSoftdrinks
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockRobustSoftdrinksFilled
cost: 1200
category: Service
group: market
- type: cargoProduct
id: CrateVendingMachineRestockSalvageEquipment
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockSalvageEquipmentFilled
cost: 1000
category: Engineering
group: market
- type: cargoProduct
id: CrateVendingMachineRestockSecTech
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockSecTechFilled
cost: 2200
category: Security
group: market
- type: cargoProduct
id: CrateVendingMachineRestockSeeds
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockSeedsFilled
cost: 2000
category: Hydroponics
group: market
- type: cargoProduct
id: CrateVendingMachineRestockSmokes
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockSmokesFilled
cost: 1200
category: Service
group: market
- type: cargoProduct
id: CrateVendingMachineRestockSnacks
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockSnacksFilled
cost: 3000
category: Service
group: market
- type: cargoProduct
id: CrateVendingMachineRestockTankDispenser
icon:
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockTankDispenserFilled
cost: 1000
category: Atmospherics
group: market

View File

@@ -0,0 +1,134 @@
- type: entity
id: CrateVendingMachineRestockBoozeFilled
parent: CratePlastic
components:
- type: StorageFill
contents:
- id: VendingMachineRestockBooze
- type: entity
id: CrateVendingMachineRestockClothesFilled
parent: CratePlastic
components:
- type: StorageFill
contents:
- id: VendingMachineRestockClothes
- id: VendingMachineRestockCostumes
- type: entity
id: CrateVendingMachineRestockDinnerwareFilled
parent: CratePlastic
components:
- type: StorageFill
contents:
- id: VendingMachineRestockDinnerware
- type: entity
id: CrateVendingMachineRestockEngineeringFilled
parent: CrateEngineeringSecure
components:
- type: StorageFill
contents:
- id: VendingMachineRestockEngineering
- type: entity
id: CrateVendingMachineRestockGamesFilled
parent: CratePlastic
components:
- type: StorageFill
contents:
- id: VendingMachineRestockGames
- type: entity
id: CrateVendingMachineRestockHotDrinksFilled
parent: CratePlastic
components:
- type: StorageFill
contents:
- id: VendingMachineRestockHotDrinks
amount: 2
- type: entity
id: CrateVendingMachineRestockMedicalFilled
parent: CrateMedicalSecure
components:
- type: StorageFill
contents:
- id: VendingMachineRestockMedical
- type: entity
id: CrateVendingMachineRestockNutriMaxFilled
parent: CrateHydroSecure
components:
- type: StorageFill
contents:
- id: VendingMachineRestockNutriMax
- type: entity
id: CrateVendingMachineRestockPTechFilled
parent: CratePlastic
components:
- type: StorageFill
contents:
- id: VendingMachineRestockPTech
- type: entity
id: CrateVendingMachineRestockRobustSoftdrinksFilled
parent: CratePlastic
components:
- type: StorageFill
contents:
- id: VendingMachineRestockRobustSoftdrinks
amount: 2
- type: entity
id: CrateVendingMachineRestockSalvageEquipmentFilled
parent: CrateGenericSteel
components:
- type: StorageFill
contents:
- id: VendingMachineRestockSalvageEquipment
- type: entity
id: CrateVendingMachineRestockSecTechFilled
parent: CrateSecgear
components:
- type: StorageFill
contents:
- id: VendingMachineRestockSecTech
- type: entity
id: CrateVendingMachineRestockSeedsFilled
parent: CrateHydroSecure
components:
- type: StorageFill
contents:
- id: VendingMachineRestockSeeds
- type: entity
id: CrateVendingMachineRestockSmokesFilled
parent: CratePlastic
components:
- type: StorageFill
contents:
- id: VendingMachineRestockSmokes
amount: 2
- type: entity
id: CrateVendingMachineRestockSnacksFilled
parent: CratePlastic
components:
- type: StorageFill
contents:
- id: VendingMachineRestockChang
- id: VendingMachineRestockDiscountDans
- id: VendingMachineRestockDonut
- id: VendingMachineRestockGetmoreChocolateCorp
- type: entity
id: CrateVendingMachineRestockTankDispenserFilled
parent: CratePlastic
components:
- type: StorageFill
contents:
- id: VendingMachineRestockTankDispenser

View File

@@ -0,0 +1,59 @@
- type: entity
id: SpawnVendingMachineRestockFoodDrink
name: Vending Machine Restock
suffix: "food or drink"
parent: MarkerBase
components:
- type: Sprite
layers:
- state: green
- sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
- sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: refill_discount
- type: ConditionalSpawner
prototypes:
- VendingMachineRestockDiscountDans
- VendingMachineRestockChang
- VendingMachineRestockDonut
- VendingMachineRestockGetmoreChocolateCorp
- VendingMachineRestockRobustSoftdrinks
- VendingMachineRestockHotDrinks
- type: entity
id: SpawnVendingMachineRestockFood
name: Vending Machine Restock
suffix: "food"
parent: MarkerBase
components:
- type: Sprite
layers:
- state: green
- sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
- sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: refill_donuts
- type: ConditionalSpawner
prototypes:
- VendingMachineRestockDiscountDans
- VendingMachineRestockChang
- VendingMachineRestockDonut
- VendingMachineRestockGetmoreChocolateCorp
- type: entity
id: SpawnVendingMachineRestockDrink
name: Vending Machine Restock
suffix: "drink"
parent: MarkerBase
components:
- type: Sprite
layers:
- state: green
- sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
- sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: refill_cola
- type: ConditionalSpawner
prototypes:
- VendingMachineRestockRobustSoftdrinks
- VendingMachineRestockHotDrinks

View File

@@ -0,0 +1,365 @@
- type: entity
parent: BaseItem
id: BaseVendingMachineRestock
abstract: true
name: vending machine restock box
description: A box for restocking vending machines with corporate goodies.
components:
- type: VendingMachineRestock
- type: Sprite
netsync: false
sprite: Objects/Specific/Service/vending_machine_restock.rsi
layers:
- state: base
- state: green_bit
shader: unshaded
- type: ItemCooldown
- type: MeleeWeapon
damage:
types:
Blunt: 5
soundHit:
path: /Audio/Weapons/genhit2.ogg
soundSwing:
path: /Audio/Weapons/punchmiss.ogg
- type: Item
size: 50
- type: Damageable
damageContainer: Inorganic
damageModifierSet: Metallic
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 20
behaviors:
- !type:PlaySoundBehavior
sound:
path: /Audio/Effects/metalbreak.ogg
- !type:DumpRestockInventory
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockBooze
name: Booze-O-Mat restock box
description: Slot into your Booze-O-Mat to start the party! Not for sale to passengers below the legal age.
components:
- type: VendingMachineRestock
canRestock:
- BoozeOMatInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_booze
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockChang
name: Mr. Chang's restock box
description: A box covered in white labels with bold red Chinese characters, ready to be loaded into the nearest Mr. Chang's vending machine.
components:
- type: VendingMachineRestock
canRestock:
- ChangInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_chinese
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockClothes
name: ClothesMate restock box
description: It's time to step up your fashion! Place inside the ClothesMate restock slot to begin.
components:
- type: VendingMachineRestock
canRestock:
- ClothesMateInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_clothes
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockCostumes
name: AutoDrobe restock box
description: A panoply of NanoTrasen employees are prancing about a colorful theater in a tragicomedy. You can join them too! Load this into your nearest AutoDrobe vending machine.
components:
- type: VendingMachineRestock
canRestock:
- AutoDrobeInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_costume
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockDinnerware
name: Plasteel Chef's restock box
description: It's never raw in this kitchen! Drop into the restock slot on the Plasteel Chef to begin.
components:
- type: VendingMachineRestock
canRestock:
- DinnerwareInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_dinner
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockDiscountDans
name: Discount Dan's restock box
description: A box full of salt and starch. Why suffer Quality when you can have Quantity? Discount Dan's!
components:
- type: VendingMachineRestock
canRestock:
- DiscountDansInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_discount
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockDonut
name: Robust Donuts box
description: A box full of toroidal bundles of fried dough for restocking a vending machine. Use only as directed by Robust Industries, LLC.
components:
- type: VendingMachineRestock
canRestock:
- DonutInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_donuts
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockEngineering
name: EngiVend resupply box
description: Only to be used by certified professionals.
components:
- type: VendingMachineRestock
canRestock:
- EngiVendInventory
- YouToolInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_engi
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockGames
name: Good Clean Fun restock box
description: It's time to roll for initiative, dice dragons! Load up at the Good Clean Fun vending machine!
components:
- type: VendingMachineRestock
canRestock:
- GoodCleanFunInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_games
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockGetmoreChocolateCorp
name: GetMore Chocolate restock box
description: A box loaded with the finest ersatz cacao. Only to be used in official Getmore Chocolate vending machines.
components:
- type: VendingMachineRestock
canRestock:
- GetmoreChocolateCorpInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_snack
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockHotDrinks
name: Solar's Best restock box
description: Toasty! For use in Solar's Best Hot Drinks or other affiliate vending machines.
components:
- type: VendingMachineRestock
canRestock:
- HotDrinksMachineInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_joe
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockMedical
name: NanoMed resupply box
description: Slot into your department's NanoMed or NanoMedPlus to dispense. Handle with care.
components:
- type: VendingMachineRestock
canRestock:
- NanoMedInventory
- NanoMedPlusInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_medical
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockNutriMax
name: NutriMax restock box
description: We'll make your thumbs green with our tools. Let's get to harvesting! Load into a NutriMax vending machine.
components:
- type: VendingMachineRestock
canRestock:
- NutriMaxInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_plant
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockPTech
name: PTech resupply box
description: All the bureaucracy you can handle, and more! Load into the PTech vending machine to get started.
components:
- type: VendingMachineRestock
canRestock:
- PTechInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_ptech
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockRobustSoftdrinks
name: Robust Softdrinks box
description: A cold, clunky container of colliding chilly cylinders. Use only as directed by Robust Industries, LLC.
components:
- type: VendingMachineRestock
canRestock:
- RobustSoftdrinksInventory
- BodaInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_cola
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockSecTech
name: SecTech resupply box
description: "Communists beware: the reinforcements have arrived! A label reads SECURITY PERSONNEL ONLY."
components:
- type: VendingMachineRestock
canRestock:
- SecTechInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_sec
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockSalvageEquipment
name: salvage resupply box
description: Strike the earth ere the space carp nip your behind! Slam into a salvage vendor to begin.
components:
- type: VendingMachineRestock
canRestock:
- SalvageEquipmentInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_salvage
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockSeeds
name: MegaSeed restock box
description: A label says they're heirloom seeds, passed down from our ancestors. Pack it into the MegaSeed Servitor!
components:
- type: VendingMachineRestock
canRestock:
- MegaSeedServitorInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_plant
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockSmokes
name: ShadyCigs restock box
description: It's hard to see anything under all the Surgeon General warnings, but it mentions loading it into a vending machine.
components:
- type: VendingMachineRestock
canRestock:
- CigaretteMachineInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_smoke
- type: entity
parent: BaseVendingMachineRestock
id: VendingMachineRestockTankDispenser
name: tank dispenser resupply box
description: Capable of replacing tanks in a gas tank dispenser. Handle with care.
components:
- type: VendingMachineRestock
canRestock:
- TankDispenserEVAInventory
- TankDispenserEngineeringInventory
- type: Sprite
layers:
- state: base
- state: green_bit
shader: unshaded
- state: refill_tanks

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

View File

@@ -0,0 +1,91 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Original source: https://github.com/tgstation/tgstation/blob/master/icons/obj/vending_restock.dmi @ commit 014c44ef6279beb02a5f3e76824439fa57181c22 - Additions and cleanup by @Vordenburg",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "base"
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
},
{
"name": "green_bit"
},
{
"name": "refill_booze"
},
{
"name": "refill_chinese"
},
{
"name": "refill_clothes"
},
{
"name": "refill_cola"
},
{
"name": "refill_costume"
},
{
"name": "refill_custom"
},
{
"name": "refill_dinner"
},
{
"name": "refill_discount"
},
{
"name": "refill_donksoft"
},
{
"name": "refill_donuts"
},
{
"name": "refill_engi"
},
{
"name": "refill_games"
},
{
"name": "refill_joe"
},
{
"name": "refill_medical"
},
{
"name": "refill_parts"
},
{
"name": "refill_plant"
},
{
"name": "refill_ptech"
},
{
"name": "refill_salvage"
},
{
"name": "refill_sec"
},
{
"name": "refill_smoke"
},
{
"name": "refill_snack"
},
{
"name": "refill_tanks"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B