/***
*
*	Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*	
*	This product contains software technology licensed from Id 
*	Software, Inc. ("Id Technology").  Id Technology (c) 1996 Id Software, Inc. 
*	All Rights Reserved.
*
*   Use, distribution, and modification of this source code and/or resulting
*   object code is restricted to non-commercial enhancements to products from
*   Valve LLC.  All other use, distribution, or modification is prohibited
*   without written permission from Valve LLC.
*
****/
//
// teamplay_gamerules.cpp
//
#include	"extdll.h"
#include	"util.h"
#include	"cbase.h"
//added by harSens
#include	"effects.h"
#include	"aura.h"
#include	"classes.h"
#include	"shake.h"
#include	"magicattack.h"
#include	"explodingattack.h"
#include <string.h>

#include	"player.h"
#include	"weapons.h"
#include	"gamerules.h"
#include	"teamplay_gamerules.h"
#include	"game.h"

//added by Rick
#include	"../bot/bot.h"
#include	"../bot/bot_util.h"
#include	"../bot/bot_ent.h"
#include	"talkmonster.h"
#include	"buddy_aura.h"
#include	"buddy.h"
extern CBotManager BotManager;
// end add by Rick

static char team_names[MAX_TEAMS][MAX_TEAMNAME_LENGTH];
static int team_scores[MAX_TEAMS];
static int num_teams = 0;

extern DLL_GLOBAL BOOL		g_fGameOver;
//added by harSens
extern int gmsgVGUIMenu;
extern cvar_t realteams;
extern int gmsgAllowSpec;
extern cvar_t allowspectator;
extern cvar_t characterlimit;


//added by harSens
#define GOOD_TEAM_LENGTH 6
#define EVIL_TEAM_LENGTH 5
const char *g_pGoodClasses[]=
{	"goku",
	"gohan",
	"krillin",
	"piccolo",
	"trunks",
	"a18"
};

const char *g_pEvilClasses[]=
{	"buu",
	"frieza",
	"vegeta",
	"cell",
	"ginyu"
};
extern void respawn( entvars_t* pev, BOOL fCopyCorpse );

CHalfLifeTeamplay :: CHalfLifeTeamplay()
{
	m_DisableDeathMessages = FALSE;
	m_DisableDeathPenalty = FALSE;

	memset( team_names, 0, sizeof(team_names) );
	memset( team_scores, 0, sizeof(team_scores) );
	num_teams = 0;

	// Copy over the team from the server config
	m_szTeamList[0] = 0;

	// Cache this because the team code doesn't want to deal with changing this in the middle of a game
	strncpy( m_szTeamList, teamlist.string, TEAMPLAY_TEAMLISTLENGTH );

	edict_t *pWorld = INDEXENT(0);
	if ( pWorld && pWorld->v.team )
	{
		if ( teamoverride.value )
		{
			const char *pTeamList = STRING(pWorld->v.team);
			if ( pTeamList && strlen(pTeamList) )
			{
				strncpy( m_szTeamList, pTeamList, TEAMPLAY_TEAMLISTLENGTH );
			}
		}
	}

	//added by harSens
	flClientAllowSpectator = allowspectator.value;
	
	// Has the server set teams
	if ( strlen( m_szTeamList ) )
		m_teamLimit = TRUE;
	else
		m_teamLimit = FALSE;

	RecountTeams();
}

extern cvar_t timeleft, fragsleft;

#include "voice_gamemgr.h"
extern CVoiceGameMgr	g_VoiceGameMgr;

