Example #1
0
// Load the bot profile database
void BotProfileManager::Init(const char *filename, unsigned int *checksum)
{
	static const char *BotDifficultyName[] = { "EASY", "NORMAL", "HARD", "EXPERT", nullptr };

	int dataLength;
	char *dataPointer = (char *)LOAD_FILE_FOR_ME(const_cast<char *>(filename), &dataLength);
	char *dataFile = dataPointer;

	if (!dataFile)
	{
		if (AreBotsAllowed())
		{
			CONSOLE_ECHO("WARNING: Cannot access bot profile database '%s'\n", filename);
		}

		return;
	}

	// compute simple checksum
	if (checksum)
	{
		*checksum = ComputeSimpleChecksum((const unsigned char *)dataPointer, dataLength);
	}

	// keep list of templates used for inheritance
	BotProfileList templateList;
	BotProfile defaultProfile;

	// Parse the BotProfile.db into BotProfile instances
	while (true)
	{
		dataFile = SharedParse(dataFile);
		if (!dataFile)
			break;

		char *token = SharedGetToken();

		bool isDefault = (!Q_stricmp(token, "Default"));
		bool isTemplate = (!Q_stricmp(token, "Template"));
		bool isCustomSkin = (!Q_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);
				FREE_FILE(dataPointer);
				return;
			}

			token = SharedGetToken();
			Q_snprintf(skinName, BufLen, "%s", token);

			// get attribute name
			dataFile = SharedParse(dataFile);
			if (!dataFile)
			{
				CONSOLE_ECHO("Error parsing %s - expected 'Model'\n", filename);
				FREE_FILE(dataPointer);
				return;
			}

			token = SharedGetToken();
			if (Q_stricmp(token, "Model") != 0)
			{
				CONSOLE_ECHO("Error parsing %s - expected 'Model'\n", filename);
				FREE_FILE(dataPointer);
				return;
			}

			// eat '='
			dataFile = SharedParse(dataFile);
			if (!dataFile)
			{
				CONSOLE_ECHO("Error parsing %s - expected '='\n", filename);
				FREE_FILE(dataPointer);
				return;
			}

			token = SharedGetToken();
			if (Q_strcmp(token, "=") != 0)
			{
				CONSOLE_ECHO("Error parsing %s - expected '='\n", filename);
				FREE_FILE(dataPointer);
				return;
			}

			// get attribute value
			dataFile = SharedParse(dataFile);
			if (!dataFile)
			{
				CONSOLE_ECHO("Error parsing %s - expected attribute value\n", filename);
				FREE_FILE(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[Q_strlen(token) * 2 + Q_strlen("models/player//.mdl") + 1];
				Q_sprintf(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);
				FREE_FILE(dataPointer);
				return;
			}

			token = SharedGetToken();
			if (Q_strcmp(token, "End") != 0)
			{
				CONSOLE_ECHO("Error parsing %s - expected 'End'\n", filename);
				FREE_FILE(dataPointer);
				return;
			}

			// it's just a custom skin - no need to do inheritance on a bot profile, etc.
			continue;
		}

		// 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 = nullptr;

			// template names are separated by "+"
			while (true)
			{
				char *c = Q_strchr(token, '+');
				if (c)
					*c = '\0';

				// find the given template name
				for (auto templates : templateList)
				{
					if (!Q_stricmp(templates->GetName(), token))
					{
						inherit = templates;
						break;
					}
				}

				if (!inherit)
				{
					CONSOLE_ECHO("Error parsing '%s' - invalid template reference '%s'\n", filename, token);
					FREE_FILE(dataPointer);
					return;
				}

				// inherit the data
				profile->Inherit(inherit, &defaultProfile);

				if (c == nullptr)
					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);
				FREE_FILE(dataPointer);
				return;
			}

			profile->m_name = CloneString(SharedGetToken());

#ifdef REGAMEDLL_FIXES
			if (RANDOM_LONG(0, 2) == 2)
#else
			// 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)
