Пример #1
0
        public bool Go()
        {
            if (this.main.TotalTime - this.LastJump < jumpCoolDown || this.Crouched)
            {
                return(false);
            }

            bool supported = this.IsSupported;

            WallRun.State wallRunState = this.WallRunState;

            Matrix  rotationMatrix = Matrix.CreateRotationY(this.Rotation);
            Vector3 forward        = -rotationMatrix.Forward;

            Vector2 jumpDirection = this.AbsoluteMovementDirection;

            Vector3 baseVelocity = Vector3.Zero;

            bool wallJumping = false;

            const float wallJumpHorizontalVelocityAmount = 0.75f;
            const float wallJumpDistance = 2.0f;

            Action <Voxel, Direction, Voxel.Coord> wallJump = delegate(Voxel wallJumpMap, Direction wallNormalDirection, Voxel.Coord wallCoordinate)
            {
                this.LastWallRunMap.Value    = wallJumpMap;
                this.LastWallDirection.Value = wallNormalDirection.GetReverse();
                this.LastWallJump.Value      = main.TotalTime;

                Voxel.State wallType = wallJumpMap[wallCoordinate];
                if (wallType == Voxel.States.Empty)                 // Empty. Must be a block possibility that hasn't been instantiated yet
                {
                    wallType = Voxel.States.Blue;
                }

                this.WalkedOn.Execute(wallJumpMap, wallCoordinate, wallNormalDirection.GetReverse());

                AkSoundEngine.PostEvent(AK.EVENTS.FOOTSTEP_PLAY, this.Entity);

                wallJumping = true;
                // Set up wall jump velocity
                Vector3 absoluteWallNormal = wallJumpMap.GetAbsoluteVector(wallNormalDirection.GetVector());
                Vector2 wallNormal2        = new Vector2(absoluteWallNormal.X, absoluteWallNormal.Z);
                wallNormal2.Normalize();

                bool wallRunningStraight = wallRunState == WallRun.State.Straight || wallRunState == WallRun.State.Down;
                if (wallRunningStraight)
                {
                    jumpDirection = new Vector2(main.Camera.Forward.Value.X, main.Camera.Forward.Value.Z);
                }
                else
                {
                    jumpDirection = new Vector2(forward.X, forward.Z);
                }

                jumpDirection.Normalize();

                float dot = Vector2.Dot(wallNormal2, jumpDirection);
                if (dot < 0)
                {
                    jumpDirection = jumpDirection - (2.0f * dot * wallNormal2);
                }
                jumpDirection *= wallJumpHorizontalVelocityAmount;

                if (!wallRunningStraight && Math.Abs(dot) < 0.5f)
                {
                    // If we're jumping perpendicular to the wall, add some velocity so we jump away from the wall a bit
                    jumpDirection += wallJumpHorizontalVelocityAmount * 0.75f * wallNormal2;
                }

                Vector3 supportLocation = this.FloorPosition;
                baseVelocity += wallJumpMap.LinearVelocity + Vector3.Cross(wallJumpMap.AngularVelocity, supportLocation - wallJumpMap.Transform.Value.Translation);
            };

            if (!supported && wallRunState == WallRun.State.None &&
                this.LinearVelocity.Value.Y > Lemma.Components.FallDamage.DamageVelocity * 1.5f)
            {
                // We're not doing our normal jump, and not wall-runnign
                // See if we can wall-jump
                Vector3 playerPos = this.Position;
                Voxel.GlobalRaycastResult?closestWallRaycastHit = null;
                Vector3 closestWallRaycastDirection             = Vector3.Zero;

                foreach (Vector3 dir in new[] { rotationMatrix.Left, rotationMatrix.Right, rotationMatrix.Backward, rotationMatrix.Forward })
                {
                    Voxel.GlobalRaycastResult hit = Voxel.GlobalRaycast(playerPos, dir, wallJumpDistance);
                    if (hit.Voxel != null && (!closestWallRaycastHit.HasValue || hit.Distance < closestWallRaycastHit.Value.Distance))
                    {
                        closestWallRaycastDirection = dir;
                        closestWallRaycastHit       = hit;
                    }
                }

                if (closestWallRaycastHit != null)
                {
                    Voxel m = closestWallRaycastHit.Value.Voxel;
                    wallJump(m, closestWallRaycastHit.Value.Normal, closestWallRaycastHit.Value.Coordinate.Value);
                }
            }

            // If we're wall-running, we can wall-jump
            // Add some velocity so we jump away from the wall a bit
            if (wallRunState != WallRun.State.None)
            {
                Vector3     pos       = this.FloorPosition + new Vector3(0, 0.5f, 0);
                Voxel.Coord wallCoord = this.WallRunMap.Value.GetCoordinate(pos).Move(this.WallDirection, 2);
                wallJump(this.WallRunMap, this.WallDirection.Value.GetReverse(), wallCoord);
            }

            bool go = supported || wallJumping;

            BlockPredictor.Possibility instantiatedBlockPossibility = null;
            Voxel.Coord instantiatedBlockPossibilityCoord           = default(Voxel.Coord);

            if (!go)
            {
                // Check block possibilities beneath us
                Vector3 jumpPos = this.FloorPosition + new Vector3(0, -1.0f, 0);
                foreach (BlockPredictor.Possibility possibility in this.Predictor.AllPossibilities)
                {
                    Voxel.Coord possibilityCoord = possibility.Map.GetCoordinate(jumpPos);
                    if (possibilityCoord.Between(possibility.StartCoord, possibility.EndCoord) &&
                        !possibility.Map.GetCoordinate(jumpPos + new Vector3(2.0f)).Between(possibility.StartCoord, possibility.EndCoord))
                    {
                        this.Predictor.InstantiatePossibility(possibility);
                        go = true;
                        instantiatedBlockPossibility      = possibility;
                        instantiatedBlockPossibilityCoord = possibilityCoord;
                        break;
                    }
                }
            }

            if (!go)
            {
                // Check block possibilities for wall jumping
                Vector3   playerPos          = this.Position;
                Vector3[] wallJumpDirections = new[] { rotationMatrix.Left, rotationMatrix.Right, rotationMatrix.Forward };
                foreach (BlockPredictor.Possibility possibility in this.Predictor.AllPossibilities)
                {
                    foreach (Vector3 dir in wallJumpDirections)
                    {
                        foreach (Voxel.Coord coord in possibility.Map.Rasterize(playerPos, playerPos + (dir * wallJumpDistance)))
                        {
                            if (coord.Between(possibility.StartCoord, possibility.EndCoord))
                            {
                                this.Predictor.InstantiatePossibility(possibility);
                                instantiatedBlockPossibility      = possibility;
                                instantiatedBlockPossibilityCoord = coord;
                                wallJump(possibility.Map, possibility.Map.GetRelativeDirection(dir).GetReverse(), coord);
                                wallJumping = true;
                                go          = true;
                                break;
                            }
                        }
                        if (wallJumping)
                        {
                            break;
                        }
                    }
                    if (wallJumping)
                    {
                        break;
                    }
                }
            }

            if (go)
            {
                float totalMultiplier = 1.0f;

                bool grunted = false;

                if (wallJumping)
                {
                    if (this.wallJumpCount == 0)
                    {
                        this.wallJumpChainStart = this.Position;
                    }
                    else
                    {
                        Vector3 chainDistance = this.Position - this.wallJumpChainStart;
                        chainDistance.Y = 0.0f;
                        if (chainDistance.Length() > 6.0f)
                        {
                            this.wallJumpCount      = 0;
                            this.wallJumpChainStart = this.Position;
                        }
                    }

                    if (this.wallJumpCount > 3)
                    {
                        return(false);
                    }
                    totalMultiplier = 1.0f - Math.Min(1.0f, this.wallJumpCount / 8.0f);
                    this.wallJumpCount++;
                }
                else
                {
                    // Regular jump
                    // Take base velocity into account
                    baseVelocity += this.SupportVelocity;

                    grunted = this.FallDamage.ApplyJump();
                    if (!this.Active)                     // We got killed by fall damage
                    {
                        return(false);
                    }
                    if (instantiatedBlockPossibility != null)
                    {
                        this.WalkedOn.Execute(instantiatedBlockPossibility.Map, instantiatedBlockPossibilityCoord, instantiatedBlockPossibility.Map.GetRelativeDirection(Direction.NegativeY));
                    }

                    // Also manually reset our ability to kick and wall-jump
                    this.CanKick.Value = true;
                    this.wallJumpCount = 0;
                }

                Vector3 velocity             = this.LinearVelocity - baseVelocity;
                float   currentVerticalSpeed = velocity.Y;
                velocity.Y = 0.0f;
                float jumpSpeed = jumpDirection.Length();
                if (jumpSpeed > 0)
                {
                    jumpDirection *= (wallJumping ? Math.Max(this.MaxSpeed, Vector3.Dot(forward, velocity)) : velocity.Length()) / jumpSpeed;
                }

                if (main.TotalTime - this.LastRollKickEnded < 0.3f)
                {
                    totalMultiplier *= 1.2f;
                }

                float verticalJumpSpeed = this.JumpSpeed;

                // If we're not instantiating a block possibility beneath us or we're not currently falling, incorporate some of our existing vertical velocity in our jump
                if (instantiatedBlockPossibility == null || wallJumping || currentVerticalSpeed > 0.0f)
                {
                    verticalJumpSpeed += currentVerticalSpeed * 0.5f;
                }

                this.LinearVelocity.Value = baseVelocity + new Vector3(jumpDirection.X, verticalJumpSpeed, jumpDirection.Y) * totalMultiplier;

                velocity   = this.LinearVelocity;
                velocity.Y = 0.0f;
                this.LastSupportedSpeed.Value = velocity.Length();

                if (supported && this.SupportEntity.Value != null)
                {
                    Vector3 impulsePosition = this.FloorPosition;
                    Vector3 impulse         = this.LinearVelocity.Value * this.Mass * -1.0f;
                    this.SupportEntity.Value.ApplyImpulse(ref impulsePosition, ref impulse);
                }

                Session.Recorder.Event(main, "Jump");

                this.IsSupported.Value   = false;
                this.SupportEntity.Value = null;
                this.HasTraction.Value   = false;

                AkSoundEngine.PostEvent(AK.EVENTS.PLAY_PLAYER_JUMP, this.Entity);
                if (!grunted && (float)this.random.NextDouble() < 0.15f)
                {
                    AkSoundEngine.PostEvent(AK.EVENTS.PLAY_PLAYER_LAND, this.Entity);                     // Grunt
                }
                this.model.Stop
                (
                    "Vault",
                    "Mantle",
                    "TopOut",
                    "Jump",
                    "Jump02",
                    "Jump03",
                    "JumpLeft",
                    "JumpRight",
                    "JumpBackward"
                );

                velocity   = -Vector3.TransformNormal(this.LinearVelocity, Matrix.CreateRotationY(-this.Rotation));
                velocity.Y = 0.0f;
                if (wallRunState == WallRun.State.Left || wallRunState == WallRun.State.Right)
                {
                    velocity.Z = 0.0f;
                }
                else if (wallJumping)
                {
                    velocity.Z *= 0.5f;
                }
                else
                {
                    velocity.X = 0.0f;
                }
                Direction direction = DirectionExtensions.GetDirectionFromVector(velocity);
                string    animation;
                switch (direction)
                {
                case Direction.NegativeX:
                    animation = "JumpLeft";
                    break;

                case Direction.PositiveX:
                    animation = "JumpRight";
                    break;

                case Direction.PositiveZ:
                    animation = wallJumping ? "JumpBackward" : this.randomJumpAnimation();
                    break;

                default:
                    animation = this.randomJumpAnimation();
                    break;
                }
                this.model.StartClip(animation, 4, false);
                this.model[animation].CurrentTime = TimeSpan.FromSeconds(0.2);

                // Deactivate any wall-running we're doing
                this.DeactivateWallRun.Execute();

                // Play a footstep sound since we're jumping off the ground
                AkSoundEngine.PostEvent(AK.EVENTS.FOOTSTEP_PLAY, this.Entity);

                this.LastJump.Value = this.main.TotalTime;
                return(true);
            }

            return(false);
        }