void CHalfLifeTeamplay :: Think ( void )
{
	///// Check game rules /////
	static int last_frags;
	static int last_time;
	static float flLastSBotTeamCheck = 0.0f;

	int frags_remaining = 0;
	int time_remaining = 0;

	//added by harSens: update allow spectator status at the client
	if (allowspectator.value != flClientAllowSpectator)
	{	//set spectator status
		MESSAGE_BEGIN(MSG_ALL, gmsgAllowSpec);
			WRITE_BYTE(allowspectator.value);		
		MESSAGE_END();
		flClientAllowSpectator = allowspectator.value;
	}

	//added by harSens: update character limit when the cvar changes (bleh, not overloaded in multiplaygamerules:)
	if (characterlimit.value != flClientCharacterLimit)
	{	UpdateGameMode(NULL);		
		flClientCharacterLimit = characterlimit.value;
	}

	g_VoiceGameMgr.Update(gpGlobals->frametime);

	if ( g_fGameOver )   // someone else quit the game already
	{
		CHalfLifeMultiplay::Think();
		return;
	}

	float flTimeLimit = CVAR_GET_FLOAT("mp_timelimit") * 60;
	
	time_remaining = (int)(flTimeLimit ? ( flTimeLimit - gpGlobals->time ) : 0);

	if ( flTimeLimit != 0 && gpGlobals->time >= flTimeLimit )
	{
		GoToIntermission();
		return;
	}

	float flFragLimit = fraglimit.value;
	if ( flFragLimit )
	{
		int bestfrags = 9999;
		int remain;

		// check if any team is over the frag limit
		for ( int i = 0; i < num_teams; i++ )
		{
			if ( team_scores[i] >= flFragLimit )
			{
				GoToIntermission();
				return;
			}

			remain = flFragLimit - team_scores[i];
			if ( remain < bestfrags )
			{
				bestfrags = remain;
			}
		}
		frags_remaining = bestfrags;
	}

	// Updates when frags change
	if ( frags_remaining != last_frags )
	{
		g_engfuncs.pfnCvar_DirectSet( &fragsleft, UTIL_VarArgs( "%i", frags_remaining ) );
	}

	// Updates once per second
	if ( timeleft.value != last_time )
	{
		g_engfuncs.pfnCvar_DirectSet( &timeleft, UTIL_VarArgs( "%i", time_remaining ) );
	}

	last_frags = frags_remaining;
	last_time  = time_remaining;

	// Added by Rick: If there are superbots ingame normal players are forced to stay in team good
	if (flLastSBotTeamCheck < gpGlobals->time && BotManager.AreSBotsIngame())
	{
		flLastSBotTeamCheck = gpGlobals->time + 0.75f; // No need to check this every frame
		CBasePlayer *pPlayer;
		short s;

		for (s=1;s<=gpGlobals->maxClients;s++)
		{
			pPlayer = (CBasePlayer *)UTIL_PlayerByIndex(s);
			if (!UTIL_IsValidPlayer(pPlayer, true, false) || UTIL_IsBot(pPlayer))
				continue; // Skip invalid players and bots

			if (pPlayer->pev->team == EVIL_TEAM)
				JoinTeam(pPlayer, TEAM1_NAME); // Let this player join team good
		} 
	}
	// End add by Rick
}

/*
added by harSens: class check
*/
BOOL CHalfLifeTeamplay::IsValidClass(const char *pClass, CBasePlayer *pPlayer)
{	if(realteams.value == 1.0)
	{	/* if realteams is enabled, you can only join 
		   the teams in the list of your team
		*/
		char pClassList[255][255];
		int iNumberOfClasses;		
		if (pPlayer->pev->team == EVIL_TEAM)
		{	iNumberOfClasses = EVIL_TEAM_LENGTH;
			for(int i=0;i<iNumberOfClasses;i++)
			{	strcpy(pClassList[i], g_pEvilClasses[i]);
			}
		}
		else if (pPlayer->pev->team == GOOD_TEAM)
		{	iNumberOfClasses = GOOD_TEAM_LENGTH;
			for(int i=0;i<iNumberOfClasses;i++)
			{	strcpy(pClassList[i], g_pGoodClasses[i]);
			}
		}
		else
		{	//not joined a team yet, can't change class
			return FALSE;
		}

		BOOL fFound = false;
		for(int i=0;i<iNumberOfClasses;i++)
		{	if (!strcmp(pClassList[i],pClass))
			{	fFound = true;
				break;
			}
		}
		if (!fFound)
		{	return false;
		}
	}
	return CHalfLifeMultiplay::IsValidClass(pClass, pPlayer);
}

