//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Entity that propagates general rts data for all players
//			team_id means what the player is controlling in the map. It also tells
//			where the player will spawn, and is being used for the color and resources,
//			etc, so it is an important value.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "hl2wars/rts_globalresource.h"
#include <KeyValues.h>
#include <filesystem.h>
#include "ai_basenpc.h"
#include "hl2wars/rts_buildings.h"
#include "hl2wars/rts_basebuilding.h"

#include "saverestore_utlvector.h"
#include "UtlCachedFileData.h"
#include "utlbuffer.h"
#include "hl2wars/rts_player_info.h"
#include "EntityList.h"
#include "hl2wars/hl2wars_player.h"
#include "hl2wars/util_hl2wars.h"
#include "hl2wars/util_hl2wars_shared.h"
#include "viewport_panel_names.h"
#include "playerinfomanager.h"
#include "sendproxy.h"
#include "networkstringtable_gamedll.h"

#include "hl2wars/hl2wars_gamerules.h"

// test reading bsp files
#include "mapentities_shared.h"
#include "mapentities.h"

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

// unit info
BEGIN_SIMPLE_DATADESC( UnitParams_t )
	DEFINE_UTLVECTOR( UnitInfoList, FIELD_EHANDLE ),
END_DATADESC()

// Datatable
IMPLEMENT_SERVERCLASS_ST(CRTSResource, DT_RTSResource)
	SendPropArray3( SENDINFO_ARRAY3(m_vTeamColorTable), SendPropVector( SENDINFO_ARRAY(m_vTeamColorTable), -1,  SPROP_COORD ) ),
	SendPropArray3( SENDINFO_ARRAY3(m_bColorInUse), SendPropInt( SENDINFO_ARRAY(m_bColorInUse), 1, SPROP_UNSIGNED ) ),
	SendPropArray3( SENDINFO_ARRAY3(m_bPositionInUse), SendPropInt( SENDINFO_ARRAY(m_bPositionInUse), 1, SPROP_UNSIGNED ) ),
	SendPropArray3( SENDINFO_ARRAY3(m_iFaction), SendPropInt( SENDINFO_ARRAY(m_iFaction), 2, SPROP_UNSIGNED ) ),

	// gameroom data
	SendPropEHandle( SENDINFO( m_hGameMaster ) ),
	SendPropArray3( SENDINFO_ARRAY3(m_iIndex), SendPropInt( SENDINFO_ARRAY(m_iIndex), 5,  SPROP_UNSIGNED ) ),
	SendPropArray3( SENDINFO_ARRAY3(m_iPosition), SendPropInt( SENDINFO_ARRAY(m_iPosition), 4 ) ),
	SendPropArray3( SENDINFO_ARRAY3(m_iColor), SendPropInt( SENDINFO_ARRAY(m_iColor), 4,  SPROP_UNSIGNED ) ),
	SendPropArray3( SENDINFO_ARRAY3(m_bReady), SendPropBool( SENDINFO_ARRAY(m_bReady) ) ),
	SendPropArray3( SENDINFO_ARRAY3(m_iFaction), SendPropInt( SENDINFO_ARRAY(m_iFaction), 4,  SPROP_UNSIGNED ) ),
	SendPropArray3( SENDINFO_ARRAY3(m_iType), SendPropInt( SENDINFO_ARRAY(m_iType), 4,  SPROP_UNSIGNED ) ),
END_SEND_TABLE()

BEGIN_DATADESC( CRTSResource )
	// Function Pointers
	DEFINE_FUNCTION( ResourceThink ),

	DEFINE_EMBEDDEDBYREF( m_pBuildingList ),
	DEFINE_EMBEDDEDBYREF( m_pBuildingModelList ),
	DEFINE_EMBEDDEDBYREF( m_pCapturePointList ),

	DEFINE_ARRAY( m_vTeamColorTable, FIELD_VECTOR, MAX_TEAMS_ID_RTS + DEFAULT_TEAMS ),
	DEFINE_ARRAY( m_bColorInUse, FIELD_BOOLEAN, TOTAL_COLORS ),
	DEFINE_ARRAY( m_bPositionInUse, FIELD_BOOLEAN, MAX_TEAMS_ID_RTS ),
	DEFINE_ARRAY( m_iFaction, FIELD_INTEGER, MAX_TEAMS_ID_RTS ),
	DEFINE_ARRAY( m_iResources, FIELD_INTEGER, MAX_TEAMS_ID_RTS ),
	DEFINE_EMBEDDED_ARRAY( m_UnitInfoList, MAX_TEAMS_ID_RTS ),

	DEFINE_FIELD( m_bLevelInit, FIELD_BOOLEAN ),
END_DATADESC()

BEGIN_DATADESC( CRTSStartPointMP )
	DEFINE_KEYFIELD( m_iTeamID, FIELD_INTEGER, "teamid" ),
END_DATADESC()

LINK_ENTITY_TO_CLASS( rts_manager, CRTSResource );
LINK_ENTITY_TO_CLASS(info_mp_startpoint, CRTSStartPointMP);

CRTSResource *g_pRTSResource;

// tells relationships between teams. Can be changed ingame/inhammer
Disposition_t		g_RelationshipsRTS[MAX_TEAMS_ID_RTS][MAX_TEAMS_ID_RTS];

// Here we will store some info between level change.
gameroom_data_t g_GRData;

//------------------------------------------------------------------------------
void CC_ChangeRelation( const CCommand &args )
{
	if( args.ArgC() < 3 )
	{
		Msg("changerelation: not enough arguments. Three required.");
		return;
	}

	int teamid1 = atoi(args[1]);
	int teamid2 = atoi(args[2]);
	Disposition_t rel = (Disposition_t)atoi(args[3]);

	if( !UTIL_IsValidTeam(teamid1, false ) || !UTIL_IsValidTeam(teamid2, false ) )
	{
		Msg("changerelation: Invalid teams specified.");
		return;
	}

	g_RelationshipsRTS[teamid1 - DEFAULT_TEAMS][teamid2 - DEFAULT_TEAMS] = rel;
	g_RelationshipsRTS[teamid2 - DEFAULT_TEAMS][teamid1 - DEFAULT_TEAMS] = rel;

}
static ConCommand changerelation("changerelation", CC_ChangeRelation, "Change the relationship between two teams", FCVAR_CHEAT);

// ##################################################################################
//	>> List
// ##################################################################################
BEGIN_SIMPLE_DATADESC( infolistparams_t )
	DEFINE_UTLVECTOR( InfoList, FIELD_EHANDLE ),	
END_DATADESC()

BEGIN_SIMPLE_DATADESC( CTeamIDList )
	DEFINE_FIELD( m_bStoreDefaultTeams, FIELD_BOOLEAN ),
	DEFINE_EMBEDDED_ARRAY( m_InfoList, MAX_TEAMS_ID_RTS + DEFAULT_TEAMS ),
END_DATADESC()

