StationAiSystem.cs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. using System.Linq;
  2. using Content.Server.Chat.Managers;
  3. using Content.Server.Chat.Systems;
  4. using Content.Shared.Chat;
  5. using Content.Shared.Mind;
  6. using Content.Shared.Roles;
  7. using Content.Shared.Silicons.StationAi;
  8. using Content.Shared.StationAi;
  9. using Robust.Shared.Audio;
  10. using Robust.Shared.Map.Components;
  11. using Robust.Shared.Player;
  12. using static Content.Server.Chat.Systems.ChatSystem;
  13. namespace Content.Server.Silicons.StationAi;
  14. public sealed class StationAiSystem : SharedStationAiSystem
  15. {
  16. [Dependency] private readonly IChatManager _chats = default!;
  17. [Dependency] private readonly EntityLookupSystem _lookup = default!;
  18. [Dependency] private readonly SharedTransformSystem _xforms = default!;
  19. [Dependency] private readonly SharedMindSystem _mind = default!;
  20. [Dependency] private readonly SharedRoleSystem _roles = default!;
  21. private readonly HashSet<Entity<StationAiCoreComponent>> _ais = new();
  22. public override void Initialize()
  23. {
  24. base.Initialize();
  25. SubscribeLocalEvent<ExpandICChatRecipientsEvent>(OnExpandICChatRecipients);
  26. }
  27. private void OnExpandICChatRecipients(ExpandICChatRecipientsEvent ev)
  28. {
  29. var xformQuery = GetEntityQuery<TransformComponent>();
  30. var sourceXform = Transform(ev.Source);
  31. var sourcePos = _xforms.GetWorldPosition(sourceXform, xformQuery);
  32. // This function ensures that chat popups appear on camera views that have connected microphones.
  33. var query = EntityManager.EntityQueryEnumerator<StationAiCoreComponent, TransformComponent>();
  34. while (query.MoveNext(out var ent, out var entStationAiCore, out var entXform))
  35. {
  36. var stationAiCore = new Entity<StationAiCoreComponent?>(ent, entStationAiCore);
  37. if (!TryGetHeld(stationAiCore, out var insertedAi) || !TryComp(insertedAi, out ActorComponent? actor))
  38. continue;
  39. if (stationAiCore.Comp?.RemoteEntity == null || stationAiCore.Comp.Remote)
  40. continue;
  41. var xform = Transform(stationAiCore.Comp.RemoteEntity.Value);
  42. var range = (xform.MapID != sourceXform.MapID)
  43. ? -1
  44. : (sourcePos - _xforms.GetWorldPosition(xform, xformQuery)).Length();
  45. if (range < 0 || range > ev.VoiceRange)
  46. continue;
  47. ev.Recipients.TryAdd(actor.PlayerSession, new ICChatRecipientData(range, false));
  48. }
  49. }
  50. public override bool SetVisionEnabled(Entity<StationAiVisionComponent> entity, bool enabled, bool announce = false)
  51. {
  52. if (!base.SetVisionEnabled(entity, enabled, announce))
  53. return false;
  54. if (announce)
  55. {
  56. AnnounceSnip(entity.Owner);
  57. }
  58. return true;
  59. }
  60. public override bool SetWhitelistEnabled(Entity<StationAiWhitelistComponent> entity, bool enabled, bool announce = false)
  61. {
  62. if (!base.SetWhitelistEnabled(entity, enabled, announce))
  63. return false;
  64. if (announce)
  65. {
  66. AnnounceSnip(entity.Owner);
  67. }
  68. return true;
  69. }
  70. public override void AnnounceIntellicardUsage(EntityUid uid, SoundSpecifier? cue = null)
  71. {
  72. if (!TryComp<ActorComponent>(uid, out var actor))
  73. return;
  74. var msg = Loc.GetString("ai-consciousness-download-warning");
  75. var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg));
  76. _chats.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, actor.PlayerSession.Channel, colorOverride: Color.Red);
  77. if (cue != null && _mind.TryGetMind(uid, out var mindId, out _))
  78. _roles.MindPlaySound(mindId, cue);
  79. }
  80. private void AnnounceSnip(EntityUid entity)
  81. {
  82. var xform = Transform(entity);
  83. if (!TryComp(xform.GridUid, out MapGridComponent? grid))
  84. return;
  85. _ais.Clear();
  86. _lookup.GetChildEntities(xform.GridUid.Value, _ais);
  87. var filter = Filter.Empty();
  88. foreach (var ai in _ais)
  89. {
  90. // TODO: Filter API?
  91. if (TryComp(ai.Owner, out ActorComponent? actorComp))
  92. {
  93. filter.AddPlayer(actorComp.PlayerSession);
  94. }
  95. }
  96. // TEST
  97. // filter = Filter.Broadcast();
  98. // No easy way to do chat notif embeds atm.
  99. var tile = Maps.LocalToTile(xform.GridUid.Value, grid, xform.Coordinates);
  100. var msg = Loc.GetString("ai-wire-snipped", ("coords", tile));
  101. _chats.ChatMessageToMany(ChatChannel.Notifications, msg, msg, entity, false, true, filter.Recipients.Select(o => o.Channel));
  102. // Apparently there's no sound for this.
  103. }
  104. }