diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/cstrike/bot/bot_profile.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/shared/cstrike/bot/bot_profile.cpp')
| -rw-r--r-- | game/shared/cstrike/bot/bot_profile.cpp | 704 |
1 files changed, 704 insertions, 0 deletions
diff --git a/game/shared/cstrike/bot/bot_profile.cpp b/game/shared/cstrike/bot/bot_profile.cpp new file mode 100644 index 0000000..13da6b0 --- /dev/null +++ b/game/shared/cstrike/bot/bot_profile.cpp @@ -0,0 +1,704 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +// Author: Michael S. Booth ([email protected]), 2003 + +#include "cbase.h" + +#pragma warning( disable : 4530 ) // STL uses exceptions, but we are not compiling with them - ignore warning + +#define DEFINE_DIFFICULTY_NAMES +#include "bot_profile.h" +#include "shared_util.h" + +#include "bot.h" +#include "bot_util.h" +#include "cs_bot.h" // BOTPORT: Remove this CS dependency + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +BotProfileManager *TheBotProfiles = NULL; + + +//-------------------------------------------------------------------------------------------------------- +/** + * Generates a filename-decorated skin name + */ +static const char * GetDecoratedSkinName( const char *name, const char *filename ) +{ + const int BufLen = _MAX_PATH + 64; + static char buf[BufLen]; + Q_snprintf( buf, sizeof( buf ), "%s/%s", filename, name ); + return buf; +} + +//-------------------------------------------------------------------------------------------------------------- +const char* BotProfile::GetWeaponPreferenceAsString( int i ) const +{ + if ( i < 0 || i >= m_weaponPreferenceCount ) + return NULL; + + return WeaponIDToAlias( m_weaponPreference[ i ] ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Return true if this profile has a primary weapon preference + */ +bool BotProfile::HasPrimaryPreference( void ) const +{ + for( int i=0; i<m_weaponPreferenceCount; ++i ) + { + if (IsPrimaryWeapon( m_weaponPreference[i] )) + return true; + } + + return false; +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Return true if this profile has a pistol weapon preference + */ +bool BotProfile::HasPistolPreference( void ) const +{ + for( int i=0; i<m_weaponPreferenceCount; ++i ) + if (IsSecondaryWeapon( m_weaponPreference[i] )) + return true; + + return false; +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Return true if this profile is valid for the specified team + */ +bool BotProfile::IsValidForTeam( int team ) const +{ + return ( team == TEAM_UNASSIGNED || m_teams == TEAM_UNASSIGNED || team == m_teams ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** +* Return true if this profile inherits from the specified template +*/ +bool BotProfile::InheritsFrom( const char *name ) const +{ + if ( WildcardMatch( name, GetName() ) ) + return true; + + for ( int i=0; i<m_templates.Count(); ++i ) + { + const BotProfile *queryTemplate = m_templates[i]; + if ( queryTemplate->InheritsFrom( name ) ) + { + return true; + } + } + return false; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Constructor + */ +BotProfileManager::BotProfileManager( void ) +{ + m_nextSkin = 0; + for (int i=0; i<NumCustomSkins; ++i) + { + m_skins[i] = NULL; + m_skinFilenames[i] = NULL; + m_skinModelnames[i] = NULL; + } +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Load the bot profile database + */ +void BotProfileManager::Init( const char *filename, unsigned int *checksum ) +{ + FileHandle_t file = filesystem->Open( filename, "r" ); + + if (!file) + { + if ( true ) // UTIL_IsGame( "czero" ) ) + { + CONSOLE_ECHO( "WARNING: Cannot access bot profile database '%s'\n", filename ); + } + return; + } + + int dataLength = filesystem->Size( filename ); + char *dataPointer = new char[ dataLength ]; + int dataReadLength = filesystem->Read( dataPointer, dataLength, file ); + filesystem->Close( file ); + if ( dataReadLength > 0 ) + { + // NULL-terminate based on the length read in, since Read() can transform \r\n to \n and + // return fewer bytes than we were expecting. + dataPointer[ dataReadLength - 1 ] = 0; + } + + const char *dataFile = dataPointer; + + // compute simple checksum + if (checksum) + { + *checksum = 0; // ComputeSimpleChecksum( (const unsigned char *)dataPointer, dataLength ); + } + + BotProfile defaultProfile; + + // + // Parse the BotProfile.db into BotProfile instances + // + while( true ) + { + dataFile = SharedParse( dataFile ); + if (!dataFile) + break; + + char *token = SharedGetToken(); + + bool isDefault = (!stricmp( token, "Default" )); + bool isTemplate = (!stricmp( token, "Template" )); + bool isCustomSkin = (!stricmp( token, "Skin" )); + + if ( isCustomSkin ) + { + const int BufLen = 64; + char skinName[BufLen]; + + // get skin name + dataFile = SharedParse( dataFile ); + if (!dataFile) + { + CONSOLE_ECHO( "Error parsing %s - expected skin name\n", filename ); + delete [] dataPointer; + return; + } + token = SharedGetToken(); + Q_snprintf( skinName, sizeof( skinName ), "%s", token ); + + // get attribute name + dataFile = SharedParse( dataFile ); + if (!dataFile) + { + CONSOLE_ECHO( "Error parsing %s - expected 'Model'\n", filename ); + delete [] dataPointer; + return; + } + token = SharedGetToken(); + if (stricmp( "Model", token )) + { + CONSOLE_ECHO( "Error parsing %s - expected 'Model'\n", filename ); + delete [] dataPointer; + return; + } + + // eat '=' + dataFile = SharedParse( dataFile ); + if (!dataFile) + { + CONSOLE_ECHO( "Error parsing %s - expected '='\n", filename ); + delete [] dataPointer; + return; + } + token = SharedGetToken(); + if (strcmp( "=", token )) + { + CONSOLE_ECHO( "Error parsing %s - expected '='\n", filename ); + delete [] dataPointer; + return; + } + + // get attribute value + dataFile = SharedParse( dataFile ); + if (!dataFile) + { + CONSOLE_ECHO( "Error parsing %s - expected attribute value\n", filename ); + delete [] dataPointer; + return; + } + token = SharedGetToken(); + + const char *decoratedName = GetDecoratedSkinName( skinName, filename ); + bool skinExists = GetCustomSkinIndex( decoratedName ) > 0; + if ( m_nextSkin < NumCustomSkins && !skinExists ) + { + // decorate the name + m_skins[ m_nextSkin ] = CloneString( decoratedName ); + + // construct the model filename + m_skinModelnames[ m_nextSkin ] = CloneString( token ); + m_skinFilenames[ m_nextSkin ] = new char[ strlen(token)*2 + strlen("models/player//.mdl") + 1 ]; + Q_snprintf( m_skinFilenames[ m_nextSkin ], sizeof( m_skinFilenames[ m_nextSkin ] ), "models/player/%s/%s.mdl", token, token ); + ++m_nextSkin; + } + + // eat 'End' + dataFile = SharedParse( dataFile ); + if (!dataFile) + { + CONSOLE_ECHO( "Error parsing %s - expected 'End'\n", filename ); + delete [] dataPointer; + return; + } + token = SharedGetToken(); + if (strcmp( "End", token )) + { + CONSOLE_ECHO( "Error parsing %s - expected 'End'\n", filename ); + delete [] dataPointer; + return; + } + + continue; // it's just a custom skin - no need to do inheritance on a bot profile, etc. + } + + // encountered a new profile + BotProfile *profile; + + if (isDefault) + { + profile = &defaultProfile; + } + else + { + profile = new BotProfile; + + // always inherit from Default + *profile = defaultProfile; + } + + // do inheritance in order of appearance + if (!isTemplate && !isDefault) + { + const BotProfile *inherit = NULL; + + // template names are separated by "+" + while(true) + { + char *c = strchr( token, '+' ); + if (c) + *c = '\000'; + + // find the given template name + FOR_EACH_LL( m_templateList, it ) + { + BotProfile *profile = m_templateList[ it ]; + if (!stricmp( profile->GetName(), token )) + { + inherit = profile; + break; + } + } + + if (inherit == NULL) + { + CONSOLE_ECHO( "Error parsing '%s' - invalid template reference '%s'\n", filename, token ); + delete [] dataPointer; + return; + } + + // inherit the data + profile->Inherit( inherit, &defaultProfile ); + + if (c == NULL) + break; + + token = c+1; + } + } + + + // get name of this profile + if (!isDefault) + { + dataFile = SharedParse( dataFile ); + if (!dataFile) + { + CONSOLE_ECHO( "Error parsing '%s' - expected name\n", filename ); + delete [] dataPointer; + return; + } + profile->m_name = CloneString( SharedGetToken() ); + + /** + * HACK HACK + * Until we have a generalized means of storing bot preferences, we're going to hardcode the bot's + * preference towards silencers based on his name. + */ + if ( profile->m_name[0] % 2 ) + { + profile->m_prefersSilencer = true; + } + } + + // read attributes for this profile + bool isFirstWeaponPref = true; + while( true ) + { + // get next token + dataFile = SharedParse( dataFile ); + if (!dataFile) + { + CONSOLE_ECHO( "Error parsing %s - expected 'End'\n", filename ); + delete [] dataPointer; + return; + } + token = SharedGetToken(); + + // check for End delimiter + if (!stricmp( token, "End" )) + break; + + // found attribute name - keep it + char attributeName[64]; + strcpy( attributeName, token ); + + // eat '=' + dataFile = SharedParse( dataFile ); + if (!dataFile) + { + CONSOLE_ECHO( "Error parsing %s - expected '='\n", filename ); + delete [] dataPointer; + return; + } + + token = SharedGetToken(); + if (strcmp( "=", token )) + { + CONSOLE_ECHO( "Error parsing %s - expected '='\n", filename ); + delete [] dataPointer; + return; + } + + // get attribute value + dataFile = SharedParse( dataFile ); + if (!dataFile) + { + CONSOLE_ECHO( "Error parsing %s - expected attribute value\n", filename ); + delete [] dataPointer; + return; + } + token = SharedGetToken(); + + // store value in appropriate attribute + if (!stricmp( "Aggression", attributeName )) + { + profile->m_aggression = (float)atof(token) / 100.0f; + } + else if (!stricmp( "Skill", attributeName )) + { + profile->m_skill = (float)atof(token) / 100.0f; + } + else if (!stricmp( "Skin", attributeName )) + { + profile->m_skin = atoi(token); + if ( profile->m_skin == 0 ) + { + // atoi() failed - try to look up a custom skin by name + profile->m_skin = GetCustomSkinIndex( token, filename ); + } + } + else if (!stricmp( "Teamwork", attributeName )) + { + profile->m_teamwork = (float)atof(token) / 100.0f; + } + else if (!stricmp( "Cost", attributeName )) + { + profile->m_cost = atoi(token); + } + else if (!stricmp( "VoicePitch", attributeName )) + { + profile->m_voicePitch = atoi(token); + } + else if (!stricmp( "VoiceBank", attributeName )) + { + profile->m_voiceBank = FindVoiceBankIndex( token ); + } + else if (!stricmp( "WeaponPreference", attributeName )) + { + // weapon preferences override parent prefs + if (isFirstWeaponPref) + { + isFirstWeaponPref = false; + profile->m_weaponPreferenceCount = 0; + } + + if (!stricmp( token, "none" )) + { + profile->m_weaponPreferenceCount = 0; + } + else + { + if (profile->m_weaponPreferenceCount < BotProfile::MAX_WEAPON_PREFS) + { + profile->m_weaponPreference[ profile->m_weaponPreferenceCount++ ] = AliasToWeaponID( token ); + } + } + } + else if (!stricmp( "ReactionTime", attributeName )) + { + profile->m_reactionTime = (float)atof(token); + +#ifndef GAMEUI_EXPORTS + // subtract off latency due to "think" update rate. + // In GameUI, we don't really care. + //profile->m_reactionTime -= g_BotUpdateInterval; +#endif + + } + else if (!stricmp( "AttackDelay", attributeName )) + { + profile->m_attackDelay = (float)atof(token); + } + else if (!stricmp( "Difficulty", attributeName )) + { + // override inheritance + profile->m_difficultyFlags = 0; + + // parse bit flags + while(true) + { + char *c = strchr( token, '+' ); + if (c) + *c = '\000'; + + for( int i=0; i<NUM_DIFFICULTY_LEVELS; ++i ) + if (!stricmp( BotDifficultyName[i], token )) + profile->m_difficultyFlags |= (1 << i); + + if (c == NULL) + break; + + token = c+1; + } + } + else if (!stricmp( "Team", attributeName )) + { + if ( !stricmp( token, "T" ) ) + { + profile->m_teams = TEAM_TERRORIST; + } + else if ( !stricmp( token, "CT" ) ) + { + profile->m_teams = TEAM_CT; + } + else + { + profile->m_teams = TEAM_UNASSIGNED; + } + } + else + { + CONSOLE_ECHO( "Error parsing %s - unknown attribute '%s'\n", filename, attributeName ); + } + } + + if (!isDefault) + { + if (isTemplate) + { + // add to template list + m_templateList.AddToTail( profile ); + } + else + { + // add profile to the master list + m_profileList.AddToTail( profile ); + } + } + } + + delete [] dataPointer; +} + +//-------------------------------------------------------------------------------------------------------------- +BotProfileManager::~BotProfileManager( void ) +{ + Reset(); +} + +//-------------------------------------------------------------------------------------------------------------- +/** + * Free all bot profiles + */ +void BotProfileManager::Reset( void ) +{ + m_profileList.PurgeAndDeleteElements(); + m_templateList.PurgeAndDeleteElements(); + + int i; + + for (i=0; i<NumCustomSkins; ++i) + { + if ( m_skins[i] ) + { + delete[] m_skins[i]; + m_skins[i] = NULL; + } + if ( m_skinFilenames[i] ) + { + delete[] m_skinFilenames[i]; + m_skinFilenames[i] = NULL; + } + if ( m_skinModelnames[i] ) + { + delete[] m_skinModelnames[i]; + m_skinModelnames[i] = NULL; + } + } + + for ( i=0; i<m_voiceBanks.Count(); ++i ) + { + delete[] m_voiceBanks[i]; + } + m_voiceBanks.RemoveAll(); +} + +//-------------------------------------------------------------------------------------------------------- +/** + * Returns custom skin name at a particular index + */ +const char * BotProfileManager::GetCustomSkin( int index ) +{ + if ( index < FirstCustomSkin || index > LastCustomSkin ) + { + return NULL; + } + + return m_skins[ index - FirstCustomSkin ]; +} + +//-------------------------------------------------------------------------------------------------------- +/** + * Returns custom skin filename at a particular index + */ +const char * BotProfileManager::GetCustomSkinFname( int index ) +{ + if ( index < FirstCustomSkin || index > LastCustomSkin ) + { + return NULL; + } + + return m_skinFilenames[ index - FirstCustomSkin ]; +} + +//-------------------------------------------------------------------------------------------------------- +/** + * Returns custom skin modelname at a particular index + */ +const char * BotProfileManager::GetCustomSkinModelname( int index ) +{ + if ( index < FirstCustomSkin || index > LastCustomSkin ) + { + return NULL; + } + + return m_skinModelnames[ index - FirstCustomSkin ]; +} + +//-------------------------------------------------------------------------------------------------------- +/** + * Looks up a custom skin index by filename-decorated name (will decorate the name if filename is given) + */ +int BotProfileManager::GetCustomSkinIndex( const char *name, const char *filename ) +{ + const char * skinName = name; + if ( filename ) + { + skinName = GetDecoratedSkinName( name, filename ); + } + + for (int i=0; i<NumCustomSkins; ++i) + { + if ( m_skins[i] ) + { + if ( !stricmp( skinName, m_skins[i] ) ) + { + return FirstCustomSkin + i; + } + } + } + return 0; +} + + +//-------------------------------------------------------------------------------------------------------- +/** + * return index of the (custom) bot phrase db, inserting it if needed + */ +int BotProfileManager::FindVoiceBankIndex( const char *filename ) +{ + int index = 0; + + for ( int i=0; i<m_voiceBanks.Count(); ++i ) + { + if ( !stricmp( filename, m_voiceBanks[i] ) ) + { + return index; + } + } + + m_voiceBanks.AddToTail( CloneString( filename ) ); + return index; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Return random unused profile that matches the given difficulty level + */ +const BotProfile *BotProfileManager::GetRandomProfile( BotDifficultyType difficulty, int team, CSWeaponType weaponType ) const +{ + // count up valid profiles + CUtlVector< const BotProfile * > profiles; + FOR_EACH_LL( m_profileList, it ) + { + const BotProfile *profile = m_profileList[ it ]; + + // Match difficulty + if ( !profile->IsDifficulty( difficulty ) ) + continue; + + // Prevent duplicate names + if ( UTIL_IsNameTaken( profile->GetName() ) ) + continue; + + // Match team choice + if ( !profile->IsValidForTeam( team ) ) + continue; + + // Match desired weapon + if ( weaponType != WEAPONTYPE_UNKNOWN ) + { + if ( !profile->GetWeaponPreferenceCount() ) + continue; + + if ( weaponType != WeaponClassFromWeaponID( (CSWeaponID)profile->GetWeaponPreference( 0 ) ) ) + continue; + } + + profiles.AddToTail( profile ); + } + + if ( !profiles.Count() ) + return NULL; + + // select one at random + int which = RandomInt( 0, profiles.Count()-1 ); + return profiles[which]; +} + |