//=========================================================
// ClientCommand
// the user has typed a command which is unrecognized by everything else;
// this check to see if the gamerules knows anything about the command
//=========================================================
BOOL CHalfLifeTeamplay :: ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
{
	if(g_VoiceGameMgr.ClientCommand(pPlayer, pcmd))
		return TRUE;

	if ( FStrEq( pcmd, "menuselect" ) )
	{
		if ( CMD_ARGC() < 2 )
			return TRUE;
		/* removed by harSens: not used	
		int slot = atoi( CMD_ARGV(1) );
		*/	

		// select the item from the current menu

		return TRUE;
	}
	//added by harSens
	else if ( FStrEq( pcmd, "jointeam" ) )
	{
		// Added by Pcjoe, dont allow meleeing players to join teams
		if(pPlayer->m_Melee.InAdvMeleeBattle() || pPlayer->m_Melee.InThrow() || 
			pPlayer->m_fPowerStruggling || pPlayer->m_fBlockStruggleAttacker || 
			pPlayer->m_fBlockStruggling)
		{	return TRUE;
		}
		
		// added by harSens, no team change while being turned into candy or being strangled
		if (pPlayer->m_fStrangled || pPlayer->m_fBecomeCandy)
		{
			return TRUE;
		}

		// Added by Rick: Check if this player is a bot
		if (UTIL_IsBot(pPlayer))
		{
			// Check wich team the bot wants to join
			CBot *pBot = UTIL_GetBotPointer(pPlayer);
			if (pBot)
			{
				switch(pBot->m_sBotTeam)
				{	case 1: JoinTeam(pPlayer, TEAM1_NAME); break;
					case 2: JoinTeam(pPlayer, TEAM2_NAME); break;
					case 5: JoinTeam(pPlayer,WeakestTeam(pPlayer));break;					
				}
				pPlayer->m_flLastTeamChange = gpGlobals->time;
				return TRUE;
			}
		}
		if (gpGlobals->time > pPlayer->m_flLastTeamChange + TEAM_CHANGE_DELAY || pPlayer->pev->team==0 )
		{	int slot = atoi( CMD_ARGV(1) );//we need to re-identify the slot
			switch(slot)
			{	case 1: JoinTeam(pPlayer, TEAM1_NAME); break;//slot 1
				case 2: JoinTeam(pPlayer, TEAM2_NAME); break;//slot 2
				case 5: JoinTeam(pPlayer,WeakestTeam(pPlayer));break;					
			}
			pPlayer->m_flLastTeamChange = gpGlobals->time;
			return TRUE;			
		}
	}

	/* modified by harSens
	return FALSE;
	*/
	return CHalfLifeMultiplay::ClientCommand(pPlayer, pcmd);
}

extern int gmsgGameMode;
extern int gmsgSayText;
extern int gmsgTeamInfo;
extern int gmsgTeamNames;
extern int gmsgScoreInfo;

void CHalfLifeTeamplay :: UpdateGameMode( CBasePlayer *pPlayer )
{
	/* modified by harSens 
	MESSAGE_BEGIN( MSG_ONE, gmsgGameMode, NULL, pPlayer->edict() );*/
	if (pPlayer)
	{
		MESSAGE_BEGIN( MSG_ONE, gmsgGameMode, NULL, pPlayer->edict() );
	}
	else
	{
		MESSAGE_BEGIN( MSG_ALL, gmsgGameMode);
	}
		WRITE_BYTE( 1 );  // game mode teamplay
		//added by harSens
		WRITE_BYTE( (unsigned char)characterlimit.value );
	MESSAGE_END();
}


const char *CHalfLifeTeamplay::SetDefaultPlayerTeam( CBasePlayer *pPlayer )
{	/*removed by harSens
	// copy out the team name from the model
	char *mdls = g_engfuncs.pfnInfoKeyValue( g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "model" );
	strncpy( pPlayer->m_szTeamName, mdls, TEAM_NAME_LENGTH );

	RecountTeams();

	// update the current player of the team he is joining
	if ( pPlayer->m_szTeamName[0] == '\0' || !IsValidTeam( pPlayer->m_szTeamName ) || defaultteam.value )
	{
		const char *pTeamName = NULL;
		
		if ( defaultteam.value )
		{
			pTeamName = team_names[0];
		}
		else
		{
			pTeamName = TeamWithFewestPlayers();
		}
		strncpy( pPlayer->m_szTeamName, pTeamName, TEAM_NAME_LENGTH );
	}

	return pPlayer->m_szTeamName;
	*/
	return NULL;
}


