#include "scripts/common.txt"

//(7) Params 1 -128..127 (Type 1 = starts as statue until triggered)

//BP_MantisStatue
//anim_mantisStatueIdle
//anim_mantisStatueTrigger

enum BP_mantisState
{
	BP_MS_NEW = 0,
    BP_MS_GROUNDCHASE,
    BP_MS_DEAD,
}

final class BP_TurokMantis : TurokEnemy
{
	kActor@ statue;
    float m_goalDistSq;             // distance to goal origin squared
    float m_goalDist;               // distance to goal origin
    kVec3 m_goalOrigin;             // goal origin to chase at
    kAngle m_desiredYaw;            // direction yaw that it wants to turn to
    kAngle m_lookAtYaw;             // direction yaw that it wants to target at
    int m_state;
    bool m_bTurning;                // playing a turning animation?
    int m_attackLoop;               // cycles through different attack animations
    float m_floorHeight;
    float m_bobTime;
    float m_acceleration;           // for flying; accelerates speed as it begins to move
    float m_pitchTilt;              // for flying; tilts pitch when ramping up speed
    float m_rollTilt;               // for flying; tilts roll when making sharp turns
    float m_targetRange;
    bool m_bAllowBobbing;
    int m_initialHealth;
    int m_stage;
    float m_animSpeed;
    const array<float> @m_pStageMode;
    const array<int> @m_pFloorAttack;
    const array<int> @m_pCloseFloorAttack;
    const array<int> @m_pLeftWallAttack;
    const array<int> @m_pRightWallAttack;
    const array<int> @m_pCeilingAttack;
    int m_floorAttackCounter;
    int m_CloseFloorAttackCounter;
    int m_leftWallAttackCounter;
    int m_rightWallAttackCounter;
    int m_ceilingAttackCounter;
    bool m_bDamageStunned;
    
    BP_TurokMantis(kActor @actor)
    {
        m_goalDistSq = 0;
        m_goalDist = 0;
        m_desiredYaw = 0;
        m_lookAtYaw = 0;
        m_attackLoop = 0;
        m_bTurning = false;
        m_floorHeight = 0;
        m_bobTime = 0;
        m_acceleration = 0;
        m_pitchTilt = 0;
        m_rollTilt = 0;
        m_targetRange = 0;
        m_bAllowBobbing = false;
        m_initialHealth = 0;
        m_state = BP_MS_NEW;
        m_stage = 0;
        m_animSpeed = 1;
        m_floorAttackCounter = 0;
        m_CloseFloorAttackCounter = 0;
        m_leftWallAttackCounter = 0;
        m_rightWallAttackCounter = 0;
        m_ceilingAttackCounter = 0;
        @m_pStageMode = g_mantisStages[0];
        @m_pFloorAttack = @g_mantisFloorAttack_Stage2;
        @m_pCloseFloorAttack = @g_mantisFloorCloseAttack_Stage2;
        @m_pLeftWallAttack = g_mantisLeftAttack_Stage2;
        @m_pRightWallAttack = g_mantisRightAttack_Stage2;
        @m_pCeilingAttack = null;
        m_bDamageStunned = false;
        
        super(actor);
    }
	
    void OnBeginLevel()
    {
		//start as statue
		if (self.SpawnParams(7) == 1)
		{
			HideAI(self);
			float zOffset = 45.0f * (self.Scale().z / 0.35f);
			@statue = ActorFactory.Spawn("BP_MantisStatue", self.Origin().x + 55.0f, self.Origin().y + 20.0f, self.Origin().z - zOffset, self.Yaw(), self.SectorIndex());
			statue.Scale() = self.Scale();
			statue.Flags() |= AF_SOLID;
			statue.AnimState().Set(anim_mantisStatueIdle, 4.0f, 0);
			statue.Origin().z = self.Origin().z - zOffset;
		}
    }
	
	void OnActivate()
	{
		self.Flags() &= ~AF_ACTIVATED;

		//start as statue
		if (self.SpawnParams(7) == 1 && ActorExists(statue) && statue.AnimState().PlayingID() == anim_mantisStatueIdle)
		{
			ShowAI(self);
			self.AnimState().Set(anim_mantisWake, 4.0f, 0);
			statue.Flags() &= ~AF_SOLID;
			statue.AnimState().Set(anim_mantisStatueTrigger, 4.0f, 0);
		}
	}
    
