diff options
Diffstat (limited to 'game/shared/cstrike/bot/bot_util.cpp')
| -rw-r--r-- | game/shared/cstrike/bot/bot_util.cpp | 604 |
1 files changed, 604 insertions, 0 deletions
diff --git a/game/shared/cstrike/bot/bot_util.cpp b/game/shared/cstrike/bot/bot_util.cpp new file mode 100644 index 0000000..bd10ff2 --- /dev/null +++ b/game/shared/cstrike/bot/bot_util.cpp @@ -0,0 +1,604 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +// Author: Michael S. Booth ([email protected]), 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 <ctype.h> +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +static int s_iBeamSprite = 0; + +//-------------------------------------------------------------------------------------------------------------- +/** + * 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<CBasePlayer *>( 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<CCSBot *>(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<CBasePlayer *>( 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<CBasePlayer *>(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 ) +{ + int i; + + // try to kick a dead bot first + for ( i = 1; i <= gpGlobals->maxClients; ++i ) + { + CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) ); + + if (player == NULL) + continue; + + if (!player->IsBot()) + continue; + + if (!player->IsAlive() && player->GetTeamNumber() == kickTeam) + { + // its a bot on the right team - kick it + engine->ServerCommand( UTIL_VarArgs( "kick \"%s\"\n", player->GetPlayerName() ) ); + + return true; + } + } + + // no dead bots, kick any bot on the given team + for ( i = 1; i <= gpGlobals->maxClients; ++i ) + { + CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) ); + + if (player == NULL) + continue; + + if (!player->IsBot()) + continue; + + if (player->GetTeamNumber() == kickTeam) + { + // its a bot on the right team - kick it + engine->ServerCommand( UTIL_VarArgs( "kick \"%s\"\n", player->GetPlayerName() ) ); + + 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<CBasePlayer *>( 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<CBasePlayer *>( 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<CBasePlayer *>( 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<CBasePlayer *>( 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<CBasePlayer *>( 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->m_WeaponType; + 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, "<difficulty>", diffStr, prefix, sizeof( prefix ) ); + Q_StrSubst( prefix, "<weaponclass>", weaponStr, temp, sizeof( temp ) ); + Q_StrSubst( temp, "<skill>", 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<CBasePlayer *>( 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; +} + + +//------------------------------------------------------------------------------------------------------------ +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( const 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.spr" ); +} + +//------------------------------------------------------------------------------------------------------------ +#define COS_TABLE_SIZE 256 +static float cosTable[ COS_TABLE_SIZE ]; + +void InitBotTrig( void ) +{ + for( int i=0; i<COS_TABLE_SIZE; ++i ) + { + float angle = (float)(2.0f * M_PI * i / (float)(COS_TABLE_SIZE-1)); + cosTable[i] = (float)cos( angle ); + } +} + +float BotCOS( float angle ) +{ + angle = AngleNormalizePositive( angle ); + int i = (int)( angle * (COS_TABLE_SIZE-1) / 360.0f ); + return cosTable[i]; +} + +float BotSIN( float angle ) +{ + angle = AngleNormalizePositive( angle - 90 ); + int i = (int)( angle * (COS_TABLE_SIZE-1) / 360.0f ); + return cosTable[i]; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Send a "hint" message to all players, dead or alive. + */ +void HintMessageToAllPlayers( const char *message ) +{ + hudtextparms_t textParms; + + textParms.x = -1.0f; + textParms.y = -1.0f; + textParms.fadeinTime = 1.0f; + textParms.fadeoutTime = 5.0f; + textParms.holdTime = 5.0f; + textParms.fxTime = 0.0f; + textParms.r1 = 100; + textParms.g1 = 255; + textParms.b1 = 100; + textParms.r2 = 255; + textParms.g2 = 255; + textParms.b2 = 255; + textParms.effect = 0; + textParms.channel = 0; + + UTIL_HudMessageAll( textParms, message ); +} + +//-------------------------------------------------------------------------------------------------------------------- +/** + * Return true if moving from "start" to "finish" will cross a player's line of fire. + * The path from "start" to "finish" is assumed to be a straight line. + * "start" and "finish" are assumed to be points on the ground. + */ +bool IsCrossingLineOfFire( const Vector &start, const Vector &finish, CBaseEntity *ignore, int ignoreTeam ) +{ + for ( int p=1; p <= gpGlobals->maxClients; ++p ) + { + CBasePlayer *player = static_cast<CBasePlayer *>( 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->EyeAngles() + player->GetPunchAngle(), &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; +} + + + |