//=========================================================
// InitHUD
//=========================================================
void CHalfLifeTeamplay::InitHUD( CBasePlayer *pPlayer )
{
	int i;
	/*removed by harSens: choose a team
	SetDefaultPlayerTeam( pPlayer );
	*/
	CHalfLifeMultiplay::InitHUD( pPlayer );

	// Send down the team names
	/* modified by harSens
	MESSAGE_BEGIN( MSG_ONE, gmsgTeamNames, NULL, pPlayer->edict() );  
		WRITE_BYTE( num_teams );
		for ( i = 0; i < num_teams; i++ )
		{
			WRITE_STRING( team_names[ i ] );
		}
	MESSAGE_END();
	*/
	extern int gmsgTeams;
	MESSAGE_BEGIN(MSG_ONE, gmsgTeamNames, NULL, pPlayer->edict());
		WRITE_BYTE(2);					// 2 teams. (You can have up to 4.)
		WRITE_STRING(TEAM1_NAME);		// team names
		WRITE_STRING(TEAM2_NAME);
	MESSAGE_END();

	//added by harSens: show team menu
	MESSAGE_BEGIN( MSG_ONE, gmsgVGUIMenu, NULL, pPlayer->pev );
		WRITE_BYTE( 2 );	// This is the menu number that needs to be sent
	MESSAGE_END();

	RecountTeams();

	/*removed by harSens: don't join a team yet	
	char *mdls = g_engfuncs.pfnInfoKeyValue( g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "model" );
	// update the current player of the team he is joining
	char text[1024];
	if ( !strcmp( mdls, pPlayer->m_szTeamName ) )
	{
		sprintf( text, "* you are on team \'%s\'\n", pPlayer->m_szTeamName );
	}
	else
	{
		sprintf( text, "* assigned to team %s\n", pPlayer->m_szTeamName );
	}

	ChangePlayerTeam( pPlayer, pPlayer->m_szTeamName, FALSE, FALSE );
	UTIL_SayText( text, pPlayer );
	int clientIndex = pPlayer->entindex();
	RecountTeams();
	*/
	// update this player with all the other players team info
	// loop through all active players and send their team info to the new client
	for ( i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CBaseEntity *plr = UTIL_PlayerByIndex( i );
		/*modified by harSens:IsValidTeam doesn't work :(
		if ( plr && IsValidTeam( plr->TeamID() ) )
		*/
		if ( plr )
		{
			MESSAGE_BEGIN( MSG_ONE, gmsgTeamInfo, NULL, pPlayer->edict() );
				WRITE_BYTE( plr->entindex() );
				WRITE_STRING( plr->TeamID() );
			MESSAGE_END();
		}
	}
}


void CHalfLifeTeamplay::ChangePlayerTeam( CBasePlayer *pPlayer, const char *pTeamName, BOOL bKill, BOOL bGib )
{
	int damageFlags = DMG_GENERIC;
	int clientIndex = pPlayer->entindex();

	if ( !bGib )
	{
		damageFlags |= DMG_NEVERGIB;
	}
	else
	{
		damageFlags |= DMG_ALWAYSGIB;
	}

	if ( bKill )
	{
		// kill the player,  remove a death,  and let them start on the new team
		m_DisableDeathMessages = TRUE;
		/*disabled by harSens		
		m_DisableDeathPenalty = TRUE;
		*/
		

		entvars_t *pevWorld = VARS( INDEXENT(0) );
		pPlayer->TakeDamage( pevWorld, pevWorld, 900, damageFlags );
		
		m_DisableDeathMessages = FALSE;
		m_DisableDeathPenalty = FALSE;
	}

	// copy out the team name from the model
	strncpy( pPlayer->m_szTeamName, pTeamName, TEAM_NAME_LENGTH );

	/* disabled by harSens
	g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "model", pPlayer->m_szTeamName );
	*/
	g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "team", pPlayer->m_szTeamName );
	//added by harSens: set proper skin for the player
	if (!strcmp(pTeamName,"Evil"))
	{	pPlayer->pev->team = EVIL_TEAM;
	}
	else
	{	pPlayer->pev->team = GOOD_TEAM;
	}

	// notify everyone's HUD of the team change
	MESSAGE_BEGIN( MSG_ALL, gmsgTeamInfo );
		WRITE_BYTE( clientIndex );
		WRITE_STRING( pPlayer->m_szTeamName );
	MESSAGE_END();

	MESSAGE_BEGIN( MSG_ALL, gmsgScoreInfo );
		WRITE_BYTE( clientIndex );
		WRITE_SHORT( pPlayer->pev->frags );
		WRITE_SHORT( pPlayer->m_iDeaths );
		//added by harSens
		WRITE_SHORT( pPlayer->m_iWishes );
		/*modified by harSens
		WRITE_SHORT( 0 );
		*/		
		WRITE_SHORT( pPlayer->pev->playerclass );
		WRITE_SHORT( g_pGameRules->GetTeamIndex( pPlayer->m_szTeamName ) + 1 );
		WRITE_SHORT(pPlayer->m_pClass->ScoreboardName());
	MESSAGE_END();
}


