//========= Copyright © 1996-2005, Sander Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================//

#include "cbase.h"
#include "hl2wars/rts_buildings.h"
#include <KeyValues.h>
#include <filesystem.h>
#include "hl2wars/rts_globalresource.h"
#include "hl2wars/rts_point_order.h"
#include "string_t.h"
#include "hl2wars/hl2wars_convars.h"
#include "hl2wars/ai_rts_strategic.h"
#include "hl2wars/util_hl2wars.h"
#include "hl2wars/util_hl2wars_shared.h"
#include "hl2wars/hl2wars_player.h"
#include "saverestore_utlvector.h"
#include "hl2wars/rts_parsedata.h"
#include "explode.h"
#include "eventqueue.h"

// thumper
#include "npcevent.h"
#include "rotorwash.h"
#include "soundenvelope.h"
#include "engine/IEngineSound.h"
#include "npc_antlion.h"
#include "te_effect_dispatch.h"

LINK_ENTITY_TO_CLASS( rts_building_brush, CBuildingBrush );
LINK_ENTITY_TO_CLASS( rts_building_model, CBuildingModel );
LINK_ENTITY_TO_CLASS( rts_building_thumper, CBuildingThumper );
LINK_ENTITY_TO_CLASS( rts_building_antlion_maker, CBuildingAntlionMaker );

#define RETRY_SPAWN_TIME 2.0f	// When respawn failed, avoid trying again immediately for perfomance reasons.

BEGIN_SIMPLE_DATADESC( unit_build_t )
	DEFINE_FIELD( ability, FIELD_INTEGER ),
	DEFINE_FIELD( amount, FIELD_INTEGER),
	DEFINE_FIELD( ai_group, FIELD_INTEGER),
END_DATADESC()

// ##################################################################################
//	>> Building Brush implementation
//
// ##################################################################################

BEGIN_DATADESC( CBuildingBrush )
	// function pointers
	DEFINE_THINKFUNC( BuildThink ),

	// keyfields
	DEFINE_KEYFIELD( m_iOwnerShip,		FIELD_INTEGER, "ownership" ),
	DEFINE_KEYFIELD( m_sBuildingData,	FIELD_STRING, "buildingdata" ),
	DEFINE_KEYFIELD( m_sOwnerShipIcon,	FIELD_STRING, "ownershipicon" ),
	DEFINE_KEYFIELD( m_sBuildTarget,	FIELD_STRING, "buildtarget" ),
	DEFINE_KEYFIELD( m_sRallyPoint,		FIELD_STRING, "rallypoint" ),

	// fields
	DEFINE_FIELD( m_vOwnerShipIconTarget,	FIELD_VECTOR ),
	DEFINE_ARRAY( m_iTypesQueue,			FIELD_INTEGER, QUEUE_MAX ),
	DEFINE_ARRAY( m_iQueueAmount,			FIELD_INTEGER, QUEUE_MAX ),
	//DEFINE_ARRAY( m_iTypesAbilities,		FIELD_INTEGER, MAXABILITIESSLOTS ),		// don't save, but restore
	DEFINE_FIELD( m_fBuildTimeMax,			FIELD_TIME ),
	DEFINE_UTLVECTOR( m_pBuildQueue,		FIELD_EMBEDDED ),
	DEFINE_UTLVECTOR( m_pBuildQueueTimes,	FIELD_FLOAT ),
	DEFINE_FIELD( m_vBuildTarget,			FIELD_VECTOR ),
	DEFINE_FIELD( m_vBuildAngle,			FIELD_VECTOR ),
	DEFINE_FIELD( m_vRallyPoint,			FIELD_VECTOR ),

	// input
	DEFINE_INPUTFUNC(FIELD_INTEGER, "changeownership", InputChangeOwnerShip),

END_DATADESC()

//server data table describing networked member variables (SendProps)
IMPLEMENT_SERVERCLASS_ST( CBuildingBrush, DT_BuildingBrush )
	SendPropArray3( SENDINFO_ARRAY3(m_iTypesQueue), SendPropInt( SENDINFO_ARRAY(m_iTypesQueue) ) ),
	SendPropArray3( SENDINFO_ARRAY3(m_iQueueAmount), SendPropInt( SENDINFO_ARRAY(m_iQueueAmount) ) ),
	SendPropFloat	(SENDINFO(m_fBuildTimeMax), 0, SPROP_NOSCALE),
	SendPropArray3( SENDINFO_ARRAY3(m_iTypesAbilities), SendPropInt( SENDINFO_ARRAY(m_iTypesAbilities) ) ),
	SendPropVector( SENDINFO(m_vRallyPoint), -1, SPROP_COORD ),

	// normal info
	SendPropVector	(SENDINFO(m_vOwnerShipIconTarget), -1, SPROP_COORD ),
	SendPropInt		(SENDINFO(m_iOwnerShip), 4, SPROP_UNSIGNED ),
	SendPropFloat	(SENDINFO(m_fBuildTime), 0, SPROP_NOSCALE),
END_SEND_TABLE()

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CBuildingBrush::CBuildingBrush() 
{
	m_bBuilding = false;
}

CBuildingBrush::~CBuildingBrush() 
{
	m_pBuildQueue.Purge();
}

