diff --git a/Content.Client/Physics/Controllers/MoverController.cs b/Content.Client/Physics/Controllers/MoverController.cs
index bac19334fc..6e37d22a4c 100644
--- a/Content.Client/Physics/Controllers/MoverController.cs
+++ b/Content.Client/Physics/Controllers/MoverController.cs
@@ -66,7 +66,7 @@ namespace Content.Client.Physics.Controllers
// Server-side should just be handled on its own so we'll just do this shizznit
if (TryComp(player, out IMobMoverComponent? mobMover))
{
- HandleMobMovement(mover, body, mobMover, xform);
+ HandleMobMovement(mover, body, mobMover, xform, frameTime);
return;
}
diff --git a/Content.Server/AI/Components/AiControllerComponent.cs b/Content.Server/AI/Components/AiControllerComponent.cs
index d6eb2a9fc7..7cbc9eecdc 100644
--- a/Content.Server/AI/Components/AiControllerComponent.cs
+++ b/Content.Server/AI/Components/AiControllerComponent.cs
@@ -95,9 +95,6 @@ namespace Content.Server.AI.Components
[ViewVariables(VVAccess.ReadWrite)]
public float PushStrength { get; set; } = IMobMoverComponent.PushStrengthDefault;
- [ViewVariables(VVAccess.ReadWrite)]
- public float WeightlessStrength { get; set; } = IMobMoverComponent.WeightlessStrengthDefault;
-
///
[ViewVariables(VVAccess.ReadWrite)]
public float GrabRange { get; set; } = IMobMoverComponent.GrabRangeDefault;
diff --git a/Content.Server/Physics/Controllers/MoverController.cs b/Content.Server/Physics/Controllers/MoverController.cs
index 4b2c7c6212..f29fc79566 100644
--- a/Content.Server/Physics/Controllers/MoverController.cs
+++ b/Content.Server/Physics/Controllers/MoverController.cs
@@ -42,7 +42,7 @@ namespace Content.Server.Physics.Controllers
foreach (var (mobMover, mover, physics, xform) in EntityManager.EntityQuery())
{
_excludedMobs.Add(mover.Owner);
- HandleMobMovement(mover, physics, mobMover, xform);
+ HandleMobMovement(mover, physics, mobMover, xform, frameTime);
}
HandleShuttleMovement(frameTime);
diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs
index 1e15e71157..f43c8f24cc 100644
--- a/Content.Shared/CCVar/CCVars.cs
+++ b/Content.Shared/CCVar/CCVars.cs
@@ -380,6 +380,54 @@ namespace Content.Shared.CCVar
* Physics
*/
+ /*
+ * WARNING: These are liable to get changed to datafields whenever movement refactor occurs and may no longer be valid.
+ * You were warned!
+ */
+
+ ///
+ /// Minimum speed a mob has to be moving before applying movement friction.
+ ///
+ public static readonly CVarDef MinimumFrictionSpeed =
+ CVarDef.Create("physics.minimum_friction_speed", 0.005f, CVar.ARCHIVE | CVar.REPLICATED);
+
+ ///
+ /// The acceleration applied to mobs when moving.
+ ///
+ public static readonly CVarDef MobAcceleration =
+ CVarDef.Create("physics.mob_acceleration", 14f, CVar.ARCHIVE | CVar.REPLICATED);
+
+ ///
+ /// The negative velocity applied for friction.
+ ///
+ public static readonly CVarDef MobFriction =
+ CVarDef.Create("physics.mob_friction", 14f, CVar.ARCHIVE | CVar.REPLICATED);
+
+ ///
+ /// The acceleration applied to mobs when moving and weightless.
+ ///
+ public static readonly CVarDef MobWeightlessAcceleration =
+ CVarDef.Create("physics.mob_weightless_acceleration", 1f, CVar.ARCHIVE | CVar.REPLICATED);
+
+ ///
+ /// The negative velocity applied for friction when weightless and providing inputs.
+ ///
+ public static readonly CVarDef MobWeightlessFriction =
+ CVarDef.Create("physics.mob_weightless_friction", 1f, CVar.ARCHIVE | CVar.REPLICATED);
+
+ ///
+ /// The negative velocity applied for friction when weightless and not providing inputs.
+ /// This is essentially how much their speed decreases per second.
+ ///
+ public static readonly CVarDef MobWeightlessFrictionNoInput =
+ CVarDef.Create("physics.mob_weightless_friction_no_input", 0.2f, CVar.ARCHIVE | CVar.REPLICATED);
+
+ ///
+ /// The movement speed modifier applied to a mob's total input velocity when weightless.
+ ///
+ public static readonly CVarDef MobWeightlessModifier =
+ CVarDef.Create("physics.mob_weightless_modifier", 0.7f, CVar.ARCHIVE | CVar.REPLICATED);
+
///
/// When a mob is walking should its X / Y movement be relative to its parent (true) or the map (false).
///
@@ -387,10 +435,10 @@ namespace Content.Shared.CCVar
CVarDef.Create("physics.relative_movement", true, CVar.ARCHIVE | CVar.REPLICATED);
public static readonly CVarDef TileFrictionModifier =
- CVarDef.Create("physics.tile_friction", 40.0f);
+ CVarDef.Create("physics.tile_friction", 40.0f, CVar.ARCHIVE | CVar.REPLICATED);
public static readonly CVarDef StopSpeed =
- CVarDef.Create("physics.stop_speed", 0.1f);
+ CVarDef.Create("physics.stop_speed", 0.1f, CVar.ARCHIVE | CVar.REPLICATED);
///
/// Whether mobs can push objects like lockers.
diff --git a/Content.Shared/Movement/Components/IMobMoverComponent.cs b/Content.Shared/Movement/Components/IMobMoverComponent.cs
index 4c22a181d6..d383738f03 100644
--- a/Content.Shared/Movement/Components/IMobMoverComponent.cs
+++ b/Content.Shared/Movement/Components/IMobMoverComponent.cs
@@ -15,7 +15,5 @@ namespace Content.Shared.Movement.Components
float GrabRange { get; set; }
float PushStrength { get; set; }
-
- float WeightlessStrength { get; set; }
}
}
diff --git a/Content.Shared/Movement/Components/SharedPlayerMobMoverComponent.cs b/Content.Shared/Movement/Components/SharedPlayerMobMoverComponent.cs
index f7365dd7c2..4e1713432e 100644
--- a/Content.Shared/Movement/Components/SharedPlayerMobMoverComponent.cs
+++ b/Content.Shared/Movement/Components/SharedPlayerMobMoverComponent.cs
@@ -18,9 +18,6 @@ namespace Content.Shared.Movement.Components
[DataField("pushStrength")]
private float _pushStrength = IMobMoverComponent.PushStrengthDefault;
- [DataField("weightlessStrength")]
- private float _weightlessStrength = IMobMoverComponent.WeightlessStrengthDefault;
-
[ViewVariables(VVAccess.ReadWrite)]
public EntityCoordinates LastPosition { get; set; }
@@ -62,18 +59,6 @@ namespace Content.Shared.Movement.Components
}
}
- [ViewVariables(VVAccess.ReadWrite)]
- public float WeightlessStrength
- {
- get => _weightlessStrength;
- set
- {
- if (MathHelper.CloseToPercent(_weightlessStrength, value)) return;
- _weightlessStrength = value;
- Dirty();
- }
- }
-
protected override void Initialize()
{
base.Initialize();
@@ -85,7 +70,7 @@ namespace Content.Shared.Movement.Components
public override ComponentState GetComponentState()
{
- return new PlayerMobMoverComponentState(_grabRange, _pushStrength, _weightlessStrength);
+ return new PlayerMobMoverComponentState(_grabRange, _pushStrength);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
@@ -101,13 +86,11 @@ namespace Content.Shared.Movement.Components
{
public float GrabRange;
public float PushStrength;
- public float WeightlessStrength;
- public PlayerMobMoverComponentState(float grabRange, float pushStrength, float weightlessStrength)
+ public PlayerMobMoverComponentState(float grabRange, float pushStrength)
{
GrabRange = grabRange;
PushStrength = pushStrength;
- WeightlessStrength = weightlessStrength;
}
}
}
diff --git a/Content.Shared/Movement/SharedMoverController.cs b/Content.Shared/Movement/SharedMoverController.cs
index 9829c05183..8139d34ff6 100644
--- a/Content.Shared/Movement/SharedMoverController.cs
+++ b/Content.Shared/Movement/SharedMoverController.cs
@@ -36,6 +36,46 @@ namespace Content.Shared.Movement
private const float FootstepVariation = 0f;
private const float FootstepVolume = 1f;
+ ///
+ ///
+ ///
+ private float _minimumFrictionSpeed;
+
+ ///
+ ///
+ ///
+ private float _stopSpeed;
+
+ ///
+ ///
+ ///
+ private float _mobAcceleration;
+
+ ///
+ ///
+ ///
+ private float _frictionVelocity;
+
+ ///
+ ///
+ ///
+ private float _mobWeightlessAcceleration;
+
+ ///
+ ///
+ ///
+ private float _weightlessFrictionVelocity;
+
+ ///
+ ///
+ ///
+ private float _weightlessFrictionVelocityNoInput;
+
+ ///
+ ///
+ ///
+ private float _mobWeightlessModifier;
+
private bool _relativeMovement;
///
@@ -47,17 +87,42 @@ namespace Content.Shared.Movement
{
base.Initialize();
var configManager = IoCManager.Resolve();
+ // Hello
configManager.OnValueChanged(CCVars.RelativeMovement, SetRelativeMovement, true);
+ configManager.OnValueChanged(CCVars.MinimumFrictionSpeed, SetMinimumFrictionSpeed, true);
+ configManager.OnValueChanged(CCVars.MobFriction, SetFrictionVelocity, true);
+ configManager.OnValueChanged(CCVars.MobWeightlessFriction, SetWeightlessFrictionVelocity, true);
+ configManager.OnValueChanged(CCVars.StopSpeed, SetStopSpeed, true);
+ configManager.OnValueChanged(CCVars.MobAcceleration, SetMobAcceleration, true);
+ configManager.OnValueChanged(CCVars.MobWeightlessAcceleration, SetMobWeightlessAcceleration, true);
+ configManager.OnValueChanged(CCVars.MobWeightlessFrictionNoInput, SetWeightlessFrictionNoInput, true);
+ configManager.OnValueChanged(CCVars.MobWeightlessModifier, SetMobWeightlessModifier, true);
UpdatesBefore.Add(typeof(SharedTileFrictionController));
}
private void SetRelativeMovement(bool value) => _relativeMovement = value;
+ private void SetMinimumFrictionSpeed(float value) => _minimumFrictionSpeed = value;
+ private void SetStopSpeed(float value) => _stopSpeed = value;
+ private void SetFrictionVelocity(float value) => _frictionVelocity = value;
+ private void SetWeightlessFrictionVelocity(float value) => _weightlessFrictionVelocity = value;
+ private void SetMobAcceleration(float value) => _mobAcceleration = value;
+ private void SetMobWeightlessAcceleration(float value) => _mobWeightlessAcceleration = value;
+ private void SetWeightlessFrictionNoInput(float value) => _weightlessFrictionVelocityNoInput = value;
+ private void SetMobWeightlessModifier(float value) => _mobWeightlessModifier = value;
public override void Shutdown()
{
base.Shutdown();
var configManager = IoCManager.Resolve();
configManager.UnsubValueChanged(CCVars.RelativeMovement, SetRelativeMovement);
+ configManager.UnsubValueChanged(CCVars.MinimumFrictionSpeed, SetMinimumFrictionSpeed);
+ configManager.UnsubValueChanged(CCVars.StopSpeed, SetStopSpeed);
+ configManager.UnsubValueChanged(CCVars.MobFriction, SetFrictionVelocity);
+ configManager.UnsubValueChanged(CCVars.MobWeightlessFriction, SetWeightlessFrictionVelocity);
+ configManager.UnsubValueChanged(CCVars.MobAcceleration, SetMobAcceleration);
+ configManager.UnsubValueChanged(CCVars.MobWeightlessAcceleration, SetMobWeightlessAcceleration);
+ configManager.UnsubValueChanged(CCVars.MobWeightlessFrictionNoInput, SetWeightlessFrictionNoInput);
+ configManager.UnsubValueChanged(CCVars.MobWeightlessModifier, SetMobWeightlessModifier);
}
public override void UpdateAfterSolve(bool prediction, float frameTime)
@@ -108,7 +173,8 @@ namespace Content.Shared.Movement
IMoverComponent mover,
PhysicsComponent physicsComponent,
IMobMoverComponent mobMover,
- TransformComponent xform)
+ TransformComponent xform,
+ float frameTime)
{
DebugTools.Assert(!UsedMobMovement.ContainsKey(mover.Owner));
@@ -121,20 +187,18 @@ namespace Content.Shared.Movement
UsedMobMovement[mover.Owner] = true;
var weightless = mover.Owner.IsWeightless(physicsComponent, mapManager: _mapManager, entityManager: EntityManager);
var (walkDir, sprintDir) = mover.VelocityDir;
+ bool touching = true;
// Handle wall-pushes.
if (weightless)
{
// No gravity: is our entity touching anything?
- var touching = IsAroundCollider(_physics, xform, mobMover, physicsComponent);
+ touching = xform.GridUid != null || IsAroundCollider(_physics, xform, mobMover, physicsComponent);
if (!touching)
{
- if (xform.GridUid != EntityUid.Invalid)
+ if (xform.GridUid != null)
mover.LastGridAngle = GetParentGridAngle(xform, mover);
-
- xform.WorldRotation = physicsComponent.LinearVelocity.GetDir().ToAngle();
- return;
}
}
@@ -142,15 +206,34 @@ namespace Content.Shared.Movement
// Target velocity.
// This is relative to the map / grid we're on.
var total = walkDir * mover.CurrentWalkSpeed + sprintDir * mover.CurrentSprintSpeed;
-
var parentRotation = GetParentGridAngle(xform, mover);
-
var worldTotal = _relativeMovement ? parentRotation.RotateVec(total) : total;
DebugTools.Assert(MathHelper.CloseToPercent(total.Length, worldTotal.Length));
+ var velocity = physicsComponent.LinearVelocity;
+ float friction;
+ float weightlessModifier;
+ float accel;
+
if (weightless)
- worldTotal *= mobMover.WeightlessStrength;
+ {
+ if (worldTotal != Vector2.Zero && touching)
+ friction = _weightlessFrictionVelocity;
+ else
+ friction = _weightlessFrictionVelocityNoInput;
+
+ weightlessModifier = _mobWeightlessModifier;
+ accel = _mobWeightlessAcceleration;
+ }
+ else
+ {
+ friction = _frictionVelocity;
+ weightlessModifier = 1f;
+ accel = _mobAcceleration;
+ }
+
+ Friction(frameTime, friction, ref velocity);
if (xform.GridUid != EntityUid.Invalid)
mover.LastGridAngle = parentRotation;
@@ -172,7 +255,47 @@ namespace Content.Shared.Movement
}
}
- _physics.SetLinearVelocity(physicsComponent, worldTotal);
+ worldTotal *= weightlessModifier;
+
+ if (touching)
+ Accelerate(ref velocity, in worldTotal, accel, frameTime);
+
+ _physics.SetLinearVelocity(physicsComponent, velocity);
+ }
+
+ private void Friction(float frameTime, float friction, ref Vector2 velocity)
+ {
+ var speed = velocity.Length;
+
+ if (speed < _minimumFrictionSpeed) return;
+
+ var drop = 0f;
+
+ var control = MathF.Max(_stopSpeed, speed);
+ drop += control * friction * frameTime;
+
+ var newSpeed = MathF.Max(0f, speed - drop);
+
+ if (newSpeed.Equals(speed)) return;
+
+ newSpeed /= speed;
+ velocity *= newSpeed;
+ }
+
+ private void Accelerate(ref Vector2 currentVelocity, in Vector2 velocity, float accel, float frameTime)
+ {
+ var wishDir = velocity != Vector2.Zero ? velocity.Normalized : Vector2.Zero;
+ var wishSpeed = velocity.Length;
+
+ var currentSpeed = Vector2.Dot(currentVelocity, wishDir);
+ var addSpeed = wishSpeed - currentSpeed;
+
+ if (addSpeed <= 0f) return;
+
+ var accelSpeed = accel * frameTime * wishSpeed;
+ accelSpeed = MathF.Min(accelSpeed, addSpeed);
+
+ currentVelocity += wishDir * accelSpeed;
}
public bool UseMobMovement(EntityUid uid)
diff --git a/Content.Shared/Throwing/ThrowingSystem.cs b/Content.Shared/Throwing/ThrowingSystem.cs
index eab04a9c4b..d049ee013e 100644
--- a/Content.Shared/Throwing/ThrowingSystem.cs
+++ b/Content.Shared/Throwing/ThrowingSystem.cs
@@ -1,4 +1,5 @@
using Content.Shared.Interaction;
+using Content.Shared.Movement.Components;
using Content.Shared.Tag;
using Robust.Shared.Physics;
using Robust.Shared.Timing;
@@ -93,7 +94,10 @@ public sealed class ThrowingSystem : EntitySystem
}
// Give thrower an impulse in the other direction
- if (user != null && pushbackRatio > 0.0f && physicsQuery.Value.TryGetComponent(user.Value, out var userPhysics))
+ if (user != null &&
+ pushbackRatio > 0.0f &&
+ physicsQuery.Value.TryGetComponent(user.Value, out var userPhysics) &&
+ user.Value.IsWeightless(userPhysics, entityManager: EntityManager))
{
var msg = new ThrowPushbackAttemptEvent();
RaiseLocalEvent(physics.Owner, msg, false);