    /*
    ==============================================================
    RunAttackStage
    ==============================================================
    */
    
    int RunAttackStage(const array<int>@ &in pRefAttack, int &in inCounter, int &out outCounter)
    {
        int anim = pRefAttack[inCounter];
        
        outCounter = inCounter + 1;
        
        if(outCounter >= int(pRefAttack.length()))
        {
            outCounter = 0;
        }
        
        return anim;
    }
    
    /*
    ==============================================================
    SpitMantisAcid
    ==============================================================
    */
    
    void SpitMantisAcid(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        if(self.AnimState().PlayingID() == anim_mantisAirLoop)
        {
            if(!self.RandomDecision(6) || Math::Fabs(m_desiredYaw) > Math::Deg2Rad(10.0f))
            {
                return;
            }
        }
        
        self.SpawnProjectile("fx/fireball.kfx", kVec3(x, y, z),
            m_goalOrigin, Math::Deg2Rad(45.0f));
    }
    
    /*
    ==============================================================
    RedBombAttack
    ==============================================================
    */
    
    void RedBombAttack(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.SpawnProjectile("fx/deathrain.kfx", kVec3(x, y, z),
            m_goalOrigin, Math::Deg2Rad(45.0f));
    }
    
    /*
    ==============================================================
    SwooshTrail2
    ==============================================================
    */
    
    void SwooshTrail2(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.RenderModel().AddTrailEffect("Trail_MantisFoot", 5);   // left front foot
        self.RenderModel().AddTrailEffect("Trail_MantisFoot", 9);   // left back foot
        self.RenderModel().AddTrailEffect("Trail_MantisFoot", 13);  // right front foot
        self.RenderModel().AddTrailEffect("Trail_MantisFoot", 17);  // right back foot
    }
    
    /*
    ==============================================================
    SwooshTrail3
    ==============================================================
    */
    
    void SwooshTrail3(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.RenderModel().AddTrailEffect("Trail_MantisHand", 24);
        self.RenderModel().AddTrailEffect("Trail_MantisHand", 27);
    }
    
    /*
    ==============================================================
    ScreenShake
    ==============================================================
    */
    
    void ScreenShake(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        Player.Actor().RecoilPitch() = -Math::Deg2Rad(w);
        Player.Actor().Velocity().z = ((w * 0.512f) * 15.0f) * (4 * GAME_DELTA_TIME);
    }
    
    /*
    ==============================================================
    BlendAnimation
    ==============================================================
    */
    
    void BlendAnimation(const int anim, const float speed, const float blend, const bool bStoppedOnly = true)
    {
        if(bStoppedOnly && self.AnimState().Blending())
        {
            // don't interrupt while blending
            return;
        }
        
        if(bStoppedOnly && (!self.AnimState().Looping() && !self.AnimState().Stopped()))
        {
            return;
        }
        else if(!bStoppedOnly && self.AnimState().PlayingID() == anim)
        {
            return;
        }
        
        if(anim != anim_mantisPain)
        {
            self.Flags() &= ~AF_INVINCIBLE;
        }
        
        self.AnimState().Blend(anim, speed*m_animSpeed, blend*m_animSpeed, ANF_ROOTMOTION);
    }
    
    /*
    ==============================================================
    BlendLoopingAnimation
    ==============================================================
    */
    
    void BlendLoopingAnimation(const int anim, const float speed, const float blend)
    {
        if(!self.AnimState().Stopped())
        {
            return;
        }
        
        if(self.AnimState().PlayingID() != anim)
        {
            if(anim != anim_mantisPain)
            {
                self.Flags() &= ~AF_INVINCIBLE;
            }
        
            self.AnimState().Blend(anim, speed*m_animSpeed, blend*m_animSpeed, ANF_ROOTMOTION|ANF_LOOP);
        }
    }
    
    /*
    ==============================================================
    GetTargetPoint
    ==============================================================
    */
    
    void GetTargetPoint(void)
    {
        m_goalOrigin.x = Player.Actor().Origin().x;
        m_goalOrigin.y = Player.Actor().Origin().y;
        m_goalOrigin.z = self.Origin().z;
        
        m_targetRange = 512.0f;
        
        m_goalDistSq = self.DistanceToPoint(m_goalOrigin);
        m_goalDist = Math::Sqrt(m_goalDistSq);
        
        m_desiredYaw = self.GetAvoidanceAngle(m_goalOrigin, 1.5f) - self.Yaw();
        m_lookAtYaw = m_desiredYaw;
    }
    