//=========================================================
// ClientUserInfoChanged
//=========================================================
/* disabled by harSens
void CHalfLifeTeamplay::ClientUserInfoChanged( CBasePlayer *pPlayer, char *infobuffer )
{
	char text[1024];

	// prevent skin/color/model changes
	char *mdls = g_engfuncs.pfnInfoKeyValue( infobuffer, "model" );

	if ( !stricmp( mdls, pPlayer->m_szTeamName ) )
		return;

	if ( defaultteam.value )
	{
		int clientIndex = pPlayer->entindex();

		g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "model", pPlayer->m_szTeamName );
		g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "team", pPlayer->m_szTeamName );
		sprintf( text, "* Not allowed to change teams in this game!\n" );
		UTIL_SayText( text, pPlayer );
		return;
	}

	if ( defaultteam.value || !IsValidTeam( mdls ) )
	{
		int clientIndex = pPlayer->entindex();

		g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "model", pPlayer->m_szTeamName );
		sprintf( text, "* Can't change team to \'%s\'\n", mdls );
		UTIL_SayText( text, pPlayer );
		sprintf( text, "* Server limits teams to \'%s\'\n", m_szTeamList );
		UTIL_SayText( text, pPlayer );
		return;
	}
	// notify everyone of the team change
	sprintf( text, "* %s has changed to team \'%s\'\n", STRING(pPlayer->pev->netname), mdls );
	UTIL_SayTextAll( text, pPlayer );

	UTIL_LogPrintf( "\"%s<%i><%s><%s>\" joined team \"%s\"\n", 
		STRING(pPlayer->pev->netname),
		GETPLAYERUSERID( pPlayer->edict() ),
		GETPLAYERAUTHID( pPlayer->edict() ),
		pPlayer->m_szTeamName,
		mdls );

	ChangePlayerTeam( pPlayer, mdls, TRUE, TRUE );
	// recound stuff
	RecountTeams( TRUE );
}
*/
extern int gmsgDeathMsg;

//=========================================================
// Deathnotice. 
//=========================================================
void CHalfLifeTeamplay::DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pevInflictor )
{
	if ( m_DisableDeathMessages )
		return;
	
	if ( pVictim && pKiller && pKiller->flags & FL_CLIENT )
	{
		CBasePlayer *pk = (CBasePlayer*) CBaseEntity::Instance( pKiller );

		if ( pk )
		{
			if ( (pk != pVictim) && (PlayerRelationship( pVictim, pk ) == GR_TEAMMATE) )
			{
				MESSAGE_BEGIN( MSG_ALL, gmsgDeathMsg );
					WRITE_BYTE( ENTINDEX(ENT(pKiller)) );		// the killer
					WRITE_BYTE( ENTINDEX(pVictim->edict()) );	// the victim
					WRITE_STRING( "teammate" );		// flag this as a teammate kill
				MESSAGE_END();
				return;
			}
		}
	}
	//Added by Rick
	else if ( pVictim && pKiller && FStrEq(STRING(pKiller->classname), "player_buddy") )
	{
		CBuddy *pk = (CBuddy*) CBaseEntity::Instance( pKiller );

		if ( pk )
		{
			if (pVictim->pev->team == pk->pev->team )
			{
				MESSAGE_BEGIN( MSG_ALL, gmsgDeathMsg );
					WRITE_BYTE( -2 );		// the killer, -2 means its a buddy
					WRITE_BYTE( ENTINDEX(pVictim->edict()) );	// the victim
					WRITE_STRING( "teammate" );		// flag this as a teammate kill
					WRITE_STRING( STRING(pKiller->netname) );
				MESSAGE_END();
				return;
			}
		}
	}
	// End add by Rick
	CHalfLifeMultiplay::DeathNotice( pVictim, pKiller, pevInflictor );
}
// Added by Rick
//=========================================================
// Deathnotice. Buddy version
//=========================================================
void CHalfLifeTeamplay::BuddyDeathNotice( CBuddy *pBuddy, entvars_t *pKiller, entvars_t *pevInflictor )
{
	if ( m_DisableDeathMessages )
		return;
	
	if ( pBuddy && pKiller && pKiller->flags & FL_CLIENT )
	{
		CBasePlayer *pk = (CBasePlayer*) CBaseEntity::Instance( pKiller );

		if ( pk )
		{
			if ( pk->pev->team == pBuddy->pev->team )
			{
				MESSAGE_BEGIN( MSG_ALL, gmsgDeathMsg );
					WRITE_BYTE( ENTINDEX(ENT(pKiller)) );		// the killer
					WRITE_BYTE( -2 );	// the victim, -2 means its a buddy
					WRITE_STRING( "teammate" );		// flag this as a teammate kill
					WRITE_STRING(STRING(pBuddy->pev->netname));
				MESSAGE_END();
				return;
			}
		}
	}
	else if ( pBuddy && pKiller && FStrEq(STRING(pKiller->classname), "player_buddy") )
	{
		CBuddy *pk = (CBuddy*) CBaseEntity::Instance( pKiller );

		if ( pk )
		{
			if (pBuddy->pev->team == pk->pev->team )
			{
				MESSAGE_BEGIN( MSG_ALL, gmsgDeathMsg );
					WRITE_BYTE( -2 );				// the killer, -2 means its a buddy
					WRITE_BYTE( -2 );				// the victim, -2 means its a buddy
					WRITE_STRING( "teammate" );		// flag this as a teammate kill
					WRITE_STRING( STRING(pKiller->netname) );
					WRITE_STRING(STRING(pBuddy->pev->netname));
				MESSAGE_END();
				return;
			}
		}
	}

	CHalfLifeMultiplay::BuddyDeathNotice( pBuddy, pKiller, pevInflictor );
}

