#include "scripts/animations.txt"

enum BP_hummerGlobalState
{
    BP_HGS_NEW             = 0,
    BP_HGS_ENTERED_ARENA,
    BP_HGS_DEAD
}

final class BP_TurokHummer : TurokEnemy
{
    float m_distance;
    kAngle m_anDesiredYaw;
    kAngle m_anWheelRot1;
    kAngle m_anWheelRot2;
    kAngle m_anWheelRot3;
    kAngle m_anWheelRot4;
    kAngle m_anWheelAngle;
    kAngle m_anSteerAngle;
    float m_wheelSpeed1;
    float m_wheelSpeed2;
    float m_wheelSpeed3;
    float m_wheelSpeed4;
    float m_knockCoolDown;
    float m_turnSpeed;
    float m_wheelVel;
    kVec3 m_vGoalOrigin;
    bool m_bSpunOut;
    int m_lastSound;
    int m_targetNumber;
    float m_distFromCenter;
    kActor @m_pDummyActor;
    kActor @m_pDummyActor2;
	bool m_bTargetDead;
	int m_bossID;
	int initialHealth;
	int state;

    BP_TurokHummer(kActor @actor)
    {
        m_distance = 0;
        m_anDesiredYaw = 0;
        m_anWheelRot1 = 0;
        m_anWheelRot2 = 0;
        m_anWheelRot3 = 0;
        m_anWheelRot4 = 0;
        m_anWheelAngle = 0;
        m_anSteerAngle = 0;
        m_knockCoolDown = 0;
        m_wheelSpeed1 = 0;
        m_wheelSpeed2 = 0;
        m_wheelSpeed3 = 0;
        m_wheelSpeed4 = 0;
        m_wheelVel = 0;
        m_targetNumber = 0;
        m_distFromCenter = 0;
        m_lastSound = -1;
        @m_pDummyActor = null;
        @m_pDummyActor2 = null;
        m_bSpunOut = false;
        m_turnSpeed = 0.033f;
		state = BP_HGS_NEW;
        super(actor);
    }
   
    /*
    ==============================================================
    HummerSounds
    ==============================================================
    */
    
    void HummerSounds(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        int soundID = int(w);
        
        if(soundID == m_lastSound)
        {
            return;
        }
        
        self.StopLoopingSounds();
        m_lastSound = soundID;
        
        switch(soundID)
        {
        case 66:
            self.PlaySound("sounds/shaders/explosion_2.ksnd");
            break;
            
        case 470:
            self.PlaySound("sounds/shaders/generic_85_humvee_180_spinout.ksnd");
            break;
            
        case 471:
            self.PlaySound("sounds/shaders/generic_86_humvee_180_turn.ksnd");
            break;
            
        case 472:
            self.PlaySound("sounds/shaders/generic_87_humvee_jump.ksnd");
            break;
            
        case 468:
            self.PlaySound("sounds/shaders/generic_83_humvee_accelerate_looped.ksnd");
            break;
            
        case 469:
            self.PlaySound("sounds/shaders/generic_84_humvee_stop_accelerate.ksnd");
            break;
        }
    }
    
    /*
    ==============================================================
    OnCollide
    ==============================================================
    */
    
    void OnCollide(kCModel @pCModel)
    {
        kActor @pTarget;
        
        if(self.Health() <= 0)
        {
            return;
        }
        
        if(m_knockCoolDown > 0.0f)
        {
            return;
        }
        
        if(pCModel.ContactActor() is null)
        {
            return;
        }
        
        @pTarget = Player.Actor().CastToActor();
        
        if(!(pCModel.ContactActor() is pTarget))
        {
            return;
        }
        
        KnockbackTarget(pTarget);
    }
    
    /*
    ==============================================================
    KnockbackTarget
    ==============================================================
    */
    
