//-----------------------------------------------------------------------------
//
//  $Logfile:: /EF2/Code/DLLs/game/mp_modeTeamBase.cpp                        $
// $Revision:: 59                                                             $
//   $Author:: Singlis                                                        $
//     $Date:: 9/26/03 2:36p                                                  $
//
// Copyright (C) 2002 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// Description:
//

#include "_pch_cpp.h"

#include "mp_manager.hpp"
#include "mp_modeBase.hpp"
#include "mp_modeTeamBase.hpp"


CLASS_DECLARATION( MultiplayerModeBase, ModeTeamBase, NULL )
{
	{ NULL, NULL }
};


//-----------------------------------------------------------------
//                   T E A M   A R E N A
//-----------------------------------------------------------------

//================================================================
// Name:        ModeTeamBase
// Class:       ModeTeamBase
//              
// Description: Constructor
//              
// Parameters:  const str& -- name of the arena
//              
// Returns:     None
//              
//================================================================
ModeTeamBase::ModeTeamBase()
{
	_maxTeams = 2;

	_leadTeam = NULL;

	_useTeamSpawnpoints = false;

	_redTeamIconIndex	= gi.imageindex( "sysimg/icons/mp/team_red" );
	_blueTeamIconIndex	= gi.imageindex( "sysimg/icons/mp/team_blue" );

	_redTeamHudIconIndex	= gi.imageindex( "sysimg/icons/mp/team_red_hud" );
	_blueTeamHudIconIndex	= gi.imageindex( "sysimg/icons/mp/team_blue_hud" );

	_redTeamSpectatorHudIconIndex	= gi.imageindex( "sysimg/icons/mp/team_red_spectator" );
	_blueTeamSpectatorHudIconIndex	= gi.imageindex( "sysimg/icons/mp/team_blue_spectator" );
}


//================================================================
// Name:        ~ModeTeamBase
// Class:       ModeTeamBase
//              
// Description: Destructor
//              
// Parameters:  None
//              
// Returns:     None
//              
//================================================================

ModeTeamBase::~ModeTeamBase()
{
	_deleteTeams();
}

void ModeTeamBase::init( int maxPlayers )
{
	MultiplayerModeBase::init( maxPlayers );

	multiplayerManager.cacheMultiplayerFiles( "mp_teamBase" );
}

//================================================================
// Name:        isEndOfMatch   
// Class:       ModeTeamBase
//              
// Description: Determines if either team has met the frag limit.
//              If so, announces a winner (should the base team 
//              arena be so bold?). 
//
//              If there is no frag limit set, no check is made.
//              
// Parameters:  None
//              
// Returns:     bool -- true if the match has ended based on fraglimit
//              
//================================================================
bool ModeTeamBase::isEndOfMatch( void )
{
	if (!getPointLimit())
	{
		return false ;
	}

	for (int idx=1; idx <= _teamList.NumObjects(); idx++) 
	{
		Team* team = _teamList.ObjectAt(idx);
		if (team->getDeaths() > getPointLimit())
		{
			multiplayerManager.centerPrintAllClients(va("$$%s$$ $$TeamLoses$$\n", team->getName().c_str() ), CENTERPRINT_IMPORTANCE_NORMAL );

			if ( team->getName() == "Red" )
				multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_redtlose.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
			else
				multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_bltlose.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
		
			return true ;
		}
	}
	return false ;
}

