public override WormAIBaseState Update()
    {
        switch (subState)
        {
        case SubState.GOING_TO_ENTRY:
            //Position head below entry point
            currentX = bb.GetJumpXGivenY(-WormBlackboard.NAVMESH_LAYER_HEIGHT, false);
            Vector3 startPosition = bb.GetJumpPositionGivenY(-WormBlackboard.NAVMESH_LAYER_HEIGHT, false);
            headTrf.position = startPosition;
            lastPosition     = startPosition;
            head.SetVisible(true);

            origin.WormEnterExit();

            subState = SubState.JUMPING;
            break;

        case SubState.JUMPING:
            //While not again below underground navmesh layer advance
            currentX        += Time.deltaTime * bb.spawnSpeed;
            lastPosition     = headTrf.position;
            headTrf.position = bb.GetJumpPositionGivenX(currentX);

            headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition), headTrf.up);

            if (lastPosition.y > headTrf.position.y && rotation < 90f)
            {
                if (!highestPointReached)
                {
                    highestPointReached = true;
                }

                float angle = 30 * Time.deltaTime;
                headTrf.Rotate(new Vector3(0, 0, angle));
                rotation += angle;
            }

            if (!destinyInRange)
            {
                float distanceToDestiny = (headTrf.position - destiny.transform.position).magnitude;
                if (distanceToDestiny <= destinyInRangeDistance ||
                    (headTrf.position.y < destiny.transform.position.y &&
                     currentX >= 0))    //Safety check. When jump is too fast distance can never be less than range distance
                {
                    destinyInRange = true;

                    AttackActions();

                    destiny.WormAboveAttackStart();
                }
            }

            if (headTrf.position.y < -WormBlackboard.NAVMESH_LAYER_HEIGHT)
            {
                SetHeadUnderground();

                subState = SubState.EXITING;
            }
            break;

        case SubState.EXITING:
            currentX        += Time.deltaTime * bb.spawnSpeed;
            lastPosition     = headTrf.position;
            headTrf.position = bb.GetJumpPositionGivenX(currentX);

            headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition));

            if (bb.tailReachedMilestone)
            {
                Vector3 pos = headTrf.position;
                pos.y            = -WormBlackboard.NAVMESH_LAYER_HEIGHT;
                headTrf.position = pos;

                return(head.wanderingState);
            }
            break;

        default:
            break;
        }

        return(null);
    }
