Clean up vending machines and port their visualizer (#10465)
This commit is contained in:
@@ -1,62 +1,87 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.VendingMachines;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.VendingMachines.SharedVendingMachineComponent;
|
||||
|
||||
namespace Content.Client.VendingMachines.UI
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class VendingMachineMenu : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private VendingMachineBoundUserInterface Owner { get; }
|
||||
public event Action<ItemList.ItemListSelectedEventArgs>? OnItemSelected;
|
||||
|
||||
private List<VendingMachineInventoryEntry> _cachedInventory = new();
|
||||
|
||||
public VendingMachineMenu(VendingMachineBoundUserInterface owner)
|
||||
public VendingMachineMenu()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
MinSize = SetSize = (250, 150);
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
Owner = owner;
|
||||
VendingContents.OnItemSelected += ItemSelected;
|
||||
VendingContents.OnItemSelected += args =>
|
||||
{
|
||||
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)
|
||||
{
|
||||
VendingContents.Clear();
|
||||
_cachedInventory = inventory;
|
||||
var longestEntry = "";
|
||||
foreach (VendingMachineInventoryEntry entry in inventory)
|
||||
if (inventory.Count == 0)
|
||||
{
|
||||
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)
|
||||
longestEntry = itemName;
|
||||
|
||||
Texture? icon = null;
|
||||
if(_prototypeManager.TryIndex(entry.ID, out EntityPrototype? prototype))
|
||||
icon = SpriteComponent.GetPrototypeIcon(prototype, _resourceCache).Default;
|
||||
|
||||
VendingContents.AddItem($"{itemName} [{entry.Amount}]", icon);
|
||||
vendingItem.Text = $"{itemName} [{entry.Amount}]";
|
||||
vendingItem.Icon = icon;
|
||||
}
|
||||
|
||||
SetSize = (Math.Clamp((longestEntry.Length + 2) * 12, 250, 300),
|
||||
Math.Clamp(VendingContents.Count * 50, 150, 350));
|
||||
SetSizeAfterUpdate(longestEntry.Length);
|
||||
}
|
||||
|
||||
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.Shared.VendingMachines;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.VendingMachines.SharedVendingMachineComponent;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Client.VendingMachines
|
||||
{
|
||||
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)
|
||||
{
|
||||
SendMessage(new InventorySyncRequestMessage());
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
@@ -24,33 +22,43 @@ namespace Content.Client.VendingMachines
|
||||
base.Open();
|
||||
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
if (!entMan.TryGetComponent(Owner.Owner, out SharedVendingMachineComponent? vendingMachine))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var vendingMachineSys = EntitySystem.Get<VendingMachineSystem>();
|
||||
|
||||
VendingMachine = vendingMachine;
|
||||
_cachedInventory = vendingMachineSys.GetAllInventory(Owner.Owner);
|
||||
|
||||
_menu = new VendingMachineMenu(this) {Title = entMan.GetComponent<MetaDataComponent>(Owner.Owner).EntityName};
|
||||
_menu.Populate(VendingMachine.AllInventory);
|
||||
_menu = new VendingMachineMenu {Title = entMan.GetComponent<MetaDataComponent>(Owner.Owner).EntityName};
|
||||
|
||||
_menu.OnClose += Close;
|
||||
_menu.OnItemSelected += OnItemSelected;
|
||||
|
||||
_menu.Populate(_cachedInventory);
|
||||
|
||||
_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)
|
||||
{
|
||||
case VendingMachineInventoryMessage msg:
|
||||
_menu?.Populate(msg.Inventory);
|
||||
break;
|
||||
}
|
||||
if (_cachedInventory == null || _cachedInventory.Count == 0)
|
||||
return;
|
||||
|
||||
var selectedItem = _cachedInventory.ElementAtOrDefault(args.ItemIndex);
|
||||
|
||||
if (selectedItem == null)
|
||||
return;
|
||||
|
||||
SendMessage(new VendingMachineEjectMessage(selectedItem.Type, selectedItem.ID));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -59,7 +67,12 @@ namespace Content.Client.VendingMachines
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_menu?.Dispose();
|
||||
if (_menu == null)
|
||||
return;
|
||||
|
||||
_menu.OnItemSelected -= OnItemSelected;
|
||||
_menu.OnClose -= Close;
|
||||
_menu.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,57 @@ namespace Content.Client.VendingMachines;
|
||||
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedVendingMachineComponent))]
|
||||
[Access(typeof(VendingMachineSystem))]
|
||||
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 Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.VendingMachines;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user