//================================================================
// Name:        AddPlayer
// Class:       ModeTeamBase
//              
// Description: Adds a player to the arena.  If force join is on,
//              this player will be force to join with the fewest
//              players.
//              
// Parameters:  Player* -- player to add
//              
// Returns:     None
//              
//================================================================
void ModeTeamBase::AddPlayer( Player *player )
{	
	// Make sure everything is ok

	assert(player);

	if ( !player )
	{
		warning("ModeDeathmatch::AddPlayer", "NULL Player\n");
		return ; 
	}

	// Make sure player hasn't alrady been added

	if ( !needToAddPlayer( player ) )
		return;

	MultiplayerModeBase::AddPlayer(player);	

	// Add player to a team or make him a spectator

	if ( playersLastTeam[ player->entnum ].length() )
	{
		addPlayerToTeam( player, getTeam( playersLastTeam[ player->entnum ] ) );
	}
	else if ( !multiplayerManager.checkFlag( MP_FLAG_NO_AUTO_JOIN_TEAM ) || multiplayerManager.checkFlag( MP_FLAG_AUTO_BALANCE_TEAMS ) || ( player->edict->svflags & SVF_BOT ) )
	{
		Team* pickedTeam   = 0 ;
		Team* team         = 0 ;
		int minimumPlayers = 0 ;

		// Automatically add this player to one of the teams

		for (int idx = 1; idx <= _teamList.NumObjects(); idx++)
		{
			team    = _teamList.ObjectAt(idx);
			
			assert(team);
			if (!team)
			{
				continue ;
			}
			
			int players = team->getActivePlayers();
			if ( (players < minimumPlayers) || (!pickedTeam) )
			{
				pickedTeam     = team ;
				minimumPlayers = players ;
			}
		}

		if (pickedTeam)
		{
			addPlayerToTeam( player, pickedTeam );
		}
	}
	else
	{
		// Not force joining of teams, start as a spectator

		addPlayerToTeam( player, NULL );
	}

	//if ( _playerGameData[ player->entnum ]._currentTeam )
	//	addPlayerToTeam( player, _playerGameData[ player->entnum ]._currentTeam );

	// If the game hasn't started yet just make the player a spectator

	if ( !_gameStarted )
	{
		multiplayerManager.makePlayerSpectator( player );
	}
}

void ModeTeamBase::addPlayerToTeam( Player *player, Team *team )
{
	Team *oldTeam;
	Entity *spawnPoint;


	MultiplayerModeBase::AddPlayer(player);	

	oldTeam = _playerGameData[ player->entnum ]._currentTeam;

	if ( team && ( oldTeam != team ) )
	{
		// Inform all of the players that the player has changed teams

		multiplayerManager.HUDPrintAllClients(va("%s $$Joined$$ $$%s$$ $$Team$$.\n", player->client->pers.netname, team->getName().c_str() ) );
	}

	if ( oldTeam )
	{
		_playerGameData[ player->entnum ]._currentTeam = NULL;

		player->SurfaceCommand( "all", "-skin1" );
		player->SurfaceCommand( "all", "-skin2" );

		oldTeam->RemovePlayer(player);
	}

	if ( team )
	{
		// Since the player is now on a team add him to the game

		if ( _gameStarted )
			multiplayerManager.playerEnterArena( player->entnum, player->health );
		else
			multiplayerManager.makePlayerSpectator( player );

		// Add the player to the team

		team->AddPlayer(player);

		_playerGameData[ player->entnum ]._currentTeam = team;

		updatePlayerSkin( player );

		if ( team->getName() == "Red" )
		{
			if ( multiplayerManager.isPlayerSpectator( player ) )
				multiplayerManager.setTeamHud( player, "mp_teamredspec" );
			else
				multiplayerManager.setTeamHud( player, "mp_teamred" );
		}
		else
		{
			if ( multiplayerManager.isPlayerSpectator( player ) )
				multiplayerManager.setTeamHud( player, "mp_teambluespec" );
			else
				multiplayerManager.setTeamHud( player, "mp_teamblue" );
		}

		multiplayerManager.playerSpawned( player );

		playersLastTeam[ player->entnum ] = team->getName();
	}
	else
	{
		// No team selected so make the player a spectator

		multiplayerManager.makePlayerSpectator( player, SPECTATOR_TYPE_FOLLOW, true );

		/* team = _playerGameData[ player->entnum ]._currentTeam;

		if ( !team )
			multiplayerManager.setTeamHud( player, "mp_teamspec" );
		else if ( team->getName() == "Red" )
			multiplayerManager.setTeamHud( player, "mp_teamredspec" );
		else
			multiplayerManager.setTeamHud( player, "mp_teambluespec" ); */
	}

	// Warp player to a spawn point

	spawnPoint = getSpawnPoint( player );

	if ( spawnPoint )
	{
		player->WarpToPoint( spawnPoint );
	}

	if ( team && _gameStarted )
	{
		KillBox( player );

		ActivatePlayer( player );
	}
}

void ModeTeamBase::changeTeams( Player *player, Team *team )
{
	RemovePlayer( player );
	addPlayerToTeam( player, team );
}