Example #2
0
    public override WormAIBaseState Update()
    {
        switch (subState)
        {
        case SubState.WAITING:
            if (elapsedTime >= bb.WanderingSettingsPhase.initialWaitTime)
            {
                head.agent.enabled = false;

                currentWP = route.wayPoints[WPIndex].transform.position;
                nextWP    = route.wayPoints[WPIndex + 1].transform.position;

                currentWPUG = currentWP - bb.navMeshLayersDistance;
                nextWPUG    = nextWP - bb.navMeshLayersDistance;

                headTrf.position = currentWPUG;
                headTrf.LookAt(nextWPUG, Vector3.up);
                bb.CalculateWorldEnterBezierPoints(headTrf);
                head.SetVisible(true);

                //Rotate head
                Vector3 headUp = currentWPUG - nextWPUG;
                headTrf.LookAt(currentWP, headUp);

                t = 0;

                HexagonController hexagon = route.wayPoints[WPIndex].GetComponent <HexagonController>();
                hexagon.WormEnterExit();

                EnterExitActions();
                head.animator.SetBool("MouthOpen", true);

                bb.isHeadOverground = true;
                bb.applySinMovement = true;

                subState = SubState.ENTERING;
            }
            else
            {
                elapsedTime += Time.deltaTime;
            }

            break;

        case SubState.ENTERING:
            if (t <= 2)
            {
                shouldMove    = Time.deltaTime * bb.WanderingSettingsPhase.wanderingSpeed;
                actuallyMoved = 0;
                lastPosition  = headTrf.position;
                Vector3 newPos = Vector3.zero;

                while (actuallyMoved < shouldMove && t <= 2)
                {
                    newPos = bb.GetEnterCurvePosition(currentWPUG, nextWP, t);

                    actuallyMoved = (newPos - lastPosition).magnitude;

                    t += Time.deltaTime / duration;
                }
                headTrf.position = newPos;
                headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition));
            }
            else
            {
                ++WPIndex;
                currentWP = route.wayPoints[WPIndex].transform.position;

                head.agent.areaMask = WormBlackboard.NAVMESH_FLOOR_LAYER;
                head.agent.enabled  = true;
                head.agent.speed    = bb.WanderingSettingsPhase.wanderingSpeed;
                head.agent.SetDestination(currentWP);

                head.animator.SetBool("MouthOpen", false);

                bb.meteorInmediate = true;
                elapsedTime        = 0;

                subState = SubState.FOLLOWING_PATH;
            }

            break;

        case SubState.FOLLOWING_PATH:

            if (bb.AboveAttackSettingsPhase.active && head.CheckPlayerInSight(bb.AboveAttackSettingsPhase.exposureMinHexagons, bb.AboveAttackSettingsPhase.exposureMaxHexagons, false))
            {
                bb.aboveAttackCurrentExposureTime += Time.deltaTime;

                if (!angryEyes)
                {
                    angryEyes = true;
                    head.WatchingPlayer();
                }
            }
            else
            {
                if (angryEyes)
                {
                    angryEyes = false;
                    head.NotWatchingPlayer();
                }
            }

            if (bb.attacksEnabled && bb.AboveAttackSettingsPhase.active && bb.aboveAttackCurrentExposureTime >= bb.AboveAttackSettingsPhase.exposureTimeNeeded &&
                bb.aboveAttackCurrentCooldownTime >= bb.AboveAttackSettingsPhase.cooldownTime &&
                head.CheckPlayerInSight(bb.AboveAttackSettingsPhase.attackMinHexagons, bb.AboveAttackSettingsPhase.attackMaxHexagons, true))
            {
                if (bb.playerInSight != null)
                {
                    HexagonController destiny = bb.playerInSight.GetNearestHexagon();

                    if (destiny != null && destiny.isWormSelectable)
                    {
                        if (angryEyes)
                        {
                            angryEyes = false;
                            head.NotWatchingPlayer();
                        }
                        return(head.aboveAttackState);
                    }
                }
            }

            //Not reached next waypoint
            if (elapsedTime >= 6f)
            {
                head.agent.enabled = false;

                destiny = GetExitHexagon();
                bb.CalculateParabola(headTrf.position, destiny.transform.position);
                speed = (headTrf.position - destiny.transform.position).magnitude / bb.headDestroyedJumpDuration;

                //Calculate start point and prior point
                currentX = bb.GetJumpXGivenY(0, false);
                Vector3 startPosition = bb.GetJumpPositionGivenY(0, false);
                headTrf.position = startPosition;

                lastPosition = bb.GetJumpPositionGivenX(currentX);

                float   fakeNextX    = currentX + Time.deltaTime * 2;
                Vector3 nextPosition = bb.GetJumpPositionGivenX(fakeNextX);
                headTrf.rotation = Quaternion.LookRotation(nextPosition - startPosition, headTrf.up);

                subState = SubState.JUMPING;
            }
            else
            {
                elapsedTime += Time.deltaTime;

                if (!head.agent.hasPath || (head.agent.hasPath && head.agent.remainingDistance <= 0.25))
                {
                    if (WPIndex == route.wayPoints.Length - 2)
                    {
                        head.agent.enabled = false;

                        currentWP = route.wayPoints[WPIndex].transform.position;
                        nextWP    = route.wayPoints[WPIndex + 1].transform.position;

                        currentWPUG = currentWP - bb.navMeshLayersDistance;
                        nextWPUG    = nextWP - bb.navMeshLayersDistance;

                        headTrf.LookAt(nextWP, Vector3.up);

                        bb.CalculateWorldExitBezierPoints(headTrf);

                        t = 0;

                        HexagonController hexagon = route.wayPoints[WPIndex + 1].GetComponent <HexagonController>();
                        hexagon.WormEnterExit();

                        if (angryEyes)
                        {
                            angryEyes = false;
                            head.NotWatchingPlayer();
                        }

                        head.animator.SetBool("MouthOpen", true);
                        bb.meteorInmediate = false;

                        subState = SubState.EXITING;
                    }
                    else
                    {
                        elapsedTime = 0;
                        ++WPIndex;
                        currentWP = route.wayPoints[WPIndex].transform.position;
                        head.agent.SetDestination(currentWP);
                    }
                }
            }

            break;

        case SubState.EXITING:
            if (t <= 2)
            {
                shouldMove    = Time.deltaTime * bb.WanderingSettingsPhase.wanderingSpeed;
                actuallyMoved = 0;
                lastPosition  = headTrf.position;
                Vector3 newPos = Vector3.zero;

                if (t >= 1.6f && !exitRumble)
                {
                    exitRumble = true;
                    EnterExitActions();
                }

                while (actuallyMoved < shouldMove && t <= 2)
                {
                    newPos        = bb.GetExitCurvePosition(currentWP, nextWPUG, t);
                    actuallyMoved = (newPos - lastPosition).magnitude;

                    t += Time.deltaTime / duration;
                }
                headTrf.position = newPos;
                headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition));
            }
            else
            {
                SetUndergroundDirection();
                SetHeadUnderground();
                head.animator.SetBool("MouthOpen", false);

                subState = SubState.WAITING_FOR_TAIL;
            }

            break;

        case SubState.WAITING_FOR_TAIL:

            //move head until tail is undeground
            if (!bb.tailReachedMilestone)
            {
                MoveUndergroundDirection();
            }
            else
            {
                bb.applySinMovement = false;

                if (bb.shouldMeteorBeTriggedAfterWandering)
                {
                    return(head.meteorAttackState);
                }
                //If some random condition attack, else new wandering state
                else if (bb.attacksEnabled && bb.BelowAttackSettingsPhase.active &&
                         Random.Range(0f, 1f) <= bb.BelowAttackSettingsPhase.chancesOfBelowAttackAfterWandering / 100)
                {
                    return(head.belowAttackState);
                }
                else
                {
                    SetInitialState();
                }
            }

            break;


        //Failsafe exit
        case SubState.JUMPING:
            //While not again below underground navmesh layer advance
            currentX        += Time.deltaTime * speed;
            lastPosition     = headTrf.position;
            headTrf.position = bb.GetJumpPositionGivenX(currentX);

            headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition), headTrf.up);

            if (!destinyInRange)
            {
                float distanceToDestiny = (headTrf.position - destiny.transform.position).magnitude;
                if (distanceToDestiny <= destinyInRangeDistance ||
                    headTrf.position.y < destiny.transform.position.y)     //Safety check. When jump is too fast distance can never be less than range distance
                {
                    destinyInRange = true;

                    JumpExitActions();

                    destiny.WormEnterExit();
                }
            }

            if (headTrf.position.y < -WormBlackboard.NAVMESH_LAYER_HEIGHT)
            {
                SetHeadUnderground();
                head.animator.SetBool("MouthOpen", false);

                subState = SubState.JUMP_EXITING;
            }
            break;

        case SubState.JUMP_EXITING:
            currentX        += Time.deltaTime * speed;
            lastPosition     = headTrf.position;
            headTrf.position = bb.GetJumpPositionGivenX(currentX);

            headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition));

            if (bb.tailReachedMilestone)
            {
                Vector3 pos = headTrf.position;
                pos.y            = -WormBlackboard.NAVMESH_LAYER_HEIGHT;
                headTrf.position = pos;

                return(head.wanderingState);
            }
            break;

        default:
            break;
        }

        return(null);
    }
    public override WormAIBaseState Update()
    {
        switch (subState)
        {
        case SubState.KNOCKED_OUT:
            if (elapsedTime >= bb.HealthSettingsPhase.knockOutTime)
            {
                //Restore
                head.recoverSoundFx.Play();
                head.ResetPhase();

                destiny = GetExitHexagon();
                bb.CalculateParabola(headTrf.position, destiny.transform.position);
                speed = (headTrf.position - destiny.transform.position).magnitude / bb.headDestroyedJumpDuration;

                //Calculate start point and prior point
                currentX = bb.GetJumpXGivenY(0, false);
                Vector3 startPosition = bb.GetJumpPositionGivenY(0, false);
                headTrf.position = startPosition;

                lastPosition = bb.GetJumpPositionGivenX(currentX);

                float   fakeNextX    = currentX + Time.deltaTime * 2;
                Vector3 nextPosition = bb.GetJumpPositionGivenX(fakeNextX);
                initialRotation = Quaternion.LookRotation(nextPosition - startPosition, headTrf.up);

                head.animator.SetBool("Stunned", false);
                head.animator.SetBool("MouthOpen", true);
                elapsedTime = 0f;

                head.SetInvulnerable();

                subState = SubState.MOVING_HEAD;
            }
            else
            {
                if (elapsedTime >= 3f && !knockOutTutorialTriggered)
                {
                    knockOutTutorialTriggered        = true;
                    TutorialEventInfo.eventInfo.type = TutorialManager.Type.HIT_BOSS_HEAD;
                    rsc.eventMng.TriggerEvent(EventManager.EventType.SHOW_TUTORIAL, TutorialEventInfo.eventInfo);
                }

                elapsedTime += Time.deltaTime;
            }
            break;

        case SubState.MOVING_HEAD:
            headTrf.rotation = Quaternion.RotateTowards(headTrf.rotation, initialRotation, bb.headDestroyedLookRotationSpeed * Time.deltaTime);

            if (elapsedTime >= bb.headDestoryedBodyWaitTime)
            {
                subState = SubState.JUMPING;
            }
            else
            {
                elapsedTime += Time.deltaTime;
            }
            break;

        case SubState.JUMPING:
            //While not again below underground navmesh layer advance
            currentX        += Time.deltaTime * speed;
            lastPosition     = headTrf.position;
            headTrf.position = bb.GetJumpPositionGivenX(currentX);

            headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition), headTrf.up);

            headTrf.Rotate(new Vector3(0, 0, bb.headDestroyedRotationSpeed * Time.deltaTime));

            if (!destinyInRange)
            {
                float distanceToDestiny = (headTrf.position - destiny.transform.position).magnitude;
                if (distanceToDestiny <= destinyInRangeDistance ||
                    headTrf.position.y < destiny.transform.position.y)     //Safety check. When jump is too fast distance can never be less than range distance
                {
                    destinyInRange = true;

                    JumpExitActions();

                    destiny.WormEnterExit();
                }
            }

            if (headTrf.position.y < -WormBlackboard.NAVMESH_LAYER_HEIGHT)
            {
                SetHeadUnderground();
                head.animator.SetBool("MouthOpen", false);

                subState = SubState.EXITING;
            }
            break;

        case SubState.EXITING:
            currentX        += Time.deltaTime * speed;
            lastPosition     = headTrf.position;
            headTrf.position = bb.GetJumpPositionGivenX(currentX);

            headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition));

            if (bb.tailReachedMilestone)
            {
                Vector3 pos = headTrf.position;
                pos.y            = -WormBlackboard.NAVMESH_LAYER_HEIGHT;
                headTrf.position = pos;

                return(head.wanderingState);
            }
            break;

        default:
            break;
        }

        return(null);
    }
