//==== Copyright © 1996-2007, Valve Corporation, All rights reserved. =========
//
// Small, fast version of the strider. Goes where striders cannot, such
// as into buildings. Best killed with physics objects and explosives.
//
//=============================================================================

#include "cbase.h"
#include "hl2wars/npc/hlw_npc_hunter.h"
#include "ai_memory.h"
#include "ai_squad.h"
#include "hl2wars/rts_globalresource.h"

LINK_ENTITY_TO_CLASS( hlw_npc_hunter, CHLW_NPC_Hunter );

extern ConVar hunter_stand_still;
extern ConVar hunter_flechette_test;
extern ConVar hunter_charge_test;
extern ConVar hunter_charge_pct;
extern ConVar hunter_flechette_max_concurrent_volleys;
extern ConVar hunter_flechette_volley_start_min_delay;
extern ConVar hunter_flechette_volley_start_max_delay;
extern ConVar hunter_allow_dissolve;
extern ConVar hunter_allow_nav_jump;
extern ConVar hunter_flechette_max_range;
extern ConVar hunter_free_knowledge;

#define HUNTER_FOV_DOT					0.0		// 180 degree field of view

void CHLW_NPC_Hunter::Spawn()
{
	Precache();

	SetModel( "models/hunter.mdl" );
	CAI_BaseActor::Spawn();

	//m_debugOverlays |= OVERLAY_NPC_ROUTE_BIT | OVERLAY_BBOX_BIT | OVERLAY_PIVOT_BIT;

	SetHullType( HULL_MEDIUM_TALL );
	SetHullSizeNormal();
	SetDefaultEyeOffset();
	
	SetNavType( NAV_GROUND );
	m_flGroundSpeed	= 500;
	m_NPCState = NPC_STATE_NONE;

	SetBloodColor( DONT_BLEED );

	m_flFieldOfView = HUNTER_FOV_DOT;

	SetSolid( SOLID_BBOX );
	AddSolidFlags( FSOLID_NOT_STANDABLE );
	SetMoveType( MOVETYPE_STEP );

	SetupGlobalModelData();
	
	CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_SQUAD | bits_CAP_ANIMATEDFACE );
	CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_RANGE_ATTACK2 | bits_CAP_INNATE_MELEE_ATTACK1 );
	CapabilitiesAdd( bits_CAP_SKIP_NAV_GROUND_CHECK );
	CapabilitiesAdd( bits_CAP_NO_HIT_PLAYER | bits_CAP_NO_HIT_SQUADMATES | bits_CAP_FRIENDLY_DMG_IMMUNE );

	if ( !hunter_allow_dissolve.GetBool() )
	{
		AddEFlags( EFL_NO_DISSOLVE );
	}

	if( hunter_allow_nav_jump.GetBool() )
	{
		CapabilitiesAdd( bits_CAP_MOVE_JUMP );
	}

	NPCInit();

	m_bEnableSquadShootDelay = true;

	m_flDistTooFar = hunter_flechette_max_range.GetFloat();

	// Discard time must be greater than free knowledge duration. Make it double.
	float freeKnowledge = hunter_free_knowledge.GetFloat();
	if ( freeKnowledge < GetEnemies()->GetEnemyDiscardTime() )
	{
		GetEnemies()->SetEnemyDiscardTime( max( freeKnowledge + 0.1, AI_DEF_ENEMY_DISCARD_TIME ) );
	}
	GetEnemies()->SetFreeKnowledgeDuration( freeKnowledge );

	// Find out what strider we should follow, if any.
	if ( m_iszFollowTarget != NULL_STRING )
	{
		m_BeginFollowDelay.Set( .1 ); // Allow time for strider to spawn
	}

	//if ( !m_pGunFiringSound )
	//{
	//	CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
	//	CPASAttenuationFilter filter( this );
	//
	//	m_pGunFiringSound = controller.SoundCreate( filter, entindex(), "NPC_Hunter.FlechetteShootLoop" );
	//	controller.Play( m_pGunFiringSound, 0.0, 100 );
	//}
}

int	CHLW_NPC_Hunter::SelectSchedule()
{
	if ( hunter_stand_still.GetBool() )
	{
		m_bPlanted = false;
		return SCHED_IDLE_STAND;
	}
	
	if ( HasCondition( COND_HUNTER_FORCED_DODGE ) )
		return SCHED_HUNTER_DODGE;

	return CAI_BaseActor::SelectSchedule();
}