void ModeTeamBase::respawnPlayer( Player *player )
{
	Entity *spawnPoint;

	if ( !player )
	{
		assert(player);
		warning("ModeDeathmatch::respawnPlayer", "NULL player\n");
		return;
	}

	// Make sure we are allowed to respawn the player

	if ( !multiplayerManager.checkRule( "respawnPlayer", true, player ) )
	{
		if ( multiplayerManager.checkRule( "allowSpectator", true ) )
		{
			multiplayerManager.makePlayerSpectator( player );
		}

		return;
	}

	MultiplayerModeBase::respawnPlayer( player );

	multiplayerManager.initPlayer( player );

	spawnPoint = getSpawnPoint( player );

	if ( spawnPoint )
	{
		player->WarpToPoint( spawnPoint );
	}

	KillBox( player );

	ActivatePlayer( player );
}

Entity *ModeTeamBase::getSpawnPoint( Player *player )
{
	Entity *spawnPoint = NULL;
	int i;
	int numSpawnPoints = 0;
	str spawnpointName;
	int randomStartingSpot;
	int spawnPointIndex;


	// Determine what kind of spawn point we want to use

	if ( multiplayerManager.checkRule( "spawnpoints-special", false, player ) )
	{
		spawnpointName = multiplayerManager.getSpawnPointType( player );

		numSpawnPoints = getNumNamedSpawnpoints( spawnpointName );
	}

	if ( !numSpawnPoints && multiplayerManager.checkRule( "spawnpoints-team", _useTeamSpawnpoints ) && _playerGameData[ player->entnum ]._currentTeam )
	{
		if ( stricmp( _playerGameData[ player->entnum ]._currentTeam->getName(), "red" ) == 0 )
			spawnpointName = "red";
		else
			spawnpointName = "blue";

		numSpawnPoints = getNumNamedSpawnpoints( spawnpointName );
	}

	if ( !numSpawnPoints )
	{
		spawnpointName = "";

		numSpawnPoints = getNumNamedSpawnpoints( spawnpointName );
	}

	randomStartingSpot = ( (int)( G_Random() * numSpawnPoints ) );

	for( i = 0 ; i < numSpawnPoints ; i++ )
	{
		spawnPointIndex = ( randomStartingSpot + i ) % numSpawnPoints;

		spawnPoint = getNamedSpawnpointbyIndex( spawnpointName, spawnPointIndex );

		// Make sure we don't telefrag someone on our team

		if ( spawnPoint )
		{
			int j;
			int num;
			int touch[ MAX_GENTITIES ];
			gentity_t *hit;
			Vector min;	
			Vector max;
			bool badSpot;
	
			min = spawnPoint->origin + player->mins + Vector( 0, 0, 1 );
			max = spawnPoint->origin + player->maxs + Vector( 0, 0, 1 );
	
			num = gi.AreaEntities( min, max, touch, MAX_GENTITIES, qfalse );

			badSpot = false;
	
			for( j = 0 ; j < num ; j++ )
			{
				hit = &g_entities[ touch[ j ] ];
		
				if ( !hit->inuse || ( hit->entity == player ) || !hit->entity || ( hit->entity == world ) || ( !hit->entity->edict->solid ) )
				{
					continue;
				}

				if ( hit->entity->isSubclassOf( Player ) )
				{
					badSpot = true;
					break;
				}
			}

			if ( badSpot )
			{
				continue;
			}
		}

		// This is a good spawn point so use it

		return spawnPoint;
	}

	// Just return the last spawn point found

	return spawnPoint;
}

//================================================================
// Name:        AddTeam
// Class:       ModeTeamBase
//              
// Description: Adds a team to the arena.  Checks to ensure that the
//              max number of teams has not yet been exceeded.
//              
// Parameters:  const str& -- new name of team
//              
// Returns:     Team* -- Created team (NULL if failed to create)
//              
//================================================================
Team* ModeTeamBase::AddTeam( const str& teamName )
{
	Team* team = _findTeamByName(teamName);

	if (team)
	{
		warning( "ModeTeamBase::AddTeam", va("Team %s already exists\n", teamName.c_str() ) );
		return NULL ;
	}

	team = new Team(teamName);
	team->setMaxPlayers(getMaxPlayers() / getMaxTeams());
	_teamList.AddObject( team );

	return team ;
}

Team *ModeTeamBase::getTeam( const str & teamName )
{
	int i;
	Team *team;

	for ( i = 1 ; i <= _teamList.NumObjects() ; i++ )
	{
		team = _teamList.ObjectAt( i );

		if ( team->getName() == teamName )
		{
			return team;
		}
	}

	return NULL;
}


