Clean up vending machines and port their visualizer (#10465)
This commit is contained in:
@@ -1,62 +1,87 @@
|
|||||||
using System;
|
using Content.Shared.VendingMachines;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.ResourceManagement;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using static Content.Shared.VendingMachines.SharedVendingMachineComponent;
|
|
||||||
|
|
||||||
namespace Content.Client.VendingMachines.UI
|
namespace Content.Client.VendingMachines.UI
|
||||||
{
|
{
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class VendingMachineMenu : DefaultWindow
|
public sealed partial class VendingMachineMenu : DefaultWindow
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
|
||||||
private VendingMachineBoundUserInterface Owner { get; }
|
public event Action<ItemList.ItemListSelectedEventArgs>? OnItemSelected;
|
||||||
|
|
||||||
private List<VendingMachineInventoryEntry> _cachedInventory = new();
|
public VendingMachineMenu()
|
||||||
|
|
||||||
public VendingMachineMenu(VendingMachineBoundUserInterface owner)
|
|
||||||
{
|
{
|
||||||
IoCManager.InjectDependencies(this);
|
MinSize = SetSize = (250, 150);
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
Owner = owner;
|
VendingContents.OnItemSelected += args =>
|
||||||
VendingContents.OnItemSelected += ItemSelected;
|
{
|
||||||
|
OnItemSelected?.Invoke(args);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populates the list of available items on the vending machine interface
|
||||||
|
/// and sets icons based on their prototypes
|
||||||
|
/// </summary>
|
||||||
public void Populate(List<VendingMachineInventoryEntry> inventory)
|
public void Populate(List<VendingMachineInventoryEntry> inventory)
|
||||||
{
|
{
|
||||||
VendingContents.Clear();
|
if (inventory.Count == 0)
|
||||||
_cachedInventory = inventory;
|
|
||||||
var longestEntry = "";
|
|
||||||
foreach (VendingMachineInventoryEntry entry in inventory)
|
|
||||||
{
|
{
|
||||||
var itemName = _prototypeManager.Index<EntityPrototype>(entry.ID).Name;
|
VendingContents.Clear();
|
||||||
|
var outOfStockText = Loc.GetString("vending-machine-component-try-eject-out-of-stock");
|
||||||
|
VendingContents.AddItem(outOfStockText);
|
||||||
|
SetSizeAfterUpdate(outOfStockText.Length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (inventory.Count != VendingContents.Count)
|
||||||
|
{
|
||||||
|
if (inventory.Count > VendingContents.Count)
|
||||||
|
VendingContents.AddItem(string.Empty);
|
||||||
|
else
|
||||||
|
VendingContents.RemoveAt(VendingContents.Count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var longestEntry = string.Empty;
|
||||||
|
var spriteSystem = EntitySystem.Get<SpriteSystem>();
|
||||||
|
for (var i = 0; i < inventory.Count; i++)
|
||||||
|
{
|
||||||
|
var entry = inventory[i];
|
||||||
|
var vendingItem = VendingContents[i];
|
||||||
|
vendingItem.Text = string.Empty;
|
||||||
|
vendingItem.Icon = null;
|
||||||
|
|
||||||
|
var itemName = entry.ID;
|
||||||
|
Texture? icon = null;
|
||||||
|
if (_prototypeManager.TryIndex<EntityPrototype>(entry.ID, out var prototype))
|
||||||
|
{
|
||||||
|
itemName = prototype.Name;
|
||||||
|
icon = spriteSystem.GetPrototypeIcon(prototype).Default;
|
||||||
|
}
|
||||||
|
|
||||||
if (itemName.Length > longestEntry.Length)
|
if (itemName.Length > longestEntry.Length)
|
||||||
longestEntry = itemName;
|
longestEntry = itemName;
|
||||||
|
|
||||||
Texture? icon = null;
|
vendingItem.Text = $"{itemName} [{entry.Amount}]";
|
||||||
if(_prototypeManager.TryIndex(entry.ID, out EntityPrototype? prototype))
|
vendingItem.Icon = icon;
|
||||||
icon = SpriteComponent.GetPrototypeIcon(prototype, _resourceCache).Default;
|
|
||||||
|
|
||||||
VendingContents.AddItem($"{itemName} [{entry.Amount}]", icon);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SetSize = (Math.Clamp((longestEntry.Length + 2) * 12, 250, 300),
|
SetSizeAfterUpdate(longestEntry.Length);
|
||||||
Math.Clamp(VendingContents.Count * 50, 150, 350));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ItemSelected(ItemList.ItemListSelectedEventArgs args)
|
private void SetSizeAfterUpdate(int longestEntryLength)
|
||||||
{
|
{
|
||||||
Owner.Eject(_cachedInventory[args.ItemIndex].Type, _cachedInventory[args.ItemIndex].ID);
|
SetSize = (Math.Clamp((longestEntryLength + 2) * 12, 250, 300),
|
||||||
|
Math.Clamp(VendingContents.Count * 50, 150, 350));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,241 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Content.Shared.VendingMachines;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Robust.Client.Animations;
|
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
using Robust.Client.ResourceManagement;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using static Content.Shared.VendingMachines.SharedVendingMachineComponent;
|
|
||||||
|
|
||||||
namespace Content.Client.VendingMachines.UI
|
|
||||||
{
|
|
||||||
[UsedImplicitly]
|
|
||||||
public sealed class VendingMachineVisualizer : AppearanceVisualizer, ISerializationHooks
|
|
||||||
{
|
|
||||||
// TODO: Should default to off or broken if damaged
|
|
||||||
//
|
|
||||||
|
|
||||||
// TODO: The length of these animations is supposed to be dictated
|
|
||||||
// by the vending machine's pack prototype's `AnimationDuration`
|
|
||||||
// but we have no good way of passing that data from the server
|
|
||||||
// to the client at the moment. Rework Visualizers?
|
|
||||||
|
|
||||||
private Dictionary<string, bool> _baseStates = new();
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, VendingMachineVisualLayers> LayerMap =
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
{"off", VendingMachineVisualLayers.Unlit},
|
|
||||||
{"screen", VendingMachineVisualLayers.Screen},
|
|
||||||
{"normal", VendingMachineVisualLayers.Base},
|
|
||||||
{"normal-unshaded", VendingMachineVisualLayers.BaseUnshaded},
|
|
||||||
{"eject", VendingMachineVisualLayers.Base},
|
|
||||||
{"eject-unshaded", VendingMachineVisualLayers.BaseUnshaded},
|
|
||||||
{"deny", VendingMachineVisualLayers.Base},
|
|
||||||
{"deny-unshaded", VendingMachineVisualLayers.BaseUnshaded},
|
|
||||||
{"broken", VendingMachineVisualLayers.Unlit},
|
|
||||||
};
|
|
||||||
|
|
||||||
[DataField("screen")]
|
|
||||||
private bool _screen;
|
|
||||||
|
|
||||||
[DataField("normal")]
|
|
||||||
private bool _normal;
|
|
||||||
|
|
||||||
[DataField("normalUnshaded")]
|
|
||||||
private bool _normalUnshaded;
|
|
||||||
|
|
||||||
[DataField("eject")]
|
|
||||||
private bool _eject;
|
|
||||||
|
|
||||||
[DataField("ejectUnshaded")]
|
|
||||||
private bool _ejectUnshaded;
|
|
||||||
|
|
||||||
[DataField("deny")]
|
|
||||||
private bool _deny;
|
|
||||||
|
|
||||||
[DataField("denyUnshaded")]
|
|
||||||
private bool _denyUnshaded;
|
|
||||||
|
|
||||||
[DataField("broken")]
|
|
||||||
private bool _broken;
|
|
||||||
|
|
||||||
[DataField("brokenUnshaded")]
|
|
||||||
private bool _brokenUnshaded;
|
|
||||||
|
|
||||||
private readonly Dictionary<string, Animation> _animations = new();
|
|
||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
|
||||||
{
|
|
||||||
// Used a dictionary so the yaml can adhere to the style-guide and the texture states can be clear
|
|
||||||
var states = new Dictionary<string, bool>
|
|
||||||
{
|
|
||||||
{"off", true},
|
|
||||||
{"screen", _screen},
|
|
||||||
{"normal", _normal},
|
|
||||||
{"normal-unshaded", _normalUnshaded},
|
|
||||||
{"eject", _eject},
|
|
||||||
{"eject-unshaded", _ejectUnshaded},
|
|
||||||
{"deny", _deny},
|
|
||||||
{"deny-unshaded", _denyUnshaded},
|
|
||||||
{"broken", _broken},
|
|
||||||
{"broken-unshaded", _brokenUnshaded},
|
|
||||||
};
|
|
||||||
|
|
||||||
_baseStates = states;
|
|
||||||
|
|
||||||
if (_baseStates["deny"])
|
|
||||||
{
|
|
||||||
InitializeAnimation("deny");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_baseStates["deny-unshaded"])
|
|
||||||
{
|
|
||||||
InitializeAnimation("deny-unshaded", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_baseStates["eject"])
|
|
||||||
{
|
|
||||||
InitializeAnimation("eject");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_baseStates["eject-unshaded"])
|
|
||||||
{
|
|
||||||
InitializeAnimation("eject-unshaded", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeAnimation(string key, bool unshaded = false)
|
|
||||||
{
|
|
||||||
_animations.Add(key, new Animation {Length = TimeSpan.FromSeconds(1.2f)});
|
|
||||||
|
|
||||||
var flick = new AnimationTrackSpriteFlick();
|
|
||||||
_animations[key].AnimationTracks.Add(flick);
|
|
||||||
flick.LayerKey = unshaded ? VendingMachineVisualLayers.BaseUnshaded : VendingMachineVisualLayers.Base;
|
|
||||||
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame(key, 0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete("Subscribe to your component being initialised instead.")]
|
|
||||||
public override void InitializeEntity(EntityUid entity)
|
|
||||||
{
|
|
||||||
base.InitializeEntity(entity);
|
|
||||||
|
|
||||||
IoCManager.Resolve<IEntityManager>().EnsureComponent<AnimationPlayerComponent>(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HideLayers(ISpriteComponent spriteComponent)
|
|
||||||
{
|
|
||||||
foreach (var layer in spriteComponent.AllLayers)
|
|
||||||
{
|
|
||||||
layer.Visible = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
spriteComponent.LayerSetVisible(VendingMachineVisualLayers.Unlit, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete("Subscribe to AppearanceChangeEvent instead.")]
|
|
||||||
public override void OnChangeData(AppearanceComponent component)
|
|
||||||
{
|
|
||||||
base.OnChangeData(component);
|
|
||||||
|
|
||||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
|
||||||
var sprite = entMan.GetComponent<SpriteComponent>(component.Owner);
|
|
||||||
|
|
||||||
// TODO when moving to a system visualizer, re work how this is done
|
|
||||||
// Currently this only gets called during init, so unless it NEEEDS to be configurable, just make this party of the entity prototype.
|
|
||||||
if (component.TryGetData(VendingMachineVisuals.Inventory, out string? invId) &&
|
|
||||||
IoCManager.Resolve<IPrototypeManager>().TryIndex(invId, out VendingMachineInventoryPrototype? prototype) &&
|
|
||||||
IoCManager.Resolve<IResourceCache>().TryGetResource<RSIResource>(
|
|
||||||
SharedSpriteComponent.TextureRoot / $"Structures/Machines/VendingMachines/{prototype.SpriteName}.rsi", out var res))
|
|
||||||
{
|
|
||||||
sprite.BaseRSI = res.RSI;
|
|
||||||
}
|
|
||||||
|
|
||||||
var animPlayer = entMan.GetComponent<AnimationPlayerComponent>(component.Owner);
|
|
||||||
if (!component.TryGetData(VendingMachineVisuals.VisualState, out VendingMachineVisualState state))
|
|
||||||
{
|
|
||||||
state = VendingMachineVisualState.Normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide last state
|
|
||||||
HideLayers(sprite);
|
|
||||||
ActivateState(sprite, "off");
|
|
||||||
|
|
||||||
switch (state)
|
|
||||||
{
|
|
||||||
case VendingMachineVisualState.Normal:
|
|
||||||
ActivateState(sprite, "screen");
|
|
||||||
ActivateState(sprite, "normal-unshaded");
|
|
||||||
ActivateState(sprite, "normal");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VendingMachineVisualState.Off:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VendingMachineVisualState.Broken:
|
|
||||||
ActivateState(sprite, "broken-unshaded");
|
|
||||||
ActivateState(sprite, "broken");
|
|
||||||
|
|
||||||
break;
|
|
||||||
case VendingMachineVisualState.Deny:
|
|
||||||
ActivateState(sprite, "screen");
|
|
||||||
ActivateAnimation(sprite, animPlayer, "deny-unshaded");
|
|
||||||
ActivateAnimation(sprite, animPlayer, "deny");
|
|
||||||
|
|
||||||
break;
|
|
||||||
case VendingMachineVisualState.Eject:
|
|
||||||
ActivateState(sprite, "screen");
|
|
||||||
ActivateAnimation(sprite, animPlayer, "eject-unshaded");
|
|
||||||
ActivateAnimation(sprite, animPlayer, "eject");
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper methods just to avoid all of that hard-to-read-indented code
|
|
||||||
private void ActivateState(ISpriteComponent spriteComponent, string stateId)
|
|
||||||
{
|
|
||||||
// No state for it on the rsi :(
|
|
||||||
if (!_baseStates[stateId])
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var stateLayer = LayerMap[stateId];
|
|
||||||
spriteComponent.LayerSetVisible(stateLayer, true);
|
|
||||||
spriteComponent.LayerSetState(stateLayer, stateId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ActivateAnimation(ISpriteComponent spriteComponent, AnimationPlayerComponent animationPlayer, string key)
|
|
||||||
{
|
|
||||||
if (!_animations.TryGetValue(key, out var animation))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!animationPlayer.HasRunningAnimation(key))
|
|
||||||
{
|
|
||||||
spriteComponent.LayerSetVisible(LayerMap[key], true);
|
|
||||||
animationPlayer.Play(animation, key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum VendingMachineVisualLayers : byte
|
|
||||||
{
|
|
||||||
// Off / Broken. The other layers will overlay this if the machine is on.
|
|
||||||
Unlit,
|
|
||||||
// Normal / Deny / Eject
|
|
||||||
Base,
|
|
||||||
BaseUnshaded,
|
|
||||||
// Screens that are persistent (where the machine is not off or broken)
|
|
||||||
Screen,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,20 @@
|
|||||||
using Content.Client.VendingMachines.UI;
|
using Content.Client.VendingMachines.UI;
|
||||||
using Content.Shared.VendingMachines;
|
using Content.Shared.VendingMachines;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Shared.IoC;
|
using System.Linq;
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
using static Content.Shared.VendingMachines.SharedVendingMachineComponent;
|
|
||||||
|
|
||||||
namespace Content.Client.VendingMachines
|
namespace Content.Client.VendingMachines
|
||||||
{
|
{
|
||||||
public sealed class VendingMachineBoundUserInterface : BoundUserInterface
|
public sealed class VendingMachineBoundUserInterface : BoundUserInterface
|
||||||
{
|
{
|
||||||
[ViewVariables] private VendingMachineMenu? _menu;
|
[ViewVariables]
|
||||||
|
private VendingMachineMenu? _menu;
|
||||||
|
|
||||||
public SharedVendingMachineComponent? VendingMachine { get; private set; }
|
private List<VendingMachineInventoryEntry> _cachedInventory = new();
|
||||||
|
|
||||||
public VendingMachineBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
|
public VendingMachineBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
|
||||||
{
|
{
|
||||||
SendMessage(new InventorySyncRequestMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
@@ -24,33 +22,43 @@ namespace Content.Client.VendingMachines
|
|||||||
base.Open();
|
base.Open();
|
||||||
|
|
||||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||||
if (!entMan.TryGetComponent(Owner.Owner, out SharedVendingMachineComponent? vendingMachine))
|
var vendingMachineSys = EntitySystem.Get<VendingMachineSystem>();
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VendingMachine = vendingMachine;
|
_cachedInventory = vendingMachineSys.GetAllInventory(Owner.Owner);
|
||||||
|
|
||||||
_menu = new VendingMachineMenu(this) {Title = entMan.GetComponent<MetaDataComponent>(Owner.Owner).EntityName};
|
_menu = new VendingMachineMenu {Title = entMan.GetComponent<MetaDataComponent>(Owner.Owner).EntityName};
|
||||||
_menu.Populate(VendingMachine.AllInventory);
|
|
||||||
|
|
||||||
_menu.OnClose += Close;
|
_menu.OnClose += Close;
|
||||||
|
_menu.OnItemSelected += OnItemSelected;
|
||||||
|
|
||||||
|
_menu.Populate(_cachedInventory);
|
||||||
|
|
||||||
_menu.OpenCentered();
|
_menu.OpenCentered();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Eject(InventoryType type, string id)
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
{
|
{
|
||||||
SendMessage(new VendingMachineEjectMessage(type, id));
|
base.UpdateState(state);
|
||||||
|
|
||||||
|
if (state is not VendingMachineInterfaceState newState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_cachedInventory = newState.Inventory;
|
||||||
|
|
||||||
|
_menu?.Populate(_cachedInventory);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
private void OnItemSelected(ItemList.ItemListSelectedEventArgs args)
|
||||||
{
|
{
|
||||||
switch (message)
|
if (_cachedInventory == null || _cachedInventory.Count == 0)
|
||||||
{
|
return;
|
||||||
case VendingMachineInventoryMessage msg:
|
|
||||||
_menu?.Populate(msg.Inventory);
|
var selectedItem = _cachedInventory.ElementAtOrDefault(args.ItemIndex);
|
||||||
break;
|
|
||||||
}
|
if (selectedItem == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SendMessage(new VendingMachineEjectMessage(selectedItem.Type, selectedItem.ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
@@ -59,7 +67,12 @@ namespace Content.Client.VendingMachines
|
|||||||
if (!disposing)
|
if (!disposing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_menu?.Dispose();
|
if (_menu == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_menu.OnItemSelected -= OnItemSelected;
|
||||||
|
_menu.OnClose -= Close;
|
||||||
|
_menu.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,57 @@ namespace Content.Client.VendingMachines;
|
|||||||
|
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(SharedVendingMachineComponent))]
|
[ComponentReference(typeof(SharedVendingMachineComponent))]
|
||||||
|
[Access(typeof(VendingMachineSystem))]
|
||||||
public sealed class VendingMachineComponent : SharedVendingMachineComponent
|
public sealed class VendingMachineComponent : SharedVendingMachineComponent
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// RSI state for when the vending machine is unpowered.
|
||||||
|
/// Will be displayed on the layer <see cref="VendingMachineVisualLayers.Base"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField("offState")]
|
||||||
|
public string? OffState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RSI state for the screen of the vending machine
|
||||||
|
/// Will be displayed on the layer <see cref="VendingMachineVisualLayers.Screen"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField("screenState")]
|
||||||
|
public string? ScreenState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RSI state for the vending machine's normal state. Usually a looping animation.
|
||||||
|
/// Will be displayed on the layer <see cref="VendingMachineVisualLayers.BaseUnshaded"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField("normalState")]
|
||||||
|
public string? NormalState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RSI state for the vending machine's eject animation.
|
||||||
|
/// Will be displayed on the layer <see cref="VendingMachineVisualLayers.BaseUnshaded"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField("ejectState")]
|
||||||
|
public string? EjectState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RSI state for the vending machine's deny animation. Will either be played once as sprite flick
|
||||||
|
/// or looped depending on how <see cref="LoopDenyAnimation"/> is set.
|
||||||
|
/// Will be displayed on the layer <see cref="VendingMachineVisualLayers.BaseUnshaded"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField("denyState")]
|
||||||
|
public string? DenyState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RSI state for when the vending machine is unpowered.
|
||||||
|
/// Will be displayed on the layer <see cref="VendingMachineVisualLayers.Base"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField("brokenState")]
|
||||||
|
public string? BrokenState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If set to <c>true</c> (default) will loop the animation of the <see cref="DenyState"/> for the duration
|
||||||
|
/// of <see cref="SharedVendingMachineComponent.DenyDelay"/>. If set to <c>false</c> will play a sprite
|
||||||
|
/// flick animation for the state and then linger on the final frame until the end of the delay.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("loopDeny")]
|
||||||
|
public bool LoopDenyAnimation = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,148 @@
|
|||||||
using Content.Shared.VendingMachines;
|
using Content.Shared.VendingMachines;
|
||||||
|
using Robust.Client.Animations;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
namespace Content.Client.VendingMachines;
|
namespace Content.Client.VendingMachines;
|
||||||
|
|
||||||
public sealed class VendingMachineSystem : SharedVendingMachineSystem
|
public sealed class VendingMachineSystem : SharedVendingMachineSystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<VendingMachineComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||||
|
SubscribeLocalEvent<VendingMachineComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAnimationCompleted(EntityUid uid, VendingMachineComponent component, AnimationCompletedEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp<SpriteComponent>(uid, out var sprite))
|
||||||
|
return;
|
||||||
|
|
||||||
|
UpdateAppearance(uid, VendingMachineVisualState.Normal, component, sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAppearanceChange(EntityUid uid, VendingMachineComponent component, ref AppearanceChangeEvent args)
|
||||||
|
{
|
||||||
|
if (args.Sprite == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!args.AppearanceData.TryGetValue(VendingMachineVisuals.VisualState, out var visualStateObject) ||
|
||||||
|
visualStateObject is not VendingMachineVisualState visualState)
|
||||||
|
{
|
||||||
|
visualState = VendingMachineVisualState.Normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateAppearance(uid, visualState, component, args.Sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAppearance(EntityUid uid, VendingMachineVisualState visualState, VendingMachineComponent component, SpriteComponent sprite)
|
||||||
|
{
|
||||||
|
SetLayerState(VendingMachineVisualLayers.Base, component.OffState, sprite);
|
||||||
|
|
||||||
|
switch (visualState)
|
||||||
|
{
|
||||||
|
case VendingMachineVisualState.Normal:
|
||||||
|
SetLayerState(VendingMachineVisualLayers.BaseUnshaded, component.NormalState, sprite);
|
||||||
|
SetLayerState(VendingMachineVisualLayers.Screen, component.ScreenState, sprite);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VendingMachineVisualState.Deny:
|
||||||
|
if (component.LoopDenyAnimation)
|
||||||
|
SetLayerState(VendingMachineVisualLayers.BaseUnshaded, component.DenyState, sprite);
|
||||||
|
else
|
||||||
|
PlayAnimation(uid, VendingMachineVisualLayers.BaseUnshaded, component.DenyState, component.DenyDelay, sprite);
|
||||||
|
|
||||||
|
SetLayerState(VendingMachineVisualLayers.Screen, component.ScreenState, sprite);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VendingMachineVisualState.Eject:
|
||||||
|
PlayAnimation(uid, VendingMachineVisualLayers.BaseUnshaded, component.EjectState, component.EjectDelay, sprite);
|
||||||
|
SetLayerState(VendingMachineVisualLayers.Screen, component.ScreenState, sprite);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VendingMachineVisualState.Broken:
|
||||||
|
HideLayers(sprite);
|
||||||
|
SetLayerState(VendingMachineVisualLayers.Base, component.BrokenState, sprite);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VendingMachineVisualState.Off:
|
||||||
|
HideLayers(sprite);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetLayerState(VendingMachineVisualLayers layer, string? state, SpriteComponent sprite)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(state))
|
||||||
|
return;
|
||||||
|
|
||||||
|
sprite.LayerSetVisible(layer, true);
|
||||||
|
sprite.LayerSetAutoAnimated(layer, true);
|
||||||
|
sprite.LayerSetState(layer, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PlayAnimation(EntityUid uid, VendingMachineVisualLayers layer, string? state, float animationTime, SpriteComponent sprite)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(state))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_animationPlayer.HasRunningAnimation(uid, state))
|
||||||
|
{
|
||||||
|
var animation = GetAnimation(layer, state, animationTime);
|
||||||
|
sprite.LayerSetVisible(layer, true);
|
||||||
|
_animationPlayer.Play(uid, animation, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Animation GetAnimation(VendingMachineVisualLayers layer, string state, float animationTime)
|
||||||
|
{
|
||||||
|
return new Animation
|
||||||
|
{
|
||||||
|
Length = TimeSpan.FromSeconds(animationTime),
|
||||||
|
AnimationTracks =
|
||||||
|
{
|
||||||
|
new AnimationTrackSpriteFlick
|
||||||
|
{
|
||||||
|
LayerKey = layer,
|
||||||
|
KeyFrames =
|
||||||
|
{
|
||||||
|
new AnimationTrackSpriteFlick.KeyFrame(state, 0f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HideLayers(SpriteComponent sprite)
|
||||||
|
{
|
||||||
|
HideLayer(VendingMachineVisualLayers.BaseUnshaded, sprite);
|
||||||
|
HideLayer(VendingMachineVisualLayers.Screen, sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HideLayer(VendingMachineVisualLayers layer, SpriteComponent sprite)
|
||||||
|
{
|
||||||
|
if (!sprite.LayerMapTryGet(layer, out var actualLayer))
|
||||||
|
return;
|
||||||
|
|
||||||
|
sprite.LayerSetVisible(actualLayer, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum VendingMachineVisualLayers : byte
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Off / Broken. The other layers will overlay this if the machine is on.
|
||||||
|
/// </summary>
|
||||||
|
Base,
|
||||||
|
/// <summary>
|
||||||
|
/// Normal / Deny / Eject
|
||||||
|
/// </summary>
|
||||||
|
BaseUnshaded,
|
||||||
|
/// <summary>
|
||||||
|
/// Screens that are persistent (where the machine is not off or broken)
|
||||||
|
/// </summary>
|
||||||
|
Screen
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace Content.IntegrationTests.Tests
|
|||||||
}
|
}
|
||||||
catch (UnknownPrototypeException)
|
catch (UnknownPrototypeException)
|
||||||
{
|
{
|
||||||
throw new UnknownPrototypeException($"Unknown prototype {item} on vending inventory {vendorProto.Name}");
|
throw new UnknownPrototypeException($"Unknown prototype {item} on vending inventory {vendorProto.ID}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using Content.Server.VendingMachines;
|
using Content.Server.VendingMachines;
|
||||||
using Content.Shared.Throwing;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
|
|
||||||
namespace Content.Server.Destructible.Thresholds.Behaviors
|
namespace Content.Server.Destructible.Thresholds.Behaviors
|
||||||
{
|
{
|
||||||
@@ -30,17 +28,15 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
|
|||||||
!system.EntityManager.TryGetComponent<TransformComponent>(owner, out var xform))
|
!system.EntityManager.TryGetComponent<TransformComponent>(owner, out var xform))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var throwingsys = system.EntityManager.EntitySysManager.GetEntitySystem<ThrowingSystem>();
|
var vendingMachineSystem = EntitySystem.Get<VendingMachineSystem>();
|
||||||
var totalItems = vendingcomp.AllInventory.Count;
|
var inventory = vendingMachineSystem.GetAvailableInventory(owner, vendingcomp);
|
||||||
|
if (inventory.Count <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
var toEject = Math.Min(totalItems * Percent, Max);
|
var toEject = Math.Min(inventory.Count * Percent, Max);
|
||||||
for (var i = 0; i < toEject; i++)
|
for (var i = 0; i < toEject; i++)
|
||||||
{
|
{
|
||||||
var entity = system.EntityManager.SpawnEntity(system.Random.PickAndTake(vendingcomp.AllInventory).ID, xform.Coordinates);
|
vendingMachineSystem.EjectRandom(owner, throwItem: true, forceEject: true, vendingcomp);
|
||||||
|
|
||||||
float range = vendingcomp.NonLimitedEjectRange;
|
|
||||||
Vector2 direction = new Vector2(system.Random.NextFloat(-range, range), system.Random.NextFloat(-range, range));
|
|
||||||
throwingsys.TryThrow(entity, direction, vendingcomp.NonLimitedEjectForce);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using Content.Server.UserInterface;
|
|
||||||
using Content.Shared.Actions.ActionTypes;
|
using Content.Shared.Actions.ActionTypes;
|
||||||
using Content.Shared.VendingMachines;
|
using Content.Shared.VendingMachines;
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
|
||||||
@@ -9,9 +7,14 @@ namespace Content.Server.VendingMachines
|
|||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(SharedVendingMachineComponent))]
|
[ComponentReference(typeof(SharedVendingMachineComponent))]
|
||||||
|
[Access(typeof(VendingMachineSystem))]
|
||||||
public sealed class VendingMachineComponent : SharedVendingMachineComponent
|
public sealed class VendingMachineComponent : SharedVendingMachineComponent
|
||||||
{
|
{
|
||||||
public bool Ejecting;
|
public bool Ejecting;
|
||||||
|
public bool Denying;
|
||||||
|
public bool DispenseOnHitCoolingDown;
|
||||||
|
|
||||||
|
public string? NextItemToEject;
|
||||||
|
|
||||||
public bool Broken;
|
public bool Broken;
|
||||||
|
|
||||||
@@ -21,6 +24,8 @@ namespace Content.Server.VendingMachines
|
|||||||
[DataField("speedLimiter")]
|
[DataField("speedLimiter")]
|
||||||
public bool CanShoot = false;
|
public bool CanShoot = false;
|
||||||
|
|
||||||
|
public bool ThrowNextItem = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The chance that a vending machine will randomly dispense an item on hit.
|
/// The chance that a vending machine will randomly dispense an item on hit.
|
||||||
/// Chance is 0 if null.
|
/// Chance is 0 if null.
|
||||||
@@ -28,24 +33,48 @@ namespace Content.Server.VendingMachines
|
|||||||
[DataField("dispenseOnHitChance")]
|
[DataField("dispenseOnHitChance")]
|
||||||
public float? DispenseOnHitChance;
|
public float? DispenseOnHitChance;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum amount of damage that must be done per hit to have a chance
|
||||||
|
/// of dispensing an item.
|
||||||
|
/// </summary>
|
||||||
[DataField("dispenseOnHitThreshold")]
|
[DataField("dispenseOnHitThreshold")]
|
||||||
public float? DispenseOnHitThreshold;
|
public float? DispenseOnHitThreshold;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Amount of time in seconds that need to pass before damage can cause a vending machine to eject again.
|
||||||
|
/// This value is separate to <see cref="SharedVendingMachineComponent.EjectDelay"/> because that value might be
|
||||||
|
/// 0 for a vending machine for legitimate reasons (no desired delay/no eject animation)
|
||||||
|
/// and can be circumvented with forced ejections.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("dispenseOnHitCooldown")]
|
||||||
|
public float? DispenseOnHitCooldown = 1.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sound that plays when ejecting an item
|
||||||
|
/// </summary>
|
||||||
[DataField("soundVend")]
|
[DataField("soundVend")]
|
||||||
// Grabbed from: https://github.com/discordia-space/CEV-Eris/blob/f702afa271136d093ddeb415423240a2ceb212f0/sound/machines/vending_drop.ogg
|
// Grabbed from: https://github.com/discordia-space/CEV-Eris/blob/f702afa271136d093ddeb415423240a2ceb212f0/sound/machines/vending_drop.ogg
|
||||||
public SoundSpecifier SoundVend = new SoundPathSpecifier("/Audio/Machines/machine_vend.ogg");
|
public SoundSpecifier SoundVend = new SoundPathSpecifier("/Audio/Machines/machine_vend.ogg");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sound that plays when an item can't be ejected
|
||||||
|
/// </summary>
|
||||||
[DataField("soundDeny")]
|
[DataField("soundDeny")]
|
||||||
// Yoinked from: https://github.com/discordia-space/CEV-Eris/blob/35bbad6764b14e15c03a816e3e89aa1751660ba9/sound/machines/Custom_deny.ogg
|
// Yoinked from: https://github.com/discordia-space/CEV-Eris/blob/35bbad6764b14e15c03a816e3e89aa1751660ba9/sound/machines/Custom_deny.ogg
|
||||||
public SoundSpecifier SoundDeny = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg");
|
public SoundSpecifier SoundDeny = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The action available to the player controlling the vending machine
|
||||||
|
/// </summary>
|
||||||
[DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer<InstantActionPrototype>))]
|
[DataField("action", customTypeSerializer: typeof(PrototypeIdSerializer<InstantActionPrototype>))]
|
||||||
public string? Action = "VendingThrow";
|
public string? Action = "VendingThrow";
|
||||||
|
|
||||||
[ViewVariables] public BoundUserInterface? UserInterface => Owner.GetUIOrNull(VendingMachineUiKey.Key);
|
|
||||||
|
|
||||||
public float NonLimitedEjectForce = 7.5f;
|
public float NonLimitedEjectForce = 7.5f;
|
||||||
|
|
||||||
public float NonLimitedEjectRange = 5f;
|
public float NonLimitedEjectRange = 5f;
|
||||||
|
|
||||||
|
public float EjectAccumulator = 0f;
|
||||||
|
public float DenyAccumulator = 0f;
|
||||||
|
public float DispenseOnHitAccumulator = 0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public sealed class VendingMachineEjectItemWireAction : BaseWireAction
|
|||||||
{
|
{
|
||||||
if (EntityManager.TryGetComponent(wire.Owner, out VendingMachineComponent? vending))
|
if (EntityManager.TryGetComponent(wire.Owner, out VendingMachineComponent? vending))
|
||||||
{
|
{
|
||||||
vending.CanShoot = true;
|
_vendingMachineSystem.SetShooting(wire.Owner, true, vending);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -52,7 +52,7 @@ public sealed class VendingMachineEjectItemWireAction : BaseWireAction
|
|||||||
{
|
{
|
||||||
if (EntityManager.TryGetComponent(wire.Owner, out VendingMachineComponent? vending))
|
if (EntityManager.TryGetComponent(wire.Owner, out VendingMachineComponent? vending))
|
||||||
{
|
{
|
||||||
vending.CanShoot = false;
|
_vendingMachineSystem.SetShooting(wire.Owner, false, vending);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Linq;
|
|
||||||
using Content.Server.Popups;
|
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.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;
|
||||||
@@ -16,7 +16,6 @@ using Robust.Shared.Audio;
|
|||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using static Content.Shared.VendingMachines.SharedVendingMachineComponent;
|
|
||||||
|
|
||||||
namespace Content.Server.VendingMachines
|
namespace Content.Server.VendingMachines
|
||||||
{
|
{
|
||||||
@@ -28,18 +27,23 @@ namespace Content.Server.VendingMachines
|
|||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
|
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
|
||||||
[Dependency] private readonly SharedActionsSystem _action = default!;
|
[Dependency] private readonly SharedActionsSystem _action = default!;
|
||||||
|
[Dependency] private readonly AudioSystem _audioSystem = default!;
|
||||||
|
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
||||||
|
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<VendingMachineComponent, PowerChangedEvent>(OnPowerChanged);
|
SubscribeLocalEvent<VendingMachineComponent, PowerChangedEvent>(OnPowerChanged);
|
||||||
SubscribeLocalEvent<VendingMachineComponent, InventorySyncRequestMessage>(OnInventoryRequestMessage);
|
|
||||||
SubscribeLocalEvent<VendingMachineComponent, VendingMachineEjectMessage>(OnInventoryEjectMessage);
|
|
||||||
SubscribeLocalEvent<VendingMachineComponent, BreakageEventArgs>(OnBreak);
|
SubscribeLocalEvent<VendingMachineComponent, BreakageEventArgs>(OnBreak);
|
||||||
SubscribeLocalEvent<VendingMachineComponent, GotEmaggedEvent>(OnEmagged);
|
SubscribeLocalEvent<VendingMachineComponent, GotEmaggedEvent>(OnEmagged);
|
||||||
SubscribeLocalEvent<VendingMachineComponent, DamageChangedEvent>(OnDamage);
|
SubscribeLocalEvent<VendingMachineComponent, DamageChangedEvent>(OnDamage);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<VendingMachineComponent, ActivatableUIOpenAttemptEvent>(OnActivatableUIOpenAttempt);
|
||||||
|
SubscribeLocalEvent<VendingMachineComponent, BoundUIOpenedEvent>(OnBoundUIOpened);
|
||||||
|
SubscribeLocalEvent<VendingMachineComponent, VendingMachineEjectMessage>(OnInventoryEjectMessage);
|
||||||
|
|
||||||
SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense);
|
SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,9 +53,9 @@ namespace Content.Server.VendingMachines
|
|||||||
|
|
||||||
var component = (VendingMachineComponent) sharedComponent;
|
var component = (VendingMachineComponent) sharedComponent;
|
||||||
|
|
||||||
if (TryComp<ApcPowerReceiverComponent>(component.Owner, out var receiver))
|
if (HasComp<ApcPowerReceiverComponent>(component.Owner))
|
||||||
{
|
{
|
||||||
TryUpdateVisualState(uid, null, component);
|
TryUpdateVisualState(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component.Action != null)
|
if (component.Action != null)
|
||||||
@@ -61,17 +65,22 @@ namespace Content.Server.VendingMachines
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInventoryRequestMessage(EntityUid uid, VendingMachineComponent component, InventorySyncRequestMessage args)
|
private void OnActivatableUIOpenAttempt(EntityUid uid, VendingMachineComponent component, ActivatableUIOpenAttemptEvent args)
|
||||||
{
|
{
|
||||||
if (!this.IsPowered(uid, EntityManager))
|
if (component.Broken)
|
||||||
return;
|
args.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
var inventory = new List<VendingMachineInventoryEntry>(component.Inventory);
|
private void OnBoundUIOpened(EntityUid uid, VendingMachineComponent component, BoundUIOpenedEvent args)
|
||||||
|
{
|
||||||
|
UpdateVendingMachineInterfaceState(component);
|
||||||
|
}
|
||||||
|
|
||||||
if (component.Emagged) inventory.AddRange(component.EmaggedInventory);
|
private void UpdateVendingMachineInterfaceState(VendingMachineComponent component)
|
||||||
if (component.Contraband) inventory.AddRange(component.ContrabandInventory);
|
{
|
||||||
|
var state = new VendingMachineInterfaceState(GetAllInventory(component.Owner, component));
|
||||||
|
|
||||||
component.UserInterface?.SendMessage(new VendingMachineInventoryMessage(inventory));
|
_userInterfaceSystem.TrySetUiState(component.Owner, VendingMachineUiKey.Key, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInventoryEjectMessage(EntityUid uid, VendingMachineComponent component, VendingMachineEjectMessage args)
|
private void OnInventoryEjectMessage(EntityUid uid, VendingMachineComponent component, VendingMachineEjectMessage args)
|
||||||
@@ -87,13 +96,13 @@ namespace Content.Server.VendingMachines
|
|||||||
|
|
||||||
private void OnPowerChanged(EntityUid uid, VendingMachineComponent component, PowerChangedEvent args)
|
private void OnPowerChanged(EntityUid uid, VendingMachineComponent component, PowerChangedEvent args)
|
||||||
{
|
{
|
||||||
TryUpdateVisualState(uid, null, component);
|
TryUpdateVisualState(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBreak(EntityUid uid, VendingMachineComponent vendComponent, BreakageEventArgs eventArgs)
|
private void OnBreak(EntityUid uid, VendingMachineComponent vendComponent, BreakageEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
vendComponent.Broken = true;
|
vendComponent.Broken = true;
|
||||||
TryUpdateVisualState(uid, VendingMachineVisualState.Broken, vendComponent);
|
TryUpdateVisualState(uid, vendComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEmagged(EntityUid uid, VendingMachineComponent component, GotEmaggedEvent args)
|
private void OnEmagged(EntityUid uid, VendingMachineComponent component, GotEmaggedEvent args)
|
||||||
@@ -107,11 +116,17 @@ namespace Content.Server.VendingMachines
|
|||||||
|
|
||||||
private void OnDamage(EntityUid uid, VendingMachineComponent component, DamageChangedEvent args)
|
private void OnDamage(EntityUid uid, VendingMachineComponent component, DamageChangedEvent args)
|
||||||
{
|
{
|
||||||
if (component.DispenseOnHitChance == null || args.DamageDelta == null)
|
if (component.Broken || component.DispenseOnHitCoolingDown ||
|
||||||
|
component.DispenseOnHitChance == null || args.DamageDelta == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (args.DamageDelta.Total >= component.DispenseOnHitThreshold && _random.Prob(component.DispenseOnHitChance.Value))
|
if (args.DamageIncreased && args.DamageDelta.Total >= component.DispenseOnHitThreshold &&
|
||||||
EjectRandom(uid, true, component);
|
_random.Prob(component.DispenseOnHitChance.Value))
|
||||||
|
{
|
||||||
|
if (component.DispenseOnHitCooldown > 0f)
|
||||||
|
component.DispenseOnHitCoolingDown = true;
|
||||||
|
EjectRandom(uid, throwItem: true, forceEject: true, component);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSelfDispense(EntityUid uid, VendingMachineComponent component, VendingMachineSelfDispenseEvent args)
|
private void OnSelfDispense(EntityUid uid, VendingMachineComponent component, VendingMachineSelfDispenseEvent args)
|
||||||
@@ -120,7 +135,18 @@ namespace Content.Server.VendingMachines
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
EjectRandom(uid, true, component);
|
EjectRandom(uid, throwItem: true, forceEject: false, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the <see cref="VendingMachineComponent.CanShoot"/> property of the vending machine.
|
||||||
|
/// </summary>
|
||||||
|
public void SetShooting(EntityUid uid, bool canShoot, VendingMachineComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.CanShoot = canShoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Deny(EntityUid uid, VendingMachineComponent? vendComponent = null)
|
public void Deny(EntityUid uid, VendingMachineComponent? vendComponent = null)
|
||||||
@@ -128,16 +154,18 @@ namespace Content.Server.VendingMachines
|
|||||||
if (!Resolve(uid, ref vendComponent))
|
if (!Resolve(uid, ref vendComponent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SoundSystem.Play(vendComponent.SoundDeny.GetSound(), Filter.Pvs(vendComponent.Owner), vendComponent.Owner, AudioParams.Default.WithVolume(-2f));
|
if (vendComponent.Denying)
|
||||||
// Play the Deny animation
|
return;
|
||||||
TryUpdateVisualState(uid, VendingMachineVisualState.Deny, vendComponent);
|
|
||||||
//TODO: This duration should be a distinct value specific to the deny animation
|
vendComponent.Denying = true;
|
||||||
vendComponent.Owner.SpawnTimer(vendComponent.AnimationDuration, () =>
|
_audioSystem.Play(vendComponent.SoundDeny, Filter.Pvs(vendComponent.Owner), vendComponent.Owner, AudioParams.Default.WithVolume(-2f));
|
||||||
{
|
TryUpdateVisualState(uid, vendComponent);
|
||||||
TryUpdateVisualState(uid, VendingMachineVisualState.Normal, vendComponent);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the user is authorized to use this vending machine
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">Entity trying to use the vending machine</param>
|
||||||
public bool IsAuthorized(EntityUid uid, EntityUid? sender, VendingMachineComponent? vendComponent = null)
|
public bool IsAuthorized(EntityUid uid, EntityUid? sender, VendingMachineComponent? vendComponent = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref vendComponent) || sender == null)
|
if (!Resolve(uid, ref vendComponent) || sender == null)
|
||||||
@@ -155,6 +183,13 @@ namespace Content.Server.VendingMachines
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to eject the provided item. Will do nothing if the vending machine is incapable of ejecting, already ejecting
|
||||||
|
/// or the item doesn't exist in its inventory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type of inventory the item is from</param>
|
||||||
|
/// <param name="itemId">The prototype ID of the item</param>
|
||||||
|
/// <param name="throwItem">Whether the item should be thrown in a random direction after ejection</param>
|
||||||
public void TryEjectVendorItem(EntityUid uid, InventoryType type, string itemId, bool throwItem, VendingMachineComponent? vendComponent = null)
|
public void TryEjectVendorItem(EntityUid uid, InventoryType type, string itemId, bool throwItem, VendingMachineComponent? vendComponent = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref vendComponent))
|
if (!Resolve(uid, ref vendComponent))
|
||||||
@@ -165,13 +200,7 @@ namespace Content.Server.VendingMachines
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var entry = type switch
|
var entry = GetEntry(itemId, type, vendComponent);
|
||||||
{
|
|
||||||
InventoryType.Regular => vendComponent.Inventory.Find(x => x.ID == itemId),
|
|
||||||
InventoryType.Emagged when vendComponent.Emagged => vendComponent.EmaggedInventory.Find(x => x.ID == itemId),
|
|
||||||
InventoryType.Contraband when vendComponent.Contraband => vendComponent.ContrabandInventory.Find(x => x.ID == itemId),
|
|
||||||
_ => null
|
|
||||||
};
|
|
||||||
|
|
||||||
if (entry == null)
|
if (entry == null)
|
||||||
{
|
{
|
||||||
@@ -195,24 +224,20 @@ namespace Content.Server.VendingMachines
|
|||||||
|
|
||||||
// Start Ejecting, and prevent users from ordering while anim playing
|
// Start Ejecting, and prevent users from ordering while anim playing
|
||||||
vendComponent.Ejecting = true;
|
vendComponent.Ejecting = true;
|
||||||
|
vendComponent.NextItemToEject = entry.ID;
|
||||||
|
vendComponent.ThrowNextItem = throwItem;
|
||||||
entry.Amount--;
|
entry.Amount--;
|
||||||
vendComponent.UserInterface?.SendMessage(new VendingMachineInventoryMessage(vendComponent.AllInventory));
|
UpdateVendingMachineInterfaceState(vendComponent);
|
||||||
TryUpdateVisualState(uid, VendingMachineVisualState.Eject, vendComponent);
|
TryUpdateVisualState(uid, vendComponent);
|
||||||
vendComponent.Owner.SpawnTimer(vendComponent.AnimationDuration, () =>
|
_audioSystem.Play(vendComponent.SoundVend, Filter.Pvs(vendComponent.Owner), vendComponent.Owner, AudioParams.Default.WithVolume(-2f));
|
||||||
{
|
|
||||||
vendComponent.Ejecting = false;
|
|
||||||
TryUpdateVisualState(uid, VendingMachineVisualState.Normal, vendComponent);
|
|
||||||
var ent = EntityManager.SpawnEntity(entry.ID, transformComp.Coordinates);
|
|
||||||
if (throwItem)
|
|
||||||
{
|
|
||||||
float range = vendComponent.NonLimitedEjectRange;
|
|
||||||
Vector2 direction = new Vector2(_random.NextFloat(-range, range), _random.NextFloat(-range, range));
|
|
||||||
_throwingSystem.TryThrow(ent, direction, vendComponent.NonLimitedEjectForce);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
SoundSystem.Play(vendComponent.SoundVend.GetSound(), Filter.Pvs(vendComponent.Owner), vendComponent.Owner, AudioParams.Default.WithVolume(-2f));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the user is authorized to use the vending machine, then ejects the provided item if true
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">Entity that is trying to use the vending machine</param>
|
||||||
|
/// <param name="type">The type of inventory the item is from</param>
|
||||||
|
/// <param name="itemId">The prototype ID of the item</param>
|
||||||
public void AuthorizedVend(EntityUid uid, EntityUid sender, InventoryType type, string itemId, VendingMachineComponent component)
|
public void AuthorizedVend(EntityUid uid, EntityUid sender, InventoryType type, string itemId, VendingMachineComponent component)
|
||||||
{
|
{
|
||||||
if (IsAuthorized(uid, sender, component))
|
if (IsAuthorized(uid, sender, component))
|
||||||
@@ -221,12 +246,15 @@ namespace Content.Server.VendingMachines
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TryUpdateVisualState(EntityUid uid, VendingMachineVisualState? state = VendingMachineVisualState.Normal, VendingMachineComponent? vendComponent = null)
|
/// <summary>
|
||||||
|
/// Tries to update the visuals of the component based on its current state.
|
||||||
|
/// </summary>
|
||||||
|
public void TryUpdateVisualState(EntityUid uid, VendingMachineComponent? vendComponent = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref vendComponent))
|
if (!Resolve(uid, ref vendComponent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var finalState = state == null ? VendingMachineVisualState.Normal : state;
|
var finalState = VendingMachineVisualState.Normal;
|
||||||
if (vendComponent.Broken)
|
if (vendComponent.Broken)
|
||||||
{
|
{
|
||||||
finalState = VendingMachineVisualState.Broken;
|
finalState = VendingMachineVisualState.Broken;
|
||||||
@@ -235,6 +263,10 @@ namespace Content.Server.VendingMachines
|
|||||||
{
|
{
|
||||||
finalState = VendingMachineVisualState.Eject;
|
finalState = VendingMachineVisualState.Eject;
|
||||||
}
|
}
|
||||||
|
else if (vendComponent.Denying)
|
||||||
|
{
|
||||||
|
finalState = VendingMachineVisualState.Deny;
|
||||||
|
}
|
||||||
else if (!this.IsPowered(uid, EntityManager))
|
else if (!this.IsPowered(uid, EntityManager))
|
||||||
{
|
{
|
||||||
finalState = VendingMachineVisualState.Off;
|
finalState = VendingMachineVisualState.Off;
|
||||||
@@ -242,23 +274,121 @@ namespace Content.Server.VendingMachines
|
|||||||
|
|
||||||
if (TryComp<AppearanceComponent>(vendComponent.Owner, out var appearance))
|
if (TryComp<AppearanceComponent>(vendComponent.Owner, out var appearance))
|
||||||
{
|
{
|
||||||
appearance.SetData(VendingMachineVisuals.VisualState, finalState);
|
_appearanceSystem.SetData(uid, VendingMachineVisuals.VisualState, finalState, appearance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EjectRandom(EntityUid uid, bool throwItem, VendingMachineComponent? vendComponent = null)
|
/// <summary>
|
||||||
|
/// Ejects a random item from the available stock. Will do nothing if the vending machine is empty.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="throwItem">Whether to throw the item in a random direction after dispensing it.</param>
|
||||||
|
/// <param name="forceEject">Whether to skip the regular ejection checks and immediately dispense the item without animation.</param>
|
||||||
|
public void EjectRandom(EntityUid uid, bool throwItem, bool forceEject = false, VendingMachineComponent? vendComponent = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref vendComponent))
|
if (!Resolve(uid, ref vendComponent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var availableItems = vendComponent.AllInventory.Where(x => x.Amount > 0).ToList();
|
var availableItems = GetAvailableInventory(uid, vendComponent);
|
||||||
if (availableItems.Count <= 0)
|
if (availableItems.Count <= 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var item = _random.Pick(availableItems);
|
var item = _random.Pick(availableItems);
|
||||||
|
|
||||||
|
if (forceEject)
|
||||||
|
{
|
||||||
|
vendComponent.NextItemToEject = item.ID;
|
||||||
|
vendComponent.ThrowNextItem = throwItem;
|
||||||
|
var entry = GetEntry(item.ID, item.Type, vendComponent);
|
||||||
|
if (entry != null)
|
||||||
|
entry.Amount--;
|
||||||
|
EjectItem(vendComponent, forceEject);
|
||||||
|
}
|
||||||
|
else
|
||||||
TryEjectVendorItem(uid, item.Type, item.ID, throwItem, vendComponent);
|
TryEjectVendorItem(uid, item.Type, item.ID, throwItem, vendComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EjectItem(VendingMachineComponent vendComponent, bool forceEject = false)
|
||||||
|
{
|
||||||
|
// No need to update the visual state because we never changed it during a forced eject
|
||||||
|
if (!forceEject)
|
||||||
|
TryUpdateVisualState(vendComponent.Owner, vendComponent);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(vendComponent.NextItemToEject))
|
||||||
|
{
|
||||||
|
vendComponent.ThrowNextItem = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ent = EntityManager.SpawnEntity(vendComponent.NextItemToEject, Transform(vendComponent.Owner).Coordinates);
|
||||||
|
if (vendComponent.ThrowNextItem)
|
||||||
|
{
|
||||||
|
var range = vendComponent.NonLimitedEjectRange;
|
||||||
|
var direction = new Vector2(_random.NextFloat(-range, range), _random.NextFloat(-range, range));
|
||||||
|
_throwingSystem.TryThrow(ent, direction, vendComponent.NonLimitedEjectForce);
|
||||||
|
}
|
||||||
|
|
||||||
|
vendComponent.NextItemToEject = null;
|
||||||
|
vendComponent.ThrowNextItem = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DenyItem(VendingMachineComponent vendComponent)
|
||||||
|
{
|
||||||
|
TryUpdateVisualState(vendComponent.Owner, vendComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VendingMachineInventoryEntry? GetEntry(string entryId, InventoryType type, VendingMachineComponent component)
|
||||||
|
{
|
||||||
|
if (type == InventoryType.Emagged && component.Emagged)
|
||||||
|
return component.EmaggedInventory.GetValueOrDefault(entryId);
|
||||||
|
|
||||||
|
if (type == InventoryType.Contraband && component.Contraband)
|
||||||
|
return component.ContrabandInventory.GetValueOrDefault(entryId);
|
||||||
|
|
||||||
|
return component.Inventory.GetValueOrDefault(entryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
foreach (var comp in EntityQuery<VendingMachineComponent>())
|
||||||
|
{
|
||||||
|
if (comp.Ejecting)
|
||||||
|
{
|
||||||
|
comp.EjectAccumulator += frameTime;
|
||||||
|
if (comp.EjectAccumulator >= comp.EjectDelay)
|
||||||
|
{
|
||||||
|
comp.EjectAccumulator = 0f;
|
||||||
|
comp.Ejecting = false;
|
||||||
|
|
||||||
|
EjectItem(comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comp.Denying)
|
||||||
|
{
|
||||||
|
comp.DenyAccumulator += frameTime;
|
||||||
|
if (comp.DenyAccumulator >= comp.DenyDelay)
|
||||||
|
{
|
||||||
|
comp.DenyAccumulator = 0f;
|
||||||
|
comp.Denying = false;
|
||||||
|
|
||||||
|
DenyItem(comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comp.DispenseOnHitCoolingDown)
|
||||||
|
{
|
||||||
|
comp.DispenseOnHitAccumulator += frameTime;
|
||||||
|
if (comp.DispenseOnHitAccumulator >= comp.DispenseOnHitCooldown)
|
||||||
|
{
|
||||||
|
comp.DispenseOnHitAccumulator = 0f;
|
||||||
|
comp.DispenseOnHitCoolingDown = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,85 +8,45 @@ namespace Content.Shared.VendingMachines
|
|||||||
[NetworkedComponent()]
|
[NetworkedComponent()]
|
||||||
public abstract class SharedVendingMachineComponent : Component
|
public abstract class SharedVendingMachineComponent : Component
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// PrototypeID for the vending machine's inventory, see <see cref="VendingMachineInventoryPrototype"/>
|
||||||
|
/// </summary>
|
||||||
[DataField("pack", customTypeSerializer: typeof(PrototypeIdSerializer<VendingMachineInventoryPrototype>))]
|
[DataField("pack", customTypeSerializer: typeof(PrototypeIdSerializer<VendingMachineInventoryPrototype>))]
|
||||||
public string PackPrototypeId = string.Empty;
|
public string PackPrototypeId = string.Empty;
|
||||||
|
|
||||||
public TimeSpan AnimationDuration = TimeSpan.Zero;
|
/// <summary>
|
||||||
|
/// Used by the server to determine how long the vending machine stays in the "Deny" state.
|
||||||
|
/// Used by the client to determine how long the deny animation should be played.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("denyDelay")]
|
||||||
|
public float DenyDelay = 2.0f;
|
||||||
|
|
||||||
[ViewVariables] public List<VendingMachineInventoryEntry> Inventory = new();
|
/// <summary>
|
||||||
[ViewVariables] public List<VendingMachineInventoryEntry> EmaggedInventory = new();
|
/// Used by the server to determine how long the vending machine stays in the "Eject" state.
|
||||||
[ViewVariables] public List<VendingMachineInventoryEntry> ContrabandInventory = new();
|
/// The selected item is dispensed afer this delay.
|
||||||
|
/// Used by the client to determine how long the deny animation should be played.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("ejectDelay")]
|
||||||
|
public float EjectDelay = 1.2f;
|
||||||
|
|
||||||
public List<VendingMachineInventoryEntry> AllInventory
|
[ViewVariables]
|
||||||
{
|
public Dictionary<string, VendingMachineInventoryEntry> Inventory = new();
|
||||||
get
|
|
||||||
{
|
|
||||||
var inventory = new List<VendingMachineInventoryEntry>(Inventory);
|
|
||||||
|
|
||||||
if (Emagged) inventory.AddRange(EmaggedInventory);
|
[ViewVariables]
|
||||||
if (Contraband) inventory.AddRange(ContrabandInventory);
|
public Dictionary<string, VendingMachineInventoryEntry> EmaggedInventory = new();
|
||||||
|
|
||||||
return inventory;
|
[ViewVariables]
|
||||||
}
|
public Dictionary<string, VendingMachineInventoryEntry> ContrabandInventory = new();
|
||||||
}
|
|
||||||
|
|
||||||
public bool Emagged;
|
public bool Emagged;
|
||||||
public bool Contraband;
|
public bool Contraband;
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public enum VendingMachineVisuals
|
|
||||||
{
|
|
||||||
VisualState,
|
|
||||||
Inventory,
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public enum VendingMachineVisualState
|
|
||||||
{
|
|
||||||
Normal,
|
|
||||||
Off,
|
|
||||||
Broken,
|
|
||||||
Eject,
|
|
||||||
Deny,
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class VendingMachineEjectMessage : BoundUserInterfaceMessage
|
|
||||||
{
|
|
||||||
public readonly InventoryType Type;
|
|
||||||
public readonly string ID;
|
|
||||||
public VendingMachineEjectMessage(InventoryType type, string id)
|
|
||||||
{
|
|
||||||
Type = type;
|
|
||||||
ID = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public enum VendingMachineUiKey
|
|
||||||
{
|
|
||||||
Key,
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class InventorySyncRequestMessage : BoundUserInterfaceMessage
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class VendingMachineInventoryMessage : BoundUserInterfaceMessage
|
|
||||||
{
|
|
||||||
public readonly List<VendingMachineInventoryEntry> Inventory;
|
|
||||||
public VendingMachineInventoryMessage(List<VendingMachineInventoryEntry> inventory)
|
|
||||||
{
|
|
||||||
Inventory = inventory;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class VendingMachineInventoryEntry
|
public sealed class VendingMachineInventoryEntry
|
||||||
{
|
{
|
||||||
[ViewVariables(VVAccess.ReadWrite)] public InventoryType Type;
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public InventoryType Type;
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public string ID;
|
public string ID;
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
@@ -98,14 +58,6 @@ namespace Content.Shared.VendingMachines
|
|||||||
Amount = amount;
|
Amount = amount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public enum VendingMachineWireStatus : byte
|
|
||||||
{
|
|
||||||
Power,
|
|
||||||
Access,
|
|
||||||
Advertisement,
|
|
||||||
Limiter
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum InventoryType : byte
|
public enum InventoryType : byte
|
||||||
@@ -114,6 +66,21 @@ namespace Content.Shared.VendingMachines
|
|||||||
Emagged,
|
Emagged,
|
||||||
Contraband
|
Contraband
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum VendingMachineVisuals
|
||||||
|
{
|
||||||
|
VisualState
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum VendingMachineVisualState
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Off,
|
||||||
|
Broken,
|
||||||
|
Eject,
|
||||||
|
Deny,
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using System.Linq;
|
||||||
using static Content.Shared.VendingMachines.SharedVendingMachineComponent;
|
using static Content.Shared.VendingMachines.SharedVendingMachineComponent;
|
||||||
|
|
||||||
namespace Content.Shared.VendingMachines;
|
namespace Content.Shared.VendingMachines;
|
||||||
@@ -18,17 +19,43 @@ public abstract class SharedVendingMachineSystem : EntitySystem
|
|||||||
if (!_prototypeManager.TryIndex(component.PackPrototypeId, out VendingMachineInventoryPrototype? packPrototype))
|
if (!_prototypeManager.TryIndex(component.PackPrototypeId, out VendingMachineInventoryPrototype? packPrototype))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MetaData(uid).EntityName = packPrototype.Name;
|
|
||||||
component.AnimationDuration = TimeSpan.FromSeconds(packPrototype.AnimationDuration);
|
|
||||||
|
|
||||||
if (TryComp(component.Owner, out AppearanceComponent? appearance))
|
|
||||||
appearance.SetData(VendingMachineVisuals.Inventory, component.PackPrototypeId);
|
|
||||||
|
|
||||||
AddInventoryFromPrototype(uid, packPrototype.StartingInventory, InventoryType.Regular, component);
|
AddInventoryFromPrototype(uid, packPrototype.StartingInventory, InventoryType.Regular, component);
|
||||||
AddInventoryFromPrototype(uid, packPrototype.EmaggedInventory, InventoryType.Emagged, component);
|
AddInventoryFromPrototype(uid, packPrototype.EmaggedInventory, InventoryType.Emagged, component);
|
||||||
AddInventoryFromPrototype(uid, packPrototype.ContrabandInventory, InventoryType.Contraband, component);
|
AddInventoryFromPrototype(uid, packPrototype.ContrabandInventory, InventoryType.Contraband, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all of the vending machine's inventory. Only includes emagged and contraband inventories if
|
||||||
|
/// <see cref="SharedVendingMachineComponent.Emagged"/> and <see cref="SharedVendingMachineComponent.Contraband"/>
|
||||||
|
/// are <c>true</c> respectively.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid"></param>
|
||||||
|
/// <param name="component"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public List<VendingMachineInventoryEntry> GetAllInventory(EntityUid uid, SharedVendingMachineComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return new();
|
||||||
|
|
||||||
|
var inventory = new List<VendingMachineInventoryEntry>(component.Inventory.Values);
|
||||||
|
|
||||||
|
if (component.Emagged)
|
||||||
|
inventory.AddRange(component.EmaggedInventory.Values);
|
||||||
|
|
||||||
|
if (component.Contraband)
|
||||||
|
inventory.AddRange(component.ContrabandInventory.Values);
|
||||||
|
|
||||||
|
return inventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<VendingMachineInventoryEntry> GetAvailableInventory(EntityUid uid, SharedVendingMachineComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return new();
|
||||||
|
|
||||||
|
return GetAllInventory(uid, component).Where(_ => _.Amount > 0).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
private void AddInventoryFromPrototype(EntityUid uid, Dictionary<string, uint>? entries,
|
private void AddInventoryFromPrototype(EntityUid uid, Dictionary<string, uint>? entries,
|
||||||
InventoryType type,
|
InventoryType type,
|
||||||
SharedVendingMachineComponent? component = null)
|
SharedVendingMachineComponent? component = null)
|
||||||
@@ -38,26 +65,26 @@ public abstract class SharedVendingMachineSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var inventory = new List<VendingMachineInventoryEntry>();
|
var inventory = new Dictionary<string, VendingMachineInventoryEntry>();
|
||||||
|
|
||||||
foreach (var (id, amount) in entries)
|
foreach (var (id, amount) in entries)
|
||||||
{
|
{
|
||||||
if (_prototypeManager.HasIndex<EntityPrototype>(id))
|
if (_prototypeManager.HasIndex<EntityPrototype>(id))
|
||||||
{
|
{
|
||||||
inventory.Add(new VendingMachineInventoryEntry(type, id, amount));
|
inventory.Add(id, new VendingMachineInventoryEntry(type, id, amount));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case InventoryType.Regular:
|
case InventoryType.Regular:
|
||||||
component.Inventory.AddRange(inventory);
|
component.Inventory = inventory;
|
||||||
break;
|
break;
|
||||||
case InventoryType.Emagged:
|
case InventoryType.Emagged:
|
||||||
component.EmaggedInventory.AddRange(inventory);
|
component.EmaggedInventory = inventory;
|
||||||
break;
|
break;
|
||||||
case InventoryType.Contraband:
|
case InventoryType.Contraband:
|
||||||
component.ContrabandInventory.AddRange(inventory);
|
component.ContrabandInventory = inventory;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.VendingMachines
|
||||||
|
{
|
||||||
|
[NetSerializable, Serializable]
|
||||||
|
public sealed class VendingMachineInterfaceState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
public List<VendingMachineInventoryEntry> Inventory;
|
||||||
|
|
||||||
|
public VendingMachineInterfaceState(List<VendingMachineInventoryEntry> inventory)
|
||||||
|
{
|
||||||
|
Inventory = inventory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class VendingMachineEjectMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly InventoryType Type;
|
||||||
|
public readonly string ID;
|
||||||
|
public VendingMachineEjectMessage(InventoryType type, string id)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
ID = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum VendingMachineUiKey
|
||||||
|
{
|
||||||
|
Key,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,19 +7,9 @@ namespace Content.Shared.VendingMachines
|
|||||||
public sealed class VendingMachineInventoryPrototype : IPrototype
|
public sealed class VendingMachineInventoryPrototype : IPrototype
|
||||||
{
|
{
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
[IdDataFieldAttribute]
|
[IdDataField]
|
||||||
public string ID { get; } = default!;
|
public string ID { get; } = default!;
|
||||||
|
|
||||||
[DataField("name")]
|
|
||||||
public string Name { get; } = string.Empty;
|
|
||||||
|
|
||||||
[DataField("animationDuration")]
|
|
||||||
public double AnimationDuration { get; }
|
|
||||||
|
|
||||||
// TODO make this a proper sprite specifier for yaml linting.
|
|
||||||
[DataField("spriteName")]
|
|
||||||
public string SpriteName { get; } = string.Empty;
|
|
||||||
|
|
||||||
[DataField("startingInventory")]
|
[DataField("startingInventory")]
|
||||||
public Dictionary<string, uint> StartingInventory { get; } = new();
|
public Dictionary<string, uint> StartingInventory { get; } = new();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: AmmoVendInventory
|
id: AmmoVendInventory
|
||||||
name: Ammovend
|
|
||||||
spriteName: ammo
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
MagazineBoxCaselessRifle: 3
|
MagazineBoxCaselessRifle: 3
|
||||||
MagazineBoxCaselessRifleHighVelocity: 3
|
MagazineBoxCaselessRifleHighVelocity: 3
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: AtmosDrobeInventory
|
id: AtmosDrobeInventory
|
||||||
name: AtmosDrobe
|
|
||||||
spriteName: atmosdrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingBackpackDuffelEngineering: 2
|
ClothingBackpackDuffelEngineering: 2
|
||||||
ClothingBackpackSatchelEngineering: 2
|
ClothingBackpackSatchelEngineering: 2
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: BarDrobeInventory
|
id: BarDrobeInventory
|
||||||
name: BarDrobe
|
|
||||||
spriteName: bardrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingHeadHatTophat: 2
|
ClothingHeadHatTophat: 2
|
||||||
ClothingEyesGlassesBeer: 2
|
ClothingEyesGlassesBeer: 2
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: BoozeOMatInventory
|
id: BoozeOMatInventory
|
||||||
name: Booze-O-Mat
|
|
||||||
spriteName: boozeomat
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
|
|
||||||
DrinkGlass: 30 #Kept glasses at top for ease to differentiate from booze.
|
DrinkGlass: 30 #Kept glasses at top for ease to differentiate from booze.
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: CargoDrobeInventory
|
id: CargoDrobeInventory
|
||||||
name: CargoDrobe
|
|
||||||
spriteName: cargodrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
AppraisalTool: 3
|
AppraisalTool: 3
|
||||||
ClothingUniformJumpsuitCargo: 3
|
ClothingUniformJumpsuitCargo: 3
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: PTechInventory
|
id: PTechInventory
|
||||||
name: PTech
|
|
||||||
spriteName: cart
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
PassengerPDA: 5
|
PassengerPDA: 5
|
||||||
ClearPDA: 5
|
ClearPDA: 5
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: ChangInventory
|
id: ChangInventory
|
||||||
name: Mr. Chang
|
|
||||||
animationDuration: 2.1
|
|
||||||
spriteName: cigs
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
FoodCondimentPacketSoy: 5
|
FoodCondimentPacketSoy: 5
|
||||||
FoodSnackCookieFortune: 5
|
FoodSnackCookieFortune: 5
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: PietyVendInventory
|
id: PietyVendInventory
|
||||||
name: PietyVend
|
|
||||||
spriteName: chapel
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingUniformJumpsuitChaplain: 2
|
ClothingUniformJumpsuitChaplain: 2
|
||||||
ClothingUniformJumpskirtChaplain: 2
|
ClothingUniformJumpskirtChaplain: 2
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: ChefDrobeInventory
|
id: ChefDrobeInventory
|
||||||
name: ChefDrobe
|
|
||||||
spriteName: chefdrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingHeadsetService: 2
|
ClothingHeadsetService: 2
|
||||||
ClothingOuterApronChef: 3
|
ClothingOuterApronChef: 3
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: ChemDrobeInventory
|
id: ChemDrobeInventory
|
||||||
name: ChemDrobe
|
|
||||||
spriteName: chemdrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingUniformJumpsuitChemistry: 2
|
ClothingUniformJumpsuitChemistry: 2
|
||||||
ClothingUniformJumpskirtChemistry: 2
|
ClothingUniformJumpskirtChemistry: 2
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: CigaretteMachineInventory
|
id: CigaretteMachineInventory
|
||||||
name: cigarette machine
|
|
||||||
animationDuration: 2.1
|
|
||||||
spriteName: cigs
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
CigPackGreen: 2
|
CigPackGreen: 2
|
||||||
CigPackRed: 2
|
CigPackRed: 2
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: ClothesMateInventory
|
id: ClothesMateInventory
|
||||||
name: ClothesMate
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingBackpack: 5
|
ClothingBackpack: 5
|
||||||
ClothingBackpackDuffel: 5
|
ClothingBackpackDuffel: 5
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: HotDrinksMachineInventory
|
id: HotDrinksMachineInventory
|
||||||
name: Hot drinks machine
|
|
||||||
animationDuration: 3.4
|
|
||||||
spriteName: coffee
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
DrinkHotCoffee: 10
|
DrinkHotCoffee: 10
|
||||||
DrinkTeacup: 10
|
DrinkTeacup: 10
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: RobustSoftdrinksInventory
|
id: RobustSoftdrinksInventory
|
||||||
name: Robust Softdrinks
|
|
||||||
animationDuration: 1.1
|
|
||||||
spriteName: cola
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
DrinkColaCan: 2
|
DrinkColaCan: 2
|
||||||
DrinkEnergyDrinkCan: 2
|
DrinkEnergyDrinkCan: 2
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: DetDrobeInventory
|
id: DetDrobeInventory
|
||||||
name: DetDrobe
|
|
||||||
spriteName: detdrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingUniformJumpsuitDetective: 2
|
ClothingUniformJumpsuitDetective: 2
|
||||||
ClothingUniformJumpskirtDetective: 2
|
ClothingUniformJumpskirtDetective: 2
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: DinnerwareInventory
|
id: DinnerwareInventory
|
||||||
name: Dinnerware
|
|
||||||
spriteName: dinnerware
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ButchCleaver: 1
|
ButchCleaver: 1
|
||||||
KitchenKnife: 5
|
KitchenKnife: 5
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: DiscountDansInventory
|
id: DiscountDansInventory
|
||||||
name: Discount Dan's
|
|
||||||
spriteName: discount
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
FoodSnackCheesie: 3
|
FoodSnackCheesie: 3
|
||||||
FoodSnackChips: 3
|
FoodSnackChips: 3
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: DonutInventory
|
id: DonutInventory
|
||||||
name: Donut
|
|
||||||
spriteName: donut
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
FoodDonutChocolate: 5
|
FoodDonutChocolate: 5
|
||||||
FoodDonutApple: 3
|
FoodDonutApple: 3
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: EmptyVendingMachineInventory
|
id: EmptyVendingMachineInventory
|
||||||
name: Empty vending machine
|
|
||||||
spriteName: empty
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: EngiDrobeInventory
|
id: EngiDrobeInventory
|
||||||
name: EngiDrobe
|
|
||||||
spriteName: engidrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingBackpackDuffelEngineering: 3
|
ClothingBackpackDuffelEngineering: 3
|
||||||
ClothingBackpackSatchelEngineering: 3
|
ClothingBackpackSatchelEngineering: 3
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: EngiVendInventory
|
id: EngiVendInventory
|
||||||
name: Engi-Vend
|
|
||||||
animationDuration: 2.1
|
|
||||||
spriteName: engivend
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingEyesGlassesMeson: 4
|
ClothingEyesGlassesMeson: 4
|
||||||
ClothingHeadHatWelding: 4
|
ClothingHeadHatWelding: 4
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: GoodCleanFunInventory
|
id: GoodCleanFunInventory
|
||||||
name: Good Clean Fun
|
|
||||||
animationDuration: 1.3
|
|
||||||
spriteName: games
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
DiceBag: 4
|
DiceBag: 4
|
||||||
Paper: 8
|
Paper: 8
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: GeneDrobeInventory
|
id: GeneDrobeInventory
|
||||||
name: GeneDrobe
|
|
||||||
spriteName: genedrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingShoesColorWhite: 2
|
ClothingShoesColorWhite: 2
|
||||||
ClothingOuterCoatLab: 2
|
ClothingOuterCoatLab: 2
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: HyDrobeInventory
|
id: HyDrobeInventory
|
||||||
name: HyDrobe
|
|
||||||
spriteName: hydrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingBackpackHydroponics: 2
|
ClothingBackpackHydroponics: 2
|
||||||
ClothingBackpackSatchelHydroponics: 2
|
ClothingBackpackSatchelHydroponics: 2
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: JaniDrobeInventory
|
id: JaniDrobeInventory
|
||||||
name: JaniDrobe
|
|
||||||
spriteName: janidrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingUniformJumpsuitJanitor: 2
|
ClothingUniformJumpsuitJanitor: 2
|
||||||
ClothingUniformJumpskirtJanitor: 2
|
ClothingUniformJumpskirtJanitor: 2
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: LawDrobeInventory
|
id: LawDrobeInventory
|
||||||
name: LawDrobe
|
|
||||||
spriteName: lawdrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingUniformJumpsuitLawyerBlue: 1
|
ClothingUniformJumpsuitLawyerBlue: 1
|
||||||
ClothingUniformJumpsuitLawyerPurple: 1
|
ClothingUniformJumpsuitLawyerPurple: 1
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: MagiVendInventory
|
id: MagiVendInventory
|
||||||
name: MagiVend
|
|
||||||
animationDuration: 1.5
|
|
||||||
spriteName: magivend
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingHeadHatWizard: 3
|
ClothingHeadHatWizard: 3
|
||||||
ClothingOuterWizard: 3
|
ClothingOuterWizard: 3
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: NanoMedPlusInventory
|
id: NanoMedPlusInventory
|
||||||
name: NanoMed Plus
|
|
||||||
animationDuration: 1.8
|
|
||||||
spriteName: medical
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
HandheldHealthAnalyzer: 3
|
HandheldHealthAnalyzer: 3
|
||||||
Brutepack: 5
|
Brutepack: 5
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: MediDrobeInventory
|
id: MediDrobeInventory
|
||||||
name: MediDrobe
|
|
||||||
spriteName: medidrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingBackpackDuffelMedical: 4
|
ClothingBackpackDuffelMedical: 4
|
||||||
ClothingBackpackMedical: 4
|
ClothingBackpackMedical: 4
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: NutriMaxInventory
|
id: NutriMaxInventory
|
||||||
name: NutriMax
|
|
||||||
spriteName: nutri
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
HydroponicsToolSpade: 2
|
HydroponicsToolSpade: 2
|
||||||
HydroponicsToolMiniHoe: 2
|
HydroponicsToolMiniHoe: 2
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: RoboDrobeInventory
|
id: RoboDrobeInventory
|
||||||
name: RoboDrobe
|
|
||||||
spriteName: robodrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingOuterCoatLab: 2
|
ClothingOuterCoatLab: 2
|
||||||
ClothingShoesColorBlack: 2
|
ClothingShoesColorBlack: 2
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: RobotechDeluxeInventory
|
id: RobotechDeluxeInventory
|
||||||
name: Robotech Deluxe
|
|
||||||
spriteName: robotics
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
#TO DO: add missing prototypes
|
#TO DO: add missing prototypes
|
||||||
ClothingOuterCoatLab: 4
|
ClothingOuterCoatLab: 4
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: SalvageEquipmentInventory
|
id: SalvageEquipmentInventory
|
||||||
name: Salvage Equipment
|
|
||||||
spriteName: mining
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
Crowbar: 2
|
Crowbar: 2
|
||||||
Pickaxe: 4
|
Pickaxe: 4
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: SciDrobeInventory
|
id: SciDrobeInventory
|
||||||
name: SciDrobe
|
|
||||||
spriteName: scidrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingBackpackScience: 3
|
ClothingBackpackScience: 3
|
||||||
ClothingBackpackSatchelScience: 3
|
ClothingBackpackSatchelScience: 3
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: SecTechInventory
|
id: SecTechInventory
|
||||||
name: SecTech
|
|
||||||
animationDuration: 2.8
|
|
||||||
spriteName: sec
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
Handcuffs: 8
|
Handcuffs: 8
|
||||||
GrenadeFlashBang: 4
|
GrenadeFlashBang: 4
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: SecDrobeInventory
|
id: SecDrobeInventory
|
||||||
name: SecDrobe
|
|
||||||
spriteName: secdrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingBackpackSecurity: 3
|
ClothingBackpackSecurity: 3
|
||||||
ClothingBackpackSatchelSecurity: 3
|
ClothingBackpackSatchelSecurity: 3
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: MegaSeedServitorInventory
|
id: MegaSeedServitorInventory
|
||||||
name: MegaSeed Servitor
|
|
||||||
animationDuration: 1.3
|
|
||||||
spriteName: seeds
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
AloeSeeds: 3
|
AloeSeeds: 3
|
||||||
AmbrosiaVulgarisSeeds: 3
|
AmbrosiaVulgarisSeeds: 3
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: SmartFridgeInventory
|
id: SmartFridgeInventory
|
||||||
name: SmartFridge
|
|
||||||
spriteName: smartfridge
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: GetmoreChocolateCorpInventory
|
id: GetmoreChocolateCorpInventory
|
||||||
name: Getmore Chocolate Corp
|
|
||||||
animationDuration: 0.5
|
|
||||||
spriteName: snack
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
FoodSnackRaisins: 3
|
FoodSnackRaisins: 3
|
||||||
FoodSnackChocolate: 3
|
FoodSnackChocolate: 3
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: BodaInventory
|
id: BodaInventory
|
||||||
name: BODA
|
|
||||||
spriteName: sovietsoda
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
DrinkColaCan: 10 #typically hacked product. Default product is "soda"
|
DrinkColaCan: 10 #typically hacked product. Default product is "soda"
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: TankDispenserEVAInventory
|
id: TankDispenserEVAInventory
|
||||||
name: tank dispenser
|
|
||||||
spriteName: tankdispenser
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
OxygenTankFilled: 5
|
OxygenTankFilled: 5
|
||||||
NitrogenTankFilled: 5
|
NitrogenTankFilled: 5
|
||||||
|
|
||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: TankDispenserEngineeringInventory
|
id: TankDispenserEngineeringInventory
|
||||||
name: tank dispenser
|
|
||||||
spriteName: tankdispenser
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
PlasmaTankFilled: 5
|
PlasmaTankFilled: 5
|
||||||
OxygenTankFilled: 5
|
OxygenTankFilled: 5
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: AutoDrobeInventory
|
id: AutoDrobeInventory
|
||||||
name: AutoDrobe
|
|
||||||
spriteName: theater
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingOuterWinterClown: 1
|
ClothingOuterWinterClown: 1
|
||||||
ClothingOuterWinterMime: 1
|
ClothingOuterWinterMime: 1
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: VendomatInventory
|
id: VendomatInventory
|
||||||
name: Vendomat
|
|
||||||
spriteName: vendomat
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: ViroDrobeInventory
|
id: ViroDrobeInventory
|
||||||
name: ViroDrobe
|
|
||||||
spriteName: virodrobe
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
ClothingShoesColorWhite: 2
|
ClothingShoesColorWhite: 2
|
||||||
ClothingOuterCoatLab: 2
|
ClothingOuterCoatLab: 2
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: NanoMedInventory
|
id: NanoMedInventory
|
||||||
name: NanoMed
|
|
||||||
spriteName: wallmed
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
Brutepack: 5
|
Brutepack: 5
|
||||||
Ointment: 5
|
Ointment: 5
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
- type: vendingMachineInventory
|
- type: vendingMachineInventory
|
||||||
id: YouToolInventory
|
id: YouToolInventory
|
||||||
name: YouTool
|
|
||||||
animationDuration: 1.1
|
|
||||||
spriteName: youtool
|
|
||||||
startingInventory:
|
startingInventory:
|
||||||
CableApcStack: 5
|
CableApcStack: 5
|
||||||
Crowbar: 5
|
Crowbar: 5
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -46,6 +46,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "off"
|
"name": "off"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "panel"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user