



#include "../Game_local.h"

extern const char* aiActionStatusString [ rvAIAction::STATUS_MAX ];

class rvMonsterStroggFlyer : public idAI {
public:

	CLASS_PROTOTYPE( rvMonsterStroggFlyer );

	rvMonsterStroggFlyer ( void );

	void				InitSpawnArgsVariables( void );
	void				Spawn				( void );
	void				Save				( idSaveGame *savefile ) const;
	void				Restore				( idRestoreGame *savefile );

	virtual void		GetDebugInfo		( debugInfoProc_t proc, void* userData );

protected:

	virtual bool		CheckActions		( void );

	virtual void		OnUpdatePlayback	( const rvDeclPlaybackData& pbd );
	virtual void		OnWakeUp			( void );

	rvAIAction			actionBombAttack;
	rvAIAction			actionBlasterAttack;
	
	idVec3				velocity;

	int					shotCount;
	jointHandle_t		jointGunRight;
	jointHandle_t		jointGunLeft;

	int					lastAttackTime;
	int					attackStartTime;

	int					blasterAttackDuration;
	int					blasterAttackRate;
	int					bombAttackDuration;
	int					bombAttackRate;
	
private:

	stateResult_t		State_ScriptedPlaybackMove	( const stateParms_t& parms );
	stateResult_t		State_Killed				( const stateParms_t& parms );

	stateResult_t		State_Torso_BlasterAttack	( const stateParms_t& parms );
	stateResult_t		State_Torso_BombAttack		( const stateParms_t& parms );

	void				AttackBlaster				( void );
	void				AttackBomb					( void );

	CLASS_STATES_PROTOTYPE ( rvMonsterStroggFlyer );
};

CLASS_DECLARATION( idAI, rvMonsterStroggFlyer )
END_CLASS

/*
================
rvMonsterStroggFlyer::rvMonsterStroggFlyer
================
*/
rvMonsterStroggFlyer::rvMonsterStroggFlyer ( ) {
	shotCount = 0;
	lastAttackTime = 0;
	attackStartTime = 0;
}

void rvMonsterStroggFlyer::InitSpawnArgsVariables( void )
{
	jointGunRight	= animator.GetJointHandle ( spawnArgs.GetString ( "joint_gun_right" ) );
	jointGunLeft	= animator.GetJointHandle ( spawnArgs.GetString ( "joint_gun_left" ) );
	
	blasterAttackDuration	= SEC2MS ( spawnArgs.GetFloat ( "blasterAttackDuration", "1" ) );
	blasterAttackRate		= SEC2MS ( spawnArgs.GetFloat ( "blasterAttackRate", ".25" ) );
	bombAttackDuration		= SEC2MS ( spawnArgs.GetFloat ( "bombAttackDuration", "1" ) );
	bombAttackRate			= SEC2MS ( spawnArgs.GetFloat ( "bombAttackRate", ".25" ) );
}

/*
================
rvMonsterStroggFlyer::Spawn
================
*/
void rvMonsterStroggFlyer::Spawn ( void ) {
	actionBombAttack.Init	 ( spawnArgs, "action_bombAttack",		"Torso_BombAttack",		AIACTIONF_ATTACK );
	actionBlasterAttack.Init ( spawnArgs, "action_blasterAttack",	"Torso_BlasterAttack",	AIACTIONF_ATTACK );
	
	InitSpawnArgsVariables();
}

/*
================
rvMonsterStroggFlyer::OnUpdatePlayback
================
*/
void rvMonsterStroggFlyer::OnUpdatePlayback ( const rvDeclPlaybackData& pbd ) {
	byte	buttons;
	byte	changed;
	byte	impulse;

	velocity = pbd.GetVelocity ( );

	buttons = pbd.GetButtons();
	changed = pbd.GetChanged();
	impulse = pbd.GetImpulse();

	// Shoot the blaster if the attack button was pressed
	if ( (changed & buttons) & BUTTON_ATTACK ){
		AttackBlaster ( );
	}

	if ( (changed & buttons) & BUTTON_ZOOM ){
		AttackBomb ( );
	}

	switch ( impulse ) {
		case 40:
			aifl.disableAttacks = true;
			break;
		
		case 41:
			aifl.disableAttacks = false;
			break;

		case 42:
			StartSound ( "snd_bombrun", SND_CHANNEL_ANY, 0, false, NULL );
			break;
	}
}

