//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "npc_vehicledriver.h"
#include "vehicle_apc.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

#define SF_APCDRIVER_NO_ROCKET_ATTACK	0x10000
#define SF_APCDRIVER_NO_GUN_ATTACK		0x20000

#define NPC_APCDRIVER_REMEMBER_TIME		4


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
class CNPC_APCDriver : public CNPC_VehicleDriver
{
	DECLARE_CLASS( CNPC_APCDriver, CNPC_VehicleDriver );
public:
	DECLARE_DATADESC();
	DEFINE_CUSTOM_AI;

	virtual void Spawn( void );
	virtual void Activate( void );
	
	virtual bool FVisible( CBaseEntity *pTarget, int traceMask, CBaseEntity **ppBlocker );
	virtual bool WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions );
	virtual Class_T Classify ( void ) { return CLASS_COMBINE; }
	virtual void PrescheduleThink( );
	virtual Disposition_t IRelationType(CBaseEntity *pTarget);

	// AI
	virtual int RangeAttack1Conditions( float flDot, float flDist );
	virtual int RangeAttack2Conditions( float flDot, float flDist );

private:
	// Are we being carried by a dropship?
	bool IsBeingCarried();

	// Enable, disable firing
	void InputEnableFiring( inputdata_t &inputdata );
	void InputDisableFiring( inputdata_t &inputdata );

	CHandle<CPropAPC>	m_hAPC;
	float m_flTimeLastSeenEnemy;
	bool m_bFiringDisabled;
};


BEGIN_DATADESC( CNPC_APCDriver )

	//DEFINE_FIELD( m_hAPC, FIELD_EHANDLE ),
	DEFINE_FIELD( m_bFiringDisabled, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_flTimeLastSeenEnemy, FIELD_TIME ),

	DEFINE_INPUTFUNC( FIELD_VOID, "EnableFiring", InputEnableFiring ),
	DEFINE_INPUTFUNC( FIELD_VOID, "DisableFiring", InputDisableFiring ),

END_DATADESC()

LINK_ENTITY_TO_CLASS( npc_apcdriver, CNPC_APCDriver );


//------------------------------------------------------------------------------
// Purpose :
//------------------------------------------------------------------------------
void CNPC_APCDriver::Spawn( void )
{
	BaseClass::Spawn();

	m_flTimeLastSeenEnemy = -NPC_APCDRIVER_REMEMBER_TIME;
	CapabilitiesClear();
	CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_RANGE_ATTACK2 );
	m_bFiringDisabled = false;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CNPC_APCDriver::Activate( void )
{
	BaseClass::Activate();

	m_hAPC = dynamic_cast<CPropAPC*>((CBaseEntity*)m_hVehicleEntity);
	if ( !m_hAPC )
	{
		Warning( "npc_apcdriver %s couldn't find his apc named %s.\n", STRING(GetEntityName()), STRING(m_iszVehicleName) );
		UTIL_Remove( this );
		return;
	}
	SetParent( m_hAPC );
	SetAbsOrigin( m_hAPC->WorldSpaceCenter() );
	SetLocalAngles( vec3_angle );

	m_flDistTooFar = m_hAPC->MaxAttackRange();
	SetDistLook( m_hAPC->MaxAttackRange() );
}


//-----------------------------------------------------------------------------
// Enable, disable firing
//-----------------------------------------------------------------------------
void CNPC_APCDriver::InputEnableFiring( inputdata_t &inputdata )
{
	m_bFiringDisabled = false;
}

void CNPC_APCDriver::InputDisableFiring( inputdata_t &inputdata )
{
	m_bFiringDisabled = true;
}

	
//-----------------------------------------------------------------------------
// Purpose: Let's not hate things the APC makes
//-----------------------------------------------------------------------------
Disposition_t CNPC_APCDriver::IRelationType(CBaseEntity *pTarget)
{
	if ( pTarget == m_hAPC || (pTarget->GetOwnerEntity() == m_hAPC) )
		return D_LI;

	return BaseClass::IRelationType(pTarget);
}


//------------------------------------------------------------------------------
// Are we being carried by a dropship?
//------------------------------------------------------------------------------
bool CNPC_APCDriver::IsBeingCarried()
{
	// Inert if we're carried...
	Vector vecVelocity;
	m_hAPC->GetVelocity( &vecVelocity, NULL );
	return ( m_hAPC->GetMoveParent() != NULL ) || (fabs(vecVelocity.z) >= 15);
}