    /*
    ==============================================================
    AngleDiffFromZero
    ==============================================================
    */
    
    float AngleDiffFromZero(float theta)
    {
        while(theta < -Math::pi) theta += 2*Math::pi;
        while(theta > Math::pi) theta -= 2*Math::pi;
        
        return -theta;
    }
    
    /*
    ==============================================================
    AngleDiff
    ==============================================================
    */
    
    float AngleDiff(float phi, float theta)
    {
        return AngleDiffFromZero(phi + AngleDiffFromZero(theta));
    }
    
    /*
    ==============================================================
    CompareAngles
    ==============================================================
    */
    
    float AngleDist(float phi, float theta)
    {
        return Math::Fabs(AngleDiff(phi, theta));
    }
    
    /*
    ==============================================================
    CheckLeapPosition
    ==============================================================
    */
    
    bool CheckLeapPosition(const float angle)
    {
        float ang = self.Yaw() + angle;
        kVec3 pos, offset;
        
        // check min distance - wall can't be too close
        pos.x = self.Origin().x - Math::Sin(ang) * (5*GAME_SCALE);
        pos.y = self.Origin().y - Math::Cos(ang) * (5*GAME_SCALE);
        pos.z = self.Origin().z + (60*GAME_SCALE);
        
        if(!self.CheckPosition(pos))
        {
            // too close to the wall
            return false;
        }
        
        // check pt1 - is there a wall inbetween max distance and the mantis?
        pos.x = self.Origin().x - Math::Sin(ang) * (150*GAME_SCALE);
        pos.y = self.Origin().y - Math::Cos(ang) * (150*GAME_SCALE);
        pos.z = self.Origin().z + (60*GAME_SCALE);
        
        if(self.CheckPosition(pos))
        {
            // too far from the wall
            return false;
        }
        
        // check pt2 - look parallel to wall to make sure wall angle next to pt1 is the same
        float wallAngle1 = CModel.ContactNormal().ToYaw();
        kVec3 vTarget2 = pos;
        
        ang += Math::Deg2Rad(90.0f);
        vTarget2.x -= (20*GAME_SCALE) * Math::Sin(ang);
        vTarget2.y -= (20*GAME_SCALE) * Math::Cos(ang);
        
        if(self.CheckPosition(vTarget2))
        {
            return false;
        }
        
        // check pt3 - look parallel to wall to make sure wall angle next to pt1 is the same
        float wallAngle2 = CModel.ContactNormal().ToYaw();
        vTarget2 = pos;
        
        ang += Math::Deg2Rad(180.0f);
        vTarget2.x -= (20*GAME_SCALE) * Math::Sin(ang);
        vTarget2.y -= (20*GAME_SCALE) * Math::Cos(ang);
        
        if(self.CheckPosition(vTarget2))
        {
            return false;
        }
        
        // make sure all angles are very close
        float wallAngle3 = CModel.ContactNormal().ToYaw();

        if(AngleDist(wallAngle1, wallAngle2) > Math::Deg2Rad(10)) return false;
        if(AngleDist(wallAngle1, wallAngle3) > Math::Deg2Rad(10)) return false;
        if(AngleDist(wallAngle2, wallAngle3) > Math::Deg2Rad(10)) return false;
        
        wallAngle1 += (Math::pi / 2);
        
        if(AngleDist(self.Yaw(), wallAngle1 + Math::pi) < AngleDist(self.Yaw(), wallAngle1))
        {
            wallAngle1 += Math::pi;
        }
        
        kAngle normalizedAngle = wallAngle1;
        
        return (AngleDist(self.Yaw(), normalizedAngle) < Math::Deg2Rad(90.0f));
    }
    
    /*
    ==============================================================
    CheckPositionInFlight
    ==============================================================
    */
    
    bool CheckPositionInFlight(const float angle)
    {
        float ang = self.Yaw() + angle;
        kVec3 pos;
        
        pos.x = self.Origin().x - Math::Sin(ang) * (10*GAME_SCALE);
        pos.y = self.Origin().y - Math::Cos(ang) * (10*GAME_SCALE);
        pos.z = self.Origin().z;
        
        return (self.CheckPosition(pos) == false);
    }
    
    /*
    ==============================================================
    CheckLeftWallForLeap
    ==============================================================
    */
    
    bool CheckLeftWallForLeap(void)
    {
        return CheckLeapPosition(Math::Deg2Rad(-90.0f));
    }
    
