//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// // Author: Michael S. Booth (mike@turtlerockstudios.com), 2003 #include "cbase.h" #include "cs_shareddefs.h" #include "engine/IEngineSound.h" #include "keyvalues.h" #include "bot.h" #include "bot_util.h" #include "bot_profile.h" #include "cs_bot.h" #include // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" static int s_iBeamSprite = 0; extern ConVar mp_randomspawn_dist; extern ConVar mp_randomspawn_los; //-------------------------------------------------------------------------------------------------------------- /** * Return true if given name is already in use by another player */ bool UTIL_IsNameTaken( const char *name, bool ignoreHumans ) { for ( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *player = static_cast( UTIL_PlayerByIndex( i ) ); if (player == NULL) continue; if (player->IsPlayer() && player->IsBot()) { // bots can have prefixes so we need to check the name // against the profile name instead. CCSBot *bot = dynamic_cast(player); if ( bot && bot->GetProfile()->GetName() && FStrEq(name, bot->GetProfile()->GetName())) { return true; } } else { if (!ignoreHumans) { if (FStrEq( name, player->GetPlayerName() )) return true; } } } return false; } //-------------------------------------------------------------------------------------------------------------- int UTIL_ClientsInGame( void ) { int count = 0; for ( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBaseEntity *player = UTIL_PlayerByIndex( i ); if (player == NULL) continue; count++; } return count; } //-------------------------------------------------------------------------------------------------------------- /** * Return the number of non-bots on the given team */ int UTIL_HumansOnTeam( int teamID, bool isAlive ) { int count = 0; for ( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBaseEntity *entity = UTIL_PlayerByIndex( i ); if ( entity == NULL ) continue; CBasePlayer *player = static_cast( entity ); if (player->IsBot()) continue; if (player->GetTeamNumber() != teamID) continue; if (isAlive && !player->IsAlive()) continue; count++; } return count; } //-------------------------------------------------------------------------------------------------------------- int UTIL_BotsInGame( void ) { int count = 0; for (int i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *player = static_cast(UTIL_PlayerByIndex( i )); if ( player == NULL ) continue; if ( !player->IsBot() ) continue; count++; } return count; } //-------------------------------------------------------------------------------------------------------------- /** * Kick a bot from the given team. If no bot exists on the team, return false. */ bool UTIL_KickBotFromTeam( int kickTeam, bool bQueue ) { int i; // try to kick a dead bot first for ( i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *player = static_cast( UTIL_PlayerByIndex( i ) ); if (player == NULL) continue; if (!player->IsBot()) continue; if ( player->GetPendingTeamNumber() == TEAM_SPECTATOR ) { // bot has already been flagged for kicking continue; } if ( !player->IsAlive() ) { // Address issue with bots getting kicked during half-time (this was resulting in bots from the wrong team being kicked) if ( player->CanKickFromTeam( kickTeam ) ) { if ( bQueue ) { // bots flagged as spectators will be kicked at the beginning of the next round restart player->SetPendingTeamNum( TEAM_SPECTATOR ); } else { // its a bot on the right team - kick it engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", engine->GetPlayerUserId( player->edict() ) ) ); } return true; } } } // no dead bots, kick any bot on the given team for ( i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *player = static_cast( UTIL_PlayerByIndex( i ) ); if (player == NULL) continue; if (!player->IsBot()) continue; if ( player->GetPendingTeamNumber() == TEAM_SPECTATOR ) { // bot has already been flagged for kicking continue; } // Address issue with bots getting kicked during half-time (this was resulting in bots from the wrong team being kicked) if ( player->CanKickFromTeam( kickTeam ) ) { if ( bQueue ) { // bots flagged as spectators will be kicked at the beginning of the next round restart player->SetPendingTeamNum( TEAM_SPECTATOR ); } else { // its a bot on the right team - kick it engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", engine->GetPlayerUserId( player->edict() ) ) ); } return true; } } return false; } //-------------------------------------------------------------------------------------------------------------- /** * Return true if all of the members of the given team are bots */ bool UTIL_IsTeamAllBots( int team ) { int botCount = 0; for( int i=1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *player = static_cast( UTIL_PlayerByIndex( i ) ); if (player == NULL) continue; // skip players on other teams if (player->GetTeamNumber() != team) continue; // if not a bot, fail the test if (!player->IsBot()) return false; // is a bot on given team ++botCount; } // if team is empty, there are no bots return (botCount) ? true : false; } //-------------------------------------------------------------------------------------------------------------- /** * Return the closest active player to the given position. * If 'distance' is non-NULL, the distance to the closest player is returned in it. */ extern CBasePlayer *UTIL_GetClosestPlayer( const Vector &pos, float *distance ) { CBasePlayer *closePlayer = NULL; float closeDistSq = 999999999999.9f; for ( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *player = static_cast( UTIL_PlayerByIndex( i ) ); if (!IsEntityValid( player )) continue; if (!player->IsAlive()) continue; Vector playerOrigin = GetCentroid( player ); float distSq = (playerOrigin - pos).LengthSqr(); if (distSq < closeDistSq) { closeDistSq = distSq; closePlayer = static_cast( player ); } } if (distance) *distance = (float)sqrt( closeDistSq ); return closePlayer; } //-------------------------------------------------------------------------------------------------------------- /** * Return the closest active player on the given team to the given position. * If 'distance' is non-NULL, the distance to the closest player is returned in it. */ extern CBasePlayer *UTIL_GetClosestPlayer( const Vector &pos, int team, float *distance ) { CBasePlayer *closePlayer = NULL; float closeDistSq = 999999999999.9f; for ( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *player = static_cast( UTIL_PlayerByIndex( i ) ); if (!IsEntityValid( player )) continue; if (!player->IsAlive()) continue; if (player->GetTeamNumber() != team) continue; Vector playerOrigin = GetCentroid( player ); float distSq = (playerOrigin - pos).LengthSqr(); if (distSq < closeDistSq) { closeDistSq = distSq; closePlayer = static_cast( player ); } } if (distance) *distance = (float)sqrt( closeDistSq ); return closePlayer; } //-------------------------------------------------------------------------------------------------------------- // Takes the bot pointer and constructs the net name using the current bot name prefix. void UTIL_ConstructBotNetName( char *name, int nameLength, const BotProfile *profile ) { if (profile == NULL) { name[0] = 0; return; } // if there is no bot prefix just use the profile name. if ((cv_bot_prefix.GetString() == NULL) || (strlen(cv_bot_prefix.GetString()) == 0)) { Q_strncpy( name, profile->GetName(), nameLength ); return; } // find the highest difficulty const char *diffStr = BotDifficultyName[0]; for ( int i=BOT_EXPERT; i>0; --i ) { if ( profile->IsDifficulty( (BotDifficultyType)i ) ) { diffStr = BotDifficultyName[i]; break; } } const char *weaponStr = NULL; if ( profile->GetWeaponPreferenceCount() ) { weaponStr = profile->GetWeaponPreferenceAsString( 0 ); const char *translatedAlias = GetTranslatedWeaponAlias( weaponStr ); char wpnName[128]; Q_snprintf( wpnName, sizeof( wpnName ), "weapon_%s", translatedAlias ); WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( wpnName ); if ( hWpnInfo != GetInvalidWeaponInfoHandle() ) { CCSWeaponInfo *pWeaponInfo = dynamic_cast< CCSWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) ); if ( pWeaponInfo ) { CSWeaponType weaponType = pWeaponInfo->GetWeaponType(); weaponStr = WeaponClassAsString( weaponType ); } } } if ( !weaponStr ) { weaponStr = ""; } char skillStr[16]; Q_snprintf( skillStr, sizeof( skillStr ), "%.0f", profile->GetSkill()*100 ); char temp[MAX_PLAYER_NAME_LENGTH*2]; char prefix[MAX_PLAYER_NAME_LENGTH*2]; Q_strncpy( temp, cv_bot_prefix.GetString(), sizeof( temp ) ); Q_StrSubst( temp, "", diffStr, prefix, sizeof( prefix ) ); Q_StrSubst( prefix, "", weaponStr, temp, sizeof( temp ) ); Q_StrSubst( temp, "", skillStr, prefix, sizeof( prefix ) ); Q_snprintf( name, nameLength, "%s %s", prefix, profile->GetName() ); } //-------------------------------------------------------------------------------------------------------------- /** * Return true if anyone on the given team can see the given spot */ bool UTIL_IsVisibleToTeam( const Vector &spot, int team ) { for( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *player = static_cast( UTIL_PlayerByIndex( i ) ); if (player == NULL) continue; if (!player->IsAlive()) continue; if (player->GetTeamNumber() != team) continue; trace_t result; UTIL_TraceLine( player->EyePosition(), spot, CONTENTS_SOLID, player, COLLISION_GROUP_NONE, &result ); if ( result.fraction == 1.0f ) return true; } return false; } //-------------------------------------------------------------------------------------------------------------- /** * Return true if anyone on the given team can see the given spot */ bool UTIL_IsRandomSpawnFarEnoughAwayFromTeam( const Vector &spot, int team ) { if ( mp_randomspawn_dist.GetInt() <= 0 ) return true; if ( mp_randomspawn_los.GetInt() == 0 ) return true; for( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *player = static_cast( UTIL_PlayerByIndex( i ) ); if (player == NULL) continue; if (!player->IsAlive()) continue; if (player->GetTeamNumber() != team) continue; if ( mp_randomspawn_dist.GetInt() > 0 && (player->GetAbsOrigin()).DistTo( spot ) < mp_randomspawn_dist.GetInt() ) { //NDebugOverlay::Line( player->EyePosition(), spot + Vector( 0, 0, 32 ), 255, 0, 0, true, 4 ); //NDebugOverlay::Line( spot, spot + Vector( 0, 0, 64 ), 255, 128, 0, true, 4 ); continue; } else { return true; } } return false; } //------------------------------------------------------------------------------------------------------------ void UTIL_DrawBeamFromEnt( int i, Vector vecEnd, int iLifetime, byte bRed, byte bGreen, byte bBlue ) { /* BOTPORT: What is the replacement for MESSAGE_BEGIN? MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecEnd ); // vecEnd = origin??? WRITE_BYTE( TE_BEAMENTPOINT ); WRITE_SHORT( i ); WRITE_COORD( vecEnd.x ); WRITE_COORD( vecEnd.y ); WRITE_COORD( vecEnd.z ); WRITE_SHORT( s_iBeamSprite ); WRITE_BYTE( 0 ); // startframe WRITE_BYTE( 0 ); // framerate WRITE_BYTE( iLifetime ); // life WRITE_BYTE( 10 ); // width WRITE_BYTE( 0 ); // noise WRITE_BYTE( bRed ); // r, g, b WRITE_BYTE( bGreen ); // r, g, b WRITE_BYTE( bBlue ); // r, g, b WRITE_BYTE( 255 ); // brightness WRITE_BYTE( 0 ); // speed MESSAGE_END(); */ } //------------------------------------------------------------------------------------------------------------ void UTIL_DrawBeamPoints( Vector vecStart, Vector vecEnd, int iLifetime, byte bRed, byte bGreen, byte bBlue ) { NDebugOverlay::Line( vecStart, vecEnd, bRed, bGreen, bBlue, true, 0.1f ); /* MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecStart ); WRITE_BYTE( TE_BEAMPOINTS ); WRITE_COORD( vecStart.x ); WRITE_COORD( vecStart.y ); WRITE_COORD( vecStart.z ); WRITE_COORD( vecEnd.x ); WRITE_COORD( vecEnd.y ); WRITE_COORD( vecEnd.z ); WRITE_SHORT( s_iBeamSprite ); WRITE_BYTE( 0 ); // startframe WRITE_BYTE( 0 ); // framerate WRITE_BYTE( iLifetime ); // life WRITE_BYTE( 10 ); // width WRITE_BYTE( 0 ); // noise WRITE_BYTE( bRed ); // r, g, b WRITE_BYTE( bGreen ); // r, g, b WRITE_BYTE( bBlue ); // r, g, b WRITE_BYTE( 255 ); // brightness WRITE_BYTE( 0 ); // speed MESSAGE_END(); */ } //------------------------------------------------------------------------------------------------------------ void CONSOLE_ECHO( char * pszMsg, ... ) { va_list argptr; static char szStr[1024]; va_start( argptr, pszMsg ); vsprintf( szStr, pszMsg, argptr ); va_end( argptr ); Msg( "%s", szStr ); } //------------------------------------------------------------------------------------------------------------ void BotPrecache( void ) { s_iBeamSprite = CBaseEntity::PrecacheModel( "sprites/smoke.vmt" ); } //------------------------------------------------------------------------------------------------------------ #define COS_TABLE_SIZE 256 static float cosTable[ COS_TABLE_SIZE ]; void InitBotTrig( void ) { for( int i=0; imaxClients; ++p ) { CBasePlayer *player = static_cast( UTIL_PlayerByIndex( p ) ); if (!IsEntityValid( player )) continue; if (player == ignore) continue; if (!player->IsAlive()) continue; if (ignoreTeam && player->GetTeamNumber() == ignoreTeam) continue; // compute player's unit aiming vector Vector viewForward; AngleVectors( player->GetFinalAimAngle(), &viewForward ); const float longRange = 5000.0f; Vector playerOrigin = GetCentroid( player ); Vector playerTarget = playerOrigin + longRange * viewForward; Vector result( 0, 0, 0 ); if (IsIntersecting2D( start, finish, playerOrigin, playerTarget, &result )) { // simple check to see if intersection lies in the Z range of the path float loZ, hiZ; if (start.z < finish.z) { loZ = start.z; hiZ = finish.z; } else { loZ = finish.z; hiZ = start.z; } if (result.z >= loZ && result.z <= hiZ + HumanHeight) return true; } } return false; } //-------------------------------------------------------------------------------------------------------------- /** * Performs a simple case-insensitive string comparison, honoring trailing * wildcards */ bool WildcardMatch( const char *query, const char *test ) { if ( !query || !test ) return false; while ( *test && *query ) { char nameChar = *test; char queryChar = *query; if ( tolower(nameChar) != tolower(queryChar) ) // case-insensitive break; ++test; ++query; } if ( *query == 0 && *test == 0 ) return true; // Support trailing * if ( *query == '*' ) return true; return false; }