/// <summary>
 /// Dismount the specified Ladder. This is used to stop
 /// autosticking to the same ladder you just dismounted.
 /// </summary>
 /// <param name="ladder">Ladder control to dismount.</param>
 public void Dismount(Ladder ladder)
 {
     Unparent ();
     dismounting = ladder.control;
     startedClimbing = false;
 }
    protected void MoveInYDirection(bool grounded)
    {
        float slope = 0.0f;
        int slopeCount = -1;
        bool isClimbing = false;
        bool cantClimbDown = false;
        bool hasHitHead = false;
        int climbCount = 0;
        groundedFeet = 0;
        isClimbingUpOrDown = false;

        // Create a copy of character input which we can modify;
        float characterInputY = characterInput.y;

        // Ignore input if stunned
        if (stunTimer > 0.0f)
            characterInputY = 0.0f;

        // Limit Velocity
        if (velocity.y < (movement.terminalVelocity * (IsSwimming ? (1 - swimming.waterResistance.y) : 1)))
            velocity.y = movement.terminalVelocity * (IsSwimming ? (1 - swimming.waterResistance.y) : 1);

        if (IsSwimming && velocity.y > swimming.maxYSpeed) velocity.y = swimming.maxYSpeed;

        // Apply velocity
        if ((myParent == null || !myParent.overrideY) && (velocity.y > movement.skinSize || velocity.y * -1 > movement.skinSize)) {
            myTransform.Translate (0.0f, velocity.y * frameTime, 0.0f, Space.World);
        }

        if (fallThroughTimer > 0.0f)
            fallThroughTimer -= frameTime;

        // Fall/Stop
        bool hasHitFeet = false;
        if ((velocity.y <= 0.0f || startedClimbing) && ledgeDropTimer <= 0.0f) {
            float maxForce = 0.0f;
            GameObject hitGameObject = null;
            float lastHitDistance = -1;
            float lastHitX = 0.0f;

            foreach (RaycastCollider feetCollider in feetColliders) {
                RaycastHit hitFeet = new RaycastHit ();
                RaycastHit hitLadder = new RaycastHit ();
                RaycastHit hitGround = new RaycastHit ();
                float closest = float.MaxValue;
                float closestLadder = float.MaxValue;

                RaycastHit[] feetCollisions = feetCollider.GetAllCollision (1 << backgroundLayer | (fallThroughTimer <= 0.0f ? 1 << passThroughLayer : 0) | (climbing.allowClimbing && fallThroughTimer <= 0.0f ? 1 << climableLayer : 0), slopes.slopeLookAhead);
                // Get closest collision
                foreach (RaycastHit collision in feetCollisions) {
                    // If we got a ladder also keep reference to ground
                    if (collision.collider != null && collision.collider.gameObject.layer == climableLayer) {
                        if (collision.distance < closestLadder) {
                            hitLadder = collision;
                            closestLadder = collision.distance;
                        }
                    } else if (collision.distance < closest) {
                        hitFeet = collision;
                        closest = collision.distance;
                    }
                    if (collision.collider.gameObject.layer != climableLayer)
                        groundedFeet++;
                }

                // If ladder is closest collider
                if (hitLadder.collider != null && hitFeet.collider != null && hitLadder.distance < closest) {
                    // Only assign ground if its a true hit, not a slope look ahead hit
                    if (hitFeet.distance <= feetCollider.distance && hitFeet.collider.gameObject.layer != climableLayer) {
                        hitGround = hitFeet;
                        hasHitFeet = true;
                    }
                    hitFeet = hitLadder;
                }

                // If only hitting a ladder
                if (hitLadder.collider != null && hitFeet.collider == null) {
                    hitFeet = hitLadder;
                }

                float force = (hitFeet.normal * (feetCollider.distance - hitFeet.distance)).y;
                // Standing on a something that has an action when you stand on it
                if (hitFeet.collider != null) {
                    Platform platform = hitFeet.collider.gameObject.GetComponent<Platform> ();
                    if (platform != null && feetCollider.distance >= hitFeet.distance) {
                        platform.DoAction (feetCollider, this);
                        Transform parentPlatform = platform.ParentOnStand (this);
                        if (parentPlatform != null) {
                            // Normal parenting (moving platforms etc)
                            myParent = platform;
                            if (myTransform.parent != parentPlatform) {
                                myTransform.parent = parentPlatform;
                            }
                            hitGameObject = hitFeet.collider.gameObject;
                        }
                        // Special case for the top of a ladder
                        if (platform is TopStepPlatform) {
                            hasHitFeet = true;
                            maxForce = force;
                            hitGameObject = hitLadder.collider.gameObject;
                        }
                    }
                    // Climbing
                    if (climbing.allowClimbing && hitLadder.collider != null && hitLadder.distance <= feetCollider.distance && jumpButtonTimer <= 0.0f) {
                        //bool ladder = hitLadder.collider.gameObject.GetComponent<Ladder>();
                        //if (ladder == null) hitLadder.collider.gameObject.GetComponent<Rope>(); // && ladder != null
                        climbCount++;
                        if (!(myParent is TopStepPlatform) && (startedClimbing || (climbing.autoStick && hitGround.collider == null && (dismounting == null || (myParent != null && dismounting != ((Ladder)myParent).control)) || characterInputY != 0))) {
                            if (climbCount >= climbing.collidersRequired) {
                                startedClimbing = true;
                                isClimbing = true;
                                hasHitFeet = true;
                                dismounting = null;
                                // Allow platforms to stop us climbing
                                if (myParent == null || !myParent.overrideY) {
                                    if (characterInputY > 0.0f) {
                                        // Ensure we dont go above step
                                        maxForce = climbing.speed * frameTime;
                                        if (maxForce > force)
                                            maxForce = force;
                                        isClimbingUpOrDown = true;
                                    } else if (!cantClimbDown && characterInputY < 0.0f) {
                                        maxForce = climbing.speed * frameTime * -1;
                                        // Ensure we don't go into ground
                                        isClimbingUpOrDown = true;
                                        if (hitGround.collider != null) {
                                            float groundForce = (hitGround.normal * (feetCollider.distance - hitGround.distance)).y;
                                            if (maxForce < groundForce)
                                                maxForce = groundForce;
                                            isClimbingUpOrDown = false;
                                            startedClimbing = false;
                                        }

                                    }
                                }
                            }
                        }
                    } else {
                        // Calculate slope
                        if (hitFeet.collider.gameObject.layer != climableLayer) {
                            if (lastHitDistance < 0.0f) {
                                lastHitDistance = hitFeet.distance;
                                lastHitX = feetCollider.offset.x;
                                if (slopeCount == -1)
                                    slopeCount = 0;
                            } else {
                                slope += Mathf.Atan ((lastHitDistance - hitFeet.distance) / (feetCollider.offset.x - lastHitX)) * Mathf.Rad2Deg;
                                slopeCount++;
                                lastHitDistance = hitFeet.distance;
                                lastHitX = feetCollider.offset.x;
                            }
                        }
                    }

                    // If we are hitting our feet on the ground we can't climb down a ladder
                    if (hitLadder.collider == null && characterInputY < 0.0f && hitFeet.distance <= feetCollider.distance) {
                        cantClimbDown = true;
                        maxForce = 0.0f;
                    }
                    // Get force to apply
                    if (force > maxForce && hitLadder.collider == null) {
                        // We hit a blocker stop all climbing
                        cantClimbDown = true;
                        isClimbingUpOrDown = false;
                        isClimbing = false;
                        startedClimbing = false;
                        hasHitFeet = true;
                        maxForce = force;
                        hitGameObject = hitFeet.collider.gameObject;
                    }

                }
            }
            if (startedClimbing && climbCount < climbing.collidersRequired) {
                startedClimbing = false;
            }

            if (hasHitFeet) {
                dismounting = null;
                if ((myParent == null || !myParent.overrideY)) {
                    if ((myParent is LadderCollider && ((LadderCollider)myParent).control.ShouldPlayLedgeClimb (this))) {
                        if (characterInput.y < 0.0f && myParent is TopStepPlatform && climbCount >= climbing.collidersRequired && groundedFeet == 0) {
                            isLadderTopClimbing = true;
                            State = CharacterState.CLIMB_TOP_OF_LADDER_DOWN;
                            ladderTopClimbState = LadderTopState.CLIMBING_DOWN;
                        } else if (characterInput.y > 0.0f && !(myParent is TopStepPlatform)) {
                            isLadderTopClimbing = true;
                            ladderTopClimbState = LadderTopState.CLIMBING_UP;
                            // Force position to be exactly right
                            myTransform.position = new Vector3 (myTransform.position.x, ((LadderCollider)myParent).LedgeClimbHeight, myTransform.position.z);
                        } else {
                            myTransform.Translate (0.0f, maxForce, 0.0f, Space.World);
                        }
                    } else {
                        myTransform.Translate (0.0f, maxForce, 0.0f, Space.World);
                    }
                }
                velocity.y = 0.0f;
                if (myParent != null && hitGameObject != myParent.gameObject && !(myParent is Rope)) {
                    Unparent ();
                }
                grounded = true;
                fallingTime = 0.0f;
            } else {
                if (myParent == null) {
                    ApplyGravity ();
                } else if (!grounded) {
                    ApplyGravity ();
                    if (!isWallSliding)
                        Unparent ();
                    startedClimbing = false;
                }
            }
        } else {
            // If we are mving through a passthrough pltform stop gravity for a little while
            // As long as pass through paltforms are kept thin this works quite well.
            bool holdOffOnGravity = false;
            foreach (RaycastCollider foot in feetColliders) {
                if (foot.IsColliding (1 << passThroughLayer)) {
                    holdOffOnGravity = true;
                }
            }
            if (!holdOffOnGravity)
                ApplyGravity ();
        }
        // Force dismount if we dont have enough colliders on the ladder
        if (climbCount < climbing.collidersRequired && myParent is Ladder) {
            Dismount ((Ladder)myParent);
        }

        // Stop crouch when climbing
        if (isClimbing)
            StopCrouch ();

        // Apply rotation from slopes
        if (slopes.allowSlopes) {
            float actualSlope = (myTransform.localEulerAngles.z % 360.0f) + (slope / (float)slopeCount);
            if (slopeCount > 0 && !isClimbing && (!(actualSlope > slopes.maxRotation && actualSlope < 360.0f - slopes.maxRotation))) {
                myTransform.Rotate (0.0f, 0.0f, slopes.rotationSpeed * (slope / (float)slopeCount));
            } else if (slopeCount == -1 || isClimbing) {
                myTransform.localRotation = Quaternion.RotateTowards (myTransform.localRotation, Quaternion.identity, slopes.rotationSpeed * 10.0f);
            }
            if ((actualSlope > slopes.maxRotation && actualSlope < 360.0f - slopes.maxRotation)) {
                myTransform.localRotation = Quaternion.Euler (0, 0, 0);
            }
        } else {
            if (slopeCount > 0) targetSlope =  (slope / (float)slopeCount);
            else targetSlope = 0.0f;
        }

        // Hitting Head
        if (velocity.y > 0.0f || isClimbing || (myParent != null && myParent.velocity.y > 0.0f)) {
            float maxForce = 0.0f;
            foreach (RaycastCollider headCollider in headColliders) {
                RaycastHit hitHead = headCollider.GetCollision (1 << backgroundLayer);
                float force = (hitHead.normal * (headCollider.distance - hitHead.distance)).y;
                if (hitHead.collider != null) {
                    // Action on headbut
                    Platform platform = hitHead.collider.gameObject.GetComponent<Platform> ();
                    if (platform != null) {
                        platform.DoAction (headCollider, this);
                    }
                    if (force < -1 * movement.skinSize && force < maxForce) {
                        hasHitHead = true;
                        maxForce = force;
                    }
                }
            }

            if (hasHitHead) {
                jumpHeldTimer = jump.jumpHeldTime;
                myTransform.Translate (0.0f, maxForce, 0.0f, Space.World);
                if (velocity.y > 0.0f)
                    velocity.y = 0.0f;
            }
            if (!isClimbing) {
                ApplyGravity ();
            }
        }

        // Jump
        if (!hasHitHead || isClimbing) {
            if (characterInput.jumpButtonDown) {
                if ((grounded || myParent != null) && jumpCount == 0 && jumpButtonTimer <= 0.0f) {
                    // Special case when launching from a rope
                    if (myParent != null && myParent is Rope) {
                        float k = Mathf.Abs (Mathf.Cos (Mathf.Deg2Rad * myTransform.rotation.eulerAngles.z));
                        k = Mathf.Pow (k, ((Rope)myParent).control.jumpFlattenFactor);
                        velocity = new Vector2 ((myParent.velocity.x + myParent.velocity.y) * ((Rope)myParent).control.ropeVelocityFactor,
                                                jump.jumpVelocity * k);
                        jumpCount = 2;
                    } else if (jump.inheritParentVelocityFactor != 0 && myParent != null) {
                        velocity.y = jump.jumpVelocity + (myParent.velocity * jump.inheritParentVelocityFactor).y;
                        velocity.x += (myParent.velocity * jump.inheritParentVelocityFactor).x;
                        jumpCount = 1;
                    } else {
                        velocity.y = jump.jumpVelocity * (IsSwimming ? (1 - swimming.waterResistance.y) : 1);
                        jumpCount = 1;
                    }
                    // Inherit parent velocity
                    Unparent ();
                    startedClimbing = false;
                    jumpButtonTimer = jump.jumpTimer;
                    jumpHeldTimer = 0.0f;
                    StopCrouch ();
                    State = CharacterState.JUMPING;
                } else if (jumpCount == 1 && jump.canDoubleJump && !IsSwimming) {
                    Unparent ();
                    startedClimbing = false;
                    jumpCount++;
                    velocity.y = jump.doubleJumpVelocity;
                    StopCrouch ();
                    State = CharacterState.DOUBLE_JUMPING;
                }
            } else if (characterInput.jumpButtonHeld && jumpHeldTimer < jump.jumpHeldTime && jumpCount == 1) {
                velocity.y += jump.jumpFrameVelocity * frameTime * (jump.jumpHeldTime - jumpHeldTimer);
                jumpHeldTimer += frameTime;
            }
        }

        // Swimming
        if (IsSwimming){
            if (State != CharacterState.JUMPING && jumpButtonTimer <= 0 && characterInput.swimButtonDown) {
                // Note this also adds acceleration in X!
                velocity.y += swimming.swimStrokeAcceleration.y * frameTime;
                velocity.x += swimming.swimStrokeAcceleration.x * CurrentDirection * frameTime;
                jumpButtonTimer = swimming.swimStrokeTime;
                State = CharacterState.SWIMMING;
            }
        }

        if (jumpButtonTimer > 0.0f)
            jumpButtonTimer -= frameTime;
        if (jumpButtonTimer <= 0.0f && grounded) {
            jumpCount = 0;
        }

        // Reset held button timer if we release button
        if (!characterInput.jumpButtonHeld) {
            jumpHeldTimer = jump.jumpHeldTime;
        }

        // Wall jump
        if (wallJumpTimer > 0) {
            wallJumpTimer -= frameTime;
        }

        if (wall.canWallJump && (wall.wallJumpTag == "" || wall.wallJumpTag == currentWallTag)) {
            // Easy wall jump
            if (wall.easyWallJump) {
                if (characterInput.jumpButtonDown && isWallHolding) {
                    Unparent();
                    startedClimbing = false;
                    velocity.y = jump.jumpVelocity;
                    jumpCount = 2;
                    wallJumpTimer = 0.0f;
                    jumpButtonTimer = jump.jumpTimer;
                    jumpHeldTimer = 0.0f;
                    StopCrouch();
                    State = CharacterState.WALL_JUMPING;
                    if (wall.wallJumpOnlyInOppositeDirection) {
                        oppositeDirectionTimer = wall.oppositeDirectionTime;
                        if (wallJumpDirection == RC_Direction.LEFT)  velocity.x = wall.wallJumpSpeed.x;
                        else if (wallJumpDirection == RC_Direction.RIGHT)  velocity.x =  -1 * wall.wallJumpSpeed.x;
                    }
                }
            }
            // "Hard" wall jump also works for easy wall jump
            if (wallJumpTimer > 0.0f) {
                if ((hasPressedJumpForWallJump && ((wallJumpDirection == RC_Direction.LEFT && characterInput.x > 0) || (wallJumpDirection == RC_Direction.RIGHT && characterInput.x < 0)))

                    || (hasPressedDirectionForWallJump && characterInput.jumpButtonDown)) {
                    Unparent();
                    startedClimbing = false;
                    velocity.y = wall.wallJumpSpeed.y;
                    jumpCount = 2;
                    wallJumpTimer = 0.0f;
                    jumpButtonTimer = jump.jumpTimer;
                    jumpHeldTimer = 0.0f;
                    StopCrouch();
                    State = CharacterState.WALL_JUMPING;
                    if (wall.wallJumpOnlyInOppositeDirection) {
                        oppositeDirectionTimer = wall.oppositeDirectionTime;
                        if (wallJumpDirection == RC_Direction.LEFT)  velocity.x = wall.wallJumpSpeed.x;
                        else if (wallJumpDirection == RC_Direction.RIGHT)  velocity.x =  -1 * wall.wallJumpSpeed.x;
                    }
                } else if (!hasPressedJumpForWallJump && characterInput.jumpButtonDown ) {
                    hasPressedJumpForWallJump = true;
                } else if (!hasPressedDirectionForWallJump && ((wallJumpDirection == RC_Direction.LEFT && characterInput.x > 0) || (wallJumpDirection == RC_Direction.RIGHT && characterInput.x < 0))) {
                    hasPressedDirectionForWallJump = true;
                }
            } else {
                hasPressedJumpForWallJump = false;
                hasPressedDirectionForWallJump = false;
            }
        }

        if (myParent != null && myParent.overrideAnimation) {
            State = myParent.GetAnimationState(this);
        } else {
            // Animations
            if ((!IsGrounded (groundedLookAhead, false) && !hasHitFeet) || jumpButtonTimer > 0.0f) {
                State = CharacterState.AIRBORNE;
            }

            if (velocity.y < jump.fallVelocity) {
                State = CharacterState.FALLING;
            }

            if (startedClimbing){
                if (isClimbingUpOrDown) {
                    State = CharacterState.CLIMBING;
                } else {
                    State = CharacterState.HOLDING;
                }
            }
        }
    }
    private void GenerateLadder()
    {
        try {
            float ladderTopDistance = float.Parse(this.ladderTopDistance);
            float stepSize          = float.Parse(this.stepSize);
            float totalLength       = float.Parse(this.totalLength);
            float stepDistance      = float.Parse(this.stepDistance);
            float ladderWidth       = float.Parse(this.ladderWidth);

            // Create parent
            GameObject ladderGo = new GameObject();
            ladderGo.name = "NewLadder";
            LadderControl control = ladderGo.AddComponent <LadderControl> ();

            if (useLadderTop)
            {
                control.ledgeClimbOffset = ladderTopDistance;
                // Create Top Step
                GameObject topStepGo = new GameObject();
                topStepGo.name  = "TopStep";
                topStepGo.layer = controller.climableLayer;
                TopStepPlatform topStep         = topStepGo.AddComponent <TopStepPlatform>();
                BoxCollider     topStepCollider = topStepGo.AddComponent <BoxCollider>();
                topStep.control                 = control;
                topStepCollider.size            = new Vector3(ladderWidth / 2.0f, stepSize / 2.0f, 0.5f);
                topStep.transform.parent        = ladderGo.transform;
                topStep.transform.localPosition = new Vector3(0, -1 * (stepSize / 2.0f), 0);
            }
            else
            {
                control.disableLedgeClimb = true;
            }

            // Create Steps

            int count = (int)((totalLength + (useLadderTop ? ladderTopDistance : 0)) / stepDistance);
            while (count > 0)
            {
                GameObject stepGo = new GameObject();
                stepGo.name  = "Step" + count;
                stepGo.layer = controller.climableLayer;
                LadderCollider step         = stepGo.AddComponent <LadderCollider>();
                BoxCollider    stepCollider = stepGo.AddComponent <BoxCollider>();
                step.control                 = control;
                stepCollider.size            = new Vector3(ladderWidth / 2.0f, stepSize / 2.0f, 0.5f);
                step.transform.parent        = ladderGo.transform;
                step.transform.localPosition = new Vector3(0, (-1 * count * stepDistance) + (stepSize / 2.0f) + (useLadderTop ? ladderTopDistance : 0), 0);

                count--;
            }

            // Create Mesh
            if (generateMesh && meshPrefab != null)
            {
                GameObject ladderMesh = (GameObject)Instantiate(meshPrefab);
                ladderMesh.transform.parent        = ladderGo.transform;
                ladderMesh.transform.localPosition = new Vector3(0, -1 * (totalLength / 2.0f), ladderMesh.transform.localPosition.z);
                ladderMesh.transform.localScale    = new Vector3(ladderWidth, totalLength, ladderMesh.transform.localScale.z);
            }
        } catch (System.Exception ex) {
            Debug.LogError("Failed to generate ladder... check parameters: " + ex.Message);
        }
    }