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); }