//=============================================================================
//
//	zombo.txt
//
//	This is the script for E1L4 miniboss, Zombo. He has 4 different
//	attacks. Attack 1 is a spinning attack where he fires two continuous
//	missiles 180 degrees apart and slowly turns. For attack 2 he fires a
//	set of missiles that turn and follow you (must be crouched under). For
//	attack 3 he fires 3 sets of rockets that must be dodged in a strafing
//	pattern. Finally, for attack 4, he paws at the ground and does a ram
//	attack.
//
//	For documentation on scripting, please consult the Wrack Wiki at:
//	http://wrack.wikia.com/
//
//=============================================================================

#include "commondefines.h"


#define	THRUST_HORIZONTAL		6
#define	THRUST_VERTICAL			10

//== VARIABLES ================================================================

// Keep track of the last attack we did so we don't do the same attack twice in
// a row.
int	g_iLastAttack;

// Did our target die? If so, don't react until we're done with an attack.
int	g_iTargetDied;

//== STATE PROTOTYPES =========================================================

state	Spawn;
state	Idle;
state	Chase;
state	WalkAlongObstruction;
state	WalkAwayFromObstruction;
state	ChooseAttack;
state	AttackMelee;
state	Attack1;
state	StartAttack2;
state	Attack2;
state	EndAttack2;
state	Attack3;
state	StartAttack4;
state	Attack4;
state	EndAttack4;
state	Killed;
state	Vanished;
state	RestoreOnContinue;

//== VARIABLES ================================================================

// What's the last frame we produced a blood splatter?
int	g_iLastBloodSplatterFrame;

//== EVENTS ===================================================================

event_instant( "bleed" )
{
	if ( g_iLastBloodSplatterFrame != GetCurrentFrame( ))
	{
		// Don't have the shotgun produce a million of these.
		g_iLastBloodSplatterFrame = GetCurrentFrame( );

		// Spawn a random blood splat.
		SetSpawnPosition( SPAWNPOSITION_FROMCENTER, GetIntProperty( "height" ) / 2, false, "" );
		switch ( rand( 1, 6 ))
		{
		case 1:

			SpawnItem( "BloodSplat1" );
			break;
		case 2:

			SpawnItem( "BloodSplat2" );
			break;
		case 3:

			SpawnItem( "BloodSplat3" );
			break;
		case 4:

			SpawnItem( "BloodSplat4" );
			break;
		case 5:

			SpawnItem( "BloodSplat5" );
			break;
		case 6:

			SpawnItem( "BloodSplat6" );
			break;
		}
	}
}

//-----------------------------------------------------------------------------
event_instant( "killed" )
	ChangeState( Killed );

//-----------------------------------------------------------------------------
event_instant( "gibbed" )
	ChangeState( Killed );

//-----------------------------------------------------------------------------
event_instant( "vanished" )
	ChangeState( Vanished );

//-----------------------------------------------------------------------------
event_instant( "player revived" )
{
	// Destroy gibs, puddles, etc.
	DestroyChildren( );

	DestroySelf( );
}

//-----------------------------------------------------------------------------
event_instant( "player continued" )
{
	// Destroy gibs, puddles, etc.
	DestroyChildren( );

	DestroySelf( );
}

//-----------------------------------------------------------------------------
event_instant( "target died" )
	changestate( Idle );

//== FUNCTIONS ================================================================
//-----------------------------------------------------------------------------
int CalcHorizontalGibThrust( void )
{
	return (( rand( 0, RAND_MAX ) - ( RAND_MAX / 2 )) * 2 * THRUST_HORIZONTAL );
}

//-----------------------------------------------------------------------------
int CalcVerticalGibThrust( void )
{
	return ( rand( 0, RAND_MAX ) * 2 * ( THRUST_VERTICAL / 2 ));
}


