#include "scripts/common.txt"

//(7) Params 1 -128..127 (type of enemy. 0 = Normal Grunt, 1 = Fireborn, 2 = Adon, 3 = Fireborn Spawn)
//(6) Params 2 -128..127 (Data)
	//if is Fireborn then when not set to 0, will not spawn another fireborn


enum humanModelNodes
{
    HUMAN_ROOT,
    HUMAN_LUPLEG,
    HUMAN_LLOWLEG,
    HUMAN_LFOOT,
    HUMAN_LTOES,
    HUMAN_RUPLEG,
    HUMAN_RLOWLEG,
    HUMAN_RFOOT,
    HUMAN_RTOES,
    HUMAN_TORSO,
    HUMAN_LSHOULDER,
    HUMAN_LUPARM,
    HUMAN_LLOWARM,
    HUMAN_LHAND,
    HUMAN_RSHOULDER,
    HUMAN_RUPARM,
    HUMAN_RLOWARM,
    HUMAN_RHAND,
    HUMAN_NECK,
    HUMAN_HEAD,
    HUMAN_MAXNODE
}

const array<int> BPGruntMeleeAnims = {anim_aiMelee2, anim_aiMelee3, anim_aiMelee4, anim_aiMelee5, anim_aiAltMelee1, anim_aiAltMelee2, anim_aiAltMelee3};
const array<int> BPGruntRangeAnims = {anim_aiRangeAttack1, anim_aiRangeAttack3, anim_aiRangeAttack9};
int MaxFirebornSpawnCount = 5; //changes based on difficulty

// const array<int> BPGruntRangeAnims = {anim_aiRangeAttack1, anim_aiRangeAttack2, anim_aiRangeAttack3, anim_aiRangeAttack4,
										// anim_aiRangeAttack5, anim_aiRangeAttack6, anim_aiRangeAttack7, anim_aiRangeAttack9, anim_aiRangeAttack10};

	//anim_aiRangeAttack10 - Shaman Powerup Speel Cast
	//anim_aiRangeAttack9 - Demon Attack (shoots fireball)
	//anim_aiRangeAttack7 - GunFire (Plasma guy fast crouch shot)
	//anim_aiRangeAttack6 - Tribal Dart1
	//anim_aiRangeAttack5 - GunFire (Plasma guy fast shot)
	//anim_aiRangeAttack4 - Heatseeking shaman spell cast
	//anim_aiRangeAttack3 - Grenade
	//anim_aiRangeAttack2 - GunFire
	//anim_aiRangeAttack1 - Shaman Spell Attack
	//anim_aiMelee2-5
	//anim_aiAltMelee1
	//anim_aiAltMelee2 - Double Swing
	//anim_aiAltMelee3 -  BackFist
	//anim_aiTeleportIn - Shaman Teleport up from ground
	//anim_aiTeleportOut - Shaman Begin Teleporting
	//hanging - anim_special_event01
	
/*
==============================================================
TurokGrunt
==============================================================
*/

class TurokGrunt : TurokEnemy
{
	//For Fireborn Spawn
	bool spawnFromFireborn;
	float fireSpawnDelay;
	float curAttackTime;
	float meleeRange = 100.0f;
	float shootRange = 4000.0f;
	float minAttackDelay = 0.5f; //in seconds
	float maxAttackDelay = 1.5f; //in seconds
	int firebornSpawnCount;
	bool hasTicked;
	float deathTime;
	
    TurokGrunt(kActor @actor)
    {
        super(actor);
    }
    
	bool IsRobot()
	{
		return self.Type() == 6050;
	}
		
	bool IsFireborn()
	{
		return Game.GetCurrentMapID() >= 95 && self.SpawnParams(7) == 1;
	}
	
	bool IsAdon()
	{
		return Game.GetCurrentMapID() >= 95 && self.SpawnParams(7) == 2;
	}

	bool IsFirebornSpawn()
	{
		return spawnFromFireborn || (Game.GetCurrentMapID() >= 95 && self.SpawnParams(7) == 3);
	}

	void OnWake()
	{
		if (spawnFromFireborn)
		{
			if ((self.Flags() & AF_DEAD) != 0)
			{
				return;
			}
			
			if (fireSpawnDelay <= 0.0f)
			{
				self.SetTarget(Player.Actor().CastToActor());
				self.CastToAI().AIFlags() |= (1 << 23); //damage panic
			}
		}
	}
	// void OnSpawn()
    // {
		// TurokEnemy::OnSpawn();
    // }
	