/*
================
rvMonsterStroggFlyer::OnWakeUp
================
*/
void rvMonsterStroggFlyer::OnWakeUp ( void ) {
	jointHandle_t joint;
	joint = GetAnimator()->GetJointHandle( spawnArgs.GetString ( "joint_thruster", "tail_thrusters" ) );
	if ( joint != INVALID_JOINT ) {
		PlayEffect ( "fx_exhaust", joint, true );
	}
	StartSound ( "snd_flyloop", SND_CHANNEL_ANY, 0, false, NULL );
	
	return idAI::OnWakeUp ( );
}


/*
================
rvMonsterStroggFlyer::AttackBlaster
================
*/
void rvMonsterStroggFlyer::AttackBlaster ( void ) {
	jointHandle_t joint;	
	joint = ((shotCount++)%2) ? jointGunRight : jointGunLeft;
	
	if ( joint != INVALID_JOINT ) {
		PlayEffect ( "fx_muzzleflash", joint );
		Attack ( "blaster", joint, enemy.ent );
	}
}

/*
================
rvMonsterStroggFlyer::AttackBomb
================
*/
void rvMonsterStroggFlyer::AttackBomb ( void ) {
	jointHandle_t joint;	
	joint = ((shotCount++)%2) ? jointGunRight : jointGunLeft;
	
	if ( joint != INVALID_JOINT ) {
		StartSound ( "snd_bombrun", SND_CHANNEL_ANY, 0, false, NULL );
		PlayEffect ( "fx_bombflash", joint );
		Attack ( "bomb", joint, enemy.ent );
	}
}

/*
================
rvMonsterStroggFlyer::CheckActions
================
*/
bool rvMonsterStroggFlyer::CheckActions ( void ) {
	if ( PerformAction ( &actionBombAttack, (checkAction_t)&idAI::CheckAction_RangedAttack, &actionTimerRangedAttack )    ||
	     PerformAction ( &actionBlasterAttack, (checkAction_t)&idAI::CheckAction_RangedAttack, &actionTimerRangedAttack )    ) {
		return true;
	}
	return idAI::CheckActions ( );
}

/*
================
rvMonsterStroggFlyer::Save
================
*/
void rvMonsterStroggFlyer::Save( idSaveGame *savefile ) const {
	actionBombAttack.Save ( savefile ) ;	
	actionBlasterAttack.Save ( savefile );
	
	savefile->WriteVec3 ( velocity );

	savefile->WriteInt ( shotCount );

	savefile->WriteInt ( lastAttackTime );
	savefile->WriteInt ( attackStartTime );
}

/*
================
rvMonsterStroggFlyer::Restore
================
*/
void rvMonsterStroggFlyer::Restore( idRestoreGame *savefile ) {
	actionBombAttack.Restore ( savefile ) ;	
	actionBlasterAttack.Restore ( savefile );
	
	savefile->ReadVec3 ( velocity );

	savefile->ReadInt ( shotCount );

	savefile->ReadInt ( lastAttackTime );
	savefile->ReadInt ( attackStartTime );

	InitSpawnArgsVariables();
}

/*
================
rvMonsterStroggFlyer::GetDebugInfo
================
*/
void rvMonsterStroggFlyer::GetDebugInfo	( debugInfoProc_t proc, void* userData ) {
	// Base class first
	idAI::GetDebugInfo ( proc, userData );
	
	proc ( "idAI", "action_blasterAttack",	aiActionStatusString[actionBlasterAttack.status], userData );
	proc ( "idAI", "action_bombAttack",		aiActionStatusString[actionBombAttack.status], userData );
}

/*
===============================================================================

	States 

===============================================================================
*/

