using Content.Shared.Movement.Components; using Robust.Shared.GameStates; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; namespace Content.Shared.Movement.Systems; public sealed class SlowContactsSystem : EntitySystem { [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly MovementSpeedModifierSystem _speedModifierSystem = default!; private HashSet _toUpdate = new(); private HashSet _toRemove = new(); public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnEntityEnter); SubscribeLocalEvent(OnEntityExit); SubscribeLocalEvent(MovementSpeedCheck); SubscribeLocalEvent(OnHandleState); SubscribeLocalEvent(OnGetState); UpdatesAfter.Add(typeof(SharedPhysicsSystem)); } public override void Update(float frameTime) { base.Update(frameTime); _toRemove.Clear(); foreach (var ent in _toUpdate) { _speedModifierSystem.RefreshMovementSpeedModifiers(ent); } foreach (var ent in _toRemove) { RemComp(ent); } _toUpdate.Clear(); } private void OnGetState(EntityUid uid, SlowContactsComponent component, ref ComponentGetState args) { args.State = new SlowContactsComponentState(component.WalkSpeedModifier, component.SprintSpeedModifier); } private void OnHandleState(EntityUid uid, SlowContactsComponent component, ref ComponentHandleState args) { if (args.Current is not SlowContactsComponentState state) return; component.WalkSpeedModifier = state.WalkSpeedModifier; component.SprintSpeedModifier = state.SprintSpeedModifier; } private void MovementSpeedCheck(EntityUid uid, SlowedByContactComponent component, RefreshMovementSpeedModifiersEvent args) { if (!EntityManager.TryGetComponent(uid, out var physicsComponent)) return; var walkSpeed = 1.0f; var sprintSpeed = 1.0f; bool remove = true; foreach (var ent in _physics.GetContactingEntities(uid, physicsComponent)) { if (!TryComp(ent, out var slowContactsComponent)) continue; if (slowContactsComponent.IgnoreWhitelist != null && slowContactsComponent.IgnoreWhitelist.IsValid(uid)) continue; walkSpeed = Math.Min(walkSpeed, slowContactsComponent.WalkSpeedModifier); sprintSpeed = Math.Min(sprintSpeed, slowContactsComponent.SprintSpeedModifier); remove = false; } args.ModifySpeed(walkSpeed, sprintSpeed); // no longer colliding with anything if (remove) _toRemove.Add(uid); } private void OnEntityExit(EntityUid uid, SlowContactsComponent component, ref EndCollideEvent args) { var otherUid = args.OtherEntity; if (HasComp(otherUid)) _toUpdate.Add(otherUid); } private void OnEntityEnter(EntityUid uid, SlowContactsComponent component, ref StartCollideEvent args) { var otherUid = args.OtherEntity; if (!HasComp(otherUid)) return; EnsureComp(otherUid); _toUpdate.Add(otherUid); } }