//=========================================================
//=========================================================
void CHalfLifeTeamplay :: PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor )
{
	if ( !m_DisableDeathPenalty )
	{
		CHalfLifeMultiplay::PlayerKilled( pVictim, pKiller, pInflictor );
		RecountTeams();
	}
}


//=========================================================
// IsTeamplay
//=========================================================
BOOL CHalfLifeTeamplay::IsTeamplay( void )
{
	return TRUE;
}

BOOL CHalfLifeTeamplay::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker )
{
	if ( pAttacker && PlayerRelationship( pPlayer, pAttacker ) == GR_TEAMMATE )
	{
		// my teammate hit me.
		if ( (friendlyfire.value == 0) && (pAttacker != pPlayer) )
		{
			// friendly fire is off, and this hit came from someone other than myself,  then don't get hurt
			return FALSE;
		}
	}

	return CHalfLifeMultiplay::FPlayerCanTakeDamage( pPlayer, pAttacker );
}

//=========================================================
//=========================================================
int CHalfLifeTeamplay::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
{
	// half life multiplay has a simple concept of Player Relationships.
	// you are either on another player's team, or you are not.
	if ( !pPlayer || !pTarget || !pTarget->IsPlayer() )
		return GR_NOTTEAMMATE;

	if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) )
	{
		return GR_TEAMMATE;
	}
	// Added by Rick
	else if ((pPlayer->pev->team == pTarget->pev->team) &&
			 (FStrEq(STRING(pTarget->pev->classname), "player_buddy")))
		return GR_TEAMMATE;

	return GR_NOTTEAMMATE;
}

//=========================================================
//=========================================================
BOOL CHalfLifeTeamplay::ShouldAutoAim( CBasePlayer *pPlayer, edict_t *target )
{
	// always autoaim, unless target is a teammate
	CBaseEntity *pTgt = CBaseEntity::Instance( target );
	if ( pTgt && pTgt->IsPlayer() )
	{
		if ( PlayerRelationship( pPlayer, pTgt ) == GR_TEAMMATE )
			return FALSE; // don't autoaim at teammates
	}

	return CHalfLifeMultiplay::ShouldAutoAim( pPlayer, target );
}

//=========================================================
//=========================================================
int CHalfLifeTeamplay::IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled )
{
	if ( !pKilled )
		return 0;

	if ( !pAttacker )
		return 1;

	if ( pAttacker != pKilled && PlayerRelationship( pAttacker, pKilled ) == GR_TEAMMATE )
		return -1;

	return 1;
}

//=========================================================
//=========================================================
const char *CHalfLifeTeamplay::GetTeamID( CBaseEntity *pEntity )
{
	if ( pEntity == NULL || pEntity->pev == NULL )
		return "";

	// return their team name
	return pEntity->TeamID();
}


int CHalfLifeTeamplay::GetTeamIndex( const char *pTeamName )
{
	if ( pTeamName && *pTeamName != 0 )
	{
		// try to find existing team
		for ( int tm = 0; tm < num_teams; tm++ )
		{
			if ( !stricmp( team_names[tm], pTeamName ) )
				return tm;
		}
	}
	
	return -1;	// No match
}


const char *CHalfLifeTeamplay::GetIndexedTeamName( int teamIndex )
{
	if ( teamIndex < 0 || teamIndex >= num_teams )
		return "";

	return team_names[ teamIndex ];
}


BOOL CHalfLifeTeamplay::IsValidTeam( const char *pTeamName ) 
{
	if ( !m_teamLimit )	// Any team is valid if the teamlist isn't set
		return TRUE;

	return ( GetTeamIndex( pTeamName ) != -1 ) ? TRUE : FALSE;
}

/* removed by harSens
const char *CHalfLifeTeamplay::TeamWithFewestPlayers( void )
{
	int i;
	int minPlayers = MAX_TEAMS;
	int teamCount[ MAX_TEAMS ];
	char *pTeamName = NULL;

	memset( teamCount, 0, MAX_TEAMS * sizeof(int) );
	
	// loop through all clients, count number of players on each team
	for ( i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CBaseEntity *plr = UTIL_PlayerByIndex( i );

		if ( plr )
		{
			int team = GetTeamIndex( plr->TeamID() );
			if ( team >= 0 )
				teamCount[team] ++;
		}
	}

	// Find team with least players
	for ( i = 0; i < num_teams; i++ )
	{
		if ( teamCount[i] < minPlayers )
		{
			minPlayers = teamCount[i];
			pTeamName = team_names[i];
		}
	}

	return pTeamName;
}
*/

