1
0

Verb.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. using Content.Shared.Database;
  2. using Content.Shared.Interaction.Events;
  3. using Content.Shared.Inventory;
  4. using Robust.Shared.Serialization;
  5. using Robust.Shared.Utility;
  6. namespace Content.Shared.Verbs
  7. {
  8. /// <summary>
  9. /// Verb objects describe actions that a user can take. The actions can be specified via an Action, local
  10. /// events, or networked events. Verbs also provide text, icons, and categories for displaying in the
  11. /// context-menu.
  12. /// </summary>
  13. [Serializable, NetSerializable, Virtual]
  14. public class Verb : IComparable
  15. {
  16. public static string DefaultTextStyleClass = "Verb";
  17. /// <summary>
  18. /// Determines the priority of this type of verb when displaying in the verb-menu. See <see
  19. /// cref="CompareTo"/>.
  20. /// </summary>
  21. public virtual int TypePriority => 0;
  22. /// <summary>
  23. /// Style class for drawing in the context menu
  24. /// </summary>
  25. public string TextStyleClass = DefaultTextStyleClass;
  26. /// <summary>
  27. /// This is an action that will be run when the verb is "acted" out.
  28. /// </summary>
  29. /// <remarks>
  30. /// This delegate probably just points to some function in the system assembling this verb. This delegate
  31. /// will be run regardless of whether <see cref="ExecutionEventArgs"/> is defined.
  32. /// </remarks>
  33. [NonSerialized]
  34. public Action? Act;
  35. /// <summary>
  36. /// This is a general local event that will be raised when the verb is executed.
  37. /// </summary>
  38. /// <remarks>
  39. /// If not null, this event will be raised regardless of whether <see cref="Act"/> was run. If this event
  40. /// exists purely to call a specific system method, then <see cref="Act"/> should probably be used instead (method
  41. /// events are a no-go).
  42. /// </remarks>
  43. [NonSerialized]
  44. public object? ExecutionEventArgs;
  45. /// <summary>
  46. /// Where do direct the local event. If invalid, the event is not raised directed at any entity.
  47. /// </summary>
  48. [NonSerialized]
  49. public EntityUid EventTarget = EntityUid.Invalid;
  50. /// <summary>
  51. /// Whether a verb is only defined client-side. Note that this has nothing to do with whether the target of
  52. /// the verb is client-side
  53. /// </summary>
  54. /// <remarks>
  55. /// If true, the client will not also ask the server to run this verb when executed locally. This just
  56. /// prevents unnecessary network events and "404-verb-not-found" log entries.
  57. /// </remarks>
  58. [NonSerialized]
  59. public bool ClientExclusive;
  60. /// <summary>
  61. /// The text that the user sees on the verb button.
  62. /// </summary>
  63. public string Text = string.Empty;
  64. /// <summary>
  65. /// Sprite of the icon that the user sees on the verb button.
  66. /// </summary>
  67. public SpriteSpecifier? Icon;
  68. /// <summary>
  69. /// Name of the category this button is under. Used to group verbs in the context menu.
  70. /// </summary>
  71. public VerbCategory? Category;
  72. /// <summary>
  73. /// Whether this verb is disabled.
  74. /// </summary>
  75. /// <remarks>
  76. /// Disabled verbs are shown in the context menu with a slightly darker background color, and cannot be
  77. /// executed. It is recommended that a <see cref="Message"/> message be provided outlining why this verb is
  78. /// disabled.
  79. /// </remarks>
  80. public bool Disabled;
  81. /// <summary>
  82. /// Optional informative message.
  83. /// </summary>
  84. /// <remarks>
  85. /// This will be shown as a tooltip when hovering over this verb in the context menu. Additionally, iF a
  86. /// <see cref="Disabled"/> verb is executed, this message will also be shown as a pop-up message. Useful for
  87. /// disabled verbs to inform users about why they cannot perform a given action.
  88. /// </remarks>
  89. public string? Message;
  90. /// <summary>
  91. /// Determines the priority of the verb. This affects both how the verb is displayed in the context menu
  92. /// GUI, and which verb is actually executed when left/alt clicking.
  93. /// </summary>
  94. /// <remarks>
  95. /// Bigger is higher priority (appears first, gets executed preferentially).
  96. /// </remarks>
  97. public int Priority;
  98. /// <summary>
  99. /// If this is not null, and no icon or icon texture were specified, a sprite view of this entity will be
  100. /// used as the icon for this verb.
  101. /// </summary>
  102. public NetEntity? IconEntity;
  103. /// <summary>
  104. /// Whether or not to close the context menu after using it to run this verb.
  105. /// </summary>
  106. /// <remarks>
  107. /// Setting this to false may be useful for repeatable actions, like rotating an object or maybe knocking on
  108. /// a window.
  109. /// </remarks>
  110. public bool? CloseMenu;
  111. public virtual bool CloseMenuDefault => true;
  112. /// <summary>
  113. /// How important is this verb, for the purposes of admin logging?
  114. /// </summary>
  115. /// <remarks>
  116. /// If this is just opening a UI or ejecting an id card, this should probably be low.
  117. /// </remarks>
  118. public LogImpact Impact = LogImpact.Low;
  119. /// <summary>
  120. /// Whether this verb requires confirmation before being executed.
  121. /// </summary>
  122. public bool ConfirmationPopup = false;
  123. /// <summary>
  124. /// If true, this verb will raise <see cref="ContactInteractionEvent"/>s when executed. If not explicitly
  125. /// specified, this will just default to raising the event if <see cref="DefaultDoContactInteraction"/> is
  126. /// true and the user is in range.
  127. /// </summary>
  128. public bool? DoContactInteraction;
  129. public virtual bool DefaultDoContactInteraction => false;
  130. /// <summary>
  131. /// Compares two verbs based on their <see cref="Priority"/>, <see cref="Category"/>, <see cref="Text"/>,
  132. /// and <see cref="IconTexture"/>.
  133. /// </summary>
  134. /// <remarks>
  135. /// <para>
  136. /// This is comparison is used when storing verbs in a SortedSet. The ordering of verbs determines both how
  137. /// the verbs are displayed in the context menu, and the order in which alternative action verbs are
  138. /// executed when alt-clicking.
  139. /// </para>
  140. /// <para>
  141. /// If two verbs are equal according to this comparison, they cannot both be added to the same sorted set of
  142. /// verbs. This is desirable, given that these verbs would also appear identical in the context menu.
  143. /// Distinct verbs should always have a unique and descriptive combination of text, icon, and category.
  144. /// </para>
  145. /// </remarks>
  146. public int CompareTo(object? obj)
  147. {
  148. if (obj is not Verb otherVerb)
  149. return -1;
  150. // Sort first by type-priority
  151. if (TypePriority != otherVerb.TypePriority)
  152. return otherVerb.TypePriority - TypePriority;
  153. // Then by verb-priority
  154. if (Priority != otherVerb.Priority)
  155. return otherVerb.Priority - Priority;
  156. // Then try use alphabetical verb categories. Uncategorized verbs always appear first.
  157. if (Category?.Text != otherVerb.Category?.Text)
  158. {
  159. return string.Compare(Category?.Text, otherVerb.Category?.Text, StringComparison.CurrentCulture);
  160. }
  161. // Then try use alphabetical verb text.
  162. if (Text != otherVerb.Text)
  163. {
  164. return string.Compare(Text, otherVerb.Text, StringComparison.CurrentCulture);
  165. }
  166. if (IconEntity != otherVerb.IconEntity)
  167. {
  168. if (IconEntity == null)
  169. return -1;
  170. if (otherVerb.IconEntity == null)
  171. return 1;
  172. return IconEntity.Value.CompareTo(otherVerb.IconEntity.Value);
  173. }
  174. // Finally, compare icon texture paths. Note that this matters for verbs that don't have any text (e.g., the rotate-verbs)
  175. return string.Compare(Icon?.ToString(), otherVerb.Icon?.ToString(), StringComparison.CurrentCulture);
  176. }
  177. // I hate this. Please somebody allow generics to be networked.
  178. /// <summary>
  179. /// Collection of all verb types,
  180. /// </summary>
  181. /// <remarks>
  182. /// Useful when iterating over verb types, though maybe this should be obtained and stored via reflection or
  183. /// something (list of all classes that inherit from Verb). Currently used for networking (apparently Type
  184. /// is not serializable?), and resolving console commands.
  185. /// </remarks>
  186. public static List<Type> VerbTypes = new()
  187. {
  188. typeof(Verb),
  189. typeof(VvVerb),
  190. typeof(InteractionVerb),
  191. typeof(UtilityVerb),
  192. typeof(InnateVerb),
  193. typeof(AlternativeVerb),
  194. typeof(ActivationVerb),
  195. typeof(ExamineVerb),
  196. typeof(EquipmentVerb)
  197. };
  198. }
  199. /// <summary>
  200. /// View variables verbs.
  201. /// </summary>
  202. /// <remarks>Currently only used for the verb that opens the view variables panel.</remarks>
  203. [Serializable, NetSerializable]
  204. public sealed class VvVerb : Verb
  205. {
  206. public override int TypePriority => int.MaxValue;
  207. }
  208. /// <summary>
  209. /// Primary interaction verbs. This includes both use-in-hand and interacting with external entities.
  210. /// </summary>
  211. /// <remarks>
  212. /// These verbs those that involve using the hands or the currently held item on some entity. These verbs usually
  213. /// correspond to interactions that can be triggered by left-clicking or using 'Z', and often depend on the
  214. /// currently held item. These verbs are collectively shown first in the context menu.
  215. /// </remarks>
  216. [Serializable, NetSerializable]
  217. public sealed class InteractionVerb : Verb
  218. {
  219. public new static string DefaultTextStyleClass = "InteractionVerb";
  220. public override int TypePriority => 4;
  221. public override bool DefaultDoContactInteraction => true;
  222. public InteractionVerb() : base()
  223. {
  224. TextStyleClass = DefaultTextStyleClass;
  225. }
  226. }
  227. /// <summary>
  228. /// These verbs are similar to the normal interaction verbs, except these interactions are facilitated by the
  229. /// currently held entity.
  230. /// </summary>
  231. /// <remarks>
  232. /// The only notable difference between these and InteractionVerbs is that they are obtained by raising an event
  233. /// directed at the currently held entity. Distinguishing between utility and interaction verbs helps avoid
  234. /// confusion if a component enables verbs both when the item is used on something else, or when it is the
  235. /// target of an interaction. These verbs are only obtained if the target and the held entity are NOT the same.
  236. /// </remarks>
  237. [Serializable, NetSerializable]
  238. public sealed class UtilityVerb : Verb
  239. {
  240. public override int TypePriority => 3;
  241. public override bool DefaultDoContactInteraction => true;
  242. public UtilityVerb() : base()
  243. {
  244. TextStyleClass = InteractionVerb.DefaultTextStyleClass;
  245. }
  246. }
  247. /// <summary>
  248. /// This is for verbs facilitated by components on the user.
  249. /// Verbs from clothing, species, etc. rather than a held item.
  250. /// </summary>
  251. /// <remarks>
  252. /// Add a component to the user's entity and sub to the get verbs event
  253. /// and it'll appear in the verbs menu on any target.
  254. /// </remarks>
  255. [Serializable, NetSerializable]
  256. public sealed class InnateVerb : Verb
  257. {
  258. public override int TypePriority => 3;
  259. public InnateVerb() : base()
  260. {
  261. TextStyleClass = InteractionVerb.DefaultTextStyleClass;
  262. }
  263. }
  264. /// <summary>
  265. /// Verbs for alternative-interactions.
  266. /// </summary>
  267. /// <remarks>
  268. /// When interacting with an entity via alt + left-click/E/Z the highest priority alt-interact verb is executed.
  269. /// These verbs are collectively shown second-to-last in the context menu.
  270. /// </remarks>
  271. [Serializable, NetSerializable]
  272. public sealed class AlternativeVerb : Verb
  273. {
  274. public override int TypePriority => 2;
  275. public new static string DefaultTextStyleClass = "AlternativeVerb";
  276. public override bool DefaultDoContactInteraction => true;
  277. public AlternativeVerb() : base()
  278. {
  279. TextStyleClass = DefaultTextStyleClass;
  280. }
  281. }
  282. /// <summary>
  283. /// Activation-type verbs.
  284. /// </summary>
  285. /// <remarks>
  286. /// These are verbs that activate an item in the world but are independent of the currently held items. For
  287. /// example, opening a door or a GUI. These verbs should correspond to interactions that can be triggered by
  288. /// using 'E', though many of those can also be triggered by left-mouse or 'Z' if there is no other interaction.
  289. /// These verbs are collectively shown second in the context menu.
  290. /// </remarks>
  291. [Serializable, NetSerializable]
  292. public sealed class ActivationVerb : Verb
  293. {
  294. public override int TypePriority => 1;
  295. public new static string DefaultTextStyleClass = "ActivationVerb";
  296. public override bool DefaultDoContactInteraction => true;
  297. public ActivationVerb() : base()
  298. {
  299. TextStyleClass = DefaultTextStyleClass;
  300. }
  301. }
  302. [Serializable, NetSerializable]
  303. public sealed class ExamineVerb : Verb
  304. {
  305. public override int TypePriority => 0;
  306. public override bool CloseMenuDefault => false; // for examine verbs, this will close the examine tooltip.
  307. public bool ShowOnExamineTooltip = true;
  308. }
  309. /// <summary>
  310. /// Verbs specifically for interactions that occur with equipped entities. These verbs are unique in that they
  311. /// can be used via the stripping UI. Additionally, when getting verbs on an entity with an inventory it will
  312. /// these automatically relay the <see cref="GetVerbsEvent{EquipmentVerb}"/> event to all equipped items via a
  313. /// <see cref="InventoryRelayedEvent{T}"/>.
  314. /// </summary>
  315. [Serializable, NetSerializable]
  316. public sealed class EquipmentVerb : Verb
  317. {
  318. public override int TypePriority => 5;
  319. }
  320. }