void PlayChaseAnimationAndSound( void )
{
	// Play our movement sound and animation.
	if ( GetSkillSettingFlags( ) & SKILL_VERYAGGRESSIVE )
	{
		SetAnimation( "walk", 50, ANIMF_LOOP );
		PlaySound( "zombo_walk_fast.wav", 100, SFXF_LOOP, SFXPRIORITY_MEDIUM );
	}
	else if ( GetSkillSettingFlags( ) & SKILL_AGGRESSIVE )
	{
		SetAnimation( "walk", 60, ANIMF_LOOP );
		PlaySound( "zombo_walk_medium.wav", 100, SFXF_LOOP, SFXPRIORITY_MEDIUM );
	}
	else
	{
		SetAnimation( "walk", 70, ANIMF_LOOP );
		PlaySound( "zombo_walk_slow.wav", 100, SFXF_LOOP, SFXPRIORITY_MEDIUM );
	}

	// Overlay an animation to face our target as we walk.
	SetLookDeltaAnimation( "Walk_Delta", 2, 6, ANGLE_1 * 75, 15 );
}

//-----------------------------------------------------------------------------
void PlayDeathAnimationAndSound( void )
{
	// Play our death sound and animation.
	PlaySound( "zombo_dead.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGH );
	SetAnimation( "death", -1, ANIMF_DONTBLEND );
}

//-----------------------------------------------------------------------------
void BeginChasing( void )
{
	// Being playing our chase animation and sound.
	PlayChaseAnimationAndSound( );

	// Switch to the chase state.
	ChangeState( Chase );
}

//== STATES ===================================================================

state Spawn
{
	event_instant( "land on floor" )
	{
		// Do a slight screen shake/knockback when we hit the ground.
		if ( QuakeAttack( 0, 8 * MAPUNIT_MULTIPLIER, 512 * MAPUNIT_MULTIPLIER, 0 ))
			DoScreenShake( SST_FADEOUT, 65536, ANGLE_1, 60 );
	}

	event_instant( "animation over" )
	{

		// Begin chasing the player.
		BeginChasing( );
	}

	OnEnter
	{
		g_iTargetDied = 0;

		// Randomly determine our "last" attack.
		g_iLastAttack = rand( 1, 4 );

		// Play our spawn animation.
		SetAnimation( "Spawn", -1, 0 );

		// Play our intro sound.
		PlaySound( "zombo_intro.wav", 100, SFXF_STOPEXISTING_ALL | SFXF_NON3D, SFXPRIORITY_HIGHEST );
	}

	MainLoop
	{
	}
}

//-----------------------------------------------------------------------------
state Idle
{
	OnEnter
	{
		g_iTargetDied = 0;

		// Just loop the idle animation until the player revives.
		SetAnimation( "Idle", -1, ANIMF_LOOP );
	}

	MainLoop
	{
	}
}

//-----------------------------------------------------------------------------
state Chase
{
	int	iRand;
	int	iTicks;

	event( "struck wall" )
	{
		// If we hit a wall, try to turn and move towards our target.
		// If we can't move towards it, first try to walk along the
		// obstruction.
		FaceTarget8Way( true, false );

		if ( CannotMoveForward( ))
			changestate( WalkAlongObstruction );
	}

	OnEnter
	{
		iTicks = 0;
	}

	MainLoop
	{
		// If the target is within our melee attack range, do a melee
		// attack.
		if ( TargetWithinAttackRange( ))
			ChangeState( AttackMelee );

		// Determine when to attack. If the skill setting calls for the
		// monsters to be more aggressive, then there's a higher chance
		// that the monster should attack.
		if ( GetSkillSettingFlags( ) & SKILL_VERYAGGRESSIVE )
			iRand = rand( 0, FPS );
		else if ( GetSkillSettingFlags( ) & SKILL_AGGRESSIVE )
			iRand = rand( 0, FPS * 2 );
		else
			iRand = rand( 0, FPS * 3 );

		// In this state, attack about once every 3 seconds.
		if (( iRand == 0 ) && ( CanSeeTarget( true )))
			changestate( ChooseAttack );

		if ( iTicks == 0 )
		{
			// Change directions.
			FaceTarget8Way( true, false );
			iTicks = rand( 1, 2 ) * FPS / 4;
		}
		else
			iTicks--;

		// Move forward.
		MoveForward( );
	}
}

//-----------------------------------------------------------------------------
state WalkAlongObstruction
{
	int	iRand;
	int	iTicks;
	int	iDistance;

	event( "struck wall" )
	{
		// If we hit a wall while trying to walk along our previous
		// obstruction, we must be really stuck. Try walking in some
		// random direction. But first, see if we can move towards our
		// target once again (maybe we're free in that direction now!)
		FaceTarget8Way( true, false );

		if ( CannotMoveForward( ))
			changestate( WalkAwayFromObstruction );
		else
			changestate( Chase );
	}

	OnEnter
	{
		// Try to walk along the wall we struck. Since we can walk in
		// one of two directions, try to go in the direction that takes
		// us more towards our target.
		SetAngle( ANGLE_YAW, GetHitFaceAngle( ) + ANGLE_90, true );
		if ( TargetInFront( ) == false )
			Turn( ANGLE_YAW, ANGLE_180, true );

		// Walk in this new direction between 32 and 96 units.
		iDistance = rand( 1, 3 ) * 32 * 65536;
		iTicks = iDistance / GetIntProperty( "speed" );
	}

	MainLoop
	{
		// If the target is within our melee attack range, do a melee
		// attack.
		if ( TargetWithinAttackRange( ))
			ChangeState( AttackMelee );

		// Determine when to attack. If the skill setting calls for the
		// monsters to be more aggressive, then there's a higher chance
		// that the monster should attack.
		if ( GetSkillSettingFlags( ) & SKILL_VERYAGGRESSIVE )
			iRand = rand( 0, FPS );
		else if ( GetSkillSettingFlags( ) & SKILL_AGGRESSIVE )
			iRand = rand( 0, FPS * 2 );
		else
			iRand = rand( 0, FPS * 3 );

		// In this state, attack about once every 3 seconds.
		if (( iRand == 0 ) && ( CanSeeTarget( true )))
			ChangeState( ChooseAttack );

		// If we're done walking in this new direction, potentially 
		// resume chasing our target.
		if ( iTicks == 0 )
		{
			// Face our target once again. If we can move towards
			// it, resume chasing.
			FaceTarget8Way( true, false );
			if ( CannotMoveForward( ))
			{
				// Walk away from the obstruction we previously
				// hit.
				changestate( WalkAwayFromObstruction );
			}
			else
				changestate( Chase );
		}

		// Decrement our timer, and move forward.
		iTicks--;
		MoveForward( );
	}
}

//-----------------------------------------------------------------------------
state WalkAwayFromObstruction
{
	int	iRand;
	int	iTicks;
	int	iDistance;

	event( "struck wall" )
	{
		// We hit a wall... again! Once again try to walk towards our
		// target. If we can't, just pick another direction to go in.
		FaceTarget8Way( true, false );

		if ( CannotMoveForward( ))
			changestate( WalkAwayFromObstruction );
		else
			changestate( Chase );
	}

	OnEnter
	{
		// Start off by facing directly at the wall we struck.
		SetAngle( ANGLE_YAW, GetHitFaceAngle( ) + ANGLE_180, true );

		// Determine how many degrees to turn. Turn in multiples of 45
		// degrees.
		iRand = rand( 0, 5 );
		if ( iRand == 0 )
			Turn( ANGLE_YAW, ANGLE_45 * 2, true );
		else if ( iRand == 1 )
			Turn( ANGLE_YAW, ANGLE_45 * 3, true );
		else if ( iRand == 2 )
			Turn( ANGLE_YAW, ANGLE_45 * 4, true );
		else if ( iRand == 3 )
			Turn( ANGLE_YAW, ANGLE_45 * 5, true );
		else
			Turn( ANGLE_YAW, ANGLE_45 * 6, true );

		// Walk in this new direction between 32 and 96 units.
		iDistance = rand( 1, 3 ) * 32 * 65536;
		iTicks = iDistance / GetIntProperty( "speed" );
	}

	MainLoop
	{
		// If the target is within our melee attack range, do a melee
		// attack.
		if ( TargetWithinAttackRange( ))
			ChangeState( AttackMelee );

		// Determine when to attack. If the skill setting calls for the
		// monsters to be more aggressive, then there's a higher chance
		// that the monster should attack.
		if ( GetSkillSettingFlags( ) & SKILL_VERYAGGRESSIVE )
			iRand = rand( 0, FPS );
		else if ( GetSkillSettingFlags( ) & SKILL_AGGRESSIVE )
			iRand = rand( 0, FPS * 2 );
		else
			iRand = rand( 0, FPS * 3 );

		// In this state, attack about once every 3 seconds.
		if (( iRand == 0 ) && ( CanSeeTarget( true )))
			ChangeState( ChooseAttack );

		// If we're done walking in this new direction, potentially 
		// resume chasing our target.
		if ( iTicks == 0 )
		{
			// Face our target once again. If we can move towards
			// it, resume chasing.
			FaceTarget8Way( true, false );
			if ( CannotMoveForward( ))
			{
				// Walk away from the obstruction we previously
				// hit.
				changestate( WalkAwayFromObstruction );
			}
			else
				changestate( Chase );
		}

		// Decrement our timer, and move forward.
		iTicks--;
		MoveForward( );
	}
}

//-----------------------------------------------------------------------------
state AttackMelee
{
	event( "target died" )
		g_iTargetDied = 1;

	event( "animation almost over" )
	{
		if ( g_iTargetDied )
			changestate( Idle );

		PlayChaseAnimationAndSound( );
		ChangeState( Chase );
	}

	event( "kick" )
	{
		// Melee attack the target.
		if ( MeleeAttackTarget( GetIntProperty( "damage" ), GetIntProperty( "knockback" ), 0 ))
			PlaySound( "zombo_melee.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );
		else
			PlaySound( "zombo_miss.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );
	}

	OnEnter
	{
		// Stop any looping movement sounds (in case we had previously
		// been chasing).
		StopSounds( true );

		// Face our target before attacking.
		FaceTarget( true, -1 );

		// Begin raising melee weapon.
		SetAnimation( "Melee", -1, 0 );
	}

	MainLoop
	{
	}
}

//-----------------------------------------------------------------------------
state ChooseAttack
{
	int	iAttack;

	MainLoop
	{
		// Randomly pick our attack.
		iAttack = rand( 1, 4 );

		// Make sure we don't do the same attack twice in a row.
		while ( iAttack == g_iLastAttack )
			iAttack = rand( 1, 4 );

		// Save the attack.
		g_iLastAttack = iAttack;

		switch ( iAttack )
		{
		case 1:

			changestate( Attack1 );
			break;
		case 2:

			changestate( StartAttack2 );
			break;
		case 3:

			changestate( Attack3 );
			break;
		case 4:

			changestate( StartAttack4 );
			break;
		}
	}
}

//-----------------------------------------------------------------------------
state Attack1
{
	int	iTick;
	int	iMissileTick;

	event( "target died" )
		g_iTargetDied = 1;

	event( "animation almost over" )
	{
		if ( g_iTargetDied )
			changestate( Idle );

		PlayChaseAnimationAndSound( );
		ChangeState( Chase );
	}

	OnEnter
	{
		// Stop any looping movement sounds (in case we had previously
		// been chasing).
		StopSounds( true );

		// Face our target before attacking.
		FaceTarget( true, -1 );

		// Play our attack animation.
		iTick = 0;
		iMissileTick = 0;
		SetAnimation( "Attack_Spin", -1, 0 );
		PlaySound( "zombo_attack_spin.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );
	}

	MainLoop
	{
		if (( iTick > 30 ) && ( iTick < 150 ))
		{
			if ( iMissileTick == 0 )
			{
				// Ensure that the animation has been properly before we spawn
				// projectiles from its bones.
				ForceUpdateAnimation( );

				// Fire from our left gun.
				SetSpawnPositionAtBone( "ZomboAttachmentGunL", true, "Missile_Spiral_Zombo" );
				FireProjectileAtTarget( "Missile_Spiral_Zombo1", 1, 0, FPF_DONTANGLE );

				// Fire from our right gun.
				SetSpawnPositionAtBone( "ZomboAttachmentGunR", true, "Missile_Spiral_Zombo" );
				FireProjectileAtTarget( "Missile_Spiral_Zombo1", 1, 0, FPF_DONTANGLE );

				iMissileTick = 12;
			}
			iMissileTick--;
		}
		iTick++;
	}
}

//-----------------------------------------------------------------------------
state StartAttack2
{
	event( "target died" )
		g_iTargetDied = 1;

	event( "animation over" )
		changestate( Attack2 );

	OnEnter
	{
		// Face our target before attacking.
		FaceTarget( true, -1 );

		// Raise our guns.
		SetAnimation( "Attack_Missiles_In", -1, 0 );
		PlaySound( "zombo_attack_missiles_in.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );
	}

	MainLoop
	{
	}
}

//-----------------------------------------------------------------------------
state Attack2
{
	int	iFire;
	int	iMissileCount;

	event( "target died" )
		g_iTargetDied = 1;

	event( "animation over" )
	{
		// Once we've fired all of our sets of missiles, end the attack.
		if ( iMissileCount == 0 )
			changestate( EndAttack2 );

		// Otherwise, we're ready to fire!
		iFire = 1;
	}

	OnEnter
	{
		iFire = 1;
		if ( GetSkillSettingFlags( ) & SKILL_VERYAGGRESSIVE )
			iMissileCount = 4;
		else if ( GetSkillSettingFlags( ) & SKILL_AGGRESSIVE )
			iMissileCount = 3;
		else
			iMissileCount = 2;
	}

	MainLoop
	{
		// Check to see if we're ready to fire our shots.
		if ( iFire == 1 )
		{
			// Play the fire animation.
			if ( GetSkillSettingFlags( ) & SKILL_VERYAGGRESSIVE )
				SetAnimation( "Attack_Missiles_Fire", 25, 0 );
			else if ( GetSkillSettingFlags( ) & SKILL_AGGRESSIVE )
				SetAnimation( "Attack_Missiles_Fire", 37, 0 );
			else
				SetAnimation( "Attack_Missiles_Fire", 50, 0 );
			PlaySound( "zombo_attack_missiles_fire.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );

			// Ensure that the animation has been properly before we spawn
			// projectiles from its bones.
			ForceUpdateAnimation( );

			// Fire from our left gun.
			SetSpawnPositionAtBone( "ZomboAttachmentGunL", true, "Missile_Seeking_Zombo" );
			FireProjectileAtTarget( "Missile_Seeking_Zombo1", 1, 0, FPF_DONTANGLE | FPF_IGNORECROUCH);

			// Fire from our right gun.
			SetSpawnPositionAtBone( "ZomboAttachmentGunR", true, "Missile_Seeking_Zombo" );
			FireProjectileAtTarget( "Missile_Seeking_Zombo1", 1, 0, FPF_DONTANGLE | FPF_IGNORECROUCH);

			// Wait until the animation is finished to fire again.
			iFire = 0;
			iMissileCount--;				
		}
	}
}

//-----------------------------------------------------------------------------
state EndAttack2
{
	event( "target died" )
		g_iTargetDied = 1;

	event( "animation almost over" )
	{
		if ( g_iTargetDied )
			changestate( Idle );

		PlayChaseAnimationAndSound( );
		ChangeState( Chase );
	}

	OnEnter
	{
		// Lower our guns.
		SetAnimation( "Attack_Missiles_Out", -1, 0 );
		PlaySound( "zombo_attack_missiles_out.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );
	}

	MainLoop
	{
	}
}

//-----------------------------------------------------------------------------
state Attack3
{
	event( "left gun rocket" )
	{
		// Fire from our left gun.
		SetSpawnPositionAtBone( "ZomboAttachmentGunL", true, "Rocket_Zombo" );
		FireProjectileAtTarget( "Rocket_Zombo1", 1, ANGLE_1 * 5, FPF_DONTANGLE );
	}

	event( "right gun rocket" )
	{
		// Fire from our right gun.
		SetSpawnPositionAtBone( "ZomboAttachmentGunR", true, "Rocket_Zombo" );
		FireProjectileAtTarget( "Rocket_Zombo1", 1, ANGLE_1 * 5, FPF_DONTANGLE );
	}

	event( "face target" )
	{
		// Face our target.
		FaceTarget( true, -1 );
	}

	event( "animation almost over" )
	{
		if ( g_iTargetDied )
			changestate( Idle );

		PlayChaseAnimationAndSound( );
		ChangeState( Chase );
	}

	OnEnter
	{
		// Stop any looping movement sounds (in case we had previously
		// been chasing).
		StopSounds( true );

		// Face our target before attacking.
		FaceTarget( true, -1 );

		// Begin our Doom 2 mancubus-style attack.
		SetAnimation( "Attack_Mancubus", -1, 0 );
		PlaySound( "zombo_attack_mancubus.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );
	}

	MainLoop
	{
	}
}

//-----------------------------------------------------------------------------
state StartAttack4
{
	event( "target died" )
		g_iTargetDied = 1;

	event( "animation almost over" )
		changestate( Attack4 );

	OnEnter
	{
		// Face our target before attacking.
		FaceTarget( true, -1 );

		// Get ready to charge!
		SetAnimation( "Attack_Charge_Intro", -1, 0 );
		PlaySound( "zombo_attack_charge_intro.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );
	}

	MainLoop
	{
	}
}

//-----------------------------------------------------------------------------
state Attack4
{
	int	iChaseTicks;

	event( "target died" )
		g_iTargetDied = 1;

	event_instant( "struck object" )
	{
		// Damage the object.
		if ( GetSkillSettingFlags( ) & SKILL_VERYHIGHDAMAGE )
			DamageStruckObject( 75, 512 * ( MAPUNIT_MULTIPLIER / 16 ), 0 );
		else if ( GetSkillSettingFlags( ) & SKILL_HIGHDAMAGE )
			DamageStruckObject( 40, 448 * ( MAPUNIT_MULTIPLIER / 16 ), 0 );
		else
			DamageStruckObject( 25, 384 * ( MAPUNIT_MULTIPLIER / 16 ), 0 );

		// Play the hit animation and sound.
		SetAnimation( "Attack_Charge_Hit", -1, 0 );
		PlaySound( "zombo_attack_charge_hit.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );
		changestate( EndAttack4 );
	}

	event_instant( "struck wall" )
	{
		// If we've made it past the player and struck a wall, end the attack.
		if ( TargetInFront( ) == false )
		{
			// Play the miss animation and sound.
			SetAnimation( "Attack_Charge_Miss", -1, 0 );
			PlaySound( "zombo_attack_charge_miss.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );
			changestate( EndAttack4 );
		}
	}

	OnEnter
	{
		// Chase for a maximum of 4 seconds.
		iChaseTicks = 240;

		// Play our charge animation.
		if ( GetSkillSettingFlags( ) & SKILL_VERYAGGRESSIVE )
			SetAnimation( "Attack_Charge_Run", 32, ANIMF_LOOP );
		else if ( GetSkillSettingFlags( ) & SKILL_AGGRESSIVE )
			SetAnimation( "Attack_Charge_Run", 36, ANIMF_LOOP );
		else
			SetAnimation( "Attack_Charge_Run", 40, ANIMF_LOOP );
		PlaySound( "zombo_attack_charge_run.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );
	}

	MainLoop
	{
		// Turn towards our target.
		if ( TargetInFront( ))
			TurnTowardsTarget( ANGLE_1 + ( ANGLE_1 / 2 ), false );

		// Update our velocity.
		if ( GetSkillSettingFlags( ) & SKILL_VERYAGGRESSIVE )
			UpdateVelocity( 10 * MAPUNIT_MULTIPLIER, true );
		else if ( GetSkillSettingFlags( ) & SKILL_AGGRESSIVE )
			UpdateVelocity( 9 * MAPUNIT_MULTIPLIER, true );
		else
			UpdateVelocity( 8 * MAPUNIT_MULTIPLIER, true );

		iChaseTicks--;
		if ( iChaseTicks == 0 )
		{
			// Play the miss animation and sound.
			SetAnimation( "Attack_Charge_Miss", -1, 0 );
			PlaySound( "zombo_attack_charge_miss.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );
			changestate( EndAttack4 );
		}
	}
}

//-----------------------------------------------------------------------------
state EndAttack4
{
	event( "target died" )
		g_iTargetDied = 1;

	event( "animation almost over" )
	{
		if ( g_iTargetDied )
			changestate( Idle );

		PlayChaseAnimationAndSound( );
		ChangeState( Chase );
	}

	MainLoop
	{
	}
}

//-----------------------------------------------------------------------------
state Killed
{
	int	iAnimationInProgress;
	int	iCheckpointReached;

	event( "target died" )
	{
	}

	// Spawn a blood puddle underneath us.
	event_instant( "animation over" )
	{
		// Spawn a large blood puddle at Zombo's spine.
		ForceUpdateAnimation( );
		SetSpawnPositionAtBone( "ZomboSpine1", false, "" );
		SpawnItem( "BloodPuddle_Large" );

		if ( iCheckpointReached == 1 )
			changestate( RestoreOnContinue );
		else
			iAnimationInProgress = 0;
		delay( 5 );
		ExecuteMapScript( "ZomboSpecialDeath", 0, 0, 0, 0 );
	}

	// Just wait to be restored if the player continues.
	event_instant( "player reached checkpoint" )
	{
		if ( iAnimationInProgress == 0 )
			changestate( RestoreOnContinue );
		else
			iCheckpointReached = 1;
	}

	OnEnter
	{
		// Play the death animation and sound.
		SetSpawnPosition( SPAWNPOSITION_FROMCENTER, GetIntProperty( "projectileheight" ), false, "" );
	SpawnParticles( "BloodExplosionParticle", 1, true, 0, ANGLE_90 * -1 );

		SetSpawnPosition( SPAWNPOSITION_FROMCENTER, GetIntProperty( "height" ) / 2, false, "" );
		TossGib( "Arcturan_OrganGib", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Arcturan_SmallChunkGib", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Arcturan_SmallChunkGib", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Arcturan_LargeChunkGib", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );

		iAnimationInProgress = 1;
		iCheckpointReached = 0;
		SetAnimation( "Death", -1, 0 );
		PlaySound( "zombo_dead.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );

		// Fade out our directional shadow.
		FadeOutShadow( 30 );

		AddFlag( FLAG_NOCOLLISIONAGAINST );
		AddFlag( FLAG_NOTHINGCOLLISION );

		// If we were hit by a multiple hits, take knockback from all
		// of the shots.
		AddFlag( FLAG_ALLOWRIPPING );
		delay( 1 );
		AddFlag( FLAG_NOTHRUST );
	}

	MainLoop
	{
	}
}

//-----------------------------------------------------------------------------
state Vanished
{
	// Just wait to be restored if the player continues.
	event_instant( "player reached checkpoint" )
		changestate( RestoreOnContinue );

	OnEnter
	{
		// Stop all of our sounds from playing.
		StopSounds( true );
		StopSounds( false );

		// Disappear and be dead.
		AddFlag( FLAG_NOCOLLISIONAGAINST );
		AddFlag( FLAG_NOTHINGCOLLISION );
		AddFlag( FLAG_DONTRENDER );
		Die( );
	}

	MainLoop
	{
	}
}

//-----------------------------------------------------------------------------
state RestoreOnContinue
{
	// If we're waiting to be restored only if the player continues, don't
	// restore ourself if the player only revives.
	event_instant( "player revived" )
	{
	}

	MainLoop
	{
	}
}
