//
// Copyright(C) 2014-2015 Samuel Villarreal
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//      Enemy/Monster Script Object Classes
//

#include "scripts/common.txt"

//-----------------------------------------------------------------------------
//
// Enemy Base Class
//
//-----------------------------------------------------------------------------
/*
==============================================================
TurokEnemy
==============================================================
*/

class TurokEnemy : ScriptObject
{
    kActor @self;
    bool m_bMortallyWounded;
    bool m_bDroppedItem;
    kAngle m_lookAngle;
    float m_deathFreezeTime;
    
    TurokEnemy(kActor @actor)
    {
        @self = actor;
        m_bMortallyWounded = false;
        m_bDroppedItem = false;
        m_lookAngle = 0;
        m_deathFreezeTime = 0;
    }
    
    ~TurokEnemy()
    {
    }

	void OnDamage(kActor @instigator, kDictMem @damageDef, const int damage)
    {
		bool bVal;

		if( self.Type() == AT_RAPTOR ||   /// if its a raptor do the following....
        self.Type() <= AT_RAPTOR)
        {
			if ( damageDef is null )
			{
			return;
			}

			if ( damageDef.GetBool("bRaptorbit",bVal) && bVal )  /// the following : if its a raptor and if the damage is braptorbits then do the following....
			{
			    kActor @item = ActorFactory.Spawn("Gib_Raptor_Tidbit",   /// spawn those gibs!
                                          self.Origin().x,               /// thexe x y z things are saying throw the gibs like this twords x y and z
                                          self.Origin().y,
                                          self.Origin().z + 32.0f,
                                          0, self.SectorIndex());
                                          
				item.Gravity() = 0.5f;
				item.BounceDamp() = 0.5f;
				item.ClipFlags() = (CF_DROPOFF|CF_CLIPEDGES|CF_NOCLIPACTORS|CF_COLLIDEFLOORS|CF_COLLIDEHEIGHT);
				item.Velocity().x = Math::RandCFloat() * 4.096f;      ///// and this tells the above statement what x y z means to begin with in the first place! :) oh also how fast they are thrown. 
				item.Velocity().y = Math::RandCFloat() * 4.096f;
				item.Velocity().z = 4.096f + (Math::RandFloat() * 4.096f);
        
				item.RunFxEvent("Item_Spawn");
				
				////kRenderModel @model = self.RenderModel();
				
                ///model.SetTexture(1, 4);    ///1 seems to be the part of the raptors body that repeats the most, i would call it the raptors torso. 
				///model.SetTexture(2, 4);   /// 2 is the part of the raptors body that is just below its neck and infront of the torso. 
				///model.SetTexture(3, 4);     ///  3 is the neck itself, but the neck of the raptor is made out of two parts so this is the lower neck, and the neck as a hole starts infront of that 2 part that is as described in the last comment
				///model.SetTexture(4, 4);      ///4 is the top part of the kneck, the neck of the the raptor some would think is actully part of the head, but raptors are not like humens and i would infact call it the top part of the neck that goes into the head.
				///model.SetTexture(5, 4);    /// i would bet that 5 is the head itself.  EDIT: YUP! number 5 repersent the raptors head!
				///model.SetTexture(6, 4);      /// not sure what 6 is, it dose not change anything, but this may be becuse im only useing tex0792_xx.png and the node or group 6 part of the raptor may use a diffrent set of textures. 
				///model.SetTexture(7, 4);     ///  7 is the raptors upper left arm
				///model.SetTexture(8, 4);    /// 8 is lower left arm but NOT upper or the hand; its just the lower left arm.
				///model.SetTexture(9, 4);  ///not sure what 9 is. again it may be becuse all that i am useing at the momment of this was tex0792_xx.png 
				///model.SetTexture(10, 4);  ///10 im not sure may need diffrent texture (texturenumber) used like before
				///model.SetTexture(11, 4);    /// again may need to use a diffrent texture, they keep doing it so ill stop here for now.
				///model.SetTexture(12, 4); 
				///model.SetTexture(13, 4);
                ///model.SetTexture(14, 4); 				
				
				bool bValue = false;
				int health;

				if(self.Health() > 48.5)
				{
				}
				if(self.Health() < 47)
				{
				}
				if(self.Health() < 46.5)
				{
				}
				if(self.Health() < 46)
				{
				}
			    if(self.Health() < 45.5)
				{
				}
				if(self.Health() < 45.0)
				{
				}
				if(self.Health() < 44.5)
				{
				}
			    if(self.Health() < 22.5)
				{
					kRenderModel @model = self.RenderModel();
					model.SetTexture(12, 4); 
					model.SetTexture(13, 4);
					model.SetTexture(14, 4);
					model.SetTexture(1, 4);
					model.SetTexture(2, 4);
					model.SetTexture(3, 4);
					model.SetTexture(4, 4); 
					model.SetTexture(5, 4);
					model.SetTexture(6, 4);
				}
				if(self.Health() < 43.5)
				{
				}
				///if(self.Health() < 2.0)
				///{					
				///	if( self.Type() == AT_RAPTOR ||   /// if its a raptor do the following....
				///	self.Type() <= AT_RAPTOR)
				///	{
				///
				///		if ( damageDef.GetBool("bRaptorbit",bVal) && bVal )
				///		{
				///			kRenderModel @model = self.RenderModel();
				///			model.SetTexture(12, 4); 
				///			model.SetTexture(13, 4);
				///			model.SetTexture(14, 4);
				///			model.SetTexture(1, 4);
				///			model.SetTexture(2, 4);
				///			model.SetTexture(3, 4);
				///			model.SetTexture(4, 4); 
				///			model.SetTexture(5, 4);
				///			model.SetTexture(6, 4);
				///		}
                ///  }						
				///}
				
				///if( self.Type() == AT_RAPTOR ||   /// if its a raptor do the following....
				///self.Type() <= AT_RAPTOR)
				///{
				///}
				
			}
		}
    }

	
    /*
    ==============================================================
    OnTick
    ==============================================================
    */
    
