1
0

SharedVerbSystem.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. using Content.Shared.ActionBlocker;
  2. using Content.Shared.Hands.Components;
  3. using Content.Shared.Interaction;
  4. using Content.Shared.Inventory.VirtualItem;
  5. using Robust.Shared.Containers;
  6. using Robust.Shared.Map;
  7. namespace Content.Shared.Verbs
  8. {
  9. public abstract class SharedVerbSystem : EntitySystem
  10. {
  11. [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
  12. [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
  13. [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
  14. public override void Initialize()
  15. {
  16. base.Initialize();
  17. SubscribeAllEvent<ExecuteVerbEvent>(HandleExecuteVerb);
  18. }
  19. private void HandleExecuteVerb(ExecuteVerbEvent args, EntitySessionEventArgs eventArgs)
  20. {
  21. var user = eventArgs.SenderSession.AttachedEntity;
  22. if (user == null)
  23. return;
  24. if (!TryGetEntity(args.Target, out var target))
  25. return;
  26. // It is possible that client-side prediction can cause this event to be raised after the target entity has
  27. // been deleted. So we need to check that the entity still exists.
  28. if (Deleted(user))
  29. return;
  30. // Get the list of verbs. This effectively also checks that the requested verb is in fact a valid verb that
  31. // the user can perform.
  32. var verbs = GetLocalVerbs(target.Value, user.Value, args.RequestedVerb.GetType());
  33. // Note that GetLocalVerbs might waste time checking & preparing unrelated verbs even though we know
  34. // precisely which one we want to run. However, MOST entities will only have 1 or 2 verbs of a given type.
  35. // The one exception here is the "other" verb type, which has 3-4 verbs + all the debug verbs.
  36. // Find the requested verb.
  37. if (verbs.TryGetValue(args.RequestedVerb, out var verb))
  38. ExecuteVerb(verb, user.Value, target.Value);
  39. }
  40. /// <summary>
  41. /// Raises a number of events in order to get all verbs of the given type(s) defined in local systems. This
  42. /// does not request verbs from the server.
  43. /// </summary>
  44. public SortedSet<Verb> GetLocalVerbs(EntityUid target, EntityUid user, Type type, bool force = false)
  45. {
  46. return GetLocalVerbs(target, user, new List<Type>() { type }, force);
  47. }
  48. /// <inheritdoc cref="GetLocalVerbs(Robust.Shared.GameObjects.EntityUid,Robust.Shared.GameObjects.EntityUid,System.Type,bool)"/>
  49. public SortedSet<Verb> GetLocalVerbs(EntityUid target, EntityUid user, List<Type> types, bool force = false)
  50. {
  51. return GetLocalVerbs(target, user, types, out _, force);
  52. }
  53. /// <summary>
  54. /// Raises a number of events in order to get all verbs of the given type(s) defined in local systems. This
  55. /// does not request verbs from the server.
  56. /// </summary>
  57. public SortedSet<Verb> GetLocalVerbs(EntityUid target, EntityUid user, List<Type> types,
  58. out List<VerbCategory> extraCategories, bool force = false)
  59. {
  60. SortedSet<Verb> verbs = new();
  61. extraCategories = new();
  62. // accessibility checks
  63. var canAccess = force || _interactionSystem.InRangeAndAccessible(user, target);
  64. // A large number of verbs need to check action blockers. Instead of repeatedly having each system individually
  65. // call ActionBlocker checks, just cache it for the verb request.
  66. var canInteract = force || _actionBlockerSystem.CanInteract(user, target);
  67. var canComplexInteract = force || _actionBlockerSystem.CanComplexInteract(user);
  68. _interactionSystem.TryGetUsedEntity(user, out var @using);
  69. TryComp<HandsComponent>(user, out var hands);
  70. // TODO: fix this garbage and use proper generics or reflection or something else, not this.
  71. if (types.Contains(typeof(InteractionVerb)))
  72. {
  73. var verbEvent = new GetVerbsEvent<InteractionVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
  74. RaiseLocalEvent(target, verbEvent, true);
  75. verbs.UnionWith(verbEvent.Verbs);
  76. }
  77. if (types.Contains(typeof(UtilityVerb))
  78. && @using != null
  79. && @using != target)
  80. {
  81. var verbEvent = new GetVerbsEvent<UtilityVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
  82. RaiseLocalEvent(@using.Value, verbEvent, true); // directed at used, not at target
  83. verbs.UnionWith(verbEvent.Verbs);
  84. }
  85. if (types.Contains(typeof(InnateVerb)))
  86. {
  87. var verbEvent = new GetVerbsEvent<InnateVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
  88. RaiseLocalEvent(user, verbEvent, true);
  89. verbs.UnionWith(verbEvent.Verbs);
  90. }
  91. if (types.Contains(typeof(AlternativeVerb)))
  92. {
  93. var verbEvent = new GetVerbsEvent<AlternativeVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
  94. RaiseLocalEvent(target, verbEvent, true);
  95. verbs.UnionWith(verbEvent.Verbs);
  96. }
  97. if (types.Contains(typeof(ActivationVerb)))
  98. {
  99. var verbEvent = new GetVerbsEvent<ActivationVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
  100. RaiseLocalEvent(target, verbEvent, true);
  101. verbs.UnionWith(verbEvent.Verbs);
  102. }
  103. if (types.Contains(typeof(ExamineVerb)))
  104. {
  105. var verbEvent = new GetVerbsEvent<ExamineVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
  106. RaiseLocalEvent(target, verbEvent, true);
  107. verbs.UnionWith(verbEvent.Verbs);
  108. }
  109. // generic verbs
  110. if (types.Contains(typeof(Verb)))
  111. {
  112. var verbEvent = new GetVerbsEvent<Verb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
  113. RaiseLocalEvent(target, verbEvent, true);
  114. verbs.UnionWith(verbEvent.Verbs);
  115. }
  116. if (types.Contains(typeof(EquipmentVerb)))
  117. {
  118. var access = canAccess || _interactionSystem.CanAccessEquipment(user, target);
  119. var verbEvent = new GetVerbsEvent<EquipmentVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories);
  120. RaiseLocalEvent(target, verbEvent);
  121. verbs.UnionWith(verbEvent.Verbs);
  122. }
  123. return verbs;
  124. }
  125. /// <summary>
  126. /// Execute the provided verb.
  127. /// </summary>
  128. /// <remarks>
  129. /// This will try to call the action delegates and raise the local events for the given verb.
  130. /// </remarks>
  131. public virtual void ExecuteVerb(Verb verb, EntityUid user, EntityUid target, bool forced = false)
  132. {
  133. // invoke any relevant actions
  134. verb.Act?.Invoke();
  135. // Maybe raise a local event
  136. if (verb.ExecutionEventArgs != null)
  137. {
  138. if (verb.EventTarget.IsValid())
  139. RaiseLocalEvent(verb.EventTarget, verb.ExecutionEventArgs);
  140. else
  141. RaiseLocalEvent(verb.ExecutionEventArgs);
  142. }
  143. if (Deleted(user) || Deleted(target))
  144. return;
  145. // Perform any contact interactions
  146. if (verb.DoContactInteraction ?? (verb.DefaultDoContactInteraction && _interactionSystem.InRangeUnobstructed(user, target)))
  147. _interactionSystem.DoContactInteraction(user, target);
  148. }
  149. }
  150. // Does nothing on server
  151. /// <summary>
  152. /// Raised directed when trying to get the entity menu visibility for entities.
  153. /// </summary>
  154. [ByRefEvent]
  155. public record struct MenuVisibilityEvent
  156. {
  157. public MapCoordinates TargetPos;
  158. public MenuVisibility Visibility;
  159. }
  160. // Does nothing on server
  161. [Flags]
  162. public enum MenuVisibility
  163. {
  164. // What entities can a user see on the entity menu?
  165. Default = 0, // They can only see entities in FoV.
  166. NoFov = 1 << 0, // They ignore FoV restrictions
  167. InContainer = 1 << 1, // They can see through containers.
  168. Invisible = 1 << 2, // They can see entities without sprites and the "HideContextMenu" tag is ignored.
  169. All = NoFov | InContainer | Invisible
  170. }
  171. }