//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Rebel npc
//
//=============================================================================//

#include "cbase.h"
#include "hl2wars/npc/hlw_npc_rebel.h"
#include "hl2wars/util_hl2wars.h"
#include "hl2wars/rts_globalresource.h"
#include "hl2wars/combine_mine_rts.h"
#include "ai_navigator.h"
#include "ai_tacticalservices.h"
#include "ai_hint.h"
#include "basegrenade_shared.h"
#include "grenade_frag.h"
#include "hl2wars/rts_point_order.h"
#include "weapon_rpg.h"

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

#define REBEL_GRENADE_THROW_SPEED 650
#define REBEL_GRENADE_TIMER		3.5
#define REBEL_GRENADE_FLUSH_TIME	3.0		// Don't try to flush an enemy who has been out of sight for longer than this.
#define REBEL_GRENADE_FLUSH_DIST	256.0	// Don't try to flush an enemy who has moved farther than this distance from the last place I saw him.

#define REBEL_EYE_STANDING_POSITION	Vector( 0, 0, 66 )
#define REBEL_GUN_STANDING_POSITION	Vector( 0, 0, 57 )
#define REBEL_EYE_CROUCHING_POSITION	Vector( 0, 0, 40 )
#define REBEL_GUN_CROUCHING_POSITION	Vector( 0, 0, 36 )
#define REBEL_SHOTGUN_STANDING_POSITION	Vector( 0, 0, 36 )
#define REBEL_SHOTGUN_CROUCHING_POSITION	Vector( 0, 0, 36 )
#define REBEL_MIN_CROUCH_DISTANCE		256.0

const float HEAL_TARGET_RANGE = 120; // 10 feet

extern ConVar	sk_citizen_heal_ally;
extern ConVar	sk_citizen_heal_ally_delay;

//---------------------------------------------------------
// Rebel activities
//---------------------------------------------------------
int ACT_PLACE_MINE;
int ACT_PLACE_SENTRY;
extern int ACT_CIT_HEAL;

// Animation events
int AE_REBEL_PLACE_MINE;
int AE_REBEL_PLACE_SENTRY;
extern int AE_CITIZEN_HEAL;

static const char *g_ppszRandomHeadsE[] = 
{
	"male_02.mdl",
	"female_01.mdl",
	"female_02.mdl",
	"male_04.mdl",
	"male_05.mdl",
	"female_04.mdl",
	"male_06.mdl",
	"female_06.mdl",
	"male_08.mdl",
	"male_09.mdl",
};

//---------------------------------------------------------

LINK_ENTITY_TO_CLASS( hlw_npc_rebel, CHLW_NPC_Rebel );

//---------------------------------------------------------

BEGIN_DATADESC( CHLW_NPC_Rebel )
	DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ),
	DEFINE_FIELD( m_flNextGrenadeCheck, FIELD_TIME ),
