
class Q2Giblet : TurokGiblet
{
	Q2Giblet( kActor@a )
	{
		super( a );
		self.Flags() |= AF_ALWAYSACTIVE;
	}

	void OnTick()
	{
		if ( timer >= 50 && (Player.Actor().Origin() - self.Origin()).UnitSq() > 4096*4096 )
		{
			self.Remove();
			return;
		}

		self.Yaw() += spinSpeed * 0.125f;
		self.Pitch() += spinSpeed * 0.125f;

		if ( self.OnGround() )
		{
			float vz = Math::Fabs( self.Velocity().z );
			if ( vz > 1.0f )
			{
//				if      ( vz > 3.413f ) self.PlaySound( "sounds/shaders/generic_238.ksnd" );
//				else if ( vz > 1.7f   ) self.PlaySound( "sounds/shaders/generic_239.ksnd" );
//				else                    self.PlaySound( "sounds/shaders/generic_240.ksnd" );
				Spin();
			}
			else
				spinSpeed = 0;
		}

		if ( (PlayLoop.Ticks() & 7) == 0 )
		{
			if ( self.Velocity().UnitSq() > 0.5f )
				self.SpawnFx( "fx/spurt_blood.kfx", Math::vecZero );
			timer++;
		}
	}
}

void GibActor( kActor@ a, const kVec3&in dir )
{
	if ( !Gibbed(a) ) return;
	if ( a.ScriptObject() is null ) return;
	TurokEnemy@ E = cast<TurokEnemy@>( a.ScriptObject().obj );
	if ( E is null ) return;
	switch ( a.Type() )
	{
		case AT_GRUNT:     SpawnGibs_Human(      E, dir ); break;
		case AT_RAPTOR:    SpawnGibs_Raptor(     E, dir ); break;
		case AT_DINOSAUR1: SpawnGibs_Dimetrodon( E, dir ); break;
		case AT_PURLIN:    SpawnGibs_PurLin(     E, dir ); break;
		case AT_INSECT:    SpawnGibs_Beetle(     E, dir ); break;
		case AT_DRAGONFLY: SpawnGibs_Dragonfly(  E, dir ); break;
		case AT_STALKER:   SpawnGibs_Leaper(     E, dir ); break;
		case AT_ALIEN:     SpawnGibs_Alien(      E, dir ); break;
		default: return;
	}
	Gib( a );
}

bool Gibbed( kActor@ a )
{
	// make sure red blood is on
	kStr val;
	if ( Sys.GetCvarValue( "g_violencelevel", val ) && val.Atoi() != 0 )
		return false;
	// check max health and "no blood" flag
	return a.Health() <= -a.SpawnParams(2) && a.Flags() & AF_NOBLOOD == 0;
}

void Gib( kActor@ a )
{
	// trigger actors, since apparently we're removing the actor before it normally would
	World.TriggerActorsByTID( a, a.SpawnParams(0) );
	// prevent death anim keyframe actions (bleeding, landing sounds, etc.)
	a.AnimState().flags |= ANF_PAUSED | ANF_NOINTERRUPT;
	// prevent being shot until the actor is fully purged
	// (AF_NONSHOOTABLE, AF_DISABLED, CF_NOCOLLIDEFUNC didn't work)
	a.Radius() = a.Height() = 0;
	a.PlaySound( "sounds/Gib.ksnd" );
	a.Remove();
}

kActor@ TossActor( kActor@ A, const kStr&in itemName, float x, float y, float z, kVec3&in velocity )
{
	kActor@ actor = ActorFactory.Spawn( itemName, x, y, z, 0, A.SectorIndex() );
	if ( actor is null )
		return null;

	actor.ClipFlags() = CF_DROPOFF | CF_CLIPEDGES | CF_NOCLIPACTORS | CF_COLLIDEFLOORS | CF_COLLIDEHEIGHT;
	actor.Velocity() = velocity / 60;

	switch ( actor.Type() )
	{
		case AT_GIB_ALIEN3: // alien head
			actor.BounceDamp() = 0.6f;
			actor.Gravity() = 0.5f;
			break;
		case AT_GIB_STALKER3: // leaper head
			actor.BounceDamp() = 0.4f;
			actor.Gravity() = 0.6f;
			break;
		case AT_GIB_STALKER5: // leaper torso
		case AT_GIB_STALKER2: // leaper foot
		case AT_GIB_STALKER1: // leaper body
			actor.BounceDamp() = 0.3f;
			actor.Gravity() = 0.6f;
			break;
		case AT_GIB_STALKER4: // alien torso
		case AT_GIB_ALIEN2:   // alien foot
		case AT_GIB_ALIEN1:   // alien body
			actor.BounceDamp() = 0.5f;
			actor.Gravity() = 0.5f;
			break;
		default: // Q2 gibs
			actor.Velocity() *= 0.65f;
			actor.BounceDamp() = Math::RandRange( 0.3f, 0.6f );
			actor.Gravity() = Math::RandRange( 0.5f, 0.6f ) * 0.4f;
	}

	return actor;
}

