PryingSystem.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. using System.Diagnostics.CodeAnalysis;
  2. using Content.Shared.Administration.Logs;
  3. using Content.Shared.Database;
  4. using Content.Shared.DoAfter;
  5. using Content.Shared.Doors.Components;
  6. using Content.Shared.Interaction;
  7. using Content.Shared.Popups;
  8. using Content.Shared.Prying.Components;
  9. using Content.Shared.Verbs;
  10. using Robust.Shared.Audio.Systems;
  11. using Robust.Shared.Serialization;
  12. using PryUnpoweredComponent = Content.Shared.Prying.Components.PryUnpoweredComponent;
  13. namespace Content.Shared.Prying.Systems;
  14. /// <summary>
  15. /// Handles prying of entities (e.g. doors)
  16. /// </summary>
  17. public sealed class PryingSystem : EntitySystem
  18. {
  19. [Dependency] private readonly ISharedAdminLogManager _adminLog = default!;
  20. [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
  21. [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
  22. [Dependency] private readonly SharedPopupSystem _popup = default!;
  23. public override void Initialize()
  24. {
  25. base.Initialize();
  26. // Mob prying doors
  27. SubscribeLocalEvent<DoorComponent, GetVerbsEvent<AlternativeVerb>>(OnDoorAltVerb);
  28. SubscribeLocalEvent<DoorComponent, DoorPryDoAfterEvent>(OnDoAfter);
  29. SubscribeLocalEvent<DoorComponent, InteractUsingEvent>(TryPryDoor);
  30. }
  31. private void TryPryDoor(EntityUid uid, DoorComponent comp, InteractUsingEvent args)
  32. {
  33. if (args.Handled)
  34. return;
  35. args.Handled = TryPry(uid, args.User, out _, args.Used);
  36. }
  37. private void OnDoorAltVerb(EntityUid uid, DoorComponent component, GetVerbsEvent<AlternativeVerb> args)
  38. {
  39. if (!args.CanInteract || !args.CanAccess)
  40. return;
  41. if (!TryComp<PryingComponent>(args.User, out _))
  42. return;
  43. args.Verbs.Add(new AlternativeVerb()
  44. {
  45. Text = Loc.GetString("door-pry"),
  46. Impact = LogImpact.Low,
  47. Act = () => TryPry(uid, args.User, out _, args.User),
  48. });
  49. }
  50. /// <summary>
  51. /// Attempt to pry an entity.
  52. /// </summary>
  53. public bool TryPry(EntityUid target, EntityUid user, out DoAfterId? id, EntityUid tool)
  54. {
  55. id = null;
  56. PryingComponent? comp = null;
  57. if (!Resolve(tool, ref comp, false))
  58. return false;
  59. if (!comp.Enabled)
  60. return false;
  61. if (!CanPry(target, user, out var message, comp))
  62. {
  63. if (!string.IsNullOrWhiteSpace(message))
  64. _popup.PopupClient(Loc.GetString(message), target, user);
  65. // If we have reached this point we want the event that caused this
  66. // to be marked as handled.
  67. return true;
  68. }
  69. StartPry(target, user, tool, comp.SpeedModifier, out id);
  70. return true;
  71. }
  72. /// <summary>
  73. /// Try to pry an entity.
  74. /// </summary>
  75. public bool TryPry(EntityUid target, EntityUid user, out DoAfterId? id)
  76. {
  77. id = null;
  78. // We don't care about displaying a message if no tool was used.
  79. if (!TryComp<PryUnpoweredComponent>(target, out var unpoweredComp) || !CanPry(target, user, out _, unpoweredComp: unpoweredComp))
  80. // If we have reached this point we want the event that caused this
  81. // to be marked as handled.
  82. return true;
  83. // hand-prying is much slower
  84. var modifier = CompOrNull<PryingComponent>(user)?.SpeedModifier ?? unpoweredComp.PryModifier;
  85. return StartPry(target, user, null, modifier, out id);
  86. }
  87. private bool CanPry(EntityUid target, EntityUid user, out string? message, PryingComponent? comp = null, PryUnpoweredComponent? unpoweredComp = null)
  88. {
  89. BeforePryEvent canev;
  90. if (comp != null || Resolve(user, ref comp, false))
  91. {
  92. canev = new BeforePryEvent(user, comp.PryPowered, comp.Force, true);
  93. }
  94. else
  95. {
  96. if (!Resolve(target, ref unpoweredComp))
  97. {
  98. message = null;
  99. return false;
  100. }
  101. canev = new BeforePryEvent(user, false, false, false);
  102. }
  103. RaiseLocalEvent(target, ref canev);
  104. message = canev.Message;
  105. return !canev.Cancelled;
  106. }
  107. private bool StartPry(EntityUid target, EntityUid user, EntityUid? tool, float toolModifier, [NotNullWhen(true)] out DoAfterId? id)
  108. {
  109. var modEv = new GetPryTimeModifierEvent(user);
  110. RaiseLocalEvent(target, ref modEv);
  111. var doAfterArgs = new DoAfterArgs(EntityManager, user, TimeSpan.FromSeconds(modEv.BaseTime * modEv.PryTimeModifier / toolModifier), new DoorPryDoAfterEvent(), target, target, tool)
  112. {
  113. BreakOnDamage = true,
  114. BreakOnMove = true,
  115. NeedHand = tool != user,
  116. };
  117. if (tool != user && tool != null)
  118. {
  119. _adminLog.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user)} is using {ToPrettyString(tool.Value)} to pry {ToPrettyString(target)}");
  120. }
  121. else
  122. {
  123. _adminLog.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user)} is prying {ToPrettyString(target)}");
  124. }
  125. return _doAfterSystem.TryStartDoAfter(doAfterArgs, out id);
  126. }
  127. private void OnDoAfter(EntityUid uid, DoorComponent door, DoorPryDoAfterEvent args)
  128. {
  129. if (args.Cancelled)
  130. return;
  131. if (args.Target is null)
  132. return;
  133. TryComp<PryingComponent>(args.Used, out var comp);
  134. if (!CanPry(uid, args.User, out var message, comp))
  135. {
  136. if (!string.IsNullOrWhiteSpace(message))
  137. _popup.PopupClient(Loc.GetString(message), uid, args.User);
  138. return;
  139. }
  140. if (args.Used != null && comp != null)
  141. {
  142. _audioSystem.PlayPredicted(comp.UseSound, args.Used.Value, args.User);
  143. }
  144. var ev = new PriedEvent(args.User);
  145. RaiseLocalEvent(uid, ref ev);
  146. }
  147. }
  148. [Serializable, NetSerializable]
  149. public sealed partial class DoorPryDoAfterEvent : SimpleDoAfterEvent;