ForensicScannerSystem.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. using System.Linq;
  2. using System.Text;
  3. using Content.Server.Popups;
  4. using Content.Shared.UserInterface;
  5. using Content.Shared.DoAfter;
  6. using Content.Shared.Fluids.Components;
  7. using Content.Shared.Forensics;
  8. using Content.Shared.Hands.EntitySystems;
  9. using Content.Shared.Interaction;
  10. using Content.Shared.Paper;
  11. using Content.Shared.Verbs;
  12. using Content.Shared.Tag;
  13. using Robust.Shared.Audio.Systems;
  14. using Robust.Server.GameObjects;
  15. using Robust.Shared.Audio;
  16. using Robust.Shared.Player;
  17. using Robust.Shared.Timing;
  18. using Content.Server.Chemistry.Containers.EntitySystems;
  19. // todo: remove this stinky LINQy
  20. namespace Content.Server.Forensics
  21. {
  22. public sealed class ForensicScannerSystem : EntitySystem
  23. {
  24. [Dependency] private readonly IGameTiming _gameTiming = default!;
  25. [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
  26. [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
  27. [Dependency] private readonly PopupSystem _popupSystem = default!;
  28. [Dependency] private readonly PaperSystem _paperSystem = default!;
  29. [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
  30. [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
  31. [Dependency] private readonly MetaDataSystem _metaData = default!;
  32. [Dependency] private readonly ForensicsSystem _forensicsSystem = default!;
  33. [Dependency] private readonly TagSystem _tag = default!;
  34. public override void Initialize()
  35. {
  36. base.Initialize();
  37. SubscribeLocalEvent<ForensicScannerComponent, AfterInteractEvent>(OnAfterInteract);
  38. SubscribeLocalEvent<ForensicScannerComponent, AfterInteractUsingEvent>(OnAfterInteractUsing);
  39. SubscribeLocalEvent<ForensicScannerComponent, BeforeActivatableUIOpenEvent>(OnBeforeActivatableUIOpen);
  40. SubscribeLocalEvent<ForensicScannerComponent, GetVerbsEvent<UtilityVerb>>(OnUtilityVerb);
  41. SubscribeLocalEvent<ForensicScannerComponent, ForensicScannerPrintMessage>(OnPrint);
  42. SubscribeLocalEvent<ForensicScannerComponent, ForensicScannerClearMessage>(OnClear);
  43. SubscribeLocalEvent<ForensicScannerComponent, ForensicScannerDoAfterEvent>(OnDoAfter);
  44. }
  45. private void UpdateUserInterface(EntityUid uid, ForensicScannerComponent component)
  46. {
  47. var state = new ForensicScannerBoundUserInterfaceState(
  48. component.Fingerprints,
  49. component.Fibers,
  50. component.TouchDNAs,
  51. component.SolutionDNAs,
  52. component.Residues,
  53. component.LastScannedName,
  54. component.PrintCooldown,
  55. component.PrintReadyAt);
  56. _uiSystem.SetUiState(uid, ForensicScannerUiKey.Key, state);
  57. }
  58. private void OnDoAfter(EntityUid uid, ForensicScannerComponent component, DoAfterEvent args)
  59. {
  60. if (args.Handled || args.Cancelled)
  61. return;
  62. if (!EntityManager.TryGetComponent(uid, out ForensicScannerComponent? scanner))
  63. return;
  64. if (args.Args.Target != null)
  65. {
  66. if (!TryComp<ForensicsComponent>(args.Args.Target, out var forensics))
  67. {
  68. scanner.Fingerprints = new();
  69. scanner.Fibers = new();
  70. scanner.TouchDNAs = new();
  71. scanner.Residues = new();
  72. }
  73. else
  74. {
  75. scanner.Fingerprints = forensics.Fingerprints.ToList();
  76. scanner.Fibers = forensics.Fibers.ToList();
  77. scanner.TouchDNAs = forensics.DNAs.ToList();
  78. scanner.Residues = forensics.Residues.ToList();
  79. }
  80. if (_tag.HasTag(args.Args.Target.Value, "DNASolutionScannable"))
  81. {
  82. scanner.SolutionDNAs = _forensicsSystem.GetSolutionsDNA(args.Args.Target.Value);
  83. } else
  84. {
  85. scanner.SolutionDNAs = new();
  86. }
  87. scanner.LastScannedName = MetaData(args.Args.Target.Value).EntityName;
  88. }
  89. OpenUserInterface(args.Args.User, (uid, scanner));
  90. }
  91. /// <remarks>
  92. /// Hosts logic common between OnUtilityVerb and OnAfterInteract.
  93. /// </remarks>
  94. private void StartScan(EntityUid uid, ForensicScannerComponent component, EntityUid user, EntityUid target)
  95. {
  96. _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.ScanDelay, new ForensicScannerDoAfterEvent(), uid, target: target, used: uid)
  97. {
  98. BreakOnMove = true,
  99. NeedHand = true
  100. });
  101. }
  102. private void OnUtilityVerb(EntityUid uid, ForensicScannerComponent component, GetVerbsEvent<UtilityVerb> args)
  103. {
  104. if (!args.CanInteract || !args.CanAccess || component.CancelToken != null)
  105. return;
  106. var verb = new UtilityVerb()
  107. {
  108. Act = () => StartScan(uid, component, args.User, args.Target),
  109. IconEntity = GetNetEntity(uid),
  110. Text = Loc.GetString("forensic-scanner-verb-text"),
  111. Message = Loc.GetString("forensic-scanner-verb-message")
  112. };
  113. args.Verbs.Add(verb);
  114. }
  115. private void OnAfterInteract(EntityUid uid, ForensicScannerComponent component, AfterInteractEvent args)
  116. {
  117. if (component.CancelToken != null || args.Target == null || !args.CanReach)
  118. return;
  119. StartScan(uid, component, args.User, args.Target.Value);
  120. }
  121. private void OnAfterInteractUsing(EntityUid uid, ForensicScannerComponent component, AfterInteractUsingEvent args)
  122. {
  123. if (args.Handled || !args.CanReach)
  124. return;
  125. if (!TryComp<ForensicPadComponent>(args.Used, out var pad))
  126. return;
  127. foreach (var fiber in component.Fibers)
  128. {
  129. if (fiber == pad.Sample)
  130. {
  131. _audioSystem.PlayPvs(component.SoundMatch, uid);
  132. _popupSystem.PopupEntity(Loc.GetString("forensic-scanner-match-fiber"), uid, args.User);
  133. return;
  134. }
  135. }
  136. foreach (var fingerprint in component.Fingerprints)
  137. {
  138. if (fingerprint == pad.Sample)
  139. {
  140. _audioSystem.PlayPvs(component.SoundMatch, uid);
  141. _popupSystem.PopupEntity(Loc.GetString("forensic-scanner-match-fingerprint"), uid, args.User);
  142. return;
  143. }
  144. }
  145. _audioSystem.PlayPvs(component.SoundNoMatch, uid);
  146. _popupSystem.PopupEntity(Loc.GetString("forensic-scanner-match-none"), uid, args.User);
  147. }
  148. private void OnBeforeActivatableUIOpen(EntityUid uid, ForensicScannerComponent component, BeforeActivatableUIOpenEvent args)
  149. {
  150. UpdateUserInterface(uid, component);
  151. }
  152. private void OpenUserInterface(EntityUid user, Entity<ForensicScannerComponent> scanner)
  153. {
  154. UpdateUserInterface(scanner, scanner.Comp);
  155. _uiSystem.OpenUi(scanner.Owner, ForensicScannerUiKey.Key, user);
  156. }
  157. private void OnPrint(EntityUid uid, ForensicScannerComponent component, ForensicScannerPrintMessage args)
  158. {
  159. var user = args.Actor;
  160. if (_gameTiming.CurTime < component.PrintReadyAt)
  161. {
  162. // This shouldn't occur due to the UI guarding against it, but
  163. // if it does, tell the user why nothing happened.
  164. _popupSystem.PopupEntity(Loc.GetString("forensic-scanner-printer-not-ready"), uid, user);
  165. return;
  166. }
  167. // Spawn a piece of paper.
  168. var printed = EntityManager.SpawnEntity(component.MachineOutput, Transform(uid).Coordinates);
  169. _handsSystem.PickupOrDrop(args.Actor, printed, checkActionBlocker: false);
  170. if (!TryComp<PaperComponent>(printed, out var paperComp))
  171. {
  172. Log.Error("Printed paper did not have PaperComponent.");
  173. return;
  174. }
  175. _metaData.SetEntityName(printed, Loc.GetString("forensic-scanner-report-title", ("entity", component.LastScannedName)));
  176. var text = new StringBuilder();
  177. text.AppendLine(Loc.GetString("forensic-scanner-interface-fingerprints"));
  178. foreach (var fingerprint in component.Fingerprints)
  179. {
  180. text.AppendLine(fingerprint);
  181. }
  182. text.AppendLine();
  183. text.AppendLine(Loc.GetString("forensic-scanner-interface-fibers"));
  184. foreach (var fiber in component.Fibers)
  185. {
  186. text.AppendLine(fiber);
  187. }
  188. text.AppendLine();
  189. text.AppendLine(Loc.GetString("forensic-scanner-interface-dnas"));
  190. foreach (var dna in component.TouchDNAs)
  191. {
  192. text.AppendLine(dna);
  193. }
  194. foreach (var dna in component.SolutionDNAs)
  195. {
  196. Log.Debug(dna);
  197. if (component.TouchDNAs.Contains(dna))
  198. continue;
  199. text.AppendLine(dna);
  200. }
  201. text.AppendLine();
  202. text.AppendLine(Loc.GetString("forensic-scanner-interface-residues"));
  203. foreach (var residue in component.Residues)
  204. {
  205. text.AppendLine(residue);
  206. }
  207. _paperSystem.SetContent((printed, paperComp), text.ToString());
  208. _audioSystem.PlayPvs(component.SoundPrint, uid,
  209. AudioParams.Default
  210. .WithVariation(0.25f)
  211. .WithVolume(3f)
  212. .WithRolloffFactor(2.8f)
  213. .WithMaxDistance(4.5f));
  214. component.PrintReadyAt = _gameTiming.CurTime + component.PrintCooldown;
  215. }
  216. private void OnClear(EntityUid uid, ForensicScannerComponent component, ForensicScannerClearMessage args)
  217. {
  218. component.Fingerprints = new();
  219. component.Fibers = new();
  220. component.TouchDNAs = new();
  221. component.SolutionDNAs = new();
  222. component.LastScannedName = string.Empty;
  223. UpdateUserInterface(uid, component);
  224. }
  225. }
  226. }