	void OnPostBeginLevel()
	{
		if (IsAdon())
		{
			if (Game.GetCurrentMapID() != 115)
			{
				self.RunFxEvent("AdonGlow");
			}
		}
		else if (IsFireborn())
		{
			self.ImpactType() = IT_FLESH_CREATURE;
			//self.RunFxEvent("FirebornGlow");
		}
		else if (IsFirebornSpawn())
		{
			DoFirebornSpawnStart();
		}
		
		TurokEnemy::OnPostBeginLevel();
	}
	
    /*
    ==============================================================
    HighPriestSpellAttack
    ==============================================================
    */
    
    void HighPriestSpellAttack(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        if(self.GetTarget() is null)
        {
            return;
        }
        
        self.SpawnFx("fx/spellcast.kfx", kVec3(x, y, z));
    }
    
    /*
    ==============================================================
    HighPriestSpellSound
    ==============================================================
    */
    
    void HighPriestSpellSound(kActor @instigator, const float w, const float x, const float y, const float z)
    {
		if (IsRobot())
		{
			self.PlaySound("sounds/shaders/high_priest_spell_attack_robot.ksnd");
		}
		else
		{
			self.PlaySound("sounds/shaders/high_priest_spell_attack.ksnd");
		}
    }
    
    /*
    ==============================================================
    SwooshTrail1
    ==============================================================
    */
    
    void SwooshTrail1(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        switch(self.ModelVariation())
        {
        case GV_GENERIC_AXE:
        case GV_DEMON_AXE:
            self.RenderModel().AddTrailEffect("Trail_Axe", 17);
            break;
            
        case GV_GENERIC_CLUB:
        case GV_SHAMAN:
            self.RenderModel().AddTrailEffect("Trail_Mace", 17);
            break;
        
        case GV_COMMANDER_SPEAR:
        case GV_WARRIOR_SPEAR:
        case GV_DEMON_SPEAR:
            self.RenderModel().AddTrailEffect("Trail_Pole1", 17);
            break;
            
        case GV_COMMANDER_RIFLE1:
        case GV_COMMANDER_RIFLE2:
        case GV_COMMANDER_RIFLE3:
        case GV_COMMANDER_RIFLE4:
        case GV_POACHER_RIFLE:
        case GV_CYBORG_PULSE_RIFLE:
            self.RenderModel().AddTrailEffect("Trail_Shotgun", 17);
            break;
            
        case GV_POACHER_KNIFE:
            self.RenderModel().AddTrailEffect("Trail_Sword", 17);
            break;
            
        case GV_POACHER_PISTOL1:
        case GV_POACHER_PISTOL2:
            self.RenderModel().AddTrailEffect("Trail_Pistol", 17);
            break;
            
        case GV_POACHER_SHOTGUN:
            self.RenderModel().AddTrailEffect("Trail_Shotgun", 17);
            break;
            
        case GV_WARRIOR_BONE:
            self.RenderModel().AddTrailEffect("Trail_Bone", 17);
            break;
            
        case GV_WARRIOR_DART:
            self.RenderModel().AddTrailEffect("Trail_DartGun", 17);
            break;
        }
    }
    
    /*
    ==============================================================
    GunFire
    ==============================================================
    */
    
    void GunFire(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        if(self.GetTarget() is null)
        {
            return;
        }
        
        if( self.ModelVariation() == GV_COMMANDER_RIFLE4 ||
            self.ModelVariation() == GV_CYBORG_PULSE_RIFLE)
        {
            self.PlaySound("sounds/shaders/machine_gun_shot_2.ksnd");
            self.SpawnProjectile("fx/enemy_longhunter.kfx", kVec3(x, y, z),
                self.GetTarget().Origin(), Math::Deg2Rad(45.0f));
                
            return;
        }
        
        self.PlaySound("sounds/shaders/generic_8_enemy_human_gunfire.ksnd");
        self.SpawnProjectile("fx/enemy_bullet.kfx", kVec3(x, y, z),
            self.GetTarget().Origin(), Math::Deg2Rad(45.0f));
    }
    
    /*
    ==============================================================
    ThrowGrenade
    ==============================================================
    */
    
    void ThrowGrenade(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.SpawnFx("fx/generic_114.kfx", kVec3(x, y, z));
    }
    
    /*
    ==============================================================
    DemonAttack
    ==============================================================
    */
    
    void DemonAttack(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        if(self.GetTarget() is null)
        {
            return;
        }
        
        self.SpawnProjectile("fx/generic_256.kfx", kVec3(x, y, z),
            self.GetTarget().Origin(), Math::Deg2Rad(45.0f));
    }
    