//================================================================
// Name:        RemoveTeam
// Class:       ModeTeamBase
//              
// Description: Removes and deletes the team specified by name from
//              the arena.  Calls _deleteTeam to do actual removal.
//              
// Parameters:  
//              
// Returns:     
//              
//================================================================
void ModeTeamBase::RemoveTeam( const str& teamName )
{
	Team* team = _findTeamByName(teamName);
	_deleteTeam(team);
}



//================================================================
// Name:        AddTeamStartingAmmo
// Class:       ModeTeamBase
//              
// Description: Adds team-specific ammo.  This enables a particular
//              team to receive a specific amount and/or type of ammo.
//              Calls _addTeamStartingAmmo() to actually give it to the team.
//              
// Parameters:  const str& teamName -- team to receive the ammo
//              const str& ammoName -- name of ammo to give
//              int        amount   -- amount of ammo to give
//              
// Returns:     None
//              
//================================================================
void ModeTeamBase::AddTeamStartingAmmo( const str& teamName, const str& ammoName, int amount )
{
	Team* team = _findTeamByName(teamName);
	SimpleAmmoType ammoType(ammoName, amount);
	_addTeamStartingAmmo(team, ammoType);
}

//================================================================
// Name:        AddTeamStartingWeapon
// Class:       ModeTeamBase
//              
// Description: Adds team-specific weapon.  This enables a particular
//              team to receive a specific weapon. Calls _addTeamStartingWeapon() 
//              to actually give it to the team.
//              
// Parameters:  const str& teamName   -- team to receive the ammo
//              const str& weaponName -- name of weapon to give
//              
// Returns:     None
//              
//================================================================
void ModeTeamBase::AddTeamStartingWeapon( const str& teamName, const str& weaponName )
{
	Team* team = _findTeamByName(teamName);
	_addTeamStartingWeapon(team, weaponName);
}

//================================================================
// Name:        setTeamStartingHealth
// Class:       ModeTeamBase
//              
// Description: Sets the starting health for the team.  Calls
//              _setTeamStartingHealth to do actual work.
//              
// Parameters:  const str&   -- team name
//              unsigned int -- amount of health
//              
// Returns:     None
//              
//================================================================
void ModeTeamBase::SetTeamStartingHealth( const str &teamName, unsigned int startingHealth )
{
	Team* team = _findTeamByName(teamName);
	_setTeamStartingHealth(team, startingHealth);
}

//----------------------------------------------------------------
//             P R O T E C T E D   M E T H O D S 
//----------------------------------------------------------------


//================================================================
// Name:        _addTeamStartingAmmo
// Class:       ModeTeamBase
//              
// Description: Adds the specified ammo to the specified team.
//              
// Parameters:  Team*          -- team to receive the ammo
//              SimpleAmmoType -- ammo to receive 
//              
// Returns:     None
//              
//================================================================
void ModeTeamBase::_addTeamStartingAmmo( Team* team, const SimpleAmmoType &ammo )
{
	// Check for valid team
	assert(team);
	if (!team)
	{
		warning("ModeTeamBase::_addTeamStartingAmmo", "NULL team passed\n");
		return ;
	}

	// Check for valid ammo type 
	assert(ammo.type.length());
	if (!ammo.type.length())
	{
		warning("ModeTeamBase::_addTeamStartingAmmo", "No ammo type specified\n");
		return ;
	}

	// Check for valid amount
	assert(ammo.amount > 0);
	if (ammo.amount < 0)
	{
		warning("ModeTeamBase::_addTeamStartingAmmo", "Negative ammo amount specified\n");
		return ;
	}

	team->AddStartingAmmo(ammo);
}


//================================================================
// Name:        _addTeamStartingWeapon
// Class:       ModeTeamBase
//              
// Description: Adds the specified weapon to the specified team.
//              
// Parameters:  Team*       -- team to receive the ammo
//              const str&  -- weapon to receive 
//              
// Returns:     None
//              
//================================================================
void ModeTeamBase::_addTeamStartingWeapon( Team* team, const str& weaponName )
{
	// Check for valid team
	assert(team);
	if (!team)
	{
		warning("ModeTeamBase::_addTeamStartingWeapon", "NULL team passed\n");
		return ;
	}

	// Check for valid weaponName
	assert(weaponName.length());
	if (!weaponName.length())
	{
		warning("ModeTeamBase::_addTeamStartingWeapon", "No weapon specified\n");
		return ;
	}

	team->AddStartingWeapon(weaponName);
}