//added by harSens
const char *CHalfLifeTeamplay::WeakestTeam( CBasePlayer *pPlayer )
{
	int i;
	int iTeam = 0;
	int minPlayers = 100;
	int teamCount[ MAX_TEAMS ];
	int teamScore[ MAX_TEAMS ];
	char *pTeamName = NULL;

	memset( teamCount, 0, MAX_TEAMS * sizeof(int) );
	memset( teamScore, 0, MAX_TEAMS * sizeof(int) );
	
	// loop through all clients, count number of players on each team
	for ( i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CBasePlayer *plr = (CBasePlayer *)UTIL_PlayerByIndex( i );

		if ( plr )
		{
			int team = GetTeamIndex( plr->TeamID() );
			if ( team >= 0 )
			{	if(pPlayer!=plr)
				{	teamCount[team] ++;				
					teamScore[team] += plr->pev->frags - plr->m_iDeaths;
				}
			}
		}
	}

	// Find team with least players
	for ( i = 0; i < num_teams; i++ )
	{
		if ( teamCount[i] < minPlayers )
		{
			minPlayers = teamCount[i];
			pTeamName = team_names[i];
			iTeam = i;
		}
		else if ( teamCount[i] == minPlayers )
		{	//find team with the lowest score
			if(teamScore[i] < teamScore[iTeam])
			{	minPlayers = teamCount[i];
				pTeamName = team_names[i];
				iTeam = i;
			}
			else if (teamScore[i] == teamScore[iTeam])
			{	//random team
				if(RANDOM_FLOAT(0,1)>0.5)
				{	minPlayers = teamCount[i];
					pTeamName = team_names[i];
					iTeam = i;
				}
			}
		}
	}
	return pTeamName;
}