    void KnockbackTarget(kActor @pActor)
    {
        kVec3 org;
        kVec3 pos;
        
        pActor.PlaySound("sounds/shaders/generic_81_kick_impact.ksnd");
        pActor.InflictGenericDamage(self, 10);
        
        Player.Actor().RecoilPitch() = -Math::Deg2Rad(45.0f);
        
        m_knockCoolDown = 1.0f;
        
        org.x = self.Origin().x;
        org.y = self.Origin().y;
        org.z = self.Origin().z + self.Height() * 0.5f;
        
        pos = pActor.Origin();
        
        kVec3 dir = pos - org;
        float dist = dir.Unit();
        
        if(dist > (1024 + self.Radius()))
        {
            return;
        }
        
        dir.Normalize();
        dir *= (1.75f * GAME_SCALE);
        dir.z = (0.875 * GAME_SCALE);
        
        pActor.Velocity() += dir;
        Player.Actor().PlayerFlags() |= PF_NOAIRFRICTION;
    }
    
    /*
    ==============================================================
    UpdateGoalOrigin
    ==============================================================
    */
    
    void UpdateGoalOrigin(void)
    {
        kVec3 vPos = Player.Actor().Origin();
        bool bSetNewTarget = false;
        
        m_distance = Math::Sqrt(self.DistanceToPoint(vPos));
        m_distFromCenter = self.Origin().Unit();
        
        if(m_distance < Math::RandRange(20.0f*GAME_SCALE, 40.0f*GAME_SCALE))
        {
            m_targetNumber++;
            if(m_targetNumber > 1)
            {
                m_targetNumber = 0;
            }
            
            bSetNewTarget = true;
        }
        else
        {
            bSetNewTarget = false;
        }
        
        switch(m_targetNumber)
        {
        case 0:
            if(bSetNewTarget)
            {
                float unit = vPos.Unit();
                
                if(unit > (68*GAME_SCALE))
                {
                    unit = (48*GAME_SCALE);
                }
                else
                {
                    unit = (88*GAME_SCALE);
                }
                
                m_vGoalOrigin = vPos.Normalize() * unit;
            }
            else
            {
                m_vGoalOrigin = vPos;
            }
            break;
            
        case 1:
            m_vGoalOrigin = vPos;
            break;
        }
        
        m_anDesiredYaw = self.GetAvoidanceAngle(m_vGoalOrigin, 1.5f) - self.Yaw();
    }
    
    /*
    ==============================================================
    UpdateWheels
    ==============================================================
    */
    
    void UpdateWheels(void)
    {
        float moveAngle = self.Movement().ToYaw();
        float diff = Math::Fabs(self.Yaw().Diff(moveAngle));
        float speed;
        int playingAnim;
        kQuat qTurnRotation;
        
        playingAnim = self.AnimState().PlayingID();
        
        if(self.Health() > 0)
        {
            float unit = self.Movement().Unit() * 0.035f;
            
            if(diff < Math::Deg2Rad(90))
            {
                speed = -unit;
            }
            else
            {
                speed = unit;
            }
            
            m_wheelVel += (speed - m_wheelVel) / 2.0f;
        }
        else
        {
            // dampen
            m_wheelVel -= (m_anWheelRot1 / 8192.0f);
            m_anWheelRot1 -= (m_anWheelRot1 / 512.0f);
        }
        
        // increase rotation of wheels
        m_anWheelRot1 += m_wheelVel;
        m_anWheelRot2 += m_wheelVel;
        m_anWheelRot3 += m_wheelVel;
        m_anWheelRot4 += m_wheelVel;
        
        if(self.AnimState().PlayingID() == anim_aiRunning)
        {
            const float maxWheelRange = Math::Deg2Rad(45.0f);
            
            m_anWheelAngle = -m_anDesiredYaw;
            
            if(m_anWheelAngle > maxWheelRange)
            {
                m_anWheelAngle =  maxWheelRange;
            }
            else if(m_anWheelAngle < -maxWheelRange)
            {
                m_anWheelAngle = -maxWheelRange;
            }
        }
        else
        {
            m_anWheelAngle = 0;
        }
        
        m_anSteerAngle += (m_anWheelAngle - m_anSteerAngle) / 8.0f;
        
        qTurnRotation = kQuat(m_anSteerAngle, 0, 0, 1);
        
        // 12 = right back
        // 14 = right front
        // 16 = left front
        // 18 = left back
        
        self.RenderModel().SetRotationOffset(16, kQuat(m_anWheelRot1, 0, 1, 0) * qTurnRotation);
        self.RenderModel().SetRotationOffset(14, kQuat(m_anWheelRot2, 0, 1, 0) * qTurnRotation);
        self.RenderModel().SetRotationOffset(12, m_anWheelRot3, 0, 1, 0);
        self.RenderModel().SetRotationOffset(18, m_anWheelRot4, 0, 1, 0);
    }
    