//================================================================
// Name:        _setTeamStartingHealth
// Class:       ModeTeamBase
//              
// Description: Sets the specified team's starting health to the
//              specified value.  A starting health of 0 means to
//              use the arena's default value (typicaly 100).
//              
// Parameters:  Team*        -- team to affect
//              unsigned int -- amount of starting health (must be >= 0)
//              
// Returns:     None
//              
//================================================================
void ModeTeamBase::_setTeamStartingHealth( Team* team, unsigned int startingHealth )
{
	// Check for valid team
	assert(team);
	if (!team)
	{
		warning("ModeTeamBase::_addTeamStartingWeapon", "NULL team passed\n");
		return ;
	}

	/* assert(startingHealth >= 0);
	if (startingHealth < 0)
	{
		warning("ModeTeamBase::_setTeamStartingHealth", va("Cannot set health to %d\n", startingHealth));
		return ;
	} */

	team->setStartingHealth(startingHealth);
}

//================================================================
// Name:        _deleteTeams
// Class:       ModeTeamBase
//              
// Description: Deletes all the teams currently in the arena.
//              
// Parameters:  None
//              
// Returns:     None
//              
//================================================================
void ModeTeamBase::_deleteTeams()
{
	for (int idx = 1; idx <= _teamList.NumObjects(); idx++)
	{
		Team* team = _teamList.ObjectAt(idx);
		_deleteTeam(team);
	}
}

//================================================================
// Name:        _deleteTeam
// Class:       ModeTeamBase
//              
// Description: Removes the specified team from the arena and deletes it.
//              Minimal error checking since it is an internal function.
//              
// Parameters:  Team* -- team to remove and delete
//              
// Returns:     None
//              
//================================================================
void ModeTeamBase::_deleteTeam( Team* team )
{
	assert(team);
	if (!team)
	{
		warning("ModeTeamBase::_removeTeam", "NULL Team passed\n");
		return ;
	}

	_teamList.RemoveObject(team);
	delete team ;
}



//================================================================
// Name:        _beginMatch
// Class:       ModeTeamBase
//              
// Description: Called when a match begins.  Calls BeginMatch on
//              every team in the arena.  The teams are responsible
//              for preparing the participants for battle.
//              
// Parameters:  None
//              
// Returns:     None
//              
//================================================================
void ModeTeamBase::_beginMatch()
{
	for (int idx = 1; idx <= _teamList.NumObjects(); idx++)
	{
		Team* team = _teamList.ObjectAt(idx);
		team->BeginMatch();
	}
}


//================================================================
// Name:        _endMatch
// Class:       ModeTeamBase
//              
// Description: Called when a match ends.  Calls EndMatch on
//              every team in the arena.  The teams are responsible
//              for removing the players from battle-readiness.
//              
// Parameters:  None
//              
// Returns:     None
//              
//================================================================
void ModeTeamBase::_endMatch()
{
	for (int idx = 1; idx <= _teamList.NumObjects(); idx++)
	{
		Team* team = _teamList.ObjectAt(idx);
		team->EndMatch();
	}
}

void ModeTeamBase::declareWinner( void )
{
	int redPoints;
	int bluePoints;

	redPoints  = getTeamPoints( "Red" );
	bluePoints = getTeamPoints( "Blue" );

	if ( redPoints > bluePoints )
	{
		multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_rtwins.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
	}
	else if ( bluePoints > redPoints )
	{
		multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_btwins.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.5f );
	}
	/* else
	{
		multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_matover.mp3" );
	} */
}


//================================================================
// Name:        _findTeamByName
// Class:       ModeTeamBase
//              
// Description: Retrieves a pointer to the team having the specified name.
//              
// Parameters:  const str& -- name of the team to retrieve
//              
// Returns:     Team* -- pointer to team found (NULL if not found)
//              
//================================================================
Team* ModeTeamBase::_findTeamByName( const str& teamName )
{
	assert(teamName.length());
	if (!teamName.length())
	{
		warning("ModeTeamBase::_findTeamByName", "Team name not specified\n");
		return NULL ;
	}

	Team* team = NULL ;
	for (int idx = 1; idx <= _teamList.NumObjects(); idx++)
	{
		team = _teamList.ObjectAt(idx);
		if ( stricmp( team->getName().c_str(), teamName.c_str() ) == 0 )
		{
			return team ;
		}
	}

	return NULL ;
}

