using Robust.Shared.Player;
using Robust.Shared.Timing;
-namespace Content.Client.Physics.Controllers
+namespace Content.Client.Physics.Controllers;
+
+public sealed class MoverController : SharedMoverController
{
- public sealed class MoverController : SharedMoverController
- {
- [Dependency] private readonly IGameTiming _timing = default!;
- [Dependency] private readonly IPlayerManager _playerManager = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<RelayInputMoverComponent, LocalPlayerAttachedEvent>(OnRelayPlayerAttached);
- SubscribeLocalEvent<RelayInputMoverComponent, LocalPlayerDetachedEvent>(OnRelayPlayerDetached);
- SubscribeLocalEvent<InputMoverComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
- SubscribeLocalEvent<InputMoverComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
-
- SubscribeLocalEvent<InputMoverComponent, UpdateIsPredictedEvent>(OnUpdatePredicted);
- SubscribeLocalEvent<MovementRelayTargetComponent, UpdateIsPredictedEvent>(OnUpdateRelayTargetPredicted);
- SubscribeLocalEvent<PullableComponent, UpdateIsPredictedEvent>(OnUpdatePullablePredicted);
- }
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent<RelayInputMoverComponent, LocalPlayerAttachedEvent>(OnRelayPlayerAttached);
+ SubscribeLocalEvent<RelayInputMoverComponent, LocalPlayerDetachedEvent>(OnRelayPlayerDetached);
+ SubscribeLocalEvent<InputMoverComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
+ SubscribeLocalEvent<InputMoverComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
+
+ SubscribeLocalEvent<InputMoverComponent, UpdateIsPredictedEvent>(OnUpdatePredicted);
+ SubscribeLocalEvent<MovementRelayTargetComponent, UpdateIsPredictedEvent>(OnUpdateRelayTargetPredicted);
+ SubscribeLocalEvent<PullableComponent, UpdateIsPredictedEvent>(OnUpdatePullablePredicted);
+ }
- private void OnUpdatePredicted(Entity<InputMoverComponent> entity, ref UpdateIsPredictedEvent args)
- {
- // Enable prediction if an entity is controlled by the player
- if (entity.Owner == _playerManager.LocalEntity)
- args.IsPredicted = true;
- }
+ private void OnUpdatePredicted(Entity<InputMoverComponent> entity, ref UpdateIsPredictedEvent args)
+ {
+ // Enable prediction if an entity is controlled by the player
+ if (entity.Owner == _playerManager.LocalEntity)
+ args.IsPredicted = true;
+ }
- private void OnUpdateRelayTargetPredicted(Entity<MovementRelayTargetComponent> entity, ref UpdateIsPredictedEvent args)
- {
- if (entity.Comp.Source == _playerManager.LocalEntity)
- args.IsPredicted = true;
- }
+ private void OnUpdateRelayTargetPredicted(Entity<MovementRelayTargetComponent> entity, ref UpdateIsPredictedEvent args)
+ {
+ if (entity.Comp.Source == _playerManager.LocalEntity)
+ args.IsPredicted = true;
+ }
- private void OnUpdatePullablePredicted(Entity<PullableComponent> entity, ref UpdateIsPredictedEvent args)
- {
- // Enable prediction if an entity is being pulled by the player.
- // Disable prediction if an entity is being pulled by some non-player entity.
+ private void OnUpdatePullablePredicted(Entity<PullableComponent> entity, ref UpdateIsPredictedEvent args)
+ {
+ // Enable prediction if an entity is being pulled by the player.
+ // Disable prediction if an entity is being pulled by some non-player entity.
- if (entity.Comp.Puller == _playerManager.LocalEntity)
- args.IsPredicted = true;
- else if (entity.Comp.Puller != null)
- args.BlockPrediction = true;
+ if (entity.Comp.Puller == _playerManager.LocalEntity)
+ args.IsPredicted = true;
+ else if (entity.Comp.Puller != null)
+ args.BlockPrediction = true;
- // TODO recursive pulling checks?
- // What if the entity is being pulled by a vehicle controlled by the player?
- }
+ // TODO recursive pulling checks?
+ // What if the entity is being pulled by a vehicle controlled by the player?
+ }
- private void OnRelayPlayerAttached(Entity<RelayInputMoverComponent> entity, ref LocalPlayerAttachedEvent args)
- {
- Physics.UpdateIsPredicted(entity.Owner);
- Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
- if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
- SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
- }
+ private void OnRelayPlayerAttached(Entity<RelayInputMoverComponent> entity, ref LocalPlayerAttachedEvent args)
+ {
+ Physics.UpdateIsPredicted(entity.Owner);
+ Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
+ if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
+ SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
+ }
- private void OnRelayPlayerDetached(Entity<RelayInputMoverComponent> entity, ref LocalPlayerDetachedEvent args)
- {
- Physics.UpdateIsPredicted(entity.Owner);
- Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
- if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
- SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
- }
+ private void OnRelayPlayerDetached(Entity<RelayInputMoverComponent> entity, ref LocalPlayerDetachedEvent args)
+ {
+ Physics.UpdateIsPredicted(entity.Owner);
+ Physics.UpdateIsPredicted(entity.Comp.RelayEntity);
+ if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
+ SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
+ }
- private void OnPlayerAttached(Entity<InputMoverComponent> entity, ref LocalPlayerAttachedEvent args)
- {
- SetMoveInput(entity, MoveButtons.None);
- }
+ private void OnPlayerAttached(Entity<InputMoverComponent> entity, ref LocalPlayerAttachedEvent args)
+ {
+ SetMoveInput(entity, MoveButtons.None);
+ }
- private void OnPlayerDetached(Entity<InputMoverComponent> entity, ref LocalPlayerDetachedEvent args)
- {
- SetMoveInput(entity, MoveButtons.None);
- }
+ private void OnPlayerDetached(Entity<InputMoverComponent> entity, ref LocalPlayerDetachedEvent args)
+ {
+ SetMoveInput(entity, MoveButtons.None);
+ }
- public override void UpdateBeforeSolve(bool prediction, float frameTime)
- {
- base.UpdateBeforeSolve(prediction, frameTime);
+ public override void UpdateBeforeSolve(bool prediction, float frameTime)
+ {
+ base.UpdateBeforeSolve(prediction, frameTime);
- if (_playerManager.LocalEntity is not {Valid: true} player)
- return;
+ if (_playerManager.LocalEntity is not {Valid: true} player)
+ return;
- if (RelayQuery.TryGetComponent(player, out var relayMover))
- HandleClientsideMovement(relayMover.RelayEntity, frameTime);
+ if (RelayQuery.TryGetComponent(player, out var relayMover))
+ HandleClientsideMovement(relayMover.RelayEntity, frameTime);
- HandleClientsideMovement(player, frameTime);
- }
+ HandleClientsideMovement(player, frameTime);
+ }
- private void HandleClientsideMovement(EntityUid player, float frameTime)
+ private void HandleClientsideMovement(EntityUid player, float frameTime)
+ {
+ if (!MoverQuery.TryGetComponent(player, out var mover) ||
+ !XformQuery.TryGetComponent(player, out var xform))
{
- if (!MoverQuery.TryGetComponent(player, out var mover) ||
- !XformQuery.TryGetComponent(player, out var xform))
- {
- return;
- }
-
- var physicsUid = player;
- PhysicsComponent? body;
- var xformMover = xform;
+ return;
+ }
- if (mover.ToParent && RelayQuery.HasComponent(xform.ParentUid))
- {
- if (!PhysicsQuery.TryGetComponent(xform.ParentUid, out body) ||
- !XformQuery.TryGetComponent(xform.ParentUid, out xformMover))
- {
- return;
- }
+ var physicsUid = player;
+ PhysicsComponent? body;
+ var xformMover = xform;
- physicsUid = xform.ParentUid;
- }
- else if (!PhysicsQuery.TryGetComponent(player, out body))
+ if (mover.ToParent && RelayQuery.HasComponent(xform.ParentUid))
+ {
+ if (!PhysicsQuery.TryGetComponent(xform.ParentUid, out body) ||
+ !XformQuery.TryGetComponent(xform.ParentUid, out xformMover))
{
return;
}
- // Server-side should just be handled on its own so we'll just do this shizznit
- HandleMobMovement(
- player,
- mover,
- physicsUid,
- body,
- xformMover,
- frameTime);
+ physicsUid = xform.ParentUid;
}
-
- protected override bool CanSound()
+ else if (!PhysicsQuery.TryGetComponent(player, out body))
{
- return _timing is { IsFirstTimePredicted: true, InSimulation: true };
+ return;
}
+
+ // Server-side should just be handled on its own so we'll just do this shizznit
+ HandleMobMovement(
+ player,
+ mover,
+ physicsUid,
+ body,
+ xformMover,
+ frameTime);
+ }
+
+ protected override bool CanSound()
+ {
+ return _timing is { IsFirstTimePredicted: true, InSimulation: true };
}
}
using DependencyAttribute = Robust.Shared.IoC.DependencyAttribute;
using Robust.Shared.Map.Components;
-namespace Content.Server.Physics.Controllers
+namespace Content.Server.Physics.Controllers;
+
+public sealed class MoverController : SharedMoverController
{
- public sealed class MoverController : SharedMoverController
+ [Dependency] private readonly ThrusterSystem _thruster = default!;
+ [Dependency] private readonly SharedTransformSystem _xformSystem = default!;
+
+ private Dictionary<EntityUid, (ShuttleComponent, List<(EntityUid, PilotComponent, InputMoverComponent, TransformComponent)>)> _shuttlePilots = new();
+
+ public override void Initialize()
{
- [Dependency] private readonly ThrusterSystem _thruster = default!;
- [Dependency] private readonly SharedTransformSystem _xformSystem = default!;
+ base.Initialize();
+ SubscribeLocalEvent<RelayInputMoverComponent, PlayerAttachedEvent>(OnRelayPlayerAttached);
+ SubscribeLocalEvent<RelayInputMoverComponent, PlayerDetachedEvent>(OnRelayPlayerDetached);
+ SubscribeLocalEvent<InputMoverComponent, PlayerAttachedEvent>(OnPlayerAttached);
+ SubscribeLocalEvent<InputMoverComponent, PlayerDetachedEvent>(OnPlayerDetached);
+ }
- private Dictionary<EntityUid, (ShuttleComponent, List<(EntityUid, PilotComponent, InputMoverComponent, TransformComponent)>)> _shuttlePilots = new();
+ private void OnRelayPlayerAttached(Entity<RelayInputMoverComponent> entity, ref PlayerAttachedEvent args)
+ {
+ if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
+ SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
+ }
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<RelayInputMoverComponent, PlayerAttachedEvent>(OnRelayPlayerAttached);
- SubscribeLocalEvent<RelayInputMoverComponent, PlayerDetachedEvent>(OnRelayPlayerDetached);
- SubscribeLocalEvent<InputMoverComponent, PlayerAttachedEvent>(OnPlayerAttached);
- SubscribeLocalEvent<InputMoverComponent, PlayerDetachedEvent>(OnPlayerDetached);
- }
+ private void OnRelayPlayerDetached(Entity<RelayInputMoverComponent> entity, ref PlayerDetachedEvent args)
+ {
+ if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
+ SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
+ }
- private void OnRelayPlayerAttached(Entity<RelayInputMoverComponent> entity, ref PlayerAttachedEvent args)
- {
- if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
- SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
- }
+ private void OnPlayerAttached(Entity<InputMoverComponent> entity, ref PlayerAttachedEvent args)
+ {
+ SetMoveInput(entity, MoveButtons.None);
+ }
- private void OnRelayPlayerDetached(Entity<RelayInputMoverComponent> entity, ref PlayerDetachedEvent args)
- {
- if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover))
- SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None);
- }
+ private void OnPlayerDetached(Entity<InputMoverComponent> entity, ref PlayerDetachedEvent args)
+ {
+ SetMoveInput(entity, MoveButtons.None);
+ }
- private void OnPlayerAttached(Entity<InputMoverComponent> entity, ref PlayerAttachedEvent args)
- {
- SetMoveInput(entity, MoveButtons.None);
- }
+ protected override bool CanSound()
+ {
+ return true;
+ }
- private void OnPlayerDetached(Entity<InputMoverComponent> entity, ref PlayerDetachedEvent args)
- {
- SetMoveInput(entity, MoveButtons.None);
- }
+ public override void UpdateBeforeSolve(bool prediction, float frameTime)
+ {
+ base.UpdateBeforeSolve(prediction, frameTime);
- protected override bool CanSound()
- {
- return true;
- }
+ var inputQueryEnumerator = AllEntityQuery<InputMoverComponent>();
- public override void UpdateBeforeSolve(bool prediction, float frameTime)
+ while (inputQueryEnumerator.MoveNext(out var uid, out var mover))
{
- base.UpdateBeforeSolve(prediction, frameTime);
+ var physicsUid = uid;
- var inputQueryEnumerator = AllEntityQuery<InputMoverComponent>();
+ if (RelayQuery.HasComponent(uid))
+ continue;
- while (inputQueryEnumerator.MoveNext(out var uid, out var mover))
+ if (!XformQuery.TryGetComponent(uid, out var xform))
{
- var physicsUid = uid;
-
- if (RelayQuery.HasComponent(uid))
- continue;
-
- if (!XformQuery.TryGetComponent(uid, out var xform))
- {
- continue;
- }
-
- PhysicsComponent? body;
- var xformMover = xform;
+ continue;
+ }
- if (mover.ToParent && RelayQuery.HasComponent(xform.ParentUid))
- {
- if (!PhysicsQuery.TryGetComponent(xform.ParentUid, out body) ||
- !XformQuery.TryGetComponent(xform.ParentUid, out xformMover))
- {
- continue;
- }
+ PhysicsComponent? body;
+ var xformMover = xform;
- physicsUid = xform.ParentUid;
- }
- else if (!PhysicsQuery.TryGetComponent(uid, out body))
+ if (mover.ToParent && RelayQuery.HasComponent(xform.ParentUid))
+ {
+ if (!PhysicsQuery.TryGetComponent(xform.ParentUid, out body) ||
+ !XformQuery.TryGetComponent(xform.ParentUid, out xformMover))
{
continue;
}
- HandleMobMovement(uid,
- mover,
- physicsUid,
- body,
- xformMover,
- frameTime);
+ physicsUid = xform.ParentUid;
}
-
- HandleShuttleMovement(frameTime);
- }
-
- public (Vector2 Strafe, float Rotation, float Brakes) GetPilotVelocityInput(PilotComponent component)
- {
- if (!Timing.InSimulation)
+ else if (!PhysicsQuery.TryGetComponent(uid, out body))
{
- // Outside of simulation we'll be running client predicted movement per-frame.
- // So return a full-length vector as if it's a full tick.
- // Physics system will have the correct time step anyways.
- ResetSubtick(component);
- ApplyTick(component, 1f);
- return (component.CurTickStrafeMovement, component.CurTickRotationMovement, component.CurTickBraking);
+ continue;
}
- float remainingFraction;
-
- if (Timing.CurTick > component.LastInputTick)
- {
- component.CurTickStrafeMovement = Vector2.Zero;
- component.CurTickRotationMovement = 0f;
- component.CurTickBraking = 0f;
- remainingFraction = 1;
- }
- else
- {
- remainingFraction = (ushort.MaxValue - component.LastInputSubTick) / (float) ushort.MaxValue;
- }
+ HandleMobMovement(uid,
+ mover,
+ physicsUid,
+ body,
+ xformMover,
+ frameTime);
+ }
- ApplyTick(component, remainingFraction);
+ HandleShuttleMovement(frameTime);
+ }
- // Logger.Info($"{curDir}{walk}{sprint}");
+ public (Vector2 Strafe, float Rotation, float Brakes) GetPilotVelocityInput(PilotComponent component)
+ {
+ if (!Timing.InSimulation)
+ {
+ // Outside of simulation we'll be running client predicted movement per-frame.
+ // So return a full-length vector as if it's a full tick.
+ // Physics system will have the correct time step anyways.
+ ResetSubtick(component);
+ ApplyTick(component, 1f);
return (component.CurTickStrafeMovement, component.CurTickRotationMovement, component.CurTickBraking);
}
- private void ResetSubtick(PilotComponent component)
- {
- if (Timing.CurTick <= component.LastInputTick) return;
+ float remainingFraction;
+ if (Timing.CurTick > component.LastInputTick)
+ {
component.CurTickStrafeMovement = Vector2.Zero;
component.CurTickRotationMovement = 0f;
component.CurTickBraking = 0f;
- component.LastInputTick = Timing.CurTick;
- component.LastInputSubTick = 0;
+ remainingFraction = 1;
+ }
+ else
+ {
+ remainingFraction = (ushort.MaxValue - component.LastInputSubTick) / (float) ushort.MaxValue;
}
- protected override void HandleShuttleInput(EntityUid uid, ShuttleButtons button, ushort subTick, bool state)
+ ApplyTick(component, remainingFraction);
+
+ // Logger.Info($"{curDir}{walk}{sprint}");
+ return (component.CurTickStrafeMovement, component.CurTickRotationMovement, component.CurTickBraking);
+ }
+
+ private void ResetSubtick(PilotComponent component)
+ {
+ if (Timing.CurTick <= component.LastInputTick) return;
+
+ component.CurTickStrafeMovement = Vector2.Zero;
+ component.CurTickRotationMovement = 0f;
+ component.CurTickBraking = 0f;
+ component.LastInputTick = Timing.CurTick;
+ component.LastInputSubTick = 0;
+ }
+
+ protected override void HandleShuttleInput(EntityUid uid, ShuttleButtons button, ushort subTick, bool state)
+ {
+ if (!TryComp<PilotComponent>(uid, out var pilot) || pilot.Console == null)
+ return;
+
+ ResetSubtick(pilot);
+
+ if (subTick >= pilot.LastInputSubTick)
{
- if (!TryComp<PilotComponent>(uid, out var pilot) || pilot.Console == null)
- return;
+ var fraction = (subTick - pilot.LastInputSubTick) / (float) ushort.MaxValue;
- ResetSubtick(pilot);
+ ApplyTick(pilot, fraction);
+ pilot.LastInputSubTick = subTick;
+ }
- if (subTick >= pilot.LastInputSubTick)
- {
- var fraction = (subTick - pilot.LastInputSubTick) / (float) ushort.MaxValue;
+ var buttons = pilot.HeldButtons;
- ApplyTick(pilot, fraction);
- pilot.LastInputSubTick = subTick;
- }
+ if (state)
+ {
+ buttons |= button;
+ }
+ else
+ {
+ buttons &= ~button;
+ }
- var buttons = pilot.HeldButtons;
+ pilot.HeldButtons = buttons;
+ }
- if (state)
- {
- buttons |= button;
- }
- else
- {
- buttons &= ~button;
- }
+ private static void ApplyTick(PilotComponent component, float fraction)
+ {
+ var x = 0;
+ var y = 0;
+ var rot = 0;
+ int brake;
- pilot.HeldButtons = buttons;
+ if ((component.HeldButtons & ShuttleButtons.StrafeLeft) != 0x0)
+ {
+ x -= 1;
}
- private static void ApplyTick(PilotComponent component, float fraction)
+ if ((component.HeldButtons & ShuttleButtons.StrafeRight) != 0x0)
{
- var x = 0;
- var y = 0;
- var rot = 0;
- int brake;
+ x += 1;
+ }
- if ((component.HeldButtons & ShuttleButtons.StrafeLeft) != 0x0)
- {
- x -= 1;
- }
+ component.CurTickStrafeMovement.X += x * fraction;
- if ((component.HeldButtons & ShuttleButtons.StrafeRight) != 0x0)
- {
- x += 1;
- }
+ if ((component.HeldButtons & ShuttleButtons.StrafeUp) != 0x0)
+ {
+ y += 1;
+ }
- component.CurTickStrafeMovement.X += x * fraction;
+ if ((component.HeldButtons & ShuttleButtons.StrafeDown) != 0x0)
+ {
+ y -= 1;
+ }
- if ((component.HeldButtons & ShuttleButtons.StrafeUp) != 0x0)
- {
- y += 1;
- }
+ component.CurTickStrafeMovement.Y += y * fraction;
- if ((component.HeldButtons & ShuttleButtons.StrafeDown) != 0x0)
- {
- y -= 1;
- }
+ if ((component.HeldButtons & ShuttleButtons.RotateLeft) != 0x0)
+ {
+ rot -= 1;
+ }
- component.CurTickStrafeMovement.Y += y * fraction;
+ if ((component.HeldButtons & ShuttleButtons.RotateRight) != 0x0)
+ {
+ rot += 1;
+ }
- if ((component.HeldButtons & ShuttleButtons.RotateLeft) != 0x0)
- {
- rot -= 1;
- }
+ component.CurTickRotationMovement += rot * fraction;
- if ((component.HeldButtons & ShuttleButtons.RotateRight) != 0x0)
- {
- rot += 1;
- }
+ if ((component.HeldButtons & ShuttleButtons.Brake) != 0x0)
+ {
+ brake = 1;
+ }
+ else
+ {
+ brake = 0;
+ }
- component.CurTickRotationMovement += rot * fraction;
+ component.CurTickBraking += brake * fraction;
+ }
+
+ /// <summary>
+ /// Helper function to extrapolate max velocity for a given Vector2 (really, its angle) and shuttle.
+ /// </summary>
+ private Vector2 ObtainMaxVel(Vector2 vel, ShuttleComponent shuttle)
+ {
+ if (vel.Length() == 0f)
+ return Vector2.Zero;
+
+ // this math could PROBABLY be simplified for performance
+ // probably
+ // __________________________________
+ // / / __ __ \2 / __ __ \2
+ // O = I : _ / |I * | 1/H | | + |I * | 0 | |
+ // V \ |_ 0 _| / \ |_1/V_| /
+
+ var horizIndex = vel.X > 0 ? 1 : 3; // east else west
+ var vertIndex = vel.Y > 0 ? 2 : 0; // north else south
+ var horizComp = vel.X != 0 ? MathF.Pow(Vector2.Dot(vel, new (shuttle.LinearThrust[horizIndex] / shuttle.LinearThrust[horizIndex], 0f)), 2) : 0;
+ var vertComp = vel.Y != 0 ? MathF.Pow(Vector2.Dot(vel, new (0f, shuttle.LinearThrust[vertIndex] / shuttle.LinearThrust[vertIndex])), 2) : 0;
+
+ return shuttle.BaseMaxLinearVelocity * vel * MathF.ReciprocalSqrtEstimate(horizComp + vertComp);
+ }
+
+ private void HandleShuttleMovement(float frameTime)
+ {
+ var newPilots = new Dictionary<EntityUid, (ShuttleComponent Shuttle, List<(EntityUid PilotUid, PilotComponent Pilot, InputMoverComponent Mover, TransformComponent ConsoleXform)>)>();
+
+ // We just mark off their movement and the shuttle itself does its own movement
+ var activePilotQuery = EntityQueryEnumerator<PilotComponent, InputMoverComponent>();
+ var shuttleQuery = GetEntityQuery<ShuttleComponent>();
+ while (activePilotQuery.MoveNext(out var uid, out var pilot, out var mover))
+ {
+ var consoleEnt = pilot.Console;
- if ((component.HeldButtons & ShuttleButtons.Brake) != 0x0)
+ // TODO: This is terrible. Just make a new mover and also make it remote piloting + device networks
+ if (TryComp<DroneConsoleComponent>(consoleEnt, out var cargoConsole))
{
- brake = 1;
+ consoleEnt = cargoConsole.Entity;
}
- else
+
+ if (!TryComp(consoleEnt, out TransformComponent? xform)) continue;
+
+ var gridId = xform.GridUid;
+ // This tries to see if the grid is a shuttle and if the console should work.
+ if (!TryComp<MapGridComponent>(gridId, out var _) ||
+ !shuttleQuery.TryGetComponent(gridId, out var shuttleComponent) ||
+ !shuttleComponent.Enabled)
+ continue;
+
+ if (!newPilots.TryGetValue(gridId!.Value, out var pilots))
{
- brake = 0;
+ pilots = (shuttleComponent, new List<(EntityUid, PilotComponent, InputMoverComponent, TransformComponent)>());
+ newPilots[gridId.Value] = pilots;
}
- component.CurTickBraking += brake * fraction;
+ pilots.Item2.Add((uid, pilot, mover, xform));
}
- /// <summary>
- /// Helper function to extrapolate max velocity for a given Vector2 (really, its angle) and shuttle.
- /// </summary>
- private Vector2 ObtainMaxVel(Vector2 vel, ShuttleComponent shuttle)
+ // Reset inputs for non-piloted shuttles.
+ foreach (var (shuttleUid, (shuttle, _)) in _shuttlePilots)
{
- if (vel.Length() == 0f)
- return Vector2.Zero;
-
- // this math could PROBABLY be simplified for performance
- // probably
- // __________________________________
- // / / __ __ \2 / __ __ \2
- // O = I : _ / |I * | 1/H | | + |I * | 0 | |
- // V \ |_ 0 _| / \ |_1/V_| /
-
- var horizIndex = vel.X > 0 ? 1 : 3; // east else west
- var vertIndex = vel.Y > 0 ? 2 : 0; // north else south
- var horizComp = vel.X != 0 ? MathF.Pow(Vector2.Dot(vel, new (shuttle.LinearThrust[horizIndex] / shuttle.LinearThrust[horizIndex], 0f)), 2) : 0;
- var vertComp = vel.Y != 0 ? MathF.Pow(Vector2.Dot(vel, new (0f, shuttle.LinearThrust[vertIndex] / shuttle.LinearThrust[vertIndex])), 2) : 0;
-
- return shuttle.BaseMaxLinearVelocity * vel * MathF.ReciprocalSqrtEstimate(horizComp + vertComp);
+ if (newPilots.ContainsKey(shuttleUid) || CanPilot(shuttleUid))
+ continue;
+
+ _thruster.DisableLinearThrusters(shuttle);
}
- private void HandleShuttleMovement(float frameTime)
+ _shuttlePilots = newPilots;
+
+ // Collate all of the linear / angular velocites for a shuttle
+ // then do the movement input once for it.
+ var xformQuery = GetEntityQuery<TransformComponent>();
+ foreach (var (shuttleUid, (shuttle, pilots)) in _shuttlePilots)
{
- var newPilots = new Dictionary<EntityUid, (ShuttleComponent Shuttle, List<(EntityUid PilotUid, PilotComponent Pilot, InputMoverComponent Mover, TransformComponent ConsoleXform)>)>();
+ if (Paused(shuttleUid) || CanPilot(shuttleUid) || !TryComp<PhysicsComponent>(shuttleUid, out var body))
+ continue;
- // We just mark off their movement and the shuttle itself does its own movement
- var activePilotQuery = EntityQueryEnumerator<PilotComponent, InputMoverComponent>();
- var shuttleQuery = GetEntityQuery<ShuttleComponent>();
- while (activePilotQuery.MoveNext(out var uid, out var pilot, out var mover))
+ var shuttleNorthAngle = _xformSystem.GetWorldRotation(shuttleUid, xformQuery);
+
+ // Collate movement linear and angular inputs together
+ var linearInput = Vector2.Zero;
+ var brakeInput = 0f;
+ var angularInput = 0f;
+
+ foreach (var (pilotUid, pilot, _, consoleXform) in pilots)
{
- var consoleEnt = pilot.Console;
+ var (strafe, rotation, brakes) = GetPilotVelocityInput(pilot);
- // TODO: This is terrible. Just make a new mover and also make it remote piloting + device networks
- if (TryComp<DroneConsoleComponent>(consoleEnt, out var cargoConsole))
+ if (brakes > 0f)
{
- consoleEnt = cargoConsole.Entity;
+ brakeInput += brakes;
}
- if (!TryComp(consoleEnt, out TransformComponent? xform)) continue;
-
- var gridId = xform.GridUid;
- // This tries to see if the grid is a shuttle and if the console should work.
- if (!TryComp<MapGridComponent>(gridId, out var _) ||
- !shuttleQuery.TryGetComponent(gridId, out var shuttleComponent) ||
- !shuttleComponent.Enabled)
- continue;
-
- if (!newPilots.TryGetValue(gridId!.Value, out var pilots))
+ if (strafe.Length() > 0f)
{
- pilots = (shuttleComponent, new List<(EntityUid, PilotComponent, InputMoverComponent, TransformComponent)>());
- newPilots[gridId.Value] = pilots;
+ var offsetRotation = consoleXform.LocalRotation;
+ linearInput += offsetRotation.RotateVec(strafe);
}
- pilots.Item2.Add((uid, pilot, mover, xform));
- }
-
- // Reset inputs for non-piloted shuttles.
- foreach (var (shuttleUid, (shuttle, _)) in _shuttlePilots)
- {
- if (newPilots.ContainsKey(shuttleUid) || CanPilot(shuttleUid))
- continue;
-
- _thruster.DisableLinearThrusters(shuttle);
+ if (rotation != 0f)
+ {
+ angularInput += rotation;
+ }
}
- _shuttlePilots = newPilots;
+ var count = pilots.Count;
+ linearInput /= count;
+ angularInput /= count;
+ brakeInput /= count;
- // Collate all of the linear / angular velocites for a shuttle
- // then do the movement input once for it.
- var xformQuery = GetEntityQuery<TransformComponent>();
- foreach (var (shuttleUid, (shuttle, pilots)) in _shuttlePilots)
+ // Handle shuttle movement
+ if (brakeInput > 0f)
{
- if (Paused(shuttleUid) || CanPilot(shuttleUid) || !TryComp<PhysicsComponent>(shuttleUid, out var body))
- continue;
-
- var shuttleNorthAngle = _xformSystem.GetWorldRotation(shuttleUid, xformQuery);
-
- // Collate movement linear and angular inputs together
- var linearInput = Vector2.Zero;
- var brakeInput = 0f;
- var angularInput = 0f;
-
- foreach (var (pilotUid, pilot, _, consoleXform) in pilots)
+ if (body.LinearVelocity.Length() > 0f)
{
- var (strafe, rotation, brakes) = GetPilotVelocityInput(pilot);
+ // Minimum brake velocity for a direction to show its thrust appearance.
+ const float appearanceThreshold = 0.1f;
- if (brakes > 0f)
- {
- brakeInput += brakes;
- }
-
- if (strafe.Length() > 0f)
- {
- var offsetRotation = consoleXform.LocalRotation;
- linearInput += offsetRotation.RotateVec(strafe);
- }
+ // Get velocity relative to the shuttle so we know which thrusters to fire
+ var shuttleVelocity = (-shuttleNorthAngle).RotateVec(body.LinearVelocity);
+ var force = Vector2.Zero;
- if (rotation != 0f)
+ if (shuttleVelocity.X < 0f)
{
- angularInput += rotation;
- }
- }
+ _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.West);
- var count = pilots.Count;
- linearInput /= count;
- angularInput /= count;
- brakeInput /= count;
+ if (shuttleVelocity.X < -appearanceThreshold)
+ _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.East);
- // Handle shuttle movement
- if (brakeInput > 0f)
- {
- if (body.LinearVelocity.Length() > 0f)
+ var index = (int) Math.Log2((int) DirectionFlag.East);
+ force.X += shuttle.LinearThrust[index];
+ }
+ else if (shuttleVelocity.X > 0f)
{
- // Minimum brake velocity for a direction to show its thrust appearance.
- const float appearanceThreshold = 0.1f;
+ _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.East);
- // Get velocity relative to the shuttle so we know which thrusters to fire
- var shuttleVelocity = (-shuttleNorthAngle).RotateVec(body.LinearVelocity);
- var force = Vector2.Zero;
+ if (shuttleVelocity.X > appearanceThreshold)
+ _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.West);
- if (shuttleVelocity.X < 0f)
- {
- _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.West);
-
- if (shuttleVelocity.X < -appearanceThreshold)
- _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.East);
-
- var index = (int) Math.Log2((int) DirectionFlag.East);
- force.X += shuttle.LinearThrust[index];
- }
- else if (shuttleVelocity.X > 0f)
- {
- _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.East);
+ var index = (int) Math.Log2((int) DirectionFlag.West);
+ force.X -= shuttle.LinearThrust[index];
+ }
- if (shuttleVelocity.X > appearanceThreshold)
- _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.West);
+ if (shuttleVelocity.Y < 0f)
+ {
+ _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.South);
- var index = (int) Math.Log2((int) DirectionFlag.West);
- force.X -= shuttle.LinearThrust[index];
- }
+ if (shuttleVelocity.Y < -appearanceThreshold)
+ _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.North);
- if (shuttleVelocity.Y < 0f)
- {
- _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.South);
+ var index = (int) Math.Log2((int) DirectionFlag.North);
+ force.Y += shuttle.LinearThrust[index];
+ }
+ else if (shuttleVelocity.Y > 0f)
+ {
+ _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.North);
- if (shuttleVelocity.Y < -appearanceThreshold)
- _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.North);
+ if (shuttleVelocity.Y > appearanceThreshold)
+ _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.South);
- var index = (int) Math.Log2((int) DirectionFlag.North);
- force.Y += shuttle.LinearThrust[index];
- }
- else if (shuttleVelocity.Y > 0f)
- {
- _thruster.DisableLinearThrustDirection(shuttle, DirectionFlag.North);
+ var index = (int) Math.Log2((int) DirectionFlag.South);
+ force.Y -= shuttle.LinearThrust[index];
+ }
- if (shuttleVelocity.Y > appearanceThreshold)
- _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.South);
+ var impulse = force * brakeInput * ShuttleComponent.BrakeCoefficient;
+ impulse = shuttleNorthAngle.RotateVec(impulse);
+ var forceMul = frameTime * body.InvMass;
+ var maxVelocity = (-body.LinearVelocity).Length() / forceMul;
- var index = (int) Math.Log2((int) DirectionFlag.South);
- force.Y -= shuttle.LinearThrust[index];
- }
+ // Don't overshoot
+ if (impulse.Length() > maxVelocity)
+ impulse = impulse.Normalized() * maxVelocity;
- var impulse = force * brakeInput * ShuttleComponent.BrakeCoefficient;
- impulse = shuttleNorthAngle.RotateVec(impulse);
- var forceMul = frameTime * body.InvMass;
- var maxVelocity = (-body.LinearVelocity).Length() / forceMul;
+ PhysicsSystem.ApplyForce(shuttleUid, impulse, body: body);
+ }
+ else
+ {
+ _thruster.DisableLinearThrusters(shuttle);
+ }
- // Don't overshoot
- if (impulse.Length() > maxVelocity)
- impulse = impulse.Normalized() * maxVelocity;
+ if (body.AngularVelocity != 0f)
+ {
+ var torque = shuttle.AngularThrust * brakeInput * (body.AngularVelocity > 0f ? -1f : 1f) * ShuttleComponent.BrakeCoefficient;
+ var torqueMul = body.InvI * frameTime;
- PhysicsSystem.ApplyForce(shuttleUid, impulse, body: body);
+ if (body.AngularVelocity > 0f)
+ {
+ torque = MathF.Max(-body.AngularVelocity / torqueMul, torque);
}
else
{
- _thruster.DisableLinearThrusters(shuttle);
+ torque = MathF.Min(-body.AngularVelocity / torqueMul, torque);
}
- if (body.AngularVelocity != 0f)
- {
- var torque = shuttle.AngularThrust * brakeInput * (body.AngularVelocity > 0f ? -1f : 1f) * ShuttleComponent.BrakeCoefficient;
- var torqueMul = body.InvI * frameTime;
-
- if (body.AngularVelocity > 0f)
- {
- torque = MathF.Max(-body.AngularVelocity / torqueMul, torque);
- }
- else
- {
- torque = MathF.Min(-body.AngularVelocity / torqueMul, torque);
- }
-
- if (!torque.Equals(0f))
- {
- PhysicsSystem.ApplyTorque(shuttleUid, torque, body: body);
- _thruster.SetAngularThrust(shuttle, true);
- }
- }
- else
+ if (!torque.Equals(0f))
{
- _thruster.SetAngularThrust(shuttle, false);
+ PhysicsSystem.ApplyTorque(shuttleUid, torque, body: body);
+ _thruster.SetAngularThrust(shuttle, true);
}
}
-
- if (linearInput.Length().Equals(0f))
+ else
{
- PhysicsSystem.SetSleepingAllowed(shuttleUid, body, true);
-
- if (brakeInput.Equals(0f))
- _thruster.DisableLinearThrusters(shuttle);
+ _thruster.SetAngularThrust(shuttle, false);
}
- else
+ }
+
+ if (linearInput.Length().Equals(0f))
+ {
+ PhysicsSystem.SetSleepingAllowed(shuttleUid, body, true);
+
+ if (brakeInput.Equals(0f))
+ _thruster.DisableLinearThrusters(shuttle);
+ }
+ else
+ {
+ PhysicsSystem.SetSleepingAllowed(shuttleUid, body, false);
+ var angle = linearInput.ToWorldAngle();
+ var linearDir = angle.GetDir();
+ var dockFlag = linearDir.AsFlag();
+ var totalForce = Vector2.Zero;
+
+ // Won't just do cardinal directions.
+ foreach (DirectionFlag dir in Enum.GetValues(typeof(DirectionFlag)))
{
- PhysicsSystem.SetSleepingAllowed(shuttleUid, body, false);
- var angle = linearInput.ToWorldAngle();
- var linearDir = angle.GetDir();
- var dockFlag = linearDir.AsFlag();
- var totalForce = Vector2.Zero;
-
- // Won't just do cardinal directions.
- foreach (DirectionFlag dir in Enum.GetValues(typeof(DirectionFlag)))
+ // Brain no worky but I just want cardinals
+ switch (dir)
{
- // Brain no worky but I just want cardinals
- switch (dir)
- {
- case DirectionFlag.South:
- case DirectionFlag.East:
- case DirectionFlag.North:
- case DirectionFlag.West:
- break;
- default:
- continue;
- }
-
- if ((dir & dockFlag) == 0x0)
- {
- _thruster.DisableLinearThrustDirection(shuttle, dir);
+ case DirectionFlag.South:
+ case DirectionFlag.East:
+ case DirectionFlag.North:
+ case DirectionFlag.West:
+ break;
+ default:
continue;
- }
-
- var force = Vector2.Zero;
- var index = (int) Math.Log2((int) dir);
- var thrust = shuttle.LinearThrust[index];
-
- switch (dir)
- {
- case DirectionFlag.North:
- force.Y += thrust;
- break;
- case DirectionFlag.South:
- force.Y -= thrust;
- break;
- case DirectionFlag.East:
- force.X += thrust;
- break;
- case DirectionFlag.West:
- force.X -= thrust;
- break;
- default:
- throw new ArgumentOutOfRangeException($"Attempted to apply thrust to shuttle {shuttleUid} along invalid dir {dir}.");
- }
-
- _thruster.EnableLinearThrustDirection(shuttle, dir);
- var impulse = force * linearInput.Length();
- totalForce += impulse;
}
- var forceMul = frameTime * body.InvMass;
+ if ((dir & dockFlag) == 0x0)
+ {
+ _thruster.DisableLinearThrustDirection(shuttle, dir);
+ continue;
+ }
- var localVel = (-shuttleNorthAngle).RotateVec(body.LinearVelocity);
- var maxVelocity = ObtainMaxVel(localVel, shuttle); // max for current travel dir
- var maxWishVelocity = ObtainMaxVel(totalForce, shuttle);
- var properAccel = (maxWishVelocity - localVel) / forceMul;
+ var force = Vector2.Zero;
+ var index = (int) Math.Log2((int) dir);
+ var thrust = shuttle.LinearThrust[index];
- var finalForce = Vector2Dot(totalForce, properAccel.Normalized()) * properAccel.Normalized();
+ switch (dir)
+ {
+ case DirectionFlag.North:
+ force.Y += thrust;
+ break;
+ case DirectionFlag.South:
+ force.Y -= thrust;
+ break;
+ case DirectionFlag.East:
+ force.X += thrust;
+ break;
+ case DirectionFlag.West:
+ force.X -= thrust;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException($"Attempted to apply thrust to shuttle {shuttleUid} along invalid dir {dir}.");
+ }
- if (localVel.Length() >= maxVelocity.Length() && Vector2.Dot(totalForce, localVel) > 0f)
- finalForce = Vector2.Zero; // burn would be faster if used as such
+ _thruster.EnableLinearThrustDirection(shuttle, dir);
+ var impulse = force * linearInput.Length();
+ totalForce += impulse;
+ }
- if (finalForce.Length() > properAccel.Length())
- finalForce = properAccel; // don't overshoot
+ var forceMul = frameTime * body.InvMass;
- //Log.Info($"shuttle: maxVelocity {maxVelocity} totalForce {totalForce} finalForce {finalForce} forceMul {forceMul} properAccel {properAccel}");
+ var localVel = (-shuttleNorthAngle).RotateVec(body.LinearVelocity);
+ var maxVelocity = ObtainMaxVel(localVel, shuttle); // max for current travel dir
+ var maxWishVelocity = ObtainMaxVel(totalForce, shuttle);
+ var properAccel = (maxWishVelocity - localVel) / forceMul;
- finalForce = shuttleNorthAngle.RotateVec(finalForce);
+ var finalForce = Vector2Dot(totalForce, properAccel.Normalized()) * properAccel.Normalized();
- if (finalForce.Length() > 0f)
- PhysicsSystem.ApplyForce(shuttleUid, finalForce, body: body);
- }
+ if (localVel.Length() >= maxVelocity.Length() && Vector2.Dot(totalForce, localVel) > 0f)
+ finalForce = Vector2.Zero; // burn would be faster if used as such
- if (MathHelper.CloseTo(angularInput, 0f))
- {
- PhysicsSystem.SetSleepingAllowed(shuttleUid, body, true);
+ if (finalForce.Length() > properAccel.Length())
+ finalForce = properAccel; // don't overshoot
- if (brakeInput <= 0f)
- _thruster.SetAngularThrust(shuttle, false);
- }
- else
- {
- PhysicsSystem.SetSleepingAllowed(shuttleUid, body, false);
- var torque = shuttle.AngularThrust * -angularInput;
+ //Log.Info($"shuttle: maxVelocity {maxVelocity} totalForce {totalForce} finalForce {finalForce} forceMul {forceMul} properAccel {properAccel}");
- // Need to cap the velocity if 1 tick of input brings us over cap so we don't continuously
- // edge onto the cap over and over.
- var torqueMul = body.InvI * frameTime;
+ finalForce = shuttleNorthAngle.RotateVec(finalForce);
- torque = Math.Clamp(torque,
- (-ShuttleComponent.MaxAngularVelocity - body.AngularVelocity) / torqueMul,
- (ShuttleComponent.MaxAngularVelocity - body.AngularVelocity) / torqueMul);
+ if (finalForce.Length() > 0f)
+ PhysicsSystem.ApplyForce(shuttleUid, finalForce, body: body);
+ }
- if (!torque.Equals(0f))
- {
- PhysicsSystem.ApplyTorque(shuttleUid, torque, body: body);
- _thruster.SetAngularThrust(shuttle, true);
- }
- }
+ if (MathHelper.CloseTo(angularInput, 0f))
+ {
+ PhysicsSystem.SetSleepingAllowed(shuttleUid, body, true);
+
+ if (brakeInput <= 0f)
+ _thruster.SetAngularThrust(shuttle, false);
}
- }
+ else
+ {
+ PhysicsSystem.SetSleepingAllowed(shuttleUid, body, false);
+ var torque = shuttle.AngularThrust * -angularInput;
- // .NET 8 seem to miscompile usage of Vector2.Dot above. This manual outline fixes it pending an upstream fix.
- // See PR #24008
- [MethodImpl(MethodImplOptions.NoInlining)]
- public static float Vector2Dot(Vector2 value1, Vector2 value2)
- {
- return Vector2.Dot(value1, value2);
- }
+ // Need to cap the velocity if 1 tick of input brings us over cap so we don't continuously
+ // edge onto the cap over and over.
+ var torqueMul = body.InvI * frameTime;
- private bool CanPilot(EntityUid shuttleUid)
- {
- return TryComp<FTLComponent>(shuttleUid, out var ftl)
- && (ftl.State & (FTLState.Starting | FTLState.Travelling | FTLState.Arriving)) != 0x0
- || HasComp<PreventPilotComponent>(shuttleUid);
+ torque = Math.Clamp(torque,
+ (-ShuttleComponent.MaxAngularVelocity - body.AngularVelocity) / torqueMul,
+ (ShuttleComponent.MaxAngularVelocity - body.AngularVelocity) / torqueMul);
+
+ if (!torque.Equals(0f))
+ {
+ PhysicsSystem.ApplyTorque(shuttleUid, torque, body: body);
+ _thruster.SetAngularThrust(shuttle, true);
+ }
+ }
}
+ }
+
+ // .NET 8 seem to miscompile usage of Vector2.Dot above. This manual outline fixes it pending an upstream fix.
+ // See PR #24008
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static float Vector2Dot(Vector2 value1, Vector2 value2)
+ {
+ return Vector2.Dot(value1, value2);
+ }
+ private bool CanPilot(EntityUid shuttleUid)
+ {
+ return TryComp<FTLComponent>(shuttleUid, out var ftl)
+ && (ftl.State & (FTLState.Starting | FTLState.Travelling | FTLState.Arriving)) != 0x0
+ || HasComp<PreventPilotComponent>(shuttleUid);
}
+
}
using Robust.Shared.Utility;
using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent;
-namespace Content.Shared.Movement.Systems
+namespace Content.Shared.Movement.Systems;
+
+/// <summary>
+/// Handles player and NPC mob movement.
+/// NPCs are handled server-side only.
+/// </summary>
+public abstract partial class SharedMoverController : VirtualController
{
+ [Dependency] private readonly IConfigurationManager _configManager = default!;
+ [Dependency] protected readonly IGameTiming Timing = default!;
+ [Dependency] private readonly IMapManager _mapManager = default!;
+ [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
+ [Dependency] private readonly EntityLookupSystem _lookup = default!;
+ [Dependency] private readonly InventorySystem _inventory = default!;
+ [Dependency] private readonly MobStateSystem _mobState = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedContainerSystem _container = default!;
+ [Dependency] private readonly SharedMapSystem _mapSystem = default!;
+ [Dependency] private readonly SharedGravitySystem _gravity = default!;
+ [Dependency] protected readonly SharedPhysicsSystem Physics = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly TagSystem _tags = default!;
+
+ protected EntityQuery<InputMoverComponent> MoverQuery;
+ protected EntityQuery<MobMoverComponent> MobMoverQuery;
+ protected EntityQuery<MovementRelayTargetComponent> RelayTargetQuery;
+ protected EntityQuery<MovementSpeedModifierComponent> ModifierQuery;
+ protected EntityQuery<PhysicsComponent> PhysicsQuery;
+ protected EntityQuery<RelayInputMoverComponent> RelayQuery;
+ protected EntityQuery<PullableComponent> PullableQuery;
+ protected EntityQuery<TransformComponent> XformQuery;
+ protected EntityQuery<CanMoveInAirComponent> CanMoveInAirQuery;
+ protected EntityQuery<NoRotateOnMoveComponent> NoRotateQuery;
+ protected EntityQuery<FootstepModifierComponent> FootstepModifierQuery;
+ protected EntityQuery<MapGridComponent> MapGridQuery;
+
+ /// <summary>
+ /// <see cref="CCVars.StopSpeed"/>
+ /// </summary>
+ private float _stopSpeed;
+
+ private bool _relativeMovement;
+
/// <summary>
- /// Handles player and NPC mob movement.
- /// NPCs are handled server-side only.
+ /// Cache the mob movement calculation to re-use elsewhere.
/// </summary>
- public abstract partial class SharedMoverController : VirtualController
+ public Dictionary<EntityUid, bool> UsedMobMovement = new();
+
+ public override void Initialize()
{
- [Dependency] private readonly IConfigurationManager _configManager = default!;
- [Dependency] protected readonly IGameTiming Timing = default!;
- [Dependency] private readonly IMapManager _mapManager = default!;
- [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
- [Dependency] private readonly EntityLookupSystem _lookup = default!;
- [Dependency] private readonly InventorySystem _inventory = default!;
- [Dependency] private readonly MobStateSystem _mobState = default!;
- [Dependency] private readonly SharedAudioSystem _audio = default!;
- [Dependency] private readonly SharedContainerSystem _container = default!;
- [Dependency] private readonly SharedMapSystem _mapSystem = default!;
- [Dependency] private readonly SharedGravitySystem _gravity = default!;
- [Dependency] protected readonly SharedPhysicsSystem Physics = default!;
- [Dependency] private readonly SharedTransformSystem _transform = default!;
- [Dependency] private readonly TagSystem _tags = default!;
-
- protected EntityQuery<InputMoverComponent> MoverQuery;
- protected EntityQuery<MobMoverComponent> MobMoverQuery;
- protected EntityQuery<MovementRelayTargetComponent> RelayTargetQuery;
- protected EntityQuery<MovementSpeedModifierComponent> ModifierQuery;
- protected EntityQuery<PhysicsComponent> PhysicsQuery;
- protected EntityQuery<RelayInputMoverComponent> RelayQuery;
- protected EntityQuery<PullableComponent> PullableQuery;
- protected EntityQuery<TransformComponent> XformQuery;
- protected EntityQuery<CanMoveInAirComponent> CanMoveInAirQuery;
- protected EntityQuery<NoRotateOnMoveComponent> NoRotateQuery;
- protected EntityQuery<FootstepModifierComponent> FootstepModifierQuery;
- protected EntityQuery<MapGridComponent> MapGridQuery;
-
- /// <summary>
- /// <see cref="CCVars.StopSpeed"/>
- /// </summary>
- private float _stopSpeed;
-
- private bool _relativeMovement;
-
- /// <summary>
- /// Cache the mob movement calculation to re-use elsewhere.
- /// </summary>
- public Dictionary<EntityUid, bool> UsedMobMovement = new();
-
- public override void Initialize()
- {
- base.Initialize();
-
- MoverQuery = GetEntityQuery<InputMoverComponent>();
- MobMoverQuery = GetEntityQuery<MobMoverComponent>();
- ModifierQuery = GetEntityQuery<MovementSpeedModifierComponent>();
- RelayTargetQuery = GetEntityQuery<MovementRelayTargetComponent>();
- PhysicsQuery = GetEntityQuery<PhysicsComponent>();
- RelayQuery = GetEntityQuery<RelayInputMoverComponent>();
- PullableQuery = GetEntityQuery<PullableComponent>();
- XformQuery = GetEntityQuery<TransformComponent>();
- NoRotateQuery = GetEntityQuery<NoRotateOnMoveComponent>();
- CanMoveInAirQuery = GetEntityQuery<CanMoveInAirComponent>();
- FootstepModifierQuery = GetEntityQuery<FootstepModifierComponent>();
- MapGridQuery = GetEntityQuery<MapGridComponent>();
-
- InitializeInput();
- InitializeRelay();
- Subs.CVar(_configManager, CCVars.RelativeMovement, value => _relativeMovement = value, true);
- Subs.CVar(_configManager, CCVars.StopSpeed, value => _stopSpeed = value, true);
- UpdatesBefore.Add(typeof(TileFrictionController));
- }
+ base.Initialize();
+
+ MoverQuery = GetEntityQuery<InputMoverComponent>();
+ MobMoverQuery = GetEntityQuery<MobMoverComponent>();
+ ModifierQuery = GetEntityQuery<MovementSpeedModifierComponent>();
+ RelayTargetQuery = GetEntityQuery<MovementRelayTargetComponent>();
+ PhysicsQuery = GetEntityQuery<PhysicsComponent>();
+ RelayQuery = GetEntityQuery<RelayInputMoverComponent>();
+ PullableQuery = GetEntityQuery<PullableComponent>();
+ XformQuery = GetEntityQuery<TransformComponent>();
+ NoRotateQuery = GetEntityQuery<NoRotateOnMoveComponent>();
+ CanMoveInAirQuery = GetEntityQuery<CanMoveInAirComponent>();
+ FootstepModifierQuery = GetEntityQuery<FootstepModifierComponent>();
+ MapGridQuery = GetEntityQuery<MapGridComponent>();
+
+ InitializeInput();
+ InitializeRelay();
+ Subs.CVar(_configManager, CCVars.RelativeMovement, value => _relativeMovement = value, true);
+ Subs.CVar(_configManager, CCVars.StopSpeed, value => _stopSpeed = value, true);
+ UpdatesBefore.Add(typeof(TileFrictionController));
+ }
- public override void Shutdown()
- {
- base.Shutdown();
- ShutdownInput();
- }
+ public override void Shutdown()
+ {
+ base.Shutdown();
+ ShutdownInput();
+ }
- public override void UpdateAfterSolve(bool prediction, float frameTime)
- {
- base.UpdateAfterSolve(prediction, frameTime);
- UsedMobMovement.Clear();
- }
+ public override void UpdateAfterSolve(bool prediction, float frameTime)
+ {
+ base.UpdateAfterSolve(prediction, frameTime);
+ UsedMobMovement.Clear();
+ }
- /// <summary>
- /// Movement while considering actionblockers, weightlessness, etc.
- /// </summary>
- protected void HandleMobMovement(
- EntityUid uid,
- InputMoverComponent mover,
- EntityUid physicsUid,
- PhysicsComponent physicsComponent,
- TransformComponent xform,
- float frameTime)
+ /// <summary>
+ /// Movement while considering actionblockers, weightlessness, etc.
+ /// </summary>
+ protected void HandleMobMovement(
+ EntityUid uid,
+ InputMoverComponent mover,
+ EntityUid physicsUid,
+ PhysicsComponent physicsComponent,
+ TransformComponent xform,
+ float frameTime)
+ {
+ var canMove = mover.CanMove;
+ if (RelayTargetQuery.TryGetComponent(uid, out var relayTarget))
{
- var canMove = mover.CanMove;
- if (RelayTargetQuery.TryGetComponent(uid, out var relayTarget))
+ if (_mobState.IsIncapacitated(relayTarget.Source) ||
+ TryComp<SleepingComponent>(relayTarget.Source, out _) ||
+ !MoverQuery.TryGetComponent(relayTarget.Source, out var relayedMover))
{
- if (_mobState.IsIncapacitated(relayTarget.Source) ||
- TryComp<SleepingComponent>(relayTarget.Source, out _) ||
- !MoverQuery.TryGetComponent(relayTarget.Source, out var relayedMover))
- {
- canMove = false;
- }
- else
- {
- mover.RelativeEntity = relayedMover.RelativeEntity;
- mover.RelativeRotation = relayedMover.RelativeRotation;
- mover.TargetRelativeRotation = relayedMover.TargetRelativeRotation;
- }
+ canMove = false;
}
-
- // Update relative movement
- if (mover.LerpTarget < Timing.CurTime)
+ else
{
- if (TryUpdateRelative(mover, xform))
- {
- Dirty(uid, mover);
- }
+ mover.RelativeEntity = relayedMover.RelativeEntity;
+ mover.RelativeRotation = relayedMover.RelativeRotation;
+ mover.TargetRelativeRotation = relayedMover.TargetRelativeRotation;
}
+ }
- LerpRotation(uid, mover, frameTime);
-
- if (!canMove
- || physicsComponent.BodyStatus != BodyStatus.OnGround && !CanMoveInAirQuery.HasComponent(uid)
- || PullableQuery.TryGetComponent(uid, out var pullable) && pullable.BeingPulled)
+ // Update relative movement
+ if (mover.LerpTarget < Timing.CurTime)
+ {
+ if (TryUpdateRelative(mover, xform))
{
- UsedMobMovement[uid] = false;
- return;
+ Dirty(uid, mover);
}
+ }
+ LerpRotation(uid, mover, frameTime);
- UsedMobMovement[uid] = true;
- // Specifically don't use mover.Owner because that may be different to the actual physics body being moved.
- var weightless = _gravity.IsWeightless(physicsUid, physicsComponent, xform);
- var (walkDir, sprintDir) = GetVelocityInput(mover);
- var touching = false;
-
- // Handle wall-pushes.
- if (weightless)
- {
- if (xform.GridUid != null)
- touching = true;
+ if (!canMove
+ || physicsComponent.BodyStatus != BodyStatus.OnGround && !CanMoveInAirQuery.HasComponent(uid)
+ || PullableQuery.TryGetComponent(uid, out var pullable) && pullable.BeingPulled)
+ {
+ UsedMobMovement[uid] = false;
+ return;
+ }
- if (!touching)
- {
- var ev = new CanWeightlessMoveEvent(uid);
- RaiseLocalEvent(uid, ref ev, true);
- // No gravity: is our entity touching anything?
- touching = ev.CanMove;
- if (!touching && TryComp<MobMoverComponent>(uid, out var mobMover))
- touching |= IsAroundCollider(PhysicsSystem, xform, mobMover, physicsUid, physicsComponent);
- }
- }
+ UsedMobMovement[uid] = true;
+ // Specifically don't use mover.Owner because that may be different to the actual physics body being moved.
+ var weightless = _gravity.IsWeightless(physicsUid, physicsComponent, xform);
+ var (walkDir, sprintDir) = GetVelocityInput(mover);
+ var touching = false;
- // Get current tile def for things like speed/friction mods
- ContentTileDefinition? tileDef = null;
+ // Handle wall-pushes.
+ if (weightless)
+ {
+ if (xform.GridUid != null)
+ touching = true;
- // Don't bother getting the tiledef here if we're weightless or in-air
- // since no tile-based modifiers should be applying in that situation
- if (MapGridQuery.TryComp(xform.GridUid, out var gridComp)
- && _mapSystem.TryGetTileRef(xform.GridUid.Value, gridComp, xform.Coordinates, out var tile)
- && !(weightless || physicsComponent.BodyStatus == BodyStatus.InAir))
+ if (!touching)
{
- tileDef = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId];
+ var ev = new CanWeightlessMoveEvent(uid);
+ RaiseLocalEvent(uid, ref ev, true);
+ // No gravity: is our entity touching anything?
+ touching = ev.CanMove;
+
+ if (!touching && TryComp<MobMoverComponent>(uid, out var mobMover))
+ touching |= IsAroundCollider(PhysicsSystem, xform, mobMover, physicsUid, physicsComponent);
}
+ }
- // Regular movement.
- // Target velocity.
- // This is relative to the map / grid we're on.
- var moveSpeedComponent = ModifierQuery.CompOrNull(uid);
+ // Get current tile def for things like speed/friction mods
+ ContentTileDefinition? tileDef = null;
- var walkSpeed = moveSpeedComponent?.CurrentWalkSpeed ?? MovementSpeedModifierComponent.DefaultBaseWalkSpeed;
- var sprintSpeed = moveSpeedComponent?.CurrentSprintSpeed ?? MovementSpeedModifierComponent.DefaultBaseSprintSpeed;
+ // Don't bother getting the tiledef here if we're weightless or in-air
+ // since no tile-based modifiers should be applying in that situation
+ if (MapGridQuery.TryComp(xform.GridUid, out var gridComp)
+ && _mapSystem.TryGetTileRef(xform.GridUid.Value, gridComp, xform.Coordinates, out var tile)
+ && !(weightless || physicsComponent.BodyStatus == BodyStatus.InAir))
+ {
+ tileDef = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId];
+ }
- var total = walkDir * walkSpeed + sprintDir * sprintSpeed;
+ // Regular movement.
+ // Target velocity.
+ // This is relative to the map / grid we're on.
+ var moveSpeedComponent = ModifierQuery.CompOrNull(uid);
- var parentRotation = GetParentGridAngle(mover);
- var worldTotal = _relativeMovement ? parentRotation.RotateVec(total) : total;
+ var walkSpeed = moveSpeedComponent?.CurrentWalkSpeed ?? MovementSpeedModifierComponent.DefaultBaseWalkSpeed;
+ var sprintSpeed = moveSpeedComponent?.CurrentSprintSpeed ?? MovementSpeedModifierComponent.DefaultBaseSprintSpeed;
- DebugTools.Assert(MathHelper.CloseToPercent(total.Length(), worldTotal.Length()));
+ var total = walkDir * walkSpeed + sprintDir * sprintSpeed;
- var velocity = physicsComponent.LinearVelocity;
- float friction;
- float weightlessModifier;
- float accel;
+ var parentRotation = GetParentGridAngle(mover);
+ var worldTotal = _relativeMovement ? parentRotation.RotateVec(total) : total;
- if (weightless)
- {
- if (gridComp == null && !MapGridQuery.HasComp(xform.GridUid))
- friction = moveSpeedComponent?.OffGridFriction ?? MovementSpeedModifierComponent.DefaultOffGridFriction;
- else if (worldTotal != Vector2.Zero && touching)
- friction = moveSpeedComponent?.WeightlessFriction ?? MovementSpeedModifierComponent.DefaultWeightlessFriction;
- else
- friction = moveSpeedComponent?.WeightlessFrictionNoInput ?? MovementSpeedModifierComponent.DefaultWeightlessFrictionNoInput;
+ DebugTools.Assert(MathHelper.CloseToPercent(total.Length(), worldTotal.Length()));
+
+ var velocity = physicsComponent.LinearVelocity;
+ float friction;
+ float weightlessModifier;
+ float accel;
+
+ if (weightless)
+ {
+ if (gridComp == null && !MapGridQuery.HasComp(xform.GridUid))
+ friction = moveSpeedComponent?.OffGridFriction ?? MovementSpeedModifierComponent.DefaultOffGridFriction;
+ else if (worldTotal != Vector2.Zero && touching)
+ friction = moveSpeedComponent?.WeightlessFriction ?? MovementSpeedModifierComponent.DefaultWeightlessFriction;
+ else
+ friction = moveSpeedComponent?.WeightlessFrictionNoInput ?? MovementSpeedModifierComponent.DefaultWeightlessFrictionNoInput;
- weightlessModifier = moveSpeedComponent?.WeightlessModifier ?? MovementSpeedModifierComponent.DefaultWeightlessModifier;
- accel = moveSpeedComponent?.WeightlessAcceleration ?? MovementSpeedModifierComponent.DefaultWeightlessAcceleration;
+ weightlessModifier = moveSpeedComponent?.WeightlessModifier ?? MovementSpeedModifierComponent.DefaultWeightlessModifier;
+ accel = moveSpeedComponent?.WeightlessAcceleration ?? MovementSpeedModifierComponent.DefaultWeightlessAcceleration;
+ }
+ else
+ {
+ if (worldTotal != Vector2.Zero || moveSpeedComponent?.FrictionNoInput == null)
+ {
+ friction = tileDef?.MobFriction ?? moveSpeedComponent?.Friction ?? MovementSpeedModifierComponent.DefaultFriction;
}
else
{
- if (worldTotal != Vector2.Zero || moveSpeedComponent?.FrictionNoInput == null)
- {
- friction = tileDef?.MobFriction ?? moveSpeedComponent?.Friction ?? MovementSpeedModifierComponent.DefaultFriction;
- }
- else
- {
- friction = tileDef?.MobFrictionNoInput ?? moveSpeedComponent.FrictionNoInput ?? MovementSpeedModifierComponent.DefaultFrictionNoInput;
- }
-
- weightlessModifier = 1f;
- accel = tileDef?.MobAcceleration ?? moveSpeedComponent?.Acceleration ?? MovementSpeedModifierComponent.DefaultAcceleration;
+ friction = tileDef?.MobFrictionNoInput ?? moveSpeedComponent.FrictionNoInput ?? MovementSpeedModifierComponent.DefaultFrictionNoInput;
}
- var minimumFrictionSpeed = moveSpeedComponent?.MinimumFrictionSpeed ?? MovementSpeedModifierComponent.DefaultMinimumFrictionSpeed;
- Friction(minimumFrictionSpeed, frameTime, friction, ref velocity);
+ weightlessModifier = 1f;
+ accel = tileDef?.MobAcceleration ?? moveSpeedComponent?.Acceleration ?? MovementSpeedModifierComponent.DefaultAcceleration;
+ }
- if (worldTotal != Vector2.Zero)
+ var minimumFrictionSpeed = moveSpeedComponent?.MinimumFrictionSpeed ?? MovementSpeedModifierComponent.DefaultMinimumFrictionSpeed;
+ Friction(minimumFrictionSpeed, frameTime, friction, ref velocity);
+
+ if (worldTotal != Vector2.Zero)
+ {
+ if (!NoRotateQuery.HasComponent(uid))
+ {
+ // TODO apparently this results in a duplicate move event because "This should have its event run during
+ // island solver"??. So maybe SetRotation needs an argument to avoid raising an event?
+ var worldRot = _transform.GetWorldRotation(xform);
+ _transform.SetLocalRotation(xform, xform.LocalRotation + worldTotal.ToWorldAngle() - worldRot);
+ }
+
+ if (!weightless && MobMoverQuery.TryGetComponent(uid, out var mobMover) &&
+ TryGetSound(weightless, uid, mover, mobMover, xform, out var sound, tileDef: tileDef))
{
- if (!NoRotateQuery.HasComponent(uid))
+ var soundModifier = mover.Sprinting ? 3.5f : 1.5f;
+
+ var audioParams = sound.Params
+ .WithVolume(sound.Params.Volume + soundModifier)
+ .WithVariation(sound.Params.Variation ?? mobMover.FootstepVariation);
+
+ // If we're a relay target then predict the sound for all relays.
+ if (relayTarget != null)
{
- // TODO apparently this results in a duplicate move event because "This should have its event run during
- // island solver"??. So maybe SetRotation needs an argument to avoid raising an event?
- var worldRot = _transform.GetWorldRotation(xform);
- _transform.SetLocalRotation(xform, xform.LocalRotation + worldTotal.ToWorldAngle() - worldRot);
+ _audio.PlayPredicted(sound, uid, relayTarget.Source, audioParams);
}
-
- if (!weightless && MobMoverQuery.TryGetComponent(uid, out var mobMover) &&
- TryGetSound(weightless, uid, mover, mobMover, xform, out var sound, tileDef: tileDef))
+ else
{
- var soundModifier = mover.Sprinting ? 3.5f : 1.5f;
-
- var audioParams = sound.Params
- .WithVolume(sound.Params.Volume + soundModifier)
- .WithVariation(sound.Params.Variation ?? mobMover.FootstepVariation);
-
- // If we're a relay target then predict the sound for all relays.
- if (relayTarget != null)
- {
- _audio.PlayPredicted(sound, uid, relayTarget.Source, audioParams);
- }
- else
- {
- _audio.PlayPredicted(sound, uid, uid, audioParams);
- }
+ _audio.PlayPredicted(sound, uid, uid, audioParams);
}
}
+ }
- worldTotal *= weightlessModifier;
+ worldTotal *= weightlessModifier;
- if (!weightless || touching)
- Accelerate(ref velocity, in worldTotal, accel, frameTime);
+ if (!weightless || touching)
+ Accelerate(ref velocity, in worldTotal, accel, frameTime);
- PhysicsSystem.SetLinearVelocity(physicsUid, velocity, body: physicsComponent);
+ PhysicsSystem.SetLinearVelocity(physicsUid, velocity, body: physicsComponent);
- // Ensures that players do not spiiiiiiin
- PhysicsSystem.SetAngularVelocity(physicsUid, 0, body: physicsComponent);
- }
+ // Ensures that players do not spiiiiiiin
+ PhysicsSystem.SetAngularVelocity(physicsUid, 0, body: physicsComponent);
+ }
- public void LerpRotation(EntityUid uid, InputMoverComponent mover, float frameTime)
+ public void LerpRotation(EntityUid uid, InputMoverComponent mover, float frameTime)
+ {
+ var angleDiff = Angle.ShortestDistance(mover.RelativeRotation, mover.TargetRelativeRotation);
+
+ // if we've just traversed then lerp to our target rotation.
+ if (!angleDiff.EqualsApprox(Angle.Zero, 0.001))
{
- var angleDiff = Angle.ShortestDistance(mover.RelativeRotation, mover.TargetRelativeRotation);
+ var adjustment = angleDiff * 5f * frameTime;
+ var minAdjustment = 0.01 * frameTime;
- // if we've just traversed then lerp to our target rotation.
- if (!angleDiff.EqualsApprox(Angle.Zero, 0.001))
+ if (angleDiff < 0)
{
- var adjustment = angleDiff * 5f * frameTime;
- var minAdjustment = 0.01 * frameTime;
-
- if (angleDiff < 0)
- {
- adjustment = Math.Min(adjustment, -minAdjustment);
- adjustment = Math.Clamp(adjustment, angleDiff, -angleDiff);
- }
- else
- {
- adjustment = Math.Max(adjustment, minAdjustment);
- adjustment = Math.Clamp(adjustment, -angleDiff, angleDiff);
- }
-
- mover.RelativeRotation += adjustment;
- mover.RelativeRotation.FlipPositive();
- Dirty(uid, mover);
+ adjustment = Math.Min(adjustment, -minAdjustment);
+ adjustment = Math.Clamp(adjustment, angleDiff, -angleDiff);
}
- else if (!angleDiff.Equals(Angle.Zero))
+ else
{
- mover.TargetRelativeRotation.FlipPositive();
- mover.RelativeRotation = mover.TargetRelativeRotation;
- Dirty(uid, mover);
+ adjustment = Math.Max(adjustment, minAdjustment);
+ adjustment = Math.Clamp(adjustment, -angleDiff, angleDiff);
}
- }
- private void Friction(float minimumFrictionSpeed, float frameTime, float friction, ref Vector2 velocity)
+ mover.RelativeRotation += adjustment;
+ mover.RelativeRotation.FlipPositive();
+ Dirty(uid, mover);
+ }
+ else if (!angleDiff.Equals(Angle.Zero))
{
- var speed = velocity.Length();
+ mover.TargetRelativeRotation.FlipPositive();
+ mover.RelativeRotation = mover.TargetRelativeRotation;
+ Dirty(uid, mover);
+ }
+ }
- if (speed < minimumFrictionSpeed)
- return;
+ private void Friction(float minimumFrictionSpeed, float frameTime, float friction, ref Vector2 velocity)
+ {
+ var speed = velocity.Length();
- var drop = 0f;
+ if (speed < minimumFrictionSpeed)
+ return;
- var control = MathF.Max(_stopSpeed, speed);
- drop += control * friction * frameTime;
+ var drop = 0f;
- var newSpeed = MathF.Max(0f, speed - drop);
+ var control = MathF.Max(_stopSpeed, speed);
+ drop += control * friction * frameTime;
- if (newSpeed.Equals(speed))
- return;
+ var newSpeed = MathF.Max(0f, speed - drop);
- newSpeed /= speed;
- velocity *= newSpeed;
- }
+ if (newSpeed.Equals(speed))
+ return;
- 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();
+ newSpeed /= speed;
+ velocity *= newSpeed;
+ }
- var currentSpeed = Vector2.Dot(currentVelocity, wishDir);
- var addSpeed = wishSpeed - currentSpeed;
+ 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();
- if (addSpeed <= 0f)
- return;
+ var currentSpeed = Vector2.Dot(currentVelocity, wishDir);
+ var addSpeed = wishSpeed - currentSpeed;
- var accelSpeed = accel * frameTime * wishSpeed;
- accelSpeed = MathF.Min(accelSpeed, addSpeed);
+ if (addSpeed <= 0f)
+ return;
- currentVelocity += wishDir * accelSpeed;
- }
+ var accelSpeed = accel * frameTime * wishSpeed;
+ accelSpeed = MathF.Min(accelSpeed, addSpeed);
- public bool UseMobMovement(EntityUid uid)
- {
- return UsedMobMovement.TryGetValue(uid, out var used) && used;
- }
+ currentVelocity += wishDir * accelSpeed;
+ }
- /// <summary>
- /// Used for weightlessness to determine if we are near a wall.
- /// </summary>
- private bool IsAroundCollider(SharedPhysicsSystem broadPhaseSystem, TransformComponent transform, MobMoverComponent mover, EntityUid physicsUid, PhysicsComponent collider)
- {
- var enlargedAABB = _lookup.GetWorldAABB(physicsUid, transform).Enlarged(mover.GrabRangeVV);
+ public bool UseMobMovement(EntityUid uid)
+ {
+ return UsedMobMovement.TryGetValue(uid, out var used) && used;
+ }
- foreach (var otherCollider in broadPhaseSystem.GetCollidingEntities(transform.MapID, enlargedAABB))
- {
- if (otherCollider == collider)
- continue; // Don't try to push off of yourself!
-
- // Only allow pushing off of anchored things that have collision.
- if (otherCollider.BodyType != BodyType.Static ||
- !otherCollider.CanCollide ||
- ((collider.CollisionMask & otherCollider.CollisionLayer) == 0 &&
- (otherCollider.CollisionMask & collider.CollisionLayer) == 0) ||
- (TryComp(otherCollider.Owner, out PullableComponent? pullable) && pullable.BeingPulled))
- {
- continue;
- }
+ /// <summary>
+ /// Used for weightlessness to determine if we are near a wall.
+ /// </summary>
+ private bool IsAroundCollider(SharedPhysicsSystem broadPhaseSystem, TransformComponent transform, MobMoverComponent mover, EntityUid physicsUid, PhysicsComponent collider)
+ {
+ var enlargedAABB = _lookup.GetWorldAABB(physicsUid, transform).Enlarged(mover.GrabRangeVV);
- return true;
+ foreach (var otherCollider in broadPhaseSystem.GetCollidingEntities(transform.MapID, enlargedAABB))
+ {
+ if (otherCollider == collider)
+ continue; // Don't try to push off of yourself!
+
+ // Only allow pushing off of anchored things that have collision.
+ if (otherCollider.BodyType != BodyType.Static ||
+ !otherCollider.CanCollide ||
+ ((collider.CollisionMask & otherCollider.CollisionLayer) == 0 &&
+ (otherCollider.CollisionMask & collider.CollisionLayer) == 0) ||
+ (TryComp(otherCollider.Owner, out PullableComponent? pullable) && pullable.BeingPulled))
+ {
+ continue;
}
- return false;
+ return true;
}
- protected abstract bool CanSound();
+ return false;
+ }
+
+ protected abstract bool CanSound();
- private bool TryGetSound(
- bool weightless,
- EntityUid uid,
- InputMoverComponent mover,
- MobMoverComponent mobMover,
- TransformComponent xform,
- [NotNullWhen(true)] out SoundSpecifier? sound,
- ContentTileDefinition? tileDef = null)
- {
- sound = null;
+ private bool TryGetSound(
+ bool weightless,
+ EntityUid uid,
+ InputMoverComponent mover,
+ MobMoverComponent mobMover,
+ TransformComponent xform,
+ [NotNullWhen(true)] out SoundSpecifier? sound,
+ ContentTileDefinition? tileDef = null)
+ {
+ sound = null;
- if (!CanSound() || !_tags.HasTag(uid, "FootstepSound"))
- return false;
+ if (!CanSound() || !_tags.HasTag(uid, "FootstepSound"))
+ return false;
- var coordinates = xform.Coordinates;
- var distanceNeeded = mover.Sprinting
- ? mobMover.StepSoundMoveDistanceRunning
- : mobMover.StepSoundMoveDistanceWalking;
+ var coordinates = xform.Coordinates;
+ var distanceNeeded = mover.Sprinting
+ ? mobMover.StepSoundMoveDistanceRunning
+ : mobMover.StepSoundMoveDistanceWalking;
- // Handle footsteps.
- if (!weightless)
+ // Handle footsteps.
+ if (!weightless)
+ {
+ // Can happen when teleporting between grids.
+ if (!coordinates.TryDistance(EntityManager, mobMover.LastPosition, out var distance) ||
+ distance > distanceNeeded)
{
- // Can happen when teleporting between grids.
- if (!coordinates.TryDistance(EntityManager, mobMover.LastPosition, out var distance) ||
- distance > distanceNeeded)
- {
- mobMover.StepSoundDistance = distanceNeeded;
- }
- else
- {
- mobMover.StepSoundDistance += distance;
- }
+ mobMover.StepSoundDistance = distanceNeeded;
}
else
{
- // In space no one can hear you squeak
- return false;
+ mobMover.StepSoundDistance += distance;
}
+ }
+ else
+ {
+ // In space no one can hear you squeak
+ return false;
+ }
- mobMover.LastPosition = coordinates;
+ mobMover.LastPosition = coordinates;
- if (mobMover.StepSoundDistance < distanceNeeded)
- return false;
+ if (mobMover.StepSoundDistance < distanceNeeded)
+ return false;
- mobMover.StepSoundDistance -= distanceNeeded;
+ mobMover.StepSoundDistance -= distanceNeeded;
- if (FootstepModifierQuery.TryComp(uid, out var moverModifier))
- {
- sound = moverModifier.FootstepSoundCollection;
- return true;
- }
+ if (FootstepModifierQuery.TryComp(uid, out var moverModifier))
+ {
+ sound = moverModifier.FootstepSoundCollection;
+ return true;
+ }
- if (_inventory.TryGetSlotEntity(uid, "shoes", out var shoes) &&
- FootstepModifierQuery.TryComp(shoes, out var modifier))
+ if (_inventory.TryGetSlotEntity(uid, "shoes", out var shoes) &&
+ FootstepModifierQuery.TryComp(shoes, out var modifier))
+ {
+ sound = modifier.FootstepSoundCollection;
+ return true;
+ }
+
+ return TryGetFootstepSound(uid, xform, shoes != null, out sound, tileDef: tileDef);
+ }
+
+ private bool TryGetFootstepSound(
+ EntityUid uid,
+ TransformComponent xform,
+ bool haveShoes,
+ [NotNullWhen(true)] out SoundSpecifier? sound,
+ ContentTileDefinition? tileDef = null)
+ {
+ sound = null;
+
+ // Fallback to the map?
+ if (!MapGridQuery.TryComp(xform.GridUid, out var grid))
+ {
+ if (FootstepModifierQuery.TryComp(xform.MapUid, out var modifier))
{
sound = modifier.FootstepSoundCollection;
return true;
}
- return TryGetFootstepSound(uid, xform, shoes != null, out sound, tileDef: tileDef);
+ return false;
}
- private bool TryGetFootstepSound(
- EntityUid uid,
- TransformComponent xform,
- bool haveShoes,
- [NotNullWhen(true)] out SoundSpecifier? sound,
- ContentTileDefinition? tileDef = null)
- {
- sound = null;
+ var position = grid.LocalToTile(xform.Coordinates);
+ var soundEv = new GetFootstepSoundEvent(uid);
- // Fallback to the map?
- if (!MapGridQuery.TryComp(xform.GridUid, out var grid))
- {
- if (FootstepModifierQuery.TryComp(xform.MapUid, out var modifier))
- {
- sound = modifier.FootstepSoundCollection;
- return true;
- }
-
- return false;
- }
-
- var position = grid.LocalToTile(xform.Coordinates);
- var soundEv = new GetFootstepSoundEvent(uid);
+ // If the coordinates have a FootstepModifier component
+ // i.e. component that emit sound on footsteps emit that sound
+ var anchored = grid.GetAnchoredEntitiesEnumerator(position);
- // If the coordinates have a FootstepModifier component
- // i.e. component that emit sound on footsteps emit that sound
- var anchored = grid.GetAnchoredEntitiesEnumerator(position);
+ while (anchored.MoveNext(out var maybeFootstep))
+ {
+ RaiseLocalEvent(maybeFootstep.Value, ref soundEv);
- while (anchored.MoveNext(out var maybeFootstep))
+ if (soundEv.Sound != null)
{
- RaiseLocalEvent(maybeFootstep.Value, ref soundEv);
-
- if (soundEv.Sound != null)
- {
- sound = soundEv.Sound;
- return true;
- }
-
- if (FootstepModifierQuery.TryComp(maybeFootstep, out var footstep))
- {
- sound = footstep.FootstepSoundCollection;
- return true;
- }
+ sound = soundEv.Sound;
+ return true;
}
- // Walking on a tile.
- // Tile def might have been passed in already from previous methods, so use that
- // if we have it
- if (tileDef == null && grid.TryGetTileRef(position, out var tileRef))
+ if (FootstepModifierQuery.TryComp(maybeFootstep, out var footstep))
{
- tileDef = (ContentTileDefinition) _tileDefinitionManager[tileRef.Tile.TypeId];
+ sound = footstep.FootstepSoundCollection;
+ return true;
}
+ }
- if (tileDef == null)
- return false;
-
- sound = haveShoes ? tileDef.FootstepSounds : tileDef.BarestepSounds;
- return sound != null;
+ // Walking on a tile.
+ // Tile def might have been passed in already from previous methods, so use that
+ // if we have it
+ if (tileDef == null && grid.TryGetTileRef(position, out var tileRef))
+ {
+ tileDef = (ContentTileDefinition) _tileDefinitionManager[tileRef.Tile.TypeId];
}
+
+ if (tileDef == null)
+ return false;
+
+ sound = haveShoes ? tileDef.FootstepSounds : tileDef.BarestepSounds;
+ return sound != null;
}
}