/// <summary>
        /// Perform climbing check, and if successful, start climbing movement.
        /// </summary>
        public void ClimbingCheck()
        {
            bool advancedClimbingOn = DaggerfallUnity.Settings.AdvancedClimbing;

            // true if we should try climbing wall and are airborne
            bool airborneGraspWall = (!isClimbing && !isSlipping && acrobatMotor.Falling);
            bool inputBack         = InputManager.Instance.HasAction(InputManager.Actions.MoveBackwards);
            bool inputForward      = InputManager.Instance.HasAction(InputManager.Actions.MoveForwards);
            //inputForward = true;

            // boolean that means ground directly below us is too close for climbing or rappelling
            bool tooCloseToGroundForClimb = (((isClimbing && (inputBack || isSlipping)) || airborneGraspWall)
                                             // short circuit evaluate the raycast, also prevents bug where you could teleport across town
                                             && Physics.Raycast(controller.transform.position, Vector3.down, controller.height / 2 + 0.12f));

            CalcFrequencyAndToleranceOfWallChecks(airborneGraspWall);

            bool inputAbortCondition;

            if (advancedClimbingOn)
            {
                // TODO: prevent crouch from toggling crouch when aborting climb
                inputAbortCondition = (InputManager.Instance.HasAction(InputManager.Actions.Crouch) ||
                                       InputManager.Instance.HasAction(InputManager.Actions.Jump));
            }
            else
            {
                inputAbortCondition = !inputForward;
            }

            // reset for next use
            WallEject = false;

            if (releasedFromCeiling)
            {
                MoveForwardIfNotClimbing();
            }
            if ((playerMotor.CollisionFlags & CollisionFlags.Sides) != 0)
            {
                releasedFromCeiling = false;
            }

            bool horizontallyStationary = Vector2.Distance(lastHorizontalPosition, new Vector2(controller.transform.position.x, controller.transform.position.z)) < startClimbHorizontalTolerance;
            bool touchingSides          = (playerMotor.CollisionFlags & CollisionFlags.Sides) != 0;
            bool touchingGround         = (playerMotor.CollisionFlags & CollisionFlags.Below) != 0;
            //bool touchingAbove = (playerMotor.CollisionFlags & CollisionFlags.Above) != 0;
            bool slippedToGround    = isSlipping && touchingGround;
            bool nonOrthogonalStart = !isClimbing && inputForward && !horizontallyStationary;
            //bool forwardStationaryNearCeiling = inputForward && hangingMotor.IsWithinHangingDistance && horizontallyStationary;
            bool pushingFaceAgainstWallNearCeiling = false; //hangingMotor.IsHanging && !isClimbing && touchingSides && forwardStationaryNearCeiling;
            bool climbingOrForwardOrGrasping       = (isClimbing || inputForward || airborneGraspWall);
            bool hangTouchNonVertical = false;              //hangingMotor.IsHanging && touchingSides && Physics.Raycast(controller.transform.position, controller.transform.forward, out hit, 0.40f) && Mathf.Abs(hit.normal.y) > 0.06f;

            ClimbQuitMoveUnderToHang = (inputBack && !moveScanner.HitSomethingInFront && moveScanner.FrontUnderCeiling != null);

            // Allow climbing slight overhangs when capsule hits above but upwards test rays have clear space overhead
            // Works by gently bumping player capsule away from wall at point of contact so they can acquire new vertical climb position
            // Provided angle not too extreme then player will keep climbing upwards or downwards
            // Allows player to climb up gently angled positions like the coffin tunnel in Scourg Barrow and up over shallow eaves
            if (isClimbing && (playerMotor.CollisionFlags & CollisionFlags.Above) == CollisionFlags.Above)
            {
                Vector3 frontTestPosition = controller.transform.position + wallDirection * controller.radius * 0.9f;
                Vector3 backTestPosition  = controller.transform.position - wallDirection * controller.radius * 0.1f;

                //Debug.DrawLine(frontTestPosition, frontTestPosition + Vector3.up * 2, Color.red);
                //Debug.DrawLine(backTestPosition, backTestPosition + Vector3.up * 2, Color.red);

                // Test close to front of head for backward sloping wall like Scourg Barrow and bump a little backwards
                // Then slightly further back for short overhangs like eaves and bump more backwards and a little upwards
                // Height of raycast test is extended to help ensure there is clear space above not just an angled ceiling
                if (!Physics.Raycast(frontTestPosition, Vector3.up, controller.height / 2 + 0.3f))
                {
                    controller.transform.position += -wallDirection * 0.1f;
                }
                else if (!Physics.Raycast(backTestPosition, Vector3.up, controller.height / 2 + 0.5f))
                {
                    controller.transform.position += -wallDirection * 0.4f + Vector3.up * 0.3f;
                }
            }

            // Handle recently restoring from save game where climbing active
            if (isClimbing && touchingSidesRestoreForce && !touchingSides)
            {
                touchingSides = true;
                //Debug.Log("Forced touchingSides...");
            }
            else
            {
                touchingSidesRestoreForce = false;
            }

            // Should we reset climbing starter timers?
            wasClimbing = isClimbing;
            if ((!pushingFaceAgainstWallNearCeiling)
                &&
                (inputAbortCondition ||
                 ClimbQuitMoveUnderToHang ||
                 !climbingOrForwardOrGrasping ||
                 !touchingSides && !releasedFromCeiling ||
                 levitateMotor.IsLevitating ||
                 playerMotor.IsRiding ||
                 slippedToGround
                 // quit climbing if climbing down and ground is really close, prevents teleportation bug
                 || tooCloseToGroundForClimb
                 // don't do horizontal position check if already climbing
                 || nonOrthogonalStart
                 // if we're hanging, and touching sides with a wall that isn't mostly vertical
                 || hangTouchNonVertical))
            {
                if (isClimbing && inputAbortCondition && advancedClimbingOn)
                {
                    WallEject = true;
                }

                StopClimbing();
                releasedFromCeiling = false;
                // Reset position for horizontal distance check and timer to wait for climbing start
                lastHorizontalPosition = new Vector2(controller.transform.position.x, controller.transform.position.z);
            }
            else // countdown climbing events
            {
                // countdown to climbing start
                if (climbingStartTimer <= (playerMotor.systemTimerUpdatesDivisor * startClimbSkillCheckFrequency))
                {
                    climbingStartTimer += Time.deltaTime;
                }
                // Begin Climbing
                else if (!isClimbing)
                {
                    //if (hangingMotor.IsHanging)
                    //{   // grab wall from ceiling
                    //    overrideSkillCheck = true;
                    //    releasedFromCeiling = true;
                    //}

                    // automatic success if not falling
                    if ((!airborneGraspWall /*&& !hangingMotor.IsHanging*/) || releasedFromCeiling)
                    {
                        if (ClimbingSkillCheck(startClimbMinChance))
                        {
                            StartClimbing();
                        }
                    } // skill check to see if we catch the wall
                    else if (ClimbingSkillCheck(graspWallMinChance))
                    {
                        StartClimbing();
                    }
                    else
                    {
                        climbingStartTimer = 0;
                    }
                }

                if (isClimbing)
                {
                    // countdown to climb update, Faster updates if slipping
                    if (climbingContinueTimer <= (playerMotor.systemTimerUpdatesDivisor * (isSlipping ? regainHoldSkillCheckFrequency : continueClimbingSkillCheckFrequency)))
                    {
                        climbingContinueTimer += Time.deltaTime;
                    }
                    else
                    {
                        climbingContinueTimer = 0;

                        // don't allow slipping if not moving.
                        if (!InputManager.Instance.HasAction(InputManager.Actions.MoveForwards) &&
                            !InputManager.Instance.HasAction(InputManager.Actions.MoveBackwards) &&
                            !InputManager.Instance.HasAction(InputManager.Actions.MoveLeft) &&
                            !InputManager.Instance.HasAction(InputManager.Actions.MoveRight))
                        {
                            isSlipping = false;
                        }
                        // it's harder to regain hold while slipping than it is to continue climbing with a good hold on wall
                        else if (isSlipping)
                        {
                            isSlipping = !ClimbingSkillCheck(regainHoldMinChance);
                        }
                        else
                        {
                            isSlipping = !ClimbingSkillCheck(continueClimbMinChance);
                        }
                    }
                }
            }

            // Climbing Cycle
            if (isClimbing)
            {
                // evalate the ledge direction
                GetClimbedWallInfo();

                ClimbMovement();

                // both variables represent similar situations, but different context
                acrobatMotor.Falling = isSlipping;
            }
            else if (!rappelMotor.IsRappelling)
            {
                isSlipping              = false;
                atOutsideCorner         = false;
                atInsideCorner          = false;
                showClimbingModeMessage = true;
                moveDirection           = Vector3.zero;
                // deletes locally and saves myLedgeDirection to variable in class
                if (myLedgeDirection != Vector3.zero)
                {
                    moveScanner.CutAndPasteVectorToDetached(ref myLedgeDirection);
                }
            }
        }
