//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //============================================================================= #include "cbase.h" #include "serverbenchmark_tf.h" #include "tf_shareddefs.h" #include "tf_bot_temp.h" #include "entity_tfstart.h" #include "tf_player.h" static ConVar sv_benchmark_freeroam( "sv_benchmark_freeroam", "0", 0, "Allow the local player to move freely in the benchmark. Only used for debugging. Don't use for real benchmarks because it will make the timing inconsistent." ); class CTFServerBenchmark : public CServerBenchmarkHook { public: virtual void StartBenchmark() { ConVarRef cvBotFlipout( "bot_flipout" ); cvBotFlipout.SetValue( 3 ); extern ConVar bot_forceattack_down; bot_forceattack_down.SetValue( true ); extern ConVar mp_teams_unbalance_limit; mp_teams_unbalance_limit.SetValue( (int)0 ); m_nBotsCreated = 0; m_bSetupLocalPlayer = false; } virtual void GetPhysicsModelNames( CUtlVector &modelNames ) { modelNames.AddToTail( "models/props_farm/wooden_barrel.mdl" ); modelNames.AddToTail( "models/props_gameplay/orange_cone001.mdl" ); } virtual CBaseEntity* GetBlueSpawnPoint( bool bBest ) { CUtlVector spawns; for ( int i=0; i( ITFTeamSpawnAutoList::AutoList()[i] ); if ( !pTFSpawn->IsDisabled() && pTFSpawn->GetTeamNumber() == TF_TEAM_BLUE ) spawns.AddToTail( pTFSpawn ); } // If we're not looking for the BEST one, return a random one. if ( !bBest ) { if ( spawns.Count() == 0 ) return NULL; return spawns[ g_pServerBenchmark->RandomInt( 0, spawns.Count()-1 ) ]; } float flBestSpawn = 0; CTFTeamSpawn *pBestSpawn = NULL; for ( int i=0; i < spawns.Count(); i++ ) { Vector vForward; AngleVectors( spawns[i]->GetLocalAngles(), &vForward ); float flCurSpawn = 0; for ( int j=0; j < spawns.Count(); j++ ) { if ( j == i ) continue; Vector vTo = spawns[j]->GetAbsOrigin() - spawns[i]->GetAbsOrigin(); VectorNormalize( vTo ); flCurSpawn += vForward.Dot( vTo ); } if ( flCurSpawn > flBestSpawn ) { flBestSpawn = flCurSpawn; pBestSpawn = spawns[i]; } } return pBestSpawn; } virtual void UpdateBenchmark() { if ( m_nBotsCreated == 0 ) { return; } RandomSeed( 0 ); // Put the player at a blue spawn point. if ( !engine->IsDedicatedServer() ) { CTFPlayer *pLocalPlayer = dynamic_cast< CTFPlayer* >( UTIL_GetListenServerHost() ); if ( pLocalPlayer ) { if ( !m_bSetupLocalPlayer ) { m_bSetupLocalPlayer = true; pLocalPlayer->ChangeTeam( TEAM_SPECTATOR ); pLocalPlayer->SetDesiredPlayerClassIndex( TF_CLASS_SNIPER ); pLocalPlayer->ForceRespawn(); // Now pick whatever blue spawn point has the most other spawn points in front of it. CBaseEntity *pBestSpawn = GetBlueSpawnPoint( true ); if ( !pBestSpawn ) Error( "Can't find spawn position for local player." ); m_vLocalPlayerOrigin = pBestSpawn->GetLocalOrigin() + Vector(0,0,80); m_vLocalPlayerEyeAngles = pBestSpawn->GetLocalAngles(); } ((CBasePlayer*)pLocalPlayer)->SetObserverMode( OBS_MODE_ROAMING ); // Lock the player in place for a little bit, then let them go free when we have some bots. if ( !sv_benchmark_freeroam.GetBool() || m_nBotsCreated < 2 ) { pLocalPlayer->Teleport( &m_vLocalPlayerOrigin, &m_vLocalPlayerEyeAngles, NULL ); pLocalPlayer->SetObserverTarget( NULL ); if ( !sv_benchmark_freeroam.GetBool() ) pLocalPlayer->AddFlag( FL_FROZEN ); } } } RespawnDeadPlayers(); MoveRedPlayersToBlueArea(); AddSentries(); } void RespawnDeadPlayers() { for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); if ( pPlayer && pPlayer->IsDead() && !g_pServerBenchmark->IsLocalBenchmarkPlayer( pPlayer ) ) { pPlayer->ForceRespawn(); } } } void AddSentries() { const char *pSentryClassName = "obj_sentrygun"; for ( int iTeamIteration=0; iTeamIteration < 2; iTeamIteration++ ) { int iTeam = (iTeamIteration == 0) ? TF_TEAM_BLUE : TF_TEAM_RED; // Get the current # of sentries. int nSentries = 0; CBaseEntity *pSpot = gEntList.FindEntityByClassname( NULL, pSentryClassName ); while( pSpot ) { if ( pSpot->GetTeamNumber() == iTeam ) ++nSentries; pSpot = gEntList.FindEntityByClassname( pSpot, pSentryClassName ); } // Make new ones if necessary. if ( nSentries < 2 ) { // Find an engineer.. for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CTFPlayer *pPlayer = dynamic_cast< CTFPlayer* >( UTIL_PlayerByIndex( i ) ); if ( pPlayer && pPlayer->GetTeamNumber() == iTeam && pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_ENGINEER ) { pPlayer->StartBuildingObjectOfType( OBJ_SENTRYGUN ); if ( pPlayer->GetActiveWeapon() ) pPlayer->GetActiveWeapon()->PrimaryAttack(); break; } } } } } void MoveRedPlayersToBlueArea() { for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); if ( pPlayer && pPlayer->GetTeamNumber() == TF_TEAM_RED ) { // If too far from the spawns, then teleport into the blue area. CBaseEntity *pSpawn = GetBlueSpawnPoint( false ); if ( pPlayer->GetAbsOrigin().DistTo( pSpawn->GetAbsOrigin() ) > 2000 ) { pPlayer->Teleport( &pSpawn->GetAbsOrigin(), NULL, NULL ); } } } } virtual CBasePlayer* CreateBot() { int iTeam = (g_pServerBenchmark->RandomInt( 0, 1 ) == 1) ? TF_TEAM_BLUE : TF_TEAM_RED; if ( m_nBotsCreated == 0 ) iTeam = TF_TEAM_BLUE; int iClass = g_pServerBenchmark->RandomInt( TF_FIRST_NORMAL_CLASS, ( TF_LAST_NORMAL_CLASS - 1 ) ); //( TF_LAST_NORMAL_CLASS - 1 ) to exclude the new civilian class if ( m_nBotsCreated < 4 ) iClass = TF_CLASS_ENGINEER; // Make engineers first so they'll build sentries. CBasePlayer *pPlayer = BotPutInServer( false, false, iTeam, iClass, NULL ); if ( !pPlayer ) Error( "Server benchmark: Can't create bot." ); ++m_nBotsCreated; return pPlayer; } private: int m_nBotsCreated; bool m_bSetupLocalPlayer; Vector m_vLocalPlayerOrigin; QAngle m_vLocalPlayerEyeAngles; }; static CTFServerBenchmark g_TFServerBenchmark;