int ModeTeamBase::getIcon( Player *player, int statNum, int value )
{
	/* if ( statNum == STAT_MP_TEAMHUD_ICON )
	{
		Team *team;

		team = _playerGameData[ player->entnum ]._currentTeam;

		if ( team )
		{
			// Player is on a team so return the appropriate team icon

			if ( multiplayerManager.isPlayerSpectator( player ) )
			{
				if ( team->getName() == "Red" )
					return _redTeamSpectatorHudIconIndex;
				else if ( team->getName() == "Blue" )
					return _blueTeamSpectatorHudIconIndex;
			}
			else
			{
				if ( team->getName() == "Red" )
					return _redTeamHudIconIndex;
				else if ( team->getName() == "Blue" )
					return _blueTeamHudIconIndex;
			}
		}
	}
	else */ if ( statNum == STAT_MP_TEAM_ICON )
	{
		Team *team;

		team = _playerGameData[ player->entnum ]._currentTeam;

		if ( team )
		{
			// Player is on a team so return the appropriate team icon

			if ( team->getName() == "Red" )
				return _redTeamIconIndex;
			else if ( team->getName() == "Blue" )
				return _blueTeamIconIndex;
		}
	}
	else if ( statNum == STAT_MP_OTHERTEAM_ICON )
	{
		Team *team;

		team = _playerGameData[ player->entnum ]._currentTeam;

		if ( team )
		{
			// Player is on a team so return the appropriate team icon

			if ( team->getName() == "Red" )
				return _blueTeamIconIndex;
			else if ( team->getName() == "Blue" )
				return _redTeamIconIndex;
		}
	}

	// We didn't return an icon so let the base mode have a chance

	return MultiplayerModeBase::getIcon( player, statNum, value );
}

int ModeTeamBase::getInfoIcon( Player *player )
{
	Team *team;

	// Make sure entity is not invisible

	if ( player->_affectingViewModes & gi.GetViewModeMask( "forcevisible" ) )
		return 0;

	team = _playerGameData[ player->entnum ]._currentTeam;

	if ( team )
	{
		if ( team->getName() == "Red" )
			return _redTeamIconIndex;
		else if ( team->getName() == "Blue" )
			return _blueTeamIconIndex;
	}

	return 0;
}

bool ModeTeamBase::canJoinTeam( Player *player, const str &teamName )
{
	Team *team;

	if ( multiplayerManager.checkFlag( MP_FLAG_AUTO_BALANCE_TEAMS ) )
	{
		Team *team;
		Team *redTeam;
		Team *blueTeam;
		int redTeamPlayers;
		int blueTeamPlayers;

		// Get all of the teams

		team = getTeam( teamName );
		redTeam = getTeam( "Red" );
		blueTeam = getTeam( "Blue" );

		// Get the number of players

		redTeamPlayers  = redTeam->getActivePlayers();
		blueTeamPlayers = blueTeam->getActivePlayers();

		if ( _playerGameData[ player->entnum ]._currentTeam == redTeam )
			redTeamPlayers -= 1;
		else if ( _playerGameData[ player->entnum ]._currentTeam == blueTeam )
			blueTeamPlayers -= 1;

		// Don't allow switch unless it's to the team that has less people

		if ( ( redTeamPlayers > blueTeamPlayers ) && ( team == redTeam ) )
		{
			multiplayerManager.centerPrint( player->entnum, "$$CannotJoinAutoBalanceRed$$", CENTERPRINT_IMPORTANCE_HIGH );
			return false;
		}
		else if ( ( blueTeamPlayers > redTeamPlayers ) && ( team == blueTeam ) )
		{
			multiplayerManager.centerPrint( player->entnum, "$$CannotJoinAutoBalanceBlue$$", CENTERPRINT_IMPORTANCE_HIGH );
			return false;
		}
	}

	if ( !multiplayerManager.checkRule( "respawnPlayer", true, player ) )
	{
		return false;
	}

	team = _findTeamByName( teamName );

	if ( _playerGameData[ player->entnum ]._currentTeam == team )
		return false;
	else
		return true;
}

void ModeTeamBase::joinTeam( Player *player, const str &teamName )
{
	Team *team;

	team = _findTeamByName( teamName );

	changeTeams( player, team );
}

