HandTeleporterSystem.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. using Content.Server.Administration.Logs;
  2. using Content.Server.Popups;
  3. using Content.Shared.DoAfter;
  4. using Content.Shared.Database;
  5. using Content.Shared.Interaction.Events;
  6. using Content.Shared.Popups;
  7. using Content.Shared.Teleportation.Components;
  8. using Content.Shared.Teleportation.Systems;
  9. using Robust.Server.Audio;
  10. using Robust.Server.GameObjects;
  11. namespace Content.Server.Teleportation;
  12. /// <summary>
  13. /// This handles creating portals from a hand teleporter.
  14. /// </summary>
  15. public sealed class HandTeleporterSystem : EntitySystem
  16. {
  17. [Dependency] private readonly IAdminLogManager _adminLogger = default!;
  18. [Dependency] private readonly LinkedEntitySystem _link = default!;
  19. [Dependency] private readonly AudioSystem _audio = default!;
  20. [Dependency] private readonly SharedDoAfterSystem _doafter = default!;
  21. [Dependency] private readonly PopupSystem _popup = default!;
  22. /// <inheritdoc/>
  23. public override void Initialize()
  24. {
  25. SubscribeLocalEvent<HandTeleporterComponent, UseInHandEvent>(OnUseInHand);
  26. SubscribeLocalEvent<HandTeleporterComponent, TeleporterDoAfterEvent>(OnDoAfter);
  27. }
  28. private void OnDoAfter(EntityUid uid, HandTeleporterComponent component, DoAfterEvent args)
  29. {
  30. if (args.Cancelled || args.Handled)
  31. return;
  32. HandlePortalUpdating(uid, component, args.Args.User);
  33. args.Handled = true;
  34. }
  35. private void OnUseInHand(EntityUid uid, HandTeleporterComponent component, UseInHandEvent args)
  36. {
  37. if (args.Handled)
  38. return;
  39. if (Deleted(component.FirstPortal))
  40. component.FirstPortal = null;
  41. if (Deleted(component.SecondPortal))
  42. component.SecondPortal = null;
  43. if (component.FirstPortal != null && component.SecondPortal != null)
  44. {
  45. // handle removing portals immediately as opposed to a doafter
  46. HandlePortalUpdating(uid, component, args.User);
  47. }
  48. else
  49. {
  50. var xform = Transform(args.User);
  51. if (xform.ParentUid != xform.GridUid)
  52. return;
  53. var doafterArgs = new DoAfterArgs(EntityManager, args.User, component.PortalCreationDelay, new TeleporterDoAfterEvent(), uid, used: uid)
  54. {
  55. BreakOnDamage = true,
  56. BreakOnMove = true,
  57. MovementThreshold = 0.5f,
  58. };
  59. _doafter.TryStartDoAfter(doafterArgs);
  60. }
  61. args.Handled = true;
  62. }
  63. /// <summary>
  64. /// Creates or removes a portal given the state of the hand teleporter.
  65. /// </summary>
  66. private void HandlePortalUpdating(EntityUid uid, HandTeleporterComponent component, EntityUid user)
  67. {
  68. if (Deleted(user))
  69. return;
  70. var xform = Transform(user);
  71. // Create the first portal.
  72. if (Deleted(component.FirstPortal) && Deleted(component.SecondPortal))
  73. {
  74. // don't portal
  75. if (xform.ParentUid != xform.GridUid)
  76. return;
  77. var timeout = EnsureComp<PortalTimeoutComponent>(user);
  78. timeout.EnteredPortal = null;
  79. component.FirstPortal = Spawn(component.FirstPortalPrototype, Transform(user).Coordinates);
  80. _adminLogger.Add(LogType.EntitySpawn, LogImpact.Low, $"{ToPrettyString(user):player} opened {ToPrettyString(component.FirstPortal.Value)} at {Transform(component.FirstPortal.Value).Coordinates} using {ToPrettyString(uid)}");
  81. _audio.PlayPvs(component.NewPortalSound, uid);
  82. }
  83. else if (Deleted(component.SecondPortal))
  84. {
  85. if (xform.ParentUid != xform.GridUid) // Still, don't portal.
  86. return;
  87. if (!component.AllowPortalsOnDifferentGrids && xform.ParentUid != Transform(component.FirstPortal!.Value).ParentUid)
  88. {
  89. // Whoops. Fizzle time. Crime time too because yippee I'm not refactoring this logic right now (I started to, I'm not going to.)
  90. FizzlePortals(uid, component, user, true);
  91. return;
  92. }
  93. var timeout = EnsureComp<PortalTimeoutComponent>(user);
  94. timeout.EnteredPortal = null;
  95. component.SecondPortal = Spawn(component.SecondPortalPrototype, Transform(user).Coordinates);
  96. _adminLogger.Add(LogType.EntitySpawn, LogImpact.Low, $"{ToPrettyString(user):player} opened {ToPrettyString(component.SecondPortal.Value)} at {Transform(component.SecondPortal.Value).Coordinates} linked to {ToPrettyString(component.FirstPortal!.Value)} using {ToPrettyString(uid)}");
  97. _link.TryLink(component.FirstPortal!.Value, component.SecondPortal.Value, true);
  98. _audio.PlayPvs(component.NewPortalSound, uid);
  99. }
  100. else
  101. {
  102. FizzlePortals(uid, component, user, false);
  103. }
  104. }
  105. private void FizzlePortals(EntityUid uid, HandTeleporterComponent component, EntityUid user, bool instability)
  106. {
  107. // Logging
  108. var portalStrings = "";
  109. portalStrings += ToPrettyString(component.FirstPortal);
  110. if (portalStrings != "")
  111. portalStrings += " and ";
  112. portalStrings += ToPrettyString(component.SecondPortal);
  113. if (portalStrings != "")
  114. _adminLogger.Add(LogType.EntityDelete, LogImpact.Low, $"{ToPrettyString(user):player} closed {portalStrings} with {ToPrettyString(uid)}");
  115. // Clear both portals
  116. if (!Deleted(component.FirstPortal))
  117. QueueDel(component.FirstPortal.Value);
  118. if (!Deleted(component.SecondPortal))
  119. QueueDel(component.SecondPortal.Value);
  120. component.FirstPortal = null;
  121. component.SecondPortal = null;
  122. _audio.PlayPvs(component.ClearPortalsSound, uid);
  123. if (instability)
  124. _popup.PopupEntity(Loc.GetString("handheld-teleporter-instability-fizzle"), uid, user, PopupType.MediumCaution);
  125. }
  126. }