//=========================================================
//=========================================================
void CHalfLifeTeamplay::RecountTeams( bool bResendInfo )
{
	char	*pName;
	char	teamlist[TEAMPLAY_TEAMLISTLENGTH];

	// loop through all teams, recounting everything
	num_teams = 0;

	// Copy all of the teams from the teamlist
	// make a copy because strtok is destructive
	strcpy( teamlist, m_szTeamList );
	pName = teamlist;
	pName = strtok( pName, ";" );
	while ( pName != NULL && *pName )
	{
		if ( GetTeamIndex( pName ) < 0 )
		{
			strcpy( team_names[num_teams], pName );
			num_teams++;
		}
		pName = strtok( NULL, ";" );
	}

	if ( num_teams < 2 )
	{
		num_teams = 0;
		m_teamLimit = FALSE;
	}

	// Sanity check
	memset( team_scores, 0, sizeof(team_scores) );

	// loop through all clients
	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CBaseEntity *plr = UTIL_PlayerByIndex( i );

		if ( plr )
		{
			const char *pTeamName = plr->TeamID();
			// try add to existing team
			int tm = GetTeamIndex( pTeamName );
			
			if ( tm < 0 ) // no team match found
			{ 
				if ( !m_teamLimit )
				{
					// add to new team
					tm = num_teams;
					num_teams++;
					team_scores[tm] = 0;
					strncpy( team_names[tm], pTeamName, MAX_TEAMNAME_LENGTH );
				}
			}

			if ( tm >= 0 )
			{
				team_scores[tm] += plr->pev->frags;
			}

			if ( bResendInfo ) //Someone's info changed, let's send the team info again.
			{
				if ( plr && IsValidTeam( plr->TeamID() ) )
				{
					MESSAGE_BEGIN( MSG_ALL, gmsgTeamInfo, NULL );
						WRITE_BYTE( plr->entindex() );
						WRITE_STRING( plr->TeamID() );
					MESSAGE_END();
				}
			}
		}
	}
}
//=========================================================
// JoinTeam Function, made by Chip
//=========================================================
void CHalfLifeTeamplay::JoinTeam(CBasePlayer *pPlayer, const char *pTeamName)
{
    char text [1024]; 

	//If already on this team, do nothing
    if (FStrEq(pPlayer->m_szTeamName, pTeamName))
    {
        return;
    }

	// Added by Rick: if there are superbots ingame don't let players let join team evil
	if (BotManager.AreSBotsIngame())
	{
		// Player is NOT a bot and wants to join evil?
		if ((!UTIL_IsBot(pPlayer)) && (FStrEq(pTeamName, TEAM2_NAME)))
		{
			UTIL_SayText("Cannot join team evil when there are superbots ingame\n", pPlayer);
			return;
		}
	}

	//added by harSens: stop immortality
	pPlayer->m_fInvulnerable = false;
	if (pPlayer->m_fSpawnInvulnerable)
	{	pPlayer->m_fSpawnInvulnerable = false;
		pPlayer->pev->rendermode = kRenderNormal;
		pPlayer->pev->renderfx = kRenderFxNone;
		// Added by Pcjoe, add attacks
		if(!(pPlayer->m_afPhysicsFlags & PFLAG_OBSERVER || realteams.value == 1.0 || pPlayer->IsSpectator()))
		{	pPlayer->m_pClass->AddAllAttacks();
		}
	}

    //Join the team 
	
    //When observer, make it respawnable, let it join a team and then respawn
	/*
	if ( pPlayer->m_afPhysicsFlags & PFLAG_OBSERVER )
	*/
	// Modified by Rick: added pPlayer->m_pSPSpawnPoint check to see if coop/hazard mode
	if ( pPlayer->m_afPhysicsFlags & PFLAG_OBSERVER || realteams.value == 1.0 || pPlayer->IsSpectator() || (pPlayer->m_pSPSpawnPoint != NULL))
    {	
		// notify everyone of the team change
        sprintf( text, "* %s has joined %s\n", STRING(pPlayer->pev->netname), pTeamName );
        UTIL_SayText( text, pPlayer );
        UTIL_LogPrintf( "\"%s<%i>\" has joined %s\n", STRING( pPlayer->pev->netname ), GETPLAYERUSERID( pPlayer->edict() ), pTeamName );
        
		/* modified by harSens: not respawn here, respawn after class menu
		pPlayer->pev->deadflag = DEAD_RESPAWNABLE;
		ChangePlayerTeam( pPlayer, pTeamName, FALSE, FALSE );
		*/

		if ( realteams.value == 1.0 )
		{	if(pPlayer->m_afPhysicsFlags & PFLAG_OBSERVER)
			{	ChangePlayerTeam( pPlayer, pTeamName, FALSE, FALSE );
			}
			else
			{	ChangePlayerTeam( pPlayer, pTeamName, TRUE, FALSE );								
				//change to base class				
				
				//delete pPlayer->m_pClass;
				//pPlayer->m_pClass = new CBaseClass(pPlayer);
				
				pPlayer->pev->playerclass = 0;				
				
				//get alive, and stop deaththink
				//respawn(pPlayer->pev,FALSE);
				//pPlayer->pev->nextthink = -1;

				pPlayer->StartObserving();			
			}
			
			// Added/modified by Rick: Select a class for the player if coop/hazard course
			if (pPlayer->m_pSPSpawnPoint)
			{
				CBaseEntity *p = pPlayer->m_pSPSpawnPoint;
				CInfoPlayerSP *pSpawn = (CInfoPlayerSP *)p;
				pSpawn->SelectClass(pPlayer);
			}
			else
			{
				MESSAGE_BEGIN(MSG_ONE, gmsgVGUIMenu, NULL, pPlayer->edict());
					WRITE_BYTE( 33 );			//show teamclass menu
				MESSAGE_END();	
			}
		}
		else
		{	ChangePlayerTeam( pPlayer, pTeamName, FALSE, FALSE );
			
			// Added/modified by Rick: Select a class for the player if coop/hazard course
			if (pPlayer->m_pSPSpawnPoint)
			{
				CBaseEntity *p = pPlayer->m_pSPSpawnPoint;
				CInfoPlayerSP *pSpawn = (CInfoPlayerSP *)p;
				pSpawn->SelectClass(pPlayer);
			}
			else
			{
				MESSAGE_BEGIN(MSG_ONE, gmsgVGUIMenu, NULL, pPlayer->edict());
					WRITE_BYTE( 3 );			//show class menu
				MESSAGE_END();
			}
		}		

        // recound stuff
        RecountTeams();
		/* modified by harSens: not respawn here, respawn after class menu
        pPlayer->Spawn();
		*/
    }
    //Else just switch teams
    else
    {
        // notify everyone of the team change
        sprintf( text, "* %s has joined %s\n", STRING(pPlayer->pev->netname), pTeamName );
        UTIL_SayText( text, pPlayer );
        UTIL_LogPrintf( "\"%s<%i>\" has joined %s\n", STRING( pPlayer->pev->netname ), GETPLAYERUSERID( pPlayer->edict() ), pTeamName );
        //ChangePlayerTeam( pPlayer, pTeamName, TRUE, FALSE);
		//no kill for changing teams
		ChangePlayerTeam( pPlayer, pTeamName, FALSE, FALSE);
        // recound stuff
        RecountTeams();
    }
} 