    /*
    ==============================================================
    TribalDart1
    ==============================================================
    */
    
    void TribalDart1(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        if(self.GetTarget() is null)
        {
            return;
        }
        
        self.SpawnFx("fx/generic_193.kfx", kVec3(x, y, z));
    }
    
    /*
    ==============================================================
    TribalDart2
    ==============================================================
    */
    
    void TribalDart2(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.SpawnFx("fx/generic_194.kfx", kVec3(x, y, z));
    }
    
    /*
    ==============================================================
    DeathSound
    ==============================================================
    */
    
    void DeathSound(kActor @instigator, const float w, const float x, const float y, const float z)
    {
		if (IsFireborn() || IsFirebornSpawn())
		{
			self.PlaySound("sounds/shaders/fireborn_death_scream_1.ksnd");
			return;
		}
		else if (IsAdon())
		{
			self.PlaySound("sounds/shaders/adon_hurt" + (Math::RandMax(2) + 1) + ".ksnd");
			return;
		}

        if( self.ModelVariation() >= GV_CYBORG_PULSE_RIFLE &&
            self.ModelVariation() <= GV_CYBORG_SPEAR)
        {
			if (IsRobot())
			{
				self.PlaySound("sounds/shaders/ancient_warrior_death_robot.ksnd");
			}
			else
			{
				self.PlaySound("sounds/shaders/ancient_warrior_death.ksnd");
			}
            return;
        }
        else if(self.ModelVariation() >= GV_DEMON_AXE &&
                self.ModelVariation() <= GV_DEMON_LORD)
        {
            switch(Math::RandMax(3))
            {
            case 0:
				if (IsRobot())
				{
					self.PlaySound("sounds/shaders/demon_death_scream_1_robot.ksnd");
				}
				else
				{
					self.PlaySound("sounds/shaders/demon_death_scream_1.ksnd");
				}
                break;
            case 1:
				if (IsRobot())
				{
					self.PlaySound("sounds/shaders/demon_death_scream_2_robot.ksnd");
				}
				else
				{
					self.PlaySound("sounds/shaders/demon_death_scream_2.ksnd");
				}
                break;
            case 2:
				if (IsRobot())
				{
					self.PlaySound("sounds/shaders/demon_death_scream_3_robot.ksnd");
				}
				else
				{
					self.PlaySound("sounds/shaders/demon_death_scream_3.ksnd");
				}
                break;
            }
            return;
        }
        
		switch(Math::RandMax(3))
		{
		case 0:
			if (IsRobot())
			{
				self.PlaySound("sounds/shaders/human_death_scream_1_robot.ksnd");
			}
			else
			{
				self.PlaySound("sounds/shaders/human_death_scream_1.ksnd");
			}
			break;
		case 1:
			if (IsRobot())
			{
				self.PlaySound("sounds/shaders/human_death_scream_2_robot.ksnd");
			}
			else
			{
				self.PlaySound("sounds/shaders/human_death_scream_2.ksnd");
			}
			break;
		case 2:
			if (IsRobot())
			{
				self.PlaySound("sounds/shaders/human_death_scream_3_robot.ksnd");
			}
			else
			{
				self.PlaySound("sounds/shaders/human_death_scream_3.ksnd");
			}
			break;
		}
    }
    
    /*
    ==============================================================
    InjurySound
    ==============================================================
    */
    
