public IState Execute(PlayerController p)
        {
            p.DetermineFacing();

            if (accelerationCurve != null)
            {
                var accel = p.Physics.Acceleration * accelerationCurve.Evaluate(Time.time - startTime);
                p.Move(p.Physics.Speed, accel);
            }
            else
            {
                p.Move();
            }

            // While inside the jump extend window, we don't apply gravity.
            var extendTime          = p.JumpExtendTime;
            var outsideExtendWindow = Utility.Elapsed(startTime, extendTime);

            if (outsideExtendWindow)
            {
                p.ApplyGravity();
            }

            foreach (var input in p.Input)
            {
                if (input.Action == InputActions.AttackDown)
                {
                    return(new AttackState());
                }

                // If we release the button mid-jump, cap our upward momentum and start to fall.
                if (cappable && input.Action == InputActions.JumpUp && !outsideExtendWindow)
                {
                    p.CapJumpIfRising();
                    return(CreateFallState());
                }

                // Walljumping.
                if (input.Action == InputActions.JumpDown)
                {
                    if (p.SweepForWall(Vector2.right))
                    {
                        return(PlayerController.WallJump(p, Vector2.left));
                    }
                    if (p.SweepForWall(Vector2.left))
                    {
                        return(PlayerController.WallJump(p, Vector2.right));
                    }
                }

                p.Input.Release(input);
            }

            if (p.Falling)
            {
                return(CreateFallState());
            }

            if (p.Surface.Unstable)
            {
                return(new FallSlideState());
            }

            return(null);
        }
        public IState Execute(PlayerController p)
        {
            p.ApplyGravity();
            p.DetermineFacing();

            if (accelerationCurve != null)
            {
                var accel = p.Physics.Acceleration * accelerationCurve.Evaluate(Time.time - atTime);
                p.Move(p.Physics.Speed, accel);
            }
            else
            {
                p.Move();
            }

            // Wall Jumping / Attacking
            foreach (var input in p.Input)
            {
                if (input.Action == InputActions.AttackDown)
                {
                    return(new AttackState());
                }

                if (input.Action == InputActions.JumpDown)
                {
                    if (p.SweepForWall(Vector2.right))
                    {
                        return(PlayerController.WallJump(p, Vector2.left));
                    }
                    if (p.SweepForWall(Vector2.left))
                    {
                        return(PlayerController.WallJump(p, Vector2.right));
                    }

                    // Coyote Time
                    if (p.StateMachine.PreviousState is RunState && !Utility.Elapsed(startTime, p.CoyoteTime))
                    {
                        return(new JumpState());
                    }
                }

                p.Input.Release(input);
            }

            if (p.Surface.Grounded)
            {
                var moving = p.GetSurfaceProjectedVelocity().sqrMagnitude > 0.01f;
                p.SpriteManager.SetAnimation(moving ? "LandMoving" : "LandStationary");
                return(new RunState());
            }

            if (p.Surface.ContactingWall)
            {
                // Wallslide.
                var i = p.GetSurfaceAlignedXInput();
                if (!p.AwayFromWall(i) && p.SweepForWall(-p.Surface.WallNormal))
                {
                    return(new WallSlideState());
                }
            }

            if (p.Surface.Unstable)
            {
                return(new FallSlideState());
            }

            return(null);
        }