//=============================================================================
//
//	oculus.txt
//
//	This is the script for the E1L7 boss, Oculus. He battles you with a
//	combination of attacks from himself, his nearby turrets, and turrets
//	along the walls. Him and his turrets are intermittently protected by
//	force fields. For his first attack, he fires three vertically seeking
//	missiles which must be shot and destroyed. For his second attack, his
//	side turrets fire a series of sweeping spread shots which you must
//	dodge under. For his third attack, his side turrets fire a series of
//	synchronized shots which must be dodged. For his fourth attack, his
//	side turrets fire a series of rocket blasts one after another which
//	must be dodged contiuously. For his fifth attack, the side turrets
//	fire a series of room-sweeping attacks which must be dodged by running
//	behind the previous set of blasts.
//
//	- Intro: Float down from top to, float behind FF
//	- Vertical seeking missiles (shoot to destroy)
//	- Charged up magnetar attack (hide up front)
//	- Seeking rockets (hide in trenches) (sealed) (red force fields)
//	- Rockets in trench areas (sealed) (green force fields)
//	- Switches sides and attacks
//
//	For documentation on scripting, please consult the Wrack Wiki at:
//	http://wrack.wikia.com/
//
//=============================================================================

#include "commondefines.h"

//== DEFINITIONS ==============================================================

#define	THRUST_HORIZONTAL	8
#define	THRUST_VERTICAL		12

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

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

// What side of the arena are we on?
int	g_iNorthSide;

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

state	Spawn;
state	IntroSequence;
state	Main;
state	Attack1;
state	Attack2;
state	Attack3;
state	Attack4;
state	DeathSequence;

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

event_instant( "killed" )
	ChangeState( DeathSequence );

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

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

	DestroySelf( );
}

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

	DestroySelf( );
}

//== 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 ));
}

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

state Spawn
{
	OnEnter
	{

		// Play our idle animation.
		SetAnimation( "Idle", -1, ANIMF_LOOP );

		g_iLastAttack = -1;
	}

	MainLoop
	{
		// Begin our intro sequence.
		ChangeState( IntroSequence );
	}
}

//-----------------------------------------------------------------------------
state IntroSequence
{
	int	iDistance;

	OnEnter
	{
	}

	MainLoop
	{
		MoveLinearFromOrigin( 0, -768, 0 );
		delay( 15 );
		// Descend from the sky.
		iDistance = 384;
		while ( iDistance > 0 )
		{
			iDistance -= 4;
			MoveLinearFromOrigin( 0, iDistance, 0 );
			delay( 1 );
		}

		// Pause for a second.
		delay( 60 );

		// Move behind our force field.
		iDistance = 0;
		while ( iDistance < 640 )
		{
			iDistance += 4;
			MoveLinearFromOrigin( iDistance, 0, 0 );
			delay( 1 );
		}

		// We start off on the north side of the arena.
		g_iNorthSide = 1;

		// Activate the force field.
		ExecuteMapScript( "EnableOculusBlueForceFields", 0, 0, 0, 0 );
		delay( 60 );

		// Un-freeze everything now that the intro sequence is complete.
		FreezeAction( FA_NONE );
		ClearFlag( FLAG_HIDELIFEBAR );

		changestate( Main );
	}
}

//-----------------------------------------------------------------------------
state Main
{
	int	iAttack;

	MainLoop
	{
		// Determine the delay between attacks. 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 )
			delay( rand( 0, FPS * 1 ));
		else if ( GetSkillSettingFlags( ) & SKILL_AGGRESSIVE )
			delay( rand( 0, FPS * 2 ));
		else
			delay( rand( 0, FPS * 3 ));

		// 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( Attack2 );
			break;
		case 3:

			changestate( Attack3 );
			break;
		case 4:

			changestate( Attack4 );
			break;
		}
	}
}

