SharedStationAiSystem.Held.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. using System.Diagnostics.CodeAnalysis;
  2. using Content.Shared.Actions.Events;
  3. using Content.Shared.IdentityManagement;
  4. using Content.Shared.Interaction.Events;
  5. using Content.Shared.Popups;
  6. using Content.Shared.Verbs;
  7. using Robust.Shared.Serialization;
  8. using Robust.Shared.Utility;
  9. namespace Content.Shared.Silicons.StationAi;
  10. public abstract partial class SharedStationAiSystem
  11. {
  12. /*
  13. * Added when an entity is inserted into a StationAiCore.
  14. */
  15. //TODO: Fix this, please
  16. private const string JobNameLocId = "job-name-station-ai";
  17. private void InitializeHeld()
  18. {
  19. SubscribeLocalEvent<StationAiRadialMessage>(OnRadialMessage);
  20. SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(OnMessageAttempt);
  21. SubscribeLocalEvent<StationAiWhitelistComponent, GetVerbsEvent<AlternativeVerb>>(OnTargetVerbs);
  22. SubscribeLocalEvent<StationAiHeldComponent, InteractionAttemptEvent>(OnHeldInteraction);
  23. SubscribeLocalEvent<StationAiHeldComponent, AttemptRelayActionComponentChangeEvent>(OnHeldRelay);
  24. SubscribeLocalEvent<StationAiHeldComponent, JumpToCoreEvent>(OnCoreJump);
  25. SubscribeLocalEvent<TryGetIdentityShortInfoEvent>(OnTryGetIdentityShortInfo);
  26. }
  27. private void OnTryGetIdentityShortInfo(TryGetIdentityShortInfoEvent args)
  28. {
  29. if (args.Handled)
  30. {
  31. return;
  32. }
  33. if (!HasComp<StationAiHeldComponent>(args.ForActor))
  34. {
  35. return;
  36. }
  37. args.Title = $"{Name(args.ForActor)} ({Loc.GetString(JobNameLocId)})";
  38. args.Handled = true;
  39. }
  40. private void OnCoreJump(Entity<StationAiHeldComponent> ent, ref JumpToCoreEvent args)
  41. {
  42. if (!TryGetCore(ent.Owner, out var core) || core.Comp?.RemoteEntity == null)
  43. return;
  44. _xforms.DropNextTo(core.Comp.RemoteEntity.Value, core.Owner) ;
  45. }
  46. /// <summary>
  47. /// Tries to get the entity held in the AI core using StationAiCore.
  48. /// </summary>
  49. public bool TryGetHeld(Entity<StationAiCoreComponent?> entity, out EntityUid held)
  50. {
  51. held = EntityUid.Invalid;
  52. if (!Resolve(entity.Owner, ref entity.Comp))
  53. return false;
  54. if (!_containers.TryGetContainer(entity.Owner, StationAiCoreComponent.Container, out var container) ||
  55. container.ContainedEntities.Count == 0)
  56. return false;
  57. held = container.ContainedEntities[0];
  58. return true;
  59. }
  60. /// <summary>
  61. /// Tries to get the entity held in the AI using StationAiHolder.
  62. /// </summary>
  63. public bool TryGetHeld(Entity<StationAiHolderComponent?> entity, out EntityUid held)
  64. {
  65. TryComp<StationAiCoreComponent>(entity.Owner, out var stationAiCore);
  66. return TryGetHeld((entity.Owner, stationAiCore), out held);
  67. }
  68. public bool TryGetCore(EntityUid entity, out Entity<StationAiCoreComponent?> core)
  69. {
  70. var xform = Transform(entity);
  71. var meta = MetaData(entity);
  72. var ent = new Entity<TransformComponent?, MetaDataComponent?>(entity, xform, meta);
  73. if (!_containers.TryGetContainingContainer(ent, out var container) ||
  74. container.ID != StationAiCoreComponent.Container ||
  75. !TryComp(container.Owner, out StationAiCoreComponent? coreComp) ||
  76. coreComp.RemoteEntity == null)
  77. {
  78. core = (EntityUid.Invalid, null);
  79. return false;
  80. }
  81. core = (container.Owner, coreComp);
  82. return true;
  83. }
  84. private void OnHeldRelay(Entity<StationAiHeldComponent> ent, ref AttemptRelayActionComponentChangeEvent args)
  85. {
  86. if (!TryGetCore(ent.Owner, out var core))
  87. return;
  88. args.Target = core.Comp?.RemoteEntity;
  89. }
  90. private void OnRadialMessage(StationAiRadialMessage ev)
  91. {
  92. if (!TryGetEntity(ev.Entity, out var target))
  93. return;
  94. ev.Event.User = ev.Actor;
  95. RaiseLocalEvent(target.Value, (object) ev.Event);
  96. }
  97. private void OnMessageAttempt(BoundUserInterfaceMessageAttempt ev)
  98. {
  99. if (ev.Actor == ev.Target)
  100. return;
  101. if (TryComp(ev.Actor, out StationAiHeldComponent? aiComp) &&
  102. (!TryComp(ev.Target, out StationAiWhitelistComponent? whitelistComponent) ||
  103. !ValidateAi((ev.Actor, aiComp))))
  104. {
  105. if (whitelistComponent is { Enabled: false })
  106. {
  107. ShowDeviceNotRespondingPopup(ev.Actor);
  108. }
  109. ev.Cancel();
  110. }
  111. }
  112. private void OnHeldInteraction(Entity<StationAiHeldComponent> ent, ref InteractionAttemptEvent args)
  113. {
  114. // Cancel if it's not us or something with a whitelist, or whitelist is disabled.
  115. args.Cancelled = (!TryComp(args.Target, out StationAiWhitelistComponent? whitelistComponent)
  116. || !whitelistComponent.Enabled)
  117. && ent.Owner != args.Target
  118. && args.Target != null;
  119. if (whitelistComponent is { Enabled: false })
  120. {
  121. ShowDeviceNotRespondingPopup(ent.Owner);
  122. }
  123. }
  124. private void OnTargetVerbs(Entity<StationAiWhitelistComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
  125. {
  126. if (!args.CanComplexInteract
  127. || !HasComp<StationAiHeldComponent>(args.User))
  128. {
  129. return;
  130. }
  131. var user = args.User;
  132. var target = args.Target;
  133. var isOpen = _uiSystem.IsUiOpen(target, AiUi.Key, user);
  134. var verb = new AlternativeVerb
  135. {
  136. Text = isOpen ? Loc.GetString("ai-close") : Loc.GetString("ai-open"),
  137. Act = () =>
  138. {
  139. // no need to show menu if device is not powered.
  140. if (!PowerReceiver.IsPowered(ent.Owner))
  141. {
  142. ShowDeviceNotRespondingPopup(user);
  143. return;
  144. }
  145. if (isOpen)
  146. {
  147. _uiSystem.CloseUi(ent.Owner, AiUi.Key, user);
  148. }
  149. else
  150. {
  151. _uiSystem.OpenUi(ent.Owner, AiUi.Key, user);
  152. }
  153. }
  154. };
  155. args.Verbs.Add(verb);
  156. }
  157. private void ShowDeviceNotRespondingPopup(EntityUid toEntity)
  158. {
  159. _popup.PopupClient(Loc.GetString("ai-device-not-responding"), toEntity, PopupType.MediumCaution);
  160. }
  161. }
  162. /// <summary>
  163. /// Raised from client to server as a BUI message wrapping the event to perform.
  164. /// Also handles AI action validation.
  165. /// </summary>
  166. [Serializable, NetSerializable]
  167. public sealed class StationAiRadialMessage : BoundUserInterfaceMessage
  168. {
  169. public BaseStationAiAction Event = default!;
  170. }
  171. // Do nothing on server just here for shared move along.
  172. /// <summary>
  173. /// Raised on client to get the relevant data for radial actions.
  174. /// </summary>
  175. public sealed class StationAiRadial : BaseStationAiAction
  176. {
  177. public SpriteSpecifier? Sprite;
  178. public string? Tooltip;
  179. public BaseStationAiAction Event = default!;
  180. }
  181. /// <summary>
  182. /// Abstract parent for radial actions events.
  183. /// When a client requests a radial action this will get sent.
  184. /// </summary>
  185. [Serializable, NetSerializable]
  186. public abstract class BaseStationAiAction
  187. {
  188. [field:NonSerialized]
  189. public EntityUid User { get; set; }
  190. }
  191. // No idea if there's a better way to do this.
  192. /// <summary>
  193. /// Grab actions possible for an AI on the target entity.
  194. /// </summary>
  195. [ByRefEvent]
  196. public record struct GetStationAiRadialEvent()
  197. {
  198. public List<StationAiRadial> Actions = new();
  199. }
  200. [Serializable, NetSerializable]
  201. public enum AiUi : byte
  202. {
  203. Key,
  204. }