using Content.Shared.Actions; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.VendingMachines { [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] public sealed partial class VendingMachineComponent : Component { /// /// PrototypeID for the vending machine's inventory, see /// [DataField("pack", customTypeSerializer: typeof(PrototypeIdSerializer), required: true)] public string PackPrototypeId = string.Empty; /// /// 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. /// [DataField] public float DenyDelay = 2.0f; /// /// Used by the server to determine how long the vending machine stays in the "Eject" state. /// The selected item is dispensed afer this delay. /// Used by the client to determine how long the deny animation should be played. /// [DataField] public float EjectDelay = 1.2f; [DataField, AutoNetworkedField] public Dictionary Inventory = new(); [DataField, AutoNetworkedField] public Dictionary EmaggedInventory = new(); [DataField, AutoNetworkedField] public Dictionary ContrabandInventory = new(); [DataField, AutoNetworkedField] public bool Contraband; public bool Ejecting; public bool Denying; public bool DispenseOnHitCoolingDown; public string? NextItemToEject; public bool Broken; /// /// When true, will forcefully throw any object it dispenses /// [DataField("speedLimiter")] public bool CanShoot = false; public bool ThrowNextItem = false; /// /// The chance that a vending machine will randomly dispense an item on hit. /// Chance is 0 if null. /// [DataField("dispenseOnHitChance")] public float? DispenseOnHitChance; /// /// The minimum amount of damage that must be done per hit to have a chance /// of dispensing an item. /// [DataField("dispenseOnHitThreshold")] public float? DispenseOnHitThreshold; /// /// Amount of time in seconds that need to pass before damage can cause a vending machine to eject again. /// This value is separate to 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. /// [DataField("dispenseOnHitCooldown")] public float? DispenseOnHitCooldown = 1.0f; /// /// Sound that plays when ejecting an item /// [DataField("soundVend")] // Grabbed from: https://github.com/tgstation/tgstation/blob/d34047a5ae911735e35cd44a210953c9563caa22/sound/machines/machine_vend.ogg public SoundSpecifier SoundVend = new SoundPathSpecifier("/Audio/Machines/machine_vend.ogg") { Params = new AudioParams { Volume = -4f, Variation = 0.15f } }; /// /// Sound that plays when an item can't be ejected /// [DataField("soundDeny")] // 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 float NonLimitedEjectForce = 7.5f; public float NonLimitedEjectRange = 5f; public float EjectAccumulator = 0f; public float DenyAccumulator = 0f; public float DispenseOnHitAccumulator = 0f; /// /// The quality of the stock in the vending machine on spawn. /// Represents the percentage chance (0.0f = 0%, 1.0f = 100%) each set of items in the machine is fully-stocked. /// If not fully stocked, the stock will have a random value between 0 (inclusive) and max stock (exclusive). /// [DataField] public float InitialStockQuality = 1.0f; /// /// While disabled by EMP it randomly ejects items /// [DataField("nextEmpEject", customTypeSerializer: typeof(TimeOffsetSerializer))] public TimeSpan NextEmpEject = TimeSpan.Zero; #region Client Visuals /// /// RSI state for when the vending machine is unpowered. /// Will be displayed on the layer /// [DataField("offState")] public string? OffState; /// /// RSI state for the screen of the vending machine /// Will be displayed on the layer /// [DataField("screenState")] public string? ScreenState; /// /// RSI state for the vending machine's normal state. Usually a looping animation. /// Will be displayed on the layer /// [DataField("normalState")] public string? NormalState; /// /// RSI state for the vending machine's eject animation. /// Will be displayed on the layer /// [DataField("ejectState")] public string? EjectState; /// /// RSI state for the vending machine's deny animation. Will either be played once as sprite flick /// or looped depending on how is set. /// Will be displayed on the layer /// [DataField("denyState")] public string? DenyState; /// /// RSI state for when the vending machine is unpowered. /// Will be displayed on the layer /// [DataField("brokenState")] public string? BrokenState; /// /// If set to true (default) will loop the animation of the for the duration /// of . If set to false will play a sprite /// flick animation for the state and then linger on the final frame until the end of the delay. /// [DataField("loopDeny")] public bool LoopDenyAnimation = true; #endregion } [Serializable, NetSerializable] public sealed class VendingMachineInventoryEntry { [ViewVariables(VVAccess.ReadWrite)] public InventoryType Type; [ViewVariables(VVAccess.ReadWrite)] public string ID; [ViewVariables(VVAccess.ReadWrite)] public uint Amount; public VendingMachineInventoryEntry(InventoryType type, string id, uint amount) { Type = type; ID = id; Amount = amount; } } [Serializable, NetSerializable] public enum InventoryType : byte { Regular, Emagged, Contraband } [Serializable, NetSerializable] public enum VendingMachineVisuals { VisualState } [Serializable, NetSerializable] public enum VendingMachineVisualState { Normal, Off, Broken, Eject, Deny, } public enum VendingMachineVisualLayers : byte { /// /// Off / Broken. The other layers will overlay this if the machine is on. /// Base, /// /// Normal / Deny / Eject /// BaseUnshaded, /// /// Screens that are persistent (where the machine is not off or broken) /// Screen } [Serializable, NetSerializable] public enum ContrabandWireKey : byte { StatusKey, TimeoutKey } [Serializable, NetSerializable] public enum EjectWireKey : byte { StatusKey, } public sealed partial class VendingMachineSelfDispenseEvent : InstantActionEvent { }; }