Exemple #2
0
        /// <summary>
        /// Perform climbing check, and if successful, start climbing movement.
        /// </summary>
        public void ClimbingCheck()
        {
            bool advancedClimbingOn = DaggerfallUnity.Settings.AdvancedClimbing;

            // true if we should try climbing wall and are airborne
            bool airborneGraspWall = (!isClimbing && !isSlipping && acrobatMotor.Falling);
            bool inputBack         = InputManager.Instance.HasAction(InputManager.Actions.MoveBackwards);
            bool inputForward      = InputManager.Instance.HasAction(InputManager.Actions.MoveForwards);
            //inputForward = true;

            // boolean that means ground directly below us is too close for climbing or rappelling
            bool tooCloseToGroundForClimb = (((isClimbing && (inputBack || isSlipping)) || airborneGraspWall)
                                             // short circuit evaluate the raycast, also prevents bug where you could teleport across town
                                             && Physics.Raycast(controller.transform.position, Vector3.down, controller.height / 2 + 0.12f));

            CalcFrequencyAndToleranceOfWallChecks(airborneGraspWall);

            bool inputAbortCondition;

            if (advancedClimbingOn)
            {
                // TODO: prevent crouch from toggling crouch when aborting climb
                inputAbortCondition = (InputManager.Instance.HasAction(InputManager.Actions.Crouch) ||
                                       InputManager.Instance.HasAction(InputManager.Actions.Jump));
            }
            else
            {
                inputAbortCondition = !inputForward;
            }

            // reset for next use
            WallEject = false;

            if (releasedFromCeiling)
            {
                MoveForwardIfNotClimbing();
            }
            if ((playerMotor.CollisionFlags & CollisionFlags.Sides) != 0)
            {
                releasedFromCeiling = false;
            }

            bool horizontallyStationary = Vector2.Distance(lastHorizontalPosition, new Vector2(controller.transform.position.x, controller.transform.position.z)) < startClimbHorizontalTolerance;
            bool touchingSides          = (playerMotor.CollisionFlags & CollisionFlags.Sides) != 0;
            bool touchingGround         = (playerMotor.CollisionFlags & CollisionFlags.Below) != 0;
            //bool touchingAbove = (playerMotor.CollisionFlags & CollisionFlags.Above) != 0;
            bool slippedToGround    = isSlipping && touchingGround;
            bool nonOrthogonalStart = !isClimbing && inputForward && !horizontallyStationary;
            //bool forwardStationaryNearCeiling = inputForward && hangingMotor.IsWithinHangingDistance && horizontallyStationary;
            bool pushingFaceAgainstWallNearCeiling = false; //hangingMotor.IsHanging && !isClimbing && touchingSides && forwardStationaryNearCeiling;
            bool climbingOrForwardOrGrasping       = (isClimbing || inputForward || airborneGraspWall);
            bool hangTouchNonVertical = false;              //hangingMotor.IsHanging && touchingSides && Physics.Raycast(controller.transform.position, controller.transform.forward, out hit, 0.40f) && Mathf.Abs(hit.normal.y) > 0.06f;

            ClimbQuitMoveUnderToHang = (inputBack && !moveScanner.HitSomethingInFront && moveScanner.FrontUnderCeiling != null);

            // Should we reset climbing starter timers?
            if ((!pushingFaceAgainstWallNearCeiling)
                &&
                (inputAbortCondition ||
                 ClimbQuitMoveUnderToHang ||
                 !climbingOrForwardOrGrasping ||
                 !touchingSides && !releasedFromCeiling ||
                 levitateMotor.IsLevitating ||
                 playerMotor.IsRiding ||
                 slippedToGround
                 // quit climbing if climbing down and ground is really close, prevents teleportation bug
                 || tooCloseToGroundForClimb
                 // don't do horizontal position check if already climbing
                 || nonOrthogonalStart
                 // if we're hanging, and touching sides with a wall that isn't mostly vertical
                 || hangTouchNonVertical))
            {
                if (isClimbing && inputAbortCondition && advancedClimbingOn)
                {
                    WallEject = true;
                }

                StopClimbing();
                releasedFromCeiling = false;
                // Reset position for horizontal distance check and timer to wait for climbing start
                lastHorizontalPosition = new Vector2(controller.transform.position.x, controller.transform.position.z);
            }
            else // countdown climbing events
            {
                // countdown to climbing start
                if (climbingStartTimer <= (playerMotor.systemTimerUpdatesDivisor * startClimbSkillCheckFrequency))
                {
                    climbingStartTimer += Time.deltaTime;
                }
                // Begin Climbing
                else if (!isClimbing)
                {
                    //if (hangingMotor.IsHanging)
                    //{   // grab wall from ceiling
                    //    overrideSkillCheck = true;
                    //    releasedFromCeiling = true;
                    //}

                    // automatic success if not falling
                    if ((!airborneGraspWall /*&& !hangingMotor.IsHanging*/) || releasedFromCeiling)
                    {
                        if (ClimbingSkillCheck(startClimbMinChance))
                        {
                            StartClimbing();
                        }
                    } // skill check to see if we catch the wall
                    else if (ClimbingSkillCheck(graspWallMinChance))
                    {
                        StartClimbing();
                    }
                    else
                    {
                        climbingStartTimer = 0;
                    }
                }

                if (isClimbing)
                {
                    // countdown to climb update, Faster updates if slipping
                    if (climbingContinueTimer <= (playerMotor.systemTimerUpdatesDivisor * (isSlipping ? regainHoldSkillCheckFrequency : continueClimbingSkillCheckFrequency)))
                    {
                        climbingContinueTimer += Time.deltaTime;
                    }
                    else
                    {
                        climbingContinueTimer = 0;

                        // don't allow slipping if not moving.
                        if (!InputManager.Instance.HasAction(InputManager.Actions.MoveForwards) &&
                            !InputManager.Instance.HasAction(InputManager.Actions.MoveBackwards) &&
                            !InputManager.Instance.HasAction(InputManager.Actions.MoveLeft) &&
                            !InputManager.Instance.HasAction(InputManager.Actions.MoveRight))
                        {
                            isSlipping = false;
                        }
                        // it's harder to regain hold while slipping than it is to continue climbing with a good hold on wall
                        else if (isSlipping)
                        {
                            isSlipping = !ClimbingSkillCheck(regainHoldMinChance);
                        }
                        else
                        {
                            isSlipping = !ClimbingSkillCheck(continueClimbMinChance);
                        }
                    }
                }
            }

            // Climbing Cycle
            if (isClimbing)
            {
                // evalate the ledge direction
                GetClimbedWallInfo();

                ClimbMovement();

                // both variables represent similar situations, but different context
                acrobatMotor.Falling = isSlipping;
            }
            else if (!rappelMotor.IsRappelling)
            {
                isSlipping              = false;
                atOutsideCorner         = false;
                atInsideCorner          = false;
                showClimbingModeMessage = true;
                moveDirection           = Vector3.zero;
                // deletes locally and saves myLedgeDirection to variable in class
                if (myLedgeDirection != Vector3.zero)
                {
                    moveScanner.CutAndPasteVectorToDetached(ref myLedgeDirection);
                }
            }
        }