    void OnTick(void)
    {
        if((self.Flags() & AF_DEAD) != 0)
        {
            if(m_deathFreezeTime > 0)
            {
                m_deathFreezeTime -= GAME_DELTA_TIME;
                
                if(m_deathFreezeTime <= 0)
                {
                    Game.SpawnFx("fx/freeze_explosion.kfx", self.Origin(), self.SectorIndex());
                    self.Remove();
                }
            }
        }
    }
    
    /*
    ==============================================================
    OnSpawn
    ==============================================================
    */
    
    void OnSpawn(void)
    {
    }
    
    /*
    ==============================================================
    TurnAngles
    ==============================================================
    */
    
    void TurnAngles(const float turnSpeed, const float angles)
    {
        float temp = angles;
        float ang = angles;
        
        if(ang > turnSpeed)
        {
            ang = turnSpeed;
        }
        else if(ang < -turnSpeed)
        {
            ang = -turnSpeed;
        }
        
        ang *= GAME_FRAME_TIME;
        if(Math::Fabs(ang) > Math::Fabs(temp))
        {
            ang = temp;
        }
        
        self.Yaw() += ang;
    }
    
    /*
    ==============================================================
    InPlayerProjectilePath
    ==============================================================
    */
    
    bool InPlayerProjectilePath(const float angleRange)
    {
        if((Player.Actor().PlayerFlags() & PF_FIREDPROJECTILE) != 0)
        {
            if(Math::Fabs(self.GetTurnYaw(Player.Actor().Origin())) < angleRange)
            {
                return true;
            }
        }
        
        return false;
    }

    /*
    ==============================================================
    TossActor
    ==============================================================
    */
	
