using System; using System.Collections.Generic; using Content.Shared.ActionBlocker; using Content.Shared.Hands.Components; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.Serialization; using Robust.Shared.IoC; using Content.Shared.Interaction; namespace Content.Shared.Verbs { [Serializable, NetSerializable] public class RequestServerVerbsEvent : EntityEventArgs { public readonly EntityUid EntityUid; public readonly VerbType Type; public RequestServerVerbsEvent(EntityUid entityUid, VerbType type) { EntityUid = entityUid; Type = type; } } [Serializable, NetSerializable] public class VerbsResponseEvent : EntityEventArgs { public readonly Dictionary>? Verbs; public readonly EntityUid Entity; public VerbsResponseEvent(EntityUid entity, Dictionary>? verbs) { Entity = entity; if (verbs == null) return; // Apparently SortedSet is not serlializable. Cast to List. Verbs = new(); foreach (var entry in verbs) { Verbs.Add(entry.Key, new List(entry.Value)); } } } [Serializable, NetSerializable] public class ExecuteVerbEvent : EntityEventArgs { public readonly EntityUid Target; public readonly Verb RequestedVerb; /// /// The type of verb to try execute. Avoids having to get a list of all verbs on the receiving end. /// public readonly VerbType Type; public ExecuteVerbEvent(EntityUid target, Verb requestedVerb, VerbType type) { Target = target; RequestedVerb = requestedVerb; Type = type; } } /// /// Request primary interaction verbs. This includes both use-in-hand and interacting with external entities. /// /// /// These verbs those that involve using the hands or the currently held item on some entity. These verbs usually /// correspond to interactions that can be triggered by left-clicking or using 'Z', and often depend on the /// currently held item. These verbs are collectively shown first in the context menu. /// public class GetInteractionVerbsEvent : GetVerbsEvent { public GetInteractionVerbsEvent(IEntity user, IEntity target, bool force=false) : base(user, target, force) { } } /// /// Request activation verbs. /// /// /// These are verbs that activate an item in the world but are independent of the currently held items. For /// example, opening a door or a GUI. These verbs should correspond to interactions that can be triggered by /// using 'E', though many of those can also be triggered by left-mouse or 'Z' if there is no other interaction. /// These verbs are collectively shown second in the context menu. /// public class GetActivationVerbsEvent : GetVerbsEvent { public GetActivationVerbsEvent(IEntity user, IEntity target, bool force=false) : base(user, target, force) { } } /// /// Request alternative-interaction verbs. /// /// /// When interacting with an entity via alt + left-click/E/Z the highest priority alt-interact verb is executed. /// These verbs are collectively shown second-to-last in the context menu. /// public class GetAlternativeVerbsEvent : GetVerbsEvent { public GetAlternativeVerbsEvent(IEntity user, IEntity target, bool force=false) : base(user, target, force) { } } /// /// Request Miscellaneous verbs. /// /// /// Includes (nearly) global interactions like "examine", "pull", or "debug". These verbs are collectively shown /// last in the context menu. /// public class GetOtherVerbsEvent : GetVerbsEvent { public GetOtherVerbsEvent(IEntity user, IEntity target, bool force=false) : base(user, target, force) { } } /// /// Directed event that requests verbs from any systems/components on a target entity. /// public class GetVerbsEvent : EntityEventArgs { /// /// Event output. Set of verbs that can be executed. /// public SortedSet Verbs = new(); /// /// Can the user physically access the target? /// /// /// This is a combination of and /// . /// public bool CanAccess; /// /// The entity being targeted for the verb. /// public IEntity Target; /// /// The entity that will be "performing" the verb. /// public IEntity User; /// /// Can the user physically interact? /// /// /// This is a just a cached result. Given that many verbs need /// to check this, it prevents it from having to be repeatedly called by each individual system that might /// contribute a verb. /// public bool CanInteract; /// /// The User's hand component. /// /// /// This may be null if the user has no hands. /// public SharedHandsComponent? Hands; /// /// The entity currently being held by the active hand. /// /// /// This is only ever not null when is true and the user /// has hands. /// public IEntity? Using; public GetVerbsEvent(IEntity user, IEntity target, bool force=false) { User = user; Target = target; CanAccess = force || (Target == User) || user.IsInSameOrParentContainer(target) && EntitySystem.Get().InRangeUnobstructed(user, target); // A large number of verbs need to check action blockers. Instead of repeatedly having each system individually // call ActionBlocker checks, just cache it for the verb request. var actionBlockerSystem = EntitySystem.Get(); CanInteract = force || actionBlockerSystem.CanInteract(user.Uid); if (!user.TryGetComponent(out Hands) || !actionBlockerSystem.CanUse(user.Uid)) return; Hands.TryGetActiveHeldEntity(out Using); // Check whether the "Held" entity is a virtual pull entity. If yes, set that as the entity being "Used". // This allows you to do things like buckle a dragged person onto a surgery table, without click-dragging // their sprite. if (Using != null && Using.TryGetComponent(out var pull)) { Using = IoCManager.Resolve().GetEntity(pull.BlockingEntity); } } } }