    /*
    ==============================================================
    CheckRightWallForLeap
    ==============================================================
    */
    
    bool CheckRightWallForLeap(void)
    {
        return CheckLeapPosition(Math::Deg2Rad(90.0f));
    }
    
    /*
    ==============================================================
    SetTilt
    ==============================================================
    */
    
    float SetTilt(float tilt, const float dest)
    {
        float amount;
        float d = dest;
        
        if(dest == 0.0f)
        {
            amount = Math::Deg2Rad(0.5f);
        }
        else
        {
            const float an = Math::Deg2Rad(15.0f);
            
            if(dest >= -an)
            {
                if(dest > an)
                {
                    d = an;
                }
            }
            else
            {
                d = -an;
            }
            
            amount = Math::Deg2Rad(1.0f);
        }
        
        return Math::NLerp(tilt, amount * 0.5f, d);
    }
    
    void FlyBob(void)
    {
        if(!m_bAllowBobbing && self.Origin().z < m_floorHeight - 0.1f)
        {
            self.Origin().z = (m_floorHeight - self.Origin().z) * 0.125f + self.Origin().z;
            return;
        }
        else if(!m_bAllowBobbing)
        {
            m_bAllowBobbing = true;
        }
        
        self.Origin().z = m_floorHeight + (Math::Sin(m_bobTime) * 15.0f);
        m_bobTime += Math::Deg2Rad(18.0f) * 0.5f;
    }
    
    /*
    ==============================================================
    TryGroundAttack
    ==============================================================
    */
    
    void TryGroundAttack(void)
    {
        int anim;
        
        if(m_goalDist < 35*GAME_SCALE)
        {
            anim = RunAttackStage(m_pCloseFloorAttack, m_CloseFloorAttackCounter, m_CloseFloorAttackCounter);
        }
        else
        {
            anim = RunAttackStage(m_pFloorAttack, m_floorAttackCounter, m_floorAttackCounter);
        }
        
        if(anim == anim_mantisGroundToLeftWall && !CheckLeftWallForLeap())
        {
            return;
        }
        if(anim == anim_mantisGroundToRightWall && !CheckRightWallForLeap())
        {
            return;
        }
        
        BlendAnimation(anim, 2.0f, 6.0f, !m_bTurning);
    }
    
    /*
    ==============================================================
    TryCeilingAttack
    ==============================================================
    */
    
    void TryCeilingAttack(void)
    {
        int anim;
        
        if(m_pCeilingAttack is null)
        {
            return;
        }
        
        anim = RunAttackStage(m_pCeilingAttack,
            m_ceilingAttackCounter, m_ceilingAttackCounter);
            
        BlendAnimation(anim, 2.0f, 3.0f);
    }
    
    /*
    ==============================================================
    TakeOffFromGround
    ==============================================================
    */
    
    void TakeOffFromGround(void)
    {
        BlendAnimation(anim_mantisGroundToAir, 4.0f, 6.0f);
        
        self.Gravity() = 0;
        self.ClipFlags() |= CF_NOSTEPDOWN;
        
        m_floorHeight = self.FloorHeight() + 50.0f;
        m_bAllowBobbing = false;
        m_bobTime = 0;
        m_acceleration = 0;
        m_pitchTilt = 0;
        m_rollTilt = 0;
    }
    
    /*
    ==============================================================
    FlightToGround
    ==============================================================
    */
    
    void FlightToGround(void)
    {
        BlendAnimation(anim_mantisAirToGround, 4.0f, 8.0f);
        self.Gravity() = 0.5f;
        self.ClipFlags() &= ~CF_NOSTEPDOWN;
    }
    
    /*
    ==============================================================
    GroundChase
    ==============================================================
    */
    