    /*
    ==============================================================
    Chase
    ==============================================================
    */
    
    void Chase(void)
    {
        float assist;
        
        if(Player.Locked())
        {
            return;
        }
            
        assist = Math::Deg2Rad(5.0f);
        
        if(m_anDesiredYaw < -Math::Deg2Rad(170.0f))
        {
            if(self.RandomDecision(3))
            {
                self.AnimState().Blend(anim_aiTurn_B_Stand, 4.0f, 4.0f, ANF_ROOTMOTION);
            }
            else
            {
                self.AnimState().Blend(anim_aiTurn_B_Run, 4.0f, 4.0f, ANF_ROOTMOTION);
            }
            
            self.StopLoopingSounds();
            m_pDummyActor.StopLoopingSounds();
            self.PlaySound("sounds/shaders/generic_84_humvee_stop_accelerate.ksnd");
            return;
        }
        else
        {
            if(Math::Fabs(m_anDesiredYaw) >= Math::Deg2Rad(90.0f))
            {
                assist *= 2.0f;
                m_bSpunOut = true;
                
                if(!(m_pDummyActor is null))
                {
                    m_pDummyActor.PlaySound("sounds/shaders/generic_85_humvee_180_spinout.ksnd");
                }
            }
            else if(m_bSpunOut)
            {
                m_bSpunOut = false;
                
                if(!(m_pDummyActor is null))
                {
                    m_pDummyActor.StopLoopingSounds();
                }
            }
        }
        
        TurnAngles(assist, m_anDesiredYaw);
    }
    
    void OnTick(void)
    {
		if (state == BP_HGS_DEAD)
		{
			return;
		}
		
        if((self.Flags() & AF_HIDDEN) != 0)
        {
            return;
        }
        
        UpdateGoalOrigin();
        UpdateWheels();
        
        if(!(m_pDummyActor is null))
        {
            m_pDummyActor.Origin() = self.Origin();
        }
        
        if(!(m_pDummyActor2 is null))
        {
            m_pDummyActor2.Origin() = self.Origin();
        }
        
        if(m_knockCoolDown > 0.0f)
        {
            m_knockCoolDown -= 0.033f;
        }
        
        if((PlayLoop.Ticks() & 255) == 0)
        {
            m_bSpunOut = false;
        }
        
        switch(self.AnimState().PlayingID())
        {
        case anim_aiTeleportIn:
            if(self.AnimState().Stopped())
            {
                self.ClipFlags() &= ~CF_DROPOFF;
                self.ClipFlags() |= (CF_NOENTERWATER|CF_NOEXITWATER);
                self.AnimState().Blend(anim_aiWalking, 16.0f, 16.0f, ANF_ROOTMOTION);
                
				state = BP_HGS_ENTERED_ARENA;
                self.RenderModel().HideSection(0, 18, false);
                
                m_vGoalOrigin = Player.Actor().Origin();
            }
            break;
            
        case anim_aiTurn_L_Stand:
        case anim_aiTurn_R_Stand:
        case anim_aiTurn_B_Stand:
        case anim_aiTurn_B_Run:
            if(self.AnimState().Stopped())
            {
                self.AnimState().Blend(anim_aiWalking, 16.0f, 16.0f, ANF_ROOTMOTION);
                self.StopLoopingSounds();
            }
            break;
            
        case anim_aiWalking:
            self.AnimState().Blend(anim_aiRunning, 6.0f, 16.0f, ANF_ROOTMOTION|ANF_LOOP);
            break;
            
        case anim_aiRunning:
            if(!self.AnimState().Blending())
            {
                Chase();
            }
            break;
            
        case anim_aiDeathStand:
            if(self.AnimState().PlayTime() >= 4.0f)
            {
                float x = Math::RandCFloat() * (12*GAME_SCALE);
                float y = Math::RandCFloat() * (12*GAME_SCALE);
                float z = Math::RandFloat() * (7*GAME_SCALE);
                
                self.SpawnFx("fx/hummer_smoke.kfx", kVec3(x, y, z));
                
                if((self.Flags() & AF_NOMOVEMENT) == 0)
                {
                    self.Flags() |= AF_NOMOVEMENT;
                }
            }
            break;
        }
    }
    