    void InjurySound(kActor @instigator, const float w, const float x, const float y, const float z)
    {
		if (IsFireborn() || IsFirebornSpawn())
		{
			self.PlaySound("sounds/shaders/fireborn_death_scream_1.ksnd");
			return;
		}
		else if (IsAdon())
		{
			self.PlaySound("sounds/shaders/adon_hurt" + (Math::RandMax(2) + 1) + ".ksnd");
			return;
		}

        if( self.ModelVariation() >= GV_CYBORG_PULSE_RIFLE &&
            self.ModelVariation() <= GV_CYBORG_SPEAR)
        {
			if (IsRobot())
			{
				self.PlaySound("sounds/shaders/ancient_warrior_death_robot.ksnd");
			}
			else
			{
				self.PlaySound("sounds/shaders/ancient_warrior_death.ksnd");
			}
            return;
        }
        else if(self.ModelVariation() >= GV_DEMON_AXE &&
                self.ModelVariation() <= GV_DEMON_LORD)
        {
            switch(Math::RandMax(3))
            {
            case 0:
				if (IsRobot())
				{
					self.PlaySound("sounds/shaders/demon_effort_injury_grunt_1_robot.ksnd");
				}
				else
				{
					self.PlaySound("sounds/shaders/demon_effort_injury_grunt_1.ksnd");
				}
                break;
            case 1:
				if (IsRobot())
				{
					self.PlaySound("sounds/shaders/demon_effort_injury_grunt_2_robot.ksnd");
				}
				else
				{
					self.PlaySound("sounds/shaders/demon_effort_injury_grunt_2.ksnd");
				}
                break;
            case 2:
				if (IsRobot())
				{
					self.PlaySound("sounds/shaders/demon_effort_injury_grunt_3_robot.ksnd");
				}
				else
				{
					self.PlaySound("sounds/shaders/demon_effort_injury_grunt_3.ksnd");
				}
                break;
            }
            return;
        }
        
        switch(Math::RandMax(3))
        {
        case 0:
			if (IsRobot())
			{
				self.PlaySound("sounds/shaders/human_effort_injury_grunt_1_robot.ksnd");
			}
			else
			{
				self.PlaySound("sounds/shaders/human_effort_injury_grunt_1.ksnd");
			}
            break;
        case 1:
			if (IsRobot())
			{
				self.PlaySound("sounds/shaders/human_effort_injury_grunt_2_robot.ksnd");
			}
			else
			{
				self.PlaySound("sounds/shaders/human_effort_injury_grunt_2.ksnd");
			}
            break;
        case 2:
			if (IsRobot())
			{
				self.PlaySound("sounds/shaders/human_effort_injury_grunt_3_robot.ksnd");
			}
			else
			{
				self.PlaySound("sounds/shaders/human_effort_injury_grunt_3.ksnd");
			}
            break;
        }
    }
    
    /*
    ==============================================================
    ViolentSound
    ==============================================================
    */
    
    void ViolentSound(kActor @instigator, const float w, const float x, const float y, const float z)
    {
		if (IsFireborn() || IsFirebornSpawn())
		{
			self.PlaySound("sounds/shaders/fireborn_death_scream_1.ksnd");
			return;
		}
		else if (IsAdon())
		{
			self.PlaySound("sounds/shaders/adon_hurt" + (Math::RandMax(2) + 1) + ".ksnd");
			return;
		}
		
        if( self.ModelVariation() >= GV_CYBORG_PULSE_RIFLE &&
            self.ModelVariation() <= GV_CYBORG_SPEAR)
        {
			if (IsRobot())
			{
				self.PlaySound("sounds/shaders/ancient_warrior_death_robot.ksnd");
			}
			else
			{
				self.PlaySound("sounds/shaders/ancient_warrior_death.ksnd");
			}
            return;
        }
        else if(self.ModelVariation() >= GV_DEMON_AXE &&
                self.ModelVariation() <= GV_DEMON_LORD)
        {
            switch(Math::RandMax(3))
            {
            case 0:
				if (IsRobot())
				{
					self.PlaySound("sounds/shaders/demon_violent_death_1_robot.ksnd");
				}
				else
				{
					self.PlaySound("sounds/shaders/demon_violent_death_1.ksnd");
				}
                break;
            case 1:
				if (IsRobot())
				{
					self.PlaySound("sounds/shaders/demon_violent_death_2_robot.ksnd");
				}
				else
				{
					self.PlaySound("sounds/shaders/demon_violent_death_2.ksnd");
				}
                break;
            case 2:
				if (IsRobot())
				{
					self.PlaySound("sounds/shaders/demon_violent_death_3_robot.ksnd");
				}
				else
				{
					self.PlaySound("sounds/shaders/demon_violent_death_3.ksnd");
				}
                break;
            }
            return;
        }
        
        switch(Math::RandMax(3))
        {
        case 0:
			if (IsRobot())
			{
				self.PlaySound("sounds/shaders/human_violent_death_1_robot.ksnd");
			}
			else
			{
				self.PlaySound("sounds/shaders/human_violent_death_1.ksnd");
			}
            break;
        case 1:
			if (IsRobot())
			{
				self.PlaySound("sounds/shaders/human_violent_death_2_robot.ksnd");
			}
			else
			{
				self.PlaySound("sounds/shaders/human_violent_death_2.ksnd");
			}
            break;
        case 2:
			if (IsRobot())
			{
				self.PlaySound("sounds/shaders/human_violent_death_3_robot.ksnd");
			}
			else
			{
				self.PlaySound("sounds/shaders/human_violent_death_3.ksnd");
			}
            break;
        }
    }
    
