public void InitiateClimbMode(int p_direction, LedgeDetails p_prevLedge, LedgeDetails p_nextLedge)
    {
        if (!climbMode)
        {
            //Time.timeScale = 0.1f;

            //Debug.Log("Initiate Climb");

            climbMode    = true;
            direction    = p_direction;
            initialLedge = p_prevLedge;
            targetLedge  = p_nextLedge;

            if (plateauClimb)
            {
                //Debug.Log("Plateau Climb");
                animator.SetLayerWeight(animator.GetLayerIndex("RightHandIK"), 0);
                animator.SetLayerWeight(animator.GetLayerIndex("LeftHandIK"), 0);
                animator.SetLayerWeight(animator.GetLayerIndex("ClimbUp"), 1);
            }
            else
            {
                //Debug.Log("Ledge Climb");
                animator.SetLayerWeight(animator.GetLayerIndex("RightHandIK"), 1);
                animator.SetLayerWeight(animator.GetLayerIndex("LeftHandIK"), 1);
            }
        }
    }
    LedgeDetails CheckLedge(RaycastHit p_hit,
                            Vector3 p_initialStartPoint,
                            float p_precisionCorrection
                            )
    {
        LedgeDetails ledgeDetail     = new LedgeDetails();
        Vector3      facingDirection = new Vector3();

        //The surface must be top facing (In this case it will mostly be top facing)
        if (p_hit.normal.y > 0.0f)
        {
            //</TBD>Additional checks to surface inclination can be done here<TBD/>

            //Find the direction to face
            facingDirection = FindFacingDirection(p_hit, p_initialStartPoint, transform.forward);


            //Check if player's entire body will fit that spot (Plateau)
            ledgeDetail = CheckFit(bodyReference, bodyFitSize, p_hit, facingDirection, LedgeType.Plateau, p_precisionCorrection);
            //Check if player's hand will fit that spot
            if (!ledgeDetail.ledgeFound)
            {
                ledgeDetail = CheckFit(handReference, handFitSize, p_hit, facingDirection, LedgeType.Ledge,
                                       p_precisionCorrection //Adding a slight distance to the capsule cast, so that it doesnt fail because its done very close to the ledge
                                       );
            }
            return(ledgeDetail);
        }
        else
        {
            ledgeDetail.ledgeFound = false;
            return(ledgeDetail);
        }
    }
    void MoveToLedge(
        Vector3 p_ledgeTargetPosition,                 //The target position on the ledge
        Quaternion p_ledgeTargetRotation,              //The target rotation on the ledge. To make sure player faces the wall, if any
        GameObject p_offsetTarget,                     //The player will be positioned on the ledge relative to this position
        PlayerStates p_finalState                      //Once the player reaches the ledge, the player is set to be in this state
        )
    {
        Vector3    playerTargetPosition;
        Quaternion playerTargetRotation;

        //Slowly lerp towards the ledge/plateau
        playerTargetPosition = p_ledgeTargetPosition - p_ledgeTargetRotation * p_offsetTarget.transform.localPosition;

        transform.position = Vector3.Lerp(transform.position,
                                          //localPosition to make sure player base location is at the offset distance
                                          //i.e.,
                                          //if the location identified is for Hand to rest (the hand needs to rest on the ledge)
                                          //The movement is still being done in the body, w.r.t. hand
                                          //So we apply the Body -> Hand distance
                                          playerTargetPosition,
                                          Time.deltaTime * climbSpeed);



        playerTargetRotation = p_ledgeTargetRotation;



        transform.rotation = Quaternion.Slerp(transform.rotation,
                                              playerTargetRotation,
                                              Time.deltaTime * climbSpeed);;

        //How far from the target is the player
        distanceToLedge = Vector3.Distance(transform.position, playerTargetPosition);

        //If player is closer to the ledge/Plateau
        //SNAP to the target position
        if (distanceToLedge <= 0.1f)
        {
            //Debug.Log("Snapping to ledge");

            transform.position = playerTargetPosition;
            transform.rotation = playerTargetRotation;
            playerState        = p_finalState;
            //If the player is on a plateau now
            //Restore normal movement
            if (playerState == PlayerStates.Normal)
            {
                ledgeMode = false;
                TriggerNormalMode();
                //Reset the ledge details, so that it is not processed in the next frame
                ledgeDetail = new LedgeDetails();
            }

            ikDirection = 0;
        }
    }
 public void InitiateLedgeMode(LedgeDetails p_prevLedge, LedgeDetails p_nextLedge)
 {
     if (!ledgeMode)
     {
         //Debug.Log("Initiate Ledge");
         direction    = 0;
         initialLedge = p_prevLedge;
         targetLedge  = p_nextLedge;
         ledgeMode    = true;
         //blockIK = false;
         plateauClimb = false;
         animator.SetLayerWeight(animator.GetLayerIndex("RightHandIK"), 1);
         animator.SetLayerWeight(animator.GetLayerIndex("LeftHandIK"), 1);
         animator.SetLayerWeight(animator.GetLayerIndex("RegularAnimations"), 0);
         animator.SetLayerWeight(animator.GetLayerIndex("DoNothing"), 1);
         //ClimbingIK(true);
     }
 }
    LedgeDetails CheckFit(Transform p_referenceObject,
                          Vector3 p_referenceObjectSize,
                          RaycastHit p_hit,
                          Vector3 p_faceDirection,
                          LedgeType p_ledgeType,
                          float p_precisionCorrection = 0.0f
                          )
    {
        LedgeDetails ledgeDetail = new LedgeDetails();

        RaycastHit[] intersectingColliders;
        Collider[]   targetPositionCollisions = new Collider[0];
        Vector3      castCapsuleP1;
        Vector3      castCapsuleP2;

        Quaternion yRotation;
        Quaternion xzRotation;



        ledgeDetail.ledgeFound = false;

        //We are going to place the Hand/Body reference at the hit point
        //in the desired direction, and see if the hand/body will fit in the identified ledge
        p_referenceObject.rotation = Quaternion.identity;
        p_referenceObject.position = p_hit.point;

        //Forward will be rotated around Y, to face the expected direction
        //i.e., Rotate the reference object around Y to face the direction
        yRotation = Quaternion.FromToRotation(p_referenceObject.forward, p_faceDirection);
        p_referenceObject.rotation *= yRotation;

        //</TBD>Temporary comment
        //Debug.DrawRay(p_hit.point,
        //                p_faceDirection,
        //                    Color.yellow,
        //                        p_hit.normal.magnitude);
        //<TBD/>Temporary comment

        //Rotate the reference object around XZ to be aligned with the ledge/normal
        xzRotation = Quaternion.FromToRotation(p_referenceObject.up, p_hit.normal);
        p_referenceObject.rotation = xzRotation * p_referenceObject.rotation;

        //Move the aligned reference object slightly above the ledge
        //Adding 0.055f to make sure the box cast starts slightly above the identified ledge surface
        //This is to avoid collision of boxcast with the ledge itself
        p_referenceObject.transform.Translate(0, 0.055f + p_referenceObjectSize.y / 2, p_referenceObjectSize.z / 2);

        //Check if there is enough room for hand/player
        //If the below box cast (size of the hand/body) does not collide with anything, there is enough room for hand/player
        intersectingColliders = Physics.BoxCastAll(
            p_referenceObject.transform.position,
            new Vector3(p_referenceObjectSize.x / 2, p_referenceObjectSize.y / 2, p_referenceObjectSize.z / 2),
            p_referenceObject.transform.up,
            p_referenceObject.rotation,
            0.0f,
            movementBlockerLayers
            );
        handGizmo.transform.position = p_referenceObject.position;
        handGizmo.transform.rotation = p_referenceObject.rotation;
        if (p_ledgeType == LedgeType.Ledge)
        {
        }
        else
        {
            bodyGizmo.transform.position = p_referenceObject.position;
            bodyGizmo.transform.rotation = p_referenceObject.rotation;
        }

        if (intersectingColliders.Length == 0)
        {
            ledgeDetail.ledgeLocation  = p_hit.point;                                               //A ledge is found at this point
            ledgeDetail.targetRotation = Quaternion.identity;
            ledgeDetail.targetRotation = Quaternion.AngleAxis(yRotation.eulerAngles.y, Vector3.up); //Apply just the Y rotation to face player to the wall

            //Identify the capsule end points
            //i.e., if the player is going to be hanging from the ledge
            //will the player be colliding with anything
            castCapsuleP1 = (ledgeDetail.ledgeLocation - ledgeDetail.targetRotation * (handTarget.transform.localPosition + new Vector3(0, 0, p_precisionCorrection))) +
                            controller.center +
                            controller.transform.up *
                            -((controller.height * 0.5f) - controller.radius);
            castCapsuleP2 = castCapsuleP1 + controller.transform.up * (controller.height - (2 * controller.radius));

            capsuleCastPoint1 = castCapsuleP1;
            capsuleCastPoint2 = castCapsuleP2;

            if (!Physics.CheckCapsule(castCapsuleP1, castCapsuleP2, controller.radius, movementBlockerLayers))
            {
                ledgeDetail.ledgeFound = true; //A ledge has been found
                ledgeDetail.ledgeType  = p_ledgeType;
                return(ledgeDetail);
            }
        }
        else
        {
        }

        return(new LedgeDetails());
    }
    LedgeDetails DetectLedge_LB(Transform p_pivot,
                                Vector3 p_scanDirection,
                                float p_startDistance,
                                float p_endDistance,
                                float p_distanceSteps,
                                float p_scanStartHeight,
                                float p_scanDepth,
                                bool p_forcePrecisionMode = false,
                                float p_precisionSteps    = 0.01f
                                )
    {
        LedgeDetails ledgeDetail = new LedgeDetails();
        Vector3      rayStartPoint;
        RaycastHit   hit;
        Vector3      overlapCheckPosition = p_pivot.position;


        for (float i = p_startDistance; i <= p_endDistance; i += p_distanceSteps)
        {
            rayStartPoint = p_pivot.transform.position + (p_scanDirection.normalized * i) + (Vector3.up * p_scanStartHeight);

            if (p_forcePrecisionMode)
            {
                Debug.DrawRay(rayStartPoint, Vector3.down * p_scanDepth, Color.blue, 3.0f);
            }
            else
            {
            }

            if (!p_forcePrecisionMode)
            {
                //</TBD>Temporary comment
                //Debug.DrawRay(rayStartPoint, Vector3.down * p_scanDepth, Color.red, 3.0f);
                //<TBD/>Temporary comment
            }


            if (Physics.Raycast(rayStartPoint, Vector3.down, out hit, p_scanDepth, ledgeLayers))
            {
                overlapCheckPosition.y = hit.point.y - (minimumLedgeHeight / 2.0f);
                ledgeDetail            = new LedgeDetails();

                if (p_forcePrecisionMode)
                {
                    //if (p_forcePrecisionMode)
                    //{
                    //</TBD>Temporary comment
                    //Debug.DrawRay(rayStartPoint, Vector3.down * p_scanDepth, Color.black, 3.0f);
                    //<TBD/>Temporary comment
                    //}

                    //Force further scans to detect the correct edge
                    ledgeDetail = DetectLedge_LB(p_pivot, p_scanDirection, (i - p_distanceSteps), i, p_precisionSteps, p_scanStartHeight, p_scanDepth, false);
                    if (ledgeDetail.ledgeFound)
                    {
                        return(ledgeDetail);
                    }
                }
                else
                {
                    //If the start point (pivot) is inside any object, do not scan
                    Debug.DrawRay(overlapCheckPosition, (overlapCheckPosition - p_pivot.transform.position), Color.red, 3.0f);
                    if (Physics.CheckSphere(overlapCheckPosition, 0.01f, movementBlockerLayers))
                    {
                        return(ledgeDetail);
                    }

                    ledgeDetail = CheckLedge(hit,
                                             p_pivot.transform.position,
                                             p_distanceSteps
                                             );        //This is needed when doing capsule cast to see if player will fit the hanging space from the ledge, so that the cast doesnt fail because it happens too close to the ledge
                    if (ledgeDetail.ledgeFound)
                    {
                        return(ledgeDetail);
                    }
                }
            }
        }
        return(ledgeDetail);
    }
    LedgeDetails DetectLedgeToSide_LB(GameObject p_shoulder,
                                      Vector3 p_moveDirection,
                                      float p_startDistance,
                                      float p_endDistance,
                                      float p_distanceStep,
                                      float p_sweepStartAngle,
                                      float p_sweepEndAngle,
                                      float p_sweepStep,
                                      float p_sideScan_startDistance,
                                      float p_sideScan_endDistance,
                                      float p_sideScan_step,
                                      float p_sideScan_precisionStep,
                                      float p_scanHeight,
                                      float p_scanDepth,
                                      int p_dirMultiplier = 1,
                                      float p_dirAngle    = 0)
    {
        LedgeDetails ledgeDetail;


        for (float i = p_startDistance; i >= p_endDistance; i -= p_distanceStep)
        {
            for (float j = p_sweepStartAngle; j >= p_sweepEndAngle; j -= p_sweepStep)
            {
                pivotReferenceObject.transform.position = p_shoulder.transform.position;
                pivotReferenceObject.transform.rotation = p_shoulder.transform.rotation;

                pivotReferenceObject.transform.Rotate(0, -p_dirMultiplier * j, /*0*/ -p_dirMultiplier * p_dirAngle);

                pivotReferenceObject.transform.position += (pivotReferenceObject.transform.right.normalized * i * -p_dirMultiplier);

                if (j == p_sweepStartAngle)
                {
                    Debug.DrawRay(p_shoulder.transform.position, (pivotReferenceObject.transform.position - p_shoulder.transform.position).normalized * i, Color.gray, 3.0f);
                }
                else if (j == p_sweepEndAngle)
                {
                    Debug.DrawRay(p_shoulder.transform.position, (pivotReferenceObject.transform.position - p_shoulder.transform.position).normalized * i, Color.gray, 3.0f);
                }
                else
                {
                    Debug.DrawRay(p_shoulder.transform.position, (pivotReferenceObject.transform.position - p_shoulder.transform.position).normalized * i, Color.gray, 3.0f);
                }
                ledgeDetail = new LedgeDetails();
                ledgeDetail = DetectLedge_LB(pivotReferenceObject.transform,
                                             pivotReferenceObject.transform.forward,
                                             p_sideScan_startDistance,
                                             p_sideScan_endDistance,
                                             p_sideScan_step,
                                             p_scanHeight,
                                             p_scanDepth,
                                             true,
                                             p_sideScan_precisionStep
                                             );
                if (ledgeDetail.ledgeFound)
                {
                    return(ledgeDetail);
                }
            }
        }

        return(new LedgeDetails());
    }
    // Update is called once per frame
    void FixedUpdate()
    {
        if (ledgeDetail.ledgeFound)
        {
        }

        if (jumping)
        {
            if (IsLedgeScanWindow())
            {
                prevLedgeDetail = ledgeDetail;
                ledgeDetail     = DetectLedge_LB(chest.transform,
                                                 transform.forward,
                                                 lineBasedLedgeScanSettings.forwardScan_startDistance,
                                                 lineBasedLedgeScanSettings.forwardScan_endDistance,
                                                 lineBasedLedgeScanSettings.forwardScan_distanceStep,
                                                 lineBasedLedgeScanSettings.scan_Height, lineBasedLedgeScanSettings.scan_Depth /*(lineBasedLedgeScanSettings.scan_Height + 0.1f)*/, true, lineBasedLedgeScanSettings.forwardScan_PrecisionSteps);
                ledgeScanDone = true;
            }
        }

        if (ledgeMode)
        {
            if (playerState == PlayerStates.LedgeHanging || playerState == PlayerStates.PlateauHanging)
            {
                if (inputTranslator.jump_Pressed &&
                    (
                        movementSettings.camController.angleBetweenPlayerAndCamera >= -sideLeapAngleCheck &&
                        movementSettings.camController.angleBetweenPlayerAndCamera <= sideLeapAngleCheck
                    )
                    )
                {
                    prevLedgeDetail = ledgeDetail;
                    //If jump is pressed when hanging on a ledge, jump in the direction of the mouse
                    if (inputTranslator.mousePos.x != 0 &&
                        inputTranslator.mousePos.y != 0)
                    {
                        leapScanDirection = new Vector3(inputTranslator.mousePos.x, inputTranslator.mousePos.y, 0.0f);
                        leapScanRL        = (int)Mathf.Sign(inputTranslator.mousePos.x);
                        leapScanDirection = transform.rotation * leapScanDirection;
                        float angle;
                        angle = Vector3.SignedAngle(leapScanRL * transform.right, leapScanRL * transform.right + leapScanDirection, transform.forward);
                        angle = leapScanRL * angle;

                        if (angle <= lineBasedLedgeScanSettings.leapScan_thresholdAngle &&
                            angle >= -lineBasedLedgeScanSettings.leapScan_thresholdAngle)
                        {
                            //The jump distance for horizontal jump will be higher than the one for non horizontal jumps
                            leapScanOverrideStartDistance = lineBasedLedgeScanSettings.leapScan_startDistance;
                        }
                        else
                        {
                            leapScanOverrideStartDistance = lineBasedLedgeScanSettings.leapScan_thresholdBreachStartDistance;
                        }

                        if (leapScanRL > 0)
                        {
                            ledgeDetail = DetectLedgeToSide_LB(shoulder_r,
                                                               leapScanDirection,
                                                               leapScanOverrideStartDistance,
                                                               lineBasedLedgeScanSettings.leapScan_endDistance,
                                                               lineBasedLedgeScanSettings.leapScan_step,
                                                               lineBasedLedgeScanSettings.leapScan_sweepStartAngle,
                                                               lineBasedLedgeScanSettings.leapScan_sweepEndAngle,
                                                               lineBasedLedgeScanSettings.leapScan_sweepStep,
                                                               lineBasedLedgeScanSettings.leapScan_scanStartDistance,
                                                               lineBasedLedgeScanSettings.leapScan_scanEndDistance,
                                                               lineBasedLedgeScanSettings.leapScan_scanStep,
                                                               lineBasedLedgeScanSettings.leapScan_precisionStep,
                                                               lineBasedLedgeScanSettings.scan_Height,
                                                               lineBasedLedgeScanSettings.scan_Depth,
                                                               -1,
                                                               angle);
                            ledgeScanDone = true;

                            //If a ledge has been detected, IK needs to update towards the right side
                            ikDirection = 1;
                        }
                        else
                        {
                            //</TBD>Temporary comment
                            //Debug.DrawRay(shoulder_l.transform.position, leapScanDirection.normalized, Color.blue, 3.0f);
                            //<TBD/>Temporary comment
                            ledgeDetail = DetectLedgeToSide_LB(shoulder_l,
                                                               -transform.right + leapScanDirection,
                                                               leapScanOverrideStartDistance,
                                                               lineBasedLedgeScanSettings.leapScan_endDistance,
                                                               lineBasedLedgeScanSettings.leapScan_step,
                                                               lineBasedLedgeScanSettings.leapScan_sweepStartAngle,
                                                               lineBasedLedgeScanSettings.leapScan_sweepEndAngle,
                                                               lineBasedLedgeScanSettings.leapScan_sweepStep,
                                                               lineBasedLedgeScanSettings.leapScan_scanStartDistance,
                                                               lineBasedLedgeScanSettings.leapScan_scanEndDistance,
                                                               lineBasedLedgeScanSettings.leapScan_scanStep,
                                                               lineBasedLedgeScanSettings.leapScan_precisionStep,
                                                               lineBasedLedgeScanSettings.scan_Height,
                                                               lineBasedLedgeScanSettings.scan_Depth,
                                                               1,
                                                               angle);
                            ledgeScanDone = true;

                            //If a ledge has been detected, IK needs to update towards the left side
                            ikDirection = -1;
                        }
                    }
                }
                //While on ledge, player can move to the right/left or jump off the ledge
                else if (inputTranslator.rightLeftMovement > 0.0f)
                {
                    prevLedgeDetail = ledgeDetail;
                    //Scan to right, to move along the ledge

                    ledgeDetail = DetectLedgeToSide_LB(shoulder_r,
                                                       transform.right,
                                                       lineBasedLedgeScanSettings.sideScan_startDistance,
                                                       lineBasedLedgeScanSettings.sideScan_endDistance,
                                                       lineBasedLedgeScanSettings.sideScan_step,
                                                       lineBasedLedgeScanSettings.sideScan_sweepStartAngle,
                                                       lineBasedLedgeScanSettings.sideScan_sweepEndAngle,
                                                       lineBasedLedgeScanSettings.sideScan_sweepStep,
                                                       lineBasedLedgeScanSettings.sideScan_scanStartDistance,
                                                       lineBasedLedgeScanSettings.sideScan_scanEndDistance,
                                                       lineBasedLedgeScanSettings.sideScan_scanStep,
                                                       lineBasedLedgeScanSettings.sideScan_precisionStep,
                                                       lineBasedLedgeScanSettings.scan_Height,
                                                       lineBasedLedgeScanSettings.scan_Depth,
                                                       -1);
                    ledgeScanDone = true;
                    //If a ledge has been detected, IK needs to update towards the right side
                    ikDirection = 1;
                }
                else if (inputTranslator.rightLeftMovement < 0.0f)
                {
                    prevLedgeDetail = ledgeDetail;
                    //Scan to left, to move along the ledge
                    //Scan to right, to move along the ledge
                    ledgeDetail = DetectLedgeToSide_LB(shoulder_l,
                                                       -transform.right,
                                                       lineBasedLedgeScanSettings.sideScan_startDistance,
                                                       lineBasedLedgeScanSettings.sideScan_endDistance,
                                                       lineBasedLedgeScanSettings.sideScan_step,
                                                       lineBasedLedgeScanSettings.sideScan_sweepStartAngle,
                                                       lineBasedLedgeScanSettings.sideScan_sweepEndAngle,
                                                       lineBasedLedgeScanSettings.sideScan_sweepStep,
                                                       lineBasedLedgeScanSettings.sideScan_scanStartDistance,
                                                       lineBasedLedgeScanSettings.sideScan_scanEndDistance,
                                                       lineBasedLedgeScanSettings.sideScan_scanStep,
                                                       lineBasedLedgeScanSettings.sideScan_precisionStep,
                                                       lineBasedLedgeScanSettings.scan_Height,
                                                       lineBasedLedgeScanSettings.scan_Depth);
                    ledgeScanDone = true;
                    //If a ledge has been detected, IK needs to update towards the left side
                    ikDirection = -1;
                }
                else if (inputTranslator.frontBackMovement > 0)
                {
                    //When hanging from ledge and Forward button is pressed
                    //Initiate ledge climb
                    if (playerState == PlayerStates.PlateauHanging)
                    {
                        playerState = PlayerStates.PlateauClimbing;
                    }
                }
                if (inputTranslator.dropOffLedge_Pressed)
                {
                    //Drop off the ledge
                    if (playerState == PlayerStates.LedgeHanging ||
                        playerState == PlayerStates.PlateauHanging)
                    {
                        //Reset the current ledge Detail
                        ledgeDetail = new LedgeDetails();
                        playerState = PlayerStates.Normal;
                        TriggerNormalMode();
                    }
                }
            }
        }

        if (ledgeScanDone)
        {
            //********************************************************************************//
            //If a ledge is found, trigger movement to the identified point
            if (ledgeDetail.ledgeFound)
            {
                TriggerLedgeMode();
                if (ledgeDetail.ledgeType == LedgeType.Ledge)
                {
                    playerState = PlayerStates.LedgeClimbing;
                }
                else if (ledgeDetail.ledgeType == LedgeType.Plateau)
                {
                    playerState = PlayerStates.LedgeClimbing;
                }
                //}
            }
            else
            {
                ledgeDetail = prevLedgeDetail;
            }
            ledgeScanDone = false;
            //********************************************************************************//
        }

        if (playerState == PlayerStates.LedgeClimbing)
        {
            //Lerp the player toward the ledge
            if (ledgeDetail.ledgeType == LedgeType.Ledge)
            {
                MoveToLedge(ledgeDetail.ledgeLocation,
                            ledgeDetail.targetRotation,
                            handTarget,
                            PlayerStates.LedgeHanging);
            }
            else if (ledgeDetail.ledgeType == LedgeType.Plateau)
            {
                MoveToLedge(ledgeDetail.ledgeLocation,
                            ledgeDetail.targetRotation,
                            handTarget,
                            PlayerStates.PlateauHanging);
            }
        }
        else if (playerState == PlayerStates.PlateauClimbing)
        {
            //If forward is pressed while player is hanging off a plateau
            //Climb up the plateau
            {
                MoveToLedge(ledgeDetail.ledgeLocation,
                            ledgeDetail.targetRotation,
                            bodyTarget,
                            PlayerStates.Normal);
            }
        }

        HandleNormalMovement();
        Animate();
    }