#endif
			{
				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);
				FREE_FILE(dataPointer);
				return;
			}

			token = SharedGetToken();

			// check for End delimiter
			if (!Q_stricmp(token, "End"))
				break;

			// found attribute name - keep it
			char attributeName[64];
			Q_strcpy(attributeName, token);

			// eat '='
			dataFile = SharedParse(dataFile);
			if (!dataFile)
			{
				CONSOLE_ECHO("Error parsing %s - expected '='\n", filename);
				FREE_FILE(dataPointer);
				return;
			}

			token = SharedGetToken();
			if (Q_strcmp(token, "=") != 0)
			{
				CONSOLE_ECHO("Error parsing %s - expected '='\n", filename);
				FREE_FILE(dataPointer);
				return;
			}

			// get attribute value
			dataFile = SharedParse(dataFile);
			if (!dataFile)
			{
				CONSOLE_ECHO("Error parsing %s - expected attribute value\n", filename);
				FREE_FILE(dataPointer);
				return;
			}

			token = SharedGetToken();

			// store value in appropriate attribute
			if (!Q_stricmp("Aggression", attributeName))
			{
				profile->m_aggression = Q_atof(token) / 100.0f;
			}
			else if (!Q_stricmp("Skill", attributeName))
			{
				profile->m_skill = Q_atof(token) / 100.0f;
			}
			else if (!Q_stricmp("Skin", attributeName))
			{
				profile->m_skin = Q_atoi(token);

				if (profile->m_skin == 0)
				{
					// Q_atoi() failed - try to look up a custom skin by name
					profile->m_skin = GetCustomSkinIndex(token, filename);
				}
			}
			else if (!Q_stricmp("Teamwork", attributeName))
			{
				profile->m_teamwork = Q_atof(token) / 100.0f;
			}
			else if (!Q_stricmp("Cost", attributeName))
			{
				profile->m_cost = Q_atoi(token);
			}
			else if (!Q_stricmp("VoicePitch", attributeName))
			{
				profile->m_voicePitch = Q_atoi(token);
			}
			else if (!Q_stricmp("VoiceBank", attributeName))
			{
				profile->m_voiceBank = FindVoiceBankIndex(token);
			}
			else if (!Q_stricmp("WeaponPreference", attributeName))
			{
				// weapon preferences override parent prefs
				if (isFirstWeaponPref)
				{
					isFirstWeaponPref = false;
					profile->m_weaponPreferenceCount = 0;
				}

				if (!Q_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 (!Q_stricmp("ReactionTime", attributeName))
			{
				profile->m_reactionTime = Q_atof(token);

#ifndef GAMEUI_EXPORTS
				// subtract off latency due to "think" update rate.
				// In GameUI, we don't really care.
				profile->m_reactionTime -= g_flBotFullThinkInterval;
#endif

			}
			else if (!Q_stricmp("AttackDelay", attributeName))
			{
				profile->m_attackDelay = Q_atof(token);
			}
			else if (!Q_stricmp("Difficulty", attributeName))
			{
				// override inheritance
				profile->m_difficultyFlags = 0;

				// parse bit flags
				while (true)
				{
					char *c = Q_strchr(token, '+');
					if (c)
						*c = '\0';

					for (int i = 0; i < NUM_DIFFICULTY_LEVELS; i++)
					{
						if (!Q_stricmp(BotDifficultyName[i], token))
							profile->m_difficultyFlags |= (1<<i);
					}

					if (c == nullptr)
						break;

					token = c + 1;
				}
			}
			else if (!Q_stricmp("Team", attributeName))
			{
				if (!Q_stricmp(token, "T"))
				{
					profile->m_teams = BOT_TEAM_T;
				}
				else if (!Q_stricmp(token, "CT"))
				{
					profile->m_teams = BOT_TEAM_CT;
				}
				else
				{
					profile->m_teams = BOT_TEAM_ANY;
				}
			}
			else
			{
				CONSOLE_ECHO("Error parsing %s - unknown attribute '%s'\n", filename, attributeName);
			}
		}

		if (!isDefault)
		{
			if (isTemplate)
			{
				// add to template list
				templateList.push_back(profile);
			}
			else
			{
				// add profile to the master list
				m_profileList.push_back(profile);
			}
		}
	}

	FREE_FILE(dataPointer);

	// free the templates
	for (auto templates : templateList)
		delete templates;

	templateList.clear();
}
/**
 * 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 );
            }
        }
    }