//-----------------------------------------------------------------------------
state Attack1
{
	int	iTick;
	int	iBaseZDistance;
	int	iNumShotsLeft;
	int	iShakeAmount;

	MainLoop
	{
		// First, disable the force fields.
		ExecuteMapScript( "DisableOculusBlueForceFields", 0, 0, 0, 0 );

		// Move up slightly.
		if ( g_iNorthSide == 1 )
		{
			iBaseZDistance = 640;
			while ( iBaseZDistance > 576 )
			{
				iBaseZDistance--;
				MoveLinearFromOrigin( iBaseZDistance, 0, 0 );
				delay( 1 );
			}
		}
		else
		{
			iBaseZDistance = -640;
			while ( iBaseZDistance < -576 )
			{
				iBaseZDistance++;
				MoveLinearFromOrigin( iBaseZDistance, 0, 0 );
				delay( 1 );
			}
		}

		// Begin vibrating slightly.
		iShakeAmount = 4;
		iTick = 60;
		while ( iTick > 0 )
		{
			MoveLinearFromOrigin( rand( 0, iShakeAmount ) - ( iShakeAmount / 2 ) + iBaseZDistance , rand( 0, iShakeAmount ) - ( iShakeAmount / 2 ), rand( 0, iShakeAmount ) - ( iShakeAmount / 2 ));
			FaceTarget( false, -1 );
			iTick--;
			delay( 1 );
		}

		// Begin vibrating move violently and play our magnetar warmup sound.
		iShakeAmount = 12;
		iTick = GetSoundLength( "magnetar_warmup.wav" );
		PlaySound( "magnetar_warmup.wav", 100, SFXF_STOPEXISTING_ALL, SFXPRIORITY_HIGHEST );
		while ( iTick > 0 )
		{
			MoveLinearFromOrigin( rand( 0, iShakeAmount ) - ( iShakeAmount / 2 ) + iBaseZDistance , rand( 0, iShakeAmount ) - ( iShakeAmount / 2 ), rand( 0, iShakeAmount ) - ( iShakeAmount / 2 ));
			FaceTarget( false, -1 );
			iTick--;
			delay( 1 );
		}

		// Stop vibrating.
		MoveLinearFromOrigin( iBaseZDistance, 0, 0 );

		iNumShotsLeft = 3;
		while ( iNumShotsLeft > 0 )
		{
			// Fire a magnetar shot at the player.
			SetSpawnPosition( SPAWNPOSITION_FROMCENTER, GetIntProperty( "projectileheight" ), false, "" );	// Eventually we'll use the position of the eye bone here.
			MagnetarAttackTarget( GetStringProperty( "Projectile" ), "ImpactHandler_Magnetar", GetIntProperty( "damage" ), GetIntProperty( "knockback" ), DF_ARMORPIERCING );
			PlaySound( "magnetar_fire.wav", 100, SFXF_STOPEXISTING_ALL | SFXF_PLAYIFOUTOFRANGE, SFXPRIORITY_HIGHEST );
			iTick = 60;
			while ( iTick > 0 )
			{
				FaceTarget( false, -1 );
				iTick--;
				delay( 1 );
			}

			iNumShotsLeft--;
		}

		// Face the center of the arena once again.
		SetTarget( SETTARGET_TAG, 11, false );
		FaceTarget( true, -1 );
		SetTarget( SETTARGET_PLAYER, -1, false );		

		// Re-enable the force fields.
		ExecuteMapScript( "EnableOculusBlueForceFields", 0, 0, 0, 0 );

		// Move back to our original position.
		if ( g_iNorthSide == 1 )
		{
			while ( iBaseZDistance < 640 )
			{
				iBaseZDistance++;
				MoveLinearFromOrigin( iBaseZDistance, 0, 0 );
				delay( 1 );
			}
		}
		else
		{
			while ( iBaseZDistance > -640 )
			{
				iBaseZDistance--;
				MoveLinearFromOrigin( iBaseZDistance, 0, 0 );
				delay( 1 );
			}
		}

		changestate( Main );
	}
}

//-----------------------------------------------------------------------------
state Attack2
{
	MainLoop
	{
		// First, disable the red force fields.
		ExecuteMapScript( "DisableOculusGreenForceFields", 0, 0, 0, 0 );
		delay( 60 );

		// Spawn some seeking rocket shooters.
		ExecuteMapScript( "OculusSpreadRocketAttack", 0, 0, 0, 0 );
		delay( 120 );

		// Re-enable the force fields.
		ExecuteMapScript( "EnableOculusGreenForceFields", 0, 0, 0, 0 );
		delay( 60 );

		changestate( Main );
	}
}

//-----------------------------------------------------------------------------
state Attack3
{
	MainLoop
	{
		// First, disable the red force fields.
		ExecuteMapScript( "DisableOculusRedForceFields", 0, 0, 0, 0 );
		delay( 60 );

		// Spawn some seeking rocket shooters.
		ExecuteMapScript( "OculusSeekingRocketAttack", 0, 0, 0, 0 );
		delay( 120 );

		// Re-enable the force fields.
		ExecuteMapScript( "EnableOculusRedForceFields", 0, 0, 0, 0 );
		delay( 60 );

		changestate( Main );
	}
}