extern bool IsStriderBuster( CBaseEntity *pEntity );
extern int g_interactionHunterFoundEnemy;
int CHLW_NPC_Hunter::SelectScheduleBehaviorNormalCombat()
{
	// If we're here with no enemy, alert
	CBaseEntity *pEnemy = GetEnemy();
	if ( pEnemy == NULL )
			return SCHED_ALERT_STAND;

	if ( hunter_flechette_test.GetBool() )
	{
		if ( HasCondition( COND_CAN_RANGE_ATTACK2 ) )
		{
			return SCHED_HUNTER_RANGE_ATTACK2;
		}
		return SCHED_COMBAT_FACE;
	}

	bool bStriderBuster = IsStriderBuster( pEnemy );
	if ( bStriderBuster )
	{
		if ( gpGlobals->curtime - CAI_HunterEscortBehavior::gm_flLastDefendSound > 10.0 )
		{
			EmitSound( "NPC_Hunter.DefendStrider" );
			CAI_HunterEscortBehavior::gm_flLastDefendSound = gpGlobals->curtime;
		}

		if ( HasCondition( COND_CAN_RANGE_ATTACK2 ) || HasCondition( COND_NOT_FACING_ATTACK ) )
		{
			return SCHED_HUNTER_RANGE_ATTACK2;
		}
		return SCHED_ESTABLISH_LINE_OF_FIRE;
	}

	// Certain behaviors, like flanking and melee attacks, only make sense on visible,
	// corporeal enemies (NOT bullseyes).
	bool bIsCorporealEnemy = IsCorporealEnemy( pEnemy );

	// Take a quick swipe at our enemy if able to do so.
	if ( bIsCorporealEnemy && HasCondition( COND_CAN_MELEE_ATTACK1 ) )
	{
		return SCHED_HUNTER_MELEE_ATTACK1;
	}

	// React to newly acquired enemies.
	if ( bIsCorporealEnemy && HasCondition( COND_NEW_ENEMY ) )
	{
		AI_EnemyInfo_t *pEnemyInfo = GetEnemies()->Find( pEnemy );

		if ( GetSquad() && pEnemyInfo && ( pEnemyInfo->timeFirstSeen == pEnemyInfo->timeAtFirstHand ) )
		{
			GetSquad()->BroadcastInteraction( g_interactionHunterFoundEnemy, NULL, this );

			// First contact for my squad.
			return SCHED_HUNTER_FOUND_ENEMY;
		}
	}

	if ( HasCondition( COND_HUNTER_SQUADMATE_FOUND_ENEMY ) )
	{
		// A squadmate found an enemy. Respond to their call.
		return SCHED_HUNTER_FOUND_ENEMY_ACK;
	}

	if ( HasCondition( COND_CAN_RANGE_ATTACK2 ) )
	{
		if ( bStriderBuster || CountRangedAttackers() < hunter_flechette_max_concurrent_volleys.GetInt() )
		{
			DelayRangedAttackers( hunter_flechette_volley_start_min_delay.GetFloat(), hunter_flechette_volley_start_max_delay.GetFloat(), true );
			return SCHED_HUNTER_RANGE_ATTACK2;
		}
	}

	if ( pEnemy->GetGroundEntity() == this )
	{
		return SCHED_HUNTER_MELEE_ATTACK1;
	}

	if ( HasCondition( COND_TOO_CLOSE_TO_ATTACK ) )
	{
		return SCHED_MOVE_AWAY_FROM_ENEMY;
	}

	if ( HasCondition( COND_HEAVY_DAMAGE ) && ( gpGlobals->curtime > m_flNextSideStepTime ) )
	{
		m_flNextSideStepTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 3.0f );
		return SCHED_HUNTER_SIDESTEP;
	}

	return BaseClass::SelectScheduleBehaviorNormalCombat();
}

int CHLW_NPC_Hunter::SelectScheduleBehaviorAggressiveCombat()
{
	return BaseClass::SelectCombatSchedule();
}

//-----------------------------------------------------------------------------
// Purpose: Our unit type depends on our weapons and flags
//-----------------------------------------------------------------------------
void CHLW_NPC_Hunter::InitUnitIndex()
{
	const char *m_sUnitType = "UNIT_HUNTER";
	m_iUnitIndex = g_pRTSResource->GetUnitIndex( m_sUnitType );
}