using System.Numerics; 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 { [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] public sealed partial class InputMoverComponent : Component { // This class has to be able to handle server TPS being lower than client FPS. // While still having perfectly responsive movement client side. // We do this by keeping track of the exact sub-tick values that inputs are pressed on the client, // and then building a total movement vector based on those sub-tick steps. // // We keep track of the last sub-tick a movement input came in, // Then when a new input comes in, we calculate the fraction of the tick the LAST input was active for // (new sub-tick - last sub-tick) // and then add to the total-this-tick movement vector // by multiplying that fraction by the movement direction for the last input. // This allows us to incrementally build the movement vector for the current tick, // without having to keep track of some kind of list of inputs and calculating it later. // // We have to keep track of a separate movement vector for walking and sprinting, // since we don't actually know our current movement speed while processing inputs. // We change which vector we write into based on whether we were sprinting after the previous input. // (well maybe we do but the code is designed such that MoverSystem applies movement speed) // (and I'm not changing that) /// /// Should our velocity be applied to our parent? /// [ViewVariables(VVAccess.ReadWrite), DataField("toParent")] public bool ToParent = false; public GameTick LastInputTick; public ushort LastInputSubTick; public Vector2 CurTickWalkMovement; public Vector2 CurTickSprintMovement; [AutoNetworkedField] public MoveButtons HeldMoveButtons = MoveButtons.None; /// /// Entity our movement is relative to. /// [AutoNetworkedField] public EntityUid? RelativeEntity; /// /// Although our movement might be relative to a particular entity we may have an additional relative rotation /// e.g. if we've snapped to a different cardinal direction /// [ViewVariables, AutoNetworkedField] public Angle TargetRelativeRotation = Angle.Zero; /// /// The current relative rotation. This will lerp towards the . /// [ViewVariables, AutoNetworkedField] public Angle RelativeRotation; /// /// If we traverse on / off a grid then set a timer to update our relative inputs. /// [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] [ViewVariables(VVAccess.ReadWrite)] public TimeSpan LerpTarget; public const float LerpTime = 1.0f; public bool Sprinting => (HeldMoveButtons & MoveButtons.Walk) == 0x0; [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] public bool CanMove { get; set; } = true; } }