PaperSystem.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. using System.Linq;
  2. using Content.Shared.Administration.Logs;
  3. using Content.Shared.UserInterface;
  4. using Content.Shared.Database;
  5. using Content.Shared.Examine;
  6. using Content.Shared.Interaction;
  7. using Content.Shared.Popups;
  8. using Content.Shared.Tag;
  9. using Robust.Shared.Player;
  10. using Robust.Shared.Audio.Systems;
  11. using static Content.Shared.Paper.PaperComponent;
  12. namespace Content.Shared.Paper;
  13. public sealed class PaperSystem : EntitySystem
  14. {
  15. [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
  16. [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
  17. [Dependency] private readonly SharedInteractionSystem _interaction = default!;
  18. [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
  19. [Dependency] private readonly TagSystem _tagSystem = default!;
  20. [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
  21. [Dependency] private readonly MetaDataSystem _metaSystem = default!;
  22. [Dependency] private readonly SharedAudioSystem _audio = default!;
  23. public override void Initialize()
  24. {
  25. base.Initialize();
  26. SubscribeLocalEvent<PaperComponent, MapInitEvent>(OnMapInit);
  27. SubscribeLocalEvent<PaperComponent, ComponentInit>(OnInit);
  28. SubscribeLocalEvent<PaperComponent, BeforeActivatableUIOpenEvent>(BeforeUIOpen);
  29. SubscribeLocalEvent<PaperComponent, ExaminedEvent>(OnExamined);
  30. SubscribeLocalEvent<PaperComponent, InteractUsingEvent>(OnInteractUsing);
  31. SubscribeLocalEvent<PaperComponent, PaperInputTextMessage>(OnInputTextMessage);
  32. SubscribeLocalEvent<ActivateOnPaperOpenedComponent, PaperWriteEvent>(OnPaperWrite);
  33. }
  34. private void OnMapInit(Entity<PaperComponent> entity, ref MapInitEvent args)
  35. {
  36. if (!string.IsNullOrEmpty(entity.Comp.Content))
  37. {
  38. SetContent(entity, Loc.GetString(entity.Comp.Content));
  39. }
  40. }
  41. private void OnInit(Entity<PaperComponent> entity, ref ComponentInit args)
  42. {
  43. entity.Comp.Mode = PaperAction.Read;
  44. UpdateUserInterface(entity);
  45. if (TryComp<AppearanceComponent>(entity, out var appearance))
  46. {
  47. if (entity.Comp.Content != "")
  48. _appearance.SetData(entity, PaperVisuals.Status, PaperStatus.Written, appearance);
  49. if (entity.Comp.StampState != null)
  50. _appearance.SetData(entity, PaperVisuals.Stamp, entity.Comp.StampState, appearance);
  51. }
  52. }
  53. private void BeforeUIOpen(Entity<PaperComponent> entity, ref BeforeActivatableUIOpenEvent args)
  54. {
  55. entity.Comp.Mode = PaperAction.Read;
  56. UpdateUserInterface(entity);
  57. }
  58. private void OnExamined(Entity<PaperComponent> entity, ref ExaminedEvent args)
  59. {
  60. if (!args.IsInDetailsRange)
  61. return;
  62. using (args.PushGroup(nameof(PaperComponent)))
  63. {
  64. if (entity.Comp.Content != "")
  65. {
  66. args.PushMarkup(
  67. Loc.GetString(
  68. "paper-component-examine-detail-has-words",
  69. ("paper", entity)
  70. )
  71. );
  72. }
  73. if (entity.Comp.StampedBy.Count > 0)
  74. {
  75. var commaSeparated =
  76. string.Join(", ", entity.Comp.StampedBy.Select(s => Loc.GetString(s.StampedName)));
  77. args.PushMarkup(
  78. Loc.GetString(
  79. "paper-component-examine-detail-stamped-by",
  80. ("paper", entity),
  81. ("stamps", commaSeparated))
  82. );
  83. }
  84. }
  85. }
  86. private void OnInteractUsing(Entity<PaperComponent> entity, ref InteractUsingEvent args)
  87. {
  88. // only allow editing if there are no stamps or when using a cyberpen
  89. var editable = entity.Comp.StampedBy.Count == 0 || _tagSystem.HasTag(args.Used, "WriteIgnoreStamps");
  90. if (_tagSystem.HasTag(args.Used, "Write"))
  91. {
  92. if (editable)
  93. {
  94. if (entity.Comp.EditingDisabled)
  95. {
  96. var paperEditingDisabledMessage = Loc.GetString("paper-tamper-proof-modified-message");
  97. _popupSystem.PopupEntity(paperEditingDisabledMessage, entity, args.User);
  98. args.Handled = true;
  99. return;
  100. }
  101. var ev = new PaperWriteAttemptEvent(entity.Owner);
  102. RaiseLocalEvent(args.User, ref ev);
  103. if (ev.Cancelled)
  104. {
  105. if (ev.FailReason is not null)
  106. {
  107. var fileWriteMessage = Loc.GetString(ev.FailReason);
  108. _popupSystem.PopupClient(fileWriteMessage, entity.Owner, args.User);
  109. }
  110. args.Handled = true;
  111. return;
  112. }
  113. var writeEvent = new PaperWriteEvent(args.User, entity);
  114. RaiseLocalEvent(args.Used, ref writeEvent);
  115. entity.Comp.Mode = PaperAction.Write;
  116. _uiSystem.OpenUi(entity.Owner, PaperUiKey.Key, args.User);
  117. UpdateUserInterface(entity);
  118. }
  119. args.Handled = true;
  120. return;
  121. }
  122. // If a stamp, attempt to stamp paper
  123. if (TryComp<StampComponent>(args.Used, out var stampComp) && TryStamp(entity, GetStampInfo(stampComp), stampComp.StampState))
  124. {
  125. // successfully stamped, play popup
  126. var stampPaperOtherMessage = Loc.GetString("paper-component-action-stamp-paper-other",
  127. ("user", args.User),
  128. ("target", args.Target),
  129. ("stamp", args.Used));
  130. _popupSystem.PopupEntity(stampPaperOtherMessage, args.User, Filter.PvsExcept(args.User, entityManager: EntityManager), true);
  131. var stampPaperSelfMessage = Loc.GetString("paper-component-action-stamp-paper-self",
  132. ("target", args.Target),
  133. ("stamp", args.Used));
  134. _popupSystem.PopupClient(stampPaperSelfMessage, args.User, args.User);
  135. _audio.PlayPredicted(stampComp.Sound, entity, args.User);
  136. UpdateUserInterface(entity);
  137. }
  138. }
  139. private static StampDisplayInfo GetStampInfo(StampComponent stamp)
  140. {
  141. return new StampDisplayInfo
  142. {
  143. StampedName = stamp.StampedName,
  144. StampedColor = stamp.StampedColor
  145. };
  146. }
  147. private void OnInputTextMessage(Entity<PaperComponent> entity, ref PaperInputTextMessage args)
  148. {
  149. var ev = new PaperWriteAttemptEvent(entity.Owner);
  150. RaiseLocalEvent(args.Actor, ref ev);
  151. if (ev.Cancelled)
  152. return;
  153. if (args.Text.Length <= entity.Comp.ContentSize)
  154. {
  155. SetContent(entity, args.Text);
  156. if (TryComp<AppearanceComponent>(entity, out var appearance))
  157. _appearance.SetData(entity, PaperVisuals.Status, PaperStatus.Written, appearance);
  158. if (TryComp(entity, out MetaDataComponent? meta))
  159. _metaSystem.SetEntityDescription(entity, "", meta);
  160. _adminLogger.Add(LogType.Chat,
  161. LogImpact.Low,
  162. $"{ToPrettyString(args.Actor):player} has written on {ToPrettyString(entity):entity} the following text: {args.Text}");
  163. _audio.PlayPvs(entity.Comp.Sound, entity);
  164. }
  165. entity.Comp.Mode = PaperAction.Read;
  166. UpdateUserInterface(entity);
  167. }
  168. private void OnPaperWrite(Entity<ActivateOnPaperOpenedComponent> entity, ref PaperWriteEvent args)
  169. {
  170. _interaction.UseInHandInteraction(args.User, entity);
  171. }
  172. /// <summary>
  173. /// Accepts the name and state to be stamped onto the paper, returns true if successful.
  174. /// </summary>
  175. public bool TryStamp(Entity<PaperComponent> entity, StampDisplayInfo stampInfo, string spriteStampState)
  176. {
  177. if (!entity.Comp.StampedBy.Contains(stampInfo))
  178. {
  179. entity.Comp.StampedBy.Add(stampInfo);
  180. Dirty(entity);
  181. if (entity.Comp.StampState == null && TryComp<AppearanceComponent>(entity, out var appearance))
  182. {
  183. entity.Comp.StampState = spriteStampState;
  184. // Would be nice to be able to display multiple sprites on the paper
  185. // but most of the existing images overlap
  186. _appearance.SetData(entity, PaperVisuals.Stamp, entity.Comp.StampState, appearance);
  187. }
  188. }
  189. return true;
  190. }
  191. public void SetContent(Entity<PaperComponent> entity, string content)
  192. {
  193. entity.Comp.Content = content;
  194. Dirty(entity);
  195. UpdateUserInterface(entity);
  196. if (!TryComp<AppearanceComponent>(entity, out var appearance))
  197. return;
  198. var status = string.IsNullOrWhiteSpace(content)
  199. ? PaperStatus.Blank
  200. : PaperStatus.Written;
  201. _appearance.SetData(entity, PaperVisuals.Status, status, appearance);
  202. }
  203. private void UpdateUserInterface(Entity<PaperComponent> entity)
  204. {
  205. _uiSystem.SetUiState(entity.Owner, PaperUiKey.Key, new PaperBoundUserInterfaceState(entity.Comp.Content, entity.Comp.StampedBy, entity.Comp.Mode));
  206. }
  207. }
  208. /// <summary>
  209. /// Event fired when using a pen on paper, opening the UI.
  210. /// </summary>
  211. [ByRefEvent]
  212. public record struct PaperWriteEvent(EntityUid User, EntityUid Paper);
  213. /// <summary>
  214. /// Cancellable event for attempting to write on a piece of paper.
  215. /// </summary>
  216. /// <param name="paper">The paper that the writing will take place on.</param>
  217. [ByRefEvent]
  218. public record struct PaperWriteAttemptEvent(EntityUid Paper, string? FailReason = null, bool Cancelled = false);