    kActor @TossActor(const kStr &in itemName, const float x, const float y, const float z, const kVec3 &in velocity)
   {

        kActor @actor = ActorFactory.Spawn(itemName, x, y, z, 0, self.SectorIndex());
        kRenderModel @model = self.RenderModel();
	    kVec3 org = self.GetTransformedVector(kVec3(x, y, z));
		m_bDroppedItem = true;
		org.x = self.Origin().x;
        org.y = self.Origin().y;
        org.z = self.Origin().z + self.Height() * 0.5f;
		
        actor.Scale().Set(0.35f, 0.35f, 0.35f);
        actor.ClipFlags() = (CF_DROPOFF|CF_CLIPEDGES|CF_NOCLIPACTORS|CF_COLLIDEFLOORS|CF_COLLIDEHEIGHT);
        actor.Velocity() = velocity;
        
        actor.Velocity() *= (1.0f / 60.0f);
        if(actor is null)
        {
            return null;
        }
		else
		switch(self.ModelVariation())
        {
        case GV_GENERIC:
            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);
            break;
			
        case GV_GENERIC_AXE:
            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);
            break;
			
        case GV_GENERIC_CLUB:
            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);
			break;
			
        }
		
        if(actor is null)
        {
            return null;
        }
		
        actor.Scale().Set(0.35f, 0.35f, 0.35f);
        actor.ClipFlags() = (CF_DROPOFF|CF_CLIPEDGES|CF_NOCLIPACTORS|CF_COLLIDEFLOORS|CF_COLLIDEHEIGHT);
        actor.Velocity() = velocity;
        
        actor.Velocity() *= (1.0f / 60.0f);
		
        kVec3 vel   = self.Velocity();
        kVec3 pos   = self.GetTransformedVector(kVec3(x, y, z));
        kVec3 pos_v = pos - vel;
        kQuat rot   = vel.ToQuat();

        float f = 0.0f;
        kVec3 newPos;
		
        
		switch(actor.Type())
        {
        case AT_GIB_ALIEN3:
            Game.SpawnFx("fx/Snakesgreenbloodstick.kfx", self, vel, newPos, rot);
            f += 2.0f;
            Game.SpawnFx("fx/Snakesspurt_greenblood.kfx", self, vel, newPos, rot);
            f += 2.0f;
            actor.BounceDamp() = 0.6f;
            actor.Gravity() = 0.5f;
            break;
        
        case AT_GIB_STALKER3:
            Game.SpawnFx("fx/Snakesgreenbloodstick.kfx", self, vel, newPos, rot);
            f += 2.0f;
            Game.SpawnFx("fx/Snakesspurt_greenblood.kfx", self, vel, newPos, rot);
            f += 2.0f;
            actor.BounceDamp() = 0.4f;
            actor.Gravity() = 0.6f;
            break;
            
        case AT_GIB_STALKER5:
        case AT_GIB_STALKER2:
        case AT_GIB_STALKER1:
            Game.SpawnFx("fx/Snakesgreenbloodstick.kfx", self, vel, newPos, rot);
            f += 2.0f;
            Game.SpawnFx("fx/Snakesspurt_greenblood.kfx", self, vel, newPos, rot);
            f += 2.0f;
            actor.BounceDamp() = 0.3f;
            actor.Gravity() = 0.6f;
            break;
		
		case AT_GIB_GRUNT:
            actor.BounceDamp() = 0.3f;
            actor.Gravity() = 0.6f;
            break;
            
        default:
            actor.BounceDamp() = 0.5f;
            actor.Gravity() = 0.5f;
            break;
        }
        
        return actor;
    }
    
    /*
    ==============================================================
    TossItem
    ==============================================================
    */
    
    void TossItem(const kStr &in itemName, const float x, const float y, const float z)
    {
        kActor @item = TossActor(itemName, x, y, z, kVec3(0, 0, 1).Randomize(0.25f).Normalize() * 409.6f);
        item.RunFxEvent("Item_Spawn");
    }
    
    /*
    ==============================================================
    TossGib
    ==============================================================
    */
    
    void TossGib(const kStr &in gibActor, const float x, const float y, const float z)
    {
        kVec3 dir = (kVec3(0, 0, 1).Randomize(0.3f).Normalize() * ((Math::Rand() % -2 + 6) * 10.24f)) * 15.0f;
        kVec3 normal, cp;
        kActor @gib;
        TurokGiblet @gibObj;
        
        self.CheckPosition(x, y);
        normal = CModel.ContactNormal();
        
        @gib = TossActor(gibActor, x, y, z, dir);
        
        dir.Normalize();
        
        cp = normal.Cross(dir);
        cp.Normalize();
        
        gib.Yaw() = cp.ToYaw();
        gib.Pitch() = -cp.ToPitch();
        
        @gibObj = cast<TurokGiblet@>(gib.ScriptObject().obj);

		if(!(gibObj is null))
        {
            gibObj.Spin();
        }
    }
    
    /*
    ==============================================================
    Knockback
    ==============================================================
    */
    
    void Knockback(kActor @instigator, const float r, const float x, const float y, const float z)
    {
        kVec3 org;
        kVec3 pos;
        kActor @targ;
        
        if(self.GetTarget() is null)
        {
            return;
        }
        
        @targ = self.GetTarget();
        
        org.x = self.Origin().x;
        org.y = self.Origin().y;
        org.z = self.Origin().z + self.Height() * 0.5f;
        
        pos = targ.GetTransformedVector(kVec3(x, y, z));
        
        kVec3 dir = pos - org;
        float dist = dir.Unit();
        
        if(dist > (r * GAME_SCALE + self.Radius()))
        {
            return;
        }
        
        dir.Normalize();
        dir *= (1.75f*GAME_SCALE);
        dir.z = (0.875f*GAME_SCALE);
        
        targ.Velocity() += dir;
        
        if(targ is Player.Actor().CastToActor())
        {
            Player.Actor().Origin().z += GAME_SCALE;
            Player.Actor().PlayerFlags() |= PF_NOAIRFRICTION;
            
            if(!(Player.Actor().ScriptObject() is null))
            {
                // WARNING: assumes script object is TurokPlayer
                TurokPlayer @p = cast<TurokPlayer@>(Player.Actor().ScriptObject().obj);
                
                if(p is null)
                {
                    return;
                }
                
                p.m_shoveTime = 0.5f;
                p.m_vShoveVector = org;
            }
        }
    }
    
    /*
    ==============================================================
    SightSound
    ==============================================================
    */
    
    void SightSound(kActor @instigator, const float w, const float x, const float y, const float z)
    {
    }
    
    /*
    ==============================================================
    DeathSound
    ==============================================================
    */
    
    void DeathSound(kActor @instigator, const float w, const float x, const float y, const float z)
    {
    }
    
    /*
    ==============================================================
    InjurySound
    ==============================================================
    */
    
    void InjurySound(kActor @instigator, const float w, const float x, const float y, const float z)
    {
    }
    
    /*
    ==============================================================
    ViolentSound
    ==============================================================
    */
    
    void ViolentSound(kActor @instigator, const float w, const float x, const float y, const float z)
    {
    }
    
    /*
    ==============================================================
    Unused23
    ==============================================================
    */
    
    void Unused23(kActor @instigator, const float w, const float x, const float y, const float z)
    {
    }
    
    /*
    ==============================================================
    Unused24
    ==============================================================
    */
    
    void Unused24(kActor @instigator, const float w, const float x, const float y, const float z)
    {
    }
    
    /*
    ==============================================================
    Unused60
    ==============================================================
    */
    
    void Unused60(kActor @instigator, const float w, const float x, const float y, const float z)
    {
    }
    
    /*
    ==============================================================
    Unused94
    ==============================================================
    */
    
    void Unused94(kActor @instigator, const float w, const float x, const float y, const float z)
    {
    }
    
    /*
    ==============================================================
    Unused398
    ==============================================================
    */
    
    void Unused398(kActor @instigator, const float w, const float x, const float y, const float z)
    {
    }
    
    /*
    ==============================================================
    CauseThump
    ==============================================================
    */
    
    void CauseThump(kActor @instigator, const float r, const float x, const float y, const float z)
    {
        kActor @actor = ActorFactory.Spawn("QuakeSource", 0, 0, 0, 0, self.SectorIndex());
        TurokQuakeSource @quake;

        if(actor is null)
        {
            return;
        }

        @quake = cast<TurokQuakeSource@>(actor.ScriptObject().obj);
        
        if(quake is null)
        {
            return;
        }

        quake.SetupThump(self.GetTransformedVector(kVec3(x, y, z)), r);
    }
    
    /*
    ==============================================================
    TriggerEvent
    ==============================================================
    */
    
    void TriggerEvent(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        World.TriggerActorsByTID(instigator, int(w));
    }
    
    /*
    ==============================================================
    FootStepPuff
    ==============================================================
    */
    
    void FootStepPuff(kActor @instigator, const float a, const float x, const float y, const float z)
    {
        if(a == 0.0f)
        {
            self.SpawnFx("fx/dustcloud_footfall.kfx", kVec3(x, y, z+4.096f));
        }
        else
        {
            self.SpawnFx("fx/dustcloud_bodyfall.kfx", kVec3(x, y, z+4.096f));
        }
    }
    
    /*
    ==============================================================
    SwishSound
    ==============================================================
    */
    
    void SwishSound(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.PlaySound("sounds/shaders/knife_swish_1.ksnd");
    }
    
    /*
    ==============================================================
    GunFire
    ==============================================================
    */
    
    void GunFire(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        Sys.Warning("GunFire is being called from base class object!");
    }
    
    /*
    ==============================================================
    MeleeVeryWimpy
    ==============================================================
    */
    
    void MeleeVeryWimpy(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.MeleeObject("Damage_Generic_1", kVec3(x, y, z), w);
    }
    
    /*
    ==============================================================
    MeleeBluntWeak
    ==============================================================
    */
    
    void MeleeBluntWeak(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.MeleeObject("Damage_Blunt_3", kVec3(x, y, z), w);
    }
    
    /*
    ==============================================================
    MeleeBluntMedium
    ==============================================================
    */
    
    void MeleeBluntMedium(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.MeleeObject("Damage_Blunt_5", kVec3(x, y, z), w);
    }
    
    /*
    ==============================================================
    MeleeAttack1
    ==============================================================
    */
    
    void MeleeAttack1(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.MeleeObject("Damage_Flesh_3", kVec3(x, y, z), w);
    }
    
    /*
    ==============================================================
    MeleeAttack2
    ==============================================================
    */
    
    void MeleeAttack2(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.MeleeObject("Damage_Flesh_5", kVec3(x, y, z), w);
    }
    
    /*
    ==============================================================
    MeleeAttack3
    ==============================================================
    */
    
    void MeleeAttack3(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.MeleeObject("Damage_Blunt_5", kVec3(x, y, z), w);
    }
    
    /*
    ==============================================================
    MeleeBluntWimpy
    ==============================================================
    */
    
    void MeleeBluntWimpy(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.MeleeObject("Damage_Blunt_2", kVec3(x, y, z), w);
    }
    
    /*
    ==============================================================
    MeleeBluntStrong
    ==============================================================
    */
    
    void MeleeBluntStrong(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.MeleeObject("Damage_Blunt_10", kVec3(x, y, z), w);
    }
    
    /*
    ==============================================================
    MeleeBluntVeryStrong
    ==============================================================
    */
    
    void MeleeBluntVeryStrong(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.MeleeObject("Damage_Blunt_15", kVec3(x, y, z), w);
    }
    
    /*
    ==============================================================
    MeleeBluntHeavy
    ==============================================================
    */
    
    void MeleeBluntHeavy(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.MeleeObject("Damage_Blunt_20", kVec3(x, y, z), w);
    }
    
    /*
    ==============================================================
    MeleeBluntVeryHeavy
    ==============================================================
    */
    
    void MeleeBluntVeryHeavy(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.MeleeObject("Damage_Blunt_30", kVec3(x, y, z), w);
    }
    
    /*
    ==============================================================
    BloodGush
    ==============================================================
    */
    
    void BloodGush(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        const int count = 3;
        
        if((self.Flags() & AF_NOBLOOD) != 0)
        {
            return;
        }
        
        kVec3 vel   = self.Velocity();
        kVec3 pos   = self.GetTransformedVector(kVec3(x, y, z));
        kVec3 pos_v = pos - vel;
        kQuat rot   = vel.ToQuat();
        
        float f = 0.0f;
        kVec3 newPos;
        
        if(self.Type() == AT_STALKER)
		{
            Game.SpawnFx("fx/Snakesgreenbloodstick.kfx", self, vel, newPos, rot);
            f += 2.0f;
            Game.SpawnFx("fx/Snakesspurt_greenblood.kfx", self, vel, newPos, rot);
            f += 2.0f;
        }
		else
		for(int i = 0; i < count; ++i)
        {
            newPos = pos;
            newPos.Lerp(pos_v, f / 3.0f);
            
            Game.SpawnFx("fx/spurt_blood.kfx", self, vel, newPos, rot);
            f += 1.0f;
            Game.SpawnFx("fx/snakesbloodrain.kfx", self, vel, newPos, rot);
            f += 2.0f;
            Game.SpawnFx("fx/Snakesbloodstick.kfx", self, vel, newPos, rot);
            f += 2.0f;
            Game.SpawnFx("fx/blood_gush.kfx", self, vel, newPos, rot);
            f += 2.0f;
        }
		
        if(self.Type() == AT_STALKER)
		{
            newPos = pos;
            newPos.Lerp(pos_v, f / 3.0f);
			
            Game.SpawnFx("fx/Snakesgreenbloodstick.kfx", self, vel, newPos, rot);
            f += 2.0f;
            Game.SpawnFx("fx/Snakesspurt_greenblood.kfx", self, vel, newPos, rot);
            f += 2.0f;		
        }
		else
        {
            return;
        }
    }
    
    /*
    ==============================================================
    TossGib1
    ==============================================================
    */

    void TossGib1(kActor @instigator, const float w, const float x, const float y, const float z)
    {
		kVec3 org = self.GetTransformedVector(kVec3(x, y, z));
		TossGib("Gib_Alien_head", org.x, org.y, org.z);
		Game.SpawnFx("fx/generic_108.kfx", org, self.SectorIndex());
	}
	
    /*
    ==============================================================
    TossGib2
    ==============================================================
    */
    
    void TossGib2(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        kVec3 org = self.GetTransformedVector(kVec3(x, y, z));
        TossGib("Gib_Alien_Torso", org.x, org.y, org.z);
        Game.SpawnFx("fx/generic_108.kfx", org, self.SectorIndex());
    }
    
    /*
    ==============================================================
    TossGib3
    ==============================================================
    */
    
    void TossGib3(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        kVec3 org = self.GetTransformedVector(kVec3(x, y, z));
        TossGib("Gib_Alien_Feet", org.x, org.y, org.z);
        Game.SpawnFx("fx/generic_108.kfx", org, self.SectorIndex());
    }
    
    /*
    ==============================================================
    TossGib4
    ==============================================================
    */
    
    void TossGib4(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        kVec3 vel   = self.Velocity();
        kVec3 pos   = self.GetTransformedVector(kVec3(x, y, z));
        kVec3 pos_v = pos - vel;
        kQuat rot   = vel.ToQuat();
        
        float f = 0.0f;
        kVec3 newPos;
		
        newPos = pos;
        newPos.Lerp(pos_v, f / 3.0f);
		
		if(self.Type() == AT_STALKER)
		{		
		kVec3 org = self.GetTransformedVector(kVec3(x, y, z));
        TossGib("Gib_Alien_Body", org.x, org.y, org.z);
        Game.SpawnFx("fx/Snakesgreenbloodstick.kfx", self, vel, newPos, rot);
        f += 2.0f;
        Game.SpawnFx("fx/Snakesspurt_greenblood.kfx", self, vel, newPos, rot);
        f += 2.0f;
        }
		else
		{
		kVec3 org = self.GetTransformedVector(kVec3(x, y, z));
        TossGib("Gib_Alien_Body", org.x, org.y, org.z);
        Game.SpawnFx("fx/generic_108.kfx", org, self.SectorIndex());
		}
    }
    
    /*
    ==============================================================
    TossGib5
    ==============================================================
    */
    
    void TossGib5(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        kVec3 vel   = self.Velocity();
        kVec3 pos   = self.GetTransformedVector(kVec3(x, y, z));
        kVec3 pos_v = pos - vel;
        kQuat rot   = vel.ToQuat();
        
        float f = 0.0f;
        kVec3 newPos;
		
        newPos = pos;
        newPos.Lerp(pos_v, f / 3.0f);
		
        kVec3 org = self.GetTransformedVector(kVec3(x, y, z));
        TossGib("Gib_Stalker_Head", org.x, org.y, org.z);
        Game.SpawnFx("fx/Snakesgreenbloodstick.kfx", self, vel, newPos, rot);
        f += 2.0f;
        Game.SpawnFx("fx/Snakesspurt_greenblood.kfx", self, vel, newPos, rot);
        f += 2.0f;
    }
    
    /*
    ==============================================================
    TossGib6
    ==============================================================
    */
    
    void TossGib6(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        kVec3 vel   = self.Velocity();
        kVec3 pos   = self.GetTransformedVector(kVec3(x, y, z));
        kVec3 pos_v = pos - vel;
        kQuat rot   = vel.ToQuat();
        
        float f = 0.0f;
        kVec3 newPos;
		
        newPos = pos;
        newPos.Lerp(pos_v, f / 3.0f);
		
        kVec3 org = self.GetTransformedVector(kVec3(x, y, z));
        TossGib("Gib_Stalker_Torso", org.x, org.y, org.z);
        Game.SpawnFx("fx/Snakesgreenbloodstick.kfx", self, vel, newPos, rot);
        f += 2.0f;
        Game.SpawnFx("fx/Snakesspurt_greenblood.kfx", self, vel, newPos, rot);
        f += 2.0f;
    }
    
    /*
    ==============================================================
    TossGib7
    ==============================================================
    */
    
    void TossGib7(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        kVec3 vel   = self.Velocity();
        kVec3 pos   = self.GetTransformedVector(kVec3(x, y, z));
        kVec3 pos_v = pos - vel;
        kQuat rot   = vel.ToQuat();
        
        float f = 0.0f;
        kVec3 newPos;
		
        newPos = pos;
        newPos.Lerp(pos_v, f / 3.0f);
		
        kVec3 org = self.GetTransformedVector(kVec3(x, y, z));
        TossGib("Gib_Stalker_Feet", org.x, org.y, org.z);
        Game.SpawnFx("fx/Snakesgreenbloodstick.kfx", self, vel, newPos, rot);
        f += 2.0f;
        Game.SpawnFx("fx/Snakesspurt_greenblood.kfx", self, vel, newPos, rot);
        f += 2.0f;
    }
    
    /*
    ==============================================================
    TossGib8
    ==============================================================
    */
    
    void TossGib8(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        kVec3 vel   = self.Velocity();
        kVec3 pos   = self.GetTransformedVector(kVec3(x, y, z));
        kVec3 pos_v = pos - vel;
        kQuat rot   = vel.ToQuat();
        
        float f = 0.0f;
        kVec3 newPos;
		
        newPos = pos;
        newPos.Lerp(pos_v, f / 3.0f);
		
        kVec3 org = self.GetTransformedVector(kVec3(x, y, z));
        TossGib("Gib_Stalker_Body", org.x, org.y, org.z);
        Game.SpawnFx("fx/Snakesgreenbloodstick.kfx", self, vel, newPos, rot);
        f += 2.0f;
        Game.SpawnFx("fx/Snakesspurt_greenblood.kfx", self, vel, newPos, rot);
        f += 2.0f;
    }
    
    /*
    ==============================================================
    TossGib9
    ==============================================================
    */
   
    void TossGib9(kActor @instigator, const float w, const float x, const float y, const float z)
    {
		
		if(self.Type() == AT_STALKER)
		{
			return;
        }
		else
		{
			kVec3 org = self.GetTransformedVector(kVec3(x, y, z));
			TossGib("Gib_Grunt_Arm", org.x, org.y, org.z);
		}
    }
    
    /*
    ==============================================================
    ExplosionSfx2
    ==============================================================
    */
    
    void ExplosionSfx2(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        self.PlaySound("sounds/shaders/explosion_2.ksnd");
    }
    
    /*
    ==============================================================
    DropItem
    ==============================================================
    */
    
    void DropItem(kActor @instigator, const float w, const float x, const float y, const float z)
    {
        if(Game.GetDifficulty() >= DIFFICULTY_HARD)
        {
            // no pickups on hard skills
            return;
        }
        
        kVec3 org;
        const uint spawnFlags = self.SpawnFlags2();
        
        if(m_bDroppedItem)
        {
            return;
        }
        
        org = self.GetTransformedVector(kVec3(x, y, z));
        m_bDroppedItem = true;
		
        if( self.ModelVariation() == AT_RAPTOR ||
            self.ModelVariation() <= AT_RAPTOR)
		{
			return;
		}
        else if( self.ModelVariation() == GV_GENERIC_CLUB ||
            self.ModelVariation() <= GV_GENERIC_AXE)
        {
			TossGib("Gib_Grunt_Arm", org.x, org.y, org.z);
		}
		else if(self.Type() != AT_RAPTOR)
		{
			return;
		}
		else
		{
			return;
		}
		
		if(self.Type() != AT_RAPTOR)
		{
			TossGib("Gib_Raptor_Tidbit", org.x, org.y, org.z);
		}
        if((spawnFlags & 0x1) != 0)
        {
            TossItem("Ammo_ExpShells_Pickup", org.x, org.y, org.z);
        }
        if((spawnFlags & 0x2) != 0)
        {
            TossItem("Ammo_Grenade_Pickup", org.x, org.y, org.z);
        }
        if((spawnFlags & 0x4) != 0)
        {
            TossItem("Health_Medium", org.x, org.y, org.z);
        }
        if((spawnFlags & 0x8) != 0)
        {
            TossItem("Health_Full", org.x, org.y, org.z);
        }
        if((spawnFlags & 0x10) != 0)
        {
            TossItem("Health_Ultra", org.x, org.y, org.z);
        }
        if((spawnFlags & 0x20) != 0)
        {
            TossItem("Health_Small", org.x, org.y, org.z);
        }
        if((spawnFlags & 0x40) != 0)
        {
            TossItem("Health_Large", org.x, org.y, org.z);
        }
        if((spawnFlags & 0x80) != 0)
        {
            TossItem("Ammo_MiniGunAmmo_Pickup", org.x, org.y, org.z);
        }
        if((spawnFlags & 0x100) != 0)
        {
            if(m_bMortallyWounded && Math::RandMax(100) > 85)
            {
                TossItem("Health_MortalWound", org.x, org.y, org.z);
            }
        }
        if((spawnFlags & 0x200) != 0)
        {
            TossItem("Ammo_Rockets_Pickup", org.x, org.y, org.z);
        }
        if((spawnFlags & 0x400) != 0)
        {
            TossItem("Ammo_Shells_Pickup", org.x, org.y, org.z);
        }
        if((spawnFlags & 0x800) != 0)
        {
            TossItem("Ammo_Cell_Pickup", org.x, org.y, org.z);
        }
        if((spawnFlags & 0x1000) != 0)
        {
            TossItem("Ammo_LargeCell_Pickup", org.x, org.y, org.z);
        }
        if((spawnFlags & 0x2000) != 0)
        {
            TossItem("Ammo_Clip_Pickup", org.x, org.y, org.z);
        }
    }
    
    /*
    ==============================================================
    OnDeath
    ==============================================================
    */
    
    void OnDeath(kActor @killer, kDictMem @damageDef)
    {
        bool bValue;
        
        if(damageDef is null)
        {
            return;
        }
        
        m_bDroppedItem = false;
        
        if(damageDef.GetBool("bAccelerator", bValue) && bValue == true)
        {
            self.AnimState().flags |= (ANF_PAUSED|ANF_NOINTERRUPT);
            m_deathFreezeTime = 3.0f;
            self.RunFxEvent("Enemy_Freeze");
            Game.SpawnFx("fx/freeze_start.kfx", self.Origin(), self.SectorIndex());
        }
    }
};
