summaryrefslogtreecommitdiff
path: root/game/shared/cstrike/bot/bot_util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/cstrike/bot/bot_util.cpp')
-rw-r--r--game/shared/cstrike/bot/bot_util.cpp604
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;
+}
+
+
+