//-----------------------------------------------------------------------------
// Purpose: Called when spawning, after keyvalues have been handled.
//-----------------------------------------------------------------------------
void CBuildingBrush::Spawn( )
{
	BaseClass::Spawn();

	g_pRTSResource->RegisterBuilding( this, m_iOwnerShip );

	// retrieve all build info
	KeyValues *pBuildingKeyValues = new KeyValues( "data" );

	char tempfile[MAX_PATH];
	Q_snprintf( tempfile, sizeof( tempfile ), "scripts/buildings/%s.txt", m_sBuildingData );
	
	if ( !pBuildingKeyValues->LoadFromFile( filesystem, tempfile, "GAME" ) )
	{
		DevMsg( 1, "Error! CBuildingBrush::CBuildingBrush: couldn't load file %s.\n", tempfile );
		return;
	}

	// retrieve build target
	CBaseEntity *pEnt = NULL;
	pEnt = gEntList.FindEntityByName( NULL, m_sBuildTarget );
	if( pEnt )
	{
		m_vBuildTarget = pEnt->GetAbsOrigin();
		m_vBuildAngle = pEnt->GetAbsAngles();
	}
	else
	{
		m_vBuildTarget = GetAbsOrigin();
		m_vBuildAngle = GetAbsAngles();
	}

	pEnt = NULL;
	pEnt = gEntList.FindEntityByName( NULL, m_sRallyPoint );
	if( pEnt )
		m_vRallyPoint = pEnt->GetAbsOrigin();
	else
		m_vRallyPoint = GetAbsOrigin();

	// retrieve icon target
	pEnt = NULL;
	pEnt = gEntList.FindEntityByName( NULL, m_sOwnerShipIcon );
	if( pEnt )
		m_vOwnerShipIconTarget = pEnt->GetAbsOrigin();
	else
		m_vOwnerShipIconTarget = GetAbsOrigin();

	// load unit type build info
	LoadBuildInfo( pBuildingKeyValues );

	// create a sprite
	m_hSprite = CSprite::SpriteCreate( "vgui/icons/icon_barracks.vmt", m_vOwnerShipIconTarget, false );
	CSprite *pSprite = (CSprite *)m_hSprite.Get();
	if ( pSprite )
	{
		pSprite->SetTransparency( kRenderTransAlpha, 255, 255, 255, 255, kRenderFxNoDissipation );
		pSprite->SetBrightness( 255, 0.5f );
		pSprite->SetScale( 0.5f, 0.0f );
		pSprite->TurnOn();
	}

	// precache
	Precache();

	SetThink ( &CBuildingBrush::BuildThink );
	SetNextThink( gpGlobals->curtime + 0.5f );
}

