//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: Team management class. Contains all the details for a specific team // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "team.h" #include "player.h" #include "team_spawnpoint.h" #include "usermessages.h" #if defined ( CSTRIKE15 ) #include "cs_bot.h" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" CUtlVector< CTeam * > g_Teams; extern ConVar mp_radar_showall; //----------------------------------------------------------------------------- // Purpose: SendProxy that converts the Team's player UtlVector to entindexes //----------------------------------------------------------------------------- void SendProxy_PlayerList( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ) { CTeam *pTeam = (CTeam*)pData; // If this assertion fails, then SendProxyArrayLength_PlayerArray must have failed. Assert( iElement < pTeam->m_aPlayers.Count() ); CBasePlayer *pPlayer = pTeam->m_aPlayers[iElement]; pOut->m_Int = pPlayer->entindex(); } int SendProxyArrayLength_PlayerArray( const void *pStruct, int objectID ) { CTeam *pTeam = (CTeam*)pStruct; return pTeam->m_aPlayers.Count(); } // Datatable IMPLEMENT_SERVERCLASS_ST_NOBASE(CTeam, DT_Team) SendPropInt( SENDINFO(m_iTeamNum), 5 ), SendPropInt( SENDINFO(m_bSurrendered), 0 ), SendPropInt( SENDINFO(m_scoreTotal), 0 ), SendPropInt( SENDINFO(m_scoreFirstHalf), 0 ), SendPropInt( SENDINFO(m_scoreSecondHalf), 0 ), SendPropInt( SENDINFO(m_scoreOvertime), 0 ), SendPropInt( SENDINFO(m_iClanID), 32, SPROP_UNSIGNED ), SendPropString( SENDINFO( m_szTeamname ) ), SendPropString( SENDINFO( m_szClanTeamname ) ), SendPropString( SENDINFO( m_szTeamFlagImage ) ), SendPropString( SENDINFO( m_szTeamLogoImage ) ), SendPropString( SENDINFO( m_szTeamMatchStat ) ), SendPropInt( SENDINFO( m_nGGLeaderEntIndex_CT ), 0 ), SendPropInt( SENDINFO( m_nGGLeaderEntIndex_T ), 0 ), SendPropInt( SENDINFO(m_numMapVictories), 4, SPROP_UNSIGNED ), SendPropArray2( SendProxyArrayLength_PlayerArray, SendPropInt("player_array_element", 0, 4, 10, SPROP_UNSIGNED, SendProxy_PlayerList), MAX_PLAYERS, 0, "player_array" ) END_SEND_TABLE() LINK_ENTITY_TO_CLASS( team_manager, CTeam ); //----------------------------------------------------------------------------- // Purpose: Get a pointer to the specified team manager //----------------------------------------------------------------------------- CTeam *GetGlobalTeam( int iIndex ) { if ( iIndex < 0 || iIndex >= GetNumberOfTeams() ) return NULL; return g_Teams[ iIndex ]; } //----------------------------------------------------------------------------- // Purpose: Get the number of team managers //----------------------------------------------------------------------------- int GetNumberOfTeams( void ) { return g_Teams.Count(); } const char* GetTeamName( int iTeam ) { CTeam *pTeam = GetGlobalTeam( iTeam ); if ( pTeam ) { return pTeam->GetName(); } else { return "UNKNOWN TEAM"; } } int CTeam::m_nStaticGGLeader_CT = -1; int CTeam::m_nStaticGGLeader_T = -1; //----------------------------------------------------------------------------- // Purpose: Needed because this is an entity, but should never be used //----------------------------------------------------------------------------- CTeam::CTeam( void ) { memset( m_szTeamname.GetForModify(), 0, sizeof(m_szTeamname) ); memset( m_szClanTeamname.GetForModify(), 0, sizeof(m_szClanTeamname) ); memset( m_szTeamFlagImage.GetForModify(), 0, sizeof(m_szTeamFlagImage) ); memset( m_szTeamLogoImage.GetForModify(), 0, sizeof(m_szTeamLogoImage) ); memset( m_szTeamMatchStat.GetForModify(), 0, sizeof( m_szTeamMatchStat ) ); m_numMapVictories.GetForModify() = 0; ResetTeamLeaders(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTeam::~CTeam( void ) { m_aSpawnPoints.Purge(); m_aPlayers.Purge(); } void CTeam::ResetTeamLeaders() { m_flLastPlayerSortTime = 0; m_nGGLeaderEntIndex_CT = -1; m_nGGLeaderEntIndex_T = -1; m_bGGHasLeader_CT = false; m_bGGHasLeader_T = false; } //----------------------------------------------------------------------------- // Purpose: Called every frame //----------------------------------------------------------------------------- void CTeam::Think( void ) { if ( m_flLastPlayerSortTime + 0.025f < gpGlobals->curtime ) { DetermineGGLeaderAndSort(); } } void CTeam::DetermineGGLeaderAndSort( void ) { CUtlVector< CCSPlayer* > playerList_CT; CUtlVector< CCSPlayer* > playerList_T; int iNumPlayers = GetNumPlayers(); // m_aPlayers.Sort( PlayerSortFunction ); for ( int i = 0; i < iNumPlayers; i++ ) { CCSPlayer *player = static_cast< CCSPlayer* >( GetPlayer( i ) ); if ( player ) { if ( player->GetTeamNumber() == TEAM_CT ) { if ( player->GetPlayerGunGameWeaponIndex() > 0 ) m_bGGHasLeader_CT = true; playerList_CT.AddToTail( player ); } else if ( player->GetTeamNumber() == TEAM_TERRORIST ) { if ( player->GetPlayerGunGameWeaponIndex() > 0 ) m_bGGHasLeader_T = true; playerList_T.AddToTail( player ); } } } m_nStaticGGLeader_CT = m_nGGLeaderEntIndex_CT; m_nStaticGGLeader_T = m_nGGLeaderEntIndex_T; playerList_CT.Sort( TeamGGSortFunction ); playerList_T.Sort( TeamGGSortFunction ); if ( playerList_CT.Count() > 0 && m_bGGHasLeader_CT ) m_nGGLeaderEntIndex_CT = playerList_CT[0]->entindex(); if ( playerList_T.Count() > 0 && m_bGGHasLeader_T ) m_nGGLeaderEntIndex_T = playerList_T[0]->entindex(); // send an event down so the client gets it asap if ( m_nLastGGLeader_CT != m_nGGLeaderEntIndex_CT || m_nLastGGLeader_T != m_nGGLeaderEntIndex_T ) { int nLeaderIndex = m_nGGLeaderEntIndex_CT; if ( m_nLastGGLeader_T != m_nGGLeaderEntIndex_T ) nLeaderIndex = m_nGGLeaderEntIndex_T; for ( int i = 1; i <= iNumPlayers; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer && pPlayer->entindex() == nLeaderIndex ) { //if we made it this far, we are the current leader IGameEvent *event = gameeventmanager->CreateEvent( "gg_team_leader" ); if ( event ) { event->SetInt( "playerid", pPlayer->GetUserID() ); gameeventmanager->FireEvent( event ); } break; } } } m_nLastGGLeader_CT = m_nGGLeaderEntIndex_CT; m_nLastGGLeader_T = m_nGGLeaderEntIndex_T; } int CTeam::GetGGLeader( int nTeam ) { if ( nTeam == TEAM_CT ) return m_nGGLeaderEntIndex_CT; else if ( nTeam == TEAM_TERRORIST ) return m_nGGLeaderEntIndex_T; return -1; } //----------------------------------------------------------------------------- // Purpose: Used for sorting players in valve containers //----------------------------------------------------------------------------- int CTeam::TeamGGSortFunction( CCSPlayer* const *entry1, CCSPlayer* const *entry2 ) { // bail out early if either player is an empty slot, i.e. has a player index of -1 if ( entry1 == NULL ) return 1; if ( entry2 == NULL ) return -1; if ( (*entry1)->entindex() == -1 ) return 1; if ( (*entry2)->entindex() == -1 ) return -1; // Higher GG Progressive weapon ranks higher. In case of ties for that, we rank according to player index so // we don't overly shuffle the ordering if ( (*entry1)->GetPlayerGunGameWeaponIndex() > (*entry2)->GetPlayerGunGameWeaponIndex() ) return -1; else if ( (*entry1)->GetPlayerGunGameWeaponIndex() < (*entry2)->GetPlayerGunGameWeaponIndex() ) return 1; else { int nLeader = m_nStaticGGLeader_CT; if ( (*entry1)->GetTeamNumber() == TEAM_TERRORIST ) nLeader = m_nStaticGGLeader_T; // Current GG leader always sorts in front in the case of a tie if ( (*entry1)->entindex() == nLeader ) return -1; else if ( (*entry2)->entindex() == nLeader ) return 1; //else // return entry1->entindex() - entry2->entindex(); } return 0; } //----------------------------------------------------------------------------- // Purpose: Teams are always transmitted to clients //----------------------------------------------------------------------------- int CTeam::UpdateTransmitState() { return SetTransmitState( FL_EDICT_ALWAYS ); } //----------------------------------------------------------------------------- // Visibility/scanners //----------------------------------------------------------------------------- int CTeam::ShouldTransmitToPlayer( CBasePlayer* pRecipient, CBaseEntity* pEntity ) { // Always transmit the observer target to players and to spectators if ( pRecipient && (pRecipient->IsObserver() && pRecipient->GetObserverTarget() == pEntity) ) return FL_EDICT_ALWAYS; // Spec that isn't a coach if ( pRecipient->IsSpectator() ) return FL_EDICT_ALWAYS; // Same team if ( pRecipient->GetAssociatedTeamNumber() == pEntity->GetTeamNumber() ) return FL_EDICT_ALWAYS; // radar_show is 'all' or set to the target's team. if ( mp_radar_showall.GetInt() && ( ( mp_radar_showall.GetInt() == 1 ) || ( mp_radar_showall.GetInt() == pRecipient->GetTeamNumber() ) ) ) return FL_EDICT_ALWAYS; return FL_EDICT_PVSCHECK; } //----------------------------------------------------------------------------- // Initialization //----------------------------------------------------------------------------- void CTeam::Init( const char *pName, int iNumber ) { InitializeSpawnpoints(); InitializePlayers(); ResetTeamLeaders(); m_bSurrendered = 0; m_scoreTotal = 0; m_scoreFirstHalf = 0; m_scoreSecondHalf = 0; m_scoreOvertime = 0; Q_strncpy( m_szTeamname.GetForModify(), pName, MAX_TEAM_NAME_LENGTH ); m_iTeamNum = iNumber; } //----------------------------------------------------------------------------- // DATA HANDLING //----------------------------------------------------------------------------- int CTeam::GetTeamNumber( void ) const { return m_iTeamNum; } //----------------------------------------------------------------------------- // Purpose: Set the team's name //----------------------------------------------------------------------------- void CTeam::SetName( const char *pName ) { Q_strncpy( m_szTeamname.GetForModify(), pName, MAX_TEAM_NAME_LENGTH ); } //----------------------------------------------------------------------------- // Purpose: Get the team's name //----------------------------------------------------------------------------- const char *CTeam::GetName( void ) { return m_szTeamname; } //----------------------------------------------------------------------------- // Purpose: Set the team's name //----------------------------------------------------------------------------- void CTeam::SetClanName( const char *pName ) { Q_strncpy( m_szClanTeamname.GetForModify(), pName, MAX_TEAM_NAME_LENGTH ); } //----------------------------------------------------------------------------- // Purpose: Get the team's name //----------------------------------------------------------------------------- const char *CTeam::GetClanName( void ) { return m_szClanTeamname; } void CTeam::SetClanID( uint32 iClanID ) { m_iClanID = iClanID; } uint32 CTeam::GetClanID( void ) { return m_iClanID; } //----------------------------------------------------------------------------- // Purpose: Set the team's name //----------------------------------------------------------------------------- void CTeam::SetFlagImageString( const char *pName ) { Q_strncpy( m_szTeamFlagImage.GetForModify(), pName, MAX_TEAM_FLAG_ICON_LENGTH ); } //----------------------------------------------------------------------------- // Purpose: Get the team's name //----------------------------------------------------------------------------- const char *CTeam::GetFlagImageString( void ) { return m_szTeamFlagImage; } //----------------------------------------------------------------------------- // Purpose: Set the team's name //----------------------------------------------------------------------------- void CTeam::SetLogoImageString( const char *pName ) { Q_strncpy( m_szTeamLogoImage.GetForModify(), pName, MAX_TEAM_FLAG_ICON_LENGTH ); } void CTeam::SetNumMapVictories( int numMapVictories ) { if ( numMapVictories != m_numMapVictories ) { m_numMapVictories = numMapVictories; } } //----------------------------------------------------------------------------- // Purpose: Get the team's name //----------------------------------------------------------------------------- const char *CTeam::GetLogoImageString( void ) { return m_szTeamLogoImage; } //----------------------------------------------------------------------------- // Purpose: Update the player's client data //----------------------------------------------------------------------------- void CTeam::UpdateClientData( CBasePlayer *pPlayer ) { } //------------------------------------------------------------------------------------------------------------------ // SPAWNPOINTS //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTeam::InitializeSpawnpoints( void ) { m_iLastSpawn = 0; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTeam::AddSpawnpoint( CTeamSpawnPoint *pSpawnpoint ) { m_aSpawnPoints.AddToTail( pSpawnpoint ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTeam::RemoveSpawnpoint( CTeamSpawnPoint *pSpawnpoint ) { for (int i = 0; i < m_aSpawnPoints.Count(); i++ ) { if ( m_aSpawnPoints[i] == pSpawnpoint ) { m_aSpawnPoints.Remove( i ); return; } } } //----------------------------------------------------------------------------- // Purpose: Spawn the player at one of this team's spawnpoints. Return true if successful. //----------------------------------------------------------------------------- CBaseEntity *CTeam::SpawnPlayer( CBasePlayer *pPlayer ) { if ( m_aSpawnPoints.Count() == 0 ) return NULL; // Randomize the start spot int iSpawn = m_iLastSpawn + random->RandomInt( 1,3 ); if ( iSpawn >= m_aSpawnPoints.Count() ) iSpawn -= m_aSpawnPoints.Count(); int iStartingSpawn = iSpawn; // Now loop through the spawnpoints and pick one int loopCount = 0; do { if ( iSpawn >= m_aSpawnPoints.Count() ) { ++loopCount; iSpawn = 0; } // check if pSpot is valid, and that the player is on the right team if ( (loopCount > 3) || m_aSpawnPoints[iSpawn]->IsValid( pPlayer ) ) { // DevMsg( 1, "player: spawning at (%s)\n", STRING(m_aSpawnPoints[iSpawn]->m_iName) ); m_aSpawnPoints[iSpawn]->m_OnPlayerSpawn.FireOutput( pPlayer, m_aSpawnPoints[iSpawn] ); m_iLastSpawn = iSpawn; return m_aSpawnPoints[iSpawn]; } iSpawn++; } while ( iSpawn != iStartingSpawn ); // loop if we're not back to the start return NULL; } //------------------------------------------------------------------------------------------------------------------ // PLAYERS //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTeam::InitializePlayers( void ) { } //----------------------------------------------------------------------------- // Purpose: Add the specified player to this team. Remove them from their current team, if any. //----------------------------------------------------------------------------- void CTeam::AddPlayer( CBasePlayer *pPlayer ) { m_aPlayers.AddToTail( pPlayer ); NetworkStateChanged(); } //----------------------------------------------------------------------------- // Purpose: Remove this player from the team //----------------------------------------------------------------------------- void CTeam::RemovePlayer( CBasePlayer *pPlayer ) { m_aPlayers.FindAndRemove( pPlayer ); NetworkStateChanged(); } //----------------------------------------------------------------------------- // Purpose: Return the number of players in this team. //----------------------------------------------------------------------------- int CTeam::GetNumPlayers( void ) { return m_aPlayers.Count(); } //----------------------------------------------------------------------------- // Purpose: Get a specific player //----------------------------------------------------------------------------- CBasePlayer *CTeam::GetPlayer( int iIndex ) { Assert( iIndex >= 0 && iIndex < m_aPlayers.Count() ); return m_aPlayers[ iIndex ]; } //------------------------------------------------------------------------------------------------------------------ // SCORING //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTeam::ResetScores( void ) { m_bSurrendered = 0; m_scoreTotal = 0; m_scoreFirstHalf = 0; m_scoreSecondHalf = 0; m_scoreOvertime = 0; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTeam::AwardAchievement( int iAchievement ) { Assert( iAchievement >= 0 && iAchievement < 255 ); // must fit in short CRecipientFilter filter; int iNumPlayers = GetNumPlayers(); for ( int i=0;iIsAlive() ) { iAlive++; } } return iAlive; } #if defined ( CSTRIKE15 ) int CTeam::GetBotMembers( CUtlVector< CCSBot* > *pOutVecBots /*= NULL*/ ) { int iBots = 0; for ( int i = 0; i < GetNumPlayers(); i++ ) { if ( GetPlayer( i ) && GetPlayer( i )->IsBot() ) { if ( pOutVecBots ) { CCSBot* pBot = dynamic_cast< CCSBot* > ( GetPlayer( i ) ); if ( pBot ) { pOutVecBots->AddToTail( pBot ); } } iBots++; } } return iBots; } int CTeam::GetHumanMembers( CUtlVector< class CCSPlayer* > *pOutVecPlayers /*= NULL */ ) { int iPlayers = 0; for ( int i = 0; i < GetNumPlayers(); i++ ) { if ( GetPlayer( i ) && !GetPlayer( i )->IsBot() ) { if ( pOutVecPlayers ) { CCSPlayer* pPlayer = dynamic_cast< CCSPlayer * > ( GetPlayer( i ) ); if ( pPlayer ) { pOutVecPlayers->AddToTail( pPlayer ); } } iPlayers++; } } return iPlayers; } #endif