RotatableSystem.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. using Content.Server.Popups;
  2. using Content.Shared.ActionBlocker;
  3. using Content.Shared.Input;
  4. using Content.Shared.Interaction;
  5. using Content.Shared.Rotatable;
  6. using Content.Shared.Verbs;
  7. using Robust.Shared.Input.Binding;
  8. using Robust.Shared.Map;
  9. using Robust.Shared.Player;
  10. using Robust.Shared.Physics;
  11. using Robust.Shared.Physics.Components;
  12. using Robust.Shared.Utility;
  13. namespace Content.Server.Rotatable
  14. {
  15. /// <summary>
  16. /// Handles verbs for the <see cref="RotatableComponent"/> and <see cref="FlippableComponent"/> components.
  17. /// </summary>
  18. public sealed class RotatableSystem : EntitySystem
  19. {
  20. [Dependency] private readonly PopupSystem _popup = default!;
  21. [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
  22. [Dependency] private readonly SharedInteractionSystem _interaction = default!;
  23. public override void Initialize()
  24. {
  25. SubscribeLocalEvent<FlippableComponent, GetVerbsEvent<Verb>>(AddFlipVerb);
  26. SubscribeLocalEvent<RotatableComponent, GetVerbsEvent<Verb>>(AddRotateVerbs);
  27. CommandBinds.Builder
  28. .Bind(ContentKeyFunctions.RotateObjectClockwise, new PointerInputCmdHandler(HandleRotateObjectClockwise))
  29. .Bind(ContentKeyFunctions.RotateObjectCounterclockwise, new PointerInputCmdHandler(HandleRotateObjectCounterclockwise))
  30. .Bind(ContentKeyFunctions.FlipObject, new PointerInputCmdHandler(HandleFlipObject))
  31. .Register<RotatableSystem>();
  32. }
  33. private void AddFlipVerb(EntityUid uid, FlippableComponent component, GetVerbsEvent<Verb> args)
  34. {
  35. if (!args.CanAccess || !args.CanInteract)
  36. return;
  37. // Check if the object is anchored.
  38. if (EntityManager.TryGetComponent(uid, out PhysicsComponent? physics) && physics.BodyType == BodyType.Static)
  39. return;
  40. Verb verb = new()
  41. {
  42. Act = () => Flip(uid, component),
  43. Text = Loc.GetString("flippable-verb-get-data-text"),
  44. Category = VerbCategory.Rotate,
  45. Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/flip.svg.192dpi.png")),
  46. Priority = -3, // show flip last
  47. DoContactInteraction = true
  48. };
  49. args.Verbs.Add(verb);
  50. }
  51. private void AddRotateVerbs(EntityUid uid, RotatableComponent component, GetVerbsEvent<Verb> args)
  52. {
  53. if (!args.CanAccess
  54. || !args.CanInteract
  55. || Transform(uid).NoLocalRotation) // Good ol prototype inheritance, eh?
  56. return;
  57. // Check if the object is anchored, and whether we are still allowed to rotate it.
  58. if (!component.RotateWhileAnchored &&
  59. EntityManager.TryGetComponent(uid, out PhysicsComponent? physics) &&
  60. physics.BodyType == BodyType.Static)
  61. return;
  62. Verb resetRotation = new()
  63. {
  64. DoContactInteraction = true,
  65. Act = () => EntityManager.GetComponent<TransformComponent>(uid).LocalRotation = Angle.Zero,
  66. Category = VerbCategory.Rotate,
  67. Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/refresh.svg.192dpi.png")),
  68. Text = "Reset",
  69. Priority = -2, // show CCW, then CW, then reset
  70. CloseMenu = false,
  71. };
  72. args.Verbs.Add(resetRotation);
  73. // rotate clockwise
  74. Verb rotateCW = new()
  75. {
  76. Act = () => EntityManager.GetComponent<TransformComponent>(uid).LocalRotation -= component.Increment,
  77. Category = VerbCategory.Rotate,
  78. Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/rotate_cw.svg.192dpi.png")),
  79. Priority = -1,
  80. CloseMenu = false, // allow for easy double rotations.
  81. };
  82. args.Verbs.Add(rotateCW);
  83. // rotate counter-clockwise
  84. Verb rotateCCW = new()
  85. {
  86. Act = () => EntityManager.GetComponent<TransformComponent>(uid).LocalRotation += component.Increment,
  87. Category = VerbCategory.Rotate,
  88. Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/rotate_ccw.svg.192dpi.png")),
  89. Priority = 0,
  90. CloseMenu = false, // allow for easy double rotations.
  91. };
  92. args.Verbs.Add(rotateCCW);
  93. }
  94. /// <summary>
  95. /// Replace a flippable entity with it's flipped / mirror-symmetric entity.
  96. /// </summary>
  97. public void Flip(EntityUid uid, FlippableComponent component)
  98. {
  99. var oldTransform = EntityManager.GetComponent<TransformComponent>(uid);
  100. var entity = EntityManager.SpawnEntity(component.MirrorEntity, oldTransform.Coordinates);
  101. var newTransform = EntityManager.GetComponent<TransformComponent>(entity);
  102. newTransform.LocalRotation = oldTransform.LocalRotation;
  103. newTransform.Anchored = false;
  104. EntityManager.DeleteEntity(uid);
  105. }
  106. public bool HandleRotateObjectClockwise(ICommonSession? playerSession, EntityCoordinates coordinates, EntityUid entity)
  107. {
  108. if (playerSession?.AttachedEntity is not { Valid: true } player || !Exists(player))
  109. return false;
  110. if (!TryComp<RotatableComponent>(entity, out var rotatableComp))
  111. return false;
  112. if (!_actionBlocker.CanInteract(player, entity) || !_interaction.InRangeAndAccessible(player, entity))
  113. return false;
  114. // Check if the object is anchored, and whether we are still allowed to rotate it.
  115. if (!rotatableComp.RotateWhileAnchored && EntityManager.TryGetComponent(entity, out PhysicsComponent? physics) &&
  116. physics.BodyType == BodyType.Static)
  117. {
  118. _popup.PopupEntity(Loc.GetString("rotatable-component-try-rotate-stuck"), entity, player);
  119. return false;
  120. }
  121. Transform(entity).LocalRotation -= rotatableComp.Increment;
  122. return true;
  123. }
  124. public bool HandleRotateObjectCounterclockwise(ICommonSession? playerSession, EntityCoordinates coordinates, EntityUid entity)
  125. {
  126. if (playerSession?.AttachedEntity is not { Valid: true } player || !Exists(player))
  127. return false;
  128. if (!TryComp<RotatableComponent>(entity, out var rotatableComp))
  129. return false;
  130. if (!_actionBlocker.CanInteract(player, entity) || !_interaction.InRangeAndAccessible(player, entity))
  131. return false;
  132. // Check if the object is anchored, and whether we are still allowed to rotate it.
  133. if (!rotatableComp.RotateWhileAnchored && EntityManager.TryGetComponent(entity, out PhysicsComponent? physics) &&
  134. physics.BodyType == BodyType.Static)
  135. {
  136. _popup.PopupEntity(Loc.GetString("rotatable-component-try-rotate-stuck"), entity, player);
  137. return false;
  138. }
  139. Transform(entity).LocalRotation += rotatableComp.Increment;
  140. return true;
  141. }
  142. public bool HandleFlipObject(ICommonSession? playerSession, EntityCoordinates coordinates, EntityUid entity)
  143. {
  144. if (playerSession?.AttachedEntity is not { Valid: true } player || !Exists(player))
  145. return false;
  146. if (!TryComp<FlippableComponent>(entity, out var flippableComp))
  147. return false;
  148. if (!_actionBlocker.CanInteract(player, entity) || !_interaction.InRangeAndAccessible(player, entity))
  149. return false;
  150. // Check if the object is anchored.
  151. if (EntityManager.TryGetComponent(entity, out PhysicsComponent? physics) && physics.BodyType == BodyType.Static)
  152. {
  153. _popup.PopupEntity(Loc.GetString("flippable-component-try-flip-is-stuck"), entity, player);
  154. return false;
  155. }
  156. Flip(entity, flippableComp);
  157. return true;
  158. }
  159. }
  160. }