int CTeamIDList::GetCount( int teamid ) 
{
	if( !UTIL_IsValidTeam( teamid, m_bStoreDefaultTeams ) )
		return 0;

	return m_InfoList[ teamid ].InfoList.Count(); 
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CBaseEntity *CTeamIDList::GetFromList( int teamid, int index )
{
	if( !UTIL_IsValidTeam( teamid, m_bStoreDefaultTeams ) )
		return NULL;

	if( m_InfoList[teamid ].InfoList.Count() == 0 || index >= m_InfoList[teamid ].InfoList.Count())
		return NULL;

	CBaseEntity *pEnt = m_InfoList[teamid ].InfoList.Element( index ).Get();
	if( !pEnt || !pEnt->IsAlive() )
		return NULL;

	return pEnt;
}

//-----------------------------------------------------------------------------
// Purpose: Adds a unit
//			Return false on duplicates or invalid teams.
//-----------------------------------------------------------------------------
bool CTeamIDList::AddToList( int teamid, CBaseEntity *pEnt )
{
	// don't add units to team default, neutral and enemy. Players don't control them.
	if( !UTIL_IsValidTeam( teamid, m_bStoreDefaultTeams ) )
		return false;

	if( m_InfoList[teamid ].InfoList.Find(pEnt) != -1 )
		return false;

	m_InfoList[teamid ].InfoList.AddToTail( pEnt );
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Removes a unit. The unit behind in the line moves to the front 
//			position, so we don't have gabs.
//-----------------------------------------------------------------------------
bool CTeamIDList::RemoveFromList( int teamid, CBaseEntity *pEnt )
{
	// don't remove units from team default, neutral and enemy. Players don't control them.
	if( !UTIL_IsValidTeam( teamid, m_bStoreDefaultTeams ) )
		return false;

	return m_InfoList[teamid ].InfoList.FindAndRemove( pEnt );
}

//-----------------------------------------------------------------------------
// Purpose: Sets over a unit. Team id still need to be changed!
//-----------------------------------------------------------------------------
void CTeamIDList::ChangeTeam( int oldteam, int newteam, CBaseEntity *pEnt )
{
	if( oldteam == newteam ) 
		return;

	RemoveFromList( oldteam, pEnt );
	AddToList( newteam, pEnt );
}

//-----------------------------------------------------------------------------
// Purpose: Counts total
//-----------------------------------------------------------------------------
int CTeamIDList::GetTotal()
{
	int total = 0;
	int i = (m_bStoreDefaultTeams) ? 0 : DEFAULT_TEAMS;
	for( i; i < MAX_TEAMS_ID_RTS + DEFAULT_TEAMS; i++ )
		total += GetCount(i);

	return total;
}

// ##################################################################################
//	>> Global data
// ##################################################################################


CRTSResource::CRTSResource()
{
	AddEFlags( EFL_FORCE_CHECK_TRANSMIT );

	// create list entities
	m_pBuildingList = new CTeamIDList();
	m_pBuildingModelList = new CTeamIDList();
	m_pCapturePointList = new CTeamIDList();
	m_pCapturePointList->SetStoreDefaultTeams( true );

	// everybody hates eachother
	for( int i = 0; i < MAX_TEAMS_ID_RTS; i++ )
	{
		for( int j = 0; j < MAX_TEAMS_ID_RTS; j++ )
		{
			if( i == j )
				g_RelationshipsRTS[i][j] = D_LI;
			else
				g_RelationshipsRTS[i][j] = D_HT;
		}
	}

	// gameroom
	m_hGameMaster = NULL;

	for( int i = 0; i < MAX_TEAMS_ID_RTS; i++ )
		m_AbilityStates[i].SetLessFunc( DefLessFunc( unsigned short ) );
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::Spawn( void )
{
	m_bLevelInit = true;

	SetThink( &CRTSResource::ResourceThink );
	SetNextThink( gpGlobals->curtime );

	// assign default colors
	m_vTeamColorTable.Set( 0, Vector( 0.58f, 0.58f, 0.58f ) );	// team default and neutral are both grey
	m_vTeamColorTable.Set( 1, Vector( 0.58f, 0.58f, 0.58f) );
	m_vTeamColorTable.Set( 2, Vector( 1.0f, 0, 0) );		// team enemy is always red

	// we store all gameinfo in here and take it to the next map.
	/*g_pRTSGRData= (CRTSGRData*)CBaseEntity::Create( "rts_grdata", vec3_origin, vec3_angle );
	g_pRTSGRData->AddEFlags( EFL_KEEP_ON_RECREATE_ENTITIES );	
	g_pRTSGRData->RestoreGRData();		// if there is save data from the gr room, this will restore it.
*/
	RestoreGRData();

	for(int i = 0; i < MAX_TEAMS_ID_RTS; i++)
		m_pSlotPlayers[i] = NULL;

	for(int i = 0; i < TOTAL_COLORS; i++)
		m_bColorInUse.Set( i, false );

	// set first two units
	GetUnitIndex("UNIT_NONE");
	GetUnitIndex("UNIT_UNKNOWN");
}

//-----------------------------------------------------------------------------
// Purpose: We can't do this in spawn because not all entities are initialized there
//-----------------------------------------------------------------------------
void CRTSResource::Activate()
{
	BaseClass::Activate();

	if( m_bLevelInit )		// make sure we won't call this again on restore
	{
		InvalidateTeamIDS();	// set it to the default, mapinfo or mp settings
		m_bLevelInit = false;
	}


}

void CRTSResource::UpdateOnRemove()
{
	for( int i = 0; i < MAX_TEAMS_ID_RTS; i++ )
	{
		m_AbilityStates[i].RemoveAll();
		m_UnitInfoList[i].UnitInfoList.Purge();
	}

	// Unlink ourself. Not sure if this is needed, but entities might be created before this entity is spawned in a new map
	g_pRTSResource = NULL;

	BaseClass::UpdateOnRemove();
}

void CRTSResource::PostClientActive()
{
	for( int i = 0; i < MAX_TEAMS_ID_RTS; i++ )
		InitTechTree(i+DEFAULT_TEAMS);
}

//-----------------------------------------------------------------------------
// Purpose: Restores the current object from disk, by iterating through the objects
//			data description hierarchy
// Input  : &restore - restore buffer which the class data is read from
// Output : int	- 0 if the restore failed, 1 on success
//-----------------------------------------------------------------------------
int CRTSResource::Restore( IRestore &restore )
{
	g_pRTSResource = this;

	// make sure the unit index of unit_none and unit_unknown are on 0 and 1 again.
	GetUnitIndex("UNIT_NONE");
	GetUnitIndex("UNIT_UNKNOWN");

	return BaseClass::Restore( restore );
}

CHL2Wars_Player *CRTSResource::GetPlayerTeamID( int team_id )
{
	CHL2Wars_Player *pPlayer;
	// if single player
	pPlayer = ToHL2WarsPlayer(UTIL_GetLocalPlayer());
	if( pPlayer )
	{
		if( pPlayer->GetTeamRTSID() == team_id )
			return pPlayer;

		return NULL;
	}

	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		pPlayer = (CHL2Wars_Player*)UTIL_PlayerByIndex( i );
		
		if ( pPlayer && pPlayer->IsConnected() )
		{
			if( pPlayer->GetTeamRTSID() == team_id )
				return pPlayer;
		}
	}
	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Requests a team_id for the player. In single player it will always 
//			be 3. In multiplayer, it will search for a free slot, or if the game
//			is made in a gameroom, it uses the info from the save/restore.
//
// Input: - CBasePlayer *pRequestingPlayer
//-----------------------------------------------------------------------------
void CRTSResource::RequestTeamRTSID( CBasePlayer *pRequestingPlayer )
{
	if( !pRequestingPlayer )
		return;

	CBasePlayer *pPlayer = NULL;

	// for now, assume single player
	pPlayer = UTIL_GetLocalPlayer();
	if( pPlayer )
	{
		pPlayer->SetTeamRTSID( 3 );
		return;
	}

	if( m_bGRActive )	// means we come from a gameroom, fill in data
	{
		CHL2Wars_Player *pHLWPlayer = ToHL2WarsPlayer(pRequestingPlayer);
		if( pHLWPlayer )
		{
			IPlayerInfo *playerinfo = pHLWPlayer->GetPlayerInfo();
			if ( playerinfo ) 
			{
				for( int i = 0; i < MAX_TEAMS_ID_RTS; i++ )
				{
					//Msg("Comparing names : %s and %s \n", g_pRTSGRData->m_szName[i], playerinfo->GetName() );
					if( FStrEq( m_szName[i], playerinfo->GetName() ) )
					{
						//pRequestingPlayer->SetTeamRTSID( i + DEFAULT_TEAMS );
						Msg("Putting player %s to teamid %d\n", playerinfo->GetName(), GetPosition(i) + DEFAULT_TEAMS);
						pRequestingPlayer->SetTeamRTSID( GetPosition(i) + DEFAULT_TEAMS );
						m_pSlotPlayers[GetPosition(i)] = pRequestingPlayer; 
						SetIndex( GetPosition(i), ENTINDEX(pRequestingPlayer) );
						pHLWPlayer->SetSlot(i);
						return;
					}
				}	

				// seems we don't have a teamid, so we are a spectator likely
				Msg("Putting player %s to teamid -1\n", playerinfo->GetName());
				pRequestingPlayer->SetTeamRTSID( -1 );
				return;
			}
		}
	}
//#endif

	// not single player, so we will search for a free slot
	// the gameroom implementation comes later
	for( int i = 0; i < MAX_TEAMS_ID_RTS; i++ )
	{
		if( m_pSlotPlayers[i].Get() == NULL )
		{
			m_pSlotPlayers[i] = pRequestingPlayer;
			pRequestingPlayer->SetTeamRTSID( i + DEFAULT_TEAMS );
			Msg("Player %d is now on teamid %d \n", i, pRequestingPlayer->GetTeamRTSID() );
			return;
		}
	}

	DevMsg("Failed to assign the player to a team id! Defaulting to 3. \n");
	pRequestingPlayer->SetTeamRTSID( 3 );
}

//-----------------------------------------------------------------------------
// Purpose: Sets the resources and teamid colors to the defaults or the playerinfo
//-----------------------------------------------------------------------------
void CRTSResource::InvalidateTeamIDS()
{
	// default start resources
	for( int i = 0; i < MAX_TEAMS_ID_RTS; i++)
		m_iResources[i] = 200;

	//
	m_vTeamColorTable.Set( 3, Vector( 0.78f, 0.47f, 0.08f) );		// team rebel is orange
	m_vTeamColorTable.Set( 4, Vector( 0.08f, 0.39f, 0.78f) );	// team combine is blue ofcourse

	if( m_bGRActive )	// means we come from a gameroom, fill in data
	{
		HL2WarsRules()->SetGameMode( GetGameMode() );
		// These are not the teamid's, but the slots from the gameroom
		// we use the position to assign the data to the right team id's
		for( int i = 0; i < MAX_TEAMS_ID_RTS; i++)
		{
			// first find out the position
			int teamid = GetPosition(i);

			if( !UTIL_IsValidTeam( teamid + DEFAULT_TEAMS, false ) )
				continue;

			m_bPositionInUse.Set( teamid, true);
			m_bPositionsAvailable[teamid] = false;
			// now apply the data to the right team id
			Vector color = TranslateColor( GetColorGR(i) );
			//Msg("Color for teamid %d is %d, in v: %f %f %f \n", teamid + DEFAULT_TEAMS, g_pRTSGRData->GetColor(i), color.x, color.y, color.z );
			m_vTeamColorTable.Set( teamid + DEFAULT_TEAMS, color );

			// find the start point
			CRTSStartPointMP *pStart = dynamic_cast<CRTSStartPointMP *>(gEntList.FindEntityByClassname( NULL, "info_mp_startpoint" ));
			CRTSStartPointMP *pCur = pStart;
			do 
			{
				if ( pCur )
				{
					if( pCur->GetTeamID() == (teamid + DEFAULT_TEAMS) )
					{
						switch( GetFaction(i) )
						{
						case FACTION_REBEL:
							UTIL_PlaceBuilding("BUILDING_REB_HQ", pCur->GetAbsOrigin(), pCur->GetAbsAngles(), teamid + DEFAULT_TEAMS, true );
							break;
						case FACTION_COMBINE:
							UTIL_PlaceBuilding("BUILDING_COMB_HQ", pCur->GetAbsOrigin(), pCur->GetAbsAngles(), teamid + DEFAULT_TEAMS, true );
							break;
						default:
							UTIL_PlaceBuilding("BUILDING_REB_HQ", pCur->GetAbsOrigin(), pCur->GetAbsAngles(), teamid + DEFAULT_TEAMS, true );
							break;
						}	
						break;
					}
				}
				// increment pSpot
				pCur = dynamic_cast<CRTSStartPointMP *>(gEntList.FindEntityByClassname( pCur, "info_mp_startpoint" ));
			} while ( pCur != pStart ); // loop if we're not back to the start
		}
	}
	else
//#endif
	{
		CRTSPlayerInfo *pPlayerInfo = dynamic_cast<CRTSPlayerInfo *>(gEntList.FindEntityByClassname( NULL, "rts_playerinfo" ));
		while( pPlayerInfo )
		{
			m_iResources[pPlayerInfo->m_iTeamid-DEFAULT_TEAMS] = pPlayerInfo->m_iResources;
			m_vTeamColorTable.Set( pPlayerInfo->m_iTeamid, ( pPlayerInfo->m_vTeamColorTable / 255.0f ) );
			m_iFaction.Set(pPlayerInfo->m_iTeamid-DEFAULT_TEAMS, pPlayerInfo->m_iFaction );

			pPlayerInfo = dynamic_cast<CRTSPlayerInfo *>(gEntList.FindEntityByClassname( pPlayerInfo, "rts_playerinfo" ));
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Vector CRTSResource::TranslateColor( int color )
{
	switch( color )
	{
	case GRCOLOR_ORANGE:
		return Vector( 0.78f, 0.47f, 0.08f);
		break;
	case GRCOLOR_BLUE:
		return Vector( 0.08f, 0.39f, 0.78f);
		break;
	case GRCOLOR_CYAN:
		return Vector( 0.0f, 1.0f, 1.0f);
		break;
	case GRCOLOR_GREEN:
		return Vector( 0.0f, 1.0f, 0.0f);
		break;
	case GRCOLOR_RED:
		return Vector( 1.0f, 0.0f, 0.0f);
		break;
	default:
		return Vector( 1.0f, 1.0f, 1.0f );
		break;
	}
}

//-----------------------------------------------------------------------------
// Purpose: each color value is in a range of 0.0 to 1.0 (float). This function 
//			converts them to a range of 0 to 255 (integer).
//-----------------------------------------------------------------------------
Vector CRTSResource::GetColor255( int team_id )
{
	Vector color = GetColor( team_id );
	color[0] *= 255;
	color[1] *= 255;
	color[2] *= 255;
	return color;
}

//-----------------------------------------------------------------------------
// Purpose: The Player resource is always transmitted to clients
//-----------------------------------------------------------------------------
int CRTSResource::UpdateTransmitState()
{
	// ALWAYS transmit to all clients.
	return SetTransmitState( FL_EDICT_ALWAYS );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRTSResource::ResourceThink( void )
{
//#ifdef HL2MP
	//UpdateAssignedTeamIDS();	// removed players that left
	//if( HL2WMPRules()->IsGameRoom() )
	if( HL2WarsRules()->IsGameRoom() )
	{
		// remove players that left
		UpdateSlots();

		UpdateGameRoomInfo();
	}
	else
	{
		// remove players that left
		ManageSlots();

		// if the player has a slot, make sure his index is uptodate
		for ( int i = 1; i <= gpGlobals->maxClients; i++ )
		{
			CHL2Wars_Player *pPlayer = (CHL2Wars_Player*)UTIL_PlayerByIndex( i );
			
			if ( pPlayer && pPlayer->IsConnected() )
			{
				// update data
				if( pPlayer->GetSlot() != -1 )
				{
					SetIndex( pPlayer->GetSlot(), i );
				}
			}
		}
	}
//#endif

		/*Msg("SERVER:\n");
		for( int i = 0; i < m_iUnitTypePointer; i++ )
		{
			Msg("%s, index: %d\n", m_sUnitTypes.Get(i), i );
		}
		Msg("\n");*/

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

// -------------- unit list
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CAI_BaseNPC *CRTSResource::GetUnit( int team, int index )
{
	if( !UTIL_IsValidTeam( team, false ) )
		return NULL;

	if( m_UnitInfoList[team  - DEFAULT_TEAMS ].UnitInfoList.Count() == 0 || index >= m_UnitInfoList[team  - DEFAULT_TEAMS ].UnitInfoList.Count())
		return NULL;

	CAI_BaseNPC *pNPC = m_UnitInfoList[team  - DEFAULT_TEAMS ].UnitInfoList.Element( index ).Get();
	if( !pNPC || !pNPC->IsAlive() )
		return NULL;

	return pNPC;
}

//-----------------------------------------------------------------------------
// Purpose: Adds a unit
//-----------------------------------------------------------------------------
void CRTSResource::AddUnit( int team, CAI_BaseNPC *pNPC )
{
	// don't add units to team default, neutral and enemy. Players don't control them.
	if( !UTIL_IsValidTeam( team, false ) )
		return;

	m_UnitInfoList[ team - DEFAULT_TEAMS ].UnitInfoList.AddToTail( pNPC );
}

//-----------------------------------------------------------------------------
// Purpose: Removes a unit. The unit behind in the line moves to the front 
//			position, so we don't have gabs.
//-----------------------------------------------------------------------------
void CRTSResource::RemoveUnit( int team, CAI_BaseNPC *pNPC )
{
	// don't remove units from team default, neutral and enemy. Players don't control them.
	if( !UTIL_IsValidTeam( team, false ) )
		return;

	m_UnitInfoList[ team - DEFAULT_TEAMS ].UnitInfoList.FindAndRemove( pNPC );
}

//-----------------------------------------------------------------------------
// Purpose: Sets over a unit. Team id still need to be changed!
//-----------------------------------------------------------------------------
void CRTSResource::ChangeTeamUnit( int oldTeam, int newTeam, CAI_BaseNPC *pNPC )
{
	if( oldTeam == newTeam ) 
		return;

	RemoveUnit( oldTeam, pNPC );
	AddUnit( newTeam, pNPC );
}

//-----------------------------------------------------------------------------
// Purpose: Returns the size
//-----------------------------------------------------------------------------
int CRTSResource::GetSize( int team )
{ 
	return m_UnitInfoList[ team - DEFAULT_TEAMS ].UnitInfoList.Count();
}

//----------------- resource stuff
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CRTSResource::GetResources( int team )
{
	if( !UTIL_IsValidTeam( team, false ) )
		return 0;

	return m_iResources[team  - DEFAULT_TEAMS ];
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::AddResources( int team, int resources )
{
	if( !UTIL_IsValidTeam( team, false ) )
		return;

	m_iResources[team  - DEFAULT_TEAMS ] += resources;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::RemoveResources( int team, int resources )
{
	if( !UTIL_IsValidTeam( team, false ) )
		return;

	m_iResources[team  - DEFAULT_TEAMS ] -= resources;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::SetResources( int team, int resources )
{
	if( !UTIL_IsValidTeam( team, false ) )
		return;

	m_iResources[team  - DEFAULT_TEAMS ] = resources;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::RegisterBuilding( CBuildingBrush *pBuilding, int teamid )
{
	m_pBuildingList->AddToList( teamid, pBuilding );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::RegisterBuilding( CBaseBuildingModel *pBuilding, int teamid )
{
	//Msg("Building %s registered\n", STRING(pBuilding->GetEntityName()) );
	if( !m_pBuildingModelList->AddToList( teamid, pBuilding ) )
		return;

	// register server side
	unsigned short lookup = LookupAbilityInfoSlot( STRING(pBuilding->GetEntityName()) );
	int index;
	if( m_AbilityStates[teamid-DEFAULT_TEAMS].IsValidIndex( index = m_AbilityStates[teamid-DEFAULT_TEAMS].Find(lookup) ) )
	{
		//Msg("Lookup is %d and valid\n", lookup );
		m_AbilityStates[teamid-DEFAULT_TEAMS].Element(index).m_iCount++;

		CBasePlayer *client = NULL;
		for ( int i = 1; i <= gpGlobals->maxClients; i++ )
		{
			client = UTIL_PlayerByIndex( i );
			if ( !client || !client->edict() )
				continue;

			if ( !(client->IsNetClient()) )	// Not a client ? (should never be true)
				continue;

			CSingleUserRecipientFilter user( client );
			user.MakeReliable();
			UserMessageBegin( user, "AbilityStateUpdate" );
				WRITE_BYTE( AS_MCOUNT );
				WRITE_BYTE( teamid );
				WRITE_STRING( GetFileAbilityName(lookup) );
				WRITE_BYTE(m_AbilityStates[teamid-DEFAULT_TEAMS].Element(index).m_iCount);
			MessageEnd();
		}
	}
	else
	{
		//Msg("Lookup is %d and invalid\n", lookup );
		index = NewAbilityState(teamid, true, true, 1, lookup );
	}

	UpdateTechTree( teamid, STRING(pBuilding->GetEntityName()), m_AbilityStates[teamid-DEFAULT_TEAMS].Element(index).m_iCount );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::UnRegisterBuilding( CBuildingBrush *pBuilding, int teamid )
{
	m_pBuildingList->RemoveFromList( teamid, pBuilding );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::UnRegisterBuilding( CBaseBuildingModel *pBuilding, int teamid )
{
	if( !m_pBuildingModelList->RemoveFromList( teamid, pBuilding ) )
		return;

	// unregister server side
	unsigned short lookup = LookupAbilityInfoSlot( STRING(pBuilding->GetEntityName()) );
	int index;
	if( m_AbilityStates[teamid-DEFAULT_TEAMS].IsValidIndex( index = m_AbilityStates[teamid-DEFAULT_TEAMS].Find(lookup) ) )
	{
		if( m_AbilityStates[teamid-DEFAULT_TEAMS].Element(index).m_iCount > 0 )
		{
			m_AbilityStates[teamid-DEFAULT_TEAMS].Element(index).m_iCount--;

			CBasePlayer *client = NULL;
			for ( int i = 1; i <= gpGlobals->maxClients; i++ )
			{
				client = UTIL_PlayerByIndex( i );
				if ( !client || !client->edict() )
					continue;

				if ( !(client->IsNetClient()) )	// Not a client ? (should never be true)
					continue;

				CSingleUserRecipientFilter user( client );
				user.MakeReliable();
				UserMessageBegin( user, "AbilityStateUpdate" );
					WRITE_BYTE( AS_MCOUNT );
					WRITE_BYTE( teamid );
					WRITE_STRING( GetFileAbilityName(lookup) );
					WRITE_BYTE(m_AbilityStates[teamid-DEFAULT_TEAMS].Element(index).m_iCount);
				MessageEnd();
			}
		}
		else
		{
			Warning("CRTSResource::UnRegisterBuilding: Incorrect building counting\n");
		}
	}
	else
	{
		Warning("CRTSResource::UnRegisterBuilding: Unregistering a building that wasn't registered!\n");
		index = NewAbilityState(teamid, true, true, 0, lookup );
	}

	UpdateTechTree( teamid, STRING(pBuilding->GetEntityName()), m_AbilityStates[teamid-DEFAULT_TEAMS].Element(index).m_iCount );
}

//-----------------------------------------------------------------------------
// Purpose: Basicly a full check of the tech tree.
//-----------------------------------------------------------------------------
void CRTSResource::InitTechTree( int teamid )
{
	FileFactionInfo_t *tree;
	for( int i = 0; i < GetCountTechTrees(); i++ )
	{
		tree = GetTechTree(i);
		if(!tree)
			continue;

		techtree_t *cur = tree->m_pRoot->m_pChild;
		while( cur )
		{
			UpdateTechTree( teamid, cur->m_szName, GetAbilityCount(teamid, cur->m_iHandle) );

			// next
			if( cur->m_pChild )
				cur = cur->m_pChild;
			else
				cur = cur->m_pSibling;
		}

	}
}

//-----------------------------------------------------------------------------
// Purpose: Update the tech tree, regarding the specified change
//			Assume for now that we can only have one entry of each
//-----------------------------------------------------------------------------
void CRTSResource::UpdateTechTree( int teamid, const char *changed, int count )
{
	FileFactionInfo_t *tree;
	for( int i = 0; i < GetCountTechTrees(); i++ )
	{
		tree = GetTechTree(i);
		if(!tree)
			continue;

		techtree_t *info = tree->FindTechInfo(NULL, changed);
		if( !info )
			continue;

		if( count == 0 )
		{
			//Msg("Disable childs of %s, count == 0\n", changed);
			if( info->m_pChild )
			{
				info = info->m_pChild;
				ModifyAbilityState( teamid, false, info->m_iHandle );
				while( info->m_pSibling )
				{
					info = info->m_pSibling;
					ModifyAbilityState( teamid, false, info->m_iHandle );		
				}
			}
			
		}
		else
		{
			//Msg("enable childs of %s, count == %d\n", changed, count );
			if( info->m_pChild )
			{
				info = info->m_pChild;
				ModifyAbilityState( teamid, true, info->m_iHandle );
				while( info->m_pSibling )
				{
					info = info->m_pSibling;
					ModifyAbilityState( teamid, true, info->m_iHandle );	
				}
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CRTSResource::NewAbilityState( int teamid, bool state, bool overridable, int count, ABILITY_FILE_INFO_HANDLE handle )
{
	ability_state_t entry;
	entry.m_bAvailable = state;
	entry.m_bOverridable = overridable;
	entry.m_iCount = count;

	CBasePlayer *client = NULL;
	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		client = UTIL_PlayerByIndex( i );
		if ( !client || !client->edict() )
			continue;

		if ( !(client->IsNetClient()) )	// Not a client ? (should never be true)
			continue;

		CSingleUserRecipientFilter user( client );
		user.MakeReliable();
		// notify the clients with this teamid about the new entry
		UserMessageBegin( user, "AbilityStateUpdate" );
			WRITE_BYTE( AS_NEW );
			WRITE_BYTE( teamid );
			WRITE_STRING( GetFileAbilityName(handle) );
			WRITE_BYTE(state);
			WRITE_BYTE(overridable);
			WRITE_BYTE(count);
		MessageEnd();
	}

	return m_AbilityStates[teamid-DEFAULT_TEAMS].Insert(handle, entry);	
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::ModifyAbilityState( int teamid, bool state, ABILITY_FILE_INFO_HANDLE handle )
{
	// first, find the entry
	int index;
	if( m_AbilityStates[teamid-DEFAULT_TEAMS].IsValidIndex( index = m_AbilityStates[teamid-DEFAULT_TEAMS].Find(handle) ) )
	{
		//Msg("Modifying ability %s, state %d, overridable %d\n", GetFileAbilityName(handle), state, m_AbilityStates[teamid-DEFAULT_TEAMS].Element(index).m_bOverridable );
		if( !m_AbilityStates[teamid-DEFAULT_TEAMS].Element(index).m_bOverridable )
			return;

		m_AbilityStates[teamid-DEFAULT_TEAMS].Element(index).m_bAvailable = state;

		CBasePlayer *client = NULL;
		for ( int i = 1; i <= gpGlobals->maxClients; i++ )
		{
			client = UTIL_PlayerByIndex( i );
			if ( !client || !client->edict() )
				continue;

			if ( !(client->IsNetClient()) )	// Not a client ? (should never be true)
				continue;

			CSingleUserRecipientFilter user( client );
			user.MakeReliable();
			// notify the clients with this teamid about the modified entry
			UserMessageBegin( user, "AbilityStateUpdate" );
				WRITE_BYTE( AS_MSTATE );
				WRITE_BYTE( teamid );
				WRITE_STRING( GetFileAbilityName(handle) );
				WRITE_BYTE(state);
			MessageEnd();
		}
	}
	else
	{
		NewAbilityState(teamid, state, true, 0, handle );
	}	
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::SetAbilityOverridable( int teamid, bool state, ABILITY_FILE_INFO_HANDLE handle )
{
	int index;
	if( m_AbilityStates[teamid-DEFAULT_TEAMS].IsValidIndex( index = m_AbilityStates[teamid-DEFAULT_TEAMS].Find(handle) ) )
	{
		m_AbilityStates[teamid-DEFAULT_TEAMS].Element(index).m_bOverridable = state;
	}
	else
	{
		NewAbilityState(teamid, true, state, 0, handle );
	}
}

//-----------------------------------------------------------------------------
// Purpose: A client becomes active on the server and requires a full update
//-----------------------------------------------------------------------------
void CRTSResource::InitTreePlayer( CBasePlayer *client )
{
	// send the state of all abilities
	for( int j = DEFAULT_TEAMS; j < MAX_TEAMS_ID_RTS + DEFAULT_TEAMS; j++ )
	{
		for( unsigned short i = 0; i < m_AbilityStates[j-DEFAULT_TEAMS].Count(); i++)
		{
			CSingleUserRecipientFilter user( client );
			user.MakeReliable();
			UserMessageBegin( user, "AbilityStateUpdate" );
				WRITE_BYTE( AS_NEW );
				WRITE_BYTE( j );
				WRITE_STRING( GetFileAbilityName(m_AbilityStates[j-DEFAULT_TEAMS].Key(i)) );
				WRITE_BYTE(m_AbilityStates[j-DEFAULT_TEAMS].Element(i).m_bAvailable );
				WRITE_BYTE(m_AbilityStates[j-DEFAULT_TEAMS].Element(i).m_bOverridable);
				WRITE_BYTE(m_AbilityStates[j-DEFAULT_TEAMS].Element(i).m_iCount);
			MessageEnd();		
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CRTSResource::GetAbilityCount( int teamid, ABILITY_FILE_INFO_HANDLE handle )
{
	// first, find the entry
	int index;
	if( m_AbilityStates[teamid-DEFAULT_TEAMS].IsValidIndex( index = m_AbilityStates[teamid-DEFAULT_TEAMS].Find(handle) ) )
	{
		return m_AbilityStates[teamid-DEFAULT_TEAMS].Element(index).m_iCount;
	}
	else
	{
		//Msg("Handle %d not created, creating new entry ability state\n", handle );
		NewAbilityState(teamid, true, true, 0, handle );
		return 0;
	}	
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CRTSResource::IsAbilityAvailable( int teamid, ABILITY_FILE_INFO_HANDLE handle )
{
	// first, find the entry
	int index;
	if( m_AbilityStates[teamid-DEFAULT_TEAMS].IsValidIndex( index = m_AbilityStates[teamid-DEFAULT_TEAMS].Find(handle) ) )
	{
		return m_AbilityStates[teamid-DEFAULT_TEAMS].Element(index).m_bAvailable;
	}
	else
	{
		// No entry yet. Means this one is available, since the tech tree didn't do anything with it.
		NewAbilityState(teamid, true, true, 0, handle );

		return true;
	}	
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CRTSResource::IsUnitAvailable( int teamid, UNIT_FILE_INFO_HANDLE handle )
{
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Maps a unittype into an array and returns the number
//			Also precaches everything that is necesarry.
//-----------------------------------------------------------------------------
int CRTSResource::GetUnitIndex( const char *unit_type )
{
	int unit_index;
	for( int i = 0; i < g_pStringTableUnitInfo->GetNumStrings(); i++ )
	{
		if( !stricmp( unit_type, g_pStringTableUnitInfo->GetString(i) ) )
		{
			unit_index = i;
			return unit_index;
		}
	}

	// this part is only reached when the unit ain't loaded yet. Hopefully not in the middle of a game.
	unit_index = g_pStringTableUnitInfo->AddString( CBaseEntity::IsServer(), unit_type );
	DevMsg(2, "Adding %s to unit index %d \n", unit_type, unit_index);

	// precache
	FileUnitInfo_t *pFUI = GetFileUnitInfoFromHandle( LookupUnitInfoSlot( unit_type ) );
	if( !pFUI )
		return unit_index;	

	if( !pFUI->m_pSoundData )
		return unit_index;	

	// precache the sound files
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("select") );
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("move") );
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("attack") );

	PrecacheScriptSound( pFUI->m_pSoundData->GetString("ability_1") );
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("ability_2") );
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("ability_3") );
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("ability_4") );
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("ability_5") );
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("ability_6") );
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("ability_7") );
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("ability_8") );
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("ability_9") );
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("ability_10") );
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("ability_11") );
	PrecacheScriptSound( pFUI->m_pSoundData->GetString("ability_12") );

	// return the new index
	return unit_index;
}

//-----------------------------------------------------------------------------
// Purpose: Returns the unit type
//-----------------------------------------------------------------------------
string_t CRTSResource::GetUnitType( int unit_index ) 
{ 
	if( unit_index < 0 || unit_index >= g_pStringTableUnitInfo->GetNumStrings() )
		return MAKE_STRING("UNIT_NONE");

	return MAKE_STRING(g_pStringTableUnitInfo->GetString( unit_index )); 
}

// ----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Everything related to the gameroom, put here
//-----------------------------------------------------------------------------
void CRTSResource::UpdateGameRoomInfo()
{
	// check for slots
	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CHL2Wars_Player *pPlayer = (CHL2Wars_Player*)UTIL_PlayerByIndex( i );
		
		if ( pPlayer && pPlayer->IsConnected() )
		{
			
			// update slot ( hl2wars game room )
			if( !HasSlot( pPlayer ) )
			{
				int slot = FindFreeSlot();
				if( slot == -1 )
					continue;

				m_pSlotPlayers[slot] = pPlayer;
				pPlayer->SetSlot( slot );
				SetType( slot, GRTYPE_HUMAN );
			}

			// update data
			if( pPlayer->GetSlot() != -1 )
			{
				SetIndex( pPlayer->GetSlot(), i );

				if( GetColorGR( pPlayer->GetSlot()) == GRCOLOR_NONE )
					SetGRColor( pPlayer->GetSlot(), FindFreeColor() );
			}
		}
	}

	// make sure we have a gamemaster
	if( !m_hGameMaster.Get() )
	{
		m_hGameMaster = FindGameMaster();
		if( m_hGameMaster.Get() )
		{
			SendGRMapList( m_hGameMaster.Get() );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Send the complete maplist to the gamemaster
//-----------------------------------------------------------------------------
void CRTSResource::SendGRMapList( CBasePlayer *client )
{
	FileFindHandle_t findHandle;
	const char *pFilename = filesystem->FindFirstEx( "maps/wmp_*.bsp", "GAME", &findHandle );
	while ( pFilename != NULL )
	{
		char *mapname;
		mapname = (char *)malloc( (strlen( pFilename ) - 3) * sizeof( char ) );
		Q_strncpy(&mapname[0], pFilename, strlen( pFilename ) - 3);
		mapname[ strlen( pFilename ) - 4 ] = '\0';

		// send this map name
		CSingleUserRecipientFilter user( client );
		user.MakeReliable();

		UserMessageBegin( user, "GRMapList" );
			WRITE_STRING( mapname );
		MessageEnd();

		pFilename = filesystem->FindNext( findHandle );
	}
	filesystem->FindClose( findHandle );	
}

//-----------------------------------------------------------------------------
// Purpose: Find the gamemaster if we don't have one
//-----------------------------------------------------------------------------
CBasePlayer *CRTSResource::FindGameMaster()
{
	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
		
		if ( pPlayer && pPlayer->IsConnected() )
		{
			return pPlayer;
		}
	}

	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Manages the slots during a game
//-----------------------------------------------------------------------------
void CRTSResource::ManageSlots()
{
	for ( int i = 0; i < MAX_TEAMS_ID_RTS; i++ )
	{
		if( !m_pSlotPlayers[i] || !m_pSlotPlayers[i]->IsConnected() )
		{
			//Msg("Clearing player slot, clearing color %d\n", i);
			CHL2Wars_Player *pPlayer = ToHL2WarsPlayer( m_pSlotPlayers[i] );
			if( pPlayer )
			{
				SetIndex( pPlayer->GetSlot(), 0 );
				pPlayer->SetSlot( -1 );
			}

			m_pSlotPlayers[i] = NULL;
			//m_bPositionInUse.Set( g_pRTSGRData->GetPosition(i), false );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Updates the slots during the gamelobby
//-----------------------------------------------------------------------------
void CRTSResource::UpdateSlots()
{
	for ( int i = 0; i < MAX_TEAMS_ID_RTS; i++ )
	{
		if( GetType(i) == GRTYPE_CPU )
			continue;

		if( !m_pSlotPlayers[i] || !m_pSlotPlayers[i]->IsConnected() )
		{
			CHL2Wars_Player *pPlayer = ToHL2WarsPlayer( m_pSlotPlayers[i] );
			if( pPlayer )
			{
				SetIndex( pPlayer->GetSlot(), 0 );
				pPlayer->SetSlot( -1 );
			}

			m_pSlotPlayers[i] = NULL;
			m_bColorInUse.Set( GetColorGR(i), false );
			SetGRPosition( i, -1 );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::SlotOpen( int slot )
{
	if( m_pSlotPlayers[slot] || GetType(slot) == GRTYPE_HUMAN )
	{
		Msg("Clearing slot failed. This only works for CPU's\n");
		return;
	}

	m_pSlotPlayers[slot] = NULL;
	SetGRColor(slot, 0 );
	SetGRPosition(slot, -1);
	SetType( slot, GRTYPE_NONE );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::SlotMovePlayer( int old_slot, int new_slot )
{
	// make all checks
	if( GetType(new_slot) == GRTYPE_HUMAN || GetType(new_slot) == GRTYPE_CPU )
	{
		DevMsg("Move player to new slot failed. New slot already in use.\n");
		return;
	}

	if( GetType(old_slot) != GRTYPE_HUMAN || !m_pSlotPlayers[old_slot] )
	{
		DevMsg("No player to move\n");
		return;
	}

	// now move the player and clear the old slot
	CHL2Wars_Player *pPlayer = ToHL2WarsPlayer( m_pSlotPlayers[old_slot] );
	if( pPlayer )
	{
		SetIndex( pPlayer->GetSlot(), 0 );
		pPlayer->SetSlot( new_slot );
		m_pSlotPlayers[new_slot] = m_pSlotPlayers[old_slot];
		SetType( new_slot, GRTYPE_HUMAN );

		m_pSlotPlayers[old_slot] = NULL;
		SetGRColor(old_slot, 0 );
		SetGRPosition(old_slot, -1);
		SetType( old_slot, GRTYPE_NONE );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::SlotAddCPU( int slot  )
{
	if( GetType(slot) == GRTYPE_HUMAN || GetType(slot) == GRTYPE_CPU )
	{
		DevMsg("Add CPU to slot %d failed. Slot already in use.\n", slot);
		return;
	}

	m_pSlotPlayers[slot] = NULL;
	SetType( slot, GRTYPE_CPU );
	SetGRColor( slot, FindFreeColor() );
	SetIndex( slot, 0 );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CRTSResource::FindFreeSlot()
{
	for ( int i = 0; i <= MAX_TEAMS_ID_RTS; i++ )
	{
		if ( m_pSlotPlayers[i] == NULL )
			return i;
	}	

	return -1;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CRTSResource::HasSlot( CBasePlayer *pPlayer )
{
	for ( int i = 0; i <= MAX_TEAMS_ID_RTS; i++ )
	{
		if ( m_pSlotPlayers[i] == pPlayer )
			return true;
	}	

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int CRTSResource::FindFreeColor()
{
	for ( int i = 1; i < TOTAL_COLORS; i++ )
	{
		if( IsColorFree( i ) )
		{
			return i;
		}
	}	

	return -1;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
/*int CRTSResource::FindFreePosition()
{
	for ( int i = 0; i < MAX_TEAMS_ID_RTS; i++ )
	{
		if( !m_bPositionInUse.Get(i) )
		{
			return i;
		}
	}	

	return -1;
}
*/
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::SetGRColor( int slot, int color )
{
	m_bColorInUse.Set( GetColorGR(slot), false );
	m_bColorInUse.Set( color, true );
	//Msg("Seting color %d in use, state is %d \n", color, m_bColorInUse.Get(color) );
	SetColor( slot, color );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::SetGRPosition( int slot, int position )
{
	m_bPositionInUse.Set( GetPosition(slot), false );
	SetPosition( slot, position );
	if(position >= 0 && position < MAX_TEAMS_ID_RTS )
	{
		m_bPositionInUse.Set( position, true );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::SetGRFaction( int slot, int faction )
{
	SetFaction( slot, faction );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::SetGRReady( int slot, int state )
{
	SetReady( slot, (state != 0) );
}

//-----------------------------------------------------------------------------
// Purpose: Seperate function to send the current map info
//			this way we can simply call this in hl2wmp_client
//-----------------------------------------------------------------------------
void CRTSResource::SendMapInfoToPlayer( CBasePlayer *pPlayer )
{
	CSingleUserRecipientFilter user( pPlayer );
	user.MakeReliable();

	UserMessageBegin( user, "GRMapInfo" );
		WRITE_STRING( m_sNum_players );

		// available positions + origins
		WRITE_BYTE( m_bPositionsAvailable[0] );				
		WRITE_VEC3COORD( m_vPositionsOrigin[0] );
		WRITE_BYTE( m_bPositionsAvailable[1] );
		WRITE_VEC3COORD( m_vPositionsOrigin[1] );
		WRITE_BYTE( m_bPositionsAvailable[2] );
		WRITE_VEC3COORD( m_vPositionsOrigin[2] );
		WRITE_BYTE( m_bPositionsAvailable[3] );
		WRITE_VEC3COORD( m_vPositionsOrigin[3] );
		WRITE_BYTE( m_bPositionsAvailable[4] );
		WRITE_VEC3COORD( m_vPositionsOrigin[4] );
		WRITE_BYTE( m_bPositionsAvailable[5] );
		WRITE_VEC3COORD( m_vPositionsOrigin[5] );
		WRITE_BYTE( m_bPositionsAvailable[6] );
		WRITE_VEC3COORD( m_vPositionsOrigin[6] );
		WRITE_BYTE( m_bPositionsAvailable[7] );
		WRITE_VEC3COORD( m_vPositionsOrigin[7] );

		WRITE_STRING( m_iszSelectedMap );
	MessageEnd();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CRTSResource::SetGRMap( const char *pMapName )
{
	if( !pMapName )
		return;

	// when the maps are not the same, send the new info
	if (stricmp(pMapName, m_iszSelectedMap)) 
	{ 
		// reset available positions
		for(int i = 0; i < MAX_TEAMS_ID_RTS; i++ )
			m_bPositionsAvailable[i] = false;

		// Load file into memory
		void *buffer = NULL;
		char szTokenBuffer[MAPKEY_MAXLENGTH];
		int len = filesystem->ReadFileEx( UTIL_VarArgs( "maps/%s.bsp", pMapName ), "GAME", &buffer );

		if ( len )
		{
			const char *pMapData = (char *)buffer;
			const char *result = NULL;

			while( result == NULL )
			{
				pMapData++;
				int c;
				while( ( c = *pMapData) != '{' )
				{	
					pMapData++;
				}

				const char *pClassName = "classname";
				result = strstr( pMapData, pClassName );
			}

			pMapData = result;
			if( !pMapData )
			{
				DevMsg("Missing entities in bsp file!\n");
				return;
			}

			int c;
			while( ( c = *pMapData) != '{' )
			{	
				pMapData++;
			}

			//  Loop through all entities in the map data, creating each.
			for ( ; true; pMapData = MapEntity_SkipToNextEntity(pMapData, szTokenBuffer) )
			{
				//
				// Parse the opening brace.
				//
				char token[MAPKEY_MAXLENGTH];
				pMapData = MapEntity_ParseToken( pMapData, token );

				//
				// Check to see if we've finished or not.
				//
				if (!pMapData)
				{
					//Msg("No map data, breaking\n");
					break;
				}

				if (token[0] != '{')
				{
					DevMsg( "CRTSResource: found %s when expecting {", token);
					continue;
				}	

				CEntityMapData entData( (char*)pMapData );
				char className[MAPKEY_MAXLENGTH];
				
				if (!entData.ExtractValue("classname", className))
				{
					Error( "classname missing from entity!\n" );
				}

				//Msg("Classname: %s\n", className );
				if (stricmp(className, "info_mp_startpoint")) 
				{
					continue;
				}

				//
				// Parse the entity and add it to the spawn list.
				//
				CBaseEntity *pEntity;
				//const char *pCurMapData = pMapData;
				pMapData = MapEntity_ParseEntity(pEntity, pMapData, NULL);
				if (pEntity == NULL)
					continue;

				CRTSStartPointMP *pST = dynamic_cast<CRTSStartPointMP *>(pEntity);
				if( pST == NULL )
					continue;

				m_bPositionsAvailable[pST->GetTeamID()-DEFAULT_TEAMS] = true;
				m_vPositionsOrigin[pST->GetTeamID()-DEFAULT_TEAMS] = pST->GetAbsOrigin();
			}
		}

		int num_players = 0;
		for(int i = 0; i < MAX_TEAMS_ID_RTS; i++ )
		{
			if( m_bPositionsAvailable[i] )
				num_players++;
		}	

		// convert 123 to string [buf]
		itoa(num_players, m_sNum_players, 10);

		Q_strncpy( m_iszSelectedMap, pMapName, 32 );

		for ( int i = 1; i <= gpGlobals->maxClients; i++ )
		{
			CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
			
			if ( pPlayer && pPlayer->IsConnected() )
			{
				// send this map name
				SendMapInfoToPlayer( pPlayer );
			}
		}
	
	}	
}

//-----------------------------------------------------------------------------
// Purpose: Save gameroom data entity and Launch game
//-----------------------------------------------------------------------------
void CRTSResource::GRLaunchGame( CBasePlayer *pPlayer )
{
	// check if everybody is ready
	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CHL2Wars_Player *pPlayer = (CHL2Wars_Player*)UTIL_PlayerByIndex( i );		
		if ( pPlayer && pPlayer->IsConnected() )
		{	
			if( pPlayer->GetSlot() == -1 )
				continue;

			if( !IsReady(pPlayer->GetSlot()) )		
			{
				engine->ServerCommand( UTIL_VarArgs( "say Some players are not ready yet.\n") );
				return;
			}
		}
	}

	// check if everybody has a position
	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CHL2Wars_Player *pPlayer = (CHL2Wars_Player*)UTIL_PlayerByIndex( i );		
		if ( pPlayer && pPlayer->IsConnected() )
		{	
			if( pPlayer->GetSlot() == -1 )
				continue;

			if( GetPosition(pPlayer->GetSlot()) == -1 )		
			{
				engine->ServerCommand( UTIL_VarArgs( "say Some players didn't selected a position yet.\n") );
				return;
			}
		}
	}

	// save settings
	m_bGRActive = true;
	SetGameMode( GAMEMODE_CLASSIC );
	SaveGRData();

	// now change map
	engine->ChangeLevel( m_iszSelectedMap, NULL );
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CRTSResource::SaveGRData()
{
	g_GRData.m_bGRActive = true;
	g_GRData.m_iGamemode = m_iGamemode;
	for ( int i = 0; i < MAX_TEAMS_ID_RTS; i++ )
	{
		g_GRData.m_iPosition[i] = m_iPosition.Get( i );	
		g_GRData.m_iColor[i] = m_iColor.Get( i ) ;
		g_GRData.m_iFaction[i] = m_iFaction.Get( i );
		g_GRData.m_iType[i] = m_iType.Get( i );
	}

	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CHL2Wars_Player *pPlayer = (CHL2Wars_Player*)UTIL_PlayerByIndex( i );
		
		if ( !pPlayer || !pPlayer->IsConnected() )
			continue;
			
		IPlayerInfo *playerinfo = pPlayer->GetPlayerInfo();
		if (playerinfo) 
		{
			Q_strncpy( g_GRData.m_szName[pPlayer->GetSlot()], playerinfo->GetName(), MAX_PLAYER_NAME_LENGTH );
		}
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CRTSResource::RestoreGRData()
{
	// check if this data is valid
	if( g_GRData.m_bGRActive == 0 )
	{
		return false;		// no restore data
	}

	m_bGRActive = true;

	m_iGamemode = g_GRData.m_iGamemode;

	// now restore the data
	for ( int i = 0; i < MAX_TEAMS_ID_RTS; i++ )
	{
		Q_strncpy( m_szName[i], g_GRData.m_szName[i], MAX_PLAYER_NAME_LENGTH );

		m_iPosition.Set( i, g_GRData.m_iPosition[i] );

		m_iColor.Set( i, g_GRData.m_iColor[i] );
	
		m_iFaction.Set( i, g_GRData.m_iFaction[i] );

		m_iType.Set( i, g_GRData.m_iType[i] );
	}

	return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRTSResource::SetIndex( int slot, int index )
{
	m_iIndex.Set( slot, index );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRTSResource::SetPosition( int slot, int pos )
{
	m_iPosition.Set( slot, pos );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRTSResource::SetColor( int slot, int color )
{
	m_iColor.Set( slot, color );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRTSResource::SetReady( int slot, bool state )
{
	m_bReady.Set( slot, state );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRTSResource::SetFaction( int slot, int faction )
{
	m_iFaction.Set( slot, faction );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CRTSResource::SetType( int slot, int type )
{
	m_iType.Set( slot, type );
}

//------------------------------------------------------------------------------
void CC_GR_RequestPosition( const CCommand &args )
{
	Msg("Requesting a position\n");
	CHL2Wars_Player *pPlayer = ToHL2WarsPlayer( UTIL_GetCommandClient() ); 
	if ( pPlayer && pPlayer != ToHL2WarsPlayer(g_pRTSResource->FindGameMaster()) )
	{
		if ( args.ArgC() == 0 )
			return;

		int position = atoi(args[1]);
		if( position == -1 )
		{
			g_pRTSResource->SetGRPosition( pPlayer->GetSlot(), -1 );
		}
		else if( g_pRTSResource->IsPositionFree( position ) && g_pRTSResource->IsPositionAvailable(position) )
		{
			g_pRTSResource->SetGRPosition( pPlayer->GetSlot(), position );
		}
		else
		{
			DevMsg(1, "Requesting a position that is not free or available! \n");
		}
	}
	else
	{
		if ( args.ArgC() == 1 )
			return;	

		int position = atoi(args[1]);
		int slot = atoi(args[2]);
		if( position == -1 )
		{
			g_pRTSResource->SetGRPosition( slot, -1 );
		}
		else if( g_pRTSResource->IsPositionFree( position ) && g_pRTSResource->IsPositionAvailable(position) )
		{
			g_pRTSResource->SetGRPosition(slot, position );
		}
		else
		{
			DevMsg(1, "Requesting a position that is not free or available! \n");
		}
	}
}
static ConCommand gr_requestposition("gr_requestposition", CC_GR_RequestPosition, "Request a position");

//------------------------------------------------------------------------------
void CC_GR_RequestColor( const CCommand &args )
{
	Msg("Found a request color \n");
	CHL2Wars_Player *pPlayer = ToHL2WarsPlayer( UTIL_GetCommandClient() ); 
	if ( pPlayer && pPlayer != ToHL2WarsPlayer(g_pRTSResource->FindGameMaster()) )
	{
		if ( args.ArgC() == 0 )
			return;

		int color = atoi(args[1]);
		if( g_pRTSResource->IsColorFree( color ) )
			g_pRTSResource->SetGRColor( pPlayer->GetSlot(), color );
		else
			DevMsg(1, "Requesting a color that is not free! \n");
	}
	else
	{
		if ( args.ArgC() == 1 )
			return;

		int color = atoi(args[1]);
		int slot = atoi(args[2]);
		if( g_pRTSResource->IsColorFree( color ) )
			g_pRTSResource->SetGRColor( slot, color );
		else
			DevMsg(1, "Requesting a color that is not free! \n");
	}
}
static ConCommand gr_requestcolor("gr_requestcolor", CC_GR_RequestColor, "Request a color");

//------------------------------------------------------------------------------
void CC_GR_Ready( const CCommand &args )
{
	CHL2Wars_Player *pPlayer = ToHL2WarsPlayer( UTIL_GetCommandClient() ); 
	if ( pPlayer )
	{
		if ( args.ArgC() == 0 )
			return;

		g_pRTSResource->SetGRReady( pPlayer->GetSlot(), atoi(args[1]) );
	}
}
static ConCommand gr_ready("gr_ready", CC_GR_Ready, "Toggle ready signal");

//------------------------------------------------------------------------------
void CC_GR_SlotAction( const CCommand &args )
{
	DevMsg("Slot action\n");
	CHL2Wars_Player *pPlayer = ToHL2WarsPlayer( UTIL_GetCommandClient() ); 
	if ( pPlayer )
	{
		if ( args.ArgC() == 0 )
			return;

		switch( atoi(args[1]) )
		{
		case GROPEN_SLOT:
			{
				if( ToHL2WarsPlayer(g_pRTSResource->FindGameMaster()) != pPlayer )
				{
					DevMsg("Invalid slot action\n");
					return;
				}

				if( args.ArgC() == 1 )
					return;

				g_pRTSResource->SlotOpen( atoi(args[2] ) );
			}
			break;
		case GROCCUPY_SLOT:
			{
				if( args.ArgC() == 1 )
					return;

				g_pRTSResource->SlotMovePlayer( pPlayer->GetSlot(), atoi(args[2]) );
			}
			break;
		case GRCPUOCCUPY_SLOT:
			{
				if( ToHL2WarsPlayer(g_pRTSResource->FindGameMaster()) != pPlayer )
				{
					DevMsg("Invalid slot action\n");
					return;
				}

				if( args.ArgC() == 1 )
					return;

				g_pRTSResource->SlotAddCPU( atoi(args[2]) );
			}
			break;
		default:
			break;
		}
	}
}
static ConCommand gr_slotaction("gr_slotaction", CC_GR_SlotAction, "Do an action on a player slot, if possible");

//------------------------------------------------------------------------------
void CC_GR_RequestFaction( const CCommand &args )
{
	Msg("Requesting faction\n");
	CHL2Wars_Player *pPlayer = ToHL2WarsPlayer( UTIL_GetCommandClient() ); 
	if ( pPlayer && pPlayer != ToHL2WarsPlayer(g_pRTSResource->FindGameMaster()) )
	{
		if ( args.ArgC() == 0 )
			return;

		int faction = atoi(args[1]);
		
		g_pRTSResource->SetGRFaction( pPlayer->GetSlot(), faction );
	}
	else
	{
		if ( args.ArgC() == 1 )
			return;

		int faction = atoi(args[1]);
		int slot = atoi(args[2]);
		
		g_pRTSResource->SetGRFaction( slot, faction );
	}
}
static ConCommand gr_requestfaction("gr_requestfaction", CC_GR_RequestFaction, "Request a faction");

//------------------------------------------------------------------------------
void CC_GR_RequestMap( const CCommand &args )
{
	Msg("Selecting a map\n");
	CHL2Wars_Player *pPlayer = ToHL2WarsPlayer( UTIL_GetCommandClient() ); 
	if ( pPlayer )
	{
		if ( args.ArgC() == 0 )
			return;

		g_pRTSResource->SetGRMap( args[1] );
	}
}
static ConCommand gr_requestmap("gr_requestmap", CC_GR_RequestMap, "Select a map");

//------------------------------------------------------------------------------
void CC_GR_LaunchGame( const CCommand &args )
{
	Msg("Going to launch the game \n");
	CHL2Wars_Player *pPlayer = ToHL2WarsPlayer( UTIL_GetCommandClient() ); 
	if ( pPlayer )
	{
		g_pRTSResource->GRLaunchGame( pPlayer );
	}
}
static ConCommand gr_launchgame("gr_launchgame", CC_GR_LaunchGame, "Launch a game from a gameroom");