void CBuildingBrush::OnRestore()
{
	BaseClass::OnRestore();

	// retrieve all build info
	KeyValues *pBuildingKeyValues = new KeyValues( "data" );

	char tempfile[MAX_PATH];
	Q_snprintf( tempfile, sizeof( tempfile ), "scripts/buildings/%s.txt", m_sBuildingData );

	if ( !pBuildingKeyValues->LoadFromFile( filesystem, tempfile, "GAME" ) )
	{
		DevMsg( 1, "Error! CBuildingBrush::CBuildingBrush: couldn't load file %s.\n", tempfile );
		return;
	}

	// load unit type build info
	LoadBuildInfo( pBuildingKeyValues );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBuildingBrush::Precache()
{
	BaseClass::Precache();

	for( int i = 1; i < MAXABILITIESSLOTS + 1; i++ ) {
		if( m_iTypesAbilities.Get( i - 1 ) == 0 )
			continue;

		FileUnitInfo_t *pFUI = GetFileUnitInfoFromHandle( LookupUnitInfoSlot( STRING( g_pRTSResource->GetUnitType( m_iTypesAbilities.Get( i - 1 ) ) ) ) );
		if( !pFUI ) {
			continue;
		}

		CBaseEntity *pEnt = CreateEntityByName( pFUI->m_szUnit );
		if( !pEnt )
			continue;

		for ( KeyValues *sub = pFUI->m_pUnitKeyValues->GetFirstSubKey(); sub != NULL ; sub = sub->GetNextKey() )
		{
			pEnt->KeyValue( sub->GetName(), sub->GetString() );
		}

		pEnt->Precache();
		UTIL_RemoveImmediate( pEnt );
	}
}

//------------------------------------------------------------------------------
// Purpose :
//------------------------------------------------------------------------------
void CBuildingBrush::LoadBuildInfo( KeyValues *pBuildingKeyValues )
{
	if( !pBuildingKeyValues )
		return; 

	for( int i = 1; i < MAXABILITIESSLOTS + 1; i++ ) {
		char ability_data[MAX_PATH];
		Q_snprintf( ability_data, sizeof( ability_data ), "ability_%d", i );

		const char *unit_type =  pBuildingKeyValues->GetString(ability_data, "UNIT_NONE");
		m_iTypesAbilities.Set( i - 1, g_pRTSResource->GetUnitIndex( unit_type ) );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Being called on right click
//-----------------------------------------------------------------------------
void CBuildingBrush::OnClickRight( CBasePlayer *pActivator ) 
{
	BaseClass::OnClickRight( pActivator );
}

//-----------------------------------------------------------------------------
// Purpose: Being called on right double click
//-----------------------------------------------------------------------------
void CBuildingBrush::OnClickRightDouble( CBasePlayer *pActivator ) 
{
	BaseClass::OnClickRight( pActivator );
}


//-----------------------------------------------------------------------------
// Purpose: Left click
//-----------------------------------------------------------------------------
void CBuildingBrush::OnClickLeft( CBasePlayer *pActivator ) 
{
	BaseClass::OnClickLeft( pActivator );

	CHL2Wars_Player *pPlayer = ToHL2WarsPlayer(pActivator);
	Assert( pPlayer );

	// we are a building and should be selected on left click
	if( pPlayer && GetOwnerShip() == pPlayer->GetTeamRTSID() )
	{
		pPlayer->ClearSelection();
		pPlayer->m_HLWLocal.m_hSelected = this;
		pPlayer->m_HLWLocal.m_iSelectionType = SELECTION_BUILDING;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Left click double
//-----------------------------------------------------------------------------
void CBuildingBrush::OnClickLeftDouble( CBasePlayer *pActivator ) 
{
	BaseClass::OnClickLeftDouble( pActivator );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : inputdata_t &inputdata
//-----------------------------------------------------------------------------
void CBuildingBrush::InputChangeOwnerShip( inputdata_t &inputdata )
{
	g_pRTSResource->UnRegisterBuilding( this, m_iOwnerShip );
	m_iOwnerShip = inputdata.value.Int();
	g_pRTSResource->RegisterBuilding( this, m_iOwnerShip );
}

// ------------ Build/recruit functions
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CBuildingBrush::AddToQueue( int ability, int ai_group )
{
	if( g_pRTSResource->GetSize( m_iOwnerShip ) >= sv_max_units.GetInt() )
	{
		UTIL_HudStrategyText( (CBasePlayer *)g_pRTSResource->GetPlayerTeamID(m_iOwnerShip), "#HLW_MAXUNITSREACHED", S_RED );
		return false;
	}

	FileUnitInfo_t *pFUI = GetFileUnitInfoFromHandle( LookupUnitInfoSlot( STRING( g_pRTSResource->GetUnitType( m_iTypesAbilities.Get( ability - 1 ) ) ) ) );
	if( !pFUI ) {
		DevMsg("Unit type data missing in building! \n");
		return false;
	}

	if( g_pRTSResource->GetResources( m_iOwnerShip ) < pFUI->m_iCost ) 
	{
		UTIL_HudStrategyText( (CBasePlayer *)g_pRTSResource->GetPlayerTeamID(m_iOwnerShip), "#HLW_NOTENOUGHRESOURCES", S_RED );
		return false;
	}

	if( m_pBuildQueue.Count() != 0 &&
		m_pBuildQueue.Element(m_pBuildQueue.Count() - 1 ).ability == ability &&
		m_pBuildQueue.Element(m_pBuildQueue.Count() - 1 ).ai_group == ai_group )
	{	// same unit, so just increase amount
		m_pBuildQueue.Element(m_pBuildQueue.Count() - 1 ).amount++;
	}
	else 
	{	// new unit to tail, new entry
		unit_build_t unitbuild;
		unitbuild.ability = ability;
		unitbuild.ai_group = ai_group;
		unitbuild.amount = 1;

		// add to the build queue
		m_pBuildQueue.AddToTail( unitbuild );
		m_pBuildQueueTimes.AddToTail( pFUI->m_iRechargeTime );
	}

	g_pRTSResource->RemoveResources( m_iOwnerShip, pFUI->m_iCost );

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CBuildingBrush::RemoveFromQueue( int queue )
{
	int m_iShiftQueue = 0;
	CHL2Wars_Player *pPlayer = g_pRTSResource->GetPlayerTeamID( GetOwnerShip() );
	if( pPlayer )
		m_iShiftQueue = pPlayer->m_iShiftQueue;

	if( m_pBuildQueue.IsValidIndex( queue + m_iShiftQueue ) )
	{
		// refund the player
		FileUnitInfo_t *pFUI = GetFileUnitInfoFromHandle( LookupUnitInfoSlot( STRING( g_pRTSResource->GetUnitType( m_iTypesAbilities.Get( m_pBuildQueue.Element( queue + m_iShiftQueue ).ability - 1 ) ) ) ) );
		if( !pFUI ) {
			DevMsg("Unit type data missing in building! \n");
			return false;
		}

		g_pRTSResource->AddResources( m_iOwnerShip, pFUI->m_iCost);

		m_pBuildQueue.Element( queue + m_iShiftQueue ).amount--;
		if( m_pBuildQueue.Element( queue + m_iShiftQueue ).amount == 0 )
		{
		
			m_pBuildQueue.Remove( queue + m_iShiftQueue );
			m_pBuildQueueTimes.Remove( queue + m_iShiftQueue );

			if( queue + m_iShiftQueue == 0 )
				m_bBuilding = false;
		}
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBuildingBrush::BuildThink( void )
{
	// update color ownership sprite
	Vector color = g_pRTSResource->GetColor255( GetOwnerShip() );
	CSprite *pSprite = (CSprite *)m_hSprite.Get();
	if ( pSprite )
	{
		pSprite->SetColor( color.x, color.y, color.z );
	}

	// get shift queue
	int m_iShiftQueue = 0;
	CHL2Wars_Player *pPlayer = g_pRTSResource->GetPlayerTeamID( GetOwnerShip() );
	if( pPlayer )
		m_iShiftQueue = pPlayer->m_iShiftQueue;

	// update queue info on client
	for( int i = 0; i < QUEUE_MAX; i++ )
	{
		if( i + m_iShiftQueue >= m_pBuildQueue.Count() )
		{
			m_iTypesQueue.Set( i, 0 );
			m_iQueueAmount.Set(i, 0 );
		}
		else
		{
			m_iTypesQueue.Set( i, m_iTypesAbilities.Get( m_pBuildQueue.Element( i + m_iShiftQueue ).ability - 1 ) );
			m_iQueueAmount.Set(i, m_pBuildQueue.Element( i + m_iShiftQueue ).amount );
		}
	}

	// build
	if( m_bBuilding ) {
		// check if the unit buildtime is done
		if( m_fBuildTime < gpGlobals->curtime ) {
			SpawnNPC( m_pBuildQueue.Element( 0 ) );
			m_pBuildQueue.Element( 0 ).amount--;
			if( m_pBuildQueue.Element( 0 ).amount <= 0 )
			{
				m_pBuildQueue.Remove( 0 );
				m_pBuildQueueTimes.Remove( 0 );
			}

			m_bBuilding = false;
		}
	}
	else {
		if( m_pBuildQueue.Count() > 0 ) {	
			m_fBuildTime = gpGlobals->curtime + m_pBuildQueueTimes.Element( 0 );
			m_fBuildTimeMax = m_pBuildQueueTimes.Element( 0 );
			m_bBuilding = true;
		}
	}

	SetNextThink( gpGlobals->curtime + 0.5f );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBuildingBrush::SetRallyPoint( Vector rallypoint )
{
	m_vRallyPoint = rallypoint;
}

//-----------------------------------------------------------------------------
// Purpose: Let the npc move to the rally point
//-----------------------------------------------------------------------------
void CBuildingBrush::SpawnNPC( unit_build_t buildunit ) 
{
	FileUnitInfo_t *pFUI = GetFileUnitInfoFromHandle( LookupUnitInfoSlot( STRING( g_pRTSResource->GetUnitType( m_iTypesAbilities.Get( buildunit.ability - 1 ) ) ) ) );
	if( !pFUI ) {
		DevMsg("Unit type data missing in building! \n");
		return;
	}

	// spawn npc
	CBaseEntity *pEnt = CreateEntityByName( pFUI->m_szUnit );
	if( !pEnt )
		return;

	CAI_BaseNPC *pNPC = pEnt->MyNPCPointer();
	if( !pNPC )
		return;

	pNPC->SetAbsOrigin( m_vBuildTarget );
	pNPC->SetAbsAngles( m_vBuildAngle );
	pNPC->SetTeamRTSID( m_iOwnerShip );

	for ( KeyValues *sub = pFUI->m_pUnitKeyValues->GetFirstSubKey(); sub != NULL ; sub = sub->GetNextKey() )
	{
		pNPC->KeyValue( sub->GetName(), sub->GetString() );
	}

	pNPC->SetAIGroup( buildunit.ai_group );
	DispatchSpawn(pNPC);
	UTIL_DropToFloor( pNPC, MASK_NPCSOLID );
	pNPC->SetUnitIndex( m_iTypesAbilities.Get( buildunit.ability - 1 ) );	// InitUnitIndex might not have set the right one.
	pNPC->InitUnitScripts(pFUI);			// do this again, override the original
	pNPC->Activate();

	// give an order to move to the rallypoint
	CRTSPointOrder *pOrder = (CRTSPointOrder *)CreateEntityByName( "rts_point_order" );	
	if( !pOrder )
		return;

	pOrder->SetAbsOrigin( m_vRallyPoint );  
	pOrder->Spawn(); 
	pOrder->SetNPC( pNPC );
	pOrder->SetOrderType( ORDER_MOVE, true );
	pOrder->Start();
	pNPC->QueueNewOrder(pOrder);

	// if strategic ai is enabled for this teamid, add to ai group
	if( UTIL_IsValidTeam( m_iOwnerShip, false ) && g_StrategicAI[ m_iOwnerShip - DEFAULT_TEAMS ]->IsEnabled() )
	{	
		g_StrategicAI[ m_iOwnerShip - DEFAULT_TEAMS ]->AddToGroup( pNPC );
	}
}

// ##################################################################################
//	>> Building Model implementation
//
// ##################################################################################

BEGIN_DATADESC( CBuildingModel )
	// function pointers
	DEFINE_THINKFUNC( BuildThink ),
	DEFINE_THINKFUNC( ConstructThink ),

	// keyfields
	DEFINE_KEYFIELD( m_iOwnerShip,		FIELD_INTEGER, "ownership" ),
	DEFINE_KEYFIELD( m_sBuildingData,	FIELD_STRING, "buildingdata" ),

	DEFINE_KEYFIELD( m_vRallyPoint,				FIELD_VECTOR, "rallypoint" ),
	DEFINE_KEYFIELD( m_vOwnerShipIconTarget,	FIELD_VECTOR, "ownershipicon" ),
	DEFINE_KEYFIELD( m_vBuildTarget,			FIELD_VECTOR, "buildtarget" ),
	DEFINE_KEYFIELD( m_vBuildAngle,				FIELD_VECTOR, "buildangle" ),

	// fields
	DEFINE_ARRAY( m_iTypesQueue,			FIELD_INTEGER, QUEUE_MAX ),
	DEFINE_ARRAY( m_iQueueAmount,			FIELD_INTEGER, QUEUE_MAX ),
	//DEFINE_ARRAY( m_iTypesAbilities,		FIELD_INTEGER, MAXABILITIESSLOTS ),		// OnRestore(), instead of save
	DEFINE_FIELD( m_fBuildTimeMax,			FIELD_TIME ),
	DEFINE_UTLVECTOR( m_pBuildQueue,		FIELD_EMBEDDED ),
	DEFINE_UTLVECTOR( m_pBuildQueueTimes,	FIELD_FLOAT ),

	DEFINE_FIELD( m_fRetrySpawn, FIELD_TIME ),

	// input
	DEFINE_INPUTFUNC(FIELD_INTEGER, "changeownership", InputChangeOwnerShip),

END_DATADESC()

IMPLEMENT_SERVERCLASS_ST( CBuildingModel, DT_BuildingModel )
	// build info
	SendPropArray3( SENDINFO_ARRAY3(m_iTypesQueue), SendPropInt( SENDINFO_ARRAY(m_iTypesQueue) ) ),
	SendPropArray3( SENDINFO_ARRAY3(m_iQueueAmount), SendPropInt( SENDINFO_ARRAY(m_iQueueAmount) ) ),
	SendPropFloat	(SENDINFO(m_fBuildTimeMax), 0, SPROP_NOSCALE),
	SendPropArray3( SENDINFO_ARRAY3(m_iTypesAbilities), SendPropInt( SENDINFO_ARRAY(m_iTypesAbilities) ) ),
	SendPropFloat	(SENDINFO(m_fBuildTime), 0, SPROP_NOSCALE),
	SendPropVector( SENDINFO(m_vRallyPoint), -1, SPROP_COORD ),

END_SEND_TABLE()

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CBuildingModel::CBuildingModel() 
{
	m_bBuilding = false;
	m_fRetrySpawn = 0.0f;
}

CBuildingModel::~CBuildingModel() 
{
	m_pBuildQueue.Purge();
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBuildingModel::KeyValue( const char *szKeyName, const char *szValue )
{
	if ( FStrEq( szKeyName, "building_data" ) )
	{
		m_sBuildingData = AllocPooledString(szValue);
		return true;
	}

	if ( FStrEq( szKeyName, "buildtarget" ) )
	{
		UTIL_StringToVector( m_vBuildTarget.Base(), szValue );
		return true;
	}

	if ( FStrEq( szKeyName, "rallypoint" ) )
	{
		UTIL_StringToVector( m_vRallyPoint.GetForModify().Base(), szValue );
		return true;
	}

	if ( FStrEq( szKeyName, "buildangle" ) )
	{
		UTIL_StringToVector( m_vBuildAngle.Base(), szValue );
		return true;
	}

	return BaseClass::KeyValue( szKeyName, szValue );
}


//-----------------------------------------------------------------------------
// Purpose: Called when spawning, after keyvalues have been handled.
//-----------------------------------------------------------------------------
void CBuildingModel::Spawn( )
{
	BaseClass::Spawn();

	// retrieve all build info
	KeyValues *pBuildingKeyValues = new KeyValues( "data" );

	char tempfile[MAX_PATH];
	Q_snprintf( tempfile, sizeof( tempfile ), "scripts/buildings/%s.txt", m_sBuildingData );
	
	if ( !pBuildingKeyValues->LoadFromFile( filesystem, tempfile, "GAME" ) )
	{
		DevMsg( 1, "Error! CBuildingBrush::CBuildingBrush: couldn't load file %s.\n", tempfile );
		return;
	}

	// setup target positions that are relative to the origin of this building
	VectorYawRotate( m_vBuildTarget, GetAbsAngles()[YAW], m_vBuildTarget );
	m_vBuildTarget += GetAbsOrigin();
	VectorYawRotate( m_vRallyPoint.GetForModify(), GetAbsAngles()[YAW], m_vRallyPoint.GetForModify() );
	m_vRallyPoint += GetAbsOrigin();

	// load unit type build info
	LoadBuildInfo( pBuildingKeyValues );

	// if strategic ai is enabled for this teamid, reconsider costs
	if( UTIL_IsValidTeam( m_iOwnerShip, false ) && g_StrategicAI[ m_iOwnerShip - DEFAULT_TEAMS ]->IsEnabled() )
	{	
		g_StrategicAI[ m_iOwnerShip - DEFAULT_TEAMS ]->ReconsiderCosts();
	}

	// precache
	Precache();
}

void CBuildingModel::UpdateOnRemove()
{
	BaseClass::UpdateOnRemove();

	if( !g_pRTSResource )
		return;

	// clean up our queue, which makes the player refounded.
	while( m_pBuildQueue.Count() > 0 )
		RemoveFromQueue(0);
}

void CBuildingModel::OnRestore()
{
	BaseClass::OnRestore();

	// retrieve all build info
	KeyValues *pBuildingKeyValues = new KeyValues( "data" );

	char tempfile[MAX_PATH];
	Q_snprintf( tempfile, sizeof( tempfile ), "scripts/buildings/%s.txt", m_sBuildingData );

	if ( !pBuildingKeyValues->LoadFromFile( filesystem, tempfile, "GAME" ) )
	{
		DevMsg( 1, "Error! CBuildingBrush::CBuildingBrush: couldn't load file %s.\n", tempfile );
		return;
	}

	// load unit type build info
	LoadBuildInfo( pBuildingKeyValues );
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBuildingModel::Precache()
{
	BaseClass::Precache();

	for( int i = 1; i < MAXABILITIESSLOTS + 1; i++ ) {
		if( m_iTypesAbilities.Get( i - 1 ) == 0 )
			continue;

		FileUnitInfo_t *pFUI = GetFileUnitInfoFromHandle( LookupUnitInfoSlot( STRING( g_pRTSResource->GetUnitType( m_iTypesAbilities.Get( i - 1 ) ) ) ) );
		if( !pFUI ) {
			continue;
		}

		CBaseEntity *pEnt = CreateEntityByName( pFUI->m_szUnit );
		if( !pEnt )
			continue;

		for ( KeyValues *sub = pFUI->m_pUnitKeyValues->GetFirstSubKey(); sub != NULL ; sub = sub->GetNextKey() )
		{
			pEnt->KeyValue( sub->GetName(), sub->GetString() );
		}

		pEnt->Precache();
		UTIL_RemoveImmediate( pEnt );
	}
}

//------------------------------------------------------------------------------
// Purpose :
//------------------------------------------------------------------------------
void CBuildingModel::LoadBuildInfo( KeyValues *pBuildingKeyValues )
{
	if( !pBuildingKeyValues )
		return; 

	for( int i = 1; i < MAXABILITIESSLOTS + 1; i++ ) {
		char ability_data[MAX_PATH];
		Q_snprintf( ability_data, sizeof( ability_data ), "ability_%d", i );

		const char *unit_type =  pBuildingKeyValues->GetString(ability_data, "UNIT_NONE");
		m_iTypesAbilities.Set( i - 1, g_pRTSResource->GetUnitIndex( unit_type ) );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Being called on right click
//-----------------------------------------------------------------------------
void CBuildingModel::OnClickRight( CBasePlayer *pActivator ) 
{
	BaseClass::OnClickRight( pActivator );
}

//-----------------------------------------------------------------------------
// Purpose: Being called on right double click
//-----------------------------------------------------------------------------
void CBuildingModel::OnClickRightDouble( CBasePlayer *pActivator ) 
{
	BaseClass::OnClickRight( pActivator );
}


//-----------------------------------------------------------------------------
// Purpose: Left click
//-----------------------------------------------------------------------------
void CBuildingModel::OnClickLeft( CBasePlayer *pActivator ) 
{
	BaseClass::OnClickLeft( pActivator );
}

//-----------------------------------------------------------------------------
// Purpose: Left click double
//-----------------------------------------------------------------------------
void CBuildingModel::OnClickLeftDouble( CBasePlayer *pActivator ) 
{
	BaseClass::OnClickLeftDouble( pActivator );
}

// -- model building functions ( setting up )
void CBuildingModel::SetBuildData( string_t building_data )
{
	m_sBuildingData = building_data;
}

void CBuildingModel::SetBuildTarget( Vector build_target )
{
	m_vBuildTarget = build_target;
}

void CBuildingModel::SetBuildAngle( QAngle build_angle )
{
	m_vBuildAngle = build_angle;
}

// -- shared functions between brush and model building ( even though they ain't shared, keep them synced )
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CBuildingModel::AddToQueue( int ability, int ai_group )
{
	if( g_pRTSResource->GetSize( m_iOwnerShip ) >= sv_max_units.GetInt() )
	{
		UTIL_HudStrategyText( (CBasePlayer *)g_pRTSResource->GetPlayerTeamID(m_iOwnerShip), "#HLW_MAXUNITSREACHED", S_RED );
		return false;
	}

	FileUnitInfo_t *pFUI = GetFileUnitInfoFromHandle( LookupUnitInfoSlot( STRING( g_pRTSResource->GetUnitType( m_iTypesAbilities.Get( ability - 1 ) ) ) ) );
	if( !pFUI ) {
		DevMsg("Unit type data missing in building! \n");
		return false;
	}

	if( g_pRTSResource->GetResources( m_iOwnerShip ) < pFUI->m_iCost ) 
	{
		UTIL_HudStrategyText( (CBasePlayer *)g_pRTSResource->GetPlayerTeamID(m_iOwnerShip), "#HLW_NOTENOUGHRESOURCES", S_RED );
		return false;
	}

	if( m_pBuildQueue.Count() != 0 &&
		m_pBuildQueue.Element(m_pBuildQueue.Count() - 1 ).ability == ability &&
		m_pBuildQueue.Element(m_pBuildQueue.Count() - 1 ).ai_group == ai_group )
	{	// same unit, so just increase amount
		m_pBuildQueue.Element(m_pBuildQueue.Count() - 1 ).amount++;
	}
	else 
	{	// new unit to tail, new entry
		unit_build_t unitbuild;
		unitbuild.ability = ability;
		unitbuild.ai_group = ai_group;
		unitbuild.amount = 1;

		// add to the build queue
		m_pBuildQueue.AddToTail( unitbuild );
		m_pBuildQueueTimes.AddToTail( pFUI->m_iRechargeTime );
	}

	g_pRTSResource->RemoveResources( m_iOwnerShip, pFUI->m_iCost );

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CBuildingModel::RemoveFromQueue( int queue )
{
	int m_iShiftQueue = 0;
	CHL2Wars_Player *pPlayer = g_pRTSResource->GetPlayerTeamID( GetOwnerShip() );
	if( pPlayer )
		m_iShiftQueue = pPlayer->m_iShiftQueue;

	if( m_pBuildQueue.IsValidIndex( queue + m_iShiftQueue ) )
	{
		// refund the player
		FileUnitInfo_t *pFUI = GetFileUnitInfoFromHandle( LookupUnitInfoSlot( STRING( g_pRTSResource->GetUnitType( m_iTypesAbilities.Get( m_pBuildQueue.Element( queue + m_iShiftQueue ).ability - 1 ) ) ) ) );
		if( !pFUI ) {
			DevMsg("Unit type data missing in building! \n");
			return false;
		}

		g_pRTSResource->AddResources( m_iOwnerShip, pFUI->m_iCost);

		m_pBuildQueue.Element( queue + m_iShiftQueue ).amount--;
		if( m_pBuildQueue.Element( queue + m_iShiftQueue ).amount == 0 )
		{
		
			m_pBuildQueue.Remove( queue + m_iShiftQueue );
			m_pBuildQueueTimes.Remove( queue + m_iShiftQueue );

			if( queue + m_iShiftQueue == 0 )
				m_bBuilding = false;
		}
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBuildingModel::BuildThink( void )
{
	// get shift
	int m_iShiftQueue = 0;
	CHL2Wars_Player *pPlayer = g_pRTSResource->GetPlayerTeamID( GetOwnerShip() );
	if( pPlayer )
		m_iShiftQueue = pPlayer->m_iShiftQueue;

	// update queue info on client
	for( int i = 0; i < QUEUE_MAX; i++ )
	{
		if( i + m_iShiftQueue >= m_pBuildQueue.Count() )
		{
			m_iTypesQueue.Set( i, 0 );
			m_iQueueAmount.Set(i, 0 );
		}
		else
		{
			m_iTypesQueue.Set( i, m_iTypesAbilities.Get( m_pBuildQueue.Element( i + m_iShiftQueue ).ability - 1 ) );
			m_iQueueAmount.Set(i, m_pBuildQueue.Element( i + m_iShiftQueue ).amount );
		}
	}

	// build
	if( m_bBuilding ) {
		// check if the unit buildtime is done
		if( m_fBuildTime < gpGlobals->curtime ) {
			if( m_fRetrySpawn < gpGlobals->curtime )
			{
				if( SpawnNPC( m_pBuildQueue.Element( 0 ) ) )
				{
					m_pBuildQueue.Element( 0 ).amount--;
					if( m_pBuildQueue.Element( 0 ).amount <= 0 )
					{
						m_pBuildQueue.Remove( 0 );
						m_pBuildQueueTimes.Remove( 0 );
					}

					m_bBuilding = false;
				}
				else
				{
					m_fRetrySpawn = gpGlobals->curtime + RETRY_SPAWN_TIME;
				}
			}
		}
	}
	else {
		if( m_pBuildQueue.Count() > 0 ) {	
			m_fBuildTime = gpGlobals->curtime + m_pBuildQueueTimes.Element( 0 );
			m_fBuildTimeMax = m_pBuildQueueTimes.Element( 0 );
			m_bBuilding = true;
		}
	}

	BaseClass::BuildThink();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBuildingModel::OnSelectedRightClick( Vector position )
{
	m_vRallyPoint = position;
}

//-----------------------------------------------------------------------------
// Purpose: Let the npc move to the rally point
//-----------------------------------------------------------------------------
bool CBuildingModel::SpawnNPC( unit_build_t buildunit ) 
{
	FileUnitInfo_t *pFUI = GetFileUnitInfoFromHandle( LookupUnitInfoSlot( STRING( g_pRTSResource->GetUnitType( m_iTypesAbilities.Get( buildunit.ability - 1 ) ) ) ) );
	if( !pFUI ) {
		DevMsg("Unit type data missing in building! \n");
		return false;
	}

	// spawn npc
	CBaseEntity *pEnt = CreateEntityByName( pFUI->m_szUnit );
	if( !pEnt )
		return false;

	CAI_BaseNPC *pNPC = pEnt->MyNPCPointer();
	if( !pNPC )
		return false;

	pNPC->SetAbsOrigin( m_vBuildTarget );
	pNPC->SetAbsAngles( m_vBuildAngle );
	pNPC->SetTeamRTSID( m_iOwnerShip );

	for ( KeyValues *sub = pFUI->m_pUnitKeyValues->GetFirstSubKey(); sub != NULL ; sub = sub->GetNextKey() )
	{
		pNPC->KeyValue( sub->GetName(), sub->GetString() );
	}

	pNPC->SetAIGroup( buildunit.ai_group );
	DispatchSpawn(pNPC);
	UTIL_DropToFloor( pNPC, MASK_NPCSOLID );

	// Now check that this is a valid location for the new npc to be
	trace_t tr;
	Vector	vUpBit = pNPC->GetAbsOrigin();
	vUpBit.z += 1;

	AI_TraceHull( pNPC->GetAbsOrigin(), vUpBit, pNPC->GetHullMins(), pNPC->GetHullMaxs(), 
		MASK_NPCSOLID, pNPC, COLLISION_GROUP_NONE, &tr );
	// blocked by something solid?
	/*if ( tr.startsolid )
	{
		pNPC->SUB_Remove();
		DevMsg("Can't spawn npc.  Bad Build Target!\n");
		NDebugOverlay::Box(pNPC->GetAbsOrigin(), pNPC->GetHullMins(), pNPC->GetHullMaxs(), 255, 0, 0, 0, 0);
		return false;
	}
	// blocked by an entity
	else */if( tr.m_pEnt && tr.m_pEnt->IsNPC() )
	{
		pNPC->SUB_Remove();

		CAI_BaseNPC *pNPCBlocking = tr.m_pEnt->MyNPCPointer();
		if( !pNPCBlocking )
			return false;

		// our own unit, make it move away!
		if( pNPCBlocking->GetTeamRTSID() == m_iOwnerShip )
		{
			CRTSPointOrder *pOrder = (CRTSPointOrder *)CreateEntityByName( "rts_point_order" );	
			if( !pOrder )
				return false;	// order fails, but npc is still spawned, so true

			Vector origin;
			UTIL_FindSpotForOrderInRadius( &origin, m_vBuildTarget, 128.0 );
			pOrder->SetAbsOrigin( origin );  
			pOrder->Spawn(); 
			pOrder->SetNPC( pNPCBlocking );
			pOrder->SetOrderType( ORDER_MOVE, true );
			pOrder->Start();
			pNPCBlocking->ClearAllOrders();
			pNPCBlocking->QueueNewOrder(pOrder);

			return false;
		}
	}

	pNPC->SetUnitIndex( m_iTypesAbilities.Get( buildunit.ability - 1 ) );	// InitUnitIndex might not have set the right one.
	pNPC->InitUnitScripts(pFUI);			// do this again, override the original
	pNPC->Activate();

	// give an order to move to the rallypoint
	CRTSPointOrder *pOrder = (CRTSPointOrder *)CreateEntityByName( "rts_point_order" );	
	if( !pOrder )
		return true;	// order fails, but npc is still spawned, so true

	pOrder->SetAbsOrigin( m_vRallyPoint );  
	pOrder->Spawn(); 
	pOrder->SetNPC( pNPC );
	pOrder->SetOrderType( ORDER_MOVE, true );
	pOrder->Start();
	pNPC->ClearAllOrders();
	pNPC->QueueNewOrder(pOrder);

	// if strategic ai is enabled for this teamid, add to ai group
	if( UTIL_IsValidTeam( m_iOwnerShip, false ) && g_StrategicAI[ m_iOwnerShip - DEFAULT_TEAMS ]->IsEnabled() )
	{	
		g_StrategicAI[ m_iOwnerShip - DEFAULT_TEAMS ]->AddToGroup( pNPC );
	}

	// play spawn sound
	if( !pFUI->m_pSoundData )
		return true;

	CHL2Wars_Player *pPlayer = g_pRTSResource->GetPlayerTeamID( GetOwnerShip() );
	if( !pPlayer )
		return true;

	CSingleUserRecipientFilter filter( pPlayer );
	EmitSound( filter, pNPC->entindex(), pFUI->m_pSoundData->GetString("spawn") );

	return true;
}

// ##################################################################################
//	>> Building Thumper
//
// ##################################################################################

#define THUMPER_RADIUS 1000
#define STATE_CHANGE_MODIFIER 0.02f
#define THUMPER_SOUND_DURATION 1.5f
#define THUMPER_MIN_SCALE 128
#define THUMPER_MODEL_NAME "models/props_combine/CombineThumper002.mdl"

//-----------------------------------------------------------------------------
// Save/load 
//-----------------------------------------------------------------------------
BEGIN_DATADESC( CBuildingThumper )
	DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_hRepellantEnt, FIELD_EHANDLE ),
	DEFINE_FIELD( m_iHammerAttachment, FIELD_INTEGER ),
	DEFINE_KEYFIELD( m_iDustScale, FIELD_INTEGER, "dustscale" ),
	DEFINE_SOUNDPATCH( m_sndMotor ),
	//DEFINE_THINKFUNC( Think ),
	//DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
	//DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
END_DATADESC()

void CBuildingThumper::Spawn( void )
{
	char *szModel = (char *)STRING( GetModelName() );
	if (!szModel || !*szModel)
	{
		szModel = THUMPER_MODEL_NAME;
		SetModelName( AllocPooledString(szModel) );
	}

	Precache();
	SetModel( szModel );

	SetSolid( SOLID_VPHYSICS );
	SetMoveType( MOVETYPE_NONE );
	VPhysicsInitStatic();

	BaseClass::Spawn();

	m_bEnabled = true;

	int iSequence = SelectHeaviestSequence ( ACT_IDLE );

	if ( iSequence != ACT_INVALID )
	{
		 SetSequence( iSequence );
		 ResetSequenceInfo();

		 //Do this so we get the nice ramp-up effect.
		 m_flPlaybackRate = random->RandomFloat( 0.0f, 1.0f);
	}

	m_iHammerAttachment = LookupAttachment( "hammer" );
	
	CAntlionRepellant *pRepellant = (CAntlionRepellant*)CreateEntityByName( "point_antlion_repellant" );

	if ( pRepellant )
	{
		pRepellant->Spawn();
		pRepellant->SetAbsOrigin( GetAbsOrigin() );
		pRepellant->SetRadius( THUMPER_RADIUS );

		m_hRepellantEnt = pRepellant;
	}

	if ( m_iDustScale == 0 )
		 m_iDustScale = THUMPER_MIN_SCALE;

}

void CBuildingThumper::Precache( void )
{
	BaseClass::Precache();

	PrecacheModel( STRING( GetModelName() ) );
	PrecacheScriptSound( "coast.thumper_hit" );
	PrecacheScriptSound( "coast.thumper_ambient" );
	PrecacheScriptSound( "coast.thumper_dust" );
	PrecacheScriptSound( "coast.thumper_startup" );
	PrecacheScriptSound( "coast.thumper_shutdown" );
	PrecacheScriptSound( "coast.thumper_large_hit" );
}

void CBuildingThumper::InitMotorSound( void )
{
	CPASAttenuationFilter filter( this );
	m_sndMotor = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "coast.thumper_ambient" , ATTN_NORM );

	if ( m_sndMotor )
	{
		(CSoundEnvelopeController::GetController()).Play( m_sndMotor, 1.0f, 100 );
	}
}

void CBuildingThumper::HandleState( void )
{
	if ( m_bEnabled == false )
	{
		 m_flPlaybackRate = max( m_flPlaybackRate - STATE_CHANGE_MODIFIER, 0.0f );
	}
	else
	{
 		 m_flPlaybackRate = min( m_flPlaybackRate + STATE_CHANGE_MODIFIER, 1.0f );
	}

	(CSoundEnvelopeController::GetController()).Play( m_sndMotor, 1.0f, m_flPlaybackRate * 100 );
}

void CBuildingThumper::BuildThink( void )
{
	BaseClass::BuildThink();

	StudioFrameAdvance();
	DispatchAnimEvents( this );

	if ( m_sndMotor == NULL )
		 InitMotorSound();
	else
		 HandleState();
}

void CBuildingThumper::Thump ( void )
{
	if ( m_iHammerAttachment != -1 )
	{
		Vector vOrigin;
		GetAttachment( m_iHammerAttachment, vOrigin );

		CEffectData	data;

		data.m_nEntIndex = entindex();
		data.m_vOrigin = vOrigin;
		data.m_flScale = m_iDustScale * m_flPlaybackRate;
		DispatchEffect( "ThumperDust", data );
		UTIL_ScreenShake( vOrigin, 10.0 * m_flPlaybackRate, m_flPlaybackRate, m_flPlaybackRate / 2, THUMPER_RADIUS * m_flPlaybackRate, SHAKE_START, false );
	}

	EmitSound( "coast.thumper_dust" );
	CSoundEnt::InsertSound ( SOUND_THUMPER, GetAbsOrigin(), THUMPER_RADIUS * m_flPlaybackRate, THUMPER_SOUND_DURATION, this );

	if ( m_flPlaybackRate < 0.7f )
		 return;

	if( m_iDustScale == THUMPER_MIN_SCALE )
		 EmitSound( "coast.thumper_hit" );
	else
		 EmitSound( "coast.thumper_large_hit" );

}

void CBuildingThumper::HandleAnimEvent( animevent_t *pEvent )
{
	if ( pEvent->event == AE_THUMPER_THUMP )
	{
		Thump();
		return;
	}

	BaseClass::HandleAnimEvent( pEvent );
}


//-----------------------------------------------------------------------------
// Shuts down sounds
//-----------------------------------------------------------------------------
void CBuildingThumper::StopLoopingSounds( void )
{
	if ( m_sndMotor != NULL )
	{
		 (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndMotor );
		 m_sndMotor = NULL;
	}

	BaseClass::StopLoopingSounds();
}

/*void CBuildingThumper::InputDisable( inputdata_t &inputdata )
{
	m_bEnabled = false;
	
	EmitSound( "coast.thumper_shutdown" );
	
	if ( m_hRepellantEnt )
	{
		variant_t emptyVariant;
		m_hRepellantEnt->AcceptInput( "Disable", this, this, emptyVariant, 0 );
	}
}

void CBuildingThumper::InputEnable( inputdata_t &inputdata )
{
	m_bEnabled = true;

	EmitSound( "coast.thumper_startup" );

	if ( m_hRepellantEnt )
	{
		variant_t emptyVariant;
		m_hRepellantEnt->AcceptInput( "Enable", this, this, emptyVariant, 0 );
	}
}*/

// ##################################################################################
//	>> Building Antlion maker
//
// ##################################################################################
void CBuildingAntlionMaker::SpawnNPC( unit_build_t buildunit )
{
	FileUnitInfo_t *pFUI = GetFileUnitInfoFromHandle( LookupUnitInfoSlot( STRING( g_pRTSResource->GetUnitType( m_iTypesAbilities.Get( buildunit.ability - 1 ) ) ) ) );
	if( !pFUI ) {
		DevMsg("Unit type data missing in building! \n");
		return;
	}

	// spawn npc
	CBaseEntity *pEnt = CreateEntityByName( pFUI->m_szUnit );
	if( !pEnt )
		return;

	CAI_BaseNPC *pNPC = pEnt->MyNPCPointer();
	if( !pNPC )
		return;

	pNPC->SetAbsOrigin( m_vBuildTarget );
	pNPC->SetAbsAngles( m_vBuildAngle );
	pNPC->SetTeamRTSID( m_iOwnerShip );

	for ( KeyValues *sub = pFUI->m_pUnitKeyValues->GetFirstSubKey(); sub != NULL ; sub = sub->GetNextKey() )
	{
		pNPC->KeyValue( sub->GetName(), sub->GetString() );
	}

	pNPC->SetAIGroup( buildunit.ai_group );
	DispatchSpawn(pNPC);
	//UTIL_DropToFloor( pNPC, MASK_NPCSOLID );
	pNPC->SetUnitIndex( m_iTypesAbilities.Get( buildunit.ability - 1 ) );	// InitUnitIndex might not have set the right one.
	pNPC->InitUnitScripts(pFUI);			// do this again, override the original
	pNPC->Activate();

	CNPC_Antlion *pAntlion = dynamic_cast<CNPC_Antlion*>(pNPC);
	if ( pAntlion )
	{
		// Unburrow the spawned antlion immediately
		if ( pAntlion->m_bStartBurrowed )
		{
			g_EventQueue.AddEvent( pAntlion, "Unburrow", RandomFloat(0.1, 1.0), this, this );
		}
	}

	// give an order to move to the rallypoint
	CRTSPointOrder *pOrder = (CRTSPointOrder *)CreateEntityByName( "rts_point_order" );	
	if( !pOrder )
		return;

	pOrder->SetAbsOrigin( m_vRallyPoint );  
	pOrder->Spawn(); 
	pOrder->SetNPC( pNPC );
	pOrder->SetOrderType( ORDER_MOVE, true );
	pOrder->Start();
	pNPC->QueueNewOrder(pOrder);

	// if strategic ai is enabled for this teamid, add to ai group
	if( UTIL_IsValidTeam( m_iOwnerShip, false ) && g_StrategicAI[ m_iOwnerShip - DEFAULT_TEAMS ]->IsEnabled() )
	{	
		g_StrategicAI[ m_iOwnerShip - DEFAULT_TEAMS ]->AddToGroup( pNPC );
	}
}