END_DATADESC()

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CHLW_NPC_Rebel::CreateBehaviors()
{
	AddBehavior( &m_PlacableTankBehavior );
	
	return BaseClass::CreateBehaviors();
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHLW_NPC_Rebel::Spawn()
{
	CNPC_PlayerCompanion::Spawn();

	//SelectModel();

	//Precache();

	SetModel( STRING( GetModelName() ) );

	SetHullType(HULL_HUMAN);
	SetHullSizeNormal();

	SetSolid( SOLID_BBOX );
	AddSolidFlags( FSOLID_NOT_STANDABLE );
	SetBloodColor( BLOOD_COLOR_RED );
	m_flFieldOfView		= 0.02;
	m_NPCState		= NPC_STATE_NONE;

	CapabilitiesClear();
	CapabilitiesAdd( bits_CAP_SQUAD );

	if ( !HasSpawnFlags( SF_NPC_START_EFFICIENT ) )
	{
		CapabilitiesAdd( bits_CAP_ANIMATEDFACE | bits_CAP_TURN_HEAD );
		CapabilitiesAdd( bits_CAP_USE_WEAPONS | bits_CAP_AIM_GUN | bits_CAP_MOVE_SHOOT );
		CapabilitiesAdd( bits_CAP_DUCK | bits_CAP_DOORS_GROUP );
		CapabilitiesAdd( bits_CAP_USE_SHOT_REGULATOR );
	}
	CapabilitiesAdd( bits_CAP_NO_HIT_PLAYER | bits_CAP_NO_HIT_SQUADMATES | bits_CAP_FRIENDLY_DMG_IMMUNE );
	CapabilitiesAdd( bits_CAP_MOVE_GROUND );
	SetMoveType( MOVETYPE_STEP );

	m_HackedGunPos = Vector( 0, 0, 55 );
	
	SetAimTarget(NULL);
	m_bReadinessCapable = IsReadinessCapable();
	SetReadinessValue( 0.0f );
	SetReadinessSensitivity( random->RandomFloat( 0.7, 1.3 ) );
	m_flReadinessLockedUntil = 0.0f;

	m_AnnounceAttackTimer.Set( 10, 30 );

	// end spawn first

#ifdef _XBOX
	// Always fade the corpse
	AddSpawnFlags( SF_NPC_FADE_CORPSE );
#endif // _XBOX

	if ( IsAmmoResupplier() )
		m_nSkin = 2;
	
	m_bRPGAvoidPlayer = false;

	m_bShouldPatrol = false;
	
	// Are we on a train? Used in trainstation to have NPCs on trains.
	if ( GetMoveParent() && FClassnameIs( GetMoveParent(), "func_tracktrain" ) )
	{
		CapabilitiesRemove( bits_CAP_MOVE_GROUND );
		SetMoveType( MOVETYPE_NONE );
		if ( NameMatches("citizen_train_2") )
		{
			SetSequenceByName( "d1_t01_TrainRide_Sit_Idle" );
			SetIdealActivity( ACT_DO_NOT_DISTURB );
		}
		else
		{
			SetSequenceByName( "d1_t01_TrainRide_Stand" );
			SetIdealActivity( ACT_DO_NOT_DISTURB );
		}
	}

	m_flStopManhackFlinch = -1;

	m_iszIdleExpression = MAKE_STRING("scenes/expressions/citizenidle.vcd");
	m_iszAlertExpression = MAKE_STRING("scenes/expressions/citizenalert_loop.vcd");
	m_iszCombatExpression = MAKE_STRING("scenes/expressions/citizencombat_loop.vcd");

	m_iszOriginalSquad = m_SquadName;

	m_flNextHealthSearchTime = gpGlobals->curtime;

	CWeaponRPG *pRPG = dynamic_cast<CWeaponRPG*>(GetActiveWeapon());
	if ( pRPG )
	{
		CapabilitiesRemove( bits_CAP_USE_SHOT_REGULATOR );
		pRPG->StopGuiding();
	}

	m_flTimePlayerStare = FLT_MAX;

	AddEFlags( EFL_NO_DISSOLVE | EFL_NO_MEGAPHYSCANNON_RAGDOLL | EFL_NO_PHYSCANNON_INTERACTION );

	NPCInit();

	//SetUse( &CNPC_Citizen::CommanderUse );
	//Assert( !ShouldAutosquad() || !IsInPlayerSquad() );

	m_bWasInPlayerSquad = IsInPlayerSquad();

	// Use render bounds instead of human hull for guys sitting in chairs, etc.
	m_ActBusyBehavior.SetUseRenderBounds( HasSpawnFlags( SF_CITIZEN_USE_RENDER_BOUNDS ) );
}

void CHLW_NPC_Rebel::Precache()
{
	BaseClass::Precache();
}

void CHLW_NPC_Rebel::SelectModel()
{
	if( IsEngineer() )
	{
		const char *pszModelName = NULL;

		Assert( m_iHead == -1 );

		m_iHead = random->RandomInt( 0, ARRAYSIZE(g_ppszRandomHeadsE) - 1 );
		pszModelName = g_ppszRandomHeadsE[m_iHead];

		SetModelName(NULL_STRING);

		Assert( pszModelName || GetModelName() != NULL_STRING );

		char *type;
		type = "e";

		if ( m_Type != CT_UNIQUE )
		{
			SetModelName( AllocPooledString( CFmtStr( "models/Humans/%s/%s", (const char *)(CFmtStr("Group03%s", type )), pszModelName ) ) );
		}
		return;
	}

	BaseClass::SelectModel();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHLW_NPC_Rebel::PrescheduleThink( void )
{
	BaseClass::PrescheduleThink();

	// FIXME: Rebels crouching is weird. Very slow with shooting. Something with the animations?
	/*if( GetCurrentOrder() )
	{
		// We are in cover and need to crouch if we can shoot the enemy or are idling.
		CAI_Hint *pHint =  GetCurrentOrder()->GetHint();
		HidingSpot *pSpot = GetCurrentOrder()->GetHidingSpot();
		if( (pHint && pHint->HintType() == HINT_TACTICAL_COVER_LOW) ||
			pSpot)
		{
			if( GetEnemy() && !CouldShootIfCrouching( GetEnemy() ) )
			{
				Stand();
			}
			else 
			{
				Crouch();
			}
		}
		else
		{
			Stand();
		}
	}*/

	if ( ShouldRegenerateHealthRTS() )
	{
		float flDelta = gpGlobals->curtime - m_flTimeLastRegenRTS;
		float flHealthPerSecond = 1.0f / 1.0f;

		float flHealthRegen = flHealthPerSecond * flDelta;

		if ( g_pGameRules->IsSkillLevel(SKILL_HARD) )
			flHealthRegen *= 0.5f;
		else if ( g_pGameRules->IsSkillLevel(SKILL_EASY) )
			flHealthRegen *= 1.5f;

		m_flTimeLastRegenRTS = gpGlobals->curtime;

		TakeHealth( flHealthRegen, DMG_GENERIC );
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CHLW_NPC_Rebel::SelectSchedule()
{
	// First, check abilities
	if( GetCurrentOrder() )
	{
		if( GetCurrentOrder()->GetOrderType() == ORDER_DO_ABILITY && GetCurrentOrder()->GetAbility() != ABILITY_NONE )
		{
			if( !HasCondition(COND_CAN_DO_ABILITY) ) 
			{
				if( HasCondition(COND_TOO_FAR_FOR_ABILITY) )
				{
					return SCHED_MOVE_TO_ABILITY_RANGE;
				}
				else if( HasCondition(COND_TOO_CLOSE_FOR_ABILITY) )
				{
					return SCHED_STANDOFF_ABILITY_RANGE;
				}
			}

			m_iActiveAbilityButton = GetCurrentOrder()->GetAbilityButton();
			
			switch( GetCurrentOrder()->GetAbility() )
			{
			case ABILITY_TURRET:
				{
					return SCHED_REBEL_PLACE_TURRET;
				}
			case ABILITY_GRENADE:
				{
					if ( m_flNextGrenadeCheck < gpGlobals->curtime )
					{
						Vector vecTarget = GetCurrentOrder()->WorldSpaceCenter();

						// If we can, throw a grenade at the target. 
						// Ignore grenade count / distance / etc
						if ( CheckCanThrowGrenade( vecTarget ) )
						{
							//m_hForcedGrenadeTarget = NULL;
							return SCHED_REBEL_FORCED_GRENADE_THROW;
						}
					}

					// Can't throw at the target, so lets try moving to somewhere where I can see it
					if ( !FVisible( GetCurrentOrder() ) )
					{
						return SCHED_REBEL_MOVE_TO_FORCED_GREN_LOS;
					}					
				}
			case ABILITY_HEAL:
				{
					return SCHED_CITIZEN_HEAL;
				}
			case ABILITY_COMB_MINE:
				{
					return SCHED_REBEL_PLACE_MINE;
				}
			case ABILITY_BUILDING:
				{
					return SCHED_REBEL_PLACE_BUILDING;
				}
			default:
				break;
			}	
		}
	}

	// auto heal
	/*if( IsMedic() ) 
	{
		// this seems very bad to hardcode the ability button number
		if( (!GetCurrentOrder() || GetCurrentOrder()->GetOrderType() == ORDER_IDLE) && CanDoAbility(9) ) 
		{
			CBaseEntity *pEnt;
			CAI_BaseNPC *pNPC;
			Vector origin = GetCurrentOrder() ? GetCurrentOrder()->GetAbsOrigin() : GetAbsOrigin();
   			CBaseEntity *pList[512]; 
			int count = 0;

			count = UTIL_EntitiesInSphere( pList, 512, origin, 256.0f, 0 );
			for ( int i = 0; i < count; i++ ) 
			{ 
				pEnt = dynamic_cast<CBaseEntity *>(pList[ i ]); 
				if( !pEnt || !pEnt->IsNPC() )
					continue;

				pNPC = pEnt->MyNPCPointer();
				if( !pNPC || pNPC->GetTeamRTSID() != GetTeamRTSID() )
					continue;

				if( pNPC->GetHealth()/(float)pNPC->GetMaxHealth() > 0.9f )
					continue;

				if( GetCurrentOrder() )
					GetCurrentOrder()->SetTarget(pNPC);
				else
					SetTarget(pNPC);

				m_iActiveAbilityButton = 9;

				return SCHED_CITIZEN_HEAL;
			}             
		}
	}*/

	if( BehaviorSelectSchedule() )
	{
		return CAI_BaseActor::SelectSchedule();
	}

	// other stuff
	CWeaponRPG *pRPG = dynamic_cast<CWeaponRPG*>(GetActiveWeapon());
	if ( pRPG && pRPG->IsGuiding() )
	{
		DevMsg( "Citizen in select schedule but RPG is guiding?\n");
		pRPG->StopGuiding();
	}
	
	return CAI_BaseActor::SelectSchedule();
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHLW_NPC_Rebel::StartTask( const Task_t *pTask )
{
	switch ( pTask->iTask )
	{
	case TASK_REBEL_FACE_TOSS_DIR:
		break;
	case TASK_REBEL_GET_PATH_TO_FORCED_GREN_LOS:
		{
			if ( !GetCurrentOrder() )
			{
				TaskFail(FAIL_NO_ENEMY);
				return;
			}
		
			float flMaxRange = 2000;
			float flMinRange = 0;
			
			Vector vecEnemy = GetCurrentOrder()->GetAbsOrigin();
			Vector vecEnemyEye = vecEnemy + GetCurrentOrder()->GetViewOffset();

			Vector posLos;
			bool found = false;

			if ( GetTacticalServices()->FindLateralLos( vecEnemyEye, &posLos ) )
			{
				float dist = ( posLos - vecEnemyEye ).Length();
				if ( dist < flMaxRange && dist > flMinRange )
					found = true;
			}

			if ( !found && GetTacticalServices()->FindLos( vecEnemy, vecEnemyEye, flMinRange, flMaxRange, 1.0, &posLos ) )
			{
				found = true;
			}

			if ( !found )
			{
				TaskFail( FAIL_NO_SHOOT );
			}
			else
			{
				// else drop into run task to offer an interrupt
				m_vInterruptSavePosition = posLos;
			}
		}
		break;
	case TASK_REBEL_THROW_GRENADE:
		{
			if( !AbilityTakeResources() ) 
				break;

			// can we do this ability? ( ain't it recharging?)
			if( !CanDoAbility( m_iActiveAbilityButton ) )
			{
				TaskFail(NO_TASK_FAILURE);
				return;
			}

			Vector vecSpin;
			vecSpin.x = random->RandomFloat( -1000.0, 1000.0 );
			vecSpin.y = random->RandomFloat( -1000.0, 1000.0 );
			vecSpin.z = random->RandomFloat( -1000.0, 1000.0 );

			Vector vecStart;
			GetAttachment( "anim_attachment_RH", vecStart );

			Fraggrenade_Create( vecStart, vec3_angle, m_vecTossVelocity, vecSpin, this, REBEL_GRENADE_TIMER, true );

			AbilityCompleted();
		}
		break;
	case TASK_REBEL_PLACE_BUILDING:
		{
			FileUnitInfo_t *pFUI = GetFileUnitInfoFromHandle( LookupUnitInfoSlot( STRING(GetUnitType() ) ) );
			if( !pFUI )
			{
				TaskFail(NO_TASK_FAILURE);
				break;
			}

			if( !AbilityTakeResources() )
			{
				TaskFail(NO_TASK_FAILURE);
				break;
			}

			// can we do this ability? ( ain't it recharging?)
			if( !CanDoAbility( m_iActiveAbilityButton ) )
			{
				TaskFail(NO_TASK_FAILURE);
				return;
			}

			CBaseBuildingModel *pBuilding = dynamic_cast<CBaseBuildingModel *>(GetCurrentOrder()->GetTarget());
			if( !pBuilding )
			{
				AbilityFailed();
				TaskFail(NO_TASK_FAILURE);
				break;
			}

			pBuilding->SetConstructionState(BS_UNDERCONSTRUCTION);

			AbilityCompleted();

			TaskComplete();
		}
		break;

	default:
		BaseClass::StartTask( pTask );
		break;
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHLW_NPC_Rebel::RunTask( const Task_t *pTask )
{
	switch ( pTask->iTask )
	{
	case TASK_REBEL_FACE_TOSS_DIR:
		{
			// project a point along the toss vector and turn to face that point.
			GetMotor()->SetIdealYawToTargetAndUpdate( GetLocalOrigin() + m_vecTossVelocity * 64, AI_KEEP_YAW_SPEED );

			if ( FacingIdeal() )
			{
				TaskComplete( true );
			}
			break;
		}

	case TASK_REBEL_GET_PATH_TO_FORCED_GREN_LOS:
		{
			if ( !GetCurrentOrder() )
			{
				TaskFail(FAIL_NO_ENEMY);
				return;
			}

			if ( GetTaskInterrupt() > 0 )
			{
				ClearTaskInterrupt();

				Vector vecEnemy = GetCurrentOrder()->GetAbsOrigin();
				AI_NavGoal_t goal( m_vInterruptSavePosition, ACT_RUN, AIN_HULL_TOLERANCE );

				GetNavigator()->SetGoal( goal, AIN_CLEAR_TARGET );
				GetNavigator()->SetArrivalDirection( vecEnemy - goal.dest );
			}
			else
			{
				TaskInterrupt();
			}
		}
		break;
	case TASK_REBEL_THROW_GRENADE:
		{
		}
		break;
	case TASK_CIT_HEAL:
		{
			if( GetCurrentOrder() )
				SetTarget( GetCurrentOrder()->GetTarget() );

			SetIdealActivity( (Activity)ACT_CIT_HEAL );

			if( !AbilityTakeResources() )
				break;

			if( !AbilityHeal() ) 
			{
				AbilityFailed();
				break;
			}

			EmitSound( "HealthKit.Touch" );
			AbilityCompleted();

			TaskComplete();
		}
		break;
	default:
		BaseClass::RunTask( pTask );
		break;
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CHLW_NPC_Rebel::AbilityHeal()
{
	CBaseEntity *pTarget = GetTarget();
	if( !pTarget )
		return false;

	Vector target = pTarget->GetAbsOrigin() - GetAbsOrigin();
	if ( target.Length() > HEAL_TARGET_RANGE * 2 )
		return false;

	// Don't heal a player that's staring at you until a few seconds have passed.
	//m_flTimeNextHealStare = gpGlobals->curtime + sk_citizen_stare_heal_time.GetFloat();

	if ( IsMedic() )
	{
		float timeFullHeal;
		float timeRecharge;
		float maximumHealAmount;

		timeFullHeal 		= m_flAllyHealTime;
		timeRecharge 		= sk_citizen_heal_ally_delay.GetFloat();
		maximumHealAmount 	= sk_citizen_heal_ally.GetFloat();
		m_flAllyHealTime 	= gpGlobals->curtime + timeRecharge;
		
		float healAmt = ( maximumHealAmount * ( 1.0 - ( timeFullHeal - gpGlobals->curtime ) / timeRecharge ) );
		
		if ( healAmt > maximumHealAmount )
			healAmt = maximumHealAmount;
		else
			healAmt = RoundFloatToInt( healAmt );
		
		if ( healAmt > 0 )
		{
			pTarget->TakeHealth( healAmt, DMG_GENERIC );
			pTarget->RemoveAllDecals();

			return true;
		}

		return false;
	}

	return false;
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
#define PC_LARGER_BURST_RANGE	(12.0f * 10.0f) // If an enemy is this close, player companions fire larger continuous bursts.
void CHLW_NPC_Rebel::OnUpdateShotRegulator()
{
	CAI_BaseNPC::OnUpdateShotRegulator();
	/*BaseClass::OnUpdateShotRegulator();

	if( GetEnemy() && HasCondition(COND_CAN_RANGE_ATTACK1) )
	{
		if( GetAbsOrigin().DistTo( GetEnemy()->GetAbsOrigin() ) <= PC_LARGER_BURST_RANGE )
		{
			if( hl2_episodic.GetBool() )
			{
				// Longer burst
				int longBurst = random->RandomInt( 10, 15 );
				GetShotRegulator()->SetBurstShotsRemaining( longBurst );
				GetShotRegulator()->SetRestInterval( 0.1, 0.2 );
			}
			else
			{
				// Longer burst
				GetShotRegulator()->SetBurstShotsRemaining( GetShotRegulator()->GetBurstShotsRemaining() * 2 );

				// Shorter Rest interval
				float flMinInterval, flMaxInterval;
				GetShotRegulator()->GetRestInterval( &flMinInterval, &flMaxInterval );
				GetShotRegulator()->SetRestInterval( flMinInterval * 0.6f, flMaxInterval * 0.6f );
			}
		}
	}*/
}

//-----------------------------------------------------------------------------
// Purpose: Override base class activiites
//-----------------------------------------------------------------------------
Activity CHLW_NPC_Rebel::NPC_TranslateActivity( Activity activity )
{
	if( GetCurrentOrder() )
	{
		if( !GetEnemy() )
		{
			CAI_Hint *pHint =  GetCurrentOrder()->GetHint();
			if( pHint && pHint->HintType() == HINT_TACTICAL_COVER_LOW )
			{

				switch( pHint->HintType() )
				{
				case HINT_TACTICAL_COVER_LOW:
				case HINT_TACTICAL_COVER_MED:
					{
						if( activity == ACT_IDLE )
							return ACT_COVER_LOW;
						if( activity == ACT_WALK )
							return ACT_COVER_LOW;
					}
				default:
					break;
				}
			}

			HidingSpot *pSpot = GetCurrentOrder()->GetHidingSpot();
			if( pSpot )
			{
				if( activity == ACT_IDLE )
					return ACT_COVER_LOW;
				if( activity == ACT_WALK )
					return ACT_COVER_LOW;
			}
		}
	}

	if (activity == ACT_RANGE_ATTACK2)
	{
			return ( Activity )ACT_RANGE_ATTACK_THROW;
	}

	return BaseClass::NPC_TranslateActivity( activity );
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void CHLW_NPC_Rebel::HandleAnimEvent( animevent_t *pEvent )
{
	if ( pEvent->event == AE_REBEL_PLACE_MINE )
	{
		CBounceBombRTS *pMine = (CBounceBombRTS *)CreateEntityByName( "combine_mine_rts" );
		if( !pMine )
			return;

		// setup team, origin and angles
		Vector vecForward; 
		QAngle angle;
		angle[0] = angle[2] = 0;
		if( GetCurrentOrder() && GetCurrentOrder()->GetArrivalDirectionSetted() )
			angle[YAW] =  GetCurrentOrder()->GetArrivalDirection()[YAW];
		else
			angle[YAW] = GetAbsAngles()[YAW];

		Vector vecOrigin = GetCurrentOrder()->GetAbsOrigin() + Vector(0,0,6); 
	
		pMine->SetTeamRTSID( GetTeamRTSID() );
		pMine->SetAbsOrigin( vecOrigin );
		pMine->SetAbsAngles( angle );

		// apply keyvalues
		FileUnitInfo_t *pFUI = GetFileUnitInfoFromHandle( LookupUnitInfoSlot( STRING(GetUnitType() ) ) );
		if( !pFUI )
			return;

		FileAbilityInfo_t *pFAI = GetFileAbilityInfoFromHandle( pFUI->m_iAbilities[m_iActiveAbilityButton-1] );
		if( !pFAI )
			return;

		for ( KeyValues *sub = pFAI->m_pAbilityKeyValues->GetFirstSubKey(); sub != NULL ; sub = sub->GetNextKey() )
		{
			pMine->KeyValue( sub->GetName(), sub->GetString() );
		}

		// finish 
		if( !AbilityTakeResources() ) 
		{
			UTIL_RemoveImmediate( pMine );
			return;
		}
		
		DispatchSpawn(pMine);
		pMine->Activate();

		AbilityCompleted();
				
		return;
	}
	else if( pEvent->event == AE_REBEL_PLACE_SENTRY )
	{
		CBaseEntity *pTarget = CreateEntityByName( "hlw_npc_turret_floor" );
		CAI_BaseNPC *pTurret = pTarget->MyNPCPointer();
		if( !pTurret )
			return;

		// setup team, origin and angles
		Vector vecForward; 
		QAngle angle;
		angle[0] = angle[2] = 0;
		if( GetCurrentOrder() && GetCurrentOrder()->GetArrivalDirectionSetted() )
			angle[YAW] =  GetCurrentOrder()->GetArrivalDirection()[YAW];
		else
			angle[YAW] = GetAbsAngles()[YAW];

		Vector vecOrigin = GetCurrentOrder()->GetAbsOrigin() + Vector(0,0,6); 
	
		pTurret->SetTeamRTSID( GetTeamRTSID() );			
		pTurret->SetAbsOrigin( vecOrigin );
		pTurret->SetAbsAngles( angle );

		// apply keyvalues
		FileUnitInfo_t *pFUI = GetFileUnitInfoFromHandle( LookupUnitInfoSlot( STRING(GetUnitType() ) ) );
		if( !pFUI )
			return;

		FileAbilityInfo_t *pFAI = GetFileAbilityInfoFromHandle( pFUI->m_iAbilities[m_iActiveAbilityButton-1] );
		if( !pFAI )
			return;

		for ( KeyValues *sub = pFAI->m_pAbilityKeyValues->GetFirstSubKey(); sub != NULL ; sub = sub->GetNextKey() )
		{
			pTurret->KeyValue( sub->GetName(), sub->GetString() );
		}

		// finish
		if( !AbilityTakeResources() ) 
		{
			UTIL_RemoveImmediate( pTurret );
			return;
		}

		UTIL_DropToFloor( pTurret, MASK_NPCSOLID );
		pTurret->SetAbsOrigin(pTurret->GetAbsOrigin() + Vector(0, 0, 4.0f ) );
		DispatchSpawn(pTurret);
		pTurret->Activate();

		AbilityCompleted();

		return;
	}

	if( pEvent->event == AE_CITIZEN_HEAL )	// avoid double heal
		return;

	switch( pEvent->event )
	{
	case 3005:	// event throw grenade
		{
			if( !AbilityTakeResources() ) 
				break;

			Vector vecSpin;
			vecSpin.x = random->RandomFloat( -1000.0, 1000.0 );
			vecSpin.y = random->RandomFloat( -1000.0, 1000.0 );
			vecSpin.z = random->RandomFloat( -1000.0, 1000.0 );

			Vector vecStart;
			GetAttachment( "anim_attachment_RH", vecStart );
			Fraggrenade_Create( vecStart, vec3_angle, m_vecTossVelocity, vecSpin, this, REBEL_GRENADE_TIMER, true );

			AbilityCompleted();
			break;
		}
	default:
		BaseClass::HandleAnimEvent( pEvent );
		break;
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int	CHLW_NPC_Rebel::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
	CTakeDamageInfo subInfo = info;
	if( GetCurrentOrder() )
	{
		//if( IsCrouching() )
		//{	
			// reward players for making there units search cover by reducing the damage they receive.
			if( (GetCurrentOrder()->GetHint() && GetCurrentOrder()->GetHint()->GetAbsOrigin().DistTo(GetAbsOrigin()) < 96.0f ) || 
				(GetCurrentOrder()->GetHidingSpot() && GetCurrentOrder()->GetHidingSpot()->GetPosition().DistTo(GetAbsOrigin()) < 96.0f ) )
				subInfo.ScaleDamage(0.6f);
		//}
	}

	return BaseClass::OnTakeDamage_Alive(subInfo);
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CHLW_NPC_Rebel::FValidateHintType( CAI_Hint *pHint )
{
	switch( pHint->HintType() )
	{
	case HINT_WORLD_VISUALLY_INTERESTING:
		return true;
		break;
	case HINT_TACTICAL_COVER_MED:
		return true;
		break;
	case HINT_TACTICAL_COVER_LOW:
		return true;
		break;
	default:
		break;
	}

	return BaseClass::FValidateHintType( pHint );
}

//-----------------------------------------------------------------------------
// Purpose: Returns true if the combine can throw a grenade at the specified target point
// Input  : &vecTarget - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CHLW_NPC_Rebel::CheckCanThrowGrenade( const Vector &vecTarget )
{
	//NDebugOverlay::Line( EyePosition(), vecTarget, 0, 255, 0, false, 5 );

	// ---------------------------------------------------------------------
	// Check that throw is legal and clear
	// ---------------------------------------------------------------------
	// FIXME: this is only valid for hand grenades, not RPG's
	Vector vecToss;
	Vector vecMins = -Vector(4,4,4);
	Vector vecMaxs = Vector(4,4,4);
	if( CBaseCombatCharacter::FInViewCone( vecTarget ) && CBaseEntity::FVisible( vecTarget ) )
	{
		vecToss = VecCheckThrow( this, EyePosition(), vecTarget, REBEL_GRENADE_THROW_SPEED, 1.0, &vecMins, &vecMaxs );
	}
	else
	{
		// Have to try a high toss. Do I have enough room?
		trace_t tr;
		AI_TraceLine( EyePosition(), EyePosition() + Vector( 0, 0, 64 ), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
		if( tr.fraction != 1.0 )
		{
			return false;
		}

		vecToss = VecCheckToss( this, EyePosition(), vecTarget, -1, 1.0, true, &vecMins, &vecMaxs );
	}

	if ( vecToss != vec3_origin )
	{
		m_vecTossVelocity = vecToss;

		// don't check again for a while.
		m_flNextGrenadeCheck = gpGlobals->curtime + 1; // 1/3 second.
		return true;
	}
	else
	{
		// don't check again for a while.
		m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
		return false;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Get shoot position of BCC at an arbitrary position
// Input  :
// Output :
//-----------------------------------------------------------------------------
Vector CHLW_NPC_Rebel::Weapon_ShootPosition( )
{
	bool bStanding = !IsCrouching();
	Vector right;
	GetVectors( NULL, &right, NULL );

	if ((CapabilitiesGet() & bits_CAP_DUCK) )
	{
		if ( IsCrouchedActivity( GetActivity() ) )
		{
			bStanding = false;
		}
	}

	// FIXME: rename this "estimated" since it's not based on animation
	// FIXME: the orientation won't be correct when testing from arbitary positions for arbitary angles

	if  ( bStanding )
	{
		//if( HasShotgun() )
		//{
		//	return GetAbsOrigin() + REBEL_SHOTGUN_STANDING_POSITION + right * 8;
		//}
		//else
		{
			return GetAbsOrigin() + REBEL_GUN_STANDING_POSITION + right * 8;
		}
	}
	else
	{
		//if( HasShotgun() )
		//{
		//	return GetAbsOrigin() + REBEL_SHOTGUN_CROUCHING_POSITION + right * 8;
		//}
		//else
		{
			return GetAbsOrigin() + REBEL_GUN_CROUCHING_POSITION + right * 8;
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Vector
//-----------------------------------------------------------------------------
Vector CHLW_NPC_Rebel::EyePosition( void ) 
{
	if ( !IsCrouching() )
	{
		return GetAbsOrigin() + REBEL_EYE_STANDING_POSITION;
	}
	else
	{
		return GetAbsOrigin() + REBEL_EYE_CROUCHING_POSITION;
	}

	/*
	Vector m_EyePos;
	GetAttachment( "eyes", m_EyePos );
	return m_EyePos;
	*/
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : nActivity - 
// Output : Vector
//-----------------------------------------------------------------------------
Vector CHLW_NPC_Rebel::EyeOffset( Activity nActivity )
{
	if (CapabilitiesGet() & bits_CAP_DUCK)
	{
		if ( IsCrouchedActivity( nActivity ) )
			return REBEL_EYE_CROUCHING_POSITION;

	}
	// if the hint doesn't tell anything, assume current state
	if ( !IsCrouching() )
	{
		return REBEL_EYE_STANDING_POSITION;
	}
	else
	{
		return REBEL_EYE_CROUCHING_POSITION;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
Vector CHLW_NPC_Rebel::GetCrouchEyeOffset( void )
{
	return REBEL_EYE_CROUCHING_POSITION;
}


//-----------------------------------------------------------------------------
// Purpose: Crouch 
//-----------------------------------------------------------------------------
/*bool CHLW_NPC_Rebel::IsCrouchedActivity( Activity activity )
{
	Activity realActivity = TranslateActivity(activity);

	switch ( realActivity )
	{
	case ACT_RELOAD_LOW:
	case ACT_COVER_LOW:
	case ACT_COVER_PISTOL_LOW:
	case ACT_COVER_SMG1_LOW:
	case ACT_RELOAD_SMG1_LOW:

	// Aren't these supposed to be a little higher than the above?
	case ACT_RANGE_ATTACK1_LOW:
	case ACT_RANGE_ATTACK2_LOW:
	case ACT_RANGE_ATTACK_AR2_LOW:
	case ACT_RANGE_ATTACK_SMG1_LOW:
	case ACT_RANGE_ATTACK_SHOTGUN_LOW:
	case ACT_RANGE_ATTACK_PISTOL_LOW:
	case ACT_RANGE_AIM_LOW:
	case ACT_RANGE_AIM_SMG1_LOW:
	case ACT_RANGE_AIM_PISTOL_LOW:
	case ACT_RANGE_AIM_AR2_LOW:
		return true;
	}
	return false;
}*/

//-----------------------------------------------------------------------------
// Purpose: Our unit type depends on our weapons and flags
//-----------------------------------------------------------------------------
void CHLW_NPC_Rebel::InitUnitIndex()
{
	CWeaponRPG *pRPG = dynamic_cast<CWeaponRPG*>(GetActiveWeapon());
	const char *m_sUnitType;

	if( IsMedic() ) {
		m_sUnitType = "UNIT_REBEL_MEDIC";
		m_iUnitIndex = g_pRTSResource->GetUnitIndex( m_sUnitType );
	}
	else if( IsEngineer() ) {
		m_sUnitType = "UNIT_REBEL_ENGINEER";
		m_iUnitIndex = g_pRTSResource->GetUnitIndex( m_sUnitType );
	}
	else if( pRPG ) {
		m_sUnitType = "UNIT_REBEL_RPG";
		m_iUnitIndex = g_pRTSResource->GetUnitIndex( m_sUnitType );
	}
	else
		BaseClass::InitUnitIndex();
}


//-----------------------------------------------------------------------------
//
// Schedules
//
//-----------------------------------------------------------------------------

AI_BEGIN_CUSTOM_NPC( hlw_npc_rebel, CHLW_NPC_Rebel )
	// activities
	DECLARE_ACTIVITY( ACT_PLACE_MINE )
	DECLARE_ACTIVITY( ACT_PLACE_SENTRY )

	//Tasks
	DECLARE_TASK( TASK_REBEL_FACE_TOSS_DIR )
	DECLARE_TASK( TASK_REBEL_GET_PATH_TO_FORCED_GREN_LOS )
	DECLARE_TASK( TASK_REBEL_THROW_GRENADE )
	DECLARE_TASK( TASK_REBEL_PLACE_BUILDING )

	//Events
	DECLARE_ANIMEVENT( AE_REBEL_PLACE_MINE )
	DECLARE_ANIMEVENT( AE_REBEL_PLACE_SENTRY )

	//=========================================================
	// Mapmaker forced grenade throw
	//=========================================================
	DEFINE_SCHEDULE
	(
	SCHED_REBEL_FORCED_GRENADE_THROW,

		"	Tasks"
		"		TASK_STOP_MOVING					0"
		"		TASK_REBEL_FACE_TOSS_DIR			0"
		"		TASK_PLAY_SEQUENCE					ACTIVITY:ACT_RANGE_ATTACK2"
		//"		TASK_REBEL_THROW_GRENADE			0"
		""
		"	Interrupts"
	)

	//=========================================================
	// Move to LOS of the mapmaker's forced grenade throw target
	//=========================================================
	DEFINE_SCHEDULE
	(
	SCHED_REBEL_MOVE_TO_FORCED_GREN_LOS,

		"	Tasks "
		"		TASK_SET_TOLERANCE_DISTANCE					48"
		"		TASK_REBEL_GET_PATH_TO_FORCED_GREN_LOS		0"
		"		TASK_RUN_PATH								0"
		"		TASK_WAIT_FOR_MOVEMENT						0"
		"	"
		"	Interrupts "
	)

	//=========================================================
	// SCHED_COMBINE_DROP_GRENADE
	//
	//	Place a grenade at my feet
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_REBEL_DROP_GRENADE,

		"	Tasks"
		"		TASK_STOP_MOVING					0"
		"		TASK_PLAY_SEQUENCE					ACTIVITY:ACT_SPECIAL_ATTACK2"
		"		TASK_FIND_COVER_FROM_ENEMY			99"
		"		TASK_FIND_FAR_NODE_COVER_FROM_ENEMY	384"
		"		TASK_CLEAR_MOVE_WAIT				0"
		"		TASK_RUN_PATH						0"
		"		TASK_WAIT_FOR_MOVEMENT				0"
		""
		"	Interrupts"
	)

	//=========================================================
	// > 
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_REBEL_PLACE_TURRET,

		"	Tasks"
		"		TASK_STOP_MOVING			0"
		"		TASK_PLAY_SEQUENCE					ACTIVITY:ACT_PLACE_SENTRY"
		""
		"	Interrupts"
	)

	//=========================================================
	// > 
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_REBEL_PLACE_MINE,

		"	Tasks"
		"		TASK_STOP_MOVING			0"
		"		TASK_PLAY_SEQUENCE					ACTIVITY:ACT_PLACE_MINE"
		""
		"	Interrupts"
	)

	//=========================================================
	// > 
	//=========================================================
	DEFINE_SCHEDULE
	(
		SCHED_REBEL_PLACE_BUILDING,

		"	Tasks"
		"		TASK_STOP_MOVING			0"
		"		TASK_REBEL_PLACE_BUILDING	0"
		""
		"	Interrupts"
	)


AI_END_CUSTOM_NPC()