//------------------------------------------------------------------------------
// Is the enemy visible?
//------------------------------------------------------------------------------
bool CNPC_APCDriver::WeaponLOSCondition(const Vector &ownerPos, const Vector &targetPos, bool bSetConditions)
{
	if ( m_hAPC->m_lifeState != LIFE_ALIVE )
		return false;

	if ( IsBeingCarried() || m_bFiringDisabled )
		return false;

	float flTargetDist = ownerPos.DistTo( targetPos );
	if (flTargetDist > m_hAPC->MaxAttackRange())
		return false;

	return true;
}

	
//-----------------------------------------------------------------------------
// Is the enemy visible?
//-----------------------------------------------------------------------------
bool CNPC_APCDriver::FVisible( CBaseEntity *pTarget, int traceMask, CBaseEntity **ppBlocker )
{
	if ( m_hAPC->m_lifeState != LIFE_ALIVE )
		return false;

	if ( IsBeingCarried() || m_bFiringDisabled )
		return false;

	float flTargetDist = GetAbsOrigin().DistTo( pTarget->GetAbsOrigin() );
	if (flTargetDist > m_hAPC->MaxAttackRange())
		return false;

	bool bVisible = m_hAPC->FVisible( pTarget, traceMask, ppBlocker );
	if ( bVisible && (pTarget == GetEnemy()) )
	{
		m_flTimeLastSeenEnemy = gpGlobals->curtime;
	}

	if ( pTarget->IsPlayer() || pTarget->Classify() == CLASS_BULLSEYE )
	{
		if (!bVisible)
		{
			if ( ( gpGlobals->curtime - m_flTimeLastSeenEnemy ) <= NPC_APCDRIVER_REMEMBER_TIME )
				return true;
		}
	}

	return bVisible;
}


//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
int CNPC_APCDriver::RangeAttack1Conditions( float flDot, float flDist )
{
	if ( HasSpawnFlags(SF_APCDRIVER_NO_GUN_ATTACK) )
		return COND_NONE;

	if ( m_hAPC->m_lifeState != LIFE_ALIVE )
		return COND_NONE;

	if ( IsBeingCarried() || m_bFiringDisabled )
		return COND_NONE;

	if ( !HasCondition( COND_SEE_ENEMY ) )
		return COND_NONE;

	if ( !m_hAPC->IsInPrimaryFiringCone() )
		return COND_NONE;

	// Vehicle not ready to fire again yet?
	if ( m_pVehicleInterface->Weapon_PrimaryCanFireAt() > gpGlobals->curtime + 0.1f )
		return COND_NONE;

	float flMinDist, flMaxDist;
	m_pVehicleInterface->Weapon_PrimaryRanges( &flMinDist, &flMaxDist );

	if (flDist < flMinDist)
		return COND_NONE;

	if (flDist > flMaxDist)
		return COND_NONE;

	return COND_CAN_RANGE_ATTACK1;
}

int CNPC_APCDriver::RangeAttack2Conditions( float flDot, float flDist )
{
	if ( HasSpawnFlags(SF_APCDRIVER_NO_ROCKET_ATTACK) )
		return COND_NONE;

	if ( m_hAPC->m_lifeState != LIFE_ALIVE )
		return COND_NONE;

	if ( IsBeingCarried() || m_bFiringDisabled )
		return COND_NONE;

	if ( !HasCondition( COND_SEE_ENEMY ) )
		return COND_NONE;

	// Vehicle not ready to fire again yet?
	if ( m_pVehicleInterface->Weapon_SecondaryCanFireAt() > gpGlobals->curtime + 0.1f )
		return COND_NONE;

	float flMinDist, flMaxDist;
	m_pVehicleInterface->Weapon_SecondaryRanges( &flMinDist, &flMaxDist );

	if (flDist < flMinDist)
		return COND_NONE;

	if (flDist > flMaxDist)
		return COND_NONE;

	return COND_CAN_RANGE_ATTACK2;
}


//-----------------------------------------------------------------------------
// Aim the laser dot!
//-----------------------------------------------------------------------------
void CNPC_APCDriver::PrescheduleThink( )
{
	BaseClass::PrescheduleThink();

	if ( m_hAPC->m_lifeState == LIFE_ALIVE )
	{
		if ( GetEnemy() )
		{
			m_hAPC->AimPrimaryWeapon( GetEnemy()->BodyTarget( GetAbsOrigin(), false ) );
		}
		m_hAPC->AimSecondaryWeaponAt( GetEnemy() );
	}
	else if ( m_hAPC->m_lifeState == LIFE_DEAD )
	{
		UTIL_Remove( this );
	}
}


//-----------------------------------------------------------------------------
//
// Schedules
//
//-----------------------------------------------------------------------------
AI_BEGIN_CUSTOM_NPC( npc_apcdriver, CNPC_APCDriver )
	
AI_END_CUSTOM_NPC()