void TossGib( TurokEnemy@ E, const kStr&in type, float x, float y, float z, kVec3&in dir,
	const kVec3&in scale=Math::vecZero )
{
	kVec3 v = E.self.GetTransformedVector( kVec3(x,y,z) );
//	E.TossGib( type, v.x, v.y, v.z );

	kVec3 vel = kVec3(0,0,1).Randomize(0.3f).Normalize() * (Math::Rand() % -2 + 3) * 10.24f * 15.0f;
	vel += dir * 300;

	E.self.CheckPosition( v.x, v.y );
	kVec3 normal = CModel.ContactNormal();

//	kActor@ gib = E.TossActor( type, v.x, v.y, v.z, vel );
	kActor@ gib = TossActor( E.self, type, v.x, v.y, v.z, vel );
	gib.Scale() = scale.x == 0 ? E.self.Scale() : scale;

	vel.Normalize();
	kVec3 cp = normal.Cross( vel );
	cp.Normalize();

	gib.Yaw() = cp.ToYaw();
	gib.Pitch() = -cp.ToPitch();

	TurokGiblet@ gibObj = cast<TurokGiblet@>( gib.ScriptObject().obj );
	if ( gibObj !is null )
		gibObj.Spin();
}

// head  - x: width, y: depth, z: height
// bone  - y: length
// bone2 - x: flat width, y: length, z: thickness

void SpawnGibs_Human( TurokEnemy@ E, const kVec3&in dir )
{
	// noggin
	TossGib( E, "Gib_Head", 0, 0, 112.5f, dir, kVec3( E.self.Scale().y*0.8f, E.self.Scale().x*0.8f, E.self.Scale().z ) );
	// thorax
	TossGib( E, "Gib_Chunk", 0, 0, 100, dir );
	// arms
	kVec3 scale( E.self.Scale().y, E.self.Scale().z, E.self.Scale().x );
	TossGib( E, "Gib_Bone2", -10, 0, 87.5f, dir, scale );
	TossGib( E, "Gib_Bone2",  10, 0, 87.5f, dir, scale );
	// thighs
	scale.Set( E.self.Scale().x, E.self.Scale().z, E.self.Scale().y );
	TossGib( E, "Gib_Bone",  10, 0, 50, dir, scale );
	TossGib( E, "Gib_Bone", -10, 0, 50, dir, scale );
	// shins
	TossGib( E, "Gib_Chunk",  10, 0, 25, dir );
	TossGib( E, "Gib_Chunk", -10, 0, 25, dir );
}

void SpawnGibs_Raptor( TurokEnemy@ E, const kVec3&in dir )
{
	kVec3 scale( E.self.Scale().z, E.self.Scale().y, E.self.Scale().x );
	// head
	TossGib( E, "Gib_Bone2", 0, -115, 105, dir, scale*1.25f );
	// neck
	TossGib( E, "Gib_Chunk", 0, -75, 95, dir );
	// arms
	TossGib( E, "Gib_Chunk",  10, -60, 65, dir );
	TossGib( E, "Gib_Chunk", -10, -60, 65, dir );
	// torso
//	TossGib( E, "Gib_Bone2", 0, -30, 95, dir, scale*1.5f );
//	TossGib( E, "Gib_Bone2", 0,  20, 95, dir, scale*1.5f );
	scale.y *= 0.75f;
	TossGib( E, "Gib_Bone2", 0, -35, 90, dir, scale*1.75f );
	TossGib( E, "Gib_Bone2", 0,  15, 90, dir, scale*1.75f );
	// tail
	TossGib( E, "Gib_Chunk", 0, 60, 90, dir );
	TossGib( E, "Gib_Chunk", 0, 90, 90, dir );
	// thighs
//	scale = kVec3( E.self.Scale().x, E.self.Scale().z, E.self.Scale().y ) * 1.4f;
	scale = kVec3( E.self.Scale().x * 1.2f, E.self.Scale().z * 1.3f, E.self.Scale().y * 1.45f );
	TossGib( E, "Gib_Bone",  10, 0, 65, dir, scale );
	TossGib( E, "Gib_Bone", -10, 0, 65, dir, scale );
	// shins
	scale.Set( E.self.Scale().y, E.self.Scale().z*1.25f, E.self.Scale().x );
	TossGib( E, "Gib_Bone2",  10, 0, 25, dir, scale );
	TossGib( E, "Gib_Bone2", -10, 0, 25, dir, scale );
}