    void GroundChase(void)
    {
        UpdateStage();
        
        if(m_goalDist <= 1433.6f)
        {
            if(m_goalDist <= m_targetRange)
            {
                if(m_lookAtYaw >= -Math::Deg2Rad(35.0f))
                {
                    if(m_lookAtYaw <= Math::Deg2Rad(35.0f))
                    {
                        TryGroundAttack();
                    }
                    else
                    {
                        BlendAnimation(anim_mantisGroundStandTurnLeft, 2.5f, 6.0f);
                        m_attackLoop = 0;
                    }
                }
                else
                {
                    BlendAnimation(anim_mantisGroundStandTurnRight, 2.5f, 6.0f);
                    m_attackLoop = 0;
                }
            }
            else
            {
                TurnAngles(Math::Deg2Rad(4.0f), m_desiredYaw);
                
                if(m_desiredYaw >= -Math::Deg2Rad(45.0f))
                {
                    if(m_desiredYaw <= Math::Deg2Rad(45.0f))
                    {
                        BlendAnimation(anim_mantisGroundForward, 1.6f, 6.0f);
                        m_attackLoop = 0;
                    }
                    else
                    {
                        BlendAnimation(anim_mantisGroundStandTurnLeft, 2.5f, 6.0f);
                        m_attackLoop = 0;
                    }
                }
                else
                {
                    BlendAnimation(anim_mantisGroundStandTurnRight, 2.5f, 6.0f);
                    m_attackLoop = 0;
                }
            }
        }
        else
        {
            TakeOffFromGround();
        }
    }
    
    void CeilingChase(void)
    {
        if (m_goalDist <= 1433.6f)
        {
            if((self.AnimState().PlayingID() == anim_mantisGroundToCeiling ||
                self.AnimState().PlayingID() == anim_mantisRightWallToCeiling ||
                self.AnimState().PlayingID() == anim_mantisLeftWallToCeiling) &&
                self.AnimState().Stopped())
            {
                BlendAnimation(anim_mantisAttackCeiling1, 2.0f, 4.0f, false);
                return;
            }
            
            if(m_goalDist <= (m_targetRange + 286.72f))
            {
                if(m_lookAtYaw >= -Math::Deg2Rad(35.0f))
                {
                    if(m_lookAtYaw <= Math::Deg2Rad(35.0f))
                    {
                        TryCeilingAttack();
                    }
                    else
                    {
                        BlendAnimation(anim_mantisCeilingStandTurnLeft, 1.5f, 3.0f);
                    }
                }
                else
                {
                    BlendAnimation(anim_mantisCeilingStandTurnRight, 1.5f, 3.0f);
                }
            }
            else
            {
                TurnAngles(Math::Deg2Rad(4.0f), m_desiredYaw);
                
                if(m_desiredYaw >= -Math::Deg2Rad(45.0f))
                {
                    if(m_desiredYaw <= Math::Deg2Rad(45.0f))
                    {
                        BlendAnimation(anim_mantisCeilingForward, 1.5f, 6.0f);
                    }
                    else
                    {
                        BlendAnimation(anim_mantisCeilingStandTurnLeft, 1.5f, 3.0f);
                    }
                }
                else
                {
                    BlendAnimation(anim_mantisCeilingStandTurnRight, 1.5f, 3.0f);
                }
            }
        }
        else
        {
            BlendAnimation(anim_mantisCeilingToGround, 2.0f, 3.0f);
        }
    }
    
    void FlyChase(void)
    {
        float mx = self.Velocity().x;
        float my = self.Velocity().y;
        float md = mx * mx + my * my;
        
        if(md != 0.0f)
        {
            md = Math::Sqrt(md);
        }
        
        if(self.AnimState().PlayingID() == anim_mantisAirLoop)
        {
            float speed = m_pStageMode[MSI_FLYSPEED];
            
            if(m_goalDist > m_targetRange)
            {
                m_acceleration = Math::IncMax(m_acceleration, 0.0625f, 1.0f);
            }
            else
            {
                m_acceleration = Math::NLerp(m_acceleration, 0.125f, 0.0f);
            }
            
            self.Velocity().x = (speed * m_acceleration) * Math::Sin(self.Yaw());
            self.Velocity().y = (speed * m_acceleration) * Math::Cos(self.Yaw());
        }
        
        FlyBob();
        TurnAngles(Math::Deg2Rad(6.0f), m_desiredYaw);
        
        if(m_goalDist < m_targetRange)
        {
            if(Math::Fabs(m_desiredYaw) < Math::Deg2Rad(10.0f))
            {
                FlightToGround();
                return;
            }
        }
        
        m_pitchTilt = SetTilt(m_pitchTilt, md * 0.125f);
        m_rollTilt = SetTilt(m_rollTilt, m_desiredYaw);
    }
    
    /*
    ==============================================================
    CheckTurning
    ==============================================================
    */
    
