// add or remove bots to match team size targets set by 'bot fill' command void G_BotFill(bool immediately) { static int nextCheck = 0; if (!immediately && level.time < nextCheck) { return; // don't check every frame to prevent warning spam } nextCheck = level.time + 2000; for (team_t team : {TEAM_ALIENS, TEAM_HUMANS}) { auto& t = level.team[team]; int teamSize = t.numClients; if (teamSize > t.botFillTeamSize && t.numBots > 0) { for (int client = 0; client < MAX_CLIENTS; client++) { if (level.clients[client].pers.connected == CON_CONNECTED && level.clients[client].pers.team == team && level.clients[client].pers.isFillerBot) { G_BotDel(client); if (--teamSize <= t.botFillTeamSize) { break; } } } } else if (teamSize < t.botFillTeamSize) { int additionalBots = t.botFillTeamSize - teamSize; while (additionalBots--) { if (!G_BotAdd(BOT_NAME_FROM_LIST, team, t.botFillSkillLevel, BOT_DEFAULT_BEHAVIOR, true)) { break; } } } } }
void G_BotDelAllBots() { int i; for ( i = 0; i < MAX_CLIENTS; i++ ) { if ( g_entities[i].r.svFlags & SVF_BOT && level.clients[i].pers.connected != CON_DISCONNECTED ) { G_BotDel( i ); } } for ( i = 0; i < botNames[TEAM_ALIENS].count; ++i ) { botNames[TEAM_ALIENS].name[i].inUse = false; } for ( i = 0; i < botNames[TEAM_HUMANS].count; ++i ) { botNames[TEAM_HUMANS].name[i].inUse = false; } for (team_t team : {TEAM_ALIENS, TEAM_HUMANS}) { level.team[team].botFillTeamSize = 0; } }
void G_BotCleanup() { for ( int i = 0; i < MAX_CLIENTS; ++i ) { if ( g_entities[i].r.svFlags & SVF_BOT && level.clients[i].pers.connected != CON_DISCONNECTED ) { G_BotDel( i ); } } G_BotClearNames(); FreeTreeList( &treeList ); G_BotNavCleanup(); }
/** * Deletes all bots */ void G_BotDelAll( void ) { int i; gentity_t *bot; //If there are no bots, don't try to remove them if(level.humanBots + level.alienBots == 0) return; bot = &g_entities[ 0 ]; for( i = 0; i < level.maxclients; i++, bot++ ) { if(bot && bot->inuse == qtrue) { if(bot->r.svFlags & SVF_BOT) { G_BotDel(i); } } } }
/** * Delete a random bot from a team (the worst score) * @param team */ void G_BotDelRandom ( team_t team ) { int i; gentity_t *ent; int score; int worst = -1; int worstScore = 9999; int reservedSlots = trap_Cvar_VariableIntegerValue( "sv_privateclients" ); for( i = level.maxclients - 1; i > level.maxclients - reservedSlots - 1; i-- ) { ent = &g_entities[ i ]; if(ent->r.svFlags & SVF_BOT && ent->client->pers.teamSelection == team) { score = ent->client->ps.persistant[ PERS_SCORE ]; if(score < worstScore) { worst = i; worstScore = score; } } } if(worst > 0) { G_BotDel(worst); } }
bool G_BotAdd( const char *name, team_t team, int skill, const char *behavior, bool filler ) { int clientNum; char userinfo[MAX_INFO_STRING]; const char* s = 0; gentity_t *bot; bool autoname = false; bool okay; if ( !navMeshLoaded ) { Log::Warn( "No Navigation Mesh file is available for this map" ); return false; } // find what clientNum to use for bot clientNum = trap_BotAllocateClient(); if ( clientNum < 0 ) { Log::Warn( "no more slots for bot" ); return false; } bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = true; if ( !Q_stricmp( name, BOT_NAME_FROM_LIST ) ) { name = G_BotSelectName( team ); autoname = name != nullptr; } //default bot data okay = G_BotSetDefaults( clientNum, team, skill, behavior ); // register user information userinfo[0] = '\0'; Info_SetValueForKey( userinfo, "name", name ? name : "", false ); // allow defaulting Info_SetValueForKey( userinfo, "rate", "25000", false ); Info_SetValueForKey( userinfo, "snaps", "20", false ); if ( autoname ) { Info_SetValueForKey( userinfo, "autoname", name, false ); } //so we can connect if server is password protected if ( g_needpass.integer == 1 ) { Info_SetValueForKey( userinfo, "password", g_password.string, false ); } trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ( ( s = ClientBotConnect( clientNum, true, team ) ) ) { // won't let us join Log::Warn( s ); okay = false; } if ( !okay ) { G_BotDel( clientNum ); return false; } if ( autoname ) { G_BotNameUsed( team, name, true ); } ClientBegin( clientNum ); bot->pain = BotPain; // ClientBegin resets the pain function level.clients[clientNum].pers.isFillerBot = filler; G_ChangeTeam( bot, team ); return true; }