float ModeTeamBase::playerDamaged( Player *damagedPlayer, Player *attackingPlayer, float damage, int meansOfDeath )
{
	// Always take telefrag damage

	if ( meansOfDeath == MOD_TELEFRAG )
		return damage;

	// Player can always hurt himself

	if ( damagedPlayer == attackingPlayer )
		return damage;

	// If on same team and not allowing team damage

	if ( ( _playerGameData[ damagedPlayer->entnum ]._currentTeam == _playerGameData[ attackingPlayer->entnum ]._currentTeam ) &&
		 ( !multiplayerManager.checkFlag( MP_FLAG_FRIENDLY_FIRE ) ) )
		return 0;
	else
		return damage;
}

int ModeTeamBase::getTeamPoints( Player *player )
{
	Team *team;

	// Return the points for this team

	team = _playerGameData[ player->entnum ]._currentTeam;

	if ( team )
		return team->getPoints();
	else
		return 0;
}

int ModeTeamBase::getTeamPoints( const str & teamName )
{
	int i;
	Team *team;

	// Return the points for this team

	for ( i = 1 ; i <= _teamList.NumObjects() ; i++ )
	{
		team = _teamList.ObjectAt( i );

		if ( team->getName() == teamName )
		{
			return team->getPoints();
		}
	}

	return 0;
}

void ModeTeamBase::addTeamPoints( const str & teamName, int points )
{
	int i;
	Team *team;

	// Return the points for this team

	for ( i = 1 ; i <= _teamList.NumObjects() ; i++ )
	{
		team = _teamList.ObjectAt( i );

		if ( team->getName() == teamName )
		{
			team->addPoints( NULL, points );
		}
	}
}

void ModeTeamBase::teamPointsChanged( Team *team, int oldPoints, int newPoints )
{
	Team *otherTeam;
	int otherTeamPoints;

	if ( team->getName() == "Red" )
		otherTeam = getTeam( "Blue" );
	else
		otherTeam = getTeam( "Red" );

	otherTeamPoints = otherTeam->getPoints();

	// Play a dialog for this team if necessary

	if ( ( newPoints == otherTeamPoints ) && ( oldPoints != otherTeamPoints ) )
	{
		// Now tied for the lead
		multiplayerManager.broadcastSound( "localization/sound/dialog/dm/comp_teamstied.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, NULL, 1.0f );
	}
	else if ( ( newPoints > otherTeamPoints ) && ( oldPoints <= otherTeamPoints ) )
	{
		// Now in first place
		multiplayerManager.teamSound( team, "localization/sound/dialog/dm/comp_team1stplace.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, 1.0f );

		// No longer in the lead
		multiplayerManager.teamSound( otherTeam, "localization/sound/dialog/dm/comp_teamnolead.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, 1.0f );
	}
	else if ( ( newPoints < otherTeamPoints ) && ( oldPoints >= otherTeamPoints ) )
	{
		// No longer in the lead
		multiplayerManager.teamSound( team, "localization/sound/dialog/dm/comp_teamnolead.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, 1.0f );

		// Now in first place
		multiplayerManager.teamSound( otherTeam, "localization/sound/dialog/dm/comp_team1stplace.mp3", CHAN_AUTO, DEFAULT_VOL, DEFAULT_MIN_DIST, 1.0f );
	}
}

void ModeTeamBase::RemovePlayer( Player *player )
{
	Team *team;

	team = _playerGameData[ player->entnum ]._currentTeam;

	if ( team )
	{
		team->RemovePlayer( player );
	}

	MultiplayerModeBase::RemovePlayer( player );
}

int ModeTeamBase::getHighestPoints( void )
{
	int i;
	int highestPoints = -999999999;
	Team *team;

	for ( i = 1 ; i <= _teamList.NumObjects() ; i++ )
	{
		team = _teamList.ObjectAt( i );

		if ( team->getPoints() > highestPoints )
		{
			highestPoints = team->getPoints();
		}
	}

	return highestPoints;
}

void ModeTeamBase::updatePlayerSkin( Player *player )
{
	Team *team;

	team = getPlayersTeam( player );

	player->SurfaceCommand( "all", "-skin1" );
	player->SurfaceCommand( "all", "-skin2" );

	if ( !team )
		return;

	if ( team->getName() == "Red" )
	{
		player->SurfaceCommand( "all", "+skin1" );
	}
	else
	{
		player->SurfaceCommand( "all", "+skin2" );
	}
}

void ModeTeamBase::playerChangedModel( Player *player )
{
	updatePlayerSkin( player );
}