Example #4
0
    public override WormAIBaseState Update()
    {
        switch (subState)
        {
        case SubState.WAITING:
            if (elapsedTime >= bb.BelowAttackSettingsPhase.initialWaitTime)
            {
                GameObject playerGO = rsc.enemyMng.SelectPlayerRandom();
                if (playerGO != null)
                {
                    PlayerController player = playerGO.GetComponent <PlayerController>();
                    origin = player.GetNearestHexagon();

                    if (origin != null)
                    {
                        if (!origin.isWormSelectable)
                        {
                            return(head.wanderingState);
                        }

                        head.animator.SetBool("Bite", true);

                        destiny = GetHexagonFacingCenter(origin, destinyHexagonsDistance);

                        bb.CalculateParabola(origin.transform.position, destiny.transform.position);

                        head.agent.enabled = false;

                        rotation       = 0f;
                        destinyInRange = false;
                    }
                    else
                    {
                        return(head.wanderingState);
                    }
                }
                else
                {
                    return(head.wanderingState);
                }

                origin.WormBelowAttackWarning(bb.BelowAttackSettingsPhase.adjacentDamagingCells);

                elapsedTime = 0f;
                head.attackWarningSoundFx.Play();

                subState = SubState.WARNING_PLAYER;
            }
            else
            {
                elapsedTime += Time.deltaTime;
            }

            break;

        case SubState.WARNING_PLAYER:

            if (elapsedTime >= bb.BelowAttackSettingsPhase.warningTime)
            {
                //Position head below entry point
                currentX = bb.GetJumpXGivenY(-WormBlackboard.NAVMESH_LAYER_HEIGHT, false);
                Vector3 startPosition = bb.GetJumpPositionGivenY(-WormBlackboard.NAVMESH_LAYER_HEIGHT, false);
                headTrf.position = startPosition;
                lastPosition     = startPosition;
                head.SetVisible(true);

                origin.WormBelowAttackStart();

                AttackActions();

                bb.isHeadOverground = true;
                subState            = SubState.JUMPING;
            }
            else
            {
                elapsedTime += Time.deltaTime;
            }

            break;

        case SubState.JUMPING:
            //While not again below underground navmesh layer advance
            currentX        += Time.deltaTime * bb.BelowAttackSettingsPhase.jumpSpeed;
            lastPosition     = headTrf.position;
            headTrf.position = bb.GetJumpPositionGivenX(currentX);

            headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition), headTrf.up);

            float angle = bb.BelowAttackSettingsPhase.rotationSpeed * Time.deltaTime;
            headTrf.Rotate(new Vector3(0, 0, angle));
            rotation += angle;

            if (!destinyInRange)
            {
                float distanceToDestiny = (headTrf.position - destiny.transform.position).magnitude;
                if (distanceToDestiny <= destinyInRangeDistance ||
                    (headTrf.position.y < destiny.transform.position.y &&
                     currentX >= 0))    //Safety check. When jump is too fast distance can never be less than range distance
                {
                    destinyInRange = true;
                    JumpExitActions();
                    destiny.WormEnterExit();
                }
            }

            if (headTrf.position.y < -WormBlackboard.NAVMESH_LAYER_HEIGHT)
            {
                bb.isHeadOverground     = false;
                bb.tailReachedMilestone = false;
                bb.FlagCurrentWaypointAsMilestone();
                head.SetVisible(false);

                subState = SubState.EXITING;
            }
            break;

        case SubState.EXITING:
            currentX        += Time.deltaTime * bb.BelowAttackSettingsPhase.jumpSpeed;
            lastPosition     = headTrf.position;
            headTrf.position = bb.GetJumpPositionGivenX(currentX);

            headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition));

            if (bb.tailReachedMilestone)
            {
                //Debug.Break();
                Vector3 pos = headTrf.position;
                pos.y            = -WormBlackboard.NAVMESH_LAYER_HEIGHT;
                headTrf.position = pos;

                return(head.wanderingState);
            }
            break;

        default:
            break;
        }

        return(null);
    }
    public override WormAIBaseState Update()
    {
        switch (subState)
        {
        //Wait for head Fx and animation
        case SubState.WAITING_HEAD:
            if (elapsedTime >= bb.headDestroyedWaitTime)
            {
                if (!shouldTriggerMeteor)
                {
                    destiny = GetExitHexagon();
                    bb.CalculateParabola(headTrf.position, destiny.transform.position);
                    speed = (headTrf.position - destiny.transform.position).magnitude / bb.headDestroyedJumpDuration;

                    //Calculate start point and prior point
                    currentX = bb.GetJumpXGivenY(0, false);
                    Vector3 startPosition = bb.GetJumpPositionGivenY(0, false);
                    headTrf.position = startPosition;

                    lastPosition = bb.GetJumpPositionGivenX(currentX);

                    float   fakeNextX    = currentX + Time.deltaTime * 2;
                    Vector3 nextPosition = bb.GetJumpPositionGivenX(fakeNextX);
                    initialRotation = Quaternion.LookRotation(nextPosition - startPosition, headTrf.up);
                }

                head.animator.SetBool("Hit", false);

                bb.ConsolidateBodyParts();
                subState = SubState.WAITING_BODY;

                elapsedTime = 0f;
            }
            else
            {
                elapsedTime += Time.deltaTime;
            }
            break;

        //Wait for body parts destruction
        case SubState.WAITING_BODY:

            if (bb.wormCurrentPhase < bb.wormMaxPhases - 1 && !shouldTriggerMeteor)
            {
                headTrf.rotation = Quaternion.RotateTowards(headTrf.rotation, initialRotation, bb.headDestroyedLookRotationSpeed * Time.deltaTime);
            }

            if (elapsedTime >= bb.headDestoryedBodyWaitTime)
            {
                if (bb.wormCurrentPhase == bb.wormMaxPhases - 1)
                {
                    return(head.dyingState);
                }
                else
                {
                    head.StartNewPhase();
                    head.SetMaterial(rsc.coloredObjectsMng.GetWormHeadMaterial(bb.headCurrentChargeLevel));
                    //rsc.eventMng.TriggerEvent(EventManager.EventType.WORM_HEAD_ACTIVATED, EventInfo.emptyInfo);

                    if (shouldTriggerMeteor)
                    {
                        bb.meteorInmediate = true;
                        return(head.meteorAttackState);
                    }
                    else
                    {
                        head.animator.SetBool("MouthOpen", true);
                        subState = SubState.JUMPING;
                    }
                }
            }
            else
            {
                elapsedTime += Time.deltaTime;
            }
            break;

        case SubState.JUMPING:
            //While not again below underground navmesh layer advance
            currentX        += Time.deltaTime * speed;
            lastPosition     = headTrf.position;
            headTrf.position = bb.GetJumpPositionGivenX(currentX);

            headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition), headTrf.up);

            headTrf.Rotate(new Vector3(0, 0, bb.headDestroyedRotationSpeed * Time.deltaTime));

            if (!destinyInRange)
            {
                float distanceToDestiny = (headTrf.position - destiny.transform.position).magnitude;
                if (distanceToDestiny <= destinyInRangeDistance ||
                    headTrf.position.y < destiny.transform.position.y)     //Safety check. When jump is too fast distance can never be less than range distance
                {
                    destinyInRange = true;

                    JumpExitActions();

                    destiny.WormEnterExit();
                }
            }

            if (headTrf.position.y < -WormBlackboard.NAVMESH_LAYER_HEIGHT)
            {
                SetHeadUnderground();
                head.animator.SetBool("MouthOpen", false);

                subState = SubState.EXITING;
            }
            break;

        case SubState.EXITING:
            currentX        += Time.deltaTime * speed;
            lastPosition     = headTrf.position;
            headTrf.position = bb.GetJumpPositionGivenX(currentX);

            headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition));

            if (bb.tailReachedMilestone)
            {
                Vector3 pos = headTrf.position;
                pos.y            = -WormBlackboard.NAVMESH_LAYER_HEIGHT;
                headTrf.position = pos;

                return(head.wanderingState);
            }
            break;

        default:
            break;
        }

        return(null);
    }
    public override WormAIBaseState Update()
    {
        switch (subState)
        {
        //Underground path
        case SubState.UNDERGROUND_START:
            GameObject playerGO = rsc.enemyMng.SelectPlayerRandom();
            if (playerGO != null)
            {
                PlayerController player = playerGO.GetComponent <PlayerController>();
                origin = player.GetNearestHexagon();

                if (origin != null)
                {
                    if (!origin.isWormSelectable)
                    {
                        return(head.wanderingState);
                    }

                    destiny = GetHexagonFacingCenter(origin, 2);
                    bb.CalculateParabola(origin.transform.position, destiny.transform.position);

                    head.agent.enabled = false;
                }
                else
                {
                    return(head.wanderingState);
                }
            }
            else
            {
                return(head.wanderingState);
            }

            origin.WormEnterExit();

            elapsedTime = 0f;
            head.attackWarningSoundFx.Play();

            subState = SubState.WARNING_PLAYER;

            break;

        case SubState.WARNING_PLAYER:

            if (elapsedTime >= bb.MeteorAttackSettingsPhase.warningTime)
            {
                //Position head below entry point
                currentX = bb.GetJumpXGivenY(-WormBlackboard.NAVMESH_LAYER_HEIGHT, false);
                Vector3 startPosition = bb.GetJumpPositionGivenY(-WormBlackboard.NAVMESH_LAYER_HEIGHT, false);
                headTrf.position = startPosition;
                lastPosition     = startPosition;
                head.SetVisible(true);

                elapsedTime         = 0f;
                bb.isHeadOverground = true;
                subState            = SubState.HEAD_EXIT;
            }
            else
            {
                elapsedTime += Time.deltaTime;
            }

            break;

        case SubState.HEAD_EXIT:
            if (headTrf.position.y < 2)
            {
                currentX        += Time.deltaTime * bb.MeteorAttackSettingsPhase.enterHeadSpeed;
                lastPosition     = headTrf.position;
                headTrf.position = bb.GetJumpPositionGivenX(currentX);

                headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition), headTrf.up);

                elapsedTime += Time.deltaTime;
            }
            else
            {
                elapsedTime += bb.MeteorAttackSettingsPhase.warningTime;
                subState     = SubState.OPENING_MOUTH;
            }

            break;

        //Overground path
        case SubState.OVERGROUND_START:
            destiny = GetExitHexagon();
            bb.CalculateParabola(headTrf.position, destiny.transform.position);
            subState = SubState.OPENING_MOUTH;
            break;

        //Common path
        case SubState.OPENING_MOUTH:
            if (elapsedTime >= 1.5f)
            {
                elapsedTime = 0;
                //head.fireSpray.transform.position = head.headModel.transform.position + (Vector3.up * 0.5f);
                //head.fireSpray.transform.LookAt(head.fireSpray.transform.position + Vector3.up, head.transform.up);
                //head.fireSpray.Play();
                subState = SubState.SHOOTING;
            }
            else
            {
                elapsedTime += Time.deltaTime;
            }
            break;

        case SubState.SHOOTING:
            if (totalShots < bb.MeteorAttackSettingsPhase.numberOfThrownMeteors)
            {
                if (elapsedTime <= 0)
                {
                    MeteorController meteor = rsc.poolMng.meteorPool.GetObject();
                    meteor.transform.position = head.meteorSpawnPoint.position;
                    meteor.GoUp(bb.MeteorAttackSettingsPhase.speedOfThrownMeteors);

                    rsc.rumbleMng.Rumble(0, 0.15f, 0, 0.6f);
                    rsc.camerasMng.PlayEffect(0, 0.1f, 0.3f);

                    ++totalShots;
                    elapsedTime = timeBetweenShots;
                }
                else
                {
                    elapsedTime -= Time.deltaTime;
                }
            }
            else
            {
                speed = (headTrf.position - destiny.transform.position).magnitude / bb.MeteorAttackSettingsPhase.jumpDuration;

                //Calculate start point and prior point
                currentX = bb.GetJumpXGivenY(headTrf.position.y, false);
                Vector3 startPosition = bb.GetJumpPositionGivenY(headTrf.position.y, false);
                headTrf.position = startPosition;

                lastPosition = bb.GetJumpPositionGivenX(currentX);

                float   fakeNextX    = currentX + Time.deltaTime * 2;
                Vector3 nextPosition = bb.GetJumpPositionGivenX(fakeNextX);
                initialRotation = Quaternion.LookRotation(nextPosition - startPosition, headTrf.up);

                head.fireSpray.Stop();

                elapsedTime = 0;
                if (underground)
                {
                    head.animator.SetBool("MouthOpen", false);
                }
                else
                {
                    head.animator.SetBool("MouthOpenTrans", false);
                    head.animator.SetBool("Jump", true);
                }
                subState = SubState.CLOSING_MOUTH;
            }
            break;

        case SubState.CLOSING_MOUTH:
            headTrf.rotation = Quaternion.RotateTowards(headTrf.rotation, initialRotation, bb.headDestroyedLookRotationSpeed * Time.deltaTime);
            if (elapsedTime >= 1)
            {
                elapsedTime = 0;
                subState    = SubState.JUMPING;
                head.animator.SetBool("Jump", false);
            }
            else
            {
                elapsedTime += Time.deltaTime;
            }
            break;

        case SubState.JUMPING:
            //While not again below underground navmesh layer advance
            currentX        += Time.deltaTime * speed;
            lastPosition     = headTrf.position;
            headTrf.position = bb.GetJumpPositionGivenX(currentX);

            headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition), headTrf.up);

            headTrf.Rotate(new Vector3(0, 0, bb.MeteorAttackSettingsPhase.rotationSpeed * Time.deltaTime));

            if (!destinyInRange)
            {
                float distanceToDestiny = (headTrf.position - destiny.transform.position).magnitude;
                if (distanceToDestiny <= destinyInRangeDistance ||
                    headTrf.position.y < destiny.transform.position.y)     //Safety check. When jump is too fast distance can never be less than range distance
                {
                    destinyInRange = true;

                    JumpExitActions();

                    destiny.WormEnterExit();
                }
            }

            if (headTrf.position.y < -WormBlackboard.NAVMESH_LAYER_HEIGHT)
            {
                SetHeadUnderground();

                subState = SubState.EXITING;
            }
            break;

        case SubState.EXITING:
            currentX        += Time.deltaTime * speed;
            lastPosition     = headTrf.position;
            headTrf.position = bb.GetJumpPositionGivenX(currentX);

            headTrf.LookAt(headTrf.position + (headTrf.position - lastPosition));

            if (bb.tailReachedMilestone)
            {
                Vector3 pos = headTrf.position;
                pos.y            = -WormBlackboard.NAVMESH_LAYER_HEIGHT;
                headTrf.position = pos;

                //Trigger meteor rain
                MeteorAttackEventInfo info = MeteorAttackEventInfo.eventInfo;
                WormBlackboard.MeteorAttackSettings settings = bb.MeteorAttackSettingsPhase;

                info.meteorInitialBurst = settings.meteorInitialBurst;
                info.meteorRainDuration = settings.meteorRainDuration;
                info.meteorInterval     = settings.meteorInterval;
                info.meteorsPerInterval = settings.meteorsPerInterval;
                info.meteorWaitTime     = settings.meteorWaitTime;
                info.meteorWarningTime  = settings.meteorWarningTime;

                rsc.eventMng.TriggerEvent(EventManager.EventType.METEOR_RAIN_START, info);
                subState = SubState.WAITING_METEOR_RAIN;
            }
            break;

        case SubState.WAITING_METEOR_RAIN:
            //Wait
            break;

        case SubState.METEOR_RAIN_ENDED:
            return(head.wanderingState);

        default:
            break;
        }

        return(null);
    }