1
0

VendingMachineComponent.cs 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. using Content.Shared.Actions;
  2. using Robust.Shared.Audio;
  3. using Robust.Shared.GameStates;
  4. using Robust.Shared.Serialization;
  5. using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
  6. using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
  7. namespace Content.Shared.VendingMachines
  8. {
  9. [RegisterComponent, NetworkedComponent, AutoGenerateComponentPause]
  10. public sealed partial class VendingMachineComponent : Component
  11. {
  12. /// <summary>
  13. /// PrototypeID for the vending machine's inventory, see <see cref="VendingMachineInventoryPrototype"/>
  14. /// </summary>
  15. // Okay so not using ProtoId here is load-bearing because the ProtoId serializer will log errors if the prototype doesn't exist.
  16. [DataField("pack", customTypeSerializer: typeof(PrototypeIdSerializer<VendingMachineInventoryPrototype>), required: true)]
  17. public string PackPrototypeId = string.Empty;
  18. /// <summary>
  19. /// Used by the server to determine how long the vending machine stays in the "Deny" state.
  20. /// Used by the client to determine how long the deny animation should be played.
  21. /// </summary>
  22. [DataField]
  23. public TimeSpan DenyDelay = TimeSpan.FromSeconds(2);
  24. /// <summary>
  25. /// Used by the server to determine how long the vending machine stays in the "Eject" state.
  26. /// The selected item is dispensed afer this delay.
  27. /// Used by the client to determine how long the deny animation should be played.
  28. /// </summary>
  29. [DataField]
  30. public TimeSpan EjectDelay = TimeSpan.FromSeconds(1.2);
  31. [DataField]
  32. public Dictionary<string, VendingMachineInventoryEntry> Inventory = new();
  33. [DataField]
  34. public Dictionary<string, VendingMachineInventoryEntry> EmaggedInventory = new();
  35. [DataField]
  36. public Dictionary<string, VendingMachineInventoryEntry> ContrabandInventory = new();
  37. /// <summary>
  38. /// If true then unlocks the <see cref="ContrabandInventory"/>
  39. /// </summary>
  40. [DataField]
  41. public bool Contraband;
  42. [ViewVariables]
  43. public bool Ejecting => EjectEnd != null;
  44. [ViewVariables]
  45. public bool Denying => DenyEnd != null;
  46. [ViewVariables]
  47. public bool DispenseOnHitCoolingDown => DispenseOnHitEnd != null;
  48. [DataField, AutoPausedField]
  49. public TimeSpan? EjectEnd;
  50. [DataField, AutoPausedField]
  51. public TimeSpan? DenyEnd;
  52. [DataField]
  53. public TimeSpan? DispenseOnHitEnd;
  54. public string? NextItemToEject;
  55. public bool Broken;
  56. /// <summary>
  57. /// When true, will forcefully throw any object it dispenses
  58. /// </summary>
  59. [DataField]
  60. public bool CanShoot = false;
  61. public bool ThrowNextItem = false;
  62. /// <summary>
  63. /// The chance that a vending machine will randomly dispense an item on hit.
  64. /// Chance is 0 if null.
  65. /// </summary>
  66. [DataField]
  67. public float? DispenseOnHitChance;
  68. /// <summary>
  69. /// The minimum amount of damage that must be done per hit to have a chance
  70. /// of dispensing an item.
  71. /// </summary>
  72. [DataField]
  73. public float? DispenseOnHitThreshold;
  74. /// <summary>
  75. /// Amount of time in seconds that need to pass before damage can cause a vending machine to eject again.
  76. /// This value is separate to <see cref="VendingMachineComponent.EjectDelay"/> because that value might be
  77. /// 0 for a vending machine for legitimate reasons (no desired delay/no eject animation)
  78. /// and can be circumvented with forced ejections.
  79. /// </summary>
  80. [DataField]
  81. public TimeSpan? DispenseOnHitCooldown = TimeSpan.FromSeconds(1.0);
  82. /// <summary>
  83. /// Sound that plays when ejecting an item
  84. /// </summary>
  85. [DataField]
  86. // Grabbed from: https://github.com/tgstation/tgstation/blob/d34047a5ae911735e35cd44a210953c9563caa22/sound/machines/machine_vend.ogg
  87. public SoundSpecifier SoundVend = new SoundPathSpecifier("/Audio/Machines/machine_vend.ogg")
  88. {
  89. Params = new AudioParams
  90. {
  91. Volume = -4f,
  92. Variation = 0.15f
  93. }
  94. };
  95. /// <summary>
  96. /// Sound that plays when an item can't be ejected
  97. /// </summary>
  98. [DataField]
  99. // Yoinked from: https://github.com/discordia-space/CEV-Eris/blob/35bbad6764b14e15c03a816e3e89aa1751660ba9/sound/machines/Custom_deny.ogg
  100. public SoundSpecifier SoundDeny = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg");
  101. public float NonLimitedEjectForce = 7.5f;
  102. public float NonLimitedEjectRange = 5f;
  103. /// <summary>
  104. /// The quality of the stock in the vending machine on spawn.
  105. /// Represents the percentage chance (0.0f = 0%, 1.0f = 100%) each set of items in the machine is fully-stocked.
  106. /// If not fully stocked, the stock will have a random value between 0 (inclusive) and max stock (exclusive).
  107. /// </summary>
  108. [DataField]
  109. public float InitialStockQuality = 1.0f;
  110. /// <summary>
  111. /// While disabled by EMP it randomly ejects items
  112. /// </summary>
  113. [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
  114. public TimeSpan NextEmpEject = TimeSpan.Zero;
  115. #region Client Visuals
  116. /// <summary>
  117. /// RSI state for when the vending machine is unpowered.
  118. /// Will be displayed on the layer <see cref="VendingMachineVisualLayers.Base"/>
  119. /// </summary>
  120. [DataField]
  121. public string? OffState;
  122. /// <summary>
  123. /// RSI state for the screen of the vending machine
  124. /// Will be displayed on the layer <see cref="VendingMachineVisualLayers.Screen"/>
  125. /// </summary>
  126. [DataField]
  127. public string? ScreenState;
  128. /// <summary>
  129. /// RSI state for the vending machine's normal state. Usually a looping animation.
  130. /// Will be displayed on the layer <see cref="VendingMachineVisualLayers.BaseUnshaded"/>
  131. /// </summary>
  132. [DataField]
  133. public string? NormalState;
  134. /// <summary>
  135. /// RSI state for the vending machine's eject animation.
  136. /// Will be displayed on the layer <see cref="VendingMachineVisualLayers.BaseUnshaded"/>
  137. /// </summary>
  138. [DataField]
  139. public string? EjectState;
  140. /// <summary>
  141. /// RSI state for the vending machine's deny animation. Will either be played once as sprite flick
  142. /// or looped depending on how <see cref="LoopDenyAnimation"/> is set.
  143. /// Will be displayed on the layer <see cref="VendingMachineVisualLayers.BaseUnshaded"/>
  144. /// </summary>
  145. [DataField]
  146. public string? DenyState;
  147. /// <summary>
  148. /// RSI state for when the vending machine is unpowered.
  149. /// Will be displayed on the layer <see cref="VendingMachineVisualLayers.Base"/>
  150. /// </summary>
  151. [DataField]
  152. public string? BrokenState;
  153. /// <summary>
  154. /// If set to <c>true</c> (default) will loop the animation of the <see cref="DenyState"/> for the duration
  155. /// of <see cref="VendingMachineComponent.DenyDelay"/>. If set to <c>false</c> will play a sprite
  156. /// flick animation for the state and then linger on the final frame until the end of the delay.
  157. /// </summary>
  158. [DataField("loopDeny")]
  159. public bool LoopDenyAnimation = true;
  160. #endregion
  161. }
  162. [Serializable, NetSerializable]
  163. public sealed class VendingMachineInventoryEntry
  164. {
  165. [ViewVariables(VVAccess.ReadWrite)]
  166. public InventoryType Type;
  167. [ViewVariables(VVAccess.ReadWrite)]
  168. public string ID;
  169. [ViewVariables(VVAccess.ReadWrite)]
  170. public uint Amount;
  171. public VendingMachineInventoryEntry(InventoryType type, string id, uint amount)
  172. {
  173. Type = type;
  174. ID = id;
  175. Amount = amount;
  176. }
  177. public VendingMachineInventoryEntry(VendingMachineInventoryEntry entry)
  178. {
  179. Type = entry.Type;
  180. ID = entry.ID;
  181. Amount = entry.Amount;
  182. }
  183. }
  184. [Serializable, NetSerializable]
  185. public enum InventoryType : byte
  186. {
  187. Regular,
  188. Emagged,
  189. Contraband
  190. }
  191. [Serializable, NetSerializable]
  192. public enum VendingMachineVisuals : byte
  193. {
  194. VisualState
  195. }
  196. [Serializable, NetSerializable]
  197. public enum VendingMachineVisualState : byte
  198. {
  199. Normal,
  200. Off,
  201. Broken,
  202. Eject,
  203. Deny,
  204. }
  205. public enum VendingMachineVisualLayers : byte
  206. {
  207. /// <summary>
  208. /// Off / Broken. The other layers will overlay this if the machine is on.
  209. /// </summary>
  210. Base,
  211. /// <summary>
  212. /// Normal / Deny / Eject
  213. /// </summary>
  214. BaseUnshaded,
  215. /// <summary>
  216. /// Screens that are persistent (where the machine is not off or broken)
  217. /// </summary>
  218. Screen
  219. }
  220. [Serializable, NetSerializable]
  221. public enum ContrabandWireKey : byte
  222. {
  223. StatusKey,
  224. TimeoutKey
  225. }
  226. [Serializable, NetSerializable]
  227. public enum EjectWireKey : byte
  228. {
  229. StatusKey,
  230. }
  231. public sealed partial class VendingMachineSelfDispenseEvent : InstantActionEvent
  232. {
  233. };
  234. [Serializable, NetSerializable]
  235. public sealed class VendingMachineComponentState : ComponentState
  236. {
  237. public Dictionary<string, VendingMachineInventoryEntry> Inventory = new();
  238. public Dictionary<string, VendingMachineInventoryEntry> EmaggedInventory = new();
  239. public Dictionary<string, VendingMachineInventoryEntry> ContrabandInventory = new();
  240. public bool Contraband;
  241. public TimeSpan? EjectEnd;
  242. public TimeSpan? DenyEnd;
  243. public TimeSpan? DispenseOnHitEnd;
  244. }
  245. }