1
0

PdaSystem.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. using Content.Server.Access.Systems;
  2. using Content.Server.AlertLevel;
  3. using Content.Server.CartridgeLoader;
  4. using Content.Server.Chat.Managers;
  5. using Content.Server.DeviceNetwork.Components;
  6. using Content.Server.Instruments;
  7. using Content.Server.Light.EntitySystems;
  8. using Content.Server.PDA.Ringer;
  9. using Content.Server.Station.Systems;
  10. using Content.Server.Store.Components;
  11. using Content.Server.Store.Systems;
  12. using Content.Server.Traitor.Uplink;
  13. using Content.Shared.Access.Components;
  14. using Content.Shared.CartridgeLoader;
  15. using Content.Shared.Chat;
  16. using Content.Shared.Light;
  17. using Content.Shared.Light.Components;
  18. using Content.Shared.Light.EntitySystems;
  19. using Content.Shared.PDA;
  20. using Content.Shared.Store.Components;
  21. using Robust.Server.Containers;
  22. using Robust.Server.GameObjects;
  23. using Robust.Shared.Containers;
  24. using Robust.Shared.Player;
  25. using Robust.Shared.Utility;
  26. namespace Content.Server.PDA
  27. {
  28. public sealed class PdaSystem : SharedPdaSystem
  29. {
  30. [Dependency] private readonly CartridgeLoaderSystem _cartridgeLoader = default!;
  31. [Dependency] private readonly InstrumentSystem _instrument = default!;
  32. [Dependency] private readonly RingerSystem _ringer = default!;
  33. [Dependency] private readonly StationSystem _station = default!;
  34. [Dependency] private readonly StoreSystem _store = default!;
  35. [Dependency] private readonly IChatManager _chatManager = default!;
  36. [Dependency] private readonly UserInterfaceSystem _ui = default!;
  37. [Dependency] private readonly UnpoweredFlashlightSystem _unpoweredFlashlight = default!;
  38. [Dependency] private readonly ContainerSystem _containerSystem = default!;
  39. [Dependency] private readonly IdCardSystem _idCard = default!;
  40. public override void Initialize()
  41. {
  42. base.Initialize();
  43. SubscribeLocalEvent<PdaComponent, LightToggleEvent>(OnLightToggle);
  44. // UI Events:
  45. SubscribeLocalEvent<PdaComponent, BoundUIOpenedEvent>(OnPdaOpen);
  46. SubscribeLocalEvent<PdaComponent, PdaRequestUpdateInterfaceMessage>(OnUiMessage);
  47. SubscribeLocalEvent<PdaComponent, PdaToggleFlashlightMessage>(OnUiMessage);
  48. SubscribeLocalEvent<PdaComponent, PdaShowRingtoneMessage>(OnUiMessage);
  49. SubscribeLocalEvent<PdaComponent, PdaShowMusicMessage>(OnUiMessage);
  50. SubscribeLocalEvent<PdaComponent, PdaShowUplinkMessage>(OnUiMessage);
  51. SubscribeLocalEvent<PdaComponent, PdaLockUplinkMessage>(OnUiMessage);
  52. SubscribeLocalEvent<PdaComponent, CartridgeLoaderNotificationSentEvent>(OnNotification);
  53. SubscribeLocalEvent<StationRenamedEvent>(OnStationRenamed);
  54. SubscribeLocalEvent<EntityRenamedEvent>(OnEntityRenamed, after: new[] { typeof(IdCardSystem) });
  55. SubscribeLocalEvent<AlertLevelChangedEvent>(OnAlertLevelChanged);
  56. }
  57. private void OnEntityRenamed(ref EntityRenamedEvent ev)
  58. {
  59. if (HasComp<IdCardComponent>(ev.Uid))
  60. return;
  61. if (_idCard.TryFindIdCard(ev.Uid, out var idCard))
  62. {
  63. var query = EntityQueryEnumerator<PdaComponent>();
  64. while (query.MoveNext(out var uid, out var comp))
  65. {
  66. if (comp.ContainedId == idCard)
  67. {
  68. SetOwner(uid, comp, ev.Uid, ev.NewName);
  69. }
  70. }
  71. }
  72. }
  73. protected override void OnComponentInit(EntityUid uid, PdaComponent pda, ComponentInit args)
  74. {
  75. base.OnComponentInit(uid, pda, args);
  76. if (!HasComp<UserInterfaceComponent>(uid))
  77. return;
  78. UpdateAlertLevel(uid, pda);
  79. UpdateStationName(uid, pda);
  80. }
  81. protected override void OnItemInserted(EntityUid uid, PdaComponent pda, EntInsertedIntoContainerMessage args)
  82. {
  83. base.OnItemInserted(uid, pda, args);
  84. var id = CompOrNull<IdCardComponent>(pda.ContainedId);
  85. if (id != null)
  86. pda.OwnerName = id.FullName;
  87. UpdatePdaUi(uid, pda);
  88. }
  89. protected override void OnItemRemoved(EntityUid uid, PdaComponent pda, EntRemovedFromContainerMessage args)
  90. {
  91. if (args.Container.ID != pda.IdSlot.ID && args.Container.ID != pda.PenSlot.ID && args.Container.ID != pda.PaiSlot.ID)
  92. return;
  93. // TODO: This is super cursed just use compstates please.
  94. if (MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating)
  95. return;
  96. base.OnItemRemoved(uid, pda, args);
  97. UpdatePdaUi(uid, pda);
  98. }
  99. private void OnLightToggle(EntityUid uid, PdaComponent pda, LightToggleEvent args)
  100. {
  101. pda.FlashlightOn = args.IsOn;
  102. UpdatePdaUi(uid, pda);
  103. }
  104. public void SetOwner(EntityUid uid, PdaComponent pda, EntityUid owner, string ownerName)
  105. {
  106. pda.OwnerName = ownerName;
  107. pda.PdaOwner = owner;
  108. UpdatePdaUi(uid, pda);
  109. }
  110. private void OnStationRenamed(StationRenamedEvent ev)
  111. {
  112. UpdateAllPdaUisOnStation();
  113. }
  114. private void OnAlertLevelChanged(AlertLevelChangedEvent args)
  115. {
  116. UpdateAllPdaUisOnStation();
  117. }
  118. private void UpdateAllPdaUisOnStation()
  119. {
  120. var query = AllEntityQuery<PdaComponent>();
  121. while (query.MoveNext(out var ent, out var comp))
  122. {
  123. UpdatePdaUi(ent, comp);
  124. }
  125. }
  126. private void OnNotification(Entity<PdaComponent> ent, ref CartridgeLoaderNotificationSentEvent args)
  127. {
  128. _ringer.RingerPlayRingtone(ent.Owner);
  129. if (!_containerSystem.TryGetContainingContainer((ent, null, null), out var container)
  130. || !TryComp<ActorComponent>(container.Owner, out var actor))
  131. return;
  132. var message = FormattedMessage.EscapeText(args.Message);
  133. var wrappedMessage = Loc.GetString("pda-notification-message",
  134. ("header", args.Header),
  135. ("message", message));
  136. _chatManager.ChatMessageToOne(
  137. ChatChannel.Notifications,
  138. message,
  139. wrappedMessage,
  140. EntityUid.Invalid,
  141. false,
  142. actor.PlayerSession.Channel);
  143. }
  144. /// <summary>
  145. /// Send new UI state to clients, call if you modify something like uplink.
  146. /// </summary>
  147. public void UpdatePdaUi(EntityUid uid, PdaComponent? pda = null)
  148. {
  149. if (!Resolve(uid, ref pda, false))
  150. return;
  151. if (!_ui.HasUi(uid, PdaUiKey.Key))
  152. return;
  153. var address = GetDeviceNetAddress(uid);
  154. var hasInstrument = HasComp<InstrumentComponent>(uid);
  155. var showUplink = HasComp<UplinkComponent>(uid) && IsUnlocked(uid);
  156. UpdateStationName(uid, pda);
  157. UpdateAlertLevel(uid, pda);
  158. // TODO: Update the level and name of the station with each call to UpdatePdaUi is only needed for latejoin players.
  159. // TODO: If someone can implement changing the level and name of the station when changing the PDA grid, this can be removed.
  160. // TODO don't make this depend on cartridge loader!?!?
  161. if (!TryComp(uid, out CartridgeLoaderComponent? loader))
  162. return;
  163. var programs = _cartridgeLoader.GetAvailablePrograms(uid, loader);
  164. var id = CompOrNull<IdCardComponent>(pda.ContainedId);
  165. var state = new PdaUpdateState(
  166. programs,
  167. GetNetEntity(loader.ActiveProgram),
  168. pda.FlashlightOn,
  169. pda.PenSlot.HasItem,
  170. pda.PaiSlot.HasItem,
  171. new PdaIdInfoText
  172. {
  173. ActualOwnerName = pda.OwnerName,
  174. IdOwner = id?.FullName,
  175. JobTitle = id?.LocalizedJobTitle,
  176. StationAlertLevel = pda.StationAlertLevel,
  177. StationAlertColor = pda.StationAlertColor
  178. },
  179. pda.StationName,
  180. showUplink,
  181. hasInstrument,
  182. address);
  183. _ui.SetUiState(uid, PdaUiKey.Key, state);
  184. }
  185. private void OnPdaOpen(Entity<PdaComponent> ent, ref BoundUIOpenedEvent args)
  186. {
  187. if (!PdaUiKey.Key.Equals(args.UiKey))
  188. return;
  189. UpdatePdaUi(ent.Owner, ent.Comp);
  190. }
  191. private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaRequestUpdateInterfaceMessage msg)
  192. {
  193. if (!PdaUiKey.Key.Equals(msg.UiKey))
  194. return;
  195. UpdatePdaUi(uid, pda);
  196. }
  197. private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaToggleFlashlightMessage msg)
  198. {
  199. if (!PdaUiKey.Key.Equals(msg.UiKey))
  200. return;
  201. // TODO PREDICTION
  202. // When moving this to shared, fill in the user field
  203. _unpoweredFlashlight.TryToggleLight(uid, user: null);
  204. }
  205. private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaShowRingtoneMessage msg)
  206. {
  207. if (!PdaUiKey.Key.Equals(msg.UiKey))
  208. return;
  209. if (HasComp<RingerComponent>(uid))
  210. _ringer.ToggleRingerUI(uid, msg.Actor);
  211. }
  212. private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaShowMusicMessage msg)
  213. {
  214. if (!PdaUiKey.Key.Equals(msg.UiKey))
  215. return;
  216. if (TryComp<InstrumentComponent>(uid, out var instrument))
  217. _instrument.ToggleInstrumentUi(uid, msg.Actor, instrument);
  218. }
  219. private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaShowUplinkMessage msg)
  220. {
  221. if (!PdaUiKey.Key.Equals(msg.UiKey))
  222. return;
  223. // check if its locked again to prevent malicious clients opening locked uplinks
  224. if (HasComp<UplinkComponent>(uid) && IsUnlocked(uid))
  225. _store.ToggleUi(msg.Actor, uid);
  226. }
  227. private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaLockUplinkMessage msg)
  228. {
  229. if (!PdaUiKey.Key.Equals(msg.UiKey))
  230. return;
  231. if (TryComp<RingerUplinkComponent>(uid, out var uplink))
  232. {
  233. _ringer.LockUplink(uid, uplink);
  234. UpdatePdaUi(uid, pda);
  235. }
  236. }
  237. private bool IsUnlocked(EntityUid uid)
  238. {
  239. return !TryComp<RingerUplinkComponent>(uid, out var uplink) || uplink.Unlocked;
  240. }
  241. private void UpdateStationName(EntityUid uid, PdaComponent pda)
  242. {
  243. var station = _station.GetOwningStation(uid);
  244. pda.StationName = station is null ? null : Name(station.Value);
  245. }
  246. private void UpdateAlertLevel(EntityUid uid, PdaComponent pda)
  247. {
  248. var station = _station.GetOwningStation(uid);
  249. if (!TryComp(station, out AlertLevelComponent? alertComp) ||
  250. alertComp.AlertLevels == null)
  251. return;
  252. pda.StationAlertLevel = alertComp.CurrentLevel;
  253. if (alertComp.AlertLevels.Levels.TryGetValue(alertComp.CurrentLevel, out var details))
  254. pda.StationAlertColor = details.Color;
  255. }
  256. private string? GetDeviceNetAddress(EntityUid uid)
  257. {
  258. string? address = null;
  259. if (TryComp(uid, out DeviceNetworkComponent? deviceNetworkComponent))
  260. {
  261. address = deviceNetworkComponent?.Address;
  262. }
  263. return address;
  264. }
  265. }
  266. }