//-----------------------------------------------------------------------------
state Attack4
{
	int	iDistance;
	int	iShootTick;

	OnEnter
	{
		iShootTick = 0;
	}

	MainLoop
	{
		// First, disable the force fields.
		ExecuteMapScript( "DisableOculusBlueForceFields", 0, 0, 0, 0 );
		delay( 60 );

		if ( g_iNorthSide )
		{
			iDistance = 640;
			while ( iDistance > -640 )
			{
				iDistance -= 4;
				MoveLinearFromOrigin( iDistance, 0, 0 );
				FaceTarget( false, -1 );

				// Once every second, fire a spread shot while floating.
				if ( iShootTick == 0 )
				{
					SetSpawnPosition( SPAWNPOSITION_INFRONT, GetIntProperty( "projectileheight" ), true, "Missile" );
					FireProjectileAtTarget( "Missile", 4, ANGLE_1 * 5, 0 );
					iShootTick = 60;
				}
				else
					iShootTick--;

				delay( 1 );
			}

			g_iNorthSide = 0;
		}
		else
		{
			iDistance = -640;
			while ( iDistance < 640 )
			{
				iDistance += 4;
				MoveLinearFromOrigin( iDistance, 0, 0 );
				FaceTarget( false, -1 );

				// Once every second, fire a spread shot while floating.
				if ( iShootTick == 0 )
				{
					SetSpawnPosition( SPAWNPOSITION_INFRONT, GetIntProperty( "projectileheight" ), true, "Missile" );
					FireProjectileAtTarget( "Missile", 4, ANGLE_1 * 5, 0 );
					iShootTick = 60;
				}
				else
					iShootTick--;

				delay( 1 );
			}

			g_iNorthSide = 1;
		}

		// Face the center of the arena once again.
		SetTarget( SETTARGET_TAG, 11, false );
		FaceTarget( true, -1 );
		SetTarget( SETTARGET_PLAYER, -1, false );		

		// Re-enable the force fields.
		ExecuteMapScript( "EnableOculusBlueForceFields", 0, 0, 0, 0 );
		delay( 60 );

		changestate( Main );
	}
}

//-----------------------------------------------------------------------------
state DeathSequence
{
	event_instant( "animation over" )
	{
		// Stop our sounds.
		StopSounds( true );
		StopSounds( false );

		// Play our gib sound.
		PlaySound( "oculus_explode.wav", 100, SFXF_STOPEXISTING_ALL | SFXF_NON3D, SFXPRIORITY_HIGH );

		// Spawn an explosion particle.
		SetSpawnPosition( SPAWNPOSITION_FROMCENTER, GetIntProperty( "height" ) / 2, false, "" );
		SpawnParticles( "ExplosionParticle", 1, true, 0, ANGLE_90 * -1 );

		// Make our body disappear.
		AddFlag( FLAG_DONTRENDER );

		// Toss some gibs.
		SetSpawnPosition( SPAWNPOSITION_FROMCENTER, GetIntProperty( "height" ) / 2, false, "" );
		TossGib( "Oculus_Gib01", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Oculus_Gib02", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Oculus_Gib03", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Oculus_Gib04", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Oculus_Gib05", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Oculus_Gib06", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Oculus_Gib07", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Oculus_Gib08", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Oculus_Gib09", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Oculus_Gib10", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Oculus_Gib11", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );
		TossGib( "Oculus_Gib12", CalcHorizontalGibThrust( ), CalcVerticalGibThrust( ), CalcHorizontalGibThrust( ), true );

		// Fade from white and pause for a couple seconds.
		FadeFromColor( 255, 255, 255, 60 );
		delay( 120 );
	}

	// One second before the end of the death animation, begin fading to
	// white.
	event_instant( "fade to white" )
{
}
	OnEnter
	{

		// Face the boss as it blows apart.
		ExecuteMapScript( "OculusSpecialDeath", 0, 0, 0, 0 );

		// Play our death sound.
		PlaySound( "oculus_dead.wav", 100, SFXF_STOPEXISTING_ALL | SFXF_NON3D, SFXPRIORITY_HIGHEST );

		// Stop allowing other objects to collide with us.
		StopBlocking( );

		// Play our death animation.
		SetAnimation( "Death", -1, ANIMF_DONTBLEND );
	}

	MainLoop
	{
	}
}