    /*
    ==============================================================
    OnBeginLevel
    ==============================================================
    */
    
    void OnBeginLevel(void)
    {
        kRenderModel @model = self.RenderModel();
        
		if (IsRobot())
		{
			// kStr violenceLevel;
			// Sys.GetCvarValue("g_violencelevel", violenceLevel);
			// // GERMAN: all grunts are robotic; if not in no-blood mode, change their
			// // impact type to metal here. Otherwise, the flesh impact in no-blood 
			// // mode matches the original game more closely.
			// if(violenceLevel.Atoi() < 2)
			// {
				// self.ImpactType() = IT_METAL;
			// }
			self.ImpactType() = IT_METAL;
			
			if(model is null)
			{
				return;
			}

			switch(self.ModelVariation())
			{
			// commander
			case GV_COMMANDER_SPEAR:
			case GV_COMMANDER_RIFLE1:
			case GV_COMMANDER_RIFLE2:
			case GV_COMMANDER_RIFLE3:
			case GV_COMMANDER_RIFLE4:
				for(int i = 0; i < HUMAN_HEAD; i++)
				{
					model.SetTexture(i, Math::SysRand() % 2);
				}
				break;
				
			// cyborg guard
			case GV_CYBORG_PULSE_RIFLE:
			// demon lord
			case GV_DEMON_LORD:
				for(int i = 0; i < HUMAN_MAXNODE; i++)
				{
					model.SetTexture(i, 1);
				}
				break;
			}
		}
		else
		{
			if(model is null)
			{
				return;
			}
			
			if( self.ModelVariation() >= GV_CYBORG_PULSE_RIFLE &&
				self.ModelVariation() <= GV_CYBORG_SPEAR)
			{
				self.ImpactType() = IT_METAL;
			}
			
			if (IsFireborn() || IsFirebornSpawn())
			{
				//Fireborn
				// chest
				model.SetTexture(0, 9);
				model.SetTexture(9, 9);
				
				// legs
				model.SetTexture(1, 5);
				model.SetTexture(3, 5);
				model.SetTexture(4, 5);
				model.SetTexture(5, 5);
				model.SetTexture(7, 5);
				model.SetTexture(8, 5);
				
				// shin
				model.SetTexture(2, 5);
				model.SetTexture(6, 5);
				
				// arms
				model.SetTexture(10, 9);
				model.SetTexture(11, 9);
				model.SetTexture(12, 9);
				model.SetTexture(13, 9);
				model.SetTexture(14, 9);
				model.SetTexture(15, 9);
				model.SetTexture(16, 9);
				model.SetTexture(17, 9); //hands
				
				// neck *
				model.SetTexture(18, 7);
				
				// face
				model.SetTexture(19, 13);
			}
			else if (IsAdon())
			{
				//Adon (also give green glow in actor fx)
				// chest
				model.SetTexture(0, 8);
				model.SetTexture(9, 8);
				
				// legs
				model.SetTexture(1, 4);
				model.SetTexture(3, 4);
				model.SetTexture(4, 4);
				model.SetTexture(5, 4);
				model.SetTexture(7, 4);
				model.SetTexture(8, 4);
				
				// shin
				model.SetTexture(2, 4);
				model.SetTexture(6, 4);
				
				// arms
				model.SetTexture(10, 8);
				model.SetTexture(11, 8);
				model.SetTexture(12, 8);
				model.SetTexture(13, 8);
				model.SetTexture(14, 8);
				model.SetTexture(15, 8);
				model.SetTexture(16, 8);
				model.SetTexture(17, 8); //hands
				
				// neck *
				model.SetTexture(18, 6);
				
				// face
				model.SetTexture(19, 12);		
			}
			else
			{
				switch(self.ModelVariation())
				{
				// common soldier
				case GV_GENERIC:
				case GV_GENERIC_AXE:
				case GV_GENERIC_CLUB:					
					// face
					model.SetTexture(19, Math::SysRand() % 12);
					break;
				
				// commander
				case GV_COMMANDER_SPEAR:
				case GV_COMMANDER_RIFLE1:
				case GV_COMMANDER_RIFLE2:
				case GV_COMMANDER_RIFLE3:
				case GV_COMMANDER_RIFLE4:
					// chest
					model.SetTexture(0, 1);
					model.SetTexture(9, 1);
					
					// arms
					model.SetTexture(10, 1);
					model.SetTexture(11, 1);
					model.SetTexture(12, 1);
					model.SetTexture(13, 1);
					model.SetTexture(14, 1);
					model.SetTexture(15, 1);
					model.SetTexture(16, 1);
					model.SetTexture(17, 1);
					
					// face
					model.SetTexture(19, Math::SysRand() % 12);
					break;
					
				// poacher
				case GV_POACHER_KNIFE:
				case GV_POACHER_PISTOL1:
				case GV_POACHER_PISTOL2:
				case GV_POACHER_RIFLE:
				case GV_POACHER_SHOTGUN:
					// chest
					model.SetTexture(0, 5);
					model.SetTexture(9, 5);
					
					// legs
					model.SetTexture(1, 2);
					model.SetTexture(3, 2);
					model.SetTexture(4, 2);
					model.SetTexture(5, 2);
					model.SetTexture(7, 2);
					model.SetTexture(8, 2);
					
					// shin
					model.SetTexture(2, 2);
					model.SetTexture(6, 2);
					
					// face
					model.SetTexture(19, Math::SysRand() % 12);
					break;
					
				// warrior
				case GV_WARRIOR_BONE:
				case GV_WARRIOR_DART:
				case GV_WARRIOR_SPEAR:
					// hands
					model.SetTexture(17, 2);
					
					// chest
					model.SetTexture(0, 2);
					model.SetTexture(9, 2);
					
					// arms
					model.SetTexture(10, 2);
					model.SetTexture(11, 2);
					model.SetTexture(12, 2);
					model.SetTexture(13, 2);
					model.SetTexture(14, 2);
					model.SetTexture(15, 2);
					model.SetTexture(16, 2);
					
					// neck
					model.SetTexture(18, 1);
					break;
					
				// shaman
				case GV_SHAMAN:
					// chest
					model.SetTexture(0, 3);
					model.SetTexture(9, 3);
					
					// neck
					model.SetTexture(18, 2);
					
					// legs
					model.SetTexture(1, 1);
					model.SetTexture(3, 1);
					model.SetTexture(4, 1);
					model.SetTexture(5, 1);
					model.SetTexture(7, 1);
					model.SetTexture(8, 1);
					
					// shin
					model.SetTexture(2, 1);
					model.SetTexture(6, 1);
					
					// hands
					model.SetTexture(17, 3);
					
					// arms
					model.SetTexture(10, 3);
					model.SetTexture(11, 3);
					model.SetTexture(12, 3);
					model.SetTexture(13, 3);
					model.SetTexture(14, 3);
					model.SetTexture(15, 3);
					model.SetTexture(16, 3);
					break;
					
				// cyborg guard
				case GV_CYBORG_PULSE_RIFLE:
					// legs
					model.SetTexture(1, 1);
					model.SetTexture(3, 1);
					model.SetTexture(4, 1);
					model.SetTexture(5, 1);
					model.SetTexture(7, 1);
					model.SetTexture(8, 1);
					
					// shin
					model.SetTexture(2, 1);
					model.SetTexture(6, 1);
					
					// chest
					model.SetTexture(0, 4);
					model.SetTexture(9, 4);
					
					// arms
					model.SetTexture(10, 4);
					model.SetTexture(11, 4);
					model.SetTexture(12, 4);
					model.SetTexture(13, 4);
					model.SetTexture(14, 4);
					model.SetTexture(15, 4);
					model.SetTexture(16, 4);
					
					// neck
					model.SetTexture(18, 3);
					break;
					
				// demon
				case GV_DEMON_AXE:
				case GV_DEMON_SPEAR:
				case GV_DEMON_HANDS:
					// hands
					model.SetTexture(17, 6);
					model.SetTexture(13, 6);
					
					// legs
					model.SetTexture(1, 2);
					model.SetTexture(3, 2);
					model.SetTexture(4, 2);
					model.SetTexture(5, 2);
					model.SetTexture(7, 2);
					model.SetTexture(8, 2);
					
					// shin
					model.SetTexture(2, 2);
					model.SetTexture(6, 2);
					
					// chest
					model.SetTexture(0, 6);
					model.SetTexture(9, 6);
					
					// arms
					model.SetTexture(10, 6);
					model.SetTexture(11, 6);
					model.SetTexture(12, 6);
					model.SetTexture(13, 6);
					model.SetTexture(14, 6);
					model.SetTexture(15, 6);
					model.SetTexture(16, 6);
					
					// neck
					model.SetTexture(18, 4);
					break;
					
				// demon lord
				case GV_DEMON_LORD:
					// hands
					model.SetTexture(17, 7);
					model.SetTexture(13, 7);
					
					// legs
					model.SetTexture(1, 3);
					model.SetTexture(3, 3);
					model.SetTexture(4, 3);
					model.SetTexture(5, 3);
					model.SetTexture(7, 3);
					model.SetTexture(8, 3);
					
					// shin
					model.SetTexture(2, 3);
					model.SetTexture(6, 3);
					
					// chest
					model.SetTexture(0, 7);
					model.SetTexture(9, 7);
					
					// arms
					model.SetTexture(10, 7);
					model.SetTexture(11, 7);
					model.SetTexture(12, 7);
					model.SetTexture(13, 7);
					model.SetTexture(14, 7);
					model.SetTexture(15, 7);
					model.SetTexture(16, 7);
					
					// neck
					model.SetTexture(18, 5);
					break;
				}
			}
		}
    }
	//------------------------------------------------------------------------------------------------------------------------
    void OnTick(void)
    {
		TurokEnemy::OnTick();
		if (!hasTicked)
		{
			hasTicked = true;
			if (IsFirebornSpawn())
			{
				self.Flags() &= ~AF_NODRAW;
			}
		}
		
		
		if (spawnFromFireborn)
		{
			if ((self.Flags() & AF_DEAD) != 0)
			{
				deathTime += GAME_DELTA_TIME;
				if (Game.GetCurrentMapID() == 112 && deathTime > 30.0f)
				{
					self.Remove();
				}
				return;
			}
			
			if (fireSpawnDelay > 0.0f)
			{
				fireSpawnDelay -= GAME_DELTA_TIME;
				if (fireSpawnDelay <= 0.0f)
				{
					self.RunFxEvent("FirebornGlow");
					self.Scale() = kVec3(0.5f, 0.5f, 0.5f);
					self.Flags() &= ~AF_INVINCIBLE;
					self.Flags() |= AF_SOLID;
					self.CastToAI().AIFlags() &= ~AIF_NOTHINK;
					self.CastToAI().AIFlags() |= (1 << 23); //damage panic
					self.SetTarget(Player.Actor().CastToActor());
					//self.Health() = 50;
				}
				return;
			}
			
			if (!noAttackEnemies)
			{
				self.SetTarget(Player.Actor().CastToActor());
				AIStandard();
			}
			
			if (Game.GetDifficulty() == DIFFICULTY_HARDCORE)
			{
				self.AnimState().ChangeSpeed(1.5f);
			}
			else
			{
				self.AnimState().ChangeSpeed(2.0f);
			}
		}
	}
	//------------------------------------------------------------------------------------------------------------------------
	void PlayFirebornAttackSound()
	{
		if (Math::RandMax(4) == 0)
		{
			if (Math::RandMax(2) == 0)
			{
				self.PlaySound("sounds/shaders/fireborn_attack0.ksnd");
			}
			else
			{
				self.PlaySound("sounds/shaders/fireborn_attack1.ksnd");
			}
		}
	}
	//------------------------------------------------------------------------------------------------------------------------
	void AIStandard()
	{
		if (self.GetTarget() is null)
		{
			return;
		}
		
		if (!IsAttacking())
		{
			kVec3 targetPos = self.GetTarget().Origin();
			float targetDist = Math::Sqrt(self.DistanceToPoint(targetPos));
			if (targetDist <= meleeRange)
			{
				PlayFirebornAttackSound();
				PlayRandomAnimation(BPGruntMeleeAnims);
			}
			else
			{
				curAttackTime -= GAME_DELTA_TIME;
				if (curAttackTime <= 0.0f)
				{
					curAttackTime = Math::RandRange(minAttackDelay, maxAttackDelay);
					if (targetDist <= shootRange)
					{
						PlayFirebornAttackSound();
						PlayRandomAnimation(BPGruntRangeAnims);
					}
				}
			}
		}
	}
	//------------------------------------------------------------------------------------------------------------------------
	bool IsAttacking()
	{
		int animID = self.AnimState().PlayingID();
		if (BPGruntMeleeAnims.find(animID) >= 0) {
			return true;
		}
		if (BPGruntRangeAnims.find(animID) >= 0) {
			return true;
		}
		return false;
	}
	//------------------------------------------------------------------------------------------------------------------------
	void PlayRandomAnimation(const array<int> &anims) {
		int i = Math::RandMax(anims.length());
		PlayAnimation(anims[i]);
    }
	//------------------------------------------------------------------------------------------------------------------------
	void PlayAnimation(int anim) {
        self.AnimState().Blend(anim, 4.0f, 4.0f, ANF_BLEND | ANF_ROOTMOTION);
    }
	//------------------------------------------------------------------------------------------------------------------------
	void DoFirebornSpawnStart()
	{
		fireSpawnDelay = 2.0f;
		self.ImpactType() = IT_FLESH_CREATURE;
		self.RunFxEvent("FirebornSpawn");
		self.Flags() |= AF_INVINCIBLE | AF_NODRAW;
		self.CastToAI().AIFlags() |= AIF_NOTHINK;
	}
	//------------------------------------------------------------------------------------------------------------------------
    void OnDeath(kActor @killer, kDictMem @damageDef)
	{
		TurokEnemy::OnDeath(@killer, @damageDef);
		if (spawnFromFireborn)
		{
			if (Game.GetCurrentMapID() == 112)
			{
				int amount = Math::RandMax(2) + 2;
				for (int i = 0; i < amount; i++)
				{
					TossItem("Health_Small", self.Origin().x, self.Origin().y, self.Origin().z);
				}
			}
			Game.CallDelayedMapScript(SpawnFirebornDeathScriptID, @self, 0);
		}
	}
	//------------------------------------------------------------------------------------------------------------------------
	void OnDamage(kActor @instigator, kDictMem @damageDef, const int damage)
	{
		TurokEnemy::OnDamage(instigator, damageDef, damage);
		bool bValue;
        if (damageDef !is null && damageDef.GetBool("bFreeze", bValue) && bValue == true)
        {
			//take extra damage on fireborn
			if (IsFireborn() || IsFirebornSpawn())
			{
				if (self.Health() > damage)
				{
					int lastHealth = self.Health();
					int extraDamage = Math::Max(Math::Min(damage, (self.Health() - damage) - 1), 0);
					self.Health() -= extraDamage;
				}
			}
			//don't spawn fireborn
			return;
        }
		
		bool hitByPlayer;
		if (damageDef is null)
        {
			//assume knife hit (but it could be enemy no way to know)
			hitByPlayer = true;
        }
		else
		{
			if (damageDef.GetBool("bPlayer", bValue) && bValue == true)
			{
				hitByPlayer = true;
			}
		}
		
		if (IsFireborn())
		{
			//always spawn more creature blood spurts
			Game.SpawnFx("fx/greenblood.kfx", self.Origin(), self.SectorIndex());
			
			if (Game.GetDifficulty() < DIFFICULTY_HARD)
			{
				MaxFirebornSpawnCount = 2;
			}
			else
			{
				MaxFirebornSpawnCount = 5;
			}
			//spawn another fireborn enemy near the hit enemy at a certain radius
			if (self.SpawnParams(6) == 0 && hitByPlayer && firebornSpawnCount < MaxFirebornSpawnCount && self.Health() - damage > 0.0f)
			{
				firebornSpawnCount++;
				float randYaw = Math::RandRange(0.0f, Math::pi);
				float x = self.Origin().x + Math::Sin(randYaw) * 100.0f;
				float y = self.Origin().y + Math::Cos(randYaw) * 100.0f;
				float z = self.FloorHeight();
				int sectorIndex = self.GetSectorIndexAtLocation(kVec3(x, y, z));
				kActor@ actor = ActorFactory.Spawn("Human_Grunt_Soldier_Spawn", x, y, z, Math::RandRange(0.0f, Math::pi), sectorIndex);
				actor.Flags() = AF_SNAPTOFLOOR | AF_CASTSHADOW | AF_AVOIDWATER | AF_FLOATINWATERONDEATH | AF_ALLOWTINYENEMYCHEAT;
				Game.SpawnFx("fx/greenblood.kfx", kVec3(actor.Origin().x, actor.Origin().y, actor.Origin().z), actor.SectorIndex());
				
				TurokGrunt @actorScript = cast<TurokGrunt@>(actor.ScriptObject().obj);
				actorScript.spawnFromFireborn = true;
				actorScript.DoFirebornSpawnStart();
				actorScript.OnBeginLevel();
			}
		}
		else if (IsFirebornSpawn())
		{
			if (!hitByPlayer && Game.GetCurrentMapID() == 112)
			{
				self.Health() += damage;
			}
			Game.SpawnFx("fx/greenblood.kfx", self.Origin(), self.SectorIndex());
		}
	}
	//------------------------------------------------------------------------------------------------------------------------
};
