SharedCameraRecoilSystem.cs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. using System.Numerics;
  2. using Content.Shared.Movement.Components;
  3. using Content.Shared.Movement.Systems;
  4. using JetBrains.Annotations;
  5. using Robust.Shared.Network;
  6. using Robust.Shared.Serialization;
  7. namespace Content.Shared.Camera;
  8. [UsedImplicitly]
  9. public abstract class SharedCameraRecoilSystem : EntitySystem
  10. {
  11. /// <summary>
  12. /// Maximum rate of magnitude restore towards 0 kick.
  13. /// </summary>
  14. private const float RestoreRateMax = 30f;
  15. /// <summary>
  16. /// Minimum rate of magnitude restore towards 0 kick.
  17. /// </summary>
  18. private const float RestoreRateMin = 0.1f;
  19. /// <summary>
  20. /// Time in seconds since the last kick that lerps RestoreRateMin and RestoreRateMax
  21. /// </summary>
  22. private const float RestoreRateRamp = 4f;
  23. /// <summary>
  24. /// The maximum magnitude of the kick applied to the camera at any point.
  25. /// </summary>
  26. protected const float KickMagnitudeMax = 1f;
  27. [Dependency] private readonly SharedContentEyeSystem _eye = default!;
  28. [Dependency] private readonly INetManager _net = default!;
  29. public override void Initialize()
  30. {
  31. SubscribeLocalEvent<CameraRecoilComponent, GetEyeOffsetEvent>(OnCameraRecoilGetEyeOffset);
  32. }
  33. private void OnCameraRecoilGetEyeOffset(Entity<CameraRecoilComponent> ent, ref GetEyeOffsetEvent args)
  34. {
  35. args.Offset += ent.Comp.BaseOffset + ent.Comp.CurrentKick;
  36. }
  37. /// <summary>
  38. /// Applies explosion/recoil/etc kickback to the view of the entity.
  39. /// </summary>
  40. /// <remarks>
  41. /// If the entity is missing <see cref="CameraRecoilComponent" /> and/or <see cref="EyeComponent" />,
  42. /// this call will have no effect. It is safe to call this function on any entity.
  43. /// </remarks>
  44. public abstract void KickCamera(EntityUid euid, Vector2 kickback, CameraRecoilComponent? component = null);
  45. private void UpdateEyes(float frameTime)
  46. {
  47. var query = AllEntityQuery<CameraRecoilComponent, EyeComponent>();
  48. while (query.MoveNext(out var uid, out var recoil, out var eye))
  49. {
  50. var magnitude = recoil.CurrentKick.Length();
  51. if (magnitude <= 0.005f)
  52. {
  53. recoil.CurrentKick = Vector2.Zero;
  54. }
  55. else // Continually restore camera to 0.
  56. {
  57. var normalized = recoil.CurrentKick.Normalized();
  58. recoil.LastKickTime += frameTime;
  59. var restoreRate = MathHelper.Lerp(RestoreRateMin, RestoreRateMax, Math.Min(1, recoil.LastKickTime / RestoreRateRamp));
  60. var restore = normalized * restoreRate * frameTime;
  61. var (x, y) = recoil.CurrentKick - restore;
  62. if (Math.Sign(x) != Math.Sign(recoil.CurrentKick.X))
  63. x = 0;
  64. if (Math.Sign(y) != Math.Sign(recoil.CurrentKick.Y))
  65. y = 0;
  66. recoil.CurrentKick = new Vector2(x, y);
  67. }
  68. if (recoil.CurrentKick == recoil.LastKick)
  69. continue;
  70. recoil.LastKick = recoil.CurrentKick;
  71. _eye.UpdateEyeOffset((uid, eye));
  72. }
  73. }
  74. public override void Update(float frameTime)
  75. {
  76. if (_net.IsServer)
  77. UpdateEyes(frameTime);
  78. }
  79. public override void FrameUpdate(float frameTime)
  80. {
  81. UpdateEyes(frameTime);
  82. }
  83. }
  84. [Serializable]
  85. [NetSerializable]
  86. public sealed class CameraKickEvent : EntityEventArgs
  87. {
  88. public readonly NetEntity NetEntity;
  89. public readonly Vector2 Recoil;
  90. public CameraKickEvent(NetEntity netEntity, Vector2 recoil)
  91. {
  92. Recoil = recoil;
  93. NetEntity = netEntity;
  94. }
  95. }