    void CheckTurning(void)
    {
        switch(self.AnimState().PlayingID())
        {
        case anim_mantisGroundStandTurnRight:
        case anim_mantisCeilingStandTurnRight:
        case anim_mantisGroundStandTurnLeft:
        case anim_mantisCeilingStandTurnLeft:
            m_bTurning = true;
            break;
        default:
            m_bTurning = false;
            break;
        }
    }
    
    /*
    ==============================================================
    CheckWallToLandOn
    ==============================================================
    */
    
    void CheckWallToLandOn(void)
    {
        if(self.AnimState().Blending() ||
            (!self.AnimState().Looping() && !self.AnimState().Stopped()))
        {
            return;
        }
        
        switch(self.AnimState().PlayingID())
        {
        case anim_mantisRightWallToAirLoop:
        case anim_mantisGroundToLeftWall:
            if(CheckPositionInFlight(Math::Deg2Rad(-90.0f)))
            {
                BlendAnimation(anim_mantisAirToLeftWall, 1.0f, 3.0f, false);
				self.Yaw() = (CModel.ContactNormal().ToYaw() + Math::pi) + Math::Deg2Rad(90.0f);
				self.Yaw() += Math::pi;
            }
            else
            {
                BlendLoopingAnimation(anim_mantisRightWallToAirLoop, 1.0f, 3.0f);
            }
            break;
            
        case anim_mantisLeftWallToAirLoop:
        case anim_mantisGroundToRightWall:
            if(CheckPositionInFlight(Math::Deg2Rad(90.0f)))
            {
                BlendAnimation(anim_mantisAirToRightWall, 1.0f, 3.0f, false);
				self.Yaw() = (CModel.ContactNormal().ToYaw() + Math::pi) + Math::Deg2Rad(90.0f);
            }
            else
            {
                BlendLoopingAnimation(anim_mantisLeftWallToAirLoop, 1.0f, 3.0f);
            }
            break;
        }
    }
    
    void LeapDecision(const bool bLeftSide)
    {
        int anim = -1;

        if(!bLeftSide)
        {
            if(m_pRightWallAttack is null)
            {
                return;
            }
            
            anim = RunAttackStage(m_pRightWallAttack,
                m_rightWallAttackCounter, m_rightWallAttackCounter);
        }
        else
        {
            if(m_pLeftWallAttack is null)
            {
                return;
            }
            
            anim = RunAttackStage(m_pLeftWallAttack,
                m_leftWallAttackCounter, m_leftWallAttackCounter);
        }
        
        if(anim == -1)
        {
            return;
        }
        
        BlendAnimation(anim, 2.0f, 6.0f);
    }
    
    /*
    ==============================================================
    CheckGroundToLeapOn
    ==============================================================
    */
    
    void CheckGroundToLeapOn(void)
    {
        int anim;
                
        if(!self.AnimState().CycleCompleted())
        {
            return;
        }
        
        switch(self.AnimState().PlayingID())
        {
        case anim_mantisAttackRightWall1:
        case anim_mantisAttackRightWall2:
        case anim_mantisWallRightIdle:
            LeapDecision(false);
            break;
            
        case anim_mantisAttackLeftWall1:
        case anim_mantisAttackLeftWall2:
        case anim_mantisWallLeftIdle:
            LeapDecision(true);
            break;
        }
    }
    
    /*
    ==============================================================
    LandedOnWall
    ==============================================================
    */
    
    void LandedOnWall(void)
    {
        switch(self.AnimState().PlayingID())
        {
        case anim_mantisAirToRightWall:
            BlendAnimation(anim_mantisAttackRightWall2, 2.0f, 6.0f);
            break;
            
        case anim_mantisAirToLeftWall:
            BlendAnimation(anim_mantisAttackLeftWall2, 2.0f, 6.0f);
            break;
        }
    }
    
    /*
    ==============================================================
    UpdateStage
    ==============================================================
    */
    
