diff --git a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs
index 87246927d7..49fdaa5c7e 100644
--- a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs
+++ b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs
@@ -21,9 +21,15 @@ namespace Content.Server.Chemistry.TileReactions
[DataField("requiredSlipSpeed")] private float _requiredSlipSpeed = 6;
[DataField("paralyzeTime")] private float _paralyzeTime = 1;
+ ///
+ ///
+ ///
+ [DataField("superSlippery")] private bool _superSlippery;
+
public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume)
{
- if (reactVolume < 5) return FixedPoint2.Zero;
+ if (reactVolume < 5)
+ return FixedPoint2.Zero;
var entityManager = IoCManager.Resolve();
@@ -33,7 +39,8 @@ namespace Content.Server.Chemistry.TileReactions
var slippery = entityManager.EnsureComponent(puddleUid);
slippery.LaunchForwardsMultiplier = _launchForwardsMultiplier;
slippery.ParalyzeTime = _paralyzeTime;
- entityManager.Dirty(slippery);
+ slippery.SuperSlippery = _superSlippery;
+ entityManager.Dirty(puddleUid, slippery);
var step = entityManager.EnsureComponent(puddleUid);
entityManager.EntitySysManager.GetEntitySystem().SetRequiredTriggerSpeed(puddleUid, _requiredSlipSpeed, step);
diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs
index b6df3a171b..44d28379ab 100644
--- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs
+++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs
@@ -293,7 +293,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
{
// Reactive entities have a chance to get a touch reaction from slipping on a puddle
// (i.e. it is implied they fell face first onto it or something)
- if (!HasComp(args.Slipped))
+ if (!HasComp(args.Slipped) || HasComp(args.Slipped))
return;
// Eventually probably have some system of 'body coverage' to tweak the probability but for now just 0.5
diff --git a/Content.Shared/Slippery/SlidingComponent.cs b/Content.Shared/Slippery/SlidingComponent.cs
new file mode 100644
index 0000000000..e48c0f2e9e
--- /dev/null
+++ b/Content.Shared/Slippery/SlidingComponent.cs
@@ -0,0 +1,22 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Slippery;
+
+///
+/// Applies continuous movement to the attached entity when colliding with super slipper entities.
+///
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class SlidingComponent : Component
+{
+ ///
+ /// A list of SuperSlippery entities the entity with this component is colliding with.
+ ///
+ [DataField, AutoNetworkedField]
+ public HashSet CollidingEntities = new ();
+
+ ///
+ /// The friction modifier that will be applied to any friction calculations.
+ ///
+ [DataField, AutoNetworkedField]
+ public float FrictionModifier;
+}
diff --git a/Content.Shared/Slippery/SlidingSystem.cs b/Content.Shared/Slippery/SlidingSystem.cs
new file mode 100644
index 0000000000..0af6b203cc
--- /dev/null
+++ b/Content.Shared/Slippery/SlidingSystem.cs
@@ -0,0 +1,63 @@
+using Content.Shared.Movement.Events;
+using Content.Shared.Standing;
+using Content.Shared.Stunnable;
+using Robust.Shared.Physics.Events;
+
+namespace Content.Shared.Slippery;
+
+public sealed class SlidingSystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnSlideAttempt);
+ SubscribeLocalEvent(OnStand);
+ SubscribeLocalEvent(OnStartCollide);
+ SubscribeLocalEvent(OnEndCollide);
+ }
+
+ ///
+ /// Modify the friction by the frictionModifier stored on the component.
+ ///
+ private void OnSlideAttempt(EntityUid uid, SlidingComponent component, ref TileFrictionEvent args)
+ {
+ args.Modifier = component.FrictionModifier;
+ }
+
+ ///
+ /// Remove the component when the entity stands up again.
+ ///
+ private void OnStand(EntityUid uid, SlidingComponent component, ref StoodEvent args)
+ {
+ RemComp(uid);
+ }
+
+ ///
+ /// Sets friction to 0 if colliding with a SuperSlippery Entity.
+ ///
+ private void OnStartCollide(EntityUid uid, SlidingComponent component, ref StartCollideEvent args)
+ {
+ if (!TryComp(args.OtherEntity, out var slippery) || !slippery.SuperSlippery)
+ return;
+
+ component.CollidingEntities.Add(args.OtherEntity);
+ component.FrictionModifier = 0;
+ Dirty(uid, component);
+ }
+
+ ///
+ /// Set friction to normal when ending collision with a SuperSlippery entity.
+ ///
+ private void OnEndCollide(EntityUid uid, SlidingComponent component, ref EndCollideEvent args)
+ {
+ if (!component.CollidingEntities.Remove(args.OtherEntity))
+ return;
+
+ if (component.CollidingEntities.Count == 0)
+ component.FrictionModifier = SharedStunSystem.KnockDownModifier;
+
+ Dirty(uid, component);
+ }
+
+}
diff --git a/Content.Shared/Slippery/SlipperyComponent.cs b/Content.Shared/Slippery/SlipperyComponent.cs
index b470e2cbbd..b80a9b57e4 100644
--- a/Content.Shared/Slippery/SlipperyComponent.cs
+++ b/Content.Shared/Slippery/SlipperyComponent.cs
@@ -23,7 +23,6 @@ namespace Content.Shared.Slippery
///
/// How many seconds the mob will be paralyzed for.
///
- [ViewVariables(VVAccess.ReadWrite)]
[DataField, AutoNetworkedField]
[Access(Other = AccessPermissions.ReadWrite)]
public float ParalyzeTime = 3f;
@@ -31,9 +30,16 @@ namespace Content.Shared.Slippery
///
/// The entity's speed will be multiplied by this to slip it forwards.
///
- [ViewVariables(VVAccess.ReadWrite)]
[DataField, AutoNetworkedField]
[Access(Other = AccessPermissions.ReadWrite)]
public float LaunchForwardsMultiplier = 1f;
+
+ ///
+ /// If this is true, any slipping entity loses its friction until
+ /// it's not colliding with any SuperSlippery entities anymore.
+ ///
+ [DataField, AutoNetworkedField]
+ [Access(Other = AccessPermissions.ReadWrite)]
+ public bool SuperSlippery;
}
}
diff --git a/Content.Shared/Slippery/SlipperySystem.cs b/Content.Shared/Slippery/SlipperySystem.cs
index 60d53eb16f..59a1c208ed 100644
--- a/Content.Shared/Slippery/SlipperySystem.cs
+++ b/Content.Shared/Slippery/SlipperySystem.cs
@@ -5,7 +5,6 @@ using Content.Shared.StatusEffect;
using Content.Shared.StepTrigger.Systems;
using Content.Shared.Stunnable;
using JetBrains.Annotations;
-using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Physics.Components;
@@ -60,7 +59,7 @@ public sealed class SlipperySystem : EntitySystem
private void TrySlip(EntityUid uid, SlipperyComponent component, EntityUid other)
{
- if (HasComp(other))
+ if (HasComp(other) && !component.SuperSlippery)
return;
var attemptEv = new SlipAttemptEvent();
@@ -71,9 +70,14 @@ public sealed class SlipperySystem : EntitySystem
var ev = new SlipEvent(other);
RaiseLocalEvent(uid, ref ev);
- if (TryComp(other, out PhysicsComponent? physics))
+ if (TryComp(other, out PhysicsComponent? physics) && !HasComp(other))
+ {
_physics.SetLinearVelocity(other, physics.LinearVelocity * component.LaunchForwardsMultiplier, body: physics);
+ if (component.SuperSlippery)
+ EnsureComp(other);
+ }
+
var playSound = !_statusEffects.HasStatusEffect(other, "KnockedDown");
_stun.TryParalyze(other, TimeSpan.FromSeconds(component.ParalyzeTime), true);
diff --git a/Resources/Prototypes/Reagents/cleaning.yml b/Resources/Prototypes/Reagents/cleaning.yml
index 49af16d93d..24f0c033cf 100644
--- a/Resources/Prototypes/Reagents/cleaning.yml
+++ b/Resources/Prototypes/Reagents/cleaning.yml
@@ -110,6 +110,7 @@
paralyzeTime: 3
launchForwardsMultiplier: 2
requiredSlipSpeed: 1
+ superSlippery: true
- type: reagent
id: SpaceGlue
@@ -150,4 +151,4 @@
footstepSound:
collection: FootstepSticky
params:
- volume: 6
\ No newline at end of file
+ volume: 6