var xformQuery = GetEntityQuery<TransformComponent>();
var moverQuery = GetEntityQuery<InputMoverComponent>();
var relayTargetQuery = GetEntityQuery<MovementRelayTargetComponent>();
+ var mobMoverQuery = GetEntityQuery<MobMoverComponent>();
+ var pullableQuery = GetEntityQuery<SharedPullableComponent>();
+ var physicsQuery = GetEntityQuery<PhysicsComponent>();
+ var modifierQuery = GetEntityQuery<MovementSpeedModifierComponent>();
- if (!TryComp(player, out InputMoverComponent? mover) ||
+ if (!moverQuery.TryGetComponent(player, out var mover) ||
!xformQuery.TryGetComponent(player, out var xform))
{
return;
{
foreach (var joint in jointComponent.GetJoints.Values)
{
- if (TryComp(joint.BodyAUid, out PhysicsComponent? physics))
+ if (physicsQuery.TryGetComponent(joint.BodyAUid, out var physics))
physics.Predict = true;
- if (TryComp(joint.BodyBUid, out physics))
+ if (physicsQuery.TryGetComponent(joint.BodyBUid, out physics))
physics.Predict = true;
}
}
// If we're being pulled then we won't predict anything and will receive server lerps so it looks way smoother.
- if (TryComp(player, out SharedPullableComponent? pullableComp))
+ if (pullableQuery.TryGetComponent(player, out var pullableComp))
{
- if (pullableComp.Puller is {Valid: true} puller && TryComp<PhysicsComponent?>(puller, out var pullerBody))
+ if (pullableComp.Puller is {Valid: true} puller && TryComp<PhysicsComponent>(puller, out var pullerBody))
{
pullerBody.Predict = false;
body.Predict = false;
if (TryComp<SharedPullerComponent>(player, out var playerPuller) && playerPuller.Pulling != null &&
- TryComp<PhysicsComponent>(playerPuller.Pulling, out var pulledBody))
+ physicsQuery.TryGetComponent(playerPuller.Pulling, out var pulledBody))
{
pulledBody.Predict = false;
}
}
// Server-side should just be handled on its own so we'll just do this shizznit
- HandleMobMovement(player, mover, physicsUid, body, xformMover, frameTime, xformQuery, moverQuery, relayTargetQuery);
+ HandleMobMovement(
+ player,
+ mover,
+ physicsUid,
+ body,
+ xformMover,
+ frameTime,
+ xformQuery,
+ moverQuery,
+ mobMoverQuery,
+ relayTargetQuery,
+ pullableQuery,
+ modifierQuery);
}
protected override bool CanSound()
using Content.Server.Shuttles.Systems;
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems;
+using Content.Shared.Pulling.Components;
using Content.Shared.Shuttles.Components;
using Content.Shared.Shuttles.Systems;
using Robust.Server.GameObjects;
var relayTargetQuery = GetEntityQuery<MovementRelayTargetComponent>();
var xformQuery = GetEntityQuery<TransformComponent>();
var moverQuery = GetEntityQuery<InputMoverComponent>();
+ var mobMoverQuery = GetEntityQuery<MobMoverComponent>();
+ var pullableQuery = GetEntityQuery<SharedPullableComponent>();
+ var inputQueryEnumerator = AllEntityQuery<InputMoverComponent>();
+ var modifierQuery = GetEntityQuery<MovementSpeedModifierComponent>();
- foreach (var mover in EntityQuery<InputMoverComponent>(true))
+ while (inputQueryEnumerator.MoveNext(out var uid, out var mover))
{
- var uid = mover.Owner;
- EntityUid physicsUid = uid;
+ var physicsUid = uid;
if (relayQuery.HasComponent(uid))
continue;
continue;
}
- HandleMobMovement(uid, mover, physicsUid, body, xformMover, frameTime, xformQuery, moverQuery, relayTargetQuery);
+ HandleMobMovement(uid,
+ mover,
+ physicsUid,
+ body,
+ xformMover,
+ frameTime,
+ xformQuery,
+ moverQuery,
+ mobMoverQuery,
+ relayTargetQuery,
+ pullableQuery,
+ modifierQuery);
}
HandleShuttleMovement(frameTime);
using Content.Shared.Movement.Systems;
using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Timing;
namespace Content.Shared.Movement.Components
/// <summary>
/// If we traverse on / off a grid then set a timer to update our relative inputs.
/// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- public float LerpAccumulator;
+ [ViewVariables(VVAccess.ReadWrite), DataField("lerpTarget", customTypeSerializer: typeof(TimeOffsetSerializer))]
+ public TimeSpan LerpTarget;
public const float LerpTime = 1.0f;
component.RelativeRotation = state.RelativeRotation;
component.TargetRelativeRotation = state.TargetRelativeRotation;
component.RelativeEntity = state.RelativeEntity;
- component.LerpAccumulator = state.LerpAccumulator;
+ component.LerpTarget = state.LerpAccumulator;
}
private void OnInputGetState(EntityUid uid, InputMoverComponent component, ref ComponentGetState args)
component.RelativeRotation,
component.TargetRelativeRotation,
component.RelativeEntity,
- component.LerpAccumulator);
+ component.LerpTarget);
}
private void ShutdownInput()
public void ResetCamera(EntityUid uid)
{
- if (CameraRotationLocked || !TryComp<InputMoverComponent>(uid, out var mover) || mover.TargetRelativeRotation.Equals(Angle.Zero))
+ if (CameraRotationLocked ||
+ !TryComp<InputMoverComponent>(uid, out var mover))
+ {
+ return;
+ }
+
+ // If we updated parent then cancel the accumulator and force it now.
+ var xformQuery = GetEntityQuery<TransformComponent>();
+
+ if (!TryUpdateRelative(mover, xformQuery.GetComponent(uid), xformQuery) && mover.TargetRelativeRotation.Equals(Angle.Zero))
return;
+ mover.LerpTarget = TimeSpan.Zero;
mover.TargetRelativeRotation = Angle.Zero;
Dirty(mover);
}
+ private bool TryUpdateRelative(InputMoverComponent mover, TransformComponent xform, EntityQuery<TransformComponent> xformQuery)
+ {
+ var relative = xform.GridUid;
+ relative ??= xform.MapUid;
+
+ // So essentially what we want:
+ // 1. If we go from grid to map then preserve our rotation and continue as usual
+ // 2. If we go from grid -> grid then (after lerp time) snap to nearest cardinal (probably imperceptible)
+ // 3. If we go from map -> grid then (after lerp time) snap to nearest cardinal
+
+ if (mover.RelativeEntity.Equals(relative))
+ return false;
+
+ // Okay need to get our old relative rotation with respect to our new relative rotation
+ // e.g. if we were right side up on our current grid need to get what that is on our new grid.
+ var currentRotation = Angle.Zero;
+ var targetRotation = Angle.Zero;
+
+ // Get our current relative rotation
+ if (xformQuery.TryGetComponent(mover.RelativeEntity, out var oldRelativeXform))
+ {
+ currentRotation = _transform.GetWorldRotation(oldRelativeXform, xformQuery) + mover.RelativeRotation;
+ }
+
+ if (xformQuery.TryGetComponent(relative, out var relativeXform))
+ {
+ // This is our current rotation relative to our new parent.
+ mover.RelativeRotation = (currentRotation - _transform.GetWorldRotation(relativeXform, xformQuery)).FlipPositive();
+ }
+
+ // If we went from grid -> map we'll preserve our worldrotation
+ if (relative != null && _mapManager.IsMap(relative.Value))
+ {
+ targetRotation = currentRotation.FlipPositive().Reduced();
+ }
+ // If we went from grid -> grid OR grid -> map then snap the target to cardinal and lerp there.
+ // OR just rotate to zero (depending on cvar)
+ else if (relative != null && _mapManager.IsGrid(relative.Value))
+ {
+ if (CameraRotationLocked)
+ targetRotation = Angle.Zero;
+ else
+ targetRotation = mover.RelativeRotation.GetCardinalDir().ToAngle().Reduced();
+ }
+
+ mover.RelativeEntity = relative;
+ mover.TargetRelativeRotation = targetRotation;
+ return true;
+ }
+
public Angle GetParentGridAngle(InputMoverComponent mover, EntityQuery<TransformComponent> xformQuery)
{
var rotation = mover.RelativeRotation;
public Angle GetParentGridAngle(InputMoverComponent mover)
{
- var rotation = mover.RelativeRotation;
-
- if (TryComp<TransformComponent>(mover.RelativeEntity, out var relativeXform))
- return (relativeXform.WorldRotation + rotation);
-
- return rotation;
+ return GetParentGridAngle(mover, GetEntityQuery<TransformComponent>());
}
private void OnFollowedParentChange(EntityUid uid, FollowedComponent component, ref EntParentChangedMessage args)
component.RelativeEntity = relative;
component.TargetRelativeRotation = Angle.Zero;
component.RelativeRotation = Angle.Zero;
- component.LerpAccumulator = 0f;
+ component.LerpTarget = TimeSpan.Zero;
Dirty(component);
return;
}
// If we go on a grid and back off then just reset the accumulator.
if (relative == component.RelativeEntity)
{
- if (component.LerpAccumulator != 0f)
+ if (component.LerpTarget >= Timing.CurTime)
{
- component.LerpAccumulator = 0f;
+ component.LerpTarget = TimeSpan.Zero;
Dirty(component);
}
return;
}
- component.LerpAccumulator = InputMoverComponent.LerpTime;
+ component.LerpTarget = TimeSpan.FromSeconds(InputMoverComponent.LerpTime) + Timing.CurTime;
Dirty(component);
}
// Relay the fact we had any movement event.
// TODO: Ideally we'd do these in a tick instead of out of sim.
- var owner = moverComp.Owner;
var moveEvent = new MoveInputEvent(entity);
- RaiseLocalEvent(owner, ref moveEvent);
+ RaiseLocalEvent(entity, ref moveEvent);
// For stuff like "Moving out of locker" or the likes
// We'll relay a movement input to the parent.
- if (_container.IsEntityInContainer(owner) &&
- TryComp<TransformComponent>(owner, out var xform) &&
+ if (_container.IsEntityInContainer(entity) &&
+ TryComp<TransformComponent>(entity, out var xform) &&
xform.ParentUid.IsValid() &&
- _mobState.IsAlive(owner))
+ _mobState.IsAlive(entity))
{
- var relayMoveEvent = new ContainerRelayMovementEntityEvent(owner);
+ var relayMoveEvent = new ContainerRelayMovementEntityEvent(entity);
RaiseLocalEvent(xform.ParentUid, ref relayMoveEvent);
}
/// </summary>
public Angle TargetRelativeRotation;
public EntityUid? RelativeEntity;
- public float LerpAccumulator = 0f;
+ public TimeSpan LerpAccumulator;
- public InputMoverComponentState(MoveButtons buttons, bool canMove, Angle relativeRotation, Angle targetRelativeRotation, EntityUid? relativeEntity, float lerpAccumulator)
+ public InputMoverComponentState(MoveButtons buttons, bool canMove, Angle relativeRotation, Angle targetRelativeRotation, EntityUid? relativeEntity, TimeSpan lerpTarget)
{
Buttons = buttons;
CanMove = canMove;
RelativeRotation = relativeRotation;
TargetRelativeRotation = targetRelativeRotation;
RelativeEntity = relativeEntity;
- LerpAccumulator = lerpAccumulator;
+ LerpAccumulator = lerpTarget;
}
}
float frameTime,
EntityQuery<TransformComponent> xformQuery,
EntityQuery<InputMoverComponent> moverQuery,
- EntityQuery<MovementRelayTargetComponent> relayTargetQuery)
+ EntityQuery<MobMoverComponent> mobMoverQuery,
+ EntityQuery<MovementRelayTargetComponent> relayTargetQuery,
+ EntityQuery<SharedPullableComponent> pullableQuery,
+ EntityQuery<MovementSpeedModifierComponent> modifierQuery)
{
- DebugTools.Assert(!UsedMobMovement.ContainsKey(uid));
-
bool canMove = mover.CanMove;
if (relayTargetQuery.TryGetComponent(uid, out var relayTarget) && relayTarget.Entities.Count > 0)
{
}
// Update relative movement
- if (mover.LerpAccumulator > 0f)
+ if (mover.LerpTarget < Timing.CurTime)
{
- Dirty(mover);
- mover.LerpAccumulator -= frameTime;
-
- if (mover.LerpAccumulator <= 0f)
+ if (TryUpdateRelative(mover, xform, xformQuery))
{
- mover.LerpAccumulator = 0f;
- var relative = xform.GridUid;
- relative ??= xform.MapUid;
-
- // So essentially what we want:
- // 1. If we go from grid to map then preserve our rotation and continue as usual
- // 2. If we go from grid -> grid then (after lerp time) snap to nearest cardinal (probably imperceptible)
- // 3. If we go from map -> grid then (after lerp time) snap to nearest cardinal
-
- if (!mover.RelativeEntity.Equals(relative))
- {
- // Okay need to get our old relative rotation with respect to our new relative rotation
- // e.g. if we were right side up on our current grid need to get what that is on our new grid.
- var currentRotation = Angle.Zero;
- var targetRotation = Angle.Zero;
-
- // Get our current relative rotation
- if (xformQuery.TryGetComponent(mover.RelativeEntity, out var oldRelativeXform))
- {
- currentRotation = oldRelativeXform.WorldRotation + mover.RelativeRotation;
- }
-
- if (xformQuery.TryGetComponent(relative, out var relativeXform))
- {
- // This is our current rotation relative to our new parent.
- mover.RelativeRotation = (currentRotation - relativeXform.WorldRotation).FlipPositive();
- }
-
- // If we went from grid -> map we'll preserve our worldrotation
- if (relative != null && _mapManager.IsMap(relative.Value))
- {
- targetRotation = currentRotation.FlipPositive().Reduced();
- }
- // If we went from grid -> grid OR grid -> map then snap the target to cardinal and lerp there.
- // OR just rotate to zero (depending on cvar)
- else if (relative != null && _mapManager.IsGrid(relative.Value))
- {
- if (CameraRotationLocked)
- targetRotation = Angle.Zero;
- else
- targetRotation = mover.RelativeRotation.GetCardinalDir().ToAngle().Reduced();
- }
-
- mover.RelativeEntity = relative;
- mover.TargetRelativeRotation = targetRotation;
- }
+ Dirty(mover);
}
}
if (!canMove
|| physicsComponent.BodyStatus != BodyStatus.OnGround
- || TryComp(uid, out SharedPullableComponent? pullable) && pullable.BeingPulled)
+ || pullableQuery.TryGetComponent(uid, out var pullable) && pullable.BeingPulled)
{
UsedMobMovement[uid] = false;
return;
touching = ev.CanMove;
if (!touching && TryComp<MobMoverComponent>(uid, out var mobMover))
- touching |= IsAroundCollider(PhysicsSystem, xform, mobMover, physicsComponent);
+ touching |= IsAroundCollider(PhysicsSystem, xform, mobMover, physicsUid, physicsComponent);
}
}
// Regular movement.
// Target velocity.
// This is relative to the map / grid we're on.
- var moveSpeedComponent = CompOrNull<MovementSpeedModifierComponent>(uid);
+ var moveSpeedComponent = modifierQuery.CompOrNull(uid);
var walkSpeed = moveSpeedComponent?.CurrentWalkSpeed ?? MovementSpeedModifierComponent.DefaultBaseWalkSpeed;
var sprintSpeed = moveSpeedComponent?.CurrentSprintSpeed ?? MovementSpeedModifierComponent.DefaultBaseSprintSpeed;
// 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?
- if (!weightless && TryComp<MobMoverComponent>(mover.Owner, out var mobMover) &&
- TryGetSound(weightless, mover, mobMover, xform, out var sound))
+ if (!weightless && mobMoverQuery.TryGetComponent(uid, out var mobMover) &&
+ TryGetSound(weightless, uid, mover, mobMover, xform, out var sound))
{
var soundModifier = mover.Sprinting ? 1.0f : FootstepWalkingAddedVolumeMultiplier;
{
var speed = velocity.Length;
- if (speed < minimumFrictionSpeed) return;
+ if (speed < minimumFrictionSpeed)
+ return;
var drop = 0f;
var newSpeed = MathF.Max(0f, speed - drop);
- if (newSpeed.Equals(speed)) return;
+ if (newSpeed.Equals(speed))
+ return;
newSpeed /= speed;
velocity *= newSpeed;
var currentSpeed = Vector2.Dot(currentVelocity, wishDir);
var addSpeed = wishSpeed - currentSpeed;
- if (addSpeed <= 0f) return;
+ if (addSpeed <= 0f)
+ return;
var accelSpeed = accel * frameTime * wishSpeed;
accelSpeed = MathF.Min(accelSpeed, addSpeed);
/// <summary>
/// Used for weightlessness to determine if we are near a wall.
/// </summary>
- private bool IsAroundCollider(SharedPhysicsSystem broadPhaseSystem, TransformComponent transform, MobMoverComponent mover, PhysicsComponent collider)
+ private bool IsAroundCollider(SharedPhysicsSystem broadPhaseSystem, TransformComponent transform, MobMoverComponent mover, EntityUid physicsUid, PhysicsComponent collider)
{
- var enlargedAABB = _lookup.GetWorldAABB(collider.Owner, transform).Enlarged(mover.GrabRangeVV);
+ var enlargedAABB = _lookup.GetWorldAABB(physicsUid, transform).Enlarged(mover.GrabRangeVV);
foreach (var otherCollider in broadPhaseSystem.GetCollidingEntities(transform.MapID, enlargedAABB))
{
protected abstract bool CanSound();
- private bool TryGetSound(bool weightless, InputMoverComponent mover, MobMoverComponent mobMover, TransformComponent xform, [NotNullWhen(true)] out SoundSpecifier? sound)
+ private bool TryGetSound(bool weightless, EntityUid uid, InputMoverComponent mover, MobMoverComponent mobMover, TransformComponent xform, [NotNullWhen(true)] out SoundSpecifier? sound)
{
sound = null;
- if (!CanSound() || !_tags.HasTag(mover.Owner, "FootstepSound")) return false;
+ if (!CanSound() || !_tags.HasTag(uid, "FootstepSound"))
+ return false;
var coordinates = xform.Coordinates;
var distanceNeeded = mover.Sprinting ? StepSoundMoveDistanceRunning : StepSoundMoveDistanceWalking;
mobMover.LastPosition = coordinates;
- if (mobMover.StepSoundDistance < distanceNeeded) return false;
+ if (mobMover.StepSoundDistance < distanceNeeded)
+ return false;
mobMover.StepSoundDistance -= distanceNeeded;
- if (TryComp<FootstepModifierComponent>(mover.Owner, out var moverModifier))
+ if (TryComp<FootstepModifierComponent>(uid, out var moverModifier))
{
sound = moverModifier.Sound;
return true;
}
- if (_inventory.TryGetSlotEntity(mover.Owner, "shoes", out var shoes) &&
+ if (_inventory.TryGetSlotEntity(uid, "shoes", out var shoes) &&
TryComp<FootstepModifierComponent>(shoes, out var modifier))
{
sound = modifier.Sound;
return true;
}
- return TryGetFootstepSound(xform, shoes != null, out sound);
+ return TryGetFootstepSound(uid, xform, shoes != null, out sound);
}
- private bool TryGetFootstepSound(TransformComponent xform, bool haveShoes, [NotNullWhen(true)] out SoundSpecifier? sound)
+ private bool TryGetFootstepSound(EntityUid uid, TransformComponent xform, bool haveShoes, [NotNullWhen(true)] out SoundSpecifier? sound)
{
sound = null;
}
var position = grid.LocalToTile(xform.Coordinates);
- var soundEv = new GetFootstepSoundEvent(xform.Owner);
+ var soundEv = new GetFootstepSoundEvent(uid);
// If the coordinates have a FootstepModifier component
// i.e. component that emit sound on footsteps emit that sound