    void UpdateStage(void)
    {
        float healthPercnt = float(self.Health()*100) / float(m_initialHealth);
        
        if(healthPercnt < m_pStageMode[MSI_HEALTHPERCNT])
        {
            m_stage++;
            if(m_stage >= int(g_mantisStages.length()))
            {
                m_stage = int(g_mantisStages.length()-1);
            }
            
            @m_pStageMode = g_mantisStages[m_stage];
            m_animSpeed = m_pStageMode[MSI_SPEED];
            
            m_floorAttackCounter = 0;
            m_CloseFloorAttackCounter = 0;
            m_leftWallAttackCounter = 0;
            m_rightWallAttackCounter = 0;
            m_ceilingAttackCounter = 0;
            
            m_bDamageStunned = true;
            
            switch(m_stage)
            {
            case 1:
                self.RunFxEvent("Mantis_GlowYellow");
                @m_pFloorAttack = @g_mantisFloorAttack_Stage2;
                @m_pCloseFloorAttack = @g_mantisFloorCloseAttack_Stage2;
                @m_pLeftWallAttack = g_mantisLeftAttack_Stage2;
                @m_pRightWallAttack = g_mantisRightAttack_Stage2;
                break;
            case 2:
                self.RunFxEvent("Mantis_GlowPurple");
                @m_pFloorAttack = @g_mantisFloorAttack_Stage3;
                @m_pCloseFloorAttack = @g_mantisFloorCloseAttack_Stage3;
                @m_pLeftWallAttack = g_mantisLeftAttack_Stage3;
                @m_pRightWallAttack = g_mantisRightAttack_Stage3;
                @m_pCeilingAttack = g_mantisCeilingAttack_Stage3;
                break;
            case 3:
                self.RunFxEvent("Mantis_GlowRed");
                @m_pCeilingAttack = g_mantisCeilingAttack_Stage4;
                break;
            }
            
            BlendAnimation(anim_mantisPain, 2.0f, 6.0f, false);
            self.Flags() |= AF_INVINCIBLE;
        }
    }
    
    void OnTick(void)
    {
        if (m_state == BP_MS_DEAD || IsAIDisabled(self))
        {
            return;
        }
                
        switch(m_state)
        {
        case BP_MS_GROUNDCHASE:
            GetTargetPoint();
            break;
        }
        
        CheckTurning();
		
        switch(self.AnimState().PlayingID())
        {
        case anim_mantisWake:
            if(self.AnimState().Stopped())
            {
                self.SetHeadTrackTarget(Player.Actor().CastToActor());
                GroundChase();
            }
            break;
        
        case anim_mantisPain:
            if(self.AnimState().Stopped())
            {
                self.Flags() &= ~AF_INVINCIBLE;
                m_bDamageStunned = false;
                GroundChase();
            }
            break;
            
        case anim_mantisChargeForward:
        case anim_mantisRightWallToGround:
        case anim_mantisLeftWallToGround:
        case anim_mantisAirToGround:
        case anim_mantisCeilingToGround:
        case anim_mantisDamageBack:
        case anim_mantisDamageRight:
        case anim_mantisDamageLeft:
            if(self.AnimState().Stopped())
            {
                GroundChase();
            }
            break;
            
        case anim_mantisGroundToRightWall:
        case anim_mantisGroundToLeftWall:
            CheckWallToLandOn();
            break;
            
        case anim_mantisRightWallToAirLoop:
        case anim_mantisLeftWallToAirLoop:
            CheckWallToLandOn();
            break;
            
        case anim_mantisAirToRightWall:
        case anim_mantisAirToLeftWall:
            LandedOnWall();
            break;
            
        case anim_mantisRightWallToAir:
            BlendLoopingAnimation(anim_mantisRightWallToAirLoop, 1.0f, 3.0f);
            break;
            
        case anim_mantisLeftWallToAir:
            BlendLoopingAnimation(anim_mantisLeftWallToAirLoop, 1.0f, 3.0f);
            break;
            
        case anim_mantisGroundToCeiling:
        case anim_mantisRightWallToCeiling:
        case anim_mantisLeftWallToCeiling:
            if(self.AnimState().Stopped())
            {
                CeilingChase();
            }
            break;
            
        case anim_mantisCeilingIdle:
        case anim_mantisCeilingForward:
        case anim_mantisCeilingStandTurnRight:
        case anim_mantisCeilingStandTurnLeft:
        case anim_mantisAttackCeiling1:
        case anim_mantisAttackCeiling2:
            CeilingChase();
            break;
            
        case anim_mantisWallRightIdle:
        case anim_mantisWallLeftIdle:
        case anim_mantisAttackRightWall1:
        case anim_mantisAttackRightWall2:
        case anim_mantisAttackLeftWall1:
        case anim_mantisAttackLeftWall2:
            CheckGroundToLeapOn();
            break;
        
        case anim_mantisAttackGround1:
        case anim_mantisAttackGround2:
        case anim_mantisSlowAttack:
        case anim_aiMelee1:
        case anim_mantisAttackGroundMelee:
            if(self.AnimState().Blending())
            {
                TurnAngles(Math::Deg2Rad(45.0f), self.GetTurnYaw(m_goalOrigin));
            }
            // fall through
        case anim_mantisGroundIdle:
        case anim_mantisGroundStandTurnRight:
        case anim_mantisGroundStandTurnLeft:
        case anim_mantisGroundForward:
            GroundChase();
            break;
            
        case anim_mantisGroundToAir:
            BlendLoopingAnimation(anim_mantisAirLoop, 1.0f, 3.0f);
            break;
            
        case anim_mantisAirLoop:
            FlyChase();
            break;
        }
        
        if(self.AnimState().PlayingID() != anim_mantisAirLoop)
        {
            m_pitchTilt = Math::NLerp(m_pitchTilt, Math::Deg2Rad(4.0f), 0.0f);
            m_rollTilt = Math::NLerp(m_rollTilt, Math::Deg2Rad(4.0f), 0.0f);
        }
        
        self.Pitch() = m_pitchTilt;
        self.Roll() = -m_rollTilt;
    }
    
