SpeedModifierContactsSystem.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. using Content.Shared.Inventory;
  2. using Content.Shared.Movement.Components;
  3. using Content.Shared.Movement.Events;
  4. using Content.Shared.Slippery;
  5. using Content.Shared.Whitelist;
  6. using Robust.Shared.Physics.Components;
  7. using Robust.Shared.Physics.Events;
  8. using Robust.Shared.Physics.Systems;
  9. namespace Content.Shared.Movement.Systems;
  10. public sealed class SpeedModifierContactsSystem : EntitySystem
  11. {
  12. [Dependency] private readonly SharedPhysicsSystem _physics = default!;
  13. [Dependency] private readonly MovementSpeedModifierSystem _speedModifierSystem = default!;
  14. [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
  15. // TODO full-game-save
  16. // Either these need to be processed before a map is saved, or slowed/slowing entities need to update on init.
  17. private HashSet<EntityUid> _toUpdate = new();
  18. private HashSet<EntityUid> _toRemove = new();
  19. public override void Initialize()
  20. {
  21. base.Initialize();
  22. SubscribeLocalEvent<SpeedModifierContactsComponent, StartCollideEvent>(OnEntityEnter);
  23. SubscribeLocalEvent<SpeedModifierContactsComponent, EndCollideEvent>(OnEntityExit);
  24. SubscribeLocalEvent<SpeedModifiedByContactComponent, RefreshMovementSpeedModifiersEvent>(MovementSpeedCheck);
  25. SubscribeLocalEvent<SpeedModifierContactsComponent, ComponentShutdown>(OnShutdown);
  26. UpdatesAfter.Add(typeof(SharedPhysicsSystem));
  27. }
  28. public override void Update(float frameTime)
  29. {
  30. base.Update(frameTime);
  31. _toRemove.Clear();
  32. foreach (var ent in _toUpdate)
  33. {
  34. _speedModifierSystem.RefreshMovementSpeedModifiers(ent);
  35. }
  36. foreach (var ent in _toRemove)
  37. {
  38. RemComp<SpeedModifiedByContactComponent>(ent);
  39. }
  40. _toUpdate.Clear();
  41. }
  42. public void ChangeModifiers(EntityUid uid, float speed, SpeedModifierContactsComponent? component = null)
  43. {
  44. ChangeModifiers(uid, speed, speed, component);
  45. }
  46. public void ChangeModifiers(EntityUid uid, float walkSpeed, float sprintSpeed, SpeedModifierContactsComponent? component = null)
  47. {
  48. if (!Resolve(uid, ref component))
  49. {
  50. return;
  51. }
  52. component.WalkSpeedModifier = walkSpeed;
  53. component.SprintSpeedModifier = sprintSpeed;
  54. Dirty(uid, component);
  55. _toUpdate.UnionWith(_physics.GetContactingEntities(uid));
  56. }
  57. private void OnShutdown(EntityUid uid, SpeedModifierContactsComponent component, ComponentShutdown args)
  58. {
  59. if (!TryComp(uid, out PhysicsComponent? phys))
  60. return;
  61. // Note that the entity may not be getting deleted here. E.g., glue puddles.
  62. _toUpdate.UnionWith(_physics.GetContactingEntities(uid, phys));
  63. }
  64. private void MovementSpeedCheck(EntityUid uid, SpeedModifiedByContactComponent component, RefreshMovementSpeedModifiersEvent args)
  65. {
  66. if (!EntityManager.TryGetComponent<PhysicsComponent>(uid, out var physicsComponent))
  67. return;
  68. var walkSpeed = 0.0f;
  69. var sprintSpeed = 0.0f;
  70. bool remove = true;
  71. var entries = 0;
  72. foreach (var ent in _physics.GetContactingEntities(uid, physicsComponent))
  73. {
  74. bool speedModified = false;
  75. if (TryComp<SpeedModifierContactsComponent>(ent, out var slowContactsComponent))
  76. {
  77. if (_whitelistSystem.IsWhitelistPass(slowContactsComponent.IgnoreWhitelist, uid))
  78. continue;
  79. walkSpeed += slowContactsComponent.WalkSpeedModifier;
  80. sprintSpeed += slowContactsComponent.SprintSpeedModifier;
  81. speedModified = true;
  82. }
  83. // SpeedModifierContactsComponent takes priority over SlowedOverSlipperyComponent, effectively overriding the slippery slow.
  84. if (TryComp<SlipperyComponent>(ent, out var slipperyComponent) && speedModified == false)
  85. {
  86. var evSlippery = new GetSlowedOverSlipperyModifierEvent();
  87. RaiseLocalEvent(uid, ref evSlippery);
  88. if (evSlippery.SlowdownModifier != 1)
  89. {
  90. walkSpeed += evSlippery.SlowdownModifier;
  91. sprintSpeed += evSlippery.SlowdownModifier;
  92. speedModified = true;
  93. }
  94. }
  95. if (speedModified)
  96. {
  97. remove = false;
  98. entries++;
  99. }
  100. }
  101. if (entries > 0)
  102. {
  103. walkSpeed /= entries;
  104. sprintSpeed /= entries;
  105. var evMax = new GetSpeedModifierContactCapEvent();
  106. RaiseLocalEvent(uid, ref evMax);
  107. walkSpeed = MathF.Max(walkSpeed, evMax.MaxWalkSlowdown);
  108. sprintSpeed = MathF.Max(sprintSpeed, evMax.MaxSprintSlowdown);
  109. args.ModifySpeed(walkSpeed, sprintSpeed);
  110. }
  111. // no longer colliding with anything
  112. if (remove)
  113. _toRemove.Add(uid);
  114. }
  115. private void OnEntityExit(EntityUid uid, SpeedModifierContactsComponent component, ref EndCollideEvent args)
  116. {
  117. var otherUid = args.OtherEntity;
  118. _toUpdate.Add(otherUid);
  119. }
  120. private void OnEntityEnter(EntityUid uid, SpeedModifierContactsComponent component, ref StartCollideEvent args)
  121. {
  122. AddModifiedEntity(args.OtherEntity);
  123. }
  124. /// <summary>
  125. /// Add an entity to be checked for speed modification from contact with another entity.
  126. /// </summary>
  127. /// <param name="uid">The entity to be added.</param>
  128. public void AddModifiedEntity(EntityUid uid)
  129. {
  130. if (!HasComp<MovementSpeedModifierComponent>(uid))
  131. return;
  132. EnsureComp<SpeedModifiedByContactComponent>(uid);
  133. _toUpdate.Add(uid);
  134. }
  135. }