void SpawnGibs_Dimetrodon( TurokEnemy@ E, const kVec3&in dir )
{
	kVec3 scale = kVec3( E.self.Scale().z*2, E.self.Scale().y, E.self.Scale().x*2 ) * 1.5f;
	// head
	TossGib( E, "Gib_Bone2", 0, -145, 55, dir, scale * 0.875f );
	// torso
	scale.y *= 1.25f;
	TossGib( E, "Gib_Bone2", 0, -70, 55, dir, scale );
	TossGib( E, "Gib_Bone2", 0,  15, 50, dir, scale * 0.875f );
	// tail
	TossGib( E, "Gib_Chunk", 0,  70, 35, dir, E.self.Scale() * 1.35f );
	TossGib( E, "Gib_Chunk", 0, 100, 30, dir, E.self.Scale() * 1.1f );
	// legs
	scale = kVec3( E.self.Scale().y, E.self.Scale().x, E.self.Scale().z ) * 1.3f;
	scale.y = scale.z = (scale.y + scale.z) / 2;
		// front
	TossGib( E, "Gib_Bone", -40, -70, 40, dir, scale );
	TossGib( E, "Gib_Bone",  40, -70, 40, dir, scale );
		// hind
	TossGib( E, "Gib_Bone", -30, 15, 40, dir, scale );
	TossGib( E, "Gib_Bone",  30, 15, 40, dir, scale );
	// feet
	scale = E.self.Scale() * 1.3f;
		// front
	TossGib( E, "Gib_Chunk", -60, -70, 20, dir, scale );
	TossGib( E, "Gib_Chunk",  60, -70, 20, dir, scale );
		// hind
	TossGib( E, "Gib_Chunk", -50, 15, 20, dir, scale );
	TossGib( E, "Gib_Chunk",  50, 15, 20, dir, scale );
}

void SpawnGibs_PurLin( TurokEnemy@ E, const kVec3&in dir )
{
	// head
	TossGib( E, "Gib_Head", 0, -20, 115, dir, kVec3( E.self.Scale().y, E.self.Scale().x*2, E.self.Scale().z*1.5f ) );
	// chest
	TossGib( E, "Gib_Chunk",  15, 0, 115, dir, E.self.Scale()*2 );
	TossGib( E, "Gib_Chunk", -15, 0, 115, dir, E.self.Scale()*2 );
	// abdomen
	TossGib( E, "Gib_Chunk",  15, 0, 75, dir, E.self.Scale()*1.5f );
	TossGib( E, "Gib_Chunk", -15, 0, 75, dir, E.self.Scale()*1.5f );
	// arms
	kVec3 scale = kVec3( E.self.Scale().x, E.self.Scale().z, E.self.Scale().y ) * 2;
	TossGib( E, "Gib_Bone",  40, 0, 105, dir, scale );
	TossGib( E, "Gib_Bone", -40, 0, 105, dir, scale );
	// hands
	scale = kVec3( E.self.Scale().y*2.5f, E.self.Scale().z*2, E.self.Scale().x*2.5f );
	TossGib( E, "Gib_Bone2",  60, -20, 80, dir, scale );
	TossGib( E, "Gib_Bone2", -60, -20, 80, dir, scale );
	// legs
	scale = kVec3( E.self.Scale().x, E.self.Scale().z, E.self.Scale().y ) * 1.5f;
	TossGib( E, "Gib_Bone",  30, 0, 35, dir, scale );
	TossGib( E, "Gib_Bone", -30, 0, 35, dir, scale );
}

void SpawnGibs_Beetle( TurokEnemy@ E, const kVec3&in dir )
{
	E.self.SpawnFx( "fx/Bug_Guts.kfx", kVec3( 0,25,10) );
}
void SpawnGibs_Dragonfly( TurokEnemy@ E, const kVec3&in dir )
{
	E.self.SpawnFx( "fx/Bug_Guts.kfx", kVec3( 0,0,250) );
}

void SpawnGibs_Leaper( TurokEnemy@ E, const kVec3&in dir )
{
	kVec3 scale = E.self.Scale() / 2;
	TossGib( E, "Gib_Stalker_Head",  0, -50, 10, dir, scale );
	TossGib( E, "Gib_Stalker_Body",  0, -25, 10, dir, scale ); // thorax
	TossGib( E, "Gib_Stalker_Torso", 0, -10, 10, dir, scale ); // abdomen
	// hind legs
	TossGib( E, "Gib_Stalker_Feet", -10, 15, 0, dir, scale );
	TossGib( E, "Gib_Stalker_Feet",  10, 15, 0, dir, scale );
	// front legs
	TossGib( E, "Gib_Stalker_Feet", -10, -35, 0, dir, scale );
	TossGib( E, "Gib_Stalker_Feet",  10, -35, 0, dir, scale );
}

void SpawnGibs_Alien( TurokEnemy@ E, const kVec3&in dir )
{
	TossGib( E, "Gib_Alien_Head",  0, -40, 100, dir );
	TossGib( E, "Gib_Alien_Body",  0,  -5,  75, dir );
	TossGib( E, "Gib_Alien_Torso", 0,   0,  50, dir  );
	// hands
	TossGib( E, "Gib_Alien_Feet", -15, -35, 60, dir );
	TossGib( E, "Gib_Alien_Feet",  15, -35, 60, dir );
	TossGib( E, "Gib_Alien_Feet", -15,   0, 50, dir );
	TossGib( E, "Gib_Alien_Feet",  15,   0, 50, dir );
	// feet
	TossGib( E, "Gib_Alien_Feet", -15, 0, 15, dir );
	TossGib( E, "Gib_Alien_Feet",  15, 0, 15, dir );
}