    /*
    ==============================================================
    OnBeginLevel
    ==============================================================
    */
    
    void OnPostBeginLevel(void)
    {
		m_initialHealth = self.Health();
		m_state = BP_MS_GROUNDCHASE;
		self.AnimState().Set(anim_mantisGroundIdle, 4.0f, ANF_ROOTMOTION);
		self.SetHeadTrackTarget(Player.Actor().CastToActor());
		self.SetTarget(Player.Actor().CastToActor());
		self.ClipFlags() &= ~CF_NOSTEPDOWN;
    }
    
	void OnEndLevel(void)
    {
    }
    
    /*
    ==============================================================
    OnDamage
    ==============================================================
    */
    
    void OnDamage(kActor @instigator, kDictMem @damageDef, const int damage)
    {
        bool bValue = false;
        
        if(self.Health() > 0 && !m_bDamageStunned)
        {
            self.RunFxEvent("Mantis_Hit");
        }
        
        if(self.Health() <= 0)
        {
            return;
        }
        
        if(damageDef is null)
        {
            return;
        }
        
        if(damageDef.GetBool("bCauseSpecialAnimation", bValue) && bValue)
        {
            float diff;
            
            if(!self.RandomDecision(2))
            {
                return;
            }
            
            switch(self.AnimState().PlayingID())
            {
            case anim_mantisGroundIdle:
            case anim_mantisGroundStandTurnRight:
            case anim_mantisGroundStandTurnLeft:
            case anim_mantisGroundForward:
                break;
                
            default:
                return;
            }
            
            diff = (Player.Actor().Yaw() - self.Yaw() - Math::Deg2Rad(45.0f)) / Math::Deg2Rad(90.0f);
            
            switch(int(diff) & 3)
            {
            case 0:
                BlendAnimation(anim_mantisDamageRight, 1.5f, 4.0f, false);
                break;
            case 1:
                BlendAnimation(anim_mantisChargeForward, 1.5f, 4.0f, false);
                break;
            case 2:
                BlendAnimation(anim_mantisDamageBack, 1.5f, 4.0f, false);
                break;
            case 3:
                BlendAnimation(anim_mantisDamageLeft, 1.5f, 4.0f, false);
                break;
            }
        }
    }
    
    /*
    ==============================================================
    OnDeath
    ==============================================================
    */
    
    void OnDeath(kActor @killer, kDictMem @damageDef)
    {
		SetKills(GetKills() + 1);
		Game.CallDelayedMapScript(EnemyDeathScriptID, @self, 0);
		
        self.SetTarget(null);
        self.SetHeadTrackTarget(null);
        self.Velocity().Clear();
        self.Movement().Clear();
        self.StopLoopingSounds();
        
        FlightToGround();
        
        m_state = BP_MS_DEAD;
		self.Flags() &= ~AF_SOLID;
		self.Flags() |= AF_DEAD;
		self.AnimState().Set(anim_mantisDeath, 8.0f, ANF_ROOTMOTION);
        self.Pitch() = 0;
        self.Roll() = 0;
		self.MarkPersistentBit(false);
    }
	//------------------------------------------------------------------------------------------------------------------------
	void OnRestore()
	{
		if (ActorExists(statue))
		{
			statue.Remove();
		}
		self.Remove();
		
	}
	//------------------------------------------------------------------------------------------------------------------------
}