    /*
    ==============================================================
    GunFire
    ==============================================================
    */
    
    void GunFire(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        if(Math::Fabs(self.GetTurnYaw(Player.Actor().Origin())) > Math::Deg2Rad(25.0f))
        {
            return;
        }
        
        if(m_distance <= (125*GAME_SCALE) && m_distance >= (25*GAME_SCALE))
        {
            kVec3 l_GunPos(-7.519531f*GAME_SCALE, -23.144531f*GAME_SCALE, 11.621094f*GAME_SCALE);
            kVec3 r_GunPos( 7.861328f*GAME_SCALE, -23.144531f*GAME_SCALE, 11.621094f*GAME_SCALE);
            
            m_pDummyActor2.PlaySound("sounds/shaders/rifle_shot.ksnd");
            
            self.SpawnFx("fx/generic_128.kfx", l_GunPos);
            self.SpawnFx("fx/generic_128.kfx", r_GunPos);
        }
        
        if(m_distance <= (200*GAME_SCALE) && m_distance >= (50*GAME_SCALE))
        {
            if(self.RandomDecision(3))
            {
                kVec3 l_RocketPos(-6.347656f*GAME_SCALE, -1.348877f*GAME_SCALE, 16.601563f*GAME_SCALE);
                kVec3 r_RocketPos( 7.861328f*GAME_SCALE, -1.348877f*GAME_SCALE, 16.601563f*GAME_SCALE);
                
                self.SpawnFx("fx/generic_124.kfx", l_RocketPos);
                self.SpawnFx("fx/generic_124.kfx", r_RocketPos);
                
                m_pDummyActor2.PlaySound("sounds/shaders/missile_launch.ksnd");
            }
        }
    }
    
    void OnPostBeginLevel(void)
    {
        @m_pDummyActor = ActorFactory.Spawn("DummyActor", 0, 0, 0, 0, self.SectorIndex());
        @m_pDummyActor2 = ActorFactory.Spawn("DummyActor", 0, 0, 0, 0, self.SectorIndex());
		initialHealth = self.Health();
		self.AnimState().Set(anim_aiWalking, 16.0f, ANF_ROOTMOTION);
		m_vGoalOrigin = Player.Actor().Origin();
		self.SetTarget(Player.Actor().CastToActor());
        self.RenderModel().HideSection(0, 18, false);
		state = BP_HGS_ENTERED_ARENA;
	}
	
    /*
    ==============================================================
    OnEndLevel
    ==============================================================
    */
    
    void OnEndLevel(void)
    {

    }
    
    /*
    ==============================================================
    OnDeath
    ==============================================================
    */
    
    void OnDeath(kActor @killer, kDictMem @damageDef)
    {
        self.AnimState().Blend(anim_aiDeathStand, 6.0f, 4.0f, ANF_ROOTMOTION|ANF_LOOP);
        self.Flags() &= ~AF_SOLID;
		self.Flags() |= AF_DEAD;
		self.SetTarget(null);
        
        self.StopLoopingSounds();
        
        if(!(m_pDummyActor is null))
        {
            m_pDummyActor.StopLoopingSounds();
        }
        
        if(!(m_pDummyActor2 is null))
        {
            m_pDummyActor2.StopLoopingSounds();
        }
		
        state = BP_HGS_DEAD;
		self.MarkPersistentBit(false);
    }
	//------------------------------------------------------------------------------------------------------------------------
	void OnRestore()
	{
        if(!(m_pDummyActor is null))
        {
            m_pDummyActor.Remove();
        }
        if(!(m_pDummyActor2 is null))
        {
            m_pDummyActor2.Remove();
        }
		self.Remove();
	}
	//------------------------------------------------------------------------------------------------------------------------
}