CLASS_STATES_DECLARATION ( rvMonsterStroggFlyer )
	STATE ( "State_WakeUp",					rvMonsterStroggFlyer::State_WakeUp )
	STATE ( "State_ScriptedPlaybackMove",	rvMonsterStroggFlyer::State_ScriptedPlaybackMove )
	STATE ( "State_Killed",					rvMonsterStroggFlyer::State_Killed )

	STATE ( "Torso_BlasterAttack",			rvMonsterStroggFlyer::State_Torso_BlasterAttack )
	STATE ( "Torso_BombAttack",				rvMonsterStroggFlyer::State_Torso_BombAttack )
END_CLASS_STATES

/*
================
rvMonsterStroggFlyer::State_ScriptedPlaybackMove
================
*/
stateResult_t rvMonsterStroggFlyer::State_ScriptedPlaybackMove ( const stateParms_t& parms ) {
	// When the playback finishes cancel any running states
	if ( !mPlayback.IsActive() ) {
 		StopAnimState ( ANIMCHANNEL_TORSO );
 		StopAnimState ( ANIMCHANNEL_LEGS );
		return SRESULT_DONE;
	}
	
	// Keep the enemy status up to date
	if ( !enemy.ent ) {
		CheckForEnemy ( true );
	}
	
	// Perform actions
	UpdateAction ( );
	
	return SRESULT_WAIT;
}

/*
================
rvMonsterStroggFlyer::State_Killed
================
*/
stateResult_t rvMonsterStroggFlyer::State_Killed ( const stateParms_t& parms ) {
	gameLocal.PlayEffect ( spawnArgs, "fx_death", GetPhysics()->GetOrigin(), (-GetPhysics()->GetGravityNormal()).ToMat3() );
	return idAI::State_Killed ( parms );
}

/*
================
rvMonsterStroggFlyer::State_Torso_BlasterAttack
================
*/
stateResult_t rvMonsterStroggFlyer::State_Torso_BlasterAttack ( const stateParms_t& parms ) {	
	enum {
		STAGE_INIT,
		STAGE_BLASTER,
		STAGE_BLASTERWAIT
	};
	switch ( parms.stage ) {
		case STAGE_INIT:
			attackStartTime = gameLocal.time;
			return SRESULT_STAGE ( STAGE_BLASTER );
		
		case STAGE_BLASTER:
			lastAttackTime = gameLocal.time;
			AttackBlaster ( );
			return SRESULT_STAGE ( STAGE_BLASTERWAIT );
		
		case STAGE_BLASTERWAIT:
			if ( !enemy.fl.inFov || gameLocal.time - attackStartTime > blasterAttackDuration ) {
				return SRESULT_DONE;
			}
			if ( gameLocal.time - lastAttackTime > blasterAttackRate ) {
				return SRESULT_STAGE ( STAGE_BLASTER );
			}
			return SRESULT_WAIT;
	}
	return SRESULT_ERROR;
}

/*
================
rvMonsterStroggFlyer::State_Torso_BombAttack
================
*/
stateResult_t rvMonsterStroggFlyer::State_Torso_BombAttack ( const stateParms_t& parms ) {
	enum {
		STAGE_INIT,
		STAGE_BOMB,
		STAGE_BOMBWAIT
	};
	switch ( parms.stage ) {
		case STAGE_INIT:
			attackStartTime = gameLocal.time;
			return SRESULT_STAGE ( STAGE_BOMB );
		
		case STAGE_BOMB:
			lastAttackTime = gameLocal.time;
			AttackBomb ( );
			return SRESULT_STAGE ( STAGE_BOMBWAIT );
		
		case STAGE_BOMBWAIT:
			if ( !enemy.fl.inFov || gameLocal.time - attackStartTime > bombAttackDuration ) {
				return SRESULT_DONE;
			}
			if ( gameLocal.time - lastAttackTime > bombAttackRate ) {
				return SRESULT_STAGE ( STAGE_BOMB );
			}
			return SRESULT_WAIT;
	}
	return SRESULT_ERROR;
}
