using Content.Shared.Movement.Systems; using Content.Shared.Standing; using Content.Shared.Throwing; using Content.Shared.Weapons.Ranged.Systems; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; namespace Content.Shared.Slippery; public sealed class SlidingSystem : EntitySystem { [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly MovementSpeedModifierSystem _speedModifierSystem = default!; private EntityQuery _slipperyQuery; public override void Initialize() { base.Initialize(); _slipperyQuery = GetEntityQuery(); SubscribeLocalEvent(OnComponentInit); SubscribeLocalEvent(OnComponentShutdown); SubscribeLocalEvent(OnStand); SubscribeLocalEvent(OnStartCollide); SubscribeLocalEvent(OnEndCollide); SubscribeLocalEvent(OnRefreshFrictionModifiers); SubscribeLocalEvent(OnThrowerImpulse); SubscribeLocalEvent(ShooterImpulseEvent); } /// /// When the component is first added, calculate the friction modifier we need. /// Don't do this more than once to avoid mispredicts. /// private void OnComponentInit(Entity entity, ref ComponentInit args) { if (CalculateSlidingModifier(entity)) _speedModifierSystem.RefreshFrictionModifiers(entity); } /// /// When the component is removed, refresh friction modifiers and set ours to 1 to avoid causing issues. /// private void OnComponentShutdown(Entity entity, ref ComponentShutdown args) { entity.Comp.FrictionModifier = 1; _speedModifierSystem.RefreshFrictionModifiers(entity); } /// /// Remove the component when the entity stands up again. /// private void OnStand(EntityUid uid, SlidingComponent component, ref StoodEvent args) { RemComp(uid); } /// /// Updates friction when we collide with a slippery entity /// private void OnStartCollide(Entity entity, ref StartCollideEvent args) { if (!_slipperyQuery.TryComp(args.OtherEntity, out var slippery) || !slippery.AffectsSliding) return; CalculateSlidingModifier(entity); _speedModifierSystem.RefreshFrictionModifiers(entity); } /// /// Update friction when we stop colliding with a slippery entity /// private void OnEndCollide(Entity entity, ref EndCollideEvent args) { if (!_slipperyQuery.TryComp(args.OtherEntity, out var slippery) || !slippery.AffectsSliding) return; if (!CalculateSlidingModifier(entity, args.OtherEntity)) { RemComp(entity); return; } _speedModifierSystem.RefreshFrictionModifiers(entity); } /// /// Gets contacting slippery entities and averages their friction modifiers. /// private bool CalculateSlidingModifier(Entity entity, EntityUid? ignore = null) { if (!Resolve(entity, ref entity.Comp2, false)) return false; var friction = 0.0f; var count = 0; entity.Comp1.Contacting.Clear(); _physics.GetContactingEntities((entity, entity.Comp2), entity.Comp1.Contacting); foreach (var ent in entity.Comp1.Contacting) { if (ent == ignore || !_slipperyQuery.TryComp(ent, out var slippery) || !slippery.AffectsSliding) continue; friction += slippery.SlipData.SlipFriction; count++; } if (count > 0) { entity.Comp1.FrictionModifier = friction / count; Dirty(entity.Owner, entity.Comp1); return true; } return false; } private void OnRefreshFrictionModifiers(Entity entity, ref RefreshFrictionModifiersEvent args) { args.ModifyFriction(entity.Comp.FrictionModifier); args.ModifyAcceleration(entity.Comp.FrictionModifier); } private void OnThrowerImpulse(Entity entity, ref ThrowerImpulseEvent args) { args.Push = true; } private void ShooterImpulseEvent(Entity entity, ref ShooterImpulseEvent args) { args.Push = true; } }